feat: Typography add suffix (#20224)

* feat:Typography add suffix

* feat:run test

* fix:lint

* feat: update suffix example

* feat: update test

* fix: updateupdateSnapshot

* fix: upate test

* run lint

* feat: measureSuffixText

* feat: suffix 不可省略

* fix: title

* fix: update node_modules

* fix: change title type
This commit is contained in:
zouxiaomingya 2019-12-24 11:59:17 +08:00 committed by 二货机器人
parent 4047e2632e
commit 1ebf5fa4b7
7 changed files with 165 additions and 14 deletions

View File

@ -37,16 +37,17 @@ interface EditConfig {
interface EllipsisConfig {
rows?: number;
expandable?: boolean;
suffix?: string;
onExpand?: () => void;
}
export interface BlockProps extends TypographyProps {
title?: string;
editable?: boolean | EditConfig;
copyable?: boolean | CopyConfig;
type?: BaseType;
disabled?: boolean;
ellipsis?: boolean | EllipsisConfig;
// decorations
code?: boolean;
mark?: boolean;
@ -268,8 +269,9 @@ class Base extends React.Component<InternalBlockProps & ConfigConsumerProps, Bas
canUseCSSEllipsis(): boolean {
const { clientRendered } = this.state;
const { editable, copyable } = this.props;
const { rows, expandable } = this.getEllipsis();
const { rows, expandable, suffix } = this.getEllipsis();
if (suffix) return false;
// Can't use css ellipsis since we need to provide the place for button
if (editable || copyable || expandable || !clientRendered) {
return false;
@ -284,7 +286,7 @@ class Base extends React.Component<InternalBlockProps & ConfigConsumerProps, Bas
syncEllipsis() {
const { ellipsisText, isEllipsis, expanded } = this.state;
const { rows } = this.getEllipsis();
const { rows, suffix } = this.getEllipsis();
const { children } = this.props;
if (!rows || rows < 0 || !this.content || expanded) return;
@ -299,7 +301,7 @@ class Base extends React.Component<InternalBlockProps & ConfigConsumerProps, Bas
const { content, text, ellipsis } = measure(
findDOMNode(this.content),
rows,
{ rows, suffix },
children,
this.renderOperations(true),
ELLIPSIS_STR,
@ -398,9 +400,10 @@ class Base extends React.Component<InternalBlockProps & ConfigConsumerProps, Bas
type,
disabled,
style,
title,
...restProps
} = this.props;
const { rows } = this.getEllipsis();
const { rows, suffix } = this.getEllipsis();
const textProps = omit(restProps, [
'prefixCls',
@ -421,18 +424,29 @@ class Base extends React.Component<InternalBlockProps & ConfigConsumerProps, Bas
const cssLineClamp = rows && rows > 1 && cssEllipsis;
let textNode: React.ReactNode = children;
let ariaLabel: string | null = null;
let ariaLabel: string | undefined;
// Only use js ellipsis when css ellipsis not support
if (rows && isEllipsis && !expanded && !cssEllipsis) {
ariaLabel = String(children);
ariaLabel = title;
if (!title && (typeof children === 'string' || typeof children === 'number')) {
ariaLabel = String(children);
}
// We move full content to outer element to avoid repeat read the content by accessibility
textNode = (
<span title={String(children)} aria-hidden="true">
<span title={ariaLabel} aria-hidden="true">
{ellipsisContent}
{ELLIPSIS_STR}
{suffix}
</span>
);
} else {
textNode = (
<>
{children}
{suffix}
</>
);
}
textNode = wrapperDecorations(this.props, textNode);

View File

@ -548,6 +548,43 @@ exports[`renders ./components/typography/demo/paragraph-debug.md correctly 1`] =
</div>
`;
exports[`renders ./components/typography/demo/suffix.md correctly 1`] = `
<div>
<div
class="ant-slider"
>
<div
class="ant-slider-rail"
/>
<div
class="ant-slider-track"
style="left:0%;right:auto;width:0%"
/>
<div
class="ant-slider-step"
/>
<div
aria-disabled="false"
aria-valuemax="10"
aria-valuemin="1"
aria-valuenow="1"
class="ant-slider-handle"
role="slider"
style="left:0%;right:auto;transform:translateX(-50%)"
tabindex="0"
/>
<div
class="ant-slider-mark"
/>
</div>
<div
class="ant-typography ant-typography-ellipsis"
>
To be, or not to be, that is a question: Whether it is nobler in the mind to suffer. The slings and arrows of outrageous fortune--William Shakespeare
</div>
</div>
`;
exports[`renders ./components/typography/demo/text.md correctly 1`] = `
<div>
<span

View File

@ -96,6 +96,45 @@ describe('Typography', () => {
wrapper.unmount();
});
it('should middle ellipsis', () => {
const suffix = '--suffix';
const wrapper = mount(
<Base ellipsis={{ rows: 1, suffix }} component="p">
{fullStr}
</Base>,
);
jest.runAllTimers();
wrapper.update();
expect(wrapper.find('p').text()).toEqual('Bamboo is...--suffix');
wrapper.unmount();
});
it('should front or middle ellipsis', () => {
const suffix = '--The information is very important';
const wrapper = mount(
<Base ellipsis={{ rows: 1, suffix }} component="p">
{fullStr}
</Base>,
);
jest.runAllTimers();
wrapper.update();
expect(wrapper.find('p').text()).toEqual('...--The information is very important');
wrapper.setProps({ ellipsis: { rows: 2, suffix } });
jest.runAllTimers();
wrapper.update();
expect(wrapper.find('p').text()).toEqual('Ba...--The information is very important');
wrapper.setProps({ ellipsis: { rows: 99, suffix } });
jest.runAllTimers();
wrapper.update();
expect(wrapper.find('p').text()).toEqual(fullStr + suffix);
wrapper.unmount();
});
it('connect children', () => {
const bamboo = 'Bamboo';
const is = ' is ';

View File

@ -0,0 +1,53 @@
---
order: 100
title:
zh-CN: 后缀
en-US: suffix
---
## zh-CN
添加后缀的省略。
## en-US
add suffix ellipsis support.
```jsx
import { Typography, Slider } from 'antd';
const { Paragraph } = Typography;
class Demo extends React.Component {
state = {
rows: 1,
};
onChange = rows => {
this.setState({ rows });
};
render() {
const { rows } = this.state;
const article =
'To be, or not to be, that is a question: Whether it is nobler in the mind to suffer. The slings and arrows of outrageous fortune';
return (
<div>
<Slider value={rows} min={1} max={10} onChange={this.onChange} />
<Paragraph
ellipsis={{
rows,
expandable: true,
suffix: '--William Shakespeare',
}}
title={`${article}--William Shakespeare`}
>
{article}
</Paragraph>
</div>
);
}
}
ReactDOM.render(<Demo />, mountNode);
```

View File

@ -55,7 +55,7 @@ Basic text writing, including headings, body text, lists, and more.
| delete | Deleted line style | boolean | false | |
| disabled | Disabled content | boolean | false | |
| editable | Editable. Can control edit state when is object | boolean \| { editing: boolean, onStart: Function, onChange: Function(string) } | false | |
| ellipsis | Display ellipsis when text overflows. Can configure rows and expandable by using object | boolean \| { rows: number, expandable: boolean, onExpand: Function } | false | |
| ellipsis | Display ellipsis when text overflows. Can configure rows expandable and suffix by using object | boolean \| { rows: number, expandable: boolean suffix: string, onExpand: Function } | false | |
| mark | Marked style | boolean | false | |
| underline | Underlined style | boolean | false | |
| onChange | Trigger when user edits the content | Function(string) | - | |

View File

@ -52,7 +52,7 @@ cols: 1
| delete | 添加删除线样式 | boolean | false | |
| disabled | 禁用文本 | boolean | false | |
| editable | 是否可编辑,为对象时可对编辑进行控制 | boolean \| { editing: boolean, onStart: Function, onChange: Function(string) } | false | |
| ellipsis | 自动溢出省略,为对象时可设置省略行数与是否可展开等 | boolean \| { rows: number, expandable: boolean, onExpand: Function } | false | |
| ellipsis | 自动溢出省略,为对象时可设置省略行数、是否可展开、添加后缀等 | boolean \| { rows: number, expandable: boolean, suffix: string, onExpand: Function } | false | |
| mark | 添加标记样式 | boolean | false | |
| underline | 添加下划线样式 | boolean | false | |
| onChange | 当用户提交编辑内容时触发 | Function(string) | - | |

View File

@ -6,6 +6,10 @@ interface MeasureResult {
finished: boolean;
reactNode: React.ReactNode;
}
interface Option {
rows: number;
suffix?: string;
}
// We only handle element & text node.
const ELEMENT_NODE = 1;
@ -53,7 +57,7 @@ function mergeChildren(children: React.ReactNode[]): React.ReactNode[] {
export default (
originEle: HTMLElement,
rows: number,
option: Option,
content: React.ReactNode,
fixedContent: React.ReactNode[],
ellipsisStr: string,
@ -64,6 +68,7 @@ export default (
document.body.appendChild(ellipsisContainer);
}
const { rows, suffix = '' } = option;
// Get origin style
const originStyle = window.getComputedStyle(originEle);
const originCSS = styleToString(originStyle);
@ -92,7 +97,10 @@ export default (
const contentList: React.ReactNode[] = mergeChildren(toArray(content));
render(
<div style={wrapperStyle}>
<span style={wrapperStyle}>{contentList}</span>
<span style={wrapperStyle}>
{contentList}
{suffix}
</span>
<span style={wrapperStyle}>{fixedContent}</span>
</div>,
ellipsisContainer,
@ -125,7 +133,7 @@ export default (
// Create origin content holder
const ellipsisContentHolder = document.createElement('span');
ellipsisContainer.appendChild(ellipsisContentHolder);
const ellipsisTextNode = document.createTextNode(ellipsisStr);
const ellipsisTextNode = document.createTextNode(ellipsisStr + suffix);
ellipsisContentHolder.appendChild(ellipsisTextNode);
fixedNodes.forEach(childNode => {
@ -155,7 +163,7 @@ export default (
const currentStepText = fullText.slice(0, step);
textNode.textContent = currentStepText;
if (inRange()) {
if (inRange() || !currentStepText) {
return step === fullText.length
? {
finished: false,