@arbor-education/design-system.components 0.24.3 → 0.25.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/.gather/instructions/changesets.md +12 -0
- package/.gather/instructions/read-contributing.md +14 -0
- package/.storybook/DocsTemplate.tsx +3 -1
- package/.storybook/preview.ts +6 -2
- package/CHANGELOG.md +12 -0
- package/CONTRIBUTING.md +20 -0
- package/dist/components/avatarGroup/AvatarGroup.stories.d.ts.map +1 -1
- package/dist/components/avatarGroup/AvatarGroup.stories.js +4 -2
- package/dist/components/avatarGroup/AvatarGroup.stories.js.map +1 -1
- package/dist/components/card/Card.d.ts.map +1 -1
- package/dist/components/card/Card.js +2 -1
- package/dist/components/card/Card.js.map +1 -1
- package/dist/components/combobox/Combobox.d.ts.map +1 -1
- package/dist/components/combobox/Combobox.js +10 -5
- package/dist/components/combobox/Combobox.js.map +1 -1
- package/dist/components/combobox/Combobox.stories.d.ts.map +1 -1
- package/dist/components/combobox/Combobox.stories.js +2 -1
- package/dist/components/combobox/Combobox.stories.js.map +1 -1
- package/dist/components/combobox/Combobox.test.js +18 -9
- package/dist/components/combobox/Combobox.test.js.map +1 -1
- package/dist/components/combobox/ComboboxButtonTrigger.d.ts.map +1 -1
- package/dist/components/combobox/ComboboxButtonTrigger.js +3 -1
- package/dist/components/combobox/ComboboxButtonTrigger.js.map +1 -1
- package/dist/components/combobox/ComboboxListbox.d.ts.map +1 -1
- package/dist/components/combobox/ComboboxListbox.js +8 -4
- package/dist/components/combobox/ComboboxListbox.js.map +1 -1
- package/dist/components/combobox/buildListboxDisplayOptions.d.ts.map +1 -1
- package/dist/components/combobox/buildListboxDisplayOptions.js +4 -2
- package/dist/components/combobox/buildListboxDisplayOptions.js.map +1 -1
- package/dist/components/combobox/highlightLabel.d.ts.map +1 -1
- package/dist/components/combobox/highlightLabel.js +2 -1
- package/dist/components/combobox/highlightLabel.js.map +1 -1
- package/dist/components/combobox/useChipSelection.d.ts.map +1 -1
- package/dist/components/combobox/useChipSelection.js +2 -1
- package/dist/components/combobox/useChipSelection.js.map +1 -1
- package/dist/components/combobox/useComboboxListboxKeyboard.d.ts.map +1 -1
- package/dist/components/combobox/useComboboxListboxKeyboard.js +8 -4
- package/dist/components/combobox/useComboboxListboxKeyboard.js.map +1 -1
- package/dist/components/combobox/useComboboxPopoverBehavior.d.ts.map +1 -1
- package/dist/components/combobox/useComboboxPopoverBehavior.js +16 -8
- 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 +22 -11
- package/dist/components/combobox/useComboboxState.js.map +1 -1
- package/dist/components/datePicker/DatePickerCalendarHeader.d.ts.map +1 -1
- package/dist/components/datePicker/DatePickerCalendarHeader.js +4 -2
- package/dist/components/datePicker/DatePickerCalendarHeader.js.map +1 -1
- package/dist/components/dateTimePicker/DateTimePicker.d.ts.map +1 -1
- package/dist/components/dateTimePicker/DateTimePicker.js +2 -1
- package/dist/components/dateTimePicker/DateTimePicker.js.map +1 -1
- package/dist/components/dateTimePicker/DateTimePicker.test.js.map +1 -1
- package/dist/components/dropdown/Dropdown.stories.d.ts.map +1 -1
- package/dist/components/dropdown/Dropdown.stories.js +10 -5
- package/dist/components/dropdown/Dropdown.stories.js.map +1 -1
- package/dist/components/filterBar/FilterBar.d.ts.map +1 -1
- package/dist/components/filterBar/FilterBar.js +4 -2
- package/dist/components/filterBar/FilterBar.js.map +1 -1
- package/dist/components/filterBar/FilterBar.stories.d.ts.map +1 -1
- package/dist/components/filterBar/FilterBar.stories.js +6 -3
- package/dist/components/filterBar/FilterBar.stories.js.map +1 -1
- package/dist/components/filterBar/FilterBar.test.js +6 -3
- package/dist/components/filterBar/FilterBar.test.js.map +1 -1
- package/dist/components/formField/fieldset/Fieldset.stories.d.ts.map +1 -1
- package/dist/components/formField/fieldset/Fieldset.stories.js.map +1 -1
- package/dist/components/formField/inputs/radio/RadioButtonGroup.d.ts +1 -1
- package/dist/components/formField/inputs/radio/RadioButtonGroup.d.ts.map +1 -1
- package/dist/components/formField/inputs/radio/RadioButtonGroup.js +1 -1
- package/dist/components/formField/inputs/radio/RadioButtonGroup.js.map +1 -1
- package/dist/components/formField/inputs/selectDropdown/SelectDropdown.d.ts.map +1 -1
- package/dist/components/formField/inputs/selectDropdown/SelectDropdown.js +2 -1
- package/dist/components/formField/inputs/selectDropdown/SelectDropdown.js.map +1 -1
- package/dist/components/formField/inputs/textArea/TextArea.stories.d.ts.map +1 -1
- package/dist/components/formField/inputs/textArea/TextArea.stories.js +10 -5
- package/dist/components/formField/inputs/textArea/TextArea.stories.js.map +1 -1
- package/dist/components/formField/inputs/time/TimeInput.js +3 -4
- package/dist/components/formField/inputs/time/TimeInput.js.map +1 -1
- package/dist/components/formField/inputs/time/TimeInput.test.js.map +1 -1
- package/dist/components/heading/Heading.stories.d.ts.map +1 -1
- package/dist/components/heading/Heading.stories.js.map +1 -1
- package/dist/components/modal/Modal.stories.d.ts.map +1 -1
- package/dist/components/modal/Modal.stories.js.map +1 -1
- package/dist/components/progress/Progress.stories.d.ts.map +1 -1
- package/dist/components/progress/Progress.stories.js +2 -1
- package/dist/components/progress/Progress.stories.js.map +1 -1
- package/dist/components/section/Section.d.ts +3 -6
- package/dist/components/section/Section.d.ts.map +1 -1
- package/dist/components/section/Section.js +2 -2
- package/dist/components/section/Section.js.map +1 -1
- package/dist/components/section/Section.stories.d.ts +1 -0
- package/dist/components/section/Section.stories.d.ts.map +1 -1
- package/dist/components/section/Section.stories.js +125 -52
- package/dist/components/section/Section.stories.js.map +1 -1
- package/dist/components/section/Section.test.js +13 -2
- package/dist/components/section/Section.test.js.map +1 -1
- package/dist/components/sidebarNav/SidebarNavItem.d.ts.map +1 -1
- package/dist/components/sidebarNav/SidebarNavItem.js +12 -11
- package/dist/components/sidebarNav/SidebarNavItem.js.map +1 -1
- package/dist/components/sidebarNav/SidebarNavRailItem.d.ts.map +1 -1
- package/dist/components/sidebarNav/SidebarNavRailItem.js +11 -10
- package/dist/components/sidebarNav/SidebarNavRailItem.js.map +1 -1
- package/dist/components/sidebarNav/useControllableBoolean.d.ts.map +1 -1
- package/dist/components/sidebarNav/useControllableBoolean.js +2 -1
- package/dist/components/sidebarNav/useControllableBoolean.js.map +1 -1
- package/dist/components/table/Table.d.ts.map +1 -1
- package/dist/components/table/Table.js +11 -1
- package/dist/components/table/Table.js.map +1 -1
- package/dist/components/table/Table.stories.d.ts.map +1 -1
- package/dist/components/table/Table.stories.js +6 -2
- package/dist/components/table/Table.stories.js.map +1 -1
- package/dist/components/table/Table.test.js +9 -3
- package/dist/components/table/Table.test.js.map +1 -1
- package/dist/components/table/cellRenderers/ComboboxCellRenderer.d.ts.map +1 -1
- package/dist/components/table/cellRenderers/ComboboxCellRenderer.js +16 -8
- package/dist/components/table/cellRenderers/ComboboxCellRenderer.js.map +1 -1
- package/dist/components/table/columnFilters/TimeFilter/TimeFilter.d.ts.map +1 -1
- package/dist/components/table/columnFilters/TimeFilter/TimeFilter.js +2 -1
- package/dist/components/table/columnFilters/TimeFilter/TimeFilter.js.map +1 -1
- package/dist/components/table/tableControls/HideColumnsDropdown.d.ts.map +1 -1
- package/dist/components/table/tableControls/HideColumnsDropdown.js +2 -1
- package/dist/components/table/tableControls/HideColumnsDropdown.js.map +1 -1
- package/dist/components/table/tableControls/TableControls.d.ts.map +1 -1
- package/dist/components/table/tableControls/TableControls.js +2 -1
- package/dist/components/table/tableControls/TableControls.js.map +1 -1
- package/dist/components/tag/Tag.test.js.map +1 -1
- package/dist/components/tagList/TagList.d.ts.map +1 -1
- package/dist/components/tagList/TagList.js +8 -4
- package/dist/components/tagList/TagList.js.map +1 -1
- package/dist/components/tagList/TagList.test.js +6 -3
- package/dist/components/tagList/TagList.test.js.map +1 -1
- package/dist/components/treeRow/TreeRowSection.d.ts.map +1 -1
- package/dist/components/treeRow/TreeRowSection.js +2 -1
- package/dist/components/treeRow/TreeRowSection.js.map +1 -1
- package/dist/components/userDropdown/UserDropdown.stories.d.ts.map +1 -1
- package/dist/components/userDropdown/UserDropdown.stories.js +66 -22
- package/dist/components/userDropdown/UserDropdown.stories.js.map +1 -1
- package/dist/components/userDropdown/internal/UserDropdownCollapsibleSection.d.ts.map +1 -1
- package/dist/components/userDropdown/internal/UserDropdownCollapsibleSection.js +3 -1
- package/dist/components/userDropdown/internal/UserDropdownCollapsibleSection.js.map +1 -1
- package/dist/utils/getTextContrast.d.ts.map +1 -1
- package/dist/utils/getTextContrast.js +4 -2
- package/dist/utils/getTextContrast.js.map +1 -1
- package/dist/utils/hooks/useElementWidth.d.ts.map +1 -1
- package/dist/utils/hooks/useElementWidth.js +2 -1
- package/dist/utils/hooks/useElementWidth.js.map +1 -1
- package/dist/utils/hooks/useMeasuredChildWidths.d.ts.map +1 -1
- package/dist/utils/hooks/useMeasuredChildWidths.js +4 -2
- package/dist/utils/hooks/useMeasuredChildWidths.js.map +1 -1
- package/dist/utils/hooks/useRovingFocus.d.ts.map +1 -1
- package/dist/utils/hooks/useRovingFocus.js +8 -4
- package/dist/utils/hooks/useRovingFocus.js.map +1 -1
- package/dist/utils/spacedWidths.d.ts.map +1 -1
- package/dist/utils/spacedWidths.js +6 -3
- package/dist/utils/spacedWidths.js.map +1 -1
- package/eslint.config.mts +32 -1
- package/package.json +1 -1
- package/src/components/avatarGroup/AvatarGroup.stories.tsx +6 -2
- package/src/components/card/Card.tsx +3 -1
- package/src/components/combobox/Combobox.stories.tsx +3 -1
- package/src/components/combobox/Combobox.test.tsx +39 -11
- package/src/components/combobox/Combobox.tsx +15 -5
- package/src/components/combobox/ComboboxButtonTrigger.tsx +3 -1
- package/src/components/combobox/ComboboxListbox.tsx +12 -4
- package/src/components/combobox/buildListboxDisplayOptions.ts +6 -2
- package/src/components/combobox/highlightLabel.tsx +2 -1
- package/src/components/combobox/useChipSelection.ts +3 -1
- package/src/components/combobox/useComboboxListboxKeyboard.ts +12 -4
- package/src/components/combobox/useComboboxPopoverBehavior.ts +24 -8
- package/src/components/combobox/useComboboxState.ts +28 -11
- package/src/components/datePicker/DatePickerCalendarHeader.tsx +6 -2
- package/src/components/dateTimePicker/DateTimePicker.test.tsx +2 -0
- package/src/components/dateTimePicker/DateTimePicker.tsx +3 -1
- package/src/components/dropdown/Dropdown.stories.tsx +19 -8
- package/src/components/filterBar/FilterBar.stories.tsx +9 -3
- package/src/components/filterBar/FilterBar.test.tsx +9 -3
- package/src/components/filterBar/FilterBar.tsx +6 -2
- package/src/components/formField/fieldset/Fieldset.stories.tsx +18 -3
- package/src/components/formField/inputs/radio/RadioButtonGroup.tsx +1 -1
- package/src/components/formField/inputs/selectDropdown/SelectDropdown.tsx +3 -1
- package/src/components/formField/inputs/textArea/TextArea.stories.tsx +10 -5
- package/src/components/formField/inputs/time/TimeInput.test.tsx +2 -0
- package/src/components/formField/inputs/time/TimeInput.tsx +4 -4
- package/src/components/heading/Heading.stories.tsx +12 -3
- package/src/components/modal/Modal.stories.tsx +15 -5
- package/src/components/progress/Progress.stories.tsx +3 -1
- package/src/components/section/Section.stories.tsx +198 -70
- package/src/components/section/Section.test.tsx +14 -8
- package/src/components/section/Section.tsx +7 -15
- package/src/components/sidebarNav/SidebarNavItem.tsx +27 -25
- package/src/components/sidebarNav/SidebarNavRailItem.tsx +34 -35
- package/src/components/sidebarNav/useControllableBoolean.ts +3 -1
- package/src/components/table/Table.stories.tsx +16 -12
- package/src/components/table/Table.test.tsx +10 -4
- package/src/components/table/Table.tsx +15 -5
- package/src/components/table/cellRenderers/ComboboxCellRenderer.tsx +19 -8
- package/src/components/table/columnFilters/TimeFilter/TimeFilter.tsx +3 -1
- package/src/components/table/tableControls/HideColumnsDropdown.tsx +3 -1
- package/src/components/table/tableControls/TableControls.tsx +3 -1
- package/src/components/tag/Tag.test.tsx +40 -8
- package/src/components/tagList/TagList.test.tsx +9 -3
- package/src/components/tagList/TagList.tsx +12 -4
- package/src/components/treeRow/TreeRowSection.tsx +3 -1
- package/src/components/userDropdown/UserDropdown.stories.tsx +66 -22
- package/src/components/userDropdown/internal/UserDropdownCollapsibleSection.tsx +3 -1
- package/src/utils/getTextContrast.ts +6 -2
- package/src/utils/hooks/useElementWidth.ts +3 -1
- package/src/utils/hooks/useMeasuredChildWidths.ts +6 -2
- package/src/utils/hooks/useRovingFocus.ts +12 -4
- package/src/utils/spacedWidths.ts +9 -3
|
@@ -122,9 +122,15 @@ describe('FilterBar', () => {
|
|
|
122
122
|
const rectSpy = vi.spyOn(HTMLElement.prototype, 'getBoundingClientRect').mockImplementation(function mockRect(this: HTMLElement) {
|
|
123
123
|
let width = 0;
|
|
124
124
|
|
|
125
|
-
if (this.classList.contains('ds-tag-list__viewport'))
|
|
126
|
-
|
|
127
|
-
|
|
125
|
+
if (this.classList.contains('ds-tag-list__viewport')) {
|
|
126
|
+
width = 220;
|
|
127
|
+
}
|
|
128
|
+
else if (this.classList.contains('ds-tag-list__item')) {
|
|
129
|
+
width = 84;
|
|
130
|
+
}
|
|
131
|
+
else if (this.classList.contains('ds-tag-list__overflow')) {
|
|
132
|
+
width = 76;
|
|
133
|
+
}
|
|
128
134
|
|
|
129
135
|
return {
|
|
130
136
|
width,
|
|
@@ -73,7 +73,9 @@ const FILTER_BAR_TYPE_ICONS: Record<FilterBarType, IconName> = {
|
|
|
73
73
|
};
|
|
74
74
|
|
|
75
75
|
const getVisibleCount = (count?: number | null): string | null => {
|
|
76
|
-
if (typeof count !== 'number' || count <= 0)
|
|
76
|
+
if (typeof count !== 'number' || count <= 0) {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
77
79
|
return count > 99 ? '99+' : `${count}`;
|
|
78
80
|
};
|
|
79
81
|
|
|
@@ -267,7 +269,9 @@ const FilterBarActiveList = ({
|
|
|
267
269
|
}
|
|
268
270
|
|
|
269
271
|
if (!children) {
|
|
270
|
-
if (!emptyState)
|
|
272
|
+
if (!emptyState) {
|
|
273
|
+
return null;
|
|
274
|
+
}
|
|
271
275
|
return <div className={classNames('ds-filter-bar__empty-state', className)}>{emptyState}</div>;
|
|
272
276
|
}
|
|
273
277
|
|
|
@@ -320,9 +320,24 @@ export const Default: Story = {
|
|
|
320
320
|
},
|
|
321
321
|
render: args => (
|
|
322
322
|
<Fieldset {...args}>
|
|
323
|
-
<CheckboxInput
|
|
324
|
-
|
|
325
|
-
|
|
323
|
+
<CheckboxInput
|
|
324
|
+
id="consent-trips"
|
|
325
|
+
name="consent"
|
|
326
|
+
label="Consent for school trips"
|
|
327
|
+
onChange={() => {}}
|
|
328
|
+
/>
|
|
329
|
+
<CheckboxInput
|
|
330
|
+
id="consent-photos"
|
|
331
|
+
name="consent"
|
|
332
|
+
label="Consent for photographs"
|
|
333
|
+
onChange={() => {}}
|
|
334
|
+
/>
|
|
335
|
+
<CheckboxInput
|
|
336
|
+
id="consent-data"
|
|
337
|
+
name="consent"
|
|
338
|
+
label="Consent for data sharing with partner schools"
|
|
339
|
+
onChange={() => {}}
|
|
340
|
+
/>
|
|
326
341
|
</Fieldset>
|
|
327
342
|
),
|
|
328
343
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ChangeEvent } from 'react';
|
|
2
2
|
import { RadioButtonInput } from './RadioButtonInput.js';
|
|
3
|
-
import { Fieldset, type FieldsetProps } from '
|
|
3
|
+
import { Fieldset, type FieldsetProps } from 'Components/formField/fieldset/Fieldset.js';
|
|
4
4
|
|
|
5
5
|
export type RadioGroupOption = {
|
|
6
6
|
id: string;
|
|
@@ -72,7 +72,9 @@ export const SelectDropdown = (props: SelectDropdownInputProps) => {
|
|
|
72
72
|
const UNGROUPED_KEY = 'Ungrouped';
|
|
73
73
|
const groupedOptions = options.reduce((acc, option) => {
|
|
74
74
|
const key = option.group ?? UNGROUPED_KEY;
|
|
75
|
-
if (!acc[key])
|
|
75
|
+
if (!acc[key]) {
|
|
76
|
+
acc[key] = [];
|
|
77
|
+
}
|
|
76
78
|
acc[key].push(option);
|
|
77
79
|
return acc;
|
|
78
80
|
}, {} as Record<string, SelectDropdownItemProps[]>);
|
|
@@ -353,11 +353,16 @@ const ControlledWithCharacterCountTemplate = () => {
|
|
|
353
353
|
const isApproaching = remaining <= 100 && remaining > 0;
|
|
354
354
|
const isAtLimit = remaining <= 0;
|
|
355
355
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
356
|
+
let countColour: string;
|
|
357
|
+
if (isAtLimit) {
|
|
358
|
+
countColour = 'var(--color-semantic-destructive-600)';
|
|
359
|
+
}
|
|
360
|
+
else if (isApproaching) {
|
|
361
|
+
countColour = 'var(--color-semantic-caution-600)';
|
|
362
|
+
}
|
|
363
|
+
else {
|
|
364
|
+
countColour = 'var(--color-grey-600)';
|
|
365
|
+
}
|
|
361
366
|
|
|
362
367
|
return (
|
|
363
368
|
<div style={{ maxWidth: '28rem', padding: 'var(--spacing-xlarge)', display: 'flex', flexDirection: 'column', gap: 'var(--spacing-xsmall)' }}>
|
|
@@ -82,16 +82,16 @@ export const TimeInput = forwardRef<HTMLInputElement, TimeInputProps>((props, re
|
|
|
82
82
|
|
|
83
83
|
const focusAndSelectHours = useCallback(() => {
|
|
84
84
|
const input = inputRef.current;
|
|
85
|
-
if (!input)
|
|
85
|
+
if (!input) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
86
88
|
|
|
87
89
|
input.focus();
|
|
88
90
|
|
|
89
91
|
try {
|
|
90
92
|
input.setSelectionRange(0, 2);
|
|
91
93
|
}
|
|
92
|
-
catch {
|
|
93
|
-
// Some browsers do not expose selection APIs on time inputs.
|
|
94
|
-
}
|
|
94
|
+
catch { /* Some browsers do not expose selection APIs on time inputs. */ }
|
|
95
95
|
}, []);
|
|
96
96
|
|
|
97
97
|
const timeOptions = useMemo<ComboboxOption[]>(
|
|
@@ -368,7 +368,10 @@ const WithBackControlLegacyTemplate = () => (
|
|
|
368
368
|
const WithEditableTitleTemplate = () => (
|
|
369
369
|
<div style={{ padding: 'var(--spacing-xlarge)' }}>
|
|
370
370
|
<Heading level={1}>
|
|
371
|
-
<EditableText
|
|
371
|
+
<EditableText
|
|
372
|
+
text="Form 7B Registration"
|
|
373
|
+
onEditSave={() => {}}
|
|
374
|
+
/>
|
|
372
375
|
</Heading>
|
|
373
376
|
</div>
|
|
374
377
|
);
|
|
@@ -377,7 +380,10 @@ const WithEditableTitleAndActionTemplate = () => (
|
|
|
377
380
|
<div style={{ padding: 'var(--spacing-xlarge)' }}>
|
|
378
381
|
<Heading level={1}>
|
|
379
382
|
<Heading.InnerContainer>
|
|
380
|
-
<EditableText
|
|
383
|
+
<EditableText
|
|
384
|
+
text="Form 7B Registration"
|
|
385
|
+
onEditSave={() => {}}
|
|
386
|
+
/>
|
|
381
387
|
</Heading.InnerContainer>
|
|
382
388
|
<Heading.InnerContainer>
|
|
383
389
|
<Button variant="primary" type="button">Save changes</Button>
|
|
@@ -393,7 +399,10 @@ const WithEditableTitleAndMultipleActionsTemplate = () => (
|
|
|
393
399
|
<Button variant="tertiary" type="button" iconLeftName="chevron-left" iconLeftScreenReaderText="Go back">
|
|
394
400
|
Back
|
|
395
401
|
</Button>
|
|
396
|
-
<EditableText
|
|
402
|
+
<EditableText
|
|
403
|
+
text="Autumn term report"
|
|
404
|
+
onEditSave={() => {}}
|
|
405
|
+
/>
|
|
397
406
|
</Heading.InnerContainer>
|
|
398
407
|
<Heading.InnerContainer className="medium-spacing-gap">
|
|
399
408
|
<Button variant="secondary" type="button">Export to CSV</Button>
|
|
@@ -548,11 +548,21 @@ const WithDropdownInsideModalTemplate = () => {
|
|
|
548
548
|
<Button variant="dropdown">Select a teacher</Button>
|
|
549
549
|
</Dropdown.Trigger>
|
|
550
550
|
<Dropdown.Content>
|
|
551
|
-
<Dropdown.Item onSelect={() => {}}>
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
<Dropdown.Item onSelect={() => {}}>
|
|
555
|
-
|
|
551
|
+
<Dropdown.Item onSelect={() => {}}>
|
|
552
|
+
Ms A. Johnson — English
|
|
553
|
+
</Dropdown.Item>
|
|
554
|
+
<Dropdown.Item onSelect={() => {}}>
|
|
555
|
+
Mr B. Patel — Mathematics
|
|
556
|
+
</Dropdown.Item>
|
|
557
|
+
<Dropdown.Item onSelect={() => {}}>
|
|
558
|
+
Mrs C. Williams — Science
|
|
559
|
+
</Dropdown.Item>
|
|
560
|
+
<Dropdown.Item onSelect={() => {}}>
|
|
561
|
+
Mr D. Thompson — History
|
|
562
|
+
</Dropdown.Item>
|
|
563
|
+
<Dropdown.Item onSelect={() => {}}>
|
|
564
|
+
Ms E. Okonkwo — Art
|
|
565
|
+
</Dropdown.Item>
|
|
556
566
|
</Dropdown.Content>
|
|
557
567
|
</Dropdown>
|
|
558
568
|
</Modal.Body>
|
|
@@ -274,7 +274,9 @@ const InteractiveLoadingTemplate = (args: React.ComponentProps<typeof Progress>)
|
|
|
274
274
|
const [running, setRunning] = useState(true);
|
|
275
275
|
|
|
276
276
|
useEffect(() => {
|
|
277
|
-
if (!running)
|
|
277
|
+
if (!running) {
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
278
280
|
if (progress >= 100) {
|
|
279
281
|
setRunning(false);
|
|
280
282
|
return;
|
|
@@ -66,11 +66,11 @@ const DEVELOPER_NOTES = [
|
|
|
66
66
|
'',
|
|
67
67
|
'---',
|
|
68
68
|
'',
|
|
69
|
-
'###
|
|
69
|
+
'### Pass all button props via `headerButtonProps`',
|
|
70
70
|
'',
|
|
71
|
-
'The action button only renders when `
|
|
72
|
-
'
|
|
73
|
-
'
|
|
71
|
+
'The action button only renders when `headerButtonProps` is provided.',
|
|
72
|
+
'Pass a single `headerButtonProps` object containing at minimum `children` (button label) and `onClick`.',
|
|
73
|
+
'Always include `variant` and `size` to avoid relying on `Button` defaults, which may not look right in the heading context.',
|
|
74
74
|
'',
|
|
75
75
|
'---',
|
|
76
76
|
'',
|
|
@@ -213,41 +213,16 @@ const meta: Meta<typeof Section> = {
|
|
|
213
213
|
defaultValue: { summary: 'false' },
|
|
214
214
|
},
|
|
215
215
|
},
|
|
216
|
-
|
|
216
|
+
headerButtonProps: {
|
|
217
217
|
description: [
|
|
218
|
-
'
|
|
219
|
-
'
|
|
218
|
+
'Props forwarded directly to the `Button` component rendered in the section heading.',
|
|
219
|
+
'Omit this prop entirely to render no button.',
|
|
220
|
+
'When provided, always include `children` (button label), `onClick`, `variant`, and `size`',
|
|
221
|
+
"to avoid unexpected visual results from `Button` defaults. Use `size: 'S'` to keep it lightweight alongside the title.",
|
|
220
222
|
].join(' '),
|
|
221
|
-
control: 'text',
|
|
222
|
-
table: {
|
|
223
|
-
type: { summary: 'string' },
|
|
224
|
-
defaultValue: { summary: 'undefined' },
|
|
225
|
-
},
|
|
226
|
-
},
|
|
227
|
-
buttonOnClick: {
|
|
228
|
-
description: 'Click handler for the heading action button.',
|
|
229
|
-
action: 'buttonOnClick',
|
|
230
223
|
control: false,
|
|
231
224
|
table: {
|
|
232
|
-
type: { summary: '
|
|
233
|
-
defaultValue: { summary: 'undefined' },
|
|
234
|
-
},
|
|
235
|
-
},
|
|
236
|
-
buttonVariant: {
|
|
237
|
-
description: 'Visual variant for the heading action button. Always supply when `buttonText` is set.',
|
|
238
|
-
control: 'select',
|
|
239
|
-
options: ['primary', 'secondary', 'tertiary', 'primary-destructive', 'secondary-destructive', 'tertiary-destructive', 'ghost'],
|
|
240
|
-
table: {
|
|
241
|
-
type: { summary: "'primary' | 'secondary' | 'tertiary' | 'primary-destructive' | 'secondary-destructive' | 'tertiary-destructive' | 'ghost'" },
|
|
242
|
-
defaultValue: { summary: 'undefined' },
|
|
243
|
-
},
|
|
244
|
-
},
|
|
245
|
-
buttonSize: {
|
|
246
|
-
description: "Size of the heading action button. Always supply when `buttonText` is set. Use `'S'` to keep it visually lightweight alongside the title.",
|
|
247
|
-
control: 'select',
|
|
248
|
-
options: ['S', 'M', 'L'],
|
|
249
|
-
table: {
|
|
250
|
-
type: { summary: "'S' | 'M' | 'L'" },
|
|
225
|
+
type: { summary: 'ButtonProps' },
|
|
251
226
|
defaultValue: { summary: 'undefined' },
|
|
252
227
|
},
|
|
253
228
|
},
|
|
@@ -311,7 +286,7 @@ export const Default: Story = withDescription(
|
|
|
311
286
|
},
|
|
312
287
|
render: args => <Section {...args} />,
|
|
313
288
|
},
|
|
314
|
-
'The default story is wired to the Controls panel. Toggle `collapsible` to add expand/collapse behaviour, or
|
|
289
|
+
'The default story is wired to the Controls panel. Toggle `collapsible` to add expand/collapse behaviour, or pass `headerButtonProps` to surface an action in the heading.',
|
|
315
290
|
);
|
|
316
291
|
|
|
317
292
|
export const WithHeadingLevel: Story = withDescription(
|
|
@@ -441,10 +416,13 @@ function PersonalDetailsSection() {
|
|
|
441
416
|
<Section
|
|
442
417
|
title="Personal Details"
|
|
443
418
|
headingLevel={2}
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
419
|
+
headerButtonProps={{
|
|
420
|
+
children: 'Edit',
|
|
421
|
+
iconLeftName: 'pencil',
|
|
422
|
+
onClick: () => openEditModal(),
|
|
423
|
+
variant: 'secondary',
|
|
424
|
+
size: 'S',
|
|
425
|
+
}}
|
|
448
426
|
>
|
|
449
427
|
<p>Section content here.</p>
|
|
450
428
|
</Section>
|
|
@@ -459,26 +437,168 @@ export default PersonalDetailsSection;
|
|
|
459
437
|
<Section
|
|
460
438
|
title="Personal Details"
|
|
461
439
|
headingLevel={2}
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
440
|
+
headerButtonProps={{
|
|
441
|
+
children: 'Edit',
|
|
442
|
+
iconLeftName: 'pencil',
|
|
443
|
+
onClick: fn(),
|
|
444
|
+
variant: 'secondary',
|
|
445
|
+
size: 'S',
|
|
446
|
+
}}
|
|
466
447
|
>
|
|
467
448
|
<p>
|
|
468
|
-
The action button appears in the heading row.
|
|
469
|
-
<code>
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
<code>
|
|
476
|
-
|
|
449
|
+
The action button appears in the heading row. Use
|
|
450
|
+
<code>iconLeftName</code>
|
|
451
|
+
{' '}
|
|
452
|
+
or
|
|
453
|
+
<code>iconRightName</code>
|
|
454
|
+
{' '}
|
|
455
|
+
inside
|
|
456
|
+
<code>headerButtonProps</code>
|
|
457
|
+
{' '}
|
|
458
|
+
to add an icon alongside the label.
|
|
477
459
|
</p>
|
|
478
460
|
</Section>
|
|
479
461
|
),
|
|
480
462
|
},
|
|
481
|
-
'
|
|
463
|
+
'Pass `headerButtonProps` to render an action button in the section heading. Icons can be added via `iconLeftName` or `iconRightName`. Use `size: "S"` to keep it visually lightweight alongside the title.',
|
|
464
|
+
);
|
|
465
|
+
|
|
466
|
+
export const WithActionButtonVariants: Story = withDescription(
|
|
467
|
+
{
|
|
468
|
+
parameters: {
|
|
469
|
+
controls: { disable: true },
|
|
470
|
+
docs: {
|
|
471
|
+
source: {
|
|
472
|
+
language: 'tsx',
|
|
473
|
+
code: `
|
|
474
|
+
import { Section } from '@arbor-education/design-system.components';
|
|
475
|
+
|
|
476
|
+
// Icon on the left
|
|
477
|
+
<Section title="Documents" headingLevel={2}
|
|
478
|
+
headerButtonProps={{ children: 'Upload', iconLeftName: 'upload', onClick: () => {}, variant: 'secondary', size: 'S' }}>
|
|
479
|
+
<p>...</p>
|
|
480
|
+
</Section>
|
|
481
|
+
|
|
482
|
+
// Icon on the right
|
|
483
|
+
<Section title="External Link" headingLevel={2}
|
|
484
|
+
headerButtonProps={{ children: 'Open', iconRightName: 'external-link', onClick: () => {}, variant: 'tertiary', size: 'S' }}>
|
|
485
|
+
<p>...</p>
|
|
486
|
+
</Section>
|
|
487
|
+
|
|
488
|
+
// Icon-only (no children — button renders in icon-only mode)
|
|
489
|
+
<Section title="Attachments" headingLevel={2}
|
|
490
|
+
headerButtonProps={{ iconLeftName: 'plus', iconLeftScreenReaderText: 'Add attachment', onClick: () => {}, variant: 'secondary', size: 'S' }}>
|
|
491
|
+
<p>...</p>
|
|
492
|
+
</Section>
|
|
493
|
+
|
|
494
|
+
// Destructive
|
|
495
|
+
<Section title="Danger Zone" headingLevel={2}
|
|
496
|
+
headerButtonProps={{ children: 'Delete', iconLeftName: 'trash', onClick: () => {}, variant: 'primary-destructive', size: 'S' }}>
|
|
497
|
+
<p>...</p>
|
|
498
|
+
</Section>
|
|
499
|
+
|
|
500
|
+
// Disabled
|
|
501
|
+
<Section title="Locked Section" headingLevel={2}
|
|
502
|
+
headerButtonProps={{ children: 'Edit', iconLeftName: 'pencil', onClick: () => {}, variant: 'secondary', size: 'S', disabled: true }}>
|
|
503
|
+
<p>...</p>
|
|
504
|
+
</Section>
|
|
505
|
+
`.trim(),
|
|
506
|
+
},
|
|
507
|
+
},
|
|
508
|
+
},
|
|
509
|
+
render: () => (
|
|
510
|
+
<>
|
|
511
|
+
<Section
|
|
512
|
+
title="Documents"
|
|
513
|
+
headingLevel={2}
|
|
514
|
+
headerButtonProps={{
|
|
515
|
+
children: 'Upload',
|
|
516
|
+
iconLeftName: 'upload',
|
|
517
|
+
onClick: fn(),
|
|
518
|
+
variant: 'secondary',
|
|
519
|
+
size: 'S',
|
|
520
|
+
}}
|
|
521
|
+
>
|
|
522
|
+
<p>Left icon — icon precedes the label.</p>
|
|
523
|
+
</Section>
|
|
524
|
+
<Section
|
|
525
|
+
title="External Link"
|
|
526
|
+
headingLevel={2}
|
|
527
|
+
headerButtonProps={{
|
|
528
|
+
children: 'Open',
|
|
529
|
+
iconRightName: 'external-link',
|
|
530
|
+
onClick: fn(),
|
|
531
|
+
variant: 'tertiary',
|
|
532
|
+
size: 'S',
|
|
533
|
+
}}
|
|
534
|
+
>
|
|
535
|
+
<p>Right icon — icon follows the label.</p>
|
|
536
|
+
</Section>
|
|
537
|
+
<Section
|
|
538
|
+
title="Attachments"
|
|
539
|
+
headingLevel={2}
|
|
540
|
+
headerButtonProps={{
|
|
541
|
+
iconLeftName: 'plus',
|
|
542
|
+
iconLeftScreenReaderText: 'Add attachment',
|
|
543
|
+
onClick: fn(),
|
|
544
|
+
variant: 'secondary',
|
|
545
|
+
size: 'S',
|
|
546
|
+
}}
|
|
547
|
+
>
|
|
548
|
+
<p>
|
|
549
|
+
Icon-only — omit
|
|
550
|
+
<code>children</code>
|
|
551
|
+
{' '}
|
|
552
|
+
and the button renders in compact icon-only mode. Always supply
|
|
553
|
+
<code>iconLeftScreenReaderText</code>
|
|
554
|
+
{' '}
|
|
555
|
+
for accessibility.
|
|
556
|
+
</p>
|
|
557
|
+
</Section>
|
|
558
|
+
<Section
|
|
559
|
+
title="Danger Zone"
|
|
560
|
+
headingLevel={2}
|
|
561
|
+
headerButtonProps={{
|
|
562
|
+
children: 'Delete',
|
|
563
|
+
iconLeftName: 'trash',
|
|
564
|
+
onClick: fn(),
|
|
565
|
+
variant: 'primary-destructive',
|
|
566
|
+
size: 'S',
|
|
567
|
+
}}
|
|
568
|
+
>
|
|
569
|
+
<p>
|
|
570
|
+
Destructive variant — use
|
|
571
|
+
<code>primary-destructive</code>
|
|
572
|
+
{' '}
|
|
573
|
+
or
|
|
574
|
+
<code>secondary-destructive</code>
|
|
575
|
+
{' '}
|
|
576
|
+
for irreversible actions.
|
|
577
|
+
</p>
|
|
578
|
+
</Section>
|
|
579
|
+
<Section
|
|
580
|
+
title="Locked Section"
|
|
581
|
+
headingLevel={2}
|
|
582
|
+
headerButtonProps={{
|
|
583
|
+
children: 'Edit',
|
|
584
|
+
iconLeftName: 'pencil',
|
|
585
|
+
onClick: fn(),
|
|
586
|
+
variant: 'secondary',
|
|
587
|
+
size: 'S',
|
|
588
|
+
disabled: true,
|
|
589
|
+
}}
|
|
590
|
+
>
|
|
591
|
+
<p>
|
|
592
|
+
Disabled — pass
|
|
593
|
+
<code>disabled: true</code>
|
|
594
|
+
{' '}
|
|
595
|
+
to prevent interaction while keeping the button visible.
|
|
596
|
+
</p>
|
|
597
|
+
</Section>
|
|
598
|
+
</>
|
|
599
|
+
),
|
|
600
|
+
},
|
|
601
|
+
'All `ButtonProps` are forwarded through `headerButtonProps`. This story covers the main edge cases: left icon, right icon, icon-only, destructive variant, and disabled state.',
|
|
482
602
|
);
|
|
483
603
|
|
|
484
604
|
export const Collapsible: Story = withDescription(
|
|
@@ -586,10 +706,12 @@ function ContactSection() {
|
|
|
586
706
|
title="Contact Information"
|
|
587
707
|
headingLevel={2}
|
|
588
708
|
collapsible
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
709
|
+
headerButtonProps={{
|
|
710
|
+
children: 'Add contact',
|
|
711
|
+
onClick: () => openAddContactModal(),
|
|
712
|
+
variant: 'secondary',
|
|
713
|
+
size: 'S',
|
|
714
|
+
}}
|
|
593
715
|
>
|
|
594
716
|
<p>Contact details here.</p>
|
|
595
717
|
</Section>
|
|
@@ -605,10 +727,12 @@ export default ContactSection;
|
|
|
605
727
|
title="Contact Information"
|
|
606
728
|
headingLevel={2}
|
|
607
729
|
collapsible
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
730
|
+
headerButtonProps={{
|
|
731
|
+
children: 'Add contact',
|
|
732
|
+
onClick: fn(),
|
|
733
|
+
variant: 'secondary',
|
|
734
|
+
size: 'S',
|
|
735
|
+
}}
|
|
612
736
|
>
|
|
613
737
|
<p>
|
|
614
738
|
A section can be both collapsible
|
|
@@ -637,10 +761,12 @@ function PupilDetailSection() {
|
|
|
637
761
|
<Section
|
|
638
762
|
title="Personal Details"
|
|
639
763
|
headingLevel={2}
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
764
|
+
headerButtonProps={{
|
|
765
|
+
children: 'Edit',
|
|
766
|
+
onClick: () => openEditModal(),
|
|
767
|
+
variant: 'secondary',
|
|
768
|
+
size: 'S',
|
|
769
|
+
}}
|
|
644
770
|
>
|
|
645
771
|
<Row label="Full name" value="Amara Osei-Bonsu" />
|
|
646
772
|
<Row label="Date of birth" value="14 March 2009" note="Age 16" />
|
|
@@ -659,10 +785,12 @@ export default PupilDetailSection;
|
|
|
659
785
|
<Section
|
|
660
786
|
title="Personal Details"
|
|
661
787
|
headingLevel={2}
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
788
|
+
headerButtonProps={{
|
|
789
|
+
children: 'Edit',
|
|
790
|
+
onClick: fn(),
|
|
791
|
+
variant: 'secondary',
|
|
792
|
+
size: 'S',
|
|
793
|
+
}}
|
|
666
794
|
>
|
|
667
795
|
<Row label="Full name" value="Amara Osei-Bonsu" />
|
|
668
796
|
<Row label="Date of birth" value="14 March 2009" note="Age 16" />
|
|
@@ -43,10 +43,12 @@ describe('Section component', () => {
|
|
|
43
43
|
render(
|
|
44
44
|
<Section
|
|
45
45
|
title="Test Section"
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
46
|
+
headerButtonProps={{
|
|
47
|
+
children: 'Click Me',
|
|
48
|
+
onClick: mockButtonClick,
|
|
49
|
+
variant: 'primary',
|
|
50
|
+
size: 'M',
|
|
51
|
+
}}
|
|
50
52
|
>
|
|
51
53
|
<p>Section content</p>
|
|
52
54
|
</Section>,
|
|
@@ -239,15 +241,19 @@ describe('Section component', () => {
|
|
|
239
241
|
<Section
|
|
240
242
|
title="Parent Section"
|
|
241
243
|
collapsible
|
|
242
|
-
|
|
243
|
-
|
|
244
|
+
headerButtonProps={{
|
|
245
|
+
children: 'Parent Action',
|
|
246
|
+
onClick: parentButtonClick,
|
|
247
|
+
}}
|
|
244
248
|
titleIconName="info"
|
|
245
249
|
>
|
|
246
250
|
<p>Parent content</p>
|
|
247
251
|
<Section
|
|
248
252
|
title="Child Section"
|
|
249
|
-
|
|
250
|
-
|
|
253
|
+
headerButtonProps={{
|
|
254
|
+
children: 'Child Action',
|
|
255
|
+
onClick: childButtonClick,
|
|
256
|
+
}}
|
|
251
257
|
titleIconName="file"
|
|
252
258
|
>
|
|
253
259
|
<p>Child content</p>
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import classNames from 'classnames';
|
|
2
|
-
import { Button, type
|
|
2
|
+
import { Button, type ButtonProps } from 'Components/button/Button';
|
|
3
3
|
import { Heading, type HeadingLevel } from 'Components/heading/Heading';
|
|
4
4
|
import type { IconName } from 'Components/icon/allowedIcons';
|
|
5
5
|
import { Icon } from 'Components/icon/Icon';
|
|
6
|
-
import { useState, type HTMLAttributes
|
|
6
|
+
import { useState, type HTMLAttributes } from 'react';
|
|
7
7
|
import { ENTER_KEY, SPACE_KEY } from 'Utils/keyboardConstants';
|
|
8
8
|
|
|
9
9
|
type SectionProps = {
|
|
@@ -14,10 +14,7 @@ type SectionProps = {
|
|
|
14
14
|
titleIconScreenReaderText?: string;
|
|
15
15
|
collapsible?: boolean;
|
|
16
16
|
collapsed?: boolean;
|
|
17
|
-
|
|
18
|
-
buttonOnClick?: MouseEventHandler<HTMLButtonElement>;
|
|
19
|
-
buttonVariant?: ButtonVariant;
|
|
20
|
-
buttonSize?: ButtonSize;
|
|
17
|
+
headerButtonProps?: ButtonProps;
|
|
21
18
|
} & HTMLAttributes<HTMLElement>;
|
|
22
19
|
|
|
23
20
|
export const Section = (props: SectionProps) => {
|
|
@@ -31,10 +28,7 @@ export const Section = (props: SectionProps) => {
|
|
|
31
28
|
titleIconScreenReaderText,
|
|
32
29
|
collapsible = false,
|
|
33
30
|
collapsed: initialCollapsed = false,
|
|
34
|
-
|
|
35
|
-
buttonOnClick,
|
|
36
|
-
buttonVariant,
|
|
37
|
-
buttonSize,
|
|
31
|
+
headerButtonProps,
|
|
38
32
|
} = props;
|
|
39
33
|
|
|
40
34
|
const [collapsed, setCollapsed] = useState(initialCollapsed);
|
|
@@ -90,13 +84,11 @@ export const Section = (props: SectionProps) => {
|
|
|
90
84
|
)}
|
|
91
85
|
</Heading.InnerContainer>
|
|
92
86
|
<Heading.InnerContainer>
|
|
93
|
-
{
|
|
87
|
+
{headerButtonProps && (
|
|
94
88
|
<Button
|
|
95
|
-
|
|
96
|
-
variant={buttonVariant}
|
|
97
|
-
size={buttonSize}
|
|
89
|
+
{...headerButtonProps}
|
|
98
90
|
>
|
|
99
|
-
{
|
|
91
|
+
{headerButtonProps.children}
|
|
100
92
|
</Button>
|
|
101
93
|
)}
|
|
102
94
|
{collapsible && (
|