Merge pull request #18599 from ant-design/feature

chore: merge feature to master for 2.23.0
This commit is contained in:
Yu 2019-09-02 14:33:11 +08:00 committed by GitHub
commit 61e319b668
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
46 changed files with 1539 additions and 740 deletions

View File

@ -19,11 +19,11 @@ timeline: true
`2019-08-27`
- 🐞修复 Mentions 在 Form 中高度略高的问题。[#18478](https://github.com/ant-design/ant-design/pull/18478)
- 🐞修复失效 Input 依然支持 allowClear 的问题。[#18482](https://github.com/ant-design/ant-design/pull/18482)
- 🐞修复 Input.Password unmount 时报错 `Cannot read property 'input' of null`。[#18475](https://github.com/ant-design/ant-design/pull/18475)
- 🐞修正 Table `style` 属性到最外层容器。[#18494](https://github.com/ant-design/ant-design/pull/18494)
- 🐞修正 PageHeader 默认英文文案。[#18471](https://github.com/ant-design/ant-design/pull/18471) [@hjiawei](https://github.com/hjiawei)
- 🐞 修复 Mentions 在 Form 中高度略高的问题。[#18478](https://github.com/ant-design/ant-design/pull/18478)
- 🐞 修复失效 Input 依然支持 allowClear 的问题。[#18482](https://github.com/ant-design/ant-design/pull/18482)
- 🐞 修复 Input.Password unmount 时报错 `Cannot read property 'input' of null`。[#18475](https://github.com/ant-design/ant-design/pull/18475)
- 🐞 修正 Table `style` 属性到最外层容器。[#18494](https://github.com/ant-design/ant-design/pull/18494)
- 🐞 修正 PageHeader 默认英文文案。[#18471](https://github.com/ant-design/ant-design/pull/18471) [@hjiawei](https://github.com/hjiawei)
## 3.22.1

View File

@ -6,7 +6,7 @@ const isStyleSupport = (styleName: string | Array<string>): boolean => {
return styleNameList.some(name => name in documentElement.style);
}
return false;
}
};
export const isFlexSupported = isStyleSupport(['flex', 'webkitFlex', 'Flex', 'msFlex']);

View File

@ -8,6 +8,7 @@ import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
export interface AnchorLinkProps {
prefixCls?: string;
href: string;
target: string;
title: React.ReactNode;
children?: React.ReactNode;
className?: string;
@ -52,7 +53,7 @@ class AnchorLink extends React.Component<AnchorLinkProps, any> {
};
renderAnchorLink = ({ getPrefixCls }: ConfigConsumerProps) => {
const { prefixCls: customizePrefixCls, href, title, children, className } = this.props;
const { prefixCls: customizePrefixCls, href, title, children, className, target } = this.props;
const prefixCls = getPrefixCls('anchor', customizePrefixCls);
const active = this.context.antAnchor.activeLink === href;
const wrapperClassName = classNames(className, `${prefixCls}-link`, {
@ -67,6 +68,7 @@ class AnchorLink extends React.Component<AnchorLinkProps, any> {
className={titleClassName}
href={href}
title={typeof title === 'string' ? title : ''}
target={target}
onClick={this.handleClick}
>
{title}

View File

@ -41,6 +41,18 @@ exports[`renders ./components/anchor/demo/basic.md correctly 1`] = `
Static demo
</a>
</div>
<div
class="ant-anchor-link"
>
<a
class="ant-anchor-link-title"
href="#components-anchor-demo-basic"
target="_blank"
title="Basic demo with Target"
>
Basic demo with Target
</a>
</div>
<div
class="ant-anchor-link"
>

View File

@ -22,6 +22,7 @@ ReactDOM.render(
<Anchor>
<Link href="#components-anchor-demo-basic" title="Basic demo" />
<Link href="#components-anchor-demo-static" title="Static demo" />
<Link href="#components-anchor-demo-basic" title="Basic demo with Target" target="_blank" />
<Link href="#API" title="API">
<Link href="#Anchor-Props" title="Anchor Props" />
<Link href="#Link-Props" title="Link Props" />

View File

@ -29,7 +29,8 @@ For displaying anchor hyperlinks on page and jumping between them.
### Link Props
| Property | Description | Type | Default | Version |
| -------- | -------------------- | ----------------- | ------- | ------- |
| href | target of hyperlink | string | | |
| title | content of hyperlink | string\|ReactNode | | |
| Property | Description | Type | Default | Version |
| -------- | ----------------------------------------- | ----------------- | ------- | ------- |
| href | target of hyperlink | string | | |
| title | content of hyperlink | string\|ReactNode | | |
| target | Specifies where to display the linked URL | string | | |

View File

@ -30,7 +30,8 @@ title: Anchor
### Link Props
| 成员 | 说明 | 类型 | 默认值 | 版本 |
| ----- | -------- | ----------------- | ------ | ---- |
| href | 锚点链接 | string | | |
| title | 文字内容 | string\|ReactNode | | |
| 成员 | 说明 | 类型 | 默认值 | 版本 |
| ------ | -------------------------------- | ----------------- | ------ | ---- |
| href | 锚点链接 | string | | |
| title | 文字内容 | string\|ReactNode | | |
| target | 该属性指定在何处显示链接的资源。 | string | | |

View File

@ -87,7 +87,9 @@ describe('Badge', () => {
it('should support offset when count is a ReactNode', () => {
const wrapper = render(
<Badge count={<span className="custom" style={{ color: '#f5222d' }} />} offset={[10, 20]}>
<a href="#" className="head-example">head</a>
<a href="#" className="head-example">
head
</a>
</Badge>,
);
expect(wrapper).toMatchSnapshot();

View File

@ -1,6 +1,7 @@
import * as React from 'react';
import * as PropTypes from 'prop-types';
import classNames from 'classnames';
import toArray from 'rc-util/lib/Children/toArray';
import BreadcrumbItem from './BreadcrumbItem';
import BreadcrumbSeparator from './BreadcrumbSeparator';
import Menu from '../menu';
@ -47,6 +48,16 @@ function defaultItemRender(route: Route, params: any, routes: Route[], paths: st
return isLastItem ? <span>{name}</span> : <a href={`#/${paths.join('/')}`}>{name}</a>;
}
function filterFragment(children: any) {
return toArray(children).map((element: any) => {
if (React.isValidElement(element) && element.type === React.Fragment) {
const props: any = element.props;
return props.children;
}
return element;
});
}
export default class Breadcrumb extends React.Component<BreadcrumbProps, any> {
static Item: typeof BreadcrumbItem;
@ -139,7 +150,7 @@ export default class Breadcrumb extends React.Component<BreadcrumbProps, any> {
// generated by route
crumbs = this.genForRoutes(this.props);
} else if (children) {
crumbs = React.Children.map(children, (element: any, index) => {
crumbs = React.Children.map(filterFragment(children), (element: any, index) => {
if (!element) {
return element;
}

View File

@ -55,6 +55,21 @@ describe('Breadcrumb', () => {
expect(wrapper).toMatchSnapshot();
});
// https://github.com/ant-design/ant-design/issues/18260
it('filter React.Fragment', () => {
const wrapper = render(
<Breadcrumb separator="">
<Breadcrumb.Item>Location</Breadcrumb.Item>
<Breadcrumb.Separator>:</Breadcrumb.Separator>
<>
<Breadcrumb.Item href="">Application Center</Breadcrumb.Item>
<Breadcrumb.Separator />
</>
</Breadcrumb>,
);
expect(wrapper).toMatchSnapshot();
});
it('should render a menu', () => {
const routes = [
{

View File

@ -1,5 +1,37 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Breadcrumb filter React.Fragment 1`] = `
<div
class="ant-breadcrumb"
>
<span>
<span
class="ant-breadcrumb-link"
>
Location
</span>
</span>
<span
class="ant-breadcrumb-separator"
>
:
</span>
<span>
<a
class="ant-breadcrumb-link"
href=""
>
Application Center
</a>
</span>
<span
class="ant-breadcrumb-separator"
>
/
</span>
</div>
`;
exports[`Breadcrumb should allow Breadcrumb.Item is null or undefined 1`] = `
<div
class="ant-breadcrumb"

View File

@ -6,14 +6,17 @@ export interface CardGridProps {
prefixCls?: string;
style?: React.CSSProperties;
className?: string;
hoverable?: boolean;
}
const Grid: React.SFC<CardGridProps> = props => (
<ConfigConsumer>
{({ getPrefixCls }: ConfigConsumerProps) => {
const { prefixCls: customizePrefixCls, className, ...others } = props;
const { prefixCls: customizePrefixCls, className, hoverable = true, ...others } = props;
const prefixCls = getPrefixCls('card', customizePrefixCls);
const classString = classNames(`${prefixCls}-grid`, className);
const classString = classNames(`${prefixCls}-grid`, className, {
[`${prefixCls}-grid-hoverable`]: hoverable,
});
return <div {...others} className={classString} />;
}}
</ConfigConsumer>

View File

@ -182,7 +182,7 @@ exports[`renders ./components/card/demo/grid-card.md correctly 1`] = `
class="ant-card-body"
>
<div
class="ant-card-grid"
class="ant-card-grid ant-card-grid-hoverable"
style="width:25%;text-align:center"
>
Content
@ -194,31 +194,31 @@ exports[`renders ./components/card/demo/grid-card.md correctly 1`] = `
Content
</div>
<div
class="ant-card-grid"
class="ant-card-grid ant-card-grid-hoverable"
style="width:25%;text-align:center"
>
Content
</div>
<div
class="ant-card-grid"
class="ant-card-grid ant-card-grid-hoverable"
style="width:25%;text-align:center"
>
Content
</div>
<div
class="ant-card-grid"
class="ant-card-grid ant-card-grid-hoverable"
style="width:25%;text-align:center"
>
Content
</div>
<div
class="ant-card-grid"
class="ant-card-grid ant-card-grid-hoverable"
style="width:25%;text-align:center"
>
Content
</div>
<div
class="ant-card-grid"
class="ant-card-grid ant-card-grid-hoverable"
style="width:25%;text-align:center"
>
Content
@ -1002,6 +1002,16 @@ exports[`renders ./components/card/demo/tabs.md correctly 1`] = `
role="tablist"
tabindex="0"
>
<div
class="ant-tabs-extra-content"
style="float:right"
>
<a
href="#"
>
More
</a>
</div>
<div
class="ant-tabs-nav-container"
>

View File

@ -24,7 +24,9 @@ const gridStyle = {
ReactDOM.render(
<Card title="Card Title">
<Card.Grid style={gridStyle}>Content</Card.Grid>
<Card.Grid style={gridStyle}>Content</Card.Grid>
<Card.Grid hoverable={false} style={gridStyle}>
Content
</Card.Grid>
<Card.Grid style={gridStyle}>Content</Card.Grid>
<Card.Grid style={gridStyle}>Content</Card.Grid>
<Card.Grid style={gridStyle}>Content</Card.Grid>

View File

@ -85,6 +85,7 @@ class TabsCard extends React.Component {
style={{ width: '100%' }}
tabList={tabListNoTitle}
activeTabKey={this.state.noTitleKey}
tabBarExtraContent={<a href="#">More</a>}
onTabChange={key => {
this.onTabChange(key, 'noTitleKey');
}}

View File

@ -32,6 +32,7 @@ A card can be used to display content related to a single subject. The content c
| hoverable | Lift up when hovering card | boolean | false | |
| loading | Shows a loading indicator while the contents of the card are being fetched | boolean | false | |
| tabList | List of TabPane's head. | Array&lt;{key: string, tab: ReactNode}> | - | |
| tabBarExtraContent | Extra content in tab bar | React.ReactNode | - | |
| size | Size of card | `default` \| `small` | `default` | 3.12.0 |
| title | Card title | string\|ReactNode | - | |
| type | Card style type, can be set to `inner` or not set | string | - | |
@ -39,10 +40,11 @@ A card can be used to display content related to a single subject. The content c
### Card.Grid
| Property | Description | Type | Default | Version |
| --------- | ------------------------- | ------ | ------- | ------- |
| className | className of container | string | - | |
| style | style object of container | object | - | |
| Property | Description | Type | Default | Version |
| --------- | ------------------------------- | ------- | ------- | ------- |
| className | className of container | string | - | |
| hoverable | Lift up when hovering card grid | boolean | true | 3.23.0 |
| style | style object of container | object | - | |
### Card.Meta

View File

@ -51,6 +51,7 @@ export interface CardProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 't
cover?: React.ReactNode;
actions?: React.ReactNode[];
tabList?: CardTabListType[];
tabBarExtraContent?: React.ReactNode | null;
onTabChange?: (key: string) => void;
activeTabKey?: string;
defaultActiveTabKey?: string;
@ -119,6 +120,7 @@ export default class Card extends React.Component<CardProps, {}> {
children,
activeTabKey,
defaultActiveTabKey,
tabBarExtraContent,
...others
} = this.props;
@ -186,6 +188,7 @@ export default class Card extends React.Component<CardProps, {}> {
[hasActiveTabKey ? 'activeKey' : 'defaultActiveKey']: hasActiveTabKey
? activeTabKey
: defaultActiveTabKey,
tabBarExtraContent,
};
let head;

View File

@ -33,6 +33,7 @@ cols: 1
| hoverable | 鼠标移过时可浮起 | boolean | false | |
| loading | 当卡片内容还在加载中时,可以用 loading 展示一个占位 | boolean | false | |
| tabList | 页签标题列表 | Array&lt;{key: string, tab: ReactNode}> | - | |
| tabBarExtraContent | tab bar 上额外的元素 | React.ReactNode | 无 | |
| size | card 的尺寸 | `default` \| `small` | `default` | 3.12.0 |
| title | 卡片标题 | string\|ReactNode | - | |
| type | 卡片类型,可设置为 `inner` 或 不设置 | string | - | |
@ -40,17 +41,18 @@ cols: 1
### Card.Grid
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --------- | ---------------------- | ------ | ------- | ---- |
| className | 网格容器类名 | string | - | |
| style | 定义网格容器类名的样式 | object | - | |
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --------- | ---------------------- | ------- | ------ | ------ |
| className | 网格容器类名 | string | - | |
| hoverable | 鼠标移过时可浮起 | boolean | true | 3.23.0 |
| style | 定义网格容器类名的样式 | object | - | |
### Card.Meta
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| ----------- | ------------------ | --------- | ------- | ---- |
| avatar | 头像/图标 | ReactNode | - | |
| className | 容器类名 | string | - | |
| description | 描述内容 | ReactNode | - | |
| style | 定义容器类名的样式 | object | - | |
| title | 标题内容 | ReactNode | - | |
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| ----------- | ------------------ | --------- | ------ | ---- |
| avatar | 头像/图标 | ReactNode | - | |
| className | 容器类名 | string | - | |
| description | 描述内容 | ReactNode | - | |
| style | 定义容器类名的样式 | object | - | |
| title | 标题内容 | ReactNode | - | |

View File

@ -98,10 +98,12 @@
1px 1px 0 0 @border-color-split, 1px 0 0 0 @border-color-split inset,
0 1px 0 0 @border-color-split inset;
transition: all 0.3s;
&:hover {
position: relative;
z-index: 1;
box-shadow: @box-shadow-base;
&-hoverable {
&:hover {
position: relative;
z-index: 1;
box-shadow: @box-shadow-base;
}
}
}

View File

@ -476,7 +476,13 @@ describe('Cascader', () => {
// https://github.com/ant-design/ant-design/issues/18176
it('have a notFoundContent that fit trigger input width', () => {
const wrapper = mount(<Cascader popupVisible options={[]} fieldNames={{ label: 'name', value: 'code', children: 'items' }} />);
const wrapper = mount(
<Cascader
popupVisible
options={[]}
fieldNames={{ label: 'name', value: 'code', children: 'items' }}
/>,
);
const popupWrapper = mount(
wrapper
.find('Trigger')

View File

@ -5363,7 +5363,7 @@ exports[`ConfigProvider components Card configProvider 1`] = `
class="config-card-body"
>
<div
class="config-card-grid"
class="config-card-grid config-card-grid-hoverable"
>
<div
class="config-card-meta"
@ -5381,7 +5381,7 @@ exports[`ConfigProvider components Card normal 1`] = `
class="ant-card-body"
>
<div
class="ant-card-grid"
class="ant-card-grid ant-card-grid-hoverable"
>
<div
class="ant-card-meta"
@ -5399,7 +5399,7 @@ exports[`ConfigProvider components Card prefixCls 1`] = `
class="prefix-Card-body"
>
<div
class="prefix-Card-grid"
class="prefix-Card-grid prefix-Card-grid-hoverable"
>
<div
class="prefix-Card-meta"

View File

@ -5,7 +5,11 @@ import Button from '../../button';
import mountTest from '../../../tests/shared/mountTest';
describe('ConfigProvider', () => {
mountTest(() => <ConfigProvider><div /></ConfigProvider>);
mountTest(() => (
<ConfigProvider>
<div />
</ConfigProvider>
));
it('Content Security Policy', () => {
const csp = { nonce: 'test-antd' };

View File

@ -5,7 +5,11 @@ import Menu from '../../menu';
import mountTest from '../../../tests/shared/mountTest';
describe('DropdownButton', () => {
mountTest(() => <Dropdown menu={<Menu />}><span /></Dropdown>);
mountTest(() => (
<Dropdown menu={<Menu />}>
<span />
</Dropdown>
));
mountTest(Dropdown.Button);
it('pass appropriate props to Dropdown', () => {

View File

@ -27,6 +27,7 @@ When a numeric value needs to be provided.
| step | The number to which the current value is increased or decreased. It can be an integer or decimal. | number\|string | 1 | |
| value | current value | number | | |
| onChange | The callback triggered when the value is changed. | function(value: number \| string) | | |
| onPressEnter | The callback function that is triggered when Enter key is pressed. | function(e) | | |
## Methods

View File

@ -29,6 +29,7 @@ export interface InputNumberProps
name?: string;
id?: string;
precision?: number;
onPressEnter?: React.KeyboardEventHandler<HTMLInputElement>;
}
export default class InputNumber extends React.Component<InputNumberProps, any> {

View File

@ -30,6 +30,7 @@ title: InputNumber
| step | 每次改变步数,可以为小数 | number\|string | 1 | |
| value | 当前值 | number | | |
| onChange | 变化回调 | Function(value: number \| string) | | |
| onPressEnter | 按下回车的回调 | function(e) | | |
## 方法

View File

@ -16,7 +16,13 @@ jest.mock('mutationobserver-shim', () => {
const { SubMenu } = Menu;
describe('Menu', () => {
mountTest(() => <Menu><Menu.Item /><Menu.ItemGroup /><Menu.SubMenu /></Menu>);
mountTest(() => (
<Menu>
<Menu.Item />
<Menu.ItemGroup />
<Menu.SubMenu />
</Menu>
));
beforeEach(() => {
jest.useFakeTimers();

View File

@ -66,4 +66,26 @@ describe('PageHeader', () => {
const wrapper = render(<PageHeader title={false} />);
expect(wrapper).toMatchSnapshot();
});
it('breadcrumbs and back icon can only have one', () => {
const routes = [
{
path: 'index',
breadcrumbName: 'First-level Menu',
},
{
path: 'first',
breadcrumbName: 'Second-level Menu',
},
{
path: 'second',
breadcrumbName: 'Third-level Menu',
},
];
const wrapper = mount(<PageHeader title="Title" onBack={() => true} breadcrumb={{ routes }} />);
expect(wrapper.find('.ant-breadcrumb')).toHaveLength(0);
wrapper.setProps({ onBack: undefined });
expect(wrapper.find('.ant-breadcrumb')).toHaveLength(1);
});
});

View File

@ -1,8 +1,8 @@
---
order: 5
order: 4
title:
zh-CN: 复杂的例子
en-US: Complex example
zh-CN: 多种形态的 PageHeader
en-US: Various forms of PageHeader
---
## zh-CN
@ -14,107 +14,68 @@ title:
Use the operating area and customize the sub-nodes, suitable for use in the need to display some complex information to help users quickly understand the information and operations of this page.
```jsx
import { PageHeader, Tag, Tabs, Button, Statistic, Row, Col } from 'antd';
const { TabPane } = Tabs;
const Description = ({ term, children, span = 12 }) => (
<Col span={span}>
<div className="description">
<div className="term">{term}</div>
<div className="detail">{children}</div>
</div>
</Col>
);
const content = (
<Row>
<Description term="Created">Lili Qu</Description>
<Description term="Association">
<a>421421</a>
</Description>
<Description term="Creation Time">2017-01-10</Description>
<Description term="Effective Time">2017-10-10</Description>
<Description term="Remarks" span={24}>
Gonghu Road, Xihu District, Hangzhou, Zhejiang, China
</Description>
</Row>
);
const extraContent = (
<Row>
<Col span={12}>
<Statistic title="Status" value="Pending" />
</Col>
<Col span={12}>
<Statistic title="Price" prefix="$" value={568.08} />
</Col>
</Row>
);
import { PageHeader, Tag, Button, Statistic, Descriptions, Row } from 'antd';
ReactDOM.render(
<PageHeader
onBack={() => window.history.back()}
title="Title"
subTitle="This is a subtitle"
tags={<Tag color="red">Warning</Tag>}
extra={[
<Button key="3">Operation</Button>,
<Button key="2">Operation</Button>,
<Button key="1" type="primary">
Primary
</Button>,
]}
footer={
<Tabs defaultActiveKey="1">
<TabPane tab="Details" key="1" />
<TabPane tab="Rule" key="2" />
</Tabs>
}
>
<div className="wrap">
<div className="content padding">{content}</div>
<div className="extraContent">{extraContent}</div>
</div>
</PageHeader>,
<div>
<PageHeader
onBack={() => window.history.back()}
title="Title"
subTitle="This is a subtitle"
extra={[
<Button key="3">Operation</Button>,
<Button key="2">Operation</Button>,
<Button key="1" type="primary">
Primary
</Button>,
]}
>
<Descriptions size="small" column={3}>
<Descriptions.item label="Created">Lili Qu</Descriptions.item>
<Descriptions.item label="Association">
<a>421421</a>
</Descriptions.item>
<Descriptions.item label="Creation Time">2017-01-10</Descriptions.item>
<Descriptions.item label="Effective Time">2017-10-10</Descriptions.item>
<Descriptions.item label="Remarks">
Gonghu Road, Xihu District, Hangzhou, Zhejiang, China
</Descriptions.item>
</Descriptions>
</PageHeader>
<br />
<PageHeader
onBack={() => window.history.back()}
title="Title"
tags={<Tag color="blue">Running</Tag>}
subTitle="This is a subtitle"
extra={[
<Button key="3">Operation</Button>,
<Button key="2">Operation</Button>,
<Button key="1" type="primary">
Primary
</Button>,
]}
>
<Row type="flex">
<Statistic title="Status" value="Pending" />
<Statistic
title="Price"
prefix="$"
value={568.08}
style={{
margin: '0 32px',
}}
/>
<Statistic title="Balance" prefix="$" value={3345.08} />
</Row>
</PageHeader>
</div>,
mountNode,
);
```
<style>
#components-page-header-demo-actions .wrap {
display: flex;
}
#components-page-header-demo-actions .content {
flex: 1;
}
#components-page-header-demo-actions .extraContent {
min-width: 240px;
text-align: right;
}
#components-page-header-demo-actions .content.padding {
padding-left: 40px;
}
#components-page-header-demo-actions .content .description {
display: table;
}
#components-page-header-demo-actions .description .term {
display: table-cell;
margin-right: 8px;
padding-bottom: 8px;
white-space: nowrap;
line-height: 20px;
}
#components-page-header-demo-actions .description .term:after {
position: relative;
top: -0.5px;
margin: 0 8px 0 2px;
content: ":";
}
#components-page-header-demo-actions .description .detail {
display: table-cell;
padding-bottom: 8px;
width: 100%;
line-height: 20px;
tr:last-child td {
padding-bottom: 0;
}
</style>

View File

@ -31,5 +31,8 @@ const routes = [
},
];
ReactDOM.render(<PageHeader title="Title" breadcrumb={{ routes }} />, mountNode);
ReactDOM.render(
<PageHeader title="Title" breadcrumb={{ routes }} subTitle="This is a subtitle" />,
mountNode,
);
```

View File

@ -1,23 +1,64 @@
---
order: 3
title:
zh-CN: 带内容的例子
en-US: Example with content
zh-CN: 组合示例
en-US: Complete example
---
## zh-CN
带内容的例子,可以优先展示页面的主要信息
使用了 pageHeader 提供的所有能力
## en-US
An example with content that gives priority to the main information of the page.
Show all props.Used all the capabilities provided by pageHeader.
```jsx
import { PageHeader, Typography } from 'antd';
import { PageHeader, Menu, Dropdown, Icon, Button, Tag, Typography, Row } from 'antd';
const { Paragraph } = Typography;
const menu = (
<Menu>
<Menu.Item>
<a target="_blank" rel="noopener noreferrer" href="http://www.alipay.com/">
1st menu item
</a>
</Menu.Item>
<Menu.Item>
<a target="_blank" rel="noopener noreferrer" href="http://www.taobao.com/">
2nd menu item
</a>
</Menu.Item>
<Menu.Item>
<a target="_blank" rel="noopener noreferrer" href="http://www.tmall.com/">
3rd menu item
</a>
</Menu.Item>
</Menu>
);
const DropdownMenu = () => {
return (
<Dropdown key="more" overlay={menu}>
<Button
style={{
border: 'none',
padding: 0,
}}
>
<Icon
type="ellipsis"
style={{
fontSize: 20,
verticalAlign: 'top',
}}
/>
</Button>
</Dropdown>
);
};
const routes = [
{
path: 'index',
@ -33,6 +74,25 @@ const routes = [
},
];
const IconLink = ({ src, text }) => (
<a
style={{
marginRight: 16,
display: 'flex',
alignItems: 'center',
}}
>
<img
style={{
marginRight: 8,
}}
src={src}
alt="start"
/>
{text}
</a>
);
const content = (
<div className="content">
<Paragraph>
@ -44,64 +104,74 @@ const content = (
easier for designers to have a clear psychological expectation of color when adjusting colors,
as well as facilitate communication in teams.
</Paragraph>
<p className="contentLink">
<a>
<img
src="https://gw.alipayobjects.com/zos/rmsportal/MjEImQtenlyueSmVEfUD.svg"
alt="start"
/>
Quick Start
</a>
<a>
<img src="https://gw.alipayobjects.com/zos/rmsportal/NbuDUAuBlIApFuDvWiND.svg" alt="info" />
Product Info
</a>
<a>
<img src="https://gw.alipayobjects.com/zos/rmsportal/ohOEPSYdDTNnyMbGuyLb.svg" alt="doc" />
Product Doc
</a>
</p>
<Row className="contentLink" type="flex">
<IconLink
src="https://gw.alipayobjects.com/zos/rmsportal/MjEImQtenlyueSmVEfUD.svg"
text="Quick Start"
/>
<IconLink
src="https://gw.alipayobjects.com/zos/rmsportal/NbuDUAuBlIApFuDvWiND.svg"
text=" Product Info"
/>
<IconLink
src="https://gw.alipayobjects.com/zos/rmsportal/ohOEPSYdDTNnyMbGuyLb.svg"
text="Product Doc"
/>
</Row>
</div>
);
const extraContent = (
<img
src="https://gw.alipayobjects.com/mdn/mpaas_user/afts/img/A*KsfVQbuLRlYAAAAAAAAAAABjAQAAAQ/original"
alt="content"
/>
);
const Content = ({ children, extraContent }) => {
return (
<Row className="content" type="flex">
<div className="main" style={{ flex: 1 }}>
{children}
</div>
<div
className="extra"
style={{
marginLeft: 80,
}}
>
{extraContent}
</div>
</Row>
);
};
ReactDOM.render(
<PageHeader title="Title" breadcrumb={{ routes }}>
<div className="wrap">
<div className="content">{content}</div>
<div className="extraContent">{extraContent}</div>
</div>
<PageHeader
title="Title"
subTitle="This is a subtitle"
tags={<Tag color="blue">Running</Tag>}
extra={[
<Button key="3">Operation</Button>,
<Button key="2">Operation</Button>,
<Button key="1" type="primary">
Primary
</Button>,
<DropdownMenu key="more" />,
]}
avatar={{ src: 'https://avatars1.githubusercontent.com/u/8186664?s=460&v=4' }}
breadcrumb={{ routes }}
>
<Content
extraContent={
<img
src="https://gw.alipayobjects.com/mdn/mpaas_user/afts/img/A*KsfVQbuLRlYAAAAAAAAAAABjAQAAAQ/original"
alt="content"
/>
}
>
{content}
</Content>
</PageHeader>,
mountNode,
);
```
<style>
#components-page-header-demo-content .wrap {
display: flex;
.ant-page-header {
border: 1px solid rgb(235, 237, 240);
}
#components-page-header-demo-content .content {
flex: 1;
}
#components-page-header-demo-content .extraContent {
min-width: 240px;
text-align: right;
}
#components-page-header-demo-content .contentLink {
padding-top: 16px;
}
#components-page-header-demo-content .contentLink a {
display: inline-block;
vertical-align: text-top;
margin-right: 32px;
}
#components-page-header-demo-content .contentLink a img {
margin-right: 8px;
}
</style>
<style>

View File

@ -0,0 +1,114 @@
---
order: 4
iframe: 240
title:
zh-CN: 响应式
en-US: responsive
---
## zh-CN
在不同大小的屏幕下,应该有不同的表现
## en-US
Under different screen sizes, there should be different performance
```jsx
import { PageHeader, Tabs, Button, Statistic, Descriptions } from 'antd';
const { TabPane } = Tabs;
const renderContent = (column = 2) => (
<Descriptions size="small" column={column}>
<Descriptions.item label="Created">Lili Qu</Descriptions.item>
<Descriptions.item label="Association">
<a>421421</a>
</Descriptions.item>
<Descriptions.item label="Creation Time">2017-01-10</Descriptions.item>
<Descriptions.item label="Effective Time">2017-10-10</Descriptions.item>
<Descriptions.item label="Remarks">
Gonghu Road, Xihu District, Hangzhou, Zhejiang, China
</Descriptions.item>
</Descriptions>
);
const extraContent = (
<div
style={{
display: 'flex',
width: 'max-content',
justifyContent: 'flex-end',
}}
>
<Statistic
title="Status"
value="Pending"
style={{
marginRight: 32,
}}
/>
<Statistic title="Price" prefix="$" value={568.08} />
</div>
);
const Content = ({ children, extra }) => {
return (
<div className="content">
<div className="main">{children}</div>
<div className="extra">{extra}</div>
</div>
);
};
ReactDOM.render(
<div>
<PageHeader
onBack={() => window.history.back()}
title="Title"
subTitle="This is a subtitle"
extra={[
<Button key="3">Operation</Button>,
<Button key="2">Operation</Button>,
<Button key="1" type="primary">
Primary
</Button>,
]}
footer={
<Tabs defaultActiveKey="1">
<TabPane tab="Details" key="1" />
<TabPane tab="Rule" key="2" />
</Tabs>
}
>
<Content extra={extraContent}>{renderContent()}</Content>
</PageHeader>
</div>,
mountNode,
);
```
<style>
tr:last-child td {
padding-bottom: 0;
}
#components-page-header-demo-responsive .content {
display: flex;
}
@media (max-width: 576px) {
#components-page-header-demo-responsive .content {
display: block;
}
#components-page-header-demo-responsive .main {
width: 100%;
margin-bottom: 12px;
}
#components-page-header-demo-responsive .extra {
width: 100%;
margin-left: 0;
text-align: left;
}
}
</style>

View File

@ -18,9 +18,12 @@ It can also be used as inter-page navigation when it is needed to make the user
| --- | --- | --- | --- | --- |
| title | custom title text | ReactNode | - | 3.14.0 |
| subTitle | custom subTitle text | ReactNode | - | 3.14.0 |
| avatar | Avatar next to the title bar | [avatar props](/components/avatar/) | - | 3.22.0 |
| backIcon | custom back icon, if false the back icon will not be displayed | ReactNode | `<Icon type="arrow-left" />` | 3.14.0 |
| tags | Tag list next to title | [Tag](https://ant.design/components/tag-cn/)[] \| [Tag](https://ant.design/components/tag-cn/) | - | 3.14.0 |
| extra | Operating area, at the end of the line of the title line | ReactNode | - | 3.14.0 |
| breadcrumb | breadcrumb config | [breadcrumb](https://ant.design/components/breadcrumb-cn/) | - | 3.14.0 |
| footer | PageHeader's footer, generally used to render TabBar | ReactNode | - | 3.14.0 |
| onBack | back icon click event | `()=>void` | `()=>history.back()` | 3.14.0 |
> breadcrumbs will automatically disappear when configuring back icon.

View File

@ -3,11 +3,12 @@ import classnames from 'classnames';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
import Icon from '../icon';
import Divider from '../divider';
import Tag from '../tag';
import Breadcrumb, { BreadcrumbProps } from '../breadcrumb';
import Avatar, { AvatarProps } from '../avatar';
import TransButton from '../_util/transButton';
import LocaleReceiver from '../locale-provider/LocaleReceiver';
import warning from '../_util/warning';
export interface PageHeaderProps {
backIcon?: React.ReactNode;
@ -19,6 +20,7 @@ export interface PageHeaderProps {
tags?: React.ReactElement<Tag> | React.ReactElement<Tag>[];
footer?: React.ReactNode;
extra?: React.ReactNode;
avatar?: AvatarProps;
onBack?: (e: React.MouseEvent<HTMLDivElement>) => void;
className?: string;
}
@ -46,7 +48,6 @@ const renderBack = (
>
{backIcon}
</TransButton>
<Divider type="vertical" />
</div>
)}
</LocaleReceiver>
@ -57,20 +58,32 @@ const renderBreadcrumb = (breadcrumb: BreadcrumbProps) => {
return <Breadcrumb {...breadcrumb} />;
};
const renderHeader = (prefixCls: string, props: PageHeaderProps) => {
const { breadcrumb, backIcon, onBack } = props;
const renderHeader = (
breadcrumb: PageHeaderProps['breadcrumb'],
{ backIcon, onBack }: PageHeaderProps,
) => {
// by design,Bread crumbs and back icon can only have one
if (backIcon && onBack) {
if (breadcrumb && breadcrumb.routes) {
warning(false, 'page-header', 'breadcrumbs and back icon can only have one');
}
return null;
}
if (breadcrumb && breadcrumb.routes) {
return renderBreadcrumb(breadcrumb);
}
return renderBack(prefixCls, backIcon, onBack);
return null;
};
const renderTitle = (prefixCls: string, props: PageHeaderProps) => {
const { title, subTitle, tags, extra } = props;
const { title, avatar, subTitle, tags, extra, backIcon, onBack } = props;
const headingPrefixCls = `${prefixCls}-heading`;
if (title || subTitle || tags || extra) {
const backIconDom = renderBack(prefixCls, backIcon, onBack);
return (
<div className={headingPrefixCls}>
{backIconDom}
{avatar && <Avatar {...avatar} />}
{title && <span className={`${headingPrefixCls}-title`}>{title}</span>}
{subTitle && <span className={`${headingPrefixCls}-sub-title`}>{subTitle}</span>}
{tags && <span className={`${headingPrefixCls}-tags`}>{tags}</span>}
@ -88,6 +101,10 @@ const renderFooter = (prefixCls: string, footer: React.ReactNode) => {
return null;
};
const renderChildren = (prefixCls: string, children: React.ReactNode) => {
return <div className={`${prefixCls}-content`}>{children}</div>;
};
const PageHeader: React.SFC<PageHeaderProps> = props => (
<ConfigConsumer>
{({ getPrefixCls }: ConfigConsumerProps) => {
@ -96,23 +113,22 @@ const PageHeader: React.SFC<PageHeaderProps> = props => (
style,
footer,
children,
breadcrumb,
className: customizeClassName,
} = props;
const prefixCls = getPrefixCls('page-header', customizePrefixCls);
const className = classnames(
prefixCls,
{
[`${prefixCls}-has-footer`]: footer,
},
customizeClassName,
);
const breadcrumbDom = renderHeader(breadcrumb, props);
const className = classnames(prefixCls, customizeClassName, {
'has-breadcrumb': breadcrumbDom,
'has-footer': footer,
});
return (
<div className={className} style={style}>
{renderHeader(prefixCls, props)}
{breadcrumbDom}
{renderTitle(prefixCls, props)}
{children && <div className={`${prefixCls}-content`}>{children}</div>}
{children && renderChildren(prefixCls, children)}
{renderFooter(prefixCls, footer)}
</div>
);

View File

@ -6,7 +6,7 @@ cols: 1
subtitle: 页头
---
页头可用于声明页面主题、展示用户所关注的页面重要信息,以及承载与当前页相关的操作项(包含页面级操作,页面间导航等)
页头位于页容器中,页容器顶部,起到了内容概览和引导页级操作的作用。包括由面包屑、标题、页面内容简介、页面级操作等、页面级导航组成。
## 何时使用
@ -18,9 +18,12 @@ subtitle: 页头
| --- | --- | --- | --- | --- |
| title | 自定义标题文字 | ReactNode | - | 3.14.0 |
| subTitle | 自定义的二级标题文字 | ReactNode | - | 3.14.0 |
| avatar | 标题栏旁的头像 | [avatar props](/components/avatar-cn/) | - | 3.22.0 |
| backIcon | 自定义 back icon ,如果为 false 不渲染 back icon | ReactNode | `<Icon type="arrow-left" />` | 3.14.0 |
| tags | title 旁的 tag 列表 | [Tag](https://ant.design/components/tag-cn/)[] \| [Tag](https://ant.design/components/tag-cn/) | - | 3.14.0 |
| extra | 操作区,位于 title 行的行尾 | ReactNode | - | 3.14.0 |
| breadcrumb | 面包屑的配置 | [breadcrumb](https://ant.design/components/breadcrumb-cn/) | - | 3.14.0 |
| footer | PageHeader 的页脚,一般用于渲染 TabBar | ReactNode | - | 3.14.0 |
| onBack | 返回按钮的点击事件 | `()=>void` | `()=>history.back()` | 3.14.0 |
> 配置返回按钮时breadcrumb 会自动隐藏。

View File

@ -7,21 +7,25 @@
.reset-component;
position: relative;
padding: @page-header-padding-vertical @page-header-padding-horizontal;
background: @component-background;
padding: @page-header-padding;
&.@{pageheader-prefix-cls}-has-footer {
padding-bottom: 0;
&.has-breadcrumb {
padding-top: @page-header-padding-breadcrumb;
}
&.has-footer {
padding-bottom: @page-header-padding-vertical;
}
&-back {
display: inline-block;
padding: 4px 0;
font-size: 16px;
line-height: 100%;
float: left;
margin: 6px 0;
margin-right: 16px;
font-size: 20px;
line-height: 1;
&-button {
.operation-unit();
color: @text-color;
color: #000;
cursor: pointer;
}
}
@ -29,40 +33,48 @@
.@{ant-prefix}-divider-vertical {
height: 14px;
margin: 0 12px;
vertical-align: middle;
}
.@{ant-prefix}-breadcrumb + &-heading {
margin-top: 12px;
margin-top: 8px;
}
&-heading {
display: inline-block;
width: 100%;
overflow: hidden;
&-title {
display: inline-block;
display: block;
float: left;
margin-bottom: 0;
padding-right: 12px;
color: @heading-color;
font-weight: bold;
font-size: 16px;
line-height: 1.4;
font-weight: 600;
font-size: @heading-3-size;
line-height: 32px;
}
.@{ant-prefix}-avatar {
float: left;
margin-right: 12px;
}
&-sub-title {
display: inline-block;
padding-right: 12px;
float: left;
margin: 5px 0;
margin-right: 12px;
color: @text-color-secondary;
font-size: 14px;
line-height: 1.8;
line-height: 22px;
}
&-tags {
display: inline-block;
vertical-align: top;
float: left;
margin: 4px 0;
}
&-extra {
position: absolute;
top: 16px;
right: @page-header-padding-horizontal;
float: right;
> * {
margin-left: 8px;
}
@ -73,18 +85,30 @@
}
&-content {
padding-top: 12px;
padding-top: 16px;
overflow: hidden;
}
&-footer {
margin: 0 -8px;
padding-top: 24px;
margin-top: 16px;
.@{ant-prefix}-tabs-bar {
margin-bottom: 1px;
border-bottom: 0;
.@{ant-prefix}-tabs-nav .@{ant-prefix}-tabs-tab {
padding: 12px 8px;
padding-top: 0;
padding: 8px;
font-size: 16px;
}
}
}
@media (max-width: @screen-sm) {
&-heading {
&-extra {
display: block;
float: unset;
width: 100%;
padding-top: 12px;
overflow: hidden;
}
}
}

View File

@ -1,5 +1,5 @@
import './index.less';
// style dependencies
import '../../divider/style';
import '../../breadcrumb/style';
import '../../avatar/style';

View File

@ -563,8 +563,9 @@
// PageHeader
// ---
@page-header-padding-horizontal: 24px;
@page-header-padding: 24px;
@page-header-padding-vertical: 16px;
@page-header-padding-breadcrumb: 12px;
// Breadcrumb
// ---
@ -674,3 +675,7 @@
@timeline-dot-border-width: 2px;
@timeline-dot-color: @primary-color;
@timeline-dot-bg: @component-background;
// Typography
// ---
@typography-title-font-weight: 600;

View File

@ -233,7 +233,10 @@ describe('Table.pagination', () => {
.getComponent(),
);
expect(dropdownWrapper.find('MenuItem').length).toBe(4);
dropdownWrapper.find('MenuItem').at(3).simulate('click');
dropdownWrapper
.find('MenuItem')
.at(3)
.simulate('click');
expect(onShowSizeChange).toHaveBeenCalled();
jest.useRealTimers();
});

View File

@ -718,7 +718,10 @@ describe('Table.rowSelection', () => {
it('select by checkbox to trigger stopPropagation', () => {
const wrapper = mount(createTable());
expect(() => {
wrapper.find('span').at(10).simulate('click');
wrapper
.find('span')
.at(10)
.simulate('click');
}).not.toThrow();
});
});

View File

@ -99,7 +99,9 @@ describe('Table', () => {
it('support onHeaderCell', () => {
const onClick = jest.fn();
const wrapper = mount(<Table columns={[{ title: 'title', onHeaderCell: () => ({ onClick }) }]} />);
const wrapper = mount(
<Table columns={[{ title: 'title', onHeaderCell: () => ({ onClick }) }]} />,
);
wrapper.find('th').simulate('click');
expect(onClick).toHaveBeenCalled();
});

View File

@ -6,7 +6,11 @@ import mountTest from '../../../tests/shared/mountTest';
const { TabPane } = Tabs;
describe('Tabs', () => {
mountTest(() => <Tabs><TabPane tab="xx" key="xx" /></Tabs>);
mountTest(() => (
<Tabs>
<TabPane tab="xx" key="xx" />
</Tabs>
));
describe('editable-card', () => {
let handleEdit;

View File

@ -12,7 +12,7 @@
.typography-title(@fontSize; @lineHeight) {
margin-bottom: 0.5em;
color: @heading-color;
font-weight: 600;
font-weight: @typography-title-font-weight;
font-size: @fontSize;
line-height: @lineHeight;
}

View File

@ -68,7 +68,7 @@
"rc-dropdown": "~2.4.1",
"rc-editor-mention": "^1.1.13",
"rc-form": "^2.4.5",
"rc-input-number": "~4.4.5",
"rc-input-number": "~4.5.0",
"rc-mentions": "~0.4.0",
"rc-menu": "~7.4.23",
"rc-notification": "~3.3.1",

View File

@ -52,7 +52,13 @@ class ComponentDoc extends React.Component {
};
render() {
const { doc, location, intl: { locale }, utils, demos } = this.props;
const {
doc,
location,
intl: { locale },
utils,
demos,
} = this.props;
const { content, meta } = doc;
const demoValues = Object.keys(demos).map(key => demos[key]);
const { expandAll, showRiddleButton } = this.state;