@weni/unnnic-system 3.9.2 → 3.9.3-alpha.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 (254) hide show
  1. package/dist/assets/tokens/colors.json.d.ts +376 -0
  2. package/dist/components/Accordion/Accordion.vue.d.ts +1 -1
  3. package/dist/components/Alert/Alert.vue.d.ts +17 -116
  4. package/dist/components/Alert/Alert.vue.d.ts.map +1 -1
  5. package/dist/components/Alert/Version1dot1.vue.d.ts +2 -38
  6. package/dist/components/Alert/Version1dot1.vue.d.ts.map +1 -1
  7. package/dist/components/AudioRecorder/AudioHandler.vue.d.ts +2 -2
  8. package/dist/components/AudioRecorder/AudioPlayer.vue.d.ts +1 -1
  9. package/dist/components/AudioRecorder/AudioRecorder.vue.d.ts +5 -5
  10. package/dist/components/AvatarIcon/AvatarIcon.vue.d.ts +3 -3
  11. package/dist/components/Banner/Banner.vue.d.ts +1 -1
  12. package/dist/components/Banner/InfoBanner.vue.d.ts +1 -1
  13. package/dist/components/Breadcrumb/Breadcrumb.vue.d.ts +1 -1
  14. package/dist/components/Button/Button.vue.d.ts +1 -1
  15. package/dist/components/Button/Button.vue.d.ts.map +1 -1
  16. package/dist/components/Button/ButtonIcon.vue.d.ts +1 -1
  17. package/dist/components/Button/types.d.ts +1 -1
  18. package/dist/components/Button/types.d.ts.map +1 -1
  19. package/dist/components/Card/AccountCard.vue.d.ts +5 -5
  20. package/dist/components/Card/BlankCard.vue.d.ts +1 -1
  21. package/dist/components/Card/Card.vue.d.ts +27 -27
  22. package/dist/components/Card/CardCompany.vue.d.ts +11 -414
  23. package/dist/components/Card/CardData.vue.d.ts +1 -1
  24. package/dist/components/Card/CardStatusesContainer.vue.d.ts +5 -5
  25. package/dist/components/Card/ContentCard.vue.d.ts +3 -3
  26. package/dist/components/Card/DashCard.vue.d.ts +5 -5
  27. package/dist/components/Card/DefaultCard.vue.d.ts +1 -1
  28. package/dist/components/Card/MarketplaceCard.vue.d.ts +2 -2
  29. package/dist/components/Card/SimpleCard.vue.d.ts +3 -3
  30. package/dist/components/Card/StatusCard.vue.d.ts +2 -2
  31. package/dist/components/Card/TitleCard.vue.d.ts +3 -3
  32. package/dist/components/CardImage/CardImage.vue.d.ts +24 -31
  33. package/dist/components/CardInformation/CardInformation.vue.d.ts +5 -5
  34. package/dist/components/CardProject/CardProject.vue.d.ts +3 -3
  35. package/dist/components/Carousel/Carousel.vue.d.ts +13 -416
  36. package/dist/components/Carousel/TagCarousel.vue.d.ts +12 -415
  37. package/dist/components/ChartBar/ChartBar.vue.d.ts +5 -5
  38. package/dist/components/ChartLine/ChartLine.vue.d.ts +1 -1
  39. package/dist/components/ChatText/ChatText.vue.d.ts +2 -2
  40. package/dist/components/ChatsContact/ChatsContact.vue.d.ts +30 -448
  41. package/dist/components/ChatsDashboardTagLive/ChatsDashboardTagLive.vue.d.ts +1 -1
  42. package/dist/components/ChatsHeader/ChatsHeader.vue.d.ts +1 -1
  43. package/dist/components/ChatsHeader/ChatsHeader.vue.d.ts.map +1 -1
  44. package/dist/components/ChatsMessage/ChatsMessage.vue.d.ts +5 -5
  45. package/dist/components/ChatsMessage/ChatsMessageStatusBackdrop.vue.d.ts +2 -2
  46. package/dist/components/ChatsNavbar/ChatsNavbar.vue.d.ts +1 -1
  47. package/dist/components/ChatsUserAvatar/ChatsUserAvatar.vue.d.ts +2 -2
  48. package/dist/components/Checkbox/Checkbox.vue.d.ts +19 -26
  49. package/dist/components/Checkbox/Checkbox.vue.d.ts.map +1 -1
  50. package/dist/components/CheckboxGroup/CheckboxGroup.vue.d.ts +28 -0
  51. package/dist/components/CheckboxGroup/CheckboxGroup.vue.d.ts.map +1 -0
  52. package/dist/components/Comment/Comment.vue.d.ts +1 -1
  53. package/dist/components/DataArea/DataArea.vue.d.ts +2 -2
  54. package/dist/components/DataTable/index.vue.d.ts +1 -1
  55. package/dist/components/DataTable/index.vue.d.ts.map +1 -1
  56. package/dist/components/DateFilter/DateFilter.vue.d.ts +251 -41
  57. package/dist/components/DatePicker/DatePicker.vue.d.ts +4 -4
  58. package/dist/components/Drawer/Drawer.vue.d.ts +4 -4
  59. package/dist/components/Dropdown/Dropdown.vue.d.ts +1 -1
  60. package/dist/components/Dropdown/LanguageSelect.vue.d.ts +3 -3
  61. package/dist/components/Flag.vue.d.ts +2 -2
  62. package/dist/components/FormElement/FormElement.vue.d.ts +51 -28
  63. package/dist/components/FormElement/FormElement.vue.d.ts.map +1 -1
  64. package/dist/components/Icon.vue.d.ts +1 -1
  65. package/dist/components/Icon.vue.d.ts.map +1 -1
  66. package/dist/components/IconLoading/IconLoading.vue.d.ts +1 -1
  67. package/dist/components/ImportCard/ImportCard.vue.d.ts +4 -4
  68. package/dist/components/Input/BaseInput.vue.d.ts +33 -2
  69. package/dist/components/Input/BaseInput.vue.d.ts.map +1 -1
  70. package/dist/components/Input/Input.vue.d.ts +251 -41
  71. package/dist/components/Input/Input.vue.d.ts.map +1 -1
  72. package/dist/components/Input/TextInput.vue.d.ts +85 -25
  73. package/dist/components/Input/TextInput.vue.d.ts.map +1 -1
  74. package/dist/components/InputDatePicker/InputDatePicker.vue.d.ts +256 -46
  75. package/dist/components/InputNext/InputNext.vue.d.ts +5 -5
  76. package/dist/components/Label/Label.vue.d.ts +9 -15
  77. package/dist/components/Label/Label.vue.d.ts.map +1 -1
  78. package/dist/components/Modal/Modal.vue.d.ts +2 -2
  79. package/dist/components/ModalDialog/ModalDialog.vue.d.ts +6 -6
  80. package/dist/components/ModalNext/ModalNext.vue.d.ts +256 -46
  81. package/dist/components/ModalUpload/ModalUpload.vue.d.ts +9 -9
  82. package/dist/components/MoodRating/MoodRating.vue.d.ts +1 -1
  83. package/dist/components/MultiSelect/MultiSelect.vue.d.ts +26 -14
  84. package/dist/components/PageHeader/PageHeader.vue.d.ts +28 -0
  85. package/dist/components/PageHeader/PageHeader.vue.d.ts.map +1 -0
  86. package/dist/components/PageHeader/index.d.ts +3 -0
  87. package/dist/components/PageHeader/index.d.ts.map +1 -0
  88. package/dist/components/PageHeader/types.d.ts +9 -0
  89. package/dist/components/PageHeader/types.d.ts.map +1 -0
  90. package/dist/components/Pagination/Pagination.vue.d.ts +3 -3
  91. package/dist/components/ProgressBar/ProgressBar.vue.d.ts +1 -1
  92. package/dist/components/Radio/Radio.vue.d.ts +10 -6
  93. package/dist/components/Radio/Radio.vue.d.ts.map +1 -1
  94. package/dist/components/SelectSmart/SelectSmart.vue.d.ts +120 -470
  95. package/dist/components/SelectSmart/SelectSmartMultipleHeader.vue.d.ts +11 -414
  96. package/dist/components/SelectSmart/SelectSmartOption.vue.d.ts +21 -28
  97. package/dist/components/SelectSmart/SelectSmartOption.vue.d.ts.map +1 -1
  98. package/dist/components/SelectTime/index.vue.d.ts +85 -25
  99. package/dist/components/SkeletonLoading/skeletonTheme.vue.d.ts +1 -1
  100. package/dist/components/Slider/Slider.vue.d.ts +2 -2
  101. package/dist/components/StarRating/StarRating.vue.d.ts +1 -1
  102. package/dist/components/Switch/Switch.vue.d.ts +55 -21
  103. package/dist/components/Switch/Switch.vue.d.ts.map +1 -1
  104. package/dist/components/Tab/Tab.vue.d.ts +13 -2
  105. package/dist/components/TableNext/TableBodyCell.vue.d.ts +2 -2
  106. package/dist/components/TableNext/TablePagination.vue.d.ts +3 -3
  107. package/dist/components/TabsExpanded/TabsExpanded.vue.d.ts +1 -1
  108. package/dist/components/Tag/DefaultTag.vue.d.ts +4 -83
  109. package/dist/components/Tag/DefaultTag.vue.d.ts.map +1 -1
  110. package/dist/components/Tag/Tag.vue.d.ts +12 -414
  111. package/dist/components/Tag/Tag.vue.d.ts.map +1 -1
  112. package/dist/components/Tag/types.d.ts +18 -0
  113. package/dist/components/Tag/types.d.ts.map +1 -0
  114. package/dist/components/TextArea/TextArea.vue.d.ts +78 -33
  115. package/dist/components/TextArea/TextArea.vue.d.ts.map +1 -1
  116. package/dist/components/Toast/Toast.vue.d.ts +16 -0
  117. package/dist/components/Toast/Toast.vue.d.ts.map +1 -0
  118. package/dist/components/Toast/ToastManager.d.ts +14 -0
  119. package/dist/components/Toast/ToastManager.d.ts.map +1 -0
  120. package/dist/components/Toast/types.d.ts +35 -0
  121. package/dist/components/Toast/types.d.ts.map +1 -0
  122. package/dist/components/ToolTip/ToolTip.vue.d.ts +1 -1
  123. package/dist/components/Tour/Tour.vue.d.ts +3 -3
  124. package/dist/components/Tour/TourPopover.vue.d.ts +3 -3
  125. package/dist/components/UploadArea/UploadArea.vue.d.ts +4 -4
  126. package/dist/components/index.d.ts +8736 -10161
  127. package/dist/components/index.d.ts.map +1 -1
  128. package/dist/components/ui/popover/PopoverContent.vue.d.ts +1 -1
  129. package/dist/components/ui/popover/PopoverContent.vue.d.ts.map +1 -1
  130. package/dist/{es-4aab69cb.mjs → es-61b41785.mjs} +1 -1
  131. package/dist/{index-789225a6.mjs → index-10160248.mjs} +9358 -8815
  132. package/dist/locales/en.json.d.ts +2 -1
  133. package/dist/locales/es.json.d.ts +2 -1
  134. package/dist/locales/pt_br.json.d.ts +2 -1
  135. package/dist/{pt-br-2f695ddd.mjs → pt-br-31a68683.mjs} +1 -1
  136. package/dist/style.css +1 -1
  137. package/dist/unnnic.mjs +158 -151
  138. package/dist/unnnic.umd.js +35 -36
  139. package/dist/utils/call.d.ts +2 -1
  140. package/dist/utils/call.d.ts.map +1 -1
  141. package/package.json +2 -2
  142. package/src/assets/icons/checkbox-checked-disabled.svg +3 -0
  143. package/src/assets/icons/checkbox-checked.svg +3 -0
  144. package/src/assets/icons/checkbox-less-disabled.svg +3 -0
  145. package/src/assets/icons/checkbox-less.svg +3 -0
  146. package/src/assets/icons/radio-checked.svg +3 -0
  147. package/src/assets/icons/switch-checked-disabled.svg +3 -0
  148. package/src/assets/icons/switch-checked.svg +3 -0
  149. package/src/assets/scss/scheme-colors.scss +309 -223
  150. package/src/components/Alert/Alert.vue +26 -135
  151. package/src/components/Alert/Version1dot1.vue +0 -36
  152. package/src/components/Alert/__tests__/Alert.spec.js +2 -45
  153. package/src/components/Alert/__tests__/Version1dot1.spec.js +0 -21
  154. package/src/components/Alert/__tests__/__snapshots__/Alert.spec.js.snap +11 -7
  155. package/src/components/Alert/__tests__/__snapshots__/AlertBanner.spec.js.snap +2 -2
  156. package/src/components/Alert/__tests__/__snapshots__/Version1dot1.spec.js.snap +1 -1
  157. package/src/components/Button/Button.vue +67 -117
  158. package/src/components/Button/types.ts +0 -1
  159. package/src/components/ChatsContact/ChatsContact.vue +16 -8
  160. package/src/components/Checkbox/Checkbox.vue +117 -65
  161. package/src/components/Checkbox/__tests__/Checkbox.spec.js +6 -21
  162. package/src/components/CheckboxGroup/CheckboxGroup.vue +96 -0
  163. package/src/components/FormElement/FormElement.vue +63 -93
  164. package/src/components/Icon.vue +2 -0
  165. package/src/components/Input/BaseInput.vue +33 -14
  166. package/src/components/Input/Input.scss +22 -22
  167. package/src/components/Input/Input.vue +79 -56
  168. package/src/components/Input/TextInput.vue +81 -65
  169. package/src/components/Input/__test__/Input.spec.js +13 -33
  170. package/src/components/Input/__test__/TextInput.spec.js +6 -8
  171. package/src/components/Input/__test__/__snapshots__/Input.spec.js.snap +17 -4
  172. package/src/components/Input/__test__/__snapshots__/TextInput.spec.js.snap +7 -1
  173. package/src/components/Label/Label.vue +52 -21
  174. package/src/components/Label/__tests__/Label.spec.js +1 -1
  175. package/src/components/Label/__tests__/__snapshots__/Label.spec.js.snap +1 -1
  176. package/src/components/MultiSelectV2/MultSelectOption.vue +67 -0
  177. package/src/components/MultiSelectV2/__tests__/MultiSelect.spec.js +556 -0
  178. package/src/components/MultiSelectV2/__tests__/MultiSelectOption.spec.js +229 -0
  179. package/src/components/MultiSelectV2/__tests__/__snapshots__/MultiSelect.spec.js.snap +121 -0
  180. package/src/components/MultiSelectV2/__tests__/__snapshots__/MultiSelectOption.spec.js.snap +51 -0
  181. package/src/components/MultiSelectV2/index.vue +221 -0
  182. package/src/components/PageHeader/PageHeader.vue +148 -0
  183. package/src/components/PageHeader/index.ts +2 -0
  184. package/src/components/PageHeader/types.ts +10 -0
  185. package/src/components/Popover/__tests__/Popover.spec.js +147 -0
  186. package/src/components/Popover/__tests__/__snapshots__/Popover.spec.js.snap +8 -0
  187. package/src/components/Popover/index.vue +146 -0
  188. package/src/components/Radio/Radio.vue +118 -66
  189. package/src/components/Radio/__test__/Radio.spec.js +14 -20
  190. package/src/components/Radio/__test__/__snapshots__/Radio.spec.js.snap +4 -3
  191. package/src/components/RadioGroup/RadioGroup.vue +142 -0
  192. package/src/components/Select/SelectOption.vue +65 -0
  193. package/src/components/Select/__tests__/Select.spec.js +412 -0
  194. package/src/components/Select/__tests__/SelectItem.spec.js +330 -0
  195. package/src/components/Select/__tests__/SelectOption.spec.js +174 -0
  196. package/src/components/Select/__tests__/__snapshots__/Select.spec.js.snap +97 -0
  197. package/src/components/Select/__tests__/__snapshots__/SelectItem.spec.js.snap +15 -0
  198. package/src/components/Select/__tests__/__snapshots__/SelectOption.spec.js.snap +25 -0
  199. package/src/components/Select/index.vue +249 -0
  200. package/src/components/SelectSmart/SelectSmart.vue +4 -3
  201. package/src/components/Switch/Switch.vue +132 -91
  202. package/src/components/Switch/__tests__/Switch.spec.js +8 -75
  203. package/src/components/Switch/__tests__/__snapshots__/Switch.spec.js.snap +5 -6
  204. package/src/components/Tab/Tab.vue +37 -23
  205. package/src/components/Tab/__test__/__snapshots__/Tab.spec.js.snap +1 -1
  206. package/src/components/TableNext/__test__/__snapshots__/TableNext.spec.js.snap +2 -2
  207. package/src/components/TableNext/__test__/__snapshots__/TablePagination.spec.js.snap +2 -2
  208. package/src/components/Tag/DefaultTag.vue +51 -107
  209. package/src/components/Tag/Tag.vue +32 -79
  210. package/src/components/Tag/types.ts +19 -0
  211. package/src/components/TextArea/TextArea.vue +41 -12
  212. package/src/components/TextArea/__test__/__snapshots__/TextArea.spec.js.snap +11 -3
  213. package/src/components/Toast/Toast.vue +246 -0
  214. package/src/components/Toast/ToastManager.ts +110 -0
  215. package/src/components/Toast/__tests__/Toast.spec.js +291 -0
  216. package/src/components/Toast/__tests__/ToastManager.spec.js +294 -0
  217. package/src/components/Toast/types.ts +57 -0
  218. package/src/components/index.ts +33 -18
  219. package/src/locales/en.json +2 -1
  220. package/src/locales/es.json +2 -1
  221. package/src/locales/pt_br.json +2 -1
  222. package/src/stories/Alert.stories.js +6 -67
  223. package/src/stories/Button.stories.js +29 -39
  224. package/src/stories/ChatsContact.stories.js +9 -0
  225. package/src/stories/Checkbox.stories.js +11 -4
  226. package/src/stories/CheckboxGroup.stories.js +105 -0
  227. package/src/stories/Input.stories.js +71 -76
  228. package/src/stories/Label.stories.js +7 -0
  229. package/src/stories/MultiSelectV2.stories.js +158 -0
  230. package/src/stories/PageHeader.stories.js +330 -0
  231. package/src/stories/Radio.stories.js +28 -1
  232. package/src/stories/RadioGroup.stories.js +144 -0
  233. package/src/stories/Select.stories.js +158 -0
  234. package/src/stories/Switch.stories.js +10 -5
  235. package/src/stories/Tab.stories.js +11 -4
  236. package/src/stories/Tag.stories.js +24 -43
  237. package/src/stories/TextArea.stories.js +14 -2
  238. package/src/stories/Toast.mdx +123 -0
  239. package/src/stories/Toast.stories.js +126 -0
  240. package/src/types/scheme-colors.d.ts +1 -0
  241. package/src/utils/call.js +46 -18
  242. package/dist/components/Tag/BrandTag.vue.d.ts +0 -51
  243. package/dist/components/Tag/BrandTag.vue.d.ts.map +0 -1
  244. package/dist/components/Tag/IndicatorTag.vue.d.ts +0 -151
  245. package/dist/components/Tag/IndicatorTag.vue.d.ts.map +0 -1
  246. package/dist/components/Tag/TagNext.vue.d.ts +0 -24
  247. package/dist/components/Tag/TagNext.vue.d.ts.map +0 -1
  248. package/src/components/Alert/AlertBanner.vue +0 -182
  249. package/src/components/Alert/AlertCaller.vue +0 -49
  250. package/src/components/Alert/__tests__/AlertBanner.spec.js +0 -89
  251. package/src/components/Alert/__tests__/AlertCaller.spec.js +0 -98
  252. package/src/components/Tag/BrandTag.vue +0 -96
  253. package/src/components/Tag/IndicatorTag.vue +0 -107
  254. package/src/components/Tag/TagNext.vue +0 -60
@@ -33,41 +33,35 @@ describe('Radio.vue', () => {
33
33
  expect(wrapper.exists()).toBe(true);
34
34
  });
35
35
 
36
- test('renders UnnnicIcon component with correct props', () => {
37
- const unnnicIcon = wrapper.findComponent(UnnnicIcon);
38
- expect(unnnicIcon.exists()).toBe(true);
39
- expect(unnnicIcon.props('icon')).toBe('radio-default');
40
- expect(unnnicIcon.props('scheme')).toBe('neutral-cleanest');
41
- expect(unnnicIcon.props('size')).toBe('md');
42
- });
43
-
44
- test('applies the correct size class', async () => {
45
- expect(wrapper.classes()).toContain('unnnic-radio-container--md');
46
-
47
- await wrapper.setProps({ size: 'sm' });
48
- expect(wrapper.classes()).toContain('unnnic-radio-container--sm');
36
+ test('renders input with correct attributes', () => {
37
+ const input = wrapper.find('input');
38
+ expect(input.exists()).toBe(true);
39
+ expect(input.attributes('type')).toBe('radio');
40
+ expect(input.attributes('disabled')).toBe(undefined);
41
+ expect(input.attributes('checked')).toBe(undefined);
49
42
  });
50
43
 
51
44
  test('applies disabled class when disabled prop is true', async () => {
52
45
  await wrapper.setProps({ disabled: true });
53
- expect(wrapper.classes()).toContain('disabled');
46
+ expect(wrapper.find('.unnnic-radio__label').classes()).toContain(
47
+ 'unnnic-radio__label--disabled',
48
+ );
54
49
  });
55
50
 
56
51
  test('icon changes based on valueName', async () => {
57
52
  await wrapper.setProps({ modelValue: 'option1' });
58
- const unnnicIcon = wrapper.findComponent(UnnnicIcon);
59
- expect(unnnicIcon.props('icon')).toBe('radio-selected');
53
+ const input = wrapper.find('input');
54
+ expect(input.attributes()).toHaveProperty('checked');
60
55
  });
61
56
 
62
57
  test('icon and color change when disabled is true', async () => {
63
58
  await wrapper.setProps({ disabled: true });
64
- const unnnicIcon = wrapper.findComponent(UnnnicIcon);
65
- expect(unnnicIcon.props('icon')).toBe('radio-disable');
66
- expect(unnnicIcon.props('scheme')).toBe('brand-sec');
59
+ const input = wrapper.find('input');
60
+ expect(input.attributes()).toHaveProperty('disabled');
67
61
  });
68
62
 
69
63
  test('emits update:modelValue with correct value when clicked and not disabled', async () => {
70
- await wrapper.trigger('click');
64
+ await wrapper.find('input').trigger('change');
71
65
  expect(wrapper.emitted('update:modelValue')).toBeTruthy();
72
66
  expect(wrapper.emitted('update:modelValue')[0]).toEqual(['option1']);
73
67
  });
@@ -1,7 +1,8 @@
1
1
  // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
2
 
3
3
  exports[`Radio.vue > matches the snapshot 1`] = `
4
- "<div data-v-38c6bf5e="" class="unnnic-radio-container unnnic-radio-container--md"><svg data-v-26446d8e="" data-v-38c6bf5e="" width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg" class="unnnic-icon unnnic-icon--size-svg-md unnnic-icon__size--md unnnic-icon-scheme--neutral-cleanest unnnic-radio" data-testid="custom-icon">
5
- <circle cx="20" cy="20.0005" r="14.5" stroke="#3B414D" class="primary-stroke"></circle>
6
- </svg><span data-v-38c6bf5e="" class="label">Option 1</span></div>"
4
+ "<section data-v-38c6bf5e="" class="unnnic-radio" size="md"><label data-v-38c6bf5e="" class="unnnic-radio__input-wrapper"><input data-v-38c6bf5e="" class="unnnic-radio__input" type="radio" name="">
5
+ <p data-v-38c6bf5e="" class="unnnic-radio__label"> Option 1</p>
6
+ <!--v-if-->
7
+ </label></section>"
7
8
  `;
@@ -0,0 +1,142 @@
1
+ <template>
2
+ <section
3
+ :class="[
4
+ 'unnnic-radio-group__container',
5
+ `unnnic-radio-group--state-${state}`,
6
+ ]"
7
+ >
8
+ <UnnnicLabel
9
+ v-if="label"
10
+ :label="label"
11
+ :tooltip="labelTooltip"
12
+ :useHtmlTooltip="labelUseHtmlTooltip"
13
+ class="unnnic-radio-group__label"
14
+ />
15
+
16
+ <section class="unnnic-radio-group__radios">
17
+ <slot />
18
+ </section>
19
+
20
+ <footer
21
+ v-if="helper"
22
+ class="unnnic-radio-group__helper"
23
+ >
24
+ {{ helper }}
25
+ </footer>
26
+ </section>
27
+ </template>
28
+
29
+ <script setup>
30
+ import { ref, provide, watch, computed } from 'vue';
31
+ import UnnnicLabel from '../Label/Label.vue';
32
+
33
+ const props = defineProps({
34
+ modelValue: {
35
+ type: [String, Number],
36
+ default: '',
37
+ },
38
+
39
+ state: {
40
+ type: String,
41
+ default: 'horizontal',
42
+ validator(value) {
43
+ return ['horizontal', 'vertical'].includes(value);
44
+ },
45
+ },
46
+
47
+ label: {
48
+ type: String,
49
+ },
50
+
51
+ labelTooltip: {
52
+ type: String,
53
+ },
54
+
55
+ labelUseHtmlTooltip: {
56
+ type: Boolean,
57
+ },
58
+
59
+ name: {
60
+ type: String,
61
+ default: '',
62
+ },
63
+
64
+ helper: {
65
+ type: String,
66
+ },
67
+ });
68
+
69
+ const emit = defineEmits(['update:modelValue']);
70
+
71
+ const contextModelValue = ref(props.modelValue);
72
+
73
+ watch(
74
+ () => props.modelValue,
75
+ (newVal) => {
76
+ if (newVal !== contextModelValue.value) {
77
+ contextModelValue.value = newVal;
78
+ }
79
+ },
80
+ );
81
+
82
+ watch(contextModelValue, (newVal) => {
83
+ if (newVal !== props.modelValue) {
84
+ emit('update:modelValue', newVal);
85
+ }
86
+ });
87
+
88
+ const computedName = computed(() => {
89
+ return (
90
+ props.name ||
91
+ `unnnic-radio-group-${Math.random().toString(36).substring(2, 15)}`
92
+ );
93
+ });
94
+
95
+ provide('contextModelValue', contextModelValue);
96
+ provide('contextName', computedName);
97
+ </script>
98
+
99
+ <style lang="scss" scoped>
100
+ @use '@/assets/scss/unnnic' as *;
101
+
102
+ .unnnic-radio-group {
103
+ &__container {
104
+ display: flex;
105
+ flex-direction: column;
106
+ }
107
+
108
+ &__label {
109
+ margin-bottom: $unnnic-space-3;
110
+ }
111
+
112
+ &__radios {
113
+ display: flex;
114
+ gap: $unnnic-space-4 $unnnic-space-6;
115
+ }
116
+
117
+ &__helper {
118
+ font: $unnnic-font-caption-2;
119
+ color: $unnnic-color-fg-base;
120
+ }
121
+
122
+ &--state-horizontal {
123
+ .unnnic-radio-group__radios {
124
+ flex-direction: row;
125
+ }
126
+
127
+ .unnnic-radio-group__helper {
128
+ margin-top: $unnnic-space-2;
129
+ }
130
+ }
131
+
132
+ &--state-vertical {
133
+ .unnnic-radio-group__radios {
134
+ flex-direction: column;
135
+ }
136
+
137
+ .unnnic-radio-group__helper {
138
+ margin-top: $unnnic-space-3;
139
+ }
140
+ }
141
+ }
142
+ </style>
@@ -0,0 +1,65 @@
1
+ <template>
2
+ <div
3
+ :class="[
4
+ 'unnnic-select-option',
5
+ {
6
+ 'unnnic-select-option--disabled': props.disabled,
7
+ 'unnnic-select-option--active': props.active,
8
+ 'unnnic-select-option--focused': props.focused,
9
+ },
10
+ ]"
11
+ >
12
+ <p class="unnnic-select-option__label">{{ props.label }}</p>
13
+ </div>
14
+ </template>
15
+
16
+ <script setup lang="ts">
17
+ defineOptions({
18
+ name: 'UnnnicSelectOption',
19
+ });
20
+
21
+ interface SelectOptionProps {
22
+ label: string;
23
+ disabled?: boolean;
24
+ active?: boolean;
25
+ focused?: boolean;
26
+ }
27
+
28
+ const props = withDefaults(defineProps<SelectOptionProps>(), {
29
+ disabled: false,
30
+ active: false,
31
+ focused: false,
32
+ });
33
+ </script>
34
+
35
+ <style lang="scss" scoped>
36
+ @use '@/assets/scss/unnnic' as *;
37
+ * {
38
+ margin: 0;
39
+ padding: 0;
40
+ box-sizing: border-box;
41
+ }
42
+
43
+ .unnnic-select-option {
44
+ cursor: pointer;
45
+ border-radius: $unnnic-radius-1;
46
+ padding: $unnnic-space-2 $unnnic-space-4;
47
+ font: $unnnic-font-emphasis;
48
+
49
+ &:hover:not(&--active):not(&--disabled),
50
+ &--focused {
51
+ background-color: $unnnic-color-bg-soft;
52
+ }
53
+
54
+ &--active {
55
+ background-color: $unnnic-color-bg-active;
56
+ color: $unnnic-color-fg-inverted;
57
+ }
58
+
59
+ &--disabled {
60
+ color: $unnnic-color-fg-muted;
61
+ background-color: $unnnic-color-bg-muted;
62
+ cursor: not-allowed;
63
+ }
64
+ }
65
+ </style>
@@ -0,0 +1,412 @@
1
+ import { mount } from '@vue/test-utils';
2
+ import { beforeEach, describe, expect, afterEach, test, vi } from 'vitest';
3
+ import UnnnicSelect from '../index.vue';
4
+
5
+ vi.mock('../../mixins/i18n', () => ({
6
+ default: {
7
+ methods: {
8
+ $t: (key) => key,
9
+ },
10
+ },
11
+ }));
12
+
13
+ describe('UnnnicSelect.vue', () => {
14
+ let wrapper;
15
+
16
+ const defaultProps = {
17
+ options: [
18
+ { label: 'Option 1', value: 'option1' },
19
+ { label: 'Option 2', value: 'option2' },
20
+ { label: 'Option 3', value: 'option3' },
21
+ ],
22
+ modelValue: null,
23
+ };
24
+
25
+ const mountWrapper = (props = {}, slots = {}) => {
26
+ return mount(UnnnicSelect, {
27
+ props: {
28
+ ...defaultProps,
29
+ ...props,
30
+ },
31
+ global: {
32
+ mocks: {
33
+ $t: (key) => key,
34
+ },
35
+ },
36
+ slots,
37
+ });
38
+ };
39
+
40
+ beforeEach(() => {
41
+ wrapper = mountWrapper();
42
+ });
43
+
44
+ afterEach(() => {
45
+ wrapper?.unmount();
46
+ });
47
+
48
+ describe('rendering', () => {
49
+ test('renders correctly', () => {
50
+ expect(wrapper.exists()).toBe(true);
51
+ expect(wrapper.find('.unnnic-select').exists()).toBe(true);
52
+ });
53
+
54
+ test('renders input component', () => {
55
+ const input = wrapper.findComponent({ name: 'UnnnicInput' });
56
+ expect(input.exists()).toBe(true);
57
+ });
58
+
59
+ test('renders popover component', () => {
60
+ const popover = wrapper.findComponent({ name: 'UnnnicPopover' });
61
+ expect(popover.exists()).toBe(true);
62
+ });
63
+
64
+ test('renders select options when popover is open', async () => {
65
+ wrapper.vm.openPopover = true;
66
+ await wrapper.vm.$nextTick();
67
+ const options = wrapper.findAllComponents({ name: 'UnnnicSelectOption' });
68
+ expect(options.length).toBe(3);
69
+ });
70
+ });
71
+
72
+ describe('props validation', () => {
73
+ test('validates required options prop', () => {
74
+ const { options } = wrapper.vm.$options.props;
75
+ expect(options.required).toBe(true);
76
+ });
77
+
78
+ test('validates required modelValue prop', () => {
79
+ const { modelValue } = wrapper.vm.$options.props;
80
+ expect(modelValue.required).toBe(true);
81
+ });
82
+
83
+ test('applies default values correctly', () => {
84
+ expect(wrapper.vm.size).toBe('md');
85
+ expect(wrapper.vm.type).toBe('normal');
86
+ expect(wrapper.vm.placeholder).toBe('');
87
+ expect(wrapper.vm.optionsLines).toBe(5);
88
+ expect(wrapper.vm.returnObject).toBe(false);
89
+ expect(wrapper.vm.itemLabel).toBe('label');
90
+ expect(wrapper.vm.itemValue).toBe('value');
91
+ expect(wrapper.vm.locale).toBe('en');
92
+ expect(wrapper.vm.enableSearch).toBe(false);
93
+ expect(wrapper.vm.disabled).toBe(false);
94
+ });
95
+ });
96
+
97
+ describe('input display', () => {
98
+ test('displays placeholder when no option is selected', () => {
99
+ const input = wrapper.findComponent({ name: 'UnnnicInput' });
100
+ expect(input.props('placeholder')).toBe('');
101
+ });
102
+
103
+ test('displays selected option label', async () => {
104
+ await wrapper.setProps({ modelValue: 'option1' });
105
+ const input = wrapper.findComponent({ name: 'UnnnicInput' });
106
+ expect(input.props('modelValue')).toBe('Option 1');
107
+ });
108
+
109
+ test('displays custom placeholder', async () => {
110
+ await wrapper.setProps({ placeholder: 'Select an option' });
111
+ const input = wrapper.findComponent({ name: 'UnnnicInput' });
112
+ expect(input.props('placeholder')).toBe('Select an option');
113
+ });
114
+
115
+ test('input is readonly', () => {
116
+ const input = wrapper.findComponent({ name: 'UnnnicInput' });
117
+ expect(input.props('readonly')).toBe(true);
118
+ });
119
+
120
+ test('input shows correct icon based on popover state', async () => {
121
+ const input = wrapper.findComponent({ name: 'UnnnicInput' });
122
+
123
+ // Initially closed
124
+ expect(input.props('iconRight')).toBe('keyboard_arrow_down');
125
+
126
+ // When popover is open
127
+ wrapper.vm.openPopover = true;
128
+ await wrapper.vm.$nextTick();
129
+ expect(input.props('iconRight')).toBe('keyboard_arrow_up');
130
+ });
131
+ });
132
+
133
+ describe('option selection', () => {
134
+ test('emits update:modelValue when option is selected', async () => {
135
+ wrapper.vm.openPopover = true;
136
+ await wrapper.vm.$nextTick();
137
+ const options = wrapper.findAllComponents({ name: 'UnnnicSelectOption' });
138
+
139
+ await options[0].vm.$emit('click');
140
+
141
+ expect(wrapper.emitted('update:modelValue')).toBeTruthy();
142
+ expect(wrapper.emitted('update:modelValue')[0]).toEqual(['option1']);
143
+ });
144
+
145
+ test('emits object when returnObject is true', async () => {
146
+ await wrapper.setProps({ returnObject: true });
147
+ wrapper.vm.openPopover = true;
148
+ await wrapper.vm.$nextTick();
149
+ const options = wrapper.findAllComponents({ name: 'UnnnicSelectOption' });
150
+
151
+ await options[0].vm.$emit('click');
152
+
153
+ expect(wrapper.emitted('update:modelValue')[0]).toEqual([
154
+ { label: 'Option 1', value: 'option1' },
155
+ ]);
156
+ });
157
+
158
+ test('does not emit when same option is selected', async () => {
159
+ await wrapper.setProps({ modelValue: 'option1' });
160
+ wrapper.vm.openPopover = true;
161
+ await wrapper.vm.$nextTick();
162
+ const options = wrapper.findAllComponents({ name: 'UnnnicSelectOption' });
163
+
164
+ await options[0].vm.$emit('click');
165
+
166
+ expect(wrapper.emitted('update:modelValue')).toBeFalsy();
167
+ });
168
+
169
+ test('does not emit when disabled option is clicked', async () => {
170
+ const disabledOptions = [
171
+ { label: 'Option 1', value: 'option1' },
172
+ { label: 'Disabled Option', value: 'disabled', disabled: true },
173
+ ];
174
+
175
+ await wrapper.setProps({ options: disabledOptions });
176
+ wrapper.vm.openPopover = true;
177
+ await wrapper.vm.$nextTick();
178
+ const options = wrapper.findAllComponents({ name: 'UnnnicSelectOption' });
179
+
180
+ await options[1].vm.$emit('click');
181
+
182
+ expect(wrapper.emitted('update:modelValue')).toBeFalsy();
183
+ });
184
+ });
185
+
186
+ describe('search functionality', () => {
187
+ test('renders search input when enableSearch is true', async () => {
188
+ await wrapper.setProps({ enableSearch: true });
189
+ wrapper.vm.openPopover = true;
190
+ await wrapper.vm.$nextTick();
191
+ const searchInputs = wrapper.findAllComponents({ name: 'UnnnicInput' });
192
+ expect(searchInputs.length).toBe(2); // Main input + search input
193
+ });
194
+
195
+ test('does not render search input when enableSearch is false', async () => {
196
+ await wrapper.setProps({ enableSearch: false, modelValue: true });
197
+ const searchInputs = wrapper.findAllComponents({ name: 'UnnnicInput' });
198
+ expect(searchInputs.length).toBe(1); // Only main input
199
+ });
200
+
201
+ test('emits update:search when search input changes', async () => {
202
+ await wrapper.setProps({ enableSearch: true });
203
+ wrapper.vm.openPopover = true;
204
+ await wrapper.vm.$nextTick();
205
+ const searchInput = wrapper.findAllComponents({ name: 'UnnnicInput' })[1];
206
+
207
+ await searchInput.vm.$emit('update:modelValue', 'test search');
208
+
209
+ expect(wrapper.emitted('update:search')).toBeTruthy();
210
+ expect(wrapper.emitted('update:search')[0]).toEqual(['test search']);
211
+ });
212
+
213
+ test('filters options based on search term', async () => {
214
+ await wrapper.setProps({ enableSearch: true, search: 'Option 1' });
215
+
216
+ const filteredOptions = wrapper.vm.filteredOptions;
217
+ expect(filteredOptions.length).toBe(1);
218
+ expect(filteredOptions[0].label).toBe('Option 1');
219
+ });
220
+
221
+ test('filters options by both label and value', async () => {
222
+ await wrapper.setProps({ enableSearch: true, search: 'option1' });
223
+
224
+ const filteredOptions = wrapper.vm.filteredOptions;
225
+ expect(filteredOptions.length).toBe(1);
226
+ expect(filteredOptions[0].value).toBe('option1');
227
+ });
228
+
229
+ test('shows all options when search is empty', async () => {
230
+ await wrapper.setProps({ enableSearch: true, search: '' });
231
+
232
+ const filteredOptions = wrapper.vm.filteredOptions;
233
+ expect(filteredOptions.length).toBe(3);
234
+ });
235
+ });
236
+
237
+ describe('computed properties', () => {
238
+ test('calculatedMaxHeight returns correct value', () => {
239
+ const maxHeight = wrapper.vm.calculatedMaxHeight;
240
+ expect(maxHeight).toBe('235px');
241
+ });
242
+
243
+ test('calculatedMaxHeight includes search height when enabled', async () => {
244
+ await wrapper.setProps({ enableSearch: true });
245
+ const maxHeight = wrapper.vm.calculatedMaxHeight;
246
+ expect(maxHeight).toBe('289px');
247
+ });
248
+
249
+ test('calculatedMaxHeight returns unset when no options', async () => {
250
+ await wrapper.setProps({ options: [] });
251
+ const maxHeight = wrapper.vm.calculatedMaxHeight;
252
+ expect(maxHeight).toBe('unset');
253
+ });
254
+
255
+ test('selectedItem returns correct item for returnObject false', async () => {
256
+ await wrapper.setProps({ modelValue: 'option2' });
257
+ const selectedItem = wrapper.vm.selectedItem;
258
+ expect(selectedItem.label).toBe('Option 2');
259
+ expect(selectedItem.value).toBe('option2');
260
+ });
261
+
262
+ test('selectedItem returns modelValue for returnObject true', async () => {
263
+ const selectedOption = { label: 'Option 2', value: 'option2' };
264
+ await wrapper.setProps({
265
+ returnObject: true,
266
+ modelValue: selectedOption,
267
+ });
268
+ const selectedItem = wrapper.vm.selectedItem;
269
+ expect(selectedItem).toStrictEqual(selectedOption);
270
+ });
271
+
272
+ test('inputValue returns correct label', async () => {
273
+ await wrapper.setProps({ modelValue: 'option3' });
274
+ const inputValue = wrapper.vm.inputValue;
275
+ expect(inputValue).toBe('Option 3');
276
+ });
277
+
278
+ test('inputValue returns undefined when no selection', () => {
279
+ const inputValue = wrapper.vm.inputValue;
280
+ expect(inputValue).toBeUndefined();
281
+ });
282
+ });
283
+
284
+ describe('disabled state', () => {
285
+ test('passes disabled prop to input', async () => {
286
+ await wrapper.setProps({ disabled: true });
287
+ const input = wrapper.findComponent({ name: 'UnnnicInput' });
288
+ expect(input.props('disabled')).toBe(true);
289
+ });
290
+
291
+ test('input is not disabled by default', () => {
292
+ const input = wrapper.findComponent({ name: 'UnnnicInput' });
293
+ expect(input.props('disabled')).toBe(false);
294
+ });
295
+ });
296
+
297
+ describe('size prop', () => {
298
+ test('passes size prop to input', async () => {
299
+ await wrapper.setProps({ size: 'sm' });
300
+ const input = wrapper.findComponent({ name: 'UnnnicInput' });
301
+ expect(input.props('size')).toBe('sm');
302
+ });
303
+
304
+ test('defaults to md size', () => {
305
+ const input = wrapper.findComponent({ name: 'UnnnicInput' });
306
+ expect(input.props('size')).toBe('md');
307
+ });
308
+ });
309
+
310
+ describe('label and message props', () => {
311
+ test('passes label prop to input', async () => {
312
+ await wrapper.setProps({ label: 'Select Label' });
313
+ const input = wrapper.findComponent({ name: 'UnnnicInput' });
314
+ expect(input.props('label')).toBe('Select Label');
315
+ });
316
+
317
+ test('passes message prop to input', async () => {
318
+ await wrapper.setProps({ message: 'Select Message' });
319
+ const input = wrapper.findComponent({ name: 'UnnnicInput' });
320
+ expect(input.props('message')).toBe('Select Message');
321
+ });
322
+
323
+ test('passes errors prop to input', async () => {
324
+ await wrapper.setProps({ errors: 'Error message' });
325
+ const input = wrapper.findComponent({ name: 'UnnnicInput' });
326
+ expect(input.props('errors')).toBe('Error message');
327
+ });
328
+ });
329
+
330
+ describe('custom item keys', () => {
331
+ test('uses custom itemLabel and itemValue', async () => {
332
+ const customOptions = [
333
+ { name: 'Custom 1', id: 'custom1' },
334
+ { name: 'Custom 2', id: 'custom2' },
335
+ ];
336
+
337
+ await wrapper.setProps({
338
+ options: customOptions,
339
+ itemLabel: 'name',
340
+ itemValue: 'id',
341
+ modelValue: 'custom1',
342
+ });
343
+
344
+ const inputValue = wrapper.vm.inputValue;
345
+ expect(inputValue).toBe('Custom 1');
346
+ });
347
+ });
348
+
349
+ describe('edge cases', () => {
350
+ test('handles empty options array', async () => {
351
+ await wrapper.setProps({ options: [] });
352
+ expect(wrapper.vm.filteredOptions).toEqual([]);
353
+ expect(wrapper.vm.calculatedMaxHeight).toBe('unset');
354
+ });
355
+
356
+ test('handles null modelValue', async () => {
357
+ await wrapper.setProps({ modelValue: null });
358
+ expect(wrapper.vm.inputValue).toBeUndefined();
359
+ expect(wrapper.vm.selectedItem).toBeUndefined();
360
+ });
361
+
362
+ test('handles undefined modelValue', async () => {
363
+ await wrapper.setProps({ modelValue: undefined });
364
+ expect(wrapper.vm.inputValue).toBeUndefined();
365
+ expect(wrapper.vm.selectedItem).toBeUndefined();
366
+ });
367
+
368
+ test('handles case insensitive search', async () => {
369
+ await wrapper.setProps({ enableSearch: true, search: 'OPTION 1' });
370
+ const filteredOptions = wrapper.vm.filteredOptions;
371
+ expect(filteredOptions.length).toBe(1);
372
+ expect(filteredOptions[0].label).toBe('Option 1');
373
+ });
374
+
375
+ test('handles partial search matches', async () => {
376
+ await wrapper.setProps({ enableSearch: true, search: 'tion' });
377
+ const filteredOptions = wrapper.vm.filteredOptions;
378
+ expect(filteredOptions.length).toBe(3); // All options contain 'tion'
379
+ });
380
+ });
381
+
382
+ describe('component structure', () => {
383
+ test('has correct component name', () => {
384
+ expect(wrapper.vm.$options.name).toBe('UnnnicSelect');
385
+ });
386
+
387
+ test('includes i18n mixin', () => {
388
+ expect(wrapper.vm.$options.mixins).toBeDefined();
389
+ });
390
+ });
391
+
392
+ describe('snapshot testing', () => {
393
+ test('matches snapshot with default props', () => {
394
+ expect(wrapper.html()).toMatchSnapshot();
395
+ });
396
+
397
+ test('matches snapshot with selected value', async () => {
398
+ await wrapper.setProps({ modelValue: 'option1' });
399
+ expect(wrapper.html()).toMatchSnapshot();
400
+ });
401
+
402
+ test('matches snapshot with search enabled', async () => {
403
+ await wrapper.setProps({ enableSearch: true, modelValue: true });
404
+ expect(wrapper.html()).toMatchSnapshot();
405
+ });
406
+
407
+ test('matches snapshot with disabled state', async () => {
408
+ await wrapper.setProps({ disabled: true });
409
+ expect(wrapper.html()).toMatchSnapshot();
410
+ });
411
+ });
412
+ });