Skip to content

Commit

Permalink
feat(Checkbox): add support for color on <Checkbox> (#3670)
Browse files Browse the repository at this point in the history
* feat(Checkbox): add support for `color` on `<Checkbox>`

* fix: update description of color

* docs: fix errors in docs
  • Loading branch information
simonguo committed Mar 14, 2024
1 parent fc59018 commit a9e14ee
Show file tree
Hide file tree
Showing 12 changed files with 219 additions and 61 deletions.
45 changes: 26 additions & 19 deletions docs/pages/components/checkbox/en-US/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ The `indeterminate` property sets the Checkbox to an indeterminate state, mainly

<!--{include:`indeterminate.md`}-->

### Colors

<!--{include:`colors.md`}-->

### Checkbox Group

<!--{include:`checkbox-group.md`}-->
Expand All @@ -47,25 +51,28 @@ WAI-ARIA: https://www.w3.org/TR/wai-aria-practices/#checkbox

### `<Checkbox>`

| Property | Type `(default)` | Description |
| -------------- | ---------------------------------------------------------- | ----------------------------------------------------------------------- |
| checked | boolean | Specifies whether the checkbox is selected |
| defaultChecked | boolean | Specifies the initial state: whether or not the checkbox is selected |
| disabled | boolean | Whether disabled |
| id | ElementType | Custom element type for the component |
| indeterminate | boolean | When being a checkbox , setting styles after the child part is selected |
| inputRef | Ref | Ref of input element |
| name | string | Used for the name of the form |
| onChange | (value: string \| number, checked: boolean, event) => void | Callback fired when checkbox is triggered and state changes |
| title | string | HTML title |
| value | string \| number | Correspond to the value of CheckboxGroup, determine whether to select |
| Property | Type `(default)` | Description |
| -------------- | ---------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| as | ElementType`(div)` | Custom element type for the component |
| checked | boolean | Specifies whether the checkbox is selected |
| color | [Color](#code-ts-color-code) | The color of the checkbox when checked or indeterminate <br/>![](https://img.shields.io/badge/min-v5.56.0-blue) |
| defaultChecked | boolean | Specifies the initial state: whether or not the checkbox is selected |
| disabled | boolean | Whether disabled |
| indeterminate | boolean | When being a checkbox , setting styles after the child part is selected |
| inputRef | Ref | Ref of input element |
| name | string | Used for the name of the form |
| onChange | (value: string \| number, checked: boolean, event) => void | Callback fired when checkbox is triggered and state changes |
| title | string | HTML title |
| value | string \| number | Correspond to the value of CheckboxGroup, determine whether to select |

### `<CheckboxGroup>`

| Property | Type `(default)` | Description |
| ------------ | ----------------------------------------- | ----------------------------------------------------------- |
| defaultValue | string[] \| number[] | Default value |
| inline | boolean | Inline layout |
| name | string | Used for the name of the form |
| onChange | (value:string \| number[], event) => void | Callback fired when checkbox is triggered and state changes |
| value | string[] \| number[] | Value of checked box (Controlled) |
| Property | Type `(default)` | Description |
| ------------ | ------------------------------------------- | ----------------------------------------------------------- |
| defaultValue | string[] \| number[] | Default value |
| inline | boolean | Inline layout |
| name | string | Used for the name of the form |
| onChange | (value:string[] \| number[], event) => void | Callback fired when checkbox is triggered and state changes |
| value | string[] \| number[] | Value of checked box (Controlled) |

<!--{include:(_common/types/color.md)}-->
34 changes: 34 additions & 0 deletions docs/pages/components/checkbox/fragments/colors.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<!--start-code-->

```js
import { Checkbox } from 'rsuite';

const App = () => (
<>
<Checkbox defaultChecked color="red">
Red
</Checkbox>
<Checkbox defaultChecked color="orange">
Orange
</Checkbox>
<Checkbox defaultChecked color="yellow">
Yellow
</Checkbox>
<Checkbox defaultChecked color="green">
Green
</Checkbox>
<Checkbox defaultChecked color="cyan">
Cyan
</Checkbox>
<Checkbox defaultChecked color="blue">
Blue
</Checkbox>
<Checkbox defaultChecked color="violet">
Violet
</Checkbox>
</>
);
ReactDOM.render(<App />, document.getElementById('root'));
```

<!--end-code-->
45 changes: 26 additions & 19 deletions docs/pages/components/checkbox/zh-CN/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@

<!--{include:`indeterminate.md`}-->

### 颜色

<!--{include:`colors.md`}-->

### 复选框分组

<!--{include:`checkbox-group.md`}-->
Expand Down Expand Up @@ -51,25 +55,28 @@ type ValueType = string | number;

### `<Checkbox>`

| 属性名称 | 类型 `(默认值)` | 描述 |
| -------------- | ---------------------------------------------------------- | -------------------------------------------- |
| checked | boolean | 被选择(受控) |
| defaultChecked | boolean | 默认被选择 |
| disabled | boolean | 禁用 |
| id | ElementType | 为组件自定义元素类型 |
| indeterminate | boolean | 作为一个全选框时,子项部分被选择后的样式设置 |
| inputRef | Ref | HTML input 元素 |
| name | string | 用于表单对应的名称 |
| onChange | (value: string \| number, checked: boolean, event) => void | checked 状态发生改变的回调函数 |
| title | string | HTML title |
| value | string \| number | 值,对应 CheckboxGroup 的值,判断是否选中 |
| 属性名称 | 类型 `(默认值)` | 描述 |
| -------------- | ---------------------------------------------------------- | -------------------------------------------------------------------------------- |
| as | ElementType`(div)` | 为组件自定义元素类型 |
| checked | boolean | 被选择(受控) |
| color | [Color](#code-ts-color-code) | 选中或不确定状态时的颜色 <br/>![](https://img.shields.io/badge/min-v5.56.0-blue) |
| defaultChecked | boolean | 默认被选择 |
| disabled | boolean | 禁用 |
| indeterminate | boolean | 作为一个全选框时,子项部分被选择后的样式设置 |
| inputRef | Ref | HTML input 元素 |
| name | string | 用于表单对应的名称 |
| onChange | (value: string \| number, checked: boolean, event) => void | checked 状态发生改变的回调函数 |
| title | string | HTML title |
| value | string \| number | 值,对应 CheckboxGroup 的值,判断是否选中 |

### `<CheckboxGroup>`

| 属性名称 | 类型 `(默认值)` | 描述 |
| ------------ | ----------------------------------------- | ------------------ |
| defaultValue | string[] \| number[] | 默认值 |
| inline | boolean | 内联布局 |
| name | string | 用于表单对应的名称 |
| onChange | (value:string \| number[], event) => void | 值改变后的回调函数 |
| value | string[] \| number[] | 值(受控) |
| 属性名称 | 类型 `(默认值)` | 描述 |
| ------------ | ------------------------------------------- | ------------------ |
| defaultValue | string[] \| number[] | 默认值 |
| inline | boolean | 内联布局 |
| name | string | 用于表单对应的名称 |
| onChange | (value:string[] \| number[], event) => void | 值改变后的回调函数 |
| value | string[] \| number[] | 值(受控) |

<!--{include:(_common/types/color.md)}-->
4 changes: 3 additions & 1 deletion src/Badge/test/BadgeSpec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import Badge from '../Badge';
import { render, screen } from '@testing-library/react';

describe('Badge', () => {
testStandardProps(<Badge />);
testStandardProps(<Badge />, {
colors: ['red', 'orange', 'yellow', 'green', 'cyan', 'blue', 'violet']
});

it('Should render independent', () => {
const { container } = render(<Badge />);
Expand Down
5 changes: 4 additions & 1 deletion src/Button/test/ButtonSpec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ import { testStandardProps } from '@test/utils';
import Button from '../Button';

describe('Button', () => {
testStandardProps(<Button />, { sizes: ['lg', 'md', 'sm', 'xs'] });
testStandardProps(<Button />, {
sizes: ['lg', 'md', 'sm', 'xs'],
colors: ['red', 'orange', 'yellow', 'green', 'cyan', 'blue', 'violet']
});

it('Should output a button', () => {
render(<Button>Title</Button>);
Expand Down
49 changes: 33 additions & 16 deletions src/Checkbox/Checkbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,30 @@ import {
partitionHTMLProps,
useClassNames,
useEventCallback,
useUniqueId,
mergeRefs
} from '../utils';
import { CheckboxGroupContext } from '../CheckboxGroup';
import { WithAsProps, RsRefForwardingComponent } from '../@types/common';
import { WithAsProps, RsRefForwardingComponent, TypeAttributes } from '../@types/common';
import { refType } from '../internals/propTypes';

export type ValueType = string | number;
export interface CheckboxProps<V = ValueType>
extends WithAsProps,
Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange'> {
/**
* Inline layout
* The color of the checkbox when checked or indeterminate
*
* @private Used in CheckboxGroup
* @version 5.56.0
*/
inline?: boolean;
color?: TypeAttributes.Color;

/**
* Whether to show checkbox
*
* @private Used in MultiCascader
*/
checkable?: boolean;

/**
* A checkbox can appear disabled and be unable to change states
Expand Down Expand Up @@ -63,16 +71,16 @@ export interface CheckboxProps<V = ValueType>
inputRef?: React.Ref<any>;

/**
* The HTML input value.
* Inline layout
*
* @private Used in CheckboxGroup
*/
value?: V;
inline?: boolean;

/**
* Whether to show checkbox
*
* @private Used in MultiCascader
* The HTML input value.
*/
checkable?: boolean;
value?: V;

/**
* Used for the name of the form
Expand Down Expand Up @@ -132,6 +140,7 @@ const Checkbox: RsRefForwardingComponent<'div', CheckboxProps> = React.forwardRe
children,
classPrefix = 'checkbox',
checkable = true,
color,
defaultChecked = false,
title,
inputRef,
Expand Down Expand Up @@ -167,7 +176,10 @@ const Checkbox: RsRefForwardingComponent<'div', CheckboxProps> = React.forwardRe
}, [checkboxGroupContext, selfChecked, value]);

const { merge, prefix, withClassPrefix } = useClassNames(classPrefix);
const classes = merge(className, withClassPrefix({ inline, indeterminate, disabled, checked }));
const classes = merge(
className,
withClassPrefix(color, { inline, indeterminate, disabled, checked })
);
const [htmlInputProps, restProps] = partitionHTMLProps(rest);

// If <Checkbox> is within a <CheckboxGroup>, it's bound to be controlled
Expand Down Expand Up @@ -200,6 +212,8 @@ const Checkbox: RsRefForwardingComponent<'div', CheckboxProps> = React.forwardRe
}
});

const labelId = useUniqueId('label-');

if (plaintext) {
return checked ? (
<Component {...restProps} ref={ref} className={classes}>
Expand All @@ -208,20 +222,21 @@ const Checkbox: RsRefForwardingComponent<'div', CheckboxProps> = React.forwardRe
) : null;
}

const input = (
const control = (
<span className={prefix`control`}>
<input
{...htmlInputProps}
{...inputProps}
aria-disabled={disabled}
aria-checked={indeterminate ? 'mixed' : checked}
aria-labelledby={labelId}
name={name}
value={value}
type="checkbox"
ref={mergeRefs(checkboxRef, inputRef)}
tabIndex={tabIndex}
readOnly={readOnly}
disabled={disabled}
aria-disabled={disabled}
aria-checked={indeterminate ? 'mixed' : checked}
onClick={onCheckboxClick}
onChange={handleChange}
/>
Expand All @@ -233,8 +248,10 @@ const Checkbox: RsRefForwardingComponent<'div', CheckboxProps> = React.forwardRe
<Component {...restProps} ref={ref} onClick={onClick} className={classes}>
<div className={prefix`checker`}>
<label title={title} onClick={handleLabelClick}>
{checkable ? input : null}
<span className={prefix`label`}>{children}</span>
{checkable ? control : null}
<span className={prefix`label`} id={labelId}>
{children}
</span>
</label>
</div>
</Component>
Expand Down
40 changes: 39 additions & 1 deletion src/Checkbox/stories/Checkbox.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react';
import type { StoryObj } from '@storybook/react';
import Checkbox from '../Checkbox';
import Checkbox, { CheckboxProps } from '../Checkbox';
import { createMeta } from '@/storybook/utils';
import '../styles/index.less';

Expand Down Expand Up @@ -50,3 +51,40 @@ export const Indeterminate: Story = {
indeterminate: true
}
};

const ColorTemplate = (CheckboxProps: CheckboxProps) => {
return (
<>
<Checkbox {...CheckboxProps} color="red">
Red
</Checkbox>
<Checkbox {...CheckboxProps} color="orange">
Orange
</Checkbox>
<Checkbox {...CheckboxProps} color="yellow">
Yellow
</Checkbox>
<Checkbox {...CheckboxProps} color="green">
Green
</Checkbox>
<Checkbox {...CheckboxProps} color="cyan">
Cyan
</Checkbox>
<Checkbox {...CheckboxProps} color="blue">
Blue
</Checkbox>
<Checkbox {...CheckboxProps} color="violet">
Violet
</Checkbox>
</>
);
};

export const Color: Story = {
render: ColorTemplate,
args: {
...defaultArgs,
children: 'Checkbox',
defaultChecked: true
}
};
22 changes: 22 additions & 0 deletions src/Checkbox/styles/index.less
Original file line number Diff line number Diff line change
Expand Up @@ -150,3 +150,25 @@
}
}
}

each(@spectrum, .(@color) {
.rs-checkbox-@{color} {
.rs-checkbox-control::before{
border-color: var(~'--rs-@{color}-500');
}

label:hover{
.rs-checkbox-inner::before {
border-color: var(~'--rs-@{color}-500');
}
}

&.rs-checkbox-checked,
&.rs-checkbox-indeterminate {
.rs-checkbox-inner::before{
border-color: var(~'--rs-@{color}-500');
background-color:var(~'--rs-@{color}-500');
}
}
}
});
4 changes: 3 additions & 1 deletion src/Checkbox/test/CheckboxSpec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import { testStandardProps } from '@test/utils';
import Checkbox from '../Checkbox';

describe('Checkbox', () => {
testStandardProps(<Checkbox />);
testStandardProps(<Checkbox />, {
colors: ['red', 'orange', 'yellow', 'green', 'cyan', 'blue', 'violet']
});

it('Should render a checkbox', () => {
render(<Checkbox>Test</Checkbox>);
Expand Down
4 changes: 3 additions & 1 deletion src/Rate/test/RateSpec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import Rate from '../Rate';
import Sinon from 'sinon';

describe('Rate', () => {
testStandardProps(<Rate />);
testStandardProps(<Rate />, {
colors: ['red', 'orange', 'yellow', 'green', 'cyan', 'blue', 'violet']
});

it('Should render a default Rate', () => {
render(<Rate />);
Expand Down

0 comments on commit a9e14ee

Please sign in to comment.