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
pull/4346/head
John 2021-07-07 21:35:41 +08:00 committed by GitHub
parent 9e0244dfc3
commit 427cf36eaa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 74 additions and 28 deletions

View File

@ -4,6 +4,7 @@ import focusTest from '../../../tests/shared/focusTest';
import { resetWarned } from '../../_util/warning'; import { resetWarned } from '../../_util/warning';
import mountTest from '../../../tests/shared/mountTest'; import mountTest from '../../../tests/shared/mountTest';
import { ref } from 'vue'; import { ref } from 'vue';
import { asyncExpect } from '@/tests/utils';
describe('Switch', () => { describe('Switch', () => {
focusTest(Switch); focusTest(Switch);
@ -42,4 +43,31 @@ describe('Switch', () => {
); );
errorSpy.mockRestore(); errorSpy.mockRestore();
}); });
it('customize checked value should work', async () => {
resetWarned();
const checked = ref(1);
const onUpdate = val => (checked.value = val);
const wrapper = mount({
render() {
return (
<Switch
{...{ 'onUpdate:checked': onUpdate }}
checked={checked.value}
uncheckedValue={1}
checkedValue={2}
/>
);
},
});
await asyncExpect(() => {
wrapper.find('button').trigger('click');
});
expect(checked.value).toBe(2);
await asyncExpect(() => {
wrapper.find('button').trigger('click');
});
expect(checked.value).toBe(1);
});
}); });

View File

@ -1,4 +1,4 @@
import type { ExtractPropTypes } from 'vue'; import type { ExtractPropTypes, PropType } from 'vue';
import { import {
defineComponent, defineComponent,
inject, inject,
@ -19,23 +19,35 @@ import { tuple, withInstall } from '../_util/type';
import { getPropsSlot } from '../_util/props-util'; import { getPropsSlot } from '../_util/props-util';
import Omit from 'omit.js'; import Omit from 'omit.js';
export const SwitchSizes = tuple('small', 'default', 'large'); export const SwitchSizes = tuple('small', 'default');
const switchProps = { const switchProps = {
prefixCls: PropTypes.string, prefixCls: PropTypes.string,
size: PropTypes.oneOf(SwitchSizes), size: PropTypes.oneOf(SwitchSizes),
disabled: PropTypes.looseBool, disabled: PropTypes.looseBool,
checkedChildren: PropTypes.any, checkedChildren: PropTypes.VNodeChild,
unCheckedChildren: PropTypes.any, unCheckedChildren: PropTypes.VNodeChild,
tabindex: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), tabindex: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
autofocus: PropTypes.looseBool, autofocus: PropTypes.looseBool,
loading: PropTypes.looseBool, loading: PropTypes.looseBool,
checked: PropTypes.looseBool, checked: PropTypes.any,
onChange: PropTypes.func, checkedValue: PropTypes.any.def(true),
onClick: PropTypes.func, uncheckedValue: PropTypes.any.def(false),
onKeydown: PropTypes.func, onChange: {
onMouseup: PropTypes.func, type: Function as PropType<(checked: any, e: Event) => void>,
'onUpdate:checked': PropTypes.func, },
onClick: {
type: Function as PropType<(checked: any, e: Event) => void>,
},
onKeydown: {
type: Function as PropType<(e: Event) => void>,
},
onMouseup: {
type: Function as PropType<(e: Event) => void>,
},
'onUpdate:checked': {
type: Function as PropType<(checked: any) => void>,
},
}; };
export type SwitchProps = Partial<ExtractPropTypes<typeof switchProps>>; export type SwitchProps = Partial<ExtractPropTypes<typeof switchProps>>;
@ -46,7 +58,7 @@ const Switch = defineComponent({
inheritAttrs: false, inheritAttrs: false,
props: switchProps, props: switchProps,
emits: ['update:checked', 'mouseup', 'change', 'click', 'keydown'], emits: ['update:checked', 'mouseup', 'change', 'click', 'keydown'],
setup(props: SwitchProps, { attrs, slots, expose, emit }) { setup(props, { attrs, slots, expose, emit }) {
onBeforeMount(() => { onBeforeMount(() => {
warning( warning(
!('defaultChecked' in attrs), !('defaultChecked' in attrs),
@ -59,12 +71,13 @@ const Switch = defineComponent({
'`value` is not validate prop, do you mean `checked`?', '`value` is not validate prop, do you mean `checked`?',
); );
}); });
const checked = ref(props.checked !== undefined ? !!props.checked : !!attrs.defaultChecked); const checked = ref(props.checked !== undefined ? props.checked : attrs.defaultChecked);
const checkedStatus = computed(() => checked.value === props.checkedValue);
watch( watch(
() => props.checked, () => props.checked,
() => { () => {
checked.value = !!props.checked; checked.value = props.checked;
}, },
); );
@ -92,29 +105,26 @@ const Switch = defineComponent({
}); });
}); });
const setChecked = (check: boolean, e: MouseEvent | KeyboardEvent) => { const setChecked = (check: any, e: MouseEvent | KeyboardEvent) => {
if (props.disabled) { if (props.disabled) {
return; return;
} }
if (props.checked === undefined) {
checked.value = check;
}
emit('update:checked', check); emit('update:checked', check);
emit('change', check, e); emit('change', check, e);
}; };
const handleClick = (e: MouseEvent) => { const handleClick = (e: MouseEvent) => {
focus(); focus();
const newChecked = !checked.value; const newChecked = checkedStatus.value ? props.uncheckedValue : props.checkedValue;
setChecked(newChecked, e); setChecked(newChecked, e);
emit('click', newChecked, e); emit('click', newChecked, e);
}; };
const handleKeyDown = (e: KeyboardEvent) => { const handleKeyDown = (e: KeyboardEvent) => {
if (e.keyCode === KeyCode.LEFT) { if (e.keyCode === KeyCode.LEFT) {
setChecked(false, e); setChecked(props.uncheckedValue, e);
} else if (e.keyCode === KeyCode.RIGHT) { } else if (e.keyCode === KeyCode.RIGHT) {
setChecked(true, e); setChecked(props.checkedValue, e);
} }
emit('keydown', e); emit('keydown', e);
}; };
@ -123,6 +133,13 @@ const Switch = defineComponent({
refSwitchNode.value?.blur(); refSwitchNode.value?.blur();
emit('mouseup', e); emit('mouseup', e);
}; };
const classNames = computed(() => ({
[`${prefixCls.value}-small`]: props.size === 'small',
[`${prefixCls.value}-loading`]: props.loading,
[`${prefixCls.value}-checked`]: checkedStatus.value,
[`${prefixCls.value}-disabled`]: props.disabled,
}));
return () => ( return () => (
<Wave insertExtraNode> <Wave insertExtraNode>
<button <button
@ -133,6 +150,8 @@ const Switch = defineComponent({
'checked', 'checked',
'autofocus', 'autofocus',
'defaultChecked', 'defaultChecked',
'checkedValue',
'uncheckedValue',
])} ])}
{...attrs} {...attrs}
onKeydown={handleKeyDown} onKeydown={handleKeyDown}
@ -142,14 +161,13 @@ const Switch = defineComponent({
role="switch" role="switch"
aria-checked={checked.value} aria-checked={checked.value}
disabled={props.disabled || props.loading} disabled={props.disabled || props.loading}
class={{ class={[
[attrs.class as string]: attrs.class, {
[prefixCls.value]: true, [attrs.class as string]: !!attrs.class,
[`${prefixCls.value}-small`]: props.size === 'small', [prefixCls.value]: true,
[`${prefixCls.value}-loading`]: props.loading, },
[`${prefixCls.value}-checked`]: checked.value, classNames.value,
[`${prefixCls.value}-disabled`]: props.disabled, ]}
}}
ref={refSwitchNode} ref={refSwitchNode}
> >
{props.loading ? <LoadingOutlined class={`${prefixCls.value}-loading-icon`} /> : null} {props.loading ? <LoadingOutlined class={`${prefixCls.value}-loading-icon`} /> : null}