@pnx-mixtape/mxds 0.1.0-rc.0 → 0.1.0-rc.2

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.
Files changed (204) hide show
  1. package/.storybook/decorators.ts +15 -0
  2. package/.storybook/preview.ts +4 -1
  3. package/.storybook/themes/campus-vibrant.css +181 -0
  4. package/.storybook/themes/civic-formal.css +246 -0
  5. package/.storybook/{theme-demo.css → themes/demo.css} +14 -7
  6. package/.storybook/themes/health-services.css +255 -0
  7. package/.storybook/themes/research-faculty.css +169 -0
  8. package/dist/build/accordion.css +1 -1
  9. package/dist/build/base.css +1 -1
  10. package/dist/build/button.css +1 -1
  11. package/dist/build/chunks/DropMenu-1baQc8Yc.js +1 -0
  12. package/dist/build/chunks/InPageNavigation-CVieEl9u.js +1 -0
  13. package/dist/build/chunks/Popover-Cxg-s5Jn.js +1 -0
  14. package/dist/build/chunks/Sticky-cWUVSOXN.js +1 -0
  15. package/dist/build/chunks/{Tabs-Dm8g301e.js → Tabs-DA_Ez8pv.js} +2 -2
  16. package/dist/build/chunks/polyfills-C7pRt54M.js +1 -0
  17. package/dist/build/constants.css +1 -1
  18. package/dist/build/drop-menu.css +1 -1
  19. package/dist/build/drop-menu.entry.js +1 -1
  20. package/dist/build/elements.js +1 -1
  21. package/dist/build/form.css +1 -1
  22. package/dist/build/grid.css +1 -1
  23. package/dist/build/header.css +1 -1
  24. package/dist/build/icon.css +1 -1
  25. package/dist/build/in-page-alert.css +1 -1
  26. package/dist/build/in-page-navigation.entry.js +1 -1
  27. package/dist/build/navigation.css +1 -1
  28. package/dist/build/pagination.css +1 -1
  29. package/dist/build/popover.entry.js +1 -1
  30. package/dist/build/print.css +1 -1
  31. package/dist/build/progress-indicator.css +1 -0
  32. package/dist/build/react.js +1 -1
  33. package/dist/build/side-navigation.css +1 -1
  34. package/dist/build/sidebar.css +1 -1
  35. package/dist/build/steps.css +1 -1
  36. package/dist/build/sticky.entry.js +1 -1
  37. package/dist/build/tabs.entry.js +1 -1
  38. package/dist/build/tag.css +1 -1
  39. package/dist/build/utilities.css +1 -1
  40. package/dist/types/Atom/DefinitionList/DefinitionList.d.ts +3 -2
  41. package/dist/types/Atom/DefinitionList/DefinitionList.d.ts.map +1 -1
  42. package/dist/types/Atom/Heading/Heading.d.ts.map +1 -1
  43. package/dist/types/Atom/List/List.d.ts +10 -0
  44. package/dist/types/Atom/List/List.d.ts.map +1 -0
  45. package/dist/types/Component/DropMenu/Elements/DropMenu.d.ts.map +1 -1
  46. package/dist/types/Component/InPageNavigation/Elements/InPageNavigation.d.ts +2 -2
  47. package/dist/types/Component/InPageNavigation/Elements/InPageNavigation.d.ts.map +1 -1
  48. package/dist/types/Component/InPageNavigation/Hooks/useInPageNavigation.d.ts.map +1 -1
  49. package/dist/types/Component/ProgressIndicator/ProgressIndicator.d.ts +9 -0
  50. package/dist/types/Component/ProgressIndicator/ProgressIndicator.d.ts.map +1 -0
  51. package/dist/types/Component/Tabs/Elements/Tabs.d.ts.map +1 -1
  52. package/dist/types/Component/Tag/Tag.d.ts +4 -2
  53. package/dist/types/Component/Tag/Tag.d.ts.map +1 -1
  54. package/dist/types/Form/Autocomplete/Autocomplete.d.ts.map +1 -1
  55. package/dist/types/Form/Checkbox/FormCheckbox.d.ts.map +1 -1
  56. package/dist/types/Form/CheckboxCard/CheckboxCard.d.ts +23 -0
  57. package/dist/types/Form/CheckboxCard/CheckboxCard.d.ts.map +1 -0
  58. package/dist/types/Form/Radio/FormRadio.d.ts.map +1 -1
  59. package/dist/types/Form/RadioCard/RadioCard.d.ts +16 -0
  60. package/dist/types/Form/RadioCard/RadioCard.d.ts.map +1 -0
  61. package/dist/types/Form/Select/FormSelect.d.ts.map +1 -1
  62. package/dist/types/Form/TextInput/FormText.d.ts.map +1 -1
  63. package/dist/types/Form/Textarea/FormTextarea.d.ts.map +1 -1
  64. package/dist/types/enums.d.ts +15 -0
  65. package/dist/types/enums.d.ts.map +1 -1
  66. package/dist/types/react.d.ts +4 -0
  67. package/dist/types/react.d.ts.map +1 -1
  68. package/package.json +7 -8
  69. package/src/Atom/Background/_background.css +2 -2
  70. package/src/Atom/Blockquote/_blockquote.css +9 -2
  71. package/src/Atom/Button/README.md +1 -0
  72. package/src/Atom/Button/_buttons-styles.css +6 -6
  73. package/src/Atom/Button/_buttons.css +6 -3
  74. package/src/Atom/DefinitionList/DefinitionList.stories.ts +39 -2
  75. package/src/Atom/DefinitionList/DefinitionList.stories.tsx +36 -0
  76. package/src/Atom/DefinitionList/DefinitionList.tsx +4 -1
  77. package/src/Atom/DefinitionList/DefinitionListItem.stories.ts +1 -1
  78. package/src/Atom/DefinitionList/README.md +43 -0
  79. package/src/Atom/DefinitionList/__snapshots__/DefinitionList.stories.ts.snap +37 -3
  80. package/src/Atom/DefinitionList/__snapshots__/DefinitionList.stories.tsx.snap +2 -0
  81. package/src/Atom/DefinitionList/__snapshots__/DefinitionListItem.stories.ts.snap +3 -3
  82. package/src/Atom/DefinitionList/definition-list-item.twig +1 -0
  83. package/src/Atom/DefinitionList/definition-list.twig +6 -1
  84. package/src/Atom/Heading/Heading.tsx +6 -0
  85. package/src/Atom/Heading/_headings.css +2 -1
  86. package/src/Atom/Icon/_extended-set.css +30 -30
  87. package/src/Atom/Icon/_icon.css +37 -36
  88. package/src/Atom/Image/README.md +2 -2
  89. package/src/Atom/Image/_image.css +12 -6
  90. package/src/Atom/List/List.stories.ts +102 -0
  91. package/src/Atom/List/List.stories.tsx +103 -0
  92. package/src/Atom/List/List.tsx +22 -0
  93. package/src/Atom/List/README.md +150 -0
  94. package/src/Atom/List/__snapshots__/List.stories.ts.snap +43 -0
  95. package/src/Atom/List/__snapshots__/List.stories.tsx.snap +13 -0
  96. package/src/Atom/List/_list.css +123 -0
  97. package/src/Atom/List/list.twig +5 -0
  98. package/src/Atom/Media/Media.stories.ts +1 -1
  99. package/src/Atom/Media/__snapshots__/Media.stories.ts.snap +2 -2
  100. package/src/Atom/Spacing/_spacing.css +24 -24
  101. package/src/Atom/Table/__snapshots__/Table.stories.ts.snap +24 -4
  102. package/src/Atom/Table/_table.css +15 -3
  103. package/src/Atom/Table/table.twig +6 -1
  104. package/src/Atom/Text/__snapshots__/Text.stories.ts.snap +3 -2
  105. package/src/Atom/Text/_text-styles.css +21 -1
  106. package/src/Atom/Text/twig/text-styles-example.twig +3 -2
  107. package/src/Atom/_base.css +6 -0
  108. package/src/Atom/_flow.css +1 -1
  109. package/src/Atom/_generic.css +18 -11
  110. package/src/Component/Accordion/accordion.css +2 -0
  111. package/src/Component/Card/__snapshots__/Card.stories.ts.snap +2 -2
  112. package/src/Component/Card/card.twig +1 -1
  113. package/src/Component/Carousel/Carousel.stories.ts +46 -23
  114. package/src/Component/DropMenu/Elements/DropMenu.ts +10 -1
  115. package/src/Component/InPageAlert/in-page-alert.css +2 -1
  116. package/src/Component/InPageNavigation/Elements/InPageNavigation.ts +10 -5
  117. package/src/Component/InPageNavigation/Hooks/useInPageNavigation.ts +10 -5
  118. package/src/Component/InPageNavigation/InPageNavigation.stories.ts +33 -0
  119. package/src/Component/InPageNavigation/InPageNavigation.stories.tsx +52 -0
  120. package/src/Component/InPageNavigation/__snapshots__/InPageNavigation.stories.ts.snap +26 -0
  121. package/src/Component/InPageNavigation/__snapshots__/InPageNavigation.stories.tsx.snap +2 -0
  122. package/src/Component/InPageNavigation/twig/content-example-duplicate-headings.twig +20 -0
  123. package/src/Component/InPageNavigation/twig/in-page-navigation-story-duplicate-headings.twig +2 -0
  124. package/src/Component/ListItem/__snapshots__/ListItem.stories.ts.snap +4 -4
  125. package/src/Component/ListItem/list-item.twig +2 -2
  126. package/src/Component/Navigation/_navigation.css +1 -1
  127. package/src/Component/Pagination/Pagination.stories.ts +8 -4
  128. package/src/Component/Pagination/pagination.css +3 -3
  129. package/src/Component/ProgressIndicator/ProgressIndicator.stories.ts +36 -0
  130. package/src/Component/ProgressIndicator/ProgressIndicator.stories.tsx +30 -0
  131. package/src/Component/ProgressIndicator/ProgressIndicator.tsx +60 -0
  132. package/src/Component/ProgressIndicator/README.md +111 -0
  133. package/src/Component/ProgressIndicator/__snapshots__/ProgressIndicator.stories.ts.snap +67 -0
  134. package/src/Component/ProgressIndicator/__snapshots__/ProgressIndicator.stories.tsx.snap +7 -0
  135. package/src/Component/ProgressIndicator/progress-indicator.css +59 -0
  136. package/src/Component/ProgressIndicator/progress-indicator.twig +22 -0
  137. package/src/Component/Sticky/Elements/Sticky.ts +1 -1
  138. package/src/Component/Sticky/sticky.css +3 -3
  139. package/src/Component/Tabs/Elements/Tabs.ts +3 -1
  140. package/src/Component/Tabs/Tabs.stories.ts +55 -21
  141. package/src/Component/Tabs/__snapshots__/Tabs.stories.ts.snap +2 -2
  142. package/src/Component/Tag/README.md +52 -26
  143. package/src/Component/Tag/Tag.stories.tsx +24 -0
  144. package/src/Component/Tag/Tag.tsx +12 -2
  145. package/src/Component/Tag/TagItem.stories.ts +25 -0
  146. package/src/Component/Tag/__snapshots__/Tag.stories.ts.snap +2 -2
  147. package/src/Component/Tag/__snapshots__/Tag.stories.tsx.snap +2 -0
  148. package/src/Component/Tag/tag-item.twig +2 -2
  149. package/src/Component/Tag/tag.css +76 -2
  150. package/src/Form/Autocomplete/Autocomplete.stories.ts +8 -3
  151. package/src/Form/Autocomplete/Autocomplete.tsx +1 -4
  152. package/src/Form/Autocomplete/README.md +26 -26
  153. package/src/Form/Autocomplete/__snapshots__/Autocomplete.stories.ts.snap +1 -1
  154. package/src/Form/Autocomplete/__snapshots__/Autocomplete.stories.tsx.snap +1 -1
  155. package/src/Form/Autocomplete/autocomplete.twig +0 -2
  156. package/src/Form/Checkbox/FormCheckbox.tsx +2 -8
  157. package/src/Form/Checkbox/README.md +23 -18
  158. package/src/Form/Checkbox/input-checkbox.twig +0 -2
  159. package/src/Form/CheckboxCard/CheckboxCard.stories.ts +90 -0
  160. package/src/Form/CheckboxCard/CheckboxCard.stories.tsx +43 -0
  161. package/src/Form/CheckboxCard/CheckboxCard.tsx +102 -0
  162. package/src/Form/CheckboxCard/README.md +163 -0
  163. package/src/Form/CheckboxCard/__snapshots__/CheckboxCard.stories.ts.snap +63 -0
  164. package/src/Form/CheckboxCard/__snapshots__/CheckboxCard.stories.tsx.snap +7 -0
  165. package/src/Form/CheckboxCard/checkbox-card.twig +29 -0
  166. package/src/Form/FormItem/__snapshots__/FormItem.stories.ts.snap +2 -2
  167. package/src/Form/README.md +65 -13
  168. package/src/Form/Radio/FormRadio.tsx +1 -4
  169. package/src/Form/Radio/input-radio.twig +0 -2
  170. package/src/Form/RadioCard/README.md +146 -0
  171. package/src/Form/RadioCard/RadioCard.stories.ts +95 -0
  172. package/src/Form/RadioCard/RadioCard.stories.tsx +26 -0
  173. package/src/Form/RadioCard/RadioCard.tsx +59 -0
  174. package/src/Form/RadioCard/__snapshots__/RadioCard.stories.ts.snap +69 -0
  175. package/src/Form/RadioCard/__snapshots__/RadioCard.stories.tsx.snap +3 -0
  176. package/src/Form/RadioCard/radio-card.twig +20 -0
  177. package/src/Form/Select/FormSelect.tsx +1 -4
  178. package/src/Form/Select/input-select.twig +0 -2
  179. package/src/Form/TextInput/FormText.stories.tsx +14 -0
  180. package/src/Form/TextInput/FormText.tsx +1 -4
  181. package/src/Form/TextInput/__snapshots__/FormText.stories.tsx.snap +2 -0
  182. package/src/Form/TextInput/input-text.twig +0 -2
  183. package/src/Form/Textarea/FormTextarea.tsx +1 -4
  184. package/src/Form/Textarea/input-textarea.twig +0 -2
  185. package/src/Form/Validation.stories.ts +153 -0
  186. package/src/Form/__snapshots__/Validation.stories.ts.snap +130 -0
  187. package/src/Form/form.css +155 -28
  188. package/src/Layout/Grid/grid.css +4 -4
  189. package/src/Layout/Sidebar/__snapshots__/Sidebar.stories.ts.snap +5 -5
  190. package/src/Layout/Sidebar/sidebar.css +1 -0
  191. package/src/Layout/Sidebar/sidebar.twig +1 -1
  192. package/src/Utility/_layout-utils.css +8 -0
  193. package/src/Utility/print.css +50 -28
  194. package/src/Utility/utilities.css +2 -2
  195. package/src/constants.css +12 -2
  196. package/src/enums.ts +17 -0
  197. package/src/react.ts +4 -0
  198. package/tools/lib/extract-classnames.mjs +0 -0
  199. package/tools/lint-classnames.mjs +1 -1
  200. package/dist/build/chunks/DropMenu-Co9jZZ4S.js +0 -1
  201. package/dist/build/chunks/InPageNavigation-CzR_KZaz.js +0 -1
  202. package/dist/build/chunks/Popover-De_CQqcd.js +0 -1
  203. package/dist/build/chunks/Sticky-Kyjykb36.js +0 -1
  204. package/dist/build/chunks/polyfills-Dokbkxxk.js +0 -1
@@ -0,0 +1,26 @@
1
+ import { Meta, StoryObj } from "@storybook/react-vite"
2
+ import RadioCard, { FormRadioCard } from "./RadioCard"
3
+ import { Page } from "../../../.storybook-react/decorators"
4
+
5
+ const meta: Meta<typeof RadioCard> = {
6
+ decorators: [Page],
7
+ component: RadioCard,
8
+ }
9
+
10
+ export default meta
11
+ export const RadioCards: StoryObj<typeof FormRadioCard> = {
12
+ render: ({ children, ...args }) => <FormRadioCard {...args}>{children}</FormRadioCard>,
13
+ args: {
14
+ label: "Do you hold a current concession card?",
15
+ children: (
16
+ <>
17
+ <RadioCard value="yes" hint="Pensioner Concession, Health Care or DVA card">
18
+ Yes, I hold a card
19
+ </RadioCard>
20
+ <RadioCard value="no" hint="You may still be eligible based on your income">
21
+ No
22
+ </RadioCard>
23
+ </>
24
+ ),
25
+ },
26
+ }
@@ -0,0 +1,59 @@
1
+ import { JSX, PropsWithChildren, useRef, createContext, use, useId } from "react"
2
+ import { useRadio, useRadioGroup, AriaRadioProps, AriaRadioGroupProps } from "react-aria"
3
+ import { useRadioGroupState, RadioGroupState } from "react-stately"
4
+ import classNames from "classnames"
5
+ import FormItem from "../FormItem/FormItem"
6
+ import { FormStatus } from "../../enums"
7
+
8
+ const RadioCardContext = createContext<RadioGroupState>(null)
9
+
10
+ type FormRadioCardProps = PropsWithChildren &
11
+ AriaRadioGroupProps & {
12
+ label?: string
13
+ }
14
+
15
+ const FormRadioCard = ({ label, children, ...props }: FormRadioCardProps): JSX.Element => {
16
+ const state = useRadioGroupState(props)
17
+ const { radioGroupProps } = useRadioGroup(props, state)
18
+
19
+ return (
20
+ <FormItem fieldset {...radioGroupProps} legend={label}>
21
+ <RadioCardContext value={state}>{children}</RadioCardContext>
22
+ </FormItem>
23
+ )
24
+ }
25
+
26
+ type RadioCardProps = PropsWithChildren &
27
+ AriaRadioProps & {
28
+ hint?: string
29
+ status?: FormStatus
30
+ className?: string
31
+ }
32
+
33
+ const RadioCard = ({
34
+ children,
35
+ hint = null,
36
+ status = FormStatus.DEFAULT,
37
+ className = "",
38
+ ...props
39
+ }: RadioCardProps): JSX.Element => {
40
+ const id = useId()
41
+ const ref = useRef<HTMLInputElement>(null)
42
+ const state = use(RadioCardContext)
43
+ const { inputProps } = useRadio(props, state, ref)
44
+
45
+ const classes = classNames("mx-radio", "mx-choice-card", className)
46
+
47
+ return (
48
+ <div className={classes}>
49
+ <input id={id} {...inputProps} ref={ref} aria-invalid={status === FormStatus.INVALID} />
50
+ <label htmlFor={id}>
51
+ {children}
52
+ {hint && <span className="mx-choice-card__hint">{hint}</span>}
53
+ </label>
54
+ </div>
55
+ )
56
+ }
57
+
58
+ export default RadioCard
59
+ export { FormRadioCard }
@@ -0,0 +1,69 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`Checked 1`] = `
4
+ "
5
+ <fieldset class="mx-form-item mx-fieldset">
6
+
7
+ <legend class="mx-legend">
8
+ Do you hold a current concession card?
9
+ </legend>
10
+
11
+
12
+ <div class="mx-radio mx-choice-card">
13
+ <input id="card-yes" type="radio" name="card" value="yes" checked="checked">
14
+ <label for="card-yes">
15
+ Yes, I hold a card
16
+ <span class="mx-choice-card__hint">Pensioner Concession, Health Care or DVA card</span> </label>
17
+ </div>
18
+ <div class="mx-radio mx-choice-card">
19
+ <input id="card-no" type="radio" name="card" value="no">
20
+ <label for="card-no">
21
+ No
22
+ <span class="mx-choice-card__hint">You may still be eligible based on your income</span> </label>
23
+ </div>
24
+
25
+
26
+
27
+ </fieldset>
28
+ "
29
+ `;
30
+
31
+ exports[`Radio Card 1`] = `
32
+ "
33
+ <fieldset class="mx-form-item mx-fieldset">
34
+
35
+ <legend class="mx-legend">
36
+ Do you hold a current concession card?
37
+ </legend>
38
+
39
+
40
+ <div class="mx-radio mx-choice-card">
41
+ <input id="card-yes" type="radio" name="card" value="yes">
42
+ <label for="card-yes">
43
+ Yes, I hold a card
44
+ <span class="mx-choice-card__hint">Pensioner Concession, Health Care or DVA card</span> </label>
45
+ </div>
46
+ <div class="mx-radio mx-choice-card">
47
+ <input id="card-applied" type="radio" name="card" value="applied">
48
+ <label for="card-applied">
49
+ I have applied but not received it
50
+ <span class="mx-choice-card__hint">You can continue, but we may ask for evidence later</span> </label>
51
+ </div>
52
+ <div class="mx-radio mx-choice-card">
53
+ <input id="card-no" type="radio" name="card" value="no">
54
+ <label for="card-no">
55
+ No
56
+ <span class="mx-choice-card__hint">You may still be eligible based on your income</span> </label>
57
+ </div>
58
+ <div class="mx-choice-divider">or</div><div class="mx-radio mx-choice-card">
59
+ <input id="card-unsure" type="radio" name="card" value="unsure">
60
+ <label for="card-unsure">
61
+ I am not sure
62
+ <span class="mx-choice-card__hint">We will ask a few more questions to work it out</span> </label>
63
+ </div>
64
+
65
+
66
+
67
+ </fieldset>
68
+ "
69
+ `;
@@ -0,0 +1,3 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`Radio Cards 1`] = `"<fieldset class="mx-form-item mx-fieldset" role="radiogroup" aria-orientation="vertical" id="react-aria-_r_0_"><legend class="mx-legend">Do you hold a current concession card?</legend><div class="mx-radio mx-choice-card"><input id="_r_6_" data-react-aria-pressable="true" tabindex="0" aria-invalid="false" type="radio" value="yes" name="react-aria-_r_5_"><label for="_r_6_">Yes, I hold a card<span class="mx-choice-card__hint">Pensioner Concession, Health Care or DVA card</span></label></div><div class="mx-radio mx-choice-card"><input id="_r_8_" data-react-aria-pressable="true" tabindex="0" aria-invalid="false" type="radio" value="no" name="react-aria-_r_5_"><label for="_r_8_">No<span class="mx-choice-card__hint">You may still be eligible based on your income</span></label></div></fieldset>"`;
@@ -0,0 +1,20 @@
1
+ {% set classes = [
2
+ 'mx-radio',
3
+ 'mx-choice-card',
4
+ ] %}
5
+ {% set attributes = (attributes ?? create_attribute()).addClass(classes) %}
6
+ <div{{ attributes }}>
7
+ <input
8
+ id="{{ id }}"
9
+ type="radio"
10
+ name="{{ name }}"
11
+ value="{{ value }}"
12
+ {% if checked %} checked="checked"{% endif %}
13
+ {% if status == "invalid" %} aria-invalid="true"{% endif %}
14
+ {% if required %} required{% endif %}
15
+ >
16
+ <label for="{{ id }}">
17
+ {{ label }}
18
+ {% if hint %}<span class="mx-choice-card__hint">{{ hint }}</span>{% endif %}
19
+ </label>
20
+ </div>
@@ -32,10 +32,7 @@ const FormSelect = ({
32
32
  const handleChange: ChangeEventHandler<HTMLSelectElement> = ({ currentTarget }) =>
33
33
  setInputValue(currentTarget.value)
34
34
 
35
- const classes = classNames("mx-input__select", className, {
36
- error: status === FormStatus.INVALID,
37
- success: status === FormStatus.VALID,
38
- })
35
+ const classes = classNames("mx-input__select", className)
39
36
 
40
37
  const items = options.map(({ label: optionLabel, value }) => (
41
38
  <Option label={optionLabel} value={value} key={value} />
@@ -1,7 +1,5 @@
1
1
  {% set classes = [
2
2
  'mx-input__select',
3
- status == "invalid" ? "error" : null,
4
- status == "valid" ? "success" : null,
5
3
  ] %}
6
4
  {% set attributes = (attributes ?? create_attribute()).addClass(classes) %}
7
5
  {% if hasDescription or status != "default" %}
@@ -14,3 +14,17 @@ const meta: Meta<typeof Component> = {
14
14
 
15
15
  export default meta
16
16
  export const TextInput: StoryObj<typeof Component> = {}
17
+
18
+ /**
19
+ * `mx-validate` opts the input into live native validation — see the Twig
20
+ * `TextInput` stories for the full explanation.
21
+ */
22
+ export const Validate: StoryObj<typeof Component> = {
23
+ args: {
24
+ type: "email",
25
+ placeholder: "you@example.com",
26
+ "aria-label": "Email address",
27
+ required: true,
28
+ className: "mx-validate",
29
+ },
30
+ }
@@ -22,10 +22,7 @@ const FormText = ({
22
22
  const handleChange: ChangeEventHandler<HTMLInputElement> = ({ currentTarget }) =>
23
23
  setInputValue(currentTarget.value)
24
24
 
25
- const classes = classNames("mx-input__text", className, {
26
- error: status === FormStatus.INVALID,
27
- success: status === FormStatus.VALID,
28
- })
25
+ const classes = classNames("mx-input__text", className)
29
26
 
30
27
  return (
31
28
  <input
@@ -1,3 +1,5 @@
1
1
  // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
2
 
3
3
  exports[`Text Input 1`] = `"<input class="mx-input__text" id="mix-text" aria-describedby="help-mix-text" aria-invalid="false" placeholder="Placeholder" aria-label="Text input" type="text" value="">"`;
4
+
5
+ exports[`Validate 1`] = `"<input class="mx-input__text mx-validate" id="mix-text" required="" aria-describedby="help-mix-text" aria-invalid="false" placeholder="you@example.com" aria-label="Email address" type="email" value="">"`;
@@ -1,7 +1,5 @@
1
1
  {% set classes = [
2
2
  'mx-input__text',
3
- status == "invalid" ? "error" : null,
4
- status == "valid" ? "success" : null,
5
3
  ] %}
6
4
  {% set attributes = (attributes ?? create_attribute()).addClass(classes) %}
7
5
  {% set status = status|default("default") %}
@@ -21,10 +21,7 @@ const FormTextarea = ({
21
21
  const handleChange: ChangeEventHandler<HTMLTextAreaElement> = ({ currentTarget }) =>
22
22
  setInputValue(currentTarget.value)
23
23
 
24
- const classes = classNames("mx-input__textarea", className, {
25
- error: status === FormStatus.INVALID,
26
- success: status === FormStatus.VALID,
27
- })
24
+ const classes = classNames("mx-input__textarea", className)
28
25
 
29
26
  return (
30
27
  <textarea
@@ -1,7 +1,5 @@
1
1
  {% set classes = [
2
2
  'mx-input__textarea',
3
- status == "invalid" ? "error" : null,
4
- status == "valid" ? "success" : null,
5
3
  ] %}
6
4
  {% set attributes = (attributes ?? create_attribute()).addClass(classes) %}
7
5
  {% if hasDescription or status != "default" %}
@@ -0,0 +1,153 @@
1
+ import { Meta, StoryObj } from "@storybook/html-vite"
2
+ import FormItem from "./FormItem/form-item.twig"
3
+ import Label from "./Label/label.twig"
4
+ import TextInput from "./TextInput/input-text.twig"
5
+ import Textarea from "./Textarea/input-textarea.twig"
6
+ import Select from "./Select/input-select.twig"
7
+ import Radio from "./Radio/input-radio.twig"
8
+ import Checkbox from "./Checkbox/input-checkbox.twig"
9
+ import Autocomplete from "./Autocomplete/autocomplete.twig"
10
+ import Button from "../Atom/Button/button.twig"
11
+ import "./form.css"
12
+ import "./Autocomplete/autocomplete.css"
13
+ import "./Autocomplete/Elements/Autocomplete"
14
+ import { InputTypeTypes } from "@pnx-mixtape/ids-shape"
15
+ import { DrupalAttribute } from "drupal-attribute"
16
+
17
+ const meta: Meta = {
18
+ tags: ["autodocs"],
19
+ parameters: {
20
+ a11y: {
21
+ config: {
22
+ rules: [
23
+ {
24
+ // ID's are not unique due to test-runner.
25
+ id: "label",
26
+ enabled: false,
27
+ },
28
+ ],
29
+ },
30
+ },
31
+ },
32
+ render: () =>
33
+ `<form class="mx-vertical-flow">${[
34
+ FormItem({
35
+ id: "validate-text",
36
+ label: Label({ id: "validate-text", title: "Email address", required: true }),
37
+ fields: TextInput({
38
+ id: "validate-text",
39
+ type: InputTypeTypes.EMAIL,
40
+ name: "validate-text",
41
+ placeholder: "you@example.com",
42
+ required: true,
43
+ attributes: new DrupalAttribute().addClass("mx-validate"),
44
+ }),
45
+ }),
46
+ FormItem({
47
+ id: "validate-textarea",
48
+ label: Label({ id: "validate-textarea", title: "Message", required: true }),
49
+ fields: Textarea({
50
+ id: "validate-textarea",
51
+ name: "validate-textarea",
52
+ required: true,
53
+ attributes: new DrupalAttribute().addClass("mx-validate"),
54
+ }),
55
+ }),
56
+ FormItem({
57
+ id: "validate-select",
58
+ label: Label({ id: "validate-select", title: "Favourite colour", required: true }),
59
+ fields: Select({
60
+ id: "validate-select",
61
+ name: "validate-select",
62
+ required: true,
63
+ attributes: new DrupalAttribute().addClass("mx-validate"),
64
+ options: [
65
+ { label: "Choose an option", value: "" },
66
+ { label: "Red", value: "red" },
67
+ { label: "Green", value: "green" },
68
+ { label: "Blue", value: "blue" },
69
+ ],
70
+ }),
71
+ }),
72
+ FormItem({
73
+ id: "validate-radio",
74
+ as: "fieldset",
75
+ label: Label({ title: "Do you like music?", as: "legend", required: true }),
76
+ fields: [
77
+ Radio({
78
+ id: "validate-radio-yes",
79
+ name: "validate-radio",
80
+ label: "Yes",
81
+ value: "yes",
82
+ required: true,
83
+ attributes: new DrupalAttribute().addClass("mx-validate"),
84
+ }),
85
+ Radio({
86
+ id: "validate-radio-no",
87
+ name: "validate-radio",
88
+ label: "No",
89
+ value: "no",
90
+ required: true,
91
+ attributes: new DrupalAttribute().addClass("mx-validate"),
92
+ }),
93
+ ].join(""),
94
+ }),
95
+ FormItem({
96
+ id: "validate-checkbox",
97
+ as: "fieldset",
98
+ label: Label({ title: "Which genres do you like?", as: "legend", required: true }),
99
+ fields: [
100
+ Checkbox({
101
+ id: "validate-checkbox-rock",
102
+ name: "validate-checkbox-rock",
103
+ label: "Rock",
104
+ value: "rock",
105
+ required: true,
106
+ attributes: new DrupalAttribute().addClass("mx-validate"),
107
+ }),
108
+ Checkbox({
109
+ id: "validate-checkbox-jazz",
110
+ name: "validate-checkbox-jazz",
111
+ label: "Jazz",
112
+ value: "jazz",
113
+ required: true,
114
+ attributes: new DrupalAttribute().addClass("mx-validate"),
115
+ }),
116
+ ].join(""),
117
+ }),
118
+ FormItem({
119
+ id: "validate-autocomplete",
120
+ label: Label({ id: "validate-autocomplete", title: "Favourite fruit", required: true }),
121
+ fields: Autocomplete({
122
+ id: "validate-autocomplete",
123
+ name: "validate-autocomplete",
124
+ label: "Favourite fruit",
125
+ type: InputTypeTypes.TEXT,
126
+ required: true,
127
+ attributes: new DrupalAttribute().addClass("mx-validate"),
128
+ options: [
129
+ { label: "Apple", value: "apple" },
130
+ { label: "Banana", value: "banana" },
131
+ { label: "Cherry", value: "cherry" },
132
+ ],
133
+ }),
134
+ }),
135
+ Button({ title: "Submit", as: "submit" }),
136
+ ].join("")}</form>`,
137
+ }
138
+
139
+ export default meta
140
+ type Story = StoryObj
141
+
142
+ /**
143
+ * `mx-validate` opts a control into live native validation — the border
144
+ * turns red once the browser rejects the value and the field has been
145
+ * interacted with, green once it's accepted.
146
+ *
147
+ * Add the class to the control itself (text input, textarea, select), or on
148
+ * the `.mx-radio` / `.mx-checkbox` / `.mx-autocomplete` wrapper for
149
+ * components that put `attributes` on the wrapper rather than the input.
150
+ *
151
+ * Hit Submit to trigger the invalid states on the empty required fields.
152
+ */
153
+ export const Validation: Story = {}
@@ -0,0 +1,130 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`Validation 1`] = `
4
+ "<form class="mx-vertical-flow">
5
+ <div class="mx-form-item">
6
+
7
+ <label class="mx-label" for="validate-text">
8
+ Email address
9
+ <span class="mx-required">(required)</span></label>
10
+
11
+
12
+
13
+ <input class="mx-validate mx-input__text" id="validate-text" name="validate-text" type="email" value="" placeholder="you@example.com" required="">
14
+
15
+
16
+
17
+ </div>
18
+
19
+ <div class="mx-form-item">
20
+
21
+ <label class="mx-label" for="validate-textarea">
22
+ Message
23
+ <span class="mx-required">(required)</span></label>
24
+
25
+
26
+
27
+ <textarea class="mx-validate mx-input__textarea" id="unique-4" name="unique-0" aria-describedby="unique-0" required=""></textarea>
28
+
29
+
30
+
31
+ </div>
32
+
33
+ <div class="mx-form-item">
34
+
35
+ <label class="mx-label" for="validate-select">
36
+ Favourite colour
37
+ <span class="mx-required">(required)</span></label>
38
+
39
+
40
+
41
+ <select class="mx-validate mx-input__select" id="unique-5" name="unique-1" aria-describedby="unique-1" required="">
42
+ <button>
43
+ <selectedcontent>Choose an option</selectedcontent>
44
+ </button>
45
+ <option value="">Choose an option</option>
46
+ <option value="red">Red</option>
47
+ <option value="green">Green</option>
48
+ <option value="blue">Blue</option>
49
+ </select>
50
+
51
+
52
+
53
+ </div>
54
+
55
+ <fieldset class="mx-form-item mx-fieldset">
56
+
57
+ <legend class="mx-legend">
58
+ Do you like music?
59
+ <span class="mx-required">(required)</span></legend>
60
+
61
+
62
+ <div class="mx-validate mx-radio">
63
+ <input id="validate-radio-yes" type="radio" name="validate-radio" value="yes" required="">
64
+ <label for="validate-radio-yes">Yes</label>
65
+ </div>
66
+ <div class="mx-validate mx-radio">
67
+ <input id="validate-radio-no" type="radio" name="validate-radio" value="no" required="">
68
+ <label for="validate-radio-no">No</label>
69
+ </div>
70
+
71
+
72
+
73
+ </fieldset>
74
+
75
+ <fieldset class="mx-form-item mx-fieldset">
76
+
77
+ <legend class="mx-legend">
78
+ Which genres do you like?
79
+ <span class="mx-required">(required)</span></legend>
80
+
81
+
82
+
83
+ <div class="mx-validate mx-checkbox">
84
+ <input id="validate-checkbox-rock" type="checkbox" name="validate-checkbox-rock" value="rock" required="">
85
+ <label for="validate-checkbox-rock">Rock</label>
86
+ </div>
87
+
88
+ <div class="mx-validate mx-checkbox">
89
+ <input id="validate-checkbox-jazz" type="checkbox" name="validate-checkbox-jazz" value="jazz" required="">
90
+ <label for="validate-checkbox-jazz">Jazz</label>
91
+ </div>
92
+
93
+
94
+
95
+ </fieldset>
96
+
97
+ <div class="mx-form-item">
98
+
99
+ <label class="mx-label" for="validate-autocomplete">
100
+ Favourite fruit
101
+ <span class="mx-required">(required)</span></label>
102
+
103
+
104
+
105
+ <mx-autocomplete class="mx-autocomplete">
106
+ <div class="mx-autocomplete__control mx-form--inline-button">
107
+ <input class="mx-validate mx-input__text" id="unique-6" name="unique-2" type="text" role="combobox" aria-expanded="false" aria-controls="unique-2" aria-autocomplete="list" aria-activedescendant="" aria-describedby="unique-2" aria-label="Favourite fruit" autocomplete="off" required="">
108
+ </div>
109
+ <div class="mx-autocomplete__suggestions">
110
+
111
+ <ul class="mx-autocomplete__listbox" id="unique-0" role="listbox" hidden="">
112
+ <li class="mx-autocomplete__option" id="validate-autocomplete-option-0" role="option" data-value="apple" aria-selected="false">Apple</li>
113
+ <li class="mx-autocomplete__option" id="validate-autocomplete-option-1" role="option" data-value="banana" aria-selected="false">Banana</li>
114
+ <li class="mx-autocomplete__option" id="validate-autocomplete-option-2" role="option" data-value="cherry" aria-selected="false">Cherry</li>
115
+ </ul>
116
+ <output class="sr-only" aria-live="polite" data-autocomplete-status=""></output>
117
+ </div>
118
+ </mx-autocomplete>
119
+
120
+
121
+
122
+ </div>
123
+
124
+ <button class="mx-button" type="submit">
125
+
126
+ <span>Submit</span>
127
+
128
+ </button>
129
+ </form>"
130
+ `;