Merge branch 'feature' into feature/add-variant-underlined

This commit is contained in:
ustcfury 2024-10-30 21:57:45 +08:00 committed by GitHub
commit e9e16945ca
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 424 additions and 70 deletions

View File

@ -430,12 +430,14 @@ exports[`renders components/collapse/demo/collapsible.tsx extend context correct
class="ant-collapse-item ant-collapse-item-active"
>
<div
aria-disabled="false"
aria-expanded="true"
class="ant-collapse-header ant-collapse-header-collapsible-only"
class="ant-collapse-header ant-collapse-collapsible-header"
>
<div
aria-disabled="false"
aria-expanded="true"
class="ant-collapse-expand-icon"
role="button"
tabindex="0"
>
<span
aria-label="expanded"
@ -459,7 +461,11 @@ exports[`renders components/collapse/demo/collapsible.tsx extend context correct
</span>
</div>
<span
aria-disabled="false"
aria-expanded="true"
class="ant-collapse-header-text"
role="button"
tabindex="0"
>
This panel can only be collapsed by clicking text
</span>
@ -490,12 +496,14 @@ exports[`renders components/collapse/demo/collapsible.tsx extend context correct
class="ant-collapse-item ant-collapse-item-active"
>
<div
aria-disabled="false"
aria-expanded="true"
class="ant-collapse-header ant-collapse-icon-collapsible-only"
class="ant-collapse-header ant-collapse-collapsible-icon"
>
<div
aria-disabled="false"
aria-expanded="true"
class="ant-collapse-expand-icon"
role="button"
tabindex="0"
>
<span
aria-label="expanded"
@ -552,7 +560,7 @@ exports[`renders components/collapse/demo/collapsible.tsx extend context correct
<div
aria-disabled="true"
aria-expanded="false"
class="ant-collapse-header"
class="ant-collapse-header ant-collapse-collapsible-disabled"
role="button"
tabindex="-1"
>

View File

@ -424,12 +424,14 @@ exports[`renders components/collapse/demo/collapsible.tsx correctly 1`] = `
class="ant-collapse-item ant-collapse-item-active"
>
<div
aria-disabled="false"
aria-expanded="true"
class="ant-collapse-header ant-collapse-header-collapsible-only"
class="ant-collapse-header ant-collapse-collapsible-header"
>
<div
aria-disabled="false"
aria-expanded="true"
class="ant-collapse-expand-icon"
role="button"
tabindex="0"
>
<span
aria-label="expanded"
@ -453,7 +455,11 @@ exports[`renders components/collapse/demo/collapsible.tsx correctly 1`] = `
</span>
</div>
<span
aria-disabled="false"
aria-expanded="true"
class="ant-collapse-header-text"
role="button"
tabindex="0"
>
This panel can only be collapsed by clicking text
</span>
@ -484,12 +490,14 @@ exports[`renders components/collapse/demo/collapsible.tsx correctly 1`] = `
class="ant-collapse-item ant-collapse-item-active"
>
<div
aria-disabled="false"
aria-expanded="true"
class="ant-collapse-header ant-collapse-icon-collapsible-only"
class="ant-collapse-header ant-collapse-collapsible-icon"
>
<div
aria-disabled="false"
aria-expanded="true"
class="ant-collapse-expand-icon"
role="button"
tabindex="0"
>
<span
aria-label="expanded"
@ -546,7 +554,7 @@ exports[`renders components/collapse/demo/collapsible.tsx correctly 1`] = `
<div
aria-disabled="true"
aria-expanded="false"
class="ant-collapse-header"
class="ant-collapse-header ant-collapse-collapsible-disabled"
role="button"
tabindex="-1"
>

View File

@ -149,7 +149,15 @@ export const genBaseStyle: GenerateStyle<CollapseToken> = (token) => {
},
},
[`${componentCls}-icon-collapsible-only`]: {
[`${componentCls}-collapsible-header`]: {
cursor: 'default',
[`${componentCls}-header-text`]: {
flex: 'none',
cursor: 'pointer',
},
},
[`${componentCls}-collapsible-icon`]: {
cursor: 'unset',
[`${componentCls}-expand-icon`]: {

View File

@ -141,6 +141,112 @@ exports[`renders components/descriptions/demo/basic.tsx extend context correctly
exports[`renders components/descriptions/demo/basic.tsx extend context correctly 2`] = `[]`;
exports[`renders components/descriptions/demo/block.tsx extend context correctly 1`] = `
<div
class="ant-descriptions ant-descriptions-bordered"
>
<div
class="ant-descriptions-header"
>
<div
class="ant-descriptions-title"
>
User Info
</div>
</div>
<div
class="ant-descriptions-view"
>
<table>
<tbody>
<tr
class="ant-descriptions-row"
>
<th
class="ant-descriptions-item-label"
colspan="1"
>
<span>
UserName
</span>
</th>
<td
class="ant-descriptions-item-content"
colspan="1"
>
<span>
Zhou Maomao
</span>
</td>
</tr>
<tr
class="ant-descriptions-row"
>
<th
class="ant-descriptions-item-label"
colspan="1"
>
<span>
Live
</span>
</th>
<td
class="ant-descriptions-item-content"
colspan="1"
>
<span>
Hangzhou, Zhejiang
</span>
</td>
</tr>
<tr
class="ant-descriptions-row"
>
<th
class="ant-descriptions-item-label"
colspan="1"
>
<span>
Remark
</span>
</th>
<td
class="ant-descriptions-item-content"
colspan="1"
>
<span>
empty
</span>
</td>
</tr>
<tr
class="ant-descriptions-row"
>
<th
class="ant-descriptions-item-label"
colspan="1"
>
<span>
Address
</span>
</th>
<td
class="ant-descriptions-item-content"
colspan="1"
>
<span>
No. 18, Wantang Road, Xihu District, Hangzhou, Zhejiang, China
</span>
</td>
</tr>
</tbody>
</table>
</div>
</div>
`;
exports[`renders components/descriptions/demo/block.tsx extend context correctly 2`] = `[]`;
exports[`renders components/descriptions/demo/border.tsx extend context correctly 1`] = `
<div
class="ant-descriptions ant-descriptions-bordered"

View File

@ -127,6 +127,106 @@ exports[`renders components/descriptions/demo/basic.tsx correctly 1`] = `
</div>
`;
exports[`renders components/descriptions/demo/block.tsx correctly 1`] = `
<div
class="ant-descriptions ant-descriptions-bordered"
>
<div
class="ant-descriptions-header"
>
<div
class="ant-descriptions-title"
>
User Info
</div>
</div>
<div
class="ant-descriptions-view"
>
<table>
<tbody>
<tr
class="ant-descriptions-row"
>
<th
class="ant-descriptions-item-label"
colspan="1"
>
<span>
UserName
</span>
</th>
<td
class="ant-descriptions-item-content"
colspan="1"
>
<span>
Zhou Maomao
</span>
</td>
<th
class="ant-descriptions-item-label"
colspan="1"
>
<span>
Live
</span>
</th>
<td
class="ant-descriptions-item-content"
colspan="3"
>
<span>
Hangzhou, Zhejiang
</span>
</td>
</tr>
<tr
class="ant-descriptions-row"
>
<th
class="ant-descriptions-item-label"
colspan="1"
>
<span>
Remark
</span>
</th>
<td
class="ant-descriptions-item-content"
colspan="5"
>
<span>
empty
</span>
</td>
</tr>
<tr
class="ant-descriptions-row"
>
<th
class="ant-descriptions-item-label"
colspan="1"
>
<span>
Address
</span>
</th>
<td
class="ant-descriptions-item-content"
colspan="5"
>
<span>
No. 18, Wantang Road, Xihu District, Hangzhou, Zhejiang, China
</span>
</td>
</tr>
</tbody>
</table>
</div>
</div>
`;
exports[`renders components/descriptions/demo/border.tsx correctly 1`] = `
<div
class="ant-descriptions ant-descriptions-bordered"

View File

@ -78,6 +78,30 @@ describe('Descriptions', () => {
expect(container.querySelectorAll('.ant-descriptions-item')[2]).toHaveAttribute('colSpan', '1');
});
it('span = filled', () => {
const { container } = render(
<Descriptions
column={3}
items={[
{ label: '0', children: '', span: 2 },
{ label: '1', children: '' },
{ label: '2', children: '' },
{ label: '3', children: '', span: 'filled' },
{ label: '4', children: '', span: 'filled' },
{ label: '5', children: '' },
{ label: '6', children: '', span: 1 },
]}
/>,
);
expect(container.querySelectorAll('.ant-descriptions-item')[0]).toHaveAttribute('colSpan', '2');
expect(container.querySelectorAll('.ant-descriptions-item')[1]).toHaveAttribute('colSpan', '1');
expect(container.querySelectorAll('.ant-descriptions-item')[2]).toHaveAttribute('colSpan', '1');
expect(container.querySelectorAll('.ant-descriptions-item')[3]).toHaveAttribute('colSpan', '2');
expect(container.querySelectorAll('.ant-descriptions-item')[4]).toHaveAttribute('colSpan', '3');
expect(container.querySelectorAll('.ant-descriptions-item')[5]).toHaveAttribute('colSpan', '1');
expect(container.querySelectorAll('.ant-descriptions-item')[6]).toHaveAttribute('colSpan', '2');
});
it('column is number', () => {
const wrapper = render(
<Descriptions column={3}>

View File

@ -0,0 +1,7 @@
## zh-CN
整行的展示。
## en-US
Display of the entire line.

View File

@ -0,0 +1,29 @@
import React from 'react';
import { Descriptions } from 'antd';
import type { DescriptionsProps } from 'antd';
const items: DescriptionsProps['items'] = [
{
label: 'UserName',
children: 'Zhou Maomao',
},
{
label: 'Live',
span: 'filled', // span = 2
children: 'Hangzhou, Zhejiang',
},
{
label: 'Remark',
span: 'filled', // span = 3
children: 'empty',
},
{
label: 'Address',
span: 1, // span will be 3 and warning for span is not align to the end
children: 'No. 18, Wantang Road, Xihu District, Hangzhou, Zhejiang, China',
},
];
const App: React.FC = () => <Descriptions bordered title="User Info" items={items} />;
export default App;

View File

@ -1,7 +1,7 @@
## zh-CN
带边框和背景颜色列表
复杂文本的情况
## en-US
Descriptions with border and background color.
The situation of complex text.

View File

@ -23,10 +23,15 @@ export default function useItems(
const responsiveItems = React.useMemo<InternalDescriptionsItemType[]>(
() =>
mergedItems.map(({ span, ...restItem }) => ({
...restItem,
span: typeof span === 'number' ? span : matchScreen(screens, span),
})),
mergedItems.map(({ span, ...restItem }) => {
if (span === 'filled') {
return { ...restItem, filled: true };
}
return {
...restItem,
span: typeof span === 'number' ? span : matchScreen(screens, span),
};
}),
[mergedItems, screens],
);

View File

@ -3,65 +3,61 @@ import { useMemo } from 'react';
import type { InternalDescriptionsItemType } from '..';
import { devUseWarning } from '../../_util/warning';
function getFilledItem(
rowItem: InternalDescriptionsItemType,
rowRestCol: number,
span?: number,
): [item: InternalDescriptionsItemType, exceed: boolean] {
let clone = rowItem;
let exceed = false;
if (span === undefined || span > rowRestCol) {
clone = {
...rowItem,
span: rowRestCol,
};
exceed = span !== undefined;
}
return [clone, exceed];
}
// Calculate the sum of span in a row
function getCalcRows(
rowItems: InternalDescriptionsItemType[],
mergedColumn: number,
): [rows: InternalDescriptionsItemType[][], exceed: boolean] {
const rows: InternalDescriptionsItemType[][] = [];
let rows: InternalDescriptionsItemType[][] = [];
let tmpRow: InternalDescriptionsItemType[] = [];
let rowRestCol = mergedColumn;
let exceed = false;
let count = 0;
rowItems
.filter((n) => n)
.forEach((rowItem, index) => {
const span = rowItem?.span;
const mergedSpan = span || 1;
.forEach((rowItem) => {
const { filled, ...restItem } = rowItem;
// Additional handle last one
if (index === rowItems.length - 1) {
const [item, itemExceed] = getFilledItem(rowItem, rowRestCol, span);
exceed = exceed || itemExceed;
tmpRow.push(item);
if (filled) {
tmpRow.push(restItem);
rows.push(tmpRow);
// reset
tmpRow = [];
count = 0;
return;
}
if (mergedSpan < rowRestCol) {
rowRestCol -= mergedSpan;
tmpRow.push(rowItem);
} else {
const [item, itemExceed] = getFilledItem(rowItem, rowRestCol, mergedSpan);
exceed = exceed || itemExceed;
tmpRow.push(item);
const restSpan = mergedColumn - count;
count += rowItem.span || 1;
if (count >= mergedColumn) {
if (count > mergedColumn) {
exceed = true;
tmpRow.push({ ...restItem, span: restSpan });
} else {
tmpRow.push(restItem);
}
rows.push(tmpRow);
rowRestCol = mergedColumn;
// reset
tmpRow = [];
count = 0;
} else {
tmpRow.push(restItem);
}
});
if (tmpRow.length > 0) {
rows.push(tmpRow);
}
rows = rows.map((rows) => {
const count = rows.reduce((acc, item) => acc + (item.span || 1), 0);
if (count < mergedColumn) {
// If the span of the last element in the current row is less than the column, then add its span to the remaining columns
const last = rows[rows.length - 1];
last.span = mergedColumn - count + 1;
return rows;
}
return rows;
});
return [rows, exceed];
}

View File

@ -71,6 +71,7 @@ const items: DescriptionsProps['items'] = [
<code src="./demo/style.tsx" debug>Customize label & wrapper style</code>
<code src="./demo/jsx.tsx" debug>JSX demo</code>
<code src="./demo/component-token.tsx" debug>Component Token</code>
<code src="./demo/block.tsx">row</code>
## API
@ -98,7 +99,7 @@ Common props ref[Common props](/docs/react/common-props)
| contentStyle | Customize content style | CSSProperties | - | 4.9.0 |
| label | The description of the content | ReactNode | - | |
| labelStyle | Customize label style | CSSProperties | - | 4.9.0 |
| span | The number of columns included | number \| [Screens](/components/grid#col) | 1 | `screens: 5.9.0` |
| span | The number of columns included(`filled` Fill the remaining part of the current row) | number \| `filled` \| [Screens](/components/grid#col) | 1 | `screens: 5.9.0`, `filled: 5.22.0` |
> The number of span Description.Item. Span={2} takes up the width of two DescriptionItems. When both `style` and `labelStyle`(or `contentStyle`) configured, both of them will work. And next one will overwrite first when conflict.

View File

@ -22,10 +22,12 @@ interface CompoundedComponent {
export interface InternalDescriptionsItemType extends DescriptionsItemProps {
key?: React.Key;
filled?: boolean;
}
export interface DescriptionsItemType extends Omit<InternalDescriptionsItemType, 'span'> {
span?: number | { [key in Breakpoint]?: number };
export interface DescriptionsItemType
extends Omit<InternalDescriptionsItemType, 'span' | 'filled'> {
span?: number | 'filled' | { [key in Breakpoint]?: number };
}
export interface DescriptionsProps {

View File

@ -72,6 +72,7 @@ const items: DescriptionsProps['items'] = [
<code src="./demo/style.tsx" debug>自定义 label & wrapper 样式</code>
<code src="./demo/jsx.tsx" debug>JSX demo</code>
<code src="./demo/component-token.tsx" debug>组件 Token</code>
<code src="./demo/block.tsx">整行</code>
## API
@ -99,7 +100,7 @@ const items: DescriptionsProps['items'] = [
| contentStyle | 自定义内容样式 | CSSProperties | - | 4.9.0 |
| label | 内容的描述 | ReactNode | - | |
| labelStyle | 自定义标签样式 | CSSProperties | - | 4.9.0 |
| span | 包含列的数量 | number \| [Screens](/components/grid-cn#col) | 1 | `screens: 5.9.0` |
| span | 包含列的数量`filled` 铺满当前行剩余部分) | number\| `filled` \| [Screens](/components/grid-cn#col) | 1 | `screens: 5.9.0``filled: 5.22.0` |
> span 是 Description.Item 的数量。 span={2} 会占用两个 DescriptionItem 的宽度。当同时配置 `style``labelStyle`(或 `contentStyle`)时,两者会同时作用。样式冲突时,后者会覆盖前者。

View File

@ -24,7 +24,8 @@ export interface OTPRef {
nativeElement: HTMLDivElement;
}
export interface OTPProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange'> {
export interface OTPProps
extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange' | 'onInput'> {
prefixCls?: string;
length?: number;
@ -48,6 +49,8 @@ export interface OTPProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'on
mask?: boolean | string;
type?: React.HTMLInputTypeAttribute;
onInput?: (value: string[]) => void;
}
function strToArr(str: string) {
@ -69,6 +72,7 @@ const OTP = React.forwardRef<OTPRef, OTPProps>((props, ref) => {
autoFocus,
mask,
type,
onInput,
...restProps
} = props;
@ -146,6 +150,10 @@ const OTP = React.forwardRef<OTPRef, OTPProps>((props, ref) => {
const triggerValueCellsChange = useEvent((nextValueCells: string[]) => {
setValueCells(nextValueCells);
if (onInput) {
onInput(nextValueCells);
}
// Trigger if all cells are filled
if (
onChange &&

View File

@ -172,4 +172,23 @@ describe('Input.OTP', () => {
const { container } = render(<OTP type="number" />);
expect(container.querySelector('input')).toHaveAttribute('type', 'number');
});
it('should call onInput with a string array when input changes', () => {
const onInput = jest.fn();
const { container } = render(<OTP length={4} onInput={onInput} />);
const inputs = Array.from(container.querySelectorAll('input'));
fireEvent.input(inputs[0], { target: { value: '1' } });
expect(onInput).toHaveBeenCalledWith(['1']);
fireEvent.input(inputs[2], { target: { value: '3' } });
expect(onInput).toHaveBeenCalledWith(['1', '', '3']);
fireEvent.input(inputs[1], { target: { value: '2' } });
expect(onInput).toHaveBeenCalledWith(['1', '2', '3']);
fireEvent.input(inputs[3], { target: { value: '4' } });
expect(onInput).toHaveBeenCalledWith(['1', '2', '3', '4']);
});
});

View File

@ -11,8 +11,13 @@ const App: React.FC = () => {
console.log('onChange:', text);
};
const onInput: OTPProps['onInput'] = (value) => {
console.log('onInput:', value);
};
const sharedProps: OTPProps = {
onChange,
onInput,
};
return (

View File

@ -140,7 +140,8 @@ Added in `5.16.0`.
| size | The size of the input box | `small` \| `middle` \| `large` | `middle` | |
| variant | Variants of Input | `outlined` \| `borderless` \| `filled` | `outlined` | |
| value | The input content value | string | - | |
| onChange | Trigger when all the fields are filled | function(value: string) | - | |
| onChange | Trigger when all the fields are filled | (value: string) => void | - | |
| onInput | Trigger when the input value changes | (value: string[]) => void | - | `5.22.0` |
#### VisibilityToggle

View File

@ -141,7 +141,8 @@ interface CountConfig {
| size | 输入框大小 | `small` \| `middle` \| `large` | `middle` | |
| variant | 形态变体 | `outlined` \| `borderless` \| `filled` | `outlined` | |
| value | 输入框内容 | string | - | |
| onChange | 当输入框内容全部填充时触发回调 | function(value: string) | - | |
| onChange | 当输入框内容全部填充时触发回调 | (value: string) => void | - | |
| onInput | 输入值变化时触发的回调 | (value: string[]) => void | - | `5.22.0` |
#### VisibilityToggle

View File

@ -47,7 +47,9 @@ const App: React.FC = () => {
components: {
Tree: {
nodeHoverBg: '#fff2f0',
nodeHoverColor: '#1677ff',
nodeSelectedBg: '#ffa39e',
nodeSelectedColor: '#fff',
indentSize: 80,
},
},

View File

@ -23,11 +23,21 @@ export interface TreeSharedToken {
* @descEN Background color of hovered node
*/
nodeHoverBg: string;
/**
* @desc
* @descEN Text color of hovered node
*/
nodeHoverColor: string;
/**
* @desc
* @descEN Background color of selected node
*/
nodeSelectedBg: string;
/**
* @desc
* @descEN Text color of selected node
*/
nodeSelectedColor: string;
}
export interface ComponentToken extends TreeSharedToken {
@ -194,6 +204,16 @@ export const genBaseStyle = (prefixCls: string, token: TreeToken): CSSObject =>
},
},
// not disable
[`&:not(${treeNodeCls}-disabled)`]: {
// >>> Title
[`${treeCls}-node-content-wrapper`]: {
'&:hover': {
color: token.nodeHoverColor,
},
},
},
[`&-active ${treeCls}-node-content-wrapper`]: {
background: token.controlItemBgHover,
},
@ -354,6 +374,7 @@ export const genBaseStyle = (prefixCls: string, token: TreeToken): CSSObject =>
},
[`&${treeCls}-node-selected`]: {
color: token.nodeSelectedColor,
backgroundColor: nodeSelectedBg,
},
@ -558,7 +579,9 @@ export const initComponentToken = (token: AliasToken): TreeSharedToken => {
titleHeight,
indentSize: titleHeight,
nodeHoverBg: controlItemBgHover,
nodeHoverColor: token.colorText,
nodeSelectedBg: controlItemBgActive,
nodeSelectedColor: token.colorText,
};
};

View File

@ -122,7 +122,7 @@
"dayjs": "^1.11.11",
"rc-cascader": "~3.29.0",
"rc-checkbox": "~3.3.0",
"rc-collapse": "~3.8.0",
"rc-collapse": "~3.9.0",
"rc-dialog": "~9.6.0",
"rc-drawer": "~7.2.0",
"rc-dropdown": "~4.2.0",
@ -344,4 +344,4 @@
"tnpm": {
"mode": "npm"
}
}
}