@arbor-education/design-system.components 4.4.0 → 4.6.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/CHANGELOG.md +18 -0
- package/dist/components/avatar/Avatar.js +2 -2
- package/dist/components/avatar/Avatar.js.map +1 -1
- package/dist/components/badge/Badge.stories.d.ts +0 -1
- package/dist/components/badge/Badge.stories.d.ts.map +1 -1
- package/dist/components/badge/Badge.stories.js +0 -69
- package/dist/components/badge/Badge.stories.js.map +1 -1
- package/dist/components/datePicker/DatePicker.stories.d.ts +5 -0
- package/dist/components/datePicker/DatePicker.stories.d.ts.map +1 -1
- package/dist/components/datePicker/DatePicker.stories.js +211 -2
- package/dist/components/datePicker/DatePicker.stories.js.map +1 -1
- package/dist/components/datePicker/DatePickerPopover.d.ts +11 -0
- package/dist/components/datePicker/DatePickerPopover.d.ts.map +1 -0
- package/dist/components/datePicker/DatePickerPopover.js +33 -0
- package/dist/components/datePicker/DatePickerPopover.js.map +1 -0
- package/dist/components/datePicker/DatePickerPopover.test.d.ts +2 -0
- package/dist/components/datePicker/DatePickerPopover.test.d.ts.map +1 -0
- package/dist/components/datePicker/DatePickerPopover.test.js +101 -0
- package/dist/components/datePicker/DatePickerPopover.test.js.map +1 -0
- package/dist/components/dot/Dot.d.ts.map +1 -1
- package/dist/components/dot/Dot.js +1 -1
- package/dist/components/dot/Dot.js.map +1 -1
- package/dist/components/formField/inputs/checkbox/CheckboxInput.d.ts +2 -2
- package/dist/components/formField/inputs/checkbox/CheckboxInput.d.ts.map +1 -1
- package/dist/components/formField/inputs/checkbox/CheckboxInput.js +1 -1
- package/dist/components/formField/inputs/checkbox/CheckboxInput.js.map +1 -1
- package/dist/components/formField/inputs/checkbox/CheckboxInput.stories.d.ts +2 -0
- package/dist/components/formField/inputs/checkbox/CheckboxInput.stories.d.ts.map +1 -1
- package/dist/components/formField/inputs/checkbox/CheckboxInput.stories.js +100 -0
- package/dist/components/formField/inputs/checkbox/CheckboxInput.stories.js.map +1 -1
- package/dist/components/formField/inputs/checkbox/CheckboxInput.test.js +17 -1
- package/dist/components/formField/inputs/checkbox/CheckboxInput.test.js.map +1 -1
- package/dist/components/searchBar/SearchBar.d.ts.map +1 -1
- package/dist/components/searchBar/SearchBar.js +1 -1
- package/dist/components/searchBar/SearchBar.js.map +1 -1
- package/dist/components/searchBar/SearchBar.test.js +3 -3
- package/dist/components/searchBar/SearchBar.test.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/utils/popupParent/PopupParentContext.stories.d.ts.map +1 -1
- package/dist/utils/popupParent/PopupParentContext.stories.js +1 -1
- package/dist/utils/popupParent/PopupParentContext.stories.js.map +1 -1
- package/package.json +1 -1
- package/src/components/avatar/Avatar.tsx +2 -2
- package/src/components/badge/Badge.stories.tsx +0 -103
- package/src/components/datePicker/DatePicker.stories.tsx +293 -0
- package/src/components/datePicker/DatePickerPopover.test.tsx +144 -0
- package/src/components/datePicker/DatePickerPopover.tsx +72 -0
- package/src/components/dot/Dot.tsx +1 -0
- package/src/components/formField/inputs/checkbox/CheckboxInput.stories.tsx +178 -1
- package/src/components/formField/inputs/checkbox/CheckboxInput.test.tsx +58 -0
- package/src/components/formField/inputs/checkbox/CheckboxInput.tsx +3 -3
- package/src/components/searchBar/SearchBar.test.tsx +3 -3
- package/src/components/searchBar/SearchBar.tsx +1 -0
- package/src/index.ts +1 -0
- package/src/utils/popupParent/PopupParentContext.stories.tsx +1 -0
|
@@ -170,7 +170,7 @@ const WithCustomContainerTemplate = () => {
|
|
|
170
170
|
borderRadius: 'var(--border-radius-small)',
|
|
171
171
|
padding: 'var(--spacing-xsmall)',
|
|
172
172
|
minHeight: '2rem',
|
|
173
|
-
}, "aria-label": "Custom popup container" }), _jsx(PopupParentContext.Provider, { value: popupContainerRef, children: _jsxs(Dropdown, { children: [_jsx(Dropdown.Trigger, { children: _jsx(Button, { variant: "dropdown", children: "Actions" }) }), _jsxs(Dropdown.Content, { children: [_jsx(Dropdown.Item, { children: "View pupil profile" }), _jsx(Dropdown.Item, { children: "Edit enrolment" }), _jsx(Dropdown.Separator, {}), _jsx(Dropdown.Item, { children: "Remove from group" })] })] }) })] }));
|
|
173
|
+
}, "aria-label": "Custom popup container", "aria-hidden": true }), _jsx(PopupParentContext.Provider, { value: popupContainerRef, children: _jsxs(Dropdown, { children: [_jsx(Dropdown.Trigger, { children: _jsx(Button, { variant: "dropdown", children: "Actions" }) }), _jsxs(Dropdown.Content, { children: [_jsx(Dropdown.Item, { children: "View pupil profile" }), _jsx(Dropdown.Item, { children: "Edit enrolment" }), _jsx(Dropdown.Separator, {}), _jsx(Dropdown.Item, { children: "Remove from group" })] })] }) })] }));
|
|
174
174
|
};
|
|
175
175
|
// ---------------------------------------------------------------------------
|
|
176
176
|
// Stories
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PopupParentContext.stories.js","sourceRoot":"","sources":["../../../src/utils/popupParent/PopupParentContext.stories.tsx"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"PopupParentContext.stories.js","sourceRoot":"","sources":["../../../src/utils/popupParent/PopupParentContext.stories.tsx"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -34,14 +34,14 @@ export const Avatar = (props: AvatarProps) => {
|
|
|
34
34
|
|
|
35
35
|
if (initials) {
|
|
36
36
|
return (
|
|
37
|
-
<span className="ds-avatar__initials" aria-label={alt}>
|
|
37
|
+
<span className="ds-avatar__initials" aria-label={alt} role="img">
|
|
38
38
|
{initials}
|
|
39
39
|
</span>
|
|
40
40
|
);
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
return (
|
|
44
|
-
<span className="ds-avatar__placeholder" aria-label={alt || 'User avatar'}>
|
|
44
|
+
<span className="ds-avatar__placeholder" aria-label={alt || 'User avatar'} role="img">
|
|
45
45
|
<svg
|
|
46
46
|
viewBox="0 0 24 24"
|
|
47
47
|
fill="currentColor"
|
|
@@ -433,42 +433,6 @@ const AllVariantsTemplate = () => (
|
|
|
433
433
|
</div>
|
|
434
434
|
);
|
|
435
435
|
|
|
436
|
-
const A11yWithLabelTemplate = () => (
|
|
437
|
-
<div
|
|
438
|
-
style={{
|
|
439
|
-
padding: 'var(--spacing-xlarge)',
|
|
440
|
-
display: 'flex',
|
|
441
|
-
flexDirection: 'column',
|
|
442
|
-
gap: 'var(--spacing-xlarge)',
|
|
443
|
-
}}
|
|
444
|
-
>
|
|
445
|
-
<div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--spacing-large)' }}>
|
|
446
|
-
<p className="ds-text" style={{ margin: 0, color: 'var(--color-semantic-destructive-600)' }}>
|
|
447
|
-
Without a11yLabel — screen reader announces "5" (no context)
|
|
448
|
-
</p>
|
|
449
|
-
<div style={{ display: 'flex', alignItems: 'center', gap: 'var(--spacing-large)' }}>
|
|
450
|
-
<Icon name="mail" size={16} screenReaderText="Notifications" />
|
|
451
|
-
<Badge>5</Badge>
|
|
452
|
-
</div>
|
|
453
|
-
<p className="ds-text" style={{ margin: 0, color: 'var(--color-grey-600)', fontStyle: 'italic' }}>
|
|
454
|
-
Screen reader: "mail Notifications, 5"
|
|
455
|
-
</p>
|
|
456
|
-
</div>
|
|
457
|
-
<div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--spacing-large)' }}>
|
|
458
|
-
<p className="ds-text" style={{ margin: 0, color: 'var(--color-semantic-success-600)' }}>
|
|
459
|
-
With a11yLabel — screen reader announces the full meaningful label
|
|
460
|
-
</p>
|
|
461
|
-
<div style={{ display: 'flex', alignItems: 'center', gap: 'var(--spacing-large)' }}>
|
|
462
|
-
<Icon name="mail" size={16} />
|
|
463
|
-
<Badge a11yLabel="5 unread messages">5</Badge>
|
|
464
|
-
</div>
|
|
465
|
-
<p className="ds-text" style={{ margin: 0, color: 'var(--color-grey-600)', fontStyle: 'italic' }}>
|
|
466
|
-
Screen reader: "5 unread messages"
|
|
467
|
-
</p>
|
|
468
|
-
</div>
|
|
469
|
-
</div>
|
|
470
|
-
);
|
|
471
|
-
|
|
472
436
|
// ---------------------------------------------------------------------------
|
|
473
437
|
// Stories
|
|
474
438
|
// ---------------------------------------------------------------------------
|
|
@@ -832,70 +796,3 @@ export default AllVariantsTemplate;
|
|
|
832
796
|
},
|
|
833
797
|
'Full 3 × 8 grid: all three sizes crossed with all eight colour states (default charcoal + 7 palette colours). Use this story as a complete visual reference for the Badge component. Each cell uses the same count (`"9"`) to make size and colour comparisons accurate.',
|
|
834
798
|
);
|
|
835
|
-
|
|
836
|
-
export const A11yWithLabel: Story = withDescription(
|
|
837
|
-
{
|
|
838
|
-
render: A11yWithLabelTemplate,
|
|
839
|
-
parameters: {
|
|
840
|
-
docs: {
|
|
841
|
-
source: {
|
|
842
|
-
language: 'tsx',
|
|
843
|
-
code: `
|
|
844
|
-
import { Badge, Icon } from '@arbor-education/design-system.components';
|
|
845
|
-
|
|
846
|
-
function A11yWithLabelTemplate() {
|
|
847
|
-
return (
|
|
848
|
-
<div
|
|
849
|
-
style={{
|
|
850
|
-
padding: 'var(--spacing-xlarge)',
|
|
851
|
-
display: 'flex',
|
|
852
|
-
flexDirection: 'column',
|
|
853
|
-
gap: 'var(--spacing-xlarge)',
|
|
854
|
-
}}
|
|
855
|
-
>
|
|
856
|
-
<div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--spacing-large)' }}>
|
|
857
|
-
<p className="ds-text" style={{ margin: 0, color: 'var(--color-semantic-destructive-600)' }}>
|
|
858
|
-
Without a11yLabel — screen reader announces "5" (no context)
|
|
859
|
-
</p>
|
|
860
|
-
<div style={{ display: 'flex', alignItems: 'center', gap: 'var(--spacing-large)' }}>
|
|
861
|
-
<Icon name="mail" size={16} screenReaderText="Notifications" />
|
|
862
|
-
<Badge>5</Badge>
|
|
863
|
-
</div>
|
|
864
|
-
<p className="ds-text" style={{ margin: 0, color: 'var(--color-grey-600)', fontStyle: 'italic' }}>
|
|
865
|
-
Screen reader: "mail Notifications, 5"
|
|
866
|
-
</p>
|
|
867
|
-
</div>
|
|
868
|
-
<div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--spacing-large)' }}>
|
|
869
|
-
<p className="ds-text" style={{ margin: 0, color: 'var(--color-semantic-success-600)' }}>
|
|
870
|
-
With a11yLabel — screen reader announces the full meaningful label
|
|
871
|
-
</p>
|
|
872
|
-
<div style={{ display: 'flex', alignItems: 'center', gap: 'var(--spacing-large)' }}>
|
|
873
|
-
<Icon name="mail" size={16} />
|
|
874
|
-
<Badge a11yLabel="5 unread messages">5</Badge>
|
|
875
|
-
</div>
|
|
876
|
-
<p className="ds-text" style={{ margin: 0, color: 'var(--color-grey-600)', fontStyle: 'italic' }}>
|
|
877
|
-
Screen reader: "5 unread messages"
|
|
878
|
-
</p>
|
|
879
|
-
</div>
|
|
880
|
-
</div>
|
|
881
|
-
);
|
|
882
|
-
}
|
|
883
|
-
export default A11yWithLabelTemplate;
|
|
884
|
-
`.trim(),
|
|
885
|
-
},
|
|
886
|
-
},
|
|
887
|
-
},
|
|
888
|
-
},
|
|
889
|
-
[
|
|
890
|
-
'Accessibility contrast: top row shows a badge **without** `a11yLabel` — a screen reader',
|
|
891
|
-
'moving through the icon and badge would announce the icon text and then the bare number `"5"`,',
|
|
892
|
-
'providing no context about what those 5 items are.',
|
|
893
|
-
'',
|
|
894
|
-
'The bottom row adds `a11yLabel="5 unread messages"`. The component sets `aria-label` on the',
|
|
895
|
-
'wrapper `<span>` and wraps `children` in an `aria-hidden="true"` span. The screen reader now',
|
|
896
|
-
'announces `"5 unread messages"` as a single coherent label — no duplicate number, full context.',
|
|
897
|
-
'',
|
|
898
|
-
'Use `a11yLabel` whenever the badge sits next to an icon-only element or in a context where',
|
|
899
|
-
'the number alone would be ambiguous to a screen reader user.',
|
|
900
|
-
].join(' '),
|
|
901
|
-
);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useState } from 'react';
|
|
2
2
|
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
3
3
|
import {
|
|
4
|
+
ArgTypes,
|
|
4
5
|
Controls,
|
|
5
6
|
Heading as DocHeading,
|
|
6
7
|
Markdown,
|
|
@@ -9,8 +10,13 @@ import {
|
|
|
9
10
|
Subtitle,
|
|
10
11
|
Title,
|
|
11
12
|
} from '@storybook/addon-docs/blocks';
|
|
13
|
+
import { fn } from 'storybook/test';
|
|
14
|
+
import { format } from 'date-fns';
|
|
15
|
+
import { Button } from 'Components/button/Button';
|
|
12
16
|
import { FormField } from 'Components/formField/FormField';
|
|
17
|
+
import { Icon } from 'Components/icon/Icon';
|
|
13
18
|
import { DatePicker } from './DatePicker.js';
|
|
19
|
+
import { DatePickerPopover } from './DatePickerPopover.js';
|
|
14
20
|
|
|
15
21
|
// ---------------------------------------------------------------------------
|
|
16
22
|
// Docs page content
|
|
@@ -104,6 +110,44 @@ const DEVELOPER_NOTES = [
|
|
|
104
110
|
'| `DatePicker.Props` | Full props interface |',
|
|
105
111
|
].join('\n');
|
|
106
112
|
|
|
113
|
+
const POPOVER_NOTES = [
|
|
114
|
+
'`DatePickerPopover` is the calendar on its own: no date input, no icon button. You supply the',
|
|
115
|
+
'trigger (usually a `Button`) and the component opens the same calendar popover as `DatePicker`.',
|
|
116
|
+
'Use it when the date is shown elsewhere in your UI and you only need a way to pick or jump to a day,',
|
|
117
|
+
'for example a "jump to day" icon in a calendar header. For a form field where the user types or',
|
|
118
|
+
'sees the chosen date, use `DatePicker` instead.',
|
|
119
|
+
'',
|
|
120
|
+
'```tsx',
|
|
121
|
+
"import { Button, DatePickerPopover, Icon } from '@arbor-education/design-system.components';",
|
|
122
|
+
'',
|
|
123
|
+
'<DatePickerPopover',
|
|
124
|
+
' value={date}',
|
|
125
|
+
' onChange={setDate}',
|
|
126
|
+
' trigger={(',
|
|
127
|
+
' <Button variant="tertiary">',
|
|
128
|
+
' <Icon name="date" screenReaderText="Jump to day" />',
|
|
129
|
+
' </Button>',
|
|
130
|
+
' )}',
|
|
131
|
+
'/>',
|
|
132
|
+
'```',
|
|
133
|
+
'',
|
|
134
|
+
'**Give the trigger an accessible name.** `DatePickerPopover` does not add one for you. A text',
|
|
135
|
+
'button is named by its text. An icon-only button needs `screenReaderText` on the `Icon` (or an',
|
|
136
|
+
'`aria-label` on the `Button`), otherwise screen reader users hear an unlabelled button.',
|
|
137
|
+
'',
|
|
138
|
+
'**The trigger must forward its ref and spread props onto a DOM node.** The design system `Button`',
|
|
139
|
+
'does. A custom component that does not will break popover anchoring and focus return to the trigger.',
|
|
140
|
+
'',
|
|
141
|
+
'**You own the value.** `onChange` receives a `Date`; store it and pass it back as `value`. If you',
|
|
142
|
+
'omit `value`, the calendar opens on the current month.',
|
|
143
|
+
'',
|
|
144
|
+
'**Time of day is kept.** Picking a day keeps the hours and minutes of the `value` you passed in.',
|
|
145
|
+
'Picking **Today** does the same.',
|
|
146
|
+
'',
|
|
147
|
+
'**Keyboard and focus.** Focus moves into the calendar when it opens. Selecting a day, choosing',
|
|
148
|
+
'**Today**, or pressing **Escape** closes the calendar and returns focus to your trigger.',
|
|
149
|
+
].join('\n');
|
|
150
|
+
|
|
107
151
|
const RELATED_COMPONENTS = [
|
|
108
152
|
'## Related components',
|
|
109
153
|
'',
|
|
@@ -130,6 +174,9 @@ function DatePickerDocsPage() {
|
|
|
130
174
|
<Markdown>{USAGE_GUIDANCE}</Markdown>
|
|
131
175
|
<DocHeading>Developer notes</DocHeading>
|
|
132
176
|
<Markdown>{DEVELOPER_NOTES}</Markdown>
|
|
177
|
+
<DocHeading>DatePickerPopover</DocHeading>
|
|
178
|
+
<Markdown>{POPOVER_NOTES}</Markdown>
|
|
179
|
+
<ArgTypes of={DatePickerPopover} />
|
|
133
180
|
<DocHeading>Examples</DocHeading>
|
|
134
181
|
<Stories title="" />
|
|
135
182
|
<Markdown>{RELATED_COMPONENTS}</Markdown>
|
|
@@ -820,3 +867,249 @@ export default ParentEveningForm;
|
|
|
820
867
|
'linked via `aria-describedby` automatically. Once a date is chosen, the error clears.',
|
|
821
868
|
].join(' '),
|
|
822
869
|
);
|
|
870
|
+
|
|
871
|
+
// ---------------------------------------------------------------------------
|
|
872
|
+
// DatePickerPopover stories
|
|
873
|
+
// ---------------------------------------------------------------------------
|
|
874
|
+
|
|
875
|
+
type PopoverStory = StoryObj<typeof DatePickerPopover>;
|
|
876
|
+
|
|
877
|
+
const withPopoverDescription = (story: PopoverStory, description: string): PopoverStory => ({
|
|
878
|
+
...story,
|
|
879
|
+
parameters: {
|
|
880
|
+
...story.parameters,
|
|
881
|
+
docs: {
|
|
882
|
+
...story.parameters?.docs,
|
|
883
|
+
description: {
|
|
884
|
+
story: description,
|
|
885
|
+
},
|
|
886
|
+
},
|
|
887
|
+
},
|
|
888
|
+
});
|
|
889
|
+
|
|
890
|
+
// Fixed date so the play functions are deterministic (15 June 2025, 14:30)
|
|
891
|
+
const POPOVER_INITIAL_DATE = new Date(2025, 5, 15, 14, 30);
|
|
892
|
+
|
|
893
|
+
const formatLongDate = (date: Date) => date.toLocaleDateString('en-GB', {
|
|
894
|
+
weekday: 'long',
|
|
895
|
+
day: 'numeric',
|
|
896
|
+
month: 'long',
|
|
897
|
+
year: 'numeric',
|
|
898
|
+
});
|
|
899
|
+
|
|
900
|
+
type PopoverTemplateProps = {
|
|
901
|
+
onChange?: (newDate: Date) => void;
|
|
902
|
+
};
|
|
903
|
+
|
|
904
|
+
const PopoverTextTriggerTemplate = ({ onChange }: PopoverTemplateProps) => {
|
|
905
|
+
const [date, setDate] = useState(POPOVER_INITIAL_DATE);
|
|
906
|
+
|
|
907
|
+
const handleChange = (newDate: Date) => {
|
|
908
|
+
setDate(newDate);
|
|
909
|
+
onChange?.(newDate);
|
|
910
|
+
};
|
|
911
|
+
|
|
912
|
+
return (
|
|
913
|
+
<div>
|
|
914
|
+
<DatePickerPopover
|
|
915
|
+
value={date}
|
|
916
|
+
onChange={handleChange}
|
|
917
|
+
trigger={<Button variant="secondary">Choose date</Button>}
|
|
918
|
+
/>
|
|
919
|
+
<p className="ds-text">
|
|
920
|
+
Selected:
|
|
921
|
+
{' '}
|
|
922
|
+
<code>{`${date.toLocaleDateString('en-GB')} ${format(date, 'HH:mm')}`}</code>
|
|
923
|
+
</p>
|
|
924
|
+
</div>
|
|
925
|
+
);
|
|
926
|
+
};
|
|
927
|
+
|
|
928
|
+
const PopoverIconTriggerTemplate = ({ onChange }: PopoverTemplateProps) => {
|
|
929
|
+
const [date, setDate] = useState(POPOVER_INITIAL_DATE);
|
|
930
|
+
|
|
931
|
+
const handleChange = (newDate: Date) => {
|
|
932
|
+
setDate(newDate);
|
|
933
|
+
onChange?.(newDate);
|
|
934
|
+
};
|
|
935
|
+
|
|
936
|
+
return (
|
|
937
|
+
<div>
|
|
938
|
+
<DatePickerPopover
|
|
939
|
+
value={date}
|
|
940
|
+
onChange={handleChange}
|
|
941
|
+
trigger={(
|
|
942
|
+
<Button variant="tertiary">
|
|
943
|
+
<Icon name="date" screenReaderText="Choose date" />
|
|
944
|
+
</Button>
|
|
945
|
+
)}
|
|
946
|
+
/>
|
|
947
|
+
<p className="ds-text">
|
|
948
|
+
Selected:
|
|
949
|
+
{' '}
|
|
950
|
+
<code>{date.toLocaleDateString('en-GB')}</code>
|
|
951
|
+
</p>
|
|
952
|
+
</div>
|
|
953
|
+
);
|
|
954
|
+
};
|
|
955
|
+
|
|
956
|
+
const PopoverCalendarHeaderTemplate = ({ onChange }: PopoverTemplateProps) => {
|
|
957
|
+
const [viewedDate, setViewedDate] = useState(POPOVER_INITIAL_DATE);
|
|
958
|
+
|
|
959
|
+
const handleChange = (newDate: Date) => {
|
|
960
|
+
setViewedDate(newDate);
|
|
961
|
+
onChange?.(newDate);
|
|
962
|
+
};
|
|
963
|
+
|
|
964
|
+
return (
|
|
965
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: 'var(--spacing-small)' }}>
|
|
966
|
+
<DatePickerPopover
|
|
967
|
+
value={viewedDate}
|
|
968
|
+
onChange={handleChange}
|
|
969
|
+
trigger={(
|
|
970
|
+
<Button variant="tertiary" borderless>
|
|
971
|
+
<Icon name="date" screenReaderText="Jump to day" />
|
|
972
|
+
</Button>
|
|
973
|
+
)}
|
|
974
|
+
/>
|
|
975
|
+
<p className="ds-text">
|
|
976
|
+
<time dateTime={format(viewedDate, 'yyyy-MM-dd')}>{formatLongDate(viewedDate)}</time>
|
|
977
|
+
</p>
|
|
978
|
+
</div>
|
|
979
|
+
);
|
|
980
|
+
};
|
|
981
|
+
|
|
982
|
+
export const PopoverWithTextTrigger: PopoverStory = withPopoverDescription(
|
|
983
|
+
{
|
|
984
|
+
args: { onChange: fn() },
|
|
985
|
+
render: args => <PopoverTextTriggerTemplate onChange={args.onChange} />,
|
|
986
|
+
parameters: {
|
|
987
|
+
controls: { disable: true },
|
|
988
|
+
docs: {
|
|
989
|
+
source: {
|
|
990
|
+
language: 'tsx',
|
|
991
|
+
code: `
|
|
992
|
+
import { useState } from 'react';
|
|
993
|
+
import { Button, DatePickerPopover } from '@arbor-education/design-system.components';
|
|
994
|
+
|
|
995
|
+
function ChooseDateButton() {
|
|
996
|
+
const [date, setDate] = useState(new Date(2025, 5, 15, 14, 30));
|
|
997
|
+
|
|
998
|
+
return (
|
|
999
|
+
<DatePickerPopover
|
|
1000
|
+
value={date}
|
|
1001
|
+
onChange={setDate}
|
|
1002
|
+
// A text button is named by its text content
|
|
1003
|
+
trigger={<Button variant="secondary">Choose date</Button>}
|
|
1004
|
+
/>
|
|
1005
|
+
);
|
|
1006
|
+
}
|
|
1007
|
+
export default ChooseDateButton;
|
|
1008
|
+
`.trim(),
|
|
1009
|
+
},
|
|
1010
|
+
},
|
|
1011
|
+
},
|
|
1012
|
+
},
|
|
1013
|
+
[
|
|
1014
|
+
'The simplest setup: a text `Button` as the trigger, with the date held in your own state.',
|
|
1015
|
+
'The calendar opens on the month of `value`. Selecting a day (or **Today**) calls `onChange` with a',
|
|
1016
|
+
'`Date`, keeps the time of day from `value`, closes the calendar and returns focus to the button.',
|
|
1017
|
+
'The button is named by its text, so no extra accessibility props are needed.',
|
|
1018
|
+
].join(' '),
|
|
1019
|
+
);
|
|
1020
|
+
|
|
1021
|
+
export const PopoverWithIconTrigger: PopoverStory = withPopoverDescription(
|
|
1022
|
+
{
|
|
1023
|
+
args: { onChange: fn() },
|
|
1024
|
+
render: args => <PopoverIconTriggerTemplate onChange={args.onChange} />,
|
|
1025
|
+
parameters: {
|
|
1026
|
+
controls: { disable: true },
|
|
1027
|
+
docs: {
|
|
1028
|
+
source: {
|
|
1029
|
+
language: 'tsx',
|
|
1030
|
+
code: `
|
|
1031
|
+
import { useState } from 'react';
|
|
1032
|
+
import { Button, DatePickerPopover, Icon } from '@arbor-education/design-system.components';
|
|
1033
|
+
|
|
1034
|
+
function ChooseDateIconButton() {
|
|
1035
|
+
const [date, setDate] = useState(new Date(2025, 5, 15, 14, 30));
|
|
1036
|
+
|
|
1037
|
+
return (
|
|
1038
|
+
<DatePickerPopover
|
|
1039
|
+
value={date}
|
|
1040
|
+
onChange={setDate}
|
|
1041
|
+
trigger={(
|
|
1042
|
+
<Button variant="tertiary">
|
|
1043
|
+
{/* screenReaderText is the button's accessible name */}
|
|
1044
|
+
<Icon name="date" screenReaderText="Choose date" />
|
|
1045
|
+
</Button>
|
|
1046
|
+
)}
|
|
1047
|
+
/>
|
|
1048
|
+
);
|
|
1049
|
+
}
|
|
1050
|
+
export default ChooseDateIconButton;
|
|
1051
|
+
`.trim(),
|
|
1052
|
+
},
|
|
1053
|
+
},
|
|
1054
|
+
},
|
|
1055
|
+
},
|
|
1056
|
+
[
|
|
1057
|
+
'An icon-only trigger. The `Icon` is hidden from assistive technology, so **you must give the button',
|
|
1058
|
+
'an accessible name**: use `screenReaderText` on the `Icon` (shown here) or `aria-label` on the `Button`.',
|
|
1059
|
+
'Keyboard users can open the calendar with Enter or Space, move between days with the arrow keys,',
|
|
1060
|
+
'and press **Escape** to close it without choosing a date. Focus returns to the icon button.',
|
|
1061
|
+
].join(' '),
|
|
1062
|
+
);
|
|
1063
|
+
|
|
1064
|
+
export const PopoverCalendarHeaderJumpToDay: PopoverStory = withPopoverDescription(
|
|
1065
|
+
{
|
|
1066
|
+
args: { onChange: fn() },
|
|
1067
|
+
render: args => <PopoverCalendarHeaderTemplate onChange={args.onChange} />,
|
|
1068
|
+
parameters: {
|
|
1069
|
+
controls: { disable: true },
|
|
1070
|
+
docs: {
|
|
1071
|
+
source: {
|
|
1072
|
+
language: 'tsx',
|
|
1073
|
+
code: `
|
|
1074
|
+
import { useState } from 'react';
|
|
1075
|
+
import { format } from 'date-fns';
|
|
1076
|
+
import { Button, DatePickerPopover, Icon } from '@arbor-education/design-system.components';
|
|
1077
|
+
|
|
1078
|
+
function CalendarHeader() {
|
|
1079
|
+
const [viewedDate, setViewedDate] = useState(new Date(2025, 5, 15, 14, 30));
|
|
1080
|
+
|
|
1081
|
+
return (
|
|
1082
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: 'var(--spacing-small)' }}>
|
|
1083
|
+
<DatePickerPopover
|
|
1084
|
+
value={viewedDate}
|
|
1085
|
+
onChange={setViewedDate}
|
|
1086
|
+
trigger={(
|
|
1087
|
+
<Button variant="tertiary" borderless>
|
|
1088
|
+
<Icon name="date" screenReaderText="Jump to day" />
|
|
1089
|
+
</Button>
|
|
1090
|
+
)}
|
|
1091
|
+
/>
|
|
1092
|
+
<time dateTime={format(viewedDate, 'yyyy-MM-dd')}>
|
|
1093
|
+
{viewedDate.toLocaleDateString('en-GB', {
|
|
1094
|
+
weekday: 'long',
|
|
1095
|
+
day: 'numeric',
|
|
1096
|
+
month: 'long',
|
|
1097
|
+
year: 'numeric',
|
|
1098
|
+
})}
|
|
1099
|
+
</time>
|
|
1100
|
+
</div>
|
|
1101
|
+
);
|
|
1102
|
+
}
|
|
1103
|
+
export default CalendarHeader;
|
|
1104
|
+
`.trim(),
|
|
1105
|
+
},
|
|
1106
|
+
},
|
|
1107
|
+
},
|
|
1108
|
+
},
|
|
1109
|
+
[
|
|
1110
|
+
'The motivating use case: a "jump to day" icon button in a calendar header, with the currently viewed',
|
|
1111
|
+
'date shown beside it. The date lives in your state, so the heading updates as soon as a day is picked',
|
|
1112
|
+
'and the same value is passed back in as `value` the next time the calendar opens.',
|
|
1113
|
+
'There is no date input, so the header stays compact and the selection is communicated by the text next to the button.',
|
|
1114
|
+
].join(' '),
|
|
1115
|
+
);
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { describe, expect, test, vi } from 'vitest';
|
|
2
|
+
import { render, screen, within } from '@testing-library/react';
|
|
3
|
+
import userEvent from '@testing-library/user-event';
|
|
4
|
+
import '@testing-library/jest-dom/vitest';
|
|
5
|
+
import { Button } from 'Components/button/Button';
|
|
6
|
+
import { DatePickerPopover } from './DatePickerPopover.js';
|
|
7
|
+
|
|
8
|
+
const renderPopover = (props: Partial<React.ComponentProps<typeof DatePickerPopover>> = {}) => render(
|
|
9
|
+
<DatePickerPopover
|
|
10
|
+
trigger={<Button>Jump to date</Button>}
|
|
11
|
+
value={new Date(2025, 5, 15, 14, 30)}
|
|
12
|
+
{...props}
|
|
13
|
+
/>,
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
const openCalendar = () => userEvent.click(screen.getByRole('button', { name: 'Jump to date' }));
|
|
17
|
+
|
|
18
|
+
describe('DatePickerPopover', () => {
|
|
19
|
+
test('renders the consumer trigger and no date input', () => {
|
|
20
|
+
const { container } = renderPopover();
|
|
21
|
+
expect(screen.getByRole('button', { name: 'Jump to date' })).toBeInTheDocument();
|
|
22
|
+
expect(container.querySelector('input')).not.toBeInTheDocument();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test('calendar is hidden until the trigger is activated', async () => {
|
|
26
|
+
renderPopover();
|
|
27
|
+
expect(screen.queryByRole('application')).not.toBeInTheDocument();
|
|
28
|
+
|
|
29
|
+
await openCalendar();
|
|
30
|
+
|
|
31
|
+
expect(screen.getByRole('application')).toBeInTheDocument();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test('opens on the month of the supplied value', async () => {
|
|
35
|
+
renderPopover();
|
|
36
|
+
await openCalendar();
|
|
37
|
+
|
|
38
|
+
expect(within(screen.getByRole('application')).getByRole('button', { name: /June 20/ })).toBeInTheDocument();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test('selecting a day calls onChange with a Date and closes the calendar', async () => {
|
|
42
|
+
const onChange = vi.fn();
|
|
43
|
+
renderPopover({ onChange });
|
|
44
|
+
await openCalendar();
|
|
45
|
+
|
|
46
|
+
await userEvent.click(within(screen.getByRole('application')).getByRole('button', { name: /June 20/ }));
|
|
47
|
+
|
|
48
|
+
expect(onChange).toHaveBeenCalledTimes(1);
|
|
49
|
+
const selected = onChange.mock.lastCall?.[0] as Date;
|
|
50
|
+
expect(selected).toBeInstanceOf(Date);
|
|
51
|
+
expect([selected.getFullYear(), selected.getMonth(), selected.getDate()]).toEqual([2025, 5, 20]);
|
|
52
|
+
expect(screen.queryByRole('application')).not.toBeInTheDocument();
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test('selecting a day preserves the time of the supplied value', async () => {
|
|
56
|
+
const onChange = vi.fn();
|
|
57
|
+
renderPopover({ onChange });
|
|
58
|
+
await openCalendar();
|
|
59
|
+
|
|
60
|
+
await userEvent.click(within(screen.getByRole('application')).getByRole('button', { name: /June 20/ }));
|
|
61
|
+
|
|
62
|
+
const selected = onChange.mock.lastCall?.[0] as Date;
|
|
63
|
+
expect([selected.getHours(), selected.getMinutes()]).toEqual([14, 30]);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test('clicking the already selected day still calls onChange and closes the calendar', async () => {
|
|
67
|
+
const onChange = vi.fn();
|
|
68
|
+
renderPopover({ onChange });
|
|
69
|
+
await openCalendar();
|
|
70
|
+
|
|
71
|
+
await userEvent.click(within(screen.getByRole('application')).getByRole('button', { name: /June 15/ }));
|
|
72
|
+
|
|
73
|
+
expect(onChange).toHaveBeenCalledTimes(1);
|
|
74
|
+
expect(onChange.mock.lastCall?.[0]).toBeInstanceOf(Date);
|
|
75
|
+
expect(screen.queryByRole('application')).not.toBeInTheDocument();
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test('clicking Today calls onChange with today and closes the calendar', async () => {
|
|
79
|
+
const onChange = vi.fn();
|
|
80
|
+
renderPopover({ onChange });
|
|
81
|
+
await openCalendar();
|
|
82
|
+
|
|
83
|
+
await userEvent.click(screen.getByRole('button', { name: 'Today' }));
|
|
84
|
+
|
|
85
|
+
const today = new Date();
|
|
86
|
+
const selected = onChange.mock.lastCall?.[0] as Date;
|
|
87
|
+
expect([selected.getFullYear(), selected.getMonth(), selected.getDate()])
|
|
88
|
+
.toEqual([today.getFullYear(), today.getMonth(), today.getDate()]);
|
|
89
|
+
expect(screen.queryByRole('application')).not.toBeInTheDocument();
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test('works without a value, opening on the current month', async () => {
|
|
93
|
+
const onChange = vi.fn();
|
|
94
|
+
renderPopover({ value: undefined, onChange });
|
|
95
|
+
await openCalendar();
|
|
96
|
+
|
|
97
|
+
await userEvent.click(screen.getByRole('button', { name: 'Today' }));
|
|
98
|
+
|
|
99
|
+
expect(onChange).toHaveBeenCalledWith(expect.any(Date));
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test('focus moves into the calendar on open', async () => {
|
|
103
|
+
renderPopover();
|
|
104
|
+
await openCalendar();
|
|
105
|
+
|
|
106
|
+
expect(screen.getByRole('application')).toContainElement(document.activeElement as HTMLElement);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test('Escape closes the calendar and returns focus to the trigger', async () => {
|
|
110
|
+
renderPopover();
|
|
111
|
+
await openCalendar();
|
|
112
|
+
|
|
113
|
+
await userEvent.keyboard('{Escape}');
|
|
114
|
+
|
|
115
|
+
expect(screen.queryByRole('application')).not.toBeInTheDocument();
|
|
116
|
+
expect(screen.getByRole('button', { name: 'Jump to date' })).toHaveFocus();
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test('selecting a day returns focus to the trigger', async () => {
|
|
120
|
+
renderPopover();
|
|
121
|
+
await openCalendar();
|
|
122
|
+
|
|
123
|
+
await userEvent.click(within(screen.getByRole('application')).getByRole('button', { name: /June 20/ }));
|
|
124
|
+
|
|
125
|
+
expect(screen.getByRole('button', { name: 'Jump to date' })).toHaveFocus();
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test('reopening shows the month of the latest value after the user browsed away', async () => {
|
|
129
|
+
const { rerender } = renderPopover();
|
|
130
|
+
await openCalendar();
|
|
131
|
+
await userEvent.click(screen.getByRole('button', { name: 'Go to the Next Month' }));
|
|
132
|
+
await userEvent.keyboard('{Escape}');
|
|
133
|
+
|
|
134
|
+
rerender(
|
|
135
|
+
<DatePickerPopover
|
|
136
|
+
trigger={<Button>Jump to date</Button>}
|
|
137
|
+
value={new Date(2025, 8, 3)}
|
|
138
|
+
/>,
|
|
139
|
+
);
|
|
140
|
+
await openCalendar();
|
|
141
|
+
|
|
142
|
+
expect(within(screen.getByRole('application')).getByRole('button', { name: /September 10/ })).toBeInTheDocument();
|
|
143
|
+
});
|
|
144
|
+
});
|