@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,497 +1,655 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
3
|
+
import {
|
|
4
|
+
Heading as DocHeading,
|
|
5
|
+
Markdown,
|
|
6
|
+
Stories,
|
|
7
|
+
Subtitle,
|
|
8
|
+
Title,
|
|
9
|
+
} from '@storybook/addon-docs/blocks';
|
|
2
10
|
import { ModalManager } from './ModalManager';
|
|
3
|
-
import {
|
|
4
|
-
import { Button } from '
|
|
5
|
-
import {
|
|
6
|
-
import { FormField } from '../../formField/FormField';
|
|
7
|
-
import { generateUuid } from '../../../utils/generateUuid';
|
|
8
|
-
import { Modal } from '../Modal';
|
|
9
|
-
import { fn } from 'storybook/test';
|
|
11
|
+
import { Modal } from 'Components/modal/Modal';
|
|
12
|
+
import { Button } from 'Components/button/Button';
|
|
13
|
+
import { FormField } from 'Components/formField/FormField';
|
|
10
14
|
import { Icon } from 'Components/icon/Icon';
|
|
11
15
|
|
|
12
|
-
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
// Docs page content
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
|
|
20
|
+
const DESCRIPTION_INTRO = [
|
|
21
|
+
'`ModalManager` is a singleton container that opens and closes modals programmatically via PubSub events.',
|
|
22
|
+
'Mount it once near your app root, then call `ModalUtils.addModal(props)` from anywhere in the component',
|
|
23
|
+
'tree — no prop drilling, no shared state. For modals that are tightly coupled to their trigger,',
|
|
24
|
+
'prefer the raw [`Modal`](?path=/docs/components-modals-modal--docs) component with local state instead.',
|
|
25
|
+
].join(' ');
|
|
26
|
+
|
|
27
|
+
const USAGE_GUIDANCE = [
|
|
28
|
+
'### When to use',
|
|
29
|
+
'',
|
|
30
|
+
'- **Deep action handlers** — triggering a modal from an API response, a table row action, or a',
|
|
31
|
+
' notification handler that has no direct access to modal state',
|
|
32
|
+
'- **Cross-cutting concerns** — session-expiry dialogs, global error modals, or app-level confirmations',
|
|
33
|
+
' that can be triggered from any module',
|
|
34
|
+
'- **Avoiding prop drilling** — when the modal trigger and the `Modal` render point are far apart in',
|
|
35
|
+
' the component tree',
|
|
36
|
+
'',
|
|
37
|
+
'---',
|
|
38
|
+
'',
|
|
39
|
+
'### When NOT to use',
|
|
40
|
+
'',
|
|
41
|
+
'| Situation | Use instead |',
|
|
42
|
+
'|---|---|',
|
|
43
|
+
'| Modal is directly triggered by a parent component | Raw [`Modal`](?path=/docs/components-modals-modal--docs) with local `useState` |',
|
|
44
|
+
'| Multiple modals open simultaneously | Not supported — `ModalManager` holds one modal at a time |',
|
|
45
|
+
'| You need full control of `open`/`closeHandler` lifecycle | Raw `Modal` component |',
|
|
46
|
+
].join('\n');
|
|
47
|
+
|
|
48
|
+
const DEVELOPER_NOTES = [
|
|
49
|
+
'### Setup',
|
|
50
|
+
'',
|
|
51
|
+
'Mount `<ModalManager />` once, near your application root:',
|
|
52
|
+
'',
|
|
53
|
+
'```tsx',
|
|
54
|
+
"import { ModalManager } from '@arbor-education/design-system.components';",
|
|
55
|
+
'',
|
|
56
|
+
'function App() {',
|
|
57
|
+
' return (',
|
|
58
|
+
' <>',
|
|
59
|
+
' <ModalManager />',
|
|
60
|
+
' <Router>',
|
|
61
|
+
' {/* rest of your app */}',
|
|
62
|
+
' </Router>',
|
|
63
|
+
' </>',
|
|
64
|
+
' );',
|
|
65
|
+
'}',
|
|
66
|
+
'```',
|
|
67
|
+
'',
|
|
68
|
+
'---',
|
|
69
|
+
'',
|
|
70
|
+
'### ModalUtils API',
|
|
71
|
+
'',
|
|
72
|
+
'```ts',
|
|
73
|
+
"import { ModalUtils } from '@arbor-education/design-system.components';",
|
|
74
|
+
'',
|
|
75
|
+
'// Open a modal — pass any ModalProps except open and closeHandler',
|
|
76
|
+
'ModalUtils.addModal({',
|
|
77
|
+
' title: "Confirm deletion",',
|
|
78
|
+
' children: (',
|
|
79
|
+
' <>',
|
|
80
|
+
' <Modal.Body>Are you sure?</Modal.Body>',
|
|
81
|
+
' <Modal.Footer>',
|
|
82
|
+
' <Button onClick={() => ModalUtils.removeModal()}>Cancel</Button>',
|
|
83
|
+
' <Button variant="primary-destructive" onClick={handleDelete}>Delete</Button>',
|
|
84
|
+
' </Modal.Footer>',
|
|
85
|
+
' </>',
|
|
86
|
+
' ),',
|
|
87
|
+
'});',
|
|
88
|
+
'',
|
|
89
|
+
'// Close the current modal',
|
|
90
|
+
'ModalUtils.removeModal();',
|
|
91
|
+
'',
|
|
92
|
+
'// Close all modals (also clears any queued modal state)',
|
|
93
|
+
'ModalUtils.removeAllModals();',
|
|
94
|
+
'```',
|
|
95
|
+
'',
|
|
96
|
+
'---',
|
|
97
|
+
'',
|
|
98
|
+
'### Critical notes',
|
|
99
|
+
'',
|
|
100
|
+
'**Do NOT pass `closeHandler` via `ModalUtils.addModal()`.**',
|
|
101
|
+
'`ModalManager` always provides its own `closeHandler` — passing one in `modalProps` would override it',
|
|
102
|
+
'and break the X button and Escape key dismissal.',
|
|
103
|
+
'',
|
|
104
|
+
'**`ModalManager` holds one modal at a time.**',
|
|
105
|
+
'Calling `ModalUtils.addModal()` while a modal is open replaces it immediately. If you need a queue or',
|
|
106
|
+
'multiple layered modals, manage that state yourself and call `addModal` once you are ready to show the next.',
|
|
107
|
+
'',
|
|
108
|
+
'**The `modal` prop sets an initial modal at mount.**',
|
|
109
|
+
'Useful for route-based or server-rendered scenarios where the modal state is known before the component mounts.',
|
|
110
|
+
'Once mounted, further changes are driven by `ModalUtils`.',
|
|
111
|
+
'',
|
|
112
|
+
'---',
|
|
113
|
+
'',
|
|
114
|
+
'### Accessibility',
|
|
115
|
+
'',
|
|
116
|
+
'- Focus trapping and restoration are inherited from the underlying `Modal` component',
|
|
117
|
+
'- `ModalManager` always provides a `closeHandler`, so the X button and Escape key are always active',
|
|
118
|
+
'- Ensure modal content includes an accessible title — use `title` prop or `Modal.Header` + `Modal.Title`',
|
|
119
|
+
'',
|
|
120
|
+
'---',
|
|
121
|
+
'',
|
|
122
|
+
'### TypeScript types',
|
|
123
|
+
'',
|
|
124
|
+
'```ts',
|
|
125
|
+
"import { ModalManager } from '@arbor-education/design-system.components';",
|
|
126
|
+
"import type { ModalProps } from '@arbor-education/design-system.components';",
|
|
127
|
+
'',
|
|
128
|
+
'// ModalManagerProps',
|
|
129
|
+
'type ModalManagerProps = {',
|
|
130
|
+
' modal?: ModalProps; // optional initial modal shown at mount',
|
|
131
|
+
'};',
|
|
132
|
+
'```',
|
|
133
|
+
].join('\n');
|
|
134
|
+
|
|
135
|
+
const RELATED_COMPONENTS = [
|
|
136
|
+
'## Related components',
|
|
137
|
+
'',
|
|
138
|
+
'[Modal](?path=/docs/components-modals-modal--docs) · [Button](?path=/docs/components-button--docs) · [FormField](?path=/docs/components-formfield--docs)',
|
|
139
|
+
].join('\n');
|
|
140
|
+
|
|
141
|
+
// ---------------------------------------------------------------------------
|
|
142
|
+
// Docs page
|
|
143
|
+
// ---------------------------------------------------------------------------
|
|
144
|
+
|
|
145
|
+
function ModalManagerDocsPage() {
|
|
146
|
+
return (
|
|
147
|
+
<>
|
|
148
|
+
<Title />
|
|
149
|
+
<Subtitle />
|
|
150
|
+
<Markdown>{DESCRIPTION_INTRO}</Markdown>
|
|
151
|
+
<DocHeading>Usage guidance</DocHeading>
|
|
152
|
+
<Markdown>{USAGE_GUIDANCE}</Markdown>
|
|
153
|
+
<DocHeading>Developer notes</DocHeading>
|
|
154
|
+
<Markdown>{DEVELOPER_NOTES}</Markdown>
|
|
155
|
+
<DocHeading>Examples</DocHeading>
|
|
156
|
+
<Stories title="" />
|
|
157
|
+
<Markdown>{RELATED_COMPONENTS}</Markdown>
|
|
158
|
+
</>
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// ---------------------------------------------------------------------------
|
|
163
|
+
// Meta
|
|
164
|
+
// ---------------------------------------------------------------------------
|
|
165
|
+
|
|
166
|
+
const meta = {
|
|
13
167
|
title: 'Components/Modals/ModalManager',
|
|
168
|
+
component: ModalManager,
|
|
169
|
+
tags: ['autodocs'],
|
|
14
170
|
parameters: {
|
|
15
|
-
layout: '
|
|
171
|
+
layout: 'padded',
|
|
16
172
|
docs: {
|
|
17
|
-
|
|
18
|
-
|
|
173
|
+
page: ModalManagerDocsPage,
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
argTypes: {
|
|
177
|
+
modal: {
|
|
178
|
+
control: false,
|
|
179
|
+
description: [
|
|
180
|
+
'Optional initial modal to show at mount. Pass any `ModalProps` value — `open` and `closeHandler`',
|
|
181
|
+
'are always managed by `ModalManager` itself and cannot be overridden.',
|
|
182
|
+
'Once mounted, further changes are driven by `ModalUtils.addModal()` and `ModalUtils.removeModal()`.',
|
|
183
|
+
].join(' '),
|
|
184
|
+
table: {
|
|
185
|
+
type: { summary: 'ModalProps' },
|
|
186
|
+
defaultValue: { summary: 'undefined' },
|
|
19
187
|
},
|
|
20
188
|
},
|
|
21
189
|
},
|
|
22
|
-
|
|
23
|
-
tags: ['autodocs'],
|
|
24
|
-
};
|
|
190
|
+
} satisfies Meta<typeof ModalManager>;
|
|
25
191
|
|
|
26
192
|
export default meta;
|
|
193
|
+
type Story = StoryObj<typeof meta>;
|
|
27
194
|
|
|
28
|
-
|
|
195
|
+
// ---------------------------------------------------------------------------
|
|
196
|
+
// Helper
|
|
197
|
+
// ---------------------------------------------------------------------------
|
|
29
198
|
|
|
30
|
-
|
|
199
|
+
const withDescription = (story: Story, description: string): Story => ({
|
|
200
|
+
...story,
|
|
201
|
+
parameters: {
|
|
202
|
+
...story.parameters,
|
|
203
|
+
docs: {
|
|
204
|
+
...story.parameters?.docs,
|
|
205
|
+
description: {
|
|
206
|
+
story: description,
|
|
207
|
+
},
|
|
208
|
+
},
|
|
209
|
+
},
|
|
210
|
+
});
|
|
31
211
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
212
|
+
// ---------------------------------------------------------------------------
|
|
213
|
+
// Templates
|
|
214
|
+
// NOTE: Story templates use raw <Modal> with local useState so the portal
|
|
215
|
+
// renders to document.body and works correctly in Storybook's canvas context.
|
|
216
|
+
// Production code uses ModalUtils.addModal() — see each story's source code.
|
|
217
|
+
// ---------------------------------------------------------------------------
|
|
38
218
|
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
219
|
+
const DefaultTemplate = () => {
|
|
220
|
+
const [open, setOpen] = useState(false);
|
|
221
|
+
return (
|
|
222
|
+
<>
|
|
223
|
+
<Modal
|
|
224
|
+
open={open}
|
|
225
|
+
closeHandler={() => setOpen(false)}
|
|
226
|
+
title="Assessment Period Added"
|
|
227
|
+
>
|
|
228
|
+
<Modal.Body>
|
|
229
|
+
<p>The Spring 2026 assessment period has been added successfully. Pupils will now be able to submit work for grading.</p>
|
|
230
|
+
</Modal.Body>
|
|
231
|
+
</Modal>
|
|
232
|
+
<Button onClick={() => setOpen(true)}>Open modal</Button>
|
|
233
|
+
</>
|
|
234
|
+
);
|
|
44
235
|
};
|
|
45
236
|
|
|
46
|
-
|
|
47
|
-
|
|
237
|
+
const WithFooterActionsTemplate = () => {
|
|
238
|
+
const [open, setOpen] = useState(false);
|
|
239
|
+
return (
|
|
48
240
|
<>
|
|
49
|
-
<
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
</Button>
|
|
66
|
-
</div>
|
|
241
|
+
<Modal
|
|
242
|
+
open={open}
|
|
243
|
+
closeHandler={() => setOpen(false)}
|
|
244
|
+
title="Edit Pupil Details"
|
|
245
|
+
>
|
|
246
|
+
<Modal.Body>
|
|
247
|
+
<p>Update the details below. Changes will be saved to the pupil record immediately.</p>
|
|
248
|
+
</Modal.Body>
|
|
249
|
+
<Modal.Footer>
|
|
250
|
+
<div style={{ display: 'flex', gap: 'var(--spacing-small)', justifyContent: 'flex-end' }}>
|
|
251
|
+
<Button variant="secondary" onClick={() => setOpen(false)}>Cancel</Button>
|
|
252
|
+
<Button variant="primary" onClick={() => setOpen(false)}>Save changes</Button>
|
|
253
|
+
</div>
|
|
254
|
+
</Modal.Footer>
|
|
255
|
+
</Modal>
|
|
256
|
+
<Button onClick={() => setOpen(true)}>Edit pupil details</Button>
|
|
67
257
|
</>
|
|
68
|
-
)
|
|
258
|
+
);
|
|
69
259
|
};
|
|
70
260
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
261
|
+
const WithFormContentTemplate = () => {
|
|
262
|
+
const [open, setOpen] = useState(false);
|
|
263
|
+
return (
|
|
264
|
+
<>
|
|
265
|
+
<Modal open={open} closeHandler={() => setOpen(false)} title="Add New Staff Member">
|
|
266
|
+
<Modal.Body>
|
|
267
|
+
<FormField
|
|
268
|
+
id="staff-first-name"
|
|
269
|
+
label="First name"
|
|
270
|
+
inputType="text"
|
|
271
|
+
inputProps={{ placeholder: 'e.g. Sarah' }}
|
|
272
|
+
/>
|
|
273
|
+
<FormField
|
|
274
|
+
id="staff-last-name"
|
|
275
|
+
label="Last name"
|
|
276
|
+
inputType="text"
|
|
277
|
+
inputProps={{ placeholder: 'e.g. Clarke' }}
|
|
278
|
+
/>
|
|
279
|
+
<FormField
|
|
280
|
+
id="staff-email"
|
|
281
|
+
label="Email address"
|
|
282
|
+
inputType="text"
|
|
283
|
+
inputProps={{ placeholder: 'e.g. s.clarke@school.edu', type: 'email' }}
|
|
284
|
+
fieldDescription="The staff member will receive a login invitation at this address."
|
|
285
|
+
/>
|
|
286
|
+
</Modal.Body>
|
|
287
|
+
<Modal.Footer>
|
|
288
|
+
<div style={{ display: 'flex', gap: 'var(--spacing-small)', justifyContent: 'flex-end' }}>
|
|
289
|
+
<Button variant="secondary" onClick={() => setOpen(false)}>Cancel</Button>
|
|
290
|
+
<Button variant="primary" onClick={() => setOpen(false)}>Add staff member</Button>
|
|
291
|
+
</div>
|
|
292
|
+
</Modal.Footer>
|
|
293
|
+
</Modal>
|
|
294
|
+
<Button onClick={() => setOpen(true)}>Add staff member</Button>
|
|
295
|
+
</>
|
|
296
|
+
);
|
|
297
|
+
};
|
|
81
298
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
</Button>
|
|
104
|
-
</div>
|
|
105
|
-
</Modal.Footer>
|
|
106
|
-
</>,
|
|
107
|
-
)}
|
|
108
|
-
>
|
|
109
|
-
Open Modal with Footer
|
|
110
|
-
</Button>
|
|
111
|
-
</div>
|
|
112
|
-
</>
|
|
113
|
-
);
|
|
114
|
-
},
|
|
299
|
+
const DeleteConfirmationTemplate = () => {
|
|
300
|
+
const [open, setOpen] = useState(false);
|
|
301
|
+
return (
|
|
302
|
+
<>
|
|
303
|
+
<Modal open={open} title="Remove Emily Clarke?">
|
|
304
|
+
<Modal.Body>
|
|
305
|
+
<p>
|
|
306
|
+
Removing Emily Clarke will permanently delete her assessment records for this term.
|
|
307
|
+
This action cannot be undone.
|
|
308
|
+
</p>
|
|
309
|
+
</Modal.Body>
|
|
310
|
+
<Modal.Footer>
|
|
311
|
+
<div style={{ display: 'flex', gap: 'var(--spacing-small)', justifyContent: 'flex-end' }}>
|
|
312
|
+
<Button variant="secondary" onClick={() => setOpen(false)}>Cancel</Button>
|
|
313
|
+
<Button variant="primary-destructive" onClick={() => setOpen(false)}>Remove pupil</Button>
|
|
314
|
+
</div>
|
|
315
|
+
</Modal.Footer>
|
|
316
|
+
</Modal>
|
|
317
|
+
<Button variant="secondary-destructive" onClick={() => setOpen(true)}>Remove pupil</Button>
|
|
318
|
+
</>
|
|
319
|
+
);
|
|
115
320
|
};
|
|
116
321
|
|
|
117
|
-
|
|
118
|
-
|
|
322
|
+
const ErrorModalTemplate = () => {
|
|
323
|
+
const [open, setOpen] = useState(false);
|
|
324
|
+
return (
|
|
119
325
|
<>
|
|
120
|
-
<
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
</p>
|
|
144
|
-
</Section>
|
|
145
|
-
<Section title="Section 3" collapsible>
|
|
146
|
-
<p>
|
|
147
|
-
Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium,
|
|
148
|
-
totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.
|
|
149
|
-
</p>
|
|
150
|
-
</Section>
|
|
151
|
-
</Modal.Body>
|
|
152
|
-
</>,
|
|
153
|
-
)}
|
|
154
|
-
>
|
|
155
|
-
Open Modal with Sections
|
|
156
|
-
</Button>
|
|
157
|
-
</div>
|
|
326
|
+
<Modal open={open} closeHandler={() => setOpen(false)}>
|
|
327
|
+
<Modal.Header>
|
|
328
|
+
<Icon name="triangle-alert" size={24} color="var(--color-semantic-destructive-500)" />
|
|
329
|
+
<Modal.Title>Sorry, we cannot find this page</Modal.Title>
|
|
330
|
+
</Modal.Header>
|
|
331
|
+
<Modal.Body>
|
|
332
|
+
<p>
|
|
333
|
+
<strong>Please try finding the page using the navigation.</strong>
|
|
334
|
+
{' '}
|
|
335
|
+
If you cannot find what you are looking for, contact support with the error ID below.
|
|
336
|
+
</p>
|
|
337
|
+
<p style={{ marginTop: 'var(--spacing-small)', fontFamily: 'monospace', fontSize: 'var(--font-size-2-13)' }}>
|
|
338
|
+
ID: f47ecbd4-319b-11f0-a5ae-06f9b8bc5d95
|
|
339
|
+
</p>
|
|
340
|
+
</Modal.Body>
|
|
341
|
+
<Modal.Footer>
|
|
342
|
+
<div style={{ display: 'flex', gap: 'var(--spacing-small)', justifyContent: 'flex-end' }}>
|
|
343
|
+
<Button variant="tertiary" onClick={() => setOpen(false)}>Close</Button>
|
|
344
|
+
<Button variant="primary" onClick={() => setOpen(false)}>Contact support</Button>
|
|
345
|
+
</div>
|
|
346
|
+
</Modal.Footer>
|
|
347
|
+
</Modal>
|
|
348
|
+
<Button onClick={() => setOpen(true)}>Simulate page not found</Button>
|
|
158
349
|
</>
|
|
159
|
-
)
|
|
350
|
+
);
|
|
160
351
|
};
|
|
161
352
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
alert('Form submitted!');
|
|
166
|
-
ModalUtils.removeModal();
|
|
167
|
-
};
|
|
353
|
+
// ---------------------------------------------------------------------------
|
|
354
|
+
// Stories
|
|
355
|
+
// ---------------------------------------------------------------------------
|
|
168
356
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
</Modal.Header>
|
|
180
|
-
<Modal.Body>
|
|
181
|
-
<FormField
|
|
182
|
-
id={generateUuid()}
|
|
183
|
-
label="Your Name"
|
|
184
|
-
inputType="text"
|
|
185
|
-
inputProps={{ placeholder: 'e.g. Jane Doe' }}
|
|
186
|
-
fieldDescription="Please enter your full name."
|
|
187
|
-
/>
|
|
188
|
-
<FormField
|
|
189
|
-
id={generateUuid()}
|
|
190
|
-
label="Email Address"
|
|
191
|
-
inputType="text"
|
|
192
|
-
inputProps={{ placeholder: 'e.g. jane@example.com', type: 'email' }}
|
|
193
|
-
/>
|
|
194
|
-
<FormField
|
|
195
|
-
id={generateUuid()}
|
|
196
|
-
label="Message"
|
|
197
|
-
inputType="textarea"
|
|
198
|
-
inputProps={{ placeholder: 'Enter your message here...', rows: 4 }}
|
|
199
|
-
fieldDescription="Maximum 500 characters."
|
|
200
|
-
/>
|
|
201
|
-
<FormField
|
|
202
|
-
id={generateUuid()}
|
|
203
|
-
label="Password"
|
|
204
|
-
inputType="selectDropdown"
|
|
205
|
-
inputProps={{
|
|
206
|
-
options: [{ label: 'Option 1', value: 'option1' }, { label: 'Option 2', value: 'option2' }, { label: 'Option 3', value: 'option3' }],
|
|
207
|
-
onSelectionChange: fn(),
|
|
208
|
-
}}
|
|
209
|
-
/>
|
|
210
|
-
</Modal.Body>
|
|
211
|
-
<Modal.Footer>
|
|
212
|
-
<div style={{ display: 'flex', gap: '1rem', justifyContent: 'flex-end' }}>
|
|
213
|
-
<Button variant="secondary" onClick={removeModal}>
|
|
214
|
-
Cancel
|
|
215
|
-
</Button>
|
|
216
|
-
<Button variant="primary" onClick={handleSubmit}>
|
|
217
|
-
Submit
|
|
218
|
-
</Button>
|
|
219
|
-
</div>
|
|
220
|
-
</Modal.Footer>
|
|
221
|
-
</>,
|
|
222
|
-
)}
|
|
223
|
-
>
|
|
224
|
-
Open Form Modal
|
|
225
|
-
</Button>
|
|
226
|
-
</div>
|
|
227
|
-
</>
|
|
228
|
-
);
|
|
229
|
-
},
|
|
230
|
-
};
|
|
357
|
+
export const Default: Story = withDescription(
|
|
358
|
+
{
|
|
359
|
+
render: DefaultTemplate,
|
|
360
|
+
parameters: {
|
|
361
|
+
controls: { disable: true },
|
|
362
|
+
docs: {
|
|
363
|
+
source: {
|
|
364
|
+
language: 'tsx',
|
|
365
|
+
code: `
|
|
366
|
+
import { ModalManager, ModalUtils, Modal, Button } from '@arbor-education/design-system.components';
|
|
231
367
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
368
|
+
// Mount once at your app root
|
|
369
|
+
function App() {
|
|
370
|
+
return (
|
|
371
|
+
<>
|
|
372
|
+
<ModalManager />
|
|
373
|
+
{/* rest of your app */}
|
|
374
|
+
</>
|
|
375
|
+
);
|
|
376
|
+
}
|
|
237
377
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
id={generateUuid()}
|
|
263
|
-
label="Field 1"
|
|
264
|
-
inputType="text"
|
|
265
|
-
inputProps={{ placeholder: 'Enter value' }}
|
|
266
|
-
/>
|
|
267
|
-
<FormField
|
|
268
|
-
id={generateUuid()}
|
|
269
|
-
label="Field 2"
|
|
270
|
-
inputType="text"
|
|
271
|
-
inputProps={{ placeholder: 'Enter value' }}
|
|
272
|
-
/>
|
|
273
|
-
</Section>
|
|
274
|
-
<Section title="Additional Information" collapsible>
|
|
275
|
-
<p>
|
|
276
|
-
Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium,
|
|
277
|
-
totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.
|
|
278
|
-
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores
|
|
279
|
-
eos qui ratione voluptatem sequi nesciunt.
|
|
280
|
-
</p>
|
|
281
|
-
<FormField
|
|
282
|
-
id={generateUuid()}
|
|
283
|
-
label="Long Text Field"
|
|
284
|
-
inputType="textarea"
|
|
285
|
-
inputProps={{ placeholder: 'Enter long text', rows: 6 }}
|
|
286
|
-
/>
|
|
287
|
-
</Section>
|
|
288
|
-
<Section title="More Content" collapsible>
|
|
289
|
-
<p>
|
|
290
|
-
Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit,
|
|
291
|
-
sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.
|
|
292
|
-
Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam.
|
|
293
|
-
</p>
|
|
294
|
-
<FormField
|
|
295
|
-
id={generateUuid()}
|
|
296
|
-
label="Field 3"
|
|
297
|
-
inputType="number"
|
|
298
|
-
inputProps={{ placeholder: 'Enter number' }}
|
|
299
|
-
/>
|
|
300
|
-
<FormField
|
|
301
|
-
id={generateUuid()}
|
|
302
|
-
label="Field 4"
|
|
303
|
-
inputType="text"
|
|
304
|
-
inputProps={{ placeholder: 'Enter value' }}
|
|
305
|
-
/>
|
|
306
|
-
</Section>
|
|
307
|
-
<Section title="Final Section" collapsible collapsed>
|
|
308
|
-
<p>
|
|
309
|
-
At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque
|
|
310
|
-
corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident, similique sunt in culpa
|
|
311
|
-
qui officia deserunt mollitia animi, id est laborum et dolorum fuga.
|
|
312
|
-
</p>
|
|
313
|
-
<FormField
|
|
314
|
-
id={generateUuid()}
|
|
315
|
-
label="Final Field"
|
|
316
|
-
inputType="text"
|
|
317
|
-
inputProps={{ placeholder: 'Enter value' }}
|
|
318
|
-
/>
|
|
319
|
-
</Section>
|
|
320
|
-
</Modal.Body>
|
|
321
|
-
<Modal.Footer>
|
|
322
|
-
<div style={{ display: 'flex', gap: '1rem', justifyContent: 'flex-end' }}>
|
|
323
|
-
<Button variant="secondary" onClick={handleClose}>
|
|
324
|
-
Close
|
|
325
|
-
</Button>
|
|
326
|
-
<Button variant="primary" onClick={handleClose}>
|
|
327
|
-
Save
|
|
328
|
-
</Button>
|
|
329
|
-
</div>
|
|
330
|
-
</Modal.Footer>
|
|
331
|
-
</>,
|
|
332
|
-
)}
|
|
333
|
-
>
|
|
334
|
-
Open Scrollable Modal
|
|
335
|
-
</Button>
|
|
336
|
-
</div>
|
|
337
|
-
</>
|
|
338
|
-
);
|
|
378
|
+
// Then trigger from anywhere:
|
|
379
|
+
function AssessmentActions() {
|
|
380
|
+
return (
|
|
381
|
+
<Button
|
|
382
|
+
onClick={() =>
|
|
383
|
+
ModalUtils.addModal({
|
|
384
|
+
title: 'Assessment Period Added',
|
|
385
|
+
children: (
|
|
386
|
+
<Modal.Body>
|
|
387
|
+
<p>The Spring 2026 assessment period has been added successfully.</p>
|
|
388
|
+
</Modal.Body>
|
|
389
|
+
),
|
|
390
|
+
})
|
|
391
|
+
}
|
|
392
|
+
>
|
|
393
|
+
Open modal
|
|
394
|
+
</Button>
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
export default AssessmentActions;
|
|
398
|
+
`.trim(),
|
|
399
|
+
},
|
|
400
|
+
},
|
|
401
|
+
},
|
|
339
402
|
},
|
|
340
|
-
|
|
403
|
+
[
|
|
404
|
+
'The canonical `ModalManager` usage pattern. Mount `<ModalManager />` once at the app root,',
|
|
405
|
+
'then call `ModalUtils.addModal(props)` from any component in the tree.',
|
|
406
|
+
'`ModalManager` always provides its own `closeHandler` — the X button and Escape key are always active.',
|
|
407
|
+
'Click the button to open the modal, then dismiss it with the X button or by pressing Escape.',
|
|
408
|
+
].join(' '),
|
|
409
|
+
);
|
|
341
410
|
|
|
342
|
-
export const
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
411
|
+
export const WithFooterActions: Story = withDescription(
|
|
412
|
+
{
|
|
413
|
+
render: WithFooterActionsTemplate,
|
|
414
|
+
parameters: {
|
|
415
|
+
controls: { disable: true },
|
|
416
|
+
docs: {
|
|
417
|
+
source: {
|
|
418
|
+
language: 'tsx',
|
|
419
|
+
code: `
|
|
420
|
+
import { ModalUtils, Modal, Button } from '@arbor-education/design-system.components';
|
|
348
421
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
422
|
+
function EditPupilButton() {
|
|
423
|
+
return (
|
|
424
|
+
<Button
|
|
425
|
+
onClick={() =>
|
|
426
|
+
ModalUtils.addModal({
|
|
427
|
+
title: 'Edit Pupil Details',
|
|
428
|
+
children: (
|
|
429
|
+
<>
|
|
430
|
+
<Modal.Body>
|
|
431
|
+
<p>Update the details below. Changes will be saved to the pupil record immediately.</p>
|
|
432
|
+
</Modal.Body>
|
|
433
|
+
<Modal.Footer>
|
|
434
|
+
<div style={{ display: 'flex', gap: 'var(--spacing-small)', justifyContent: 'flex-end' }}>
|
|
435
|
+
<Button variant="secondary" onClick={() => ModalUtils.removeModal()}>
|
|
436
|
+
Cancel
|
|
437
|
+
</Button>
|
|
438
|
+
<Button variant="primary" onClick={() => ModalUtils.removeModal()}>
|
|
439
|
+
Save changes
|
|
440
|
+
</Button>
|
|
441
|
+
</div>
|
|
442
|
+
</Modal.Footer>
|
|
443
|
+
</>
|
|
444
|
+
),
|
|
445
|
+
})
|
|
446
|
+
}
|
|
447
|
+
>
|
|
448
|
+
Edit pupil details
|
|
449
|
+
</Button>
|
|
450
|
+
);
|
|
451
|
+
}
|
|
452
|
+
export default EditPupilButton;
|
|
453
|
+
`.trim(),
|
|
454
|
+
},
|
|
455
|
+
},
|
|
456
|
+
},
|
|
457
|
+
},
|
|
458
|
+
[
|
|
459
|
+
'A modal with a full header, body, and footer. Use `Modal.Footer` for action buttons.',
|
|
460
|
+
'Always place the primary action on the right and the cancel action on the left.',
|
|
461
|
+
'Call `ModalUtils.removeModal()` inside button handlers to close the modal after the action completes.',
|
|
462
|
+
].join(' '),
|
|
463
|
+
);
|
|
352
464
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
465
|
+
export const WithFormContent: Story = withDescription(
|
|
466
|
+
{
|
|
467
|
+
render: WithFormContentTemplate,
|
|
468
|
+
parameters: {
|
|
469
|
+
controls: { disable: true },
|
|
470
|
+
docs: {
|
|
471
|
+
source: {
|
|
472
|
+
language: 'tsx',
|
|
473
|
+
code: `
|
|
474
|
+
import { ModalUtils, Modal, Button, FormField } from '@arbor-education/design-system.components';
|
|
475
|
+
|
|
476
|
+
function AddStaffButton() {
|
|
477
|
+
return (
|
|
478
|
+
<Button
|
|
479
|
+
onClick={() =>
|
|
480
|
+
ModalUtils.addModal({
|
|
481
|
+
title: 'Add New Staff Member',
|
|
482
|
+
children: (
|
|
483
|
+
<>
|
|
484
|
+
<Modal.Body>
|
|
485
|
+
<FormField
|
|
486
|
+
id="staff-first-name"
|
|
487
|
+
label="First name"
|
|
488
|
+
inputType="text"
|
|
489
|
+
inputProps={{ placeholder: 'e.g. Sarah' }}
|
|
490
|
+
/>
|
|
491
|
+
<FormField
|
|
492
|
+
id="staff-last-name"
|
|
493
|
+
label="Last name"
|
|
494
|
+
inputType="text"
|
|
495
|
+
inputProps={{ placeholder: 'e.g. Clarke' }}
|
|
496
|
+
/>
|
|
497
|
+
<FormField
|
|
498
|
+
id="staff-email"
|
|
499
|
+
label="Email address"
|
|
500
|
+
inputType="text"
|
|
501
|
+
inputProps={{ placeholder: 'e.g. s.clarke@school.edu', type: 'email' }}
|
|
502
|
+
fieldDescription="The staff member will receive a login invitation at this address."
|
|
503
|
+
/>
|
|
504
|
+
</Modal.Body>
|
|
505
|
+
<Modal.Footer>
|
|
506
|
+
<div style={{ display: 'flex', gap: 'var(--spacing-small)', justifyContent: 'flex-end' }}>
|
|
507
|
+
<Button variant="secondary" onClick={() => ModalUtils.removeModal()}>Cancel</Button>
|
|
508
|
+
<Button variant="primary" onClick={() => ModalUtils.removeModal()}>Add staff member</Button>
|
|
509
|
+
</div>
|
|
510
|
+
</Modal.Footer>
|
|
511
|
+
</>
|
|
512
|
+
),
|
|
513
|
+
})
|
|
514
|
+
}
|
|
515
|
+
>
|
|
516
|
+
Add staff member
|
|
517
|
+
</Button>
|
|
518
|
+
);
|
|
519
|
+
}
|
|
520
|
+
export default AddStaffButton;
|
|
521
|
+
`.trim(),
|
|
522
|
+
},
|
|
523
|
+
},
|
|
524
|
+
},
|
|
388
525
|
},
|
|
389
|
-
|
|
526
|
+
[
|
|
527
|
+
'A modal containing a form with `FormField` components. The `Modal.Body` provides a scrollable',
|
|
528
|
+
'container so long forms scroll independently of the header and footer.',
|
|
529
|
+
'Wrap form fields in `Modal.Body` and action buttons in `Modal.Footer` to preserve this layout.',
|
|
530
|
+
].join(' '),
|
|
531
|
+
);
|
|
390
532
|
|
|
391
|
-
export const
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
533
|
+
export const DeleteConfirmation: Story = withDescription(
|
|
534
|
+
{
|
|
535
|
+
render: DeleteConfirmationTemplate,
|
|
536
|
+
parameters: {
|
|
537
|
+
controls: { disable: true },
|
|
538
|
+
docs: {
|
|
539
|
+
source: {
|
|
540
|
+
language: 'tsx',
|
|
541
|
+
code: `
|
|
542
|
+
import { ModalUtils, Modal, Button } from '@arbor-education/design-system.components';
|
|
397
543
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
544
|
+
function RemovePupilButton() {
|
|
545
|
+
const handleDelete = () => {
|
|
546
|
+
// perform the deletion...
|
|
547
|
+
ModalUtils.removeModal();
|
|
548
|
+
};
|
|
401
549
|
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
550
|
+
return (
|
|
551
|
+
<Button
|
|
552
|
+
variant="secondary-destructive"
|
|
553
|
+
onClick={() =>
|
|
554
|
+
ModalUtils.addModal({
|
|
555
|
+
title: 'Remove Emily Clarke?',
|
|
556
|
+
children: (
|
|
557
|
+
<>
|
|
558
|
+
<Modal.Body>
|
|
559
|
+
<p>
|
|
560
|
+
Removing Emily Clarke will permanently delete her assessment records for this term.
|
|
561
|
+
This action cannot be undone.
|
|
562
|
+
</p>
|
|
563
|
+
</Modal.Body>
|
|
564
|
+
<Modal.Footer>
|
|
565
|
+
<div style={{ display: 'flex', gap: 'var(--spacing-small)', justifyContent: 'flex-end' }}>
|
|
566
|
+
<Button variant="secondary" onClick={() => ModalUtils.removeModal()}>
|
|
567
|
+
Cancel
|
|
568
|
+
</Button>
|
|
569
|
+
<Button variant="primary-destructive" onClick={handleDelete}>
|
|
570
|
+
Remove pupil
|
|
571
|
+
</Button>
|
|
572
|
+
</div>
|
|
573
|
+
</Modal.Footer>
|
|
574
|
+
</>
|
|
575
|
+
),
|
|
576
|
+
})
|
|
577
|
+
}
|
|
578
|
+
>
|
|
579
|
+
Remove pupil
|
|
580
|
+
</Button>
|
|
581
|
+
);
|
|
582
|
+
}
|
|
583
|
+
export default RemovePupilButton;
|
|
584
|
+
`.trim(),
|
|
585
|
+
},
|
|
586
|
+
},
|
|
587
|
+
},
|
|
434
588
|
},
|
|
435
|
-
|
|
589
|
+
[
|
|
590
|
+
'The Arbor delete confirmation pattern. Use `variant="secondary-destructive"` on the trigger button',
|
|
591
|
+
'and `variant="primary-destructive"` on the confirm action inside the modal.',
|
|
592
|
+
'Always name the item being deleted in the modal title so users are certain of what they are removing.',
|
|
593
|
+
'Cancel goes on the left; the destructive action goes on the right.',
|
|
594
|
+
].join(' '),
|
|
595
|
+
);
|
|
436
596
|
|
|
437
|
-
export const
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
597
|
+
export const ErrorModal: Story = withDescription(
|
|
598
|
+
{
|
|
599
|
+
render: ErrorModalTemplate,
|
|
600
|
+
parameters: {
|
|
601
|
+
controls: { disable: true },
|
|
602
|
+
docs: {
|
|
603
|
+
source: {
|
|
604
|
+
language: 'tsx',
|
|
605
|
+
code: `
|
|
606
|
+
import { ModalUtils, Modal, Button, Icon } from '@arbor-education/design-system.components';
|
|
442
607
|
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
<Button variant="primary" onClick={handleConfirm}>
|
|
484
|
-
Primary Action
|
|
485
|
-
</Button>
|
|
486
|
-
</div>
|
|
487
|
-
</Modal.Footer>
|
|
488
|
-
</>,
|
|
489
|
-
)}
|
|
490
|
-
>
|
|
491
|
-
Open Deletion Confirmation Modal
|
|
492
|
-
</Button>
|
|
493
|
-
</div>
|
|
494
|
-
</>
|
|
495
|
-
);
|
|
608
|
+
function SimulateError() {
|
|
609
|
+
return (
|
|
610
|
+
<Button
|
|
611
|
+
onClick={() =>
|
|
612
|
+
ModalUtils.addModal({
|
|
613
|
+
children: (
|
|
614
|
+
<>
|
|
615
|
+
<Modal.Header>
|
|
616
|
+
<Icon name="triangle-alert" size={24} color="var(--color-semantic-destructive-500)" />
|
|
617
|
+
<Modal.Title>Sorry, we cannot find this page</Modal.Title>
|
|
618
|
+
</Modal.Header>
|
|
619
|
+
<Modal.Body>
|
|
620
|
+
<p>
|
|
621
|
+
<strong>Please try finding the page using the navigation.</strong>{' '}
|
|
622
|
+
If you cannot find what you are looking for, contact support with the error ID below.
|
|
623
|
+
</p>
|
|
624
|
+
<p style={{ fontFamily: 'monospace', fontSize: 'var(--font-size-2-13)' }}>
|
|
625
|
+
ID: f47ecbd4-319b-11f0-a5ae-06f9b8bc5d95
|
|
626
|
+
</p>
|
|
627
|
+
</Modal.Body>
|
|
628
|
+
<Modal.Footer>
|
|
629
|
+
<div style={{ display: 'flex', gap: 'var(--spacing-small)', justifyContent: 'flex-end' }}>
|
|
630
|
+
<Button variant="tertiary" onClick={() => ModalUtils.removeModal()}>Close</Button>
|
|
631
|
+
<Button variant="primary" onClick={() => ModalUtils.removeModal()}>Contact support</Button>
|
|
632
|
+
</div>
|
|
633
|
+
</Modal.Footer>
|
|
634
|
+
</>
|
|
635
|
+
),
|
|
636
|
+
})
|
|
637
|
+
}
|
|
638
|
+
>
|
|
639
|
+
Simulate page not found
|
|
640
|
+
</Button>
|
|
641
|
+
);
|
|
642
|
+
}
|
|
643
|
+
export default SimulateError;
|
|
644
|
+
`.trim(),
|
|
645
|
+
},
|
|
646
|
+
},
|
|
647
|
+
},
|
|
496
648
|
},
|
|
497
|
-
|
|
649
|
+
[
|
|
650
|
+
'An application-level error modal using `Modal.Header` for custom icon + title composition.',
|
|
651
|
+
'Note that `title` is omitted from `ModalUtils.addModal()` here — when using `Modal.Header` manually,',
|
|
652
|
+
'omit the `title` prop to avoid rendering two titles. Add `hideCloseButton` if you need to suppress',
|
|
653
|
+
'the auto-rendered X button when placing `Modal.CloseButton` inside the header.',
|
|
654
|
+
].join(' '),
|
|
655
|
+
);
|