@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,37 +1,974 @@
|
|
|
1
|
-
import type { Meta } from '@storybook/react-vite';
|
|
2
|
-
import { fn } from 'storybook/test';
|
|
3
|
-
import { Modal, type ModalProps } from './Modal';
|
|
4
|
-
import { Button } from 'Components/button/Button';
|
|
5
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 { Modal } from './Modal';
|
|
13
|
+
import { Button } from 'Components/button/Button';
|
|
14
|
+
import { FormField } from 'Components/formField/FormField';
|
|
15
|
+
import { Dropdown } from 'Components/dropdown/Dropdown';
|
|
16
|
+
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
// Docs page content
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
|
|
21
|
+
const DESCRIPTION_INTRO = [
|
|
22
|
+
'Modal is a focused, interruptive dialog that blocks all background interaction until the user',
|
|
23
|
+
'takes an explicit action or dismisses it.',
|
|
24
|
+
'',
|
|
25
|
+
'> **Tip:** For programmatic modal control — opening modals from event handlers, PubSub events,',
|
|
26
|
+
'> or outside the React tree — use [`ModalManager`](?path=/docs/components-modals-modalmanager--docs)',
|
|
27
|
+
'> with `ModalUtils.addModal()` instead of wiring `open` state manually.',
|
|
28
|
+
'',
|
|
29
|
+
'> **Built on [Radix UI Dialog](https://www.radix-ui.com/primitives/docs/components/dialog).**',
|
|
30
|
+
'> Modal renders via a portal. The overlay registers itself as a `PopupParentContext`, so any',
|
|
31
|
+
'> floating elements (Dropdown, Tooltip) inside the modal automatically portal into the overlay.',
|
|
32
|
+
].join('\n');
|
|
33
|
+
|
|
34
|
+
const USAGE_GUIDANCE = [
|
|
35
|
+
'### When to use',
|
|
36
|
+
'',
|
|
37
|
+
'- The user must make a decision or complete a short task before continuing (e.g. a delete',
|
|
38
|
+
' confirmation, a quick-add form)',
|
|
39
|
+
'- You need to fully interrupt the flow to prevent data loss or an irreversible action',
|
|
40
|
+
'- The content is self-contained and does not require the user to reference the underlying page',
|
|
41
|
+
'- Multi-step flows where each step is short and sequential',
|
|
42
|
+
'',
|
|
43
|
+
'---',
|
|
44
|
+
'',
|
|
45
|
+
'### When NOT to use',
|
|
46
|
+
'',
|
|
47
|
+
'| Situation | Use instead |',
|
|
48
|
+
'|---|---|',
|
|
49
|
+
'| User needs to reference the background page while editing | [Slideover](?path=/docs/components-modals-slideover--docs) |',
|
|
50
|
+
'| Non-blocking contextual info anchored to a trigger | [Dropdown](?path=/docs/components-dropdown--docs) |',
|
|
51
|
+
'| Brief status feedback that auto-dismisses | [Toast](?path=/docs/components-toast--docs) |',
|
|
52
|
+
'| Content too complex for a dialog | A dedicated page or Slideover |',
|
|
53
|
+
].join('\n');
|
|
54
|
+
|
|
55
|
+
const DEVELOPER_NOTES = [
|
|
56
|
+
'### Critical usage patterns',
|
|
57
|
+
'',
|
|
58
|
+
'**`closeHandler` gates everything.** With no `closeHandler`:',
|
|
59
|
+
'- The X close button is **not rendered** (even if `hideCloseButton` is `false`)',
|
|
60
|
+
'- Pressing **Escape** does nothing',
|
|
61
|
+
'- Clicking the overlay does nothing (overlay clicks never dismiss — intentional)',
|
|
62
|
+
'',
|
|
63
|
+
'A modal without `closeHandler` can only be closed by action buttons you render inside it. Use',
|
|
64
|
+
'this pattern for destructive confirmations where an accidental dismiss would be dangerous.',
|
|
65
|
+
'',
|
|
66
|
+
'**`hideCloseButton` vs no `closeHandler`:**',
|
|
67
|
+
'',
|
|
68
|
+
'| Scenario | `closeHandler` | `hideCloseButton` | Result |',
|
|
69
|
+
'|---|---|---|---|',
|
|
70
|
+
'| Standard dismissible modal | provided | `false` | X button shown; Escape works |',
|
|
71
|
+
'| Escape only, no visible X | provided | `true` | X button hidden; Escape still works |',
|
|
72
|
+
'| Forced decision (no escape route) | omitted | n/a | No X, no Escape, no overlay dismiss |',
|
|
73
|
+
'',
|
|
74
|
+
'**`title` prop shortcut.** Passing `title="My Title"` auto-renders `Modal.Header` + `Modal.Title`.',
|
|
75
|
+
'Do **not** also render a manual `<Modal.Header>` — you will get a double header.',
|
|
76
|
+
'',
|
|
77
|
+
'**Width is consumer-controlled.** There is no `size` prop. Pass `className` to control width.',
|
|
78
|
+
'Standard modals: 480–600 px; delete confirmations: 400–480 px.',
|
|
79
|
+
'',
|
|
80
|
+
'---',
|
|
81
|
+
'',
|
|
82
|
+
'### Accessibility',
|
|
83
|
+
'',
|
|
84
|
+
'- The dialog root receives `role="dialog"` and `aria-modal="true"` (Radix UI)',
|
|
85
|
+
'- `Modal.Title` renders an `<h2>` and is linked to the dialog via `aria-labelledby`',
|
|
86
|
+
'- Focus is **trapped** inside the modal — Tab and Shift+Tab cycle only within the modal',
|
|
87
|
+
'- Focus returns to the trigger element when the modal closes',
|
|
88
|
+
'- Always provide `Modal.Title` (or the `title` prop) — a dialog without a label breaks screen',
|
|
89
|
+
' reader announcement',
|
|
90
|
+
'',
|
|
91
|
+
'---',
|
|
92
|
+
'',
|
|
93
|
+
'### Sub-component props',
|
|
94
|
+
'',
|
|
95
|
+
'| Sub-component | Props | Description |',
|
|
96
|
+
'|---|---|---|',
|
|
97
|
+
'| `Modal.Header` | `className?`, `children?` | Flex row header bar — put `Modal.Title` here |',
|
|
98
|
+
'| `Modal.Body` | `className?`, `children?` | Scrollable content area with grey background |',
|
|
99
|
+
'| `Modal.Footer` | `className?`, `children?` | Flex row footer — put action `Button`s here |',
|
|
100
|
+
'| `Modal.Title` | `className?`, `children?`, all `DialogTitleProps` | `<h2>` dialog title — required for accessibility |',
|
|
101
|
+
'| `Modal.CloseButton` | all `ButtonProps` | Reads `closeHandler` from context — renders `null` if no `closeHandler` |',
|
|
102
|
+
'',
|
|
103
|
+
'---',
|
|
104
|
+
'',
|
|
105
|
+
'### Design tokens used',
|
|
106
|
+
'',
|
|
107
|
+
'| Token | Usage |',
|
|
108
|
+
'|---|---|',
|
|
109
|
+
'| `--modal-overlay-color-background` | Overlay backdrop colour |',
|
|
110
|
+
'| `--modal-color-background` | Dialog box background |',
|
|
111
|
+
'| `--modal-radius` | Dialog border radius |',
|
|
112
|
+
'| `--modal-header-color-background` | Header section background |',
|
|
113
|
+
'| `--modal-header-spacing-padding` | Header padding |',
|
|
114
|
+
'| `--modal-footer-color-background` | Footer section background |',
|
|
115
|
+
'| `--modal-footer-spacing-padding` | Footer padding |',
|
|
116
|
+
].join('\n');
|
|
117
|
+
|
|
118
|
+
const RELATED_COMPONENTS = [
|
|
119
|
+
'## Related components',
|
|
120
|
+
'',
|
|
121
|
+
'[Slideover](?path=/docs/components-modals-slideover--docs) · [Dropdown](?path=/docs/components-dropdown--docs) · [Toast](?path=/docs/components-toast--docs) · [Button](?path=/docs/components-button--docs)',
|
|
122
|
+
].join('\n');
|
|
123
|
+
|
|
124
|
+
const PROPS_INTRO = 'The preview below is wired to the **Controls** panel — tweak any prop to see the story update in real time.';
|
|
125
|
+
|
|
126
|
+
// ---------------------------------------------------------------------------
|
|
127
|
+
// Docs page
|
|
128
|
+
// ---------------------------------------------------------------------------
|
|
6
129
|
|
|
7
|
-
|
|
130
|
+
function ModalDocsPage() {
|
|
131
|
+
return (
|
|
132
|
+
<>
|
|
133
|
+
<Title />
|
|
134
|
+
<Subtitle />
|
|
135
|
+
<Markdown>{DESCRIPTION_INTRO}</Markdown>
|
|
136
|
+
<DocHeading>Interactive example</DocHeading>
|
|
137
|
+
<Markdown>{PROPS_INTRO}</Markdown>
|
|
138
|
+
<DocPrimary />
|
|
139
|
+
<Controls />
|
|
140
|
+
<DocHeading>Usage guidance</DocHeading>
|
|
141
|
+
<Markdown>{USAGE_GUIDANCE}</Markdown>
|
|
142
|
+
<DocHeading>Developer notes</DocHeading>
|
|
143
|
+
<Markdown>{DEVELOPER_NOTES}</Markdown>
|
|
144
|
+
<DocHeading>Examples</DocHeading>
|
|
145
|
+
<Stories title="" />
|
|
146
|
+
<Markdown>{RELATED_COMPONENTS}</Markdown>
|
|
147
|
+
</>
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// ---------------------------------------------------------------------------
|
|
152
|
+
// Meta
|
|
153
|
+
// ---------------------------------------------------------------------------
|
|
154
|
+
|
|
155
|
+
const meta = {
|
|
8
156
|
title: 'Components/Modals/Modal',
|
|
9
157
|
component: Modal,
|
|
158
|
+
parameters: {
|
|
159
|
+
layout: 'padded',
|
|
160
|
+
docs: {
|
|
161
|
+
page: ModalDocsPage,
|
|
162
|
+
},
|
|
163
|
+
},
|
|
10
164
|
tags: ['autodocs'],
|
|
165
|
+
argTypes: {
|
|
166
|
+
open: {
|
|
167
|
+
control: 'boolean',
|
|
168
|
+
description:
|
|
169
|
+
'Controls whether the modal is visible. Pair with `closeHandler` for a fully controlled component.',
|
|
170
|
+
table: {
|
|
171
|
+
type: { summary: 'boolean' },
|
|
172
|
+
defaultValue: { summary: 'undefined' },
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
closeHandler: {
|
|
176
|
+
control: false,
|
|
177
|
+
description: [
|
|
178
|
+
'Callback fired when the modal requests to close (Escape key or X button).',
|
|
179
|
+
'**Omitting this prop disables all dismiss mechanisms** — no X button, no Escape key.',
|
|
180
|
+
].join(' '),
|
|
181
|
+
table: {
|
|
182
|
+
type: { summary: '() => void' },
|
|
183
|
+
defaultValue: { summary: 'undefined' },
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
hideCloseButton: {
|
|
187
|
+
control: 'boolean',
|
|
188
|
+
description:
|
|
189
|
+
'Hides the X close button while keeping Escape-key dismiss active (requires `closeHandler`).',
|
|
190
|
+
table: {
|
|
191
|
+
type: { summary: 'boolean' },
|
|
192
|
+
defaultValue: { summary: 'false' },
|
|
193
|
+
},
|
|
194
|
+
},
|
|
195
|
+
title: {
|
|
196
|
+
control: 'text',
|
|
197
|
+
description: [
|
|
198
|
+
'Shortcut that auto-renders `Modal.Header` + `Modal.Title`.',
|
|
199
|
+
'Do **not** combine with a manual `<Modal.Header>` — you will get a double header.',
|
|
200
|
+
].join(' '),
|
|
201
|
+
table: {
|
|
202
|
+
type: { summary: 'string' },
|
|
203
|
+
defaultValue: { summary: 'undefined' },
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
className: {
|
|
207
|
+
control: 'text',
|
|
208
|
+
description:
|
|
209
|
+
'CSS class applied to the dialog element. Use this to control modal width — there is no `size` prop.',
|
|
210
|
+
table: {
|
|
211
|
+
type: { summary: 'string' },
|
|
212
|
+
defaultValue: { summary: 'undefined' },
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
overlayClassName: {
|
|
216
|
+
control: 'text',
|
|
217
|
+
description: 'CSS class applied to the overlay / backdrop element.',
|
|
218
|
+
table: {
|
|
219
|
+
type: { summary: 'string' },
|
|
220
|
+
defaultValue: { summary: 'undefined' },
|
|
221
|
+
},
|
|
222
|
+
},
|
|
223
|
+
portalTarget: {
|
|
224
|
+
control: false,
|
|
225
|
+
description:
|
|
226
|
+
'DOM element into which the modal portal mounts. Defaults to `PopupParentContext` value (`div#ds-popup-parent` in body).',
|
|
227
|
+
table: {
|
|
228
|
+
type: { summary: 'HTMLElement | null' },
|
|
229
|
+
defaultValue: { summary: 'undefined' },
|
|
230
|
+
},
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
} satisfies Meta<typeof Modal>;
|
|
234
|
+
|
|
235
|
+
export default meta;
|
|
236
|
+
type Story = StoryObj<typeof meta>;
|
|
237
|
+
|
|
238
|
+
// ---------------------------------------------------------------------------
|
|
239
|
+
// Helper
|
|
240
|
+
// ---------------------------------------------------------------------------
|
|
241
|
+
|
|
242
|
+
const withDescription = (story: Story, description: string): Story => ({
|
|
243
|
+
...story,
|
|
244
|
+
parameters: {
|
|
245
|
+
...story.parameters,
|
|
246
|
+
docs: {
|
|
247
|
+
...story.parameters?.docs,
|
|
248
|
+
description: {
|
|
249
|
+
story: description,
|
|
250
|
+
},
|
|
251
|
+
},
|
|
252
|
+
},
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
// ---------------------------------------------------------------------------
|
|
256
|
+
// Templates
|
|
257
|
+
// ---------------------------------------------------------------------------
|
|
258
|
+
|
|
259
|
+
const DefaultTemplate = () => {
|
|
260
|
+
const [open, setOpen] = useState(false);
|
|
261
|
+
return (
|
|
262
|
+
<>
|
|
263
|
+
<Button variant="primary" onClick={() => setOpen(true)}>
|
|
264
|
+
Add Assessment Period
|
|
265
|
+
</Button>
|
|
266
|
+
<Modal open={open} closeHandler={() => setOpen(false)} title="Add Assessment Period">
|
|
267
|
+
<Modal.Body>
|
|
268
|
+
<p>
|
|
269
|
+
Create a new assessment period for the current academic year. Assessment periods define
|
|
270
|
+
the date ranges used when recording and reporting pupil attainment data.
|
|
271
|
+
</p>
|
|
272
|
+
</Modal.Body>
|
|
273
|
+
<Modal.Footer>
|
|
274
|
+
<Button variant="secondary" onClick={() => setOpen(false)}>
|
|
275
|
+
Cancel
|
|
276
|
+
</Button>
|
|
277
|
+
<Button variant="primary" onClick={() => setOpen(false)}>
|
|
278
|
+
Add Assessment Period
|
|
279
|
+
</Button>
|
|
280
|
+
</Modal.Footer>
|
|
281
|
+
</Modal>
|
|
282
|
+
</>
|
|
283
|
+
);
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
const NoCloseHandlerTemplate = () => {
|
|
287
|
+
const [open, setOpen] = useState(false);
|
|
288
|
+
return (
|
|
289
|
+
<>
|
|
290
|
+
<Button variant="primary" onClick={() => setOpen(true)}>
|
|
291
|
+
Open Session Timeout Warning
|
|
292
|
+
</Button>
|
|
293
|
+
<Modal open={open} title="Your session is about to expire">
|
|
294
|
+
<Modal.Body>
|
|
295
|
+
<p>
|
|
296
|
+
For security reasons, your session will expire in 2 minutes due to inactivity. Choose an
|
|
297
|
+
option below to continue — you cannot dismiss this dialog by pressing Escape or clicking
|
|
298
|
+
outside it.
|
|
299
|
+
</p>
|
|
300
|
+
</Modal.Body>
|
|
301
|
+
<Modal.Footer>
|
|
302
|
+
<Button variant="secondary" onClick={() => setOpen(false)}>
|
|
303
|
+
Log out now
|
|
304
|
+
</Button>
|
|
305
|
+
<Button variant="primary" onClick={() => setOpen(false)}>
|
|
306
|
+
Stay logged in
|
|
307
|
+
</Button>
|
|
308
|
+
</Modal.Footer>
|
|
309
|
+
</Modal>
|
|
310
|
+
</>
|
|
311
|
+
);
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
const HideCloseButtonTemplate = () => {
|
|
315
|
+
const [open, setOpen] = useState(false);
|
|
316
|
+
return (
|
|
317
|
+
<>
|
|
318
|
+
<Button variant="primary" onClick={() => setOpen(true)}>
|
|
319
|
+
Review Data Export
|
|
320
|
+
</Button>
|
|
321
|
+
<Modal
|
|
322
|
+
open={open}
|
|
323
|
+
closeHandler={() => setOpen(false)}
|
|
324
|
+
hideCloseButton
|
|
325
|
+
title="Export Pupil Data"
|
|
326
|
+
>
|
|
327
|
+
<Modal.Body>
|
|
328
|
+
<p>
|
|
329
|
+
You are about to export sensitive pupil data. Please review the export settings before
|
|
330
|
+
proceeding. Press
|
|
331
|
+
{' '}
|
|
332
|
+
<strong>Escape</strong>
|
|
333
|
+
{' '}
|
|
334
|
+
or select an action button to close this
|
|
335
|
+
dialog — the X button is intentionally hidden for this flow.
|
|
336
|
+
</p>
|
|
337
|
+
</Modal.Body>
|
|
338
|
+
<Modal.Footer>
|
|
339
|
+
<Button variant="secondary" onClick={() => setOpen(false)}>
|
|
340
|
+
Cancel
|
|
341
|
+
</Button>
|
|
342
|
+
<Button variant="primary" onClick={() => setOpen(false)}>
|
|
343
|
+
Export Data
|
|
344
|
+
</Button>
|
|
345
|
+
</Modal.Footer>
|
|
346
|
+
</Modal>
|
|
347
|
+
</>
|
|
348
|
+
);
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
const ManualHeaderCompositionTemplate = () => {
|
|
352
|
+
const [open, setOpen] = useState(false);
|
|
353
|
+
return (
|
|
354
|
+
<>
|
|
355
|
+
<Button variant="primary" onClick={() => setOpen(true)}>
|
|
356
|
+
Edit Year Group Settings
|
|
357
|
+
</Button>
|
|
358
|
+
{/* hideCloseButton suppresses the auto top-right X so the manual one in the header is the only dismiss button */}
|
|
359
|
+
<Modal open={open} closeHandler={() => setOpen(false)} hideCloseButton>
|
|
360
|
+
<Modal.Header>
|
|
361
|
+
<div
|
|
362
|
+
style={{
|
|
363
|
+
display: 'flex',
|
|
364
|
+
flexDirection: 'column',
|
|
365
|
+
gap: 'var(--spacing-xsmall)',
|
|
366
|
+
flex: 1,
|
|
367
|
+
}}
|
|
368
|
+
>
|
|
369
|
+
<Modal.Title>Year Group Settings</Modal.Title>
|
|
370
|
+
<p style={{ color: 'var(--color-grey-600)', margin: 0, fontSize: 'var(--font-size-2-13)' }}>
|
|
371
|
+
Year 9 · Autumn Term 2024
|
|
372
|
+
</p>
|
|
373
|
+
</div>
|
|
374
|
+
<Modal.CloseButton />
|
|
375
|
+
</Modal.Header>
|
|
376
|
+
<Modal.Body>
|
|
377
|
+
<p>
|
|
378
|
+
Adjust the settings for this year group. Changes will apply to all classes within Year 9
|
|
379
|
+
for the current term.
|
|
380
|
+
</p>
|
|
381
|
+
</Modal.Body>
|
|
382
|
+
<Modal.Footer>
|
|
383
|
+
<Button variant="secondary" onClick={() => setOpen(false)}>
|
|
384
|
+
Cancel
|
|
385
|
+
</Button>
|
|
386
|
+
<Button variant="primary" onClick={() => setOpen(false)}>
|
|
387
|
+
Save Settings
|
|
388
|
+
</Button>
|
|
389
|
+
</Modal.Footer>
|
|
390
|
+
</Modal>
|
|
391
|
+
</>
|
|
392
|
+
);
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
const LongScrollingContentTemplate = () => {
|
|
396
|
+
const [open, setOpen] = useState(false);
|
|
397
|
+
return (
|
|
398
|
+
<>
|
|
399
|
+
<Button variant="primary" onClick={() => setOpen(true)}>
|
|
400
|
+
View Attendance Policy
|
|
401
|
+
</Button>
|
|
402
|
+
<Modal open={open} closeHandler={() => setOpen(false)} title="Attendance Policy">
|
|
403
|
+
<Modal.Body>
|
|
404
|
+
{Array.from({ length: 12 }).map((_, i) => (
|
|
405
|
+
<p key={i} style={{ marginBottom: 'var(--spacing-large)' }}>
|
|
406
|
+
Section
|
|
407
|
+
{' '}
|
|
408
|
+
{i + 1}
|
|
409
|
+
: Attendance records must be completed by 9:30 am each school day.
|
|
410
|
+
Persistent absence is defined as missing 10% or more of sessions. Parents and carers
|
|
411
|
+
must be notified when a pupil's attendance falls below 90%. Schools are required
|
|
412
|
+
to keep accurate records for inspection and reporting purposes.
|
|
413
|
+
</p>
|
|
414
|
+
))}
|
|
415
|
+
</Modal.Body>
|
|
416
|
+
<Modal.Footer>
|
|
417
|
+
<Button variant="secondary" onClick={() => setOpen(false)}>
|
|
418
|
+
Close
|
|
419
|
+
</Button>
|
|
420
|
+
<Button variant="primary" onClick={() => setOpen(false)}>
|
|
421
|
+
Acknowledge Policy
|
|
422
|
+
</Button>
|
|
423
|
+
</Modal.Footer>
|
|
424
|
+
</Modal>
|
|
425
|
+
</>
|
|
426
|
+
);
|
|
427
|
+
};
|
|
428
|
+
|
|
429
|
+
const WithFormContentTemplate = () => {
|
|
430
|
+
const [open, setOpen] = useState(false);
|
|
431
|
+
const [firstName, setFirstName] = useState('');
|
|
432
|
+
const [lastName, setLastName] = useState('');
|
|
433
|
+
const [yearGroup, setYearGroup] = useState('');
|
|
434
|
+
|
|
435
|
+
const handleSave = () => {
|
|
436
|
+
setOpen(false);
|
|
437
|
+
setFirstName('');
|
|
438
|
+
setLastName('');
|
|
439
|
+
setYearGroup('');
|
|
440
|
+
};
|
|
441
|
+
|
|
442
|
+
return (
|
|
443
|
+
<>
|
|
444
|
+
<Button variant="primary" onClick={() => setOpen(true)}>
|
|
445
|
+
Add New Pupil
|
|
446
|
+
</Button>
|
|
447
|
+
<Modal open={open} closeHandler={() => setOpen(false)} title="Add New Pupil">
|
|
448
|
+
<Modal.Body>
|
|
449
|
+
<p style={{ marginBottom: 'var(--spacing-large)' }}>
|
|
450
|
+
Enter the pupil's details below. Tab through fields to navigate — focus is trapped
|
|
451
|
+
within this dialog.
|
|
452
|
+
</p>
|
|
453
|
+
<FormField
|
|
454
|
+
label="First name"
|
|
455
|
+
id="pupil-first-name"
|
|
456
|
+
inputType="text"
|
|
457
|
+
inputProps={{
|
|
458
|
+
value: firstName,
|
|
459
|
+
onChange: e => setFirstName(e.target.value),
|
|
460
|
+
placeholder: 'e.g. Emily',
|
|
461
|
+
}}
|
|
462
|
+
/>
|
|
463
|
+
<FormField
|
|
464
|
+
label="Last name"
|
|
465
|
+
id="pupil-last-name"
|
|
466
|
+
inputType="text"
|
|
467
|
+
inputProps={{
|
|
468
|
+
value: lastName,
|
|
469
|
+
onChange: e => setLastName(e.target.value),
|
|
470
|
+
placeholder: 'e.g. Clarke',
|
|
471
|
+
}}
|
|
472
|
+
/>
|
|
473
|
+
<FormField
|
|
474
|
+
label="Year group"
|
|
475
|
+
id="pupil-year-group"
|
|
476
|
+
inputType="text"
|
|
477
|
+
inputProps={{
|
|
478
|
+
value: yearGroup,
|
|
479
|
+
onChange: e => setYearGroup(e.target.value),
|
|
480
|
+
placeholder: 'e.g. Year 7',
|
|
481
|
+
}}
|
|
482
|
+
/>
|
|
483
|
+
</Modal.Body>
|
|
484
|
+
<Modal.Footer>
|
|
485
|
+
<Button variant="secondary" onClick={() => setOpen(false)}>
|
|
486
|
+
Cancel
|
|
487
|
+
</Button>
|
|
488
|
+
<Button variant="primary" onClick={handleSave}>
|
|
489
|
+
Add Pupil
|
|
490
|
+
</Button>
|
|
491
|
+
</Modal.Footer>
|
|
492
|
+
</Modal>
|
|
493
|
+
</>
|
|
494
|
+
);
|
|
495
|
+
};
|
|
496
|
+
|
|
497
|
+
const DeleteConfirmationTemplate = () => {
|
|
498
|
+
const [open, setOpen] = useState(false);
|
|
499
|
+
return (
|
|
500
|
+
<>
|
|
501
|
+
<Button variant="secondary-destructive" onClick={() => setOpen(true)}>
|
|
502
|
+
Remove Pupil
|
|
503
|
+
</Button>
|
|
504
|
+
{/* No closeHandler — the user must explicitly choose Cancel or Remove */}
|
|
505
|
+
<Modal open={open} title="Remove Emily Clarke?">
|
|
506
|
+
<Modal.Body>
|
|
507
|
+
<p style={{ marginBottom: 'var(--spacing-small)' }}>
|
|
508
|
+
You are about to permanently remove
|
|
509
|
+
{' '}
|
|
510
|
+
<strong>Emily Clarke (Year 9)</strong>
|
|
511
|
+
{' '}
|
|
512
|
+
from the
|
|
513
|
+
register. This action cannot be undone.
|
|
514
|
+
</p>
|
|
515
|
+
<p>
|
|
516
|
+
All associated assessment records, attendance data, and report history will be archived
|
|
517
|
+
and will no longer appear in active reports.
|
|
518
|
+
</p>
|
|
519
|
+
</Modal.Body>
|
|
520
|
+
<Modal.Footer>
|
|
521
|
+
<Button variant="secondary" onClick={() => setOpen(false)}>
|
|
522
|
+
Cancel
|
|
523
|
+
</Button>
|
|
524
|
+
<Button variant="primary-destructive" onClick={() => setOpen(false)}>
|
|
525
|
+
Remove Pupil
|
|
526
|
+
</Button>
|
|
527
|
+
</Modal.Footer>
|
|
528
|
+
</Modal>
|
|
529
|
+
</>
|
|
530
|
+
);
|
|
11
531
|
};
|
|
12
532
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
533
|
+
const WithDropdownInsideModalTemplate = () => {
|
|
534
|
+
const [open, setOpen] = useState(false);
|
|
535
|
+
return (
|
|
536
|
+
<>
|
|
537
|
+
<Button variant="primary" onClick={() => setOpen(true)}>
|
|
538
|
+
Assign Class Teacher
|
|
539
|
+
</Button>
|
|
540
|
+
<Modal open={open} closeHandler={() => setOpen(false)} title="Assign Class Teacher">
|
|
541
|
+
<Modal.Body>
|
|
542
|
+
<p style={{ marginBottom: 'var(--spacing-large)' }}>
|
|
543
|
+
Select a teacher to assign as the lead for this class. The dropdown portals into the
|
|
544
|
+
modal overlay automatically — no additional configuration required.
|
|
545
|
+
</p>
|
|
546
|
+
<Dropdown>
|
|
547
|
+
<Dropdown.Trigger>
|
|
548
|
+
<Button variant="dropdown">Select a teacher</Button>
|
|
549
|
+
</Dropdown.Trigger>
|
|
550
|
+
<Dropdown.Content>
|
|
551
|
+
<Dropdown.Item onSelect={() => {}}>Ms A. Johnson — English</Dropdown.Item>
|
|
552
|
+
<Dropdown.Item onSelect={() => {}}>Mr B. Patel — Mathematics</Dropdown.Item>
|
|
553
|
+
<Dropdown.Item onSelect={() => {}}>Mrs C. Williams — Science</Dropdown.Item>
|
|
554
|
+
<Dropdown.Item onSelect={() => {}}>Mr D. Thompson — History</Dropdown.Item>
|
|
555
|
+
<Dropdown.Item onSelect={() => {}}>Ms E. Okonkwo — Art</Dropdown.Item>
|
|
556
|
+
</Dropdown.Content>
|
|
557
|
+
</Dropdown>
|
|
558
|
+
</Modal.Body>
|
|
559
|
+
<Modal.Footer>
|
|
560
|
+
<Button variant="secondary" onClick={() => setOpen(false)}>
|
|
561
|
+
Cancel
|
|
562
|
+
</Button>
|
|
563
|
+
<Button variant="primary" onClick={() => setOpen(false)}>
|
|
564
|
+
Assign Teacher
|
|
565
|
+
</Button>
|
|
566
|
+
</Modal.Footer>
|
|
567
|
+
</Modal>
|
|
568
|
+
</>
|
|
569
|
+
);
|
|
570
|
+
};
|
|
571
|
+
|
|
572
|
+
// ---------------------------------------------------------------------------
|
|
573
|
+
// Stories
|
|
574
|
+
// ---------------------------------------------------------------------------
|
|
575
|
+
|
|
576
|
+
export const Default: Story = withDescription(
|
|
577
|
+
{
|
|
578
|
+
parameters: {
|
|
579
|
+
controls: { disable: true },
|
|
580
|
+
docs: {
|
|
581
|
+
source: {
|
|
582
|
+
language: 'tsx',
|
|
583
|
+
code: `
|
|
584
|
+
import { useState } from 'react';
|
|
585
|
+
import { Button, Modal } from '@arbor-education/design-system.components';
|
|
586
|
+
|
|
587
|
+
function AddAssessmentPeriodModal() {
|
|
588
|
+
const [open, setOpen] = useState(false);
|
|
589
|
+
return (
|
|
590
|
+
<>
|
|
591
|
+
<Button variant="primary" onClick={() => setOpen(true)}>
|
|
592
|
+
Add Assessment Period
|
|
593
|
+
</Button>
|
|
594
|
+
<Modal open={open} closeHandler={() => setOpen(false)} title="Add Assessment Period">
|
|
595
|
+
<Modal.Body>
|
|
596
|
+
<p>
|
|
597
|
+
Create a new assessment period for the current academic year.
|
|
598
|
+
</p>
|
|
599
|
+
</Modal.Body>
|
|
600
|
+
<Modal.Footer>
|
|
601
|
+
<Button variant="secondary" onClick={() => setOpen(false)}>Cancel</Button>
|
|
602
|
+
<Button variant="primary" onClick={() => setOpen(false)}>Add Assessment Period</Button>
|
|
603
|
+
</Modal.Footer>
|
|
604
|
+
</Modal>
|
|
605
|
+
</>
|
|
606
|
+
);
|
|
607
|
+
}
|
|
608
|
+
export default AddAssessmentPeriodModal;
|
|
609
|
+
`.trim(),
|
|
610
|
+
},
|
|
611
|
+
},
|
|
612
|
+
},
|
|
613
|
+
render: () => <DefaultTemplate />,
|
|
18
614
|
},
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
return (
|
|
22
|
-
<>
|
|
23
|
-
<Button type="primary" onClick={() => setShowModal(true)}>Open Modal</Button>
|
|
24
|
-
<Modal {...args} open={showModal} closeHandler={() => setShowModal(false)}>
|
|
25
|
-
<Modal.Body>Cheddar and Pickle were Norwich terriers who lived next door. Cheddar, the older, was cautious and methodical; Pickle, the younger, was bold and impulsive. One morning, Pickle spotted a squirrel in the garden and gave chase, while Cheddar watched from the porch. When Pickle got stuck under the neighbor’s fence, Cheddar squeezed through a gap and nudged him free. From then on, they worked as a team: Pickle scouted, and Cheddar planned. They became inseparable, exploring the neighborhood together and always looking out for each other.</Modal.Body>
|
|
26
|
-
<Modal.Footer>
|
|
27
|
-
<Button variant="tertiary" onClick={() => setShowModal(false)}>Close</Button>
|
|
28
|
-
<Button variant="primary" onClick={() => setShowModal(false)}>Primary Action</Button>
|
|
29
|
-
</Modal.Footer>
|
|
30
|
-
</Modal>
|
|
31
|
-
</>
|
|
615
|
+
'The standard controlled modal pattern. A trigger button opens the modal; `closeHandler` enables the X button and Escape key. The `title` prop auto-renders `Modal.Header` + `Modal.Title` with no manual composition needed.',
|
|
616
|
+
);
|
|
32
617
|
|
|
33
|
-
|
|
618
|
+
export const NoCloseHandler: Story = withDescription(
|
|
619
|
+
{
|
|
620
|
+
parameters: {
|
|
621
|
+
controls: { disable: true },
|
|
622
|
+
docs: {
|
|
623
|
+
source: {
|
|
624
|
+
language: 'tsx',
|
|
625
|
+
code: `
|
|
626
|
+
import { useState } from 'react';
|
|
627
|
+
import { Button, Modal } from '@arbor-education/design-system.components';
|
|
628
|
+
|
|
629
|
+
// No closeHandler — no X button, no Escape key, no overlay dismiss.
|
|
630
|
+
// The user MUST choose an action button.
|
|
631
|
+
function SessionTimeoutModal() {
|
|
632
|
+
const [open, setOpen] = useState(false);
|
|
633
|
+
return (
|
|
634
|
+
<>
|
|
635
|
+
<Button variant="primary" onClick={() => setOpen(true)}>
|
|
636
|
+
Open Session Timeout Warning
|
|
637
|
+
</Button>
|
|
638
|
+
<Modal open={open} title="Your session is about to expire">
|
|
639
|
+
<Modal.Body>
|
|
640
|
+
<p>
|
|
641
|
+
For security reasons, your session will expire in 2 minutes.
|
|
642
|
+
Choose an option below to continue.
|
|
643
|
+
</p>
|
|
644
|
+
</Modal.Body>
|
|
645
|
+
<Modal.Footer>
|
|
646
|
+
<Button variant="secondary" onClick={() => setOpen(false)}>Log out now</Button>
|
|
647
|
+
<Button variant="primary" onClick={() => setOpen(false)}>Stay logged in</Button>
|
|
648
|
+
</Modal.Footer>
|
|
649
|
+
</Modal>
|
|
650
|
+
</>
|
|
651
|
+
);
|
|
652
|
+
}
|
|
653
|
+
export default SessionTimeoutModal;
|
|
654
|
+
`.trim(),
|
|
655
|
+
},
|
|
656
|
+
},
|
|
657
|
+
},
|
|
658
|
+
render: () => <NoCloseHandlerTemplate />,
|
|
34
659
|
},
|
|
35
|
-
|
|
660
|
+
[
|
|
661
|
+
'**No `closeHandler` provided.** The X button is not rendered, Escape does nothing, and clicking',
|
|
662
|
+
'the overlay does nothing. The user must select one of the action buttons. Use this pattern for',
|
|
663
|
+
'flows where an accidental dismiss would be confusing or dangerous — session timeouts, required',
|
|
664
|
+
'acknowledgements, and critical confirmations.',
|
|
665
|
+
].join(' '),
|
|
666
|
+
);
|
|
36
667
|
|
|
37
|
-
export
|
|
668
|
+
export const HideCloseButton: Story = withDescription(
|
|
669
|
+
{
|
|
670
|
+
parameters: {
|
|
671
|
+
controls: { disable: true },
|
|
672
|
+
docs: {
|
|
673
|
+
source: {
|
|
674
|
+
language: 'tsx',
|
|
675
|
+
code: `
|
|
676
|
+
import { useState } from 'react';
|
|
677
|
+
import { Button, Modal } from '@arbor-education/design-system.components';
|
|
678
|
+
|
|
679
|
+
function ExportModal() {
|
|
680
|
+
const [open, setOpen] = useState(false);
|
|
681
|
+
return (
|
|
682
|
+
<>
|
|
683
|
+
<Button variant="primary" onClick={() => setOpen(true)}>Review Data Export</Button>
|
|
684
|
+
<Modal
|
|
685
|
+
open={open}
|
|
686
|
+
closeHandler={() => setOpen(false)}
|
|
687
|
+
hideCloseButton
|
|
688
|
+
title="Export Pupil Data"
|
|
689
|
+
>
|
|
690
|
+
<Modal.Body>
|
|
691
|
+
<p>Review the export settings below. Press Escape to cancel.</p>
|
|
692
|
+
</Modal.Body>
|
|
693
|
+
<Modal.Footer>
|
|
694
|
+
<Button variant="secondary" onClick={() => setOpen(false)}>Cancel</Button>
|
|
695
|
+
<Button variant="primary" onClick={() => setOpen(false)}>Export Data</Button>
|
|
696
|
+
</Modal.Footer>
|
|
697
|
+
</Modal>
|
|
698
|
+
</>
|
|
699
|
+
);
|
|
700
|
+
}
|
|
701
|
+
export default ExportModal;
|
|
702
|
+
`.trim(),
|
|
703
|
+
},
|
|
704
|
+
},
|
|
705
|
+
},
|
|
706
|
+
render: () => <HideCloseButtonTemplate />,
|
|
707
|
+
},
|
|
708
|
+
[
|
|
709
|
+
'`hideCloseButton` removes the top-right X button while keeping the Escape key active (because',
|
|
710
|
+
'`closeHandler` is still provided). Use this for flows that want a cleaner look without a visible',
|
|
711
|
+
'close affordance — the user can still press Escape to dismiss.',
|
|
712
|
+
].join(' '),
|
|
713
|
+
);
|
|
714
|
+
|
|
715
|
+
export const ManualHeaderComposition: Story = withDescription(
|
|
716
|
+
{
|
|
717
|
+
parameters: {
|
|
718
|
+
controls: { disable: true },
|
|
719
|
+
docs: {
|
|
720
|
+
source: {
|
|
721
|
+
language: 'tsx',
|
|
722
|
+
code: `
|
|
723
|
+
import { useState } from 'react';
|
|
724
|
+
import { Button, Modal } from '@arbor-education/design-system.components';
|
|
725
|
+
|
|
726
|
+
function YearGroupSettingsModal() {
|
|
727
|
+
const [open, setOpen] = useState(false);
|
|
728
|
+
return (
|
|
729
|
+
<>
|
|
730
|
+
<Button variant="primary" onClick={() => setOpen(true)}>Edit Year Group Settings</Button>
|
|
731
|
+
{/* hideCloseButton suppresses the auto top-right X; the manual CloseButton in the header takes its place */}
|
|
732
|
+
<Modal open={open} closeHandler={() => setOpen(false)} hideCloseButton>
|
|
733
|
+
<Modal.Header>
|
|
734
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--spacing-xsmall)', flex: 1 }}>
|
|
735
|
+
<Modal.Title>Year Group Settings</Modal.Title>
|
|
736
|
+
<p style={{ color: 'var(--color-grey-600)', margin: 0 }}>
|
|
737
|
+
Year 9 · Autumn Term 2024
|
|
738
|
+
</p>
|
|
739
|
+
</div>
|
|
740
|
+
<Modal.CloseButton />
|
|
741
|
+
</Modal.Header>
|
|
742
|
+
<Modal.Body>
|
|
743
|
+
<p>Adjust the settings for this year group.</p>
|
|
744
|
+
</Modal.Body>
|
|
745
|
+
<Modal.Footer>
|
|
746
|
+
<Button variant="secondary" onClick={() => setOpen(false)}>Cancel</Button>
|
|
747
|
+
<Button variant="primary" onClick={() => setOpen(false)}>Save Settings</Button>
|
|
748
|
+
</Modal.Footer>
|
|
749
|
+
</Modal>
|
|
750
|
+
</>
|
|
751
|
+
);
|
|
752
|
+
}
|
|
753
|
+
export default YearGroupSettingsModal;
|
|
754
|
+
`.trim(),
|
|
755
|
+
},
|
|
756
|
+
},
|
|
757
|
+
},
|
|
758
|
+
render: () => <ManualHeaderCompositionTemplate />,
|
|
759
|
+
},
|
|
760
|
+
[
|
|
761
|
+
'Full header composition using `Modal.Header` + `Modal.Title` instead of the `title` prop shortcut.',
|
|
762
|
+
'Use this pattern when the header needs more than a plain title string — a subtitle, an icon, or',
|
|
763
|
+
'custom header controls. Pass `hideCloseButton` on `<Modal>` to suppress the auto top-right X,',
|
|
764
|
+
'then place `<Modal.CloseButton />` inside the header where you want it.',
|
|
765
|
+
].join(' '),
|
|
766
|
+
);
|
|
767
|
+
|
|
768
|
+
export const LongScrollingContent: Story = withDescription(
|
|
769
|
+
{
|
|
770
|
+
parameters: {
|
|
771
|
+
controls: { disable: true },
|
|
772
|
+
docs: {
|
|
773
|
+
source: {
|
|
774
|
+
language: 'tsx',
|
|
775
|
+
code: `
|
|
776
|
+
import { useState } from 'react';
|
|
777
|
+
import { Button, Modal } from '@arbor-education/design-system.components';
|
|
778
|
+
|
|
779
|
+
function AttendancePolicyModal() {
|
|
780
|
+
const [open, setOpen] = useState(false);
|
|
781
|
+
return (
|
|
782
|
+
<>
|
|
783
|
+
<Button variant="primary" onClick={() => setOpen(true)}>View Attendance Policy</Button>
|
|
784
|
+
<Modal open={open} closeHandler={() => setOpen(false)} title="Attendance Policy">
|
|
785
|
+
<Modal.Body>
|
|
786
|
+
{/* Long content — Modal.Body scrolls independently; header and footer stay pinned */}
|
|
787
|
+
{sections.map((text, i) => <p key={i}>{text}</p>)}
|
|
788
|
+
</Modal.Body>
|
|
789
|
+
<Modal.Footer>
|
|
790
|
+
<Button variant="secondary" onClick={() => setOpen(false)}>Close</Button>
|
|
791
|
+
<Button variant="primary" onClick={() => setOpen(false)}>Acknowledge Policy</Button>
|
|
792
|
+
</Modal.Footer>
|
|
793
|
+
</Modal>
|
|
794
|
+
</>
|
|
795
|
+
);
|
|
796
|
+
}
|
|
797
|
+
export default AttendancePolicyModal;
|
|
798
|
+
`.trim(),
|
|
799
|
+
},
|
|
800
|
+
},
|
|
801
|
+
},
|
|
802
|
+
render: () => <LongScrollingContentTemplate />,
|
|
803
|
+
},
|
|
804
|
+
[
|
|
805
|
+
'`Modal.Body` scrolls independently when content overflows — the header and footer remain pinned.',
|
|
806
|
+
'The modal container enforces `max-height: calc(100vh - var(--spacing-medium))` so the dialog',
|
|
807
|
+
'never exceeds the viewport. No extra CSS needed on the consumer side.',
|
|
808
|
+
].join(' '),
|
|
809
|
+
);
|
|
810
|
+
|
|
811
|
+
export const WithFormContent: Story = withDescription(
|
|
812
|
+
{
|
|
813
|
+
parameters: {
|
|
814
|
+
controls: { disable: true },
|
|
815
|
+
docs: {
|
|
816
|
+
source: {
|
|
817
|
+
language: 'tsx',
|
|
818
|
+
code: `
|
|
819
|
+
import { useState } from 'react';
|
|
820
|
+
import { Button, FormField, Modal } from '@arbor-education/design-system.components';
|
|
821
|
+
|
|
822
|
+
function AddPupilModal() {
|
|
823
|
+
const [open, setOpen] = useState(false);
|
|
824
|
+
const [firstName, setFirstName] = useState('');
|
|
825
|
+
const [lastName, setLastName] = useState('');
|
|
826
|
+
|
|
827
|
+
return (
|
|
828
|
+
<>
|
|
829
|
+
<Button variant="primary" onClick={() => setOpen(true)}>Add New Pupil</Button>
|
|
830
|
+
<Modal open={open} closeHandler={() => setOpen(false)} title="Add New Pupil">
|
|
831
|
+
<Modal.Body>
|
|
832
|
+
<FormField
|
|
833
|
+
label="First name"
|
|
834
|
+
id="pupil-first-name"
|
|
835
|
+
inputType="text"
|
|
836
|
+
inputProps={{
|
|
837
|
+
value: firstName,
|
|
838
|
+
onChange: (e) => setFirstName(e.target.value),
|
|
839
|
+
placeholder: 'e.g. Emily',
|
|
840
|
+
}}
|
|
841
|
+
/>
|
|
842
|
+
<FormField
|
|
843
|
+
label="Last name"
|
|
844
|
+
id="pupil-last-name"
|
|
845
|
+
inputType="text"
|
|
846
|
+
inputProps={{
|
|
847
|
+
value: lastName,
|
|
848
|
+
onChange: (e) => setLastName(e.target.value),
|
|
849
|
+
placeholder: 'e.g. Clarke',
|
|
850
|
+
}}
|
|
851
|
+
/>
|
|
852
|
+
</Modal.Body>
|
|
853
|
+
<Modal.Footer>
|
|
854
|
+
<Button variant="secondary" onClick={() => setOpen(false)}>Cancel</Button>
|
|
855
|
+
<Button variant="primary" onClick={() => { setOpen(false); }}>Add Pupil</Button>
|
|
856
|
+
</Modal.Footer>
|
|
857
|
+
</Modal>
|
|
858
|
+
</>
|
|
859
|
+
);
|
|
860
|
+
}
|
|
861
|
+
export default AddPupilModal;
|
|
862
|
+
`.trim(),
|
|
863
|
+
},
|
|
864
|
+
},
|
|
865
|
+
},
|
|
866
|
+
render: () => <WithFormContentTemplate />,
|
|
867
|
+
},
|
|
868
|
+
[
|
|
869
|
+
'Form fields inside `Modal.Body` demonstrating the focus trap — Tab and Shift+Tab cycle only',
|
|
870
|
+
'within the open dialog. Radix UI Dialog handles this automatically; no additional `aria` or',
|
|
871
|
+
'`tabIndex` props are required. Use `FormField` with `inputType` + `inputProps` for consistent',
|
|
872
|
+
'layout and accessibility wiring.',
|
|
873
|
+
].join(' '),
|
|
874
|
+
);
|
|
875
|
+
|
|
876
|
+
export const DeleteConfirmation: Story = withDescription(
|
|
877
|
+
{
|
|
878
|
+
parameters: {
|
|
879
|
+
controls: { disable: true },
|
|
880
|
+
docs: {
|
|
881
|
+
source: {
|
|
882
|
+
language: 'tsx',
|
|
883
|
+
code: `
|
|
884
|
+
import { useState } from 'react';
|
|
885
|
+
import { Button, Modal } from '@arbor-education/design-system.components';
|
|
886
|
+
|
|
887
|
+
function RemovePupilConfirmation() {
|
|
888
|
+
const [open, setOpen] = useState(false);
|
|
889
|
+
return (
|
|
890
|
+
<>
|
|
891
|
+
<Button variant="secondary-destructive" onClick={() => setOpen(true)}>Remove Pupil</Button>
|
|
892
|
+
{/* No closeHandler — user must explicitly choose Cancel or Remove */}
|
|
893
|
+
<Modal open={open} title="Remove Emily Clarke?">
|
|
894
|
+
<Modal.Body>
|
|
895
|
+
<p>
|
|
896
|
+
You are about to permanently remove <strong>Emily Clarke (Year 9)</strong> from the
|
|
897
|
+
register. This action cannot be undone.
|
|
898
|
+
</p>
|
|
899
|
+
</Modal.Body>
|
|
900
|
+
<Modal.Footer>
|
|
901
|
+
<Button variant="secondary" onClick={() => setOpen(false)}>Cancel</Button>
|
|
902
|
+
<Button variant="primary-destructive" onClick={() => setOpen(false)}>Remove Pupil</Button>
|
|
903
|
+
</Modal.Footer>
|
|
904
|
+
</Modal>
|
|
905
|
+
</>
|
|
906
|
+
);
|
|
907
|
+
}
|
|
908
|
+
export default RemovePupilConfirmation;
|
|
909
|
+
`.trim(),
|
|
910
|
+
},
|
|
911
|
+
},
|
|
912
|
+
},
|
|
913
|
+
render: () => <DeleteConfirmationTemplate />,
|
|
914
|
+
},
|
|
915
|
+
[
|
|
916
|
+
'The destructive confirmation pattern. No `closeHandler` is provided — the user must explicitly',
|
|
917
|
+
'choose "Cancel" or "Remove Pupil". There is no X button and Escape does nothing, preventing an',
|
|
918
|
+
'accidental dismiss of a destructive action. Use `variant="primary-destructive"` on the confirm',
|
|
919
|
+
'button and `variant="secondary"` on the cancel button.',
|
|
920
|
+
].join(' '),
|
|
921
|
+
);
|
|
922
|
+
|
|
923
|
+
export const WithDropdownInsideModal: Story = withDescription(
|
|
924
|
+
{
|
|
925
|
+
parameters: {
|
|
926
|
+
controls: { disable: true },
|
|
927
|
+
docs: {
|
|
928
|
+
source: {
|
|
929
|
+
language: 'tsx',
|
|
930
|
+
code: `
|
|
931
|
+
import { useState } from 'react';
|
|
932
|
+
import { Button, Dropdown, Modal } from '@arbor-education/design-system.components';
|
|
933
|
+
|
|
934
|
+
function AssignTeacherModal() {
|
|
935
|
+
const [open, setOpen] = useState(false);
|
|
936
|
+
return (
|
|
937
|
+
<>
|
|
938
|
+
<Button variant="primary" onClick={() => setOpen(true)}>Assign Class Teacher</Button>
|
|
939
|
+
<Modal open={open} closeHandler={() => setOpen(false)} title="Assign Class Teacher">
|
|
940
|
+
<Modal.Body>
|
|
941
|
+
<p>Select a teacher to assign as the lead for this class.</p>
|
|
942
|
+
<Dropdown>
|
|
943
|
+
<Dropdown.Trigger>
|
|
944
|
+
<Button variant="dropdown">Select a teacher</Button>
|
|
945
|
+
</Dropdown.Trigger>
|
|
946
|
+
<Dropdown.Content>
|
|
947
|
+
<Dropdown.Item onSelect={() => {}}>Ms A. Johnson — English</Dropdown.Item>
|
|
948
|
+
<Dropdown.Item onSelect={() => {}}>Mr B. Patel — Mathematics</Dropdown.Item>
|
|
949
|
+
<Dropdown.Item onSelect={() => {}}>Mrs C. Williams — Science</Dropdown.Item>
|
|
950
|
+
</Dropdown.Content>
|
|
951
|
+
</Dropdown>
|
|
952
|
+
</Modal.Body>
|
|
953
|
+
<Modal.Footer>
|
|
954
|
+
<Button variant="secondary" onClick={() => setOpen(false)}>Cancel</Button>
|
|
955
|
+
<Button variant="primary" onClick={() => setOpen(false)}>Assign Teacher</Button>
|
|
956
|
+
</Modal.Footer>
|
|
957
|
+
</Modal>
|
|
958
|
+
</>
|
|
959
|
+
);
|
|
960
|
+
}
|
|
961
|
+
export default AssignTeacherModal;
|
|
962
|
+
`.trim(),
|
|
963
|
+
},
|
|
964
|
+
},
|
|
965
|
+
},
|
|
966
|
+
render: () => <WithDropdownInsideModalTemplate />,
|
|
967
|
+
},
|
|
968
|
+
[
|
|
969
|
+
'A `Dropdown` inside `Modal.Body`. Because `Modal` registers its overlay ref as a',
|
|
970
|
+
'`PopupParentContext`, the Dropdown menu portals into the modal overlay automatically — it will',
|
|
971
|
+
'never be clipped by the modal boundary or appear behind the overlay. No extra configuration',
|
|
972
|
+
'is required.',
|
|
973
|
+
].join(' '),
|
|
974
|
+
);
|