@vcita/design-system 1.3.2 → 1.3.4
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/config/locales/ds.en.yml +4 -0
- package/dist/@vcita/design-system.esm.js +1882 -1140
- package/dist/@vcita/design-system.min.js +2 -2
- package/dist/@vcita/design-system.ssr.js +1688 -965
- package/init/DesignSystem.js +3 -1
- package/init/initI18n.js +24 -16
- package/package.json +2 -1
- package/src/components/VcActionList/VcActionList.spec.js +16 -7
- package/src/components/VcActionList/VcActionList.stories.js +16 -3
- package/src/components/VcActionList/VcActionList.vue +35 -11
- package/src/components/VcBottomActions/VcBottomActions.vue +2 -1
- package/src/components/VcBottomSheet/VcBottomSheet.stories.js +6 -13
- package/src/components/VcBottomSheet/VcBottomSheet.vue +2 -3
- package/src/components/VcButton/VcButton.vue +1 -1
- package/src/components/VcCheckbox/VcCheckbox.vue +8 -1
- package/src/components/VcColorPicker/VcColorPicker.spec.js +206 -0
- package/src/components/VcColorPicker/VcColorPicker.stories.js +107 -0
- package/src/components/VcColorPicker/VcColorPicker.vue +270 -0
- package/src/components/VcFilterPanel/VcFilterPanel.spec.js +15 -0
- package/src/components/VcFilterPanel/VcFilterPanel.stories.js +9 -1
- package/src/components/VcFilterPanel/VcFilterPanel.vue +24 -3
- package/src/components/VcGalleryItem/VcGalleryItem.stories.js +2 -0
- package/src/components/VcGroupedItems/VcGroupedItems.spec.js +148 -0
- package/src/components/VcGroupedItems/VcGroupedItems.stories.js +135 -0
- package/src/components/VcGroupedItems/VcGroupedItems.vue +155 -0
- package/src/components/VcLink/VcLink.spec.js +3 -3
- package/src/components/VcLink/VcLink.stories.js +2 -6
- package/src/components/VcLink/VcLink.vue +1 -18
- package/src/components/VcMenu/VcDropdown.spec.js +120 -0
- package/src/components/VcMenu/VcDropdown.stories.js +272 -0
- package/src/components/VcMenu/VcDropdown.vue +93 -0
- package/src/components/VcMenu/VcMenu.spec.js +61 -10
- package/src/components/VcMenu/VcMenu.stories.js +38 -33
- package/src/components/VcMenu/VcMenu.vue +19 -3
- package/src/components/VcPopover/VcPopover.stories.js +2 -2
- package/src/components/VcRadioGroup/VcRadioGroup.spec.js +28 -0
- package/src/components/VcRadioGroup/VcRadioGroup.stories.js +3 -1
- package/src/components/VcRadioGroup/VcRadioGroup.vue +6 -1
- package/src/components/VcSearchPicker/VcSearchPicker.stories.js +3 -4
- package/src/components/VcSelectField/VcSelectField.vue +6 -0
- package/src/components/VcSideNav/VcSideNav.spec.js +1 -1
- package/src/components/VcSideNav/VcSideNav.vue +21 -104
- package/src/components/VcTextField/VcTextField.spec.js +13 -0
- package/src/components/VcTextField/VcTextField.stories.js +2 -1
- package/src/components/VcTextField/VcTextField.vue +11 -0
- package/src/components/VcTooltip/VcTooltip.stories.js +3 -1
- package/src/components/VcTooltip/VcTooltip.vue +6 -1
- package/src/components/index.js +4 -0
- package/src/components/list/VcBaseListItem/VcBaseListItem.stories.js +22 -13
- package/src/components/list/VcBaseListItem/VcBaseListItem.vue +4 -1
- package/src/components/list/VcList/VcList.stories.js +245 -240
- package/src/components/list/VcList/VcList.vue +11 -4
- package/src/components/page/layouts/centeredPage/CenteredPageLayout.stories.js +17 -16
- package/styles/variables.scss +1 -0
- package/styles/vuetify-variables.scss +9 -1
- package/CHANGELOG.md +0 -342
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="VcColorPicker d-flex flex-column"
|
|
3
|
+
ref="pickerInput" :data-qa="dataQa"
|
|
4
|
+
v-click-outside="closePicker">
|
|
5
|
+
<div class="VcColorPicker__input d-flex align-center">
|
|
6
|
+
<VcSelectField
|
|
7
|
+
:data-qa="`${dataQa}_select`"
|
|
8
|
+
:disabled="disabled"
|
|
9
|
+
class="VcColorPicker__input__dropdown"
|
|
10
|
+
:class="{'error--text' : getError}"
|
|
11
|
+
@click.native.stop.prevent="showPicker = true"
|
|
12
|
+
:readonly="!disabled">
|
|
13
|
+
<template v-slot:prepend>
|
|
14
|
+
<div class="color-preview" :style="{backgroundColor: color}"></div>
|
|
15
|
+
</template>
|
|
16
|
+
</VcSelectField>
|
|
17
|
+
<VcTextField
|
|
18
|
+
:data-qa="`${dataQa}_input`"
|
|
19
|
+
:class="{'error--text' : getError}"
|
|
20
|
+
:label="$dst('ds.color_picker.label')"
|
|
21
|
+
:disabled="disabled"
|
|
22
|
+
@blur="validateInput"
|
|
23
|
+
@input="onInputUpdate"
|
|
24
|
+
v-model="inputColor"></VcTextField>
|
|
25
|
+
</div>
|
|
26
|
+
<div class="VcColorPicker__messages mt-1"
|
|
27
|
+
:class="{'error-message' : getError}"
|
|
28
|
+
v-if="!showPicker">
|
|
29
|
+
<span v-if="hint && !getError" :data-qa="`${dataQa}-hint`">{{ hint }}</span>
|
|
30
|
+
<span v-if="getError" :data-qa="`${dataQa}-error`">{{ getError }}</span>
|
|
31
|
+
</div>
|
|
32
|
+
<div class="VcColorPicker__picker mt-2 pa-2" v-if="showPicker && !isMobile">
|
|
33
|
+
<v-color-picker
|
|
34
|
+
:hide-mode-switch="hexMode"
|
|
35
|
+
mode="hexa"
|
|
36
|
+
:value="color"
|
|
37
|
+
show-swatches
|
|
38
|
+
flat
|
|
39
|
+
:width="getPickerWidth"
|
|
40
|
+
:data-qa="`${dataQa}_picker`"
|
|
41
|
+
:swatches="swatches"
|
|
42
|
+
@update:color="onPickerUpdate">
|
|
43
|
+
</v-color-picker>
|
|
44
|
+
</div>
|
|
45
|
+
<v-bottom-sheet
|
|
46
|
+
v-if="isMobile"
|
|
47
|
+
v-model="showPicker">
|
|
48
|
+
<v-color-picker
|
|
49
|
+
:data-qa="`${dataQa}_picker`"
|
|
50
|
+
v-if="showPicker"
|
|
51
|
+
:hide-mode-switch="hexMode"
|
|
52
|
+
class="VcColorPicker__picker"
|
|
53
|
+
mode="hexa"
|
|
54
|
+
:value="color"
|
|
55
|
+
show-swatches
|
|
56
|
+
flat
|
|
57
|
+
:width="getPickerWidth"
|
|
58
|
+
@update:color="onPickerUpdate"
|
|
59
|
+
:swatches="swatches">
|
|
60
|
+
</v-color-picker>
|
|
61
|
+
</v-bottom-sheet>
|
|
62
|
+
</div>
|
|
63
|
+
</template>
|
|
64
|
+
|
|
65
|
+
<script>
|
|
66
|
+
const regexHex = /^#[0-9A-F]{6}$/i;
|
|
67
|
+
const regexHexa = /^#[0-9A-F]{8}$/i;
|
|
68
|
+
const swatchesConst = [["#EF4444", '#F97316'], ['#FACC15', '#4ADE80'], ['#2DD4BF', '#3B82F6'], ['#6467F2', '#EC4899'], ['#F43F5E', '#D946EF'], ['#8B5CF6', '#0EA5E9'], ['#10B981', '#84CC16']];
|
|
69
|
+
|
|
70
|
+
// import ClickOutside from 'vue-click-outside';
|
|
71
|
+
import VcTextField from "@/components/VcTextField/VcTextField.vue";
|
|
72
|
+
import VcSelectField from '@/components/VcSelectField/VcSelectField.vue';
|
|
73
|
+
|
|
74
|
+
export default {
|
|
75
|
+
name: "VcColorPicker",
|
|
76
|
+
// directives: {
|
|
77
|
+
// ClickOutside
|
|
78
|
+
// },
|
|
79
|
+
components: {VcTextField, VcSelectField},
|
|
80
|
+
props: {
|
|
81
|
+
dataQa: {
|
|
82
|
+
type: String,
|
|
83
|
+
default: 'VcColorPicker'
|
|
84
|
+
},
|
|
85
|
+
disabled: {
|
|
86
|
+
type: Boolean,
|
|
87
|
+
default: false
|
|
88
|
+
},
|
|
89
|
+
hint: {
|
|
90
|
+
type: String,
|
|
91
|
+
default: ""
|
|
92
|
+
},
|
|
93
|
+
color: {
|
|
94
|
+
type: String,
|
|
95
|
+
default: "",
|
|
96
|
+
validator: prop => !prop || (regexHex.test(prop)) || (regexHexa.test(prop)),
|
|
97
|
+
},
|
|
98
|
+
/**
|
|
99
|
+
* Similar to Vuetify rules prop - array of functions that return true or the message error as a string
|
|
100
|
+
* See an example in the storybook
|
|
101
|
+
*/
|
|
102
|
+
rules: {
|
|
103
|
+
type: Array,
|
|
104
|
+
validator: prop => prop.every(rule => typeof rule === "function"),
|
|
105
|
+
},
|
|
106
|
+
hexMode: {
|
|
107
|
+
type: Boolean,
|
|
108
|
+
default: false
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
model: {
|
|
112
|
+
prop: 'color',
|
|
113
|
+
event: 'change'
|
|
114
|
+
},
|
|
115
|
+
data(vm) {
|
|
116
|
+
return {
|
|
117
|
+
showPicker: false,
|
|
118
|
+
inputColor: this.getHexMode(vm.color?.toUpperCase()),
|
|
119
|
+
swatches: swatchesConst,
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
methods: {
|
|
123
|
+
isValidHexColor(val) {
|
|
124
|
+
return (regexHex.test(val)) || (regexHexa.test(val));
|
|
125
|
+
},
|
|
126
|
+
closePicker() {
|
|
127
|
+
if (this.isMobile) return; // Don't close the picker after it's clicked
|
|
128
|
+
this.showPicker = false;
|
|
129
|
+
},
|
|
130
|
+
onPickerUpdate(color) {
|
|
131
|
+
this.inputColor = color.hexa.toUpperCase();
|
|
132
|
+
this.$emit('change', this.inputColor);
|
|
133
|
+
},
|
|
134
|
+
validateInput() {
|
|
135
|
+
this.inputColor = (this.isValidHexColor(this.inputColor) ? this.inputColor : this.color).toUpperCase();
|
|
136
|
+
},
|
|
137
|
+
onInputUpdate(color) {
|
|
138
|
+
if (this.isValidHexColor(color)) this.$emit('change', color.toUpperCase())
|
|
139
|
+
},
|
|
140
|
+
getHexMode(color) {
|
|
141
|
+
if (this.hexMode && color?.toString().match(/#[a-zA-Z0-9]{8}/)) {
|
|
142
|
+
return color.substr(0, 7).toUpperCase();
|
|
143
|
+
}
|
|
144
|
+
return color;
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
computed: {
|
|
148
|
+
getPickerWidth() {
|
|
149
|
+
if (this.isMobile) return window.innerWidth;
|
|
150
|
+
else return (this.$refs?.pickerInput?.offsetWidth);
|
|
151
|
+
},
|
|
152
|
+
isMobile() {
|
|
153
|
+
return (!this.$vuetify.breakpoint.mdAndUp);
|
|
154
|
+
},
|
|
155
|
+
getError() {
|
|
156
|
+
if (!this.rules) return false;
|
|
157
|
+
let errorMessages = this.rules.map((rule) => {
|
|
158
|
+
const resolve = rule.call(this, this.inputColor)
|
|
159
|
+
if (typeof (resolve) === 'string') return resolve;
|
|
160
|
+
}).filter(item => !!item);
|
|
161
|
+
return errorMessages[0];
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
watch: {
|
|
165
|
+
color(value) {
|
|
166
|
+
this.inputColor = this.getHexMode(value);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
</script>
|
|
171
|
+
|
|
172
|
+
<style lang="scss">
|
|
173
|
+
.v-dialog:has(.v-color-picker__color) {
|
|
174
|
+
.v-color-picker__color {
|
|
175
|
+
&:has(span.v-icon) {
|
|
176
|
+
border: 2px solid #FFFFFF;
|
|
177
|
+
box-shadow: 0px 0px 0px 2px rgba(0, 0, 0, 0.25);
|
|
178
|
+
|
|
179
|
+
span.v-icon {
|
|
180
|
+
display: none;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
</style>
|
|
186
|
+
<style lang="scss" scoped>
|
|
187
|
+
.VcColorPicker {
|
|
188
|
+
direction: ltr !important;
|
|
189
|
+
|
|
190
|
+
&__input {
|
|
191
|
+
&__dropdown {
|
|
192
|
+
width: var(--size-value18);
|
|
193
|
+
max-width: var(--size-value18);
|
|
194
|
+
|
|
195
|
+
.color-preview {
|
|
196
|
+
width: var(--size-value6);
|
|
197
|
+
height: var(--size-value6);
|
|
198
|
+
border-radius: var(--border-radius-circle);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
&__picker {
|
|
204
|
+
box-shadow: var(--shadow-6);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
&__messages {
|
|
208
|
+
font-weight: var(--font-weight-medium);
|
|
209
|
+
font-size: var(--font-size-xx-small);
|
|
210
|
+
line-height: var(--size-value4);
|
|
211
|
+
color: var(--gray-darken-3);
|
|
212
|
+
|
|
213
|
+
&.error-message {
|
|
214
|
+
color: var(--red);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
::v-deep {
|
|
219
|
+
.VcSelectField {
|
|
220
|
+
border-radius: var(--border-radius) 0 0 var(--border-radius);
|
|
221
|
+
|
|
222
|
+
fieldset {
|
|
223
|
+
margin-top: 1px;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
.VcTextInput.v-text-field .v-input__slot {
|
|
228
|
+
border-radius: 0 var(--border-radius) var(--border-radius) 0;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
.v-color-picker__canvas {
|
|
232
|
+
border-radius: var(--border-radius);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
.v-dialog__content {
|
|
236
|
+
background-color: red;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
.v-color-picker__color {
|
|
240
|
+
&:has(span.v-icon) {
|
|
241
|
+
border: 2px solid #FFFFFF;
|
|
242
|
+
box-shadow: 0px 0px 0px 2px rgba(0, 0, 0, 0.25);
|
|
243
|
+
|
|
244
|
+
span.v-icon {
|
|
245
|
+
display: none;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
.v-color-picker__input {
|
|
251
|
+
font-size: var(--font-size-x-small);
|
|
252
|
+
font-weight: var(--font-weight-medium2);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
.v-color-picker__swatches > div {
|
|
256
|
+
padding: unset;
|
|
257
|
+
justify-content: space-between;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
.v-color-picker__controls {
|
|
261
|
+
padding: var(--size-value4) var(--size-value2) var(--size-value6);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
.v-color-picker__edit {
|
|
265
|
+
padding-bottom: var(--size-value4);
|
|
266
|
+
border-bottom: var(--border-frame);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
</style>
|
|
@@ -77,4 +77,19 @@ describe("VcFilterPanel.vue", () => {
|
|
|
77
77
|
const slotContent = getByText('content');
|
|
78
78
|
expect(slotContent).toBeInTheDocument();
|
|
79
79
|
});
|
|
80
|
+
it("does not render header with the right prop", async () => {
|
|
81
|
+
const {queryByText, updateProps} = renderWithVuetify(VcFilterPanel, {
|
|
82
|
+
props: {
|
|
83
|
+
title: 'filter panel',
|
|
84
|
+
shouldDisplayHeader: true,
|
|
85
|
+
},
|
|
86
|
+
})
|
|
87
|
+
let title = queryByText('filter panel');
|
|
88
|
+
expect(title).toBeInTheDocument();
|
|
89
|
+
await updateProps({
|
|
90
|
+
shouldDisplayHeader: false,
|
|
91
|
+
})
|
|
92
|
+
title = queryByText('filter panel');
|
|
93
|
+
expect(title).not.toBeInTheDocument();
|
|
94
|
+
});
|
|
80
95
|
});
|
|
@@ -9,6 +9,8 @@ const Template = (args, {argTypes}) => ({
|
|
|
9
9
|
<VcFilterPanel :title="title"
|
|
10
10
|
:is-loading="isLoading"
|
|
11
11
|
:show-close-button="showCloseButton"
|
|
12
|
+
:type="type"
|
|
13
|
+
:shouldDisplayHeader="shouldDisplayHeader"
|
|
12
14
|
:data-qa="dataQa"
|
|
13
15
|
@close="onClose"/>
|
|
14
16
|
</div>`,
|
|
@@ -21,7 +23,9 @@ Playground.args = {
|
|
|
21
23
|
title: 'Filter',
|
|
22
24
|
isLoading: false,
|
|
23
25
|
showCloseButton: true,
|
|
24
|
-
dataQa: 'VcFilterPanel'
|
|
26
|
+
dataQa: 'VcFilterPanel',
|
|
27
|
+
type: 'primary',
|
|
28
|
+
shouldDisplayHeader: true,
|
|
25
29
|
}
|
|
26
30
|
|
|
27
31
|
export default {
|
|
@@ -30,6 +34,10 @@ export default {
|
|
|
30
34
|
component: VcFilterPanelCmp,
|
|
31
35
|
argTypes: {
|
|
32
36
|
onClose: {action: 'close', table: {disable: true}},
|
|
37
|
+
type: {
|
|
38
|
+
options: ['primary', 'secondary'],
|
|
39
|
+
control: {type: 'radio'}
|
|
40
|
+
},
|
|
33
41
|
},
|
|
34
42
|
parameters: {
|
|
35
43
|
design: {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<VcLayout column align-content-space-between class="VcFilterPanel" :data-qa="dataQa">
|
|
2
|
+
<VcLayout column align-content-space-between class="VcFilterPanel" :class="`vc-${flavor}`" :data-qa="dataQa">
|
|
3
3
|
<VcLayout class="VcFilterPanel-container" column>
|
|
4
|
-
<VcLayout class="VcFilterPanel-label-container">
|
|
4
|
+
<VcLayout class="VcFilterPanel-label-container" v-if="shouldDisplayHeader">
|
|
5
5
|
<div class="filter">
|
|
6
6
|
<span class="filter-label">{{ title }}</span>
|
|
7
7
|
<slot name="header"/>
|
|
@@ -44,6 +44,15 @@ export default {
|
|
|
44
44
|
type: Boolean,
|
|
45
45
|
default: false,
|
|
46
46
|
},
|
|
47
|
+
flavor: {
|
|
48
|
+
type: String,
|
|
49
|
+
default: 'primary',
|
|
50
|
+
validator: prop => ['primary', 'secondary'].includes(prop),
|
|
51
|
+
},
|
|
52
|
+
shouldDisplayHeader: {
|
|
53
|
+
type: Boolean,
|
|
54
|
+
default: true,
|
|
55
|
+
},
|
|
47
56
|
dataQa: {
|
|
48
57
|
type: String,
|
|
49
58
|
default: 'VcFilterPanel'
|
|
@@ -101,6 +110,18 @@ export default {
|
|
|
101
110
|
}
|
|
102
111
|
}
|
|
103
112
|
|
|
113
|
+
&.vc-secondary {
|
|
114
|
+
.filter {
|
|
115
|
+
border-bottom: none;
|
|
116
|
+
background-color: var(--modal-bg-color);
|
|
117
|
+
|
|
118
|
+
.filter-label {
|
|
119
|
+
font-weight: 700;
|
|
120
|
+
font-size: 18px;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
104
125
|
.VcFilterPanel-content-wrapper {
|
|
105
126
|
flex-grow: 1;
|
|
106
127
|
}
|
|
@@ -125,7 +146,7 @@ export default {
|
|
|
125
146
|
margin-right: var(--size-value0);
|
|
126
147
|
|
|
127
148
|
svg {
|
|
128
|
-
fill:var(--gray-darken-2);
|
|
149
|
+
fill: var(--gray-darken-2);
|
|
129
150
|
width: var(--size-value4);
|
|
130
151
|
height: var(--size-value4);
|
|
131
152
|
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import '@testing-library/jest-dom';
|
|
2
|
+
import VcGroupedItems from "./VcGroupedItems.vue";
|
|
3
|
+
import Vue from 'vue';
|
|
4
|
+
import Vuetify from 'vuetify';
|
|
5
|
+
import {render} from "@testing-library/vue";
|
|
6
|
+
import init from "../../../testing-library.config";
|
|
7
|
+
import userEvent from "@testing-library/user-event";
|
|
8
|
+
|
|
9
|
+
init();
|
|
10
|
+
|
|
11
|
+
Vue.use(Vuetify);
|
|
12
|
+
|
|
13
|
+
const basicProps = {
|
|
14
|
+
itemGroups: [
|
|
15
|
+
{
|
|
16
|
+
label: 'GROUP 1',
|
|
17
|
+
id: 1,
|
|
18
|
+
items: [
|
|
19
|
+
{id: '1', label: 'Menu Item 1'},
|
|
20
|
+
{id: '2', label: 'Menu Item 2'},
|
|
21
|
+
{id: '3', label: 'Menu Item 3'},
|
|
22
|
+
{id: '4', label: 'Menu Item 4'},
|
|
23
|
+
{id: '5', label: 'Menu Item 5', disabled: true},
|
|
24
|
+
]
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
label: 'GROUP 2',
|
|
28
|
+
id: 2,
|
|
29
|
+
items: [
|
|
30
|
+
{id: '6', label: 'Menu Item 6'},
|
|
31
|
+
{id: '7', label: 'Menu Item 7'},
|
|
32
|
+
{id: '8', label: 'Menu Item 8'},
|
|
33
|
+
{id: '9', label: 'Menu Item 9'},
|
|
34
|
+
{id: '10', label: 'Menu Item 10'},
|
|
35
|
+
]
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
showDividers: false,
|
|
39
|
+
flavor: 'flat',
|
|
40
|
+
dataQa: 'VcGroupedItems',
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
describe("VcGroupedItems.vue", () => {
|
|
44
|
+
|
|
45
|
+
const renderWithVuetify = (component, options, callback, isMobile = false) => {
|
|
46
|
+
const root = document.createElement('div')
|
|
47
|
+
root.setAttribute('data-app', 'true')
|
|
48
|
+
|
|
49
|
+
const vuetify = new Vuetify()
|
|
50
|
+
if (isMobile) {
|
|
51
|
+
const breakpoint = {
|
|
52
|
+
init: jest.fn(),
|
|
53
|
+
framework: {},
|
|
54
|
+
smAndDown: true,
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
vuetify.framework.breakpoint = breakpoint;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return render(
|
|
61
|
+
component,
|
|
62
|
+
{
|
|
63
|
+
container: document.body.appendChild(root),
|
|
64
|
+
// for Vuetify components that use the vuetify instance property
|
|
65
|
+
vuetify,
|
|
66
|
+
...options,
|
|
67
|
+
mocks: {
|
|
68
|
+
$t: value => value,
|
|
69
|
+
$dst: value => value, // <- for the design system
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
callback,
|
|
73
|
+
)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
it("mounts and shows groups and items", () => {
|
|
77
|
+
// Queries: https://testing-library.com/docs/queries/about#types-of-queries
|
|
78
|
+
const {container, getByTestId, getByText} = renderWithVuetify(VcGroupedItems, {
|
|
79
|
+
props: {
|
|
80
|
+
...basicProps,
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// Expect options: https://github.com/testing-library/jest-dom
|
|
85
|
+
expect(container).toHaveAttribute('data-app', 'true');
|
|
86
|
+
|
|
87
|
+
// Make sure the component has the data-qa attribute
|
|
88
|
+
const component = getByTestId('VcGroupedItems');
|
|
89
|
+
expect(component).toBeInTheDocument();
|
|
90
|
+
|
|
91
|
+
const group1 = getByText('GROUP 1');
|
|
92
|
+
expect(group1).toBeInTheDocument();
|
|
93
|
+
|
|
94
|
+
const group2 = getByText('GROUP 2');
|
|
95
|
+
expect(group2).toBeInTheDocument();
|
|
96
|
+
|
|
97
|
+
for (let i = 1; i < 11; i++) {
|
|
98
|
+
const item = getByText(`Menu Item ${i}`);
|
|
99
|
+
expect(item).toBeInTheDocument();
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it("shows dividers", async () => {
|
|
104
|
+
const {getByRole, updateProps, getAllByRole} = renderWithVuetify(VcGroupedItems, {
|
|
105
|
+
props: {
|
|
106
|
+
...basicProps,
|
|
107
|
+
showDividers: true,
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
const divider = getByRole('separator');
|
|
112
|
+
expect(divider).toBeInTheDocument();
|
|
113
|
+
|
|
114
|
+
const thirdGroup = {
|
|
115
|
+
label: 'GROUP 3',
|
|
116
|
+
id: 3,
|
|
117
|
+
items: [
|
|
118
|
+
{id: '11', label: 'Menu Item 11'},
|
|
119
|
+
{id: '12', label: 'Menu Item 12'},
|
|
120
|
+
{id: '13', label: 'Menu Item 13'},
|
|
121
|
+
{id: '14', label: 'Menu Item 14'},
|
|
122
|
+
{id: '15', label: 'Menu Item 15'},
|
|
123
|
+
]
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
await updateProps({itemGroups: [...basicProps.itemGroups, thirdGroup]});
|
|
127
|
+
|
|
128
|
+
const dividers = getAllByRole('separator');
|
|
129
|
+
expect(dividers.length).toBe(2);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it("emits selection events", async () => {
|
|
133
|
+
const {getByText, emitted} = renderWithVuetify(VcGroupedItems, {
|
|
134
|
+
props: {
|
|
135
|
+
...basicProps,
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
await userEvent.click(getByText('Menu Item 1'));
|
|
140
|
+
expect(emitted().change.length).toBe(1);
|
|
141
|
+
expect(emitted().change[0][0]).toBe('1');
|
|
142
|
+
|
|
143
|
+
await userEvent.click(getByText('Menu Item 5'));
|
|
144
|
+
expect(emitted().change.length).toBe(2);
|
|
145
|
+
expect(emitted().change[1][0]).toBe('5');
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
});
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import VcGroupedItemsCmp from './VcGroupedItems';
|
|
2
|
+
import VcBaseDocs from '@/stories/VcBaseDocs.mdx'
|
|
3
|
+
import VcBadge from "@/components/VcBadge/VcBadge";
|
|
4
|
+
import VcIcon from "@/components/VcIcon/VcIcon";
|
|
5
|
+
|
|
6
|
+
const basicProps = {
|
|
7
|
+
itemGroups: [
|
|
8
|
+
{
|
|
9
|
+
label: 'GROUP 1',
|
|
10
|
+
id: 1,
|
|
11
|
+
items: [
|
|
12
|
+
{id: '1', label: 'Menu Item'},
|
|
13
|
+
{id: '2', label: 'Menu Item'},
|
|
14
|
+
{id: '3', label: 'Menu Item'},
|
|
15
|
+
{id: '4', label: 'Menu Item'},
|
|
16
|
+
{id: '5', label: 'Menu Item', disabled: true},
|
|
17
|
+
]
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
label: 'GROUP 2',
|
|
21
|
+
id: 2,
|
|
22
|
+
items: [
|
|
23
|
+
{id: '6', label: 'Menu Item'},
|
|
24
|
+
{id: '7', label: 'Menu Item'},
|
|
25
|
+
{id: '8', label: 'Menu Item'},
|
|
26
|
+
{id: '9', label: 'Menu Item'},
|
|
27
|
+
{id: '10', label: 'Menu Item'},
|
|
28
|
+
]
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
showDividers: false,
|
|
32
|
+
flavor: 'flat',
|
|
33
|
+
dataQa: 'VcGroupedItems',
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const Template = (args, {argTypes}) => ({
|
|
37
|
+
components: {VcGroupedItems: VcGroupedItemsCmp},
|
|
38
|
+
props: Object.keys(argTypes),
|
|
39
|
+
data: () => ({
|
|
40
|
+
selectedIdx: undefined,
|
|
41
|
+
}),
|
|
42
|
+
template: `
|
|
43
|
+
<div>
|
|
44
|
+
<VcGroupedItems :item-groups="itemGroups" :selected="selectedIdx" :show-dividers="showDividers" :flavor="flavor"
|
|
45
|
+
:dataQa="dataQa"
|
|
46
|
+
@change="onChange"/>
|
|
47
|
+
</div>`,
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
export const Playground = Template.bind({});
|
|
51
|
+
|
|
52
|
+
// Set default values
|
|
53
|
+
Playground.args = {
|
|
54
|
+
...basicProps,
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export const ElevatedFlavor = Template.bind({});
|
|
58
|
+
|
|
59
|
+
// Set default values
|
|
60
|
+
ElevatedFlavor.args = {
|
|
61
|
+
...basicProps,
|
|
62
|
+
flavor: 'elevated',
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export const FlatFlavor = Template.bind({});
|
|
66
|
+
|
|
67
|
+
// Set default values
|
|
68
|
+
FlatFlavor.args = {
|
|
69
|
+
...basicProps,
|
|
70
|
+
flavor: 'elevated',
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export const WithDividers = Template.bind({});
|
|
74
|
+
|
|
75
|
+
// Set default values
|
|
76
|
+
WithDividers.args = {
|
|
77
|
+
...basicProps,
|
|
78
|
+
showDividers: true,
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const TemplateWithSlots = (args, {argTypes}) => ({
|
|
82
|
+
components: {VcGroupedItems: VcGroupedItemsCmp, VcBadge, VcIcon},
|
|
83
|
+
props: Object.keys(argTypes),
|
|
84
|
+
data: () => ({
|
|
85
|
+
selectedIdx: undefined,
|
|
86
|
+
}),
|
|
87
|
+
template: `
|
|
88
|
+
<div>
|
|
89
|
+
<VcGroupedItems :item-groups="itemGroups" :selected="selectedIdx" :show-dividers="showDividers" :flavor="flavor"
|
|
90
|
+
:dataQa="dataQa">
|
|
91
|
+
<template #group-1-header>
|
|
92
|
+
<div class="d-flex align-center">
|
|
93
|
+
<span>Group Title</span>
|
|
94
|
+
<VcBadge class="ps-2" badgeText="new" :offsetX="-2" :offsetY="0"></VcBadge>
|
|
95
|
+
</div>
|
|
96
|
+
</template>
|
|
97
|
+
<template #item-4>
|
|
98
|
+
<div class="d-flex align-center flex-grow-1 justify-space-between px-6">
|
|
99
|
+
<span>Slot Label</span>
|
|
100
|
+
<div class="d-flex align-center">
|
|
101
|
+
<vc-icon size="16" color="black" class="mx-3">$magnify_glass</vc-icon>
|
|
102
|
+
<vc-icon size="16" color="black" class="mx-3">$plus</vc-icon>
|
|
103
|
+
</div>
|
|
104
|
+
</div>
|
|
105
|
+
</template>
|
|
106
|
+
</VcGroupedItems>
|
|
107
|
+
</div>`,
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
export const UsingGroupAndItemSlots = TemplateWithSlots.bind({});
|
|
111
|
+
|
|
112
|
+
UsingGroupAndItemSlots.args = {
|
|
113
|
+
...basicProps,
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export default {
|
|
117
|
+
title: 'containers / VcGroupedItems', // This will control the story sidebar position
|
|
118
|
+
id: 'VcGroupedItems', // This will be the permanent link to this component
|
|
119
|
+
component: VcGroupedItemsCmp,
|
|
120
|
+
argTypes: {
|
|
121
|
+
onChange: {action: 'change', table: {disable: true}},
|
|
122
|
+
},
|
|
123
|
+
parameters: {
|
|
124
|
+
design: {
|
|
125
|
+
type: 'figma',
|
|
126
|
+
url: 'https://www.figma.com/file/xIOY6fBoA1wpy1tHv3i5js/vcita---ui-library?node-id=251%3A908',
|
|
127
|
+
},
|
|
128
|
+
status: {
|
|
129
|
+
type: 'beta', // 'beta' | 'stable' | 'deprecated' | 'releaseCandidate'
|
|
130
|
+
},
|
|
131
|
+
docs: {
|
|
132
|
+
page: VcBaseDocs,
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
};
|