Browse Source

feat: Add <Numeral /> component for number formatting

Signed-off-by: uiuing <[email protected]>
uiuing 3 years ago
parent
commit
95e45b544c

+ 69 - 3
content/basic/typography/index-en-US.md

@@ -5,7 +5,7 @@ category: Basic
 title:  Typography
 subTitle: Typography
 icon: doc-typography
-brief: The basic format of text, images, and paragraphs.
+brief: The basic format of text, images, paragraphs, and numeric.
 ---
 
 
@@ -147,6 +147,49 @@ function Demo() {
 }
 ```
 
+### Numeral
+
+[//]: # (基于Text组件,添加了属性: `rule`, `precision`, `truncate`, `parser`, 以提供需要单独处理文本中Numeral 的能力)
+Based on Text component, added properties: `rule`, `precision`, `truncate`, `parser`, to provide the ability to handle Numeral in text separately.
+
+```jsx live=true
+import React from 'react';
+import { Typography } from '@douyinfe/semi-ui';
+
+function Demo() {
+    const { Numeral } = Typography;
+    return (
+        <div>
+            <Numeral rule="bytes-binary" truncate="floor" precision={2}>
+                <p>Used: 1224</p>
+                <p>
+                    Available: <b>{Number(2e12)}</b>
+                </p>
+            </Numeral>
+            <br />  
+            <Numeral rule="percentages">
+                <p>Favorable rating: .915</p>
+            </Numeral>
+            <Numeral type="danger" rule="currency">
+                <p>Price: $1200</p>
+            </Numeral>
+            <br />
+            <Numeral
+                link={{ href: 'javascript:;' }}
+                parser={oldVal =>
+                    oldVal
+                        .split(' ')
+                        .map(item => (item.match(/^\d{4}-\d{1,2}-\d{1,2}$/) ? new Date(item).toDateString() : item))
+                        .join(' ')
+                }
+            >
+              <div>&gt; 2022-6-18 Offer Details</div>
+            </Numeral>
+        </div>
+    );
+}
+```
+
 ### Size
 
 Paragraph and Text component support two sizes, `small`(12px) and `normal`(14px). By default it is set to `normal`。
@@ -182,13 +225,15 @@ import React from 'react';
 import { Typography, TextArea } from '@douyinfe/semi-ui';
 
 function Demo() {
-    const { Paragraph, Text } = Typography;
+    const { Paragraph, Text, Numeral } = Typography;
 
     return (
         <div>
             <Paragraph copyable>Click the right icon to copy text.</Paragraph>
             <Paragraph copyable={{ content: 'Hello, Semi Design!' }}>Click to copy text.</Paragraph>
             <Paragraph copyable={{ onCopy: () => Toast.success({ content: 'Successfully copied.'}) }}>Click the right icon to copy.</Paragraph>
+            Timestamp: <Numeral truncate="ceil" copyable underline>{new Date().getTime()/1000}s</Numeral>
+            <br/>
             <br/>
             <Text type="secondary">Paste here: </Text>
             <br/>
@@ -363,6 +408,27 @@ function Demo() {
 | type       | Type, one of `primary`, `secondary`, `warning`, `danger`, `tertiary`(**v>=1.2.0**), `quaternary`(**v>=1.2.0**), `success`(**v>=1.7.0**) | string                                                | `primary` | 0.27.0  |
 | underline  | Underlined style                                                                                                                        | boolean                                               | false     | 0.27.0  |
 
+### Typography.Numeral
+
+| Properties | Instructions                                                                                                                             | type                                                  | Default   | version |
+| ---------- |------------------------------------------------------------------------------------------------------------------------------------------| ----------------------------------------------------- | --------- | ------- |
+| rule      | Parsing rules, one of `text`, `numbers`, `bytes-decimal`, `bytes-binary`, `percentages`, `currency`, `exponential`                       | string                    | `text`                                     |        |
+| precision  | Number of decimal places retained                                                                                                        | number                    | 0                                          |        |
+| truncate  | Truncation of decimal places,one of `ceil`, `floor`, `round`                                                                             | string                    | `round`                                    |        |
+| parser    | Custom numeral parsing functions                                                                                                         | (str: string) => string | -                                          |        |
+| copyable   | Toggle whether to be copyable                                                                                                            | boolean \| object:[Copyable Config](#Copyable-Config) | false     | 0.27.0  |
+| code       | wrap with `code` element                                                                                                                 | boolean                                               | -         |         |
+| component  | Custom rendering html element                                                                                                            | html element                                          | span      |         |
+| delete     | Deleted style                                                                                                                            | boolean                                               | false     | 0.27.0  |
+| disabled   | Disabled style                                                                                                                           | boolean                                               | false     | 0.27.0  |
+| ellipsis   | Display ellipsis when text overflows                                                                                                     | boolean\|object:Ellipsis Config                       | false     | 0.34.0  |
+| icon       | Prefix icon.                                                                                                                             | ReactNode                                             | -         | 0.27.0  |
+| link       | Toggle whether to display as a link. When passing object, the attributes will be transparently passed to the a tag                       | boolean\|object                                       | false     | 0.27.0  |
+| mark       | Marked style                                                                                                                             | boolean                                               | false     | 0.27.0  |
+| size       | Size, one of `normal`,`small`                                                                                                            | string                                                | `normal`  | 0.27.0  |
+| strong     | Bold style                                                                                                                               | boolean                                               | false     | 0.27.0  |
+| type       | Type, one of `primary`, `secondary`, `warning`, `danger`, `tertiary`(**v>=1.2.0**) , `quaternary`(**v>=1.2.0**), `success`(**v>=1.7.0**) | string                                                | `primary` | 0.27.0  |
+| underline  | Underlined style                                                                                                                         | boolean                                               | false     | 0.27.0  |
 
 ### Ellipsis Config
 **v >= 0.34.0**
@@ -425,4 +491,4 @@ function Demo() {
 | View <PureA> user documentation </PureA> for details| View the<PureA> user documentation</PureA> for details |
 
 ## Design Tokens
-<DesignToken/>
+<DesignToken/>

+ 76 - 10
content/basic/typography/index.md

@@ -4,7 +4,7 @@ order: 17
 category: 基础
 title:  Typography 版式
 icon: doc-typography
-brief: 文字,图片,段落的基本格式。
+brief: 文字,图片,段落,数值的基本格式。
 ---
 
 ## 使用场景
@@ -137,6 +137,46 @@ function Demo() {
 }
 ```
 
+### 数值组件
+基于Text组件,添加了属性: `rule`, `precision`, `truncate`, `parser`, 以提供需要单独处理文本中Numeral 的能力
+```jsx live=true
+import React from 'react';
+import { Typography } from '@douyinfe/semi-ui';
+
+function Demo() {
+    const { Numeral } = Typography;
+    return (
+        <div>
+            <Numeral rule="bytes-binary" truncate="floor" precision={2}>
+                <p>已使用: 1224</p>
+                <p>
+                    未使用: <b>{Number(2e12)}</b>
+                </p>
+            </Numeral>
+            <br />
+            <Numeral rule="percentages">
+                <p>好评率: .915</p>
+            </Numeral>
+            <Numeral type="danger" rule="currency" precision={2}>
+                <p>价格: ¥8539.20</p>
+            </Numeral>
+            <br />
+            <Numeral
+                link={{ href: 'javascript:;' }}
+                parser={oldVal =>
+                    oldVal
+                        .split(' ')
+                        .map(item => (item.match(/^\d{4}-\d{1,2}-\d{1,2}$/) ? new Date(item).toDateString() : item))
+                        .join(' ')
+                }
+            >
+              <div>&gt; 2022-6-18 优惠详情</div>
+            </Numeral>
+        </div>
+    );
+}
+```
+
 ### 文本大小
 段落组件和文本组件支持两种尺寸,`small`(12px) 和 `normal`(14px),默认为`normal`。
 ```jsx live=true
@@ -168,17 +208,19 @@ import React from 'react';
 import { Typography, TextArea } from '@douyinfe/semi-ui';
 
 function Demo() {
-    const { Paragraph, Text } = Typography;
+    const { Paragraph, Text, Numeral } = Typography;
 
     return (
         <div>
-            <Paragraph copyable>点击右边的图标复制文本。</Paragraph>
-            <Paragraph copyable={{ content: 'Hello, Semi Design!' }}>点击复制文本。</Paragraph>
-            <Paragraph copyable={{ onCopy: () => Toast.success({ content: '复制文本成功'}) }}>点击右边的图标复制文本。</Paragraph>
-            <br/>
-            <Text type="secondary">粘贴区域:</Text>
-            <br/>
-            <TextArea autosize style={{width: 320, marginTop: 4}} rows={3} />
+          <Paragraph copyable>点击右边的图标复制文本。</Paragraph>
+          <Paragraph copyable={{ content: 'Hello, Semi Design!' }}>点击复制文本。</Paragraph>
+          <Paragraph copyable={{ onCopy: () => Toast.success({ content: '复制文本成功'}) }}>点击右边的图标复制文本。</Paragraph>
+          时间戳: <Numeral truncate="ceil" copyable underline>{new Date().getTime()/1000}s</Numeral>
+          <br/>
+          <br/>
+          <Text type="secondary">粘贴区域:</Text>
+          <br/>
+          <TextArea autosize style={{width: 320, marginTop: 4}} rows={3} />
         </div>
     );
 }
@@ -334,6 +376,7 @@ function Demo() {
 | underline | 添加下划线样式                                                                                                                            | boolean                           | false     | 0.27.0 |
 
 ### Typography.Paragraph
+
 | 属性      | 说明                                                                                                                                      | 类型                              | 默认值    | 版本   |
 | --------- | ----------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------- | --------- | ------ |
 | component | 自定义渲染元素                                                                                                                            | html element                      | p         |        |
@@ -349,6 +392,29 @@ function Demo() {
 | type      | 文本类型,可选 `primary`, `secondary`, `warning`, `danger`, `tertiary`(**v>=1.2.0**), `quaternary`(**v>=1.2.0**), `success`(**v>=1.7.0**) | string                            | `primary` | 0.27.0 |
 | underline | 添加下划线样式                                                                                                                            | boolean                           | false     | 0.27.0 |
 
+### Typography.Numeral
+
+| 属性        | 说明                                                                                                                                 | 类型                        | 默认值                                        | 版本   |
+|-----------|------------------------------------------------------------------------------------------------------------------------------------|---------------------------|--------------------------------------------| ------ |
+| rule      | 解析规则,可选 `text`, `numbers`, `bytes-decimal`, `bytes-binary`, `percentages`, `currency`, `exponential`                               | string                    | `text`                                     |        |
+| precision  | 小数点后保留位数                                                                                                                           | number                    | 0                                          |        |
+| truncate  | 小数点后保留位截段取整方式,可选 `ceil`, `floor`, `round`                                                                                          | string                    | `round`                                    |        |
+| parser    | 自定义数值解析函数                                                                                                                          | (str: string) => string | -                                          |        |
+| component | 自定义渲染元素                                                                                                                            | html element              | span                                       |        |
+| code      | 是否被 `code` 元素包裹                                                                                                                    | boolean                   | -                                          |        |
+| copyable  | 是否可拷贝                                                                                                                              | boolean \                 | object:[Copyable Config](#Copyable-Config) | false     | 0.27.0 |
+| delete    | 添加删除线样式                                                                                                                            | boolean                   | false                                      | 0.27.0 |
+| disabled  | 禁用文本                                                                                                                               | boolean                   | false                                      | 0.27.0 |
+| ellipsis  | 设置自动溢出省略                                                                                                                           | boolean\                  | object:Ellipsis Config                     | false     | 0.34.0 |
+| icon      | 前缀图标                                                                                                                               | ReactNode                 | -                                          | 0.27.0 |
+| link      | 是否为链接,传object时,属性将透传给a标签                                                                                                           | boolean\                  | object                                     | false     | 0.27.0 |
+| mark      | 添加标记样式                                                                                                                             | boolean                   | false                                      | 0.27.0 |
+| size      | 文本大小,可选`normal`,`small`                                                                                                            | string                    | `normal`                                   | 0.27.0 |
+| strong    | 是否加粗                                                                                                                               | boolean                   | false                                      | 0.27.0 |
+| type      | 文本类型,可选 `primary`, `secondary`, `warning`, `danger`, `tertiary`(**v>=1.2.0**), `quaternary`(**v>=1.2.0**), `success`(**v>=1.7.0**) | string                    | `primary`                                  | 0.27.0 |
+| underline | 添加下划线样式                                                                                                                            | boolean                   | false                                      | 0.27.0 |
+
+
 ### Ellipsis Config
 **v >= 0.34.0**
 
@@ -409,4 +475,4 @@ function Demo() {
 | View <PureA> user documentation </PureA> for details| View the<PureA> user documentation</PureA> for details |
 
 ## 设计变量
-<DesignToken/>
+<DesignToken/>

+ 2 - 5
packages/semi-foundation/typography/constants.ts

@@ -10,10 +10,7 @@ const strings = {
     SPACING: ['normal', 'extended'],
     HEADING: [1, 2, 3, 4, 5, 6],
     RULE: ['text', 'numbers', 'bytes-decimal', 'bytes-binary', 'percentages', 'currency', 'exponential'],
-    MANTISSA_ROUND: ['ceil', 'floor', 'round'],
+    TRUNCATE: ['ceil', 'floor', 'round'],
 } as const;
 
-export {
-    cssClasses,
-    strings
-};
+export { cssClasses, strings };

+ 47 - 46
packages/semi-foundation/typography/formatNumeral.ts

@@ -2,7 +2,7 @@ import { strings } from './constants';
 
 // rule types: 'text' | 'numbers' | 'bytes-decimal' | 'bytes-binary' | 'percentages' | 'currency' | 'exponential'
 type Rule = typeof strings.RULE[number];
-type Truncate = typeof strings.MANTISSA_ROUND[number];
+type Truncate = typeof strings.TRUNCATE[number];
 type Parser = (value: string) => string;
 
 type RuleMethods = {
@@ -15,17 +15,12 @@ type TruncateMethods = {
 export default class FormatNumeral {
     private readonly content: string;
     private readonly rule: Rule;
-    private readonly mantissa: number;
+    private readonly precision: number;
     private readonly truncate: Truncate;
     private readonly parser: Parser | undefined;
     private readonly isDiyParser: boolean;
 
-    private readonly truncateMethods: TruncateMethods = {
-        ceil: Math.ceil,
-        floor: Math.floor,
-        round: Math.round,
-    };
-    // Collection of formatting methods;  Methods: Rule (strings.RULE);  Not included: 'text' & 'numbers'
+    // A collection of methods for formatting numbers;  Methods key: Rule (strings.RULE);  Not included: 'text' & 'numbers'
     private readonly ruleMethods: RuleMethods = {
         'bytes-decimal': (value: number) => {
             const units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
@@ -34,7 +29,7 @@ export default class FormatNumeral {
                 value /= 1000;
                 i++;
             }
-            return `${this.truncateMantissa(value)} ${units[i]}`;
+            return `${this.truncatePrecision(value)} ${units[i]}`;
         },
         'bytes-binary': (value: number) => {
             const units = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
@@ -43,79 +38,85 @@ export default class FormatNumeral {
                 value /= 1024;
                 i++;
             }
-            return `${this.truncateMantissa(value)} ${units[i]}`;
+            return `${this.truncatePrecision(value)} ${units[i]}`;
         },
         percentages: (value: number) => {
             const cArr = value.toString().split('.');
             if (Number(cArr[0]) === 0) {
-                return `${this.truncateMantissa(value * 100)}%`;
+                return `${this.truncatePrecision(value * 100)}%`;
             }
-            return `${this.truncateMantissa(value)}%`;
+            return `${this.truncatePrecision(value)}%`;
         },
         currency: (value: number) => {
-            const cArr = this.truncateMantissa(value).split('.');
+            const cArr = this.truncatePrecision(value).split('.');
             const cInt = cArr[0].replace(/\d{1,3}(?=(\d{3})+$)/g, '$&,');
             const cFloat = cArr[1] ? `.${cArr[1]}` : '';
             return `${cInt}${cFloat}`;
         },
         exponential: (value: number) => {
-            const vExponential = value.toExponential(this.mantissa + 2);
-            return this.truncateMantissa(vExponential);
+            const vExponential = value.toExponential(this.precision + 2);
+            const vArr = vExponential.split('e');
+            return `${this.truncatePrecision(Number(vArr[0]))}e${vArr[1]}`;
         },
     };
+    // A collection of methods for truncating numbers; Methods key: Truncate (strings.Truncate);
+    private readonly truncateMethods: TruncateMethods = {
+        ceil: Math.ceil,
+        floor: Math.floor,
+        round: Math.round,
+    };
 
-    constructor(content: string, rule: Rule, mantissa: number, truncate: Truncate, parser: Parser | undefined) {
+    constructor(content: string, rule: Rule, precision: number, truncate: Truncate, parser: Parser | undefined) {
         this.isDiyParser = typeof parser !== 'undefined';
         this.content = content;
         this.rule = rule;
-        this.mantissa = mantissa;
+        this.precision = precision;
         this.truncate = truncate;
         this.parser = parser;
     }
 
-    private truncateMantissa(content: string | number): string {
-        // Truncation and selection of rounding methods for processing. function from: truncateMethods
-        const cTruncated =
-            this.truncateMethods[this.truncate](Number(content) * Math.pow(10, this.mantissa)) /
-            Math.pow(10, this.mantissa);
-        const cArr = cTruncated.toString().split('.');
-        // is an integer then the end number is normalised
-        if (cArr.length === 1) {
-            return cTruncated.toFixed(this.mantissa);
-        }
-        const cTLength = cArr[1].length;
-        // Fill in any missing `0` at the end.
-        if (cTLength < this.mantissa) {
-            return `${cArr[0]}.${cArr[1]}${'0'.repeat(this.mantissa - cTLength)}`;
-        }
-        return cTruncated.toString();
-    }
-
     // Formatting numbers within a string.
     public format(): string {
         // Executed when a custom method exists
         if (this.isDiyParser) {
             return this.parser(this.content);
-        } else if (this.rule === 'text') {
-            return this.content;
         }
-        // Separate extraction of numbers when `rule` type is `numbers`.
+        //  When the `rule` is `text`, only the `truncatePrecision` method is executed for numeric processing.
+        if (this.rule === 'text') {
+            return extractNumbers(this.content)
+                .map(item => (checkIsNumeral(item) ? this.truncatePrecision(item) : item))
+                .join('');
+        }
+        // Separate extraction of numbers when `rule` is `numbers`.
         if (this.rule === 'numbers') {
             return extractNumbers(this.content)
                 .filter(item => checkIsNumeral(item))
-                .map(item => this.truncateMantissa(item))
+                .map(item => this.truncatePrecision(item))
                 .join(',');
         }
         // Run formatting methods that exist.
         return extractNumbers(this.content)
-            .map(item => {
-                if (checkIsNumeral(item)) {
-                    return this.ruleMethods[this.rule](Number(item));
-                }
-                return item;
-            })
+            .map(item => (checkIsNumeral(item) ? this.ruleMethods[this.rule](Number(item)) : item))
             .join('');
     }
+
+    private truncatePrecision(content: string | number): string {
+        // Truncation and selection of rounding methods for processing. function from: truncateMethods
+        const cTruncated =
+            this.truncateMethods[this.truncate](Number(content) * Math.pow(10, this.precision)) /
+            Math.pow(10, this.precision);
+        const cArr = cTruncated.toString().split('.');
+        // is an integer then the end number is normalised
+        if (cArr.length === 1) {
+            return cTruncated.toFixed(this.precision);
+        }
+        const cTLength = cArr[1].length;
+        // Fill in any missing `0` at the end.
+        if (cTLength < this.precision) {
+            return `${cArr[0]}.${cArr[1]}${'0'.repeat(this.precision - cTLength)}`;
+        }
+        return cTruncated.toString();
+    }
 }
 
 // Separate numbers from strings, the `-` symbol is a numeric prefix not allowed on its own.
@@ -125,5 +126,5 @@ function extractNumbers(content: string): Array<string> {
 }
 
 function checkIsNumeral(str: string): boolean {
-    return !(isNaN(Number(str)) && str.replace(/\s+/g, '') === '');
+    return !(isNaN(Number(str)) || str.replace(/\s+/g, '') === '');
 }

+ 62 - 25
packages/semi-ui/typography/__test__/typography.test.js

@@ -6,70 +6,107 @@ describe(`Typography`, () => {
     beforeEach(() => {
         document.getSelection = () => {
             return {
-                removeAllRanges: () => { }
+                removeAllRanges: () => {},
             };
-        }
+        };
     });
 
     it('custom component', () => {
         let props = { component: 'div' };
-        const typographyTitle = mount(<Typography.Title {...props} heading={1}>Semi Design</Typography.Title>)
+        const typographyTitle = mount(
+            <Typography.Title {...props} heading={1}>
+                Semi Design
+            </Typography.Title>
+        );
         const title = typographyTitle.find('div.semi-typography-h1');
         expect(title.length).toEqual(1);
 
-        const typographyText = mount(<Typography.Text {...props} id="text">Semi Design</Typography.Text>)
+        const typographyText = mount(
+            <Typography.Text {...props} id="text">
+                Semi Design
+            </Typography.Text>
+        );
         const text = typographyText.find('div.semi-typography');
         expect(text.length).toEqual(1);
 
-        const typographyParagraph = mount(<Typography.Paragraph {...props}>Semi Design</Typography.Paragraph>)
+        const typographyParagraph = mount(<Typography.Paragraph {...props}>Semi Design</Typography.Paragraph>);
         const p = typographyParagraph.find('div.semi-typography-paragraph');
         expect(p.length).toEqual(1);
         typographyParagraph.unmount();
     });
 
     it('typography copyable', () => {
-        const typographyParagraph = mount(<Typography.Paragraph copyable >Semi Design</Typography.Paragraph>)
+        const typographyParagraph = mount(<Typography.Paragraph copyable>Semi Design</Typography.Paragraph>);
         const p = typographyParagraph.find('.semi-icon-copy');
         expect(p.length).toEqual(1);
         p.at(0).simulate('click');
         expect(typographyParagraph.find('.semi-typography-action-copied').length).toEqual(1);
-        typographyParagraph.setProps({copyable: false})
-        typographyParagraph.update()
+        typographyParagraph.setProps({ copyable: false });
+        typographyParagraph.update();
         expect(typographyParagraph.find('.semi-icon-copy').length).toEqual(0);
     });
 
     it('typography link', () => {
-        const text = mount(
-            <Typography.Text link={{ href: 'https://semi.design/' }}>链接文本</Typography.Text>
-        )
+        const text = mount(<Typography.Text link={{ href: 'https://semi.design/' }}>链接文本</Typography.Text>);
         expect(text.find('.semi-typography.semi-typography-link').length).toEqual(1);
-        text.setProps({disabled: true})
-        text.update()
+        text.setProps({ disabled: true });
+        text.update();
         expect(text.find('.semi-typography.semi-typography-disabled').length).toEqual(1);
-        text.setProps({underline: true, link: false})
-        text.update()
+        text.setProps({ underline: true, link: false });
+        text.update();
         expect(text.find('.semi-typography u').length).toEqual(1);
     });
 
     it('typography ellipsis', () => {
-        const typographyParagraph = mount(<Typography.Paragraph  ellipsis={{ showTooltip: true }} style={{ width: 250 }}>
-            是一个很长很长很长很长5号标题</Typography.Paragraph>)
+        const typographyParagraph = mount(
+            <Typography.Paragraph ellipsis={{ showTooltip: true }} style={{ width: 250 }}>
+                是一个很长很长很长很长5号标题
+            </Typography.Paragraph>
+        );
         // jest 测不出layout,补一些无效用例,提高coverage
         expect(typographyParagraph.find('semi-typography-ellipsis').length).toEqual(0);
-        typographyParagraph.setProps({children: '的撒的撒打算的撒的撒的撒打算打的撒的撒打算的撒的撒的撒打算打'})
-        typographyParagraph.update()
+        typographyParagraph.setProps({ children: '的撒的撒打算的撒的撒的撒打算打的撒的撒打算的撒的撒的撒打算打' });
+        typographyParagraph.update();
         expect(typographyParagraph.find('semi-typography-ellipsis').length).toEqual(0);
         typographyParagraph.setProps({
             ellipsis: {
-                expandText:'expandText',collapseText:'collapseText',
+                expandText: 'expandText',
+                collapseText: 'collapseText',
                 rows: 1,
                 showTooltip: {
-                    type: 'popover'
+                    type: 'popover',
                 },
-                suffix: 'suffix'
-            }
-        })
-        typographyParagraph.update()
+                suffix: 'suffix',
+            },
+        });
+        typographyParagraph.update();
         expect(typographyParagraph.find('semi-typography-ellipsis').length).toEqual(0);
     });
+
+    it('typography Numeral', () => {
+        let numeral = mount(
+            <Typography.Numeral rule={'numbers'} truncate={'ceil'} precision={2}>
+                <div className="price">
+                    <span>预期价格:{() => 1.555}; 成本: -1; 盈利: 0.555</span>
+                    <b>Currency symbols: $</b>
+                </div>
+            </Typography.Numeral>
+        );
+        expect(numeral.find('.price').text()).toEqual('1.56-1.00,0.56');
+        numeral = mount(
+            <Typography.Numeral rule={'currency'} truncate={'round'} precision={1}>
+                <div className="price">
+                    Total revenue: <b>$ 1992.15</b>
+                </div>
+            </Typography.Numeral>
+        );
+        expect(numeral.find('.price').text()).toEqual('Total revenue: $ 1,992.2');
+        numeral.setProps({ rule: 'exponential', truncate: 'floor', precision: 2 });
+        expect(numeral.find('.price').text()).toEqual('Total revenue: $ 1.99e+3');
+        // test: parser
+        numeral.setProps({
+            parser: oldVal => oldVal.replace(/[^\d.]/g, ''),
+        });
+        expect(numeral.find('.price').text()).toEqual('1992.15');
+    });
 });

+ 4 - 4
packages/semi-ui/typography/base.tsx

@@ -240,9 +240,9 @@ export default class Base extends Component<BaseTypographyProps, BaseTypographyS
             return false;
         }
         const updateOverflow =
-            rows <= 1 ?
-                this.wrapperRef.current.scrollWidth > this.wrapperRef.current.clientWidth :
-                this.wrapperRef.current.scrollHeight > this.wrapperRef.current.offsetHeight;
+            rows <= 1
+                ? this.wrapperRef.current.scrollWidth > this.wrapperRef.current.clientWidth
+                : this.wrapperRef.current.scrollHeight > this.wrapperRef.current.offsetHeight;
         return updateOverflow;
     };
 
@@ -527,7 +527,7 @@ export default class Base extends Component<BaseTypographyProps, BaseTypographyS
         const iconSize: Size = size === 'small' ? 'small' : 'default';
         return (
             <span className={`${prefixCls}-icon`} x-semi-prop="icon">
-                {isSemiIcon(icon) ? React.cloneElement((icon as React.ReactElement), { size: iconSize }) : icon}
+                {isSemiIcon(icon) ? React.cloneElement(icon as React.ReactElement, { size: iconSize }) : icon}
             </span>
         );
     }

+ 1 - 1
packages/semi-ui/typography/interface.ts

@@ -26,4 +26,4 @@ export type TypographyBaseType = ArrayElement<typeof strings.TYPE>;
 export type TypographyBaseSize = ArrayElement<typeof strings.SIZE>;
 export type TypographyBaseSpacing = ArrayElement<typeof strings.SPACING>;
 export type TypographyBaseRule = ArrayElement<typeof strings.RULE>;
-export type TypographyBaseTruncate = ArrayElement<typeof strings.MANTISSA_ROUND>;
+export type TypographyBaseTruncate = ArrayElement<typeof strings.TRUNCATE>;

+ 5 - 5
packages/semi-ui/typography/numeral.tsx

@@ -16,7 +16,7 @@ type OmitNumeralProps = OmitTypographyProps;
 
 export interface NumeralProps extends Omit<React.HTMLAttributes<HTMLSpanElement>, OmitNumeralProps> {
     rule?: TypographyBaseRule;
-    mantissa?: number;
+    precision?: number;
     truncate?: TypographyBaseTruncate;
     parser?: (value: string) => string;
     children?: React.ReactNode;
@@ -39,8 +39,8 @@ export interface NumeralProps extends Omit<React.HTMLAttributes<HTMLSpanElement>
 export default class Numeral extends PureComponent<NumeralProps> {
     static propTypes = {
         rule: PropTypes.oneOf(strings.RULE),
-        mantissa: PropTypes.number,
-        truncate: PropTypes.oneOf(strings.MANTISSA_ROUND),
+        precision: PropTypes.number,
+        truncate: PropTypes.oneOf(strings.TRUNCATE),
         parser: PropTypes.func,
         copyable: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]),
         delete: PropTypes.bool,
@@ -60,7 +60,7 @@ export default class Numeral extends PureComponent<NumeralProps> {
 
     static defaultProps = {
         rule: 'text',
-        mantissa: 0,
+        precision: 0,
         truncate: 'round',
         parser: undefined,
         copyable: false,
@@ -88,7 +88,7 @@ export default class Numeral extends PureComponent<NumeralProps> {
                 return new FormatNumeral(
                     String(item),
                     this.props.rule,
-                    this.props.mantissa,
+                    this.props.precision,
                     this.props.truncate,
                     this.props.parser
                 ).format();