@veritree/ui 0.35.0 → 0.37.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,31 @@
1
+ # This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
2
+ # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs
3
+
4
+ name: Node.js CI
5
+
6
+ on:
7
+ push:
8
+ branches: [ "main" ]
9
+ pull_request:
10
+ branches: [ "main" ]
11
+
12
+ jobs:
13
+ build:
14
+
15
+ runs-on: ubuntu-latest
16
+
17
+ strategy:
18
+ matrix:
19
+ node-version: [14.x, 16.x, 18.x]
20
+ # See supported Node.js release schedule at https://nodejs.org/en/about/releases/
21
+
22
+ steps:
23
+ - uses: actions/checkout@v3
24
+ - name: Use Node.js ${{ matrix.node-version }}
25
+ uses: actions/setup-node@v3
26
+ with:
27
+ node-version: ${{ matrix.node-version }}
28
+ cache: 'npm'
29
+ - run: npm ci
30
+ - run: npm run build --if-present
31
+ - run: npm test
@@ -0,0 +1 @@
1
+ {}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@veritree/ui",
3
- "version": "0.35.0",
3
+ "version": "0.37.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
- "@veritree/icons": "^0.43.0"
20
+ "@veritree/icons": "^0.43.0",
21
+ "vue": "^2.7.2"
17
22
  },
18
23
  "devDependencies": {
24
+ "@vitejs/plugin-vue2": "^1.1.2",
25
+ "@vue/test-utils": "^1.3.0",
26
+ "jsdom": "latest",
19
27
  "prettier": "^2.7.1",
20
28
  "prettier-plugin-tailwindcss": "^0.1.13",
21
- "tailwindcss": "^3.2.4"
29
+ "tailwindcss": "^3.2.4",
30
+ "vite": "latest",
31
+ "vitest": "latest",
32
+ "vue-template-compiler": "^2.7.2"
22
33
  }
23
34
  }
@@ -3,9 +3,7 @@
3
3
  v-if="show"
4
4
  :class="[
5
5
  // default styles
6
- headless
7
- ? 'alert'
8
- : 'flex items-start gap-3 rounded border border-solid p-3',
6
+ headless ? 'alert' : 'flex items-start gap-3 rounded border border-solid',
9
7
  // variant styles
10
8
  headless
11
9
  ? `alert--${variant}`
@@ -16,6 +14,14 @@
16
14
  : isWarning
17
15
  ? 'border-warning-300 bg-warning-200 text-warning-700'
18
16
  : null,
17
+ // sizes styles
18
+ headless
19
+ ? `alert--${size}`
20
+ : isLarge
21
+ ? 'p-3'
22
+ : isSmall
23
+ ? 'py-1 px-2 text-sm'
24
+ : null,
19
25
  ]"
20
26
  role="alert"
21
27
  >
@@ -23,9 +29,7 @@
23
29
  <button
24
30
  v-if="dismissable"
25
31
  :class="[
26
- !headless
27
- ? 'ml-auto mt-1 h-4 w-4 shrink-0 text-current'
28
- : 'alert-close',
32
+ headless ? 'alert-close' : 'ml-auto mt-1 h-4 w-4 shrink-0 text-current',
29
33
  ]"
30
34
  @click="hide"
31
35
  >
@@ -35,14 +39,24 @@
35
39
  </template>
36
40
 
37
41
  <script>
42
+ import IconClose from '@veritree/icons/src/components/IconClose.vue';
43
+
38
44
  export default {
39
45
  name: 'VTAlert',
40
46
 
47
+ components: {
48
+ IconClose,
49
+ },
50
+
41
51
  props: {
42
52
  variant: {
43
53
  type: String,
44
54
  default: 'success',
45
55
  },
56
+ size: {
57
+ type: String,
58
+ default: 'large',
59
+ },
46
60
  headless: {
47
61
  type: Boolean,
48
62
  default: false,
@@ -71,12 +85,20 @@ export default {
71
85
  isWarning() {
72
86
  return this.variant === 'warning';
73
87
  },
88
+
89
+ isLarge() {
90
+ return this.size === 'large';
91
+ },
92
+
93
+ isSmall() {
94
+ return this.size === 'small';
95
+ },
74
96
  },
75
97
 
76
98
  methods: {
77
99
  hide() {
78
- this.$emit('dismiss');
79
100
  this.show = false;
101
+ this.$emit('dismiss');
80
102
  },
81
103
  },
82
104
  };
@@ -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>
@@ -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
+ });