@pequity/squirrel 6.0.14 → 6.1.1

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 (46) hide show
  1. package/dist/cjs/chunks/p-action-bar.js +4 -4
  2. package/dist/cjs/chunks/p-btn.js +204 -0
  3. package/dist/cjs/chunks/p-select-btn.js +8 -8
  4. package/dist/cjs/index.js +2 -2
  5. package/dist/cjs/p-btn.js +2 -169
  6. package/dist/es/chunks/p-action-bar.js +8 -8
  7. package/dist/es/chunks/p-btn.js +205 -0
  8. package/dist/es/chunks/p-select-btn.js +9 -9
  9. package/dist/es/index.js +62 -62
  10. package/dist/es/p-btn.js +2 -169
  11. package/dist/squirrel/components/p-btn/p-btn.vue.d.ts +106 -46
  12. package/dist/squirrel/components/p-card/p-card.vue.d.ts +27 -14
  13. package/dist/squirrel/components/p-checkbox/p-checkbox.vue.d.ts +43 -12
  14. package/dist/squirrel/components/p-close-btn/p-close-btn.vue.d.ts +1 -1
  15. package/dist/squirrel/components/p-drawer/p-drawer.vue.d.ts +1 -1
  16. package/dist/squirrel/components/p-dropdown-select/p-dropdown-select.vue.d.ts +15 -422
  17. package/dist/squirrel/components/p-file-upload/p-file-upload.vue.d.ts +1 -3
  18. package/dist/squirrel/components/p-info-icon/p-info-icon.vue.d.ts +13 -11
  19. package/dist/squirrel/components/p-inline-date-picker/p-inline-date-picker.vue.d.ts +39 -14
  20. package/dist/squirrel/components/p-input/p-input.vue.d.ts +2 -2
  21. package/dist/squirrel/components/p-input-number/p-input-number.vue.d.ts +2 -2
  22. package/dist/squirrel/components/p-input-search/p-input-search.vue.d.ts +2 -2
  23. package/dist/squirrel/components/p-link/p-link.vue.d.ts +17 -13
  24. package/dist/squirrel/components/p-loading/p-loading.vue.d.ts +1 -3
  25. package/dist/squirrel/components/p-modal/p-modal.vue.d.ts +240 -22
  26. package/dist/squirrel/components/p-pagination-info/p-pagination-info.vue.d.ts +77 -10
  27. package/dist/squirrel/components/p-select/p-select.vue.d.ts +116 -14
  28. package/dist/squirrel/components/p-select-btn/p-select-btn.vue.d.ts +37 -13
  29. package/dist/squirrel/components/p-select-list/p-select-list.vue.d.ts +14 -418
  30. package/dist/squirrel/components/p-table/p-table.vue.d.ts +61 -19
  31. package/dist/squirrel/components/p-table-header-cell/p-table-header-cell.vue.d.ts +2 -2
  32. package/dist/squirrel/components/p-table-loader/p-table-loader.vue.d.ts +1 -1
  33. package/dist/squirrel/components/p-table-td/p-table-td.vue.d.ts +23 -11
  34. package/dist/squirrel/components/p-tabs/p-tabs.vue.d.ts +1 -1
  35. package/dist/squirrel/components/p-textarea/p-textarea.vue.d.ts +1 -1
  36. package/dist/squirrel/components/p-toggle/p-toggle.vue.d.ts +1 -1
  37. package/dist/squirrel/utils/inputClassesMixin.d.ts +1 -1
  38. package/package.json +25 -24
  39. package/squirrel/components/p-btn/p-btn.spec.js +229 -161
  40. package/squirrel/components/p-btn/p-btn.stories.js +32 -8
  41. package/squirrel/components/p-btn/p-btn.vue +106 -73
  42. package/squirrel/components/p-dropdown/p-dropdown.vue +0 -1
  43. package/squirrel/components/p-inline-date-picker/p-inline-date-picker.vue +1 -1
  44. package/squirrel/components/p-select-btn/p-select-btn.spec.js +24 -5
  45. package/squirrel/components/p-select-btn/p-select-btn.stories.js +45 -4
  46. package/squirrel/components/p-select-btn/p-select-btn.vue +3 -3
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pequity/squirrel",
3
3
  "description": "Squirrel component library",
4
- "version": "6.0.14",
4
+ "version": "6.1.1",
5
5
  "packageManager": "pnpm@9.15.5",
6
6
  "type": "module",
7
7
  "scripts": {
@@ -18,6 +18,7 @@
18
18
  "storybook": "storybook dev -p 6006",
19
19
  "build-storybook": "storybook build",
20
20
  "test-storybook": "test-storybook",
21
+ "quality": "pnpm run lint && pnpm run typecheck && pnpm run test:unit && pnpm run build",
21
22
  "prepare": "husky"
22
23
  },
23
24
  "files": [
@@ -53,30 +54,30 @@
53
54
  "@playwright/test": "^1.50.1",
54
55
  "@semantic-release/changelog": "^6.0.3",
55
56
  "@semantic-release/git": "^10.0.1",
56
- "@storybook/addon-a11y": "^8.5.7",
57
- "@storybook/addon-actions": "^8.5.7",
58
- "@storybook/addon-essentials": "^8.5.7",
59
- "@storybook/addon-interactions": "^8.5.7",
60
- "@storybook/addon-links": "^8.5.7",
61
- "@storybook/blocks": "^8.5.7",
62
- "@storybook/manager-api": "^8.5.7",
63
- "@storybook/test": "^8.5.7",
64
- "@storybook/test-runner": "^0.21.2",
65
- "@storybook/theming": "^8.5.7",
66
- "@storybook/vue3": "^8.5.7",
67
- "@storybook/vue3-vite": "^8.5.7",
68
- "@tanstack/vue-virtual": "3.13.0",
57
+ "@storybook/addon-a11y": "^8.6.0",
58
+ "@storybook/addon-actions": "^8.6.0",
59
+ "@storybook/addon-essentials": "^8.6.0",
60
+ "@storybook/addon-interactions": "^8.6.0",
61
+ "@storybook/addon-links": "^8.6.0",
62
+ "@storybook/blocks": "^8.6.0",
63
+ "@storybook/manager-api": "^8.6.0",
64
+ "@storybook/test": "^8.6.0",
65
+ "@storybook/test-runner": "^0.21.3",
66
+ "@storybook/theming": "^8.6.0",
67
+ "@storybook/vue3": "^8.6.0",
68
+ "@storybook/vue3-vite": "^8.6.0",
69
+ "@tanstack/vue-virtual": "3.13.2",
69
70
  "@types/jsdom": "^21.1.7",
70
71
  "@types/lodash-es": "^4.17.12",
71
- "@types/node": "^22.13.4",
72
+ "@types/node": "^22.13.5",
72
73
  "@vitejs/plugin-vue": "^5.2.1",
73
- "@vitest/coverage-v8": "^3.0.6",
74
+ "@vitest/coverage-v8": "^3.0.7",
74
75
  "@vue/compiler-sfc": "3.5.13",
75
76
  "@vue/test-utils": "^2.4.6",
76
77
  "@vuepic/vue-datepicker": "11.0.1",
77
78
  "autoprefixer": "^10.4.20",
78
79
  "dayjs": "1.11.13",
79
- "eslint": "^9.20.1",
80
+ "eslint": "^9.21.0",
80
81
  "eslint-plugin-storybook": "^0.11.3",
81
82
  "floating-vue": "5.2.2",
82
83
  "glob": "^11.0.1",
@@ -86,24 +87,24 @@
86
87
  "lint-staged": "^15.4.3",
87
88
  "lodash-es": "4.17.21",
88
89
  "make-coverage-badge": "^1.2.0",
89
- "postcss": "^8.5.2",
90
- "prettier": "^3.5.1",
90
+ "postcss": "^8.5.3",
91
+ "prettier": "^3.5.2",
91
92
  "prettier-plugin-tailwindcss": "^0.6.11",
92
93
  "resolve-tspaths": "^0.8.23",
93
94
  "rimraf": "^6.0.1",
94
- "sass": "^1.85.0",
95
+ "sass": "^1.85.1",
95
96
  "semantic-release": "^24.2.3",
96
- "storybook": "^8.5.7",
97
+ "storybook": "^8.6.0",
97
98
  "svgo": "^3.3.2",
98
99
  "tailwindcss": "^3.4.17",
99
100
  "typescript": "5.7.3",
100
- "vite": "^6.1.0",
101
- "vitest": "^3.0.6",
101
+ "vite": "^6.2.0",
102
+ "vitest": "^3.0.7",
102
103
  "vue": "3.5.13",
103
104
  "vue-currency-input": "3.2.1",
104
105
  "vue-router": "4.5.0",
105
106
  "vue-toastification": "2.0.0-rc.5",
106
- "vue-tsc": "2.2.0"
107
+ "vue-tsc": "2.2.4"
107
108
  },
108
109
  "dependencies": {
109
110
  "tailwind-variants": "^0.3.1"
@@ -1,6 +1,7 @@
1
1
  import PBtn from '@squirrel/components/p-btn/p-btn.vue';
2
2
  import { sanitizeUrl } from '@squirrel/utils/sanitization';
3
3
  import { createWrapperFor } from '@tests/vitest.helpers';
4
+ import { describe } from 'vitest';
4
5
 
5
6
  vi.mock('@squirrel/utils/sanitization', () => {
6
7
  return {
@@ -27,215 +28,282 @@ const DEFAULT_CLASSES_ARRAY = [
27
28
  ];
28
29
 
29
30
  describe('PBtn.vue', () => {
30
- Object.keys(ELEMENTS_MAP).forEach((el) => {
31
- const to = ELEMENTS_MAP[el];
31
+ describe('default button', () => {
32
+ Object.keys(ELEMENTS_MAP).forEach((el) => {
33
+ const to = ELEMENTS_MAP[el];
32
34
 
33
- it.each([
34
- ['primary', ['text-surface', 'bg-primary', 'hover:bg-accent', 'active:bg-p-blue-80']],
35
- ['secondary', ['bg-p-gray-20', 'hover:bg-p-gray-30', 'active:bg-p-gray-40']],
36
- [
37
- 'primary-outline',
38
- ['text-p-purple-60', 'bg-p-blue-10', 'ring-1', 'ring-inset', 'ring-p-purple-60', 'hover:bg-p-gray-20'],
39
- ],
40
- [
41
- 'secondary-outline',
35
+ it.each([
36
+ ['primary', ['text-surface', 'bg-primary', 'hover:bg-accent', 'active:bg-p-blue-80']],
37
+ ['secondary', ['bg-p-gray-20', 'hover:bg-p-gray-30', 'active:bg-p-gray-40']],
38
+ [
39
+ 'primary-outline',
40
+ ['text-p-purple-60', 'bg-p-blue-10', 'ring-1', 'ring-inset', 'ring-p-purple-60', 'hover:bg-p-gray-20'],
41
+ ],
42
42
  [
43
- 'text-p-purple-60',
44
- 'bg-surface',
45
- 'ring-1',
46
- 'ring-inset',
47
- 'ring-p-gray-30',
48
- 'hover:bg-p-blue-10',
49
- 'aria-selected:bg-p-blue-10',
43
+ 'secondary-outline',
44
+ [
45
+ 'text-p-purple-60',
46
+ 'bg-surface',
47
+ 'ring-1',
48
+ 'ring-inset',
49
+ 'ring-p-gray-30',
50
+ 'hover:bg-p-blue-10',
51
+ 'aria-selected:bg-p-blue-10',
52
+ ],
50
53
  ],
51
- ],
52
- [
53
- 'secondary-outline-blue',
54
54
  [
55
- 'text-p-purple-60',
56
- 'bg-surface',
57
- 'ring-1',
58
- 'ring-inset',
59
- 'ring-p-gray-30',
60
- 'hover:bg-p-blue-10',
61
- 'aria-selected:bg-p-blue-15',
62
- 'aria-selected:text-p-blue-60',
55
+ 'secondary-outline-blue',
56
+ [
57
+ 'text-p-purple-60',
58
+ 'bg-surface',
59
+ 'ring-1',
60
+ 'ring-inset',
61
+ 'ring-p-gray-30',
62
+ 'hover:bg-p-blue-10',
63
+ 'aria-selected:bg-p-blue-15',
64
+ 'aria-selected:text-p-blue-60',
65
+ ],
63
66
  ],
64
- ],
65
- ['error', ['text-white', 'bg-p-red-40', 'hover:bg-p-red-50']],
66
- ['success', ['text-white', 'bg-p-green-40', 'hover:bg-p-green-50']],
67
- ['primary-link', ['text-primary', 'bg-transparent', 'hover:text-accent', 'underline']],
68
- ['secondary-ghost', ['text-on-surface', 'hover:bg-p-gray-20']],
69
- ])(`renders a ${el} of type %s`, async (type, classes) => {
67
+ ['error', ['text-white', 'bg-p-red-40', 'hover:bg-p-red-50']],
68
+ ['success', ['text-white', 'bg-p-green-40', 'hover:bg-p-green-50']],
69
+ ['primary-link', ['text-primary', 'bg-transparent', 'hover:text-accent', 'underline']],
70
+ ['secondary-ghost', ['text-on-surface', 'hover:bg-p-gray-20']],
71
+ ])(`renders a ${el} of type %s`, async (type, classes) => {
72
+ const wrapper = createWrapperFor(PBtn, {
73
+ props: { type },
74
+ attrs: { to },
75
+ global: {
76
+ stubs: { RouterLink: { template: '<section class="router-link-stub"><slot /></section>' } },
77
+ },
78
+ });
79
+
80
+ const element = wrapper.find(el);
81
+
82
+ if (el !== 'a') {
83
+ const slotWrapper = wrapper.find('.slot-wrapper');
84
+ expect(slotWrapper.classes()).toEqual(['slot-wrapper', 'empty:hidden']);
85
+ }
86
+
87
+ expect(classes.every((c) => element.classes().includes(c))).toBe(true);
88
+ expect(DEFAULT_CLASSES_ARRAY.every((c) => element.classes().includes(c))).toBe(true);
89
+ });
90
+ });
91
+
92
+ it.each([
93
+ ['sm', ['flex', 'items-center', 'justify-center', 'has-[.slot-wrapper:empty]:gap-0', 'gap-1']],
94
+ ['md', ['flex', 'items-center', 'justify-center', 'has-[.slot-wrapper:empty]:gap-0', 'gap-2']],
95
+ ['lg', ['flex', 'items-center', 'justify-center', 'has-[.slot-wrapper:empty]:gap-0', 'gap-2.5']],
96
+ ])('renders slots for content', async (size, classes) => {
70
97
  const wrapper = createWrapperFor(PBtn, {
71
- props: { type },
72
- attrs: { to },
73
- global: {
74
- stubs: { RouterLink: { template: '<section class="router-link-stub"><slot /></section>' } },
98
+ props: { size },
99
+ slots: {
100
+ default: `This is a button`,
75
101
  },
76
102
  });
77
103
 
78
- const element = await wrapper.find(el);
104
+ const contentWrapper = await wrapper.find('div');
105
+ const button = await wrapper.find('button');
79
106
 
80
- expect(classes.every((c) => element.classes().includes(c))).toBe(true);
81
- expect(DEFAULT_CLASSES_ARRAY.every((c) => element.classes().includes(c))).toBe(true);
107
+ expect(contentWrapper.classes()).toEqual(classes);
108
+ expect(button.text()).toContain(`This is a button`);
82
109
  });
83
- });
84
110
 
85
- it('renders slots for icon and content', async () => {
86
- const wrapper = createWrapperFor(PBtn, {
87
- slots: {
88
- default: `This is a button`,
89
- },
90
- });
111
+ it.each([
112
+ ['button', undefined],
113
+ ['a', 'https://pequity.com/'],
114
+ ['a', { name: 'home', params: { id: 1 } }],
115
+ ])('gets disabled when the element is a %s and points to %s', async (el, to) => {
116
+ const wrapper = createWrapperFor(PBtn, {
117
+ props: {
118
+ to,
119
+ },
120
+ attrs: {
121
+ disabled: true,
122
+ },
123
+ global: {
124
+ stubs: { RouterLink: { template: '<a class="router-link-stub"><slot /></a>' } },
125
+ },
126
+ });
91
127
 
92
- const button = await wrapper.find('button');
128
+ const button = await wrapper.find(el);
93
129
 
94
- expect(button.text()).toContain(`This is a button`);
95
- });
130
+ await button.trigger('click');
96
131
 
97
- it.each([
98
- ['button', undefined],
99
- ['a', 'https://pequity.com/'],
100
- ['a', { name: 'home', params: { id: 1 } }],
101
- ])('gets disabled when the element is a %s and points to %s', async (el, to) => {
102
- const wrapper = createWrapperFor(PBtn, {
103
- props: {
104
- to,
105
- },
106
- attrs: {
107
- disabled: true,
108
- },
109
- global: {
110
- stubs: { RouterLink: { template: '<a class="router-link-stub"><slot /></a>' } },
111
- },
112
- });
132
+ if (el === 'button') {
133
+ expect(wrapper.emitted().click).toBeFalsy();
134
+ }
135
+ expect(button.attributes()).toHaveProperty('disabled');
136
+ expect(button.attributes()['aria-disabled']).toBe('true');
113
137
 
114
- const button = await wrapper.find(el);
138
+ await wrapper.setProps({ disabled: false });
115
139
 
116
- await button.trigger('click');
140
+ await button.trigger('click');
117
141
 
118
- if (el === 'button') {
119
- expect(wrapper.emitted().click).toBeFalsy();
120
- }
121
- expect(button.attributes()).toHaveProperty('disabled');
122
- expect(button.attributes()['aria-disabled']).toBe('true');
142
+ if (el === 'button') {
143
+ expect(wrapper.emitted().click[0][0] instanceof MouseEvent).toBe(true);
144
+ }
145
+ expect(button.attributes()).not.toHaveProperty('disabled');
146
+ expect(button.attributes()['aria-disabled']).toBe('false');
147
+ });
123
148
 
124
- await wrapper.setProps({ disabled: false });
149
+ it('has a loading state', async () => {
150
+ const wrapper = createWrapperFor(PBtn, {
151
+ props: {
152
+ loading: true,
153
+ },
154
+ global: {
155
+ stubs: { PRingLoader: { template: '<div class="loader-stub">loader</div>' } },
156
+ },
157
+ });
125
158
 
126
- await button.trigger('click');
159
+ const button = await wrapper.find('button');
160
+ const loader = await wrapper.find('.loader-stub');
127
161
 
128
- if (el === 'button') {
129
- expect(wrapper.emitted().click[0][0] instanceof MouseEvent).toBe(true);
130
- }
131
- expect(button.attributes()).not.toHaveProperty('disabled');
132
- expect(button.attributes()['aria-disabled']).toBe('false');
133
- });
162
+ await button.trigger('click');
134
163
 
135
- it('has a loading state', async () => {
136
- const wrapper = createWrapperFor(PBtn, {
137
- props: {
138
- loading: true,
139
- },
140
- global: {
141
- stubs: { PRingLoader: { template: '<div class="loader-stub">loader</div>' } },
142
- },
164
+ expect(wrapper.emitted().click).toBeFalsy();
165
+ expect(button.attributes()).toHaveProperty('disabled');
166
+ expect(loader.exists()).toBe(true);
143
167
  });
144
168
 
145
- const button = await wrapper.find('button');
146
- const loader = await wrapper.find('.loader-stub');
169
+ it.each(['button', 'submit', 'reset'])('renders a button of native type %s', async (nativeType) => {
170
+ const wrapper = createWrapperFor(PBtn, { props: { nativeType } });
147
171
 
148
- await button.trigger('click');
172
+ const button = await wrapper.find('button');
149
173
 
150
- expect(wrapper.emitted().click).toBeFalsy();
151
- expect(button.attributes()).toHaveProperty('disabled');
152
- expect(loader.exists()).toBe(true);
153
- });
174
+ expect(button.attributes().type).toBe(nativeType);
175
+ });
154
176
 
155
- it.each(['button', 'submit', 'reset'])('renders a button of native type %s', async (nativeType) => {
156
- const wrapper = createWrapperFor(PBtn, { props: { nativeType } });
177
+ it.each([
178
+ ['sm', ['py-1.5', 'px-3', 'rounded', 'font-medium', 'text-sm', 'leading-5']],
179
+ ['md', ['py-2', 'px-6', 'rounded', 'font-medium', 'text-base']],
180
+ ['lg', ['py-3', 'px-6', 'rounded', 'font-medium', 'text-lg']],
181
+ ])('renders a button of size %s', async (size, classes) => {
182
+ const wrapper = createWrapperFor(PBtn, { props: { size }, slots: { default: `button` } });
157
183
 
158
- const button = await wrapper.find('button');
184
+ const button = await wrapper.find('button');
159
185
 
160
- expect(button.attributes().type).toBe(nativeType);
161
- });
186
+ expect(classes.every((c) => button.classes().includes(c))).toBe(true);
187
+ });
162
188
 
163
- it.each([
164
- ['sm', ['py-1', 'px-2', 'rounded', 'font-medium', 'text-sm']],
165
- ['md', ['py-2', 'px-6', 'rounded', 'font-medium', 'text-base']],
166
- ['lg', ['py-2.5', 'px-6', 'rounded', 'font-medium', 'text-lg']],
167
- ])('renders a button of size %s', async (size, classes) => {
168
- const wrapper = createWrapperFor(PBtn, { props: { size } });
189
+ it('renders an href in case it is an external link', async () => {
190
+ const wrapper = createWrapperFor(PBtn, {
191
+ props: {
192
+ to: 'https://pequity.com/',
193
+ },
194
+ slots: {
195
+ default: `This is a button`,
196
+ },
197
+ });
169
198
 
170
- const button = await wrapper.find('button');
199
+ const a = await wrapper.find('a');
171
200
 
172
- expect(classes.every((c) => button.classes().includes(c))).toBe(true);
173
- });
201
+ expect(a.attributes().href).toBe(`sanitized-https://pequity.com/`);
202
+ expect(a.attributes().target).toBe('_blank');
203
+ expect(a.text()).toBe('This is a button');
204
+ });
205
+
206
+ it('renders a router link', async () => {
207
+ const wrapper = createWrapperFor(PBtn, {
208
+ props: {
209
+ to: { name: 'home', params: { id: 1 } },
210
+ },
211
+ slots: {
212
+ default: `This is a button`,
213
+ },
214
+ global: {
215
+ stubs: { RouterLink: { template: '<section class="router-link-stub"><slot /></section>' } },
216
+ },
217
+ });
174
218
 
175
- it('renders an href in case it is an external link', async () => {
176
- const wrapper = createWrapperFor(PBtn, {
177
- props: {
178
- to: 'https://pequity.com/',
179
- },
180
- slots: {
181
- default: `This is a button`,
182
- },
219
+ const routerLink = await wrapper.find('section');
220
+
221
+ expect(routerLink.classes()).toContain('router-link-stub');
222
+ expect(routerLink.text()).toBe('This is a button');
183
223
  });
184
224
 
185
- const a = await wrapper.find('a');
225
+ it('sets the aria-selected attribute when the selected prop is set', async () => {
226
+ const wrapper = createWrapperFor(PBtn, {
227
+ props: {
228
+ selected: true,
229
+ },
230
+ slots: {
231
+ default: `This is a button`,
232
+ },
233
+ });
186
234
 
187
- expect(a.attributes().href).toBe(`sanitized-https://pequity.com/`);
188
- expect(a.attributes().target).toBe('_blank');
189
- expect(a.text()).toBe('This is a button');
190
- });
235
+ const button = await wrapper.find('button');
191
236
 
192
- it('renders a router link', async () => {
193
- const wrapper = createWrapperFor(PBtn, {
194
- props: {
195
- to: { name: 'home', params: { id: 1 } },
196
- },
197
- slots: {
198
- default: `This is a button`,
199
- },
200
- global: {
201
- stubs: { RouterLink: { template: '<section class="router-link-stub"><slot /></section>' } },
202
- },
237
+ expect(button.attributes()['aria-selected']).toBe('true');
203
238
  });
204
239
 
205
- const routerLink = await wrapper.find('section');
240
+ it('sanitizes an invalid link', async () => {
241
+ const wrapper = createWrapperFor(PBtn, {
242
+ props: {
243
+ to: 'javascript:evil()',
244
+ },
245
+ slots: {
246
+ default: `This is a button`,
247
+ },
248
+ });
249
+
250
+ const a = await wrapper.find('a');
206
251
 
207
- expect(routerLink.classes()).toContain('router-link-stub');
208
- expect(routerLink.text()).toBe('This is a button');
252
+ expect(a.text()).toBe('This is a button');
253
+ expect(sanitizeUrl).toHaveBeenCalledTimes(1);
254
+ });
209
255
  });
210
256
 
211
- it('sets the aria-selected attribute when the selected prop is set', async () => {
212
- const wrapper = createWrapperFor(PBtn, {
213
- props: {
214
- selected: true,
215
- },
216
- slots: {
217
- default: `This is a button`,
218
- },
257
+ describe('icon button', () => {
258
+ it.each([
259
+ ['sm', ['shrink-0', 'text-[20px]']],
260
+ ['md', ['shrink-0', 'text-xl']],
261
+ ['lg', ['shrink-0', 'text-2xl']],
262
+ ])('renders a button with icon of size %s', async (size, classes) => {
263
+ const wrapper = createWrapperFor(PBtn, {
264
+ props: { size, icon: 'edit' },
265
+ slots: { default: 'text' },
266
+ global: { stubs: { PIcon: true } },
267
+ });
268
+
269
+ const icon = await wrapper.findComponent({ name: 'PIcon' });
270
+
271
+ console.log(icon.classes());
272
+
273
+ expect(classes.every((c) => icon.classes().includes(c))).toBe(true);
219
274
  });
220
275
 
221
- const button = await wrapper.find('button');
276
+ it.each([
277
+ ['sm', ['shrink-0', 'text-[20px]']],
278
+ ['md', ['shrink-0', 'text-xl']],
279
+ ['lg', ['shrink-0', 'text-2xl']],
280
+ ])('renders a button with a right icon of size %s', async (size, classes) => {
281
+ const wrapper = createWrapperFor(PBtn, {
282
+ props: { size, iconRight: 'edit' },
283
+ slots: { default: 'text' },
284
+ global: { stubs: { PIcon: true } },
285
+ });
222
286
 
223
- expect(button.attributes()['aria-selected']).toBe('true');
224
- });
287
+ const icon = await wrapper.findComponent({ name: 'PIcon' });
225
288
 
226
- it('sanitizes an invalid link', async () => {
227
- const wrapper = createWrapperFor(PBtn, {
228
- props: {
229
- to: 'javascript:evil()',
230
- },
231
- slots: {
232
- default: `This is a button`,
233
- },
289
+ expect(classes.every((c) => icon.classes().includes(c))).toBe(true);
234
290
  });
235
291
 
236
- const a = await wrapper.find('a');
292
+ it.each([
293
+ ['sm', ['shrink-0', 'text-[20px]'], ['has-[.slot-wrapper:empty]:px-1.5']],
294
+ ['md', ['shrink-0', 'text-xl'], ['has-[.slot-wrapper:empty]:px-2.5']],
295
+ ['lg', ['shrink-0', 'text-2xl'], ['has-[.slot-wrapper:empty]:px-3']],
296
+ ])('renders a button with an icon without text of size %s', async (size, classes, buttonClasses) => {
297
+ const wrapper = createWrapperFor(PBtn, {
298
+ props: { size, icon: 'edit' },
299
+ global: { stubs: { PIcon: true } },
300
+ });
237
301
 
238
- expect(a.text()).toBe('This is a button');
239
- expect(sanitizeUrl).toHaveBeenCalledTimes(1);
302
+ const button = wrapper.find('button');
303
+ const icon = wrapper.findComponent({ name: 'PIcon' });
304
+
305
+ expect(classes.every((c) => icon.classes().includes(c))).toBe(true);
306
+ expect(buttonClasses.every((c) => button.classes().includes(c))).toBe(true);
307
+ });
240
308
  });
241
309
  });
@@ -1,4 +1,3 @@
1
- import Icon from '@squirrel/assets/info-circle-icon.svg?inline';
2
1
  import PBtn from '@squirrel/components/p-btn/p-btn.vue';
3
2
  import { action } from '@storybook/addon-actions';
4
3
  import { expect, within } from '@storybook/test';
@@ -101,19 +100,44 @@ export const PrimaryLink = {
101
100
  },
102
101
  };
103
102
 
104
- export const IconButtonSmall = {
103
+ export const Icon = {
105
104
  render: (args) => ({
106
- components: { PBtn, Icon },
105
+ components: { PBtn },
107
106
  setup() {
108
107
  return { args };
109
108
  },
110
- template: `
111
- <PBtn v-bind="args">
112
- <Icon class="my-1" />
113
- </PBtn>`,
109
+ template: ` <PBtn v-bind="args" icon="archive">Icon Button</PBtn> `,
114
110
  }),
115
111
  args: {
116
- size: 'sm',
112
+ size: 'md',
113
+ type: 'secondary-outline',
114
+ },
115
+ };
116
+
117
+ export const IconRight = {
118
+ render: (args) => ({
119
+ components: { PBtn },
120
+ setup() {
121
+ return { args };
122
+ },
123
+ template: ` <PBtn v-bind="args" icon-right="archive">Icon Button</PBtn> `,
124
+ }),
125
+ args: {
126
+ size: 'md',
127
+ type: 'secondary-outline',
128
+ },
129
+ };
130
+
131
+ export const IconWithoutContent = {
132
+ render: (args) => ({
133
+ components: { PBtn },
134
+ setup() {
135
+ return { args };
136
+ },
137
+ template: ` <PBtn v-bind="args" icon-right="archive"></PBtn> `,
138
+ }),
139
+ args: {
140
+ size: 'md',
117
141
  type: 'secondary-outline',
118
142
  },
119
143
  };