@weni/unnnic-system 3.13.0 → 3.14.0-alpha-teleports.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 (88) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/README.md +9 -1
  3. package/dist/{es-41fceca9.mjs → es-0d53b5b2.mjs} +1 -1
  4. package/dist/{index-cca96b43.mjs → index-d7070de8.mjs} +29952 -27094
  5. package/dist/index.d.ts +5111 -1770
  6. package/dist/{pt-br-a3088529.mjs → pt-br-bf4e1f97.mjs} +1 -1
  7. package/dist/style.css +1 -1
  8. package/dist/unnnic.mjs +232 -204
  9. package/dist/unnnic.umd.js +48 -44
  10. package/package.json +3 -2
  11. package/src/assets/scss/tailwind.scss +8 -0
  12. package/src/components/Alert/__tests__/__snapshots__/Alert.spec.js.snap +1 -1
  13. package/src/components/ChartFunnel/DefaultFunnel/ChartDefaultFunnelBase.vue +2 -1
  14. package/src/components/ChartFunnel/SvgFunnel/ChartFunnelTwoRows.vue +61 -60
  15. package/src/components/Checkbox/Checkbox.vue +1 -1
  16. package/src/components/CheckboxGroup/CheckboxGroup.vue +5 -7
  17. package/src/components/Chip/Chip.vue +1 -1
  18. package/src/components/DatePicker/DatePicker.vue +10 -1
  19. package/src/components/Drawer/Drawer.vue +180 -270
  20. package/src/components/Drawer/__tests__/Drawer.spec.js +32 -43
  21. package/src/components/Drawer/__tests__/__snapshots__/Drawer.spec.js.snap +18 -19
  22. package/src/components/FormElement/FormElement.vue +87 -96
  23. package/src/components/InputDatePicker/InputDatePicker.vue +68 -73
  24. package/src/components/InputDatePicker/__test__/InputDatePicker.spec.js +31 -24
  25. package/src/components/ModalDialog/ModalDialog.vue +63 -154
  26. package/src/components/ModalDialog/__tests__/ModalDialog.spec.js +30 -210
  27. package/src/components/ModalDialog/__tests__/__snapshots__/ModalDialog.spec.js.snap +1 -22
  28. package/src/components/MultiSelect/MultSelectOption.vue +49 -0
  29. package/src/components/MultiSelect/__tests__/MultiSelect.spec.js +557 -0
  30. package/src/components/MultiSelect/__tests__/MultiSelectOption.spec.js +229 -0
  31. package/src/components/MultiSelect/__tests__/__snapshots__/MultiSelect.spec.js.snap +87 -0
  32. package/src/components/MultiSelect/__tests__/__snapshots__/MultiSelectOption.spec.js.snap +51 -0
  33. package/src/components/MultiSelect/index.vue +265 -0
  34. package/src/components/Radio/Radio.vue +6 -12
  35. package/src/components/Radio/__test__/Radio.spec.js +1 -3
  36. package/src/components/RadioGroup/RadioGroup.vue +10 -18
  37. package/src/components/Select/__tests__/SelectItem.spec.js +15 -35
  38. package/src/components/Select/index.vue +3 -3
  39. package/src/components/Switch/Switch.vue +3 -10
  40. package/src/components/Tab/__test__/__snapshots__/Tab.spec.js.snap +3 -1
  41. package/src/components/TemplatePreview/TemplatePreview.vue +25 -28
  42. package/src/components/TemplatePreview/TemplatePreviewModal.vue +10 -10
  43. package/src/components/TemplatePreview/types.d.ts +3 -3
  44. package/src/components/Toast/Toast.vue +4 -1
  45. package/src/components/Toast/ToastManager.ts +4 -1
  46. package/src/components/Toast/__tests__/ToastManager.spec.js +10 -6
  47. package/src/components/ToolTip/ToolTip.vue +25 -177
  48. package/src/components/ToolTip/__tests__/ToolTip.spec.js +339 -61
  49. package/src/components/index.ts +58 -2
  50. package/src/components/ui/dialog/Dialog.vue +19 -0
  51. package/src/components/ui/dialog/DialogClose.vue +29 -0
  52. package/src/components/ui/dialog/DialogContent.vue +140 -0
  53. package/src/components/ui/dialog/DialogFooter.vue +50 -0
  54. package/src/components/ui/dialog/DialogHeader.vue +83 -0
  55. package/src/components/ui/dialog/DialogTitle.vue +38 -0
  56. package/src/components/ui/dialog/DialogTrigger.vue +16 -0
  57. package/src/components/ui/dialog/index.ts +7 -0
  58. package/src/components/ui/drawer/Drawer.vue +27 -0
  59. package/src/components/ui/drawer/DrawerClose.vue +31 -0
  60. package/src/components/ui/drawer/DrawerContent.vue +113 -0
  61. package/src/components/ui/drawer/DrawerDescription.vue +40 -0
  62. package/src/components/ui/drawer/DrawerFooter.vue +38 -0
  63. package/src/components/ui/drawer/DrawerHeader.vue +57 -0
  64. package/src/components/ui/drawer/DrawerOverlay.vue +33 -0
  65. package/src/components/ui/drawer/DrawerTitle.vue +37 -0
  66. package/src/components/ui/drawer/DrawerTrigger.vue +31 -0
  67. package/src/components/ui/drawer/index.ts +10 -0
  68. package/src/components/ui/popover/PopoverContent.vue +7 -4
  69. package/src/components/ui/popover/PopoverTrigger.vue +5 -1
  70. package/src/components/ui/tooltip/Tooltip.vue +21 -0
  71. package/src/components/ui/tooltip/TooltipContent.vue +77 -0
  72. package/src/components/ui/tooltip/TooltipTrigger.vue +24 -0
  73. package/src/components/ui/tooltip/index.ts +3 -0
  74. package/src/index.ts +9 -2
  75. package/src/lib/__tests__/teleport-target.spec.ts +73 -0
  76. package/src/lib/layer-manager.ts +64 -0
  77. package/src/lib/teleport-target.ts +46 -0
  78. package/src/stories/Dialog.stories.js +832 -0
  79. package/src/stories/Drawer.stories.js +1 -1
  80. package/src/stories/DrawerNext.stories.js +611 -0
  81. package/src/stories/LayerManager.docs.mdx +40 -0
  82. package/src/stories/LayerManager.stories.js +407 -0
  83. package/src/stories/ModalDialog.mdx +3 -0
  84. package/src/stories/ModalDialog.stories.js +96 -1
  85. package/src/stories/MultiSelect.stories.js +143 -45
  86. package/src/stories/TemplatePreview.stories.js +27 -27
  87. package/src/stories/TemplatePreviewModal.stories.js +31 -31
  88. package/src/components/MultiSelect/MultiSelect.vue +0 -297
@@ -43,9 +43,7 @@ describe('Radio.vue', () => {
43
43
 
44
44
  test('applies disabled class when disabled prop is true', async () => {
45
45
  await wrapper.setProps({ disabled: true });
46
- expect(wrapper.find('.unnnic-radio__label').classes()).toContain(
47
- 'unnnic-radio__label--disabled',
48
- );
46
+ expect(wrapper.find('.unnnic-radio__label').classes()).toContain('unnnic-radio__label--disabled');
49
47
  });
50
48
 
51
49
  test('icon changes based on valueName', async () => {
@@ -1,10 +1,8 @@
1
1
  <template>
2
- <section
3
- :class="[
4
- 'unnnic-radio-group__container',
5
- `unnnic-radio-group--state-${state}`,
6
- ]"
7
- >
2
+ <section :class="[
3
+ 'unnnic-radio-group__container',
4
+ `unnnic-radio-group--state-${state}`
5
+ ]">
8
6
  <UnnnicLabel
9
7
  v-if="label"
10
8
  :label="label"
@@ -70,14 +68,11 @@ const emit = defineEmits(['update:modelValue']);
70
68
 
71
69
  const contextModelValue = ref(props.modelValue);
72
70
 
73
- watch(
74
- () => props.modelValue,
75
- (newVal) => {
76
- if (newVal !== contextModelValue.value) {
77
- contextModelValue.value = newVal;
78
- }
79
- },
80
- );
71
+ watch(() => props.modelValue, (newVal) => {
72
+ if (newVal !== contextModelValue.value) {
73
+ contextModelValue.value = newVal;
74
+ }
75
+ });
81
76
 
82
77
  watch(contextModelValue, (newVal) => {
83
78
  if (newVal !== props.modelValue) {
@@ -86,10 +81,7 @@ watch(contextModelValue, (newVal) => {
86
81
  });
87
82
 
88
83
  const computedName = computed(() => {
89
- return (
90
- props.name ||
91
- `unnnic-radio-group-${Math.random().toString(36).substring(2, 15)}`
92
- );
84
+ return props.name || `unnnic-radio-group-${Math.random().toString(36).substring(2, 15)}`;
93
85
  });
94
86
 
95
87
  provide('contextModelValue', contextModelValue);
@@ -3,9 +3,9 @@ import { beforeEach, describe, expect, test } from 'vitest';
3
3
  import SelectItem from '../SelectItem.vue';
4
4
 
5
5
  const createWrapper = (props = {}, slots = {}) => {
6
- return mount(SelectItem, {
6
+ return mount(SelectItem, {
7
7
  props,
8
- slots: slots.default ? { default: slots.default } : {},
8
+ slots: slots.default ? { default: slots.default } : {}
9
9
  });
10
10
  };
11
11
 
@@ -76,12 +76,8 @@ describe('SelectItem.vue', () => {
76
76
  test('does not apply size class when size is empty', async () => {
77
77
  await wrapper.setProps({ size: '' });
78
78
  const labelElement = wrapper.find('.unnnic-select-item__label');
79
- expect(labelElement.classes()).not.toContain(
80
- 'unnnic-select-item__label--md',
81
- );
82
- expect(labelElement.classes()).not.toContain(
83
- 'unnnic-select-item__label--sm',
84
- );
79
+ expect(labelElement.classes()).not.toContain('unnnic-select-item__label--md');
80
+ expect(labelElement.classes()).not.toContain('unnnic-select-item__label--sm');
85
81
  });
86
82
  });
87
83
 
@@ -132,10 +128,7 @@ describe('SelectItem.vue', () => {
132
128
  });
133
129
 
134
130
  test('emits click event even when selectable is false', async () => {
135
- const clickWrapper = createWrapper(
136
- { selectable: false },
137
- { default: 'Test Item' },
138
- );
131
+ const clickWrapper = createWrapper({ selectable: false }, { default: 'Test Item' });
139
132
  await clickWrapper.trigger('click');
140
133
  expect(clickWrapper.emitted('click')).toBeTruthy();
141
134
  expect(clickWrapper.emitted('click').length).toBeGreaterThanOrEqual(1);
@@ -201,7 +194,7 @@ describe('SelectItem.vue', () => {
201
194
  test('has proper semantic structure', () => {
202
195
  const div = wrapper.find('.unnnic-select-item');
203
196
  const span = wrapper.find('.unnnic-select-item__label');
204
-
197
+
205
198
  expect(div.exists()).toBe(true);
206
199
  expect(span.exists()).toBe(true);
207
200
  expect(span.element.tagName).toBe('SPAN');
@@ -221,8 +214,7 @@ describe('SelectItem.vue', () => {
221
214
  });
222
215
 
223
216
  test('handles long slot content', async () => {
224
- const longText =
225
- 'This is a very long text content that should be handled properly by the component';
217
+ const longText = 'This is a very long text content that should be handled properly by the component';
226
218
  const longWrapper = createWrapper({}, { default: longText });
227
219
  const labelElement = longWrapper.find('.unnnic-select-item__label');
228
220
  expect(labelElement.text()).toBe(longText);
@@ -245,12 +237,8 @@ describe('SelectItem.vue', () => {
245
237
 
246
238
  describe('CSS class combinations', () => {
247
239
  test('applies correct classes for non-selectable inactive item', async () => {
248
- await wrapper.setProps({
249
- selectable: false,
250
- active: false,
251
- textFocused: false,
252
- });
253
-
240
+ await wrapper.setProps({ selectable: false, active: false, textFocused: false });
241
+
254
242
  expect(wrapper.classes()).toContain('unnnic-select-item');
255
243
  expect(wrapper.classes()).not.toContain('unnnic-select-item--selectable');
256
244
  expect(wrapper.classes()).not.toContain('unnnic--clickable');
@@ -259,12 +247,8 @@ describe('SelectItem.vue', () => {
259
247
  });
260
248
 
261
249
  test('applies correct classes for selectable active item', async () => {
262
- await wrapper.setProps({
263
- selectable: true,
264
- active: true,
265
- textFocused: false,
266
- });
267
-
250
+ await wrapper.setProps({ selectable: true, active: true, textFocused: false });
251
+
268
252
  expect(wrapper.classes()).toContain('unnnic-select-item');
269
253
  expect(wrapper.classes()).toContain('unnnic-select-item--selectable');
270
254
  expect(wrapper.classes()).toContain('unnnic--clickable');
@@ -273,12 +257,8 @@ describe('SelectItem.vue', () => {
273
257
  });
274
258
 
275
259
  test('applies correct classes for text-focused item', async () => {
276
- await wrapper.setProps({
277
- selectable: true,
278
- active: false,
279
- textFocused: true,
280
- });
281
-
260
+ await wrapper.setProps({ selectable: true, active: false, textFocused: true });
261
+
282
262
  expect(wrapper.classes()).toContain('unnnic-select-item');
283
263
  expect(wrapper.classes()).toContain('unnnic-select-item--selectable');
284
264
  expect(wrapper.classes()).toContain('unnnic--clickable');
@@ -318,11 +298,11 @@ describe('SelectItem.vue', () => {
318
298
  });
319
299
 
320
300
  test('matches snapshot with all states combined', async () => {
321
- await wrapper.setProps({
301
+ await wrapper.setProps({
322
302
  size: 'md',
323
303
  selectable: true,
324
304
  active: true,
325
- textFocused: true,
305
+ textFocused: true
326
306
  });
327
307
  expect(wrapper.html()).toMatchSnapshot();
328
308
  });
@@ -9,7 +9,7 @@
9
9
  >
10
10
  <PopoverTrigger class="w-full">
11
11
  <UnnnicInput
12
- ref="inputRef"
12
+ ref="selectInputRef"
13
13
  :modelValue="inputValue"
14
14
  class="unnnic-select__input"
15
15
  readonly
@@ -132,8 +132,8 @@ const emit = defineEmits<{
132
132
  }>();
133
133
 
134
134
  const openPopover = ref(false);
135
- const inputRef = ref<HTMLInputElement | null>(null);
136
- const { width: inputWidth } = useElementSize(inputRef);
135
+ const selectInputRef = ref<HTMLInputElement | null>(null);
136
+ const { width: inputWidth } = useElementSize(selectInputRef);
137
137
 
138
138
  const inputWidthString = computed(() => {
139
139
  return `${inputWidth.value}px`;
@@ -8,12 +8,7 @@
8
8
  class="unnnic-switch__label"
9
9
  />
10
10
 
11
- <label
12
- :class="[
13
- 'unnnic-switch__input-wrapper',
14
- { 'unnnic-switch__input-wrapper--disabled': disabled },
15
- ]"
16
- >
11
+ <label :class="['unnnic-switch__input-wrapper', {'unnnic-switch__input-wrapper--disabled': disabled}]">
17
12
  <input
18
13
  class="unnnic-switch__input"
19
14
  type="checkbox"
@@ -83,7 +78,7 @@ export default {
83
78
  type: String,
84
79
  default: '',
85
80
  },
86
-
81
+
87
82
  helper: {
88
83
  type: String,
89
84
  default: '',
@@ -181,9 +176,7 @@ $switch-height: 20px;
181
176
  background-repeat: no-repeat;
182
177
  background-position: 4px center;
183
178
 
184
- transition:
185
- 120ms linear background-position,
186
- 120ms linear background-color;
179
+ transition: 120ms linear background-position, 120ms linear background-color;
187
180
 
188
181
  cursor: pointer;
189
182
 
@@ -4,7 +4,9 @@ exports[`Tab.vue > matches the snapshot 1`] = `
4
4
  "<div data-v-b4e39fac="" class="tab size-md">
5
5
  <header data-v-b4e39fac="" class="tab-header">
6
6
  <ul data-v-b4e39fac="" class="tab-content">
7
- <li data-v-b4e39fac="" class="tab-head">tab1<div data-v-bf0cf546="" data-v-b4e39fac="" class="unnnic-tooltip"><span data-v-26446d8e="" data-v-b4e39fac="" class="unnnic-icon material-symbols-rounded unnnic-icon-scheme--fg-base unnnic-icon-size--sm unnnic-icon__size--sm" data-testid="material-icon" translate="no">help</span><span data-v-bf0cf546="" class="unnnic-tooltip-label unnnic-tooltip-label-bottom" data-testid="tooltip-label" style="left: 0px; top: 8px;">Tooltip text <br data-v-bf0cf546=""><!--v-if--></span></div>
7
+ <li data-v-b4e39fac="" class="tab-head">tab1<div data-v-b3d24f2b="" class="unnnic-tooltip-trigger" data-testid="tooltip-trigger" data-state="closed" data-grace-area-trigger=""><span data-v-26446d8e="" data-v-b4e39fac="" class="unnnic-icon material-symbols-rounded unnnic-icon-scheme--fg-base unnnic-icon-size--sm unnnic-icon__size--sm" data-testid="material-icon" translate="no">help</span></div>
8
+ <!--teleport start-->
9
+ <!--teleport end-->
8
10
  </li>
9
11
  <li data-v-b4e39fac="" class="tab-head tab-head--active">tab2
10
12
  <!--v-if-->
@@ -75,10 +75,7 @@
75
75
  {{ template?.footer }}
76
76
  </footer>
77
77
  </section>
78
- <footer
79
- v-if="hasButtons"
80
- class="unnnic-template-preview__buttons"
81
- >
78
+ <footer v-if="hasButtons" class="unnnic-template-preview__buttons">
82
79
  <section
83
80
  v-for="(button, index) in template?.buttons"
84
81
  :key="`button-${index}`"
@@ -99,15 +96,15 @@
99
96
  </template>
100
97
 
101
98
  <script lang="ts" setup>
102
- import { computed } from 'vue';
99
+ import { computed } from "vue";
103
100
 
104
- import type { Template } from './types';
101
+ import type { Template } from "./types";
105
102
 
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';
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";
109
106
 
110
- import UnnnicIcon from '../Icon.vue';
107
+ import UnnnicIcon from "../Icon.vue";
111
108
 
112
109
  interface Props {
113
110
  template?: Template | null;
@@ -118,30 +115,30 @@ const props = withDefaults(defineProps<Props>(), {
118
115
  });
119
116
 
120
117
  const hasHeader = computed(
121
- () => props.template?.header && props.template.header.type,
118
+ () => props.template?.header && props.template.header.type
122
119
  );
123
120
  const hasHeaderMedia = computed(
124
- () => !!props.template?.header && props.template.header.type === 'MEDIA',
121
+ () => !!props.template?.header && props.template.header.type === "MEDIA"
125
122
  );
126
123
  const hasBody = computed(
127
- () => !!props.template?.body && props.template.body.length > 0,
124
+ () => !!props.template?.body && props.template.body.length > 0
128
125
  );
129
126
  const hasFooter = computed(
130
- () => !!props.template?.footer && props.template.footer.length > 0,
127
+ () => !!props.template?.footer && props.template.footer.length > 0
131
128
  );
132
129
  const hasButtons = computed(
133
- () => !!props.template?.buttons && props.template.buttons.length > 0,
130
+ () => !!props.template?.buttons && props.template.buttons.length > 0
134
131
  );
135
132
  const parsedBody = computed(() => {
136
- if (!hasBody.value) return '';
133
+ if (!hasBody.value) return "";
137
134
 
138
135
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
139
136
  const result = props
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>')
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>")
145
142
  .replaceAll(/{{.*?}}/g, (match) => `<strong>${match}</strong>`);
146
143
 
147
144
  return result;
@@ -149,19 +146,19 @@ const parsedBody = computed(() => {
149
146
 
150
147
  const getButtonIcon = (buttonType) => {
151
148
  const buttonMapper = {
152
- PHONE_NUMBER: 'phone',
153
- URL: 'open_in_new',
154
- COPY_CODE: 'content_copy',
155
- FLOW: '',
156
- QUICK_REPLY: 'reply',
149
+ PHONE_NUMBER: "phone",
150
+ URL: "open_in_new",
151
+ COPY_CODE: "content_copy",
152
+ FLOW: "",
153
+ QUICK_REPLY: "reply",
157
154
  };
158
155
 
159
- return buttonMapper[buttonType] || '';
156
+ return buttonMapper[buttonType] || "";
160
157
  };
161
158
  </script>
162
159
 
163
160
  <style lang="scss" scoped>
164
- @use '@/assets/scss/unnnic' as *;
161
+ @use "@/assets/scss/unnnic" as *;
165
162
 
166
163
  .unnnic-template-preview {
167
164
  display: flex;
@@ -1,9 +1,9 @@
1
1
  <template>
2
2
  <UnnnicModalDialog
3
- :modelValue="modelValue"
3
+ :model-value="modelValue"
4
4
  @update:modelValue="$event === false && $emit('close')"
5
5
  :title="defaultTranslations.title[props.locale]"
6
- :showCloseIcon="true"
6
+ :show-close-icon="true"
7
7
  class="unnnic-template-preview-modal"
8
8
  >
9
9
  <UnnnicTemplatePreview :template="template" />
@@ -11,16 +11,16 @@
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;
@@ -11,6 +11,7 @@
11
11
  :role="type === 'error' ? 'alert' : 'status'"
12
12
  :aria-live="type === 'error' ? 'assertive' : 'polite'"
13
13
  data-testid="toast"
14
+ :style="{ zIndex: toastZIndex }"
14
15
  >
15
16
  <section
16
17
  class="unnnic-toast__content"
@@ -74,6 +75,7 @@ import { ref, computed, onMounted, onUnmounted } from 'vue';
74
75
 
75
76
  import UnnnicIcon from '@/components/Icon.vue';
76
77
  import UnnnicButton from '@/components/Button/Button.vue';
78
+ import { useLayerZIndex } from '@/lib/layer-manager';
77
79
 
78
80
  import type { ToastProps, ToastEmits } from './types';
79
81
  import type { SchemeColor } from '@/types/scheme-colors';
@@ -109,6 +111,8 @@ const typeConfig = computed(() => {
109
111
  };
110
112
  });
111
113
 
114
+ const toastZIndex = useLayerZIndex();
115
+
112
116
  const handleClose = () => {
113
117
  isVisible.value = false;
114
118
  emit('close');
@@ -152,7 +156,6 @@ onUnmounted(() => {
152
156
  position: fixed;
153
157
  bottom: $unnnic-space-4;
154
158
  right: $unnnic-space-4;
155
- z-index: 9999;
156
159
 
157
160
  display: flex;
158
161
  align-items: flex-end;
@@ -1,4 +1,5 @@
1
1
  import { createApp } from 'vue';
2
+ import { getTeleportContainer } from '@/lib/teleport-target';
2
3
  import Toast from './Toast.vue';
3
4
  import type {
4
5
  ToastProps,
@@ -20,7 +21,9 @@ class ToastManager implements ToastManager {
20
21
  this.container = document.createElement('div');
21
22
  this.container.setAttribute('unnnic-toast-container', 'true');
22
23
 
23
- document.body.appendChild(this.container);
24
+ const host = getTeleportContainer() ?? document.body;
25
+ host?.appendChild(this.container);
26
+
24
27
  return this.container;
25
28
  }
26
29
 
@@ -1,11 +1,15 @@
1
1
  import { beforeEach, describe, expect, afterEach, test, vi } from 'vitest';
2
2
 
3
- vi.mock('vue', () => ({
4
- createApp: vi.fn(() => ({
5
- mount: vi.fn(),
6
- unmount: vi.fn(),
7
- })),
8
- }));
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
+ });
9
13
 
10
14
  vi.mock('../Toast.vue', () => ({
11
15
  default: {