@livestorm/ui-vue 1.1.0 → 1.1.2

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 (80) hide show
  1. package/.env +1 -0
  2. package/.eslintignore +4 -0
  3. package/.eslintrc.js +47 -0
  4. package/.pkg +11 -0
  5. package/babel.config.js +5 -0
  6. package/build/Dockerfile +31 -0
  7. package/ci/ci.yml +54 -0
  8. package/components-vite.js +29 -0
  9. package/components-webpack.js +29 -0
  10. package/components.d.ts +4 -0
  11. package/index.d.ts +3 -0
  12. package/index.js +24 -0
  13. package/package.json +12 -16
  14. package/plugins.js +17 -0
  15. package/postcss.config.js +1 -0
  16. package/src/components/BaseAutocomplete/BaseAutocomplete.spec.js +506 -0
  17. package/src/components/BaseAutocomplete/BaseAutocomplete.stories.js +184 -0
  18. package/src/components/BaseAutocomplete/BaseAutocomplete.vue +431 -0
  19. package/src/components/BaseBanner/BaseBanner.spec.js +32 -0
  20. package/src/components/BaseBanner/BaseBanner.stories.js +133 -0
  21. package/src/components/BaseBanner/BaseBanner.vue +229 -0
  22. package/src/components/BaseButton/BaseButton.spec.js +98 -0
  23. package/src/components/BaseButton/BaseButton.stories.js +291 -0
  24. package/src/components/BaseButton/BaseButton.vue +193 -0
  25. package/src/components/BaseButton/__snapshots__/BaseButton.spec.js.snap +37 -0
  26. package/src/components/BaseButton/__snapshots__/BaseButton.spec.js.snap.old +31 -0
  27. package/src/components/BaseCheckbox/BaseCheckbox.stories.js +82 -0
  28. package/src/components/BaseCheckbox/BaseCheckbox.vue +161 -0
  29. package/src/components/BaseDropdownMenu/BaseDropdownMenu.stories.js +84 -0
  30. package/src/components/BaseDropdownMenu/BaseDropdownMenu.vue +251 -0
  31. package/src/components/BaseDropdownMenu/BaseDropdownMenuButton.vue +26 -0
  32. package/src/components/BaseDropdownMenu/BaseDropdownMenuDivider.vue +10 -0
  33. package/src/components/BaseDropdownMenu/BaseDropdownMenuItem.vue +206 -0
  34. package/src/components/BaseDropdownMenu/BaseDropdownMenuSearch.vue +49 -0
  35. package/src/components/BaseDropdownMenu/BaseDropdownMenuTitle.vue +26 -0
  36. package/src/components/BaseModal/BaseModal.stories.js +143 -0
  37. package/src/components/BaseModal/BaseModal.vue +187 -0
  38. package/src/components/BaseModal/BaseModalContent.vue +164 -0
  39. package/src/components/BaseModal/BaseModalContentOld.vue +242 -0
  40. package/src/components/BaseRating/BaseRating.spec.js +46 -0
  41. package/src/components/BaseRating/BaseRating.stories.js +46 -0
  42. package/src/components/BaseRating/BaseRating.vue +119 -0
  43. package/src/components/BaseSelect/BaseSelect.stories.js +177 -0
  44. package/src/components/BaseSelect/BaseSelect.vue +443 -0
  45. package/src/components/BaseSelect/BaseSelectGroup.vue +34 -0
  46. package/src/components/BaseSelect/BaseSelectOption.vue +219 -0
  47. package/src/components/BaseSwitch/BaseSwitch.stories.js +44 -0
  48. package/src/components/BaseSwitch/BaseSwitch.vue +114 -0
  49. package/src/components/BaseTag/BaseTag.stories.js +123 -0
  50. package/src/components/BaseTag/BaseTag.vue +244 -0
  51. package/src/css/body.postcss +29 -0
  52. package/src/css/button.postcss +354 -0
  53. package/src/css/checkbox-radio.postcss +152 -0
  54. package/src/css/icon.postcss +9 -0
  55. package/src/css/loader.postcss +120 -0
  56. package/src/css/popper.postcss +143 -0
  57. package/src/css/tailwind-utilities.postcss +1 -0
  58. package/src/css/tailwind.postcss +2 -0
  59. package/src/css/transitions.postcss +66 -0
  60. package/src/mixins/DynamicAction.js +59 -0
  61. package/src/utils/breakpoints.js +8 -0
  62. package/src/utils/bus.js +3 -0
  63. package/src/utils/clipboard.js +38 -0
  64. package/src/utils/devices.js +134 -0
  65. package/src/utils/elements.js +25 -0
  66. package/src/utils/index.js +8 -0
  67. package/src/utils/responsive.js +61 -0
  68. package/src/utils/router.js +94 -0
  69. package/src/utils/slot.js +27 -0
  70. package/src/utils/stack-view.js +116 -0
  71. package/src/utils/uid.js +5 -0
  72. package/style-postcss.d.ts +3 -0
  73. package/style-postcss.js +2 -0
  74. package/style.js +12 -0
  75. package/tailwind.config.base.cjs +88 -0
  76. package/tailwind.config.js +9 -0
  77. package/tsconfig.check.json +13 -0
  78. package/tsconfig.json +66 -0
  79. package/vite.config.js +43 -0
  80. package/vitest.setup.js +26 -0
@@ -0,0 +1,187 @@
1
+ <script lang="ts">
2
+ import type { InjectionKey } from 'vue'
3
+
4
+ interface PropsFromContent {
5
+ locked?: boolean
6
+ fullScreen?: boolean
7
+ }
8
+
9
+ export const InjectPropsFromContent = Symbol('BaseModal.propsFromContent') as InjectionKey<PropsFromContent>
10
+
11
+ export const InjectClose = Symbol('BaseModal.close') as InjectionKey<() => void>
12
+
13
+ type OnCloseHandler = () => unknown
14
+
15
+ export const InjectOnClose = Symbol('BaseModal.onClose') as InjectionKey<(handler: OnCloseHandler) => void>
16
+ </script>
17
+
18
+ <script setup lang="ts">
19
+ import { computed, onMounted, onUnmounted, provide, reactive, ref, toRefs } from 'vue'
20
+ // @ts-ignore
21
+ import { useStackedComponent, useStackAutofocus } from '../../utils'
22
+
23
+ const props = withDefaults(defineProps<{
24
+ shown?: boolean
25
+ }>(), { shown: true })
26
+
27
+ const emit = defineEmits([
28
+ 'close',
29
+ 'update:shown',
30
+ ])
31
+
32
+ const propsFromContent = reactive<PropsFromContent>({
33
+ locked: false,
34
+ fullScreen: false,
35
+ })
36
+ provide(InjectPropsFromContent, propsFromContent)
37
+
38
+ const lockedFinal = computed(() => propsFromContent.locked)
39
+ const fullScreenFinal = computed(() => propsFromContent.fullScreen)
40
+
41
+ function close () {
42
+ if (!lockedFinal.value) {
43
+ emit('update:shown', false)
44
+ emit('close')
45
+ }
46
+ }
47
+
48
+ provide(InjectClose, close)
49
+
50
+ const onCloseHandlers: OnCloseHandler[] = []
51
+
52
+ provide(InjectOnClose, (handler) => {
53
+ onCloseHandlers.push(handler)
54
+
55
+ // Child context
56
+ // On child unmount
57
+ onUnmounted(() => {
58
+ const index = onCloseHandlers.indexOf(handler)
59
+ onCloseHandlers.splice(index, 1)
60
+ })
61
+ })
62
+
63
+ function closeHandler () {
64
+ if (!lockedFinal.value) {
65
+ close()
66
+ onCloseHandlers.forEach(handler => handler())
67
+ }
68
+ }
69
+
70
+ const { shown } = toRefs(props)
71
+ const {
72
+ isActiveStacked,
73
+ stackIndex,
74
+ onClose,
75
+ } = useStackedComponent(shown, 'modal')
76
+ onClose(closeHandler)
77
+
78
+ // Allow the opening animation in case `shown` is initially true
79
+
80
+ const firstFrame = ref(false)
81
+
82
+ onMounted(() => {
83
+ requestAnimationFrame(() => {
84
+ firstFrame.value = true
85
+ })
86
+ })
87
+
88
+ const finalShown = computed(() => shown.value && firstFrame.value)
89
+
90
+ // Autofocus
91
+ useStackAutofocus()
92
+ </script>
93
+
94
+ <template>
95
+ <SafeTeleport
96
+ to="#teleport-end"
97
+ >
98
+ <transition
99
+ :duration="200"
100
+ name="modal"
101
+ appear
102
+ >
103
+ <!-- Suspense will wait for async child components before rendering (so the transition will wait for them to be loaded) -->
104
+ <Suspense
105
+ v-if="finalShown"
106
+ >
107
+ <div
108
+ class="base-modal-wrapper fixed inset-0 overflow-y-auto"
109
+ :class="[
110
+ isActiveStacked ? 'stack-active' : 'grayscale stack-inactive',
111
+ {
112
+ 'full-screen': fullScreenFinal,
113
+ },
114
+ ]"
115
+ :style="{
116
+ 'z-index': stackIndex + 8000,
117
+ }"
118
+ data-testid="modal-wrapper"
119
+ >
120
+ <!-- Close overlay -->
121
+ <div
122
+ class="absolute inset-0"
123
+ @click="closeHandler()"
124
+ />
125
+
126
+ <div class="backdrop bg-alpha-black-300 fixed inset-0 -z-10" />
127
+
128
+ <slot />
129
+ </div>
130
+ </Suspense>
131
+ </transition>
132
+ </SafeTeleport>
133
+ </template>
134
+
135
+ <style lang="postcss" scoped>
136
+ .base-modal-wrapper {
137
+ &.full-screen {
138
+ overflow-y: initial;
139
+
140
+ :deep(.close-button) {
141
+ height: 100%;
142
+ display: flex;
143
+ flex-direction: column;
144
+ justify-content: center;
145
+ margin: 0;
146
+ .icon {
147
+ margin: 0;
148
+ }
149
+ }
150
+
151
+ :deep(.base-modal-layout) {
152
+ height: 100%;
153
+ padding: 0;
154
+
155
+ .base-modal-header {
156
+ background-color: #fff;
157
+ margin: 0 !important;
158
+ z-index: 50;
159
+ position: sticky;
160
+ top: 0;
161
+ }
162
+
163
+ & > section {
164
+ margin: 0;
165
+ padding: 0;
166
+ width: 100%;
167
+ height: 100%;
168
+ max-width: unset;
169
+ border-radius: 0px;
170
+ display: flex;
171
+ flex-direction: column;
172
+ }
173
+
174
+ .base-modal-content {
175
+ height: 100%;
176
+ overflow-y: auto;
177
+ }
178
+ }
179
+ }
180
+
181
+ &.stack-inactive {
182
+ :deep(.base-modal-layout > section) {
183
+ @apply scale-95;
184
+ }
185
+ }
186
+ }
187
+ </style>
@@ -0,0 +1,164 @@
1
+ <script setup lang="ts">
2
+ import { computed, defineEmits, defineProps, inject, watch } from 'vue'
3
+ import BaseButton from '../BaseButton/BaseButton.vue'
4
+ import { useSlotsAsComponents } from '../../utils/slot'
5
+ import { InjectClose, InjectOnClose, InjectPropsFromContent } from './BaseModal.vue'
6
+
7
+ interface ModalProps {
8
+ title: string
9
+ locked?: boolean
10
+ width?: number
11
+ confirmLabel?: string
12
+ cancelLabel?: string
13
+ confirmLoading?: boolean
14
+ confirmColor?: string
15
+ confirmHref?: string
16
+ type?: 'alert'|'warning'|'info'|'success'
17
+ }
18
+
19
+ const props = defineProps<ModalProps>()
20
+
21
+ // Send back to BaseModal the props used in BaseModal
22
+
23
+ const propsToParent = inject(InjectPropsFromContent)
24
+
25
+ if (!propsToParent) {
26
+ throw new Error('BaseModalContent must be used inside a BaseModal')
27
+ }
28
+
29
+ watch(() => props.locked, value => {
30
+ propsToParent.locked = value
31
+ }, { immediate: true })
32
+
33
+ // Close
34
+
35
+ const closeParent = inject(InjectClose)!
36
+
37
+ function close (alsoCloseParent = true) {
38
+ if (!props.locked) {
39
+ emit('close')
40
+ if (alsoCloseParent) {
41
+ closeParent()
42
+ }
43
+ }
44
+ }
45
+
46
+ const onParentClose = inject(InjectOnClose)!
47
+
48
+ onParentClose(() => {
49
+ close(false)
50
+ })
51
+
52
+ // Slots
53
+ const slots = useSlotsAsComponents()
54
+ const contentSlot = computed(() => slots.content ? slots.content() : [])
55
+ const buttonsSlot = computed(() => slots['additional-buttons']!())
56
+
57
+ const emit = defineEmits([
58
+ 'confirm',
59
+ 'cancel',
60
+ 'close',
61
+ ])
62
+
63
+ // Types
64
+
65
+ const typeIcon = computed(() => {
66
+ switch (props.type) {
67
+ case 'alert':
68
+ return 'i-ls-alert-circle text-red-600'
69
+ case 'warning':
70
+ return 'i-ls-alert-triangle text-orange-600'
71
+ case 'info':
72
+ return 'i-ls-info text-livestorm-600'
73
+ case 'success':
74
+ return 'i-ls-check-circle text-green-600'
75
+ default:
76
+ return 'i-ls-alert-circle text-livestorm-600'
77
+ }
78
+ })
79
+ </script>
80
+
81
+ <template>
82
+ <div class="flex min-h-full items-end justify-center p-2 text-center sm:items-center">
83
+ <div
84
+ class="relative bg-white w-full min-w-[320px] max-w-[650px] rounded-8 base-modal-box"
85
+ :style="{ 'max-width': `${width}px` }"
86
+ @click.stop
87
+ >
88
+ <div class="flex justify-between">
89
+ <div
90
+ v-if="props.title"
91
+ class="flex items-center px-6 pt-6"
92
+ >
93
+ <div
94
+ v-if="props.type"
95
+ class="w-6 h-6 mr-2 sm:mr-3 shrink-0"
96
+ :class="typeIcon"
97
+ />
98
+ <span
99
+ data-testid="modal-title"
100
+ class="block text-16-semibold text-grey-900 text-left"
101
+ :class="{ 'pb-6': !contentSlot }"
102
+ >
103
+ {{ props.title }}
104
+ </span>
105
+ </div>
106
+ <span v-else />
107
+ <div
108
+ v-if="!locked"
109
+ class="i-ls-x w-6 h-6 text-grey-700 hover:text-grey-900 cursor-pointer shrink-0 mt-6 mr-6"
110
+ data-testid="modal-close-icon"
111
+ tabindex="0"
112
+ role="button"
113
+ aria-label="close"
114
+ @click="close()"
115
+ @keydown.enter="close()"
116
+ />
117
+ </div>
118
+ <div
119
+ v-if="contentSlot"
120
+ class="p-6 text-left text-grey-700"
121
+ data-testid="modal-content"
122
+ >
123
+ <slot name="content" />
124
+ </div>
125
+ <div
126
+ v-if="confirmLabel || cancelLabel || buttonsSlot"
127
+ class="px-6 pb-6 flex flex-row-reverse w-full sm:w-auto"
128
+ :class="{ 'pt-6': !props.title && !contentSlot }"
129
+ >
130
+ <div
131
+ class="flex flex-col-reverse sm:flex-row gap-2.5 w-full justify-end"
132
+ >
133
+ <slot name="additional-buttons" />
134
+
135
+ <BaseButton
136
+ v-if="cancelLabel"
137
+ class="base-button-anti-primary w-full sm:w-auto"
138
+ @click="$emit('cancel')"
139
+ >
140
+ {{ cancelLabel }}
141
+ </BaseButton>
142
+
143
+ <BaseButton
144
+ v-if="confirmLabel"
145
+ :loading="confirmLoading"
146
+ :href="confirmHref"
147
+ :target="confirmHref ? '_blank' : null"
148
+ class="w-full sm:w-auto"
149
+ :class="confirmColor ? `base-button-${confirmColor}` : 'base-button-primary'"
150
+ @click="$emit('confirm', $event)"
151
+ >
152
+ {{ confirmLabel }}
153
+ </BaseButton>
154
+ </div>
155
+ </div>
156
+ </div>
157
+ </div>
158
+ </template>
159
+
160
+ <style scoped lang="postcss">
161
+ .base-modal-box {
162
+ box-shadow: 0px 2px 4px 0px #0D16261A;
163
+ }
164
+ </style>
@@ -0,0 +1,242 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * @TODO Remove it once the fullscreen mode is supported
4
+ * in BaseModal or another dedicated component (to define)
5
+ */
6
+ import { computed, inject, watch, toRefs } from 'vue'
7
+ import { useSlotsAsComponents } from '../../utils/slot'
8
+ import { InjectPropsFromContent, InjectClose, InjectOnClose } from './BaseModal.vue'
9
+
10
+ const props = defineProps({
11
+ locked: {
12
+ type: Boolean,
13
+ default: false,
14
+ },
15
+
16
+ width: {
17
+ type: Number,
18
+ default: null,
19
+ },
20
+
21
+ // @deprecated
22
+ fullScreen: {
23
+ type: Boolean,
24
+ default: false,
25
+ },
26
+
27
+ // @deprecated
28
+ noPadding: {
29
+ type: Boolean,
30
+ default: false,
31
+ },
32
+
33
+ dataTestid: {
34
+ type: String,
35
+ default: null,
36
+ },
37
+ })
38
+
39
+ const {
40
+ locked,
41
+ fullScreen,
42
+ width,
43
+ noPadding,
44
+ } = toRefs(props)
45
+
46
+ const emit = defineEmits([
47
+ 'close',
48
+ ])
49
+
50
+ // Send back to BaseModal the props used in BaseModal
51
+
52
+ const propsToParent = inject(InjectPropsFromContent)
53
+
54
+ if (!propsToParent) {
55
+ throw new Error('BaseModalContentOld must be used inside BaseModal')
56
+ }
57
+
58
+ watch(() => props.locked, value => {
59
+ propsToParent.locked = value
60
+ }, { immediate: true })
61
+ watch(() => props.fullScreen, value => {
62
+ propsToParent.fullScreen = value
63
+ }, { immediate: true })
64
+
65
+ // Close
66
+
67
+ const closeParent = inject(InjectClose)!
68
+
69
+ function close (alsoCloseParent = true) {
70
+ if (!locked.value) {
71
+ emit('close')
72
+ if (alsoCloseParent) {
73
+ closeParent()
74
+ }
75
+ }
76
+ }
77
+
78
+ const onParentClose = inject(InjectOnClose)!
79
+
80
+ onParentClose(() => {
81
+ close(false)
82
+ })
83
+
84
+ // Slots
85
+
86
+ const slots = useSlotsAsComponents()
87
+ const titleSlot = computed(() => slots.title!())
88
+ const subtitleSlot = computed(() => slots.subtitle!())
89
+ const contentSlot = computed(() => slots.content!())
90
+ </script>
91
+
92
+ <template>
93
+ <div
94
+ :class="{
95
+ 'has-header': titleSlot || subtitleSlot,
96
+ 'has-title': titleSlot,
97
+ 'has-subtitle': subtitleSlot,
98
+ 'has-content': contentSlot,
99
+ }"
100
+ class="base-modal-layout"
101
+ @click.stop
102
+ >
103
+ <section
104
+ :style="{ 'max-width': `${width}px` }"
105
+ :class="[noPadding ? 'p-0': 'p-5']"
106
+ class="transition-transform"
107
+ :data-testid="dataTestid"
108
+ >
109
+ <slot name="outside-header" />
110
+
111
+ <div class="base-modal-header">
112
+ <div
113
+ v-if="!locked && !fullScreen"
114
+ class="close-button"
115
+ data-testid="modal-close-icon"
116
+ @click="close()"
117
+ >
118
+ <div
119
+ class="i-ls-x icon"
120
+ />
121
+ </div>
122
+
123
+ <div
124
+ v-if="titleSlot"
125
+ class="title text-grey-900"
126
+ :class="{
127
+ 'flex flex-row items-center border-b h-16 border-grey-200 px-6 py-4': fullScreen,
128
+ }"
129
+ >
130
+ <div
131
+ v-if="fullScreen"
132
+ class="i-ls-chevron-left arrow-back cursor-pointer w-6 h-6 mr-2 text-grey-600 hover:text-grey-600"
133
+ @click="close()"
134
+ />
135
+ <RenderSlot :vnodes="titleSlot" />
136
+ </div>
137
+ <div
138
+ v-if="subtitleSlot"
139
+ class="subtitle"
140
+ >
141
+ <RenderSlot :vnodes="subtitleSlot" />
142
+ </div>
143
+ </div>
144
+
145
+ <div
146
+ v-if="contentSlot"
147
+ class="base-modal-content"
148
+ >
149
+ <RenderSlot :vnodes="contentSlot" />
150
+ </div>
151
+ </section>
152
+ </div>
153
+ </template>
154
+
155
+ <style lang="postcss" scoped>
156
+ .close-button {
157
+ position: absolute;
158
+ top: 0;
159
+ right: 0;
160
+ cursor: pointer;
161
+ opacity: 0.5;
162
+ transition: opacity 0.2s ease;
163
+ margin: 2px;
164
+ padding: 16px;
165
+ .icon {
166
+ width: 24px;
167
+ height: 24px;
168
+ }
169
+ &:hover {
170
+ opacity: 1;
171
+ }
172
+ }
173
+
174
+ .base-modal-layout {
175
+ position: absolute;
176
+ top: 0;
177
+ left: 0;
178
+ width: 100%;
179
+ height: 0;
180
+ z-index: 8001;
181
+ padding: 0 10px;
182
+ @apply text-grey-900;
183
+ & > section {
184
+ position: relative;
185
+ background: #fff;
186
+ margin: 30px auto;
187
+ max-width: 650px;
188
+ @apply rounded-8;
189
+ }
190
+ &.has-content {
191
+ &.has-header {
192
+ .base-modal-header {
193
+ margin-bottom: 16px;
194
+ }
195
+ }
196
+ }
197
+ &.has-title {
198
+ &.has-subtitle {
199
+ .base-modal-header {
200
+ .subtitle {
201
+ margin-top: 6px;
202
+ }
203
+ }
204
+ }
205
+ }
206
+ &:not(.has-header) {
207
+ .close-button {
208
+ padding: 6px;
209
+ }
210
+ }
211
+ }
212
+
213
+ .base-modal-header {
214
+ @apply text-grey-700;
215
+ text-align: center;
216
+ & > span {
217
+ display: block;
218
+ }
219
+ .title {
220
+ font-size: 1.1em;
221
+ font-weight: 600;
222
+ }
223
+ .subtitle {
224
+ font-weight: 300;
225
+ }
226
+ }
227
+
228
+ .img-ico {
229
+ display: block;
230
+ margin: 0 auto 16px auto;
231
+ }
232
+
233
+ .cover-ico {
234
+ width: 36px;
235
+ height: 36px;
236
+ margin: 0 auto 16px auto;
237
+ }
238
+
239
+ :deep(.warning-title) {
240
+ @apply font-semibold text-grey-900
241
+ }
242
+ </style>
@@ -0,0 +1,46 @@
1
+ import { mount } from '@vue/test-utils'
2
+ import BaseRating from './BaseRating.vue'
3
+
4
+ const Selectors = {
5
+ Item: '[data-testid="base-rating-item"]',
6
+ ItemLabel: 'label',
7
+ ItemActive: '.active',
8
+ }
9
+
10
+ describe('BaseRating', () => {
11
+ test('it should interact with the user', async () => {
12
+ const wrapper = mount(BaseRating)
13
+ await wrapper.findAll(Selectors.Item)[2].find(Selectors.ItemLabel).trigger('click')
14
+ expect(wrapper.emitted()['update:modelValue'][0][0]).toBe(3)
15
+
16
+ await wrapper.findAll(Selectors.Item)[4].find(Selectors.ItemLabel).trigger('click')
17
+ expect(wrapper.emitted()['update:modelValue'][1][0]).toBe(5)
18
+ })
19
+
20
+ test('it should active items on hover', async () => {
21
+ const wrapper = mount(BaseRating, {
22
+ props: {
23
+ hover: true,
24
+ },
25
+ })
26
+ expect(wrapper.findAll(`${Selectors.Item}${Selectors.ItemActive}`).length).toBe(0)
27
+
28
+ await wrapper.findAll(Selectors.Item)[2].trigger('mouseenter')
29
+ expect(wrapper.findAll(Selectors.Item)[0].classes()).toContain(Selectors.ItemActive.slice(1))
30
+ expect(wrapper.findAll(Selectors.Item)[1].classes()).toContain(Selectors.ItemActive.slice(1))
31
+ expect(wrapper.findAll(Selectors.Item)[2].classes()).toContain(Selectors.ItemActive.slice(1))
32
+ expect(wrapper.findAll(Selectors.Item)[3].classes()).not.toContain(Selectors.ItemActive.slice(1))
33
+ expect(wrapper.findAll(Selectors.Item)[4].classes()).not.toContain(Selectors.ItemActive.slice(1))
34
+ expect(wrapper.emitted()['update:modelValue']).toBeUndefined()
35
+ })
36
+
37
+ test('it should not react to events when readonly', async () => {
38
+ const wrapper = mount(BaseRating, {
39
+ props: {
40
+ readonly: true,
41
+ },
42
+ })
43
+ await wrapper.findAll(Selectors.Item)[2].find(Selectors.ItemLabel).trigger('click')
44
+ expect(wrapper.emitted()['update:modelValue']).toBeUndefined()
45
+ })
46
+ })
@@ -0,0 +1,46 @@
1
+ import BaseRating from './BaseRating.vue'
2
+
3
+ export default {
4
+ title: 'common/vue/components/BaseRating',
5
+ component: BaseRating,
6
+ }
7
+
8
+ const defaultTemplate = (args) => ({
9
+ setup: () => ({ args }),
10
+ template: `
11
+ <BaseRating v-bind="args" />
12
+ `,
13
+ })
14
+
15
+ export const Simple = defaultTemplate.bind({})
16
+
17
+ export const Labels = defaultTemplate.bind({})
18
+ Labels.args = {
19
+ itemLabels: ['Very poor', 'Poor', 'Average', 'Good', 'Great'],
20
+ }
21
+
22
+ export const StyledLabels = defaultTemplate.bind({})
23
+ StyledLabels.args = {
24
+ itemLabels: ['Very poor', 'Poor', 'Not so bad', 'Good', 'Very good'],
25
+ itemLabelClass: 'w-[50px] border border-grey-200 rounded p-1',
26
+ }
27
+
28
+ export const TenItems = defaultTemplate.bind({})
29
+ TenItems.args = {
30
+ length: 10,
31
+ }
32
+
33
+ export const Rated = defaultTemplate.bind({})
34
+ Rated.args = {
35
+ modelValue: 3,
36
+ }
37
+
38
+ export const Hover = defaultTemplate.bind({})
39
+ Hover.args = {
40
+ hover: true,
41
+ }
42
+
43
+ export const Name = defaultTemplate.bind({})
44
+ Name.args = {
45
+ name: 'session-quality',
46
+ }