feat(Button): support color from Antd's colors (#51550)

* feat(button): add antd preset colors

* chore(button): add colors to docs

* test(button): add tests for colors

* refactor: remove LiteralUnion

* test: add const to colors

* refactor(button): import from preset colors

* chore(button): update docs

* test(button): test with preset colors

* test(button): import from relative path

* fix(button): color

* fix(button): add missing box shadow

* test(button): merge preset colors and variants test case

* refactor(button): use light color as box shadow

* refactor(button): set hover color to 5

* docs(button): update doc

* docs(button): update doc

---------

Co-authored-by: ice <49827327+coding-ice@users.noreply.github.com>
Co-authored-by: lijianan <574980606@qq.com>
This commit is contained in:
Oyster Lee 2024-12-09 14:27:07 +08:00 committed by GitHub
parent 6973bf2344
commit 6b98f84c62
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 496 additions and 5 deletions

View File

@ -472,6 +472,162 @@ exports[`renders components/button/demo/color-variant.tsx extend context correct
</span>
</button>
</div>
<div
class="ant-flex ant-flex-wrap-wrap ant-flex-gap-middle"
>
<button
class="ant-btn ant-btn-default ant-btn-color-pink ant-btn-variant-solid ant-btn-sm"
type="button"
>
<span>
Solid
</span>
</button>
<button
class="ant-btn ant-btn-default ant-btn-color-pink ant-btn-variant-outlined ant-btn-sm"
type="button"
>
<span>
Outlined
</span>
</button>
<button
class="ant-btn ant-btn-default ant-btn-color-pink ant-btn-variant-dashed ant-btn-sm"
type="button"
>
<span>
Dashed
</span>
</button>
<button
class="ant-btn ant-btn-default ant-btn-color-pink ant-btn-variant-filled ant-btn-sm"
type="button"
>
<span>
Filled
</span>
</button>
<button
class="ant-btn ant-btn-default ant-btn-color-pink ant-btn-variant-text ant-btn-sm"
type="button"
>
<span>
Text
</span>
</button>
<button
class="ant-btn ant-btn-default ant-btn-color-pink ant-btn-variant-link ant-btn-sm"
type="button"
>
<span>
Link
</span>
</button>
</div>
<div
class="ant-flex ant-flex-wrap-wrap ant-flex-gap-middle"
>
<button
class="ant-btn ant-btn-default ant-btn-color-purple ant-btn-variant-solid ant-btn-sm"
type="button"
>
<span>
Solid
</span>
</button>
<button
class="ant-btn ant-btn-default ant-btn-color-purple ant-btn-variant-outlined ant-btn-sm"
type="button"
>
<span>
Outlined
</span>
</button>
<button
class="ant-btn ant-btn-default ant-btn-color-purple ant-btn-variant-dashed ant-btn-sm"
type="button"
>
<span>
Dashed
</span>
</button>
<button
class="ant-btn ant-btn-default ant-btn-color-purple ant-btn-variant-filled ant-btn-sm"
type="button"
>
<span>
Filled
</span>
</button>
<button
class="ant-btn ant-btn-default ant-btn-color-purple ant-btn-variant-text ant-btn-sm"
type="button"
>
<span>
Text
</span>
</button>
<button
class="ant-btn ant-btn-default ant-btn-color-purple ant-btn-variant-link ant-btn-sm"
type="button"
>
<span>
Link
</span>
</button>
</div>
<div
class="ant-flex ant-flex-wrap-wrap ant-flex-gap-middle"
>
<button
class="ant-btn ant-btn-default ant-btn-color-cyan ant-btn-variant-solid ant-btn-sm"
type="button"
>
<span>
Solid
</span>
</button>
<button
class="ant-btn ant-btn-default ant-btn-color-cyan ant-btn-variant-outlined ant-btn-sm"
type="button"
>
<span>
Outlined
</span>
</button>
<button
class="ant-btn ant-btn-default ant-btn-color-cyan ant-btn-variant-dashed ant-btn-sm"
type="button"
>
<span>
Dashed
</span>
</button>
<button
class="ant-btn ant-btn-default ant-btn-color-cyan ant-btn-variant-filled ant-btn-sm"
type="button"
>
<span>
Filled
</span>
</button>
<button
class="ant-btn ant-btn-default ant-btn-color-cyan ant-btn-variant-text ant-btn-sm"
type="button"
>
<span>
Text
</span>
</button>
<button
class="ant-btn ant-btn-default ant-btn-color-cyan ant-btn-variant-link ant-btn-sm"
type="button"
>
<span>
Link
</span>
</button>
</div>
</div>
`;

View File

@ -461,6 +461,162 @@ exports[`renders components/button/demo/color-variant.tsx correctly 1`] = `
</span>
</button>
</div>
<div
class="ant-flex ant-flex-wrap-wrap ant-flex-gap-middle"
>
<button
class="ant-btn ant-btn-default ant-btn-color-pink ant-btn-variant-solid ant-btn-sm"
type="button"
>
<span>
Solid
</span>
</button>
<button
class="ant-btn ant-btn-default ant-btn-color-pink ant-btn-variant-outlined ant-btn-sm"
type="button"
>
<span>
Outlined
</span>
</button>
<button
class="ant-btn ant-btn-default ant-btn-color-pink ant-btn-variant-dashed ant-btn-sm"
type="button"
>
<span>
Dashed
</span>
</button>
<button
class="ant-btn ant-btn-default ant-btn-color-pink ant-btn-variant-filled ant-btn-sm"
type="button"
>
<span>
Filled
</span>
</button>
<button
class="ant-btn ant-btn-default ant-btn-color-pink ant-btn-variant-text ant-btn-sm"
type="button"
>
<span>
Text
</span>
</button>
<button
class="ant-btn ant-btn-default ant-btn-color-pink ant-btn-variant-link ant-btn-sm"
type="button"
>
<span>
Link
</span>
</button>
</div>
<div
class="ant-flex ant-flex-wrap-wrap ant-flex-gap-middle"
>
<button
class="ant-btn ant-btn-default ant-btn-color-purple ant-btn-variant-solid ant-btn-sm"
type="button"
>
<span>
Solid
</span>
</button>
<button
class="ant-btn ant-btn-default ant-btn-color-purple ant-btn-variant-outlined ant-btn-sm"
type="button"
>
<span>
Outlined
</span>
</button>
<button
class="ant-btn ant-btn-default ant-btn-color-purple ant-btn-variant-dashed ant-btn-sm"
type="button"
>
<span>
Dashed
</span>
</button>
<button
class="ant-btn ant-btn-default ant-btn-color-purple ant-btn-variant-filled ant-btn-sm"
type="button"
>
<span>
Filled
</span>
</button>
<button
class="ant-btn ant-btn-default ant-btn-color-purple ant-btn-variant-text ant-btn-sm"
type="button"
>
<span>
Text
</span>
</button>
<button
class="ant-btn ant-btn-default ant-btn-color-purple ant-btn-variant-link ant-btn-sm"
type="button"
>
<span>
Link
</span>
</button>
</div>
<div
class="ant-flex ant-flex-wrap-wrap ant-flex-gap-middle"
>
<button
class="ant-btn ant-btn-default ant-btn-color-cyan ant-btn-variant-solid ant-btn-sm"
type="button"
>
<span>
Solid
</span>
</button>
<button
class="ant-btn ant-btn-default ant-btn-color-cyan ant-btn-variant-outlined ant-btn-sm"
type="button"
>
<span>
Outlined
</span>
</button>
<button
class="ant-btn ant-btn-default ant-btn-color-cyan ant-btn-variant-dashed ant-btn-sm"
type="button"
>
<span>
Dashed
</span>
</button>
<button
class="ant-btn ant-btn-default ant-btn-color-cyan ant-btn-variant-filled ant-btn-sm"
type="button"
>
<span>
Filled
</span>
</button>
<button
class="ant-btn ant-btn-default ant-btn-color-cyan ant-btn-variant-text ant-btn-sm"
type="button"
>
<span>
Text
</span>
</button>
<button
class="ant-btn ant-btn-default ant-btn-color-cyan ant-btn-variant-link ant-btn-sm"
type="button"
>
<span>
Link
</span>
</button>
</div>
</div>
`;

View File

@ -2,13 +2,14 @@ import React, { Suspense, useRef, useState } from 'react';
import { SearchOutlined } from '@ant-design/icons';
import { resetWarned } from 'rc-util/lib/warning';
import Button from '..';
import Button, { _ButtonVariantTypes } from '..';
import type { GetRef } from '../../_util/type';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
import { act, fireEvent, render, waitFakeTimer } from '../../../tests/utils';
import ConfigProvider from '../../config-provider';
import theme from '../../theme';
import { PresetColors } from '../../theme/interface';
import type { BaseButtonProps } from '../button';
describe('Button', () => {
@ -475,6 +476,20 @@ describe('Button', () => {
});
});
it('should render preset colors and variants correctly', () => {
PresetColors.forEach((color) => {
_ButtonVariantTypes.forEach((variant) => {
const { container } = render(
<Button color={color} variant={variant}>
{color}
</Button>,
);
expect(container.firstChild).toHaveClass(`ant-btn-color-${color}`);
expect(container.firstChild).toHaveClass(`ant-btn-variant-${variant}`);
});
});
});
it('autoFocus should work', () => {
const { container } = render(<Button autoFocus>button</Button>);

View File

@ -1,6 +1,7 @@
import React from 'react';
import { cloneElement, isFragment } from '../_util/reactNode';
import { PresetColors } from '../theme/interface';
import type { BaseButtonProps, LegacyButtonType } from './button';
const rxTwoCNChar = /^[\u4E00-\u9FA5]{2}$/;
@ -94,5 +95,6 @@ export const _ButtonVariantTypes = [
] as const;
export type ButtonVariantType = (typeof _ButtonVariantTypes)[number];
export const _ButtonColorTypes = ['default', 'primary', 'danger'] as const;
export const _ButtonColorTypes = ['default', 'primary', 'danger', ...PresetColors] as const;
export type ButtonColorType = (typeof _ButtonColorTypes)[number];

View File

@ -68,6 +68,67 @@ const App: React.FC = () => {
Link
</Button>
</Flex>
<Flex gap="middle" wrap>
<Button color="pink" variant="solid">
Solid
</Button>
<Button color="pink" variant="outlined">
Outlined
</Button>
<Button color="pink" variant="dashed">
Dashed
</Button>
<Button color="pink" variant="filled">
Filled
</Button>
<Button color="pink" variant="text">
Text
</Button>
<Button color="pink" variant="link">
Link
</Button>
</Flex>
<Flex gap="middle" wrap>
<Button color="purple" variant="solid">
Solid
</Button>
<Button color="purple" variant="outlined">
Outlined
</Button>
<Button color="purple" variant="dashed">
Dashed
</Button>
<Button color="purple" variant="filled">
Filled
</Button>
<Button color="purple" variant="text">
Text
</Button>
<Button color="purple" variant="link">
Link
</Button>
</Flex>
<Flex gap="middle" wrap>
<Button color="cyan" variant="solid">
Solid
</Button>
<Button color="cyan" variant="outlined">
Outlined
</Button>
<Button color="cyan" variant="dashed">
Dashed
</Button>
<Button color="cyan" variant="filled">
Filled
</Button>
<Button color="cyan" variant="text">
Text
</Button>
<Button color="cyan" variant="link">
Link
</Button>
</Flex>
</Flex>
</ConfigProvider>
);

View File

@ -63,7 +63,7 @@ Different button styles generated by setting Button properties. The recommended
| autoInsertSpace | We add a space between two Chinese characters by default, which removed by setting `autoInsertSpace` to `false`. | boolean | `true` | 5.17.0 |
| block | Option to fit button width to its parent width | boolean | false | |
| classNames | Semantic DOM class | [Record<SemanticDOM, string>](#semantic-dom) | - | 5.4.0 |
| color | Set button color | `default` \| `primary` \| `danger` | - | 5.21.0 |
| color | Set button color | `default` \| `primary` \| `danger` \| [PresetColors](#presetcolors) | - | `default`, `primary` and `danger`: 5.21.0, `PresetColors`: 5.23.0 |
| danger | Syntactic sugar. Set the danger status of button. will follow `color` if provided | boolean | false | |
| disabled | Disabled state of button | boolean | false | |
| ghost | Make background transparent and invert text and border colors | boolean | false | |
@ -82,6 +82,10 @@ Different button styles generated by setting Button properties. The recommended
It accepts all props which native buttons support.
### PresetColors
> type PresetColors = 'blue' | 'purple' | 'cyan' | 'green' | 'magenta' | 'pink' | 'red' | 'orange' | 'yellow' | 'volcano' | 'geekblue' | 'lime' | 'gold';
## Semantic DOM
<code src="./demo/_semantic.tsx" simplify="true"></code>

View File

@ -69,7 +69,7 @@ group:
| autoInsertSpace | 我们默认提供两个汉字之间的空格,可以设置 `autoInsertSpace``false` 关闭 | boolean | `true` | 5.17.0 |
| block | 将按钮宽度调整为其父宽度的选项 | boolean | false | |
| classNames | 语义化结构 class | [Record<SemanticDOM, string>](#semantic-dom) | - | 5.4.0 |
| color | 设置按钮的颜色 | `default` \| `primary` \| `danger` | - | 5.21.0 |
| color | 设置按钮的颜色 | `default` \| `primary` \| `danger` \| [PresetColors](#presetcolors) | - | `default`、`primary` 和 `danger`: 5.21.0, `PresetColors`: 5.23.0 |
| danger | 语法糖,设置危险按钮。当设置 `color` 时会以后者为准 | boolean | false | |
| disabled | 设置按钮失效状态 | boolean | false | |
| ghost | 幽灵属性,使按钮背景透明 | boolean | false | |
@ -88,6 +88,10 @@ group:
支持原生 button 的其他所有属性。
### PresetColors
> type PresetColors = 'blue' | 'purple' | 'cyan' | 'green' | 'magenta' | 'pink' | 'red' | 'orange' | 'yellow' | 'volcano' | 'geekblue' | 'lime' | 'gold';
## Semantic DOM
<code src="./demo/_semantic.tsx" simplify="true"></code>

View File

@ -2,7 +2,8 @@ import type { CSSInterpolation, CSSObject } from '@ant-design/cssinjs';
import { unit } from '@ant-design/cssinjs';
import { genFocusStyle } from '../../style';
import type { GenerateStyle } from '../../theme/internal';
import { PresetColors } from '../../theme/interface';
import type { GenerateStyle, PresetColorKey } from '../../theme/internal';
import { genStyleHooks, mergeToken } from '../../theme/internal';
import type { ButtonVariantType } from '../buttonHelpers';
import genGroupStyle from './group';
@ -14,6 +15,7 @@ export type { ComponentToken };
// ============================== Shared ==============================
const genSharedButtonStyle: GenerateStyle<ButtonToken, CSSObject> = (token): CSSObject => {
const { componentCls, iconCls, fontWeight } = token;
return {
[componentCls]: {
outline: 'none',
@ -234,6 +236,95 @@ const genTextLinkButtonStyle = (
});
// =============================== Color ==============================
const genPresetColorStyle: GenerateStyle<ButtonToken, CSSObject> = (token) => {
const { componentCls } = token;
return PresetColors.reduce<CSSObject>((prev: CSSObject, colorKey: PresetColorKey) => {
const darkColor = token[`${colorKey}6`];
const lightColor = token[`${colorKey}1`];
const hoverColor = token[`${colorKey}5`];
const lightHoverColor = token[`${colorKey}2`];
const lightBorderColor = token[`${colorKey}3`];
const activeColor = token[`${colorKey}7`];
const boxShadow = `0 ${token.controlOutlineWidth} 0 ${token[`${colorKey}1`]}`;
return {
...prev,
[`&${componentCls}-color-${colorKey}`]: {
color: darkColor,
boxShadow,
...genSolidButtonStyle(
token,
token.colorTextLightSolid,
darkColor,
{
background: hoverColor,
},
{
background: activeColor,
},
),
...genOutlinedDashedButtonStyle(
token,
darkColor,
token.colorBgContainer,
{
color: hoverColor,
borderColor: hoverColor,
background: token.colorBgContainer,
},
{
color: activeColor,
borderColor: activeColor,
background: token.colorBgContainer,
},
),
...genDashedButtonStyle(token),
...genFilledButtonStyle(
token,
lightColor,
{
background: lightHoverColor,
},
{
background: lightBorderColor,
},
),
...genTextLinkButtonStyle(
token,
darkColor,
'link',
{
color: hoverColor,
},
{
color: activeColor,
},
),
...genTextLinkButtonStyle(
token,
darkColor,
'text',
{
color: hoverColor,
background: lightColor,
},
{
color: activeColor,
background: lightBorderColor,
},
),
},
};
}, {});
};
const genDefaultButtonStyle: GenerateStyle<ButtonToken, CSSObject> = (token) => ({
color: token.defaultColor,
@ -447,6 +538,8 @@ const genColorButtonStyle: GenerateStyle<ButtonToken> = (token) => {
[`${componentCls}-color-default`]: genDefaultButtonStyle(token),
[`${componentCls}-color-primary`]: genPrimaryButtonStyle(token),
[`${componentCls}-color-dangerous`]: genDangerousStyle(token),
...genPresetColorStyle(token),
};
};