From 3e9f75888074868c9c988a375b166d1a4227e2e3 Mon Sep 17 00:00:00 2001 From: kev1n77 Date: Tue, 8 Sep 2026 22:10:54 +0800 Subject: [PATCH 1/2] fix(ui): align field states and search affordances Align light field borders and placeholder colors while preserving visible keyboard focus contrast, named theme palettes, and legacy imported overrides. Size search artwork through the existing Input slot, show shortcuts together with clear actions, and prevent clearing disabled or read-only fields. Keep all existing size tokens, size APIs, heights, and product size selections. Update Design Lab state examples, DOM regression coverage and generated theme projections. Validate with design-system:check, check:web, 48 product tests, and 22 focused component tests. --- .../apps/design-lab/src/i18n/messages.ts | 9 ++ .../src/pages/ComponentDetailPage.tsx | 59 +++++++------ .../vite/component-detail-contract.test.mjs | 10 ++- .../packages/theme-openbitfun/README.md | 11 +++ .../theme-openbitfun/src/dark.tokens.json | 1 + .../src/high-contrast-dark.tokens.json | 1 + .../src/high-contrast-light.tokens.json | 1 + .../theme-openbitfun/src/light.tokens.json | 5 +- design-system/packages/ui/README.md | 4 + .../ui/src/components/Input/Input.meta.ts | 5 +- .../ui/src/components/Input/Input.module.css | 18 +++- .../SearchField/SearchField.meta.ts | 4 +- .../SearchField/SearchField.module.css | 16 ++++ .../components/SearchField/SearchField.tsx | 20 +++-- .../ui/src/components/Select/Select.meta.ts | 2 + .../src/components/Select/Select.module.css | 5 +- .../packages/ui/tests/icon-slots.test.mjs | 1 + .../packages/ui/tests/search-field.test.mjs | 3 +- .../ui/generated/design-system.css | 8 +- .../desktop/src/generated/bootstrap_theme.css | 1 + .../generated/default_appearance_style.html | 2 +- .../builtins/appearancePresetOutput.test.ts | 19 +++++ .../builtins/buildBuiltinAppearance.ts | 11 +++ .../builtins/composeAppearancePackage.test.ts | 36 ++++++++ .../builtins/composeAppearancePackage.ts | 5 +- .../builtins/fieldThemeCompatibility.ts | 14 +++ src/web-ui/src/shared/ui/SearchField.test.tsx | 85 +++++++++++++++++++ 27 files changed, 306 insertions(+), 50 deletions(-) create mode 100644 src/web-ui/src/infrastructure/appearance/builtins/fieldThemeCompatibility.ts create mode 100644 src/web-ui/src/shared/ui/SearchField.test.tsx diff --git a/design-system/apps/design-lab/src/i18n/messages.ts b/design-system/apps/design-lab/src/i18n/messages.ts index e8a7318e65..0537a227e5 100644 --- a/design-system/apps/design-lab/src/i18n/messages.ts +++ b/design-system/apps/design-lab/src/i18n/messages.ts @@ -528,6 +528,9 @@ export const enUSMessages = { "detail.option.selected-item": "Selected item", "detail.option.focus-visible": "Focus visible", "detail.option.disabled": "Disabled", + "detail.option.filled": "Filled", + "detail.option.read-only": "Read only", + "components.preview.searchClear": "Clear search", "detail.option.display": "Display", "detail.option.completed": "Completed", "detail.option.expanded": "Expanded", @@ -1268,6 +1271,9 @@ export const zhCNMessages = { "detail.option.selected-item": "选中项目", "detail.option.focus-visible": "焦点可见", "detail.option.disabled": "禁用", + "detail.option.filled": "已输入", + "detail.option.read-only": "只读", + "components.preview.searchClear": "清除搜索", "detail.option.display": "展示", "detail.option.completed": "已完成", "detail.option.expanded": "展开", @@ -1947,6 +1953,9 @@ export const zhTWMessages = { "detail.option.selected-item": "選取項目", "detail.option.focus-visible": "焦點可見", "detail.option.disabled": "停用", + "detail.option.filled": "已輸入", + "detail.option.read-only": "唯讀", + "components.preview.searchClear": "清除搜尋", "detail.option.display": "展示", "detail.option.completed": "已完成", "detail.option.expanded": "展開", diff --git a/design-system/apps/design-lab/src/pages/ComponentDetailPage.tsx b/design-system/apps/design-lab/src/pages/ComponentDetailPage.tsx index 5c7012c66a..73075f9fd7 100644 --- a/design-system/apps/design-lab/src/pages/ComponentDetailPage.tsx +++ b/design-system/apps/design-lab/src/pages/ComponentDetailPage.tsx @@ -178,6 +178,8 @@ const optionLabelKeys: Readonly> = { default: "detail.option.default", replacing: "detail.option.replacing", disabled: "detail.option.disabled", + filled: "detail.option.filled", + "read-only": "detail.option.read-only", display: "detail.option.display", error: "detail.option.error", expanded: "detail.option.expanded", @@ -298,6 +300,27 @@ function NumberInputPreview({ state }: { state: string }) { ); } +function SearchFieldStatePreview({ state }: { state: string }) { + const { t } = useI18n(); + const [value, setValue] = useState(state === "default" ? "" : "OpenBitFun"); + return ( + } + onClear={() => setValue("")} + onValueChange={setValue} + placeholder={t("components.preview.searchPlaceholder")} + readOnly={state === "read-only"} + shortcut={}>K} + value={value} + /> + ); +} + export function ComponentDetailPage({ colorScheme, component, @@ -399,7 +422,7 @@ export function ComponentDetailPage({ return ["raised", "subtle", "media"] as const; case "Input": case "SearchField": - return ["default", "hover", "focus-visible", "invalid", "disabled"] as const; + return ["default", "filled", "hover", "focus-visible", "read-only", "invalid", "disabled"] as const; case "Select": return ["default", "hover", "focus-visible", "open", "invalid", "disabled"] as const; case "Field": @@ -541,7 +564,9 @@ export function ComponentDetailPage({ ? " disabled" : previewState === "invalid" ? " invalid" - : ""; + : previewState === "read-only" + ? ' readOnly defaultValue="OpenBitFun"' + : previewState === "default" ? "" : ' defaultValue="OpenBitFun"'; return `import { Icon, Input } from "@openbitfun/ui";\n\n}${stateProps}\n/>`; } if (component.name === "KeyHint") { @@ -570,12 +595,8 @@ export function ComponentDetailPage({ return `import { Icon, IconButton, PageHeader } from "@openbitfun/ui";\n\n} />}\n align="${pageHeaderAlign}"\n description="${t("components.preview.appearanceDescription")}"\n leading={}\n level={2}${requiredProp}\n size="${pageHeaderSize}"\n title="${t("components.preview.appearance")}"\n/>`; } if (component.name === "SearchField") { - const stateProps = previewState === "disabled" - ? " disabled" - : previewState === "invalid" - ? " invalid" - : ""; - return `import { Icon, KeyHint, SearchField } from "@openbitfun/ui";\n\n}\n placeholder="${t("components.preview.searchPlaceholder")}"\n shortcut={}>K}${stateProps}\n/>`; + const searchStateProps = previewState === "disabled" ? " disabled" : previewState === "invalid" ? " invalid" : previewState === "read-only" ? " readOnly" : ""; + return `import { useState } from "react";\nimport { Icon, KeyHint, SearchField } from "@openbitfun/ui";\n\nfunction Example() {\n const [query, setQuery] = useState(${JSON.stringify(previewState === "default" ? "" : "OpenBitFun")});\n return (\n setQuery("")}\n onValueChange={setQuery}\n value={query}\n aria-label="${t("components.preview.searchLabel")}"\n leadingIcon={}\n placeholder="${t("components.preview.searchPlaceholder")}"\n shortcut={}>K}${searchStateProps}\n />\n );\n}`; } if (component.name === "Combobox") { return `import { Combobox } from "@openbitfun/ui";\n\n`; @@ -1327,10 +1348,13 @@ export function ComponentDetailPage({ } + readOnly={state === "read-only"} + trailing={} /> ); } @@ -1775,22 +1799,7 @@ export function ComponentDetailPage({ } if (component.name === "SearchField") { - const previewClassName = state === "hover" - ? "lab-force-hover" - : state === "focus-visible" - ? "lab-force-focus" - : undefined; - return ( -