srcdev-nuxt-components 9.6.7 → 9.6.9

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 (152) hide show
  1. package/.claude/rules/consumer/ids-from-useid.md +14 -0
  2. package/.claude/skills/component-compliance-checklist.md +18 -1
  3. package/.claude/skills/component-inline-action-button.md +1 -1
  4. package/.claude/skills/component-local-style-override.md +1 -1
  5. package/.claude/skills/component-naming.md +13 -13
  6. package/.claude/skills/components/container-glow.md +2 -2
  7. package/.claude/skills/components/form-field.md +2 -2
  8. package/.claude/skills/components/form-wrapper.md +1 -1
  9. package/.claude/skills/components/glowing-border.md +1 -1
  10. package/.claude/skills/components/input-checkbox-radio.md +19 -3
  11. package/.claude/skills/components/input-checkbox.md +14 -3
  12. package/.claude/skills/components/input-description.md +7 -7
  13. package/.claude/skills/components/input-error.md +3 -3
  14. package/.claude/skills/components/input-label.md +7 -8
  15. package/.claude/skills/components/input-number.md +60 -29
  16. package/.claude/skills/components/input-radio.md +15 -1
  17. package/.claude/skills/components/{input-range-core.md → input-range.md} +53 -18
  18. package/.claude/skills/components/input-select.md +153 -0
  19. package/.claude/skills/components/input-text.md +220 -0
  20. package/.claude/skills/components/input-textarea.md +147 -0
  21. package/.claude/skills/components/select-menu.md +4 -4
  22. package/.claude/skills/components/toggle-switch.md +143 -0
  23. package/.claude/skills/composable-zod-validation.md +1 -1
  24. package/.claude/skills/icon-sets.md +1 -1
  25. package/.claude/skills/icon-sizing.md +1 -1
  26. package/.claude/skills/index.md +10 -10
  27. package/.claude/skills/storybook-add-story.md +1 -1
  28. package/.claude/skills/testing-add-unit-test.md +17 -0
  29. package/.claude/skills/theming-component-token-pattern.md +8 -8
  30. package/.claude/skills/theming-form-geometry-tokens.md +1 -1
  31. package/.vscode/srcdev-component-input-checkbox-radio.code-snippets +0 -1
  32. package/.vscode/srcdev-component-input-range.code-snippets +38 -18
  33. package/.vscode/srcdev-component-input-select.code-snippets +20 -14
  34. package/.vscode/srcdev-component-input-text.code-snippets +46 -40
  35. package/.vscode/srcdev-component-input-textarea.code-snippets +19 -14
  36. package/.vscode/srcdev-component-toggle-switch.code-snippets +19 -29
  37. package/app/assets/styles/setup/04.elements/forms/00.element-defaults.css +2 -2
  38. package/app/assets/styles/setup/04.elements/forms/06.button-geometry.css +1 -1
  39. package/app/components/01.atoms/animations/glowing-border/CONSUMER-STYLING.md +2 -2
  40. package/app/components/01.atoms/animations/glowing-border/stories/GlowingBorder.stories.ts +4 -4
  41. package/app/components/02.molecules/progress-tracker/stories/ProgressTracker.stories.ts +4 -4
  42. package/app/components/05.forms/form-errors/CONSUMER-STYLING.md +1 -1
  43. package/app/components/05.forms/form-field/CONSUMER-STYLING.md +2 -2
  44. package/app/components/05.forms/input-button/stories/InputButton.stories.ts +3 -3
  45. package/app/components/05.forms/input-checkbox/CONSUMER-STYLING.md +10 -0
  46. package/app/components/05.forms/input-checkbox/MultipleCheckboxes.vue +18 -4
  47. package/app/components/05.forms/input-checkbox/SingleCheckbox.vue +14 -2
  48. package/app/components/05.forms/input-checkbox/stories/MultipleCheckboxes.stories.ts +146 -37
  49. package/app/components/05.forms/input-checkbox/stories/SingleCheckbox.stories.ts +107 -30
  50. package/app/components/05.forms/input-checkbox/tests/MultipleCheckboxes.spec.ts +38 -4
  51. package/app/components/05.forms/input-checkbox/tests/SingleCheckbox.spec.ts +5 -0
  52. package/app/components/05.forms/input-checkbox-radio/CONSUMER-STYLING.md +10 -3
  53. package/app/components/05.forms/input-checkbox-radio/InputCheckboxRadio.vue +1 -0
  54. package/app/components/05.forms/input-checkbox-radio/InputCheckboxRadioButton.vue +11 -10
  55. package/app/components/05.forms/input-checkbox-radio/InputCheckboxRadioField.vue +3 -0
  56. package/app/components/05.forms/input-checkbox-radio/stories/InputCheckboxRadio.stories.ts +44 -17
  57. package/app/components/05.forms/input-checkbox-radio/stories/InputCheckboxRadioButton.stories.ts +164 -58
  58. package/app/components/05.forms/input-checkbox-radio/stories/InputCheckboxRadioField.stories.ts +94 -21
  59. package/app/components/05.forms/input-checkbox-radio/tests/InputCheckboxRadio.spec.ts +14 -3
  60. package/app/components/05.forms/input-checkbox-radio/tests/InputCheckboxRadioButton.spec.ts +38 -3
  61. package/app/components/05.forms/input-checkbox-radio/tests/InputCheckboxRadioField.spec.ts +14 -3
  62. package/app/components/05.forms/input-description/CONSUMER-STYLING.md +7 -7
  63. package/app/components/05.forms/input-label/CONSUMER-STYLING.md +6 -6
  64. package/app/components/05.forms/input-number/CONSUMER-STYLING.md +50 -6
  65. package/app/components/05.forms/input-number/InputNumber.vue +39 -27
  66. package/app/components/05.forms/input-number/InputNumberField.vue +29 -16
  67. package/app/components/05.forms/input-number/stories/InputNumber.stories.ts +76 -25
  68. package/app/components/05.forms/input-number/stories/InputNumberField.stories.ts +116 -50
  69. package/app/components/05.forms/input-number/tests/InputNumber.spec.ts +49 -13
  70. package/app/components/05.forms/input-number/tests/InputNumberField.spec.ts +59 -5
  71. package/app/components/05.forms/input-radio/CONSUMER-STYLING.md +11 -0
  72. package/app/components/05.forms/input-radio/MultipleRadiobuttons.vue +14 -3
  73. package/app/components/05.forms/input-radio/stories/MultipleRadiobuttons.stories.ts +138 -38
  74. package/app/components/05.forms/input-radio/tests/MultipleRadioButtons.spec.ts +50 -3
  75. package/app/components/05.forms/input-range/CONSUMER-STYLING.md +68 -26
  76. package/app/components/05.forms/input-range/InputRange.vue +247 -0
  77. package/app/components/05.forms/input-range/{InputRangeDefault.vue → InputRangeField.vue} +41 -18
  78. package/app/components/05.forms/input-range/stories/InputRange.stories.ts +187 -0
  79. package/app/components/05.forms/input-range/stories/InputRangeField.stories.ts +196 -0
  80. package/app/components/05.forms/input-range/tests/{InputRangeCore.spec.ts → InputRange.spec.ts} +39 -15
  81. package/app/components/05.forms/input-range/tests/{InputRangeDefault.spec.ts → InputRangeField.spec.ts} +71 -7
  82. package/app/components/05.forms/input-select/CONSUMER-STYLING.md +80 -10
  83. package/app/components/05.forms/input-select/InputSelect.vue +198 -0
  84. package/app/components/05.forms/input-select/InputSelectField.vue +130 -0
  85. package/app/components/05.forms/input-select/stories/InputSelect.stories.ts +184 -0
  86. package/app/components/05.forms/input-select/stories/InputSelectField.stories.ts +187 -0
  87. package/app/components/05.forms/input-select/tests/{InputSelectCore.spec.ts → InputSelect.spec.ts} +75 -12
  88. package/app/components/05.forms/input-select/tests/InputSelectField.spec.ts +121 -0
  89. package/app/components/05.forms/input-text/CONSUMER-STYLING.md +85 -15
  90. package/app/components/05.forms/input-text/{InputPasswordWithLabel.vue → InputPasswordField.vue} +16 -19
  91. package/app/components/05.forms/input-text/{InputTextCore.vue → InputText.vue} +42 -78
  92. package/app/components/05.forms/input-text/{InputTextAsNumberWithLabel.vue → InputTextAsNumberField.vue} +66 -62
  93. package/app/components/05.forms/input-text/InputTextField.vue +168 -0
  94. package/app/components/05.forms/input-text/stories/InputPasswordField.stories.ts +163 -0
  95. package/app/components/05.forms/input-text/stories/InputText.stories.ts +194 -0
  96. package/app/components/05.forms/input-text/stories/InputTextAsNumberField.stories.ts +182 -0
  97. package/app/components/05.forms/input-text/stories/InputTextField.stories.ts +206 -0
  98. package/app/components/05.forms/input-text/tests/{InputPasswordWithLabel.spec.ts → InputPasswordField.spec.ts} +42 -3
  99. package/app/components/05.forms/input-text/tests/InputText.spec.ts +166 -0
  100. package/app/components/05.forms/input-text/tests/InputTextAsNumberField.spec.ts +135 -0
  101. package/app/components/05.forms/input-text/tests/InputTextField.spec.ts +130 -0
  102. package/app/components/05.forms/input-textarea/CONSUMER-STYLING.md +94 -12
  103. package/app/components/05.forms/input-textarea/InputTextarea.vue +165 -0
  104. package/app/components/05.forms/input-textarea/InputTextareaField.vue +159 -0
  105. package/app/components/05.forms/input-textarea/stories/InputTextarea.stories.ts +198 -0
  106. package/app/components/05.forms/input-textarea/stories/InputTextareaField.stories.ts +161 -0
  107. package/app/components/05.forms/input-textarea/tests/InputTextarea.spec.ts +149 -0
  108. package/app/components/05.forms/input-textarea/tests/InputTextareaField.spec.ts +122 -0
  109. package/app/components/05.forms/patterns/stories/ContactForm.stories.ts +13 -13
  110. package/app/components/05.forms/patterns/stories/LoginForm.stories.ts +6 -6
  111. package/app/components/05.forms/patterns/stories/MigratedFieldsForm.stories.ts +34 -36
  112. package/app/components/05.forms/toggle-switch/CONSUMER-STYLING.md +71 -50
  113. package/app/components/05.forms/toggle-switch/{ToggleSwitchCore.vue → ToggleSwitch.vue} +58 -73
  114. package/app/components/05.forms/toggle-switch/{ToggleSwitchWithLabel.vue → ToggleSwitchField.vue} +65 -19
  115. package/app/components/05.forms/toggle-switch/stories/ToggleSwitch.stories.ts +148 -0
  116. package/app/components/05.forms/toggle-switch/stories/ToggleSwitchField.stories.ts +176 -0
  117. package/app/components/05.forms/toggle-switch/tests/ToggleSwitch.spec.ts +145 -0
  118. package/app/components/05.forms/toggle-switch/tests/ToggleSwitchField.spec.ts +155 -0
  119. package/package.json +1 -1
  120. package/.claude/skills/components/input-select-core.md +0 -151
  121. package/.claude/skills/components/input-text-core.md +0 -204
  122. package/.claude/skills/components/input-textarea-core.md +0 -154
  123. package/.claude/skills/components/toggle-switch-core.md +0 -169
  124. package/app/components/05.forms/input-range/InputRangeCore.vue +0 -169
  125. package/app/components/05.forms/input-range/stories/InputRangeCore.stories.ts +0 -126
  126. package/app/components/05.forms/input-range/stories/InputRangeDefault.stories.ts +0 -108
  127. package/app/components/05.forms/input-select/InputSelectCore.vue +0 -188
  128. package/app/components/05.forms/input-select/InputSelectWithLabel.vue +0 -118
  129. package/app/components/05.forms/input-select/stories/InputSelectCore.stories.ts +0 -69
  130. package/app/components/05.forms/input-select/stories/InputSelectWithLabel.stories.ts +0 -115
  131. package/app/components/05.forms/input-select/tests/InputSelectWithLabel.spec.ts +0 -87
  132. package/app/components/05.forms/input-text/InputTextWithLabel.vue +0 -154
  133. package/app/components/05.forms/input-text/stories/InputPasswordWithLabel.stories.ts +0 -368
  134. package/app/components/05.forms/input-text/stories/InputTextAsNumberWithLabel.stories.ts +0 -480
  135. package/app/components/05.forms/input-text/stories/InputTextCore.stories.ts +0 -267
  136. package/app/components/05.forms/input-text/stories/InputTextWithLabel.stories.ts +0 -319
  137. package/app/components/05.forms/input-text/tests/InputTextAsNumberWithLabel.spec.ts +0 -75
  138. package/app/components/05.forms/input-text/tests/InputTextCore.spec.ts +0 -60
  139. package/app/components/05.forms/input-text/tests/InputTextWithLabel.spec.ts +0 -71
  140. package/app/components/05.forms/input-textarea/InputTextareaCore.vue +0 -159
  141. package/app/components/05.forms/input-textarea/InputTextareaWithLabel.vue +0 -136
  142. package/app/components/05.forms/input-textarea/stories/InputTextareaCore.stories.ts +0 -247
  143. package/app/components/05.forms/input-textarea/stories/InputTextareaWithLabel.stories.ts +0 -458
  144. package/app/components/05.forms/input-textarea/tests/InputTextareaCore.spec.ts +0 -119
  145. package/app/components/05.forms/input-textarea/tests/InputTextareaWithLabel.spec.ts +0 -93
  146. package/app/components/05.forms/toggle-switch/ToggleSwitchWithLabelInline.vue +0 -81
  147. package/app/components/05.forms/toggle-switch/stories/ToggleSwitchCore.stories.ts +0 -251
  148. package/app/components/05.forms/toggle-switch/stories/ToggleSwitchWithLabel.stories.ts +0 -251
  149. package/app/components/05.forms/toggle-switch/stories/ToggleSwitchWithLabelInline.stories.ts +0 -283
  150. package/app/components/05.forms/toggle-switch/tests/ToggleSwitchCore.spec.ts +0 -101
  151. package/app/components/05.forms/toggle-switch/tests/ToggleSwitchWithLabel.spec.ts +0 -133
  152. package/app/components/05.forms/toggle-switch/tests/ToggleSwitchWithLabelInline.spec.ts +0 -66
@@ -1,283 +0,0 @@
1
- import type { Meta, StoryFn } from "@nuxtjs/storybook";
2
- import { ref } from "vue";
3
- import StorybookComponent from "../ToggleSwitchWithLabelInline.vue";
4
- import type { FormUiTheme, LabelWeight } from "~/types/forms/types.forms.d";
5
-
6
- interface ToggleSwitchWithLabelInlineStoryArgs {
7
- name: string;
8
- label: string;
9
- labelWeight: LabelWeight;
10
- trueValue: string | number | boolean;
11
- falseValue: string | number | boolean;
12
- styleClassPassthrough: string[];
13
- theme: FormUiTheme;
14
- round: boolean;
15
- useCustomIcons: boolean;
16
- iconOnContent: string;
17
- iconOffContent: string;
18
- }
19
-
20
- export default {
21
- title: "Components/Forms/Toggle Switch/ToggleSwitchWithLabelInline",
22
- component: StorybookComponent,
23
- argTypes: {
24
- // Basic Configuration
25
- name: {
26
- control: "text",
27
- description: "Name attribute for the toggle switch",
28
- table: {
29
- category: "Basic",
30
- },
31
- },
32
- label: {
33
- control: "text",
34
- description: "Label text for the toggle switch",
35
- table: {
36
- category: "Basic",
37
- },
38
- },
39
- labelWeight: {
40
- control: { type: "select" },
41
- options: ["normal", "semi-bold", "bold"],
42
- description: "Font weight for the label",
43
- table: {
44
- category: "Basic",
45
- },
46
- },
47
-
48
- // Values
49
- trueValue: {
50
- control: "text",
51
- description: "Value when toggle is on/checked",
52
- table: {
53
- category: "Values",
54
- },
55
- },
56
- falseValue: {
57
- control: "text",
58
- description: "Value when toggle is off/unchecked",
59
- table: {
60
- category: "Values",
61
- },
62
- },
63
-
64
- // Styling
65
- theme: {
66
- control: { type: "select" },
67
- options: ["default", "success", "error", "warning"],
68
- description: "Toggle switch theme",
69
- table: {
70
- category: "Styling",
71
- },
72
- },
73
- round: {
74
- control: "boolean",
75
- description: "Whether the toggle switch is rounded",
76
- table: {
77
- category: "Styling",
78
- },
79
- },
80
- styleClassPassthrough: {
81
- control: "object",
82
- description: "Additional CSS classes",
83
- table: {
84
- category: "Styling",
85
- },
86
- },
87
-
88
- // Icons
89
- useCustomIcons: {
90
- control: "boolean",
91
- description: "Use custom icons instead of default",
92
- table: {
93
- category: "Icons",
94
- },
95
- },
96
- iconOnContent: {
97
- control: "text",
98
- description: "Content for the 'on' icon slot",
99
- table: {
100
- category: "Icons",
101
- },
102
- },
103
- iconOffContent: {
104
- control: "text",
105
- description: "Content for the 'off' icon slot",
106
- table: {
107
- category: "Icons",
108
- },
109
- },
110
- },
111
- } as Meta<ToggleSwitchWithLabelInlineStoryArgs>;
112
-
113
- const Template: StoryFn<ToggleSwitchWithLabelInlineStoryArgs> = (_args, { argTypes }) => ({
114
- components: { StorybookComponent },
115
- props: Object.keys(argTypes),
116
- setup() {
117
- const modelValue = ref(false);
118
- return { args: _args, modelValue };
119
- },
120
- template: `
121
- <StorybookComponent
122
- v-model="modelValue"
123
- :name="args.name"
124
- :label="args.label"
125
- :label-weight="args.labelWeight"
126
- :true-value="args.trueValue"
127
- :false-value="args.falseValue"
128
- :style-class-passthrough="args.styleClassPassthrough"
129
- :theme="args.theme"
130
- :round="args.round"
131
- >
132
- <template v-if="args.useCustomIcons" #iconOn>
133
- <span v-html="args.iconOnContent"></span>
134
- </template>
135
- <template v-if="args.useCustomIcons" #iconOff>
136
- <span v-html="args.iconOffContent"></span>
137
- </template>
138
- </StorybookComponent>
139
- <div style="margin-top: 1rem; font-size: 0.875rem; color: #666;">
140
- Current value: {{ modelValue }}
141
- </div>
142
- `,
143
- });
144
-
145
- export const Default = Template.bind({});
146
- Default.args = {
147
- name: "toggleInline",
148
- label: "Enable notifications",
149
- labelWeight: "normal",
150
- trueValue: true,
151
- falseValue: false,
152
- styleClassPassthrough: [],
153
- theme: "default",
154
- round: true,
155
- useCustomIcons: false,
156
- iconOnContent: "✓",
157
- iconOffContent: "✗",
158
- };
159
-
160
- export const WithCustomIcons = Template.bind({});
161
- WithCustomIcons.args = {
162
- ...Default.args,
163
- name: "toggleInlineCustomIcons",
164
- label: "Dark mode",
165
- useCustomIcons: true,
166
- iconOnContent:
167
- '<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z"/></svg>',
168
- iconOffContent:
169
- '<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"/></svg>',
170
- };
171
-
172
- export const BoldLabel = Template.bind({});
173
- BoldLabel.args = {
174
- ...Default.args,
175
- name: "toggleInlineBold",
176
- label: "Important setting",
177
- labelWeight: "bold",
178
- };
179
-
180
- export const SemiBoldLabel = Template.bind({});
181
- SemiBoldLabel.args = {
182
- ...Default.args,
183
- name: "toggleInlineSemiBold",
184
- label: "Feature toggle",
185
- labelWeight: "semi-bold",
186
- };
187
-
188
- export const StringValues = Template.bind({});
189
- StringValues.args = {
190
- ...Default.args,
191
- name: "toggleInlineStringValues",
192
- label: "Email preferences",
193
- trueValue: "enabled",
194
- falseValue: "disabled",
195
- };
196
-
197
- export const InlineFormExample: StoryFn = () => ({
198
- components: { StorybookComponent },
199
- setup() {
200
- const preferences = ref({
201
- notifications: true,
202
- autoSave: false,
203
- darkMode: true,
204
- soundEffects: false,
205
- showTips: true,
206
- });
207
-
208
- const handleReset = () => {
209
- Object.keys(preferences.value).forEach((key) => {
210
- preferences.value[key as keyof typeof preferences.value] = false;
211
- });
212
- };
213
-
214
- const handleSelectAll = () => {
215
- Object.keys(preferences.value).forEach((key) => {
216
- preferences.value[key as keyof typeof preferences.value] = true;
217
- });
218
- };
219
-
220
- return { preferences, handleReset, handleSelectAll };
221
- },
222
- template: `
223
- <div style="max-width: 400px; padding: 1rem; border: 1px solid #ddd; border-radius: 8px;">
224
- <h3 style="margin: 0 0 1rem 0;">App Preferences</h3>
225
-
226
- <div style="display: flex; flex-direction: column; gap: 1rem;">
227
- <StorybookComponent
228
- v-model="preferences.notifications"
229
- name="notifications"
230
- label="Push notifications"
231
- label-weight="semi-bold"
232
- />
233
-
234
- <StorybookComponent
235
- v-model="preferences.autoSave"
236
- name="autoSave"
237
- label="Auto-save documents"
238
- label-weight="normal"
239
- />
240
-
241
- <StorybookComponent
242
- v-model="preferences.darkMode"
243
- name="darkMode"
244
- label="Dark mode"
245
- label-weight="normal"
246
- theme="default"
247
- />
248
-
249
- <StorybookComponent
250
- v-model="preferences.soundEffects"
251
- name="soundEffects"
252
- label="Sound effects"
253
- label-weight="normal"
254
- />
255
-
256
- <StorybookComponent
257
- v-model="preferences.showTips"
258
- name="showTips"
259
- label="Show helpful tips"
260
- label-weight="normal"
261
- theme="success"
262
- />
263
- </div>
264
-
265
- <div style="margin-top: 1.5rem; padding-top: 1rem; border-top: 1px solid #eee; display: flex; gap: 0.5rem;">
266
- <button @click="handleSelectAll" type="button" style="padding: 0.5rem 1rem; background: #28a745; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 0.875rem;">
267
- Select All
268
- </button>
269
- <button @click="handleReset" type="button" style="padding: 0.5rem 1rem; background: #6c757d; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 0.875rem;">
270
- Reset All
271
- </button>
272
- </div>
273
-
274
- <div style="margin-top: 1rem; font-size: 0.875rem; color: #666;">
275
- <strong>Current settings:</strong><br>
276
- <pre style="font-size: 0.75rem; background: #f5f5f5; padding: 0.5rem; border-radius: 4px; margin-top: 0.5rem;">{{ JSON.stringify(preferences, null, 2) }}</pre>
277
- </div>
278
- </div>
279
- `,
280
- });
281
-
282
- // Fixed showcase: hardcoded values, so Controls would do nothing here.
283
- InlineFormExample.parameters = { controls: { disable: true } };
@@ -1,101 +0,0 @@
1
- // https://nuxt.com/docs/getting-started/testing#unit-testing
2
- import type { VueWrapper } from "@vue/test-utils";
3
- import { mountSuspended } from "@nuxt/test-utils/runtime";
4
- import ComponentUnderTest from "../ToggleSwitchCore.vue";
5
-
6
- const initialPropsData = {
7
- id: "test-toggle",
8
- name: "testName",
9
- modelValue: false,
10
- };
11
-
12
- let wrapper: VueWrapper<InstanceType<typeof ComponentUnderTest>>;
13
- const wrapperFactory = (propsData = {}) => {
14
- const mockPropsData = { ...initialPropsData, ...propsData };
15
-
16
- return mountSuspended(ComponentUnderTest, {
17
- attachTo: document.body,
18
- props: mockPropsData,
19
- });
20
- };
21
-
22
- describe("ToggleSwitchCore", () => {
23
- it("mounts without error", async () => {
24
- wrapper = await wrapperFactory();
25
- expect(wrapper.vm).toBeTruthy();
26
- });
27
-
28
- it("binds v-model to the checkbox checked state", async () => {
29
- wrapper = await wrapperFactory({ modelValue: true });
30
- const input = wrapper.find<HTMLInputElement>("input[type='checkbox']");
31
- expect(input.element.checked).toBe(true);
32
- });
33
-
34
- it("toggles modelValue when the wrapper is clicked", async () => {
35
- wrapper = await wrapperFactory({ modelValue: false });
36
- await wrapper.find(".toggle-switch-wrapper").trigger("click");
37
- expect(wrapper.emitted("update:modelValue")?.at(-1)).toEqual([true]);
38
- });
39
-
40
- it("toggles between custom trueValue/falseValue", async () => {
41
- wrapper = await wrapperFactory({ modelValue: "off", trueValue: "on", falseValue: "off" });
42
- await wrapper.find(".toggle-switch-wrapper").trigger("click");
43
- expect(wrapper.emitted("update:modelValue")?.at(-1)).toEqual(["on"]);
44
- });
45
-
46
- it("sets required attribute when required is true", async () => {
47
- wrapper = await wrapperFactory({ required: true });
48
- expect(wrapper.find("input").attributes("required")).toBeDefined();
49
- });
50
-
51
- it("does not set required attribute by default", async () => {
52
- wrapper = await wrapperFactory();
53
- expect(wrapper.find("input").attributes("required")).toBeUndefined();
54
- });
55
-
56
- it("sets data-theme from the theme prop", async () => {
57
- wrapper = await wrapperFactory({ theme: "success" });
58
- expect(wrapper.find(".toggle-switch-core").attributes("data-theme")).toBe("success");
59
- });
60
-
61
- it("sets data-theme to error and aria-invalid when fieldHasError is true, overriding theme", async () => {
62
- wrapper = await wrapperFactory({ fieldHasError: true, theme: "success" });
63
- expect(wrapper.find(".toggle-switch-core").attributes("data-theme")).toBe("error");
64
- expect(wrapper.find("input").attributes("aria-invalid")).toBe("true");
65
- });
66
-
67
- it("applies styleClassPassthrough to the root element", async () => {
68
- wrapper = await wrapperFactory({ styleClassPassthrough: ["custom-toggle-class"] });
69
- expect(wrapper.find(".toggle-switch-core").classes()).toContain("custom-toggle-class");
70
- });
71
-
72
- it("renders default icons when no icon slots are provided", async () => {
73
- wrapper = await wrapperFactory();
74
- expect(wrapper.find(".toggle-switch-wrapper").classes()).toContain("use-default-icons");
75
- });
76
-
77
- it("renders custom icon slot content instead of the default icon", async () => {
78
- wrapper = await mountSuspended(ComponentUnderTest, {
79
- attachTo: document.body,
80
- props: initialPropsData,
81
- slots: {
82
- iconOn: () => "ON",
83
- iconOff: () => "OFF",
84
- },
85
- });
86
- expect(wrapper.find(".toggle-switch-wrapper").classes()).not.toContain("use-default-icons");
87
- expect(wrapper.find(".icon-on").text()).toBe("ON");
88
- expect(wrapper.find(".icon-off").text()).toBe("OFF");
89
- });
90
-
91
- it("consumes round as a declared prop instead of leaking it as a DOM attribute", async () => {
92
- wrapper = await wrapperFactory({ round: false });
93
- expect(wrapper.props("round")).toBe(false);
94
- expect(wrapper.attributes("round")).toBeUndefined();
95
- });
96
-
97
- it("defaults round to true", async () => {
98
- wrapper = await wrapperFactory();
99
- expect(wrapper.props("round")).toBe(true);
100
- });
101
- });
@@ -1,133 +0,0 @@
1
- // https://nuxt.com/docs/getting-started/testing#unit-testing
2
- import type { VueWrapper } from "@vue/test-utils";
3
- import { mountSuspended } from "@nuxt/test-utils/runtime";
4
- import ComponentUnderTest from "../ToggleSwitchWithLabel.vue";
5
- import { defineComponent, nextTick, ref } from "vue";
6
-
7
- const initialPropsData = {
8
- name: "notifications",
9
- label: "Enable notifications",
10
- errorMessage: "",
11
- modelValue: false,
12
- };
13
-
14
- let wrapper: VueWrapper<InstanceType<typeof ComponentUnderTest>>;
15
- const wrapperFactory = (propsData = {}, slots = {}) => {
16
- const mockPropsData = { ...initialPropsData, ...propsData };
17
-
18
- return mountSuspended(ComponentUnderTest, {
19
- attachTo: document.body,
20
- props: mockPropsData,
21
- slots,
22
- });
23
- };
24
-
25
- describe("ToggleSwitchWithLabel", () => {
26
- it("mounts without error", async () => {
27
- wrapper = await wrapperFactory();
28
- expect(wrapper.vm).toBeTruthy();
29
- });
30
-
31
- it("renders the label text", async () => {
32
- wrapper = await wrapperFactory({ label: "Enable dark mode" });
33
- expect(wrapper.text()).toContain("Enable dark mode");
34
- });
35
-
36
- it("renders a checkbox input", async () => {
37
- wrapper = await wrapperFactory();
38
- expect(wrapper.find("input[type='checkbox']").exists()).toBe(true);
39
- });
40
-
41
- it("associates the label with the checkbox via matching for/id", async () => {
42
- wrapper = await wrapperFactory();
43
- const inputId = wrapper.find("input[type='checkbox']").attributes("id");
44
- expect(wrapper.find("label").attributes("for")).toBe(inputId);
45
- });
46
-
47
- it("renders the error message when fieldHasError is true", async () => {
48
- wrapper = await wrapperFactory({ fieldHasError: true, errorMessage: "You must accept" });
49
- expect(wrapper.text()).toContain("You must accept");
50
- });
51
-
52
- it("renders descriptionText through InputDescription", async () => {
53
- wrapper = await wrapperFactory({}, { descriptionText: () => "Turn this on to receive notifications" });
54
- const description = wrapper.find(".input-description.toggle-switch-description");
55
- expect(description.exists()).toBe(true);
56
- expect(description.find(".input-description-text").text()).toBe("Turn this on to receive notifications");
57
- });
58
-
59
- it("renders descriptionHtml through InputDescription", async () => {
60
- wrapper = await wrapperFactory({}, { descriptionHtml: () => "Rich description" });
61
- expect(wrapper.find(".toggle-switch-description .input-description-html").text()).toBe("Rich description");
62
- });
63
-
64
- it("renders no description element without a description slot", async () => {
65
- wrapper = await wrapperFactory();
66
- expect(wrapper.find(".input-description").exists()).toBe(false);
67
- });
68
-
69
- it("no longer renders the removed description slot", async () => {
70
- wrapper = await wrapperFactory({}, { description: () => "Old slot content" });
71
- expect(wrapper.text()).not.toContain("Old slot content");
72
- });
73
-
74
- it("forwards required to the underlying input", async () => {
75
- wrapper = await wrapperFactory({ required: true });
76
- expect(wrapper.find("input").attributes("required")).toBeDefined();
77
- });
78
-
79
- it("applies the error theme to ToggleSwitchCore when fieldHasError is true", async () => {
80
- wrapper = await wrapperFactory({ fieldHasError: true, theme: "default" });
81
- expect(wrapper.find(".toggle-switch-core").attributes("data-theme")).toBe("error");
82
- });
83
-
84
- it("applies styleClassPassthrough to the root element", async () => {
85
- wrapper = await wrapperFactory({ styleClassPassthrough: ["custom-toggle"] });
86
- expect(wrapper.find(".toggle-switch-with-label").classes()).toContain("custom-toggle");
87
- });
88
-
89
- it("forwards round to ToggleSwitchCore without leaking it as a DOM attribute", async () => {
90
- wrapper = await wrapperFactory({ round: false });
91
- const core = wrapper.findComponent({ name: "ToggleSwitchCore" });
92
- expect(core.props("round")).toBe(false);
93
- expect(wrapper.find(".toggle-switch-core").attributes("round")).toBeUndefined();
94
- });
95
-
96
- it("points aria-describedby at the description once it appears after mount", async () => {
97
- const Host = defineComponent({
98
- components: { ComponentUnderTest },
99
- setup() {
100
- return { modelValue: ref(false), showDescription: ref(false) };
101
- },
102
- template: `
103
- <ComponentUnderTest v-model="modelValue" name="notifications" label="Enable notifications" error-message="">
104
- <template v-if="showDescription" #descriptionText>Sends at most one email a week.</template>
105
- </ComponentUnderTest>
106
- `,
107
- });
108
- const host = await mountSuspended(Host);
109
- const input = () => host.find("input[type='checkbox']");
110
- expect(input().attributes("aria-describedby")).toBeFalsy();
111
- (host.vm as unknown as { showDescription: boolean }).showDescription = true;
112
- await nextTick();
113
- expect(input().attributes("aria-describedby")).toMatch(/-description$/);
114
- });
115
-
116
- it("points aria-describedby at the error message when the field has an error", async () => {
117
- wrapper = await wrapperFactory({ fieldHasError: true, errorMessage: "Required" });
118
- expect(wrapper.find("input[type='checkbox']").attributes("aria-describedby")).toMatch(/-error-message$/);
119
- });
120
-
121
- it("points aria-describedby at both the description and the error when both are present", async () => {
122
- wrapper = await wrapperFactory(
123
- { fieldHasError: true, errorMessage: "Required" },
124
- { descriptionText: () => "Sends at most one email a week." }
125
- );
126
- const describedBy = wrapper.find("input[type='checkbox']").attributes("aria-describedby") ?? "";
127
- const ids = describedBy.split(" ");
128
- expect(ids).toHaveLength(2);
129
- expect(ids[0]).toMatch(/-description$/);
130
- expect(ids[1]).toMatch(/-error-message$/);
131
- expect(wrapper.find(`[id="${ids[0]}"]`).exists()).toBe(true);
132
- });
133
- });
@@ -1,66 +0,0 @@
1
- // https://nuxt.com/docs/getting-started/testing#unit-testing
2
- import type { VueWrapper } from "@vue/test-utils";
3
- import { mountSuspended } from "@nuxt/test-utils/runtime";
4
- import ComponentUnderTest from "../ToggleSwitchWithLabelInline.vue";
5
-
6
- const initialPropsData = {
7
- name: "notifications",
8
- label: "Enable notifications",
9
- modelValue: false,
10
- };
11
-
12
- let wrapper: VueWrapper<InstanceType<typeof ComponentUnderTest>>;
13
- const wrapperFactory = (propsData = {}, slots = {}) => {
14
- const mockPropsData = { ...initialPropsData, ...propsData };
15
-
16
- return mountSuspended(ComponentUnderTest, {
17
- attachTo: document.body,
18
- props: mockPropsData,
19
- slots,
20
- });
21
- };
22
-
23
- describe("ToggleSwitchWithLabelInline", () => {
24
- it("mounts without error", async () => {
25
- wrapper = await wrapperFactory();
26
- expect(wrapper.vm).toBeTruthy();
27
- });
28
-
29
- it("renders the label text", async () => {
30
- wrapper = await wrapperFactory({ label: "Dark mode" });
31
- expect(wrapper.text()).toContain("Dark mode");
32
- });
33
-
34
- it("renders a checkbox input", async () => {
35
- wrapper = await wrapperFactory();
36
- expect(wrapper.find("input[type='checkbox']").exists()).toBe(true);
37
- });
38
-
39
- it("associates the label with the checkbox via matching for/id", async () => {
40
- wrapper = await wrapperFactory();
41
- const inputId = wrapper.find("input[type='checkbox']").attributes("id");
42
- expect(wrapper.find("label").attributes("for")).toBe(inputId);
43
- });
44
-
45
- it("defaults labelWeight to normal", async () => {
46
- wrapper = await wrapperFactory();
47
- expect(wrapper.find(".input-switch-label").classes()).toContain("body-normal");
48
- });
49
-
50
- it("maps labelWeight to the matching body-* class", async () => {
51
- wrapper = await wrapperFactory({ labelWeight: "bold" });
52
- expect(wrapper.find(".input-switch-label").classes()).toContain("body-normal-bold");
53
- });
54
-
55
- it("applies styleClassPassthrough to the root element", async () => {
56
- wrapper = await wrapperFactory({ styleClassPassthrough: ["custom-toggle-inline"] });
57
- expect(wrapper.find(".toggle-switch-with-label-inline").classes()).toContain("custom-toggle-inline");
58
- });
59
-
60
- it("forwards round to ToggleSwitchCore without leaking it as a DOM attribute", async () => {
61
- wrapper = await wrapperFactory({ round: false });
62
- const core = wrapper.findComponent({ name: "ToggleSwitchCore" });
63
- expect(core.props("round")).toBe(false);
64
- expect(wrapper.find(".toggle-switch-core").attributes("round")).toBeUndefined();
65
- });
66
- });