@arbor-education/design-system.components 0.12.0 → 0.13.1
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/.agent-memory/blanche-designspert/MEMORY.md +189 -0
- package/.agent-memory/dorothy-fact-checker/MEMORY.md +228 -0
- package/.agent-memory/dorothy-fact-checker/numberinput_component.md +53 -0
- package/.agent-memory/dorothy-fact-checker/progress_component.md +36 -0
- package/.agent-memory/rose-storybookspert/MEMORY.md +105 -0
- package/.agent-memory/sophia-componentspert/MEMORY.md +34 -0
- package/{.claude/agent-memory → .agent-memory}/sophia-componentspert/components.md +170 -17
- package/{.claude → .gather}/agents/blanche-designspert.md +7 -2
- package/{.claude → .gather}/agents/dorothy-fact-checker.md +7 -2
- package/{.claude → .gather}/agents/rose-storybookspert.md +80 -11
- package/{.claude → .gather}/agents/sophia-componentspert.md +9 -4
- package/.gather/gather.yaml +9 -0
- package/{CLAUDE.md → .gather/instructions/project-overview.md} +42 -9
- package/{.claude → .gather}/skills/analyze-design/README.md +5 -0
- package/{.claude → .gather}/skills/analyze-design/SKILL.md +1 -1
- package/.gather/skills/analyze-design/meta.md +4 -0
- package/{.claude → .gather}/skills/create-page/README.md +5 -0
- package/{.claude → .gather}/skills/create-page/design-analysis-template.md +5 -0
- package/.gather/skills/create-page/meta.md +4 -0
- package/{.claude → .gather}/skills/create-page/page-template.scss +5 -0
- package/{.claude → .gather}/skills/create-page/page-template.tsx +5 -0
- package/{.claude → .gather}/skills/map-legacy/README.md +5 -0
- package/.gather/skills/map-legacy/meta.md +4 -0
- package/{.claude → .gather}/skills/migrate-page/README.md +5 -0
- package/.gather/skills/migrate-page/meta.md +4 -0
- package/.gather/skills/write-stories/README.md +157 -0
- package/.gather/skills/write-stories/SKILL.md +841 -0
- package/.gather/skills/write-stories/meta.md +4 -0
- package/.ralph/storybook-upgrade/knowledge.md +308 -0
- package/.ralph/storybook-upgrade/prd.json +777 -0
- package/.ralph/storybook-upgrade/progress.md +342 -0
- package/.storybook/DocsTemplate.tsx +122 -0
- package/.storybook/preview.ts +40 -0
- package/.stylelintignore +2 -0
- package/CHANGELOG.md +12 -0
- package/{.claude/component-library.md → component-library.md} +27 -10
- package/dist/components/badge/Badge.stories.d.ts +85 -6
- package/dist/components/badge/Badge.stories.d.ts.map +1 -1
- package/dist/components/badge/Badge.stories.js +626 -27
- package/dist/components/badge/Badge.stories.js.map +1 -1
- package/dist/components/banner/Banner.stories.d.ts +129 -63
- package/dist/components/banner/Banner.stories.d.ts.map +1 -1
- package/dist/components/banner/Banner.stories.js +855 -39
- package/dist/components/banner/Banner.stories.js.map +1 -1
- package/dist/components/button/Button.stories.d.ts +148 -8
- package/dist/components/button/Button.stories.d.ts.map +1 -1
- package/dist/components/button/Button.stories.js +1089 -80
- package/dist/components/button/Button.stories.js.map +1 -1
- package/dist/components/datePicker/DatePicker.d.ts +1 -0
- package/dist/components/datePicker/DatePicker.d.ts.map +1 -1
- package/dist/components/datePicker/DatePicker.js +2 -2
- package/dist/components/datePicker/DatePicker.js.map +1 -1
- package/dist/components/datePicker/DatePicker.stories.d.ts +1 -0
- package/dist/components/datePicker/DatePicker.stories.d.ts.map +1 -1
- package/dist/components/dot/Dot.stories.d.ts +46 -11
- package/dist/components/dot/Dot.stories.d.ts.map +1 -1
- package/dist/components/dot/Dot.stories.js +504 -15
- package/dist/components/dot/Dot.stories.js.map +1 -1
- package/dist/components/dropdown/Dropdown.stories.d.ts +89 -14
- package/dist/components/dropdown/Dropdown.stories.d.ts.map +1 -1
- package/dist/components/dropdown/Dropdown.stories.js +769 -17
- package/dist/components/dropdown/Dropdown.stories.js.map +1 -1
- package/dist/components/formField/FormField.stories.d.ts +95 -35
- package/dist/components/formField/FormField.stories.d.ts.map +1 -1
- package/dist/components/formField/FormField.stories.js +1174 -69
- package/dist/components/formField/FormField.stories.js.map +1 -1
- package/dist/components/formField/inputs/checkbox/CheckboxInput.stories.d.ts +96 -9
- package/dist/components/formField/inputs/checkbox/CheckboxInput.stories.d.ts.map +1 -1
- package/dist/components/formField/inputs/checkbox/CheckboxInput.stories.js +717 -10
- package/dist/components/formField/inputs/checkbox/CheckboxInput.stories.js.map +1 -1
- package/dist/components/formField/inputs/number/NumberInput.stories.d.ts +149 -11
- package/dist/components/formField/inputs/number/NumberInput.stories.d.ts.map +1 -1
- package/dist/components/formField/inputs/number/NumberInput.stories.js +624 -10
- package/dist/components/formField/inputs/number/NumberInput.stories.js.map +1 -1
- package/dist/components/formField/inputs/radio/RadioButtonInput.stories.d.ts +74 -1
- package/dist/components/formField/inputs/radio/RadioButtonInput.stories.d.ts.map +1 -1
- package/dist/components/formField/inputs/radio/RadioButtonInput.stories.js +673 -44
- package/dist/components/formField/inputs/radio/RadioButtonInput.stories.js.map +1 -1
- package/dist/components/formField/inputs/text/TextInput.stories.d.ts +119 -1
- package/dist/components/formField/inputs/text/TextInput.stories.d.ts.map +1 -1
- package/dist/components/formField/inputs/text/TextInput.stories.js +549 -10
- package/dist/components/formField/inputs/text/TextInput.stories.js.map +1 -1
- package/dist/components/formField/inputs/textArea/TextArea.stories.d.ts +129 -4
- package/dist/components/formField/inputs/textArea/TextArea.stories.d.ts.map +1 -1
- package/dist/components/formField/inputs/textArea/TextArea.stories.js +577 -3
- package/dist/components/formField/inputs/textArea/TextArea.stories.js.map +1 -1
- package/dist/components/heading/Heading.stories.d.ts +449 -50
- package/dist/components/heading/Heading.stories.d.ts.map +1 -1
- package/dist/components/heading/Heading.stories.js +536 -60
- package/dist/components/heading/Heading.stories.js.map +1 -1
- package/dist/components/icon/Icon.stories.d.ts +81 -10
- package/dist/components/icon/Icon.stories.d.ts.map +1 -1
- package/dist/components/icon/Icon.stories.js +979 -8
- package/dist/components/icon/Icon.stories.js.map +1 -1
- package/dist/components/pill/Pill.stories.d.ts +71 -19
- package/dist/components/pill/Pill.stories.d.ts.map +1 -1
- package/dist/components/pill/Pill.stories.js +573 -14
- package/dist/components/pill/Pill.stories.js.map +1 -1
- package/dist/components/progress/Progress.stories.d.ts +75 -298
- package/dist/components/progress/Progress.stories.d.ts.map +1 -1
- package/dist/components/progress/Progress.stories.js +449 -52
- package/dist/components/progress/Progress.stories.js.map +1 -1
- package/dist/components/separator/Separator.stories.d.ts +58 -5
- package/dist/components/separator/Separator.stories.d.ts.map +1 -1
- package/dist/components/separator/Separator.stories.js +443 -4
- package/dist/components/separator/Separator.stories.js.map +1 -1
- package/dist/components/table/Table.d.ts +7 -0
- package/dist/components/table/Table.d.ts.map +1 -1
- package/dist/components/table/Table.js +9 -0
- package/dist/components/table/Table.js.map +1 -1
- package/dist/components/table/Table.stories.d.ts +1 -0
- package/dist/components/table/Table.stories.d.ts.map +1 -1
- package/dist/components/table/Table.stories.js +87 -0
- package/dist/components/table/Table.stories.js.map +1 -1
- package/dist/components/table/Table.test.js +49 -1
- package/dist/components/table/Table.test.js.map +1 -1
- package/dist/components/table/cellEditors/DateCellEditor.d.ts +3 -0
- package/dist/components/table/cellEditors/DateCellEditor.d.ts.map +1 -0
- package/dist/components/table/cellEditors/DateCellEditor.js +13 -0
- package/dist/components/table/cellEditors/DateCellEditor.js.map +1 -0
- package/dist/components/table/cellEditors/DateCellEditor.test.d.ts +2 -0
- package/dist/components/table/cellEditors/DateCellEditor.test.d.ts.map +1 -0
- package/dist/components/table/cellEditors/DateCellEditor.test.js +81 -0
- package/dist/components/table/cellEditors/DateCellEditor.test.js.map +1 -0
- package/dist/components/tag/Tag.stories.d.ts +116 -5
- package/dist/components/tag/Tag.stories.d.ts.map +1 -1
- package/dist/components/tag/Tag.stories.js +581 -28
- package/dist/components/tag/Tag.stories.js.map +1 -1
- package/dist/index.css +8 -1
- package/dist/index.css.map +1 -1
- package/dist/index.d.ts +0 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -5
- package/dist/index.js.map +1 -1
- package/eslint.config.mts +5 -1
- package/package.json +3 -3
- package/src/components/badge/Badge.stories.tsx +869 -42
- package/src/components/banner/Banner.stories.tsx +1081 -63
- package/src/components/button/Button.stories.tsx +1394 -99
- package/src/components/datePicker/DatePicker.tsx +3 -0
- package/src/components/dot/Dot.stories.tsx +723 -32
- package/src/components/dropdown/Dropdown.stories.tsx +1174 -35
- package/src/components/formField/FormField.stories.tsx +1522 -105
- package/src/components/formField/inputs/checkbox/CheckboxInput.stories.tsx +1020 -15
- package/src/components/formField/inputs/number/NumberInput.stories.tsx +908 -15
- package/src/components/formField/inputs/radio/RadioButtonInput.stories.tsx +932 -51
- package/src/components/formField/inputs/text/TextInput.stories.tsx +773 -13
- package/src/components/formField/inputs/textArea/TextArea.stories.tsx +756 -8
- package/src/components/heading/Heading.stories.tsx +752 -120
- package/src/components/icon/Icon.stories.tsx +1446 -12
- package/src/components/pill/Pill.stories.tsx +867 -21
- package/src/components/progress/Progress.stories.tsx +625 -58
- package/src/components/separator/Separator.stories.tsx +730 -8
- package/src/components/separator/separator.scss +12 -3
- package/src/components/table/Table.stories.tsx +102 -0
- package/src/components/table/Table.test.tsx +82 -3
- package/src/components/table/Table.tsx +9 -0
- package/src/components/table/cellEditors/DateCellEditor.test.tsx +109 -0
- package/src/components/table/cellEditors/DateCellEditor.tsx +27 -0
- package/src/components/tag/Tag.stories.tsx +755 -53
- package/src/index.ts +0 -5
- package/.claude/agent-memory/blanche-designspert/MEMORY.md +0 -64
- package/.claude/agent-memory/dorothy-fact-checker/MEMORY.md +0 -129
- package/.claude/agent-memory/rose-storybookspert/MEMORY.md +0 -29
- package/.claude/agent-memory/sophia-componentspert/MEMORY.md +0 -14
- package/.claude/design-assessment-daily-attendance-2026-04-10.md +0 -566
- package/.claude/figma-assessment-7154-58899.md +0 -404
- package/.claude/figma-assessment-UKQfcxnT4rlHHNuiumt4o1-11086-97537.md +0 -392
- package/.claude/figma-assessment-UKQfcxnT4rlHHNuiumt4o1-551-41974.md +0 -474
- package/.claude/figma-assessment-UKQfcxnT4rlHHNuiumt4o1-551-43094.md +0 -462
- package/.claude/figma-assessment-fcFK4CGzkz2fVyY3koX8ZE-7154-59061.md +0 -440
- package/.claude/migration-report-custom-report-writer-2026-02-19.md +0 -591
- /package/{.claude/agent-memory → .agent-memory}/blanche-designspert/token-review-patterns.md +0 -0
- /package/{.claude/agent-memory → .agent-memory}/rose-storybookspert/patterns.md +0 -0
- /package/{.claude → .gather}/skills/create-page/SKILL.md +0 -0
- /package/{.claude → .gather}/skills/map-legacy/SKILL.md +0 -0
- /package/{.claude → .gather}/skills/migrate-page/SKILL.md +0 -0
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
.ds-separator {
|
|
2
|
-
width: 100%;
|
|
3
|
-
height: 1px;
|
|
4
2
|
background-color: var(--page-base-color-border);
|
|
5
|
-
|
|
3
|
+
|
|
4
|
+
&[data-orientation='horizontal'] {
|
|
5
|
+
width: 100%;
|
|
6
|
+
height: 1px;
|
|
7
|
+
margin: var(--spacing-small) 0;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
&[data-orientation='vertical'] {
|
|
11
|
+
width: 1px;
|
|
12
|
+
height: 100%;
|
|
13
|
+
margin: 0 var(--spacing-small);
|
|
14
|
+
}
|
|
6
15
|
}
|
|
@@ -1432,6 +1432,108 @@ export const TableWithSemanticColors: Story = {
|
|
|
1432
1432
|
},
|
|
1433
1433
|
};
|
|
1434
1434
|
|
|
1435
|
+
interface DateEditorRowData {
|
|
1436
|
+
id: number;
|
|
1437
|
+
name: { value: string };
|
|
1438
|
+
dateOfBirth: Date;
|
|
1439
|
+
enrollmentDate: Date;
|
|
1440
|
+
graduationDate: Date | null;
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1443
|
+
const dateEditorSampleData: DateEditorRowData[] = [
|
|
1444
|
+
{
|
|
1445
|
+
id: 1,
|
|
1446
|
+
name: { value: 'Alice Johnson' },
|
|
1447
|
+
dateOfBirth: new Date(2008, 4, 15),
|
|
1448
|
+
enrollmentDate: new Date(2022, 8, 1),
|
|
1449
|
+
graduationDate: new Date(2026, 6, 20),
|
|
1450
|
+
},
|
|
1451
|
+
{
|
|
1452
|
+
id: 2,
|
|
1453
|
+
name: { value: 'Bob Smith' },
|
|
1454
|
+
dateOfBirth: new Date(2009, 1, 28),
|
|
1455
|
+
enrollmentDate: new Date(2023, 8, 1),
|
|
1456
|
+
graduationDate: null,
|
|
1457
|
+
},
|
|
1458
|
+
{
|
|
1459
|
+
id: 3,
|
|
1460
|
+
name: { value: 'Charlie Brown' },
|
|
1461
|
+
dateOfBirth: new Date(2007, 10, 3),
|
|
1462
|
+
enrollmentDate: new Date(2021, 8, 1),
|
|
1463
|
+
graduationDate: new Date(2025, 6, 18),
|
|
1464
|
+
},
|
|
1465
|
+
{
|
|
1466
|
+
id: 4,
|
|
1467
|
+
name: { value: 'Diana Prince' },
|
|
1468
|
+
dateOfBirth: new Date(2008, 7, 22),
|
|
1469
|
+
enrollmentDate: new Date(2022, 8, 1),
|
|
1470
|
+
graduationDate: new Date(2026, 6, 20),
|
|
1471
|
+
},
|
|
1472
|
+
{
|
|
1473
|
+
id: 5,
|
|
1474
|
+
name: { value: 'Ethan Hunt' },
|
|
1475
|
+
dateOfBirth: new Date(2009, 11, 10),
|
|
1476
|
+
enrollmentDate: new Date(2023, 8, 1),
|
|
1477
|
+
graduationDate: null,
|
|
1478
|
+
},
|
|
1479
|
+
];
|
|
1480
|
+
|
|
1481
|
+
const dateEditorColDefs: (ColDef | ColGroupDef)[] = [
|
|
1482
|
+
{
|
|
1483
|
+
field: 'name',
|
|
1484
|
+
headerName: 'Student Name',
|
|
1485
|
+
editable: false,
|
|
1486
|
+
valueFormatter: Table.DefaultValueFormatter,
|
|
1487
|
+
},
|
|
1488
|
+
{
|
|
1489
|
+
field: 'dateOfBirth',
|
|
1490
|
+
headerName: 'Date of Birth',
|
|
1491
|
+
cellEditor: 'dsDateCellEditor',
|
|
1492
|
+
valueFormatter: params =>
|
|
1493
|
+
params.value instanceof Date
|
|
1494
|
+
? params.value.toLocaleDateString('en-GB')
|
|
1495
|
+
: params.value ?? '',
|
|
1496
|
+
},
|
|
1497
|
+
{
|
|
1498
|
+
field: 'enrollmentDate',
|
|
1499
|
+
headerName: 'Enrollment Date',
|
|
1500
|
+
cellEditor: 'dsDateCellEditor',
|
|
1501
|
+
valueFormatter: params =>
|
|
1502
|
+
params.value instanceof Date
|
|
1503
|
+
? params.value.toLocaleDateString('en-GB')
|
|
1504
|
+
: params.value ?? '',
|
|
1505
|
+
},
|
|
1506
|
+
{
|
|
1507
|
+
field: 'graduationDate',
|
|
1508
|
+
headerName: 'Graduation Date',
|
|
1509
|
+
cellEditor: 'dsDateCellEditor',
|
|
1510
|
+
valueFormatter: params =>
|
|
1511
|
+
params.value instanceof Date
|
|
1512
|
+
? params.value.toLocaleDateString('en-GB')
|
|
1513
|
+
: params.value ?? '',
|
|
1514
|
+
},
|
|
1515
|
+
];
|
|
1516
|
+
|
|
1517
|
+
export const WithDateCellEditor: Story = {
|
|
1518
|
+
parameters: {
|
|
1519
|
+
docs: {
|
|
1520
|
+
description: {
|
|
1521
|
+
story:
|
|
1522
|
+
'Date columns can use the `dsDateCellEditor` cell editor to provide an inline date picker. Double-click or press Enter on a date cell to open the editor.',
|
|
1523
|
+
},
|
|
1524
|
+
},
|
|
1525
|
+
},
|
|
1526
|
+
args: {
|
|
1527
|
+
rowData: dateEditorSampleData,
|
|
1528
|
+
columnDefs: dateEditorColDefs,
|
|
1529
|
+
defaultColDef: {
|
|
1530
|
+
...defaultColDef,
|
|
1531
|
+
editable: true,
|
|
1532
|
+
},
|
|
1533
|
+
domLayout: 'autoHeight',
|
|
1534
|
+
},
|
|
1535
|
+
};
|
|
1536
|
+
|
|
1435
1537
|
interface BooleanRowData {
|
|
1436
1538
|
id: number;
|
|
1437
1539
|
name: string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, expectTypeOf, test, vi } from 'vitest';
|
|
2
|
-
import { render, screen, waitFor } from '@testing-library/react';
|
|
2
|
+
import { render, screen, waitFor, fireEvent } from '@testing-library/react';
|
|
3
3
|
import { Table } from './Table';
|
|
4
4
|
import '@testing-library/jest-dom/vitest';
|
|
5
5
|
import { BulkActionsDropdown } from 'Components/table/tableControls/BulkActionsDropdown';
|
|
@@ -753,7 +753,7 @@ describe('Table', () => {
|
|
|
753
753
|
actions={[
|
|
754
754
|
{
|
|
755
755
|
displayName: 'Test Action',
|
|
756
|
-
callback: () => {},
|
|
756
|
+
callback: () => { },
|
|
757
757
|
},
|
|
758
758
|
]}
|
|
759
759
|
/>
|
|
@@ -1049,7 +1049,7 @@ describe('Table', () => {
|
|
|
1049
1049
|
];
|
|
1050
1050
|
|
|
1051
1051
|
const rowData = [
|
|
1052
|
-
{
|
|
1052
|
+
{},
|
|
1053
1053
|
];
|
|
1054
1054
|
await expect(async () => {
|
|
1055
1055
|
render(
|
|
@@ -1487,6 +1487,85 @@ describe('Table', () => {
|
|
|
1487
1487
|
});
|
|
1488
1488
|
});
|
|
1489
1489
|
|
|
1490
|
+
describe('DateCellEditor', () => {
|
|
1491
|
+
const columnDefs = [{
|
|
1492
|
+
field: 'dateOfBirth',
|
|
1493
|
+
headerName: 'Date of Birth',
|
|
1494
|
+
editable: true,
|
|
1495
|
+
cellEditor: 'dsDateCellEditor',
|
|
1496
|
+
}];
|
|
1497
|
+
|
|
1498
|
+
test('opens the date picker editor via grid API', async () => {
|
|
1499
|
+
let gridApi: GridApi;
|
|
1500
|
+
const rowData = [{ dateOfBirth: new Date(2024, 5, 15) }];
|
|
1501
|
+
render(
|
|
1502
|
+
<Table
|
|
1503
|
+
columnDefs={columnDefs}
|
|
1504
|
+
rowData={rowData}
|
|
1505
|
+
onGridReady={(event) => { gridApi = event.api; }}
|
|
1506
|
+
/>,
|
|
1507
|
+
);
|
|
1508
|
+
await waitFor(() => expect(screen.getByRole('grid')).toBeInTheDocument());
|
|
1509
|
+
|
|
1510
|
+
gridApi!.startEditingCell({ rowIndex: 0, colKey: 'dateOfBirth' });
|
|
1511
|
+
|
|
1512
|
+
await waitFor(() => expect(screen.getByRole('button', { name: 'Open date picker' })).toBeInTheDocument());
|
|
1513
|
+
});
|
|
1514
|
+
|
|
1515
|
+
test('autofocuses the date input so a user can type a date without clicking', async () => {
|
|
1516
|
+
let gridApi: GridApi;
|
|
1517
|
+
const onCellValueChanged = vi.fn();
|
|
1518
|
+
const rowData = [{ dateOfBirth: new Date(2024, 5, 15) }];
|
|
1519
|
+
const { container } = render(
|
|
1520
|
+
<Table
|
|
1521
|
+
columnDefs={columnDefs}
|
|
1522
|
+
rowData={rowData}
|
|
1523
|
+
onGridReady={(event) => { gridApi = event.api; }}
|
|
1524
|
+
onCellValueChanged={onCellValueChanged}
|
|
1525
|
+
/>,
|
|
1526
|
+
);
|
|
1527
|
+
await waitFor(() => expect(screen.getByRole('grid')).toBeInTheDocument());
|
|
1528
|
+
|
|
1529
|
+
gridApi!.setFocusedCell(0, 'dateOfBirth');
|
|
1530
|
+
await userEvent.keyboard('{Enter}');
|
|
1531
|
+
|
|
1532
|
+
await waitFor(() => expect(container.querySelector('input[type="date"]')).toBeInTheDocument());
|
|
1533
|
+
const input = container.querySelector('input[type="date"]') as HTMLInputElement;
|
|
1534
|
+
expect(input).toHaveFocus();
|
|
1535
|
+
|
|
1536
|
+
fireEvent.change(input, { target: { value: '2024-07-20' } });
|
|
1537
|
+
await userEvent.keyboard('{Enter}');
|
|
1538
|
+
|
|
1539
|
+
await waitFor(() => {
|
|
1540
|
+
expect(onCellValueChanged).toHaveBeenCalled();
|
|
1541
|
+
});
|
|
1542
|
+
const lastDate: Date = onCellValueChanged.mock.lastCall![0].newValue;
|
|
1543
|
+
expect(lastDate.getFullYear()).toBe(2024);
|
|
1544
|
+
expect(lastDate.getMonth()).toBe(6);
|
|
1545
|
+
expect(lastDate.getDate()).toBe(20);
|
|
1546
|
+
});
|
|
1547
|
+
|
|
1548
|
+
test('opens the editor with the current value and closes it cleanly', async () => {
|
|
1549
|
+
let gridApi: GridApi;
|
|
1550
|
+
const rowData = [{ dateOfBirth: new Date(2024, 5, 15) }];
|
|
1551
|
+
const { container } = render(
|
|
1552
|
+
<Table
|
|
1553
|
+
columnDefs={columnDefs}
|
|
1554
|
+
rowData={rowData}
|
|
1555
|
+
onGridReady={(event) => { gridApi = event.api; }}
|
|
1556
|
+
/>,
|
|
1557
|
+
);
|
|
1558
|
+
await waitFor(() => expect(screen.getByRole('grid')).toBeInTheDocument());
|
|
1559
|
+
|
|
1560
|
+
gridApi!.startEditingCell({ rowIndex: 0, colKey: 'dateOfBirth' });
|
|
1561
|
+
await waitFor(() => expect(container.querySelector('input[type="date"]')).toBeInTheDocument());
|
|
1562
|
+
expect(container.querySelector('input[type="date"]')).toHaveValue('2024-06-15');
|
|
1563
|
+
|
|
1564
|
+
gridApi!.stopEditing();
|
|
1565
|
+
await waitFor(() => expect(container.querySelector('input[type="date"]')).not.toBeInTheDocument());
|
|
1566
|
+
});
|
|
1567
|
+
});
|
|
1568
|
+
|
|
1490
1569
|
describe('CheckboxCellRenderer', () => {
|
|
1491
1570
|
test('renders checkboxes in table cells', async () => {
|
|
1492
1571
|
const columnDefs = [
|
|
@@ -23,6 +23,7 @@ import { tidyTheme } from './theme/tidyTheme';
|
|
|
23
23
|
import { focusFirstFocusableElement } from 'Utils/focusFirstFocusableElement';
|
|
24
24
|
import { BooleanFilter } from './columnFilters/BooleanFilter/BooleanFilter';
|
|
25
25
|
import { TimeFilter } from './columnFilters/TimeFilter/TimeFilter';
|
|
26
|
+
import { DateCellEditor } from './cellEditors/DateCellEditor';
|
|
26
27
|
import { TableSettingsDropdown } from './tableControls/TableSettingsDropdown';
|
|
27
28
|
import { TableControls, type TableControlsProps, type BulkAction as BulkActionType } from './tableControls/TableControls';
|
|
28
29
|
import type { HideColumnsDropdownProps } from './tableControls/HideColumnsDropdown';
|
|
@@ -30,6 +31,7 @@ import { TABLE_SPACING } from './tableConsts';
|
|
|
30
31
|
import { TableSettingsContext } from './TableSettingsContext';
|
|
31
32
|
import { BooleanCellRenderer } from './cellRenderers/BooleanCellRenderer';
|
|
32
33
|
import { CheckboxCellRenderer } from './cellRenderers/CheckboxCellRenderer';
|
|
34
|
+
import { DefaultCellRenderer } from './cellRenderers/DefaultCellRenderer';
|
|
33
35
|
|
|
34
36
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
35
37
|
type TableProps<TData = any> = {
|
|
@@ -183,6 +185,7 @@ export const Table = (props: TableProps) => {
|
|
|
183
185
|
dsSelectDropdownCellRenderer: SelectDropdownCellRenderer,
|
|
184
186
|
dsBooleanFilter: BooleanFilter,
|
|
185
187
|
dsTimeFilter: TimeFilter,
|
|
188
|
+
dsDateCellEditor: DateCellEditor,
|
|
186
189
|
dsCheckboxCellRenderer: CheckboxCellRenderer,
|
|
187
190
|
dsBooleanCellRenderer: BooleanCellRenderer,
|
|
188
191
|
...components,
|
|
@@ -208,6 +211,12 @@ Table.RowCountInfo = RowCountInfo;
|
|
|
208
211
|
Table.BulkActionsDropdown = BulkActionsDropdown;
|
|
209
212
|
Table.HideColumnsDropdown = HideColumnsDropdown;
|
|
210
213
|
Table.ButtonCellRenderer = ButtonCellRenderer;
|
|
214
|
+
Table.BooleanCellRenderer = BooleanCellRenderer;
|
|
215
|
+
Table.DefaultCellRenderer = DefaultCellRenderer;
|
|
216
|
+
Table.DateCellEditor = DateCellEditor;
|
|
217
|
+
Table.DefaultColDef = DSDefaultColDef;
|
|
218
|
+
Table.GridApiContext = GridApiContext;
|
|
219
|
+
Table.CheckboxCellRenderer = CheckboxCellRenderer;
|
|
211
220
|
Table.DefaultValueFormatter = defaultValueFormatter;
|
|
212
221
|
Table.TableSettingsDropdown = TableSettingsDropdown;
|
|
213
222
|
Table.TableControls = TableControls;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { describe, expect, test, vi } from 'vitest';
|
|
2
|
+
import { render, screen, within, fireEvent } from '@testing-library/react';
|
|
3
|
+
import userEvent from '@testing-library/user-event';
|
|
4
|
+
import '@testing-library/jest-dom/vitest';
|
|
5
|
+
import { DateCellEditor } from './DateCellEditor';
|
|
6
|
+
import type { CustomCellEditorProps } from 'ag-grid-react';
|
|
7
|
+
|
|
8
|
+
const createMockProps = (overrides: Partial<CustomCellEditorProps> = {}): CustomCellEditorProps => ({
|
|
9
|
+
value: undefined,
|
|
10
|
+
initialValue: undefined,
|
|
11
|
+
onValueChange: vi.fn(),
|
|
12
|
+
colDef: { cellEditorParams: {} },
|
|
13
|
+
...overrides,
|
|
14
|
+
} as CustomCellEditorProps);
|
|
15
|
+
|
|
16
|
+
const getDateField = (container: HTMLElement) => (
|
|
17
|
+
container.querySelector('input[type="date"]') as HTMLInputElement
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
describe('DateCellEditor', () => {
|
|
21
|
+
test('renders a date picker', () => {
|
|
22
|
+
const { container } = render(<DateCellEditor {...createMockProps()} />);
|
|
23
|
+
expect(getDateField(container)).toBeInTheDocument();
|
|
24
|
+
expect(screen.getByRole('button', { name: 'Open date picker' })).toBeInTheDocument();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test('displays the provided value', () => {
|
|
28
|
+
const { container } = render(<DateCellEditor {...createMockProps({ value: new Date(2024, 5, 15) })} />);
|
|
29
|
+
expect(getDateField(container)).toHaveValue('2024-06-15');
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test('displays empty when value is undefined', () => {
|
|
33
|
+
const { container } = render(<DateCellEditor {...createMockProps({ value: undefined })} />);
|
|
34
|
+
expect(getDateField(container)).toHaveValue('');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('displays empty when value is null', () => {
|
|
38
|
+
const { container } = render(<DateCellEditor {...createMockProps({ value: null })} />);
|
|
39
|
+
expect(getDateField(container)).toHaveValue('');
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test('calls onValueChange when a date is typed', () => {
|
|
43
|
+
const onValueChange = vi.fn();
|
|
44
|
+
const { container } = render(<DateCellEditor {...createMockProps({ onValueChange })} />);
|
|
45
|
+
onValueChange.mockClear();
|
|
46
|
+
|
|
47
|
+
fireEvent.change(getDateField(container), { target: { value: '2024-06-15' } });
|
|
48
|
+
|
|
49
|
+
expect(onValueChange).toHaveBeenCalledWith(expect.any(Date));
|
|
50
|
+
const lastDate: Date = onValueChange.mock.lastCall![0];
|
|
51
|
+
expect(lastDate.getFullYear()).toBe(2024);
|
|
52
|
+
expect(lastDate.getMonth()).toBe(5);
|
|
53
|
+
expect(lastDate.getDate()).toBe(15);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test('calls onValueChange when a date is picked from the calendar', async () => {
|
|
57
|
+
const onValueChange = vi.fn();
|
|
58
|
+
const { container } = render(<DateCellEditor {...createMockProps({ onValueChange })} />);
|
|
59
|
+
|
|
60
|
+
// Navigate to a known month via text input first
|
|
61
|
+
fireEvent.change(getDateField(container), { target: { value: '2024-06-01' } });
|
|
62
|
+
onValueChange.mockClear();
|
|
63
|
+
|
|
64
|
+
await userEvent.click(screen.getByRole('button', { name: 'Open date picker' }));
|
|
65
|
+
await userEvent.click(within(screen.getByRole('application')).getByRole('button', { name: /June 20/ }));
|
|
66
|
+
|
|
67
|
+
expect(onValueChange).toHaveBeenCalledWith(expect.any(Date));
|
|
68
|
+
const lastDate: Date = onValueChange.mock.lastCall![0];
|
|
69
|
+
expect(lastDate.getFullYear()).toBe(2024);
|
|
70
|
+
expect(lastDate.getMonth()).toBe(5);
|
|
71
|
+
expect(lastDate.getDate()).toBe(20);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test('calls onValueChange with undefined when the date is cleared', () => {
|
|
75
|
+
const onValueChange = vi.fn();
|
|
76
|
+
const { container } = render(
|
|
77
|
+
<DateCellEditor {...createMockProps({ value: new Date(2024, 5, 15), onValueChange })} />,
|
|
78
|
+
);
|
|
79
|
+
onValueChange.mockClear();
|
|
80
|
+
|
|
81
|
+
fireEvent.change(getDateField(container), { target: { value: '' } });
|
|
82
|
+
|
|
83
|
+
expect(onValueChange).toHaveBeenCalledWith(undefined);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test('autofocuses the date input on mount', () => {
|
|
87
|
+
const { container } = render(<DateCellEditor {...createMockProps()} />);
|
|
88
|
+
expect(getDateField(container)).toHaveFocus();
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test('autofocuses the date input even when a value is already set', () => {
|
|
92
|
+
const { container } = render(
|
|
93
|
+
<DateCellEditor {...createMockProps({ value: new Date(2024, 5, 15) })} />,
|
|
94
|
+
);
|
|
95
|
+
expect(getDateField(container)).toHaveFocus();
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test('passes cellEditorParams through to DatePicker', () => {
|
|
99
|
+
render(
|
|
100
|
+
<DateCellEditor
|
|
101
|
+
{...createMockProps({
|
|
102
|
+
value: undefined,
|
|
103
|
+
colDef: { cellEditorParams: { placeholder: 'Pick a birthday' } },
|
|
104
|
+
})}
|
|
105
|
+
/>,
|
|
106
|
+
);
|
|
107
|
+
expect(screen.getByText('Pick a birthday')).toBeInTheDocument();
|
|
108
|
+
});
|
|
109
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { CustomCellEditorProps } from 'ag-grid-react';
|
|
2
|
+
import { DatePicker } from 'Components/datePicker/DatePicker';
|
|
3
|
+
|
|
4
|
+
export const DateCellEditor = (props: CustomCellEditorProps) => {
|
|
5
|
+
const {
|
|
6
|
+
value,
|
|
7
|
+
onValueChange,
|
|
8
|
+
colDef: {
|
|
9
|
+
cellEditorParams,
|
|
10
|
+
},
|
|
11
|
+
} = props;
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<DatePicker
|
|
15
|
+
value={value}
|
|
16
|
+
onChange={(newValue) => {
|
|
17
|
+
// intentionally not updating always, because AG Grid controls the component and
|
|
18
|
+
// ends up interfering with things, leading to lots of jank
|
|
19
|
+
if (newValue !== value) {
|
|
20
|
+
onValueChange(newValue);
|
|
21
|
+
}
|
|
22
|
+
}}
|
|
23
|
+
autoFocus
|
|
24
|
+
{...cellEditorParams}
|
|
25
|
+
/>
|
|
26
|
+
);
|
|
27
|
+
};
|