@arbor-education/design-system.components 0.9.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/agent-memory/blanche-designspert/MEMORY.md +64 -0
- package/.claude/agent-memory/blanche-designspert/token-review-patterns.md +29 -0
- package/.claude/agent-memory/dorothy-fact-checker/MEMORY.md +129 -0
- package/.claude/agent-memory/rose-storybookspert/MEMORY.md +29 -0
- package/.claude/agent-memory/rose-storybookspert/patterns.md +132 -0
- package/.claude/agent-memory/sophia-componentspert/MEMORY.md +14 -0
- package/.claude/agent-memory/sophia-componentspert/components.md +367 -0
- package/.claude/agents/blanche-designspert.md +150 -0
- package/.claude/agents/dorothy-fact-checker.md +145 -0
- package/.claude/agents/rose-storybookspert.md +148 -0
- package/.claude/agents/sophia-componentspert.md +133 -0
- package/.claude/component-library.md +1107 -0
- package/.claude/design-assessment-daily-attendance-2026-04-10.md +566 -0
- package/.claude/figma-assessment-7154-58899.md +404 -0
- package/.claude/figma-assessment-UKQfcxnT4rlHHNuiumt4o1-11086-97537.md +392 -0
- package/.claude/figma-assessment-UKQfcxnT4rlHHNuiumt4o1-551-41974.md +474 -0
- package/.claude/figma-assessment-UKQfcxnT4rlHHNuiumt4o1-551-43094.md +462 -0
- package/.claude/figma-assessment-fcFK4CGzkz2fVyY3koX8ZE-7154-59061.md +440 -0
- package/.claude/migration-report-custom-report-writer-2026-02-19.md +591 -0
- package/.claude/skills/analyze-design/README.md +295 -0
- package/.claude/skills/analyze-design/SKILL.md +741 -0
- package/.claude/skills/create-page/README.md +246 -0
- package/.claude/skills/create-page/SKILL.md +634 -0
- package/.claude/skills/create-page/design-analysis-template.md +333 -0
- package/.claude/skills/create-page/page-template.scss +118 -0
- package/.claude/skills/create-page/page-template.tsx +230 -0
- package/.claude/skills/map-legacy/README.md +87 -0
- package/.claude/skills/map-legacy/SKILL.md +465 -0
- package/.claude/skills/migrate-page/README.md +125 -0
- package/.claude/skills/migrate-page/SKILL.md +374 -0
- package/.github/CODEOWNERS +1 -0
- package/.github/pull_request_template.md +39 -0
- package/.github/workflows/release.yml +1 -1
- package/CHANGELOG.md +16 -0
- package/CLAUDE.md +31 -0
- package/CONTRIBUTING.md +191 -0
- package/README.md +110 -20
- package/dist/components/button/Button.d.ts.map +1 -1
- package/dist/components/button/Button.js +2 -2
- package/dist/components/button/Button.js.map +1 -1
- package/dist/components/combobox/Combobox.d.ts.map +1 -1
- package/dist/components/combobox/Combobox.js +2 -1
- package/dist/components/combobox/Combobox.js.map +1 -1
- package/dist/components/combobox/Combobox.test.js +98 -61
- package/dist/components/combobox/Combobox.test.js.map +1 -1
- package/dist/components/combobox/useComboboxPopoverBehavior.d.ts +3 -1
- package/dist/components/combobox/useComboboxPopoverBehavior.d.ts.map +1 -1
- package/dist/components/combobox/useComboboxPopoverBehavior.js +7 -6
- package/dist/components/combobox/useComboboxPopoverBehavior.js.map +1 -1
- package/dist/components/combobox/useComboboxState.d.ts.map +1 -1
- package/dist/components/combobox/useComboboxState.js +4 -1
- package/dist/components/combobox/useComboboxState.js.map +1 -1
- package/dist/components/datePicker/DatePicker.d.ts +4 -1
- package/dist/components/datePicker/DatePicker.d.ts.map +1 -1
- package/dist/components/datePicker/DatePicker.js +77 -37
- package/dist/components/datePicker/DatePicker.js.map +1 -1
- package/dist/components/datePicker/DatePicker.stories.d.ts +28 -3
- package/dist/components/datePicker/DatePicker.stories.d.ts.map +1 -1
- package/dist/components/datePicker/DatePicker.stories.js +62 -9
- package/dist/components/datePicker/DatePicker.stories.js.map +1 -1
- package/dist/components/datePicker/DatePicker.test.js +133 -66
- package/dist/components/datePicker/DatePicker.test.js.map +1 -1
- package/dist/components/datePicker/DatePickerCalendarHeader.d.ts +8 -0
- package/dist/components/datePicker/DatePickerCalendarHeader.d.ts.map +1 -0
- package/dist/components/datePicker/DatePickerCalendarHeader.js +36 -0
- package/dist/components/datePicker/DatePickerCalendarHeader.js.map +1 -0
- package/dist/components/datePicker/dateInputUtils.d.ts +25 -0
- package/dist/components/datePicker/dateInputUtils.d.ts.map +1 -0
- package/dist/components/datePicker/dateInputUtils.js +60 -0
- package/dist/components/datePicker/dateInputUtils.js.map +1 -0
- package/dist/components/datePicker/datePickerTestUtils.test-helpers.d.ts +2 -0
- package/dist/components/datePicker/datePickerTestUtils.test-helpers.d.ts.map +1 -0
- package/dist/components/datePicker/datePickerTestUtils.test-helpers.js +4 -0
- package/dist/components/datePicker/datePickerTestUtils.test-helpers.js.map +1 -0
- package/dist/components/dateTimePicker/DateTimePicker.d.ts +22 -0
- package/dist/components/dateTimePicker/DateTimePicker.d.ts.map +1 -0
- package/dist/components/dateTimePicker/DateTimePicker.js +132 -0
- package/dist/components/dateTimePicker/DateTimePicker.js.map +1 -0
- package/dist/components/dateTimePicker/DateTimePicker.stories.d.ts +77 -0
- package/dist/components/dateTimePicker/DateTimePicker.stories.d.ts.map +1 -0
- package/dist/components/dateTimePicker/DateTimePicker.stories.js +163 -0
- package/dist/components/dateTimePicker/DateTimePicker.stories.js.map +1 -0
- package/dist/components/dateTimePicker/DateTimePicker.test.d.ts +2 -0
- package/dist/components/dateTimePicker/DateTimePicker.test.d.ts.map +1 -0
- package/dist/components/dateTimePicker/DateTimePicker.test.js +235 -0
- package/dist/components/dateTimePicker/DateTimePicker.test.js.map +1 -0
- package/dist/components/formField/FormField.test.d.ts.map +1 -1
- package/dist/components/formField/FormField.test.js +5 -5
- package/dist/components/formField/FormField.test.js.map +1 -1
- package/dist/components/formField/inputs/selectDropdown/SelectDropdown.d.ts +1 -0
- package/dist/components/formField/inputs/selectDropdown/SelectDropdown.d.ts.map +1 -1
- package/dist/components/formField/inputs/selectDropdown/SelectDropdown.js +7 -3
- package/dist/components/formField/inputs/selectDropdown/SelectDropdown.js.map +1 -1
- package/dist/components/formField/inputs/selectDropdown/SelectDropdown.test.js +12 -0
- package/dist/components/formField/inputs/selectDropdown/SelectDropdown.test.js.map +1 -1
- package/dist/components/formField/inputs/text/TextInput.d.ts +4 -1
- package/dist/components/formField/inputs/text/TextInput.d.ts.map +1 -1
- package/dist/components/formField/inputs/text/TextInput.js +5 -4
- package/dist/components/formField/inputs/text/TextInput.js.map +1 -1
- package/dist/components/formField/inputs/text/TextInput.stories.d.ts +4 -1
- package/dist/components/formField/inputs/text/TextInput.stories.d.ts.map +1 -1
- package/dist/components/table/DSDefaultColDef.js +2 -2
- package/dist/components/table/DSDefaultColDef.js.map +1 -1
- package/dist/components/table/Table.d.ts.map +1 -1
- package/dist/components/table/Table.js +4 -0
- package/dist/components/table/Table.js.map +1 -1
- package/dist/components/table/Table.stories.d.ts +2 -0
- package/dist/components/table/Table.stories.d.ts.map +1 -1
- package/dist/components/table/Table.stories.js +132 -3
- package/dist/components/table/Table.stories.js.map +1 -1
- package/dist/components/table/Table.test.js +106 -5
- package/dist/components/table/Table.test.js.map +1 -1
- package/dist/components/table/cellRenderers/BooleanCellRenderer.d.ts +3 -0
- package/dist/components/table/cellRenderers/BooleanCellRenderer.d.ts.map +1 -0
- package/dist/components/table/cellRenderers/BooleanCellRenderer.js +15 -0
- package/dist/components/table/cellRenderers/BooleanCellRenderer.js.map +1 -0
- package/dist/components/table/cellRenderers/BooleanCellRenderer.test.d.ts +2 -0
- package/dist/components/table/cellRenderers/BooleanCellRenderer.test.d.ts.map +1 -0
- package/dist/components/table/cellRenderers/BooleanCellRenderer.test.js +31 -0
- package/dist/components/table/cellRenderers/BooleanCellRenderer.test.js.map +1 -0
- package/dist/components/table/cellRenderers/CheckboxCellRenderer.d.ts +3 -0
- package/dist/components/table/cellRenderers/CheckboxCellRenderer.d.ts.map +1 -0
- package/dist/components/table/cellRenderers/CheckboxCellRenderer.js +12 -0
- package/dist/components/table/cellRenderers/CheckboxCellRenderer.js.map +1 -0
- package/dist/components/table/cellRenderers/CheckboxCellRenderer.test.d.ts +2 -0
- package/dist/components/table/cellRenderers/CheckboxCellRenderer.test.d.ts.map +1 -0
- package/dist/components/table/cellRenderers/CheckboxCellRenderer.test.js +65 -0
- package/dist/components/table/cellRenderers/CheckboxCellRenderer.test.js.map +1 -0
- package/dist/index.css +259 -4
- package/dist/index.css.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/button/Button.tsx +2 -1
- package/src/components/combobox/Combobox.test.tsx +104 -61
- package/src/components/combobox/Combobox.tsx +3 -1
- package/src/components/combobox/useComboboxPopoverBehavior.ts +10 -5
- package/src/components/combobox/useComboboxState.ts +4 -1
- package/src/components/datePicker/DatePicker.stories.tsx +67 -9
- package/src/components/datePicker/DatePicker.test.tsx +157 -72
- package/src/components/datePicker/DatePicker.tsx +163 -69
- package/src/components/datePicker/DatePickerCalendarHeader.tsx +82 -0
- package/src/components/datePicker/date-field-hint.scss +152 -0
- package/src/components/datePicker/dateInputUtils.ts +117 -0
- package/src/components/datePicker/datePicker.scss +53 -29
- package/src/components/datePicker/datePickerTestUtils.test-helpers.ts +6 -0
- package/src/components/dateTimePicker/DateTimePicker.stories.tsx +202 -0
- package/src/components/dateTimePicker/DateTimePicker.test.tsx +295 -0
- package/src/components/dateTimePicker/DateTimePicker.tsx +293 -0
- package/src/components/dateTimePicker/dateTimePicker.scss +17 -0
- package/src/components/formField/FormField.test.tsx +5 -5
- package/src/components/formField/inputs/selectDropdown/SelectDropdown.test.tsx +28 -0
- package/src/components/formField/inputs/selectDropdown/SelectDropdown.tsx +8 -2
- package/src/components/formField/inputs/text/TextInput.tsx +6 -3
- package/src/components/table/DSDefaultColDef.ts +2 -2
- package/src/components/table/Table.stories.tsx +147 -3
- package/src/components/table/Table.test.tsx +131 -5
- package/src/components/table/Table.tsx +4 -0
- package/src/components/table/cellRenderers/BooleanCellRenderer.test.tsx +37 -0
- package/src/components/table/cellRenderers/BooleanCellRenderer.tsx +34 -0
- package/src/components/table/cellRenderers/CheckboxCellRenderer.test.tsx +74 -0
- package/src/components/table/cellRenderers/CheckboxCellRenderer.tsx +28 -0
- package/src/components/table/cellRenderers/booleanCellRenderer.scss +7 -0
- package/src/components/table/table.scss +1 -1
- package/src/index.scss +2 -0
- package/src/index.ts +4 -0
|
@@ -16,6 +16,7 @@ export type SelectDropdownInputProps = {
|
|
|
16
16
|
'id'?: string;
|
|
17
17
|
'alwaysShowPlaceholder'?: boolean;
|
|
18
18
|
'initialSelectedValues'?: string[];
|
|
19
|
+
'selectedValues'?: string[];
|
|
19
20
|
'open'?: boolean;
|
|
20
21
|
'onOpenChange'?: (open: boolean) => void;
|
|
21
22
|
};
|
|
@@ -33,11 +34,14 @@ export const SelectDropdown = (props: SelectDropdownInputProps) => {
|
|
|
33
34
|
'aria-invalid': ariaInvalid,
|
|
34
35
|
alwaysShowPlaceholder = false,
|
|
35
36
|
initialSelectedValues = [],
|
|
37
|
+
selectedValues: controlledSelectedValues,
|
|
36
38
|
open,
|
|
37
39
|
onOpenChange,
|
|
38
40
|
} = props;
|
|
39
41
|
|
|
40
|
-
const
|
|
42
|
+
const isControlled = controlledSelectedValues !== undefined;
|
|
43
|
+
const [internalSelectedValues, setInternalSelectedValues] = useState<string[]>(initialSelectedValues);
|
|
44
|
+
const selectedValues = isControlled ? controlledSelectedValues : internalSelectedValues;
|
|
41
45
|
const [renderedSelectContent, setRenderedSelectContent] = useState('');
|
|
42
46
|
const selectedValuesRef = useRef(selectedValues);
|
|
43
47
|
selectedValuesRef.current = selectedValues;
|
|
@@ -90,7 +94,9 @@ export const SelectDropdown = (props: SelectDropdownInputProps) => {
|
|
|
90
94
|
else {
|
|
91
95
|
nextValues = [...prev, value];
|
|
92
96
|
}
|
|
93
|
-
|
|
97
|
+
if (!isControlled) {
|
|
98
|
+
setInternalSelectedValues(nextValues);
|
|
99
|
+
}
|
|
94
100
|
onSelectionChange?.(nextValues);
|
|
95
101
|
};
|
|
96
102
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import classNames from 'classnames';
|
|
2
|
-
import { type InputHTMLAttributes } from 'react';
|
|
2
|
+
import { forwardRef, type InputHTMLAttributes } from 'react';
|
|
3
3
|
|
|
4
4
|
export type TextInputProps = {
|
|
5
5
|
size?: 'M' | 'S';
|
|
6
6
|
hasError?: boolean;
|
|
7
7
|
} & Omit<InputHTMLAttributes<HTMLInputElement>, 'size'>;
|
|
8
8
|
|
|
9
|
-
export const TextInput = (props
|
|
9
|
+
export const TextInput = forwardRef<HTMLInputElement, TextInputProps>((props, ref) => {
|
|
10
10
|
const {
|
|
11
11
|
size = 'M',
|
|
12
12
|
hasError,
|
|
@@ -27,9 +27,12 @@ export const TextInput = (props: TextInputProps) => {
|
|
|
27
27
|
|
|
28
28
|
return (
|
|
29
29
|
<input
|
|
30
|
+
ref={ref}
|
|
30
31
|
className={inputClasses}
|
|
31
32
|
disabled={disabled}
|
|
32
33
|
{...rest}
|
|
33
34
|
/>
|
|
34
35
|
);
|
|
35
|
-
};
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
TextInput.displayName = 'TextInput';
|
|
@@ -19,7 +19,7 @@ export const shouldSuppressFocus = (params: SuppressMouseEventHandlingParams | C
|
|
|
19
19
|
if (typeof params.column !== 'object') {
|
|
20
20
|
return false;
|
|
21
21
|
}
|
|
22
|
-
return params.column?.getColDef().cellRendererParams.
|
|
22
|
+
return params.column?.getColDef().cellRendererParams.suppressCellFocusAndFocusFirstElement ?? false;
|
|
23
23
|
};
|
|
24
24
|
|
|
25
25
|
export const DSDefaultColDef: ColDef = {
|
|
@@ -54,7 +54,7 @@ export const DSDefaultColDef: ColDef = {
|
|
|
54
54
|
useFormatter: true,
|
|
55
55
|
},
|
|
56
56
|
cellClassRules: {
|
|
57
|
-
'ds-table__cell--
|
|
57
|
+
'ds-table__cell--suppress-focus': shouldSuppressFocus,
|
|
58
58
|
},
|
|
59
59
|
cellRendererParams: {
|
|
60
60
|
suppressMouseEventHandling: (params: SuppressMouseEventHandlingParams) => {
|
|
@@ -490,7 +490,7 @@ const sampleColumnDefsWithButtonCellRenderer: (ColDef | ColGroupDef)[] = [
|
|
|
490
490
|
headerTooltip: 'The name of the user',
|
|
491
491
|
cellRenderer: 'dsButtonCellRenderer',
|
|
492
492
|
cellRendererParams: {
|
|
493
|
-
|
|
493
|
+
suppressCellFocusAndFocusFirstElement: true,
|
|
494
494
|
},
|
|
495
495
|
editable: false,
|
|
496
496
|
},
|
|
@@ -818,7 +818,7 @@ export const WithInlineTextCellRenderer: Story = {
|
|
|
818
818
|
field: 'name',
|
|
819
819
|
cellRenderer: 'dsInlineTextCellRenderer',
|
|
820
820
|
cellRendererParams: {
|
|
821
|
-
|
|
821
|
+
suppressCellFocusAndFocusFirstElement: true,
|
|
822
822
|
suppressCount: true,
|
|
823
823
|
suppressDoubleClickExpand: true,
|
|
824
824
|
suppressEnterExpand: true,
|
|
@@ -1171,7 +1171,7 @@ export const TidyTable: Story = {
|
|
|
1171
1171
|
editable: false,
|
|
1172
1172
|
cellRenderer: 'agGroupCellRenderer',
|
|
1173
1173
|
cellRendererParams: {
|
|
1174
|
-
|
|
1174
|
+
suppressCellFocusAndFocusFirstElement: true,
|
|
1175
1175
|
suppressCount: true,
|
|
1176
1176
|
suppressDoubleClickExpand: true,
|
|
1177
1177
|
suppressEnterExpand: true,
|
|
@@ -1361,4 +1361,148 @@ export const TableWithSemanticColors: Story = {
|
|
|
1361
1361
|
},
|
|
1362
1362
|
};
|
|
1363
1363
|
|
|
1364
|
+
interface BooleanRowData {
|
|
1365
|
+
id: number;
|
|
1366
|
+
name: string;
|
|
1367
|
+
hasPet: boolean | null | undefined;
|
|
1368
|
+
isEnrolled: boolean | null | undefined;
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
const booleanCellRendererData: BooleanRowData[] = [
|
|
1372
|
+
{ id: 1, name: 'Alice Johnson', hasPet: true, isEnrolled: true },
|
|
1373
|
+
{ id: 2, name: 'Bob Smith', hasPet: false, isEnrolled: false },
|
|
1374
|
+
{ id: 3, name: 'Charlie Brown', hasPet: null, isEnrolled: undefined },
|
|
1375
|
+
{ id: 4, name: 'Diana Prince', hasPet: true, isEnrolled: null },
|
|
1376
|
+
{ id: 5, name: 'Ethan Hunt', hasPet: undefined, isEnrolled: true },
|
|
1377
|
+
];
|
|
1378
|
+
|
|
1379
|
+
const booleanCellRendererColumnDefs: ColDef<BooleanRowData>[] = [
|
|
1380
|
+
{ field: 'name', headerName: 'Name' },
|
|
1381
|
+
{
|
|
1382
|
+
field: 'hasPet',
|
|
1383
|
+
headerName: 'Has Pet',
|
|
1384
|
+
cellRenderer: 'dsBooleanCellRenderer',
|
|
1385
|
+
editable: false,
|
|
1386
|
+
},
|
|
1387
|
+
{
|
|
1388
|
+
field: 'isEnrolled',
|
|
1389
|
+
headerName: 'Is Enrolled',
|
|
1390
|
+
cellRenderer: 'dsBooleanCellRenderer',
|
|
1391
|
+
editable: false,
|
|
1392
|
+
},
|
|
1393
|
+
];
|
|
1394
|
+
|
|
1395
|
+
export const WithBooleanCellRenderer: Story = {
|
|
1396
|
+
parameters: {
|
|
1397
|
+
docs: {
|
|
1398
|
+
description: {
|
|
1399
|
+
story:
|
|
1400
|
+
'The BooleanCellRenderer displays a true or false icon for boolean values, and renders nothing for any other value regardless of truthiness. Only explicit true and false are accepted, anything else is treated as nullish',
|
|
1401
|
+
},
|
|
1402
|
+
},
|
|
1403
|
+
},
|
|
1404
|
+
args: {
|
|
1405
|
+
rowData: booleanCellRendererData,
|
|
1406
|
+
columnDefs: booleanCellRendererColumnDefs,
|
|
1407
|
+
defaultColDef,
|
|
1408
|
+
domLayout: 'autoHeight',
|
|
1409
|
+
},
|
|
1410
|
+
};
|
|
1411
|
+
|
|
1412
|
+
const checkboxRowData = [
|
|
1413
|
+
{
|
|
1414
|
+
id: 1,
|
|
1415
|
+
name: { value: 'Alice Johnson' },
|
|
1416
|
+
email: { value: 'alice.johnson@example.com' },
|
|
1417
|
+
role: { value: 'Developer' },
|
|
1418
|
+
attended: true,
|
|
1419
|
+
consented: false,
|
|
1420
|
+
},
|
|
1421
|
+
{
|
|
1422
|
+
id: 2,
|
|
1423
|
+
name: { value: 'Bob Smith' },
|
|
1424
|
+
email: { value: 'bob.smith@example.com' },
|
|
1425
|
+
role: { value: 'Designer' },
|
|
1426
|
+
attended: false,
|
|
1427
|
+
consented: true,
|
|
1428
|
+
},
|
|
1429
|
+
{
|
|
1430
|
+
id: 3,
|
|
1431
|
+
name: { value: 'Charlie Brown' },
|
|
1432
|
+
email: { value: 'charlie.brown@example.com' },
|
|
1433
|
+
role: { value: 'Manager' },
|
|
1434
|
+
attended: true,
|
|
1435
|
+
consented: true,
|
|
1436
|
+
},
|
|
1437
|
+
{
|
|
1438
|
+
id: 4,
|
|
1439
|
+
name: { value: 'Diana Prince' },
|
|
1440
|
+
email: { value: 'diana.prince@example.com' },
|
|
1441
|
+
role: { value: 'Developer' },
|
|
1442
|
+
attended: false,
|
|
1443
|
+
consented: false,
|
|
1444
|
+
},
|
|
1445
|
+
{
|
|
1446
|
+
id: 5,
|
|
1447
|
+
name: { value: 'Ethan Hunt' },
|
|
1448
|
+
email: { value: 'ethan.hunt@example.com' },
|
|
1449
|
+
role: { value: 'Analyst' },
|
|
1450
|
+
attended: true,
|
|
1451
|
+
consented: false,
|
|
1452
|
+
},
|
|
1453
|
+
];
|
|
1454
|
+
|
|
1455
|
+
const checkboxColumnDefs: ColDef[] = [
|
|
1456
|
+
{
|
|
1457
|
+
field: 'name',
|
|
1458
|
+
headerName: 'Name',
|
|
1459
|
+
valueFormatter: Table.DefaultValueFormatter,
|
|
1460
|
+
},
|
|
1461
|
+
{
|
|
1462
|
+
field: 'email',
|
|
1463
|
+
headerName: 'Email',
|
|
1464
|
+
valueFormatter: Table.DefaultValueFormatter,
|
|
1465
|
+
},
|
|
1466
|
+
{
|
|
1467
|
+
field: 'role',
|
|
1468
|
+
headerName: 'Role',
|
|
1469
|
+
valueFormatter: Table.DefaultValueFormatter,
|
|
1470
|
+
},
|
|
1471
|
+
{
|
|
1472
|
+
field: 'attended',
|
|
1473
|
+
headerName: 'Attended',
|
|
1474
|
+
cellRenderer: 'dsCheckboxCellRenderer',
|
|
1475
|
+
cellRendererParams: {
|
|
1476
|
+
suppressCellFocusAndFocusFirstElement: true,
|
|
1477
|
+
},
|
|
1478
|
+
editable: false,
|
|
1479
|
+
},
|
|
1480
|
+
{
|
|
1481
|
+
field: 'consented',
|
|
1482
|
+
headerName: 'Consented',
|
|
1483
|
+
cellRenderer: 'dsCheckboxCellRenderer',
|
|
1484
|
+
cellRendererParams: {
|
|
1485
|
+
suppressCellFocusAndFocusFirstElement: true,
|
|
1486
|
+
},
|
|
1487
|
+
editable: false,
|
|
1488
|
+
},
|
|
1489
|
+
];
|
|
1490
|
+
|
|
1491
|
+
export const WithCheckboxCellRenderer: Story = {
|
|
1492
|
+
parameters: {
|
|
1493
|
+
docs: {
|
|
1494
|
+
description: {
|
|
1495
|
+
story:
|
|
1496
|
+
'The CheckboxCellRenderer renders a checkbox input inside a table cell. Pass a boolean value to the cell data to control the checked state.',
|
|
1497
|
+
},
|
|
1498
|
+
},
|
|
1499
|
+
},
|
|
1500
|
+
args: {
|
|
1501
|
+
rowData: checkboxRowData,
|
|
1502
|
+
columnDefs: checkboxColumnDefs,
|
|
1503
|
+
defaultColDef,
|
|
1504
|
+
domLayout: 'autoHeight',
|
|
1505
|
+
},
|
|
1506
|
+
};
|
|
1507
|
+
|
|
1364
1508
|
export default meta;
|
|
@@ -1487,14 +1487,140 @@ describe('Table', () => {
|
|
|
1487
1487
|
});
|
|
1488
1488
|
});
|
|
1489
1489
|
|
|
1490
|
-
describe('
|
|
1490
|
+
describe('CheckboxCellRenderer', () => {
|
|
1491
|
+
test('renders checkboxes in table cells', async () => {
|
|
1492
|
+
const columnDefs = [
|
|
1493
|
+
{ field: 'name', headerName: 'Name' },
|
|
1494
|
+
{
|
|
1495
|
+
field: 'attended',
|
|
1496
|
+
headerName: 'Attended',
|
|
1497
|
+
cellRenderer: 'dsCheckboxCellRenderer',
|
|
1498
|
+
editable: false,
|
|
1499
|
+
},
|
|
1500
|
+
];
|
|
1501
|
+
const rowData = [
|
|
1502
|
+
{ name: 'Alice', attended: true },
|
|
1503
|
+
{ name: 'Bob', attended: false },
|
|
1504
|
+
];
|
|
1505
|
+
|
|
1506
|
+
render(<Table columnDefs={columnDefs} rowData={rowData} />);
|
|
1507
|
+
await waitFor(() => expect(screen.getByRole('grid')).toBeInTheDocument());
|
|
1508
|
+
await waitFor(() => {
|
|
1509
|
+
const checkboxes = screen.getAllByRole('checkbox');
|
|
1510
|
+
expect(checkboxes.length).toBe(2);
|
|
1511
|
+
});
|
|
1512
|
+
});
|
|
1513
|
+
|
|
1514
|
+
test('checked state reflects row data value', async () => {
|
|
1515
|
+
const columnDefs = [{
|
|
1516
|
+
field: 'active',
|
|
1517
|
+
headerName: 'Active',
|
|
1518
|
+
cellRenderer: 'dsCheckboxCellRenderer',
|
|
1519
|
+
editable: false,
|
|
1520
|
+
}];
|
|
1521
|
+
const rowData = [
|
|
1522
|
+
{ active: true },
|
|
1523
|
+
{ active: false },
|
|
1524
|
+
];
|
|
1525
|
+
|
|
1526
|
+
render(<Table columnDefs={columnDefs} rowData={rowData} />);
|
|
1527
|
+
await waitFor(() => expect(screen.getByRole('grid')).toBeInTheDocument());
|
|
1528
|
+
await waitFor(() => {
|
|
1529
|
+
const checkboxes = screen.getAllByRole('checkbox');
|
|
1530
|
+
expect(checkboxes[0]).toBeChecked();
|
|
1531
|
+
expect(checkboxes[1]).not.toBeChecked();
|
|
1532
|
+
});
|
|
1533
|
+
});
|
|
1534
|
+
|
|
1535
|
+
test('clicking checkbox triggers onCellValueChanged', async () => {
|
|
1536
|
+
const onCellValueChanged = vi.fn();
|
|
1537
|
+
const columnDefs = [{
|
|
1538
|
+
field: 'active',
|
|
1539
|
+
headerName: 'Active',
|
|
1540
|
+
cellRenderer: 'dsCheckboxCellRenderer',
|
|
1541
|
+
editable: false,
|
|
1542
|
+
}];
|
|
1543
|
+
const rowData = [{ active: false }];
|
|
1544
|
+
|
|
1545
|
+
render(
|
|
1546
|
+
<Table
|
|
1547
|
+
columnDefs={columnDefs}
|
|
1548
|
+
rowData={rowData}
|
|
1549
|
+
onCellValueChanged={onCellValueChanged}
|
|
1550
|
+
/>,
|
|
1551
|
+
);
|
|
1552
|
+
await waitFor(() => expect(screen.getByRole('grid')).toBeInTheDocument());
|
|
1553
|
+
await waitFor(() => expect(screen.getByRole('checkbox')).toBeInTheDocument());
|
|
1554
|
+
|
|
1555
|
+
await userEvent.click(screen.getByRole('checkbox'));
|
|
1556
|
+
|
|
1557
|
+
await waitFor(() => {
|
|
1558
|
+
expect(onCellValueChanged).toHaveBeenCalledWith(expect.objectContaining({ oldValue: false, newValue: true }));
|
|
1559
|
+
});
|
|
1560
|
+
});
|
|
1561
|
+
|
|
1562
|
+
test('works with multiple checkbox columns', async () => {
|
|
1563
|
+
const columnDefs = [
|
|
1564
|
+
{ field: 'name', headerName: 'Name' },
|
|
1565
|
+
{
|
|
1566
|
+
field: 'attended',
|
|
1567
|
+
headerName: 'Attended',
|
|
1568
|
+
cellRenderer: 'dsCheckboxCellRenderer',
|
|
1569
|
+
editable: false,
|
|
1570
|
+
},
|
|
1571
|
+
{
|
|
1572
|
+
field: 'consented',
|
|
1573
|
+
headerName: 'Consented',
|
|
1574
|
+
cellRenderer: 'dsCheckboxCellRenderer',
|
|
1575
|
+
editable: false,
|
|
1576
|
+
},
|
|
1577
|
+
];
|
|
1578
|
+
const rowData = [
|
|
1579
|
+
{ name: 'Alice', attended: true, consented: false },
|
|
1580
|
+
];
|
|
1581
|
+
|
|
1582
|
+
render(<Table columnDefs={columnDefs} rowData={rowData} />);
|
|
1583
|
+
await waitFor(() => expect(screen.getByRole('grid')).toBeInTheDocument());
|
|
1584
|
+
await waitFor(() => {
|
|
1585
|
+
const checkboxes = screen.getAllByRole('checkbox');
|
|
1586
|
+
expect(checkboxes[0]).toBeChecked();
|
|
1587
|
+
expect(checkboxes[1]).not.toBeChecked();
|
|
1588
|
+
});
|
|
1589
|
+
});
|
|
1590
|
+
|
|
1591
|
+
test('disabled flag via cellRendererParams', async () => {
|
|
1592
|
+
const onCellValueChanged = vi.fn();
|
|
1593
|
+
const columnDefs = [{
|
|
1594
|
+
field: 'active',
|
|
1595
|
+
headerName: 'Active',
|
|
1596
|
+
cellRenderer: 'dsCheckboxCellRenderer',
|
|
1597
|
+
editable: false,
|
|
1598
|
+
cellRendererParams: { disabled: true },
|
|
1599
|
+
}];
|
|
1600
|
+
const rowData = [{ active: false }];
|
|
1601
|
+
|
|
1602
|
+
render(
|
|
1603
|
+
<Table
|
|
1604
|
+
columnDefs={columnDefs}
|
|
1605
|
+
rowData={rowData}
|
|
1606
|
+
onCellValueChanged={onCellValueChanged}
|
|
1607
|
+
/>,
|
|
1608
|
+
);
|
|
1609
|
+
await waitFor(() => expect(screen.getByRole('grid')).toBeInTheDocument());
|
|
1610
|
+
await waitFor(() => expect(screen.getByRole('checkbox')).toBeInTheDocument());
|
|
1611
|
+
|
|
1612
|
+
expect(screen.getByRole('checkbox')).toBeDisabled();
|
|
1613
|
+
});
|
|
1614
|
+
});
|
|
1615
|
+
|
|
1616
|
+
describe('suppressCellFocusAndFocusFirstElement', () => {
|
|
1491
1617
|
const handleClick = vi.fn();
|
|
1492
1618
|
const columnDefs = [{
|
|
1493
1619
|
field: 'action',
|
|
1494
1620
|
headerName: 'Action',
|
|
1495
1621
|
cellRenderer: 'dsButtonCellRenderer',
|
|
1496
1622
|
cellRendererParams: {
|
|
1497
|
-
|
|
1623
|
+
suppressCellFocusAndFocusFirstElement: true,
|
|
1498
1624
|
},
|
|
1499
1625
|
}];
|
|
1500
1626
|
const rowData = [{
|
|
@@ -1504,14 +1630,14 @@ describe('Table', () => {
|
|
|
1504
1630
|
},
|
|
1505
1631
|
}];
|
|
1506
1632
|
|
|
1507
|
-
test('setting
|
|
1633
|
+
test('setting suppressCellFocusAndFocusFirstElement to true should add the ds-table__cell--suppress-focus class', async () => {
|
|
1508
1634
|
const { container } = render(<Table columnDefs={columnDefs} rowData={rowData} />);
|
|
1509
1635
|
await screen.findByRole('grid', {}, { timeout: 10000 });
|
|
1510
|
-
const cellWithClass = container.querySelector('.ds-table__cell--
|
|
1636
|
+
const cellWithClass = container.querySelector('.ds-table__cell--suppress-focus');
|
|
1511
1637
|
expect(cellWithClass).toBeInTheDocument();
|
|
1512
1638
|
});
|
|
1513
1639
|
|
|
1514
|
-
test('setting
|
|
1640
|
+
test('setting suppressCellFocusAndFocusFirstElement to true should suppress cell focus and focus the first element', async () => {
|
|
1515
1641
|
const focusFirstFocusableElementSpy = vi.spyOn(
|
|
1516
1642
|
focusFirstFocusableElementModule,
|
|
1517
1643
|
'focusFirstFocusableElement',
|
|
@@ -24,6 +24,8 @@ import { focusFirstFocusableElement } from 'Utils/focusFirstFocusableElement';
|
|
|
24
24
|
import { BooleanFilter } from './columnFilters/BooleanFilter/BooleanFilter';
|
|
25
25
|
import { TimeFilter } from './columnFilters/TimeFilter/TimeFilter';
|
|
26
26
|
import { TableSettingsDropdown } from './TableSettingsDropdown';
|
|
27
|
+
import { CheckboxCellRenderer } from './cellRenderers/CheckboxCellRenderer';
|
|
28
|
+
import { BooleanCellRenderer } from './cellRenderers/BooleanCellRenderer';
|
|
27
29
|
|
|
28
30
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
29
31
|
type TableProps<TData = any> = {
|
|
@@ -194,6 +196,8 @@ export const Table = (props: TableProps) => {
|
|
|
194
196
|
dsSelectDropdownCellRenderer: SelectDropdownCellRenderer,
|
|
195
197
|
dsBooleanFilter: BooleanFilter,
|
|
196
198
|
dsTimeFilter: TimeFilter,
|
|
199
|
+
dsCheckboxCellRenderer: CheckboxCellRenderer,
|
|
200
|
+
dsBooleanCellRenderer: BooleanCellRenderer,
|
|
197
201
|
...components,
|
|
198
202
|
}}
|
|
199
203
|
{...rest}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import '@testing-library/jest-dom/vitest';
|
|
2
|
+
import { describe, expect, test } from 'vitest';
|
|
3
|
+
import { render, screen } from '@testing-library/react';
|
|
4
|
+
import { BooleanCellRenderer } from './BooleanCellRenderer';
|
|
5
|
+
import type { CustomCellRendererProps } from 'ag-grid-react';
|
|
6
|
+
|
|
7
|
+
const renderWithValue = (value: unknown) =>
|
|
8
|
+
render(<BooleanCellRenderer {...{ value } as CustomCellRendererProps} />);
|
|
9
|
+
|
|
10
|
+
describe('BooleanCellRenderer', () => {
|
|
11
|
+
test('renders a check icon when value is true', () => {
|
|
12
|
+
renderWithValue(true);
|
|
13
|
+
expect(screen.getByRole('img', { hidden: true })).toBeInTheDocument();
|
|
14
|
+
expect(screen.getByText('true')).toBeInTheDocument();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test('renders an x icon when value is false', () => {
|
|
18
|
+
renderWithValue(false);
|
|
19
|
+
expect(screen.getByRole('img', { hidden: true })).toBeInTheDocument();
|
|
20
|
+
expect(screen.getByText('false')).toBeInTheDocument();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test('renders nothing when value is null', () => {
|
|
24
|
+
const { container } = renderWithValue(null);
|
|
25
|
+
expect(container.innerHTML).toBeFalsy();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test('renders nothing when value is undefined', () => {
|
|
29
|
+
const { container } = renderWithValue(undefined);
|
|
30
|
+
expect(container.innerHTML).toBeFalsy();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test('renders nothing when value is a non-boolean', () => {
|
|
34
|
+
const { container } = renderWithValue('some string');
|
|
35
|
+
expect(container.innerHTML).toBeFalsy();
|
|
36
|
+
});
|
|
37
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { CustomCellRendererProps } from 'ag-grid-react';
|
|
2
|
+
import { Icon } from 'Components/icon/Icon';
|
|
3
|
+
|
|
4
|
+
export const BooleanCellRenderer = (props: CustomCellRendererProps) => {
|
|
5
|
+
const { value } = props;
|
|
6
|
+
|
|
7
|
+
if (value === true) {
|
|
8
|
+
return (
|
|
9
|
+
<div className="ds-boolean-cell-renderer">
|
|
10
|
+
<Icon
|
|
11
|
+
size={24}
|
|
12
|
+
name="check-solid"
|
|
13
|
+
color="var(--color-semantic-success-700)"
|
|
14
|
+
screenReaderText="true"
|
|
15
|
+
/>
|
|
16
|
+
</div>
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
else if (value === false) {
|
|
20
|
+
return (
|
|
21
|
+
<div className="ds-boolean-cell-renderer">
|
|
22
|
+
<Icon
|
|
23
|
+
size={24}
|
|
24
|
+
name="x-solid"
|
|
25
|
+
color="var(--color-semantic-destructive-700)"
|
|
26
|
+
screenReaderText="false"
|
|
27
|
+
/>
|
|
28
|
+
</div>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { describe, expect, test, vi } from 'vitest';
|
|
2
|
+
import { render, screen } from '@testing-library/react';
|
|
3
|
+
import '@testing-library/jest-dom/vitest';
|
|
4
|
+
import userEvent from '@testing-library/user-event';
|
|
5
|
+
import { CheckboxCellRenderer } from './CheckboxCellRenderer';
|
|
6
|
+
import type { CustomCellRendererProps } from 'ag-grid-react';
|
|
7
|
+
|
|
8
|
+
const createMockProps = (overrides: Partial<CustomCellRendererProps> = {}): CustomCellRendererProps => ({
|
|
9
|
+
value: false,
|
|
10
|
+
node: {
|
|
11
|
+
setDataValue: vi.fn(),
|
|
12
|
+
},
|
|
13
|
+
column: 'testField',
|
|
14
|
+
...overrides,
|
|
15
|
+
} as unknown as CustomCellRendererProps);
|
|
16
|
+
|
|
17
|
+
describe('CheckboxCellRenderer', () => {
|
|
18
|
+
test('renders a checkbox', () => {
|
|
19
|
+
const props = createMockProps();
|
|
20
|
+
render(<CheckboxCellRenderer {...props} />);
|
|
21
|
+
expect(screen.getByRole('checkbox')).toBeInTheDocument();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test('renders unchecked when value is false', () => {
|
|
25
|
+
const props = createMockProps({ value: false });
|
|
26
|
+
render(<CheckboxCellRenderer {...props} />);
|
|
27
|
+
expect(screen.getByRole('checkbox')).not.toBeChecked();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('renders checked when value is true', () => {
|
|
31
|
+
const props = createMockProps({ value: true });
|
|
32
|
+
render(<CheckboxCellRenderer {...props} />);
|
|
33
|
+
expect(screen.getByRole('checkbox')).toBeChecked();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test('calls node.setDataValue with toggled value on change', async () => {
|
|
37
|
+
const setDataValue = vi.fn();
|
|
38
|
+
const props = createMockProps({
|
|
39
|
+
value: false,
|
|
40
|
+
node: { setDataValue } as unknown as CustomCellRendererProps['node'],
|
|
41
|
+
column: 'attended' as unknown as CustomCellRendererProps['column'],
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
render(<CheckboxCellRenderer {...props} />);
|
|
45
|
+
await userEvent.click(screen.getByRole('checkbox'));
|
|
46
|
+
expect(setDataValue).toHaveBeenCalledWith('attended', true);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test('toggles from true to false on change', async () => {
|
|
50
|
+
const setDataValue = vi.fn();
|
|
51
|
+
const props = createMockProps({
|
|
52
|
+
value: true,
|
|
53
|
+
node: { setDataValue } as unknown as CustomCellRendererProps['node'],
|
|
54
|
+
column: 'attended' as unknown as CustomCellRendererProps['column'],
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
render(<CheckboxCellRenderer {...props} />);
|
|
58
|
+
await userEvent.click(screen.getByRole('checkbox'));
|
|
59
|
+
expect(setDataValue).toHaveBeenCalledWith('attended', false);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test('does not call setDataValue when column is undefined', async () => {
|
|
63
|
+
const setDataValue = vi.fn();
|
|
64
|
+
const props = createMockProps({
|
|
65
|
+
value: false,
|
|
66
|
+
node: { setDataValue } as unknown as CustomCellRendererProps['node'],
|
|
67
|
+
column: undefined as unknown as CustomCellRendererProps['column'],
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
render(<CheckboxCellRenderer {...props} />);
|
|
71
|
+
await userEvent.click(screen.getByRole('checkbox'));
|
|
72
|
+
expect(setDataValue).not.toHaveBeenCalled();
|
|
73
|
+
});
|
|
74
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { CustomCellRendererProps } from 'ag-grid-react';
|
|
2
|
+
import { CheckboxInput } from 'Components/formField/inputs/checkbox/CheckboxInput';
|
|
3
|
+
|
|
4
|
+
export const CheckboxCellRenderer = (props: CustomCellRendererProps) => {
|
|
5
|
+
const {
|
|
6
|
+
value,
|
|
7
|
+
node,
|
|
8
|
+
column,
|
|
9
|
+
colDef = {},
|
|
10
|
+
} = props;
|
|
11
|
+
|
|
12
|
+
const { cellRendererParams, headerName } = colDef;
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<div className="ds-checkbox-cell-renderer">
|
|
16
|
+
<CheckboxInput
|
|
17
|
+
checked={value}
|
|
18
|
+
aria-label={headerName && `Checkbox: ${headerName}`}
|
|
19
|
+
onChange={() => {
|
|
20
|
+
if (column) {
|
|
21
|
+
node.setDataValue(column, !value);
|
|
22
|
+
}
|
|
23
|
+
}}
|
|
24
|
+
{...cellRendererParams}
|
|
25
|
+
/>
|
|
26
|
+
</div>
|
|
27
|
+
);
|
|
28
|
+
};
|
package/src/index.scss
CHANGED
|
@@ -37,10 +37,12 @@
|
|
|
37
37
|
@use "components/progress/progress.scss";
|
|
38
38
|
@use "components/toast/toast.scss";
|
|
39
39
|
@use "components/datePicker/datePicker.scss";
|
|
40
|
+
@use "components/dateTimePicker/dateTimePicker.scss";
|
|
40
41
|
@use "components/avatar/avatar.scss";
|
|
41
42
|
@use "components/singleUser/singleUser.scss";
|
|
42
43
|
@use "components/avatarGroup/avatarGroup.scss";
|
|
43
44
|
@use "components/userDropdown/userDropdown.scss";
|
|
45
|
+
@use "components/table/cellRenderers/booleanCellRenderer.scss";
|
|
44
46
|
@use "components/row/row.scss";
|
|
45
47
|
@use "components/combobox/combobox.scss";
|
|
46
48
|
@use "components/toggle/toggle.scss";
|
package/src/index.ts
CHANGED
|
@@ -10,6 +10,8 @@ export { Banner, BANNER_LEVEL, type BannerProps } from 'Components/banner/Banner
|
|
|
10
10
|
export { Button } from 'Components/button/Button';
|
|
11
11
|
export { Card } from 'Components/card/Card';
|
|
12
12
|
export { DatePicker } from 'Components/datePicker/DatePicker';
|
|
13
|
+
export { DateTimePicker } from 'Components/dateTimePicker/DateTimePicker';
|
|
14
|
+
export type { DateTimePickerDisplayFormat, DateTimePickerProps } from 'Components/dateTimePicker/DateTimePicker';
|
|
13
15
|
export { Dropdown } from 'Components/dropdown/Dropdown';
|
|
14
16
|
export { Tag } from 'Components/tag/Tag';
|
|
15
17
|
export type { TagProps, TagColor } from 'Components/tag/Tag';
|
|
@@ -42,6 +44,7 @@ export { SingleUser, type SingleUserProps } from 'Components/singleUser/SingleUs
|
|
|
42
44
|
export { Slideover, type SlideoverProps } from 'Components/slideover/Slideover';
|
|
43
45
|
export { SlideoverManager } from 'Components/slideoverManager/SlideoverManager';
|
|
44
46
|
export { DefaultCellRenderer } from 'Components/table/cellRenderers/DefaultCellRenderer';
|
|
47
|
+
export { BooleanCellRenderer } from 'Components/table/cellRenderers/BooleanCellRenderer';
|
|
45
48
|
export { DSDefaultColDef } from 'Components/table/DSDefaultColDef';
|
|
46
49
|
export { GridApiContext } from 'Components/table/GridApiContext';
|
|
47
50
|
export { Table } from 'Components/table/Table';
|
|
@@ -63,3 +66,4 @@ export type {
|
|
|
63
66
|
} from 'Components/combobox/types';
|
|
64
67
|
export { Toggle } from 'Components/toggle/Toggle';
|
|
65
68
|
export { SlideoverUtils } from 'Utils/SlideoverUtils';
|
|
69
|
+
export { CheckboxCellRenderer } from 'Components/table/cellRenderers/CheckboxCellRenderer';
|