mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-19 10:33:29 +00:00
Register selection card group governance
This commit is contained in:
parent
52928706d9
commit
36af6620d3
3 changed files with 99 additions and 0 deletions
|
|
@ -2426,6 +2426,12 @@ column chooser trigger, panel title, reset action, empty-state copy, hidden
|
|||
count badge, dropdown width, and outside-click lifecycle through the shared
|
||||
shell/state/model split. Table surfaces must compose that primitive instead of
|
||||
recreating local column chooser buttons or panels.
|
||||
Selection-card groups are registry-backed too. `SelectionCardGroup` owns the
|
||||
compact/detail card grid, active-card tone, disabled selection behavior,
|
||||
pressed-state semantics, title/description styling, and icon container
|
||||
treatment through the shared shell/state/model split. Settings and feature
|
||||
surfaces must compose that primitive instead of copying compact/detail
|
||||
border-card styling locally.
|
||||
Grouped/list table-mode controls are registry-backed as well.
|
||||
`GroupedTableModeSegmentedControl` owns the shared `Group by` label,
|
||||
`Grouped` / `List` labels, tooltip titles, and icons for table mode switching.
|
||||
|
|
|
|||
|
|
@ -273,6 +273,24 @@
|
|||
"scripts/shared-template-audit.mjs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "selection-card-group-shell",
|
||||
"category": "selection-card-group",
|
||||
"summary": "Selectable settings card groups must compose SelectionCardGroup instead of recreating compact/detail active-card styling and disabled selection behavior locally.",
|
||||
"canonical": {
|
||||
"path": "src/components/shared/SelectionCardGroup.tsx",
|
||||
"export": "SelectionCardGroup"
|
||||
},
|
||||
"requiredConsumers": [
|
||||
{ "path": "src/components/Settings/AISettingsDialogs.tsx" },
|
||||
{ "path": "src/components/Settings/UpdatesSettingsPanel.tsx" }
|
||||
],
|
||||
"proof": [
|
||||
"src/components/shared/SharedPrimitives.guardrails.test.ts",
|
||||
"src/components/shared/SelectionCardGroup.test.tsx",
|
||||
"scripts/shared-template-audit.mjs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "grouped-table-mode-segmented-control",
|
||||
"category": "table-mode-control",
|
||||
|
|
@ -424,6 +442,27 @@
|
|||
"scripts/shared-template-audit.mjs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "selection-card-group-local-active-card-styles",
|
||||
"category": "selection-card-group",
|
||||
"summary": "Settings and feature surfaces must not copy the compact/detail active-card styling owned by SelectionCardGroup.",
|
||||
"canonical": {
|
||||
"path": "src/components/shared/selectionCardGroupModel.ts",
|
||||
"export": "getSelectionCardButtonClass"
|
||||
},
|
||||
"scopes": ["src/components/Settings", "src/features", "src/pages"],
|
||||
"extensions": [".tsx"],
|
||||
"allPatterns": [
|
||||
"p-4 rounded-md border-2 transition-all text-left",
|
||||
"p-3 rounded-md border-2 transition-all text-center"
|
||||
],
|
||||
"legacyReason": "Retired migration debt. Selectable settings card groups must use SelectionCardGroup; local compact/detail active-card styling forks are not allowed.",
|
||||
"allowedPaths": [],
|
||||
"proof": [
|
||||
"src/components/shared/SharedPrimitives.guardrails.test.ts",
|
||||
"scripts/shared-template-audit.mjs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "grouped-table-mode-local-segmented-control",
|
||||
"category": "table-mode-control",
|
||||
|
|
|
|||
|
|
@ -244,20 +244,74 @@ describe('shared primitive guardrails', () => {
|
|||
});
|
||||
|
||||
it('routes selectable settings cards through SelectionCardGroup', () => {
|
||||
const registry = JSON.parse(sharedTemplateRegistrySource) as {
|
||||
rules?: Array<{
|
||||
id: string;
|
||||
canonical?: { path?: string; export?: string };
|
||||
requiredConsumers?: Array<{ path?: string }>;
|
||||
}>;
|
||||
patternGuards?: Array<{
|
||||
id: string;
|
||||
canonical?: { path?: string; export?: string };
|
||||
allPatterns?: string[];
|
||||
scopes?: string[];
|
||||
allowedPaths?: string[];
|
||||
}>;
|
||||
};
|
||||
const registeredRule = registry.rules?.find(
|
||||
(rule) => rule.id === 'selection-card-group-shell',
|
||||
);
|
||||
const registeredGuard = registry.patternGuards?.find(
|
||||
(guard) => guard.id === 'selection-card-group-local-active-card-styles',
|
||||
);
|
||||
|
||||
expect(registeredRule?.canonical?.path).toBe(
|
||||
'src/components/shared/SelectionCardGroup.tsx',
|
||||
);
|
||||
expect(registeredRule?.canonical?.export).toBe('SelectionCardGroup');
|
||||
expect(registeredRule?.requiredConsumers?.map((consumer) => consumer.path)).toEqual([
|
||||
'src/components/Settings/AISettingsDialogs.tsx',
|
||||
'src/components/Settings/UpdatesSettingsPanel.tsx',
|
||||
]);
|
||||
expect(registeredGuard?.canonical?.path).toBe(
|
||||
'src/components/shared/selectionCardGroupModel.ts',
|
||||
);
|
||||
expect(registeredGuard?.canonical?.export).toBe(
|
||||
'getSelectionCardButtonClass',
|
||||
);
|
||||
expect(registeredGuard?.allPatterns).toEqual([
|
||||
'p-4 rounded-md border-2 transition-all text-left',
|
||||
'p-3 rounded-md border-2 transition-all text-center',
|
||||
]);
|
||||
expect(registeredGuard?.allowedPaths ?? []).toHaveLength(0);
|
||||
expect(registeredGuard?.scopes).toEqual(
|
||||
expect.arrayContaining([
|
||||
'src/components/Settings',
|
||||
'src/features',
|
||||
'src/pages',
|
||||
]),
|
||||
);
|
||||
|
||||
expect(selectionCardGroupSource).toContain('useSelectionCardGroupState');
|
||||
expect(selectionCardGroupSource).toContain('getSelectionCardGroupClass');
|
||||
expect(selectionCardGroupSource).toContain('getSelectionCardButtonClass');
|
||||
expect(selectionCardGroupSource).toContain('getSelectionCardTitleClass');
|
||||
expect(selectionCardGroupSource).toContain('aria-pressed');
|
||||
expect(selectionCardGroupSource).toContain('disabled={selectionCardGroup.isOptionDisabled');
|
||||
expect(selectionCardGroupSource).not.toContain('resolveSelectionCardTone');
|
||||
expect(selectionCardGroupSource).not.toContain('props.onChange(option.value)');
|
||||
expect(selectionCardGroupStateSource).toContain('export function useSelectionCardGroupState');
|
||||
expect(selectionCardGroupStateSource).toContain('createMemo');
|
||||
expect(selectionCardGroupStateSource).toContain('resolveSelectionCardTone');
|
||||
expect(selectionCardGroupStateSource).toContain('props.disabled || option.disabled');
|
||||
expect(selectionCardGroupStateSource).toContain('props.onChange(option.value)');
|
||||
expect(selectionCardGroupModelSource).toContain('resolveSelectionCardGroupVariant');
|
||||
expect(selectionCardGroupModelSource).toContain('resolveSelectionCardTone');
|
||||
expect(selectionCardGroupModelSource).toContain('getSelectionCardButtonClass');
|
||||
expect(selectionCardGroupModelSource).toContain("compact: 'grid grid-cols-2 gap-2'");
|
||||
expect(selectionCardGroupModelSource).toContain(
|
||||
"detail: 'grid grid-cols-1 gap-3'",
|
||||
);
|
||||
expect(aiSettingsDialogsSource).toContain('SelectionCardGroup');
|
||||
expect(aiSettingsDialogsSource).toContain('variant="compact"');
|
||||
expect(aiSettingsDialogsSource).not.toContain(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue