fix: Lots of Descriptions (#21542)

* refactor

* class name

* update demo snapshot

* clean up

* update snapshot

* fix test case

* more test case

* adjust logic
This commit is contained in:
二货机器人 2020-02-23 20:48:16 +08:00 committed by GitHub
parent 8d7f1dadef
commit 1fad2b0277
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 459 additions and 388 deletions

View File

@ -0,0 +1,67 @@
import * as React from 'react';
import classNames from 'classnames';
export interface CellProps {
itemPrefixCls: string;
span: number;
className?: string;
component: string;
style?: React.CSSProperties;
bordered?: boolean;
label?: React.ReactNode;
content?: React.ReactNode;
colon?: boolean;
}
const Cell: React.FC<CellProps> = ({
itemPrefixCls,
component,
span,
className,
style,
bordered,
label,
content,
colon,
}) => {
const Component = component as any;
if (bordered) {
return (
<Component
className={classNames(
{
[`${itemPrefixCls}-item-label`]: label,
[`${itemPrefixCls}-item-content`]: content,
},
className,
)}
style={style}
colSpan={span}
>
{label || content}
</Component>
);
}
return (
<Component
className={classNames(`${itemPrefixCls}-item`, className)}
style={style}
colSpan={span}
>
{label && (
<span
className={classNames(`${itemPrefixCls}-item-label`, {
[`${itemPrefixCls}-item-colon`]: !bordered && colon,
})}
>
{label}
</span>
)}
{content && <span className={classNames(`${itemPrefixCls}-item-content`)}>{content}</span>}
</Component>
);
};
export default Cell;

View File

@ -1,72 +0,0 @@
import * as React from 'react';
import classNames from 'classnames';
import { DescriptionsItemProps } from './index';
interface ColProps {
child: React.ReactElement<DescriptionsItemProps>;
bordered: boolean;
colon: boolean;
type?: 'label' | 'content';
layout?: 'horizontal' | 'vertical';
}
const Col: React.SFC<ColProps> = props => {
const { child, bordered, colon, type, layout } = props;
const { prefixCls, label, className, children, span = 1 } = child.props;
const labelProps: any = {
className: classNames(`${prefixCls}-item-label`, {
[`${prefixCls}-item-colon`]: colon,
[`${prefixCls}-item-no-label`]: !label,
}),
key: 'label',
};
if (layout === 'vertical') {
labelProps.colSpan = span * 2 - 1;
}
if (bordered) {
if (type === 'label') {
return <th {...labelProps}>{label}</th>;
}
return (
<td
className={classNames(`${prefixCls}-item-content`, className)}
key="content"
colSpan={span * 2 - 1}
>
{children}
</td>
);
}
if (layout === 'vertical') {
if (type === 'content') {
return (
<td colSpan={span} className={classNames(`${prefixCls}-item`, className)}>
<span className={`${prefixCls}-item-content`} key="content">
{children}
</span>
</td>
);
}
return (
<td colSpan={span} className={classNames(`${prefixCls}-item`, className)}>
<span
className={classNames(`${prefixCls}-item-label`, { [`${prefixCls}-item-colon`]: colon })}
key="label"
>
{label}
</span>
</td>
);
}
return (
<td colSpan={span} className={classNames(`${prefixCls}-item`, className)}>
<span {...labelProps}>{label}</span>
<span className={`${prefixCls}-item-content`} key="content">
{children}
</span>
</td>
);
};
export default Col;

View File

@ -0,0 +1,15 @@
import * as React from 'react';
export interface DescriptionsItemProps {
prefixCls?: string;
className?: string;
style?: React.CSSProperties;
label?: React.ReactNode;
children: React.ReactNode;
span?: number;
}
const DescriptionsItem: React.SFC<DescriptionsItemProps> = ({ children }) =>
children as JSX.Element;
export default DescriptionsItem;

View File

@ -0,0 +1,118 @@
import * as React from 'react';
import { DescriptionsItemProps } from './Item';
import Cell from './Cell';
interface CellConfig {
component: string | [string, string];
type: string;
showLabel?: boolean;
showContent?: boolean;
}
function renderCells(
items: React.ReactElement<DescriptionsItemProps>[],
{ colon, prefixCls, bordered }: RowProps,
{ component, type, showLabel, showContent }: CellConfig,
) {
return items.map(
(
{
props: {
label,
children,
prefixCls: itemPrefixCls = prefixCls,
className,
style,
span = 1,
},
key,
},
index,
) => {
if (typeof component === 'string') {
return (
<Cell
key={`${type}-${key || index}`}
className={className}
style={style}
span={span}
colon={colon}
component={component}
itemPrefixCls={itemPrefixCls}
bordered={bordered}
label={showLabel ? label : null}
content={showContent ? children : null}
/>
);
}
return (
<>
<Cell
key={`label-${key || index}`}
className={className}
style={style}
span={1}
colon={colon}
component={component[0]}
itemPrefixCls={itemPrefixCls}
bordered={bordered}
label={label}
/>
<Cell
key={`content-${key || index}`}
className={className}
style={style}
span={span * 2 - 1}
component={component[1]}
itemPrefixCls={itemPrefixCls}
bordered={bordered}
content={children}
/>
</>
);
},
);
}
export interface RowProps {
prefixCls: string;
vertical: boolean;
children: React.ReactElement<DescriptionsItemProps>[];
bordered?: boolean;
colon: boolean;
index: number;
}
const Row: React.FC<RowProps> = props => {
const { prefixCls, vertical, children, index, bordered } = props;
if (vertical) {
return (
<>
<tr key={`label-${index}`} className={`${prefixCls}-row`}>
{renderCells(children, props, { component: 'th', type: 'label', showLabel: true })}
</tr>
<tr key={`content-${index}`} className={`${prefixCls}-row`}>
{renderCells(children, props, {
component: 'td',
type: 'content',
showContent: true,
})}
</tr>
</>
);
}
return (
<tr key={index} className={`${prefixCls}-row`}>
{renderCells(children, props, {
component: bordered ? ['th', 'td'] : 'td',
type: 'item',
showLabel: true,
showContent: true,
})}
</tr>
);
};
export default Row;

View File

@ -121,7 +121,8 @@ exports[`renders ./components/descriptions/demo/border.md correctly 1`] = `
class="ant-descriptions-row"
>
<th
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
colspan="1"
>
Product
</th>
@ -132,7 +133,8 @@ exports[`renders ./components/descriptions/demo/border.md correctly 1`] = `
Cloud Database
</td>
<th
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
colspan="1"
>
Billing Mode
</th>
@ -143,7 +145,8 @@ exports[`renders ./components/descriptions/demo/border.md correctly 1`] = `
Prepaid
</td>
<th
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
colspan="1"
>
Automatic Renewal
</th>
@ -158,7 +161,8 @@ exports[`renders ./components/descriptions/demo/border.md correctly 1`] = `
class="ant-descriptions-row"
>
<th
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
colspan="1"
>
Order time
</th>
@ -169,7 +173,8 @@ exports[`renders ./components/descriptions/demo/border.md correctly 1`] = `
2018-04-24 18:00:00
</td>
<th
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
colspan="1"
>
Usage Time
</th>
@ -184,7 +189,8 @@ exports[`renders ./components/descriptions/demo/border.md correctly 1`] = `
class="ant-descriptions-row"
>
<th
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
colspan="1"
>
Status
</th>
@ -210,7 +216,8 @@ exports[`renders ./components/descriptions/demo/border.md correctly 1`] = `
class="ant-descriptions-row"
>
<th
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
colspan="1"
>
Negotiated Amount
</th>
@ -221,7 +228,8 @@ exports[`renders ./components/descriptions/demo/border.md correctly 1`] = `
$80.00
</td>
<th
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
colspan="1"
>
Discount
</th>
@ -232,7 +240,8 @@ exports[`renders ./components/descriptions/demo/border.md correctly 1`] = `
$20.00
</td>
<th
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
colspan="1"
>
Official Receipts
</th>
@ -247,7 +256,8 @@ exports[`renders ./components/descriptions/demo/border.md correctly 1`] = `
class="ant-descriptions-row"
>
<th
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
colspan="1"
>
Config Info
</th>
@ -294,7 +304,8 @@ exports[`renders ./components/descriptions/demo/responsive.md correctly 1`] = `
class="ant-descriptions-row"
>
<th
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
colspan="1"
>
Product
</th>
@ -305,7 +316,8 @@ exports[`renders ./components/descriptions/demo/responsive.md correctly 1`] = `
Cloud Database
</td>
<th
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
colspan="1"
>
Billing
</th>
@ -316,7 +328,8 @@ exports[`renders ./components/descriptions/demo/responsive.md correctly 1`] = `
Prepaid
</td>
<th
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
colspan="1"
>
time
</th>
@ -331,7 +344,8 @@ exports[`renders ./components/descriptions/demo/responsive.md correctly 1`] = `
class="ant-descriptions-row"
>
<th
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
colspan="1"
>
Amount
</th>
@ -342,7 +356,8 @@ exports[`renders ./components/descriptions/demo/responsive.md correctly 1`] = `
$80.00
</td>
<th
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
colspan="1"
>
Discount
</th>
@ -353,7 +368,8 @@ exports[`renders ./components/descriptions/demo/responsive.md correctly 1`] = `
$20.00
</td>
<th
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
colspan="1"
>
Official
</th>
@ -368,7 +384,8 @@ exports[`renders ./components/descriptions/demo/responsive.md correctly 1`] = `
class="ant-descriptions-row"
>
<th
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
colspan="1"
>
Config Info
</th>
@ -479,7 +496,8 @@ exports[`renders ./components/descriptions/demo/size.md correctly 1`] = `
class="ant-descriptions-row"
>
<th
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
colspan="1"
>
Product
</th>
@ -490,7 +508,8 @@ exports[`renders ./components/descriptions/demo/size.md correctly 1`] = `
Cloud Database
</td>
<th
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
colspan="1"
>
Billing
</th>
@ -501,7 +520,8 @@ exports[`renders ./components/descriptions/demo/size.md correctly 1`] = `
Prepaid
</td>
<th
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
colspan="1"
>
time
</th>
@ -516,7 +536,8 @@ exports[`renders ./components/descriptions/demo/size.md correctly 1`] = `
class="ant-descriptions-row"
>
<th
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
colspan="1"
>
Amount
</th>
@ -527,7 +548,8 @@ exports[`renders ./components/descriptions/demo/size.md correctly 1`] = `
$80.00
</td>
<th
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
colspan="1"
>
Discount
</th>
@ -538,7 +560,8 @@ exports[`renders ./components/descriptions/demo/size.md correctly 1`] = `
$20.00
</td>
<th
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
colspan="1"
>
Official
</th>
@ -553,7 +576,8 @@ exports[`renders ./components/descriptions/demo/size.md correctly 1`] = `
class="ant-descriptions-row"
>
<th
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
colspan="1"
>
Config Info
</th>
@ -716,7 +740,7 @@ exports[`renders ./components/descriptions/demo/vertical.md correctly 1`] = `
<tr
class="ant-descriptions-row"
>
<td
<th
class="ant-descriptions-item"
colspan="1"
>
@ -725,8 +749,8 @@ exports[`renders ./components/descriptions/demo/vertical.md correctly 1`] = `
>
UserName
</span>
</td>
<td
</th>
<th
class="ant-descriptions-item"
colspan="1"
>
@ -735,8 +759,8 @@ exports[`renders ./components/descriptions/demo/vertical.md correctly 1`] = `
>
Telephone
</span>
</td>
<td
</th>
<th
class="ant-descriptions-item"
colspan="1"
>
@ -745,7 +769,7 @@ exports[`renders ./components/descriptions/demo/vertical.md correctly 1`] = `
>
Live
</span>
</td>
</th>
</tr>
<tr
class="ant-descriptions-row"
@ -784,7 +808,7 @@ exports[`renders ./components/descriptions/demo/vertical.md correctly 1`] = `
<tr
class="ant-descriptions-row"
>
<td
<th
class="ant-descriptions-item"
colspan="2"
>
@ -793,8 +817,8 @@ exports[`renders ./components/descriptions/demo/vertical.md correctly 1`] = `
>
Address
</span>
</td>
<td
</th>
<th
class="ant-descriptions-item"
colspan="1"
>
@ -803,7 +827,7 @@ exports[`renders ./components/descriptions/demo/vertical.md correctly 1`] = `
>
Remark
</span>
</td>
</th>
</tr>
<tr
class="ant-descriptions-row"
@ -853,19 +877,19 @@ exports[`renders ./components/descriptions/demo/vertical-border.md correctly 1`]
class="ant-descriptions-row"
>
<th
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
colspan="1"
>
Product
</th>
<th
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
colspan="1"
>
Billing Mode
</th>
<th
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
colspan="1"
>
Automatic Renewal
@ -897,14 +921,14 @@ exports[`renders ./components/descriptions/demo/vertical-border.md correctly 1`]
class="ant-descriptions-row"
>
<th
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
colspan="1"
>
Order time
</th>
<th
class="ant-descriptions-item-label ant-descriptions-item-colon"
colspan="3"
class="ant-descriptions-item-label"
colspan="2"
>
Usage Time
</th>
@ -920,7 +944,7 @@ exports[`renders ./components/descriptions/demo/vertical-border.md correctly 1`]
</td>
<td
class="ant-descriptions-item-content"
colspan="3"
colspan="2"
>
2019-04-24 18:00:00
</td>
@ -929,8 +953,8 @@ exports[`renders ./components/descriptions/demo/vertical-border.md correctly 1`]
class="ant-descriptions-row"
>
<th
class="ant-descriptions-item-label ant-descriptions-item-colon"
colspan="5"
class="ant-descriptions-item-label"
colspan="3"
>
Status
</th>
@ -940,7 +964,7 @@ exports[`renders ./components/descriptions/demo/vertical-border.md correctly 1`]
>
<td
class="ant-descriptions-item-content"
colspan="5"
colspan="3"
>
<span
class="ant-badge ant-badge-status ant-badge-not-a-wrapper"
@ -960,19 +984,19 @@ exports[`renders ./components/descriptions/demo/vertical-border.md correctly 1`]
class="ant-descriptions-row"
>
<th
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
colspan="1"
>
Negotiated Amount
</th>
<th
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
colspan="1"
>
Discount
</th>
<th
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
colspan="1"
>
Official Receipts
@ -1004,8 +1028,8 @@ exports[`renders ./components/descriptions/demo/vertical-border.md correctly 1`]
class="ant-descriptions-row"
>
<th
class="ant-descriptions-item-label ant-descriptions-item-colon"
colspan="5"
class="ant-descriptions-item-label"
colspan="3"
>
Config Info
</th>
@ -1015,7 +1039,7 @@ exports[`renders ./components/descriptions/demo/vertical-border.md correctly 1`]
>
<td
class="ant-descriptions-item-content"
colspan="5"
colspan="3"
>
Data disk type: MongoDB
<br />

View File

@ -51,9 +51,6 @@ exports[`Descriptions Descriptions support style 1`] = `
class="ant-descriptions-item"
colspan="1"
>
<span
class="ant-descriptions-item-label ant-descriptions-item-colon ant-descriptions-item-no-label"
/>
<span
class="ant-descriptions-item-content"
>
@ -268,7 +265,7 @@ exports[`Descriptions vertical layout 1`] = `
<tr
class="ant-descriptions-row"
>
<td
<th
class="ant-descriptions-item"
colspan="1"
>
@ -277,7 +274,7 @@ exports[`Descriptions vertical layout 1`] = `
>
Product
</span>
</td>
</th>
</tr>
<tr
class="ant-descriptions-row"
@ -296,7 +293,7 @@ exports[`Descriptions vertical layout 1`] = `
<tr
class="ant-descriptions-row"
>
<td
<th
class="ant-descriptions-item"
colspan="1"
>
@ -305,7 +302,7 @@ exports[`Descriptions vertical layout 1`] = `
>
Billing
</span>
</td>
</th>
</tr>
<tr
class="ant-descriptions-row"
@ -324,7 +321,7 @@ exports[`Descriptions vertical layout 1`] = `
<tr
class="ant-descriptions-row"
>
<td
<th
class="ant-descriptions-item"
colspan="1"
>
@ -333,7 +330,7 @@ exports[`Descriptions vertical layout 1`] = `
>
time
</span>
</td>
</th>
</tr>
<tr
class="ant-descriptions-row"
@ -352,7 +349,7 @@ exports[`Descriptions vertical layout 1`] = `
<tr
class="ant-descriptions-row"
>
<td
<th
class="ant-descriptions-item"
colspan="1"
>
@ -361,7 +358,7 @@ exports[`Descriptions vertical layout 1`] = `
>
Amount
</span>
</td>
</th>
</tr>
<tr
class="ant-descriptions-row"

View File

@ -30,7 +30,7 @@ describe('Descriptions', () => {
</Descriptions>,
);
expect(wrapper.find('tr')).toHaveLength(5);
expect(wrapper.find('.ant-descriptions-item-no-label')).toHaveLength(1);
expect(wrapper.find('.ant-descriptions-item-label')).toHaveLength(4);
wrapper.unmount();
});
@ -71,7 +71,7 @@ describe('Descriptions', () => {
<Descriptions.Item label="Amount">$80.00</Descriptions.Item>
</Descriptions>,
);
expect(wrapper.instance().getColumn()).toBe(8);
expect(wrapper.find('td').reduce((total, td) => total + td.props().colSpan, 0)).toBe(8);
wrapper.unmount();
});
@ -158,7 +158,7 @@ describe('Descriptions', () => {
</Descriptions>,
);
expect(wrapper.find('Col').key()).toBe('label-bamboo');
expect(wrapper.find('Cell').key()).toBe('item-bamboo');
});
// https://github.com/ant-design/ant-design/issues/19887
@ -178,4 +178,42 @@ describe('Descriptions', () => {
expect(wrapper.render()).toMatchSnapshot();
});
// https://github.com/ant-design/ant-design/issues/20255
it('columns 5 with customize', () => {
const wrapper = mount(
<Descriptions layout="vertical" column={4}>
{/* 1 1 1 1 */}
<Descriptions.Item label="bamboo">bamboo</Descriptions.Item>
<Descriptions.Item label="bamboo">bamboo</Descriptions.Item>
<Descriptions.Item label="bamboo">bamboo</Descriptions.Item>
<Descriptions.Item label="bamboo">bamboo</Descriptions.Item>
{/* 2 2 */}
<Descriptions.Item label="bamboo" span={2}>
bamboo
</Descriptions.Item>
<Descriptions.Item label="bamboo" span={2}>
bamboo
</Descriptions.Item>
{/* 3 1 */}
<Descriptions.Item label="bamboo" span={3}>
bamboo
</Descriptions.Item>
<Descriptions.Item label="bamboo">bamboo</Descriptions.Item>
</Descriptions>,
);
function matchSpan(rowIndex, spans) {
const tr = wrapper.find('tr').at(rowIndex);
const tds = tr.find('th');
expect(tds).toHaveLength(spans.length);
tds.forEach((td, index) => {
expect(td.props().colSpan).toEqual(spans[index]);
});
}
matchSpan(0, [1, 1, 1, 1]);
matchSpan(2, [2, 2]);
matchSpan(4, [3, 1]);
});
});

View File

@ -1,39 +1,95 @@
/* eslint-disable react/no-array-index-key */
import * as React from 'react';
import classNames from 'classnames';
import toArray from 'rc-util/lib/Children/toArray';
import warning from '../_util/warning';
import ResponsiveObserve, {
Breakpoint,
ScreenMap,
responsiveArray,
} from '../_util/responsiveObserve';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
import Col from './Col';
import warning from '../_util/warning';
import { ConfigContext } from '../config-provider';
import Row from './Row';
import DescriptionsItem from './Item';
// https://github.com/smooth-code/react-flatten-children/
function flattenChildren(children: React.ReactNode): JSX.Element[] {
if (!children) {
return [];
const DEFAULT_COLUMN_MAP: Record<Breakpoint, number> = {
xxl: 3,
xl: 3,
lg: 3,
md: 3,
sm: 2,
xs: 1,
};
function getColumn(column: DescriptionsProps['column'], screens: ScreenMap): number {
if (typeof column === 'number') {
return column;
}
return toArray(children).reduce((flatChildren: JSX.Element[], child: JSX.Element) => {
if (child && child.type === React.Fragment) {
return flatChildren.concat(flattenChildren(child.props.children));
if (typeof column === 'object') {
for (let i = 0; i < responsiveArray.length; i++) {
const breakpoint: Breakpoint = responsiveArray[i];
if (screens[breakpoint] && column[breakpoint] !== undefined) {
return column[breakpoint] || DEFAULT_COLUMN_MAP[breakpoint];
}
}
flatChildren.push(child);
return flatChildren;
}, []);
}
return 3;
}
export interface DescriptionsItemProps {
prefixCls?: string;
className?: string;
label?: React.ReactNode;
children: React.ReactNode;
span?: number;
function getFilledItem(
node: React.ReactElement,
span: number | undefined,
rowRestCol: number,
): React.ReactElement {
let clone = node;
if (span === undefined || span > rowRestCol) {
clone = React.cloneElement(node, {
span: rowRestCol,
});
warning(
span === undefined,
'Descriptions',
'Sum of column `span` in a line not match `column` of Descriptions.',
);
}
return clone;
}
const DescriptionsItem: React.SFC<DescriptionsItemProps> = ({ children }) =>
children as JSX.Element;
function getRows(children: React.ReactNode, column: number) {
const childNodes = toArray(children).filter(n => n);
const rows: React.ReactElement[][] = [];
let tmpRow: React.ReactElement[] = [];
let rowRestCol = column;
childNodes.forEach((node, index) => {
const span: number | undefined = node.props?.span;
const mergedSpan = span || 1;
// Additional handle last one
if (index === childNodes.length - 1) {
tmpRow.push(getFilledItem(node, span, rowRestCol));
rows.push(tmpRow);
return;
}
if (mergedSpan < rowRestCol) {
rowRestCol -= mergedSpan;
tmpRow.push(node);
} else {
tmpRow.push(getFilledItem(node, mergedSpan, rowRestCol));
rows.push(tmpRow);
rowRestCol = column;
tmpRow = [];
}
});
return rows;
}
export interface DescriptionsProps {
prefixCls?: string;
@ -48,245 +104,73 @@ export interface DescriptionsProps {
colon?: boolean;
}
/**
* Convert children into `column` groups.
* @param children: DescriptionsItem
* @param column: number
*/
const generateChildrenRows = (
children: React.ReactNode,
column: number,
): React.ReactElement<DescriptionsItemProps>[][] => {
const rows: React.ReactElement<DescriptionsItemProps>[][] = [];
let columns: React.ReactElement<DescriptionsItemProps>[] | null = null;
let leftSpans: number;
function Descriptions({
prefixCls: customizePrefixCls,
title,
column = DEFAULT_COLUMN_MAP,
colon = true,
bordered,
layout,
children,
className,
style,
size,
}: DescriptionsProps) {
const { getPrefixCls, direction } = React.useContext(ConfigContext);
const prefixCls = getPrefixCls('descriptions', customizePrefixCls);
const [screens, setScreens] = React.useState<ScreenMap>({});
const mergedColumn = getColumn(column, screens);
const itemNodes = flattenChildren(children);
itemNodes.forEach((node: React.ReactElement<DescriptionsItemProps>, index: number) => {
let itemNode = node;
if (!columns) {
leftSpans = column;
columns = [];
rows.push(columns);
}
// Always set last span to align the end of Descriptions
const lastItem = index === itemNodes.length - 1;
let lastSpanSame = true;
if (lastItem) {
lastSpanSame = !itemNode.props.span || itemNode.props.span === leftSpans;
itemNode = React.cloneElement(itemNode, {
span: leftSpans,
});
}
// Calculate left fill span
const { span = 1 } = itemNode.props;
columns.push(itemNode);
leftSpans -= span;
if (leftSpans <= 0) {
columns = null;
warning(
leftSpans === 0 && lastSpanSame,
'Descriptions',
'Sum of column `span` in a line not match `column` of Descriptions.',
);
}
});
return rows;
};
const renderRow = (
children: React.ReactElement<DescriptionsItemProps>[],
index: number,
{ prefixCls }: { prefixCls: string },
bordered: boolean,
layout: 'horizontal' | 'vertical',
colon: boolean,
) => {
const renderCol = (
colItem: React.ReactElement<DescriptionsItemProps>,
type: 'label' | 'content',
idx: number,
) => {
return (
<Col
child={colItem}
bordered={bordered}
colon={colon}
type={type}
key={`${type}-${colItem.key || idx}`}
layout={layout}
/>
);
};
const cloneChildren: JSX.Element[] = [];
const cloneContentChildren: JSX.Element[] = [];
flattenChildren(children).forEach(
(childrenItem: React.ReactElement<DescriptionsItemProps>, idx: number) => {
cloneChildren.push(renderCol(childrenItem, 'label', idx));
if (layout === 'vertical') {
cloneContentChildren.push(renderCol(childrenItem, 'content', idx));
} else if (bordered) {
cloneChildren.push(renderCol(childrenItem, 'content', idx));
}
},
);
if (layout === 'vertical') {
return [
<tr className={`${prefixCls}-row`} key={`label-${index}`}>
{cloneChildren}
</tr>,
<tr className={`${prefixCls}-row`} key={`content-${index}`}>
{cloneContentChildren}
</tr>,
];
}
return (
<tr className={`${prefixCls}-row`} key={index}>
{cloneChildren}
</tr>
);
};
const defaultColumnMap = {
xxl: 3,
xl: 3,
lg: 3,
md: 3,
sm: 2,
xs: 1,
};
class Descriptions extends React.Component<
DescriptionsProps,
{
screens: ScreenMap;
}
> {
static defaultProps: DescriptionsProps = {
size: 'default',
column: defaultColumnMap,
};
static Item: typeof DescriptionsItem = DescriptionsItem;
state: {
screens: ScreenMap;
} = {
screens: {},
};
token: string;
componentDidMount() {
const { column } = this.props;
this.token = ResponsiveObserve.subscribe(screens => {
// Responsive
React.useEffect(() => {
const token = ResponsiveObserve.subscribe(newScreens => {
if (typeof column !== 'object') {
return;
}
this.setState({
screens,
});
setScreens(newScreens);
});
}
componentWillUnmount() {
ResponsiveObserve.unsubscribe(this.token);
}
return () => {
ResponsiveObserve.unsubscribe(token);
};
}, []);
getColumn(): number {
const { column } = this.props;
if (typeof column === 'object') {
for (let i = 0; i < responsiveArray.length; i++) {
const breakpoint: Breakpoint = responsiveArray[i];
if (this.state.screens[breakpoint] && column[breakpoint] !== undefined) {
return column[breakpoint] || defaultColumnMap[breakpoint];
}
}
}
// If the configuration is not an object, it is a number, return number
if (typeof column === 'number') {
return column as number;
}
// If it is an object, but no response is found, this happens only in the test.
// Maybe there are some strange environments
return 3;
}
// Children
const rows = getRows(children, mergedColumn);
render() {
return (
<ConfigConsumer>
{({ getPrefixCls, direction }: ConfigConsumerProps) => {
const {
className,
prefixCls: customizePrefixCls,
title,
size,
children,
bordered = false,
layout = 'horizontal',
colon = true,
style,
} = this.props;
const prefixCls = getPrefixCls('descriptions', customizePrefixCls);
return (
<div
className={classNames(prefixCls, className, {
[`${prefixCls}-${size}`]: size && size !== 'default',
[`${prefixCls}-bordered`]: !!bordered,
[`${prefixCls}-rtl`]: direction === 'rtl',
})}
style={style}
>
{title && <div className={`${prefixCls}-title`}>{title}</div>}
const column = this.getColumn();
const cloneChildren = flattenChildren(children)
.map((child: React.ReactElement<DescriptionsItemProps>) => {
if (React.isValidElement(child)) {
return React.cloneElement(child, {
prefixCls,
});
}
return null;
})
.filter((node: React.ReactElement) => node);
const childrenArray: Array<React.ReactElement<
DescriptionsItemProps
>[]> = generateChildrenRows(cloneChildren, column);
return (
<div
className={classNames(prefixCls, className, {
[`${prefixCls}-${size}`]: size !== 'default',
[`${prefixCls}-bordered`]: !!bordered,
[`${prefixCls}-rtl`]: direction === 'rtl',
})}
style={style}
>
{title && <div className={`${prefixCls}-title`}>{title}</div>}
<div className={`${prefixCls}-view`}>
<table>
<tbody>
{childrenArray.map((child, index) =>
renderRow(
child,
index,
{
prefixCls,
},
bordered,
layout,
colon,
),
)}
</tbody>
</table>
</div>
</div>
);
}}
</ConfigConsumer>
);
}
<div className={`${prefixCls}-view`}>
<table>
<tbody>
{rows.map((row, index) => (
<Row
key={index}
index={index}
colon={colon}
prefixCls={prefixCls}
vertical={layout === 'vertical'}
bordered={bordered}
>
{row}
</Row>
))}
</tbody>
</table>
</div>
</div>
);
}
Descriptions.Item = DescriptionsItem;
export default Descriptions;

View File

@ -130,7 +130,7 @@
"rc-tree-select": "~3.0.0-alpha.5",
"rc-trigger": "~4.0.0-rc.0",
"rc-upload": "~3.0.0-alpha.0",
"rc-util": "^4.17.0",
"rc-util": "^4.20.0",
"rc-virtual-list": "^0.0.0-alpha.25",
"resize-observer-polyfill": "^1.5.1",
"scroll-into-view-if-needed": "^2.2.20",