Skip to content

Commit dcf5025

Browse files
chenxi24kagol
authored andcommitted
refactor: Refactor code and component structure
1 parent 0e9799b commit dcf5025

26 files changed

+2001
-1038
lines changed
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
import { reactive, ref, nextTick } from 'vue';
2+
import { mount } from '@vue/test-utils';
3+
import EditableSelect from '../src/editable-select';
4+
import { useNamespace } from '../../shared/hooks/use-namespace';
5+
6+
const ns = useNamespace('editable-select', true);
7+
const inputClasses = useNamespace('editable-select-input', true);
8+
9+
const createData = (len = 5) => {
10+
return reactive(
11+
Array.from({ length: len }).map((_, index) => {
12+
return {
13+
label: `Option${index}`,
14+
value: index,
15+
};
16+
})
17+
);
18+
};
19+
20+
jest.mock('../../locale/create', () => ({
21+
createI18nTranslate: () => jest.fn(),
22+
}));
23+
24+
describe('Basic EditableSelect', () => {
25+
it('Should render basic editable select correctly.', async () => {
26+
const wrapper = mount({
27+
setup() {
28+
return () => <EditableSelect />;
29+
},
30+
});
31+
await nextTick();
32+
33+
expect(wrapper.find(ns.b()).exists()).toBeTruthy();
34+
expect(wrapper.find(ns.e('arrow-icon')).isVisible()).toBeTruthy();
35+
expect(wrapper.find(ns.e('clear-icon')).isVisible()).toBeFalsy();
36+
expect(wrapper.find(ns.e('dropdown')).exists()).toBeFalsy();
37+
38+
const input = wrapper.find(inputClasses.e('inner'));
39+
expect(input.attributes('placeholder')).toBe('Select');
40+
await wrapper.trigger('click');
41+
42+
expect(wrapper.find(inputClasses.b()).classes()).toContain(inputClasses.m('open').slice(1));
43+
expect(wrapper.find(ns.e('dropdown')).exists()).toBeTruthy();
44+
45+
wrapper.unmount();
46+
});
47+
48+
it('Should render correct number of item and item content.', async () => {
49+
const data = createData();
50+
const wrapper = mount({
51+
setup() {
52+
return () => <EditableSelect options={data} />;
53+
},
54+
});
55+
56+
await nextTick();
57+
58+
await wrapper.trigger('click');
59+
60+
const options = wrapper.findAll(ns.e('item'));
61+
expect(options).toHaveLength(data.length);
62+
63+
const res = options.every((option, index) => {
64+
return option.text() === data[index].label;
65+
});
66+
67+
expect(res).toBeTruthy();
68+
wrapper.unmount();
69+
});
70+
71+
it('Should render correct default value.', async () => {
72+
const wrapper = mount({
73+
setup() {
74+
const value = ref(4);
75+
const data = createData();
76+
return () => {
77+
return <EditableSelect v-model={value.value} options={data} />;
78+
};
79+
},
80+
});
81+
82+
await nextTick();
83+
expect((wrapper.find(inputClasses.e('inner')).element as HTMLInputElement).value).toBe('Option4');
84+
});
85+
86+
it('The dropdown item should be highlighted when clicked.', async () => {
87+
const wrapper = mount({
88+
setup() {
89+
const data = createData();
90+
const value = ref(2);
91+
return () => {
92+
return <EditableSelect v-model={value.value} options={data} />;
93+
};
94+
},
95+
});
96+
await nextTick();
97+
98+
await wrapper.trigger('click');
99+
await wrapper.findAll(ns.e('item'))[1].trigger('click');
100+
await wrapper.trigger('click');
101+
102+
expect(wrapper.findAll(ns.e('item'))[1].classes()).toContain(ns.em('item', 'selected').slice(1));
103+
expect((wrapper.find(inputClasses.e('inner')).element as HTMLInputElement).value).toBe('Option1');
104+
105+
wrapper.unmount();
106+
});
107+
108+
it('keyboard operations', async () => {
109+
const wrapper = mount({
110+
setup() {
111+
const data = createData();
112+
const value = ref(1);
113+
return () => {
114+
return <EditableSelect v-model={value.value} options={data} />;
115+
};
116+
},
117+
});
118+
119+
await nextTick();
120+
121+
await wrapper.trigger('click');
122+
const input = wrapper.find(inputClasses.e('inner'));
123+
await input.element.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' }));
124+
expect(wrapper.findAll(ns.e('item'))[2].classes()).toContain(ns.em('item', 'hover').slice(1));
125+
await input.element.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp' }));
126+
expect(wrapper.findAll(ns.e('item'))[1].classes()).toContain(ns.em('item', 'hover').slice(1));
127+
128+
await input.element.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter' }));
129+
expect(wrapper.find('input').element.value).toBe('Option1');
130+
131+
await input.element.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' }));
132+
expect(wrapper.findComponent({ name: 'DEditableSelectDropdown' }).exists()).toBeFalsy();
133+
await input.element.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' }));
134+
expect(wrapper.findComponent({ name: 'DEditableSelectDropdown' }).exists()).toBeTruthy();
135+
136+
await input.element.dispatchEvent(new KeyboardEvent('keydown', { key: 'Tab' }));
137+
expect(wrapper.findComponent({ name: 'DEditableSelectDropdown' }).exists()).toBeFalsy();
138+
139+
wrapper.unmount();
140+
});
141+
});
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { reactive, ref, nextTick } from 'vue';
2+
import { mount } from '@vue/test-utils';
3+
import EditableSelect from '../src/editable-select';
4+
import { useNamespace } from '../../shared/hooks/use-namespace';
5+
6+
const ns = useNamespace('editable-select', true);
7+
const inputClasses = useNamespace('editable-select-input', true);
8+
9+
const createData = (len = 5) => {
10+
return reactive(
11+
Array.from({ length: len }).map((_, index) => {
12+
return {
13+
label: `Option${index}`,
14+
value: index,
15+
};
16+
})
17+
);
18+
};
19+
20+
describe('Clearable EditableSelect', () => {
21+
it('editable select clear work ', async () => {
22+
const handleClear = jest.fn();
23+
const wrapper = mount({
24+
setup() {
25+
const value = ref(1);
26+
const data = createData();
27+
return () => {
28+
return <EditableSelect v-model={value.value} options={data} allow-clear onClear={handleClear} />;
29+
};
30+
},
31+
});
32+
33+
await nextTick();
34+
expect(wrapper.find(ns.e('arrow-icon')).isVisible()).toBeTruthy();
35+
expect(wrapper.find(ns.e('clear-icon')).isVisible()).toBeFalsy();
36+
37+
await wrapper.find(inputClasses.b()).trigger('mouseenter');
38+
39+
expect(wrapper.find(ns.e('clear-icon')).isVisible()).toBeTruthy();
40+
expect(wrapper.find(ns.e('arrow-icon')).isVisible()).toBeFalsy();
41+
42+
expect((wrapper.find(inputClasses.e('inner')).element as HTMLInputElement).value).toBe('Option1');
43+
await wrapper.find(ns.e('clear-icon')).trigger('click');
44+
expect(handleClear).toBeCalled();
45+
46+
expect((wrapper.find(inputClasses.e('inner')).element as HTMLInputElement).value).toBe('');
47+
48+
await wrapper.trigger('click');
49+
await wrapper.findAll(ns.e('item'))[1].trigger('click');
50+
51+
expect((wrapper.find(inputClasses.e('inner')).element as HTMLInputElement).value).toBe('Option1');
52+
await wrapper.find(inputClasses.b()).trigger('mouseleave');
53+
54+
expect(wrapper.find(ns.e('arrow-icon')).isVisible()).toBeTruthy();
55+
expect(wrapper.find(ns.e('clear-icon')).isVisible()).toBeFalsy();
56+
57+
wrapper.unmount();
58+
});
59+
});
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import { reactive, ref, nextTick } from 'vue';
2+
import { mount } from '@vue/test-utils';
3+
import EditableSelect from '../src/editable-select';
4+
import { useNamespace } from '../../shared/hooks/use-namespace';
5+
import { Option } from '../src/editable-select-types';
6+
7+
interface SlotPropsType {
8+
option: Option;
9+
index: number;
10+
}
11+
12+
const ns = useNamespace('editable-select', true);
13+
const inputClasses = useNamespace('editable-select-input', true);
14+
15+
const createData = (len = 5) => {
16+
return reactive(
17+
Array.from({ length: len }).map((_, index) => {
18+
return {
19+
label: `Option${index}`,
20+
value: index,
21+
};
22+
})
23+
);
24+
};
25+
26+
describe('Custom EditableSelect', () => {
27+
it('custom editable select item renderer', async () => {
28+
const wrapper = mount({
29+
setup() {
30+
const value = ref();
31+
const data = createData();
32+
return () => {
33+
return (
34+
<EditableSelect v-model={value.value} options={data}>
35+
{{
36+
item: (slotProps: SlotPropsType) => {
37+
return <div>{`第${slotProps.index}项:${slotProps.option.label}`}</div>;
38+
},
39+
}}
40+
</EditableSelect>
41+
);
42+
};
43+
},
44+
});
45+
46+
await nextTick();
47+
await wrapper.trigger('click');
48+
await wrapper.findAll(ns.e('item'))[0].trigger('click');
49+
expect((wrapper.find(inputClasses.e('inner')).element as HTMLInputElement).value).toBe('Option0');
50+
wrapper.unmount();
51+
});
52+
53+
it('custom editable select no result item renderer.', async () => {
54+
const wrapper = mount({
55+
setup() {
56+
const value = ref('option1');
57+
return () => {
58+
return (
59+
<EditableSelect v-model={value.value}>
60+
{{
61+
noResultItem: () => {
62+
{
63+
return <div>{`没有匹配项`}</div>;
64+
}
65+
},
66+
}}
67+
</EditableSelect>
68+
);
69+
};
70+
},
71+
});
72+
73+
await nextTick();
74+
await wrapper.trigger('click');
75+
expect(wrapper.find(ns.em('item', 'no-data-tip')).text()).toContain('没有匹配项');
76+
wrapper.unmount();
77+
});
78+
});
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import { reactive, ref } from 'vue';
2+
import { mount } from '@vue/test-utils';
3+
import EditableSelect from '../src/editable-select';
4+
import { useNamespace } from '../../shared/hooks/use-namespace';
5+
6+
const ns = useNamespace('editable-select', true);
7+
const inputClasses = useNamespace('editable-select-input', true);
8+
9+
const createData = (len = 5) => {
10+
return reactive(
11+
Array.from({ length: len }).map((_, index) => {
12+
return {
13+
label: `Option${index}`,
14+
value: index,
15+
};
16+
})
17+
);
18+
};
19+
20+
describe('Disabled EditableSelect', () => {
21+
it('disabled editable select', () => {
22+
const wrapper = mount({
23+
setup() {
24+
const value = ref(4);
25+
const data = createData();
26+
return () => {
27+
return <EditableSelect v-model={value.value} options={data} disabled />;
28+
};
29+
},
30+
});
31+
32+
expect((wrapper.find(inputClasses.e('inner')).element as HTMLInputElement).disabled).toBeTruthy();
33+
wrapper.unmount();
34+
});
35+
36+
it('disabled option', async () => {
37+
const wrapper = mount({
38+
setup() {
39+
const value = ref('label2');
40+
const data = reactive([
41+
{
42+
label: 'label0',
43+
value: 'label0',
44+
},
45+
{
46+
label: 'label1',
47+
value: 'label1',
48+
disabled: true,
49+
},
50+
{
51+
label: 'label2',
52+
value: 'label2',
53+
},
54+
]);
55+
return () => {
56+
return <EditableSelect v-model={value.value} options={data} disabled-key="disabled" />;
57+
};
58+
},
59+
});
60+
61+
await wrapper.trigger('click');
62+
63+
const options = wrapper.findAll(ns.e('item'));
64+
expect(options[1].classes()).toContain(ns.em('item', 'disabled').slice(1));
65+
66+
await options[0].trigger('click');
67+
68+
expect((wrapper.find(inputClasses.e('inner')).element as HTMLInputElement).value).toBe('label0');
69+
70+
await options[1].trigger('click');
71+
72+
expect((wrapper.find(inputClasses.e('inner')).element as HTMLInputElement).value).toBe('label0');
73+
wrapper.unmount();
74+
});
75+
});

0 commit comments

Comments
 (0)