@weni/unnnic-system 3.12.8-alpha-teleports.0 → 3.12.8-alpha.1

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 (84) hide show
  1. package/.vscode/extensions.json +3 -0
  2. package/CHANGELOG.md +1114 -0
  3. package/README.md +1 -9
  4. package/dist/{es-2f6793d2.mjs → es-9d6892f7.mjs} +1 -1
  5. package/dist/{index-799af668.mjs → index-78117c0a.mjs} +9299 -8975
  6. package/dist/index.d.ts +870 -321
  7. package/dist/{pt-br-f5121b47.mjs → pt-br-89839646.mjs} +1 -1
  8. package/dist/style.css +1 -1
  9. package/dist/unnnic.mjs +156 -154
  10. package/dist/unnnic.umd.js +33 -33
  11. package/package.json +1 -1
  12. package/src/components/Alert/__tests__/__snapshots__/Alert.spec.js.snap +1 -1
  13. package/src/components/ChartFunnel/DefaultFunnel/ChartDefaultFunnelBase.vue +1 -2
  14. package/src/components/ChartFunnel/SvgFunnel/ChartFunnelTwoRows.vue +60 -61
  15. package/src/components/Checkbox/Checkbox.vue +9 -3
  16. package/src/components/CheckboxGroup/CheckboxGroup.vue +7 -5
  17. package/src/components/Chip/Chip.vue +1 -1
  18. package/src/components/DatePicker/DatePicker.vue +1 -11
  19. package/src/components/Drawer/Drawer.vue +20 -9
  20. package/src/components/Drawer/__tests__/Drawer.spec.js +11 -9
  21. package/src/components/Drawer/__tests__/__snapshots__/Drawer.spec.js.snap +9 -9
  22. package/src/components/EmojiPicker/EmojiPicker.vue +1 -1
  23. package/src/components/FormElement/FormElement.vue +97 -88
  24. package/src/components/Input/BaseInput.vue +25 -5
  25. package/src/components/Input/Input.scss +2 -1
  26. package/src/components/Input/Input.vue +26 -3
  27. package/src/components/Input/TextInput.vue +64 -25
  28. package/src/components/Input/__test__/TextInput.spec.js +1 -1
  29. package/src/components/Input/__test__/__snapshots__/Input.spec.js.snap +5 -1
  30. package/src/components/Input/__test__/__snapshots__/TextInput.spec.js.snap +7 -1
  31. package/src/components/InputDatePicker/InputDatePicker.vue +73 -68
  32. package/src/components/InputDatePicker/__test__/InputDatePicker.spec.js +24 -31
  33. package/src/components/ModalDialog/ModalDialog.vue +8 -1
  34. package/src/components/ModalDialog/__tests__/ModalDialog.spec.js +1 -31
  35. package/src/components/MultiSelect/MultSelectOption.vue +49 -0
  36. package/src/components/MultiSelect/__tests__/MultiSelect.spec.js +557 -0
  37. package/src/components/MultiSelect/__tests__/MultiSelectOption.spec.js +229 -0
  38. package/src/components/MultiSelect/__tests__/__snapshots__/MultiSelect.spec.js.snap +87 -0
  39. package/src/components/MultiSelect/__tests__/__snapshots__/MultiSelectOption.spec.js.snap +51 -0
  40. package/src/components/MultiSelect/index.vue +265 -0
  41. package/src/components/Radio/Radio.vue +13 -7
  42. package/src/components/Radio/__test__/Radio.spec.js +3 -1
  43. package/src/components/RadioGroup/RadioGroup.vue +18 -10
  44. package/src/components/Select/__tests__/Select.spec.js +422 -0
  45. package/src/components/Select/__tests__/SelectItem.spec.js +330 -0
  46. package/src/components/Select/__tests__/__snapshots__/Popover.spec.js.snap +8 -0
  47. package/src/components/Select/__tests__/__snapshots__/Select.spec.js.snap +71 -0
  48. package/src/components/Select/__tests__/__snapshots__/SelectItem.spec.js.snap +15 -0
  49. package/src/components/Select/__tests__/__snapshots__/SelectOption.spec.js.snap +25 -0
  50. package/src/components/Select/__tests__/__snapshots__/SelectPopover.spec.js.snap +8 -0
  51. package/src/components/Select/index.vue +308 -0
  52. package/src/components/Switch/Switch.vue +11 -4
  53. package/src/components/TemplatePreview/TemplatePreview.vue +30 -27
  54. package/src/components/TemplatePreview/TemplatePreviewModal.vue +11 -11
  55. package/src/components/TemplatePreview/types.d.ts +3 -3
  56. package/src/components/Toast/Toast.vue +13 -9
  57. package/src/components/Toast/ToastManager.ts +1 -4
  58. package/src/components/Toast/__tests__/ToastManager.spec.js +6 -10
  59. package/src/components/ToolTip/ToolTip.vue +1 -1
  60. package/src/components/index.ts +6 -6
  61. package/src/components/ui/dialog/DialogContent.vue +5 -5
  62. package/src/components/ui/drawer/DrawerContent.vue +2 -4
  63. package/src/components/ui/popover/PopoverContent.vue +2 -4
  64. package/src/components/ui/popover/PopoverOption.vue +4 -0
  65. package/src/components/ui/tooltip/TooltipContent.vue +2 -5
  66. package/src/components/ui/tooltip/TooltipTrigger.vue +4 -2
  67. package/src/index.ts +2 -9
  68. package/src/lib/layer-manager.ts +52 -24
  69. package/src/locales/en.json +3 -1
  70. package/src/locales/es.json +3 -1
  71. package/src/locales/pt_br.json +3 -1
  72. package/src/stories/Input.mdx +3 -0
  73. package/src/stories/LayerManager.docs.mdx +9 -9
  74. package/src/stories/LayerManager.stories.js +11 -54
  75. package/src/stories/ModalDialog.stories.js +0 -95
  76. package/src/stories/MultiSelect.stories.js +143 -45
  77. package/src/stories/Select.stories.js +161 -0
  78. package/src/stories/TemplatePreview.stories.js +27 -27
  79. package/src/stories/TemplatePreviewModal.stories.js +31 -31
  80. package/dist/apple-64.png +0 -0
  81. package/public/apple-64.png +0 -0
  82. package/src/components/MultiSelect/MultiSelect.vue +0 -297
  83. package/src/lib/__tests__/teleport-target.spec.ts +0 -73
  84. package/src/lib/teleport-target.ts +0 -46
@@ -0,0 +1,308 @@
1
+ <template>
2
+ <div
3
+ class="unnnic-select"
4
+ @keydown="handleKeyDown"
5
+ >
6
+ <Popover
7
+ :open="openPopover"
8
+ @update:open="openPopover = $event"
9
+ >
10
+ <PopoverTrigger class="w-full">
11
+ <UnnnicInput
12
+ ref="selectInputRef"
13
+ :modelValue="inputValue"
14
+ class="unnnic-select__input"
15
+ readonly
16
+ useFocusProp
17
+ :focus="openPopover"
18
+ :size="props.size"
19
+ :placeholder="props.placeholder"
20
+ :label="props.label"
21
+ :errors="props.errors"
22
+ :message="props.message"
23
+ :iconRight="openPopover ? 'keyboard_arrow_up' : 'keyboard_arrow_down'"
24
+ :disabled="props.disabled"
25
+ :showClear="!!selectedItem"
26
+ @clear="emit('update:modelValue', '')"
27
+ />
28
+ </PopoverTrigger>
29
+ <PopoverContent
30
+ align="start"
31
+ :class="'h-full'"
32
+ :style="popoverContentCustomStyles"
33
+ :width="inputWidthString"
34
+ >
35
+ <div class="unnnic-select__content">
36
+ <UnnnicInput
37
+ v-if="props.enableSearch"
38
+ class="unnnic-select__input-search"
39
+ :modelValue="props.search"
40
+ :placeholder="$t('search')"
41
+ iconLeft="search"
42
+ @update:model-value="handleSearch"
43
+ />
44
+ <p
45
+ v-if="filteredOptions.length === 0"
46
+ class="unnnic-select__content-no-results"
47
+ >
48
+ {{ $t('without_results') }}
49
+ </p>
50
+ <PopoverOption
51
+ v-for="(option, index) in filteredOptions"
52
+ v-else
53
+ :key="option[props.itemValue]"
54
+ :data-option-index="index"
55
+ data-testid="select-option"
56
+ :label="option[props.itemLabel]"
57
+ :active="
58
+ option[props.itemValue] === selectedItem?.[props.itemValue]
59
+ "
60
+ :focused="focusedOptionIndex === index"
61
+ :disabled="option.disabled"
62
+ @click="handleSelectOption(option)"
63
+ />
64
+ </div>
65
+ </PopoverContent>
66
+ </Popover>
67
+ </div>
68
+ </template>
69
+
70
+ <script setup lang="ts">
71
+ import { computed, ref, watch, nextTick } from 'vue';
72
+ import { useElementSize } from '@vueuse/core';
73
+
74
+ import UnnnicInput from '../Input/Input.vue';
75
+
76
+ import {
77
+ Popover,
78
+ PopoverTrigger,
79
+ PopoverContent,
80
+ PopoverOption,
81
+ } from '../ui/popover/index';
82
+
83
+ import UnnnicI18n from '../../mixins/i18n';
84
+
85
+ defineOptions({
86
+ name: 'UnnnicSelect',
87
+ mixins: [UnnnicI18n],
88
+ });
89
+
90
+ interface SelectProps {
91
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
92
+ options: Array<{ [key: string]: any }>;
93
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
94
+ modelValue: any;
95
+ returnObject?: boolean;
96
+ itemLabel?: string;
97
+ itemValue?: string;
98
+ placeholder?: string;
99
+ label?: string;
100
+ type?: 'normal' | 'error';
101
+ errors?: string | Array<string>;
102
+ message?: string;
103
+ size?: 'sm' | 'md';
104
+ optionsLines?: number;
105
+ enableSearch?: boolean;
106
+ search?: string;
107
+ locale?: string;
108
+ disabled?: boolean;
109
+ }
110
+
111
+ const props = withDefaults(defineProps<SelectProps>(), {
112
+ size: 'md',
113
+ type: 'normal',
114
+ placeholder: '',
115
+ optionsLines: 5,
116
+ returnObject: false,
117
+ itemLabel: 'label',
118
+ itemValue: 'value',
119
+ locale: 'en',
120
+ enableSearch: false,
121
+ disabled: false,
122
+ label: '',
123
+ errors: '',
124
+ message: '',
125
+ search: '',
126
+ });
127
+
128
+ const emit = defineEmits<{
129
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
130
+ 'update:modelValue': [value: any];
131
+ 'update:search': [value: string];
132
+ }>();
133
+
134
+ const openPopover = ref(false);
135
+ const selectInputRef = ref<HTMLInputElement | null>(null);
136
+ const { width: inputWidth } = useElementSize(selectInputRef);
137
+
138
+ const inputWidthString = computed(() => {
139
+ return `${inputWidth.value}px`;
140
+ });
141
+
142
+ watch(openPopover, () => {
143
+ if (!openPopover.value) {
144
+ handleSearch('');
145
+ } else {
146
+ focusedOptionIndex.value = -1;
147
+ }
148
+
149
+ if (openPopover.value && props.modelValue) {
150
+ const selectedOptionIndex = props.options.findIndex(
151
+ (option) =>
152
+ option[props.itemValue] === selectedItem.value?.[props.itemValue],
153
+ );
154
+ scrollToOption(selectedOptionIndex, 'instant', 'center');
155
+ }
156
+ });
157
+
158
+ const handleKeyDown = (event) => {
159
+ const { key } = event;
160
+ const validKeys = ['ArrowUp', 'ArrowDown', 'Enter'];
161
+
162
+ if (validKeys.includes(key)) {
163
+ event.preventDefault();
164
+ if (key === 'ArrowUp') {
165
+ if (focusedOptionIndex.value === 0) return;
166
+ focusedOptionIndex.value--;
167
+ scrollToOption(focusedOptionIndex.value);
168
+ }
169
+ if (key === 'ArrowDown') {
170
+ if (focusedOptionIndex.value === filteredOptions.value.length - 1) return;
171
+ focusedOptionIndex.value++;
172
+ scrollToOption(focusedOptionIndex.value);
173
+ }
174
+ if (key === 'Enter' && focusedOptionIndex.value !== -1) {
175
+ handleSelectOption(filteredOptions.value[focusedOptionIndex.value]);
176
+ }
177
+ }
178
+ };
179
+
180
+ const focusedOptionIndex = ref<number>(-1);
181
+
182
+ const scrollToOption = (
183
+ index: number,
184
+ behavior: 'smooth' | 'instant' = 'smooth',
185
+ block: 'center' | 'start' | 'end' | 'nearest' = 'center',
186
+ ) => {
187
+ nextTick(() => {
188
+ const option = document.querySelector(`[data-option-index="${index}"]`);
189
+ if (option) {
190
+ option.scrollIntoView?.({ behavior, block });
191
+ }
192
+ });
193
+ };
194
+
195
+ const calculatedPopoverHeight = computed(() => {
196
+ if (!props.options || props.options.length === 0) return 'unset';
197
+ const popoverPadding = 32;
198
+ const popoverGap = 4;
199
+ // 37 = 21px (height) + 16px (padding)
200
+ const fieldsHeight = 37 * props.optionsLines;
201
+ const gapsCompensation = props.enableSearch ? 1 : 2;
202
+
203
+ const size =
204
+ fieldsHeight +
205
+ popoverPadding +
206
+ (popoverGap * props.optionsLines - gapsCompensation);
207
+
208
+ return `${props.enableSearch ? size + 45 + 1 : size}px`;
209
+ });
210
+
211
+ const popoverContentCustomStyles = computed(() => {
212
+ const emptyFilteredOptions = filteredOptions.value?.length === 0;
213
+ return {
214
+ overflow: 'auto',
215
+ display: 'flex',
216
+ flexDirection: 'column',
217
+ minHeight: calculatedPopoverHeight.value,
218
+ maxHeight: emptyFilteredOptions ? 'unset' : calculatedPopoverHeight.value,
219
+ height: emptyFilteredOptions ? calculatedPopoverHeight.value : 'unset',
220
+ };
221
+ });
222
+
223
+ const selectedItem = computed(() => {
224
+ if (props.returnObject) return props.modelValue;
225
+
226
+ return props.options.find(
227
+ (option) => option[props.itemValue] === props.modelValue,
228
+ );
229
+ });
230
+
231
+ const inputValue = computed(() => {
232
+ return selectedItem.value?.[props.itemLabel];
233
+ });
234
+
235
+ const handleSelectOption = (option) => {
236
+ if (
237
+ option[props.itemValue] === selectedItem.value?.[props.itemValue] ||
238
+ option.disabled
239
+ )
240
+ return;
241
+
242
+ emit(
243
+ 'update:modelValue',
244
+ props.returnObject ? option : option[props.itemValue],
245
+ );
246
+ openPopover.value = false;
247
+ };
248
+
249
+ const handleSearch = (value: string) => {
250
+ emit('update:search', value);
251
+ };
252
+
253
+ const filteredOptions = computed(() => {
254
+ if (!props.enableSearch || !props.search) return props.options;
255
+
256
+ return props.options.filter(
257
+ (option) =>
258
+ option[props.itemLabel]
259
+ .toLowerCase()
260
+ .includes(props.search?.toLowerCase()) ||
261
+ option[props.itemValue]
262
+ .toLowerCase()
263
+ .includes(props.search?.toLowerCase()),
264
+ );
265
+ });
266
+ </script>
267
+
268
+ <style lang="scss" scoped>
269
+ @use '@/assets/scss/unnnic' as *;
270
+
271
+ :deep(.unnnic-select__input) {
272
+ cursor: pointer;
273
+ }
274
+
275
+ :deep(.unnnic-select__input-search) {
276
+ > .icon-left {
277
+ color: $unnnic-color-fg-base;
278
+ }
279
+ }
280
+
281
+ :deep(.unnnic-select__input) {
282
+ > .icon-right {
283
+ color: $unnnic-color-fg-base;
284
+ }
285
+ }
286
+
287
+ .unnnic-select {
288
+ &__content {
289
+ display: flex;
290
+ flex-direction: column;
291
+ padding: 0;
292
+ margin: 0;
293
+ gap: $unnnic-space-1;
294
+
295
+ height: -webkit-fill-available;
296
+
297
+ &-no-results {
298
+ margin: 0;
299
+ display: flex;
300
+ align-items: center;
301
+ justify-content: center;
302
+ height: 100%;
303
+ font: $unnnic-font-emphasis;
304
+ color: $unnnic-color-fg-muted;
305
+ }
306
+ }
307
+ }
308
+ </style>
@@ -8,14 +8,19 @@
8
8
  class="unnnic-switch__label"
9
9
  />
10
10
 
11
- <label :class="['unnnic-switch__input-wrapper', {'unnnic-switch__input-wrapper--disabled': disabled}]">
11
+ <label
12
+ :class="[
13
+ 'unnnic-switch__input-wrapper',
14
+ { 'unnnic-switch__input-wrapper--disabled': disabled },
15
+ ]"
16
+ >
12
17
  <input
13
18
  class="unnnic-switch__input"
14
19
  type="checkbox"
15
20
  :disabled="disabled"
16
21
  :checked="modelValue"
17
- @change="toggleState"
18
22
  v-bind="pick($attrs, ['id', 'name'])"
23
+ @change="toggleState"
19
24
  />
20
25
 
21
26
  <p
@@ -78,7 +83,7 @@ export default {
78
83
  type: String,
79
84
  default: '',
80
85
  },
81
-
86
+
82
87
  helper: {
83
88
  type: String,
84
89
  default: '',
@@ -176,7 +181,9 @@ $switch-height: 20px;
176
181
  background-repeat: no-repeat;
177
182
  background-position: 4px center;
178
183
 
179
- transition: 120ms linear background-position, 120ms linear background-color;
184
+ transition:
185
+ 120ms linear background-position,
186
+ 120ms linear background-color;
180
187
 
181
188
  cursor: pointer;
182
189
 
@@ -13,11 +13,11 @@
13
13
  }`"
14
14
  >
15
15
  <img
16
- class="unnnic-template-preview__header__media__preview"
17
16
  v-if="
18
17
  template?.header.type === 'MEDIA' &&
19
18
  template?.header.mediaType === 'IMAGE'
20
19
  "
20
+ class="unnnic-template-preview__header__media__preview"
21
21
  :src="template.header.src || imagePreview"
22
22
  />
23
23
  <template
@@ -50,8 +50,8 @@
50
50
  />
51
51
  </template>
52
52
  <h1
53
- class="unnnic-template-preview__header__text__preview"
54
53
  v-else-if="template?.header.type === 'TEXT'"
54
+ class="unnnic-template-preview__header__text__preview"
55
55
  >
56
56
  {{ template.header.text }}
57
57
  </h1>
@@ -75,7 +75,10 @@
75
75
  {{ template?.footer }}
76
76
  </footer>
77
77
  </section>
78
- <footer v-if="hasButtons" class="unnnic-template-preview__buttons">
78
+ <footer
79
+ v-if="hasButtons"
80
+ class="unnnic-template-preview__buttons"
81
+ >
79
82
  <section
80
83
  v-for="(button, index) in template?.buttons"
81
84
  :key="`button-${index}`"
@@ -96,15 +99,15 @@
96
99
  </template>
97
100
 
98
101
  <script lang="ts" setup>
99
- import { computed } from "vue";
102
+ import { computed } from 'vue';
100
103
 
101
- import type { Template } from "./types";
104
+ import type { Template } from './types';
102
105
 
103
- import imagePreview from "../../assets/img/previews/image-preview.png";
104
- import documentPreview from "../../assets/img/previews/doc-preview.png";
105
- import videoPreview from "../../assets/img/previews/video-preview.png";
106
+ import imagePreview from '../../assets/img/previews/image-preview.png';
107
+ import documentPreview from '../../assets/img/previews/doc-preview.png';
108
+ import videoPreview from '../../assets/img/previews/video-preview.png';
106
109
 
107
- import UnnnicIcon from "../Icon.vue";
110
+ import UnnnicIcon from '../Icon.vue';
108
111
 
109
112
  interface Props {
110
113
  template?: Template | null;
@@ -115,30 +118,30 @@ const props = withDefaults(defineProps<Props>(), {
115
118
  });
116
119
 
117
120
  const hasHeader = computed(
118
- () => props.template?.header && props.template.header.type
121
+ () => props.template?.header && props.template.header.type,
119
122
  );
120
123
  const hasHeaderMedia = computed(
121
- () => !!props.template?.header && props.template.header.type === "MEDIA"
124
+ () => !!props.template?.header && props.template.header.type === 'MEDIA',
122
125
  );
123
126
  const hasBody = computed(
124
- () => !!props.template?.body && props.template.body.length > 0
127
+ () => !!props.template?.body && props.template.body.length > 0,
125
128
  );
126
129
  const hasFooter = computed(
127
- () => !!props.template?.footer && props.template.footer.length > 0
130
+ () => !!props.template?.footer && props.template.footer.length > 0,
128
131
  );
129
132
  const hasButtons = computed(
130
- () => !!props.template?.buttons && props.template.buttons.length > 0
133
+ () => !!props.template?.buttons && props.template.buttons.length > 0,
131
134
  );
132
135
  const parsedBody = computed(() => {
133
- if (!hasBody.value) return "";
136
+ if (!hasBody.value) return '';
134
137
 
135
138
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
136
139
  const result = props
137
- .template!.body!.replaceAll("\n", "<br/>")
138
- .replaceAll(/(?:\*)([^*<\n]+)(?:\*)/g, "<strong>$1</strong>")
139
- .replaceAll(/(?:_)([^_<\n]+)(?:_)/g, "<i>$1</i>")
140
- .replaceAll(/(?:~)([^~<\n]+)(?:~)/g, "<s>$1</s>")
141
- .replaceAll(/(?:```)([^```<\n]+)(?:```)/g, "<tt>$1</tt>")
140
+ .template!.body!.replaceAll('\n', '<br/>')
141
+ .replaceAll(/(?:\*)([^*<\n]+)(?:\*)/g, '<strong>$1</strong>')
142
+ .replaceAll(/(?:_)([^_<\n]+)(?:_)/g, '<i>$1</i>')
143
+ .replaceAll(/(?:~)([^~<\n]+)(?:~)/g, '<s>$1</s>')
144
+ .replaceAll(/(?:```)([^```<\n]+)(?:```)/g, '<tt>$1</tt>')
142
145
  .replaceAll(/{{.*?}}/g, (match) => `<strong>${match}</strong>`);
143
146
 
144
147
  return result;
@@ -146,19 +149,19 @@ const parsedBody = computed(() => {
146
149
 
147
150
  const getButtonIcon = (buttonType) => {
148
151
  const buttonMapper = {
149
- PHONE_NUMBER: "phone",
150
- URL: "open_in_new",
151
- COPY_CODE: "content_copy",
152
- FLOW: "",
153
- QUICK_REPLY: "reply",
152
+ PHONE_NUMBER: 'phone',
153
+ URL: 'open_in_new',
154
+ COPY_CODE: 'content_copy',
155
+ FLOW: '',
156
+ QUICK_REPLY: 'reply',
154
157
  };
155
158
 
156
- return buttonMapper[buttonType] || "";
159
+ return buttonMapper[buttonType] || '';
157
160
  };
158
161
  </script>
159
162
 
160
163
  <style lang="scss" scoped>
161
- @use "@/assets/scss/unnnic" as *;
164
+ @use '@/assets/scss/unnnic' as *;
162
165
 
163
166
  .unnnic-template-preview {
164
167
  display: flex;
@@ -1,26 +1,26 @@
1
1
  <template>
2
2
  <UnnnicModalDialog
3
- :model-value="modelValue"
4
- @update:modelValue="$event === false && $emit('close')"
3
+ :modelValue="modelValue"
5
4
  :title="defaultTranslations.title[props.locale]"
6
- :show-close-icon="true"
5
+ :showCloseIcon="true"
7
6
  class="unnnic-template-preview-modal"
7
+ @update:model-value="$event === false && $emit('close')"
8
8
  >
9
9
  <UnnnicTemplatePreview :template="template" />
10
10
  </UnnnicModalDialog>
11
11
  </template>
12
12
 
13
13
  <script lang="ts" setup>
14
- import UnnnicTemplatePreview from "./TemplatePreview.vue";
15
- import UnnnicModalDialog from "../ModalDialog/ModalDialog.vue";
14
+ import UnnnicTemplatePreview from './TemplatePreview.vue';
15
+ import UnnnicModalDialog from '../ModalDialog/ModalDialog.vue';
16
16
 
17
- import type { Template } from "./types";
17
+ import type { Template } from './types';
18
18
 
19
19
  const defaultTranslations = {
20
20
  title: {
21
- "pt-br": "Visualizar modelo",
22
- en: "Template preview",
23
- es: "Vista previa de plantilla",
21
+ 'pt-br': 'Visualizar modelo',
22
+ en: 'Template preview',
23
+ es: 'Vista previa de plantilla',
24
24
  },
25
25
  };
26
26
 
@@ -31,7 +31,7 @@ interface Props {
31
31
  }
32
32
 
33
33
  const props = withDefaults(defineProps<Props>(), {
34
- locale: "en",
34
+ locale: 'en',
35
35
  });
36
36
 
37
37
  defineEmits<{
@@ -40,7 +40,7 @@ defineEmits<{
40
40
  </script>
41
41
 
42
42
  <style lang="scss" scoped>
43
- @use "@/assets/scss/unnnic" as *;
43
+ @use '@/assets/scss/unnnic' as *;
44
44
 
45
45
  :deep(.unnnic-modal-dialog__container__content) {
46
46
  display: flex;
@@ -1,14 +1,14 @@
1
1
  export interface Template {
2
2
  header: {
3
- type: "TEXT" | "MEDIA";
4
- mediaType?: "IMAGE" | "VIDEO" | "DOCUMENT";
3
+ type: 'TEXT' | 'MEDIA';
4
+ mediaType?: 'IMAGE' | 'VIDEO' | 'DOCUMENT';
5
5
  text?: string | null;
6
6
  src?: string | null;
7
7
  };
8
8
  body?: string;
9
9
  footer?: string;
10
10
  buttons?: Array<{
11
- type: "QUICK_REPLY" | "PHONE_NUMBER";
11
+ type: 'QUICK_REPLY' | 'PHONE_NUMBER';
12
12
  text: string;
13
13
  countryCode?: string;
14
14
  phoneNumber?: string;
@@ -7,9 +7,9 @@
7
7
  >
8
8
  <aside
9
9
  v-if="isVisible"
10
- :class="['unnnic-toast', `unnnic-toast--${type}`]"
11
- :role="type === 'error' ? 'alert' : 'status'"
12
- :aria-live="type === 'error' ? 'assertive' : 'polite'"
10
+ :class="['unnnic-toast', `unnnic-toast--${validType}`]"
11
+ :role="validType === 'error' ? 'alert' : 'status'"
12
+ :aria-live="validType === 'error' ? 'assertive' : 'polite'"
13
13
  data-testid="toast"
14
14
  :style="{ zIndex: toastZIndex }"
15
15
  >
@@ -23,7 +23,7 @@
23
23
  >
24
24
  <UnnnicIcon
25
25
  :icon="typeConfig.icon"
26
- :scheme="typeConfig.scheme"
26
+ :scheme="typeConfig.scheme as SchemeColor"
27
27
  size="ant"
28
28
  data-testid="toast-type-icon"
29
29
  />
@@ -97,6 +97,13 @@ const emit = defineEmits<ToastEmits>();
97
97
  const isVisible = ref(false);
98
98
  let timeoutId: number | null = null;
99
99
 
100
+ const validType = computed(() => {
101
+ if (['informational', 'attention', 'success', 'error'].includes(props.type)) {
102
+ return props.type;
103
+ }
104
+ return 'informational';
105
+ });
106
+
100
107
  const typeConfig = computed(() => {
101
108
  const configMap = {
102
109
  informational: { icon: 'info', scheme: 'blue-500' },
@@ -105,13 +112,10 @@ const typeConfig = computed(() => {
105
112
  error: { icon: 'cancel', scheme: 'red-500' },
106
113
  };
107
114
 
108
- return configMap[props.type || 'informational'] as {
109
- icon: string;
110
- scheme: SchemeColor;
111
- };
115
+ return configMap[validType.value];
112
116
  });
113
117
 
114
- const toastZIndex = useLayerZIndex();
118
+ const toastZIndex = useLayerZIndex('toast');
115
119
 
116
120
  const handleClose = () => {
117
121
  isVisible.value = false;
@@ -1,5 +1,4 @@
1
1
  import { createApp } from 'vue';
2
- import { getTeleportContainer } from '@/lib/teleport-target';
3
2
  import Toast from './Toast.vue';
4
3
  import type {
5
4
  ToastProps,
@@ -21,9 +20,7 @@ class ToastManager implements ToastManager {
21
20
  this.container = document.createElement('div');
22
21
  this.container.setAttribute('unnnic-toast-container', 'true');
23
22
 
24
- const host = getTeleportContainer() ?? document.body;
25
- host?.appendChild(this.container);
26
-
23
+ document.body.appendChild(this.container);
27
24
  return this.container;
28
25
  }
29
26
 
@@ -1,15 +1,11 @@
1
1
  import { beforeEach, describe, expect, afterEach, test, vi } from 'vitest';
2
2
 
3
- vi.mock('vue', async () => {
4
- const actual = await vi.importActual('vue');
5
- return {
6
- ...actual,
7
- createApp: vi.fn(() => ({
8
- mount: vi.fn(),
9
- unmount: vi.fn(),
10
- })),
11
- };
12
- });
3
+ vi.mock('vue', () => ({
4
+ createApp: vi.fn(() => ({
5
+ mount: vi.fn(),
6
+ unmount: vi.fn(),
7
+ })),
8
+ }));
13
9
 
14
10
  vi.mock('../Toast.vue', () => ({
15
11
  default: {
@@ -17,8 +17,8 @@
17
17
  <template v-if="enableHtml">
18
18
  <!-- eslint-disable-next-line vue/no-v-html -->
19
19
  <section
20
- v-html="text"
21
20
  data-testid="tooltip-html-content"
21
+ v-html="text"
22
22
  ></section>
23
23
  </template>
24
24
  <template