diff --git a/.changeset/30070-customization-mark-words.md b/.changeset/30070-customization-mark-words.md new file mode 100644 index 00000000000..906234c5b17 --- /dev/null +++ b/.changeset/30070-customization-mark-words.md @@ -0,0 +1,11 @@ +--- +'@object-ui/app-shell': patch +'@object-ui/i18n': patch +--- + +fix(app-shell): Studio's rails mark a customized row with the word 定制 / Custom and an added row with 新增 / Added, no fill, instead of a filled square with a glyph that read as an add button (#30070) + +Clause-②: yes (widening) — new studio i18n key `engine.customizationMark.customizedShort` (en, zh) +Metadata-protocol: none + +The compact customization mark the pillar rails draw (and the Interfaces rail's navigation-override mark, which wraps it) now prints one short coloured word with no background. Its tooltip and accessible name are unchanged: the full text ("已定制(HotCRM)") followed by the sentence naming the customization package. It is not a control: a click on it is a click on its row. diff --git a/ui/packages/app-shell/src/views/studio-design/CustomizationMark.tsx b/ui/packages/app-shell/src/views/studio-design/CustomizationMark.tsx index 9dd7fe3ac73..5a34f52afed 100644 --- a/ui/packages/app-shell/src/views/studio-design/CustomizationMark.tsx +++ b/ui/packages/app-shell/src/views/studio-design/CustomizationMark.tsx @@ -15,7 +15,6 @@ */ import * as React from 'react'; -import { Layers3, PencilRuler, Plus } from 'lucide-react'; import { cn } from '@object-ui/components'; import { customizationPackageId } from '@objectstack/spec/kernel'; import { t, tFormat } from '../metadata-admin/i18n.js'; @@ -36,18 +35,20 @@ const KIND_CLASS: Record = { customizationOf: 'bg-muted text-muted-foreground', }; -/** objectstack#29693 — the glyph the compact mark draws in place of its text. */ -const KIND_ICON: Record = { - customized: PencilRuler, - added: Plus, - customizationOf: Layers3, +/** The compact mark's colour: its word alone, no fill, so it never reads as a button. */ +const KIND_TEXT_CLASS: Record = { + customized: 'text-violet-700 dark:text-violet-300', + added: 'text-emerald-700 dark:text-emerald-300', + customizationOf: 'text-muted-foreground', }; /** - * objectstack#29693 — `compact` (the pillar rails): the mark is its glyph - * alone, so it never takes a row's name ("已定制(ObjectStack Showcase)" left a - * selected flow row no room for its label). Its text is its accessible name - * and leads its tooltip, before the sentence naming the pair. + * `compact` (the pillar rails): the mark prints one short word with no + * background (objectstack#30070: 定制 for customized, 新增 for added), so it + * reads as a status, never as a button, and never takes a row's name + * (objectstack#29693). Its full text is its accessible name and leads its + * tooltip, before the sentence naming the pair. It is not a control: a click + * on it is a click on its row. */ export function CustomizationMark({ kind, @@ -64,7 +65,7 @@ export function CustomizationMark({ pairId?: string; locale: string; className?: string; - /** Draw the glyph alone, the text in its accessible name and tooltip. */ + /** Print one short word, no fill; the full text is its accessible name and leads its tooltip. */ compact?: boolean; }): React.ReactElement { const base = baseName ?? ''; @@ -76,7 +77,6 @@ export function CustomizationMark({ ? tFormat(`engine.customizationMark.${kind}Title`, locale, { baseName: base, pair: pairId }) : undefined; if (compact) { - const Icon = KIND_ICON[kind]; return ( - ); } diff --git a/ui/packages/app-shell/src/views/studio-design/StudioDesignSurface.oneSurface-28572.test.tsx b/ui/packages/app-shell/src/views/studio-design/StudioDesignSurface.oneSurface-28572.test.tsx index 7192c516a89..12f74ec1140 100644 --- a/ui/packages/app-shell/src/views/studio-design/StudioDesignSurface.oneSurface-28572.test.tsx +++ b/ui/packages/app-shell/src/views/studio-design/StudioDesignSurface.oneSurface-28572.test.tsx @@ -464,8 +464,8 @@ describe('the Interfaces rail lists a base and its pair as one list (objectstack }); }); -describe('the rail mark never takes the row’s name (objectstack#29693)', () => { - it('is its glyph alone: the row prints only its label, and the mark’s text is its name and leads its tooltip', async () => { +describe('the rail mark never takes the row’s name (objectstack#29693, objectstack#30070)', () => { + it('prints one short word with no fill and no glyph: its full text is its name and leads its tooltip', async () => { renderAt(`/studio/${BASE}/interfaces`); await interfacesReady(); const row = rowOf(ifRail(), 'dashboard', 'pipeline')!; @@ -475,11 +475,16 @@ describe('the rail mark never takes the row’s name (objectstack#29693)', () => expect(mark).toHaveAttribute('data-compact'); expect(mark).toHaveAccessibleName(label); expect(mark.getAttribute('title')?.split('\n')[0]).toBe(label); - expect(mark.textContent).toBe(''); + expect(mark.textContent).toBe(t('engine.customizationMark.customizedShort', 'en')); expect(within(row).queryByText(label)).toBeNull(); const added = within(rowOf(ifRail(), 'dashboard', 'wins')!).getByTestId('customization-mark'); expect(added).toHaveAccessibleName(t('engine.customizationMark.added', 'en')); - expect(added.textContent).toBe(''); + expect(added.textContent).toBe(t('engine.customizationMark.added', 'en')); + // A status, not a control: no filled block, no glyph that reads as an add button. + for (const m of [mark, added]) { + expect(m.querySelector('svg')).toBeNull(); + expect(m.className).not.toMatch(/(^|\s)bg-/); + } }); }); diff --git a/ui/packages/app-shell/src/views/studio-design/StudioRailCustomizations.tsx b/ui/packages/app-shell/src/views/studio-design/StudioRailCustomizations.tsx index cb25be17dff..31a56dc2ed5 100644 --- a/ui/packages/app-shell/src/views/studio-design/StudioRailCustomizations.tsx +++ b/ui/packages/app-shell/src/views/studio-design/StudioRailCustomizations.tsx @@ -115,9 +115,9 @@ export function railCustomizationsOf( } /** - * A row's mark in the rail's trailing slot, or nothing. objectstack#29693 — - * compact (its glyph, the text in its name and tooltip), so it never takes the - * row's label. + * A row's mark in the rail's trailing slot, or nothing. Compact (one short + * word, no fill; the full text in its name and tooltip), so it never takes the + * row's label and never reads as a button. */ export function RailCustomizationMark({ row, diff --git a/ui/packages/i18n/src/namespaces/studio/en.ts b/ui/packages/i18n/src/namespaces/studio/en.ts index d69c504d241..b557f376859 100644 --- a/ui/packages/i18n/src/namespaces/studio/en.ts +++ b/ui/packages/i18n/src/namespaces/studio/en.ts @@ -3677,6 +3677,7 @@ const STUDIO_EN: Record = { // objectstack ADR-0146 D3 (#28568) — the customization marks, counts and // filter of the All metadata area, from a base's customization read. 'engine.customizationMark.customized': 'Customized ({baseName})', + 'engine.customizationMark.customizedShort': 'Custom', 'engine.customizationMark.customizedTitle': 'The customization package {pair} overrides this item of {baseName}', 'engine.customizationMark.added': 'Added', 'engine.customizationMark.addedTitle': 'Added to {baseName} by its customization package {pair}', diff --git a/ui/packages/i18n/src/namespaces/studio/zh.ts b/ui/packages/i18n/src/namespaces/studio/zh.ts index e4a62e157cd..bd3c1cff6db 100644 --- a/ui/packages/i18n/src/namespaces/studio/zh.ts +++ b/ui/packages/i18n/src/namespaces/studio/zh.ts @@ -3254,6 +3254,7 @@ const STUDIO_ZH: Record = { // objectstack ADR-0146 D3 (#28568) — the customization marks, counts and // filter of the All metadata area, from a base's customization read. 'engine.customizationMark.customized': '已定制({baseName})', + 'engine.customizationMark.customizedShort': '定制', 'engine.customizationMark.customizedTitle': '定制包 {pair} 覆盖了 {baseName} 的此项', 'engine.customizationMark.added': '新增', 'engine.customizationMark.addedTitle': '由 {baseName} 的定制包 {pair} 新增',