Skip to content

Commit 427cf36

Browse files
authored
refactor(switch): support customize checked value #4329 (#4332)
* refactor(switch): support customize checked value #4329 * test: add test case * refactor: update props name * refactor: update ts * refactor: optimize
1 parent 9e0244d commit 427cf36

File tree

2 files changed

+74
-28
lines changed

2 files changed

+74
-28
lines changed

components/switch/__tests__/index.test.js

+28
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import focusTest from '../../../tests/shared/focusTest';
44
import { resetWarned } from '../../_util/warning';
55
import mountTest from '../../../tests/shared/mountTest';
66
import { ref } from 'vue';
7+
import { asyncExpect } from '@/tests/utils';
78

89
describe('Switch', () => {
910
focusTest(Switch);
@@ -42,4 +43,31 @@ describe('Switch', () => {
4243
);
4344
errorSpy.mockRestore();
4445
});
46+
47+
it('customize checked value should work', async () => {
48+
resetWarned();
49+
const checked = ref(1);
50+
const onUpdate = val => (checked.value = val);
51+
const wrapper = mount({
52+
render() {
53+
return (
54+
<Switch
55+
{...{ 'onUpdate:checked': onUpdate }}
56+
checked={checked.value}
57+
uncheckedValue={1}
58+
checkedValue={2}
59+
/>
60+
);
61+
},
62+
});
63+
await asyncExpect(() => {
64+
wrapper.find('button').trigger('click');
65+
});
66+
expect(checked.value).toBe(2);
67+
68+
await asyncExpect(() => {
69+
wrapper.find('button').trigger('click');
70+
});
71+
expect(checked.value).toBe(1);
72+
});
4573
});

components/switch/index.tsx

+46-28
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { ExtractPropTypes } from 'vue';
1+
import type { ExtractPropTypes, PropType } from 'vue';
22
import {
33
defineComponent,
44
inject,
@@ -19,23 +19,35 @@ import { tuple, withInstall } from '../_util/type';
1919
import { getPropsSlot } from '../_util/props-util';
2020
import Omit from 'omit.js';
2121

22-
export const SwitchSizes = tuple('small', 'default', 'large');
22+
export const SwitchSizes = tuple('small', 'default');
2323

2424
const switchProps = {
2525
prefixCls: PropTypes.string,
2626
size: PropTypes.oneOf(SwitchSizes),
2727
disabled: PropTypes.looseBool,
28-
checkedChildren: PropTypes.any,
29-
unCheckedChildren: PropTypes.any,
28+
checkedChildren: PropTypes.VNodeChild,
29+
unCheckedChildren: PropTypes.VNodeChild,
3030
tabindex: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
3131
autofocus: PropTypes.looseBool,
3232
loading: PropTypes.looseBool,
33-
checked: PropTypes.looseBool,
34-
onChange: PropTypes.func,
35-
onClick: PropTypes.func,
36-
onKeydown: PropTypes.func,
37-
onMouseup: PropTypes.func,
38-
'onUpdate:checked': PropTypes.func,
33+
checked: PropTypes.any,
34+
checkedValue: PropTypes.any.def(true),
35+
uncheckedValue: PropTypes.any.def(false),
36+
onChange: {
37+
type: Function as PropType<(checked: any, e: Event) => void>,
38+
},
39+
onClick: {
40+
type: Function as PropType<(checked: any, e: Event) => void>,
41+
},
42+
onKeydown: {
43+
type: Function as PropType<(e: Event) => void>,
44+
},
45+
onMouseup: {
46+
type: Function as PropType<(e: Event) => void>,
47+
},
48+
'onUpdate:checked': {
49+
type: Function as PropType<(checked: any) => void>,
50+
},
3951
};
4052

4153
export type SwitchProps = Partial<ExtractPropTypes<typeof switchProps>>;
@@ -46,7 +58,7 @@ const Switch = defineComponent({
4658
inheritAttrs: false,
4759
props: switchProps,
4860
emits: ['update:checked', 'mouseup', 'change', 'click', 'keydown'],
49-
setup(props: SwitchProps, { attrs, slots, expose, emit }) {
61+
setup(props, { attrs, slots, expose, emit }) {
5062
onBeforeMount(() => {
5163
warning(
5264
!('defaultChecked' in attrs),
@@ -59,12 +71,13 @@ const Switch = defineComponent({
5971
'`value` is not validate prop, do you mean `checked`?',
6072
);
6173
});
62-
const checked = ref(props.checked !== undefined ? !!props.checked : !!attrs.defaultChecked);
74+
const checked = ref(props.checked !== undefined ? props.checked : attrs.defaultChecked);
75+
const checkedStatus = computed(() => checked.value === props.checkedValue);
6376

6477
watch(
6578
() => props.checked,
6679
() => {
67-
checked.value = !!props.checked;
80+
checked.value = props.checked;
6881
},
6982
);
7083

@@ -92,29 +105,26 @@ const Switch = defineComponent({
92105
});
93106
});
94107

95-
const setChecked = (check: boolean, e: MouseEvent | KeyboardEvent) => {
108+
const setChecked = (check: any, e: MouseEvent | KeyboardEvent) => {
96109
if (props.disabled) {
97110
return;
98111
}
99-
if (props.checked === undefined) {
100-
checked.value = check;
101-
}
102112
emit('update:checked', check);
103113
emit('change', check, e);
104114
};
105115

106116
const handleClick = (e: MouseEvent) => {
107117
focus();
108-
const newChecked = !checked.value;
118+
const newChecked = checkedStatus.value ? props.uncheckedValue : props.checkedValue;
109119
setChecked(newChecked, e);
110120
emit('click', newChecked, e);
111121
};
112122

113123
const handleKeyDown = (e: KeyboardEvent) => {
114124
if (e.keyCode === KeyCode.LEFT) {
115-
setChecked(false, e);
125+
setChecked(props.uncheckedValue, e);
116126
} else if (e.keyCode === KeyCode.RIGHT) {
117-
setChecked(true, e);
127+
setChecked(props.checkedValue, e);
118128
}
119129
emit('keydown', e);
120130
};
@@ -123,6 +133,13 @@ const Switch = defineComponent({
123133
refSwitchNode.value?.blur();
124134
emit('mouseup', e);
125135
};
136+
137+
const classNames = computed(() => ({
138+
[`${prefixCls.value}-small`]: props.size === 'small',
139+
[`${prefixCls.value}-loading`]: props.loading,
140+
[`${prefixCls.value}-checked`]: checkedStatus.value,
141+
[`${prefixCls.value}-disabled`]: props.disabled,
142+
}));
126143
return () => (
127144
<Wave insertExtraNode>
128145
<button
@@ -133,6 +150,8 @@ const Switch = defineComponent({
133150
'checked',
134151
'autofocus',
135152
'defaultChecked',
153+
'checkedValue',
154+
'uncheckedValue',
136155
])}
137156
{...attrs}
138157
onKeydown={handleKeyDown}
@@ -142,14 +161,13 @@ const Switch = defineComponent({
142161
role="switch"
143162
aria-checked={checked.value}
144163
disabled={props.disabled || props.loading}
145-
class={{
146-
[attrs.class as string]: attrs.class,
147-
[prefixCls.value]: true,
148-
[`${prefixCls.value}-small`]: props.size === 'small',
149-
[`${prefixCls.value}-loading`]: props.loading,
150-
[`${prefixCls.value}-checked`]: checked.value,
151-
[`${prefixCls.value}-disabled`]: props.disabled,
152-
}}
164+
class={[
165+
{
166+
[attrs.class as string]: !!attrs.class,
167+
[prefixCls.value]: true,
168+
},
169+
classNames.value,
170+
]}
153171
ref={refSwitchNode}
154172
>
155173
{props.loading ? <LoadingOutlined class={`${prefixCls.value}-loading-icon`} /> : null}

0 commit comments

Comments
 (0)