@saooti/octopus-sdk 41.11.1 → 41.12.0-beta2
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/CHANGELOG.md +29 -0
- package/eslint-config.mjs +10 -2
- package/index.ts +5 -2
- package/package.json +4 -1
- package/src/api/radioApi.ts +14 -1
- package/src/components/buttons/ClassicButton.vue +18 -1
- package/src/components/composable/form/useOctopusDropdown.ts +104 -0
- package/src/components/composable/useSticky.ts +45 -0
- package/src/components/display/emission/EmissionGroupChooser.vue +22 -24
- package/src/components/display/podcasts/PodcastPlayButton.vue +3 -3
- package/src/components/form/ClassicButtonGroup.vue +15 -13
- package/src/components/form/ClassicSelect.vue +11 -2
- package/src/components/form/OctopusMultiselect.vue +236 -109
- package/src/components/form/OctopusSelect.vue +278 -0
- package/src/components/misc/ClassicPopover.vue +9 -2
- package/src/components/misc/modal/ClassicModal.vue +3 -0
- package/src/components/misc/player/elements/PlayerPlayButton.vue +22 -18
- package/src/components/pages/PlaylistsPage.vue +1 -0
- package/src/helper/colorFromString.ts +12 -5
- package/src/helper/equals.ts +21 -9
- package/src/stores/ParamSdkStore.ts +1 -0
- package/src/stores/class/general/organisation.ts +6 -0
- package/src/stores/class/general/podcast.ts +8 -0
- package/src/stores/class/rubrique/rubrique.ts +1 -1
- package/src/style/_utilities.scss +1 -0
- package/src/style/_variables.scss +11 -0
- package/src/style/bootstrap.scss +9 -2
- package/tests/components/form/ClassicButtonGroup.spec.ts +10 -10
- package/tests/components/form/OctopusMultiselect.spec.ts +84 -39
- package/tests/components/form/OctopusSelect.spec.ts +168 -0
- package/tsconfig.json +3 -7
- package/tsconfig.test.json +13 -0
- package/vitest.config.js +2 -1
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import '@tests/mocks/i18n';
|
|
2
2
|
|
|
3
3
|
import OctopusMultiselect from '@/components/form/OctopusMultiselect.vue';
|
|
4
|
+
import { DOMWrapper, type VueWrapper } from '@vue/test-utils';
|
|
4
5
|
import { mount } from '@tests/utils';
|
|
5
|
-
import { describe, expect, it, vi } from 'vitest';
|
|
6
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
6
7
|
|
|
7
8
|
const options = [
|
|
8
9
|
{ id: 1, name: 'Alpha' },
|
|
@@ -10,66 +11,89 @@ const options = [
|
|
|
10
11
|
{ id: 3, name: 'Gamma' },
|
|
11
12
|
];
|
|
12
13
|
|
|
14
|
+
// The dropdown is teleported to .octopus-app — helper to query it from the document.
|
|
15
|
+
function getDropdown(): Element | null {
|
|
16
|
+
return document.body.querySelector('.octopus-multiselect-dropdown');
|
|
17
|
+
}
|
|
18
|
+
|
|
13
19
|
describe('OctopusMultiselect', () => {
|
|
20
|
+
// The dropdown teleports to .octopus-app, which is normally the app root (see App.vue).
|
|
21
|
+
// It doesn't exist in the test DOM, so it must be created before mount.
|
|
22
|
+
let appElement: HTMLElement;
|
|
23
|
+
|
|
24
|
+
beforeEach(() => {
|
|
25
|
+
appElement = document.createElement('div');
|
|
26
|
+
appElement.className = 'octopus-app';
|
|
27
|
+
document.body.appendChild(appElement);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// Shared wrapper ref so afterEach can properly unmount it.
|
|
31
|
+
// Proper unmount lets Vue clean up the teleport target before the next test starts.
|
|
32
|
+
let wrapper: VueWrapper;
|
|
33
|
+
afterEach(() => {
|
|
34
|
+
wrapper?.unmount();
|
|
35
|
+
appElement.remove();
|
|
36
|
+
});
|
|
37
|
+
|
|
14
38
|
it('renders label when provided', async () => {
|
|
15
|
-
|
|
39
|
+
wrapper = await mount(OctopusMultiselect, {
|
|
16
40
|
props: { options, optionLabel: 'name', label: 'My label' },
|
|
17
41
|
});
|
|
18
42
|
expect(wrapper.find('label.form-label').text()).toBe('My label');
|
|
19
43
|
});
|
|
20
44
|
|
|
21
45
|
it('does not render label when not provided', async () => {
|
|
22
|
-
|
|
46
|
+
wrapper = await mount(OctopusMultiselect, {
|
|
23
47
|
props: { options, optionLabel: 'name' },
|
|
24
48
|
});
|
|
25
49
|
expect(wrapper.find('label.form-label').exists()).toBe(false);
|
|
26
50
|
});
|
|
27
51
|
|
|
28
52
|
it('opens dropdown on input focus', async () => {
|
|
29
|
-
|
|
53
|
+
wrapper = await mount(OctopusMultiselect, {
|
|
30
54
|
props: { options, optionLabel: 'name' },
|
|
31
55
|
});
|
|
32
|
-
expect(
|
|
56
|
+
expect(getDropdown()).toBeNull();
|
|
33
57
|
await wrapper.find('input').trigger('focus');
|
|
34
|
-
expect(
|
|
58
|
+
expect(getDropdown()).not.toBeNull();
|
|
35
59
|
});
|
|
36
60
|
|
|
37
61
|
it('shows "All" checkbox and one checkbox per option when open', async () => {
|
|
38
|
-
|
|
62
|
+
wrapper = await mount(OctopusMultiselect, {
|
|
39
63
|
props: { options, optionLabel: 'name' },
|
|
40
64
|
});
|
|
41
65
|
await wrapper.find('input').trigger('focus');
|
|
42
|
-
const checkboxes =
|
|
66
|
+
const checkboxes = document.body.querySelectorAll('input[type="checkbox"]');
|
|
43
67
|
// 1 for "All" + 3 for options
|
|
44
68
|
expect(checkboxes).toHaveLength(4);
|
|
45
69
|
});
|
|
46
70
|
|
|
47
71
|
it('filters options locally by search query', async () => {
|
|
48
|
-
|
|
72
|
+
wrapper = await mount(OctopusMultiselect, {
|
|
49
73
|
props: { options, optionLabel: 'name' },
|
|
50
74
|
});
|
|
51
75
|
await wrapper.find('input').trigger('focus');
|
|
52
76
|
await wrapper.find('input').setValue('al');
|
|
53
77
|
await wrapper.find('input').trigger('input');
|
|
54
|
-
const checkboxes =
|
|
78
|
+
const checkboxes = document.body.querySelectorAll('input[type="checkbox"]');
|
|
55
79
|
// 1 for "All" + 1 matching "Alpha"
|
|
56
80
|
expect(checkboxes).toHaveLength(2);
|
|
57
81
|
});
|
|
58
82
|
|
|
59
83
|
it('shows no-results message when filter matches nothing', async () => {
|
|
60
|
-
|
|
84
|
+
wrapper = await mount(OctopusMultiselect, {
|
|
61
85
|
props: { options, optionLabel: 'name' },
|
|
62
86
|
});
|
|
63
87
|
await wrapper.find('input').trigger('focus');
|
|
64
88
|
await wrapper.find('input').setValue('zzz');
|
|
65
89
|
await wrapper.find('input').trigger('input');
|
|
66
|
-
expect(
|
|
90
|
+
expect(document.body.querySelector('.text-indic')).not.toBeNull();
|
|
67
91
|
});
|
|
68
92
|
|
|
69
93
|
it('calls onSearch with current query and updates displayed options', async () => {
|
|
70
94
|
const searchResult = [{ id: 4, name: 'Delta' }];
|
|
71
95
|
const onSearch = vi.fn().mockResolvedValue(searchResult);
|
|
72
|
-
|
|
96
|
+
wrapper = await mount(OctopusMultiselect, {
|
|
73
97
|
props: { options, optionLabel: 'name', onSearch },
|
|
74
98
|
});
|
|
75
99
|
await wrapper.find('input').trigger('focus');
|
|
@@ -80,83 +104,104 @@ describe('OctopusMultiselect', () => {
|
|
|
80
104
|
});
|
|
81
105
|
|
|
82
106
|
it('emits update:selected when toggling a single option', async () => {
|
|
83
|
-
|
|
107
|
+
wrapper = await mount(OctopusMultiselect, {
|
|
84
108
|
props: { options, optionLabel: 'name', selected: [] },
|
|
85
109
|
});
|
|
86
110
|
await wrapper.find('input').trigger('focus');
|
|
87
|
-
const optionCheckboxes =
|
|
88
|
-
await optionCheckboxes[0].trigger('input');
|
|
111
|
+
const optionCheckboxes = document.body.querySelectorAll('.octopus-multiselect-options input[type="checkbox"]');
|
|
112
|
+
await new DOMWrapper(optionCheckboxes[0] as Element).trigger('input');
|
|
89
113
|
expect(wrapper.emitted('update:selected')?.[0]).toEqual([[options[0]]]);
|
|
90
114
|
});
|
|
91
115
|
|
|
92
116
|
it('deselects an already-selected option', async () => {
|
|
93
|
-
|
|
117
|
+
wrapper = await mount(OctopusMultiselect, {
|
|
94
118
|
props: { options, optionLabel: 'name', selected: [options[0]] },
|
|
95
119
|
});
|
|
96
120
|
await wrapper.find('input').trigger('focus');
|
|
97
|
-
const optionCheckboxes =
|
|
98
|
-
await optionCheckboxes[0].trigger('input');
|
|
121
|
+
const optionCheckboxes = document.body.querySelectorAll('.octopus-multiselect-options input[type="checkbox"]');
|
|
122
|
+
await new DOMWrapper(optionCheckboxes[0] as Element).trigger('input');
|
|
99
123
|
expect(wrapper.emitted('update:selected')?.[0]).toEqual([[]]);
|
|
100
124
|
});
|
|
101
125
|
|
|
102
126
|
it('toggleAll selects all displayed options', async () => {
|
|
103
|
-
|
|
127
|
+
wrapper = await mount(OctopusMultiselect, {
|
|
104
128
|
props: { options, optionLabel: 'name', selected: [] },
|
|
105
129
|
});
|
|
106
130
|
await wrapper.find('input').trigger('focus');
|
|
107
|
-
const allCheckbox =
|
|
108
|
-
await allCheckbox.trigger('input');
|
|
131
|
+
const allCheckbox = document.body.querySelector('.octopus-multiselect-dropdown > .octopus-form-item input[type="checkbox"]')!;
|
|
132
|
+
await new DOMWrapper(allCheckbox).trigger('input');
|
|
109
133
|
const emitted = wrapper.emitted('update:selected')?.[0]?.[0] as unknown[];
|
|
110
134
|
expect(emitted).toHaveLength(3);
|
|
111
135
|
});
|
|
112
136
|
|
|
113
137
|
it('toggleAll deselects all displayed options when all are selected', async () => {
|
|
114
|
-
|
|
138
|
+
wrapper = await mount(OctopusMultiselect, {
|
|
115
139
|
props: { options, optionLabel: 'name', selected: [...options] },
|
|
116
140
|
});
|
|
117
141
|
await wrapper.find('input').trigger('focus');
|
|
118
|
-
const allCheckbox =
|
|
119
|
-
await allCheckbox.trigger('input');
|
|
142
|
+
const allCheckbox = document.body.querySelector('.octopus-multiselect-dropdown > .octopus-form-item input[type="checkbox"]')!;
|
|
143
|
+
await new DOMWrapper(allCheckbox).trigger('input');
|
|
120
144
|
expect(wrapper.emitted('update:selected')?.[0]).toEqual([[]]);
|
|
121
145
|
});
|
|
122
146
|
|
|
123
|
-
it('shows selected items
|
|
124
|
-
|
|
147
|
+
it('shows selected items in the selection display', async () => {
|
|
148
|
+
wrapper = await mount(OctopusMultiselect, {
|
|
125
149
|
props: { options, optionLabel: 'name', selected: [options[0], options[1]] },
|
|
126
150
|
});
|
|
127
|
-
|
|
128
|
-
expect(
|
|
151
|
+
expect(wrapper.find('.octopus-multiselect-selection-text').text()).toBe('Alpha, Beta');
|
|
152
|
+
expect(wrapper.find('.octopus-multiselect-selection-count').exists()).toBe(false);
|
|
129
153
|
});
|
|
130
154
|
|
|
131
|
-
it('shows count
|
|
132
|
-
|
|
155
|
+
it('shows overflow count badge when more than visible items are selected', async () => {
|
|
156
|
+
wrapper = await mount(OctopusMultiselect, {
|
|
133
157
|
props: { options, optionLabel: 'name', selected: [...options] },
|
|
134
158
|
});
|
|
135
|
-
|
|
136
|
-
expect(input.attributes('placeholder')).toBe('Alpha, Beta (+1)');
|
|
159
|
+
expect(wrapper.find('.octopus-multiselect-selection-count').exists()).toBe(true);
|
|
137
160
|
});
|
|
138
161
|
|
|
139
162
|
it('disables input when isDisabled is true', async () => {
|
|
140
|
-
|
|
163
|
+
wrapper = await mount(OctopusMultiselect, {
|
|
141
164
|
props: { options, optionLabel: 'name', isDisabled: true },
|
|
142
165
|
});
|
|
143
166
|
expect(wrapper.find('input').attributes('disabled')).toBeDefined();
|
|
144
167
|
});
|
|
145
168
|
|
|
146
169
|
it('does not open dropdown when disabled', async () => {
|
|
147
|
-
|
|
170
|
+
wrapper = await mount(OctopusMultiselect, {
|
|
148
171
|
props: { options, optionLabel: 'name', isDisabled: true },
|
|
149
172
|
});
|
|
150
173
|
await wrapper.find('input').trigger('focus');
|
|
151
|
-
expect(
|
|
174
|
+
expect(getDropdown()).toBeNull();
|
|
152
175
|
});
|
|
153
176
|
|
|
154
177
|
it('uses selectAllText as the "All" checkbox label when provided', async () => {
|
|
155
|
-
|
|
178
|
+
wrapper = await mount(OctopusMultiselect, {
|
|
156
179
|
props: { options, optionLabel: 'name', selectAllText: 'Tout' },
|
|
157
180
|
});
|
|
158
181
|
await wrapper.find('input').trigger('focus');
|
|
159
|
-
const allCheckboxLabel =
|
|
160
|
-
expect(allCheckboxLabel.
|
|
182
|
+
const allCheckboxLabel = document.body.querySelector('.octopus-multiselect-dropdown > .octopus-form-item label')!;
|
|
183
|
+
expect(allCheckboxLabel.textContent?.trim()).toBe('Tout');
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it('deselects by optionKey even when selected objects are different references', async () => {
|
|
187
|
+
const selected = [{ id: 1, name: 'Alpha' }];
|
|
188
|
+
wrapper = await mount(OctopusMultiselect, {
|
|
189
|
+
props: { options, optionLabel: 'name', optionKey: 'id', selected },
|
|
190
|
+
});
|
|
191
|
+
await wrapper.find('input').trigger('focus');
|
|
192
|
+
const optionCheckboxes = document.body.querySelectorAll('.octopus-multiselect-options input[type="checkbox"]');
|
|
193
|
+
await new DOMWrapper(optionCheckboxes[0] as Element).trigger('input');
|
|
194
|
+
expect(wrapper.emitted('update:selected')?.[0]).toEqual([[]]);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it('toggleAll deselects by optionKey even when selected objects are different references', async () => {
|
|
198
|
+
const selected = options.map((o) => ({ ...o }));
|
|
199
|
+
wrapper = await mount(OctopusMultiselect, {
|
|
200
|
+
props: { options, optionLabel: 'name', optionKey: 'id', selected },
|
|
201
|
+
});
|
|
202
|
+
await wrapper.find('input').trigger('focus');
|
|
203
|
+
const allCheckbox = document.body.querySelector('.octopus-multiselect-dropdown > .octopus-form-item input[type="checkbox"]')!;
|
|
204
|
+
await new DOMWrapper(allCheckbox).trigger('input');
|
|
205
|
+
expect(wrapper.emitted('update:selected')?.[0]).toEqual([[]]);
|
|
161
206
|
});
|
|
162
207
|
});
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import '@tests/mocks/i18n';
|
|
2
|
+
|
|
3
|
+
import OctopusSelect from '@/components/form/OctopusSelect.vue';
|
|
4
|
+
import { DOMWrapper, type VueWrapper } from '@vue/test-utils';
|
|
5
|
+
import { mount } from '@tests/utils';
|
|
6
|
+
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
7
|
+
|
|
8
|
+
const options = [
|
|
9
|
+
{ id: 1, name: 'Alpha' },
|
|
10
|
+
{ id: 2, name: 'Beta' },
|
|
11
|
+
{ id: 3, name: 'Gamma' },
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
// The dropdown is teleported to .octopus-app — helper to query it from the document.
|
|
15
|
+
function getDropdown(): Element | null {
|
|
16
|
+
return document.body.querySelector('.octopus-select-dropdown');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
describe('OctopusSelect', () => {
|
|
20
|
+
// The dropdown teleports to .octopus-app, which is normally the app root (see App.vue).
|
|
21
|
+
// It doesn't exist in the test DOM, so it must be created before mount.
|
|
22
|
+
let appElement: HTMLElement;
|
|
23
|
+
|
|
24
|
+
beforeEach(() => {
|
|
25
|
+
appElement = document.createElement('div');
|
|
26
|
+
appElement.className = 'octopus-app';
|
|
27
|
+
document.body.appendChild(appElement);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// Shared wrapper ref so afterEach can properly unmount it.
|
|
31
|
+
// Proper unmount lets Vue clean up the teleport target before the next test starts.
|
|
32
|
+
let wrapper: VueWrapper;
|
|
33
|
+
afterEach(() => {
|
|
34
|
+
wrapper?.unmount();
|
|
35
|
+
appElement.remove();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('renders label when provided', async () => {
|
|
39
|
+
wrapper = await mount(OctopusSelect, {
|
|
40
|
+
props: { options, optionLabel: 'name', label: 'My label' },
|
|
41
|
+
});
|
|
42
|
+
expect(wrapper.find('label.form-label').text()).toBe('My label');
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('does not render label when not provided', async () => {
|
|
46
|
+
wrapper = await mount(OctopusSelect, {
|
|
47
|
+
props: { options, optionLabel: 'name' },
|
|
48
|
+
});
|
|
49
|
+
expect(wrapper.find('label.form-label').exists()).toBe(false);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('opens dropdown on input focus', async () => {
|
|
53
|
+
wrapper = await mount(OctopusSelect, {
|
|
54
|
+
props: { options, optionLabel: 'name' },
|
|
55
|
+
});
|
|
56
|
+
expect(getDropdown()).toBeNull();
|
|
57
|
+
await wrapper.find('input').trigger('focus');
|
|
58
|
+
expect(getDropdown()).not.toBeNull();
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('shows one button per option when open', async () => {
|
|
62
|
+
wrapper = await mount(OctopusSelect, {
|
|
63
|
+
props: { options, optionLabel: 'name' },
|
|
64
|
+
});
|
|
65
|
+
await wrapper.find('input').trigger('focus');
|
|
66
|
+
const optionButtons = document.body.querySelectorAll('.octopus-select-option');
|
|
67
|
+
expect(optionButtons).toHaveLength(3);
|
|
68
|
+
expect(optionButtons[0].textContent?.trim()).toBe('Alpha');
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('filters options locally by search query', async () => {
|
|
72
|
+
wrapper = await mount(OctopusSelect, {
|
|
73
|
+
props: { options, optionLabel: 'name' },
|
|
74
|
+
});
|
|
75
|
+
await wrapper.find('input').trigger('focus');
|
|
76
|
+
await wrapper.find('input').setValue('al');
|
|
77
|
+
await wrapper.find('input').trigger('input');
|
|
78
|
+
expect(document.body.querySelectorAll('.octopus-select-option')).toHaveLength(1);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('shows no-results message when filter matches nothing', async () => {
|
|
82
|
+
wrapper = await mount(OctopusSelect, {
|
|
83
|
+
props: { options, optionLabel: 'name' },
|
|
84
|
+
});
|
|
85
|
+
await wrapper.find('input').trigger('focus');
|
|
86
|
+
await wrapper.find('input').setValue('zzz');
|
|
87
|
+
await wrapper.find('input').trigger('input');
|
|
88
|
+
expect(document.body.querySelector('.text-indic')).not.toBeNull();
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('emits update:value with the clicked option', async () => {
|
|
92
|
+
wrapper = await mount(OctopusSelect, {
|
|
93
|
+
props: { options, optionLabel: 'name' },
|
|
94
|
+
});
|
|
95
|
+
await wrapper.find('input').trigger('focus');
|
|
96
|
+
await new DOMWrapper(document.body.querySelectorAll('.octopus-select-option')[1]).trigger('click');
|
|
97
|
+
expect(wrapper.emitted('update:value')?.[0]).toEqual([options[1]]);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('closes dropdown after selecting an option', async () => {
|
|
101
|
+
wrapper = await mount(OctopusSelect, {
|
|
102
|
+
props: { options, optionLabel: 'name' },
|
|
103
|
+
});
|
|
104
|
+
await wrapper.find('input').trigger('focus');
|
|
105
|
+
await new DOMWrapper(document.body.querySelectorAll('.octopus-select-option')[0]).trigger('click');
|
|
106
|
+
expect(getDropdown()).toBeNull();
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('shows selected item label in the field when closed', async () => {
|
|
110
|
+
wrapper = await mount(OctopusSelect, {
|
|
111
|
+
props: { options, optionLabel: 'name', value: options[0] },
|
|
112
|
+
});
|
|
113
|
+
expect(wrapper.find('.octopus-select-value').text()).toBe('Alpha');
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('marks the selected option with the selected class', async () => {
|
|
117
|
+
wrapper = await mount(OctopusSelect, {
|
|
118
|
+
props: { options, optionLabel: 'name', value: options[0] },
|
|
119
|
+
});
|
|
120
|
+
await wrapper.find('.octopus-select-field').trigger('click');
|
|
121
|
+
const optionButtons = document.body.querySelectorAll('.octopus-select-option');
|
|
122
|
+
expect(optionButtons[0].classList).toContain('selected');
|
|
123
|
+
expect(optionButtons[1].classList).not.toContain('selected');
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('deselects the current option by default (allowDeselect defaults to true)', async () => {
|
|
127
|
+
wrapper = await mount(OctopusSelect, {
|
|
128
|
+
props: { options, optionLabel: 'name', optionKey: 'id', value: options[0] },
|
|
129
|
+
});
|
|
130
|
+
await wrapper.find('.octopus-select-field').trigger('click');
|
|
131
|
+
await new DOMWrapper(document.body.querySelectorAll('.octopus-select-option')[0]).trigger('click');
|
|
132
|
+
expect(wrapper.emitted('update:value')?.[0]).toEqual([undefined]);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('does not deselect when allowDeselect is false', async () => {
|
|
136
|
+
wrapper = await mount(OctopusSelect, {
|
|
137
|
+
props: { options, optionLabel: 'name', optionKey: 'id', value: options[0], allowDeselect: false as boolean },
|
|
138
|
+
});
|
|
139
|
+
await wrapper.find('.octopus-select-field').trigger('click');
|
|
140
|
+
await new DOMWrapper(document.body.querySelectorAll('.octopus-select-option')[0]).trigger('click');
|
|
141
|
+
expect(wrapper.emitted('update:value')?.[0]).toEqual([options[0]]);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('does not open dropdown when disabled', async () => {
|
|
145
|
+
wrapper = await mount(OctopusSelect, {
|
|
146
|
+
props: { options, optionLabel: 'name', isDisabled: true },
|
|
147
|
+
});
|
|
148
|
+
await wrapper.find('input').trigger('focus');
|
|
149
|
+
expect(getDropdown()).toBeNull();
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it('disables input when isDisabled is true', async () => {
|
|
153
|
+
wrapper = await mount(OctopusSelect, {
|
|
154
|
+
props: { options, optionLabel: 'name', isDisabled: true },
|
|
155
|
+
});
|
|
156
|
+
expect(wrapper.find('input').attributes('disabled')).toBeDefined();
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it('matches by optionKey when checking selection', async () => {
|
|
160
|
+
const value = { id: 1, name: 'Alpha' };
|
|
161
|
+
wrapper = await mount(OctopusSelect, {
|
|
162
|
+
props: { options, optionLabel: 'name', optionKey: 'id', value },
|
|
163
|
+
});
|
|
164
|
+
await wrapper.find('.octopus-select-field').trigger('click');
|
|
165
|
+
const optionButtons = document.body.querySelectorAll('.octopus-select-option');
|
|
166
|
+
expect(optionButtons[0].classList).toContain('selected');
|
|
167
|
+
});
|
|
168
|
+
});
|
package/tsconfig.json
CHANGED
|
@@ -5,15 +5,13 @@
|
|
|
5
5
|
// "strict": true,
|
|
6
6
|
"jsx": "preserve",
|
|
7
7
|
"importHelpers": true,
|
|
8
|
-
"moduleResolution": "
|
|
8
|
+
"moduleResolution": "bundler",
|
|
9
9
|
"experimentalDecorators": true,
|
|
10
10
|
"skipLibCheck": true,
|
|
11
11
|
"esModuleInterop": true,
|
|
12
12
|
"allowSyntheticDefaultImports": true,
|
|
13
13
|
"sourceMap": true,
|
|
14
|
-
"baseUrl": ".",
|
|
15
14
|
"types": [
|
|
16
|
-
"webpack-env",
|
|
17
15
|
"sockjs-client"
|
|
18
16
|
],
|
|
19
17
|
"paths": {
|
|
@@ -21,7 +19,7 @@
|
|
|
21
19
|
"./src/*"
|
|
22
20
|
],
|
|
23
21
|
"@tests/*": [
|
|
24
|
-
"tests/*"
|
|
22
|
+
"./tests/*"
|
|
25
23
|
]
|
|
26
24
|
},
|
|
27
25
|
"lib": [
|
|
@@ -34,9 +32,7 @@
|
|
|
34
32
|
"include": [
|
|
35
33
|
"src/**/*.ts",
|
|
36
34
|
"src/**/*.tsx",
|
|
37
|
-
"src/**/*.vue"
|
|
38
|
-
"tests/**/*.ts",
|
|
39
|
-
"tests/**/*.tsx"
|
|
35
|
+
"src/**/*.vue"
|
|
40
36
|
],
|
|
41
37
|
"exclude": [
|
|
42
38
|
"node_modules"
|
package/vitest.config.js
CHANGED