@cnamts/synapse 0.0.8-alpha → 0.0.9-alpha
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/dist/design-system-v3.d.ts +584 -128
- package/dist/design-system-v3.js +4176 -2694
- package/dist/design-system-v3.umd.cjs +1 -1
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/src/assets/settings.scss +1 -1
- package/src/components/ContextualMenu/Accessibilite.mdx +14 -0
- package/src/components/ContextualMenu/Accessibilite.stories.ts +191 -0
- package/src/components/ContextualMenu/AccessibiliteItems.ts +89 -0
- package/src/components/ContextualMenu/constants/ExpertiseLevelEnum.ts +4 -0
- package/src/components/Customs/SySelect/SySelect.stories.ts +7 -7
- package/src/components/Customs/SySelect/SySelect.vue +9 -4
- package/src/components/Customs/SySelect/tests/SySelect.spec.ts +2 -2
- package/src/components/Customs/SyTextField/SyTextField.stories.ts +187 -2
- package/src/components/Customs/SyTextField/SyTextField.vue +185 -16
- package/src/components/Customs/SyTextField/tests/SyTextField.spec.ts +2 -4
- package/src/components/Customs/SyTextField/tests/__snapshots__/SyTextField.spec.ts.snap +18 -16
- package/src/components/Customs/SyTextField/types.d.ts +2 -2
- package/src/components/DatePicker/DatePicker.mdx +191 -0
- package/src/components/DatePicker/DatePicker.stories.ts +787 -0
- package/src/components/DatePicker/DatePicker.vue +560 -0
- package/src/components/DatePicker/DateTextInput.vue +409 -0
- package/src/components/DatePicker/tests/DatePicker.spec.ts +266 -0
- package/src/components/DialogBox/DialogBox.stories.ts +1 -1
- package/src/components/ExternalLinks/Accessibilite.mdx +14 -0
- package/src/components/ExternalLinks/Accessibilite.stories.ts +191 -0
- package/src/components/ExternalLinks/AccessibiliteItems.ts +197 -0
- package/src/components/ExternalLinks/constants/ExpertiseLevelEnum.ts +4 -0
- package/src/components/ExternalLinks/tests/__snapshots__/ExternalLinks.spec.ts.snap +9 -9
- package/src/components/FileUpload/FileUpload.mdx +165 -0
- package/src/components/FileUpload/FileUpload.stories.ts +429 -0
- package/src/components/FileUpload/FileUpload.vue +195 -0
- package/src/components/FileUpload/FileUploadContent.vue +109 -0
- package/src/components/FileUpload/locales.ts +10 -0
- package/src/components/FileUpload/tests/FileUpload.spec.ts +332 -0
- package/src/components/FileUpload/tests/__snapshots__/FileUpload.spec.ts.snap +7 -0
- package/src/components/FileUpload/useFileDrop.ts +23 -0
- package/src/components/FileUpload/validateFiles.ts +39 -0
- package/src/components/NirField/NirField.stories.ts +1 -1
- package/src/components/NirField/NirField.vue +2 -1
- package/src/components/PasswordField/Accessibilite.mdx +14 -0
- package/src/components/PasswordField/Accessibilite.stories.ts +191 -0
- package/src/components/PasswordField/AccessibiliteItems.ts +184 -0
- package/src/components/PasswordField/PasswordField.vue +3 -3
- package/src/components/PasswordField/constants/ExpertiseLevelEnum.ts +4 -0
- package/src/components/PhoneField/PhoneField.vue +44 -60
- package/src/components/PhoneField/tests/PhoneField.spec.ts +0 -15
- package/src/components/RangeField/RangeField.mdx +54 -0
- package/src/components/RangeField/RangeField.stories.ts +189 -0
- package/src/components/RangeField/RangeField.vue +157 -0
- package/src/components/RangeField/RangeSlider/RangeSlider.vue +387 -0
- package/src/components/RangeField/RangeSlider/Tooltip/Tooltip.vue +64 -0
- package/src/components/RangeField/RangeSlider/tests/__snapshots__/rangeSlider.spec.ts.snap +27 -0
- package/src/components/RangeField/RangeSlider/tests/rangeSlider.spec.ts +100 -0
- package/src/components/RangeField/RangeSlider/tests/useDoubleSlider.spec.ts +246 -0
- package/src/components/RangeField/RangeSlider/tests/useMouseSlide.spec.ts +204 -0
- package/src/components/RangeField/RangeSlider/tests/useThumb.spec.ts +22 -0
- package/src/components/RangeField/RangeSlider/tests/useThumbKeyboard.spec.ts +233 -0
- package/src/components/RangeField/RangeSlider/tests/useTooltipsNudge.spec.ts +150 -0
- package/src/components/RangeField/RangeSlider/tests/useTrack.spec.ts +314 -0
- package/src/components/RangeField/RangeSlider/tests/vAnimateClick.spec.ts +32 -0
- package/src/components/RangeField/RangeSlider/types.ts +15 -0
- package/src/components/RangeField/RangeSlider/useMouseSlide.ts +109 -0
- package/src/components/RangeField/RangeSlider/useRangeSlider.ts +126 -0
- package/src/components/RangeField/RangeSlider/useThumb.ts +18 -0
- package/src/components/RangeField/RangeSlider/useThumbKeyboard.ts +84 -0
- package/src/components/RangeField/RangeSlider/useTooltipsNudge.ts +92 -0
- package/src/components/RangeField/RangeSlider/useTrack.ts +116 -0
- package/src/components/RangeField/RangeSlider/vAnimateClick.ts +19 -0
- package/src/components/RangeField/config.ts +7 -0
- package/src/components/RangeField/locales.ts +4 -0
- package/src/components/RangeField/tests/RangeField.spec.ts +224 -0
- package/src/components/RangeField/tests/__snapshots__/RangeField.spec.ts.snap +379 -0
- package/src/components/RatingPicker/EmotionPicker/EmotionPicker.vue +205 -0
- package/src/components/RatingPicker/EmotionPicker/locales.ts +3 -0
- package/src/components/RatingPicker/EmotionPicker/tests/EmotionPicker.spec.ts +104 -0
- package/src/components/RatingPicker/EmotionPicker/tests/__snapshots__/EmotionPicker.spec.ts.snap +66 -0
- package/src/components/RatingPicker/NumberPicker/NumberPicker.vue +159 -0
- package/src/components/RatingPicker/NumberPicker/locales.ts +4 -0
- package/src/components/RatingPicker/NumberPicker/tests/NumberPicker.spec.ts +73 -0
- package/src/components/RatingPicker/NumberPicker/tests/__snapshots__/NumberPicker.spec.ts.snap +105 -0
- package/src/components/RatingPicker/Rating.ts +45 -0
- package/src/components/RatingPicker/RatingPicker.mdx +56 -0
- package/src/components/RatingPicker/RatingPicker.stories.ts +515 -0
- package/src/components/RatingPicker/RatingPicker.vue +122 -0
- package/src/components/RatingPicker/StarsPicker/StarsPicker.vue +116 -0
- package/src/components/RatingPicker/StarsPicker/tests/StarsPicker.spec.ts +95 -0
- package/src/components/RatingPicker/StarsPicker/tests/__snapshots__/StarsPicker.spec.ts.snap +36 -0
- package/src/components/RatingPicker/locales.ts +3 -0
- package/src/components/RatingPicker/tests/Rating.spec.ts +104 -0
- package/src/components/RatingPicker/tests/RatingPicker.spec.ts +187 -0
- package/src/components/RatingPicker/tests/__snapshots__/RatingPicker.spec.ts.snap +108 -0
- package/src/components/SearchListField/SearchListField.mdx +74 -0
- package/src/components/SearchListField/SearchListField.stories.ts +126 -0
- package/src/components/SearchListField/SearchListField.vue +194 -0
- package/src/components/SearchListField/locales.ts +5 -0
- package/src/components/SearchListField/tests/SearchListField.spec.ts +323 -0
- package/src/components/SearchListField/types.d.ts +4 -0
- package/src/components/SelectBtnField/SelectBtnField.mdx +50 -0
- package/src/components/SelectBtnField/SelectBtnField.stories.ts +763 -0
- package/src/components/SelectBtnField/SelectBtnField.vue +283 -0
- package/src/components/SelectBtnField/config.ts +11 -0
- package/src/components/SelectBtnField/tests/SelectBtnField.spec.ts +327 -0
- package/src/components/SelectBtnField/tests/__snapshots__/SelectBtnField.spec.ts.snap +125 -0
- package/src/components/SelectBtnField/types.d.ts +11 -0
- package/src/components/index.ts +8 -1
- package/src/composables/rules/useFieldValidation.ts +172 -44
- package/src/designTokens/index.ts +3 -3
- package/src/stories/Fondamentaux/CustomisationEtThemes.mdx +52 -2
- package/src/utils/calcHumanFileSize/index.ts +12 -0
- package/src/utils/calcHumanFileSize/tests/calcHumanFileSize.spec.ts +21 -0
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import { type PropType, computed, onMounted } from 'vue'
|
|
3
|
+
import { RatingEnum, useRating } from '../Rating'
|
|
4
|
+
import { locales } from './locales'
|
|
5
|
+
import { propValidator } from '@/utils/propValidator'
|
|
6
|
+
import {
|
|
7
|
+
mdiEmoticonHappyOutline,
|
|
8
|
+
mdiEmoticonSadOutline,
|
|
9
|
+
mdiEmoticonNeutralOutline,
|
|
10
|
+
} from '@mdi/js'
|
|
11
|
+
|
|
12
|
+
import { useDisplay } from 'vuetify'
|
|
13
|
+
|
|
14
|
+
const { smAndDown } = useDisplay()
|
|
15
|
+
const isMobile = computed(() => smAndDown.value)
|
|
16
|
+
|
|
17
|
+
const props = defineProps({
|
|
18
|
+
label: {
|
|
19
|
+
type: String as PropType<string | null>,
|
|
20
|
+
default: RatingEnum.EMOTION,
|
|
21
|
+
},
|
|
22
|
+
length: {
|
|
23
|
+
type: Number,
|
|
24
|
+
default: 3,
|
|
25
|
+
validator: (value: number) => propValidator('length', ['2', '3'], value.toString()),
|
|
26
|
+
},
|
|
27
|
+
itemLabels: {
|
|
28
|
+
type: Array as PropType<string[]>,
|
|
29
|
+
default: () => locales.defaultEmotionLabels,
|
|
30
|
+
},
|
|
31
|
+
readonly: {
|
|
32
|
+
type: Boolean,
|
|
33
|
+
default: false,
|
|
34
|
+
},
|
|
35
|
+
modelValue: {
|
|
36
|
+
type: Number,
|
|
37
|
+
default: -1,
|
|
38
|
+
},
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
const sadIcon = mdiEmoticonSadOutline
|
|
42
|
+
const neutralIcon = mdiEmoticonNeutralOutline
|
|
43
|
+
const happyIcon = mdiEmoticonHappyOutline
|
|
44
|
+
|
|
45
|
+
const btnSize = computed(() => {
|
|
46
|
+
return isMobile.value ? '70px' : '88px'
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
const emit = defineEmits(['update:modelValue'])
|
|
50
|
+
const { hasAnswered, emitInputEvent } = useRating(props, emit)
|
|
51
|
+
|
|
52
|
+
const isActive = (index: number) => {
|
|
53
|
+
return index === props.modelValue - 1
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const getIcon = (index: number) => {
|
|
57
|
+
if (index === 0) {
|
|
58
|
+
return sadIcon
|
|
59
|
+
}
|
|
60
|
+
if (index === 1 && props.length === 3) {
|
|
61
|
+
return neutralIcon
|
|
62
|
+
}
|
|
63
|
+
return happyIcon
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const getColor = (index: number) => {
|
|
67
|
+
const colors = ['sad', 'neutral', 'happy']
|
|
68
|
+
if (props.length === 2) {
|
|
69
|
+
const filteredColors = colors.filter(item => item !== 'neutral')
|
|
70
|
+
return filteredColors[index]
|
|
71
|
+
}
|
|
72
|
+
return colors[index]
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const getEmotionLabel = (value: number) => {
|
|
76
|
+
if (props.length === 2) {
|
|
77
|
+
const filteredLabels = props.itemLabels.filter((_, index) => index !== 1)
|
|
78
|
+
return filteredLabels[value]
|
|
79
|
+
}
|
|
80
|
+
return props.itemLabels[value]
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
onMounted(() => {
|
|
84
|
+
const hiddenInputs = document.querySelectorAll('.v-rating__hidden')
|
|
85
|
+
hiddenInputs.forEach((input) => {
|
|
86
|
+
(input as HTMLElement).setAttribute('aria-hidden', 'true')
|
|
87
|
+
})
|
|
88
|
+
})
|
|
89
|
+
</script>
|
|
90
|
+
|
|
91
|
+
<template>
|
|
92
|
+
<fieldset class="vd-emotion-picker">
|
|
93
|
+
<legend class="text-h6 mb-6">
|
|
94
|
+
<slot name="label">
|
|
95
|
+
{{ props.label }}
|
|
96
|
+
</slot>
|
|
97
|
+
</legend>
|
|
98
|
+
|
|
99
|
+
<VRating
|
|
100
|
+
:model-value="props.modelValue"
|
|
101
|
+
:length="props.length"
|
|
102
|
+
:readonly="props.readonly"
|
|
103
|
+
class="max-width-none mx-n1 mx-sm-n2"
|
|
104
|
+
@update:model-value="(value) => emit('update:modelValue', value)"
|
|
105
|
+
>
|
|
106
|
+
<template #item="{ index }">
|
|
107
|
+
<VBtn
|
|
108
|
+
:disabled="props.readonly || hasAnswered"
|
|
109
|
+
:title="getEmotionLabel(index)"
|
|
110
|
+
:aria-pressed="isActive(index).toString()"
|
|
111
|
+
:class="[getColor(index), { 'v-btn--active': isActive(index) }]"
|
|
112
|
+
:min-height="btnSize"
|
|
113
|
+
:min-width="btnSize"
|
|
114
|
+
variant="text"
|
|
115
|
+
class="rounded-lg px-1 px-sm-4 mx-1 mx-sm-2"
|
|
116
|
+
@click="emitInputEvent(index + 1)"
|
|
117
|
+
>
|
|
118
|
+
<VIcon
|
|
119
|
+
size="40"
|
|
120
|
+
color="currentColor"
|
|
121
|
+
class="pa-0"
|
|
122
|
+
>
|
|
123
|
+
{{ getIcon(index) }}
|
|
124
|
+
</VIcon>
|
|
125
|
+
|
|
126
|
+
<span
|
|
127
|
+
v-if="getEmotionLabel(index)"
|
|
128
|
+
:class="{ 'text-secondary': !isActive(index) }"
|
|
129
|
+
class="text-subtitle-2 mt-1"
|
|
130
|
+
>
|
|
131
|
+
{{ getEmotionLabel(index) }}
|
|
132
|
+
</span>
|
|
133
|
+
</VBtn>
|
|
134
|
+
</template>
|
|
135
|
+
</VRating>
|
|
136
|
+
</fieldset>
|
|
137
|
+
</template>
|
|
138
|
+
|
|
139
|
+
<style lang="scss" scoped>
|
|
140
|
+
@use '@/assets/tokens';
|
|
141
|
+
|
|
142
|
+
.vd-emotion-picker {
|
|
143
|
+
border: 0;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.v-rating .v-btn {
|
|
147
|
+
transition: 0.2s;
|
|
148
|
+
border: 1px solid transparent;
|
|
149
|
+
opacity: 1;
|
|
150
|
+
background: transparent;
|
|
151
|
+
|
|
152
|
+
:deep(.v-btn__content) {
|
|
153
|
+
flex-direction: column;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.text-secondary {
|
|
157
|
+
color: tokens.$grey-darken-20 !important;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
&.sad {
|
|
161
|
+
color: tokens.$orange-darken-40 !important;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
&.neutral {
|
|
165
|
+
color: tokens.$yellow-darken-40 !important;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
&.happy {
|
|
169
|
+
color: tokens.$turquoise-darken-40 !important;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
&--active.v-btn--disabled .v-icon {
|
|
173
|
+
color: currentcolor !important;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
&:focus,
|
|
177
|
+
&--active {
|
|
178
|
+
border-color: currentcolor !important;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
&:hover,
|
|
182
|
+
&:focus,
|
|
183
|
+
&--active {
|
|
184
|
+
:deep(.v-btn__overlay, .v-btn__underlay) {
|
|
185
|
+
display: none;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
&.sad {
|
|
189
|
+
background: tokens.$orange-lighten-90;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
&.neutral {
|
|
193
|
+
background: tokens.$yellow-lighten-90;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
&.happy {
|
|
197
|
+
background: tokens.$turquoise-lighten-90;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
.v-theme--light.v-btn--disabled :deep(.v-icon) {
|
|
203
|
+
color: tokens.$grey-lighten-20 !important;
|
|
204
|
+
}
|
|
205
|
+
</style>
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { mount } from '@vue/test-utils'
|
|
3
|
+
import { vuetify } from '@tests/unit/setup'
|
|
4
|
+
|
|
5
|
+
import EmotionPicker from '../EmotionPicker.vue'
|
|
6
|
+
|
|
7
|
+
describe('EmotionPicker', () => {
|
|
8
|
+
it('renders correctly', () => {
|
|
9
|
+
const wrapper = mount(EmotionPicker, {
|
|
10
|
+
global: {
|
|
11
|
+
plugins: [vuetify],
|
|
12
|
+
},
|
|
13
|
+
propsData: {
|
|
14
|
+
label: 'Pourriez-vous donner une note ?',
|
|
15
|
+
itemLabels: ['Pas du tout', 'Peut-être', 'Oui super'],
|
|
16
|
+
},
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
expect(wrapper.html()).toContain('Pourriez-vous donner une note ?')
|
|
20
|
+
expect(wrapper.html()).toContain('Pas du tout')
|
|
21
|
+
expect(wrapper.html()).toContain('Peut-être')
|
|
22
|
+
expect(wrapper.html()).toContain('Oui super')
|
|
23
|
+
expect(wrapper.html()).toMatchSnapshot()
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it('renders correctly with only 2 items', () => {
|
|
27
|
+
const wrapper = mount(EmotionPicker, {
|
|
28
|
+
global: {
|
|
29
|
+
plugins: [vuetify],
|
|
30
|
+
},
|
|
31
|
+
propsData: {
|
|
32
|
+
label: 'Pourriez-vous donner une note ?',
|
|
33
|
+
itemLabels: ['Pas du tout', 'Not used', 'Oui super'],
|
|
34
|
+
length: 2,
|
|
35
|
+
},
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
expect(wrapper.html()).toContain('Pourriez-vous donner une note ?')
|
|
39
|
+
expect(wrapper.html()).toContain('Pas du tout')
|
|
40
|
+
expect(wrapper.html()).toContain('Oui super')
|
|
41
|
+
expect(wrapper.html()).not.toContain('Not used')
|
|
42
|
+
expect(wrapper.html()).toMatchSnapshot()
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it('renders in mobile mode', async () => {
|
|
46
|
+
// @ts-expect-error - Property 'happyDOM' does not exist on type 'Window & typeof globalThis'.
|
|
47
|
+
window.happyDOM.setInnerWidth(600)
|
|
48
|
+
|
|
49
|
+
const wrapper = mount(EmotionPicker, {
|
|
50
|
+
global: {
|
|
51
|
+
plugins: [vuetify],
|
|
52
|
+
},
|
|
53
|
+
propsData: {
|
|
54
|
+
label: 'Pourriez-vous donner une note ?',
|
|
55
|
+
itemLabels: ['Pas du tout', 'Peut-être', 'Oui super'],
|
|
56
|
+
},
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
await wrapper.vm.$nextTick()
|
|
60
|
+
|
|
61
|
+
expect(wrapper.html()).toContain('70px')
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
it('emit the right value when an item is clicked', async () => {
|
|
65
|
+
const wrapper = mount(EmotionPicker, {
|
|
66
|
+
global: {
|
|
67
|
+
plugins: [vuetify],
|
|
68
|
+
},
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
const items = wrapper.findAll('button')
|
|
72
|
+
await items[0].trigger('click')
|
|
73
|
+
expect(wrapper.emitted('update:modelValue')).toBeTruthy()
|
|
74
|
+
expect(wrapper.emitted('update:modelValue')?.[0]?.[0]).toBe(1)
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
it('have no labels when no labels are provided', () => {
|
|
78
|
+
const wrapper = mount(EmotionPicker, {
|
|
79
|
+
global: {
|
|
80
|
+
plugins: [vuetify],
|
|
81
|
+
},
|
|
82
|
+
propsData: {
|
|
83
|
+
itemLabels: [],
|
|
84
|
+
},
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
wrapper.findAll('button').forEach((button) => {
|
|
88
|
+
expect(button.text()).toBe('')
|
|
89
|
+
})
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
it('change the style of the buttons when an item is clicked', async () => {
|
|
93
|
+
const wrapper = mount(EmotionPicker, {
|
|
94
|
+
global: {
|
|
95
|
+
plugins: [vuetify],
|
|
96
|
+
},
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
const items = wrapper.findAll('button')
|
|
100
|
+
|
|
101
|
+
await wrapper.setProps({ modelValue: 1 })
|
|
102
|
+
expect(items[0].classes()).toContain('v-btn--active')
|
|
103
|
+
})
|
|
104
|
+
})
|
package/src/components/RatingPicker/EmotionPicker/tests/__snapshots__/EmotionPicker.spec.ts.snap
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
|
+
|
|
3
|
+
exports[`EmotionPicker > renders correctly 1`] = `
|
|
4
|
+
"<fieldset data-v-9f5befe4="" class="vd-emotion-picker">
|
|
5
|
+
<legend data-v-9f5befe4="" class="text-h6 mb-6">Pourriez-vous donner une note ?</legend>
|
|
6
|
+
<div data-v-9f5befe4="" class="v-rating v-theme--light max-width-none mx-n1 mx-sm-n2"><label for="v-rating-0-0" class=""><span class="v-rating__hidden">Rating 0 of 3</span>
|
|
7
|
+
<!---->
|
|
8
|
+
</label><input class="v-rating__hidden" name="v-rating-0" id="v-rating-0-0" type="radio" checked="" tabindex="-1" value="0">
|
|
9
|
+
<div class="v-rating__wrapper">
|
|
10
|
+
<!---->
|
|
11
|
+
<div class="v-rating__item"><label for="v-rating-0-1" class=""><span class="v-rating__hidden">Rating 1 of 3</span><button data-v-9f5befe4="" type="button" class="v-btn v-theme--light v-btn--density-default v-btn--size-default v-btn--variant-text sad rounded-lg px-1 px-sm-4 mx-1 mx-sm-2" style="min-height: 88px; min-width: 88px;" title="Pas du tout" aria-pressed="false"><span class="v-btn__overlay"></span><span class="v-btn__underlay"></span>
|
|
12
|
+
<!----><span class="v-btn__content" data-no-activator=""><i data-v-9f5befe4="" class="M20,12A8,8 0 0,0 12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,12M22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2A10,10 0 0,1 22,12M15.5,8C16.3,8 17,8.7 17,9.5C17,10.3 16.3,11 15.5,11C14.7,11 14,10.3 14,9.5C14,8.7 14.7,8 15.5,8M10,9.5C10,10.3 9.3,11 8.5,11C7.7,11 7,10.3 7,9.5C7,8.7 7.7,8 8.5,8C9.3,8 10,8.7 10,9.5M12,14C13.75,14 15.29,14.72 16.19,15.81L14.77,17.23C14.32,16.5 13.25,16 12,16C10.75,16 9.68,16.5 9.23,17.23L7.81,15.81C8.71,14.72 10.25,14 12,14Z mdi v-icon notranslate v-theme--light text-currentColor pa-0" style="font-size: 40px; height: 40px; width: 40px;" aria-hidden="true"></i><span data-v-9f5befe4="" class="text-secondary text-subtitle-2 mt-1">Pas du tout</span></span>
|
|
13
|
+
<!---->
|
|
14
|
+
<!---->
|
|
15
|
+
</button></label><input class="v-rating__hidden" name="v-rating-0" id="v-rating-0-1" type="radio" tabindex="-1" value="1"></div>
|
|
16
|
+
<!---->
|
|
17
|
+
</div>
|
|
18
|
+
<div class="v-rating__wrapper">
|
|
19
|
+
<!---->
|
|
20
|
+
<div class="v-rating__item"><label for="v-rating-0-2" class=""><span class="v-rating__hidden">Rating 2 of 3</span><button data-v-9f5befe4="" type="button" class="v-btn v-theme--light v-btn--density-default v-btn--size-default v-btn--variant-text neutral rounded-lg px-1 px-sm-4 mx-1 mx-sm-2" style="min-height: 88px; min-width: 88px;" title="Peut-être" aria-pressed="false"><span class="v-btn__overlay"></span><span class="v-btn__underlay"></span>
|
|
21
|
+
<!----><span class="v-btn__content" data-no-activator=""><i data-v-9f5befe4="" class="M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M12,20A8,8 0 0,1 4,12A8,8 0 0,1 12,4A8,8 0 0,1 20,12A8,8 0 0,1 12,20M8.5,11A1.5,1.5 0 0,1 7,9.5A1.5,1.5 0 0,1 8.5,8A1.5,1.5 0 0,1 10,9.5A1.5,1.5 0 0,1 8.5,11M17,9.5A1.5,1.5 0 0,1 15.5,11A1.5,1.5 0 0,1 14,9.5A1.5,1.5 0 0,1 15.5,8A1.5,1.5 0 0,1 17,9.5M16,14V16H8V14H16Z mdi v-icon notranslate v-theme--light text-currentColor pa-0" style="font-size: 40px; height: 40px; width: 40px;" aria-hidden="true"></i><span data-v-9f5befe4="" class="text-secondary text-subtitle-2 mt-1">Peut-être</span></span>
|
|
22
|
+
<!---->
|
|
23
|
+
<!---->
|
|
24
|
+
</button></label><input class="v-rating__hidden" name="v-rating-0" id="v-rating-0-2" type="radio" tabindex="-1" value="2"></div>
|
|
25
|
+
<!---->
|
|
26
|
+
</div>
|
|
27
|
+
<div class="v-rating__wrapper">
|
|
28
|
+
<!---->
|
|
29
|
+
<div class="v-rating__item"><label for="v-rating-0-3" class=""><span class="v-rating__hidden">Rating 3 of 3</span><button data-v-9f5befe4="" type="button" class="v-btn v-theme--light v-btn--density-default v-btn--size-default v-btn--variant-text happy rounded-lg px-1 px-sm-4 mx-1 mx-sm-2" style="min-height: 88px; min-width: 88px;" title="Oui super" aria-pressed="false"><span class="v-btn__overlay"></span><span class="v-btn__underlay"></span>
|
|
30
|
+
<!----><span class="v-btn__content" data-no-activator=""><i data-v-9f5befe4="" class="M20,12A8,8 0 0,0 12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,12M22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2A10,10 0 0,1 22,12M10,9.5C10,10.3 9.3,11 8.5,11C7.7,11 7,10.3 7,9.5C7,8.7 7.7,8 8.5,8C9.3,8 10,8.7 10,9.5M17,9.5C17,10.3 16.3,11 15.5,11C14.7,11 14,10.3 14,9.5C14,8.7 14.7,8 15.5,8C16.3,8 17,8.7 17,9.5M12,17.23C10.25,17.23 8.71,16.5 7.81,15.42L9.23,14C9.68,14.72 10.75,15.23 12,15.23C13.25,15.23 14.32,14.72 14.77,14L16.19,15.42C15.29,16.5 13.75,17.23 12,17.23Z mdi v-icon notranslate v-theme--light text-currentColor pa-0" style="font-size: 40px; height: 40px; width: 40px;" aria-hidden="true"></i><span data-v-9f5befe4="" class="text-secondary text-subtitle-2 mt-1">Oui super</span></span>
|
|
31
|
+
<!---->
|
|
32
|
+
<!---->
|
|
33
|
+
</button></label><input class="v-rating__hidden" name="v-rating-0" id="v-rating-0-3" type="radio" tabindex="-1" value="3"></div>
|
|
34
|
+
<!---->
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
37
|
+
</fieldset>"
|
|
38
|
+
`;
|
|
39
|
+
|
|
40
|
+
exports[`EmotionPicker > renders correctly with only 2 items 1`] = `
|
|
41
|
+
"<fieldset data-v-9f5befe4="" class="vd-emotion-picker">
|
|
42
|
+
<legend data-v-9f5befe4="" class="text-h6 mb-6">Pourriez-vous donner une note ?</legend>
|
|
43
|
+
<div data-v-9f5befe4="" class="v-rating v-theme--light max-width-none mx-n1 mx-sm-n2"><label for="v-rating-0-0" class=""><span class="v-rating__hidden">Rating 0 of 2</span>
|
|
44
|
+
<!---->
|
|
45
|
+
</label><input class="v-rating__hidden" name="v-rating-0" id="v-rating-0-0" type="radio" checked="" tabindex="-1" value="0">
|
|
46
|
+
<div class="v-rating__wrapper">
|
|
47
|
+
<!---->
|
|
48
|
+
<div class="v-rating__item"><label for="v-rating-0-1" class=""><span class="v-rating__hidden">Rating 1 of 2</span><button data-v-9f5befe4="" type="button" class="v-btn v-theme--light v-btn--density-default v-btn--size-default v-btn--variant-text sad rounded-lg px-1 px-sm-4 mx-1 mx-sm-2" style="min-height: 88px; min-width: 88px;" title="Pas du tout" aria-pressed="false"><span class="v-btn__overlay"></span><span class="v-btn__underlay"></span>
|
|
49
|
+
<!----><span class="v-btn__content" data-no-activator=""><i data-v-9f5befe4="" class="M20,12A8,8 0 0,0 12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,12M22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2A10,10 0 0,1 22,12M15.5,8C16.3,8 17,8.7 17,9.5C17,10.3 16.3,11 15.5,11C14.7,11 14,10.3 14,9.5C14,8.7 14.7,8 15.5,8M10,9.5C10,10.3 9.3,11 8.5,11C7.7,11 7,10.3 7,9.5C7,8.7 7.7,8 8.5,8C9.3,8 10,8.7 10,9.5M12,14C13.75,14 15.29,14.72 16.19,15.81L14.77,17.23C14.32,16.5 13.25,16 12,16C10.75,16 9.68,16.5 9.23,17.23L7.81,15.81C8.71,14.72 10.25,14 12,14Z mdi v-icon notranslate v-theme--light text-currentColor pa-0" style="font-size: 40px; height: 40px; width: 40px;" aria-hidden="true"></i><span data-v-9f5befe4="" class="text-secondary text-subtitle-2 mt-1">Pas du tout</span></span>
|
|
50
|
+
<!---->
|
|
51
|
+
<!---->
|
|
52
|
+
</button></label><input class="v-rating__hidden" name="v-rating-0" id="v-rating-0-1" type="radio" tabindex="-1" value="1"></div>
|
|
53
|
+
<!---->
|
|
54
|
+
</div>
|
|
55
|
+
<div class="v-rating__wrapper">
|
|
56
|
+
<!---->
|
|
57
|
+
<div class="v-rating__item"><label for="v-rating-0-2" class=""><span class="v-rating__hidden">Rating 2 of 2</span><button data-v-9f5befe4="" type="button" class="v-btn v-theme--light v-btn--density-default v-btn--size-default v-btn--variant-text happy rounded-lg px-1 px-sm-4 mx-1 mx-sm-2" style="min-height: 88px; min-width: 88px;" title="Oui super" aria-pressed="false"><span class="v-btn__overlay"></span><span class="v-btn__underlay"></span>
|
|
58
|
+
<!----><span class="v-btn__content" data-no-activator=""><i data-v-9f5befe4="" class="M20,12A8,8 0 0,0 12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,12M22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2A10,10 0 0,1 22,12M10,9.5C10,10.3 9.3,11 8.5,11C7.7,11 7,10.3 7,9.5C7,8.7 7.7,8 8.5,8C9.3,8 10,8.7 10,9.5M17,9.5C17,10.3 16.3,11 15.5,11C14.7,11 14,10.3 14,9.5C14,8.7 14.7,8 15.5,8C16.3,8 17,8.7 17,9.5M12,17.23C10.25,17.23 8.71,16.5 7.81,15.42L9.23,14C9.68,14.72 10.75,15.23 12,15.23C13.25,15.23 14.32,14.72 14.77,14L16.19,15.42C15.29,16.5 13.75,17.23 12,17.23Z mdi v-icon notranslate v-theme--light text-currentColor pa-0" style="font-size: 40px; height: 40px; width: 40px;" aria-hidden="true"></i><span data-v-9f5befe4="" class="text-secondary text-subtitle-2 mt-1">Oui super</span></span>
|
|
59
|
+
<!---->
|
|
60
|
+
<!---->
|
|
61
|
+
</button></label><input class="v-rating__hidden" name="v-rating-0" id="v-rating-0-2" type="radio" tabindex="-1" value="2"></div>
|
|
62
|
+
<!---->
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
</fieldset>"
|
|
66
|
+
`;
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import { computed, onMounted } from 'vue'
|
|
3
|
+
import { RatingEnum, useRating } from '../Rating'
|
|
4
|
+
import { locales } from './locales'
|
|
5
|
+
import type { PropType } from 'vue'
|
|
6
|
+
|
|
7
|
+
import SySelect from '@/components/Customs/SySelect/SySelect.vue'
|
|
8
|
+
|
|
9
|
+
import { useDisplay } from 'vuetify'
|
|
10
|
+
|
|
11
|
+
interface SelectItem {
|
|
12
|
+
text: string
|
|
13
|
+
value: number
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const props = defineProps({
|
|
17
|
+
label: {
|
|
18
|
+
type: String as PropType<string | undefined>,
|
|
19
|
+
default: RatingEnum.NUMBER,
|
|
20
|
+
},
|
|
21
|
+
length: {
|
|
22
|
+
type: Number,
|
|
23
|
+
default: 10,
|
|
24
|
+
},
|
|
25
|
+
itemLabels: {
|
|
26
|
+
type: Array as PropType<string[]>,
|
|
27
|
+
default: () => [],
|
|
28
|
+
},
|
|
29
|
+
readonly: {
|
|
30
|
+
type: Boolean,
|
|
31
|
+
default: false,
|
|
32
|
+
},
|
|
33
|
+
modelValue: {
|
|
34
|
+
type: Number,
|
|
35
|
+
default: -1,
|
|
36
|
+
},
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
const { smAndDown } = useDisplay()
|
|
40
|
+
const isMobile = computed(() => smAndDown.value)
|
|
41
|
+
|
|
42
|
+
const emit = defineEmits(['update:modelValue'])
|
|
43
|
+
const { hasAnswered, emitInputEvent } = useRating(props, emit)
|
|
44
|
+
|
|
45
|
+
const selectItems = computed<SelectItem[]>(() => {
|
|
46
|
+
return [...Array(props.length)].map((_, index) => ({
|
|
47
|
+
text: `${index + 1}`,
|
|
48
|
+
value: index + 1,
|
|
49
|
+
}))
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
const shouldDisplayLabels = computed(() => props.itemLabels.length === 2)
|
|
53
|
+
|
|
54
|
+
onMounted(() => {
|
|
55
|
+
const hiddenInputs = document.querySelectorAll('.v-rating__hidden')
|
|
56
|
+
hiddenInputs.forEach((input) => {
|
|
57
|
+
(input as HTMLElement).setAttribute('aria-hidden', 'true')
|
|
58
|
+
})
|
|
59
|
+
})
|
|
60
|
+
</script>
|
|
61
|
+
|
|
62
|
+
<template>
|
|
63
|
+
<fieldset class="vd-number-picker">
|
|
64
|
+
<legend class="d-sr-only">
|
|
65
|
+
<slot name="label">
|
|
66
|
+
{{ props.label }}
|
|
67
|
+
</slot>
|
|
68
|
+
</legend>
|
|
69
|
+
<SySelect
|
|
70
|
+
v-if="isMobile"
|
|
71
|
+
:model-value="props.modelValue === -1 ? undefined : props.modelValue"
|
|
72
|
+
:label="props.label"
|
|
73
|
+
:disabled="props.readonly || hasAnswered"
|
|
74
|
+
:items="selectItems"
|
|
75
|
+
@update:model-value="(value) => emit('update:modelValue', value)"
|
|
76
|
+
/>
|
|
77
|
+
<template v-else>
|
|
78
|
+
<legend class="text-h6 mb-6">
|
|
79
|
+
<slot name="label">
|
|
80
|
+
{{ props.label }}
|
|
81
|
+
</slot>
|
|
82
|
+
</legend>
|
|
83
|
+
<div
|
|
84
|
+
v-if="!hasAnswered"
|
|
85
|
+
class="d-inline-block"
|
|
86
|
+
>
|
|
87
|
+
<VRating
|
|
88
|
+
:model-value="props.modelValue"
|
|
89
|
+
:length="props.length"
|
|
90
|
+
:readonly="props.readonly || hasAnswered"
|
|
91
|
+
class="d-flex flex-wrap max-width-none"
|
|
92
|
+
@update:model-value="(value) => emit('update:modelValue', value)"
|
|
93
|
+
>
|
|
94
|
+
<template #item="{ index }">
|
|
95
|
+
<VBtn
|
|
96
|
+
:aria-label="locales.ariaLabel(index + 1, props.length)"
|
|
97
|
+
:disabled="props.readonly"
|
|
98
|
+
size="x-small"
|
|
99
|
+
variant="outlined"
|
|
100
|
+
color="primary"
|
|
101
|
+
height="36px"
|
|
102
|
+
class="text-body-2 pa-0 mr-2"
|
|
103
|
+
@click="emitInputEvent(index + 1)"
|
|
104
|
+
>
|
|
105
|
+
{{ index + 1 }}
|
|
106
|
+
</VBtn>
|
|
107
|
+
</template>
|
|
108
|
+
</VRating>
|
|
109
|
+
<div
|
|
110
|
+
v-if="shouldDisplayLabels"
|
|
111
|
+
class="d-flex justify-space-between mt-1"
|
|
112
|
+
>
|
|
113
|
+
<span
|
|
114
|
+
:aria-label="`${locales.ariaLabel(1, props.length)} ${
|
|
115
|
+
props.itemLabels[0]
|
|
116
|
+
}.`"
|
|
117
|
+
class="text-caption"
|
|
118
|
+
v-text="props.itemLabels[0]"
|
|
119
|
+
/>
|
|
120
|
+
<span
|
|
121
|
+
:aria-label="`${locales.ariaLabel(props.length, props.length)} ${
|
|
122
|
+
props.itemLabels[1]
|
|
123
|
+
}.`"
|
|
124
|
+
class="text-caption mr-2"
|
|
125
|
+
v-text="props.itemLabels[1]"
|
|
126
|
+
/>
|
|
127
|
+
</div>
|
|
128
|
+
</div>
|
|
129
|
+
<p
|
|
130
|
+
v-else
|
|
131
|
+
:aria-label="locales.ariaLabel(props.modelValue, props.length)"
|
|
132
|
+
class="mb-0 d-flex align-center"
|
|
133
|
+
>
|
|
134
|
+
<VBtn
|
|
135
|
+
aria-hidden="true"
|
|
136
|
+
:disabled="true"
|
|
137
|
+
size="x-small"
|
|
138
|
+
variant="outlined"
|
|
139
|
+
color="primary"
|
|
140
|
+
height="36px"
|
|
141
|
+
class="vd-btn-answer text-body-2 mr-1 pa-0"
|
|
142
|
+
>
|
|
143
|
+
{{ props.modelValue }}
|
|
144
|
+
</VBtn>
|
|
145
|
+
/ {{ props.length }}
|
|
146
|
+
</p>
|
|
147
|
+
</template>
|
|
148
|
+
</fieldset>
|
|
149
|
+
</template>
|
|
150
|
+
|
|
151
|
+
<style lang="scss" scoped>
|
|
152
|
+
.vd-number-picker {
|
|
153
|
+
border: 0;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.vd-btn-answer.v-btn.v-btn--disabled {
|
|
157
|
+
opacity: 1;
|
|
158
|
+
}
|
|
159
|
+
</style>
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { mount } from '@vue/test-utils'
|
|
3
|
+
import { vuetify } from '@tests/unit/setup'
|
|
4
|
+
|
|
5
|
+
import NumberPicker from '../NumberPicker.vue'
|
|
6
|
+
|
|
7
|
+
describe('NumberPicker', () => {
|
|
8
|
+
it('renders correctly', () => {
|
|
9
|
+
const wrapper = mount(NumberPicker, {
|
|
10
|
+
global: {
|
|
11
|
+
plugins: [vuetify],
|
|
12
|
+
},
|
|
13
|
+
propsData: {
|
|
14
|
+
label: 'Pourriez-vous donner une note ?',
|
|
15
|
+
},
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
const btns = wrapper.findAll('button')
|
|
19
|
+
|
|
20
|
+
expect(btns).toHaveLength(10)
|
|
21
|
+
btns.forEach((btn, i) => {
|
|
22
|
+
expect(btn.text()).toBe((i + 1).toString())
|
|
23
|
+
})
|
|
24
|
+
expect(wrapper.html()).toMatchSnapshot()
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
it('emits an event when a number is selected', async () => {
|
|
28
|
+
const wrapper = mount(NumberPicker, {
|
|
29
|
+
global: {
|
|
30
|
+
plugins: [vuetify],
|
|
31
|
+
},
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
await wrapper.findAll('button')!.at(3)!.trigger('click')
|
|
35
|
+
|
|
36
|
+
expect(wrapper.emitted('update:modelValue')![0]).toEqual([4])
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it('change the displayed value when the modelValue is updated', async () => {
|
|
40
|
+
const wrapper = mount(NumberPicker, {
|
|
41
|
+
global: {
|
|
42
|
+
plugins: [vuetify],
|
|
43
|
+
},
|
|
44
|
+
props: {
|
|
45
|
+
modelValue: 3,
|
|
46
|
+
},
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
const btn = wrapper.findAll('button')[0]
|
|
50
|
+
|
|
51
|
+
await wrapper.setProps({ modelValue: 4 })
|
|
52
|
+
expect(btn.text()).toBe('4')
|
|
53
|
+
|
|
54
|
+
await wrapper.setProps({ modelValue: 5 })
|
|
55
|
+
expect(btn.text()).toBe('5')
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
it('renders correctly in xs window', async () => {
|
|
59
|
+
// @ts-expect-error - Property 'happyDOM' does not exist on type 'Window & typeof globalThis'.
|
|
60
|
+
window.happyDOM.setInnerWidth(600)
|
|
61
|
+
|
|
62
|
+
const wrapper = mount(NumberPicker, {
|
|
63
|
+
global: {
|
|
64
|
+
plugins: [vuetify],
|
|
65
|
+
},
|
|
66
|
+
})
|
|
67
|
+
await wrapper.vm.$nextTick()
|
|
68
|
+
|
|
69
|
+
const select = wrapper.find('.sy-select')
|
|
70
|
+
|
|
71
|
+
expect(select.exists()).toBe(true)
|
|
72
|
+
})
|
|
73
|
+
})
|