@citizenplane/pimp 16.0.3 → 16.1.0

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 (61) hide show
  1. package/dist/pimp.es.js +313 -285
  2. package/dist/pimp.umd.js +21 -21
  3. package/dist/style.css +1 -1
  4. package/package.json +2 -1
  5. package/src/components/CpHeading.vue +4 -5
  6. package/src/components/CpText.vue +141 -0
  7. package/src/components/index.ts +2 -0
  8. package/src/stories/BaseInputLabel.stories.ts +36 -9
  9. package/src/stories/Colors.mdx +9 -0
  10. package/src/stories/Colors.stories.ts +177 -0
  11. package/src/stories/CpAccordion.stories.ts +187 -158
  12. package/src/stories/CpAccordionGroup.stories.ts +50 -94
  13. package/src/stories/CpAirlineLogo.stories.ts +49 -28
  14. package/src/stories/CpAlert.stories.ts +195 -158
  15. package/src/stories/CpBadge.stories.ts +259 -193
  16. package/src/stories/CpButton.stories.ts +257 -426
  17. package/src/stories/CpCheckbox.stories.ts +101 -29
  18. package/src/stories/CpContextualMenu.stories.ts +9 -8
  19. package/src/stories/CpDate.stories.ts +52 -25
  20. package/src/stories/CpDatepicker.stories.ts +57 -88
  21. package/src/stories/CpDialog.stories.ts +22 -1
  22. package/src/stories/CpHeading.stories.ts +59 -20
  23. package/src/stories/CpIcon.stories.ts +98 -31
  24. package/src/stories/CpInput.stories.ts +142 -67
  25. package/src/stories/CpItemActions.stories.ts +22 -27
  26. package/src/stories/CpLoader.stories.ts +54 -6
  27. package/src/stories/CpMenuItem.stories.ts +52 -26
  28. package/src/stories/CpMultiselect.stories.ts +52 -71
  29. package/src/stories/CpPartnerBadge.stories.ts +53 -74
  30. package/src/stories/CpRadio.stories.ts +44 -48
  31. package/src/stories/CpRadioGroup.stories.ts +46 -39
  32. package/src/stories/CpSelect.stories.ts +98 -39
  33. package/src/stories/CpSelectMenu.stories.ts +49 -57
  34. package/src/stories/CpSelectableButton.stories.ts +170 -81
  35. package/src/stories/CpSwitch.stories.ts +135 -133
  36. package/src/stories/CpTable.stories.ts +54 -1
  37. package/src/stories/CpTableEmptyState.stories.ts +11 -7
  38. package/src/stories/CpTabs.stories.ts +22 -4
  39. package/src/stories/CpTelInput.stories.ts +25 -23
  40. package/src/stories/CpText.stories.ts +131 -0
  41. package/src/stories/CpTextarea.stories.ts +59 -23
  42. package/src/stories/CpToast.stories.ts +53 -103
  43. package/src/stories/CpTooltip.stories.ts +82 -77
  44. package/src/stories/CpTransitionCounter.stories.ts +4 -0
  45. package/src/stories/CpTransitionExpand.stories.ts +11 -6
  46. package/src/stories/CpTransitionListItems.stories.ts +5 -0
  47. package/src/stories/CpTransitionSize.stories.ts +8 -0
  48. package/src/stories/CpTransitionSlide.stories.ts +4 -0
  49. package/src/stories/CpTransitionTabContent.stories.ts +4 -0
  50. package/src/stories/Dimensions.mdx +9 -0
  51. package/src/stories/Dimensions.stories.ts +119 -0
  52. package/src/stories/Easings.mdx +9 -0
  53. package/src/stories/Easings.stories.ts +101 -0
  54. package/src/stories/FocusRings.mdx +9 -0
  55. package/src/stories/FocusRings.stories.ts +74 -0
  56. package/src/stories/Shadows.mdx +9 -0
  57. package/src/stories/Shadows.stories.ts +100 -0
  58. package/src/stories/Typography.mdx +9 -0
  59. package/src/stories/Typography.stories.ts +181 -0
  60. package/src/stories/documentationStyles.ts +2 -10
  61. package/src/stories/tokenUtils.ts +259 -0
@@ -1,19 +1,35 @@
1
- import type { Meta, StoryObj } from '@storybook/vue3'
1
+ import type { Args, Meta, StoryObj } from '@storybook/vue3'
2
2
 
3
3
  import CpHeading from '@/components/CpHeading.vue'
4
4
 
5
+ import { HeadingLevels } from '@/constants'
6
+ import { docLabelStyle } from '@/stories/documentationStyles'
7
+
8
+ const headingLevels = Object.values(HeadingLevels)
9
+ const headingSizes = [100, 200, 300, 400, 500, 600, 700, 800, 900] as const
10
+
11
+ const headingRowStyle = 'display: grid; grid-template-columns: 90px 1fr; gap: 16px; align-items: center;'
12
+
5
13
  const meta = {
6
- title: 'Visual/CpHeading',
14
+ title: 'Atoms/CpHeading',
7
15
  component: CpHeading,
16
+ tags: ['deprecated'],
17
+ parameters: {
18
+ docs: {
19
+ description: {
20
+ component: '> ⚠️ **Deprecated.** `CpHeading` is kept for backwards compatibility only. Use CpText instead.',
21
+ },
22
+ },
23
+ },
8
24
  argTypes: {
9
25
  headingLevel: {
10
26
  control: 'select',
11
- options: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'],
27
+ options: headingLevels,
12
28
  description: 'HTML heading level',
13
29
  },
14
30
  size: {
15
31
  control: 'select',
16
- options: [100, 200, 300, 400, 500, 600, 700, 800, 900],
32
+ options: headingSizes,
17
33
  description: 'Size variant of the heading',
18
34
  },
19
35
  },
@@ -22,46 +38,69 @@ const meta = {
22
38
  export default meta
23
39
  type Story = StoryObj<typeof meta>
24
40
 
41
+ /**
42
+ * @deprecated Default heading. Prefer native `<h1>`–`<h6>` styled with typography CSS variables.
43
+ */
25
44
  export const Default: Story = {
26
45
  args: {
27
- headingLevel: 'h1',
46
+ headingLevel: HeadingLevels.H1,
28
47
  size: 500,
29
48
  },
30
- render: (args) => ({
49
+ render: (args: Args) => ({
31
50
  components: { CpHeading },
32
51
  setup() {
33
52
  return { args }
34
53
  },
35
54
  template: `
36
55
  <div style="padding: 20px;">
37
- <CpHeading v-bind="args">
38
- Default Heading
39
- </CpHeading>
56
+ <CpHeading v-bind="args">Default Heading</CpHeading>
40
57
  </div>
41
58
  `,
42
59
  }),
43
60
  }
44
61
 
45
- export const AllSizes: Story = {
62
+ /* -------------------------------------------------------------------------- */
63
+ /* Sizes */
64
+ /* -------------------------------------------------------------------------- */
65
+
66
+ /**
67
+ * @deprecated All sizes stacked from `100` (smallest) to `900` (largest). The
68
+ * visual size is decoupled from the semantic HTML level.
69
+ */
70
+ export const Sizes: Story = {
71
+ parameters: { controls: { disable: true } },
46
72
  render: () => ({
47
73
  components: { CpHeading },
74
+ setup() {
75
+ return {
76
+ headingSizes,
77
+ headingRowStyle,
78
+ docLabelStyle,
79
+ }
80
+ },
48
81
  template: `
49
82
  <div style="padding: 20px; display: flex; flex-direction: column; gap: 16px;">
50
- <CpHeading size="100">Size 100 Heading</CpHeading>
51
- <CpHeading size="200">Size 200 Heading</CpHeading>
52
- <CpHeading size="300">Size 300 Heading</CpHeading>
53
- <CpHeading size="400">Size 400 Heading</CpHeading>
54
- <CpHeading size="500">Size 500 Heading</CpHeading>
55
- <CpHeading size="600">Size 600 Heading</CpHeading>
56
- <CpHeading size="700">Size 700 Heading</CpHeading>
57
- <CpHeading size="800">Size 800 Heading</CpHeading>
58
- <CpHeading size="900">Size 900 Heading</CpHeading>
83
+ <div v-for="size in headingSizes" :key="size" :style="headingRowStyle">
84
+ <span :style="docLabelStyle">{{ size }}</span>
85
+ <CpHeading :size="size">Size {{ size }} Heading</CpHeading>
86
+ </div>
59
87
  </div>
60
88
  `,
61
89
  }),
62
90
  }
63
91
 
64
- export const SizeAndLevelCombination: Story = {
92
+ /* -------------------------------------------------------------------------- */
93
+ /* Semantic level */
94
+ /* -------------------------------------------------------------------------- */
95
+
96
+ /**
97
+ * @deprecated The recommended pairing between semantic HTML level and visual
98
+ * size — but any combination is valid. Picking the right `headingLevel`
99
+ * matters for accessibility and document outline, while `size` controls the
100
+ * look.
101
+ */
102
+ export const LevelAndSizePairing: Story = {
103
+ parameters: { controls: { disable: true } },
65
104
  render: () => ({
66
105
  components: { CpHeading },
67
106
  template: `
@@ -1,8 +1,10 @@
1
- import type { Meta, StoryObj } from '@storybook/vue3'
1
+ import type { Args, Meta, StoryObj } from '@storybook/vue3'
2
2
 
3
3
  import CpIcon from '@/components/CpIcon.vue'
4
4
 
5
5
  import { CustomCpIcons } from '@/constants'
6
+ import { docCellStyle, docLabelStyle, docRowWrapStyle } from '@/stories/documentationStyles'
7
+ import { copyableClass, copyableCopiedClass, useCopier } from '@/stories/tokenUtils'
6
8
 
7
9
  const iconsOptions = [
8
10
  ...Object.keys(CustomCpIcons),
@@ -29,9 +31,12 @@ const iconsOptions = [
29
31
  'invalid-icon-name',
30
32
  ].sort()
31
33
 
34
+ const iconSizes = [16, 24, 32, 48] as const
35
+
32
36
  const meta = {
33
- title: 'Visual/CpIcon',
37
+ title: 'Foundations/CpIcon',
34
38
  component: CpIcon,
39
+ tags: ['!dev'],
35
40
  argTypes: {
36
41
  type: {
37
42
  control: 'select',
@@ -52,13 +57,16 @@ const meta = {
52
57
  export default meta
53
58
  type Story = StoryObj<typeof meta>
54
59
 
60
+ /**
61
+ * Default icon. Use the controls to experiment with each prop in isolation.
62
+ */
55
63
  export const Default: Story = {
56
64
  args: {
57
65
  type: 'check',
58
66
  size: 24,
59
67
  tag: 'i',
60
68
  },
61
- render: (args) => ({
69
+ render: (args: Args) => ({
62
70
  components: { CpIcon },
63
71
  setup() {
64
72
  return { args }
@@ -71,42 +79,76 @@ export const Default: Story = {
71
79
  }),
72
80
  }
73
81
 
74
- export const DifferentSizes: Story = {
75
- args: {
76
- type: 'check',
77
- },
78
- render: (args) => ({
82
+ /* -------------------------------------------------------------------------- */
83
+ /* Sizes */
84
+ /* -------------------------------------------------------------------------- */
85
+
86
+ /**
87
+ * The icon scales freely through the `size` prop (pixel value). Below are
88
+ * the most common sizes used across the design system.
89
+ */
90
+ export const Sizes: Story = {
91
+ args: { type: 'check' },
92
+ parameters: { controls: { disable: true } },
93
+ render: () => ({
79
94
  components: { CpIcon },
80
95
  setup() {
81
- return { args }
96
+ return { iconSizes, docCellStyle, docLabelStyle, docRowWrapStyle }
82
97
  },
83
98
  template: `
84
- <div style="padding: 20px; display: flex; align-items: center; gap: 16px;">
85
- <CpIcon v-bind="{ ...args, size: 16 }" />
86
- <CpIcon v-bind="{ ...args, size: 24 }" />
87
- <CpIcon v-bind="{ ...args, size: 32 }" />
88
- <CpIcon v-bind="{ ...args, size: 48 }" />
99
+ <div :style="docRowWrapStyle">
100
+ <div v-for="size in iconSizes" :key="size" :style="docCellStyle">
101
+ <span :style="docLabelStyle">{{ size }}px</span>
102
+ <CpIcon type="check" :size="size" />
103
+ </div>
89
104
  </div>
90
105
  `,
91
106
  }),
92
107
  }
93
108
 
109
+ /* -------------------------------------------------------------------------- */
110
+ /* Catalog */
111
+ /* -------------------------------------------------------------------------- */
112
+
113
+ /**
114
+ * Every available icon rendered on a neutral surface. Hover a cell to see
115
+ * the icon name.
116
+ */
94
117
  export const AllIcons: Story = {
95
- args: {
96
- type: 'check',
97
- },
118
+ args: { type: 'check' },
119
+ parameters: { controls: { disable: true } },
98
120
  render: () => ({
99
121
  components: { CpIcon },
100
122
  setup() {
101
- return { icons: iconsOptions }
123
+ const { copiedKey, copy } = useCopier()
124
+ return {
125
+ icons: iconsOptions,
126
+ copiedKey,
127
+ copy,
128
+ copyClass: copyableClass,
129
+ copiedClass: copyableCopiedClass,
130
+ }
102
131
  },
103
132
  template: `
104
133
  <div style="padding: 20px; display: grid; grid-template-columns: repeat(4, minmax(100px, 3fr)); gap: 16px;">
105
- <div v-for="icon in icons" style="display: flex; flex-direction: column; align-items: center;border: 1px solid #eee;border-radius: 4px;">
106
- <span style="font-size: 12px; padding: 8px; word-break: break-word;">{{ icon }}</span>
107
- <hr style="width: 100%; background-color: #eee; height: 1px; border: 0" />
134
+ <div
135
+ v-for="icon in icons"
136
+ :key="icon"
137
+ style="display: flex; flex-direction: column; align-items: center; border: 1px solid #eee; border-radius: 4px;"
138
+ >
139
+ <span
140
+ style="font-size: 12px; padding: 8px; word-break: break-word;"
141
+ :class="[copyClass, copiedKey === icon ? copiedClass : '']"
142
+ role="button"
143
+ tabindex="0"
144
+ :title="'Click to copy ' + icon"
145
+ @click="copy(icon)"
146
+ @keydown.enter.prevent="copy(icon)"
147
+ @keydown.space.prevent="copy(icon)"
148
+ >{{ copiedKey === icon ? 'Copied!' : icon }}</span>
149
+ <hr style="width: 100%; background-color: #eee; height: 1px; border: 0;" />
108
150
  <div style="padding: 20px;">
109
- <CpIcon :key="icon" :type="icon" />
151
+ <CpIcon :type="icon" />
110
152
  </div>
111
153
  </div>
112
154
  </div>
@@ -114,22 +156,47 @@ export const AllIcons: Story = {
114
156
  }),
115
157
  }
116
158
 
159
+ /**
160
+ * Same catalog rendered on a colored surface to verify that icons inherit
161
+ * the current text color.
162
+ */
117
163
  export const AllIconsColored: Story = {
118
- args: {
119
- type: 'check',
120
- },
164
+ args: { type: 'check' },
165
+ parameters: { controls: { disable: true } },
121
166
  render: () => ({
122
167
  components: { CpIcon },
123
168
  setup() {
124
- return { icons: iconsOptions }
169
+ const { copiedKey, copy } = useCopier()
170
+ return {
171
+ icons: iconsOptions,
172
+ copiedKey,
173
+ copy,
174
+ copyClass: copyableClass,
175
+ copiedClass: copyableCopiedClass,
176
+ }
125
177
  },
126
178
  template: `
127
179
  <div style="padding: 20px; display: grid; grid-template-columns: repeat(4, minmax(100px, 3fr)); gap: 16px;">
128
- <div v-for="icon in icons" style="display: flex; flex-direction: column; align-items: center;border: 1px solid #eee;border-radius: 4px;">
129
- <span style="font-size: 12px; padding: 8px; word-break: break-word;">{{ icon }}</span>
130
- <hr style="width: 100%; background-color: #eee; height: 1px; border: 0" />
131
- <div style="padding: 20px; color: green;background-color: #f0fdf4;width: 100%;display: flex;align-items: center;justify-content: center;height: 100%;">
132
- <CpIcon :key="icon" :type="icon" />
180
+ <div
181
+ v-for="icon in icons"
182
+ :key="icon"
183
+ style="display: flex; flex-direction: column; align-items: center; border: 1px solid #eee; border-radius: 4px;"
184
+ >
185
+ <span
186
+ style="font-size: 12px; padding: 8px; word-break: break-word;"
187
+ :class="[copyClass, copiedKey === icon ? copiedClass : '']"
188
+ role="button"
189
+ tabindex="0"
190
+ :title="'Click to copy ' + icon"
191
+ @click="copy(icon)"
192
+ @keydown.enter.prevent="copy(icon)"
193
+ @keydown.space.prevent="copy(icon)"
194
+ >{{ copiedKey === icon ? 'Copied!' : icon }}</span>
195
+ <hr style="width: 100%; background-color: #eee; height: 1px; border: 0;" />
196
+ <div
197
+ style="padding: 20px; color: green; background-color: #f0fdf4; width: 100%; display: flex; align-items: center; justify-content: center; height: 100%;"
198
+ >
199
+ <CpIcon :type="icon" />
133
200
  </div>
134
201
  </div>
135
202
  </div>
@@ -5,8 +5,15 @@ import type { Meta, StoryObj } from '@storybook/vue3'
5
5
  import CpIcon from '@/components/CpIcon.vue'
6
6
  import CpInput from '@/components/CpInput.vue'
7
7
 
8
+ import { docCellStyle, docLabelStyle, docRowColumnStyle } from '@/stories/documentationStyles'
9
+
10
+ const inputSizes = ['sm', 'md', 'lg'] as const
11
+ const inputTypes = ['text', 'email', 'password', 'number', 'tel', 'url'] as const
12
+
13
+ const inputStackStyle = `${docCellStyle} width: 100%; max-width: 360px;`
14
+
8
15
  const meta = {
9
- title: 'Form/CpInput',
16
+ title: 'Atoms/CpInput',
10
17
  component: CpInput,
11
18
  argTypes: {
12
19
  modelValue: {
@@ -15,12 +22,12 @@ const meta = {
15
22
  },
16
23
  size: {
17
24
  control: 'select',
18
- options: ['sm', 'md', 'lg'],
25
+ options: inputSizes,
19
26
  description: 'The size of the input',
20
27
  },
21
28
  type: {
22
29
  control: 'select',
23
- options: ['text', 'email', 'password', 'number', 'tel', 'url'],
30
+ options: inputTypes,
24
31
  description: 'The type of input',
25
32
  },
26
33
  label: {
@@ -57,6 +64,9 @@ const meta = {
57
64
  export default meta
58
65
  type Story = StoryObj<typeof meta>
59
66
 
67
+ /**
68
+ * Default input. Use the controls to experiment with each prop in isolation.
69
+ */
60
70
  export const Default: Story = {
61
71
  args: {
62
72
  label: 'Input Label',
@@ -80,64 +90,127 @@ export const Default: Story = {
80
90
  },
81
91
  template: `
82
92
  <div style="max-width: 400px; padding: 20px;">
83
- <CpInput
84
- v-model="value"
85
- v-bind="args"
86
- />
93
+ <CpInput v-model="value" v-bind="args" />
87
94
  </div>
88
95
  `,
89
96
  }),
90
97
  }
91
98
 
92
- export const WithError: Story = {
93
- args: {
94
- ...Default.args,
95
- isInvalid: true,
96
- errorMessage: 'This field is required',
97
- },
98
- }
99
+ /* -------------------------------------------------------------------------- */
100
+ /* Sizes */
101
+ /* -------------------------------------------------------------------------- */
99
102
 
100
- export const Required: Story = {
101
- args: {
102
- ...Default.args,
103
- required: true,
104
- },
103
+ /**
104
+ * All sizes rendered side by side, from `sm` to `lg`.
105
+ */
106
+ export const Sizes: Story = {
107
+ parameters: { controls: { disable: true } },
108
+ render: () => ({
109
+ components: { CpInput },
110
+ setup() {
111
+ const value = ref('')
112
+ return { value, inputSizes, docRowColumnStyle, inputStackStyle, docLabelStyle }
113
+ },
114
+ template: `
115
+ <div :style="docRowColumnStyle">
116
+ <div v-for="size in inputSizes" :key="size" :style="inputStackStyle">
117
+ <span :style="docLabelStyle">{{ size }}</span>
118
+ <CpInput v-model="value" :size="size" label="Input label" placeholder="Enter text here" />
119
+ </div>
120
+ </div>
121
+ `,
122
+ }),
105
123
  }
106
124
 
107
- export const Disabled: Story = {
108
- args: {
109
- ...Default.args,
110
- disabled: true,
111
- },
112
- }
125
+ /* -------------------------------------------------------------------------- */
126
+ /* States */
127
+ /* -------------------------------------------------------------------------- */
113
128
 
114
- export const Email: Story = {
115
- args: {
116
- ...Default.args,
117
- type: 'email',
118
- label: 'Email Address',
119
- placeholder: 'Enter your email',
120
- },
129
+ /**
130
+ * Default, required, disabled and invalid states compared side by side.
131
+ */
132
+ export const States: Story = {
133
+ parameters: { controls: { disable: true } },
134
+ render: () => ({
135
+ components: { CpInput },
136
+ setup() {
137
+ const value = ref('')
138
+ return { value, docRowColumnStyle, inputStackStyle, docLabelStyle }
139
+ },
140
+ template: `
141
+ <div :style="docRowColumnStyle">
142
+ <div :style="inputStackStyle">
143
+ <span :style="docLabelStyle">Default</span>
144
+ <CpInput v-model="value" label="Input label" placeholder="Enter text here" />
145
+ </div>
146
+ <div :style="inputStackStyle">
147
+ <span :style="docLabelStyle">Required</span>
148
+ <CpInput v-model="value" label="Input label" placeholder="Enter text here" :required="true" />
149
+ </div>
150
+ <div :style="inputStackStyle">
151
+ <span :style="docLabelStyle">Disabled</span>
152
+ <CpInput v-model="value" label="Input label" placeholder="Enter text here" :disabled="true" />
153
+ </div>
154
+ <div :style="inputStackStyle">
155
+ <span :style="docLabelStyle">Invalid</span>
156
+ <CpInput
157
+ v-model="value"
158
+ label="Input label"
159
+ placeholder="Enter text here"
160
+ :is-invalid="true"
161
+ error-message="This field is required"
162
+ />
163
+ </div>
164
+ </div>
165
+ `,
166
+ }),
121
167
  }
122
168
 
123
- export const Password: Story = {
124
- args: {
125
- ...Default.args,
126
- type: 'password',
127
- label: 'Password',
128
- placeholder: 'Enter your password',
129
- },
130
- }
169
+ /* -------------------------------------------------------------------------- */
170
+ /* Types */
171
+ /* -------------------------------------------------------------------------- */
131
172
 
132
- export const Number: Story = {
133
- args: {
134
- ...Default.args,
135
- type: 'number',
136
- label: 'Amount',
137
- placeholder: 'Enter amount',
138
- },
173
+ /**
174
+ * Native HTML input types wired to the appropriate keyboard and validation.
175
+ */
176
+ export const Types: Story = {
177
+ parameters: { controls: { disable: true } },
178
+ render: () => ({
179
+ components: { CpInput },
180
+ setup() {
181
+ const value = ref('')
182
+ return { value, docRowColumnStyle, inputStackStyle, docLabelStyle }
183
+ },
184
+ template: `
185
+ <div :style="docRowColumnStyle">
186
+ <div :style="inputStackStyle">
187
+ <span :style="docLabelStyle">text</span>
188
+ <CpInput v-model="value" type="text" label="Name" placeholder="Your full name" />
189
+ </div>
190
+ <div :style="inputStackStyle">
191
+ <span :style="docLabelStyle">email</span>
192
+ <CpInput v-model="value" type="email" label="Email address" placeholder="you@example.com" />
193
+ </div>
194
+ <div :style="inputStackStyle">
195
+ <span :style="docLabelStyle">password</span>
196
+ <CpInput v-model="value" type="password" label="Password" placeholder="Enter your password" />
197
+ </div>
198
+ <div :style="inputStackStyle">
199
+ <span :style="docLabelStyle">number</span>
200
+ <CpInput v-model="value" type="number" label="Amount" placeholder="Enter amount" />
201
+ </div>
202
+ </div>
203
+ `,
204
+ }),
139
205
  }
140
206
 
207
+ /* -------------------------------------------------------------------------- */
208
+ /* Help & tooltip */
209
+ /* -------------------------------------------------------------------------- */
210
+
211
+ /**
212
+ * Attach a help text under the field and a tooltip next to the label.
213
+ */
141
214
  export const HelpAndTooltip: Story = {
142
215
  args: {
143
216
  ...Default.args,
@@ -146,6 +219,13 @@ export const HelpAndTooltip: Story = {
146
219
  },
147
220
  }
148
221
 
222
+ /* -------------------------------------------------------------------------- */
223
+ /* Variants */
224
+ /* -------------------------------------------------------------------------- */
225
+
226
+ /**
227
+ * Search input with a built-in icon.
228
+ */
149
229
  export const Search: Story = {
150
230
  args: {
151
231
  ...Default.args,
@@ -153,6 +233,9 @@ export const Search: Story = {
153
233
  },
154
234
  }
155
235
 
236
+ /**
237
+ * Input masked with a pattern (phone numbers, card numbers, etc.).
238
+ */
156
239
  export const Mask: Story = {
157
240
  args: {
158
241
  ...Default.args,
@@ -161,10 +244,11 @@ export const Mask: Story = {
161
244
  },
162
245
  }
163
246
 
247
+ /**
248
+ * Slot icons on either side of the input.
249
+ */
164
250
  export const WithIcon: Story = {
165
- args: {
166
- ...Default.args,
167
- },
251
+ args: { ...Default.args },
168
252
  render: (args) => ({
169
253
  components: { CpInput, CpIcon },
170
254
  setup() {
@@ -173,10 +257,7 @@ export const WithIcon: Story = {
173
257
  },
174
258
  template: `
175
259
  <div style="max-width: 400px; padding: 20px;">
176
- <CpInput
177
- v-model="value"
178
- v-bind="args"
179
- >
260
+ <CpInput v-model="value" v-bind="args">
180
261
  <template #trailing-icon>
181
262
  <CpIcon type="users" />
182
263
  </template>
@@ -189,10 +270,12 @@ export const WithIcon: Story = {
189
270
  }),
190
271
  }
191
272
 
273
+ /**
274
+ * Use the icon slots to render unit labels, perfect for currencies or
275
+ * measurement units.
276
+ */
192
277
  export const WithUnits: Story = {
193
- args: {
194
- ...Default.args,
195
- },
278
+ args: { ...Default.args },
196
279
  render: (args) => ({
197
280
  components: { CpInput },
198
281
  setup() {
@@ -201,11 +284,7 @@ export const WithUnits: Story = {
201
284
  },
202
285
  template: `
203
286
  <div style="display: flex; flex-direction: column; gap: 10px; max-width: 400px; padding: 20px;">
204
- <CpInput
205
- v-model="value"
206
- mask="####"
207
- v-bind="args"
208
- >
287
+ <CpInput v-model="value" mask="####" v-bind="args">
209
288
  <template #leading-icon>
210
289
 
211
290
  </template>
@@ -214,11 +293,7 @@ export const WithUnits: Story = {
214
293
  </template>
215
294
  </CpInput>
216
295
 
217
- <CpInput
218
- v-model="value"
219
- mask="####"
220
- v-bind="args"
221
- >
296
+ <CpInput v-model="value" mask="####" v-bind="args">
222
297
  <template #leading-icon>
223
298
  kg
224
299
  </template>