@weni/unnnic-system 3.11.1-alpha.2 → 3.11.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 (190) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/dist/components/Alert/Alert.vue.d.ts +3 -3
  3. package/dist/components/Alert/Version1dot1.vue.d.ts +1 -1
  4. package/dist/components/AudioRecorder/AudioHandler.vue.d.ts +18 -109
  5. package/dist/components/AudioRecorder/AudioRecorder.vue.d.ts +36 -218
  6. package/dist/components/AvatarIcon/AvatarIcon.vue.d.ts +1 -1
  7. package/dist/components/Button/Button.vue.d.ts +2 -2
  8. package/dist/components/Card/AccountCard.vue.d.ts +2 -2
  9. package/dist/components/Card/Card.vue.d.ts +29 -120
  10. package/dist/components/Card/CardCompany.vue.d.ts +2 -2
  11. package/dist/components/Card/CardStatusesContainer.vue.d.ts +19 -110
  12. package/dist/components/Card/ContentCard.vue.d.ts +1 -1
  13. package/dist/components/Card/DashCard.vue.d.ts +2 -2
  14. package/dist/components/Card/DefaultCard.vue.d.ts +1 -1
  15. package/dist/components/Card/MarketplaceCard.vue.d.ts +1 -1
  16. package/dist/components/Card/SimpleCard.vue.d.ts +18 -109
  17. package/dist/components/Card/StatusCard.vue.d.ts +1 -1
  18. package/dist/components/Card/TitleCard.vue.d.ts +18 -109
  19. package/dist/components/CardImage/CardImage.vue.d.ts +5 -5
  20. package/dist/components/CardInformation/CardInformation.vue.d.ts +19 -110
  21. package/dist/components/CardProject/CardProject.vue.d.ts +2 -2
  22. package/dist/components/Carousel/Carousel.vue.d.ts +2 -2
  23. package/dist/components/Carousel/TagCarousel.vue.d.ts +2 -2
  24. package/dist/components/ChartBar/ChartBar.vue.d.ts +24 -115
  25. package/dist/components/ChartLine/ChartLine.vue.d.ts +18 -109
  26. package/dist/components/ChatText/ChatText.vue.d.ts +18 -109
  27. package/dist/components/ChatsContact/ChatsContact.vue.d.ts +6 -6
  28. package/dist/components/ChatsMessage/ChatsMessage.vue.d.ts +18 -109
  29. package/dist/components/ChatsUserAvatar/ChatsUserAvatar.vue.d.ts +1 -1
  30. package/dist/components/Checkbox/Checkbox.vue.d.ts +3 -3
  31. package/dist/components/DataArea/DataArea.vue.d.ts +18 -109
  32. package/dist/components/DataTable/index.vue.d.ts +1 -1
  33. package/dist/components/DataTable/index.vue.d.ts.map +1 -1
  34. package/dist/components/DateFilter/DateFilter.vue.d.ts +10 -10
  35. package/dist/components/DatePicker/DatePicker.vue.d.ts +69 -249
  36. package/dist/components/DatePicker/DatePicker.vue.d.ts.map +1 -1
  37. package/dist/components/Drawer/Drawer.vue.d.ts +207 -35
  38. package/dist/components/Drawer/Drawer.vue.d.ts.map +1 -1
  39. package/dist/components/Dropdown/Dropdown.vue.d.ts +1 -1
  40. package/dist/components/FormElement/FormElement.vue.d.ts +4 -4
  41. package/dist/components/ImportCard/ImportCard.vue.d.ts +6 -6
  42. package/dist/components/Input/Input.vue.d.ts +9 -9
  43. package/dist/components/Input/TextInput.vue.d.ts +2 -2
  44. package/dist/components/InputDatePicker/InputDatePicker.vue.d.ts +47 -911
  45. package/dist/components/InputDatePicker/InputDatePicker.vue.d.ts.map +1 -1
  46. package/dist/components/InputNext/InputNext.vue.d.ts +3 -3
  47. package/dist/components/Modal/Modal.vue.d.ts +1 -1
  48. package/dist/components/ModalDialog/ModalDialog.vue.d.ts +35 -209
  49. package/dist/components/ModalDialog/ModalDialog.vue.d.ts.map +1 -1
  50. package/dist/components/ModalNext/ModalNext.vue.d.ts +15 -15
  51. package/dist/components/ModalUpload/ModalUpload.vue.d.ts +12 -12
  52. package/dist/components/MoodRating/MoodRating.vue.d.ts +18 -109
  53. package/dist/components/MultiSelect/MultiSelect.vue.d.ts +18 -18
  54. package/dist/components/Pagination/Pagination.vue.d.ts +6 -6
  55. package/dist/components/Radio/Radio.vue.d.ts +6 -6
  56. package/dist/components/Select/SelectItem.vue.d.ts +1 -1
  57. package/dist/components/SelectSmart/SelectSmart.vue.d.ts +12 -12
  58. package/dist/components/SelectSmart/SelectSmartMultipleHeader.vue.d.ts +2 -2
  59. package/dist/components/SelectSmart/SelectSmartOption.vue.d.ts +6 -6
  60. package/dist/components/SelectTime/index.vue.d.ts +3 -3
  61. package/dist/components/Sidebar/SidebarItem.vue.d.ts +2 -2
  62. package/dist/components/Slider/Slider.vue.d.ts +18 -109
  63. package/dist/components/Switch/Switch.vue.d.ts +2 -2
  64. package/dist/components/Tab/Tab.vue.d.ts +18 -109
  65. package/dist/components/TableNext/TableBodyCell.vue.d.ts +2 -2
  66. package/dist/components/TableNext/TablePagination.vue.d.ts +6 -6
  67. package/dist/components/Tag/DefaultTag.vue.d.ts +1 -1
  68. package/dist/components/Tag/Tag.vue.d.ts +2 -2
  69. package/dist/components/TextArea/TextArea.vue.d.ts +5 -5
  70. package/dist/components/Toast/Toast.vue.d.ts +1 -1
  71. package/dist/components/ToolTip/ToolTip.vue.d.ts +18 -109
  72. package/dist/components/ToolTip/ToolTip.vue.d.ts.map +1 -1
  73. package/dist/components/Tour/Tour.vue.d.ts +6 -6
  74. package/dist/components/Tour/TourPopover.vue.d.ts +6 -6
  75. package/dist/components/UploadArea/UploadArea.vue.d.ts +6 -6
  76. package/dist/components/ui/popover/PopoverContent.vue.d.ts +2 -2
  77. package/dist/components/ui/popover/PopoverContent.vue.d.ts.map +1 -1
  78. package/dist/components/ui/tabs/TabsTrigger.vue.d.ts.map +1 -1
  79. package/dist/{es-2f0d1dd1.mjs → es-042a0d15.mjs} +1 -1
  80. package/dist/{index-7d496127.mjs → index-fd0ea6b9.mjs} +51896 -54819
  81. package/dist/{pt-br-ec24bd23.mjs → pt-br-3b7cced5.mjs} +1 -1
  82. package/dist/style.css +1 -1
  83. package/dist/unnnic.mjs +196 -232
  84. package/dist/unnnic.umd.js +46 -50
  85. package/package.json +1 -2
  86. package/src/assets/scss/tailwind.scss +0 -8
  87. package/src/components/DatePicker/DatePicker.vue +628 -516
  88. package/src/components/DatePicker/__tests__/DatePicker.spec.js +227 -0
  89. package/src/components/Drawer/Drawer.vue +270 -177
  90. package/src/components/Drawer/__tests__/Drawer.spec.js +43 -32
  91. package/src/components/Drawer/__tests__/__snapshots__/Drawer.spec.js.snap +19 -18
  92. package/src/components/InputDatePicker/InputDatePicker.vue +149 -183
  93. package/src/components/InputDatePicker/__test__/InputDatePicker.spec.js +159 -0
  94. package/src/components/ModalDialog/ModalDialog.vue +148 -62
  95. package/src/components/ModalDialog/__tests__/ModalDialog.spec.js +221 -11
  96. package/src/components/ModalDialog/__tests__/__snapshots__/ModalDialog.spec.js.snap +22 -1
  97. package/src/components/Tab/__test__/__snapshots__/Tab.spec.js.snap +1 -3
  98. package/src/components/ToolTip/ToolTip.vue +188 -41
  99. package/src/components/ToolTip/__tests__/ToolTip.spec.js +61 -345
  100. package/src/components/index.ts +12 -86
  101. package/src/components/ui/tabs/TabsTrigger.vue +20 -4
  102. package/src/stories/DatePicker.stories.js +71 -0
  103. package/src/stories/Drawer.stories.js +1 -1
  104. package/src/stories/InputDatePicker.stories.js +22 -0
  105. package/src/stories/ModalDialog.mdx +0 -3
  106. package/src/stories/ModalDialog.stories.js +1 -1
  107. package/src/stories/Tabs.stories.js +1 -1
  108. package/dist/components/index.d.ts +0 -25946
  109. package/dist/components/index.d.ts.map +0 -1
  110. package/dist/components/ui/dialog/Dialog.vue.d.ts +0 -23
  111. package/dist/components/ui/dialog/Dialog.vue.d.ts.map +0 -1
  112. package/dist/components/ui/dialog/DialogClose.vue.d.ts +0 -19
  113. package/dist/components/ui/dialog/DialogClose.vue.d.ts.map +0 -1
  114. package/dist/components/ui/dialog/DialogContent.vue.d.ts +0 -43
  115. package/dist/components/ui/dialog/DialogContent.vue.d.ts.map +0 -1
  116. package/dist/components/ui/dialog/DialogFooter.vue.d.ts +0 -25
  117. package/dist/components/ui/dialog/DialogFooter.vue.d.ts.map +0 -1
  118. package/dist/components/ui/dialog/DialogHeader.vue.d.ts +0 -29
  119. package/dist/components/ui/dialog/DialogHeader.vue.d.ts.map +0 -1
  120. package/dist/components/ui/dialog/DialogTitle.vue.d.ts +0 -23
  121. package/dist/components/ui/dialog/DialogTitle.vue.d.ts.map +0 -1
  122. package/dist/components/ui/dialog/DialogTrigger.vue.d.ts +0 -19
  123. package/dist/components/ui/dialog/DialogTrigger.vue.d.ts.map +0 -1
  124. package/dist/components/ui/dialog/index.d.ts +0 -8
  125. package/dist/components/ui/dialog/index.d.ts.map +0 -1
  126. package/dist/components/ui/drawer/Drawer.vue.d.ts +0 -35
  127. package/dist/components/ui/drawer/Drawer.vue.d.ts.map +0 -1
  128. package/dist/components/ui/drawer/DrawerClose.vue.d.ts +0 -19
  129. package/dist/components/ui/drawer/DrawerClose.vue.d.ts.map +0 -1
  130. package/dist/components/ui/drawer/DrawerContent.vue.d.ts +0 -42
  131. package/dist/components/ui/drawer/DrawerContent.vue.d.ts.map +0 -1
  132. package/dist/components/ui/drawer/DrawerDescription.vue.d.ts +0 -23
  133. package/dist/components/ui/drawer/DrawerDescription.vue.d.ts.map +0 -1
  134. package/dist/components/ui/drawer/DrawerFooter.vue.d.ts +0 -22
  135. package/dist/components/ui/drawer/DrawerFooter.vue.d.ts.map +0 -1
  136. package/dist/components/ui/drawer/DrawerHeader.vue.d.ts +0 -25
  137. package/dist/components/ui/drawer/DrawerHeader.vue.d.ts.map +0 -1
  138. package/dist/components/ui/drawer/DrawerOverlay.vue.d.ts +0 -8
  139. package/dist/components/ui/drawer/DrawerOverlay.vue.d.ts.map +0 -1
  140. package/dist/components/ui/drawer/DrawerTitle.vue.d.ts +0 -23
  141. package/dist/components/ui/drawer/DrawerTitle.vue.d.ts.map +0 -1
  142. package/dist/components/ui/drawer/DrawerTrigger.vue.d.ts +0 -19
  143. package/dist/components/ui/drawer/DrawerTrigger.vue.d.ts.map +0 -1
  144. package/dist/components/ui/drawer/index.d.ts +0 -11
  145. package/dist/components/ui/drawer/index.d.ts.map +0 -1
  146. package/dist/components/ui/segmented-control/SegmentedControlList.vue.d.ts +0 -26
  147. package/dist/components/ui/segmented-control/SegmentedControlList.vue.d.ts.map +0 -1
  148. package/dist/components/ui/segmented-control/SegmentedControlTrigger.vue.d.ts +0 -24
  149. package/dist/components/ui/segmented-control/SegmentedControlTrigger.vue.d.ts.map +0 -1
  150. package/dist/components/ui/segmented-control/index.d.ts +0 -5
  151. package/dist/components/ui/segmented-control/index.d.ts.map +0 -1
  152. package/dist/components/ui/tooltip/Tooltip.vue.d.ts +0 -23
  153. package/dist/components/ui/tooltip/Tooltip.vue.d.ts.map +0 -1
  154. package/dist/components/ui/tooltip/TooltipContent.vue.d.ts +0 -31
  155. package/dist/components/ui/tooltip/TooltipContent.vue.d.ts.map +0 -1
  156. package/dist/components/ui/tooltip/TooltipProvider.vue.d.ts +0 -19
  157. package/dist/components/ui/tooltip/TooltipProvider.vue.d.ts.map +0 -1
  158. package/dist/components/ui/tooltip/TooltipTrigger.vue.d.ts +0 -19
  159. package/dist/components/ui/tooltip/TooltipTrigger.vue.d.ts.map +0 -1
  160. package/dist/components/ui/tooltip/index.d.ts +0 -5
  161. package/dist/components/ui/tooltip/index.d.ts.map +0 -1
  162. package/src/components/ui/dialog/Dialog.vue +0 -15
  163. package/src/components/ui/dialog/DialogClose.vue +0 -25
  164. package/src/components/ui/dialog/DialogContent.vue +0 -133
  165. package/src/components/ui/dialog/DialogFooter.vue +0 -46
  166. package/src/components/ui/dialog/DialogHeader.vue +0 -79
  167. package/src/components/ui/dialog/DialogTitle.vue +0 -34
  168. package/src/components/ui/dialog/DialogTrigger.vue +0 -12
  169. package/src/components/ui/dialog/index.ts +0 -7
  170. package/src/components/ui/drawer/Drawer.vue +0 -27
  171. package/src/components/ui/drawer/DrawerClose.vue +0 -37
  172. package/src/components/ui/drawer/DrawerContent.vue +0 -102
  173. package/src/components/ui/drawer/DrawerDescription.vue +0 -40
  174. package/src/components/ui/drawer/DrawerFooter.vue +0 -38
  175. package/src/components/ui/drawer/DrawerHeader.vue +0 -57
  176. package/src/components/ui/drawer/DrawerOverlay.vue +0 -34
  177. package/src/components/ui/drawer/DrawerTitle.vue +0 -37
  178. package/src/components/ui/drawer/DrawerTrigger.vue +0 -31
  179. package/src/components/ui/drawer/index.ts +0 -10
  180. package/src/components/ui/segmented-control/SegmentedControlList.vue +0 -51
  181. package/src/components/ui/segmented-control/SegmentedControlTrigger.vue +0 -81
  182. package/src/components/ui/segmented-control/index.ts +0 -4
  183. package/src/components/ui/tooltip/Tooltip.vue +0 -15
  184. package/src/components/ui/tooltip/TooltipContent.vue +0 -73
  185. package/src/components/ui/tooltip/TooltipProvider.vue +0 -15
  186. package/src/components/ui/tooltip/TooltipTrigger.vue +0 -22
  187. package/src/components/ui/tooltip/index.ts +0 -4
  188. package/src/stories/Dialog.stories.js +0 -832
  189. package/src/stories/DrawerNext.stories.js +0 -610
  190. package/src/stories/SegmentedControl.stories.js +0 -253
@@ -0,0 +1,159 @@
1
+ import { mount } from '@vue/test-utils';
2
+ import { beforeEach, describe, expect, it } from 'vitest';
3
+ import InputDatePicker from '../InputDatePicker.vue';
4
+
5
+ const factory = (props = {}) =>
6
+ mount(InputDatePicker, {
7
+ props: {
8
+ modelValue: {
9
+ start: null,
10
+ end: null,
11
+ },
12
+ ...props,
13
+ },
14
+ global: {
15
+ stubs: {
16
+ UnnnicInput: {
17
+ name: 'UnnnicInput',
18
+ template:
19
+ '<input data-testid="input" v-bind="$attrs" @focus="$emit(\'focus\', $event)" />',
20
+ },
21
+ UnnnicDatePicker: {
22
+ name: 'UnnnicDatePicker',
23
+ props: ['minDate', 'maxDate', 'periodBaseDate', 'options'],
24
+ template: '<div data-testid="datepicker"></div>',
25
+ },
26
+ },
27
+ },
28
+ });
29
+
30
+ describe('InputDatePicker.vue', () => {
31
+ let wrapper;
32
+
33
+ beforeEach(() => {
34
+ wrapper = factory();
35
+ });
36
+
37
+ it('renders input and does not show datepicker by default', () => {
38
+ expect(wrapper.find('[data-testid="input"]').exists()).toBe(true);
39
+ expect(wrapper.find('[data-testid="datepicker"]').exists()).toBe(false);
40
+ });
41
+
42
+ it('opens datepicker when input receives focus', async () => {
43
+ const input = wrapper.find('[data-testid="input"]');
44
+ await input.trigger('focus');
45
+
46
+ wrapper.vm.showCalendarFilter = true;
47
+ await wrapper.vm.$nextTick();
48
+
49
+ expect(wrapper.findComponent({ name: 'UnnnicDatePicker' }).exists()).toBe(
50
+ true,
51
+ );
52
+ });
53
+
54
+ it('computes filterText placeholder when there is no date selected', () => {
55
+ expect(wrapper.vm.filterText).toBe('mm/dd/yyyy');
56
+ });
57
+
58
+ it('computes filterText from start and end dates', async () => {
59
+ const withDates = factory({
60
+ modelValue: {
61
+ start: '2025-01-10',
62
+ end: '2025-01-20',
63
+ },
64
+ format: 'YYYY-MM-DD',
65
+ inputFormat: 'MM-DD-YYYY',
66
+ });
67
+
68
+ expect(withDates.vm.filterText).toBe('01-10-2025 ~ 01-20-2025');
69
+ });
70
+
71
+ it('computes initialStartDate and initialEndDate for DatePicker', () => {
72
+ const withDates = factory({
73
+ modelValue: {
74
+ start: '2025-01-10',
75
+ end: '2025-01-20',
76
+ },
77
+ });
78
+
79
+ expect(withDates.vm.initialStartDate).toBe('01 10 2025');
80
+ expect(withDates.vm.initialEndDate).toBe('01 20 2025');
81
+ });
82
+
83
+ it('emits selectDate with formatted dates when DatePicker emits change', async () => {
84
+ wrapper.vm.showCalendarFilter = true;
85
+ await wrapper.vm.$nextTick();
86
+
87
+ const datePicker = wrapper.findComponent({ name: 'UnnnicDatePicker' });
88
+
89
+ const payload = {
90
+ startDate: '01-10-2025',
91
+ endDate: '01-20-2025',
92
+ };
93
+
94
+ await datePicker.vm.$emit('change', payload);
95
+
96
+ const emitted = wrapper.emitted('selectDate');
97
+ expect(emitted).toBeTruthy();
98
+
99
+ const [formatted] = emitted[0];
100
+ expect(formatted).toEqual({
101
+ start: '2025-01-10',
102
+ end: '2025-01-20',
103
+ });
104
+ });
105
+
106
+ it('emits update:model-value and closes dropdown when DatePicker emits submit', async () => {
107
+ wrapper.vm.showCalendarFilter = true;
108
+ await wrapper.vm.$nextTick();
109
+
110
+ const datePicker = wrapper.findComponent({ name: 'UnnnicDatePicker' });
111
+
112
+ const payload = {
113
+ startDate: '01-10-2025',
114
+ endDate: '01-20-2025',
115
+ };
116
+
117
+ await datePicker.vm.$emit('submit', payload);
118
+
119
+ const emitted = wrapper.emitted('update:model-value');
120
+ expect(emitted).toBeTruthy();
121
+
122
+ const [newValue] = emitted[0];
123
+ expect(newValue).toEqual({
124
+ start: '2025-01-10',
125
+ end: '2025-01-20',
126
+ });
127
+
128
+ expect(wrapper.vm.showCalendarFilter).toBe(false);
129
+ });
130
+
131
+ it('passes minDate, maxDate, options and periodBaseDate down to DatePicker', async () => {
132
+ const props = {
133
+ minDate: '2025-01-01',
134
+ maxDate: '2025-01-31',
135
+ periodBaseDate: '2025-01-15',
136
+ options: [{ name: 'Last 7 days', id: 'last-7-days' }],
137
+ };
138
+
139
+ wrapper = factory(props);
140
+ wrapper.vm.showCalendarFilter = true;
141
+ await wrapper.vm.$nextTick();
142
+
143
+ const datePicker = wrapper.findComponent({ name: 'UnnnicDatePicker' });
144
+ const dpProps = datePicker.props();
145
+
146
+ expect(dpProps.minDate).toBe(props.minDate);
147
+ expect(dpProps.maxDate).toBe(props.maxDate);
148
+ expect(dpProps.periodBaseDate).toBe(props.periodBaseDate);
149
+ expect(dpProps.options).toEqual(props.options);
150
+ });
151
+
152
+ it('closes dropdown on mouseout when clicking outside', () => {
153
+ wrapper.vm.showCalendarFilter = true;
154
+
155
+ wrapper.vm.mouseout({ target: document.createElement('div') });
156
+
157
+ expect(wrapper.vm.showCalendarFilter).toBe(false);
158
+ });
159
+ });
@@ -1,46 +1,64 @@
1
1
  <template>
2
- <UnnnicDialog
3
- :open="modelValue"
4
- @update:open="$emit('update:modelValue', $event)"
2
+ <section
3
+ v-if="modelValue"
4
+ class="unnnic-modal-dialog"
5
+ data-testid="modal-dialog"
5
6
  >
6
- <UnnnicDialogContent
7
- :size="size === 'sm' ? 'small' : size === 'lg' ? 'large' : 'medium'"
8
- :parentClass="['unnnic-modal-dialog', $attrs.class]"
9
- class="unnnic-modal-dialog__container"
10
- data-testid="modal-dialog"
11
- @escape-key-down="persistentHandler"
12
- @pointer-down-outside="persistentHandler"
7
+ <section
8
+ class="unnnic-modal-dialog__overlay"
9
+ data-testid="modal-overlay"
10
+ @click.stop="!persistent && close()"
11
+ />
12
+ <section
13
+ :class="[
14
+ 'unnnic-modal-dialog__container',
15
+ `unnnic-modal-dialog__container--${size}`,
16
+ ]"
17
+ data-testid="modal-container"
13
18
  >
14
19
  <section
15
- :class="[
16
- 'unnnic-modal-dialog__container__body',
17
- {
18
- 'unnnic-modal-dialog__container__body--left-sidebar':
19
- $slots.leftSidebar,
20
- },
21
- ]"
20
+ v-if="$slots.leftSidebar"
21
+ class="unnnic-modal-dialog__container__left-sidebar"
22
22
  >
23
- <section
24
- v-if="$slots.leftSidebar"
25
- class="unnnic-modal-dialog__container__left-sidebar"
26
- >
27
- <slot name="leftSidebar" />
28
- </section>
29
- <UnnnicDialogHeader
23
+ <slot name="leftSidebar" />
24
+ </section>
25
+
26
+ <section class="unnnic-modal-dialog__container__body">
27
+ <header
30
28
  v-if="title"
31
- :closeButton="showCloseIcon"
32
- :type="type"
29
+ class="unnnic-modal-dialog__container__header"
33
30
  >
34
- <UnnnicDialogTitle>
35
- {{ title }}
36
- </UnnnicDialogTitle>
37
- </UnnnicDialogHeader>
31
+ <section class="unnnic-modal-dialog__container__title-container">
32
+ <UnnnicIcon
33
+ v-if="icon || type"
34
+ data-testid="title-icon"
35
+ class="unnnic-modal-dialog__container__title-icon"
36
+ :icon="icon || iconsMapper[type]?.icon"
37
+ :scheme="iconScheme || iconsMapper[type]?.scheme"
38
+ size="md"
39
+ />
40
+ <h1
41
+ class="unnnic-modal-dialog__container__title-text"
42
+ data-testid="title-text"
43
+ >
44
+ {{ title }}
45
+ </h1>
46
+ </section>
47
+ <UnnnicIcon
48
+ v-if="showCloseIcon"
49
+ data-testid="close-icon"
50
+ icon="close"
51
+ clickable
52
+ scheme="neutral-cloudy"
53
+ @click="close()"
54
+ />
55
+ </header>
38
56
  <section class="unnnic-modal-dialog__container__content">
39
57
  <slot></slot>
40
58
  </section>
41
-
42
- <UnnnicDialogFooter
59
+ <section
43
60
  v-if="primaryButtonProps.text"
61
+ data-testid="actions-section"
44
62
  :class="[
45
63
  'unnnic-modal-dialog__container__actions',
46
64
  {
@@ -48,8 +66,6 @@
48
66
  showActionsDivider,
49
67
  },
50
68
  ]"
51
- :divider="showActionsDivider"
52
- data-testid="actions-section"
53
69
  >
54
70
  <UnnnicButton
55
71
  v-if="!hideSecondaryButton"
@@ -76,30 +92,22 @@
76
92
  class="unnnic-modal-dialog__container__actions__primary-button"
77
93
  @click.stop="$emit('primaryButtonClick')"
78
94
  />
79
- </UnnnicDialogFooter>
95
+ </section>
80
96
  </section>
81
- </UnnnicDialogContent>
82
- </UnnnicDialog>
97
+ </section>
98
+ </section>
83
99
  </template>
84
100
 
85
101
  <script>
102
+ import UnnnicIcon from '../Icon.vue';
86
103
  import UnnnicButton from '../Button/Button.vue';
87
104
  import UnnnicI18n from '../../mixins/i18n';
88
- import UnnnicDialog from '../ui/dialog/Dialog.vue';
89
- import UnnnicDialogContent from '../ui/dialog/DialogContent.vue';
90
- import UnnnicDialogHeader from '../ui/dialog/DialogHeader.vue';
91
- import UnnnicDialogTitle from '../ui/dialog/DialogTitle.vue';
92
- import UnnnicDialogFooter from '../ui/dialog/DialogFooter.vue';
93
105
 
94
106
  export default {
95
107
  name: 'UnnnicModalDialog',
96
108
  components: {
109
+ UnnnicIcon,
97
110
  UnnnicButton,
98
- UnnnicDialog,
99
- UnnnicDialogContent,
100
- UnnnicDialogHeader,
101
- UnnnicDialogTitle,
102
- UnnnicDialogFooter,
103
111
  },
104
112
  mixins: [UnnnicI18n],
105
113
  props: {
@@ -169,6 +177,11 @@ export default {
169
177
  es: 'Cancelar',
170
178
  },
171
179
  },
180
+ iconsMapper: {
181
+ success: { icon: 'check_circle', scheme: 'aux-green-500' },
182
+ warning: { icon: 'warning', scheme: 'aux-red-500' },
183
+ attention: { icon: 'error', scheme: 'aux-yellow-500' },
184
+ },
172
185
  primaryButtonTypeMapper: {
173
186
  success: 'primary',
174
187
  warning: 'warning',
@@ -188,39 +201,91 @@ export default {
188
201
  updateBodyOverflow(isHidden) {
189
202
  document.body.style.overflow = isHidden ? 'hidden' : '';
190
203
  },
191
- persistentHandler(event) {
192
- if (this.persistent) {
193
- event.preventDefault();
194
- }
195
- },
196
204
  },
197
205
  };
198
206
  </script>
199
207
 
200
208
  <style lang="scss" scoped>
201
209
  @use '@/assets/scss/unnnic' as *;
210
+ * {
211
+ margin: 0;
212
+ padding: 0;
213
+ box-sizing: border-box;
214
+ }
215
+ .unnnic-modal-dialog {
216
+ width: 100vw;
217
+ height: 100vh;
218
+ position: fixed;
219
+ left: 0;
220
+ top: 0;
221
+ display: flex;
222
+ justify-content: center;
223
+ align-items: center;
224
+ z-index: 9999;
225
+
226
+ &__overlay {
227
+ position: absolute;
228
+ width: 100%;
229
+ height: 100%;
230
+ background-color: rgba(0, 0, 0, 0.4);
231
+ }
232
+ }
233
+
202
234
  .unnnic-modal-dialog__container {
235
+ display: flex;
236
+ background: $unnnic-color-neutral-white;
237
+ border-radius: $unnnic-spacing-xs;
238
+ box-shadow: $unnnic-shadow-level-near;
239
+ position: fixed;
240
+ max-height: calc(100vh - $unnnic-spacing-giant);
241
+ overflow: hidden;
242
+
243
+ &--sm {
244
+ width: 400px;
245
+ }
246
+ &--md {
247
+ width: 600px;
248
+ }
249
+ &--lg {
250
+ width: 800px;
251
+ }
252
+
203
253
  &__left-sidebar {
204
254
  background-color: $unnnic-color-neutral-black;
205
255
  color: $unnnic-color-neutral-white;
206
-
207
- grid-area: left-sidebar;
208
- grid-row: span 3;
209
256
  }
210
257
 
211
258
  &__body {
212
259
  flex: 1;
213
260
  display: flex;
214
261
  flex-direction: column;
262
+ }
215
263
 
216
- overflow-y: auto;
264
+ &__header {
265
+ display: flex;
266
+ justify-content: space-between;
267
+ align-items: center;
268
+ border-bottom: 1px solid $unnnic-color-neutral-soft;
269
+ padding: $unnnic-spacing-md;
270
+ flex-shrink: 0;
271
+ }
217
272
 
218
- &--left-sidebar {
219
- border-radius: $unnnic-radius-4 0 0 $unnnic-radius-4;
220
- display: grid;
221
- grid-template-columns: auto 1fr;
222
- grid-template-areas: 'left-sidebar content';
223
- }
273
+ &__title-container {
274
+ display: flex;
275
+ align-items: center;
276
+ gap: $unnnic-spacing-ant;
277
+ }
278
+
279
+ &__title-icon {
280
+ font-size: 28px;
281
+ }
282
+
283
+ &__title-text {
284
+ font-family: $unnnic-font-family-secondary;
285
+ font-size: $unnnic-font-size-title-sm;
286
+ font-weight: $unnnic-font-weight-black;
287
+ line-height: 28px;
288
+ color: $unnnic-color-neutral-darkest;
224
289
  }
225
290
 
226
291
  &__content {
@@ -244,5 +309,26 @@ export default {
244
309
  border-radius: $unnnic-border-radius-pill;
245
310
  }
246
311
  }
312
+
313
+ &__actions {
314
+ display: grid;
315
+ grid-template-columns: 1fr 1fr;
316
+ grid-template-areas: 'secondary-button primary-button';
317
+ gap: $unnnic-spacing-sm;
318
+ padding: $unnnic-spacing-md;
319
+ flex-shrink: 0;
320
+
321
+ &--divider {
322
+ border-top: 1px solid $unnnic-color-neutral-soft;
323
+ }
324
+
325
+ &__secondary-button {
326
+ grid-area: secondary-button;
327
+ }
328
+
329
+ &__primary-button {
330
+ grid-area: primary-button;
331
+ }
332
+ }
247
333
  }
248
334
  </style>
@@ -1,5 +1,5 @@
1
1
  import { mount } from '@vue/test-utils';
2
- import { describe, it } from 'vitest';
2
+ import { describe, it, vi } from 'vitest';
3
3
  import ModalDialog from '../ModalDialog.vue';
4
4
 
5
5
  describe('ModalDialog.vue', () => {
@@ -16,12 +16,7 @@ describe('ModalDialog.vue', () => {
16
16
  'onUpdate:modelValue': (e) => wrapper.setProps({ modelValue: e }),
17
17
  },
18
18
  global: {
19
- stubs: [
20
- 'teleport',
21
- 'UnnnicIcon',
22
- 'UnnnicButton',
23
- 'UnnnicDialogContent',
24
- ],
19
+ stubs: ['UnnnicIcon', 'UnnnicButton'],
25
20
  },
26
21
  });
27
22
  });
@@ -31,16 +26,231 @@ describe('ModalDialog.vue', () => {
31
26
  expect(wrapper.html()).toMatchSnapshot();
32
27
  });
33
28
 
29
+ it('should render correctly when modelValue is true', () => {
30
+ const modal = wrapper.find('[data-testid="modal-dialog"]');
31
+ expect(modal.exists()).toBe(true);
32
+ });
33
+
34
+ it('should not render when modelValue is false', async () => {
35
+ await wrapper.setProps({ modelValue: false });
36
+
37
+ const modal = wrapper.find('[data-testid="modal-dialog"]');
38
+ expect(modal.exists()).toBe(false);
39
+ });
40
+
34
41
  it('should apply the correct size class based on the size prop', async () => {
35
- const modalContainer = wrapper.find('[data-testid="modal-dialog"]');
42
+ const modalContainer = wrapper.find('[data-testid="modal-container"]');
43
+ const defaultClass = 'unnnic-modal-dialog__container';
36
44
 
37
- expect(modalContainer.attributes('size')).toContain('medium');
45
+ expect(modalContainer.classes()).toContain(defaultClass + '--md');
38
46
 
39
47
  await wrapper.setProps({ size: 'lg' });
40
- expect(modalContainer.attributes('size')).toContain('large');
48
+ expect(modalContainer.classes()).toContain(defaultClass + '--lg');
41
49
 
42
50
  await wrapper.setProps({ size: 'sm' });
43
- expect(modalContainer.attributes('size')).toContain('small');
51
+ expect(modalContainer.classes()).toContain(defaultClass + '--sm');
52
+ });
53
+
54
+ it('should render the icon and title when provided', () => {
55
+ const title = wrapper.find('[data-testid="title-text"]');
56
+ expect(title.exists()).toBe(true);
57
+ expect(title.text()).toBe('Test Title');
58
+
59
+ const icon = wrapper.findComponent('[data-testid="title-icon"]');
60
+ expect(icon.exists()).toBe(true);
61
+ expect(icon.props('icon')).toBe('test-icon');
62
+ });
63
+
64
+ it('should render the icon from iconsMapper when type prop is provided', async () => {
65
+ await wrapper.setProps({
66
+ type: 'success',
67
+ icon: '',
68
+ });
69
+
70
+ const icon = wrapper.findComponent('[data-testid="title-icon"]');
71
+ expect(icon.exists()).toBe(true);
72
+ expect(icon.props().icon).toBe(wrapper.vm.iconsMapper['success'].icon);
73
+ });
74
+
75
+ it('should not render the icon when both icon and type props are not provided', async () => {
76
+ await wrapper.setProps({
77
+ icon: '',
78
+ type: '',
79
+ });
80
+
81
+ const icon = wrapper.find('[data-testid="title-icon"]');
82
+ expect(icon.exists()).toBe(false);
83
+ });
84
+
85
+ it('should render the close icon when showCloseIcon is true', () => {
86
+ const closeIcon = wrapper.find('[data-testid="close-icon"]');
87
+ expect(closeIcon.exists()).toBe(true);
88
+ });
89
+
90
+ it('should not render buttons when primaryButtonProps is not provided', async () => {
91
+ await wrapper.setProps({ primaryButtonProps: undefined });
92
+
93
+ const primaryButton = wrapper.find('[data-testid="primary-button"]');
94
+ const secondaryButton = wrapper.find('[data-testid="secondary-button"]');
95
+ expect(primaryButton.exists()).toBe(false);
96
+ expect(secondaryButton.exists()).toBe(false);
97
+ });
98
+ });
99
+
100
+ describe('Overlay behavior', () => {
101
+ it('should close the modal when clicking on the overlay if persistent is false', async () => {
102
+ expect(wrapper.props().modelValue).toBe(true);
103
+
104
+ const overlay = wrapper.find('[data-testid="modal-overlay"]');
105
+ await overlay.trigger('click');
106
+
107
+ expect(wrapper.emitted('update:modelValue')).toBeTruthy();
108
+ expect(wrapper.emitted('update:modelValue')[0]).toEqual([false]);
109
+ expect(wrapper.props().modelValue).toBe(false);
110
+ });
111
+
112
+ it('should not close the modal when clicking on the overlay if persistent is true', async () => {
113
+ await wrapper.setProps({ persistent: true });
114
+
115
+ const overlay = wrapper.find('[data-testid="modal-overlay"]');
116
+ await overlay.trigger('click');
117
+
118
+ expect(wrapper.emitted('update:modelValue')).toBeFalsy();
119
+ expect(wrapper.props().modelValue).toBe(true);
120
+ });
121
+ });
122
+
123
+ describe('Slot rendering', () => {
124
+ it('should render leftSidebar slot when provided', () => {
125
+ const wrapper = mount(ModalDialog, {
126
+ props: {
127
+ modelValue: true,
128
+ },
129
+ slots: {
130
+ leftSidebar:
131
+ '<div data-testid="left-sidebar">Left Sidebar Content</div>',
132
+ },
133
+ });
134
+
135
+ const leftSidebar = wrapper.find('[data-testid="left-sidebar"]');
136
+ expect(leftSidebar.exists()).toBe(true);
137
+ expect(leftSidebar.text()).toBe('Left Sidebar Content');
138
+ });
139
+
140
+ it('should render default slot content', () => {
141
+ const wrapper = mount(ModalDialog, {
142
+ props: {
143
+ modelValue: true,
144
+ },
145
+ slots: {
146
+ default: '<div data-testid="default-slot">Default Slot Content</div>',
147
+ },
148
+ });
149
+
150
+ const defaultSlot = wrapper.find('[data-testid="default-slot"]');
151
+ expect(defaultSlot.exists()).toBe(true);
152
+ expect(defaultSlot.text()).toBe('Default Slot Content');
153
+ });
154
+ });
155
+
156
+ describe('Buttons actions', () => {
157
+ it('should close the modal when clicking on close icon', async () => {
158
+ const closeIcon = wrapper.find('[data-testid="close-icon"]');
159
+ await closeIcon.trigger('click');
160
+
161
+ expect(wrapper.emitted('update:modelValue')).toBeTruthy();
162
+ expect(wrapper.emitted('update:modelValue')[0]).toEqual([false]);
163
+ expect(wrapper.props().modelValue).toBe(false);
164
+ });
165
+
166
+ it('should emit primaryButtonClick event when the primary button is clicked', async () => {
167
+ const primaryButton = wrapper.find('[data-testid="primary-button"]');
168
+ await primaryButton.trigger('click');
169
+
170
+ expect(wrapper.emitted('primaryButtonClick')).toBeTruthy();
171
+ });
172
+
173
+ it('should emit secondaryButtonClick event when the secondary button is clicked', async () => {
174
+ await wrapper.setProps({
175
+ secondaryButtonProps: { text: 'Cancel' },
176
+ });
177
+
178
+ const secondaryButton = wrapper.find('[data-testid="secondary-button"]');
179
+ await secondaryButton.trigger('click');
180
+
181
+ expect(wrapper.emitted('secondaryButtonClick')).toBeTruthy();
182
+ });
183
+
184
+ it('should close the modal when the secondary button is clicked and no secondaryButtonClick event is provided', async () => {
185
+ const secondaryButton = wrapper.find('[data-testid="secondary-button"]');
186
+ await secondaryButton.trigger('click');
187
+
188
+ const emittedValue = wrapper.emitted('update:modelValue');
189
+
190
+ expect(emittedValue).toBeTruthy();
191
+ expect(emittedValue[0][0]).toEqual(false);
192
+ });
193
+ });
194
+
195
+ describe('Actions and appearance', () => {
196
+ it('should not render the secondary button when hideSecondaryButton is true', async () => {
197
+ await wrapper.setProps({
198
+ hideSecondaryButton: true,
199
+ });
200
+ const secondaryButton = wrapper.find('[data-testid="secondary-button"]');
201
+ expect(secondaryButton.exists()).toBe(false);
202
+ });
203
+
204
+ it('should apply a divider class to the actions section when showActionsDivider is true', async () => {
205
+ await wrapper.setProps({
206
+ showActionsDivider: true,
207
+ });
208
+
209
+ const actionsSection = wrapper.find('[data-testid="actions-section"]');
210
+ expect(actionsSection.classes()).toContain(
211
+ 'unnnic-modal-dialog__container__actions--divider',
212
+ );
213
+ });
214
+ });
215
+
216
+ describe('Body overflow', () => {
217
+ it('should toggle body overflow based on modal visibility', async () => {
218
+ await wrapper.setProps({ modelValue: false });
219
+ const updateBodyOverflowSpy = vi.spyOn(wrapper.vm, 'updateBodyOverflow');
220
+
221
+ await wrapper.setProps({ modelValue: true });
222
+ expect(updateBodyOverflowSpy).toHaveBeenCalledWith(true);
223
+
224
+ await wrapper.setProps({ modelValue: false });
225
+ expect(updateBodyOverflowSpy).toHaveBeenCalledWith(false);
226
+ });
227
+ });
228
+
229
+ describe('Validators', () => {
230
+ describe('type prop validator', () => {
231
+ it('should validate type prop when a valid value is provided', () => {
232
+ const validTypes = ['success', 'warning', 'attention'];
233
+ validTypes.forEach((validType) => {
234
+ expect(ModalDialog.props.type.validate(validType)).toBe(true);
235
+ });
236
+ });
237
+
238
+ it('should invalidate type prop when an invalid value is provided', () => {
239
+ expect(ModalDialog.props.type.validate('invalidType')).toBe(false);
240
+ });
241
+ });
242
+
243
+ describe('size prop validator', () => {
244
+ it('should validate size prop when a valid value is provided', () => {
245
+ const validSizes = ['sm', 'md', 'lg'];
246
+ validSizes.forEach((validSize) => {
247
+ expect(ModalDialog.props.size.validate(validSize)).toBe(true);
248
+ });
249
+ });
250
+
251
+ it('should invalidate size prop when an invalid value is provided', () => {
252
+ expect(ModalDialog.props.size.validate('invalidSize')).toBe(false);
253
+ });
44
254
  });
45
255
  });
46
256
  });