@veritree/ui 0.36.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.
- package/.github/workflows/node.js.yml +31 -0
- package/.vscode/settings.json +1 -0
- package/package.json +14 -3
- package/src/components/Alert/VTAlert.vue +8 -4
- package/src/components/Tabs/VTTabGroup.vue +14 -14
- package/test/alert.test.js +158 -0
- package/test/button.test.js +134 -0
- package/vitest.config.js +22 -0
|
@@ -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.
|
|
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
|
}
|
|
@@ -29,9 +29,7 @@
|
|
|
29
29
|
<button
|
|
30
30
|
v-if="dismissable"
|
|
31
31
|
:class="[
|
|
32
|
-
|
|
33
|
-
? 'ml-auto mt-1 h-4 w-4 shrink-0 text-current'
|
|
34
|
-
: 'alert-close',
|
|
32
|
+
headless ? 'alert-close' : 'ml-auto mt-1 h-4 w-4 shrink-0 text-current',
|
|
35
33
|
]"
|
|
36
34
|
@click="hide"
|
|
37
35
|
>
|
|
@@ -41,9 +39,15 @@
|
|
|
41
39
|
</template>
|
|
42
40
|
|
|
43
41
|
<script>
|
|
42
|
+
import IconClose from '@veritree/icons/src/components/IconClose.vue';
|
|
43
|
+
|
|
44
44
|
export default {
|
|
45
45
|
name: 'VTAlert',
|
|
46
46
|
|
|
47
|
+
components: {
|
|
48
|
+
IconClose,
|
|
49
|
+
},
|
|
50
|
+
|
|
47
51
|
props: {
|
|
48
52
|
variant: {
|
|
49
53
|
type: String,
|
|
@@ -93,8 +97,8 @@ export default {
|
|
|
93
97
|
|
|
94
98
|
methods: {
|
|
95
99
|
hide() {
|
|
96
|
-
this.$emit('dismiss');
|
|
97
100
|
this.show = false;
|
|
101
|
+
this.$emit('dismiss');
|
|
98
102
|
},
|
|
99
103
|
},
|
|
100
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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
+
});
|
package/vitest.config.js
ADDED
|
@@ -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
|
+
});
|