From e5bfe3ac9afd6b3c44d4b7cea6ea898081bd73b5 Mon Sep 17 00:00:00 2001 From: Yuri Victor Munayev Date: Thu, 10 Nov 2022 16:31:44 -0500 Subject: [PATCH] feat: add percent column component using intl formatter (#622) * feat: add percent column component using intl formatter * fix: change title to container * fix: useLocale import from react-rainbow-component * feat: add cellAlignment prop Co-authored-by: Jose Leandro Torres --- package.json | 2 +- .../docs/components/PercentColumn.story.mdx | 19 +++ .../docs/stories/PercentColumn.story.js | 125 ++++++++++++++++++ .../__test__/percentColumn.spec.js | 29 ++++ .../PercentColumn/helpers/formatPercent.ts | 16 +++ .../src/components/PercentColumn/index.tsx | 65 +++++++++ .../src/components/PercentColumn/styled.ts | 9 ++ .../src/components/PercentColumn/types.ts | 14 ++ packages/listview/src/index.d.ts | 1 + packages/listview/src/index.js | 1 + yarn.lock | 8 +- 11 files changed, 284 insertions(+), 5 deletions(-) create mode 100644 packages/listview/docs/components/PercentColumn.story.mdx create mode 100644 packages/listview/docs/stories/PercentColumn.story.js create mode 100644 packages/listview/src/components/PercentColumn/__test__/percentColumn.spec.js create mode 100644 packages/listview/src/components/PercentColumn/helpers/formatPercent.ts create mode 100644 packages/listview/src/components/PercentColumn/index.tsx create mode 100644 packages/listview/src/components/PercentColumn/styled.ts create mode 100644 packages/listview/src/components/PercentColumn/types.ts diff --git a/package.json b/package.json index 80b90ddf..e427cf2d 100644 --- a/package.json +++ b/package.json @@ -101,7 +101,7 @@ "react-dom": "^17.0.0", "react-intl": "^4.4.0", "react-query": "^3.34.0", - "react-rainbow-components": "1.31.0-canary.082c6c0", + "react-rainbow-components": "1.31.0-canary.6ccebb7", "react-redux": "^7.2.0", "react-router-dom": "^5.0.0", "redux": "^4.0.5", diff --git a/packages/listview/docs/components/PercentColumn.story.mdx b/packages/listview/docs/components/PercentColumn.story.mdx new file mode 100644 index 00000000..8f676288 --- /dev/null +++ b/packages/listview/docs/components/PercentColumn.story.mdx @@ -0,0 +1,19 @@ +import { Meta, Story, Preview, Props } from '@storybook/addon-docs/blocks'; +import PercentColumn from '../../src/components/PercentColumn'; +import * as stories from '../stories/PercentColumn.story.js'; + + + +# Overview + +`PercentColumn` is a column that renders the percent number passed as value. + +### This is an example with the PercentColumn. + + + {stories.percentColumn()} + + +# Component props + + diff --git a/packages/listview/docs/stories/PercentColumn.story.js b/packages/listview/docs/stories/PercentColumn.story.js new file mode 100644 index 00000000..c51a0d44 --- /dev/null +++ b/packages/listview/docs/stories/PercentColumn.story.js @@ -0,0 +1,125 @@ +import React from 'react'; +import styled from 'styled-components'; +import { Table, Column, Application } from 'react-rainbow-components'; +import PercentColumn from '../../src/components/PercentColumn'; +import ColoredStatusColumn from '../../src/components/ColoredStatusColumn'; + +const initialData = [ + { + name: 'Carls Smith', + status: 'canceled', + company: 'Google', + percent: 0.1, + createdAt: '09/06/2020 09:00 AM', + }, + { + name: 'John Snow', + status: 'delivered', + company: 'Google', + percent: 0.0025, + createdAt: '09/06/2020 09:00 AM', + }, + { + name: 'Anna Adams', + status: 'pending', + company: 'Google', + percent: 0.3045, + createdAt: '09/06/2020 09:00 AM', + }, + { + name: 'William Adams', + status: 'arrived', + company: 'Google', + percent: 0, + createdAt: '09/06/2020 09:00 AM', + }, + { + name: 'Joe Smith', + status: 'arrived', + company: 'Google', + percent: 0.8, + createdAt: '09/06/2020 09:00 AM', + }, + { + name: 'John Doe', + status: 'arrived', + company: 'Google', + percent: 1, + createdAt: '09/06/2020 09:00 AM', + }, + { + name: 'Jane Adams', + status: 'arrived', + company: 'Google', + percent: 1.2, + createdAt: '09/06/2020 09:00 AM', + }, +]; + +const Container = styled.div` + padding: 2rem; +`; + +const colors = { + canceled: { backgroundColor: '#f2707a', color: 'rgba(255, 255, 255)' }, + delivered: '#009900', + pending: { backgroundColor: '#EBC665', color: '#fff' }, + arrived: { backgroundColor: '#4dc9cb', color: '#fff' }, +}; + +export const percentColumn = () => { + return ( + + + + + + + + +
+
+
+ ); +}; + +export const percentWithIntlOptionColumn = () => { + return ( + + + + + + + + +
+
+
+ ); +}; + +export default { + title: 'Modules/Listview/Stories/PercentColumn', + parameters: { + viewOnGithub: { + fileName: __filename, + }, + }, +}; diff --git a/packages/listview/src/components/PercentColumn/__test__/percentColumn.spec.js b/packages/listview/src/components/PercentColumn/__test__/percentColumn.spec.js new file mode 100644 index 00000000..b6c4ebfa --- /dev/null +++ b/packages/listview/src/components/PercentColumn/__test__/percentColumn.spec.js @@ -0,0 +1,29 @@ +import React from 'react'; +import { mount } from 'enzyme'; +import PercentColumn from '../index'; +import { StyledCellContainer } from '../styled'; + +describe('', () => { + it('should render a PercentColumn with the value passed', () => { + const wrapper = mount(); + const output = wrapper.find(StyledCellContainer); + expect(output.exists()).toBe(true); + expect(output.text()).toBe('100%'); + }); + + it('should render a PercentColumn with the value passed and intl options', () => { + const wrapper = mount( + , + ); + const output = wrapper.find(StyledCellContainer); + expect(output.exists()).toBe(true); + expect(output.text()).toBe('50.25%'); + }); + + it('should render a center text whne cellAlignment is center', () => { + const wrapper = mount(); + const output = wrapper.find(StyledCellContainer); + expect(output.exists()).toBe(true); + expect(output.prop('cellAlignment')).toBe('center'); + }); +}); diff --git a/packages/listview/src/components/PercentColumn/helpers/formatPercent.ts b/packages/listview/src/components/PercentColumn/helpers/formatPercent.ts new file mode 100644 index 00000000..822fd92b --- /dev/null +++ b/packages/listview/src/components/PercentColumn/helpers/formatPercent.ts @@ -0,0 +1,16 @@ +interface Options { + minimumIntegerDigits?: number; + minimumFractionDigits?: number; + maximumFractionDigits?: number; + minimumSignificantDigits?: number; + maximumSignificantDigits?: number; +} + +const formatPercent = (value: number, locale: string, options: Options): string => { + return new Intl.NumberFormat(locale, { + style: 'percent', + ...options, + }).format(value); +}; + +export default formatPercent; diff --git a/packages/listview/src/components/PercentColumn/index.tsx b/packages/listview/src/components/PercentColumn/index.tsx new file mode 100644 index 00000000..09c841a9 --- /dev/null +++ b/packages/listview/src/components/PercentColumn/index.tsx @@ -0,0 +1,65 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { useLocale } from 'react-rainbow-components'; +import formatPercent from './helpers/formatPercent'; +import { StyledCellContainer } from './styled'; +import { PercentColumnProps } from './types'; + +const PercentColumn: React.FC = (props: PercentColumnProps) => { + const { value, locale: localeProp, className, style, cellAlignment, ...rest } = props; + const locale = useLocale(localeProp); + const content = formatPercent(value ?? 0, locale, rest); + + return ( + + {content} + + ); +}; + +PercentColumn.propTypes = { + /** A number that comes from the data and is displayed in the table cell */ + value: PropTypes.number, + /** The PercentColumn locale. Defaults to browser's language. */ + locale: PropTypes.string, + /** The minimum number of integer digits to use. + * A value with a smaller number of integer digits than this number will be left-padded with zeros (to the specified + * length) when formatted. Possible values are from 1 to 21; The default is 1. */ + minimumIntegerDigits: PropTypes.number, + /** The minimum number of fraction digits to use. Possible values are from 0 to 20; + * the default for percent formatting is 0; */ + minimumFractionDigits: PropTypes.number, + /** The maximum number of fraction digits to use. Possible values are from 0 to 20; + * the default for percent formatting is the larger of minimumFractionDigits and 0. */ + maximumFractionDigits: PropTypes.number, + /** The minimum number of significant digits to use. Possible values are from 1 to 21; The default is 1. */ + minimumSignificantDigits: PropTypes.number, + /** The maximum number of significant digits to use. Possible values are from 1 to 21; The default is 21. */ + maximumSignificantDigits: PropTypes.number, + /** A CSS class for the outer element, in addition to the component's base classes. */ + className: PropTypes.string, + /** An object with custom style applied to the outer element. */ + style: PropTypes.object, + /** Determines the alignment of the text in each column cell. */ + cellAlignment: PropTypes.oneOf(['left', 'right', 'center']), +}; + +PercentColumn.defaultProps = { + value: undefined, + locale: undefined, + minimumIntegerDigits: undefined, + minimumFractionDigits: undefined, + maximumFractionDigits: undefined, + minimumSignificantDigits: undefined, + maximumSignificantDigits: undefined, + className: undefined, + style: undefined, + cellAlignment: 'right', +}; + +export default PercentColumn; diff --git a/packages/listview/src/components/PercentColumn/styled.ts b/packages/listview/src/components/PercentColumn/styled.ts new file mode 100644 index 00000000..3cb574c6 --- /dev/null +++ b/packages/listview/src/components/PercentColumn/styled.ts @@ -0,0 +1,9 @@ +/* eslint-disable import/prefer-default-export */ +import styled from 'styled-components'; + +export const StyledCellContainer = styled.div<{ cellAlignment?: 'left' | 'right' | 'center' }>` + padding: 0 5px; + overflow: hidden; + text-overflow: ellipsis; + text-align: ${(props) => props.cellAlignment}; +`; diff --git a/packages/listview/src/components/PercentColumn/types.ts b/packages/listview/src/components/PercentColumn/types.ts new file mode 100644 index 00000000..48e7f780 --- /dev/null +++ b/packages/listview/src/components/PercentColumn/types.ts @@ -0,0 +1,14 @@ +import { CSSProperties } from 'react'; + +export interface PercentColumnProps { + value?: number; + locale?: string; + minimumIntegerDigits?: number; + minimumFractionDigits?: number; + maximumFractionDigits?: number; + minimumSignificantDigits?: number; + maximumSignificantDigits?: number; + className?: string; + style?: CSSProperties; + cellAlignment?: 'left' | 'right' | 'center'; +} diff --git a/packages/listview/src/index.d.ts b/packages/listview/src/index.d.ts index 2465e74a..7bcf5ef7 100644 --- a/packages/listview/src/index.d.ts +++ b/packages/listview/src/index.d.ts @@ -11,3 +11,4 @@ export { default as filterByFields } from './helpers/filterByFields'; export { default as MarkdownColumn } from './components/MarkdownColumn'; export { default as VirtualizedTable, useTableDataSource } from './components/VirtualizedTable'; export { default as ColumnHeaderFilterText } from './components/ColumnHeaderFilterText'; +export { default as PercentColumn } from './components/PercentColumn'; diff --git a/packages/listview/src/index.js b/packages/listview/src/index.js index 2a05f5ef..8cd61d75 100644 --- a/packages/listview/src/index.js +++ b/packages/listview/src/index.js @@ -13,3 +13,4 @@ export { default as VirtualizedTable, useTableDataSource } from './components/Vi export { default as ColumnHeaderFilterText } from './components/ColumnHeaderFilterText'; export { default as ColumnHeaderFilterMultiselect } from './components/ColumnHeaderFilterMultiselect'; export { default as DownloadModal } from './components/DownloadModal'; +export { default as PercentColumn } from './components/PercentColumn'; diff --git a/yarn.lock b/yarn.lock index 900b0ddf..0b57cb6a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -18613,10 +18613,10 @@ react-query@^3.34.0: broadcast-channel "^3.4.1" match-sorter "^6.0.2" -react-rainbow-components@1.31.0-canary.082c6c0: - version "1.31.0-canary.082c6c0" - resolved "https://registry.yarnpkg.com/react-rainbow-components/-/react-rainbow-components-1.31.0-canary.082c6c0.tgz#cea930987fc567d9aa289264d63b722e34f861b9" - integrity sha512-2YGhvhjtalJZrBfVcYf+NkjJ9PT750Wo6eaZlNFjJ7d2/sQD2dD3cFvJlghHRAgSauI8wqjVZtDumodBGLHdYQ== +react-rainbow-components@1.31.0-canary.6ccebb7: + version "1.31.0-canary.6ccebb7" + resolved "https://registry.yarnpkg.com/react-rainbow-components/-/react-rainbow-components-1.31.0-canary.6ccebb7.tgz#ad85a9264f5d86864bb71e730496e8181d6dafca" + integrity sha512-L82R9j0G35p3oa1Ji66UZ55xfcHAaFTLOaghja7gIaQz6jeximwcY9AebI/s0CM+OuhdrZV2mc9mpBV4lWFGzw== dependencies: "@rainbow-modules/hooks" "^0.12.0" "@rainbow-modules/validation" "^0.53.0"