srcdev-nuxt-components 9.6.8 → 9.6.10
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/.claude/skills/component-compliance-checklist.md +28 -1
- package/.claude/skills/component-inline-action-button.md +1 -1
- package/.claude/skills/component-local-style-override.md +1 -1
- package/.claude/skills/component-naming.md +17 -12
- package/.claude/skills/components/container-glow.md +2 -2
- package/.claude/skills/components/form-field.md +2 -2
- package/.claude/skills/components/form-wrapper.md +1 -1
- package/.claude/skills/components/glowing-border.md +1 -1
- package/.claude/skills/components/input-description.md +7 -7
- package/.claude/skills/components/input-error.md +3 -3
- package/.claude/skills/components/input-label.md +7 -8
- package/.claude/skills/components/input-number.md +17 -17
- package/.claude/skills/components/input-select.md +153 -0
- package/.claude/skills/components/input-text.md +220 -0
- package/.claude/skills/components/input-textarea.md +147 -0
- package/.claude/skills/components/select-menu.md +4 -4
- package/.claude/skills/components/toggle-switch.md +143 -0
- package/.claude/skills/composable-zod-validation.md +1 -1
- package/.claude/skills/css-nesting-conventions.md +22 -1
- package/.claude/skills/icon-sets.md +1 -1
- package/.claude/skills/icon-sizing.md +1 -1
- package/.claude/skills/index.md +5 -5
- package/.claude/skills/storybook-add-story.md +1 -1
- package/.claude/skills/theming-component-token-pattern.md +8 -8
- package/.claude/skills/theming-form-geometry-tokens.md +1 -1
- package/.vscode/srcdev-component-input-select.code-snippets +20 -14
- package/.vscode/srcdev-component-input-text.code-snippets +46 -40
- package/.vscode/srcdev-component-input-textarea.code-snippets +19 -14
- package/.vscode/srcdev-component-toggle-switch.code-snippets +19 -29
- package/app/assets/styles/setup/04.elements/forms/00.element-defaults.css +2 -2
- package/app/assets/styles/setup/04.elements/forms/06.button-geometry.css +1 -1
- package/app/components/01.atoms/animations/glowing-border/CONSUMER-STYLING.md +2 -2
- package/app/components/01.atoms/animations/glowing-border/stories/GlowingBorder.stories.ts +4 -4
- package/app/components/02.molecules/progress-tracker/stories/ProgressTracker.stories.ts +4 -4
- package/app/components/05.forms/form-errors/CONSUMER-STYLING.md +1 -1
- package/app/components/05.forms/form-field/CONSUMER-STYLING.md +2 -2
- package/app/components/05.forms/input-button/stories/InputButton.stories.ts +3 -3
- package/app/components/05.forms/input-description/CONSUMER-STYLING.md +6 -6
- package/app/components/05.forms/input-label/CONSUMER-STYLING.md +7 -7
- package/app/components/05.forms/input-number/CONSUMER-STYLING.md +9 -5
- package/app/components/05.forms/input-number/InputNumber.vue +2 -2
- package/app/components/05.forms/input-select/CONSUMER-STYLING.md +80 -10
- package/app/components/05.forms/input-select/InputSelect.vue +198 -0
- package/app/components/05.forms/input-select/InputSelectField.vue +130 -0
- package/app/components/05.forms/input-select/stories/InputSelect.stories.ts +184 -0
- package/app/components/05.forms/input-select/stories/InputSelectField.stories.ts +187 -0
- package/app/components/05.forms/input-select/tests/{InputSelectCore.spec.ts → InputSelect.spec.ts} +75 -12
- package/app/components/05.forms/input-select/tests/InputSelectField.spec.ts +121 -0
- package/app/components/05.forms/input-text/CONSUMER-STYLING.md +85 -15
- package/app/components/05.forms/input-text/{InputPasswordWithLabel.vue → InputPasswordField.vue} +16 -19
- package/app/components/05.forms/input-text/{InputTextCore.vue → InputText.vue} +42 -78
- package/app/components/05.forms/input-text/{InputTextAsNumberWithLabel.vue → InputTextAsNumberField.vue} +66 -62
- package/app/components/05.forms/input-text/InputTextField.vue +168 -0
- package/app/components/05.forms/input-text/stories/InputPasswordField.stories.ts +163 -0
- package/app/components/05.forms/input-text/stories/InputText.stories.ts +194 -0
- package/app/components/05.forms/input-text/stories/InputTextAsNumberField.stories.ts +182 -0
- package/app/components/05.forms/input-text/stories/InputTextField.stories.ts +206 -0
- package/app/components/05.forms/input-text/tests/{InputPasswordWithLabel.spec.ts → InputPasswordField.spec.ts} +42 -3
- package/app/components/05.forms/input-text/tests/InputText.spec.ts +166 -0
- package/app/components/05.forms/input-text/tests/InputTextAsNumberField.spec.ts +135 -0
- package/app/components/05.forms/input-text/tests/InputTextField.spec.ts +130 -0
- package/app/components/05.forms/input-textarea/CONSUMER-STYLING.md +94 -12
- package/app/components/05.forms/input-textarea/InputTextarea.vue +165 -0
- package/app/components/05.forms/input-textarea/InputTextareaField.vue +159 -0
- package/app/components/05.forms/input-textarea/stories/InputTextarea.stories.ts +198 -0
- package/app/components/05.forms/input-textarea/stories/InputTextareaField.stories.ts +161 -0
- package/app/components/05.forms/input-textarea/tests/InputTextarea.spec.ts +149 -0
- package/app/components/05.forms/input-textarea/tests/InputTextareaField.spec.ts +122 -0
- package/app/components/05.forms/patterns/stories/ContactForm.stories.ts +13 -13
- package/app/components/05.forms/patterns/stories/LoginForm.stories.ts +6 -6
- package/app/components/05.forms/patterns/stories/MigratedFieldsForm.stories.ts +30 -32
- package/app/components/05.forms/toggle-switch/CONSUMER-STYLING.md +71 -50
- package/app/components/05.forms/toggle-switch/{ToggleSwitchCore.vue → ToggleSwitch.vue} +58 -73
- package/app/components/05.forms/toggle-switch/{ToggleSwitchWithLabel.vue → ToggleSwitchField.vue} +65 -19
- package/app/components/05.forms/toggle-switch/stories/ToggleSwitch.stories.ts +148 -0
- package/app/components/05.forms/toggle-switch/stories/ToggleSwitchField.stories.ts +176 -0
- package/app/components/05.forms/toggle-switch/tests/ToggleSwitch.spec.ts +145 -0
- package/app/components/05.forms/toggle-switch/tests/ToggleSwitchField.spec.ts +155 -0
- package/package.json +1 -1
- package/.claude/skills/components/input-select-core.md +0 -151
- package/.claude/skills/components/input-text-core.md +0 -204
- package/.claude/skills/components/input-textarea-core.md +0 -154
- package/.claude/skills/components/toggle-switch-core.md +0 -169
- package/app/components/05.forms/input-select/InputSelectCore.vue +0 -188
- package/app/components/05.forms/input-select/InputSelectWithLabel.vue +0 -118
- package/app/components/05.forms/input-select/stories/InputSelectCore.stories.ts +0 -69
- package/app/components/05.forms/input-select/stories/InputSelectWithLabel.stories.ts +0 -115
- package/app/components/05.forms/input-select/tests/InputSelectWithLabel.spec.ts +0 -87
- package/app/components/05.forms/input-text/InputTextWithLabel.vue +0 -154
- package/app/components/05.forms/input-text/stories/InputPasswordWithLabel.stories.ts +0 -368
- package/app/components/05.forms/input-text/stories/InputTextAsNumberWithLabel.stories.ts +0 -480
- package/app/components/05.forms/input-text/stories/InputTextCore.stories.ts +0 -267
- package/app/components/05.forms/input-text/stories/InputTextWithLabel.stories.ts +0 -319
- package/app/components/05.forms/input-text/tests/InputTextAsNumberWithLabel.spec.ts +0 -75
- package/app/components/05.forms/input-text/tests/InputTextCore.spec.ts +0 -60
- package/app/components/05.forms/input-text/tests/InputTextWithLabel.spec.ts +0 -71
- package/app/components/05.forms/input-textarea/InputTextareaCore.vue +0 -159
- package/app/components/05.forms/input-textarea/InputTextareaWithLabel.vue +0 -136
- package/app/components/05.forms/input-textarea/stories/InputTextareaCore.stories.ts +0 -247
- package/app/components/05.forms/input-textarea/stories/InputTextareaWithLabel.stories.ts +0 -458
- package/app/components/05.forms/input-textarea/tests/InputTextareaCore.spec.ts +0 -119
- package/app/components/05.forms/input-textarea/tests/InputTextareaWithLabel.spec.ts +0 -93
- package/app/components/05.forms/toggle-switch/ToggleSwitchWithLabelInline.vue +0 -81
- package/app/components/05.forms/toggle-switch/stories/ToggleSwitchCore.stories.ts +0 -251
- package/app/components/05.forms/toggle-switch/stories/ToggleSwitchWithLabel.stories.ts +0 -251
- package/app/components/05.forms/toggle-switch/stories/ToggleSwitchWithLabelInline.stories.ts +0 -283
- package/app/components/05.forms/toggle-switch/tests/ToggleSwitchCore.spec.ts +0 -101
- package/app/components/05.forms/toggle-switch/tests/ToggleSwitchWithLabel.spec.ts +0 -133
- package/app/components/05.forms/toggle-switch/tests/ToggleSwitchWithLabelInline.spec.ts +0 -66
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div>
|
|
3
|
-
<div
|
|
4
|
-
class="input-text-with-label"
|
|
5
|
-
:data-theme="theme"
|
|
6
|
-
:data-invalid="fieldHasError ? '' : null"
|
|
7
|
-
:class="[elementClasses, inputVariant, { dirty: isDirty }, { active: isActive }]"
|
|
8
|
-
>
|
|
9
|
-
<InputLabel
|
|
10
|
-
:id
|
|
11
|
-
:required
|
|
12
|
-
:input-variant
|
|
13
|
-
:field-has-error
|
|
14
|
-
:style-class-passthrough="['input-text-label']"
|
|
15
|
-
>
|
|
16
|
-
<template #textLabel>{{ label }}</template>
|
|
17
|
-
</InputLabel>
|
|
18
|
-
|
|
19
|
-
<InputDescription
|
|
20
|
-
v-if="inputVariant !== 'outlined'"
|
|
21
|
-
:description-id
|
|
22
|
-
:input-variant
|
|
23
|
-
:field-has-error="fieldHasError"
|
|
24
|
-
:style-class-passthrough="['input-text-description']"
|
|
25
|
-
>
|
|
26
|
-
<template v-if="slots.descriptionHtml" #descriptionHtml>
|
|
27
|
-
<slot name="descriptionHtml"></slot>
|
|
28
|
-
</template>
|
|
29
|
-
<template v-if="slots.descriptionText" #descriptionText>
|
|
30
|
-
<slot name="descriptionText"></slot>
|
|
31
|
-
</template>
|
|
32
|
-
</InputDescription>
|
|
33
|
-
|
|
34
|
-
<InputTextCore
|
|
35
|
-
:id
|
|
36
|
-
v-model="modelValue"
|
|
37
|
-
v-model:is-dirty="isDirty"
|
|
38
|
-
v-model:is-active="isActive"
|
|
39
|
-
:type
|
|
40
|
-
:inputmode
|
|
41
|
-
:maxlength
|
|
42
|
-
:min
|
|
43
|
-
:max
|
|
44
|
-
:name
|
|
45
|
-
:placeholder
|
|
46
|
-
:label
|
|
47
|
-
:error-message
|
|
48
|
-
:field-has-error
|
|
49
|
-
:required
|
|
50
|
-
:style-class-passthrough
|
|
51
|
-
:theme
|
|
52
|
-
:aria-describedby="ariaDescribedby()"
|
|
53
|
-
:input-variant
|
|
54
|
-
>
|
|
55
|
-
<template v-if="slots.left" #left>
|
|
56
|
-
<slot name="left"></slot>
|
|
57
|
-
</template>
|
|
58
|
-
<template v-if="slots.right" #right>
|
|
59
|
-
<slot name="right"></slot>
|
|
60
|
-
</template>
|
|
61
|
-
</InputTextCore>
|
|
62
|
-
|
|
63
|
-
<InputError :id="errorId" :error-message :show-error="fieldHasError" :is-detached="false" :input-variant />
|
|
64
|
-
</div>
|
|
65
|
-
|
|
66
|
-
<InputDescription
|
|
67
|
-
v-if="inputVariant === 'outlined'"
|
|
68
|
-
:description-id
|
|
69
|
-
:input-variant
|
|
70
|
-
:field-has-error="fieldHasError"
|
|
71
|
-
:style-class-passthrough="['input-text-description']"
|
|
72
|
-
>
|
|
73
|
-
<template v-if="slots.descriptionHtml" #descriptionHtml>
|
|
74
|
-
<slot name="descriptionHtml"></slot>
|
|
75
|
-
</template>
|
|
76
|
-
<template v-if="slots.descriptionText" #descriptionText>
|
|
77
|
-
<slot name="descriptionText"></slot>
|
|
78
|
-
</template>
|
|
79
|
-
</InputDescription>
|
|
80
|
-
</div>
|
|
81
|
-
</template>
|
|
82
|
-
|
|
83
|
-
<script setup lang="ts">
|
|
84
|
-
import type { InputTypesText, FormUiTheme, InputUiVariant, InputMode } from "~/types/forms/types.forms";
|
|
85
|
-
|
|
86
|
-
interface Props {
|
|
87
|
-
maxlength?: number;
|
|
88
|
-
/** Passed straight through to the native input — meaningful for type="date"/"number" etc. */
|
|
89
|
-
min?: string | number;
|
|
90
|
-
/** Passed straight through to the native input — meaningful for type="date"/"number" etc. */
|
|
91
|
-
max?: string | number;
|
|
92
|
-
type: InputTypesText;
|
|
93
|
-
inputmode?: InputMode;
|
|
94
|
-
name: string;
|
|
95
|
-
placeholder?: string;
|
|
96
|
-
label: string;
|
|
97
|
-
errorMessage: object | string;
|
|
98
|
-
fieldHasError?: boolean;
|
|
99
|
-
required?: boolean;
|
|
100
|
-
styleClassPassthrough?: string | string[];
|
|
101
|
-
theme?: FormUiTheme;
|
|
102
|
-
inputVariant?: InputUiVariant;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
const props = withDefaults(defineProps<Props>(), {
|
|
106
|
-
maxlength: 255,
|
|
107
|
-
min: undefined,
|
|
108
|
-
max: undefined,
|
|
109
|
-
inputmode: "text",
|
|
110
|
-
placeholder: "",
|
|
111
|
-
fieldHasError: false,
|
|
112
|
-
required: false,
|
|
113
|
-
styleClassPassthrough: () => [],
|
|
114
|
-
theme: "default",
|
|
115
|
-
inputVariant: "normal",
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
const slots = useSlots();
|
|
119
|
-
|
|
120
|
-
const FormUiTheme = computed(() => {
|
|
121
|
-
return props.fieldHasError ? "error" : props.theme;
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
const { id, errorId, descriptionId, ariaDescribedby } = useAriaDescribedById(
|
|
125
|
-
props.name,
|
|
126
|
-
toRef(props, "fieldHasError"),
|
|
127
|
-
slots
|
|
128
|
-
);
|
|
129
|
-
|
|
130
|
-
const modelValue = defineModel<string>({ required: true });
|
|
131
|
-
const isActive = ref<boolean>(false);
|
|
132
|
-
const isDirty = ref<boolean>(false);
|
|
133
|
-
|
|
134
|
-
const { elementClasses } = useStyleClassPassthrough(props.styleClassPassthrough);
|
|
135
|
-
|
|
136
|
-
const testDirty = () => {
|
|
137
|
-
const watchValue = modelValue.value ?? "";
|
|
138
|
-
|
|
139
|
-
if (!isDirty.value && typeof watchValue === "string" && watchValue.length > 0) {
|
|
140
|
-
isDirty.value = true;
|
|
141
|
-
}
|
|
142
|
-
};
|
|
143
|
-
|
|
144
|
-
onMounted(() => {
|
|
145
|
-
testDirty();
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
watch(
|
|
149
|
-
() => modelValue.value,
|
|
150
|
-
() => {
|
|
151
|
-
testDirty();
|
|
152
|
-
}
|
|
153
|
-
);
|
|
154
|
-
</script>
|
|
@@ -1,368 +0,0 @@
|
|
|
1
|
-
import type { Meta, StoryFn } from "@nuxtjs/storybook";
|
|
2
|
-
import { computed } from "vue";
|
|
3
|
-
import StorybookComponent from "../InputPasswordWithLabel.vue";
|
|
4
|
-
import type { FormUiTheme, InputUiVariant } from "~/types/forms/types.forms.d";
|
|
5
|
-
|
|
6
|
-
interface InputPasswordWithLabelStoryArgs {
|
|
7
|
-
modelValue: string;
|
|
8
|
-
maxlength: number;
|
|
9
|
-
name: string;
|
|
10
|
-
placeholder: string;
|
|
11
|
-
label: string;
|
|
12
|
-
errorMessage: string;
|
|
13
|
-
fieldHasError: boolean;
|
|
14
|
-
required: boolean;
|
|
15
|
-
theme: FormUiTheme;
|
|
16
|
-
inputVariant: InputUiVariant;
|
|
17
|
-
styleClassPassthrough: string[];
|
|
18
|
-
useDescriptionSlot: boolean;
|
|
19
|
-
useDescriptionHtmlSlot: boolean;
|
|
20
|
-
descriptionContent: string;
|
|
21
|
-
descriptionHtmlContent: string;
|
|
22
|
-
showPasswordText: string;
|
|
23
|
-
hidePasswordText: string;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export default {
|
|
27
|
-
title: "Components/Forms/Input Text/InputPasswordWithLabel",
|
|
28
|
-
component: StorybookComponent,
|
|
29
|
-
argTypes: {
|
|
30
|
-
// Model
|
|
31
|
-
modelValue: {
|
|
32
|
-
control: "text",
|
|
33
|
-
description: "The password value",
|
|
34
|
-
table: {
|
|
35
|
-
category: "Model",
|
|
36
|
-
},
|
|
37
|
-
},
|
|
38
|
-
|
|
39
|
-
// Basic Configuration
|
|
40
|
-
maxlength: {
|
|
41
|
-
control: { type: "number", min: 1, max: 1000 },
|
|
42
|
-
description: "Maximum length of password",
|
|
43
|
-
table: {
|
|
44
|
-
category: "Basic",
|
|
45
|
-
},
|
|
46
|
-
},
|
|
47
|
-
name: {
|
|
48
|
-
control: "text",
|
|
49
|
-
description: "Input name attribute",
|
|
50
|
-
table: {
|
|
51
|
-
category: "Basic",
|
|
52
|
-
},
|
|
53
|
-
},
|
|
54
|
-
placeholder: {
|
|
55
|
-
control: "text",
|
|
56
|
-
description: "Placeholder text",
|
|
57
|
-
table: {
|
|
58
|
-
category: "Basic",
|
|
59
|
-
},
|
|
60
|
-
},
|
|
61
|
-
label: {
|
|
62
|
-
control: "text",
|
|
63
|
-
description: "Input label",
|
|
64
|
-
table: {
|
|
65
|
-
category: "Basic",
|
|
66
|
-
},
|
|
67
|
-
},
|
|
68
|
-
|
|
69
|
-
// Validation
|
|
70
|
-
errorMessage: {
|
|
71
|
-
control: "text",
|
|
72
|
-
description: "Error message to display",
|
|
73
|
-
table: {
|
|
74
|
-
category: "Validation",
|
|
75
|
-
},
|
|
76
|
-
},
|
|
77
|
-
fieldHasError: {
|
|
78
|
-
control: "boolean",
|
|
79
|
-
description: "Whether field has validation error",
|
|
80
|
-
table: {
|
|
81
|
-
category: "Validation",
|
|
82
|
-
},
|
|
83
|
-
},
|
|
84
|
-
required: {
|
|
85
|
-
control: "boolean",
|
|
86
|
-
description: "Whether field is required",
|
|
87
|
-
table: {
|
|
88
|
-
category: "Validation",
|
|
89
|
-
},
|
|
90
|
-
},
|
|
91
|
-
|
|
92
|
-
// Styling
|
|
93
|
-
theme: {
|
|
94
|
-
control: { type: "select" },
|
|
95
|
-
options: ["primary", "secondary", "tertiary", "ghost", "error", "success", "warning"],
|
|
96
|
-
description: "Theme for styling",
|
|
97
|
-
table: {
|
|
98
|
-
category: "Styling",
|
|
99
|
-
},
|
|
100
|
-
},
|
|
101
|
-
inputVariant: {
|
|
102
|
-
control: { type: "select" },
|
|
103
|
-
options: ["normal", "outlined", "underlined"],
|
|
104
|
-
description: "Input variant style",
|
|
105
|
-
table: {
|
|
106
|
-
category: "Styling",
|
|
107
|
-
},
|
|
108
|
-
},
|
|
109
|
-
styleClassPassthrough: {
|
|
110
|
-
control: "object",
|
|
111
|
-
description: "Additional CSS classes",
|
|
112
|
-
table: {
|
|
113
|
-
category: "Styling",
|
|
114
|
-
},
|
|
115
|
-
},
|
|
116
|
-
|
|
117
|
-
// Slots
|
|
118
|
-
useDescriptionSlot: {
|
|
119
|
-
control: "boolean",
|
|
120
|
-
description: "Use description slot",
|
|
121
|
-
table: {
|
|
122
|
-
category: "Slots",
|
|
123
|
-
},
|
|
124
|
-
},
|
|
125
|
-
useDescriptionHtmlSlot: {
|
|
126
|
-
control: "boolean",
|
|
127
|
-
description: "Use description HTML slot",
|
|
128
|
-
table: {
|
|
129
|
-
category: "Slots",
|
|
130
|
-
},
|
|
131
|
-
},
|
|
132
|
-
descriptionContent: {
|
|
133
|
-
control: "text",
|
|
134
|
-
description: "Content for description slot",
|
|
135
|
-
table: {
|
|
136
|
-
category: "Slots",
|
|
137
|
-
},
|
|
138
|
-
},
|
|
139
|
-
descriptionHtmlContent: {
|
|
140
|
-
control: "text",
|
|
141
|
-
description: "HTML content for description slot",
|
|
142
|
-
table: {
|
|
143
|
-
category: "Slots",
|
|
144
|
-
},
|
|
145
|
-
},
|
|
146
|
-
|
|
147
|
-
// Localisation
|
|
148
|
-
showPasswordText: {
|
|
149
|
-
control: "text",
|
|
150
|
-
description: "Accessible name for the toggle button while the password is hidden",
|
|
151
|
-
table: {
|
|
152
|
-
category: "Localisation",
|
|
153
|
-
},
|
|
154
|
-
},
|
|
155
|
-
hidePasswordText: {
|
|
156
|
-
control: "text",
|
|
157
|
-
description: "Accessible name for the toggle button while the password is visible",
|
|
158
|
-
table: {
|
|
159
|
-
category: "Localisation",
|
|
160
|
-
},
|
|
161
|
-
},
|
|
162
|
-
},
|
|
163
|
-
args: {
|
|
164
|
-
modelValue: "",
|
|
165
|
-
maxlength: 255,
|
|
166
|
-
name: "password-input",
|
|
167
|
-
placeholder: "Enter your password...",
|
|
168
|
-
label: "Password",
|
|
169
|
-
errorMessage: "",
|
|
170
|
-
fieldHasError: false,
|
|
171
|
-
required: false,
|
|
172
|
-
theme: "default",
|
|
173
|
-
inputVariant: "normal",
|
|
174
|
-
styleClassPassthrough: [],
|
|
175
|
-
useDescriptionSlot: false,
|
|
176
|
-
useDescriptionHtmlSlot: false,
|
|
177
|
-
descriptionContent: "Password should be at least 8 characters long",
|
|
178
|
-
descriptionHtmlContent:
|
|
179
|
-
"Password must contain: <strong>8+ characters</strong>, <em>uppercase</em>, <em>lowercase</em>, <em>numbers</em>",
|
|
180
|
-
showPasswordText: "Show password",
|
|
181
|
-
hidePasswordText: "Hide password",
|
|
182
|
-
},
|
|
183
|
-
} as Meta<typeof StorybookComponent>;
|
|
184
|
-
|
|
185
|
-
// `args` is Storybook's own reactive object — bind to it directly (`args.x`) rather than
|
|
186
|
-
// destructuring it into local variables/refs, which would snapshot the values once at setup()
|
|
187
|
-
// and stop reflecting later Controls-panel changes. `componentArgs` strips the non-prop
|
|
188
|
-
// slot-toggle args so they don't leak onto the component as unknown attributes.
|
|
189
|
-
const Template: StoryFn<InputPasswordWithLabelStoryArgs> = (args) => ({
|
|
190
|
-
components: { StorybookComponent },
|
|
191
|
-
setup() {
|
|
192
|
-
const componentArgs = computed(() => {
|
|
193
|
-
const { useDescriptionSlot, useDescriptionHtmlSlot, descriptionContent, descriptionHtmlContent, ...rest } = args;
|
|
194
|
-
return rest;
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
return { args, componentArgs };
|
|
198
|
-
},
|
|
199
|
-
template: `
|
|
200
|
-
<StorybookComponent
|
|
201
|
-
v-model="args.modelValue"
|
|
202
|
-
v-bind="componentArgs"
|
|
203
|
-
>
|
|
204
|
-
<template v-if="args.useDescriptionSlot" #description>{{ args.descriptionContent }}</template>
|
|
205
|
-
<template v-if="args.useDescriptionHtmlSlot" #descriptionHtml v-html="args.descriptionHtmlContent"></template>
|
|
206
|
-
</StorybookComponent>
|
|
207
|
-
<div class="mt-4 text-sm text-gray-600">
|
|
208
|
-
Current value: {{ args.modelValue ? '•'.repeat(args.modelValue.length) : 'Empty' }}
|
|
209
|
-
</div>
|
|
210
|
-
`,
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
export const Default = Template.bind({});
|
|
214
|
-
Default.args = {};
|
|
215
|
-
|
|
216
|
-
export const WithDescription = Template.bind({});
|
|
217
|
-
WithDescription.args = {
|
|
218
|
-
useDescriptionSlot: true,
|
|
219
|
-
label: "Account Password",
|
|
220
|
-
placeholder: "Create a secure password",
|
|
221
|
-
};
|
|
222
|
-
|
|
223
|
-
export const WithHtmlDescription = Template.bind({});
|
|
224
|
-
WithHtmlDescription.args = {
|
|
225
|
-
useDescriptionHtmlSlot: true,
|
|
226
|
-
label: "New Password",
|
|
227
|
-
placeholder: "Enter new password",
|
|
228
|
-
};
|
|
229
|
-
|
|
230
|
-
export const Required = Template.bind({});
|
|
231
|
-
Required.args = {
|
|
232
|
-
required: true,
|
|
233
|
-
label: "Login Password",
|
|
234
|
-
placeholder: "Enter your password",
|
|
235
|
-
useDescriptionSlot: true,
|
|
236
|
-
descriptionContent: "This field is required for login",
|
|
237
|
-
};
|
|
238
|
-
|
|
239
|
-
export const WithError = Template.bind({});
|
|
240
|
-
WithError.args = {
|
|
241
|
-
fieldHasError: true,
|
|
242
|
-
errorMessage: "Password must be at least 8 characters long",
|
|
243
|
-
theme: "error",
|
|
244
|
-
label: "Password",
|
|
245
|
-
placeholder: "Enter password",
|
|
246
|
-
modelValue: "123", // Short password to show error
|
|
247
|
-
};
|
|
248
|
-
|
|
249
|
-
export const PasswordTooShort = Template.bind({});
|
|
250
|
-
PasswordTooShort.args = {
|
|
251
|
-
fieldHasError: true,
|
|
252
|
-
errorMessage: "Password is too weak",
|
|
253
|
-
theme: "error",
|
|
254
|
-
label: "Strong Password",
|
|
255
|
-
placeholder: "Enter a strong password",
|
|
256
|
-
modelValue: "weak",
|
|
257
|
-
useDescriptionHtmlSlot: true,
|
|
258
|
-
};
|
|
259
|
-
|
|
260
|
-
export const Outlined = Template.bind({});
|
|
261
|
-
Outlined.args = {
|
|
262
|
-
inputVariant: "outlined",
|
|
263
|
-
label: "Confirm Password",
|
|
264
|
-
placeholder: "Confirm your password",
|
|
265
|
-
};
|
|
266
|
-
|
|
267
|
-
export const Underlined = Template.bind({});
|
|
268
|
-
Underlined.args = {
|
|
269
|
-
inputVariant: "underlined",
|
|
270
|
-
label: "Master Password",
|
|
271
|
-
placeholder: "Enter master password",
|
|
272
|
-
};
|
|
273
|
-
|
|
274
|
-
export const LocalisedToggleText = Template.bind({});
|
|
275
|
-
LocalisedToggleText.storyName = "Localised Toggle Text";
|
|
276
|
-
LocalisedToggleText.args = {
|
|
277
|
-
label: "Mot de passe",
|
|
278
|
-
placeholder: "Saisissez votre mot de passe",
|
|
279
|
-
showPasswordText: "Afficher le mot de passe",
|
|
280
|
-
hidePasswordText: "Masquer le mot de passe",
|
|
281
|
-
};
|
|
282
|
-
|
|
283
|
-
export const AllVariants = Template.bind({});
|
|
284
|
-
AllVariants.storyName = "All Input Variants";
|
|
285
|
-
AllVariants.render = (args) => ({
|
|
286
|
-
components: { StorybookComponent },
|
|
287
|
-
setup() {
|
|
288
|
-
const variants: InputUiVariant[] = ["normal", "outlined", "underlined"];
|
|
289
|
-
const passwordValues = reactive(
|
|
290
|
-
variants.reduce(
|
|
291
|
-
(acc, variant) => ({
|
|
292
|
-
...acc,
|
|
293
|
-
[variant]: "",
|
|
294
|
-
}),
|
|
295
|
-
{} as Record<string, string>
|
|
296
|
-
)
|
|
297
|
-
);
|
|
298
|
-
|
|
299
|
-
return {
|
|
300
|
-
variants,
|
|
301
|
-
passwordValues,
|
|
302
|
-
args,
|
|
303
|
-
};
|
|
304
|
-
},
|
|
305
|
-
template: `
|
|
306
|
-
<div class="space-y-6">
|
|
307
|
-
<div v-for="variant in variants" :key="variant" class="space-y-2">
|
|
308
|
-
<h3 class="text-lg font-semibold capitalize">{{ variant }} Variant</h3>
|
|
309
|
-
<StorybookComponent
|
|
310
|
-
v-model="passwordValues[variant]"
|
|
311
|
-
v-bind="{ ...args, inputVariant: variant, label: variant + ' Password', name: variant + '-password' }"
|
|
312
|
-
/>
|
|
313
|
-
</div>
|
|
314
|
-
</div>
|
|
315
|
-
`,
|
|
316
|
-
});
|
|
317
|
-
|
|
318
|
-
export const SecurityFeatures = Template.bind({});
|
|
319
|
-
SecurityFeatures.storyName = "Password Security Demo";
|
|
320
|
-
SecurityFeatures.render = (args) => ({
|
|
321
|
-
components: { StorybookComponent },
|
|
322
|
-
setup() {
|
|
323
|
-
const passwords = reactive({
|
|
324
|
-
weak: "123",
|
|
325
|
-
medium: "password123",
|
|
326
|
-
strong: "MyStr0ng!Pass",
|
|
327
|
-
});
|
|
328
|
-
|
|
329
|
-
const getStrengthColor = (password: string) => {
|
|
330
|
-
if (password.length < 6) return "error";
|
|
331
|
-
if (password.length < 10) return "warning";
|
|
332
|
-
return "success";
|
|
333
|
-
};
|
|
334
|
-
|
|
335
|
-
return {
|
|
336
|
-
passwords,
|
|
337
|
-
getStrengthColor,
|
|
338
|
-
args,
|
|
339
|
-
};
|
|
340
|
-
},
|
|
341
|
-
template: `
|
|
342
|
-
<div class="space-y-6">
|
|
343
|
-
<div class="space-y-2">
|
|
344
|
-
<h3 class="text-lg font-semibold">Weak Password (Error State)</h3>
|
|
345
|
-
<StorybookComponent
|
|
346
|
-
v-model="passwords.weak"
|
|
347
|
-
v-bind="{ ...args, theme: getStrengthColor(passwords.weak), fieldHasError: passwords.weak.length < 6, errorMessage: 'Password too short', label: 'Weak Password', name: 'weak-password' }"
|
|
348
|
-
/>
|
|
349
|
-
</div>
|
|
350
|
-
|
|
351
|
-
<div class="space-y-2">
|
|
352
|
-
<h3 class="text-lg font-semibold">Medium Password (Warning State)</h3>
|
|
353
|
-
<StorybookComponent
|
|
354
|
-
v-model="passwords.medium"
|
|
355
|
-
v-bind="{ ...args, theme: getStrengthColor(passwords.medium), label: 'Medium Password', name: 'medium-password' }"
|
|
356
|
-
/>
|
|
357
|
-
</div>
|
|
358
|
-
|
|
359
|
-
<div class="space-y-2">
|
|
360
|
-
<h3 class="text-lg font-semibold">Strong Password (Success State)</h3>
|
|
361
|
-
<StorybookComponent
|
|
362
|
-
v-model="passwords.strong"
|
|
363
|
-
v-bind="{ ...args, theme: getStrengthColor(passwords.strong), label: 'Strong Password', name: 'strong-password' }"
|
|
364
|
-
/>
|
|
365
|
-
</div>
|
|
366
|
-
</div>
|
|
367
|
-
`,
|
|
368
|
-
});
|