diff --git a/src/packages/switch/__test__/switch.spec.tsx b/src/packages/switch/__test__/switch.spec.tsx index 37d1f0b62f..094ee8f66d 100644 --- a/src/packages/switch/__test__/switch.spec.tsx +++ b/src/packages/switch/__test__/switch.spec.tsx @@ -1,6 +1,7 @@ import * as React from 'react' import { render, fireEvent, waitFor, act } from '@testing-library/react' import '@testing-library/jest-dom' +import { Loading1 } from '@nutui/icons-react' import { Switch } from '../switch' test('activeText && checked && onChange && inactiveText && className && style test', async () => { @@ -49,8 +50,32 @@ test('activeText && checked && onChange && inactiveText && className && style te } }) -test('disabled test', () => { - render() +test('disabled test', async () => { + const { container } = render() const el = document.getElementsByClassName('nut-switch-disabled') expect(el.length > 0).toBe(true) + const buttonEl: Element | null = container.querySelector('.nut-switch-button') + if (buttonEl) { + await act(() => { + fireEvent.click(buttonEl) + }) + } +}) + +test('loadingIcon test', async () => { + const testFn = vi.fn() + const { container } = render( + } onChange={testFn} /> + ) + const el: Element | null = container.querySelector('.nut-switch-button') + if (el) { + await act(() => { + fireEvent.click(el) + }) + waitFor(() => { + // 异步 + const el = document.getElementsByClassName('.nut-icon') + expect(el.length > 0).toBe(true) + }) + } }) diff --git a/src/packages/switch/demos/h5/demo2.tsx b/src/packages/switch/demos/h5/demo2.tsx index faf4eaeb7f..973eee37ec 100644 --- a/src/packages/switch/demos/h5/demo2.tsx +++ b/src/packages/switch/demos/h5/demo2.tsx @@ -4,17 +4,24 @@ import { Cell, Switch, Toast } from '@nutui/nutui-react' const Demo2 = () => { const [checkedAsync, setCheckedAsync] = useState(true) - const onChangeAsync = (value: boolean, event: any) => { + const mockRequest = (): Promise => { + return new Promise((resolve) => { + setTimeout(() => { + resolve() + }, 2000) + }) + } + + const onChangeAsync = async (value: boolean) => { Toast.show(`2秒后异步触发 ${value}`) - setTimeout(() => { - setCheckedAsync(value) - }, 2000) + await mockRequest() + setCheckedAsync(value) } return ( onChangeAsync(value, event)} + onChange={(value) => onChangeAsync(value)} /> ) diff --git a/src/packages/switch/demos/taro/demo2.tsx b/src/packages/switch/demos/taro/demo2.tsx index 33f793cd7a..58214bb5fe 100644 --- a/src/packages/switch/demos/taro/demo2.tsx +++ b/src/packages/switch/demos/taro/demo2.tsx @@ -5,20 +5,26 @@ const Demo2 = () => { const [checkedAsync, setCheckedAsync] = useState(true) const [value, setValue] = useState(false) const [showToast, setShowToast] = useState(false) + const mockRequest = (): Promise => { + return new Promise((resolve) => { + setTimeout(() => { + resolve() + }, 2000) + }) + } - const onChangeAsync = (value: boolean, event: any) => { + const onChangeAsync = async (value: boolean) => { setValue(value) setShowToast(true) - setTimeout(() => { - setCheckedAsync(value) - }, 2000) + await mockRequest() + setCheckedAsync(value) } return ( <> onChangeAsync(value, event)} + onChange={(value) => onChangeAsync(value)} /> { const [value, setValue] = useState(false) const [showToast, setShowToast] = useState(false) - const onChange = ( - value: boolean, - event: React.MouseEvent - ) => { + const onChange = (value: boolean) => { setValue(value) setShowToast(true) } return ( <> - onChange(value, event)} - /> + onChange(value)} /> ` | | onChange | Trigger when switching switches | `onChange:(value: boolean, event: Event)` | `-` | ## Theming diff --git a/src/packages/switch/doc.md b/src/packages/switch/doc.md index 35f78c57b1..d699265bd2 100644 --- a/src/packages/switch/doc.md +++ b/src/packages/switch/doc.md @@ -77,6 +77,7 @@ import { Switch } from '@nutui/nutui-react' | disabled | 禁用状态 | `boolean` | `false` | | activeText | 打开时文字描述 | `ReactNode` | `-` | | inactiveText | 关闭时文字描述 | `ReactNode` | `-` | +| loadingIcon | 受控 loading 态图标 | `ReactNode` | `` | | onChange | 切换开关时触发 | `onChange:(value: boolean, event: Event)` | `-` | ## 主题定制 diff --git a/src/packages/switch/doc.taro.md b/src/packages/switch/doc.taro.md index 5ff506aa74..77a4646b2f 100644 --- a/src/packages/switch/doc.taro.md +++ b/src/packages/switch/doc.taro.md @@ -77,6 +77,7 @@ import { Switch } from '@nutui/nutui-react-taro' | disabled | 禁用状态 | `boolean` | `false` | | activeText | 打开时文字描述 | `ReactNode` | `-` | | inactiveText | 关闭时文字描述 | `ReactNode` | `-` | +| loadingIcon | 受控 loading 态图标 | `ReactNode` | `` | | onChange | 切换开关时触发 | `onChange:(value: boolean, event: Event)` | `-` | ## 主题定制 diff --git a/src/packages/switch/doc.zh-TW.md b/src/packages/switch/doc.zh-TW.md index e6ccd1f303..0f12e4205a 100644 --- a/src/packages/switch/doc.zh-TW.md +++ b/src/packages/switch/doc.zh-TW.md @@ -77,6 +77,7 @@ import { Switch } from '@nutui/nutui-react' | disabled | 禁用狀態 | `boolean` | `false` | | activeText | 打開時文字描述 | `ReactNode` | `-` | | inactiveText | 關閉時文字描述 | `ReactNode` | `-` | +| loadingIcon | 受控 loading 態圖標 | `ReactNode` | `` | | onChange | 切換開關時觸發 | `onChange:(value: boolean, event: Event)` | `-` | ## 主題定製 diff --git a/src/packages/switch/switch.scss b/src/packages/switch/switch.scss index 2a6a174faa..687c8a0eb1 100644 --- a/src/packages/switch/switch.scss +++ b/src/packages/switch/switch.scss @@ -40,6 +40,11 @@ left: calc(100% - $switch-height + $switch-border-width); } } + .nut-icon { + width: calc(($switch-height - $switch-border-width * 2) / 2); + height: calc(($switch-height - $switch-border-width * 2) / 2); + color: $switch-active-disabled-background-color; + } } &-close { diff --git a/src/packages/switch/switch.taro.tsx b/src/packages/switch/switch.taro.tsx index 6a22f4b38b..a8883ec65e 100644 --- a/src/packages/switch/switch.taro.tsx +++ b/src/packages/switch/switch.taro.tsx @@ -1,7 +1,8 @@ -import React, { FunctionComponent } from 'react' +import React, { FunctionComponent, useEffect, useState } from 'react' -import { ITouchEvent, View } from '@tarojs/components' +import { View } from '@tarojs/components' import classNames from 'classnames' +import { Loading1 } from '@nutui/icons-react-taro' import { ComponentDefaults } from '@/utils/typings' import { usePropsValue } from '@/hooks/use-props-value' import { useRtl } from '@/packages/configprovider/index.taro' @@ -12,6 +13,7 @@ const defaultProps = { disabled: false, activeText: '', inactiveText: '', + loadingIcon: , } as TaroSwitchProps export const Switch: FunctionComponent> = (props) => { const { @@ -20,6 +22,7 @@ export const Switch: FunctionComponent> = (props) => { disabled, activeText, inactiveText, + loadingIcon, className, style, onChange, @@ -37,6 +40,12 @@ export const Switch: FunctionComponent> = (props) => { defaultValue: defaultChecked, }) + useEffect(() => { + changing && setChanging(false) + }, [value]) + + const [changing, setChanging] = useState(false) + const classes = () => { return classNames([ classPrefix, @@ -49,20 +58,16 @@ export const Switch: FunctionComponent> = (props) => { ]) } - const onClick = ( - event: React.MouseEvent | ITouchEvent - ) => { - if (disabled) return - onChange && onChange(!value, event) + const onClick = () => { + if (disabled || changing) return + if (props.onChange) { + setChanging(true) + props.onChange(!value) + } setValue(!value) } return ( - onClick(e)} - style={style} - {...rest} - > + > = (props) => { }, ])} > - {!value && !activeText && ( - + {changing && loadingIcon ? ( + <>{loadingIcon} + ) : ( + <> + {!value && !activeText && ( + + )} + )} {activeText && ( diff --git a/src/packages/switch/switch.tsx b/src/packages/switch/switch.tsx index d252b294a9..08e736eecf 100644 --- a/src/packages/switch/switch.tsx +++ b/src/packages/switch/switch.tsx @@ -1,5 +1,6 @@ -import React, { FunctionComponent } from 'react' +import React, { FunctionComponent, useEffect, useState } from 'react' import classNames from 'classnames' +import { Loading1 } from '@nutui/icons-react' import { ComponentDefaults } from '@/utils/typings' import { usePropsValue } from '@/hooks/use-props-value' import { useRtl } from '@/packages/configprovider' @@ -10,6 +11,7 @@ const defaultProps = { disabled: false, activeText: '', inactiveText: '', + loadingIcon: , } as WebSwitchProps export const Switch: FunctionComponent> = (props) => { const { @@ -18,6 +20,7 @@ export const Switch: FunctionComponent> = (props) => { disabled, activeText, inactiveText, + loadingIcon, className, style, onChange, @@ -35,6 +38,12 @@ export const Switch: FunctionComponent> = (props) => { defaultValue: defaultChecked, }) + useEffect(() => { + changing && setChanging(false) + }, [value]) + + const [changing, setChanging] = useState(false) + const classes = () => { return classNames([ classPrefix, @@ -47,18 +56,16 @@ export const Switch: FunctionComponent> = (props) => { ]) } - const onClick = (event: React.MouseEvent) => { - if (disabled) return - onChange && onChange(!value, event) + const onClick = () => { + if (disabled || changing) return + if (props.onChange) { + setChanging(true) + props.onChange(!value) + } setValue(!value) } return ( -
onClick(e)} - style={style} - {...rest} - > +
> = (props) => { }, ])} > - {!value && !activeText && ( -
+ {changing && loadingIcon ? ( + <>{loadingIcon} + ) : ( + <> + {!value && !activeText && ( +
+ )} + )}
{activeText && ( diff --git a/src/sites/sites-react/doc/docs/react/migrate-from-v2.md b/src/sites/sites-react/doc/docs/react/migrate-from-v2.md index a9cdf02f2f..c83d7d3743 100644 --- a/src/sites/sites-react/doc/docs/react/migrate-from-v2.md +++ b/src/sites/sites-react/doc/docs/react/migrate-from-v2.md @@ -225,6 +225,7 @@ plugins: [ - `activeText` 属性类型更改为`ReactNode` - `inactiveText` 属性类型更改为 `ReactNode` +- 新增 `loadingIcon` 属性,受控 loading 态图标 [//]: # '#### Toast' diff --git a/src/sites/sites-react/doc/docs/taro/migrate-from-v2.md b/src/sites/sites-react/doc/docs/taro/migrate-from-v2.md index 4d673d5814..7211a296e3 100644 --- a/src/sites/sites-react/doc/docs/taro/migrate-from-v2.md +++ b/src/sites/sites-react/doc/docs/taro/migrate-from-v2.md @@ -228,6 +228,7 @@ plugins: [ - `activeText` 属性类型更改为`ReactNode` - `inactiveText` 属性类型更改为 `ReactNode` +- 新增 `loadingIcon` 属性,受控 loading 态图标 [//]: # '#### Toast' diff --git a/src/types/spec/switch/base.ts b/src/types/spec/switch/base.ts index 4ae77ee4a9..156821089c 100644 --- a/src/types/spec/switch/base.ts +++ b/src/types/spec/switch/base.ts @@ -1,11 +1,12 @@ import { ReactNode } from 'react' import { BaseProps } from '../../base/props' -export interface BaseSwitch extends BaseProps { +export interface BaseSwitch extends BaseProps { checked: boolean defaultChecked: boolean disabled: boolean activeText: ReactNode inactiveText: ReactNode - onChange: (val: boolean, event: EVENT) => void + loadingIcon: ReactNode + onChange: (val: boolean) => void } diff --git a/src/types/spec/switch/h5.ts b/src/types/spec/switch/h5.ts index e7612313ab..83982f8094 100644 --- a/src/types/spec/switch/h5.ts +++ b/src/types/spec/switch/h5.ts @@ -1,3 +1,3 @@ import { BaseSwitch } from '../switch/base' -export interface WebSwitchProps extends BaseSwitch {} +export interface WebSwitchProps extends BaseSwitch {} diff --git a/src/types/spec/switch/taro.ts b/src/types/spec/switch/taro.ts index 62a81eeb2c..2ede77b150 100644 --- a/src/types/spec/switch/taro.ts +++ b/src/types/spec/switch/taro.ts @@ -1,5 +1,3 @@ -import { ITouchEvent } from '@tarojs/components' import { BaseSwitch } from './base' -type UnionEvent = React.MouseEvent | ITouchEvent -export interface TaroSwitchProps extends BaseSwitch {} +export interface TaroSwitchProps extends BaseSwitch {}