mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-25 04:33:03 +00:00
Split selection card group owners
This commit is contained in:
@@ -279,6 +279,14 @@ owns variant resolution plus disabled selection/change runtime, and
|
||||
variant class catalog, compact-label policy, and segmented button class
|
||||
selection. Future filter-button-group work should extend those owners instead
|
||||
of pushing label truncation or segmented variant policy back into the shell.
|
||||
The shared selection-card primitive now follows that same owner split.
|
||||
`frontend-modern/src/components/shared/SelectionCardGroup.tsx` stays the render
|
||||
shell, `frontend-modern/src/components/shared/useSelectionCardGroupState.ts`
|
||||
owns variant resolution plus disabled selection/change runtime, and
|
||||
`frontend-modern/src/components/shared/selectionCardGroupModel.ts` owns the
|
||||
tone fallback, group/button class catalog, and title/description presentation
|
||||
policy. Future selection-card-group work should extend those owners instead of
|
||||
pushing tone or active-card presentation logic back into the shell.
|
||||
The shared dialog now follows that same owner split.
|
||||
`frontend-modern/src/components/shared/Dialog.tsx` stays the render shell,
|
||||
`frontend-modern/src/components/shared/useDialogState.ts` owns focus trap,
|
||||
|
||||
@@ -1,12 +1,37 @@
|
||||
import { cleanup, fireEvent, render, screen } from '@solidjs/testing-library';
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||
import { SelectionCardGroup } from './SelectionCardGroup';
|
||||
import selectionCardGroupSource from './SelectionCardGroup.tsx?raw';
|
||||
import selectionCardGroupModelSource from './selectionCardGroupModel.ts?raw';
|
||||
import selectionCardGroupStateSource from './useSelectionCardGroupState.ts?raw';
|
||||
|
||||
describe('SelectionCardGroup', () => {
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
});
|
||||
|
||||
it('keeps shell, runtime, and model owners split', () => {
|
||||
expect(selectionCardGroupSource).toContain('useSelectionCardGroupState');
|
||||
expect(selectionCardGroupSource).toContain('getSelectionCardGroupClass');
|
||||
expect(selectionCardGroupSource).toContain('getSelectionCardButtonClass');
|
||||
expect(selectionCardGroupSource).toContain('getSelectionCardTitleClass');
|
||||
expect(selectionCardGroupSource).not.toContain('resolveSelectionCardTone');
|
||||
expect(selectionCardGroupSource).not.toContain('props.onChange(option.value)');
|
||||
expect(selectionCardGroupSource).not.toContain('groupClassByVariant');
|
||||
|
||||
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('getSelectionCardTitleClass');
|
||||
expect(selectionCardGroupModelSource).toContain("compact: 'grid grid-cols-2 gap-2'");
|
||||
});
|
||||
|
||||
it('routes compact card selection changes through the shared primitive', () => {
|
||||
const onChange = vi.fn();
|
||||
|
||||
@@ -32,6 +57,28 @@ describe('SelectionCardGroup', () => {
|
||||
expect(onChange).toHaveBeenCalledWith('openai');
|
||||
});
|
||||
|
||||
it('blocks disabled selection changes in the runtime owner', () => {
|
||||
const onChange = vi.fn();
|
||||
|
||||
render(() => (
|
||||
<SelectionCardGroup
|
||||
options={[
|
||||
{ value: 'stable', title: 'Stable' },
|
||||
{ value: 'rc', title: 'Release Candidate', disabled: true },
|
||||
]}
|
||||
value="stable"
|
||||
onChange={onChange}
|
||||
variant="detail"
|
||||
/>
|
||||
));
|
||||
|
||||
const rcButton = screen.getByRole('button', { name: /release candidate/i });
|
||||
expect(rcButton).toBeDisabled();
|
||||
|
||||
fireEvent.click(rcButton);
|
||||
expect(onChange).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('supports detail cards with success tone styling', () => {
|
||||
render(() => (
|
||||
<SelectionCardGroup
|
||||
|
||||
@@ -1,131 +1,89 @@
|
||||
import { For, type JSX } from 'solid-js';
|
||||
import { For } from 'solid-js';
|
||||
import {
|
||||
getSelectionCardButtonClass,
|
||||
getSelectionCardDescriptionClass,
|
||||
getSelectionCardGroupClass,
|
||||
getSelectionCardIconContainerClass,
|
||||
getSelectionCardTitleClass,
|
||||
type SelectionCardGroupProps,
|
||||
} from './selectionCardGroupModel';
|
||||
import { useSelectionCardGroupState } from './useSelectionCardGroupState';
|
||||
|
||||
export type SelectionCardTone = 'accent' | 'success';
|
||||
type SelectionCardGroupVariant = 'compact' | 'detail';
|
||||
|
||||
export interface SelectionCardOption<T extends string | number> {
|
||||
value: T;
|
||||
title: string;
|
||||
description?: string;
|
||||
icon?: (props: { active: boolean }) => JSX.Element;
|
||||
tone?: SelectionCardTone;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
interface SelectionCardGroupProps<T extends string | number> {
|
||||
options: SelectionCardOption<T>[];
|
||||
value: T;
|
||||
onChange: (value: T) => void;
|
||||
class?: string;
|
||||
variant?: SelectionCardGroupVariant;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
const groupClassByVariant: Record<SelectionCardGroupVariant, string> = {
|
||||
compact: 'grid grid-cols-2 gap-2',
|
||||
detail: 'grid grid-cols-1 gap-3',
|
||||
};
|
||||
|
||||
function activeCardClass(tone: SelectionCardTone): string {
|
||||
if (tone === 'success') {
|
||||
return 'border-green-500 bg-green-50 dark:bg-green-900';
|
||||
}
|
||||
return 'border-blue-500 bg-blue-50 dark:bg-blue-900';
|
||||
}
|
||||
|
||||
function inactiveCardClass(variant: SelectionCardGroupVariant): string {
|
||||
if (variant === 'compact') {
|
||||
return 'border-border hover:border-blue-300';
|
||||
}
|
||||
return 'border-border hover:border-border';
|
||||
}
|
||||
|
||||
function buttonClass(
|
||||
variant: SelectionCardGroupVariant,
|
||||
tone: SelectionCardTone,
|
||||
active: boolean,
|
||||
disabled: boolean,
|
||||
): string {
|
||||
const base =
|
||||
variant === 'detail'
|
||||
? 'p-4 rounded-md border-2 transition-all text-left'
|
||||
: 'p-3 rounded-md border-2 transition-all text-center';
|
||||
|
||||
return [
|
||||
base,
|
||||
active ? activeCardClass(tone) : inactiveCardClass(variant),
|
||||
disabled ? 'disabled:opacity-50 disabled:cursor-not-allowed' : '',
|
||||
].join(' ');
|
||||
}
|
||||
|
||||
function iconContainerClass(tone: SelectionCardTone, active: boolean): string {
|
||||
const activeClass =
|
||||
tone === 'success' ? 'bg-green-100 dark:bg-green-800' : 'bg-blue-100 dark:bg-blue-800';
|
||||
return ['p-2 rounded-md', active ? activeClass : 'bg-surface-alt'].join(' ');
|
||||
}
|
||||
|
||||
function titleClass(
|
||||
variant: SelectionCardGroupVariant,
|
||||
tone: SelectionCardTone,
|
||||
active: boolean,
|
||||
): string {
|
||||
if (variant === 'compact') {
|
||||
return 'text-sm font-medium text-base-content';
|
||||
}
|
||||
if (!active) {
|
||||
return 'text-sm font-semibold text-base-content';
|
||||
}
|
||||
return tone === 'success'
|
||||
? 'text-sm font-semibold text-green-900 dark:text-green-100'
|
||||
: 'text-sm font-semibold text-blue-900 dark:text-blue-100';
|
||||
}
|
||||
|
||||
function descriptionClass(variant: SelectionCardGroupVariant): string {
|
||||
return variant === 'compact' ? 'text-xs text-slate-500 mt-0.5' : 'text-xs text-muted';
|
||||
}
|
||||
export type {
|
||||
SelectionCardGroupProps,
|
||||
SelectionCardGroupVariant,
|
||||
SelectionCardOption,
|
||||
SelectionCardTone,
|
||||
} from './selectionCardGroupModel';
|
||||
|
||||
export function SelectionCardGroup<T extends string | number>(props: SelectionCardGroupProps<T>) {
|
||||
const variant = () => props.variant ?? 'detail';
|
||||
const selectionCardGroup = useSelectionCardGroupState(props);
|
||||
|
||||
return (
|
||||
<div
|
||||
class={`${groupClassByVariant[variant()]} ${props.class ?? ''}`.trim()}
|
||||
class={getSelectionCardGroupClass(selectionCardGroup.variant(), props.class)}
|
||||
role="group"
|
||||
aria-label="Selection Cards"
|
||||
>
|
||||
<For each={props.options}>
|
||||
{(option) => {
|
||||
const isActive = () => option.value === props.value;
|
||||
const isDisabled = () => props.disabled || option.disabled || false;
|
||||
const tone = () => option.tone ?? 'accent';
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => props.onChange(option.value)}
|
||||
class={buttonClass(variant(), tone(), isActive(), isDisabled())}
|
||||
aria-pressed={isActive()}
|
||||
disabled={isDisabled()}
|
||||
onClick={() => selectionCardGroup.handleOptionClick(option)}
|
||||
class={getSelectionCardButtonClass(
|
||||
selectionCardGroup.variant(),
|
||||
selectionCardGroup.getOptionTone(option),
|
||||
selectionCardGroup.isOptionActive(option),
|
||||
selectionCardGroup.isOptionDisabled(option),
|
||||
)}
|
||||
aria-pressed={selectionCardGroup.isOptionActive(option)}
|
||||
disabled={selectionCardGroup.isOptionDisabled(option)}
|
||||
>
|
||||
{variant() === 'detail' ? (
|
||||
{selectionCardGroup.variant() === 'detail' ? (
|
||||
<div class="flex items-center gap-3">
|
||||
{option.icon && (
|
||||
<div class={iconContainerClass(tone(), isActive())}>
|
||||
{option.icon({ active: isActive() })}
|
||||
<div
|
||||
class={getSelectionCardIconContainerClass(
|
||||
selectionCardGroup.getOptionTone(option),
|
||||
selectionCardGroup.isOptionActive(option),
|
||||
)}
|
||||
>
|
||||
{option.icon({ active: selectionCardGroup.isOptionActive(option) })}
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<p class={titleClass(variant(), tone(), isActive())}>{option.title}</p>
|
||||
<p
|
||||
class={getSelectionCardTitleClass(
|
||||
selectionCardGroup.variant(),
|
||||
selectionCardGroup.getOptionTone(option),
|
||||
selectionCardGroup.isOptionActive(option),
|
||||
)}
|
||||
>
|
||||
{option.title}
|
||||
</p>
|
||||
{option.description && (
|
||||
<p class={descriptionClass(variant())}>{option.description}</p>
|
||||
<p class={getSelectionCardDescriptionClass(selectionCardGroup.variant())}>
|
||||
{option.description}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
<div class={titleClass(variant(), tone(), isActive())}>{option.title}</div>
|
||||
<div
|
||||
class={getSelectionCardTitleClass(
|
||||
selectionCardGroup.variant(),
|
||||
selectionCardGroup.getOptionTone(option),
|
||||
selectionCardGroup.isOptionActive(option),
|
||||
)}
|
||||
>
|
||||
{option.title}
|
||||
</div>
|
||||
{option.description && (
|
||||
<div class={descriptionClass(variant())}>{option.description}</div>
|
||||
<div class={getSelectionCardDescriptionClass(selectionCardGroup.variant())}>
|
||||
{option.description}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -60,6 +60,7 @@ import infrastructureSummaryTableStateSource from '@/components/shared/useInfras
|
||||
import monitoredSystemLimitWarningBannerSource from '@/components/shared/MonitoredSystemLimitWarningBanner.tsx?raw';
|
||||
import monitoredSystemLimitWarningBannerModelSource from '@/components/shared/monitoredSystemLimitWarningBannerModel.ts?raw';
|
||||
import selectionCardGroupSource from '@/components/shared/SelectionCardGroup.tsx?raw';
|
||||
import selectionCardGroupModelSource from '@/components/shared/selectionCardGroupModel.ts?raw';
|
||||
import tagBadgesSource from '@/components/shared/TagBadges.tsx?raw';
|
||||
import commandPaletteStateSource from '@/components/shared/useCommandPaletteState.ts?raw';
|
||||
import activeUseTrialNudgeStateSource from '@/components/shared/useActiveUseTrialNudgeState.ts?raw';
|
||||
@@ -87,6 +88,7 @@ import tooltipStateSource from '@/components/shared/useTooltipState.ts?raw';
|
||||
import trialBannerStateSource from '@/components/shared/useTrialBannerState.ts?raw';
|
||||
import interactiveSparklineStateSource from '@/components/shared/useInteractiveSparklineState.ts?raw';
|
||||
import monitoredSystemLimitWarningBannerStateSource from '@/components/shared/useMonitoredSystemLimitWarningBannerState.ts?raw';
|
||||
import selectionCardGroupStateSource from '@/components/shared/useSelectionCardGroupState.ts?raw';
|
||||
import webInterfaceUrlFieldSource from '@/components/shared/WebInterfaceUrlField.tsx?raw';
|
||||
import webInterfaceUrlFieldModelSource from '@/components/shared/webInterfaceUrlFieldModel.ts?raw';
|
||||
import webInterfaceUrlFieldStateSource from '@/components/shared/useWebInterfaceUrlFieldState.ts?raw';
|
||||
@@ -152,9 +154,20 @@ describe('shared primitive guardrails', () => {
|
||||
});
|
||||
|
||||
it('routes selectable settings cards through SelectionCardGroup', () => {
|
||||
expect(selectionCardGroupSource).toContain(
|
||||
"type SelectionCardGroupVariant = 'compact' | 'detail'",
|
||||
);
|
||||
expect(selectionCardGroupSource).toContain('useSelectionCardGroupState');
|
||||
expect(selectionCardGroupSource).toContain('getSelectionCardGroupClass');
|
||||
expect(selectionCardGroupSource).toContain('getSelectionCardButtonClass');
|
||||
expect(selectionCardGroupSource).toContain('getSelectionCardTitleClass');
|
||||
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.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(aiSettingsDialogsSource).toContain('SelectionCardGroup');
|
||||
expect(aiSettingsDialogsSource).toContain('variant="compact"');
|
||||
expect(aiSettingsDialogsSource).not.toContain(
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
import type { JSX } from 'solid-js';
|
||||
|
||||
export type SelectionCardTone = 'accent' | 'success';
|
||||
export type SelectionCardGroupVariant = 'compact' | 'detail';
|
||||
|
||||
export interface SelectionCardOption<T extends string | number> {
|
||||
value: T;
|
||||
title: string;
|
||||
description?: string;
|
||||
icon?: (props: { active: boolean }) => JSX.Element;
|
||||
tone?: SelectionCardTone;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export interface SelectionCardGroupProps<T extends string | number> {
|
||||
options: SelectionCardOption<T>[];
|
||||
value: T;
|
||||
onChange: (value: T) => void;
|
||||
class?: string;
|
||||
variant?: SelectionCardGroupVariant;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
const groupClassByVariant: Record<SelectionCardGroupVariant, string> = {
|
||||
compact: 'grid grid-cols-2 gap-2',
|
||||
detail: 'grid grid-cols-1 gap-3',
|
||||
};
|
||||
|
||||
export function resolveSelectionCardGroupVariant(
|
||||
variant: SelectionCardGroupVariant | undefined,
|
||||
): SelectionCardGroupVariant {
|
||||
return variant ?? 'detail';
|
||||
}
|
||||
|
||||
export function resolveSelectionCardTone(tone: SelectionCardTone | undefined): SelectionCardTone {
|
||||
return tone ?? 'accent';
|
||||
}
|
||||
|
||||
export function getSelectionCardGroupClass(
|
||||
variant: SelectionCardGroupVariant,
|
||||
className?: string,
|
||||
): string {
|
||||
return `${groupClassByVariant[variant]} ${className ?? ''}`.trim();
|
||||
}
|
||||
|
||||
function getSelectionCardActiveClass(tone: SelectionCardTone): string {
|
||||
if (tone === 'success') {
|
||||
return 'border-green-500 bg-green-50 dark:bg-green-900';
|
||||
}
|
||||
return 'border-blue-500 bg-blue-50 dark:bg-blue-900';
|
||||
}
|
||||
|
||||
function getSelectionCardInactiveClass(variant: SelectionCardGroupVariant): string {
|
||||
if (variant === 'compact') {
|
||||
return 'border-border hover:border-blue-300';
|
||||
}
|
||||
return 'border-border hover:border-border';
|
||||
}
|
||||
|
||||
export function getSelectionCardButtonClass(
|
||||
variant: SelectionCardGroupVariant,
|
||||
tone: SelectionCardTone,
|
||||
active: boolean,
|
||||
disabled: boolean,
|
||||
): string {
|
||||
const base =
|
||||
variant === 'detail'
|
||||
? 'p-4 rounded-md border-2 transition-all text-left'
|
||||
: 'p-3 rounded-md border-2 transition-all text-center';
|
||||
|
||||
return [
|
||||
base,
|
||||
active ? getSelectionCardActiveClass(tone) : getSelectionCardInactiveClass(variant),
|
||||
disabled ? 'disabled:opacity-50 disabled:cursor-not-allowed' : '',
|
||||
].join(' ');
|
||||
}
|
||||
|
||||
export function getSelectionCardIconContainerClass(
|
||||
tone: SelectionCardTone,
|
||||
active: boolean,
|
||||
): string {
|
||||
const activeClass =
|
||||
tone === 'success' ? 'bg-green-100 dark:bg-green-800' : 'bg-blue-100 dark:bg-blue-800';
|
||||
return ['p-2 rounded-md', active ? activeClass : 'bg-surface-alt'].join(' ');
|
||||
}
|
||||
|
||||
export function getSelectionCardTitleClass(
|
||||
variant: SelectionCardGroupVariant,
|
||||
tone: SelectionCardTone,
|
||||
active: boolean,
|
||||
): string {
|
||||
if (variant === 'compact') {
|
||||
return 'text-sm font-medium text-base-content';
|
||||
}
|
||||
if (!active) {
|
||||
return 'text-sm font-semibold text-base-content';
|
||||
}
|
||||
return tone === 'success'
|
||||
? 'text-sm font-semibold text-green-900 dark:text-green-100'
|
||||
: 'text-sm font-semibold text-blue-900 dark:text-blue-100';
|
||||
}
|
||||
|
||||
export function getSelectionCardDescriptionClass(variant: SelectionCardGroupVariant): string {
|
||||
return variant === 'compact' ? 'text-xs text-slate-500 mt-0.5' : 'text-xs text-muted';
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import { createMemo } from 'solid-js';
|
||||
import {
|
||||
resolveSelectionCardGroupVariant,
|
||||
resolveSelectionCardTone,
|
||||
type SelectionCardGroupProps,
|
||||
type SelectionCardOption,
|
||||
} from './selectionCardGroupModel';
|
||||
|
||||
export function useSelectionCardGroupState<T extends string | number>(
|
||||
props: SelectionCardGroupProps<T>,
|
||||
) {
|
||||
const variant = createMemo(() => resolveSelectionCardGroupVariant(props.variant));
|
||||
|
||||
const isOptionActive = (option: SelectionCardOption<T>) => option.value === props.value;
|
||||
const isOptionDisabled = (option: SelectionCardOption<T>) => Boolean(props.disabled || option.disabled);
|
||||
const getOptionTone = (option: SelectionCardOption<T>) => resolveSelectionCardTone(option.tone);
|
||||
|
||||
const handleOptionClick = (option: SelectionCardOption<T>) => {
|
||||
if (isOptionDisabled(option)) {
|
||||
return;
|
||||
}
|
||||
|
||||
props.onChange(option.value);
|
||||
};
|
||||
|
||||
return {
|
||||
getOptionTone,
|
||||
handleOptionClick,
|
||||
isOptionActive,
|
||||
isOptionDisabled,
|
||||
variant,
|
||||
};
|
||||
}
|
||||
@@ -63,6 +63,8 @@ import infrastructureSummaryTableRowSource from '@/components/shared/Infrastruct
|
||||
import interactiveSparklineSource from '@/components/shared/InteractiveSparkline.tsx?raw';
|
||||
import interactiveSparklineModelSource from '@/components/shared/interactiveSparklineModel.ts?raw';
|
||||
import infrastructureSelectorModelSource from '@/components/shared/infrastructureSelectorModel.ts?raw';
|
||||
import selectionCardGroupSource from '@/components/shared/SelectionCardGroup.tsx?raw';
|
||||
import selectionCardGroupModelSource from '@/components/shared/selectionCardGroupModel.ts?raw';
|
||||
import sharedInfrastructureSummaryTableModelSource from '@/components/shared/infrastructureSummaryTableModel.ts?raw';
|
||||
import commandPaletteStateSource from '@/components/shared/useCommandPaletteState.ts?raw';
|
||||
import activeUseTrialNudgeStateSource from '@/components/shared/useActiveUseTrialNudgeState.ts?raw';
|
||||
@@ -89,6 +91,7 @@ import trialBannerStateSource from '@/components/shared/useTrialBannerState.ts?r
|
||||
import interactiveSparklineStateSource from '@/components/shared/useInteractiveSparklineState.ts?raw';
|
||||
import monitoredSystemLimitWarningBannerStateSource from '@/components/shared/useMonitoredSystemLimitWarningBannerState.ts?raw';
|
||||
import infrastructureSummaryTableStateSource from '@/components/shared/useInfrastructureSummaryTableState.ts?raw';
|
||||
import selectionCardGroupStateSource from '@/components/shared/useSelectionCardGroupState.ts?raw';
|
||||
import resourceBadgePresentationSource from '@/utils/resourceBadgePresentation.ts?raw';
|
||||
import workloadTypeBadgesSource from '@/components/shared/workloadTypeBadges.ts?raw';
|
||||
import tagBadgesSource from '@/components/shared/TagBadges.tsx?raw';
|
||||
@@ -2865,6 +2868,20 @@ describe('frontend resource type boundaries', () => {
|
||||
expect(statusBadgeModelSource).toContain('getStatusBadgeLabel');
|
||||
expect(statusBadgeModelSource).toContain('getStatusBadgeTitle');
|
||||
expect(statusBadgeModelSource).toContain("labelEnabled ?? 'Enabled'");
|
||||
expect(selectionCardGroupSource).toContain('useSelectionCardGroupState');
|
||||
expect(selectionCardGroupSource).toContain('getSelectionCardGroupClass');
|
||||
expect(selectionCardGroupSource).toContain('getSelectionCardButtonClass');
|
||||
expect(selectionCardGroupSource).toContain('getSelectionCardTitleClass');
|
||||
expect(selectionCardGroupSource).not.toContain('resolveSelectionCardTone');
|
||||
expect(selectionCardGroupSource).not.toContain('props.onChange(option.value)');
|
||||
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(monitoredSystemLimitWarningBannerSource).toContain(
|
||||
'useMonitoredSystemLimitWarningBannerState',
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user