Merge pull request #16187 from ant-design/page-header-button

Back icon should is a button
This commit is contained in:
陈帅 2019-04-25 16:29:13 +08:00 committed by GitHub
commit 4e5497328a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 141 additions and 95 deletions

View File

@ -5,8 +5,8 @@
import * as React from 'react';
import KeyCode from 'rc-util/lib/KeyCode';
interface TransButtonProps extends React.HTMLAttributes<HTMLButtonElement> {
onClick?: () => void;
interface TransButtonProps extends React.HTMLAttributes<HTMLDivElement> {
onClick?: (e?: React.MouseEvent<HTMLDivElement>) => void;
}
const inlineStyle: React.CSSProperties = {
@ -14,20 +14,21 @@ const inlineStyle: React.CSSProperties = {
background: 'transparent',
padding: 0,
lineHeight: 'inherit',
display: 'inline-block',
};
class TransButton extends React.Component<TransButtonProps> {
button?: HTMLButtonElement;
div?: HTMLDivElement;
lastKeyCode?: number;
onKeyDown: React.KeyboardEventHandler<HTMLButtonElement> = event => {
onKeyDown: React.KeyboardEventHandler<HTMLDivElement> = event => {
const { keyCode } = event;
if (keyCode === KeyCode.ENTER) {
event.preventDefault();
}
};
onKeyUp: React.KeyboardEventHandler<HTMLButtonElement> = event => {
onKeyUp: React.KeyboardEventHandler<HTMLDivElement> = event => {
const { keyCode } = event;
const { onClick } = this.props;
if (keyCode === KeyCode.ENTER && onClick) {
@ -35,26 +36,28 @@ class TransButton extends React.Component<TransButtonProps> {
}
};
setRef = (btn: HTMLButtonElement) => {
this.button = btn;
setRef = (btn: HTMLDivElement) => {
this.div = btn;
};
focus() {
if (this.button) {
this.button.focus();
if (this.div) {
this.div.focus();
}
}
blur() {
if (this.button) {
this.button.blur();
if (this.div) {
this.div.blur();
}
}
render() {
const { style } = this.props;
return (
<button
<div
role="button"
tabIndex={0}
ref={this.setRef}
{...this.props}
onKeyDown={this.onKeyDown}

View File

@ -53,4 +53,7 @@ export default {
copied: 'copy success',
expand: 'expand',
},
PageHeader: {
back: 'back',
},
};

View File

@ -53,4 +53,7 @@ export default {
copied: '复制成功',
expand: '展开',
},
PageHeader: {
back: '返回',
},
};

View File

@ -39,4 +39,7 @@ export default {
Empty: {
description: '無此資料',
},
PageHeader: {
back: '返回',
},
};

View File

@ -5,27 +5,35 @@ exports[`renders ./components/page-header/demo/actions.md correctly 1`] = `
class="ant-page-header ant-page-header-has-footer"
>
<div
class="ant-page-header-back-icon"
class="ant-page-header-back"
>
<i
aria-label="icon: arrow-left"
class="anticon anticon-arrow-left"
<div
aria-label="back"
class="ant-page-header-back-button"
role="button"
style="border:0;background:transparent;padding:0;line-height:inherit;display:inline-block"
tabindex="0"
>
<svg
aria-hidden="true"
class=""
data-icon="arrow-left"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
<i
aria-label="icon: arrow-left"
class="anticon anticon-arrow-left"
>
<path
d="M872 474H286.9l350.2-304c5.6-4.9 2.2-14-5.2-14h-88.5c-3.9 0-7.6 1.4-10.5 3.9L155 487.8a31.96 31.96 0 0 0 0 48.3L535.1 866c1.5 1.3 3.3 2 5.2 2h91.5c7.4 0 10.8-9.2 5.2-14L286.9 550H872c4.4 0 8-3.6 8-8v-60c0-4.4-3.6-8-8-8z"
/>
</svg>
</i>
<svg
aria-hidden="true"
class=""
data-icon="arrow-left"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M872 474H286.9l350.2-304c5.6-4.9 2.2-14-5.2-14h-88.5c-3.9 0-7.6 1.4-10.5 3.9L155 487.8a31.96 31.96 0 0 0 0 48.3L535.1 866c1.5 1.3 3.3 2 5.2 2h91.5c7.4 0 10.8-9.2 5.2-14L286.9 550H872c4.4 0 8-3.6 8-8v-60c0-4.4-3.6-8-8-8z"
/>
</svg>
</i>
</div>
<div
class="ant-divider ant-divider-vertical"
/>
@ -406,27 +414,35 @@ exports[`renders ./components/page-header/demo/basic.md correctly 1`] = `
class="ant-page-header"
>
<div
class="ant-page-header-back-icon"
class="ant-page-header-back"
>
<i
aria-label="icon: arrow-left"
class="anticon anticon-arrow-left"
<div
aria-label="back"
class="ant-page-header-back-button"
role="button"
style="border:0;background:transparent;padding:0;line-height:inherit;display:inline-block"
tabindex="0"
>
<svg
aria-hidden="true"
class=""
data-icon="arrow-left"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
<i
aria-label="icon: arrow-left"
class="anticon anticon-arrow-left"
>
<path
d="M872 474H286.9l350.2-304c5.6-4.9 2.2-14-5.2-14h-88.5c-3.9 0-7.6 1.4-10.5 3.9L155 487.8a31.96 31.96 0 0 0 0 48.3L535.1 866c1.5 1.3 3.3 2 5.2 2h91.5c7.4 0 10.8-9.2 5.2-14L286.9 550H872c4.4 0 8-3.6 8-8v-60c0-4.4-3.6-8-8-8z"
/>
</svg>
</i>
<svg
aria-hidden="true"
class=""
data-icon="arrow-left"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M872 474H286.9l350.2-304c5.6-4.9 2.2-14-5.2-14h-88.5c-3.9 0-7.6 1.4-10.5 3.9L155 487.8a31.96 31.96 0 0 0 0 48.3L535.1 866c1.5 1.3 3.3 2 5.2 2h91.5c7.4 0 10.8-9.2 5.2-14L286.9 550H872c4.4 0 8-3.6 8-8v-60c0-4.4-3.6-8-8-8z"
/>
</svg>
</i>
</div>
<div
class="ant-divider ant-divider-vertical"
/>

View File

@ -19,23 +19,23 @@ describe('PageHeader', () => {
},
];
const wrapper = mount(<PageHeader title="Page Title" breadcrumb={{ routes }} />);
expect(wrapper.find('.ant-page-header-back-icon')).toHaveLength(0);
expect(wrapper.find('.ant-page-header-back')).toHaveLength(0);
});
it('pageHeader should no contain back it back', () => {
it('pageHeader should no contain back', () => {
const wrapper = mount(<PageHeader title="Page Title" backIcon={false} />);
expect(wrapper.find('.ant-page-header-back-icon')).toHaveLength(0);
expect(wrapper.find('.ant-page-header-back')).toHaveLength(0);
});
it('pageHeader should contain back it back', () => {
const callback = jest.fn(() => true);
const wrapper = mount(<PageHeader title="Page Title" onBack={callback} />);
expect(wrapper.find('.ant-page-header-back-icon')).toHaveLength(1);
expect(wrapper.find('.ant-page-header-back')).toHaveLength(1);
});
it('pageHeader onBack transfer', () => {
const callback = jest.fn(() => true);
const wrapper = mount(<PageHeader title="Page Title" onBack={callback} />);
wrapper.find('.ant-page-header-back-icon').simulate('click');
wrapper.find('div.ant-page-header-back-button').simulate('click');
expect(callback).toHaveBeenCalled();
});

View File

@ -6,7 +6,8 @@ import { BreadcrumbProps } from '../breadcrumb';
import Divider from '../divider';
import Tag from '../tag';
import Breadcrumb from '../breadcrumb';
import Wave from '../_util/wave';
import TransButton from '../_util/transButton';
import LocaleReceiver from '../locale-provider/LocaleReceiver';
export interface PageHeaderProps {
backIcon?: React.ReactNode;
@ -18,7 +19,7 @@ export interface PageHeaderProps {
tags?: React.ReactElement<Tag> | React.ReactElement<Tag>[];
footer?: React.ReactNode;
extra?: React.ReactNode;
onBack?: (e: React.MouseEvent<HTMLElement>) => void;
onBack?: (e: React.MouseEvent<HTMLDivElement>) => void;
className?: string;
}
@ -31,17 +32,24 @@ const renderBack = (
return null;
}
return (
<div
className={`${prefixCls}-back-icon`}
onClick={e => {
if (onBack) {
onBack(e);
}
}}
>
<Wave>{backIcon}</Wave>
<Divider type="vertical" />
</div>
<LocaleReceiver componentName="PageHeader">
{({ back }: { back: string }) => (
<div className={`${prefixCls}-back`}>
<TransButton
onClick={(e: React.MouseEvent<HTMLDivElement>) => {
if (onBack) {
onBack(e);
}
}}
className={`${prefixCls}-back-button`}
aria-label={back}
>
{backIcon}
</TransButton>
<Divider type="vertical" />
</div>
)}
</LocaleReceiver>
);
};

View File

@ -14,14 +14,16 @@
padding-bottom: 0;
}
&-back-icon {
&-back {
display: inline-block;
padding: 4px 0;
font-size: 16px;
line-height: 100%;
cursor: pointer;
i:hover {
color: @primary-color;
&-button {
.operation-unit();
color: @text-color;
}
}

View File

@ -6,3 +6,4 @@
@import 'iconfont';
@import 'motion';
@import 'reset';
@import 'operation-unit';

View File

@ -0,0 +1,18 @@
@import '../../style/themes/default';
.operation-unit() {
color: @link-color;
text-decoration: none;
outline: none;
cursor: pointer;
transition: color 0.3s;
&:focus,
&:hover {
color: @link-hover-color;
}
&:active {
color: @link-active-color;
}
}

View File

@ -283,10 +283,12 @@ exports[`renders ./components/typography/demo/interactive.md correctly 1`] = `
class="ant-typography"
>
This is an editable text.
<button
<div
aria-label="edit"
class="ant-typography-edit"
style="border:0;background:transparent;padding:0;line-height:inherit"
role="button"
style="border:0;background:transparent;padding:0;line-height:inherit;display:inline-block"
tabindex="0"
>
<i
aria-label="icon: edit"
@ -308,16 +310,18 @@ exports[`renders ./components/typography/demo/interactive.md correctly 1`] = `
/>
</svg>
</i>
</button>
</div>
</div>
<div
class="ant-typography"
>
This is a copyable text.
<button
<div
aria-label="copy"
class="ant-typography-copy"
style="border:0;background:transparent;padding:0;line-height:inherit"
role="button"
style="border:0;background:transparent;padding:0;line-height:inherit;display:inline-block"
tabindex="0"
>
<i
aria-label="icon: copy"
@ -339,16 +343,18 @@ exports[`renders ./components/typography/demo/interactive.md correctly 1`] = `
/>
</svg>
</i>
</button>
</div>
</div>
<div
class="ant-typography"
>
Replace copy text.
<button
<div
aria-label="copy"
class="ant-typography-copy"
style="border:0;background:transparent;padding:0;line-height:inherit"
role="button"
style="border:0;background:transparent;padding:0;line-height:inherit;display:inline-block"
tabindex="0"
>
<i
aria-label="icon: copy"
@ -370,7 +376,7 @@ exports[`renders ./components/typography/demo/interactive.md correctly 1`] = `
/>
</svg>
</i>
</button>
</div>
</div>
</div>
`;

View File

@ -30,23 +30,6 @@
.typography-title(@heading-4-size, 1.4);
}
.operation-unit() {
color: @link-color;
text-decoration: none;
outline: none;
cursor: pointer;
transition: color 0.3s;
&:focus,
&:hover {
color: @link-hover-color;
}
&:active {
color: @link-active-color;
}
}
// =============== Basic ===============
.@{typography-prefix-cls} {
color: @text-color;