@pnx-mixtape/mxds 0.1.0-rc.1 → 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.
- package/.storybook/decorators.ts +15 -0
- package/.storybook/preview.ts +4 -1
- package/.storybook/themes/campus-vibrant.css +181 -0
- package/.storybook/themes/civic-formal.css +246 -0
- package/.storybook/{theme-demo.css → themes/demo.css} +14 -7
- package/.storybook/themes/health-services.css +255 -0
- package/.storybook/themes/research-faculty.css +169 -0
- package/dist/build/accordion.css +1 -1
- package/dist/build/base.css +1 -1
- package/dist/build/button.css +1 -1
- package/dist/build/chunks/DropMenu-1baQc8Yc.js +1 -0
- package/dist/build/chunks/InPageNavigation-CVieEl9u.js +1 -0
- package/dist/build/chunks/Popover-Cxg-s5Jn.js +1 -0
- package/dist/build/chunks/Sticky-cWUVSOXN.js +1 -0
- package/dist/build/chunks/{Tabs-Dm8g301e.js → Tabs-DA_Ez8pv.js} +2 -2
- package/dist/build/chunks/polyfills-C7pRt54M.js +1 -0
- package/dist/build/constants.css +1 -1
- package/dist/build/drop-menu.entry.js +1 -1
- package/dist/build/elements.js +1 -1
- package/dist/build/form.css +1 -1
- package/dist/build/grid.css +1 -1
- package/dist/build/in-page-alert.css +1 -1
- package/dist/build/in-page-navigation.entry.js +1 -1
- package/dist/build/navigation.css +1 -1
- package/dist/build/popover.entry.js +1 -1
- package/dist/build/print.css +1 -1
- package/dist/build/progress-indicator.css +1 -0
- package/dist/build/react.js +1 -1
- package/dist/build/sidebar.css +1 -1
- package/dist/build/sticky.entry.js +1 -1
- package/dist/build/tabs.entry.js +1 -1
- package/dist/build/tag.css +1 -1
- package/dist/build/utilities.css +1 -1
- package/dist/types/Atom/DefinitionList/DefinitionList.d.ts +3 -2
- package/dist/types/Atom/DefinitionList/DefinitionList.d.ts.map +1 -1
- package/dist/types/Atom/Heading/Heading.d.ts.map +1 -1
- package/dist/types/Atom/List/List.d.ts +10 -0
- package/dist/types/Atom/List/List.d.ts.map +1 -0
- package/dist/types/Component/DropMenu/Elements/DropMenu.d.ts.map +1 -1
- package/dist/types/Component/InPageNavigation/Elements/InPageNavigation.d.ts +2 -2
- package/dist/types/Component/InPageNavigation/Elements/InPageNavigation.d.ts.map +1 -1
- package/dist/types/Component/InPageNavigation/Hooks/useInPageNavigation.d.ts.map +1 -1
- package/dist/types/Component/ProgressIndicator/ProgressIndicator.d.ts +9 -0
- package/dist/types/Component/ProgressIndicator/ProgressIndicator.d.ts.map +1 -0
- package/dist/types/Component/Tabs/Elements/Tabs.d.ts.map +1 -1
- package/dist/types/Component/Tag/Tag.d.ts +4 -2
- package/dist/types/Component/Tag/Tag.d.ts.map +1 -1
- package/dist/types/Form/Autocomplete/Autocomplete.d.ts.map +1 -1
- package/dist/types/Form/Checkbox/FormCheckbox.d.ts.map +1 -1
- package/dist/types/Form/CheckboxCard/CheckboxCard.d.ts +23 -0
- package/dist/types/Form/CheckboxCard/CheckboxCard.d.ts.map +1 -0
- package/dist/types/Form/Radio/FormRadio.d.ts.map +1 -1
- package/dist/types/Form/RadioCard/RadioCard.d.ts +16 -0
- package/dist/types/Form/RadioCard/RadioCard.d.ts.map +1 -0
- package/dist/types/Form/Select/FormSelect.d.ts.map +1 -1
- package/dist/types/Form/TextInput/FormText.d.ts.map +1 -1
- package/dist/types/Form/Textarea/FormTextarea.d.ts.map +1 -1
- package/dist/types/enums.d.ts +15 -0
- package/dist/types/enums.d.ts.map +1 -1
- package/dist/types/react.d.ts +4 -0
- package/dist/types/react.d.ts.map +1 -1
- package/package.json +7 -7
- package/src/Atom/Background/_background.css +2 -2
- package/src/Atom/Blockquote/_blockquote.css +9 -2
- package/src/Atom/Button/README.md +1 -0
- package/src/Atom/Button/_buttons-styles.css +6 -6
- package/src/Atom/Button/_buttons.css +5 -3
- package/src/Atom/DefinitionList/DefinitionList.stories.ts +39 -2
- package/src/Atom/DefinitionList/DefinitionList.stories.tsx +36 -0
- package/src/Atom/DefinitionList/DefinitionList.tsx +4 -1
- package/src/Atom/DefinitionList/DefinitionListItem.stories.ts +1 -1
- package/src/Atom/DefinitionList/README.md +43 -0
- package/src/Atom/DefinitionList/__snapshots__/DefinitionList.stories.ts.snap +37 -3
- package/src/Atom/DefinitionList/__snapshots__/DefinitionList.stories.tsx.snap +2 -0
- package/src/Atom/DefinitionList/__snapshots__/DefinitionListItem.stories.ts.snap +3 -3
- package/src/Atom/DefinitionList/definition-list-item.twig +1 -0
- package/src/Atom/DefinitionList/definition-list.twig +6 -1
- package/src/Atom/Heading/Heading.tsx +6 -0
- package/src/Atom/Heading/_headings.css +2 -1
- package/src/Atom/Icon/_extended-set.css +30 -30
- package/src/Atom/Icon/_icon.css +36 -36
- package/src/Atom/Image/README.md +2 -2
- package/src/Atom/Image/_image.css +12 -6
- package/src/Atom/List/List.stories.ts +102 -0
- package/src/Atom/List/List.stories.tsx +103 -0
- package/src/Atom/List/List.tsx +22 -0
- package/src/Atom/List/README.md +150 -0
- package/src/Atom/List/__snapshots__/List.stories.ts.snap +43 -0
- package/src/Atom/List/__snapshots__/List.stories.tsx.snap +13 -0
- package/src/Atom/List/_list.css +123 -0
- package/src/Atom/List/list.twig +5 -0
- package/src/Atom/Media/Media.stories.ts +1 -1
- package/src/Atom/Media/__snapshots__/Media.stories.ts.snap +2 -2
- package/src/Atom/Spacing/_spacing.css +24 -24
- package/src/Atom/Table/__snapshots__/Table.stories.ts.snap +24 -4
- package/src/Atom/Table/_table.css +15 -3
- package/src/Atom/Table/table.twig +6 -1
- package/src/Atom/Text/__snapshots__/Text.stories.ts.snap +3 -2
- package/src/Atom/Text/_text-styles.css +21 -1
- package/src/Atom/Text/twig/text-styles-example.twig +3 -2
- package/src/Atom/_base.css +6 -0
- package/src/Atom/_flow.css +1 -1
- package/src/Atom/_generic.css +18 -11
- package/src/Component/Accordion/accordion.css +2 -0
- package/src/Component/Card/__snapshots__/Card.stories.ts.snap +2 -2
- package/src/Component/Card/card.twig +1 -1
- package/src/Component/Carousel/Carousel.stories.ts +46 -23
- package/src/Component/DropMenu/Elements/DropMenu.ts +10 -1
- package/src/Component/InPageAlert/in-page-alert.css +2 -1
- package/src/Component/InPageNavigation/Elements/InPageNavigation.ts +10 -5
- package/src/Component/InPageNavigation/Hooks/useInPageNavigation.ts +10 -5
- package/src/Component/InPageNavigation/InPageNavigation.stories.ts +33 -0
- package/src/Component/InPageNavigation/InPageNavigation.stories.tsx +52 -0
- package/src/Component/InPageNavigation/__snapshots__/InPageNavigation.stories.ts.snap +26 -0
- package/src/Component/InPageNavigation/__snapshots__/InPageNavigation.stories.tsx.snap +2 -0
- package/src/Component/InPageNavigation/twig/content-example-duplicate-headings.twig +20 -0
- package/src/Component/InPageNavigation/twig/in-page-navigation-story-duplicate-headings.twig +2 -0
- package/src/Component/ListItem/__snapshots__/ListItem.stories.ts.snap +4 -4
- package/src/Component/ListItem/list-item.twig +2 -2
- package/src/Component/Navigation/_navigation.css +1 -1
- package/src/Component/Pagination/Pagination.stories.ts +8 -4
- package/src/Component/Pagination/pagination.css +2 -2
- package/src/Component/ProgressIndicator/ProgressIndicator.stories.ts +36 -0
- package/src/Component/ProgressIndicator/ProgressIndicator.stories.tsx +30 -0
- package/src/Component/ProgressIndicator/ProgressIndicator.tsx +60 -0
- package/src/Component/ProgressIndicator/README.md +111 -0
- package/src/Component/ProgressIndicator/__snapshots__/ProgressIndicator.stories.ts.snap +67 -0
- package/src/Component/ProgressIndicator/__snapshots__/ProgressIndicator.stories.tsx.snap +7 -0
- package/src/Component/ProgressIndicator/progress-indicator.css +59 -0
- package/src/Component/ProgressIndicator/progress-indicator.twig +22 -0
- package/src/Component/Sticky/Elements/Sticky.ts +1 -1
- package/src/Component/Sticky/sticky.css +3 -3
- package/src/Component/Tabs/Elements/Tabs.ts +3 -1
- package/src/Component/Tabs/Tabs.stories.ts +55 -21
- package/src/Component/Tabs/__snapshots__/Tabs.stories.ts.snap +2 -2
- package/src/Component/Tag/README.md +52 -26
- package/src/Component/Tag/Tag.stories.tsx +24 -0
- package/src/Component/Tag/Tag.tsx +12 -2
- package/src/Component/Tag/TagItem.stories.ts +25 -0
- package/src/Component/Tag/__snapshots__/Tag.stories.ts.snap +2 -2
- package/src/Component/Tag/__snapshots__/Tag.stories.tsx.snap +2 -0
- package/src/Component/Tag/tag-item.twig +2 -2
- package/src/Component/Tag/tag.css +76 -2
- package/src/Form/Autocomplete/Autocomplete.stories.ts +8 -3
- package/src/Form/Autocomplete/Autocomplete.tsx +1 -4
- package/src/Form/Autocomplete/README.md +26 -26
- package/src/Form/Autocomplete/__snapshots__/Autocomplete.stories.ts.snap +1 -1
- package/src/Form/Autocomplete/__snapshots__/Autocomplete.stories.tsx.snap +1 -1
- package/src/Form/Autocomplete/autocomplete.twig +0 -2
- package/src/Form/Checkbox/FormCheckbox.tsx +2 -8
- package/src/Form/Checkbox/README.md +23 -18
- package/src/Form/Checkbox/input-checkbox.twig +0 -2
- package/src/Form/CheckboxCard/CheckboxCard.stories.ts +90 -0
- package/src/Form/CheckboxCard/CheckboxCard.stories.tsx +43 -0
- package/src/Form/CheckboxCard/CheckboxCard.tsx +102 -0
- package/src/Form/CheckboxCard/README.md +163 -0
- package/src/Form/CheckboxCard/__snapshots__/CheckboxCard.stories.ts.snap +63 -0
- package/src/Form/CheckboxCard/__snapshots__/CheckboxCard.stories.tsx.snap +7 -0
- package/src/Form/CheckboxCard/checkbox-card.twig +29 -0
- package/src/Form/FormItem/__snapshots__/FormItem.stories.ts.snap +2 -2
- package/src/Form/README.md +65 -13
- package/src/Form/Radio/FormRadio.tsx +1 -4
- package/src/Form/Radio/input-radio.twig +0 -2
- package/src/Form/RadioCard/README.md +146 -0
- package/src/Form/RadioCard/RadioCard.stories.ts +95 -0
- package/src/Form/RadioCard/RadioCard.stories.tsx +26 -0
- package/src/Form/RadioCard/RadioCard.tsx +59 -0
- package/src/Form/RadioCard/__snapshots__/RadioCard.stories.ts.snap +69 -0
- package/src/Form/RadioCard/__snapshots__/RadioCard.stories.tsx.snap +3 -0
- package/src/Form/RadioCard/radio-card.twig +20 -0
- package/src/Form/Select/FormSelect.tsx +1 -4
- package/src/Form/Select/input-select.twig +0 -2
- package/src/Form/TextInput/FormText.stories.tsx +14 -0
- package/src/Form/TextInput/FormText.tsx +1 -4
- package/src/Form/TextInput/__snapshots__/FormText.stories.tsx.snap +2 -0
- package/src/Form/TextInput/input-text.twig +0 -2
- package/src/Form/Textarea/FormTextarea.tsx +1 -4
- package/src/Form/Textarea/input-textarea.twig +0 -2
- package/src/Form/Validation.stories.ts +153 -0
- package/src/Form/__snapshots__/Validation.stories.ts.snap +130 -0
- package/src/Form/form.css +155 -28
- package/src/Layout/Grid/grid.css +4 -4
- package/src/Layout/Sidebar/__snapshots__/Sidebar.stories.ts.snap +5 -5
- package/src/Layout/Sidebar/sidebar.css +1 -0
- package/src/Layout/Sidebar/sidebar.twig +1 -1
- package/src/Utility/_layout-utils.css +8 -0
- package/src/Utility/print.css +50 -28
- package/src/Utility/utilities.css +2 -2
- package/src/constants.css +12 -2
- package/src/enums.ts +17 -0
- package/src/react.ts +4 -0
- package/tools/lib/extract-classnames.mjs +0 -0
- package/tools/lint-classnames.mjs +1 -1
- package/dist/build/chunks/DropMenu-Co9jZZ4S.js +0 -1
- package/dist/build/chunks/InPageNavigation-CzR_KZaz.js +0 -1
- package/dist/build/chunks/Popover-De_CQqcd.js +0 -1
- package/dist/build/chunks/Sticky-Kyjykb36.js +0 -1
- package/dist/build/chunks/polyfills-Dokbkxxk.js +0 -1
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# CheckboxCard
|
|
2
|
+
|
|
3
|
+
A card-style checkbox — a full padded, bordered, selectable card instead of a small inline control. Use for a small set of options that benefit from a larger tap target and optional secondary hint text (e.g. a high-stakes form question), rather than a dense list of ordinary checkboxes.
|
|
4
|
+
|
|
5
|
+
> **HTML/Twig users:** use native `<input type="checkbox">` with both the `.mx-checkbox` and `.mx-choice-card` wrapper classes. The react-aria wiring is React-only.
|
|
6
|
+
|
|
7
|
+
`.mx-choice-card` layers on top of `.mx-checkbox` rather than replacing it — the base class still supplies input reset/sizing, disabled state, and the spacing between stacked cards in a group; `.mx-choice-card`'s higher cascade layer (`design-system.components` vs. `.mx-checkbox`'s `design-system.atoms`) overrides only what the card treatment needs to change (position, padding, border).
|
|
8
|
+
|
|
9
|
+
`.mx-choice-card` is shared with `RadioCard` — it lives in `src/Form/form.css` alongside the base `.mx-checkbox`/`.mx-radio` rules, not in a per-component CSS file, since the two components' card treatments were identical enough to not warrant separate copies.
|
|
10
|
+
|
|
11
|
+
## CSS Import
|
|
12
|
+
|
|
13
|
+
```css
|
|
14
|
+
@import "@pnx-mixtape/mxds/src/Form/form.css";
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Twig
|
|
20
|
+
|
|
21
|
+
```twig
|
|
22
|
+
{# Single card #}
|
|
23
|
+
{% include '@mixtape/Form/CheckboxCard/checkbox-card.twig' with {
|
|
24
|
+
id: 'pay-carer',
|
|
25
|
+
name: 'payments',
|
|
26
|
+
value: 'Carer Payment or Allowance',
|
|
27
|
+
label: 'Carer Payment or Carer Allowance',
|
|
28
|
+
} %}
|
|
29
|
+
|
|
30
|
+
{# With a secondary hint line under the label #}
|
|
31
|
+
{% include '@mixtape/Form/CheckboxCard/checkbox-card.twig' with {
|
|
32
|
+
id: 'pay-other',
|
|
33
|
+
name: 'payments',
|
|
34
|
+
value: 'Another income support payment',
|
|
35
|
+
label: 'Another income support payment',
|
|
36
|
+
hint: 'You will be asked which one before you submit',
|
|
37
|
+
} %}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**checkbox-card.twig props:**
|
|
41
|
+
|
|
42
|
+
| Prop | Type | Required | Description |
|
|
43
|
+
| ---------------- | ----------------- | -------- | --------------------------------------------------------------------------------------------------------------------- |
|
|
44
|
+
| `id` | `string` | Yes | Input id — also used to derive the `aria-describedby` ids (`help-{id}`, `status-{id}`) |
|
|
45
|
+
| `name` | `string` | Yes | `name` attribute on the input |
|
|
46
|
+
| `value` | `string` | Yes | `value` attribute on the input |
|
|
47
|
+
| `label` | `string` | Yes | Label text rendered inside `<label for="{id}">` |
|
|
48
|
+
| `hint` | `string` | No | Secondary line rendered under the label, inside the card |
|
|
49
|
+
| `checked` | `boolean` | No | Adds `checked="checked"` to the input |
|
|
50
|
+
| `required` | `boolean` | No | Adds the `required` attribute |
|
|
51
|
+
| `status` | `FormStatusTypes` | No | `invalid` adds the `error` class and `aria-invalid="true"`; `valid` adds the `success` class. Defaults to `default` |
|
|
52
|
+
| `hasDescription` | `boolean` | No | When `true`, `aria-describedby` is wired up to `help-{id}` (and `status-{id}` when `status` is set) |
|
|
53
|
+
| `attributes` | `Attribute` | No | Optional Drupal-style attributes object — `mx-checkbox mx-choice-card` (and the status class) is always merged on top |
|
|
54
|
+
|
|
55
|
+
For a group of cards, compose several `checkbox-card.twig` includes inside a plain `<fieldset>`, same as `Checkbox` — see HTML below.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## HTML
|
|
60
|
+
|
|
61
|
+
```html
|
|
62
|
+
<fieldset class="mx-form-item mx-fieldset mx-fieldset--plain">
|
|
63
|
+
<legend class="mx-legend">Which payments do you currently receive?</legend>
|
|
64
|
+
|
|
65
|
+
<div class="mx-checkbox mx-choice-card">
|
|
66
|
+
<input type="checkbox" id="pay-age" name="payments" value="Age Pension" />
|
|
67
|
+
<label for="pay-age">Age Pension</label>
|
|
68
|
+
</div>
|
|
69
|
+
|
|
70
|
+
<div class="mx-checkbox mx-choice-card">
|
|
71
|
+
<input type="checkbox" id="pay-other" name="payments" value="Another income support payment" />
|
|
72
|
+
<label for="pay-other">
|
|
73
|
+
Another income support payment
|
|
74
|
+
<span class="mx-choice-card__hint">You will be asked which one before you submit</span>
|
|
75
|
+
</label>
|
|
76
|
+
</div>
|
|
77
|
+
</fieldset>
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### "Or" divider between two option groups
|
|
81
|
+
|
|
82
|
+
`.mx-choice-divider` renders a horizontal rule with centred text — useful for splitting a set of card choices into "pick one of these — or — pick this other option" (see the source mockup's card question). It's plain markup, not tied to `CheckboxCard`/`RadioCard` specifically:
|
|
83
|
+
|
|
84
|
+
```html
|
|
85
|
+
<div class="mx-choice-divider">or</div>
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## React
|
|
91
|
+
|
|
92
|
+
### Standalone card
|
|
93
|
+
|
|
94
|
+
```tsx
|
|
95
|
+
import { SingleCheckboxCard } from "@pnx-mixtape/mxds/react"
|
|
96
|
+
|
|
97
|
+
;<SingleCheckboxCard value="carer">Carer Payment or Carer Allowance</SingleCheckboxCard>
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Card group
|
|
101
|
+
|
|
102
|
+
Use `FormCheckboxCard` as the group wrapper and `CheckboxCard` for each option — same pattern as `FormCheckbox`/`Checkbox`.
|
|
103
|
+
|
|
104
|
+
```tsx
|
|
105
|
+
import { FormCheckboxCard, CheckboxCard } from "@pnx-mixtape/mxds/react"
|
|
106
|
+
|
|
107
|
+
;<FormCheckboxCard label="Which payments do you currently receive?" onChange={setSelected}>
|
|
108
|
+
<CheckboxCard value="age">Age Pension</CheckboxCard>
|
|
109
|
+
<CheckboxCard value="dsp">Disability Support Pension</CheckboxCard>
|
|
110
|
+
<CheckboxCard value="other" hint="You will be asked which one before you submit">
|
|
111
|
+
Another income support payment
|
|
112
|
+
</CheckboxCard>
|
|
113
|
+
</FormCheckboxCard>
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
**`FormCheckboxCard` props:** all `CheckboxGroupProps` from react-stately + `label: string`
|
|
117
|
+
|
|
118
|
+
**`CheckboxCard` props:** all `AriaCheckboxGroupItemProps` + `hint?: string`, `status?: FormStatus`, `className?: string`
|
|
119
|
+
|
|
120
|
+
**`SingleCheckboxCard` props:** all `AriaCheckboxProps` + `hint?: string`, `status?: FormStatus`, `className?: string`
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
### React ⇄ Twig parity
|
|
125
|
+
|
|
126
|
+
The prop shapes intentionally diverge, same as `Checkbox`/`Radio`:
|
|
127
|
+
|
|
128
|
+
- Twig's `label` becomes React `children` — react-aria composes the accessible name from rendered content, not a string prop.
|
|
129
|
+
- Twig's `id`/`name`/`checked`/`required`/`value` are handled by the react-aria hooks (`useCheckboxGroupItem`/`useCheckbox`) via prop spread — they aren't declared as explicit props because react-aria owns generating/wiring them (e.g. `id` comes from `useId()`).
|
|
130
|
+
- Twig's `hasDescription` manually wires `aria-describedby` to a `help-{id}` node; React doesn't need it because react-aria's hooks generate and wire that association themselves when a description is passed through their own props.
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## Modifiers
|
|
135
|
+
|
|
136
|
+
No BEM modifier classes — `error`/`success` are applied as plain (unprefixed) status classes, matching `Checkbox`'s convention, driven by the `status` prop/Twig var.
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## Accessibility
|
|
141
|
+
|
|
142
|
+
- The input stays a real, focusable `<input type="checkbox">` — only its default appearance is replaced (`appearance: none` plus a custom checked-state image), so it keeps native checkbox semantics, keyboard support (<kbd>Space</kbd> to toggle), and form submission behaviour.
|
|
143
|
+
- The whole card is clickable because the `<label>` fills it — the input itself only needs to be as large as `--control-size` dictates, not the full card.
|
|
144
|
+
- The focus ring (`:focus-visible`) renders on the input itself, inside the card, rather than around the whole card — keeps the indicator anchored to the actual interactive control.
|
|
145
|
+
- `hint` text is inside the same `<label>` as the primary text, so both are announced together when the control receives focus — don't rely on `hint` to convey anything the primary `label` text doesn't already establish.
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## Customisation
|
|
150
|
+
|
|
151
|
+
| Custom property | Applies to | Default |
|
|
152
|
+
| ------------------------- | ----------------------------------------------------------------- | --------------------------------------------------- |
|
|
153
|
+
| `--choice-padding-block` | `.mx-choice-card` | `var(--spacing-s)` |
|
|
154
|
+
| `--choice-padding-inline` | `.mx-choice-card` | `var(--spacing-m)` |
|
|
155
|
+
| `--choice-colour-active` | `.mx-choice-card` | `var(--colour-primary)` |
|
|
156
|
+
| `--control-size` | `.mx-choice-card input`, and `.mx-checkbox`'s own input styling | `var(--spacing-m)` (shared with `Checkbox`/`Radio`) |
|
|
157
|
+
| `--line-width` | `.mx-choice-card`'s border, and `.mx-checkbox`'s own input border | `2px` |
|
|
158
|
+
|
|
159
|
+
```css
|
|
160
|
+
.theme--health-services .mx-choice-card {
|
|
161
|
+
--choice-colour-active: var(--colour-primary);
|
|
162
|
+
}
|
|
163
|
+
```
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
|
+
|
|
3
|
+
exports[`Checkbox Card 1`] = `
|
|
4
|
+
"
|
|
5
|
+
<div class="mx-checkbox mx-choice-card">
|
|
6
|
+
<input id="pay-carer" type="checkbox" name="payments" value="Carer Payment or Allowance">
|
|
7
|
+
<label for="pay-carer">
|
|
8
|
+
Carer Payment or Carer Allowance
|
|
9
|
+
</label>
|
|
10
|
+
</div>
|
|
11
|
+
"
|
|
12
|
+
`;
|
|
13
|
+
|
|
14
|
+
exports[`Checked 1`] = `
|
|
15
|
+
"
|
|
16
|
+
<div class="mx-checkbox mx-choice-card">
|
|
17
|
+
<input id="pay-carer" type="checkbox" name="payments" value="Carer Payment or Allowance" checked="checked">
|
|
18
|
+
<label for="pay-carer">
|
|
19
|
+
Carer Payment or Carer Allowance
|
|
20
|
+
</label>
|
|
21
|
+
</div>
|
|
22
|
+
"
|
|
23
|
+
`;
|
|
24
|
+
|
|
25
|
+
exports[`Divider 1`] = `
|
|
26
|
+
"
|
|
27
|
+
<div class="mx-checkbox mx-choice-card">
|
|
28
|
+
<input id="card-yes" type="checkbox" name="card" value="Yes, I hold a card">
|
|
29
|
+
<label for="card-yes">
|
|
30
|
+
Yes, I hold a card
|
|
31
|
+
<span class="mx-choice-card__hint">Pensioner Concession, Health Care or DVA card</span> </label>
|
|
32
|
+
</div>
|
|
33
|
+
<div class="mx-choice-divider">or</div>
|
|
34
|
+
<div class="mx-checkbox mx-choice-card">
|
|
35
|
+
<input id="card-unsure" type="checkbox" name="card" value="Not sure">
|
|
36
|
+
<label for="card-unsure">
|
|
37
|
+
I am not sure
|
|
38
|
+
<span class="mx-choice-card__hint">We will ask a few more questions to work it out</span> </label>
|
|
39
|
+
</div>
|
|
40
|
+
"
|
|
41
|
+
`;
|
|
42
|
+
|
|
43
|
+
exports[`Invalid 1`] = `
|
|
44
|
+
"
|
|
45
|
+
<div class="mx-checkbox mx-choice-card">
|
|
46
|
+
<input id="pay-carer" type="checkbox" name="unique-0" value="Carer Payment or Allowance" aria-describedby="unique-0" aria-invalid="true">
|
|
47
|
+
<label for="pay-carer">
|
|
48
|
+
Carer Payment or Carer Allowance
|
|
49
|
+
</label>
|
|
50
|
+
</div>
|
|
51
|
+
"
|
|
52
|
+
`;
|
|
53
|
+
|
|
54
|
+
exports[`With Hint 1`] = `
|
|
55
|
+
"
|
|
56
|
+
<div class="mx-checkbox mx-choice-card">
|
|
57
|
+
<input id="pay-other" type="checkbox" name="payments" value="Another income support payment">
|
|
58
|
+
<label for="pay-other">
|
|
59
|
+
Another income support payment
|
|
60
|
+
<span class="mx-choice-card__hint">You will be asked which one before you submit</span> </label>
|
|
61
|
+
</div>
|
|
62
|
+
"
|
|
63
|
+
`;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
|
+
|
|
3
|
+
exports[`Group 1`] = `"<fieldset class="mx-form-item mx-fieldset" role="group" id="react-aria-_r_6_"><legend class="mx-legend">Which payments do you currently receive?</legend><div class="mx-checkbox mx-choice-card"><input id="_r_b_" data-react-aria-pressable="true" tabindex="0" type="checkbox" value="age"><label for="_r_b_">Age Pension</label></div><div class="mx-checkbox mx-choice-card"><input id="_r_e_" data-react-aria-pressable="true" tabindex="0" type="checkbox" value="dsp"><label for="_r_e_">Disability Support Pension</label></div><div class="mx-checkbox mx-choice-card"><input id="_r_h_" data-react-aria-pressable="true" tabindex="0" type="checkbox" value="carer"><label for="_r_h_">Carer Payment or Carer Allowance</label></div><div class="mx-checkbox mx-choice-card"><input id="_r_k_" data-react-aria-pressable="true" tabindex="0" type="checkbox" value="other"><label for="_r_k_">Another income support payment<span class="mx-choice-card__hint">You will be asked which one before you submit</span></label></div></fieldset>"`;
|
|
4
|
+
|
|
5
|
+
exports[`Single 1`] = `"<div class="mx-checkbox mx-choice-card"><input id="_r_0_" data-react-aria-pressable="true" tabindex="0" type="checkbox" value="carer"><label for="_r_0_">Carer Payment or Carer Allowance</label></div>"`;
|
|
6
|
+
|
|
7
|
+
exports[`With Hint 1`] = `"<div class="mx-checkbox mx-choice-card"><input id="_r_3_" data-react-aria-pressable="true" tabindex="0" type="checkbox" value="other"><label for="_r_3_">Another income support payment<span class="mx-choice-card__hint">You will be asked which one before you submit</span></label></div>"`;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{% set classes = [
|
|
2
|
+
'mx-checkbox',
|
|
3
|
+
'mx-choice-card',
|
|
4
|
+
] %}
|
|
5
|
+
{% set attributes = (attributes ?? create_attribute()).addClass(classes) %}
|
|
6
|
+
{% set status = status|default("default") %}
|
|
7
|
+
{% if hasDescription or status != "default" %}
|
|
8
|
+
{% set helpers = [
|
|
9
|
+
hasDescription ? "help-"~ id : null,
|
|
10
|
+
status != "default" ? "status-"~ id : null,
|
|
11
|
+
]|join(" ")|trim() %}
|
|
12
|
+
{% endif %}
|
|
13
|
+
|
|
14
|
+
<div{{ attributes }}>
|
|
15
|
+
<input
|
|
16
|
+
id="{{ id }}"
|
|
17
|
+
type="checkbox"
|
|
18
|
+
name="{{ name }}"
|
|
19
|
+
value="{{ value }}"
|
|
20
|
+
{% if checked %} checked="checked"{% endif %}
|
|
21
|
+
{% if helpers is not empty %} aria-describedby="{{ helpers }}"{% endif %}
|
|
22
|
+
{% if status == "invalid" %} aria-invalid="true"{% endif %}
|
|
23
|
+
{% if required %} required{% endif %}
|
|
24
|
+
>
|
|
25
|
+
<label for="{{ id }}">
|
|
26
|
+
{{ label }}
|
|
27
|
+
{% if hint %}<span class="mx-choice-card__hint">{{ hint }}</span>{% endif %}
|
|
28
|
+
</label>
|
|
29
|
+
</div>
|
|
@@ -31,7 +31,7 @@ exports[`Error 1`] = `
|
|
|
31
31
|
|
|
32
32
|
|
|
33
33
|
|
|
34
|
-
<input class="mx-input__text
|
|
34
|
+
<input class="mx-input__text" id="form-item" name="unique-0" type="text" value="Musicals" placeholder="eg. Funk, Soul, Blues.." aria-describedby="unique-0" aria-invalid="true">
|
|
35
35
|
|
|
36
36
|
<div class="mx-description" id="help-form-item">
|
|
37
37
|
Help text that provides additional information about the field.
|
|
@@ -118,7 +118,7 @@ exports[`Success 1`] = `
|
|
|
118
118
|
|
|
119
119
|
|
|
120
120
|
|
|
121
|
-
<input class="mx-input__text
|
|
121
|
+
<input class="mx-input__text" id="form-item" name="unique-0" type="text" value="Jazz" placeholder="eg. Funk, Soul, Blues.." aria-describedby="unique-0">
|
|
122
122
|
|
|
123
123
|
<div class="mx-description" id="help-form-item">
|
|
124
124
|
Help text that provides additional information about the field.
|
package/src/Form/README.md
CHANGED
|
@@ -32,7 +32,7 @@ The input's `aria-describedby` is automatically wired to `help-{id}` — `FormDe
|
|
|
32
32
|
<div class="mx-form-item">
|
|
33
33
|
<label class="mx-label" for="email">Email address</label>
|
|
34
34
|
<input
|
|
35
|
-
class="mx-input__text
|
|
35
|
+
class="mx-input__text"
|
|
36
36
|
id="email"
|
|
37
37
|
type="email"
|
|
38
38
|
name="email"
|
|
@@ -46,12 +46,13 @@ The input's `aria-describedby` is automatically wired to `help-{id}` — `FormDe
|
|
|
46
46
|
|
|
47
47
|
## Input modifier classes
|
|
48
48
|
|
|
49
|
-
| Class | Effect
|
|
50
|
-
| ------------------------ |
|
|
51
|
-
| `mx-input--wide` | `inline-size: 100%` — fills its container
|
|
52
|
-
| `mx-input--small` | Reduced border (1px), smaller chevron offset
|
|
53
|
-
| `mx-input--icon` | Adds leading icon via `::before` mask
|
|
54
|
-
| `mx-form--inline-button` | Input + button fused into one bordered unit (used in Search)
|
|
49
|
+
| Class | Effect |
|
|
50
|
+
| ------------------------ | ------------------------------------------------------------- |
|
|
51
|
+
| `mx-input--wide` | `inline-size: 100%` — fills its container |
|
|
52
|
+
| `mx-input--small` | Reduced border (1px), smaller chevron offset |
|
|
53
|
+
| `mx-input--icon` | Adds leading icon via `::before` mask |
|
|
54
|
+
| `mx-form--inline-button` | Input + button fused into one bordered unit (used in Search) |
|
|
55
|
+
| `mx-validate` | Opt in to live `:user-valid` / `:user-invalid` border colours |
|
|
55
56
|
|
|
56
57
|
## Form actions
|
|
57
58
|
|
|
@@ -186,13 +187,64 @@ import { FormStatus } from "../../enums"
|
|
|
186
187
|
|
|
187
188
|
---
|
|
188
189
|
|
|
189
|
-
##
|
|
190
|
+
## Validation state
|
|
190
191
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
192
|
+
There is no `error` or `success` class to apply. Two hooks drive input borders:
|
|
193
|
+
|
|
194
|
+
### 1. Live native validation — opt in with `mx-validate`
|
|
195
|
+
|
|
196
|
+
Add `mx-validate` to a control to let the browser colour it as the user types.
|
|
197
|
+
Without the class, a control is **never** coloured automatically — so filling in
|
|
198
|
+
an unmarked field doesn't turn it green.
|
|
199
|
+
|
|
200
|
+
```html
|
|
201
|
+
<input class="mx-input__text mx-validate" type="email" required />
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
For checkbox and radio, put it on the wrapper — that's where `className` and
|
|
205
|
+
Drupal `attributes` land:
|
|
206
|
+
|
|
207
|
+
```html
|
|
208
|
+
<div class="mx-checkbox mx-validate"><input type="checkbox" required /><label>…</label></div>
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
| Selector | Effect | When it matches |
|
|
212
|
+
| --------------- | ------------ | -------------------------------------------------------------------- |
|
|
213
|
+
| `:user-valid` | Green border | Field passes validation **and** the user has interacted or submitted |
|
|
214
|
+
| `:user-invalid` | Red border | Field fails validation **and** the user has interacted or submitted |
|
|
215
|
+
|
|
216
|
+
`:user-*` is used rather than `:valid`/`:invalid` so a blank `required` field
|
|
217
|
+
isn't red on page load.
|
|
218
|
+
|
|
219
|
+
### 2. Explicit errors — `aria-invalid`, no opt-in
|
|
220
|
+
|
|
221
|
+
`[aria-invalid="true"]` always paints a red border, with or without
|
|
222
|
+
`mx-validate`. This is for failures the browser can't see: server-rendered
|
|
223
|
+
(Drupal) or async. The consumer has already decided the field is wrong, so
|
|
224
|
+
there's nothing to opt into.
|
|
225
|
+
|
|
226
|
+
Live success styling excludes `[aria-invalid="true"]`, so an explicit error
|
|
227
|
+
stays red even when the field passes native validation.
|
|
228
|
+
|
|
229
|
+
### `status` prop (`FormStatus` enum)
|
|
230
|
+
|
|
231
|
+
`status` no longer sets border classes. It controls the ARIA state and which
|
|
232
|
+
message block renders:
|
|
233
|
+
|
|
234
|
+
| Value | Effect |
|
|
235
|
+
| -------------------- | -------------------------------------------------------------- |
|
|
236
|
+
| `FormStatus.DEFAULT` | `aria-invalid="false"`, no message block |
|
|
237
|
+
| `FormStatus.VALID` | `mx-success-message` block, green icon — **no green border** |
|
|
238
|
+
| `FormStatus.INVALID` | `aria-invalid="true"` (⇒ red border), `mx-error-message` block |
|
|
239
|
+
|
|
240
|
+
For a server-side error, set `status={FormStatus.INVALID}` — the border follows
|
|
241
|
+
from `aria-invalid`. For client-side rules, use native attributes (`required`,
|
|
242
|
+
`pattern`, `type="email"`, `min`/`max`), add `mx-validate`, and let the browser
|
|
243
|
+
do it.
|
|
244
|
+
|
|
245
|
+
Green borders are live-only: there is no ARIA counterpart to `aria-invalid`, so
|
|
246
|
+
an explicitly-set `VALID` status renders the success message without recolouring
|
|
247
|
+
the border.
|
|
196
248
|
|
|
197
249
|
---
|
|
198
250
|
|
|
@@ -40,10 +40,7 @@ const Radio = ({
|
|
|
40
40
|
const state = use(RadioContext)
|
|
41
41
|
const { inputProps } = useRadio(props, state, ref)
|
|
42
42
|
|
|
43
|
-
const classes = classNames("mx-radio", className
|
|
44
|
-
error: status === FormStatus.INVALID,
|
|
45
|
-
success: status === FormStatus.VALID,
|
|
46
|
-
})
|
|
43
|
+
const classes = classNames("mx-radio", className)
|
|
47
44
|
|
|
48
45
|
return (
|
|
49
46
|
<div className={classes}>
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# RadioCard
|
|
2
|
+
|
|
3
|
+
A card-style radio — a full padded, bordered, selectable card instead of a small inline control. Use for a small set of mutually-exclusive options that benefit from a larger tap target and optional secondary hint text (e.g. a high-stakes form question), rather than a dense list of ordinary radios.
|
|
4
|
+
|
|
5
|
+
> **HTML/Twig users:** use native `<input type="radio">` with both the `.mx-radio` and `.mx-choice-card` wrapper classes. The react-aria wiring is React-only.
|
|
6
|
+
|
|
7
|
+
`.mx-choice-card` layers on top of `.mx-radio` rather than replacing it — the base class still supplies input reset/sizing, disabled state, and the spacing between stacked cards in a group; `.mx-choice-card`'s higher cascade layer (`design-system.components` vs. `.mx-radio`'s `design-system.atoms`) overrides only what the card treatment needs to change (position, padding, border).
|
|
8
|
+
|
|
9
|
+
`.mx-choice-card` is shared with `CheckboxCard` — it lives in `src/Form/form.css` alongside the base `.mx-checkbox`/`.mx-radio` rules, not in a per-component CSS file, since the two components' card treatments were identical enough to not warrant separate copies.
|
|
10
|
+
|
|
11
|
+
## CSS Import
|
|
12
|
+
|
|
13
|
+
```css
|
|
14
|
+
@import "@pnx-mixtape/mxds/src/Form/form.css";
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Twig
|
|
20
|
+
|
|
21
|
+
```twig
|
|
22
|
+
{% include '@mixtape/Form/RadioCard/radio-card.twig' with {
|
|
23
|
+
id: 'card-yes',
|
|
24
|
+
name: 'card',
|
|
25
|
+
value: 'yes',
|
|
26
|
+
label: 'Yes, I hold a card',
|
|
27
|
+
hint: 'Pensioner Concession, Health Care or DVA card',
|
|
28
|
+
} %}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**radio-card.twig props:**
|
|
32
|
+
|
|
33
|
+
| Prop | Type | Required | Description |
|
|
34
|
+
| ------------ | ----------------- | -------- | ------------------------------------------------------------------------------------------------------------------- |
|
|
35
|
+
| `id` | `string` | Yes | Input id |
|
|
36
|
+
| `name` | `string` | Yes | `name` attribute on the input |
|
|
37
|
+
| `value` | `string` | Yes | `value` attribute on the input |
|
|
38
|
+
| `label` | `string` | Yes | Label text rendered inside `<label for="{id}">` |
|
|
39
|
+
| `hint` | `string` | No | Secondary line rendered under the label, inside the card |
|
|
40
|
+
| `checked` | `boolean` | No | Adds `checked="checked"` to the input |
|
|
41
|
+
| `required` | `boolean` | No | Adds the `required` attribute |
|
|
42
|
+
| `status` | `FormStatusTypes` | No | `invalid` adds the `error` class and `aria-invalid="true"`; `valid` adds the `success` class. Defaults to `default` |
|
|
43
|
+
| `attributes` | `Attribute` | No | Optional Drupal-style attributes object — `mx-radio mx-choice-card` (and the status class) is always merged on top |
|
|
44
|
+
|
|
45
|
+
For a group of cards, compose several `radio-card.twig` includes inside a `<fieldset>` — see HTML below.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## HTML
|
|
50
|
+
|
|
51
|
+
```html
|
|
52
|
+
<fieldset class="mx-form-item mx-fieldset mx-fieldset--plain">
|
|
53
|
+
<legend class="mx-legend">Do you hold a current concession card?</legend>
|
|
54
|
+
|
|
55
|
+
<div class="mx-radio mx-choice-card">
|
|
56
|
+
<input type="radio" id="card-yes" name="card" value="yes" />
|
|
57
|
+
<label for="card-yes">
|
|
58
|
+
Yes, I hold a card
|
|
59
|
+
<span class="mx-choice-card__hint">Pensioner Concession, Health Care or DVA card</span>
|
|
60
|
+
</label>
|
|
61
|
+
</div>
|
|
62
|
+
|
|
63
|
+
<div class="mx-radio mx-choice-card">
|
|
64
|
+
<input type="radio" id="card-no" name="card" value="no" />
|
|
65
|
+
<label for="card-no">
|
|
66
|
+
No
|
|
67
|
+
<span class="mx-choice-card__hint">You may still be eligible based on your income</span>
|
|
68
|
+
</label>
|
|
69
|
+
</div>
|
|
70
|
+
|
|
71
|
+
<div class="mx-choice-divider">or</div>
|
|
72
|
+
|
|
73
|
+
<div class="mx-radio mx-choice-card">
|
|
74
|
+
<input type="radio" id="card-unsure" name="card" value="unsure" />
|
|
75
|
+
<label for="card-unsure">I am not sure</label>
|
|
76
|
+
</div>
|
|
77
|
+
</fieldset>
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
`.mx-choice-divider` is also defined in `src/Form/form.css` — plain markup used to split either card type into two groups, already available once you import `form.css`. See [`CheckboxCard`'s README](../CheckboxCard/README.md#or-divider-between-two-option-groups) for more.
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## React
|
|
85
|
+
|
|
86
|
+
Use `FormRadioCard` as the group wrapper and `RadioCard` for each option — same pattern as `FormRadio`/`Radio`. There's no standalone/single variant (a lone radio isn't a meaningful control on its own, same as `Radio`).
|
|
87
|
+
|
|
88
|
+
```tsx
|
|
89
|
+
import { FormRadioCard, RadioCard } from "@pnx-mixtape/mxds/react"
|
|
90
|
+
|
|
91
|
+
;<FormRadioCard label="Do you hold a current concession card?" onChange={setSelected}>
|
|
92
|
+
<RadioCard value="yes" hint="Pensioner Concession, Health Care or DVA card">
|
|
93
|
+
Yes, I hold a card
|
|
94
|
+
</RadioCard>
|
|
95
|
+
<RadioCard value="no" hint="You may still be eligible based on your income">
|
|
96
|
+
No
|
|
97
|
+
</RadioCard>
|
|
98
|
+
</FormRadioCard>
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**`FormRadioCard` props:** all `AriaRadioGroupProps` from react-aria + `label: string`
|
|
102
|
+
|
|
103
|
+
**`RadioCard` props:** all `AriaRadioProps` + `hint?: string`, `status?: FormStatus`, `className?: string`
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
### React ⇄ Twig parity
|
|
108
|
+
|
|
109
|
+
Same intentional divergence as `Radio`/`Checkbox`/`CheckboxCard`:
|
|
110
|
+
|
|
111
|
+
- Twig's `label` becomes React `children` — react-aria composes the accessible name from rendered content, not a string prop.
|
|
112
|
+
- Twig's `id`/`name`/`checked`/`required`/`value` are handled by the react-aria hooks (`useRadio`/`useRadioGroup`) via prop spread rather than declared explicitly.
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Modifiers
|
|
117
|
+
|
|
118
|
+
No BEM modifier classes — `error`/`success` are applied as plain (unprefixed) status classes, matching `Radio`'s convention, driven by the `status` prop/Twig var.
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## Accessibility
|
|
123
|
+
|
|
124
|
+
- The input stays a real, focusable `<input type="radio">` — only its default appearance is replaced (`appearance: none` plus a custom checked-state dot), so it keeps native radio semantics, keyboard support (arrow keys to move within the group, per the browser's native radio-group behaviour), and form submission behaviour.
|
|
125
|
+
- The whole card is clickable because the `<label>` fills it — the input itself only needs to be as large as `--control-size` dictates, not the full card.
|
|
126
|
+
- The focus ring (`:focus-visible`) renders on the input itself, inside the card, rather than around the whole card.
|
|
127
|
+
- `hint` text is inside the same `<label>` as the primary text, so both are announced together when the control receives focus — don't rely on `hint` to convey anything the primary `label` text doesn't already establish.
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Customisation
|
|
132
|
+
|
|
133
|
+
| Custom property | Applies to | Default |
|
|
134
|
+
| ------------------------- | -------------------------------------------------------------------------- | --------------------------------------------------- |
|
|
135
|
+
| `--choice-padding-block` | `.mx-choice-card` | `var(--spacing-s)` |
|
|
136
|
+
| `--choice-padding-inline` | `.mx-choice-card` | `var(--spacing-m)` |
|
|
137
|
+
| `--choice-colour-active` | `.mx-choice-card`'s border/background tint when checked | `var(--colour-primary)` |
|
|
138
|
+
| `--radio-colour` | The checked dot itself — inherited from `.mx-radio`, not `.mx-choice-card` | `var(--colour-primary)` |
|
|
139
|
+
| `--control-size` | `.mx-choice-card input`, and `.mx-radio`'s own input styling | `var(--spacing-m)` (shared with `Checkbox`/`Radio`) |
|
|
140
|
+
| `--line-width` | `.mx-choice-card`'s border, and `.mx-radio`'s own input border | `2px` |
|
|
141
|
+
|
|
142
|
+
```css
|
|
143
|
+
.theme--health-services .mx-choice-card {
|
|
144
|
+
--choice-colour-active: var(--colour-primary);
|
|
145
|
+
}
|
|
146
|
+
```
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { Meta, StoryObj } from "@storybook/html-vite"
|
|
2
|
+
import Component from "../FormItem/form-item.twig"
|
|
3
|
+
import InputRadioCard from "./radio-card.twig"
|
|
4
|
+
import Label from "../Label/label.twig"
|
|
5
|
+
import "../form.css"
|
|
6
|
+
import { FormItem as FormItemType } from "@pnx-mixtape/ids-shape"
|
|
7
|
+
|
|
8
|
+
type MxFormItemType = Omit<FormItemType, "fields"> & {
|
|
9
|
+
fields?: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const meta: Meta<MxFormItemType> = {
|
|
13
|
+
tags: ["autodocs"],
|
|
14
|
+
component: Component,
|
|
15
|
+
args: {
|
|
16
|
+
id: "card-set",
|
|
17
|
+
as: "fieldset",
|
|
18
|
+
label: Label({
|
|
19
|
+
title: "Do you hold a current concession card?",
|
|
20
|
+
as: "legend",
|
|
21
|
+
}),
|
|
22
|
+
fields: [
|
|
23
|
+
InputRadioCard({
|
|
24
|
+
id: "card-yes",
|
|
25
|
+
name: "card",
|
|
26
|
+
label: "Yes, I hold a card",
|
|
27
|
+
hint: "Pensioner Concession, Health Care or DVA card",
|
|
28
|
+
value: "yes",
|
|
29
|
+
}),
|
|
30
|
+
InputRadioCard({
|
|
31
|
+
id: "card-applied",
|
|
32
|
+
name: "card",
|
|
33
|
+
label: "I have applied but not received it",
|
|
34
|
+
hint: "You can continue, but we may ask for evidence later",
|
|
35
|
+
value: "applied",
|
|
36
|
+
}),
|
|
37
|
+
InputRadioCard({
|
|
38
|
+
id: "card-no",
|
|
39
|
+
name: "card",
|
|
40
|
+
label: "No",
|
|
41
|
+
hint: "You may still be eligible based on your income",
|
|
42
|
+
value: "no",
|
|
43
|
+
}),
|
|
44
|
+
'<div class="mx-choice-divider">or</div>',
|
|
45
|
+
InputRadioCard({
|
|
46
|
+
id: "card-unsure",
|
|
47
|
+
name: "card",
|
|
48
|
+
label: "I am not sure",
|
|
49
|
+
hint: "We will ask a few more questions to work it out",
|
|
50
|
+
value: "unsure",
|
|
51
|
+
}),
|
|
52
|
+
].join(""),
|
|
53
|
+
},
|
|
54
|
+
parameters: {
|
|
55
|
+
deepControls: { enabled: true },
|
|
56
|
+
a11y: {
|
|
57
|
+
config: {
|
|
58
|
+
rules: [
|
|
59
|
+
{
|
|
60
|
+
// ID's are not unique due to test-runner.
|
|
61
|
+
id: "label",
|
|
62
|
+
enabled: false,
|
|
63
|
+
},
|
|
64
|
+
],
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export default meta
|
|
71
|
+
type Story = StoryObj<MxFormItemType>
|
|
72
|
+
|
|
73
|
+
export const RadioCard: Story = {}
|
|
74
|
+
|
|
75
|
+
export const Checked: Story = {
|
|
76
|
+
args: {
|
|
77
|
+
fields: [
|
|
78
|
+
InputRadioCard({
|
|
79
|
+
id: "card-yes",
|
|
80
|
+
name: "card",
|
|
81
|
+
label: "Yes, I hold a card",
|
|
82
|
+
hint: "Pensioner Concession, Health Care or DVA card",
|
|
83
|
+
value: "yes",
|
|
84
|
+
checked: true,
|
|
85
|
+
}),
|
|
86
|
+
InputRadioCard({
|
|
87
|
+
id: "card-no",
|
|
88
|
+
name: "card",
|
|
89
|
+
label: "No",
|
|
90
|
+
hint: "You may still be eligible based on your income",
|
|
91
|
+
value: "no",
|
|
92
|
+
}),
|
|
93
|
+
].join(""),
|
|
94
|
+
},
|
|
95
|
+
}
|