@veritree/ui 0.36.0 → 0.38.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.
@@ -0,0 +1 @@
1
+ {}
@@ -20,7 +20,7 @@ export const floatingUiContentMixin = {
20
20
  },
21
21
 
22
22
  mounted() {
23
- document.addEventListener('click', (e) => this.onDocumentClick(e));
23
+ document.addEventListener('click', (e) => this.onDocumentClick);
24
24
  },
25
25
 
26
26
  destroyed() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@veritree/ui",
3
- "version": "0.36.0",
3
+ "version": "0.38.0",
4
4
  "description": "veritree ui library",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -10,14 +10,25 @@
10
10
  "publishConfig": {
11
11
  "access": "public"
12
12
  },
13
+ "scripts": {
14
+ "test": "vitest",
15
+ "coverage": "vitest run --coverage"
16
+ },
13
17
  "dependencies": {
14
18
  "@floating-ui/dom": "^1.2.0",
15
19
  "@linusborg/vue-simple-portal": "^0.1.5",
16
20
  "@veritree/icons": "^0.43.0"
17
21
  },
18
22
  "devDependencies": {
23
+ "@vitejs/plugin-vue2": "^1.1.2",
24
+ "@vue/test-utils": "^1.3.0",
25
+ "jsdom": "latest",
19
26
  "prettier": "^2.7.1",
20
27
  "prettier-plugin-tailwindcss": "^0.1.13",
21
- "tailwindcss": "^3.2.4"
28
+ "tailwindcss": "^3.2.4",
29
+ "vite": "latest",
30
+ "vitest": "latest",
31
+ "vue": "^2.7.2",
32
+ "vue-template-compiler": "^2.7.2"
22
33
  }
23
34
  }
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <div
3
- v-if="show"
3
+ v-if="visible"
4
4
  :class="[
5
5
  // default styles
6
6
  headless ? 'alert' : 'flex items-start gap-3 rounded border border-solid',
@@ -29,9 +29,15 @@
29
29
  <button
30
30
  v-if="dismissable"
31
31
  :class="[
32
- !headless
33
- ? 'ml-auto mt-1 h-4 w-4 shrink-0 text-current'
34
- : 'alert-close',
32
+ // default styles
33
+ headless ? 'alert-close' : 'ml-auto h-4 w-4 shrink-0 text-current',
34
+ headless
35
+ ? `alert-close--${variant}`
36
+ : isLarge
37
+ ? 'mt-1'
38
+ : isSmall
39
+ ? 'mt-0.5'
40
+ : null,
35
41
  ]"
36
42
  @click="hide"
37
43
  >
@@ -41,9 +47,19 @@
41
47
  </template>
42
48
 
43
49
  <script>
50
+ import IconClose from '@veritree/icons/src/components/IconClose.vue';
51
+
44
52
  export default {
45
53
  name: 'VTAlert',
46
54
 
55
+ components: {
56
+ IconClose,
57
+ },
58
+
59
+ model: {
60
+ prop: 'visible',
61
+ },
62
+
47
63
  props: {
48
64
  variant: {
49
65
  type: String,
@@ -61,12 +77,10 @@ export default {
61
77
  type: Boolean,
62
78
  default: false,
63
79
  },
64
- },
65
-
66
- data() {
67
- return {
68
- show: true,
69
- };
80
+ visible: {
81
+ type: Boolean,
82
+ default: false,
83
+ },
70
84
  },
71
85
 
72
86
  computed: {
@@ -93,8 +107,8 @@ export default {
93
107
 
94
108
  methods: {
95
109
  hide() {
110
+ this.$emit('input', false);
96
111
  this.$emit('dismiss');
97
- this.show = false;
98
112
  },
99
113
  },
100
114
  };
@@ -4,24 +4,14 @@
4
4
  </div>
5
5
  </template>
6
6
 
7
- <style scoped>
8
- .TabGroup.vertical{
9
- display: flex;
10
- }
11
- .TabGroup.vertical .TabList{
12
- display: flex;
13
- flex-direction: column;
14
- }
15
- </style>
16
-
17
7
  <script>
18
8
  export default {
19
9
  name: 'VTTabGroup',
20
10
  props: {
21
- vertical: {
22
- type: Boolean,
23
- default: false
24
- }
11
+ vertical: {
12
+ type: Boolean,
13
+ default: false,
14
+ },
25
15
  },
26
16
  provide() {
27
17
  return {
@@ -113,3 +103,13 @@ export default {
113
103
  },
114
104
  };
115
105
  </script>
106
+
107
+ <style scoped>
108
+ .TabGroup.vertical {
109
+ display: flex;
110
+ }
111
+ .TabGroup.vertical .TabList {
112
+ display: flex;
113
+ flex-direction: column;
114
+ }
115
+ </style>
@@ -2,7 +2,6 @@
2
2
  <div
3
3
  :id="id"
4
4
  :aria-describedby="ariaDescribedBy"
5
- class="flex min-w-min"
6
5
  @mouseenter="onMouseenter"
7
6
  @mouseleave="onmouseout"
8
7
  >
@@ -75,8 +74,7 @@ export default {
75
74
  // delay stop propagation to close other visible
76
75
  // dropdowns and delay click event to control
77
76
  // this dropdown visibility
78
- setTimeout(() => e.stopImmediatePropagation(), 50);
79
- setTimeout(() => this.showComponentContent(), 100);
77
+ this.showComponentContent();
80
78
  },
81
79
 
82
80
  cancel() {
@@ -2,11 +2,11 @@
2
2
  <Portal>
3
3
  <transition
4
4
  enter-active-class="duration-200 ease-out"
5
- enter-class="translate-y-[15px] opacity-0"
6
- enter-to-class="translate-y-0 opacity-100"
5
+ :enter-class="transitionEnterClass"
6
+ :enter-to-class="transitionEnterToClass"
7
7
  leave-active-class="duration-200 ease-in"
8
- leave-class="translate-y-0 opacity-100"
9
- leave-to-class="translate-y-[15px] opacity-0"
8
+ :leave-class="transitionLeaveClass"
9
+ :leave-to-class="transitionLeaveToClass"
10
10
  @after-leave="hidden"
11
11
  @after-enter="shown"
12
12
  >
@@ -62,6 +62,22 @@ export default {
62
62
  ? 'bg-gray-800 text-sm text-white py-1 px-2'
63
63
  : 'bg-white py-2 px-3';
64
64
  },
65
+
66
+ transitionEnterClass() {
67
+ return this.isTooltip ? 'opacity-0' : 'translate-y-[15px] opacity-0';
68
+ },
69
+
70
+ transitionEnterToClass() {
71
+ return this.isTooltip ? 'opacity-100' : 'translate-y-0 opacity-100';
72
+ },
73
+
74
+ transitionLeaveClass() {
75
+ return this.transitionEnterToClass;
76
+ },
77
+
78
+ transitionLeaveToClass() {
79
+ return this.transitionEnterClass;
80
+ },
65
81
  },
66
82
 
67
83
  methods: {
@@ -0,0 +1,158 @@
1
+ import { mount } from '@vue/test-utils';
2
+ import IconClose from '@veritree/icons/src/components/IconClose.vue';
3
+ import VTAlert from '@/components/Alert/VTAlert.vue';
4
+
5
+ describe('VTAlert.vue', () => {
6
+ let wrapper;
7
+
8
+ beforeEach(() => {
9
+ wrapper = mount(VTAlert, {
10
+ slots: {
11
+ default: '<p>Alert message goes here</p>',
12
+ },
13
+ });
14
+ });
15
+
16
+ afterEach(() => {
17
+ wrapper.destroy();
18
+ });
19
+
20
+ it('renders the component', () => {
21
+ expect(VTAlert).toBeTruthy();
22
+ });
23
+
24
+ it('has the correct default props', () => {
25
+ expect(wrapper.props().variant).toBe('success');
26
+ expect(wrapper.props().size).toBe('large');
27
+ expect(wrapper.props().headless).toBe(false);
28
+ expect(wrapper.props().dismissable).toBe(false);
29
+ });
30
+
31
+ it('displays the component with default styles', () => {
32
+ expect(wrapper.classes()).toEqual(
33
+ expect.arrayContaining([
34
+ 'flex',
35
+ 'items-start',
36
+ 'gap-3',
37
+ 'rounded',
38
+ 'border',
39
+ 'border-solid',
40
+ ])
41
+ );
42
+
43
+ expect(wrapper.classes()).not.toContain('alert');
44
+ });
45
+
46
+ it('displays the component with success variant styles', async () => {
47
+ await wrapper.setProps({ variant: 'success' });
48
+
49
+ expect(wrapper.classes()).toEqual(
50
+ expect.arrayContaining([
51
+ 'border-success-300',
52
+ 'bg-success-200',
53
+ 'text-success-700',
54
+ ])
55
+ );
56
+
57
+ expect(wrapper.classes()).not.toContain('border-error-300');
58
+ expect(wrapper.classes()).not.toContain('bg-error-200');
59
+ expect(wrapper.classes()).not.toContain('text-error-700');
60
+ expect(wrapper.classes()).not.toContain('border-warning-300');
61
+ expect(wrapper.classes()).not.toContain('bg-warning-200');
62
+ expect(wrapper.classes()).not.toContain('text-warning-700');
63
+ });
64
+
65
+ it('displays the component with error variant styles', async () => {
66
+ await wrapper.setProps({ variant: 'error' });
67
+
68
+ expect(wrapper.classes()).toEqual(
69
+ expect.arrayContaining([
70
+ 'border-error-300',
71
+ 'bg-error-200',
72
+ 'text-error-700',
73
+ ])
74
+ );
75
+
76
+ expect(wrapper.classes()).not.toContain('border-success-300');
77
+ expect(wrapper.classes()).not.toContain('bg-success-200');
78
+ expect(wrapper.classes()).not.toContain('text-success-700');
79
+ expect(wrapper.classes()).not.toContain('border-warning-300');
80
+ expect(wrapper.classes()).not.toContain('bg-warning-200');
81
+ expect(wrapper.classes()).not.toContain('text-warning-700');
82
+ });
83
+
84
+ it('displays the component with warning variant styles', async () => {
85
+ await wrapper.setProps({ variant: 'warning' });
86
+
87
+ expect(wrapper.classes()).toEqual(
88
+ expect.arrayContaining([
89
+ 'border-warning-300',
90
+ 'bg-warning-200',
91
+ 'text-warning-700',
92
+ ])
93
+ );
94
+
95
+ expect(wrapper.classes()).not.toContain('border-success-300');
96
+ expect(wrapper.classes()).not.toContain('bg-success-200');
97
+ expect(wrapper.classes()).not.toContain('text-success-700');
98
+ expect(wrapper.classes()).not.toContain('border-error-300');
99
+ expect(wrapper.classes()).not.toContain('bg-error-200');
100
+ expect(wrapper.classes()).not.toContain('text-error-700');
101
+ });
102
+
103
+ it('displays the default large styles', () => {
104
+ expect(wrapper.classes()).toContain('p-3');
105
+ });
106
+
107
+ it('displays the component with small size styles', async () => {
108
+ await wrapper.setProps({ size: 'small' });
109
+
110
+ expect(wrapper.classes()).toEqual(
111
+ expect.arrayContaining(['py-1', 'px-2', 'text-sm'])
112
+ );
113
+ });
114
+
115
+ describe('dismiss button', () => {
116
+ it('displays dismiss button default classes', async () => {
117
+ await wrapper.setProps({ dismissable: true });
118
+
119
+ expect(wrapper.isVisible()).toBe(true);
120
+
121
+ // Find dismiss button
122
+ const dismissButton = wrapper.find('button');
123
+
124
+ expect(dismissButton.classes()).toEqual(
125
+ expect.arrayContaining([
126
+ 'ml-auto',
127
+ 'mt-1',
128
+ 'h-4',
129
+ 'w-4',
130
+ 'shrink-0',
131
+ 'text-current',
132
+ ])
133
+ );
134
+ });
135
+
136
+ it('renders the icon close when dismiss is visible', async () => {
137
+ await wrapper.setProps({ dismissable: true });
138
+
139
+ expect(wrapper.findComponent(IconClose).exists()).toBe(true);
140
+ });
141
+
142
+ it('hides the alert when the dismiss button is clicked', async () => {
143
+ await wrapper.setProps({ dismissable: true });
144
+
145
+ expect(wrapper.isVisible()).toBe(true);
146
+
147
+ // Click the dismiss button
148
+ const dismissButton = wrapper.find('button');
149
+ await dismissButton.trigger('click');
150
+
151
+ // Verify that the alert is now hidden
152
+ expect(wrapper.isVisible()).toBe(false);
153
+
154
+ // Verify that the 'dismiss' event was emitted
155
+ expect(wrapper.emitted('dismiss')).toBeTruthy();
156
+ });
157
+ });
158
+ });
@@ -0,0 +1,134 @@
1
+ import { mount } from '@vue/test-utils';
2
+ import VTButton from '@/components/Button/VTButton.vue';
3
+
4
+ describe('VTButton.vue', () => {
5
+ let wrapper;
6
+
7
+ beforeEach(() => {
8
+ wrapper = mount(VTButton, {
9
+ slots: {
10
+ default: 'Submit',
11
+ },
12
+ });
13
+ });
14
+
15
+ afterEach(() => {
16
+ wrapper.destroy();
17
+ });
18
+
19
+ it('renders the button if "to" and "href" props are not provided', () => {
20
+ expect(wrapper.find('button').exists()).toBe(true);
21
+ });
22
+
23
+ it('renders a NuxtLink when the "to" prop is provided', async () => {
24
+ await wrapper.setProps({ to: '/some-page' });
25
+
26
+ expect(wrapper.find('NuxtLink').exists()).toBe(true);
27
+ });
28
+
29
+ it('renders an "a" tag when the "href" prop is provided', async () => {
30
+ await wrapper.setProps({ href: 'https://example.com' });
31
+
32
+ expect(wrapper.find('a').exists()).toBe(true);
33
+ });
34
+
35
+ it('has the correct default props', () => {
36
+ expect(wrapper.props().variant).toBe('primary');
37
+ expect(wrapper.props().size).toBe('large');
38
+ expect(wrapper.props().to).toBe(null);
39
+ expect(wrapper.props().href).toBe(null);
40
+ expect(wrapper.props().headless).toBe(false);
41
+ expect(wrapper.props().busy).toBe(false);
42
+ expect(wrapper.props().disabled).toBe(false);
43
+ });
44
+
45
+ it('displays the component default styles', () => {
46
+ expect(wrapper.classes()).toEqual(
47
+ expect.arrayContaining([
48
+ 'relative',
49
+ 'inline-flex',
50
+ 'rounded',
51
+ 'border',
52
+ 'border-solid',
53
+ 'px-4',
54
+ 'text-sm',
55
+ 'font-semibold',
56
+ 'leading-none',
57
+ 'no-underline',
58
+ 'transition-all',
59
+ ])
60
+ );
61
+
62
+ expect(wrapper.classes()).not.toContain('button');
63
+ });
64
+
65
+ it('displays the component with primary variant styles', async () => {
66
+ await wrapper.setProps({ variant: 'primary' });
67
+
68
+ expect(wrapper.classes()).toEqual(
69
+ expect.arrayContaining([
70
+ 'bg-secondary-400',
71
+ 'hover:bg-secondary-500',
72
+ 'focus:bg-secondary-600',
73
+ 'active:bg-secondary-600',
74
+ 'border-transparent',
75
+ 'text-white',
76
+ 'disabled:bg-gray-200',
77
+ 'disabled:text-gray-400'
78
+ ])
79
+ );
80
+
81
+ expect(wrapper.classes()).not.toContain('button');
82
+ });
83
+
84
+ it('displays the component with secondary variant styles', async () => {
85
+ await wrapper.setProps({ variant: 'secondary' });
86
+
87
+ expect(wrapper.classes()).toEqual(
88
+ expect.arrayContaining([
89
+ 'border-gray-400',
90
+ 'bg-white',
91
+ 'text-gray-700',
92
+ 'hover:bg-gray-100',
93
+ 'hover:bg-gray-200',
94
+ 'active:bg-gray-200',
95
+ 'disabled:border-gray-300',
96
+ 'disabled:text-gray-400'
97
+ ])
98
+ );
99
+
100
+ expect(wrapper.classes()).not.toContain('button');
101
+ });
102
+
103
+ it('displays the component with tertiary variant styles', async () => {
104
+ await wrapper.setProps({ variant: 'tertiary' });
105
+
106
+ expect(wrapper.classes()).toEqual(
107
+ expect.arrayContaining([
108
+ 'text-secondary-400',
109
+ 'hover:text-secondary-500',
110
+ 'focus:text-secondary-600',
111
+ 'active:text-secondary-600',
112
+ 'border-transparent',
113
+ 'disabled:text-gray-400'
114
+ ])
115
+ );
116
+
117
+ expect(wrapper.classes()).not.toContain('button');
118
+ });
119
+
120
+ it('displays the component with icon variant styles', async () => {
121
+ await wrapper.setProps({ variant: 'icon' });
122
+
123
+ expect(wrapper.classes()).toEqual(
124
+ expect.arrayContaining([
125
+ 'text-primary-100',
126
+ 'focus-within:bg-gray-200',
127
+ 'hover:bg-gray-200',
128
+ 'active:bg-gray-300'
129
+ ])
130
+ );
131
+
132
+ expect(wrapper.classes()).not.toContain('button');
133
+ });
134
+ });
@@ -0,0 +1,22 @@
1
+ import { defineConfig } from 'vite';
2
+ import Vue2 from '@vitejs/plugin-vue2';
3
+ import path from 'path';
4
+
5
+ export default defineConfig({
6
+ plugins: [Vue2()],
7
+ test: {
8
+ globals: true,
9
+ environment: 'jsdom',
10
+ alias: [
11
+ {
12
+ find: /^vue$/,
13
+ replacement: 'vue/dist/vue.runtime.common.js',
14
+ },
15
+ ],
16
+ },
17
+ resolve: {
18
+ alias: {
19
+ '@': path.resolve(__dirname, './src'),
20
+ },
21
+ },
22
+ });