@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
@@ -4,8 +4,10 @@ import type { Meta, StoryObj } from '@storybook/vue3'
4
4
 
5
5
  import CpRadio from '@/components/CpRadio.vue'
6
6
 
7
+ const radioColors = ['accent', 'blue'] as const
8
+
7
9
  const meta = {
8
- title: 'Form/CpRadio',
10
+ title: 'Atoms/CpRadio',
9
11
  component: CpRadio,
10
12
  argTypes: {
11
13
  modelValue: {
@@ -22,7 +24,7 @@ const meta = {
22
24
  },
23
25
  color: {
24
26
  control: 'select',
25
- options: ['accent', 'blue'],
27
+ options: radioColors,
26
28
  description: 'Color variant of the radio',
27
29
  },
28
30
  autofocus: {
@@ -41,6 +43,23 @@ const sampleOptions = [
41
43
  { value: 'option3', label: 'Option 3' },
42
44
  ]
43
45
 
46
+ const wrappedRender = (args: { modelValue?: string }) => ({
47
+ components: { CpRadio },
48
+ setup() {
49
+ const value = ref(args.modelValue)
50
+ return { args, value }
51
+ },
52
+ template: `
53
+ <div style="min-width: 400px; padding: 20px;">
54
+ <CpRadio v-model="value" v-bind="args" />
55
+ </div>
56
+ `,
57
+ })
58
+
59
+ /**
60
+ * Default radio group with three plain options. Use the controls to
61
+ * experiment with each prop in isolation.
62
+ */
44
63
  export const Default: Story = {
45
64
  args: {
46
65
  modelValue: 'option1',
@@ -49,23 +68,13 @@ export const Default: Story = {
49
68
  color: 'accent',
50
69
  autofocus: false,
51
70
  },
52
- render: (args) => ({
53
- components: { CpRadio },
54
- setup() {
55
- const value = ref(args.modelValue)
56
- return { args, value }
57
- },
58
- template: `
59
- <div style="min-width: 400px; padding: 20px;">
60
- <CpRadio
61
- v-model="value"
62
- v-bind="args"
63
- />
64
- </div>
65
- `,
66
- }),
71
+ render: wrappedRender,
67
72
  }
68
73
 
74
+ /**
75
+ * Each option can provide a secondary `description` displayed under the
76
+ * label — great for presenting plans or multi-line choices.
77
+ */
69
78
  export const WithDescriptions: Story = {
70
79
  args: {
71
80
  ...Default.args,
@@ -77,6 +86,10 @@ export const WithDescriptions: Story = {
77
86
  },
78
87
  }
79
88
 
89
+ /**
90
+ * Append a right-aligned piece of information through `additionalData`
91
+ * (e.g. price, unit, shortcut).
92
+ */
80
93
  export const WithAdditionalData: Story = {
81
94
  args: {
82
95
  ...Default.args,
@@ -88,7 +101,11 @@ export const WithAdditionalData: Story = {
88
101
  },
89
102
  }
90
103
 
91
- export const WithDisabledOptions: Story = {
104
+ /**
105
+ * Disable individual options using their `disabled` flag — the value stays
106
+ * selectable on other rows.
107
+ */
108
+ export const WithDisabledOption: Story = {
92
109
  args: {
93
110
  ...Default.args,
94
111
  options: [
@@ -97,23 +114,13 @@ export const WithDisabledOptions: Story = {
97
114
  { value: 'option3', label: 'Option 3' },
98
115
  ],
99
116
  },
100
- render: (args) => ({
101
- components: { CpRadio },
102
- setup() {
103
- const value = ref(args.modelValue)
104
- return { args, value }
105
- },
106
- template: `
107
- <div style="min-width: 400px; padding: 20px;">
108
- <CpRadio
109
- v-model="value"
110
- v-bind="args"
111
- />
112
- </div>
113
- `,
114
- }),
117
+ render: wrappedRender,
115
118
  }
116
119
 
120
+ /**
121
+ * A disabled option can still be the current value — useful when the user
122
+ * cannot change a pre-selected choice.
123
+ */
117
124
  export const CheckedDisabled: Story = {
118
125
  args: {
119
126
  ...Default.args,
@@ -124,23 +131,12 @@ export const CheckedDisabled: Story = {
124
131
  ],
125
132
  modelValue: 'option1',
126
133
  },
127
- render: (args) => ({
128
- components: { CpRadio },
129
- setup() {
130
- const value = ref(args.modelValue)
131
- return { args, value }
132
- },
133
- template: `
134
- <div style="min-width: 400px; padding: 20px;">
135
- <CpRadio
136
- v-model="value"
137
- v-bind="args"
138
- />
139
- </div>
140
- `,
141
- }),
134
+ render: wrappedRender,
142
135
  }
143
136
 
137
+ /**
138
+ * Combine `description` and `additionalData` for rich option rows.
139
+ */
144
140
  export const ComplexOptions: Story = {
145
141
  args: {
146
142
  ...Default.args,
@@ -4,8 +4,11 @@ import type { Meta, StoryObj } from '@storybook/vue3'
4
4
 
5
5
  import CpRadioGroup from '@/components/CpRadioGroup.vue'
6
6
 
7
+ const radioGroupSizes = ['md', 'lg'] as const
8
+ const radioGroupDirections = ['vertical', 'horizontal'] as const
9
+
7
10
  const meta = {
8
- title: 'Form/CpRadioGroup',
11
+ title: 'Molecules/CpRadioGroup',
9
12
  component: CpRadioGroup,
10
13
  argTypes: {
11
14
  modelValue: {
@@ -26,7 +29,7 @@ const meta = {
26
29
  },
27
30
  direction: {
28
31
  control: 'select',
29
- options: ['vertical', 'horizontal'],
32
+ options: radioGroupDirections,
30
33
  description: 'Stack options vertically or place them in a row',
31
34
  },
32
35
  groupLabel: {
@@ -43,7 +46,7 @@ const meta = {
43
46
  },
44
47
  size: {
45
48
  control: 'select',
46
- options: ['md', 'lg'],
49
+ options: radioGroupSizes,
47
50
  description: 'The size of the radio',
48
51
  },
49
52
  },
@@ -58,6 +61,23 @@ const sampleOptions = [
58
61
  { value: 'option3', label: 'Option 3' },
59
62
  ]
60
63
 
64
+ const wrappedRender = (args: { modelValue?: string }) => ({
65
+ components: { CpRadioGroup },
66
+ setup() {
67
+ const value = ref(args.modelValue)
68
+ return { args, value }
69
+ },
70
+ template: `
71
+ <div style="min-width: 800px; padding: 20px;">
72
+ <CpRadioGroup v-model="value" v-bind="args" />
73
+ </div>
74
+ `,
75
+ })
76
+
77
+ /**
78
+ * Default radio group, stacked vertically. Use the controls to experiment
79
+ * with each prop in isolation.
80
+ */
61
81
  export const Default: Story = {
62
82
  args: {
63
83
  modelValue: 'option1',
@@ -78,16 +98,16 @@ export const Default: Story = {
78
98
  },
79
99
  template: `
80
100
  <div style="min-width: 800px; padding: 20px;">
81
- <CpRadioGroup
82
- v-model="value"
83
- v-bind="args"
84
- />
85
- <p style="margin-top: 24px;font-size:12px;">modelValue: <code>{{ value }}</code></p>
101
+ <CpRadioGroup v-model="value" v-bind="args" />
102
+ <p style="margin-top: 24px; font-size: 12px;">modelValue: <code>{{ value }}</code></p>
86
103
  </div>
87
104
  `,
88
105
  }),
89
106
  }
90
107
 
108
+ /**
109
+ * Arrange the options on a single row with `direction="horizontal"`.
110
+ */
91
111
  export const Horizontal: Story = {
92
112
  args: {
93
113
  ...Default.args,
@@ -95,6 +115,10 @@ export const Horizontal: Story = {
95
115
  },
96
116
  }
97
117
 
118
+ /**
119
+ * Add a visible label and helper text describing the whole group. Setting
120
+ * `required` puts the asterisk on the group label.
121
+ */
98
122
  export const WithGroupLabel: Story = {
99
123
  args: {
100
124
  ...Default.args,
@@ -104,6 +128,10 @@ export const WithGroupLabel: Story = {
104
128
  },
105
129
  }
106
130
 
131
+ /**
132
+ * Attach a `helperText` to individual options — useful for short
133
+ * explanations next to each choice.
134
+ */
107
135
  export const WithHelperText: Story = {
108
136
  args: {
109
137
  ...Default.args,
@@ -115,7 +143,10 @@ export const WithHelperText: Story = {
115
143
  },
116
144
  }
117
145
 
118
- export const WithDisabledOptions: Story = {
146
+ /**
147
+ * Disable individual options; the rest of the group stays selectable.
148
+ */
149
+ export const WithDisabledOption: Story = {
119
150
  args: {
120
151
  ...Default.args,
121
152
  options: [
@@ -124,23 +155,13 @@ export const WithDisabledOptions: Story = {
124
155
  { value: 'option3', label: 'Option 3' },
125
156
  ],
126
157
  },
127
- render: (args) => ({
128
- components: { CpRadioGroup },
129
- setup() {
130
- const value = ref(args.modelValue)
131
- return { args, value }
132
- },
133
- template: `
134
- <div style="min-width: 800px; padding: 20px;">
135
- <CpRadioGroup
136
- v-model="value"
137
- v-bind="args"
138
- />
139
- </div>
140
- `,
141
- }),
158
+ render: wrappedRender,
142
159
  }
143
160
 
161
+ /**
162
+ * A disabled option can still be the current value. Use this pattern for
163
+ * read-only pre-selections.
164
+ */
144
165
  export const CheckedDisabled: Story = {
145
166
  args: {
146
167
  ...Default.args,
@@ -151,19 +172,5 @@ export const CheckedDisabled: Story = {
151
172
  ],
152
173
  modelValue: 'option1',
153
174
  },
154
- render: (args) => ({
155
- components: { CpRadioGroup },
156
- setup() {
157
- const value = ref(args.modelValue)
158
- return { args, value }
159
- },
160
- template: `
161
- <div style="min-width: 800px; padding: 20px;">
162
- <CpRadioGroup
163
- v-model="value"
164
- v-bind="args"
165
- />
166
- </div>
167
- `,
168
- }),
175
+ render: wrappedRender,
169
176
  }
@@ -5,8 +5,14 @@ import type { Meta, StoryObj } from '@storybook/vue3'
5
5
  import CpInput from '@/components/CpInput.vue'
6
6
  import CpSelect from '@/components/CpSelect.vue'
7
7
 
8
+ import { docCellStyle, docLabelStyle, docRowColumnStyle } from '@/stories/documentationStyles'
9
+
10
+ const selectSizes = ['sm', 'md', 'lg'] as const
11
+
12
+ const selectStackStyle = `${docCellStyle} width: 100%; max-width: 360px;`
13
+
8
14
  const meta = {
9
- title: 'Form/CpSelect',
15
+ title: 'Molecules/CpSelect',
10
16
  component: CpSelect,
11
17
  argTypes: {
12
18
  modelValue: {
@@ -47,7 +53,7 @@ const meta = {
47
53
  },
48
54
  size: {
49
55
  control: 'select',
50
- options: ['sm', 'md', 'lg'],
56
+ options: selectSizes,
51
57
  description: 'The size of the select',
52
58
  },
53
59
  autocomplete: {
@@ -79,6 +85,9 @@ const sampleOptions = [
79
85
  { value: '4', label: 'Option 4' },
80
86
  ]
81
87
 
88
+ /**
89
+ * Default select. Use the controls to experiment with each prop in isolation.
90
+ */
82
91
  export const Default: Story = {
83
92
  args: {
84
93
  label: 'Select Label',
@@ -101,51 +110,97 @@ export const Default: Story = {
101
110
  },
102
111
  template: `
103
112
  <div style="max-width: 400px; padding: 20px;">
104
- <CpSelect
105
- v-model="value"
106
- v-bind="args"
107
- />
113
+ <CpSelect v-model="value" v-bind="args" />
108
114
  </div>
109
115
  `,
110
116
  }),
111
117
  }
112
118
 
113
- export const WithError: Story = {
114
- args: {
115
- ...Default.args,
116
- isInvalid: true,
117
- errorMessage: 'This field is required',
118
- },
119
- }
119
+ /* -------------------------------------------------------------------------- */
120
+ /* Sizes */
121
+ /* -------------------------------------------------------------------------- */
120
122
 
121
- export const Required: Story = {
122
- args: {
123
- ...Default.args,
124
- required: true,
125
- },
123
+ /**
124
+ * All sizes rendered side by side, from `sm` to `lg`.
125
+ */
126
+ export const Sizes: Story = {
127
+ parameters: { controls: { disable: true } },
128
+ render: () => ({
129
+ components: { CpSelect },
130
+ setup() {
131
+ const value = ref('3')
132
+ return { value, sampleOptions, selectSizes, docRowColumnStyle, selectStackStyle, docLabelStyle }
133
+ },
134
+ template: `
135
+ <div :style="docRowColumnStyle">
136
+ <div v-for="size in selectSizes" :key="size" :style="selectStackStyle">
137
+ <span :style="docLabelStyle">{{ size }}</span>
138
+ <CpSelect
139
+ v-model="value"
140
+ :size="size"
141
+ label="Select label"
142
+ :options="sampleOptions"
143
+ name="select-field"
144
+ />
145
+ </div>
146
+ </div>
147
+ `,
148
+ }),
126
149
  }
127
150
 
128
- export const Disabled: Story = {
129
- args: {
130
- ...Default.args,
131
- disabled: true,
132
- },
133
- }
151
+ /* -------------------------------------------------------------------------- */
152
+ /* States */
153
+ /* -------------------------------------------------------------------------- */
134
154
 
135
- export const Small: Story = {
136
- args: {
137
- ...Default.args,
138
- size: 'sm',
139
- },
155
+ /**
156
+ * Default, required, disabled and invalid states compared side by side.
157
+ */
158
+ export const States: Story = {
159
+ parameters: { controls: { disable: true } },
160
+ render: () => ({
161
+ components: { CpSelect },
162
+ setup() {
163
+ const value = ref('3')
164
+ return { value, sampleOptions, docRowColumnStyle, selectStackStyle, docLabelStyle }
165
+ },
166
+ template: `
167
+ <div :style="docRowColumnStyle">
168
+ <div :style="selectStackStyle">
169
+ <span :style="docLabelStyle">Default</span>
170
+ <CpSelect v-model="value" label="Select label" :options="sampleOptions" name="s-default" />
171
+ </div>
172
+ <div :style="selectStackStyle">
173
+ <span :style="docLabelStyle">Required</span>
174
+ <CpSelect v-model="value" label="Select label" :options="sampleOptions" :required="true" name="s-required" />
175
+ </div>
176
+ <div :style="selectStackStyle">
177
+ <span :style="docLabelStyle">Disabled</span>
178
+ <CpSelect v-model="value" label="Select label" :options="sampleOptions" :disabled="true" name="s-disabled" />
179
+ </div>
180
+ <div :style="selectStackStyle">
181
+ <span :style="docLabelStyle">Invalid</span>
182
+ <CpSelect
183
+ v-model="value"
184
+ label="Select label"
185
+ :options="sampleOptions"
186
+ :is-invalid="true"
187
+ error-message="This field is required"
188
+ name="s-invalid"
189
+ />
190
+ </div>
191
+ </div>
192
+ `,
193
+ }),
140
194
  }
141
195
 
142
- export const Large: Story = {
143
- args: {
144
- ...Default.args,
145
- size: 'lg',
146
- },
147
- }
196
+ /* -------------------------------------------------------------------------- */
197
+ /* Misc */
198
+ /* -------------------------------------------------------------------------- */
148
199
 
200
+ /**
201
+ * Hides the default "Select an option" entry. Use this when an option must
202
+ * always be picked.
203
+ */
149
204
  export const WithoutDefaultOption: Story = {
150
205
  args: {
151
206
  ...Default.args,
@@ -153,6 +208,9 @@ export const WithoutDefaultOption: Story = {
153
208
  },
154
209
  }
155
210
 
211
+ /**
212
+ * Long option labels are truncated with an ellipsis.
213
+ */
156
214
  export const WithLongOptions: Story = {
157
215
  args: {
158
216
  ...Default.args,
@@ -165,6 +223,10 @@ export const WithLongOptions: Story = {
165
223
  },
166
224
  }
167
225
 
226
+ /**
227
+ * Combines a select and an input on the same line — for example a currency
228
+ * and an amount.
229
+ */
168
230
  export const NextToInput: Story = {
169
231
  args: {
170
232
  ...Default.args,
@@ -180,10 +242,7 @@ export const NextToInput: Story = {
180
242
  },
181
243
  template: `
182
244
  <div style="display: flex; flex-wrap: wrap; align-items: center; gap: 10px; max-width: 400px; padding: 20px;">
183
- <CpSelect
184
- v-model="value"
185
- v-bind="args"
186
- />
245
+ <CpSelect v-model="value" v-bind="args" />
187
246
  <CpInput :size="args.size" label="Input Label" name="input-field" placeholder="Enter text here" v-model="textValue" />
188
247
  </div>
189
248
  `,
@@ -5,39 +5,21 @@ import type { Meta, StoryObj } from '@storybook/vue3'
5
5
  import CpSelectMenu from '@/components/CpSelectMenu.vue'
6
6
 
7
7
  const meta = {
8
- title: 'Form/CpSelectMenu',
8
+ title: 'Molecules/CpSelectMenu',
9
9
  component: CpSelectMenu,
10
10
  argTypes: {
11
- values: {
12
- control: 'object',
13
- description: 'Array of options to display',
14
- },
15
- selectedValue: {
16
- control: 'object',
17
- description: 'Currently selected value',
18
- },
19
- hasFilter: {
20
- control: 'boolean',
21
- description: 'Whether to show search filter',
22
- },
23
- dropdownTitle: {
24
- control: 'text',
25
- description: 'Title text for the dropdown',
26
- },
27
- dropdownFilterPlaceholder: {
28
- control: 'text',
29
- description: 'Placeholder text for the filter input',
30
- },
11
+ values: { control: 'object', description: 'Array of options to display' },
12
+ selectedValue: { control: 'object', description: 'Currently selected value' },
13
+ hasFilter: { control: 'boolean', description: 'Whether to show search filter' },
14
+ dropdownTitle: { control: 'text', description: 'Title text for the dropdown' },
15
+ dropdownFilterPlaceholder: { control: 'text', description: 'Placeholder text for the filter input' },
31
16
  dropdownEmptyViewPlaceholder: {
32
17
  control: 'text',
33
18
  description: 'Text to show when no options match the filter',
34
19
  },
35
- closeOnSelect: {
36
- control: 'boolean',
37
- description: 'Whether to close dropdown after selection',
38
- },
20
+ closeOnSelect: { control: 'boolean', description: 'Whether to close dropdown after selection' },
39
21
  },
40
- decorators: [() => ({ template: '<div style="min-height: 30vh;"><story/></div>' })],
22
+ decorators: [() => ({ template: '<div style="min-height: 30vh;"><story /></div>' })],
41
23
  } satisfies Meta<typeof CpSelectMenu>
42
24
 
43
25
  export default meta
@@ -53,6 +35,10 @@ const sampleOptions = [
53
35
  { value: '7', label: 'Cherry' },
54
36
  ]
55
37
 
38
+ /**
39
+ * Default select menu. Use the controls to experiment with each prop in
40
+ * isolation.
41
+ */
56
42
  export const Default: Story = {
57
43
  args: {
58
44
  values: sampleOptions,
@@ -79,53 +65,59 @@ export const Default: Story = {
79
65
  </div>
80
66
  `,
81
67
  methods: {
82
- onUpdateSelectedValue: (data) => {
68
+ onUpdateSelectedValue(data: unknown) {
83
69
  args.selectedValue = data
84
70
  },
85
71
  },
86
72
  }),
87
73
  }
88
74
 
89
- const searchQuery = ref('')
90
-
75
+ /**
76
+ * Enable `hasFilter` to render a search input above the list. The
77
+ * `@on-filter-change` event lets the caller filter its own options list.
78
+ */
91
79
  export const WithFilter: Story = {
92
80
  args: {
93
81
  ...Default.args,
94
82
  hasFilter: true,
95
83
  dropdownTitle: 'Search for a fruit',
96
- selectedValue: { value: '1', label: 'Apple' },
97
- values: sampleOptions,
98
84
  },
99
- render: (args) => ({
100
- components: { CpSelectMenu },
101
- setup() {
102
- const selectedValue = ref(args.selectedValue)
103
- return { args, selectedValue }
104
- },
105
- template: `
106
- <div style="max-width: 400px; padding: 20px;">
107
- <CpSelectMenu
108
- v-model:selectedValue="selectedValue"
109
- v-bind="args"
110
- @on-filter-change="onFilterChange"
111
- @update:selected-value="onUpdateSelectedValue"
112
- />
113
- </div>
114
- `,
115
- methods: {
116
- onFilterChange: (data) => {
117
- searchQuery.value = data
118
- args.values = sampleOptions.filter((option) =>
119
- option.label.toLowerCase().includes(searchQuery.value.toLowerCase()),
120
- )
85
+ render: (args) => {
86
+ const searchQuery = ref('')
87
+ return {
88
+ components: { CpSelectMenu },
89
+ setup() {
90
+ const selectedValue = ref(args.selectedValue)
91
+ return { args, selectedValue }
121
92
  },
122
- onUpdateSelectedValue: (data) => {
123
- args.selectedValue = data
93
+ template: `
94
+ <div style="max-width: 400px; padding: 20px;">
95
+ <CpSelectMenu
96
+ v-model:selectedValue="selectedValue"
97
+ v-bind="args"
98
+ @on-filter-change="onFilterChange"
99
+ @update:selected-value="onUpdateSelectedValue"
100
+ />
101
+ </div>
102
+ `,
103
+ methods: {
104
+ onFilterChange(data: string) {
105
+ searchQuery.value = data
106
+ args.values = sampleOptions.filter((option) =>
107
+ option.label.toLowerCase().includes(searchQuery.value.toLowerCase()),
108
+ )
109
+ },
110
+ onUpdateSelectedValue(data: unknown) {
111
+ args.selectedValue = data
112
+ },
124
113
  },
125
- },
126
- }),
114
+ }
115
+ },
127
116
  }
128
117
 
118
+ /**
119
+ * Override the dropdown title via `dropdownTitle`.
120
+ */
129
121
  export const CustomTitle: Story = {
130
122
  args: {
131
123
  ...Default.args,