@weni/unnnic-system 3.12.3-alpha.1 → 3.12.3-alpha.3

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@weni/unnnic-system",
3
- "version": "3.12.3-alpha.1",
3
+ "version": "3.12.3-alpha.3",
4
4
  "type": "commonjs",
5
5
  "files": [
6
6
  "dist",
@@ -1,71 +1,165 @@
1
1
  <template>
2
- <section class="unnnic-disclaimer">
2
+ <section
3
+ :class="['unnnic-disclaimer', `unnnic-disclaimer--${type}`]"
4
+ data-testid="disclaimer"
5
+ >
3
6
  <UnnnicIcon
4
7
  class="unnnic-disclaimer__icon"
5
- size="avatar-nano"
6
- :icon="icon"
7
- :scheme="iconColor"
8
+ size="ant"
9
+ :icon="variant.icon"
10
+ :scheme="variant.scheme"
8
11
  data-testid="disclaimer-icon"
9
12
  />
10
- <p
11
- class="unnnic-disclaimer__text"
12
- data-testid="disclaimer-text"
13
- v-html="text"
14
- />
13
+
14
+ <section class="unnnic-disclaimer__content">
15
+ <p
16
+ v-if="title"
17
+ class="unnnic-disclaimer__title"
18
+ data-testid="disclaimer-title"
19
+ >
20
+ {{ title }}
21
+ </p>
22
+ <p
23
+ v-if="description"
24
+ class="unnnic-disclaimer__description"
25
+ data-testid="disclaimer-description"
26
+ >
27
+ {{ description }}
28
+ </p>
29
+ </section>
15
30
  </section>
16
31
  </template>
17
32
 
18
33
  <script setup lang="ts">
19
- import icons from '../../utils/iconList';
20
- import colors from '../../utils/colorsList';
34
+ import { computed } from 'vue';
35
+
21
36
  import UnnnicIcon from '../Icon.vue';
22
- import type { DisclaimerProps } from './types';
37
+ import type { SchemeColor } from '@/types/scheme-colors';
38
+ import type { DisclaimerType, DisclaimerProps } from './types';
23
39
 
24
40
  defineOptions({
25
41
  name: 'UnnnicDisclaimer',
26
42
  });
27
43
 
28
- export type { DisclaimerProps };
29
-
30
44
  const props = withDefaults(defineProps<DisclaimerProps>(), {
31
- icon: 'alert-circle-1-1',
32
- iconColor: 'neutral-darkest',
45
+ title: 'Disclaimer',
46
+ description: 'The quick brown fox jumps over the lazy dog',
47
+ type: 'informational',
48
+ icon: undefined,
49
+ iconColor: undefined,
33
50
  });
34
51
 
35
- const validateIcon = (value: string): boolean => {
36
- return icons.includes(value);
37
- };
52
+ // This is a temporary solution to ensure backwards compatibility with the older version of the component.
53
+ // TODO: It should be removed once the older version is discontinued.
54
+ const variantCompatibleWithOlderVersion = computed((): DisclaimerType => {
55
+ if (props.icon) {
56
+ if (props.icon.includes('alert-circle')) return 'attention';
57
+ if (props.icon === 'info') return 'informational';
58
+ if (props.icon === 'error') return 'error';
59
+ return 'neutral';
60
+ }
38
61
 
39
- const validateIconColor = (value: string): boolean => {
40
- return colors.includes(value);
41
- };
62
+ if (props.iconColor) {
63
+ if (props.iconColor.includes('yellow')) return 'attention';
64
+ if (props.iconColor.includes('red')) return 'error';
65
+ return 'neutral';
66
+ }
42
67
 
43
- if (!validateIcon(props.icon as string)) {
44
- console.warn(`Invalid icon prop: ${props.icon}`);
45
- }
68
+ return props.type;
69
+ });
46
70
 
47
- if (!validateIconColor(props.iconColor as string)) {
48
- console.warn(`Invalid iconColor prop: ${props.iconColor}`);
49
- }
71
+ const variant = computed(() => {
72
+ const variants: Record<
73
+ DisclaimerType,
74
+ { icon: string; scheme: SchemeColor }
75
+ > = {
76
+ informational: {
77
+ icon: 'info',
78
+ scheme: 'blue-500',
79
+ },
80
+ success: {
81
+ icon: 'check_circle',
82
+ scheme: 'green-500',
83
+ },
84
+ attention: {
85
+ icon: 'error',
86
+ scheme: 'yellow-500',
87
+ },
88
+ error: {
89
+ icon: 'cancel',
90
+ scheme: 'red-500',
91
+ },
92
+ neutral: {
93
+ icon: 'info',
94
+ scheme: 'gray-400',
95
+ },
96
+ };
97
+
98
+ return variants[variantCompatibleWithOlderVersion.value || props.type];
99
+ });
50
100
  </script>
51
101
 
52
- <style lang="scss" scoped>
102
+ <style scoped lang="scss">
53
103
  @use '@/assets/scss/unnnic' as *;
104
+
54
105
  .unnnic-disclaimer {
55
- display: inline-flex;
56
- align-items: center;
57
- gap: $unnnic-spacing-xs;
58
- padding: $unnnic-spacing-sm;
59
- border-radius: $unnnic-border-radius-sm;
60
- border: 1px solid $unnnic-color-neutral-soft;
61
- background: $unnnic-color-background-lightest;
62
- .unnnic-disclaimer__text {
106
+ $border-width: 1px;
107
+
108
+ display: flex;
109
+ gap: $unnnic-space-2;
110
+
111
+ width: 100%;
112
+ min-height: 53px;
113
+ padding: calc($unnnic-space-4 - $border-width) $unnnic-space-4;
114
+ box-sizing: border-box;
115
+
116
+ border: $border-width solid $unnnic-color-border-base;
117
+ border-radius: $unnnic-radius-2;
118
+
119
+ background-color: $unnnic-color-bg-soft;
120
+
121
+ color: $unnnic-color-fg-emphasized;
122
+
123
+ &__content {
124
+ display: flex;
125
+ flex-direction: column;
126
+ justify-content: center;
127
+ gap: $unnnic-space-1;
128
+ }
129
+
130
+ &__title {
131
+ margin: 0;
132
+ font: $unnnic-font-action;
133
+ }
134
+
135
+ &__description {
63
136
  margin: 0;
64
- font-family: $unnnic-font-family-secondary;
65
- font-size: $unnnic-font-size-body-gt;
66
- line-height: $unnnic-line-height-large * 1.375;
67
- font-weight: $unnnic-font-weight-regular;
68
- color: $unnnic-color-neutral-dark;
137
+ font: $unnnic-font-caption-2;
138
+ }
139
+
140
+ &--informational {
141
+ background-color: $unnnic-color-bg-info;
142
+ border-color: $unnnic-color-border-info;
143
+ }
144
+
145
+ &--success {
146
+ background-color: $unnnic-color-bg-success;
147
+ border-color: $unnnic-color-border-success;
148
+ }
149
+
150
+ &--attention {
151
+ background-color: $unnnic-color-bg-warning;
152
+ border-color: $unnnic-color-border-warning;
153
+ }
154
+
155
+ &--error {
156
+ background-color: $unnnic-color-bg-critical;
157
+ border-color: $unnnic-color-border-critical;
158
+ }
159
+
160
+ &--neutral {
161
+ background-color: $unnnic-color-bg-soft;
162
+ border-color: $unnnic-color-border-base;
69
163
  }
70
164
  }
71
165
  </style>
@@ -1,72 +1,97 @@
1
1
  import { mount } from '@vue/test-utils';
2
- import { describe, it, expect, beforeEach } from 'vitest';
2
+ import { describe, it, expect } from 'vitest';
3
3
 
4
4
  import Disclaimer from '../Disclaimer.vue';
5
5
  import Icon from '../../Icon.vue';
6
6
 
7
+ const mountComponent = (props = {}) =>
8
+ mount(Disclaimer, {
9
+ props,
10
+ global: {
11
+ components: { UnnnicIcon: Icon },
12
+ },
13
+ });
14
+
7
15
  describe('Disclaimer', () => {
8
- let wrapper;
16
+ it('renders default title and description', () => {
17
+ const wrapper = mountComponent();
9
18
 
10
- beforeEach(() => {
11
- wrapper = mount(Disclaimer, {
12
- props: { text: 'Test Disclaimer Text' },
13
- global: { components: { UnnnicIcon: Icon } },
14
- });
19
+ expect(wrapper.find('[data-testid="disclaimer-title"]').text()).toBe(
20
+ 'Disclaimer',
21
+ );
22
+ expect(wrapper.find('[data-testid="disclaimer-description"]').text()).toBe(
23
+ 'The quick brown fox jumps over the lazy dog',
24
+ );
15
25
  });
16
26
 
17
- it('renders the text prop correctly', () => {
18
- expect(wrapper.find('[data-testid="disclaimer-text"]').text()).toBe(
19
- 'Test Disclaimer Text',
27
+ it('hides title when no title is provided', () => {
28
+ const wrapper = mountComponent({ title: '' });
29
+
30
+ expect(wrapper.find('[data-testid="disclaimer-title"]').exists()).toBe(
31
+ false,
20
32
  );
21
33
  });
22
34
 
23
- it('renders the UnnnicIcon component with correct icon and color', async () => {
24
- const icon = 'alert-circle-1-1';
25
- const iconColor = 'neutral-darkest';
35
+ it('hides description when no description is provided', () => {
36
+ const wrapper = mountComponent({ description: '' });
37
+
38
+ expect(
39
+ wrapper.find('[data-testid="disclaimer-description"]').exists(),
40
+ ).toBe(false);
41
+ });
26
42
 
27
- await wrapper.setProps({ icon, iconColor });
43
+ it.each([
44
+ ['informational', 'info', 'blue-500'],
45
+ ['success', 'check_circle', 'green-500'],
46
+ ['attention', 'error', 'yellow-500'],
47
+ ['error', 'cancel', 'red-500'],
48
+ ['neutral', 'info', 'gray-400'],
49
+ ])('applies variant %s styles', (type, icon, scheme) => {
50
+ const wrapper = mountComponent({ type });
51
+
52
+ expect(wrapper.classes()).toContain(`unnnic-disclaimer--${type}`);
28
53
 
29
54
  const iconComponent = wrapper.findComponent(
30
55
  '[data-testid="disclaimer-icon"]',
31
56
  );
32
- expect(iconComponent.exists()).toBe(true);
57
+
33
58
  expect(iconComponent.props('icon')).toBe(icon);
34
- expect(iconComponent.props('scheme')).toBe(iconColor);
59
+ expect(iconComponent.props('scheme')).toBe(scheme);
35
60
  });
36
61
 
37
- it('renders with default icon and color if not provided', () => {
38
- const iconComponent = wrapper.findComponent(
39
- '[data-testid="disclaimer-icon"]',
40
- );
41
- expect(iconComponent.props('icon')).toBe('alert-circle-1-1');
42
- expect(iconComponent.props('scheme')).toBe('neutral-darkest');
43
- });
62
+ describe('legacy compatibility', () => {
63
+ it.each([
64
+ ['alert-circle-1-1', 'error', 'yellow-500'],
65
+ ['info', 'info', 'blue-500'],
66
+ ['error', 'cancel', 'red-500'],
67
+ ['custom-icon', 'info', 'gray-400'],
68
+ ])('maps icon prop "%s" to variant', (icon, expectedIcon, scheme) => {
69
+ const wrapper = mountComponent({ icon });
44
70
 
45
- it('validates the icon prop correctly', () => {
46
- const wrapperValid = mount(Disclaimer, {
47
- props: { text: 'Test', icon: 'alert-circle-1-1' },
48
- global: { components: { UnnnicIcon: Icon } },
49
- });
50
- expect(wrapperValid.exists()).toBe(true);
71
+ const iconComponent = wrapper.findComponent(
72
+ '[data-testid="disclaimer-icon"]',
73
+ );
51
74
 
52
- const wrapperInvalid = mount(Disclaimer, {
53
- props: { text: 'Test', icon: 'invalid-icon' },
54
- global: { components: { UnnnicIcon: Icon } },
75
+ expect(iconComponent.props('icon')).toBe(expectedIcon);
76
+ expect(iconComponent.props('scheme')).toBe(scheme);
55
77
  });
56
- expect(wrapperInvalid.exists()).toBe(true);
57
- });
58
78
 
59
- it('validates the iconColor prop correctly', () => {
60
- const wrapperValid = mount(Disclaimer, {
61
- props: { text: 'Test', iconColor: 'neutral-darkest' },
62
- global: { components: { UnnnicIcon: Icon } },
63
- });
64
- expect(wrapperValid.exists()).toBe(true);
79
+ it.each([
80
+ ['feedback-yellow', 'error', 'yellow-500'],
81
+ ['feedback-red', 'cancel', 'red-500'],
82
+ ['feedback-blue', 'info', 'gray-400'],
83
+ ])(
84
+ 'maps iconColor prop "%s" to variant',
85
+ (iconColor, expectedIcon, scheme) => {
86
+ const wrapper = mountComponent({ iconColor });
65
87
 
66
- const wrapperInvalid = mount(Disclaimer, {
67
- props: { text: 'Test', iconColor: 'invalid-color' },
68
- global: { components: { UnnnicIcon: Icon } },
69
- });
70
- expect(wrapperInvalid.exists()).toBe(true);
88
+ const iconComponent = wrapper.findComponent(
89
+ '[data-testid="disclaimer-icon"]',
90
+ );
91
+
92
+ expect(iconComponent.props('icon')).toBe(expectedIcon);
93
+ expect(iconComponent.props('scheme')).toBe(scheme);
94
+ },
95
+ );
71
96
  });
72
97
  });
@@ -1,7 +1,16 @@
1
1
  import type { SchemeColor } from '@/types/scheme-colors';
2
2
 
3
+ export type DisclaimerType =
4
+ | 'informational'
5
+ | 'success'
6
+ | 'attention'
7
+ | 'error'
8
+ | 'neutral';
9
+
3
10
  export interface DisclaimerProps {
4
- text: string;
5
- icon?: string;
6
- iconColor?: SchemeColor;
11
+ title?: string;
12
+ description?: string;
13
+ type?: DisclaimerType;
14
+ icon?: string | undefined;
15
+ iconColor?: SchemeColor | undefined;
7
16
  }
@@ -6,7 +6,9 @@ defineOptions({
6
6
  name: 'UnnnicDialogClose',
7
7
  });
8
8
 
9
- const props = defineProps<DialogCloseProps>();
9
+ const props = withDefaults(defineProps<DialogCloseProps>(), {
10
+ asChild: true,
11
+ });
10
12
  </script>
11
13
 
12
14
  <template>
@@ -19,8 +21,6 @@ const props = defineProps<DialogCloseProps>();
19
21
  </template>
20
22
 
21
23
  <style lang="scss" scoped>
22
- @use '@/assets/scss/unnnic' as *;
23
-
24
24
  .unnnic-dialog-close {
25
25
  > * {
26
26
  width: 100%;
@@ -6,7 +6,9 @@ defineOptions({
6
6
  name: 'UnnnicDrawerClose',
7
7
  });
8
8
 
9
- const props = defineProps<DrawerCloseProps>();
9
+ const props = withDefaults(defineProps<DrawerCloseProps>(), {
10
+ asChild: true,
11
+ });
10
12
  </script>
11
13
 
12
14
  <template>
@@ -22,14 +24,6 @@ const props = defineProps<DrawerCloseProps>();
22
24
  @use '@/assets/scss/unnnic' as *;
23
25
 
24
26
  .unnnic-drawer__close {
25
- display: flex;
26
-
27
- border: none;
28
- background: none;
29
- padding: 0;
30
- margin: 0;
31
- cursor: pointer;
32
-
33
27
  > * {
34
28
  width: 100%;
35
29
  }
@@ -14,14 +14,14 @@ export type LayerToken = keyof typeof layerScale;
14
14
 
15
15
  const DEFAULT_STEP = 5;
16
16
 
17
- const counters: Record<LayerToken, number> = {
18
- hide: 0,
19
- base: 0,
20
- drawer: 0,
21
- modal: 0,
22
- popover: 0,
23
- tooltip: 0,
24
- toast: 0,
17
+ const activeValues: Record<LayerToken, number[]> = {
18
+ hide: [],
19
+ base: [],
20
+ drawer: [],
21
+ modal: [],
22
+ popover: [],
23
+ tooltip: [],
24
+ toast: [],
25
25
  };
26
26
 
27
27
  interface Allocation {
@@ -34,10 +34,14 @@ const allocations = new Map<symbol, Allocation>();
34
34
 
35
35
  function acquire(type: LayerToken): Allocation {
36
36
  const id = Symbol('layer');
37
- counters[type] = (counters[type] ?? 0) + 1;
38
- const value = layerScale[type] + counters[type] * DEFAULT_STEP;
37
+ const list = activeValues[type];
38
+ const lastValue = list.length ? list[list.length - 1] : layerScale[type];
39
+ const value = lastValue + DEFAULT_STEP;
39
40
  const allocation: Allocation = { id, type, value };
41
+
42
+ list.push(value);
40
43
  allocations.set(id, allocation);
44
+
41
45
  return allocation;
42
46
  }
43
47
 
@@ -48,8 +52,12 @@ function release(id: symbol) {
48
52
  }
49
53
 
50
54
  allocations.delete(id);
51
- const current = Math.max((counters[allocation.type] ?? 1) - 1, 0);
52
- counters[allocation.type] = current;
55
+ const list = activeValues[allocation.type];
56
+ const index = list.indexOf(allocation.value);
57
+
58
+ if (index !== -1) {
59
+ list.splice(index, 1);
60
+ }
53
61
  }
54
62
 
55
63
  export interface LayerZIndexOptions {
@@ -1,22 +1,63 @@
1
1
  import UnnnicDisclaimer from '../components/Disclaimer/Disclaimer.vue';
2
- import icons from '../utils/iconList';
3
- import colors from '../utils/colorsList';
4
2
 
5
3
  export default {
6
- title: 'Example/Disclaimer',
4
+ title: 'Feedback/Disclaimer',
7
5
  component: UnnnicDisclaimer,
8
- args: {},
9
6
  argTypes: {
10
- text: { control: { type: 'text' } },
11
- icon: { options: icons, control: { type: 'select' } },
12
- iconColor: { options: colors, control: { type: 'select' } },
7
+ type: {
8
+ control: { type: 'select' },
9
+ options: ['informational', 'success', 'attention', 'error', 'neutral'],
10
+ },
13
11
  },
14
12
  };
15
13
 
16
- export const Default = {
17
- args: {
18
- text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
19
- icon: 'alert-circle-1-1',
20
- iconColor: 'feedback-yellow',
14
+ const Template = (args) => ({
15
+ components: { UnnnicDisclaimer },
16
+ setup() {
17
+ return { args };
21
18
  },
19
+ template: '<UnnnicDisclaimer v-bind="args" />',
20
+ });
21
+
22
+ export const Default = Template.bind({});
23
+ Default.args = {
24
+ title: 'Disclaimer',
25
+ description: 'The quick brown fox jumps over the lazy dog',
26
+ type: 'informational',
27
+ };
28
+
29
+ export const Success = Template.bind({});
30
+ Success.args = {
31
+ ...Default.args,
32
+ type: 'success',
33
+ };
34
+
35
+ export const Attention = Template.bind({});
36
+ Attention.args = {
37
+ ...Default.args,
38
+ type: 'attention',
39
+ };
40
+
41
+ export const ErrorState = Template.bind({});
42
+ ErrorState.args = {
43
+ ...Default.args,
44
+ type: 'error',
45
+ };
46
+
47
+ export const Neutral = Template.bind({});
48
+ Neutral.args = {
49
+ ...Default.args,
50
+ type: 'neutral',
51
+ };
52
+
53
+ export const OnlyTitle = Template.bind({});
54
+ OnlyTitle.args = {
55
+ ...Default.args,
56
+ description: '',
57
+ };
58
+
59
+ export const OnlyDescription = Template.bind({});
60
+ OnlyDescription.args = {
61
+ ...Default.args,
62
+ title: '',
22
63
  };