From 300b5f9b415ff2cad047dad4632e53df1f221a33 Mon Sep 17 00:00:00 2001
From: Michael Han <107991372+shimmyshimmer@users.noreply.github.com>
Date: Wed, 15 Jul 2026 07:43:40 -0700
Subject: [PATCH] Fix Studio toast close-button positioning (#7142)
* Fix Studio toast close-button positioning
* Use UTF-8 for locale regression test
* [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
* Harden toast close-button positioning
* Limit language menu height
---------
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
---
studio/frontend/src/components/ui/sonner.tsx | 2 +-
.../settings/components/language-select.tsx | 6 +++-
studio/frontend/src/i18n/locale-store.ts | 1 -
studio/frontend/src/i18n/messages.ts | 25 ++++++++--------
.../test_locale_root_direction_contract.py | 30 +++++++++++++++++++
5 files changed, 48 insertions(+), 16 deletions(-)
create mode 100644 tests/studio/test_locale_root_direction_contract.py
diff --git a/studio/frontend/src/components/ui/sonner.tsx b/studio/frontend/src/components/ui/sonner.tsx
index d6df0f4eaa..aec1235b81 100644
--- a/studio/frontend/src/components/ui/sonner.tsx
+++ b/studio/frontend/src/components/ui/sonner.tsx
@@ -90,7 +90,7 @@ const Toaster = ({ ...props }: ToasterProps) => {
// Pin the close button inside the toast's top-right corner.
// Sonner defaults to the left/outside edge, so keep the horizontal
// override here and the top offset in index.css.
- "--toast-close-button-start": "unset",
+ "--toast-close-button-start": "auto",
"--toast-close-button-end": "8px",
"--toast-close-button-transform": "none",
} as React.CSSProperties
diff --git a/studio/frontend/src/features/settings/components/language-select.tsx b/studio/frontend/src/features/settings/components/language-select.tsx
index 01fe049a56..1f388d6f2c 100644
--- a/studio/frontend/src/features/settings/components/language-select.tsx
+++ b/studio/frontend/src/features/settings/components/language-select.tsx
@@ -35,7 +35,11 @@ export function LanguageSelect() {
>
-
+
{t("settings.appearance.language.autoDetect")}
diff --git a/studio/frontend/src/i18n/locale-store.ts b/studio/frontend/src/i18n/locale-store.ts
index cd5cac8db0..792e7e77fb 100644
--- a/studio/frontend/src/i18n/locale-store.ts
+++ b/studio/frontend/src/i18n/locale-store.ts
@@ -98,7 +98,6 @@ function writeStoredPreference(preference: LocalePreference): void {
function syncDocumentLang(locale: Locale): void {
if (typeof document === "undefined") return;
document.documentElement.lang = locale;
- document.documentElement.dir = LOCALES[locale].dir;
}
function notifySubscribers(): void {
diff --git a/studio/frontend/src/i18n/messages.ts b/studio/frontend/src/i18n/messages.ts
index 074f4ab44f..ead94a966e 100644
--- a/studio/frontend/src/i18n/messages.ts
+++ b/studio/frontend/src/i18n/messages.ts
@@ -15,19 +15,18 @@ import { de } from "./locales/de";
import { ko } from "./locales/ko";
import type { InterpolationValues, MessageKey } from "./types";
-// dir sets documentElement.dir; Arabic stays ltr (CSS still physical-direction) but renders rtl via bidi.
export const LOCALES = {
- en: { label: "English", nativeLabel: "English", dir: "ltr" },
- "zh-CN": { label: "Chinese (Simplified)", nativeLabel: "简体中文", dir: "ltr" },
- ja: { label: "Japanese", nativeLabel: "日本語", dir: "ltr" },
- ko: { label: "Korean", nativeLabel: "한국어", dir: "ltr" },
- es: { label: "Spanish", nativeLabel: "Español", dir: "ltr" },
- "pt-BR": { label: "Portuguese (Brazil)", nativeLabel: "Português (Brasil)", dir: "ltr" },
- fr: { label: "French", nativeLabel: "Français", dir: "ltr" },
- de: { label: "German", nativeLabel: "Deutsch", dir: "ltr" },
- ru: { label: "Russian", nativeLabel: "Русский", dir: "ltr" },
- hi: { label: "Hindi", nativeLabel: "हिन्दी", dir: "ltr" },
- ar: { label: "Arabic", nativeLabel: "العربية", dir: "ltr" },
+ en: { label: "English", nativeLabel: "English" },
+ "zh-CN": { label: "Chinese (Simplified)", nativeLabel: "简体中文" },
+ ja: { label: "Japanese", nativeLabel: "日本語" },
+ ko: { label: "Korean", nativeLabel: "한국어" },
+ es: { label: "Spanish", nativeLabel: "Español" },
+ "pt-BR": { label: "Portuguese (Brazil)", nativeLabel: "Português (Brasil)" },
+ fr: { label: "French", nativeLabel: "Français" },
+ de: { label: "German", nativeLabel: "Deutsch" },
+ ru: { label: "Russian", nativeLabel: "Русский" },
+ hi: { label: "Hindi", nativeLabel: "हिन्दी" },
+ ar: { label: "Arabic", nativeLabel: "العربية" },
} as const;
export type Locale = keyof typeof LOCALES;
@@ -105,4 +104,4 @@ export function isSupportedLocale(value: unknown): value is Locale {
typeof value === "string" &&
Object.prototype.hasOwnProperty.call(LOCALES, value)
);
-}
\ No newline at end of file
+}
diff --git a/tests/studio/test_locale_root_direction_contract.py b/tests/studio/test_locale_root_direction_contract.py
new file mode 100644
index 0000000000..1baabdbbca
--- /dev/null
+++ b/tests/studio/test_locale_root_direction_contract.py
@@ -0,0 +1,30 @@
+"""Regression guard for locale changes affecting the entire Studio layout."""
+
+from pathlib import Path
+
+
+REPO = Path(__file__).resolve().parents[2]
+LOCALE_STORE = REPO / "studio/frontend/src/i18n/locale-store.ts"
+MESSAGES = REPO / "studio/frontend/src/i18n/messages.ts"
+SONNER = REPO / "studio/frontend/src/components/ui/sonner.tsx"
+
+
+def test_locale_changes_do_not_force_document_direction():
+ src = LOCALE_STORE.read_text(encoding = "utf-8")
+ assert "document.documentElement.lang = locale" in src
+ assert "document.documentElement.dir" not in src, (
+ "locale changes must not force the root direction; html[dir] changes "
+ "third-party component layout, including Sonner close-button positioning"
+ )
+
+
+def test_locale_metadata_does_not_advertise_unused_layout_direction():
+ src = MESSAGES.read_text(encoding = "utf-8")
+ locales_block = src[src.index("export const LOCALES") : src.index("export type Locale")]
+ assert "dir:" not in locales_block
+
+
+def test_toast_close_position_does_not_inherit_root_direction():
+ src = SONNER.read_text(encoding = "utf-8")
+ assert '"--toast-close-button-start": "auto"' in src
+ assert '"--toast-close-button-start": "unset"' not in src