@dashforge/tw 0.1.0-beta
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/.swcrc +29 -0
- package/CHANGELOG.md +131 -0
- package/LICENSE +21 -0
- package/README.md +244 -0
- package/dist/components/AppShell/AppShell.d.ts +32 -0
- package/dist/components/AppShell/AppShell.d.ts.map +1 -0
- package/dist/components/AppShell/appShell.types.d.ts +62 -0
- package/dist/components/AppShell/appShell.types.d.ts.map +1 -0
- package/dist/components/AppShell/appShell.variants.d.ts +65 -0
- package/dist/components/AppShell/appShell.variants.d.ts.map +1 -0
- package/dist/components/Autocomplete/Autocomplete.d.ts +32 -0
- package/dist/components/Autocomplete/Autocomplete.d.ts.map +1 -0
- package/dist/components/Autocomplete/autocomplete.types.d.ts +214 -0
- package/dist/components/Autocomplete/autocomplete.types.d.ts.map +1 -0
- package/dist/components/Autocomplete/autocomplete.variants.d.ts +214 -0
- package/dist/components/Autocomplete/autocomplete.variants.d.ts.map +1 -0
- package/dist/components/Breadcrumbs/Breadcrumbs.d.ts +23 -0
- package/dist/components/Breadcrumbs/Breadcrumbs.d.ts.map +1 -0
- package/dist/components/Breadcrumbs/breadcrumbs.types.d.ts +98 -0
- package/dist/components/Breadcrumbs/breadcrumbs.types.d.ts.map +1 -0
- package/dist/components/Breadcrumbs/breadcrumbs.variants.d.ts +85 -0
- package/dist/components/Breadcrumbs/breadcrumbs.variants.d.ts.map +1 -0
- package/dist/components/Button/Button.d.ts +44 -0
- package/dist/components/Button/Button.d.ts.map +1 -0
- package/dist/components/Button/button.types.d.ts +66 -0
- package/dist/components/Button/button.types.d.ts.map +1 -0
- package/dist/components/Button/button.variants.d.ts +104 -0
- package/dist/components/Button/button.variants.d.ts.map +1 -0
- package/dist/components/Checkbox/Checkbox.d.ts +31 -0
- package/dist/components/Checkbox/Checkbox.d.ts.map +1 -0
- package/dist/components/Checkbox/checkbox.types.d.ts +86 -0
- package/dist/components/Checkbox/checkbox.types.d.ts.map +1 -0
- package/dist/components/Checkbox/checkbox.variants.d.ts +109 -0
- package/dist/components/Checkbox/checkbox.variants.d.ts.map +1 -0
- package/dist/components/ConfirmDialog/ConfirmDialog.d.ts +38 -0
- package/dist/components/ConfirmDialog/ConfirmDialog.d.ts.map +1 -0
- package/dist/components/ConfirmDialog/confirmDialog.types.d.ts +80 -0
- package/dist/components/ConfirmDialog/confirmDialog.types.d.ts.map +1 -0
- package/dist/components/ConfirmDialog/confirmDialog.variants.d.ts +90 -0
- package/dist/components/ConfirmDialog/confirmDialog.variants.d.ts.map +1 -0
- package/dist/components/DateTimePicker/DateTimePicker.d.ts +49 -0
- package/dist/components/DateTimePicker/DateTimePicker.d.ts.map +1 -0
- package/dist/components/DateTimePicker/dateTimePicker.types.d.ts +95 -0
- package/dist/components/DateTimePicker/dateTimePicker.types.d.ts.map +1 -0
- package/dist/components/DateTimePicker/dateTimePicker.variants.d.ts +165 -0
- package/dist/components/DateTimePicker/dateTimePicker.variants.d.ts.map +1 -0
- package/dist/components/LeftNav/LeftNav.d.ts +34 -0
- package/dist/components/LeftNav/LeftNav.d.ts.map +1 -0
- package/dist/components/LeftNav/leftNav.types.d.ts +136 -0
- package/dist/components/LeftNav/leftNav.types.d.ts.map +1 -0
- package/dist/components/LeftNav/leftNav.variants.d.ts +143 -0
- package/dist/components/LeftNav/leftNav.variants.d.ts.map +1 -0
- package/dist/components/NumberField/NumberField.d.ts +18 -0
- package/dist/components/NumberField/NumberField.d.ts.map +1 -0
- package/dist/components/NumberField/numberField.types.d.ts +79 -0
- package/dist/components/NumberField/numberField.types.d.ts.map +1 -0
- package/dist/components/NumberField/numberField.variants.d.ts +169 -0
- package/dist/components/NumberField/numberField.variants.d.ts.map +1 -0
- package/dist/components/OTPField/OTPField.d.ts +25 -0
- package/dist/components/OTPField/OTPField.d.ts.map +1 -0
- package/dist/components/OTPField/otpField.types.d.ts +74 -0
- package/dist/components/OTPField/otpField.types.d.ts.map +1 -0
- package/dist/components/OTPField/otpField.variants.d.ts +105 -0
- package/dist/components/OTPField/otpField.variants.d.ts.map +1 -0
- package/dist/components/RadioGroup/RadioGroup.d.ts +35 -0
- package/dist/components/RadioGroup/RadioGroup.d.ts.map +1 -0
- package/dist/components/RadioGroup/radioGroup.types.d.ts +103 -0
- package/dist/components/RadioGroup/radioGroup.types.d.ts.map +1 -0
- package/dist/components/RadioGroup/radioGroup.variants.d.ts +166 -0
- package/dist/components/RadioGroup/radioGroup.variants.d.ts.map +1 -0
- package/dist/components/Snackbar/Snackbar.d.ts +37 -0
- package/dist/components/Snackbar/Snackbar.d.ts.map +1 -0
- package/dist/components/Snackbar/snackbar.types.d.ts +86 -0
- package/dist/components/Snackbar/snackbar.types.d.ts.map +1 -0
- package/dist/components/Snackbar/snackbar.variants.d.ts +153 -0
- package/dist/components/Snackbar/snackbar.variants.d.ts.map +1 -0
- package/dist/components/Switch/Switch.d.ts +15 -0
- package/dist/components/Switch/Switch.d.ts.map +1 -0
- package/dist/components/Switch/switch.types.d.ts +47 -0
- package/dist/components/Switch/switch.types.d.ts.map +1 -0
- package/dist/components/Switch/switch.variants.d.ts +105 -0
- package/dist/components/Switch/switch.variants.d.ts.map +1 -0
- package/dist/components/TextField/TextField.d.ts +25 -0
- package/dist/components/TextField/TextField.d.ts.map +1 -0
- package/dist/components/TextField/textField.types.d.ts +72 -0
- package/dist/components/TextField/textField.types.d.ts.map +1 -0
- package/dist/components/TextField/textField.variants.d.ts +160 -0
- package/dist/components/TextField/textField.variants.d.ts.map +1 -0
- package/dist/components/Textarea/Textarea.d.ts +16 -0
- package/dist/components/Textarea/Textarea.d.ts.map +1 -0
- package/dist/components/Textarea/textarea.types.d.ts +63 -0
- package/dist/components/Textarea/textarea.types.d.ts.map +1 -0
- package/dist/components/Textarea/textarea.variants.d.ts +197 -0
- package/dist/components/Textarea/textarea.variants.d.ts.map +1 -0
- package/dist/components/TopBar/TopBar.d.ts +33 -0
- package/dist/components/TopBar/TopBar.d.ts.map +1 -0
- package/dist/components/TopBar/topBar.types.d.ts +49 -0
- package/dist/components/TopBar/topBar.types.d.ts.map +1 -0
- package/dist/components/TopBar/topBar.variants.d.ts +79 -0
- package/dist/components/TopBar/topBar.variants.d.ts.map +1 -0
- package/dist/components/_shared/resolveValidationState.d.ts +42 -0
- package/dist/components/_shared/resolveValidationState.d.ts.map +1 -0
- package/dist/hooks/useAccessState.d.ts +37 -0
- package/dist/hooks/useAccessState.d.ts.map +1 -0
- package/dist/index.d.ts +82 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.esm.js +4958 -0
- package/dist/src/components/AppShell/AppShell.d.ts +32 -0
- package/dist/src/components/AppShell/AppShell.d.ts.map +1 -0
- package/dist/src/components/AppShell/appShell.types.d.ts +62 -0
- package/dist/src/components/AppShell/appShell.types.d.ts.map +1 -0
- package/dist/src/components/AppShell/appShell.variants.d.ts +65 -0
- package/dist/src/components/AppShell/appShell.variants.d.ts.map +1 -0
- package/dist/src/components/Autocomplete/Autocomplete.d.ts +32 -0
- package/dist/src/components/Autocomplete/Autocomplete.d.ts.map +1 -0
- package/dist/src/components/Autocomplete/autocomplete.types.d.ts +214 -0
- package/dist/src/components/Autocomplete/autocomplete.types.d.ts.map +1 -0
- package/dist/src/components/Autocomplete/autocomplete.variants.d.ts +214 -0
- package/dist/src/components/Autocomplete/autocomplete.variants.d.ts.map +1 -0
- package/dist/src/components/Breadcrumbs/Breadcrumbs.d.ts +23 -0
- package/dist/src/components/Breadcrumbs/Breadcrumbs.d.ts.map +1 -0
- package/dist/src/components/Breadcrumbs/breadcrumbs.types.d.ts +98 -0
- package/dist/src/components/Breadcrumbs/breadcrumbs.types.d.ts.map +1 -0
- package/dist/src/components/Breadcrumbs/breadcrumbs.variants.d.ts +85 -0
- package/dist/src/components/Breadcrumbs/breadcrumbs.variants.d.ts.map +1 -0
- package/dist/src/components/Button/Button.d.ts +44 -0
- package/dist/src/components/Button/Button.d.ts.map +1 -0
- package/dist/src/components/Button/button.types.d.ts +66 -0
- package/dist/src/components/Button/button.types.d.ts.map +1 -0
- package/dist/src/components/Button/button.variants.d.ts +104 -0
- package/dist/src/components/Button/button.variants.d.ts.map +1 -0
- package/dist/src/components/Checkbox/Checkbox.d.ts +31 -0
- package/dist/src/components/Checkbox/Checkbox.d.ts.map +1 -0
- package/dist/src/components/Checkbox/checkbox.types.d.ts +86 -0
- package/dist/src/components/Checkbox/checkbox.types.d.ts.map +1 -0
- package/dist/src/components/Checkbox/checkbox.variants.d.ts +109 -0
- package/dist/src/components/Checkbox/checkbox.variants.d.ts.map +1 -0
- package/dist/src/components/ConfirmDialog/ConfirmDialog.d.ts +38 -0
- package/dist/src/components/ConfirmDialog/ConfirmDialog.d.ts.map +1 -0
- package/dist/src/components/ConfirmDialog/confirmDialog.types.d.ts +80 -0
- package/dist/src/components/ConfirmDialog/confirmDialog.types.d.ts.map +1 -0
- package/dist/src/components/ConfirmDialog/confirmDialog.variants.d.ts +90 -0
- package/dist/src/components/ConfirmDialog/confirmDialog.variants.d.ts.map +1 -0
- package/dist/src/components/DateTimePicker/DateTimePicker.d.ts +49 -0
- package/dist/src/components/DateTimePicker/DateTimePicker.d.ts.map +1 -0
- package/dist/src/components/DateTimePicker/dateTimePicker.types.d.ts +95 -0
- package/dist/src/components/DateTimePicker/dateTimePicker.types.d.ts.map +1 -0
- package/dist/src/components/DateTimePicker/dateTimePicker.variants.d.ts +165 -0
- package/dist/src/components/DateTimePicker/dateTimePicker.variants.d.ts.map +1 -0
- package/dist/src/components/LeftNav/LeftNav.d.ts +34 -0
- package/dist/src/components/LeftNav/LeftNav.d.ts.map +1 -0
- package/dist/src/components/LeftNav/leftNav.types.d.ts +136 -0
- package/dist/src/components/LeftNav/leftNav.types.d.ts.map +1 -0
- package/dist/src/components/LeftNav/leftNav.variants.d.ts +143 -0
- package/dist/src/components/LeftNav/leftNav.variants.d.ts.map +1 -0
- package/dist/src/components/NumberField/NumberField.d.ts +18 -0
- package/dist/src/components/NumberField/NumberField.d.ts.map +1 -0
- package/dist/src/components/NumberField/numberField.types.d.ts +79 -0
- package/dist/src/components/NumberField/numberField.types.d.ts.map +1 -0
- package/dist/src/components/NumberField/numberField.variants.d.ts +169 -0
- package/dist/src/components/NumberField/numberField.variants.d.ts.map +1 -0
- package/dist/src/components/OTPField/OTPField.d.ts +25 -0
- package/dist/src/components/OTPField/OTPField.d.ts.map +1 -0
- package/dist/src/components/OTPField/otpField.types.d.ts +74 -0
- package/dist/src/components/OTPField/otpField.types.d.ts.map +1 -0
- package/dist/src/components/OTPField/otpField.variants.d.ts +105 -0
- package/dist/src/components/OTPField/otpField.variants.d.ts.map +1 -0
- package/dist/src/components/RadioGroup/RadioGroup.d.ts +35 -0
- package/dist/src/components/RadioGroup/RadioGroup.d.ts.map +1 -0
- package/dist/src/components/RadioGroup/radioGroup.types.d.ts +103 -0
- package/dist/src/components/RadioGroup/radioGroup.types.d.ts.map +1 -0
- package/dist/src/components/RadioGroup/radioGroup.variants.d.ts +166 -0
- package/dist/src/components/RadioGroup/radioGroup.variants.d.ts.map +1 -0
- package/dist/src/components/Snackbar/Snackbar.d.ts +37 -0
- package/dist/src/components/Snackbar/Snackbar.d.ts.map +1 -0
- package/dist/src/components/Snackbar/snackbar.types.d.ts +86 -0
- package/dist/src/components/Snackbar/snackbar.types.d.ts.map +1 -0
- package/dist/src/components/Snackbar/snackbar.variants.d.ts +153 -0
- package/dist/src/components/Snackbar/snackbar.variants.d.ts.map +1 -0
- package/dist/src/components/Switch/Switch.d.ts +15 -0
- package/dist/src/components/Switch/Switch.d.ts.map +1 -0
- package/dist/src/components/Switch/switch.types.d.ts +47 -0
- package/dist/src/components/Switch/switch.types.d.ts.map +1 -0
- package/dist/src/components/Switch/switch.variants.d.ts +105 -0
- package/dist/src/components/Switch/switch.variants.d.ts.map +1 -0
- package/dist/src/components/TextField/TextField.d.ts +25 -0
- package/dist/src/components/TextField/TextField.d.ts.map +1 -0
- package/dist/src/components/TextField/textField.types.d.ts +72 -0
- package/dist/src/components/TextField/textField.types.d.ts.map +1 -0
- package/dist/src/components/TextField/textField.variants.d.ts +160 -0
- package/dist/src/components/TextField/textField.variants.d.ts.map +1 -0
- package/dist/src/components/Textarea/Textarea.d.ts +16 -0
- package/dist/src/components/Textarea/Textarea.d.ts.map +1 -0
- package/dist/src/components/Textarea/textarea.types.d.ts +63 -0
- package/dist/src/components/Textarea/textarea.types.d.ts.map +1 -0
- package/dist/src/components/Textarea/textarea.variants.d.ts +197 -0
- package/dist/src/components/Textarea/textarea.variants.d.ts.map +1 -0
- package/dist/src/components/TopBar/TopBar.d.ts +33 -0
- package/dist/src/components/TopBar/TopBar.d.ts.map +1 -0
- package/dist/src/components/TopBar/topBar.types.d.ts +49 -0
- package/dist/src/components/TopBar/topBar.types.d.ts.map +1 -0
- package/dist/src/components/TopBar/topBar.variants.d.ts +79 -0
- package/dist/src/components/TopBar/topBar.variants.d.ts.map +1 -0
- package/dist/src/components/_shared/resolveValidationState.d.ts +42 -0
- package/dist/src/components/_shared/resolveValidationState.d.ts.map +1 -0
- package/dist/src/hooks/useAccessState.d.ts +37 -0
- package/dist/src/hooks/useAccessState.d.ts.map +1 -0
- package/dist/src/index.d.ts +82 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/utils/cn.d.ts +26 -0
- package/dist/src/utils/cn.d.ts.map +1 -0
- package/dist/tsconfig.lib.tsbuildinfo +1 -0
- package/dist/utils/cn.d.ts +26 -0
- package/dist/utils/cn.d.ts.map +1 -0
- package/eslint.config.mjs +47 -0
- package/package.json +37 -0
- package/project.json +15 -0
- package/rollup.config.cjs +16 -0
- package/src/components/AppShell/AppShell.test.tsx +223 -0
- package/src/components/AppShell/AppShell.tsx +114 -0
- package/src/components/AppShell/appShell.types.ts +49 -0
- package/src/components/AppShell/appShell.variants.ts +48 -0
- package/src/components/Autocomplete/Autocomplete.perf.test.tsx +225 -0
- package/src/components/Autocomplete/Autocomplete.test.tsx +556 -0
- package/src/components/Autocomplete/Autocomplete.tsx +996 -0
- package/src/components/Autocomplete/autocomplete.types.ts +198 -0
- package/src/components/Autocomplete/autocomplete.variants.ts +135 -0
- package/src/components/Breadcrumbs/Breadcrumbs.perf.test.tsx +125 -0
- package/src/components/Breadcrumbs/Breadcrumbs.test.tsx +231 -0
- package/src/components/Breadcrumbs/Breadcrumbs.tsx +203 -0
- package/src/components/Breadcrumbs/breadcrumbs.types.ts +87 -0
- package/src/components/Breadcrumbs/breadcrumbs.variants.ts +49 -0
- package/src/components/Button/Button.test.tsx +183 -0
- package/src/components/Button/Button.tsx +139 -0
- package/src/components/Button/button.types.ts +74 -0
- package/src/components/Button/button.variants.ts +159 -0
- package/src/components/Checkbox/Checkbox.test.tsx +146 -0
- package/src/components/Checkbox/Checkbox.tsx +228 -0
- package/src/components/Checkbox/checkbox.types.ts +88 -0
- package/src/components/Checkbox/checkbox.variants.ts +68 -0
- package/src/components/ConfirmDialog/ConfirmDialog.perf.test.tsx +172 -0
- package/src/components/ConfirmDialog/ConfirmDialog.test.tsx +311 -0
- package/src/components/ConfirmDialog/ConfirmDialog.tsx +252 -0
- package/src/components/ConfirmDialog/confirmDialog.types.ts +70 -0
- package/src/components/ConfirmDialog/confirmDialog.variants.ts +76 -0
- package/src/components/DateTimePicker/DateTimePicker.perf.test.tsx +153 -0
- package/src/components/DateTimePicker/DateTimePicker.test.tsx +204 -0
- package/src/components/DateTimePicker/DateTimePicker.tsx +319 -0
- package/src/components/DateTimePicker/dateTimePicker.types.ts +83 -0
- package/src/components/DateTimePicker/dateTimePicker.variants.ts +84 -0
- package/src/components/LeftNav/LeftNav.perf.test.tsx +172 -0
- package/src/components/LeftNav/LeftNav.test.tsx +302 -0
- package/src/components/LeftNav/LeftNav.tsx +330 -0
- package/src/components/LeftNav/leftNav.types.ts +120 -0
- package/src/components/LeftNav/leftNav.variants.ts +96 -0
- package/src/components/NumberField/NumberField.test.tsx +83 -0
- package/src/components/NumberField/NumberField.tsx +293 -0
- package/src/components/NumberField/numberField.types.ts +72 -0
- package/src/components/NumberField/numberField.variants.ts +94 -0
- package/src/components/OTPField/OTPField.test.tsx +121 -0
- package/src/components/OTPField/OTPField.tsx +250 -0
- package/src/components/OTPField/otpField.types.ts +58 -0
- package/src/components/OTPField/otpField.variants.ts +72 -0
- package/src/components/RadioGroup/RadioGroup.test.tsx +140 -0
- package/src/components/RadioGroup/RadioGroup.tsx +261 -0
- package/src/components/RadioGroup/radioGroup.types.ts +85 -0
- package/src/components/RadioGroup/radioGroup.variants.ts +87 -0
- package/src/components/Snackbar/Snackbar.perf.test.tsx +144 -0
- package/src/components/Snackbar/Snackbar.test.tsx +327 -0
- package/src/components/Snackbar/Snackbar.tsx +298 -0
- package/src/components/Snackbar/snackbar.types.ts +83 -0
- package/src/components/Snackbar/snackbar.variants.ts +72 -0
- package/src/components/Switch/Switch.test.tsx +131 -0
- package/src/components/Switch/Switch.tsx +158 -0
- package/src/components/Switch/switch.types.ts +36 -0
- package/src/components/Switch/switch.variants.ts +65 -0
- package/src/components/TextField/TextField.test.tsx +194 -0
- package/src/components/TextField/TextField.tsx +216 -0
- package/src/components/TextField/textField.types.ts +70 -0
- package/src/components/TextField/textField.variants.ts +87 -0
- package/src/components/Textarea/Textarea.test.tsx +75 -0
- package/src/components/Textarea/Textarea.tsx +190 -0
- package/src/components/Textarea/textarea.types.ts +60 -0
- package/src/components/Textarea/textarea.variants.ts +85 -0
- package/src/components/TopBar/TopBar.test.tsx +129 -0
- package/src/components/TopBar/TopBar.tsx +66 -0
- package/src/components/TopBar/topBar.types.ts +42 -0
- package/src/components/TopBar/topBar.variants.ts +38 -0
- package/src/components/_shared/resolveValidationState.ts +60 -0
- package/src/hooks/useAccessState.spec.tsx +161 -0
- package/src/hooks/useAccessState.ts +81 -0
- package/src/index.ts +191 -0
- package/src/utils/cn.spec.ts +16 -0
- package/src/utils/cn.ts +29 -0
- package/tsconfig.json +13 -0
- package/tsconfig.lib.json +55 -0
- package/tsconfig.spec.json +34 -0
- package/vite.config.ts +25 -0
- package/vitest.config.mts +22 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { tv, type VariantProps } from 'tailwind-variants';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Tailwind-variants recipe for `<Checkbox>` with slots.
|
|
5
|
+
*
|
|
6
|
+
* Slots:
|
|
7
|
+
* - `root` — outer flex wrapper enclosing the control + label
|
|
8
|
+
* - `control` — the visible square (Radix `Checkbox.Root`)
|
|
9
|
+
* - `indicator` — checkmark SVG container (Radix `Checkbox.Indicator`)
|
|
10
|
+
* - `label` — text label associated with the control
|
|
11
|
+
* - `helperText` — descriptive line below
|
|
12
|
+
* - `errorText` — error message line (semantic counterpart of helperText)
|
|
13
|
+
*
|
|
14
|
+
* Variants:
|
|
15
|
+
* - `size` : sm / md / lg (drives control box dimensions + label text size)
|
|
16
|
+
* - `error` : bool — swaps borders + helper-text color to danger tones
|
|
17
|
+
*/
|
|
18
|
+
export const checkboxVariants = tv({
|
|
19
|
+
slots: {
|
|
20
|
+
root: 'inline-flex items-start gap-2',
|
|
21
|
+
control: [
|
|
22
|
+
'inline-flex items-center justify-center shrink-0',
|
|
23
|
+
// bg-neutral-50 inverts — dark-mode aware via the dashforge preset.
|
|
24
|
+
'rounded border bg-neutral-50',
|
|
25
|
+
'border-neutral-300',
|
|
26
|
+
'transition-colors',
|
|
27
|
+
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-1 focus-visible:ring-primary-500',
|
|
28
|
+
// Radix data-state hooks
|
|
29
|
+
'data-[state=checked]:bg-primary-500 data-[state=checked]:border-primary-500',
|
|
30
|
+
'data-[state=indeterminate]:bg-primary-500 data-[state=indeterminate]:border-primary-500',
|
|
31
|
+
'data-[disabled]:opacity-50 data-[disabled]:cursor-not-allowed',
|
|
32
|
+
],
|
|
33
|
+
indicator: 'flex items-center justify-center text-white',
|
|
34
|
+
label: 'select-none cursor-pointer text-neutral-900',
|
|
35
|
+
helperText: 'text-sm text-neutral-600 mt-1',
|
|
36
|
+
errorText: 'text-sm text-danger-600 mt-1',
|
|
37
|
+
},
|
|
38
|
+
variants: {
|
|
39
|
+
size: {
|
|
40
|
+
sm: {
|
|
41
|
+
control: 'h-4 w-4',
|
|
42
|
+
label: 'text-sm',
|
|
43
|
+
indicator: 'h-3 w-3',
|
|
44
|
+
},
|
|
45
|
+
md: {
|
|
46
|
+
control: 'h-5 w-5',
|
|
47
|
+
label: 'text-base',
|
|
48
|
+
indicator: 'h-3.5 w-3.5',
|
|
49
|
+
},
|
|
50
|
+
lg: {
|
|
51
|
+
control: 'h-6 w-6',
|
|
52
|
+
label: 'text-lg',
|
|
53
|
+
indicator: 'h-4 w-4',
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
error: {
|
|
57
|
+
true: {
|
|
58
|
+
control: 'border-danger-500 focus-visible:ring-danger-500',
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
defaultVariants: {
|
|
63
|
+
size: 'md',
|
|
64
|
+
error: false,
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
export type CheckboxVariants = VariantProps<typeof checkboxVariants>;
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
/**
|
|
3
|
+
* Performance + re-render budget tests for `<ConfirmDialogProvider>`.
|
|
4
|
+
*
|
|
5
|
+
* Guardrails:
|
|
6
|
+
*
|
|
7
|
+
* 1. Provider mount is cheap — it lives near the top of the tree, so
|
|
8
|
+
* it must not chase any expensive setup paths.
|
|
9
|
+
*
|
|
10
|
+
* 2. `confirm()` reference is stable across provider re-renders so
|
|
11
|
+
* consumers can pass it through `useEffect` deps without thrashing.
|
|
12
|
+
*
|
|
13
|
+
* 3. Children do NOT re-render every time a confirm() opens/closes —
|
|
14
|
+
* state lives inside the provider, but a memoized context value
|
|
15
|
+
* plus React's bail-out on identical context means the children
|
|
16
|
+
* subtree stays put.
|
|
17
|
+
*
|
|
18
|
+
* 4. Dismissing the dialog with the queue drained does not allocate
|
|
19
|
+
* a new array (cheap idle path).
|
|
20
|
+
*/
|
|
21
|
+
import * as React from 'react';
|
|
22
|
+
import { describe, it, expect, afterEach, beforeAll } from 'vitest';
|
|
23
|
+
import {
|
|
24
|
+
render,
|
|
25
|
+
screen,
|
|
26
|
+
fireEvent,
|
|
27
|
+
cleanup,
|
|
28
|
+
act,
|
|
29
|
+
} from '@testing-library/react';
|
|
30
|
+
import {
|
|
31
|
+
ConfirmDialogProvider,
|
|
32
|
+
useConfirm,
|
|
33
|
+
} from './ConfirmDialog.js';
|
|
34
|
+
|
|
35
|
+
void React;
|
|
36
|
+
afterEach(() => cleanup());
|
|
37
|
+
|
|
38
|
+
beforeAll(() => {
|
|
39
|
+
if (typeof HTMLDialogElement === 'undefined') return;
|
|
40
|
+
const proto = HTMLDialogElement.prototype as unknown as {
|
|
41
|
+
showModal?: () => void;
|
|
42
|
+
close?: (returnValue?: string) => void;
|
|
43
|
+
};
|
|
44
|
+
if (typeof proto.showModal !== 'function') {
|
|
45
|
+
proto.showModal = function (this: HTMLDialogElement) {
|
|
46
|
+
this.setAttribute('open', '');
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
if (typeof proto.close !== 'function') {
|
|
50
|
+
proto.close = function (this: HTMLDialogElement) {
|
|
51
|
+
this.removeAttribute('open');
|
|
52
|
+
this.dispatchEvent(new Event('close'));
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
describe('<ConfirmDialogProvider> performance', () => {
|
|
58
|
+
it('mounts under 200ms with empty children (cold-start safe budget)', () => {
|
|
59
|
+
const t0 = performance.now();
|
|
60
|
+
render(
|
|
61
|
+
<ConfirmDialogProvider>
|
|
62
|
+
<div />
|
|
63
|
+
</ConfirmDialogProvider>
|
|
64
|
+
);
|
|
65
|
+
const t1 = performance.now();
|
|
66
|
+
// Generous bound to accommodate cold-start variance under full
|
|
67
|
+
// workspace test run; warm path is in single-digit ms.
|
|
68
|
+
expect(t1 - t0).toBeLessThan(200);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('opens + closes a dialog in under 30ms', async () => {
|
|
72
|
+
const Harness = () => {
|
|
73
|
+
const confirm = useConfirm();
|
|
74
|
+
return (
|
|
75
|
+
<button
|
|
76
|
+
data-testid="t"
|
|
77
|
+
onClick={async () => {
|
|
78
|
+
await confirm({ title: 'X' });
|
|
79
|
+
}}
|
|
80
|
+
>
|
|
81
|
+
go
|
|
82
|
+
</button>
|
|
83
|
+
);
|
|
84
|
+
};
|
|
85
|
+
render(
|
|
86
|
+
<ConfirmDialogProvider>
|
|
87
|
+
<Harness />
|
|
88
|
+
</ConfirmDialogProvider>
|
|
89
|
+
);
|
|
90
|
+
const t0 = performance.now();
|
|
91
|
+
await act(async () => {
|
|
92
|
+
fireEvent.click(screen.getByTestId('t'));
|
|
93
|
+
});
|
|
94
|
+
await act(async () => {
|
|
95
|
+
fireEvent.click(screen.getByRole('button', { name: 'Cancel' }));
|
|
96
|
+
});
|
|
97
|
+
const t1 = performance.now();
|
|
98
|
+
// Generous budget to absorb cold-start variance when run alongside
|
|
99
|
+
// the rest of the workspace test suite.
|
|
100
|
+
expect(t1 - t0).toBeLessThan(200);
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
describe('<ConfirmDialogProvider> re-render budget', () => {
|
|
105
|
+
it('does NOT re-render unrelated children when confirm() opens', async () => {
|
|
106
|
+
const sibCount = { count: 0 };
|
|
107
|
+
const Sibling = () => {
|
|
108
|
+
sibCount.count++;
|
|
109
|
+
return <div data-testid="sib" />;
|
|
110
|
+
};
|
|
111
|
+
const Trigger = () => {
|
|
112
|
+
const confirm = useConfirm();
|
|
113
|
+
return (
|
|
114
|
+
<button data-testid="t" onClick={() => void confirm({ title: 'X' })}>
|
|
115
|
+
go
|
|
116
|
+
</button>
|
|
117
|
+
);
|
|
118
|
+
};
|
|
119
|
+
render(
|
|
120
|
+
<ConfirmDialogProvider>
|
|
121
|
+
<Sibling />
|
|
122
|
+
<Trigger />
|
|
123
|
+
</ConfirmDialogProvider>
|
|
124
|
+
);
|
|
125
|
+
const baseline = sibCount.count;
|
|
126
|
+
await act(async () => {
|
|
127
|
+
fireEvent.click(screen.getByTestId('t'));
|
|
128
|
+
});
|
|
129
|
+
// The provider re-renders to inflate the dialog, which causes one
|
|
130
|
+
// children-pass re-render at the parent level — Sibling re-renders
|
|
131
|
+
// once. Budget: ≤ 1 extra.
|
|
132
|
+
expect(sibCount.count - baseline).toBeLessThanOrEqual(1);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('confirm() reference is stable across provider re-renders', () => {
|
|
136
|
+
const refs: Array<unknown> = [];
|
|
137
|
+
const Capture = () => {
|
|
138
|
+
const confirm = useConfirm();
|
|
139
|
+
refs.push(confirm);
|
|
140
|
+
return null;
|
|
141
|
+
};
|
|
142
|
+
const Outer = ({ x }: { x: number }) => (
|
|
143
|
+
<ConfirmDialogProvider>
|
|
144
|
+
<div data-x={x} />
|
|
145
|
+
<Capture />
|
|
146
|
+
</ConfirmDialogProvider>
|
|
147
|
+
);
|
|
148
|
+
const { rerender } = render(<Outer x={1} />);
|
|
149
|
+
rerender(<Outer x={2} />);
|
|
150
|
+
rerender(<Outer x={3} />);
|
|
151
|
+
// Three renders → confirm reference must be identical each time.
|
|
152
|
+
expect(refs.length).toBeGreaterThanOrEqual(3);
|
|
153
|
+
expect(refs[0]).toBe(refs[1]);
|
|
154
|
+
expect(refs[1]).toBe(refs[2]);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it('renders at most 2 times on mount', () => {
|
|
158
|
+
let count = 0;
|
|
159
|
+
const Counted = (
|
|
160
|
+
props: React.ComponentProps<typeof ConfirmDialogProvider>
|
|
161
|
+
) => {
|
|
162
|
+
count++;
|
|
163
|
+
return <ConfirmDialogProvider {...props} />;
|
|
164
|
+
};
|
|
165
|
+
render(
|
|
166
|
+
<Counted>
|
|
167
|
+
<div />
|
|
168
|
+
</Counted>
|
|
169
|
+
);
|
|
170
|
+
expect(count).toBeLessThanOrEqual(2);
|
|
171
|
+
});
|
|
172
|
+
});
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { describe, it, expect, vi, afterEach, beforeAll } from 'vitest';
|
|
4
|
+
import {
|
|
5
|
+
render,
|
|
6
|
+
screen,
|
|
7
|
+
fireEvent,
|
|
8
|
+
cleanup,
|
|
9
|
+
act,
|
|
10
|
+
waitFor,
|
|
11
|
+
} from '@testing-library/react';
|
|
12
|
+
import {
|
|
13
|
+
ConfirmDialogProvider,
|
|
14
|
+
useConfirm,
|
|
15
|
+
} from './ConfirmDialog.js';
|
|
16
|
+
|
|
17
|
+
void React;
|
|
18
|
+
afterEach(() => cleanup());
|
|
19
|
+
|
|
20
|
+
// jsdom 22 doesn't implement HTMLDialogElement open/close. Shim the bits
|
|
21
|
+
// our component touches BEFORE any test runs — without this the very
|
|
22
|
+
// first `dlg.showModal()` call throws "not implemented".
|
|
23
|
+
beforeAll(() => {
|
|
24
|
+
if (typeof HTMLDialogElement === 'undefined') return;
|
|
25
|
+
const proto = HTMLDialogElement.prototype as unknown as {
|
|
26
|
+
showModal?: () => void;
|
|
27
|
+
close?: (returnValue?: string) => void;
|
|
28
|
+
open?: boolean;
|
|
29
|
+
};
|
|
30
|
+
if (typeof proto.showModal !== 'function') {
|
|
31
|
+
proto.showModal = function (this: HTMLDialogElement) {
|
|
32
|
+
this.setAttribute('open', '');
|
|
33
|
+
// Mirror native dispatch order (open is sync, no 'open' event).
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
if (typeof proto.close !== 'function') {
|
|
37
|
+
proto.close = function (this: HTMLDialogElement) {
|
|
38
|
+
this.removeAttribute('open');
|
|
39
|
+
this.dispatchEvent(new Event('close'));
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Test harness — exposes the imperative `confirm()` to the test via a
|
|
46
|
+
* `data-` attribute on a click target. Each click triggers a NEW
|
|
47
|
+
* confirm call, and `last result` is recorded on the harness for
|
|
48
|
+
* assertions.
|
|
49
|
+
*/
|
|
50
|
+
function Harness({
|
|
51
|
+
capture,
|
|
52
|
+
options,
|
|
53
|
+
}: {
|
|
54
|
+
capture: { last?: boolean | null };
|
|
55
|
+
options?: Parameters<ReturnType<typeof useConfirm>>[0];
|
|
56
|
+
}) {
|
|
57
|
+
const confirm = useConfirm();
|
|
58
|
+
return (
|
|
59
|
+
<button
|
|
60
|
+
type="button"
|
|
61
|
+
data-testid="trigger"
|
|
62
|
+
onClick={async () => {
|
|
63
|
+
capture.last = null;
|
|
64
|
+
const ok = await confirm(options);
|
|
65
|
+
capture.last = ok;
|
|
66
|
+
}}
|
|
67
|
+
>
|
|
68
|
+
Open
|
|
69
|
+
</button>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
describe('<ConfirmDialogProvider> + useConfirm()', () => {
|
|
74
|
+
describe('rendering', () => {
|
|
75
|
+
it('renders only its children when no request is active', () => {
|
|
76
|
+
render(
|
|
77
|
+
<ConfirmDialogProvider>
|
|
78
|
+
<div data-testid="child">app</div>
|
|
79
|
+
</ConfirmDialogProvider>
|
|
80
|
+
);
|
|
81
|
+
expect(screen.getByTestId('child')).toBeTruthy();
|
|
82
|
+
// The <dialog> exists (always mounted) but has no `open` attribute.
|
|
83
|
+
const dlg = document.querySelector('dialog');
|
|
84
|
+
expect(dlg).toBeTruthy();
|
|
85
|
+
expect(dlg?.hasAttribute('open')).toBe(false);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('opens the native dialog when confirm() is called', async () => {
|
|
89
|
+
const capture: { last?: boolean | null } = {};
|
|
90
|
+
render(
|
|
91
|
+
<ConfirmDialogProvider>
|
|
92
|
+
<Harness capture={capture} options={{ title: 'T', body: 'B' }} />
|
|
93
|
+
</ConfirmDialogProvider>
|
|
94
|
+
);
|
|
95
|
+
await act(async () => {
|
|
96
|
+
fireEvent.click(screen.getByTestId('trigger'));
|
|
97
|
+
});
|
|
98
|
+
const dlg = document.querySelector('dialog') as HTMLDialogElement;
|
|
99
|
+
expect(dlg.hasAttribute('open')).toBe(true);
|
|
100
|
+
expect(screen.getByText('T')).toBeTruthy();
|
|
101
|
+
expect(screen.getByText('B')).toBeTruthy();
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
describe('event resolution', () => {
|
|
106
|
+
it('resolves true on Confirm click', async () => {
|
|
107
|
+
const capture: { last?: boolean | null } = {};
|
|
108
|
+
render(
|
|
109
|
+
<ConfirmDialogProvider>
|
|
110
|
+
<Harness capture={capture} />
|
|
111
|
+
</ConfirmDialogProvider>
|
|
112
|
+
);
|
|
113
|
+
await act(async () => {
|
|
114
|
+
fireEvent.click(screen.getByTestId('trigger'));
|
|
115
|
+
});
|
|
116
|
+
await act(async () => {
|
|
117
|
+
fireEvent.click(screen.getByRole('button', { name: 'Confirm' }));
|
|
118
|
+
});
|
|
119
|
+
await waitFor(() => expect(capture.last).toBe(true));
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('resolves false on Cancel click', async () => {
|
|
123
|
+
const capture: { last?: boolean | null } = {};
|
|
124
|
+
render(
|
|
125
|
+
<ConfirmDialogProvider>
|
|
126
|
+
<Harness capture={capture} />
|
|
127
|
+
</ConfirmDialogProvider>
|
|
128
|
+
);
|
|
129
|
+
await act(async () => {
|
|
130
|
+
fireEvent.click(screen.getByTestId('trigger'));
|
|
131
|
+
});
|
|
132
|
+
await act(async () => {
|
|
133
|
+
fireEvent.click(screen.getByRole('button', { name: 'Cancel' }));
|
|
134
|
+
});
|
|
135
|
+
await waitFor(() => expect(capture.last).toBe(false));
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('resolves false on backdrop click', async () => {
|
|
139
|
+
const capture: { last?: boolean | null } = {};
|
|
140
|
+
render(
|
|
141
|
+
<ConfirmDialogProvider>
|
|
142
|
+
<Harness capture={capture} />
|
|
143
|
+
</ConfirmDialogProvider>
|
|
144
|
+
);
|
|
145
|
+
await act(async () => {
|
|
146
|
+
fireEvent.click(screen.getByTestId('trigger'));
|
|
147
|
+
});
|
|
148
|
+
const dlg = document.querySelector('dialog') as HTMLDialogElement;
|
|
149
|
+
// Backdrop click: event target equals the dialog element itself.
|
|
150
|
+
await act(async () => {
|
|
151
|
+
fireEvent.click(dlg);
|
|
152
|
+
});
|
|
153
|
+
await waitFor(() => expect(capture.last).toBe(false));
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it('does NOT close on backdrop when disableBackdropClose=true', async () => {
|
|
157
|
+
const capture: { last?: boolean | null } = {};
|
|
158
|
+
render(
|
|
159
|
+
<ConfirmDialogProvider>
|
|
160
|
+
<Harness
|
|
161
|
+
capture={capture}
|
|
162
|
+
options={{ disableBackdropClose: true }}
|
|
163
|
+
/>
|
|
164
|
+
</ConfirmDialogProvider>
|
|
165
|
+
);
|
|
166
|
+
await act(async () => {
|
|
167
|
+
fireEvent.click(screen.getByTestId('trigger'));
|
|
168
|
+
});
|
|
169
|
+
const dlg = document.querySelector('dialog') as HTMLDialogElement;
|
|
170
|
+
await act(async () => {
|
|
171
|
+
fireEvent.click(dlg);
|
|
172
|
+
});
|
|
173
|
+
// No resolution yet — dialog stays open.
|
|
174
|
+
expect(capture.last).toBeNull();
|
|
175
|
+
expect(dlg.hasAttribute('open')).toBe(true);
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
describe('useConfirm() outside provider', () => {
|
|
180
|
+
it('throws a helpful error', () => {
|
|
181
|
+
const Lone = () => {
|
|
182
|
+
useConfirm();
|
|
183
|
+
return null;
|
|
184
|
+
};
|
|
185
|
+
// Suppress React's console.error noise for this expected throw.
|
|
186
|
+
const spy = vi.spyOn(console, 'error').mockImplementation(() => undefined);
|
|
187
|
+
expect(() => render(<Lone />)).toThrow(/useConfirm/);
|
|
188
|
+
spy.mockRestore();
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
describe('severity', () => {
|
|
193
|
+
it('applies the danger color class on the confirm button', async () => {
|
|
194
|
+
const capture: { last?: boolean | null } = {};
|
|
195
|
+
render(
|
|
196
|
+
<ConfirmDialogProvider>
|
|
197
|
+
<Harness capture={capture} options={{ severity: 'danger' }} />
|
|
198
|
+
</ConfirmDialogProvider>
|
|
199
|
+
);
|
|
200
|
+
await act(async () => {
|
|
201
|
+
fireEvent.click(screen.getByTestId('trigger'));
|
|
202
|
+
});
|
|
203
|
+
const btn = screen.getByRole('button', { name: 'Confirm' });
|
|
204
|
+
expect(btn.className).toMatch(/danger/);
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
describe('queueing', () => {
|
|
209
|
+
it('processes concurrent confirm() calls FIFO', async () => {
|
|
210
|
+
const captures: Array<{ last?: boolean | null }> = [{}, {}];
|
|
211
|
+
function Multi() {
|
|
212
|
+
const confirm = useConfirm();
|
|
213
|
+
return (
|
|
214
|
+
<>
|
|
215
|
+
<button
|
|
216
|
+
data-testid="t1"
|
|
217
|
+
onClick={async () => {
|
|
218
|
+
captures[0].last = await confirm({ title: 'First' });
|
|
219
|
+
}}
|
|
220
|
+
>
|
|
221
|
+
t1
|
|
222
|
+
</button>
|
|
223
|
+
<button
|
|
224
|
+
data-testid="t2"
|
|
225
|
+
onClick={async () => {
|
|
226
|
+
captures[1].last = await confirm({ title: 'Second' });
|
|
227
|
+
}}
|
|
228
|
+
>
|
|
229
|
+
t2
|
|
230
|
+
</button>
|
|
231
|
+
</>
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
render(
|
|
235
|
+
<ConfirmDialogProvider>
|
|
236
|
+
<Multi />
|
|
237
|
+
</ConfirmDialogProvider>
|
|
238
|
+
);
|
|
239
|
+
await act(async () => {
|
|
240
|
+
fireEvent.click(screen.getByTestId('t1'));
|
|
241
|
+
fireEvent.click(screen.getByTestId('t2'));
|
|
242
|
+
});
|
|
243
|
+
// First request is showing.
|
|
244
|
+
expect(screen.getByText('First')).toBeTruthy();
|
|
245
|
+
// Resolve the first one — second should pop up.
|
|
246
|
+
await act(async () => {
|
|
247
|
+
fireEvent.click(screen.getByRole('button', { name: 'Confirm' }));
|
|
248
|
+
});
|
|
249
|
+
await waitFor(() => expect(captures[0].last).toBe(true));
|
|
250
|
+
await waitFor(() => expect(screen.getByText('Second')).toBeTruthy());
|
|
251
|
+
// Resolve the second one.
|
|
252
|
+
await act(async () => {
|
|
253
|
+
fireEvent.click(screen.getByRole('button', { name: 'Cancel' }));
|
|
254
|
+
});
|
|
255
|
+
await waitFor(() => expect(captures[1].last).toBe(false));
|
|
256
|
+
});
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
describe('custom labels', () => {
|
|
260
|
+
it('renders confirmLabel + cancelLabel from options', async () => {
|
|
261
|
+
const capture: { last?: boolean | null } = {};
|
|
262
|
+
render(
|
|
263
|
+
<ConfirmDialogProvider>
|
|
264
|
+
<Harness
|
|
265
|
+
capture={capture}
|
|
266
|
+
options={{ confirmLabel: 'Delete', cancelLabel: 'Keep' }}
|
|
267
|
+
/>
|
|
268
|
+
</ConfirmDialogProvider>
|
|
269
|
+
);
|
|
270
|
+
await act(async () => {
|
|
271
|
+
fireEvent.click(screen.getByTestId('trigger'));
|
|
272
|
+
});
|
|
273
|
+
expect(screen.getByRole('button', { name: 'Delete' })).toBeTruthy();
|
|
274
|
+
expect(screen.getByRole('button', { name: 'Keep' })).toBeTruthy();
|
|
275
|
+
});
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
describe('a11y wiring', () => {
|
|
279
|
+
it('sets aria-labelledby when a title is provided', async () => {
|
|
280
|
+
const capture: { last?: boolean | null } = {};
|
|
281
|
+
render(
|
|
282
|
+
<ConfirmDialogProvider>
|
|
283
|
+
<Harness capture={capture} options={{ title: 'Heads up' }} />
|
|
284
|
+
</ConfirmDialogProvider>
|
|
285
|
+
);
|
|
286
|
+
await act(async () => {
|
|
287
|
+
fireEvent.click(screen.getByTestId('trigger'));
|
|
288
|
+
});
|
|
289
|
+
const dlg = document.querySelector('dialog') as HTMLDialogElement;
|
|
290
|
+
const labelledBy = dlg.getAttribute('aria-labelledby');
|
|
291
|
+
expect(labelledBy).toBeTruthy();
|
|
292
|
+
expect(
|
|
293
|
+
labelledBy ? document.getElementById(labelledBy)?.textContent : null
|
|
294
|
+
).toBe('Heads up');
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
it('does NOT set aria-labelledby when no title', async () => {
|
|
298
|
+
const capture: { last?: boolean | null } = {};
|
|
299
|
+
render(
|
|
300
|
+
<ConfirmDialogProvider>
|
|
301
|
+
<Harness capture={capture} options={{ body: 'no title' }} />
|
|
302
|
+
</ConfirmDialogProvider>
|
|
303
|
+
);
|
|
304
|
+
await act(async () => {
|
|
305
|
+
fireEvent.click(screen.getByTestId('trigger'));
|
|
306
|
+
});
|
|
307
|
+
const dlg = document.querySelector('dialog') as HTMLDialogElement;
|
|
308
|
+
expect(dlg.getAttribute('aria-labelledby')).toBeNull();
|
|
309
|
+
});
|
|
310
|
+
});
|
|
311
|
+
});
|