Browse Source

feat: add type API for checkbox & radio (#1061)

* feat: add type API for checkbox & radio

* fix: add checked state for radio
YyumeiZhang 3 years ago
parent
commit
6ec39bb021

+ 3 - 2
content/input/checkbox/index-en-US.md

@@ -399,9 +399,10 @@ import { CheckboxGroup, Checkbox, Row, Col } from '@douyinfe/semi-ui';
 
 
 | PROPERTIES     | Instructions                                                 | type               | Default |
 | PROPERTIES     | Instructions                                                 | type               | Default |
 | -------------- | ------------------------------------------------------------ | ------------------ | ------- |
 | -------------- | ------------------------------------------------------------ | ------------------ | ------- |
-| addonId | id of addon node, aria-labelledby refers to this id, if not set, it will generate an id randomly  **provided after v2.11.0**                                 | string            |       |
+| addonId | id of addon node, aria-labelledby refers to this id, if not set, it will generate an id randomly  <br/>**provided after v2.11.0**                                 | string            |       |
 | aria-label     | Define label of the Checkbox  | string | - |
 | aria-label     | Define label of the Checkbox  | string | - |
 | checked        | Specify whether the current Checkbox is selected (it is invalid when used in Group)                     | boolean            | false   |
 | checked        | Specify whether the current Checkbox is selected (it is invalid when used in Group)                     | boolean            | false   |
+| type           | Set the type of checkboxe, one of: `default`、`card`、`pureCard` <br/>**provided after v2.18.0** | string        | `default`  |
 | defaultChecked | Whether Checked by default (it is invalid when used in Group)                                           | boolean            | false   |
 | defaultChecked | Whether Checked by default (it is invalid when used in Group)                                           | boolean            | false   |
 | disabled       | Disabled state                                               | boolean            | false   |
 | disabled       | Disabled state                                               | boolean            | false   |
 | extra          | Provide extra information <br/>**>= v0.25.0**                | ReactNode          | -       |
 | extra          | Provide extra information <br/>**>= v0.25.0**                | ReactNode          | -       |
@@ -420,7 +421,7 @@ import { CheckboxGroup, Checkbox, Row, Col } from '@douyinfe/semi-ui';
 | disabled     | Disable the entire group                                                | boolean                | false      |
 | disabled     | Disable the entire group                                                | boolean                | false      |
 | name         | The `name` attribute for all `input[type="checkbox"]` in Checkbox Group | string                 | -          |
 | name         | The `name` attribute for all `input[type="checkbox"]` in Checkbox Group | string                 | -          |
 | options      | Specify optional                                                        | any\[]              | \[]        |
 | options      | Specify optional                                                        | any\[]              | \[]        |
-| type         | Set the type of checkboxes, one of: `default`、`card`、`pureCard` **provided after v1.30.0**	| string        | `default`  |
+| type         | Set the type of checkboxes, one of: `default`、`card`、`pureCard` <br/>**provided after v1.30.0**	| string        | `default`  |
 | value        | Specify selected options                                                | any\[]              | \[]        |
 | value        | Specify selected options                                                | any\[]              | \[]        |
 | onChange     | Callback function when selected options change                          | function(checkedValue) | -          |
 | onChange     | Callback function when selected options change                          | function(checkedValue) | -          |
 
 

+ 3 - 2
content/input/checkbox/index.md

@@ -381,9 +381,10 @@ import { Checkbox, CheckboxGroup, Row, Col } from '@douyinfe/semi-ui';
 
 
 | 参数 | 说明 | 类型 | 默认值 |
 | 参数 | 说明 | 类型 | 默认值 |
 | --- | --- | --- | --- |
 | --- | --- | --- | --- |
-| addonId | addon 节点 id,aria-labelledby 指向这个 id,若无设置会随机生成一个 id  **v2.11.0 后提供**                                 | string            |       |
+| addonId | addon 节点 id,aria-labelledby 指向这个 id,若无设置会随机生成一个 id <br/>**v2.11.0 后提供**                                 | string            |       |
 | aria-label | 定义 Checkbox 的作用 | string | - |
 | aria-label | 定义 Checkbox 的作用 | string | - |
 | checked | 指定当前Checkbox是否选中(在Group中使用时无效) | boolean | false |
 | checked | 指定当前Checkbox是否选中(在Group中使用时无效) | boolean | false |
+| type |设置checkbox 的样式类型,可选值为: `default`、`card`、`pureCard`<br/>**v2.18.0 后提供** |string|`default`|
 | defaultChecked | 初始是否选中(在Group中使用时无效) | boolean | false |
 | defaultChecked | 初始是否选中(在Group中使用时无效) | boolean | false |
 | disabled | 失效状态 | boolean | false |
 | disabled | 失效状态 | boolean | false |
 | extra | 副文本<br/>__v0.25.0后提供__ | ReactNode | - |
 | extra | 副文本<br/>__v0.25.0后提供__ | ReactNode | - |
@@ -402,7 +403,7 @@ import { Checkbox, CheckboxGroup, Row, Col } from '@douyinfe/semi-ui';
 | disabled | 整组失效 | boolean | false |
 | disabled | 整组失效 | boolean | false |
 | name | CheckboxGroup 下所有 `input[type="checkbox"]` 的 `name` 属性 | string | - |
 | name | CheckboxGroup 下所有 `input[type="checkbox"]` 的 `name` 属性 | string | - |
 | options | 指定可选项 | any\[] | \[] |
 | options | 指定可选项 | any\[] | \[] |
-| type |	设置所有 checkbox 的样式类型,可选值为: `default`、`card`、`pureCard` **v1.30.0 后提供**|string|`default`|
+| type |设置所有 checkbox 的样式类型,可选值为: `default`、`card`、`pureCard` <br/>**v1.30.0 后提供**|string|`default`|
 | value | 指定选中的选项 | any\[] | \[] |
 | value | 指定选中的选项 | any\[] | \[] |
 | onChange | 变化时回调函数 | function(checkedValue) | - |
 | onChange | 变化时回调函数 | function(checkedValue) | - |
 
 

+ 2 - 1
content/input/radio/index-en-US.md

@@ -376,12 +376,13 @@ class App extends React.Component {
 | PROPERTIES | Instructions | Type | Default |
 | PROPERTIES | Instructions | Type | Default |
 | --- | --- | --- | --- |
 | --- | --- | --- | --- |
 | addonClassName | classname of content wrapper<br/>**provided after v1.16.0** | string |  |
 | addonClassName | classname of content wrapper<br/>**provided after v1.16.0** | string |  |
-| addonId | id of addon node, aria-labelledby refers to this id, if not set, it will generate an id randomly  **provided after v2.11.0**                                 | string            |       |
+| addonId | id of addon node, aria-labelledby refers to this id, if not set, it will generate an id randomly  <br/>**provided after v2.11.0**                                 | string            |       |
 | addonStyle | inline style of content wrapper<br/>**provided after v1.16.0** | object |  |
 | addonStyle | inline style of content wrapper<br/>**provided after v1.16.0** | object |  |
 | aria-label      | Label of Radio                                                            | string           | -  |
 | aria-label      | Label of Radio                                                            | string           | -  |
 | name         | The `name` attribute passed to `input[type="radio"]` in the Radio component, Radios with the same `name` belong to the same RadioGroup,The `name` attribute can refer to [MDN Radio](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Input/radio#value)   | string         | -  |
 | name         | The `name` attribute passed to `input[type="radio"]` in the Radio component, Radios with the same `name` belong to the same RadioGroup,The `name` attribute can refer to [MDN Radio](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Input/radio#value)   | string         | -  |
 | autoFocus | Automatically focus the form control when the page is loaded | boolean | false |
 | autoFocus | Automatically focus the form control when the page is loaded | boolean | false |
 | checked | Specify whether it is currently selected | boolean | false |
 | checked | Specify whether it is currently selected | boolean | false |
+| type | Set the type of radio, one of `default`, `button`, `card`, `pureCard` <br/>**This api is provided after v2.18.0**| string | `default` |
 | className | Class name | string |  |
 | className | Class name | string |  |
 | defaultChecked | Checked by default | boolean | false |
 | defaultChecked | Checked by default | boolean | false |
 | disabled | Disable the radio | boolean | false |
 | disabled | Disable the radio | boolean | false |

+ 4 - 3
content/input/radio/index.md

@@ -321,13 +321,14 @@ class App extends React.Component {
 
 
 | 属性           | 说明                                                                   | 类型              | 默认值  |
 | 属性           | 说明                                                                   | 类型              | 默认值  |
 |----------------|-----------------------------------------------------------------------|------------------|--------|
 |----------------|-----------------------------------------------------------------------|------------------|--------|
-| addonClassName | 包裹内容容器的样式类名  **v1.16.0 后提供**                                 | string            |       |
-| addonId | addon 节点 id,aria-labelledby 指向这个 id,若无设置会随机生成一个 id  **v2.11.0 后提供**                                 | string            |       |
-| addonStyle     | 包裹内容容器的内联样式  **v1.16.0 后提供**                                 | CSSProperties     |       |
+| addonClassName | 包裹内容容器的样式类名  <br/>**v1.16.0 后提供**                                 | string            |       |
+| addonId | addon 节点 id,aria-labelledby 指向这个 id,若无设置会随机生成一个 id  <br/>**v2.11.0 后提供**                                 | string            |       |
+| addonStyle     | 包裹内容容器的内联样式  <br/>**v1.16.0 后提供**                                 | CSSProperties     |       |
 | aria-label      | Radio 的 label                                                            | string           | -  |
 | aria-label      | Radio 的 label                                                            | string           | -  |
 | name         | Radio组件中`input[type="radio"]`的`name`属性,具有相同`name`的Radio属于同一个RadioGroup,`name`属性可参考[MDN Radio](https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/Input/radio#%E5%80%BC)                              | string         | -  |
 | name         | Radio组件中`input[type="radio"]`的`name`属性,具有相同`name`的Radio属于同一个RadioGroup,`name`属性可参考[MDN Radio](https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/Input/radio#%E5%80%BC)                              | string         | -  |
 | autoFocus      | 自动获取焦点                                                            | boolean           | false  |
 | autoFocus      | 自动获取焦点                                                            | boolean           | false  |
 | checked        | 指定当前是否选中                                                         | boolean           | false  |
 | checked        | 指定当前是否选中                                                         | boolean           | false  |
+|type            |设置 radio的样式类型,可选值为:`default`、`button`、`card`、`pureCard` <br/>**该 api 在 v2.18.0 后提供**    |string|`default`|
 | className      | 样式类名                                                                | string            |        |
 | className      | 样式类名                                                                | string            |        |
 | defaultChecked | 初始是否选中                                                             | boolean           | false  |
 | defaultChecked | 初始是否选中                                                             | boolean           | false  |
 | disabled       | 禁选单选框                                                              |boolean            | false    |
 | disabled       | 禁选单选框                                                              |boolean            | false    |

+ 5 - 0
packages/semi-foundation/radio/radioFoundation.ts

@@ -3,6 +3,7 @@ import warning from '../utils/warning';
 
 
 export interface RadioAdapter extends DefaultAdapter {
 export interface RadioAdapter extends DefaultAdapter {
     setHover: (hover: boolean) => void;
     setHover: (hover: boolean) => void;
+    setChecked: (checked: boolean) => void;
     setAddonId: () => void;
     setAddonId: () => void;
     setExtraId: () => void;
     setExtraId: () => void;
     setFocusVisible: (focusVisible: boolean) => void;
     setFocusVisible: (focusVisible: boolean) => void;
@@ -21,6 +22,10 @@ export default class RadioFoundation extends BaseFoundation<RadioAdapter> {
         this._adapter.setHover(hover);
         this._adapter.setHover(hover);
     }
     }
 
 
+    setChecked(checked: boolean) {
+        this._adapter.setChecked(checked);
+    }
+
     handleFocusVisible = (event: any) => {
     handleFocusVisible = (event: any) => {
         const { target } = event;
         const { target } = event;
         try {
         try {

+ 10 - 2
packages/semi-ui/checkbox/checkbox.tsx

@@ -2,7 +2,7 @@
 import React from 'react';
 import React from 'react';
 import PropTypes from 'prop-types';
 import PropTypes from 'prop-types';
 import classnames from 'classnames';
 import classnames from 'classnames';
-import { checkboxClasses as css } from '@douyinfe/semi-foundation/checkbox/constants';
+import { checkboxClasses as css, strings } from '@douyinfe/semi-foundation/checkbox/constants';
 import CheckboxFoundation, { CheckboxAdapter, BasicCheckboxEvent, BasicTargetObject, BaseCheckboxProps } from '@douyinfe/semi-foundation/checkbox/checkboxFoundation';
 import CheckboxFoundation, { CheckboxAdapter, BasicCheckboxEvent, BasicTargetObject, BaseCheckboxProps } from '@douyinfe/semi-foundation/checkbox/checkboxFoundation';
 import CheckboxInner from './checkboxInner';
 import CheckboxInner from './checkboxInner';
 import BaseComponent from '../_base/baseComponent';
 import BaseComponent from '../_base/baseComponent';
@@ -10,6 +10,7 @@ import '@douyinfe/semi-foundation/checkbox/checkbox.scss';
 import { Context, CheckboxContextType } from './context';
 import { Context, CheckboxContextType } from './context';
 import { isUndefined, isBoolean, noop } from 'lodash';
 import { isUndefined, isBoolean, noop } from 'lodash';
 import { getUuidShort } from '@douyinfe/semi-foundation/utils/uuid';
 import { getUuidShort } from '@douyinfe/semi-foundation/utils/uuid';
+import { CheckboxType } from './checkboxGroup';
 export type CheckboxEvent = BasicCheckboxEvent;
 export type CheckboxEvent = BasicCheckboxEvent;
 export type TargetObject = BasicTargetObject;
 export type TargetObject = BasicTargetObject;
 
 
@@ -31,6 +32,7 @@ export interface CheckboxProps extends BaseCheckboxProps {
     tabIndex?: number; // a11y: wrapper tabIndex
     tabIndex?: number; // a11y: wrapper tabIndex
     addonId?: string;
     addonId?: string;
     extraId?: string;
     extraId?: string;
+    type?: CheckboxType;
 }
 }
 interface CheckboxState {
 interface CheckboxState {
     checked: boolean;
     checked: boolean;
@@ -68,6 +70,7 @@ class Checkbox extends BaseComponent<CheckboxProps, CheckboxState> {
         'aria-label': PropTypes.string,
         'aria-label': PropTypes.string,
         tabIndex: PropTypes.number,
         tabIndex: PropTypes.number,
         preventScroll: PropTypes.bool,
         preventScroll: PropTypes.bool,
+        type: PropTypes.string,
     };
     };
 
 
     static defaultProps = {
     static defaultProps = {
@@ -76,6 +79,7 @@ class Checkbox extends BaseComponent<CheckboxProps, CheckboxState> {
         onChange: noop,
         onChange: noop,
         onMouseEnter: noop,
         onMouseEnter: noop,
         onMouseLeave: noop,
         onMouseLeave: noop,
+        type: 'default',
     };
     };
     checkboxEntity: CheckboxInner;
     checkboxEntity: CheckboxInner;
     context: CheckboxContextType;
     context: CheckboxContextType;
@@ -179,7 +183,8 @@ class Checkbox extends BaseComponent<CheckboxProps, CheckboxState> {
             value,
             value,
             role,
             role,
             tabIndex,
             tabIndex,
-            id
+            id,
+            type,
         } = this.props;
         } = this.props;
         const { checked, addonId, extraId, focusVisible } = this.state;
         const { checked, addonId, extraId, focusVisible } = this.state;
         const props: Record<string, any> = {
         const props: Record<string, any> = {
@@ -200,6 +205,9 @@ class Checkbox extends BaseComponent<CheckboxProps, CheckboxState> {
             props.isCardType = isCardType;
             props.isCardType = isCardType;
             props.isPureCardType = isPureCardType;
             props.isPureCardType = isPureCardType;
             props['name'] = this.context.checkboxGroup.name;
             props['name'] = this.context.checkboxGroup.name;
+        } else {
+            props.isPureCardType = type === strings.TYPE_PURECARD;
+            props.isCardType = type === strings.TYPE_CARD || props.isPureCardType;
         }
         }
 
 
         const prefix = prefixCls || css.PREFIX;
         const prefix = prefixCls || css.PREFIX;

+ 27 - 5
packages/semi-ui/radio/radio.tsx

@@ -2,7 +2,7 @@
 import React from 'react';
 import React from 'react';
 import PropTypes from 'prop-types';
 import PropTypes from 'prop-types';
 import cls from 'classnames';
 import cls from 'classnames';
-import { noop } from 'lodash';
+import { noop, isUndefined, isBoolean } from 'lodash';
 
 
 import RadioFoundation, { RadioAdapter } from '@douyinfe/semi-foundation/radio/radioFoundation';
 import RadioFoundation, { RadioAdapter } from '@douyinfe/semi-foundation/radio/radioFoundation';
 import { RadioChangeEvent } from '@douyinfe/semi-foundation/radio/radioInnerFoundation';
 import { RadioChangeEvent } from '@douyinfe/semi-foundation/radio/radioInnerFoundation';
@@ -52,6 +52,7 @@ export interface RadioState {
     addonId?: string;
     addonId?: string;
     extraId?: string;
     extraId?: string;
     focusVisible?: boolean;
     focusVisible?: boolean;
+    checked?: boolean;
 }
 }
 
 
 export { RadioChangeEvent };
 export { RadioChangeEvent };
@@ -104,11 +105,22 @@ class Radio extends BaseComponent<RadioProps, RadioState> {
             hover: false,
             hover: false,
             addonId: props.addonId,
             addonId: props.addonId,
             extraId: props.extraId,
             extraId: props.extraId,
+            checked: props.checked || props.defaultChecked || false,
         };
         };
         this.foundation = new RadioFoundation(this.adapter);
         this.foundation = new RadioFoundation(this.adapter);
         this.radioEntity = null;
         this.radioEntity = null;
     }
     }
 
 
+    componentDidUpdate(prevProps: RadioProps) {
+        if (this.props.checked !== prevProps.checked) {
+            if (isUndefined(this.props.checked)) {
+                this.foundation.setChecked(false);
+            } else if (isBoolean(this.props.checked)) {
+                this.foundation.setChecked(this.props.checked);
+            }
+        }
+    }
+
     get adapter(): RadioAdapter {
     get adapter(): RadioAdapter {
         return {
         return {
             ...super.adapter,
             ...super.adapter,
@@ -118,6 +130,9 @@ class Radio extends BaseComponent<RadioProps, RadioState> {
             setAddonId: () => {
             setAddonId: () => {
                 this.setState({ addonId: getUuidShort({ prefix: 'addon' }) });
                 this.setState({ addonId: getUuidShort({ prefix: 'addon' }) });
             },
             },
+            setChecked: (checked: boolean) => {
+                this.setState({ checked });
+            },
             setExtraId: () => {
             setExtraId: () => {
                 this.setState({ extraId: getUuidShort({ prefix: 'extra' }) });
                 this.setState({ extraId: getUuidShort({ prefix: 'extra' }) });
             },
             },
@@ -146,6 +161,7 @@ class Radio extends BaseComponent<RadioProps, RadioState> {
             const { radioGroup } = this.context;
             const { radioGroup } = this.context;
             radioGroup.onChange && radioGroup.onChange(e);
             radioGroup.onChange && radioGroup.onChange(e);
         }
         }
+        !('checked' in this.props) && this.foundation.setChecked(e.target.checked);
         onChange && onChange(e);
         onChange && onChange(e);
     };
     };
 
 
@@ -171,7 +187,6 @@ class Radio extends BaseComponent<RadioProps, RadioState> {
         const {
         const {
             addonClassName,
             addonClassName,
             addonStyle,
             addonStyle,
-            checked,
             disabled,
             disabled,
             style,
             style,
             className,
             className,
@@ -194,8 +209,11 @@ class Radio extends BaseComponent<RadioProps, RadioState> {
             isButtonRadioComponent,
             isButtonRadioComponent,
             buttonSize,
             buttonSize,
             realPrefixCls;
             realPrefixCls;
-        const { hover: isHover, addonId, extraId, focusVisible } = this.state;
-        let props = {};
+        const { hover: isHover, addonId, extraId, focusVisible, checked, } = this.state;
+        const props: Record<string, any> = {
+            checked,
+            disabled,
+        };
 
 
         if (this.isInGroup()) {
         if (this.isInGroup()) {
             realChecked = this.context.radioGroup.value === propValue;
             realChecked = this.context.radioGroup.value === propValue;
@@ -206,13 +224,17 @@ class Radio extends BaseComponent<RadioProps, RadioState> {
             isPureCardRadioGroup = this.context.radioGroup.isPureCardRadio;
             isPureCardRadioGroup = this.context.radioGroup.isPureCardRadio;
             buttonSize = this.context.radioGroup.buttonSize;
             buttonSize = this.context.radioGroup.buttonSize;
             realPrefixCls = prefixCls || this.context.radioGroup.prefixCls;
             realPrefixCls = prefixCls || this.context.radioGroup.prefixCls;
-            props = { checked: realChecked, disabled: isDisabled };
+            props.checked = realChecked;
+            props.disabled = isDisabled;
         } else {
         } else {
             realChecked = checked;
             realChecked = checked;
             isDisabled = disabled;
             isDisabled = disabled;
             realMode = mode;
             realMode = mode;
             isButtonRadioComponent = type === 'button';
             isButtonRadioComponent = type === 'button';
             realPrefixCls = prefixCls;
             realPrefixCls = prefixCls;
+            isButtonRadioGroup = type === strings.TYPE_BUTTON;
+            isPureCardRadioGroup = type === strings.TYPE_PURECARD;
+            isCardRadioGroup = type === strings.TYPE_CARD || isPureCardRadioGroup;
         }
         }
         const isButtonRadio = typeof isButtonRadioGroup === 'undefined' ? isButtonRadioComponent : isButtonRadioGroup;
         const isButtonRadio = typeof isButtonRadioGroup === 'undefined' ? isButtonRadioComponent : isButtonRadioGroup;