@arbor-education/design-system.components 0.15.0 → 0.16.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/skills/write-stories/SKILL.md +207 -271
- package/.storybook/preview.ts +5 -0
- package/CHANGELOG.md +23 -0
- package/README.md +8 -0
- package/component-library.md +144 -13
- package/dist/components/articleCard/ArticleCard.stories.d.ts +137 -11
- package/dist/components/articleCard/ArticleCard.stories.d.ts.map +1 -1
- package/dist/components/articleCard/ArticleCard.stories.js +358 -91
- package/dist/components/articleCard/ArticleCard.stories.js.map +1 -1
- package/dist/components/avatar/Avatar.stories.d.ts +6 -6
- package/dist/components/avatar/Avatar.stories.d.ts.map +1 -1
- package/dist/components/avatar/Avatar.stories.js +393 -49
- package/dist/components/avatar/Avatar.stories.js.map +1 -1
- package/dist/components/avatarGroup/AvatarGroup.stories.d.ts +9 -7
- package/dist/components/avatarGroup/AvatarGroup.stories.d.ts.map +1 -1
- package/dist/components/avatarGroup/AvatarGroup.stories.js +688 -65
- package/dist/components/avatarGroup/AvatarGroup.stories.js.map +1 -1
- package/dist/components/banner/Banner.stories.d.ts.map +1 -1
- package/dist/components/banner/Banner.stories.js +7 -3
- package/dist/components/banner/Banner.stories.js.map +1 -1
- package/dist/components/card/Card.stories.d.ts +105 -4
- package/dist/components/card/Card.stories.d.ts.map +1 -1
- package/dist/components/card/Card.stories.js +336 -18
- package/dist/components/card/Card.stories.js.map +1 -1
- package/dist/components/combobox/Combobox.stories.d.ts +134 -21
- package/dist/components/combobox/Combobox.stories.d.ts.map +1 -1
- package/dist/components/combobox/Combobox.stories.js +676 -175
- package/dist/components/combobox/Combobox.stories.js.map +1 -1
- package/dist/components/datePicker/DatePicker.stories.d.ts +119 -27
- package/dist/components/datePicker/DatePicker.stories.d.ts.map +1 -1
- package/dist/components/datePicker/DatePicker.stories.js +575 -47
- package/dist/components/datePicker/DatePicker.stories.js.map +1 -1
- package/dist/components/dateTimePicker/DateTimePicker.stories.d.ts +155 -39
- package/dist/components/dateTimePicker/DateTimePicker.stories.d.ts.map +1 -1
- package/dist/components/dateTimePicker/DateTimePicker.stories.js +674 -103
- package/dist/components/dateTimePicker/DateTimePicker.stories.js.map +1 -1
- package/dist/components/editableText/EditableText.stories.d.ts +53 -12
- package/dist/components/editableText/EditableText.stories.d.ts.map +1 -1
- package/dist/components/editableText/EditableText.stories.js +401 -64
- package/dist/components/editableText/EditableText.stories.js.map +1 -1
- package/dist/components/formField/FormField.d.ts +4 -0
- package/dist/components/formField/FormField.d.ts.map +1 -1
- package/dist/components/formField/FormField.js +2 -1
- package/dist/components/formField/FormField.js.map +1 -1
- package/dist/components/formField/FormField.test.js +5 -0
- package/dist/components/formField/FormField.test.js.map +1 -1
- package/dist/components/formField/fieldset/Fieldset.stories.d.ts +56 -4
- package/dist/components/formField/fieldset/Fieldset.stories.d.ts.map +1 -1
- package/dist/components/formField/fieldset/Fieldset.stories.js +534 -28
- package/dist/components/formField/fieldset/Fieldset.stories.js.map +1 -1
- package/dist/components/formField/inputs/checkbox/CheckboxGroup.d.ts +3 -1
- package/dist/components/formField/inputs/checkbox/CheckboxGroup.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/colourPickerDropdown/ColourPickerDropdown.stories.d.ts +95 -1
- package/dist/components/formField/inputs/colourPickerDropdown/ColourPickerDropdown.stories.d.ts.map +1 -1
- package/dist/components/formField/inputs/colourPickerDropdown/ColourPickerDropdown.stories.js +386 -9
- package/dist/components/formField/inputs/colourPickerDropdown/ColourPickerDropdown.stories.js.map +1 -1
- package/dist/components/formField/inputs/radio/RadioButtonGroup.d.ts +6 -2
- package/dist/components/formField/inputs/radio/RadioButtonGroup.d.ts.map +1 -1
- package/dist/components/formField/inputs/radio/RadioButtonGroup.js.map +1 -1
- package/dist/components/formField/inputs/radio/RadioButtonInput.stories.d.ts.map +1 -1
- package/dist/components/formField/inputs/radio/RadioButtonInput.stories.js +61 -49
- package/dist/components/formField/inputs/radio/RadioButtonInput.stories.js.map +1 -1
- package/dist/components/formField/inputs/selectDropdown/SelectDropdown.stories.d.ts +188 -166
- package/dist/components/formField/inputs/selectDropdown/SelectDropdown.stories.d.ts.map +1 -1
- package/dist/components/formField/inputs/selectDropdown/SelectDropdown.stories.js +821 -160
- package/dist/components/formField/inputs/selectDropdown/SelectDropdown.stories.js.map +1 -1
- package/dist/components/formField/inputs/time/TimeInput.stories.d.ts +176 -22
- package/dist/components/formField/inputs/time/TimeInput.stories.d.ts.map +1 -1
- package/dist/components/formField/inputs/time/TimeInput.stories.js +851 -92
- package/dist/components/formField/inputs/time/TimeInput.stories.js.map +1 -1
- package/dist/components/formField/label/Label.stories.d.ts +54 -5
- package/dist/components/formField/label/Label.stories.d.ts.map +1 -1
- package/dist/components/formField/label/Label.stories.js +238 -4
- package/dist/components/formField/label/Label.stories.js.map +1 -1
- package/dist/components/icoText/IcoText.stories.d.ts +32 -6
- package/dist/components/icoText/IcoText.stories.d.ts.map +1 -1
- package/dist/components/icoText/IcoText.stories.js +309 -14
- package/dist/components/icoText/IcoText.stories.js.map +1 -1
- package/dist/components/kpiCard/KPICard.stories.d.ts +100 -2
- package/dist/components/kpiCard/KPICard.stories.d.ts.map +1 -1
- package/dist/components/kpiCard/KPICard.stories.js +354 -10
- package/dist/components/kpiCard/KPICard.stories.js.map +1 -1
- package/dist/components/kvpList/KVPList.stories.d.ts +57 -4
- package/dist/components/kvpList/KVPList.stories.d.ts.map +1 -1
- package/dist/components/kvpList/KVPList.stories.js +403 -10
- package/dist/components/kvpList/KVPList.stories.js.map +1 -1
- package/dist/components/modal/Modal.stories.d.ts +113 -9
- package/dist/components/modal/Modal.stories.d.ts.map +1 -1
- package/dist/components/modal/Modal.stories.js +633 -13
- package/dist/components/modal/Modal.stories.js.map +1 -1
- package/dist/components/modal/modalManager/ModalManager.stories.d.ts +34 -10
- package/dist/components/modal/modalManager/ModalManager.stories.d.ts.map +1 -1
- package/dist/components/modal/modalManager/ModalManager.stories.js +463 -85
- package/dist/components/modal/modalManager/ModalManager.stories.js.map +1 -1
- package/dist/components/pill/Pill.d.ts.map +1 -1
- package/dist/components/pill/Pill.js +1 -1
- package/dist/components/pill/Pill.js.map +1 -1
- package/dist/components/pill/Pill.stories.d.ts.map +1 -1
- package/dist/components/pill/Pill.stories.js +11 -13
- package/dist/components/pill/Pill.stories.js.map +1 -1
- package/dist/components/row/Row.stories.d.ts +1 -2
- package/dist/components/row/Row.stories.d.ts.map +1 -1
- package/dist/components/row/Row.stories.js +360 -50
- package/dist/components/row/Row.stories.js.map +1 -1
- package/dist/components/searchBar/SearchBar.stories.d.ts +52 -4
- package/dist/components/searchBar/SearchBar.stories.d.ts.map +1 -1
- package/dist/components/searchBar/SearchBar.stories.js +428 -36
- package/dist/components/searchBar/SearchBar.stories.js.map +1 -1
- package/dist/components/section/Section.stories.d.ts +11 -41
- package/dist/components/section/Section.stories.d.ts.map +1 -1
- package/dist/components/section/Section.stories.js +494 -56
- package/dist/components/section/Section.stories.js.map +1 -1
- package/dist/components/singleUser/SingleUser.stories.d.ts +5 -4
- package/dist/components/singleUser/SingleUser.stories.d.ts.map +1 -1
- package/dist/components/singleUser/SingleUser.stories.js +303 -31
- package/dist/components/singleUser/SingleUser.stories.js.map +1 -1
- package/dist/components/slideoverManager/SlideoverManager.stories.d.ts +32 -11
- package/dist/components/slideoverManager/SlideoverManager.stories.d.ts.map +1 -1
- package/dist/components/slideoverManager/SlideoverManager.stories.js +380 -84
- package/dist/components/slideoverManager/SlideoverManager.stories.js.map +1 -1
- package/dist/components/table/DSDefaultColDef.d.ts.map +1 -1
- package/dist/components/table/DSDefaultColDef.js +4 -3
- package/dist/components/table/DSDefaultColDef.js.map +1 -1
- package/dist/components/table/Table.d.ts +6 -1
- package/dist/components/table/Table.d.ts.map +1 -1
- package/dist/components/table/Table.js +8 -3
- package/dist/components/table/Table.js.map +1 -1
- package/dist/components/table/Table.stories.d.ts +3 -0
- package/dist/components/table/Table.stories.d.ts.map +1 -1
- package/dist/components/table/Table.stories.js +384 -5
- package/dist/components/table/Table.stories.js.map +1 -1
- package/dist/components/table/Table.test.js +30 -0
- package/dist/components/table/Table.test.js.map +1 -1
- package/dist/components/table/TableFooter.stories.d.ts +49 -0
- package/dist/components/table/TableFooter.stories.d.ts.map +1 -0
- package/dist/components/table/TableFooter.stories.js +137 -0
- package/dist/components/table/TableFooter.stories.js.map +1 -0
- package/dist/components/table/TableHeader.stories.d.ts +93 -0
- package/dist/components/table/TableHeader.stories.d.ts.map +1 -0
- package/dist/components/table/TableHeader.stories.js +176 -0
- package/dist/components/table/TableHeader.stories.js.map +1 -0
- package/dist/components/table/cellEditors/DateCellEditor.stories.d.ts +44 -0
- package/dist/components/table/cellEditors/DateCellEditor.stories.d.ts.map +1 -0
- package/dist/components/table/cellEditors/DateCellEditor.stories.js +186 -0
- package/dist/components/table/cellEditors/DateCellEditor.stories.js.map +1 -0
- package/dist/components/table/cellRenderers/BooleanCellRenderer.stories.d.ts +40 -0
- package/dist/components/table/cellRenderers/BooleanCellRenderer.stories.d.ts.map +1 -0
- package/dist/components/table/cellRenderers/BooleanCellRenderer.stories.js +209 -0
- package/dist/components/table/cellRenderers/BooleanCellRenderer.stories.js.map +1 -0
- package/dist/components/table/cellRenderers/ButtonCellRenderer.stories.d.ts +48 -0
- package/dist/components/table/cellRenderers/ButtonCellRenderer.stories.d.ts.map +1 -0
- package/dist/components/table/cellRenderers/ButtonCellRenderer.stories.js +244 -0
- package/dist/components/table/cellRenderers/ButtonCellRenderer.stories.js.map +1 -0
- package/dist/components/table/cellRenderers/CheckboxCellRenderer.d.ts.map +1 -1
- package/dist/components/table/cellRenderers/CheckboxCellRenderer.js +3 -1
- package/dist/components/table/cellRenderers/CheckboxCellRenderer.js.map +1 -1
- package/dist/components/table/cellRenderers/CheckboxCellRenderer.stories.d.ts +64 -0
- package/dist/components/table/cellRenderers/CheckboxCellRenderer.stories.d.ts.map +1 -0
- package/dist/components/table/cellRenderers/CheckboxCellRenderer.stories.js +241 -0
- package/dist/components/table/cellRenderers/CheckboxCellRenderer.stories.js.map +1 -0
- package/dist/components/table/cellRenderers/DefaultCellRenderer.stories.d.ts +55 -0
- package/dist/components/table/cellRenderers/DefaultCellRenderer.stories.d.ts.map +1 -0
- package/dist/components/table/cellRenderers/DefaultCellRenderer.stories.js +245 -0
- package/dist/components/table/cellRenderers/DefaultCellRenderer.stories.js.map +1 -0
- package/dist/components/table/cellRenderers/InlineTextCellRenderer.stories.d.ts +67 -0
- package/dist/components/table/cellRenderers/InlineTextCellRenderer.stories.d.ts.map +1 -0
- package/dist/components/table/cellRenderers/InlineTextCellRenderer.stories.js +221 -0
- package/dist/components/table/cellRenderers/InlineTextCellRenderer.stories.js.map +1 -0
- package/dist/components/table/cellRenderers/SelectDropdownCellRenderer.stories.d.ts +75 -0
- package/dist/components/table/cellRenderers/SelectDropdownCellRenderer.stories.d.ts.map +1 -0
- package/dist/components/table/cellRenderers/SelectDropdownCellRenderer.stories.js +270 -0
- package/dist/components/table/cellRenderers/SelectDropdownCellRenderer.stories.js.map +1 -0
- package/dist/components/table/columnFilters/BooleanFilter.stories.d.ts +57 -0
- package/dist/components/table/columnFilters/BooleanFilter.stories.d.ts.map +1 -0
- package/dist/components/table/columnFilters/BooleanFilter.stories.js +198 -0
- package/dist/components/table/columnFilters/BooleanFilter.stories.js.map +1 -0
- package/dist/components/table/columnFilters/TimeFilter.stories.d.ts +58 -0
- package/dist/components/table/columnFilters/TimeFilter.stories.d.ts.map +1 -0
- package/dist/components/table/columnFilters/TimeFilter.stories.js +207 -0
- package/dist/components/table/columnFilters/TimeFilter.stories.js.map +1 -0
- package/dist/components/table/pagination/PaginationPanel.stories.d.ts +113 -0
- package/dist/components/table/pagination/PaginationPanel.stories.d.ts.map +1 -0
- package/dist/components/table/pagination/PaginationPanel.stories.js +272 -0
- package/dist/components/table/pagination/PaginationPanel.stories.js.map +1 -0
- package/dist/components/table/tableControls/HideColumnsDropdown.d.ts.map +1 -1
- package/dist/components/table/tableControls/HideColumnsDropdown.js +9 -3
- package/dist/components/table/tableControls/HideColumnsDropdown.js.map +1 -1
- package/dist/components/table/tableControls/TableControls.stories.d.ts +151 -0
- package/dist/components/table/tableControls/TableControls.stories.d.ts.map +1 -0
- package/dist/components/table/tableControls/TableControls.stories.js +356 -0
- package/dist/components/table/tableControls/TableControls.stories.js.map +1 -0
- package/dist/components/table/tableControls/TableSettingsDropdown.d.ts +27 -1
- package/dist/components/table/tableControls/TableSettingsDropdown.d.ts.map +1 -1
- package/dist/components/table/tableControls/TableSettingsDropdown.js +53 -26
- package/dist/components/table/tableControls/TableSettingsDropdown.js.map +1 -1
- package/dist/components/table/tableControls/TableSettingsDropdown.test.d.ts +2 -0
- package/dist/components/table/tableControls/TableSettingsDropdown.test.d.ts.map +1 -0
- package/dist/components/table/tableControls/TableSettingsDropdown.test.js +178 -0
- package/dist/components/table/tableControls/TableSettingsDropdown.test.js.map +1 -0
- package/dist/components/tabs/Tabs.stories.d.ts +22 -4
- package/dist/components/tabs/Tabs.stories.d.ts.map +1 -1
- package/dist/components/tabs/Tabs.stories.js +398 -22
- package/dist/components/tabs/Tabs.stories.js.map +1 -1
- package/dist/components/tabs/TabsItem.stories.d.ts +54 -1
- package/dist/components/tabs/TabsItem.stories.d.ts.map +1 -1
- package/dist/components/tabs/TabsItem.stories.js +61 -9
- package/dist/components/tabs/TabsItem.stories.js.map +1 -1
- package/dist/components/toast/Toast.stories.d.ts +103 -10
- package/dist/components/toast/Toast.stories.d.ts.map +1 -1
- package/dist/components/toast/Toast.stories.js +409 -47
- package/dist/components/toast/Toast.stories.js.map +1 -1
- package/dist/components/toggle/Toggle.stories.d.ts +61 -46
- package/dist/components/toggle/Toggle.stories.d.ts.map +1 -1
- package/dist/components/toggle/Toggle.stories.js +311 -122
- package/dist/components/toggle/Toggle.stories.js.map +1 -1
- package/dist/components/tooltip/Tooltip.stories.d.ts +78 -6
- package/dist/components/tooltip/Tooltip.stories.d.ts.map +1 -1
- package/dist/components/tooltip/Tooltip.stories.js +413 -7
- package/dist/components/tooltip/Tooltip.stories.js.map +1 -1
- package/dist/components/tooltip/TooltipWrapper.stories.d.ts +71 -7
- package/dist/components/tooltip/TooltipWrapper.stories.d.ts.map +1 -1
- package/dist/components/tooltip/TooltipWrapper.stories.js +238 -10
- package/dist/components/tooltip/TooltipWrapper.stories.js.map +1 -1
- package/dist/index.css +8 -0
- package/dist/index.css.map +1 -1
- package/dist/utils/PopupParentContext.stories.d.ts +17 -0
- package/dist/utils/PopupParentContext.stories.d.ts.map +1 -0
- package/dist/utils/PopupParentContext.stories.js +266 -0
- package/dist/utils/PopupParentContext.stories.js.map +1 -0
- package/dist/utils/getDefaultPopupParent.d.ts.map +1 -1
- package/dist/utils/getDefaultPopupParent.js +6 -0
- package/dist/utils/getDefaultPopupParent.js.map +1 -1
- package/package.json +1 -1
- package/src/components/articleCard/ArticleCard.stories.tsx +524 -111
- package/src/components/avatar/Avatar.stories.tsx +504 -59
- package/src/components/avatarGroup/AvatarGroup.stories.tsx +977 -175
- package/src/components/banner/Banner.stories.tsx +7 -3
- package/src/components/card/Card.stories.tsx +466 -36
- package/src/components/combobox/Combobox.stories.tsx +867 -260
- package/src/components/datePicker/DatePicker.stories.tsx +777 -60
- package/src/components/dateTimePicker/DateTimePicker.stories.tsx +910 -132
- package/src/components/editableText/EditableText.stories.tsx +567 -91
- package/src/components/formField/FormField.test.tsx +6 -0
- package/src/components/formField/FormField.tsx +5 -0
- package/src/components/formField/fieldset/Fieldset.stories.tsx +761 -51
- package/src/components/formField/inputs/checkbox/CheckboxGroup.tsx +1 -1
- package/src/components/formField/inputs/checkbox/CheckboxInput.tsx +1 -1
- package/src/components/formField/inputs/colourPickerDropdown/ColourPickerDropdown.stories.tsx +504 -11
- package/src/components/formField/inputs/radio/RadioButtonGroup.tsx +17 -4
- package/src/components/formField/inputs/radio/RadioButtonInput.stories.tsx +71 -59
- package/src/components/formField/inputs/selectDropdown/SelectDropdown.stories.tsx +1079 -168
- package/src/components/formField/inputs/time/TimeInput.stories.tsx +1140 -104
- package/src/components/formField/label/Label.stories.tsx +317 -8
- package/src/components/icoText/IcoText.stories.tsx +442 -31
- package/src/components/kpiCard/KPICard.stories.tsx +475 -30
- package/src/components/kvpList/KVPList.stories.tsx +593 -26
- package/src/components/modal/Modal.stories.tsx +963 -26
- package/src/components/modal/modalManager/ModalManager.stories.tsx +612 -454
- package/src/components/pill/Pill.stories.tsx +11 -13
- package/src/components/pill/Pill.tsx +1 -0
- package/src/components/row/Row.stories.tsx +474 -58
- package/src/components/searchBar/SearchBar.stories.tsx +570 -38
- package/src/components/section/Section.stories.tsx +723 -70
- package/src/components/singleUser/SingleUser.stories.tsx +393 -34
- package/src/components/slideoverManager/SlideoverManager.stories.tsx +572 -342
- package/src/components/table/DSDefaultColDef.ts +25 -5
- package/src/components/table/Table.stories.tsx +460 -5
- package/src/components/table/Table.test.tsx +53 -0
- package/src/components/table/Table.tsx +9 -2
- package/src/components/table/TableFooter.stories.tsx +196 -0
- package/src/components/table/TableHeader.stories.tsx +251 -0
- package/src/components/table/cellEditors/DateCellEditor.stories.tsx +245 -0
- package/src/components/table/cellRenderers/BooleanCellRenderer.stories.tsx +278 -0
- package/src/components/table/cellRenderers/ButtonCellRenderer.stories.tsx +333 -0
- package/src/components/table/cellRenderers/CheckboxCellRenderer.stories.tsx +337 -0
- package/src/components/table/cellRenderers/CheckboxCellRenderer.tsx +5 -1
- package/src/components/table/cellRenderers/DefaultCellRenderer.stories.tsx +342 -0
- package/src/components/table/cellRenderers/InlineTextCellRenderer.stories.tsx +292 -0
- package/src/components/table/cellRenderers/SelectDropdownCellRenderer.stories.tsx +369 -0
- package/src/components/table/columnFilters/BooleanFilter.stories.tsx +268 -0
- package/src/components/table/columnFilters/TimeFilter.stories.tsx +281 -0
- package/src/components/table/pagination/PaginationPanel.stories.tsx +327 -0
- package/src/components/table/tableControls/HideColumnsDropdown.tsx +11 -2
- package/src/components/table/tableControls/TableControls.stories.tsx +415 -0
- package/src/components/table/tableControls/TableSettingsDropdown.test.tsx +207 -0
- package/src/components/table/tableControls/TableSettingsDropdown.tsx +103 -39
- package/src/components/tabs/Tabs.stories.tsx +540 -60
- package/src/components/tabs/TabsItem.stories.tsx +82 -8
- package/src/components/toast/Toast.stories.tsx +539 -77
- package/src/components/toggle/Toggle.stories.tsx +371 -135
- package/src/components/tooltip/Tooltip.stories.tsx +606 -15
- package/src/components/tooltip/TooltipWrapper.stories.tsx +348 -12
- package/src/docs/Contributing.mdx +241 -0
- package/src/docs/UsingComponents.mdx +93 -0
- package/src/docs/Welcome.mdx +68 -0
- package/src/global.scss +7 -0
- package/src/utils/PopupParentContext.stories.tsx +367 -0
- package/src/utils/getDefaultPopupParent.ts +6 -0
- package/.ralph/storybook-upgrade/knowledge.md +0 -308
- package/.ralph/storybook-upgrade/prd.json +0 -777
- package/.ralph/storybook-upgrade/progress.md +0 -342
- package/src/components/table/TableWIP.mdx +0 -3
|
@@ -1,200 +1,1111 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
3
|
+
import {
|
|
4
|
+
Controls,
|
|
5
|
+
Heading as DocHeading,
|
|
6
|
+
Markdown,
|
|
7
|
+
Primary as DocPrimary,
|
|
8
|
+
Stories,
|
|
9
|
+
Subtitle,
|
|
10
|
+
Title,
|
|
11
|
+
} from '@storybook/addon-docs/blocks';
|
|
12
|
+
import { FormField } from 'Components/formField/FormField';
|
|
2
13
|
import { SelectDropdown } from './SelectDropdown';
|
|
3
14
|
|
|
4
|
-
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
// Docs page content
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
|
|
19
|
+
const DESCRIPTION_INTRO = [
|
|
20
|
+
'SelectDropdown is a stylised dropdown select input that matches the Arbor design language.',
|
|
21
|
+
'It supports single-select and multi-select, option grouping, icons, two-line item layouts,',
|
|
22
|
+
'and both controlled and uncontrolled state management.',
|
|
23
|
+
'',
|
|
24
|
+
'> **Built on [Radix UI DropdownMenu](https://www.radix-ui.com/primitives/docs/components/dropdown-menu).**',
|
|
25
|
+
'> The dropdown panel renders via a portal — do **not** place SelectDropdown inside a container',
|
|
26
|
+
'> with `overflow: hidden`, or the panel will be clipped.',
|
|
27
|
+
].join('\n');
|
|
28
|
+
|
|
29
|
+
const USAGE_GUIDANCE = [
|
|
30
|
+
'### When to use',
|
|
31
|
+
'',
|
|
32
|
+
'- Replacing a native `<select>` when you need the Arbor visual style',
|
|
33
|
+
'- Offering a list of options for single or multi-selection within a form',
|
|
34
|
+
'- Displaying options with icons, section headers, or group separators',
|
|
35
|
+
'- When built-in form serialisation is useful (a hidden `<input type="hidden">` is always rendered)',
|
|
36
|
+
'',
|
|
37
|
+
'---',
|
|
38
|
+
'',
|
|
39
|
+
'### When NOT to use',
|
|
40
|
+
'',
|
|
41
|
+
'| Situation | Use instead |',
|
|
42
|
+
'|---|---|',
|
|
43
|
+
'| Need native browser select for mobile or performance | Plain `<select>` inside a FormField |',
|
|
44
|
+
'| Need async search or type-to-filter | Combobox |',
|
|
45
|
+
'| Need individual options to be disabled | Custom `Dropdown` composition |',
|
|
46
|
+
'| Thousands of options | No virtualisation — consider a search-first pattern |',
|
|
47
|
+
].join('\n');
|
|
48
|
+
|
|
49
|
+
const DEVELOPER_NOTES = [
|
|
50
|
+
'### Critical usage patterns',
|
|
51
|
+
'',
|
|
52
|
+
'**`errorText` does not exist on SelectDropdown.** Error text belongs on `FormField`. Pass `errorText`',
|
|
53
|
+
'to `FormField` and use `inputType="selectDropdown"` — it automatically wires `hasError`,',
|
|
54
|
+
'`aria-invalid`, and `aria-describedby` for you.',
|
|
55
|
+
'',
|
|
56
|
+
'**`hasError` is visual-only.** It applies error styling to the trigger button but tells screen readers',
|
|
57
|
+
'nothing. Always pair it with `aria-invalid={true}`.',
|
|
58
|
+
'',
|
|
59
|
+
'**`id` is dual-purpose.** It is set as both the `name` on the hidden form input and the `id` on the',
|
|
60
|
+
'trigger button. Set this when using inside a `<form>` or when you need a `<label htmlFor>` to target the trigger.',
|
|
61
|
+
'',
|
|
62
|
+
'**Multi-select count display.** With `multiple` and no `placeholder` prop, the trigger shows',
|
|
63
|
+
'`"Select (2)"` when two items are selected. Setting a `placeholder` suppresses the count — the',
|
|
64
|
+
'trigger shows your placeholder string instead. Choose deliberately.',
|
|
65
|
+
'',
|
|
66
|
+
'**Controlled vs uncontrolled.** Providing `selectedValues` activates controlled mode — the component',
|
|
67
|
+
'stops managing its own state. You must update `selectedValues` via `onSelectionChange` or the',
|
|
68
|
+
'dropdown will appear frozen. Do not mix `initialSelectedValues` with `selectedValues`.',
|
|
69
|
+
'',
|
|
70
|
+
'**Groups are automatic.** Add `group: "Year 7"` to options and the component clusters them under',
|
|
71
|
+
'`<h3>` section headers. Headers only appear when two or more distinct `group` values exist.',
|
|
72
|
+
'',
|
|
73
|
+
'---',
|
|
74
|
+
'',
|
|
75
|
+
'### Accessibility',
|
|
76
|
+
'',
|
|
77
|
+
'- Always pair `hasError` with `aria-invalid={true}` — `hasError` is visual-only',
|
|
78
|
+
'- Use `aria-describedBy` to link to your error/hint message element',
|
|
79
|
+
'- When used standalone (outside FormField), set `id` and associate a `<label htmlFor={id}>`',
|
|
80
|
+
'- When used inside `FormField`, label association and ARIA wiring are automatic — prefer that pattern',
|
|
81
|
+
'- Keyboard navigation is built in via Radix UI: `↑ ↓` to move through options, `Enter` to select, `Esc` to close',
|
|
82
|
+
'',
|
|
83
|
+
'---',
|
|
84
|
+
'',
|
|
85
|
+
'### TypeScript types',
|
|
86
|
+
'',
|
|
87
|
+
'```ts',
|
|
88
|
+
"import { SelectDropdown } from '@arbor-education/design-system.components';",
|
|
89
|
+
'',
|
|
90
|
+
'function MySelect(props: SelectDropdown.Props) { ... }',
|
|
91
|
+
'```',
|
|
92
|
+
'',
|
|
93
|
+
'| Type | Description |',
|
|
94
|
+
'|---|---|',
|
|
95
|
+
'| `SelectDropdown.Props` | Full props interface (`SelectDropdownInputProps`) |',
|
|
96
|
+
].join('\n');
|
|
97
|
+
|
|
98
|
+
const RELATED_COMPONENTS = [
|
|
99
|
+
'## Related components',
|
|
100
|
+
'',
|
|
101
|
+
'[FormField](?path=/docs/components-formfield--docs) · [Dropdown](?path=/docs/components-dropdown--docs) · [ColourPickerDropdown](?path=/docs/components-formfield-inputs-colourpickerdropdown--docs)',
|
|
102
|
+
].join('\n');
|
|
103
|
+
|
|
104
|
+
const PROPS_INTRO = 'The preview below is wired to the **Controls** panel — tweak any prop to see the story update in real time.';
|
|
105
|
+
|
|
106
|
+
function SelectDropdownDocsPage() {
|
|
107
|
+
return (
|
|
108
|
+
<>
|
|
109
|
+
<Title />
|
|
110
|
+
<Subtitle />
|
|
111
|
+
<Markdown>{DESCRIPTION_INTRO}</Markdown>
|
|
112
|
+
<DocHeading>Interactive example</DocHeading>
|
|
113
|
+
<Markdown>{PROPS_INTRO}</Markdown>
|
|
114
|
+
<DocPrimary />
|
|
115
|
+
<Controls />
|
|
116
|
+
<DocHeading>Usage guidance</DocHeading>
|
|
117
|
+
<Markdown>{USAGE_GUIDANCE}</Markdown>
|
|
118
|
+
<DocHeading>Developer notes</DocHeading>
|
|
119
|
+
<Markdown>{DEVELOPER_NOTES}</Markdown>
|
|
120
|
+
<DocHeading>Examples</DocHeading>
|
|
121
|
+
<Stories title="" />
|
|
122
|
+
<Markdown>{RELATED_COMPONENTS}</Markdown>
|
|
123
|
+
</>
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// ---------------------------------------------------------------------------
|
|
128
|
+
// Option fixtures
|
|
129
|
+
// ---------------------------------------------------------------------------
|
|
130
|
+
|
|
131
|
+
const yearGroupOptions = [
|
|
132
|
+
{ label: 'Year 7', value: 'year7' },
|
|
133
|
+
{ label: 'Year 8', value: 'year8' },
|
|
134
|
+
{ label: 'Year 9', value: 'year9' },
|
|
135
|
+
{ label: 'Year 10', value: 'year10' },
|
|
136
|
+
{ label: 'Year 11', value: 'year11' },
|
|
137
|
+
];
|
|
138
|
+
|
|
139
|
+
const subjectOptions = [
|
|
140
|
+
{ label: 'Mathematics', value: 'maths' },
|
|
141
|
+
{ label: 'English', value: 'english' },
|
|
142
|
+
{ label: 'Science', value: 'science' },
|
|
143
|
+
{ label: 'History', value: 'history' },
|
|
144
|
+
{ label: 'Geography', value: 'geography' },
|
|
145
|
+
{ label: 'Art', value: 'art' },
|
|
146
|
+
];
|
|
147
|
+
|
|
148
|
+
const subjectOptionsWithIcons = [
|
|
149
|
+
{ label: 'Mathematics', value: 'maths', icon: 'chart-column-increasing' as const },
|
|
150
|
+
{ label: 'English', value: 'english', icon: 'book-open' as const },
|
|
151
|
+
{ label: 'Science', value: 'science', icon: 'circle-check' as const },
|
|
152
|
+
{ label: 'History', value: 'history', icon: 'clock-3' as const },
|
|
153
|
+
{ label: 'Geography', value: 'geography', icon: 'flag' as const },
|
|
154
|
+
];
|
|
155
|
+
|
|
156
|
+
const attendanceReasonOptions = [
|
|
157
|
+
{ label: 'Authorised absence', value: 'authorised', header: 'Planned' },
|
|
158
|
+
{ label: 'Holiday', value: 'holiday', header: 'Planned' },
|
|
159
|
+
{ label: 'Medical appointment', value: 'medical', header: 'Health' },
|
|
160
|
+
{ label: 'Illness', value: 'illness', header: 'Health' },
|
|
161
|
+
{ label: 'Unauthorised absence', value: 'unauthorised', header: 'Unplanned' },
|
|
162
|
+
{ label: 'Late arrival', value: 'late', header: 'Unplanned' },
|
|
163
|
+
];
|
|
164
|
+
|
|
165
|
+
const groupedYearOptions = [
|
|
166
|
+
{ label: 'Year 7', value: 'year7', group: 'Lower school' },
|
|
167
|
+
{ label: 'Year 8', value: 'year8', group: 'Lower school' },
|
|
168
|
+
{ label: 'Year 9', value: 'year9', group: 'Lower school' },
|
|
169
|
+
{ label: 'Year 10', value: 'year10', group: 'Upper school' },
|
|
170
|
+
{ label: 'Year 11', value: 'year11', group: 'Upper school' },
|
|
171
|
+
{ label: 'Year 12', value: 'year12', group: 'Sixth form' },
|
|
172
|
+
{ label: 'Year 13', value: 'year13', group: 'Sixth form' },
|
|
173
|
+
];
|
|
174
|
+
|
|
175
|
+
// ---------------------------------------------------------------------------
|
|
176
|
+
// Meta
|
|
177
|
+
// ---------------------------------------------------------------------------
|
|
178
|
+
|
|
179
|
+
const meta = {
|
|
5
180
|
title: 'Components/FormField/Inputs/SelectDropdown',
|
|
6
181
|
component: SelectDropdown,
|
|
7
|
-
|
|
182
|
+
tags: ['autodocs'],
|
|
183
|
+
parameters: {
|
|
184
|
+
layout: 'padded',
|
|
185
|
+
docs: {
|
|
186
|
+
page: SelectDropdownDocsPage,
|
|
187
|
+
},
|
|
188
|
+
},
|
|
189
|
+
argTypes: {
|
|
190
|
+
'options': {
|
|
191
|
+
description: [
|
|
192
|
+
'Array of option items to display in the dropdown.',
|
|
193
|
+
'Each item must have a `value` string.',
|
|
194
|
+
'`label`, `icon`, `header`, and `group` are optional.',
|
|
195
|
+
].join(' '),
|
|
196
|
+
control: false,
|
|
197
|
+
table: {
|
|
198
|
+
type: { summary: 'SelectDropdownItemProps[]' },
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
'placeholder': {
|
|
202
|
+
description: [
|
|
203
|
+
'Text shown in the trigger when nothing is selected.',
|
|
204
|
+
'In multi-select mode, also shown when 2+ items are selected (suppressing the count display).',
|
|
205
|
+
'Defaults to `"Select"`.',
|
|
206
|
+
].join(' '),
|
|
207
|
+
control: 'text',
|
|
208
|
+
table: {
|
|
209
|
+
type: { summary: 'string' },
|
|
210
|
+
defaultValue: { summary: "'Select'" },
|
|
211
|
+
},
|
|
212
|
+
},
|
|
213
|
+
'multiple': {
|
|
214
|
+
description: [
|
|
215
|
+
'Enables multi-select mode.',
|
|
216
|
+
'The dropdown stays open after each selection and items toggle on/off.',
|
|
217
|
+
].join(' '),
|
|
218
|
+
control: 'boolean',
|
|
219
|
+
table: {
|
|
220
|
+
type: { summary: 'boolean' },
|
|
221
|
+
defaultValue: { summary: 'false' },
|
|
222
|
+
},
|
|
223
|
+
},
|
|
224
|
+
'disabled': {
|
|
225
|
+
description: 'Disables the trigger button, preventing the dropdown from opening.',
|
|
226
|
+
control: 'boolean',
|
|
227
|
+
table: {
|
|
228
|
+
type: { summary: 'boolean' },
|
|
229
|
+
defaultValue: { summary: 'false' },
|
|
230
|
+
},
|
|
231
|
+
},
|
|
232
|
+
'hasError': {
|
|
233
|
+
description: [
|
|
234
|
+
'**Visual only.** Applies error styling to the trigger button.',
|
|
235
|
+
'Must be paired with `aria-invalid={true}` for screen reader support.',
|
|
236
|
+
'Do not pass `errorText` here — that prop does not exist. Use `FormField` with `errorText` instead.',
|
|
237
|
+
].join(' '),
|
|
238
|
+
control: 'boolean',
|
|
239
|
+
table: {
|
|
240
|
+
type: { summary: 'boolean' },
|
|
241
|
+
defaultValue: { summary: 'false' },
|
|
242
|
+
},
|
|
243
|
+
},
|
|
244
|
+
'aria-invalid': {
|
|
245
|
+
description: [
|
|
246
|
+
'Communicates an invalid state to screen readers via the trigger button.',
|
|
247
|
+
'Always pair with `hasError`.',
|
|
248
|
+
].join(' '),
|
|
249
|
+
control: 'boolean',
|
|
250
|
+
table: {
|
|
251
|
+
type: { summary: 'boolean' },
|
|
252
|
+
defaultValue: { summary: 'undefined' },
|
|
253
|
+
},
|
|
254
|
+
},
|
|
255
|
+
'aria-describedBy': {
|
|
256
|
+
description: [
|
|
257
|
+
'ID of the element that describes this field (e.g. an error message or hint).',
|
|
258
|
+
'Passed through to the trigger button.',
|
|
259
|
+
].join(' '),
|
|
260
|
+
control: 'text',
|
|
261
|
+
table: {
|
|
262
|
+
type: { summary: 'string' },
|
|
263
|
+
defaultValue: { summary: 'undefined' },
|
|
264
|
+
},
|
|
265
|
+
},
|
|
266
|
+
'id': {
|
|
267
|
+
description: [
|
|
268
|
+
'**Dual-purpose.** Set as `name` on the hidden form input AND as `id` on the trigger button.',
|
|
269
|
+
'Required for label association (`<label htmlFor={id}>`) and form submission.',
|
|
270
|
+
].join(' '),
|
|
271
|
+
control: 'text',
|
|
272
|
+
table: {
|
|
273
|
+
type: { summary: 'string' },
|
|
274
|
+
defaultValue: { summary: 'undefined' },
|
|
275
|
+
},
|
|
276
|
+
},
|
|
277
|
+
'alwaysShowPlaceholder': {
|
|
278
|
+
description: [
|
|
279
|
+
'When `true`, the trigger always displays the placeholder regardless of what is selected.',
|
|
280
|
+
'Use for action-style dropdowns (e.g. "Add filter") where the label must not change.',
|
|
281
|
+
].join(' '),
|
|
282
|
+
control: 'boolean',
|
|
283
|
+
table: {
|
|
284
|
+
type: { summary: 'boolean' },
|
|
285
|
+
defaultValue: { summary: 'false' },
|
|
286
|
+
},
|
|
287
|
+
},
|
|
288
|
+
'initialSelectedValues': {
|
|
289
|
+
description: [
|
|
290
|
+
'Uncontrolled mode only — sets the initial selection on mount.',
|
|
291
|
+
'Ignored if `selectedValues` is provided.',
|
|
292
|
+
].join(' '),
|
|
293
|
+
control: false,
|
|
294
|
+
table: {
|
|
295
|
+
type: { summary: 'string[]' },
|
|
296
|
+
defaultValue: { summary: '[]' },
|
|
297
|
+
},
|
|
298
|
+
},
|
|
299
|
+
'selectedValues': {
|
|
300
|
+
description: [
|
|
301
|
+
'Activates fully controlled mode.',
|
|
302
|
+
'You must update this via `onSelectionChange` — the component stops managing its own state.',
|
|
303
|
+
'Do not mix with `initialSelectedValues`.',
|
|
304
|
+
].join(' '),
|
|
305
|
+
control: false,
|
|
306
|
+
table: {
|
|
307
|
+
type: { summary: 'string[]' },
|
|
308
|
+
defaultValue: { summary: 'undefined' },
|
|
309
|
+
},
|
|
310
|
+
},
|
|
311
|
+
'onSelectionChange': {
|
|
312
|
+
description: 'Fires after every selection change with the full updated array of selected values.',
|
|
313
|
+
action: 'onSelectionChange',
|
|
314
|
+
control: false,
|
|
315
|
+
table: {
|
|
316
|
+
type: { summary: '(value: string[]) => void' },
|
|
317
|
+
},
|
|
318
|
+
},
|
|
319
|
+
'open': {
|
|
320
|
+
description: 'Controls the open/closed state of the dropdown externally.',
|
|
321
|
+
control: 'boolean',
|
|
322
|
+
table: {
|
|
323
|
+
type: { summary: 'boolean' },
|
|
324
|
+
defaultValue: { summary: 'undefined' },
|
|
325
|
+
},
|
|
326
|
+
},
|
|
327
|
+
'onOpenChange': {
|
|
328
|
+
description: 'Fires when the dropdown attempts to open or close. Required when `open` is controlled.',
|
|
329
|
+
action: 'onOpenChange',
|
|
330
|
+
control: false,
|
|
331
|
+
table: {
|
|
332
|
+
type: { summary: '(open: boolean) => void' },
|
|
333
|
+
},
|
|
334
|
+
},
|
|
335
|
+
},
|
|
336
|
+
} satisfies Meta<typeof SelectDropdown>;
|
|
337
|
+
|
|
338
|
+
export default meta;
|
|
339
|
+
type Story = StoryObj<typeof SelectDropdown>;
|
|
340
|
+
|
|
341
|
+
// ---------------------------------------------------------------------------
|
|
342
|
+
// Helper: attach a per-story description to docs
|
|
343
|
+
// ---------------------------------------------------------------------------
|
|
8
344
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
345
|
+
const withDescription = (story: Story, description: string): Story => ({
|
|
346
|
+
...story,
|
|
347
|
+
parameters: {
|
|
348
|
+
...story.parameters,
|
|
349
|
+
docs: {
|
|
350
|
+
...story.parameters?.docs,
|
|
351
|
+
description: {
|
|
352
|
+
story: description,
|
|
353
|
+
},
|
|
354
|
+
},
|
|
19
355
|
},
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
// ---------------------------------------------------------------------------
|
|
359
|
+
// Template components for stateful and composite stories
|
|
360
|
+
// ---------------------------------------------------------------------------
|
|
361
|
+
|
|
362
|
+
const WithInitialSelectionTemplate = () => (
|
|
363
|
+
<div style={{ maxWidth: '320px' }}>
|
|
364
|
+
<SelectDropdown
|
|
365
|
+
placeholder="Select a year group"
|
|
366
|
+
initialSelectedValues={['year9']}
|
|
367
|
+
options={yearGroupOptions}
|
|
368
|
+
onSelectionChange={values => console.log(values)}
|
|
369
|
+
/>
|
|
370
|
+
</div>
|
|
371
|
+
);
|
|
372
|
+
|
|
373
|
+
const MultiSelectTemplate = () => (
|
|
374
|
+
<div style={{ maxWidth: '320px' }}>
|
|
375
|
+
<SelectDropdown
|
|
376
|
+
placeholder="Select subjects"
|
|
377
|
+
multiple
|
|
378
|
+
options={subjectOptions}
|
|
379
|
+
onSelectionChange={values => console.log(values)}
|
|
380
|
+
/>
|
|
381
|
+
</div>
|
|
382
|
+
);
|
|
383
|
+
|
|
384
|
+
const MultiSelectWithCountTemplate = () => (
|
|
385
|
+
<div style={{ maxWidth: '320px' }}>
|
|
386
|
+
<SelectDropdown
|
|
387
|
+
multiple
|
|
388
|
+
initialSelectedValues={['maths', 'english', 'science']}
|
|
389
|
+
options={subjectOptions}
|
|
390
|
+
onSelectionChange={values => console.log(values)}
|
|
391
|
+
/>
|
|
392
|
+
</div>
|
|
393
|
+
);
|
|
394
|
+
|
|
395
|
+
const DisabledTemplate = () => (
|
|
396
|
+
<div style={{ maxWidth: '320px' }}>
|
|
397
|
+
<SelectDropdown
|
|
398
|
+
placeholder="Select a year group"
|
|
399
|
+
disabled
|
|
400
|
+
options={yearGroupOptions}
|
|
401
|
+
onSelectionChange={values => console.log(values)}
|
|
402
|
+
/>
|
|
403
|
+
</div>
|
|
404
|
+
);
|
|
405
|
+
|
|
406
|
+
const WithErrorTemplate = () => (
|
|
407
|
+
<div style={{ maxWidth: '320px', display: 'flex', flexDirection: 'column', gap: 'var(--spacing-xsmall)' }}>
|
|
408
|
+
<SelectDropdown
|
|
409
|
+
placeholder="Select a year group"
|
|
410
|
+
hasError
|
|
411
|
+
aria-invalid
|
|
412
|
+
aria-describedBy="year-group-error"
|
|
413
|
+
options={yearGroupOptions}
|
|
414
|
+
onSelectionChange={values => console.log(values)}
|
|
415
|
+
/>
|
|
416
|
+
<span
|
|
417
|
+
id="year-group-error"
|
|
418
|
+
style={{ fontSize: '0.875rem', color: 'var(--color-feedback-danger-600)' }}
|
|
419
|
+
>
|
|
420
|
+
Please select a year group
|
|
421
|
+
</span>
|
|
422
|
+
</div>
|
|
423
|
+
);
|
|
424
|
+
|
|
425
|
+
const WithIconsTemplate = () => (
|
|
426
|
+
<div style={{ maxWidth: '320px' }}>
|
|
427
|
+
<SelectDropdown
|
|
428
|
+
placeholder="Select a subject"
|
|
429
|
+
options={subjectOptionsWithIcons}
|
|
430
|
+
onSelectionChange={values => console.log(values)}
|
|
431
|
+
/>
|
|
432
|
+
</div>
|
|
433
|
+
);
|
|
434
|
+
|
|
435
|
+
const WithItemHeadersTemplate = () => (
|
|
436
|
+
<div style={{ maxWidth: '320px' }}>
|
|
437
|
+
<SelectDropdown
|
|
438
|
+
placeholder="Select absence reason"
|
|
439
|
+
options={attendanceReasonOptions}
|
|
440
|
+
onSelectionChange={values => console.log(values)}
|
|
441
|
+
/>
|
|
442
|
+
</div>
|
|
443
|
+
);
|
|
444
|
+
|
|
445
|
+
const WithGroupsTemplate = () => (
|
|
446
|
+
<div style={{ maxWidth: '320px' }}>
|
|
447
|
+
<SelectDropdown
|
|
448
|
+
placeholder="Select a year group"
|
|
449
|
+
options={groupedYearOptions}
|
|
450
|
+
onSelectionChange={values => console.log(values)}
|
|
451
|
+
/>
|
|
452
|
+
</div>
|
|
453
|
+
);
|
|
454
|
+
|
|
455
|
+
const WithGroupsMultiSelectTemplate = () => (
|
|
456
|
+
<div style={{ maxWidth: '320px' }}>
|
|
457
|
+
<SelectDropdown
|
|
458
|
+
placeholder="Select year groups"
|
|
459
|
+
multiple
|
|
460
|
+
options={groupedYearOptions}
|
|
461
|
+
onSelectionChange={values => console.log(values)}
|
|
462
|
+
/>
|
|
463
|
+
</div>
|
|
464
|
+
);
|
|
465
|
+
|
|
466
|
+
const AlwaysShowPlaceholderTemplate = () => (
|
|
467
|
+
<div style={{ maxWidth: '320px' }}>
|
|
468
|
+
<SelectDropdown
|
|
469
|
+
placeholder="Add filter"
|
|
470
|
+
alwaysShowPlaceholder
|
|
471
|
+
multiple
|
|
472
|
+
options={subjectOptions}
|
|
473
|
+
onSelectionChange={values => console.log(values)}
|
|
474
|
+
/>
|
|
475
|
+
</div>
|
|
476
|
+
);
|
|
477
|
+
|
|
478
|
+
const ControlledSingleSelectTemplate = () => {
|
|
479
|
+
const [selected, setSelected] = useState<string[]>([]);
|
|
480
|
+
return (
|
|
481
|
+
<div style={{ maxWidth: '320px', display: 'flex', flexDirection: 'column', gap: 'var(--spacing-small)' }}>
|
|
482
|
+
<SelectDropdown
|
|
483
|
+
placeholder="Select a subject"
|
|
484
|
+
selectedValues={selected}
|
|
485
|
+
onSelectionChange={setSelected}
|
|
486
|
+
options={subjectOptions}
|
|
487
|
+
/>
|
|
488
|
+
<span style={{ fontSize: '0.875rem', color: 'var(--color-grey-600)' }}>
|
|
489
|
+
Selected:
|
|
490
|
+
{' '}
|
|
491
|
+
{selected.length > 0 ? selected.join(', ') : 'nothing yet'}
|
|
492
|
+
</span>
|
|
493
|
+
</div>
|
|
494
|
+
);
|
|
20
495
|
};
|
|
21
496
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
497
|
+
const ControlledOpenTemplate = () => {
|
|
498
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
499
|
+
return (
|
|
500
|
+
<div style={{ maxWidth: '320px', display: 'flex', flexDirection: 'column', gap: 'var(--spacing-small)' }}>
|
|
501
|
+
<button type="button" onClick={() => setIsOpen(true)}>
|
|
502
|
+
Open dropdown externally
|
|
503
|
+
</button>
|
|
504
|
+
<SelectDropdown
|
|
505
|
+
placeholder="Select a subject"
|
|
506
|
+
open={isOpen}
|
|
507
|
+
onOpenChange={setIsOpen}
|
|
508
|
+
options={subjectOptions}
|
|
509
|
+
onSelectionChange={(values) => {
|
|
510
|
+
console.log(values);
|
|
511
|
+
setIsOpen(false);
|
|
512
|
+
}}
|
|
513
|
+
/>
|
|
514
|
+
</div>
|
|
515
|
+
);
|
|
33
516
|
};
|
|
34
517
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
{
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
518
|
+
const InsideFormFieldTemplate = () => (
|
|
519
|
+
<div style={{ maxWidth: '320px' }}>
|
|
520
|
+
<FormField
|
|
521
|
+
label="Year group"
|
|
522
|
+
id="year-group-field"
|
|
523
|
+
inputType="selectDropdown"
|
|
524
|
+
inputProps={{
|
|
525
|
+
placeholder: 'Select a year group',
|
|
526
|
+
options: yearGroupOptions,
|
|
527
|
+
onSelectionChange: values => console.log(values),
|
|
528
|
+
}}
|
|
529
|
+
/>
|
|
530
|
+
</div>
|
|
531
|
+
);
|
|
532
|
+
|
|
533
|
+
const InsideFormFieldWithErrorTemplate = () => (
|
|
534
|
+
<div style={{ maxWidth: '320px' }}>
|
|
535
|
+
<FormField
|
|
536
|
+
label="Year group"
|
|
537
|
+
id="year-group-error-field"
|
|
538
|
+
inputType="selectDropdown"
|
|
539
|
+
errorText="Please select a year group"
|
|
540
|
+
inputProps={{
|
|
541
|
+
placeholder: 'Select a year group',
|
|
542
|
+
options: yearGroupOptions,
|
|
543
|
+
onSelectionChange: values => console.log(values),
|
|
544
|
+
}}
|
|
545
|
+
/>
|
|
546
|
+
</div>
|
|
547
|
+
);
|
|
548
|
+
|
|
549
|
+
// ---------------------------------------------------------------------------
|
|
550
|
+
// Stories
|
|
551
|
+
// ---------------------------------------------------------------------------
|
|
552
|
+
|
|
553
|
+
export const Default: Story = withDescription(
|
|
554
|
+
{
|
|
555
|
+
args: {
|
|
556
|
+
options: yearGroupOptions,
|
|
557
|
+
placeholder: 'Select a year group',
|
|
558
|
+
multiple: false,
|
|
559
|
+
disabled: false,
|
|
560
|
+
},
|
|
561
|
+
render: args => (
|
|
562
|
+
<div style={{ maxWidth: '320px' }}>
|
|
563
|
+
<SelectDropdown {...args} />
|
|
564
|
+
</div>
|
|
565
|
+
),
|
|
51
566
|
},
|
|
52
|
-
|
|
567
|
+
'The interactive canvas — every prop is wired to the Controls panel above. Use `options`, `placeholder`, `multiple`, and `disabled` to explore the full range of configurations.',
|
|
568
|
+
);
|
|
569
|
+
|
|
570
|
+
export const WithInitialSelection: Story = withDescription(
|
|
571
|
+
{
|
|
572
|
+
render: WithInitialSelectionTemplate,
|
|
573
|
+
parameters: {
|
|
574
|
+
controls: { disable: true },
|
|
575
|
+
docs: {
|
|
576
|
+
source: {
|
|
577
|
+
language: 'tsx',
|
|
578
|
+
code: `
|
|
579
|
+
import { SelectDropdown } from '@arbor-education/design-system.components';
|
|
53
580
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
{
|
|
59
|
-
{
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
581
|
+
function WithInitialSelectionExample() {
|
|
582
|
+
return (
|
|
583
|
+
<SelectDropdown
|
|
584
|
+
placeholder="Select a year group"
|
|
585
|
+
initialSelectedValues={['year9']}
|
|
586
|
+
options={[
|
|
587
|
+
{ label: 'Year 7', value: 'year7' },
|
|
588
|
+
{ label: 'Year 8', value: 'year8' },
|
|
589
|
+
{ label: 'Year 9', value: 'year9' },
|
|
590
|
+
{ label: 'Year 10', value: 'year10' },
|
|
591
|
+
{ label: 'Year 11', value: 'year11' },
|
|
592
|
+
]}
|
|
593
|
+
onSelectionChange={(values) => console.log(values)}
|
|
594
|
+
/>
|
|
595
|
+
);
|
|
596
|
+
}
|
|
597
|
+
export default WithInitialSelectionExample;
|
|
598
|
+
`.trim(),
|
|
599
|
+
},
|
|
600
|
+
},
|
|
601
|
+
},
|
|
64
602
|
},
|
|
65
|
-
|
|
603
|
+
'Uncontrolled mode with a pre-selected value via `initialSelectedValues`. The component manages its own state after mount — no `selectedValues` prop is needed. Use this when you only need to set a default and let the user take over.',
|
|
604
|
+
);
|
|
66
605
|
|
|
67
|
-
export const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
606
|
+
export const MultiSelect: Story = withDescription(
|
|
607
|
+
{
|
|
608
|
+
render: MultiSelectTemplate,
|
|
609
|
+
parameters: {
|
|
610
|
+
controls: { disable: true },
|
|
611
|
+
docs: {
|
|
612
|
+
source: {
|
|
613
|
+
language: 'tsx',
|
|
614
|
+
code: `
|
|
615
|
+
import { SelectDropdown } from '@arbor-education/design-system.components';
|
|
616
|
+
|
|
617
|
+
function MultiSelectExample() {
|
|
618
|
+
return (
|
|
619
|
+
<SelectDropdown
|
|
620
|
+
placeholder="Select subjects"
|
|
621
|
+
multiple
|
|
622
|
+
options={[
|
|
623
|
+
{ label: 'Mathematics', value: 'maths' },
|
|
624
|
+
{ label: 'English', value: 'english' },
|
|
625
|
+
{ label: 'Science', value: 'science' },
|
|
626
|
+
{ label: 'History', value: 'history' },
|
|
627
|
+
{ label: 'Geography', value: 'geography' },
|
|
628
|
+
]}
|
|
629
|
+
onSelectionChange={(values) => console.log(values)}
|
|
630
|
+
/>
|
|
631
|
+
);
|
|
632
|
+
}
|
|
633
|
+
export default MultiSelectExample;
|
|
634
|
+
`.trim(),
|
|
635
|
+
},
|
|
636
|
+
},
|
|
637
|
+
},
|
|
77
638
|
},
|
|
78
|
-
}
|
|
639
|
+
'`multiple={true}` allows selecting more than one item. The dropdown stays open after each selection so users can make multiple choices without re-opening. Items toggle on and off.',
|
|
640
|
+
);
|
|
79
641
|
|
|
80
|
-
export const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
642
|
+
export const MultiSelectWithCount: Story = withDescription(
|
|
643
|
+
{
|
|
644
|
+
render: MultiSelectWithCountTemplate,
|
|
645
|
+
parameters: {
|
|
646
|
+
controls: { disable: true },
|
|
647
|
+
docs: {
|
|
648
|
+
source: {
|
|
649
|
+
language: 'tsx',
|
|
650
|
+
code: `
|
|
651
|
+
import { SelectDropdown } from '@arbor-education/design-system.components';
|
|
652
|
+
|
|
653
|
+
function MultiSelectWithCountExample() {
|
|
654
|
+
return (
|
|
655
|
+
// No placeholder prop — the trigger shows "Select (3)" when items are selected.
|
|
656
|
+
// Setting a placeholder would suppress this count display.
|
|
657
|
+
<SelectDropdown
|
|
658
|
+
multiple
|
|
659
|
+
initialSelectedValues={['maths', 'english', 'science']}
|
|
660
|
+
options={[
|
|
661
|
+
{ label: 'Mathematics', value: 'maths' },
|
|
662
|
+
{ label: 'English', value: 'english' },
|
|
663
|
+
{ label: 'Science', value: 'science' },
|
|
664
|
+
{ label: 'History', value: 'history' },
|
|
665
|
+
]}
|
|
666
|
+
onSelectionChange={(values) => console.log(values)}
|
|
667
|
+
/>
|
|
668
|
+
);
|
|
669
|
+
}
|
|
670
|
+
export default MultiSelectWithCountExample;
|
|
671
|
+
`.trim(),
|
|
672
|
+
},
|
|
673
|
+
},
|
|
674
|
+
},
|
|
90
675
|
},
|
|
91
|
-
|
|
676
|
+
[
|
|
677
|
+
'When `multiple` is true and **no `placeholder` prop is set**, the trigger shows `"Select (3)"` when three items are selected.',
|
|
678
|
+
'Setting a `placeholder` suppresses this count — the trigger shows your placeholder text instead.',
|
|
679
|
+
'Choose deliberately based on whether you want users to see how many items are selected.',
|
|
680
|
+
].join(' '),
|
|
681
|
+
);
|
|
92
682
|
|
|
93
|
-
export const
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
683
|
+
export const Disabled: Story = withDescription(
|
|
684
|
+
{
|
|
685
|
+
render: DisabledTemplate,
|
|
686
|
+
parameters: {
|
|
687
|
+
controls: { disable: true },
|
|
688
|
+
docs: {
|
|
689
|
+
source: {
|
|
690
|
+
language: 'tsx',
|
|
691
|
+
code: `
|
|
692
|
+
import { SelectDropdown } from '@arbor-education/design-system.components';
|
|
693
|
+
|
|
694
|
+
function DisabledExample() {
|
|
695
|
+
return (
|
|
696
|
+
<SelectDropdown
|
|
697
|
+
placeholder="Select a year group"
|
|
698
|
+
disabled
|
|
699
|
+
options={[
|
|
700
|
+
{ label: 'Year 7', value: 'year7' },
|
|
701
|
+
{ label: 'Year 8', value: 'year8' },
|
|
702
|
+
{ label: 'Year 9', value: 'year9' },
|
|
703
|
+
]}
|
|
704
|
+
onSelectionChange={(values) => console.log(values)}
|
|
705
|
+
/>
|
|
706
|
+
);
|
|
707
|
+
}
|
|
708
|
+
export default DisabledExample;
|
|
709
|
+
`.trim(),
|
|
710
|
+
},
|
|
711
|
+
},
|
|
712
|
+
},
|
|
104
713
|
},
|
|
105
|
-
|
|
714
|
+
'The `disabled` prop prevents the trigger from opening. Use when the field is not applicable in the current context — for example, a dependent field that requires another field to be filled first.',
|
|
715
|
+
);
|
|
106
716
|
|
|
107
|
-
export const
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
717
|
+
export const WithError: Story = withDescription(
|
|
718
|
+
{
|
|
719
|
+
render: WithErrorTemplate,
|
|
720
|
+
parameters: {
|
|
721
|
+
controls: { disable: true },
|
|
722
|
+
docs: {
|
|
723
|
+
source: {
|
|
724
|
+
language: 'tsx',
|
|
725
|
+
code: `
|
|
726
|
+
import { SelectDropdown } from '@arbor-education/design-system.components';
|
|
727
|
+
|
|
728
|
+
function WithErrorExample() {
|
|
729
|
+
return (
|
|
730
|
+
<div>
|
|
731
|
+
<SelectDropdown
|
|
732
|
+
placeholder="Select a year group"
|
|
733
|
+
hasError
|
|
734
|
+
aria-invalid
|
|
735
|
+
aria-describedBy="year-group-error"
|
|
736
|
+
options={[
|
|
737
|
+
{ label: 'Year 7', value: 'year7' },
|
|
738
|
+
{ label: 'Year 8', value: 'year8' },
|
|
739
|
+
{ label: 'Year 9', value: 'year9' },
|
|
740
|
+
]}
|
|
741
|
+
onSelectionChange={(values) => console.log(values)}
|
|
742
|
+
/>
|
|
743
|
+
<span id="year-group-error">Please select a year group</span>
|
|
744
|
+
</div>
|
|
745
|
+
);
|
|
746
|
+
}
|
|
747
|
+
export default WithErrorExample;
|
|
748
|
+
`.trim(),
|
|
749
|
+
},
|
|
750
|
+
},
|
|
751
|
+
},
|
|
117
752
|
},
|
|
118
|
-
|
|
753
|
+
[
|
|
754
|
+
'`hasError` applies error styling to the trigger. `aria-invalid` tells screen readers the field is invalid.',
|
|
755
|
+
'`aria-describedBy` links to the error message element by ID.',
|
|
756
|
+
'**All three should be used together for an accessible error state.**',
|
|
757
|
+
'When using `FormField`, these are wired automatically via `errorText` — prefer the `InsideFormFieldWithError` pattern.',
|
|
758
|
+
].join(' '),
|
|
759
|
+
);
|
|
760
|
+
|
|
761
|
+
export const WithIcons: Story = withDescription(
|
|
762
|
+
{
|
|
763
|
+
render: WithIconsTemplate,
|
|
764
|
+
parameters: {
|
|
765
|
+
controls: { disable: true },
|
|
766
|
+
docs: {
|
|
767
|
+
source: {
|
|
768
|
+
language: 'tsx',
|
|
769
|
+
code: `
|
|
770
|
+
import { SelectDropdown } from '@arbor-education/design-system.components';
|
|
119
771
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
{
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
772
|
+
function WithIconsExample() {
|
|
773
|
+
return (
|
|
774
|
+
<SelectDropdown
|
|
775
|
+
placeholder="Select a subject"
|
|
776
|
+
options={[
|
|
777
|
+
{ label: 'Mathematics', value: 'maths', icon: 'chart-column-increasing' },
|
|
778
|
+
{ label: 'English', value: 'english', icon: 'book-open' },
|
|
779
|
+
{ label: 'Science', value: 'science', icon: 'circle-check' },
|
|
780
|
+
{ label: 'History', value: 'history', icon: 'clock-3' },
|
|
781
|
+
{ label: 'Geography', value: 'geography', icon: 'flag' },
|
|
782
|
+
]}
|
|
783
|
+
onSelectionChange={(values) => console.log(values)}
|
|
784
|
+
/>
|
|
785
|
+
);
|
|
786
|
+
}
|
|
787
|
+
export default WithIconsExample;
|
|
788
|
+
`.trim(),
|
|
789
|
+
},
|
|
790
|
+
},
|
|
791
|
+
},
|
|
133
792
|
},
|
|
134
|
-
|
|
793
|
+
'Options can include an `icon` prop to display an icon to the left of the label. Use icon names from the Arbor Icon component.',
|
|
794
|
+
);
|
|
795
|
+
|
|
796
|
+
export const WithItemHeaders: Story = withDescription(
|
|
797
|
+
{
|
|
798
|
+
render: WithItemHeadersTemplate,
|
|
799
|
+
parameters: {
|
|
800
|
+
controls: { disable: true },
|
|
801
|
+
docs: {
|
|
802
|
+
source: {
|
|
803
|
+
language: 'tsx',
|
|
804
|
+
code: `
|
|
805
|
+
import { SelectDropdown } from '@arbor-education/design-system.components';
|
|
135
806
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
{
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
807
|
+
function WithItemHeadersExample() {
|
|
808
|
+
return (
|
|
809
|
+
<SelectDropdown
|
|
810
|
+
placeholder="Select absence reason"
|
|
811
|
+
options={[
|
|
812
|
+
{ label: 'Authorised absence', value: 'authorised', header: 'Planned' },
|
|
813
|
+
{ label: 'Holiday', value: 'holiday', header: 'Planned' },
|
|
814
|
+
{ label: 'Medical appointment', value: 'medical', header: 'Health' },
|
|
815
|
+
{ label: 'Illness', value: 'illness', header: 'Health' },
|
|
816
|
+
{ label: 'Unauthorised absence', value: 'unauthorised', header: 'Unplanned' },
|
|
817
|
+
]}
|
|
818
|
+
onSelectionChange={(values) => console.log(values)}
|
|
819
|
+
/>
|
|
820
|
+
);
|
|
821
|
+
}
|
|
822
|
+
export default WithItemHeadersExample;
|
|
823
|
+
`.trim(),
|
|
824
|
+
},
|
|
825
|
+
},
|
|
826
|
+
},
|
|
146
827
|
},
|
|
147
|
-
|
|
828
|
+
'The `header` prop on an option renders a small label above the option text, creating a two-line item layout. Use this to add context to complex options — for example, showing the category of an absence type alongside its name.',
|
|
829
|
+
);
|
|
830
|
+
|
|
831
|
+
export const WithGroups: Story = withDescription(
|
|
832
|
+
{
|
|
833
|
+
render: WithGroupsTemplate,
|
|
834
|
+
parameters: {
|
|
835
|
+
controls: { disable: true },
|
|
836
|
+
docs: {
|
|
837
|
+
source: {
|
|
838
|
+
language: 'tsx',
|
|
839
|
+
code: `
|
|
840
|
+
import { SelectDropdown } from '@arbor-education/design-system.components';
|
|
148
841
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
{
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
842
|
+
function WithGroupsExample() {
|
|
843
|
+
return (
|
|
844
|
+
<SelectDropdown
|
|
845
|
+
placeholder="Select a year group"
|
|
846
|
+
options={[
|
|
847
|
+
{ label: 'Year 7', value: 'year7', group: 'Lower school' },
|
|
848
|
+
{ label: 'Year 8', value: 'year8', group: 'Lower school' },
|
|
849
|
+
{ label: 'Year 9', value: 'year9', group: 'Lower school' },
|
|
850
|
+
{ label: 'Year 10', value: 'year10', group: 'Upper school' },
|
|
851
|
+
{ label: 'Year 11', value: 'year11', group: 'Upper school' },
|
|
852
|
+
{ label: 'Year 12', value: 'year12', group: 'Sixth form' },
|
|
853
|
+
{ label: 'Year 13', value: 'year13', group: 'Sixth form' },
|
|
854
|
+
]}
|
|
855
|
+
onSelectionChange={(values) => console.log(values)}
|
|
856
|
+
/>
|
|
857
|
+
);
|
|
858
|
+
}
|
|
859
|
+
export default WithGroupsExample;
|
|
860
|
+
`.trim(),
|
|
861
|
+
},
|
|
862
|
+
},
|
|
863
|
+
},
|
|
164
864
|
},
|
|
165
|
-
|
|
865
|
+
'Adding a `group` string to options automatically clusters them under section headings. Group headers are only rendered when two or more distinct group values exist. Items without a `group` are rendered ungrouped.',
|
|
866
|
+
);
|
|
867
|
+
|
|
868
|
+
export const WithGroupsMultiSelect: Story = withDescription(
|
|
869
|
+
{
|
|
870
|
+
render: WithGroupsMultiSelectTemplate,
|
|
871
|
+
parameters: {
|
|
872
|
+
controls: { disable: true },
|
|
873
|
+
docs: {
|
|
874
|
+
source: {
|
|
875
|
+
language: 'tsx',
|
|
876
|
+
code: `
|
|
877
|
+
import { SelectDropdown } from '@arbor-education/design-system.components';
|
|
166
878
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
{
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
{
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
879
|
+
function WithGroupsMultiSelectExample() {
|
|
880
|
+
return (
|
|
881
|
+
<SelectDropdown
|
|
882
|
+
placeholder="Select year groups"
|
|
883
|
+
multiple
|
|
884
|
+
options={[
|
|
885
|
+
{ label: 'Year 7', value: 'year7', group: 'Lower school' },
|
|
886
|
+
{ label: 'Year 8', value: 'year8', group: 'Lower school' },
|
|
887
|
+
{ label: 'Year 9', value: 'year9', group: 'Lower school' },
|
|
888
|
+
{ label: 'Year 10', value: 'year10', group: 'Upper school' },
|
|
889
|
+
{ label: 'Year 11', value: 'year11', group: 'Upper school' },
|
|
890
|
+
]}
|
|
891
|
+
onSelectionChange={(values) => console.log(values)}
|
|
892
|
+
/>
|
|
893
|
+
);
|
|
894
|
+
}
|
|
895
|
+
export default WithGroupsMultiSelectExample;
|
|
896
|
+
`.trim(),
|
|
897
|
+
},
|
|
898
|
+
},
|
|
899
|
+
},
|
|
183
900
|
},
|
|
184
|
-
|
|
901
|
+
'Grouped options combined with multi-select. The dropdown stays open while users make multiple selections across groups.',
|
|
902
|
+
);
|
|
903
|
+
|
|
904
|
+
export const AlwaysShowPlaceholder: Story = withDescription(
|
|
905
|
+
{
|
|
906
|
+
render: AlwaysShowPlaceholderTemplate,
|
|
907
|
+
parameters: {
|
|
908
|
+
controls: { disable: true },
|
|
909
|
+
docs: {
|
|
910
|
+
source: {
|
|
911
|
+
language: 'tsx',
|
|
912
|
+
code: `
|
|
913
|
+
import { SelectDropdown } from '@arbor-education/design-system.components';
|
|
185
914
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
{
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
915
|
+
function AlwaysShowPlaceholderExample() {
|
|
916
|
+
return (
|
|
917
|
+
<SelectDropdown
|
|
918
|
+
placeholder="Add filter"
|
|
919
|
+
alwaysShowPlaceholder
|
|
920
|
+
multiple
|
|
921
|
+
options={[
|
|
922
|
+
{ label: 'Mathematics', value: 'maths' },
|
|
923
|
+
{ label: 'English', value: 'english' },
|
|
924
|
+
{ label: 'Science', value: 'science' },
|
|
925
|
+
]}
|
|
926
|
+
onSelectionChange={(values) => console.log(values)}
|
|
927
|
+
/>
|
|
928
|
+
);
|
|
929
|
+
}
|
|
930
|
+
export default AlwaysShowPlaceholderExample;
|
|
931
|
+
`.trim(),
|
|
932
|
+
},
|
|
933
|
+
},
|
|
934
|
+
},
|
|
197
935
|
},
|
|
198
|
-
|
|
936
|
+
'`alwaysShowPlaceholder` keeps the trigger label fixed regardless of selection state. Use for action-style dropdowns (e.g. "Add filter") where the trigger label should not change to reflect the selected value.',
|
|
937
|
+
);
|
|
199
938
|
|
|
200
|
-
export
|
|
939
|
+
export const ControlledSingleSelect: Story = withDescription(
|
|
940
|
+
{
|
|
941
|
+
render: ControlledSingleSelectTemplate,
|
|
942
|
+
parameters: {
|
|
943
|
+
controls: { disable: true },
|
|
944
|
+
docs: {
|
|
945
|
+
source: {
|
|
946
|
+
language: 'tsx',
|
|
947
|
+
code: `
|
|
948
|
+
import { useState } from 'react';
|
|
949
|
+
import { SelectDropdown } from '@arbor-education/design-system.components';
|
|
950
|
+
|
|
951
|
+
function ControlledSingleSelectExample() {
|
|
952
|
+
const [selected, setSelected] = useState<string[]>([]);
|
|
953
|
+
|
|
954
|
+
return (
|
|
955
|
+
<SelectDropdown
|
|
956
|
+
placeholder="Select a subject"
|
|
957
|
+
selectedValues={selected}
|
|
958
|
+
onSelectionChange={setSelected}
|
|
959
|
+
options={[
|
|
960
|
+
{ label: 'Mathematics', value: 'maths' },
|
|
961
|
+
{ label: 'English', value: 'english' },
|
|
962
|
+
{ label: 'Science', value: 'science' },
|
|
963
|
+
{ label: 'History', value: 'history' },
|
|
964
|
+
]}
|
|
965
|
+
/>
|
|
966
|
+
);
|
|
967
|
+
}
|
|
968
|
+
export default ControlledSingleSelectExample;
|
|
969
|
+
`.trim(),
|
|
970
|
+
},
|
|
971
|
+
},
|
|
972
|
+
},
|
|
973
|
+
},
|
|
974
|
+
[
|
|
975
|
+
'Fully controlled single-select. Providing `selectedValues` activates controlled mode — the component stops managing its own state.',
|
|
976
|
+
'You **must** update `selectedValues` in response to `onSelectionChange`, otherwise the dropdown will appear frozen.',
|
|
977
|
+
'Use this pattern when the selected value needs to be derived from, or synchronised with, external state.',
|
|
978
|
+
].join(' '),
|
|
979
|
+
);
|
|
980
|
+
|
|
981
|
+
export const ControlledOpen: Story = withDescription(
|
|
982
|
+
{
|
|
983
|
+
render: ControlledOpenTemplate,
|
|
984
|
+
parameters: {
|
|
985
|
+
controls: { disable: true },
|
|
986
|
+
docs: {
|
|
987
|
+
source: {
|
|
988
|
+
language: 'tsx',
|
|
989
|
+
code: `
|
|
990
|
+
import { useState } from 'react';
|
|
991
|
+
import { SelectDropdown } from '@arbor-education/design-system.components';
|
|
992
|
+
|
|
993
|
+
function ControlledOpenExample() {
|
|
994
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
995
|
+
|
|
996
|
+
return (
|
|
997
|
+
<div>
|
|
998
|
+
<button type="button" onClick={() => setIsOpen(true)}>Open dropdown externally</button>
|
|
999
|
+
<SelectDropdown
|
|
1000
|
+
placeholder="Select a subject"
|
|
1001
|
+
open={isOpen}
|
|
1002
|
+
onOpenChange={setIsOpen}
|
|
1003
|
+
options={[
|
|
1004
|
+
{ label: 'Mathematics', value: 'maths' },
|
|
1005
|
+
{ label: 'English', value: 'english' },
|
|
1006
|
+
{ label: 'Science', value: 'science' },
|
|
1007
|
+
]}
|
|
1008
|
+
onSelectionChange={(values) => {
|
|
1009
|
+
console.log(values);
|
|
1010
|
+
setIsOpen(false);
|
|
1011
|
+
}}
|
|
1012
|
+
/>
|
|
1013
|
+
</div>
|
|
1014
|
+
);
|
|
1015
|
+
}
|
|
1016
|
+
export default ControlledOpenExample;
|
|
1017
|
+
`.trim(),
|
|
1018
|
+
},
|
|
1019
|
+
},
|
|
1020
|
+
},
|
|
1021
|
+
},
|
|
1022
|
+
'`open` and `onOpenChange` let you control the dropdown visibility externally. Use when you need to open the dropdown programmatically — from a toolbar button, a keyboard shortcut, or after completing a validation step.',
|
|
1023
|
+
);
|
|
1024
|
+
|
|
1025
|
+
export const InsideFormField: Story = withDescription(
|
|
1026
|
+
{
|
|
1027
|
+
render: InsideFormFieldTemplate,
|
|
1028
|
+
parameters: {
|
|
1029
|
+
controls: { disable: true },
|
|
1030
|
+
docs: {
|
|
1031
|
+
source: {
|
|
1032
|
+
language: 'tsx',
|
|
1033
|
+
code: `
|
|
1034
|
+
import { FormField } from '@arbor-education/design-system.components';
|
|
1035
|
+
|
|
1036
|
+
function InsideFormFieldExample() {
|
|
1037
|
+
return (
|
|
1038
|
+
<FormField
|
|
1039
|
+
label="Year group"
|
|
1040
|
+
id="year-group-field"
|
|
1041
|
+
inputType="selectDropdown"
|
|
1042
|
+
inputProps={{
|
|
1043
|
+
placeholder: 'Select a year group',
|
|
1044
|
+
options: [
|
|
1045
|
+
{ label: 'Year 7', value: 'year7' },
|
|
1046
|
+
{ label: 'Year 8', value: 'year8' },
|
|
1047
|
+
{ label: 'Year 9', value: 'year9' },
|
|
1048
|
+
{ label: 'Year 10', value: 'year10' },
|
|
1049
|
+
{ label: 'Year 11', value: 'year11' },
|
|
1050
|
+
],
|
|
1051
|
+
onSelectionChange: (values) => console.log(values),
|
|
1052
|
+
}}
|
|
1053
|
+
/>
|
|
1054
|
+
);
|
|
1055
|
+
}
|
|
1056
|
+
export default InsideFormFieldExample;
|
|
1057
|
+
`.trim(),
|
|
1058
|
+
},
|
|
1059
|
+
},
|
|
1060
|
+
},
|
|
1061
|
+
},
|
|
1062
|
+
[
|
|
1063
|
+
'The recommended way to use SelectDropdown in forms.',
|
|
1064
|
+
'`FormField` with `inputType="selectDropdown"` provides an accessible `<label>` linked to the trigger,',
|
|
1065
|
+
'and automatically wires `aria-invalid` and `aria-describedby` when `errorText` is set.',
|
|
1066
|
+
'Pass SelectDropdown props via `inputProps`.',
|
|
1067
|
+
].join(' '),
|
|
1068
|
+
);
|
|
1069
|
+
|
|
1070
|
+
export const InsideFormFieldWithError: Story = withDescription(
|
|
1071
|
+
{
|
|
1072
|
+
render: InsideFormFieldWithErrorTemplate,
|
|
1073
|
+
parameters: {
|
|
1074
|
+
controls: { disable: true },
|
|
1075
|
+
docs: {
|
|
1076
|
+
source: {
|
|
1077
|
+
language: 'tsx',
|
|
1078
|
+
code: `
|
|
1079
|
+
import { FormField } from '@arbor-education/design-system.components';
|
|
1080
|
+
|
|
1081
|
+
function InsideFormFieldWithErrorExample() {
|
|
1082
|
+
return (
|
|
1083
|
+
<FormField
|
|
1084
|
+
label="Year group"
|
|
1085
|
+
id="year-group-error-field"
|
|
1086
|
+
inputType="selectDropdown"
|
|
1087
|
+
errorText="Please select a year group"
|
|
1088
|
+
inputProps={{
|
|
1089
|
+
placeholder: 'Select a year group',
|
|
1090
|
+
options: [
|
|
1091
|
+
{ label: 'Year 7', value: 'year7' },
|
|
1092
|
+
{ label: 'Year 8', value: 'year8' },
|
|
1093
|
+
{ label: 'Year 9', value: 'year9' },
|
|
1094
|
+
],
|
|
1095
|
+
onSelectionChange: (values) => console.log(values),
|
|
1096
|
+
}}
|
|
1097
|
+
/>
|
|
1098
|
+
);
|
|
1099
|
+
}
|
|
1100
|
+
export default InsideFormFieldWithErrorExample;
|
|
1101
|
+
`.trim(),
|
|
1102
|
+
},
|
|
1103
|
+
},
|
|
1104
|
+
},
|
|
1105
|
+
},
|
|
1106
|
+
[
|
|
1107
|
+
'Error state via `FormField`. Setting `errorText` on `FormField` automatically passes `hasError`,',
|
|
1108
|
+
'`aria-invalid`, and `aria-describedby` to the SelectDropdown, and renders the error message below the field.',
|
|
1109
|
+
'This is the correct and recommended pattern for accessible form validation.',
|
|
1110
|
+
].join(' '),
|
|
1111
|
+
);
|