Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add currency column component using intl formatter #623

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions packages/listview/docs/components/CurrencyColumn.story.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Meta, Story, Preview, Props } from '@storybook/addon-docs/blocks';
import CurrencyColumn from '../../src/components/CurrencyColumn';
import * as stories from '../stories/CurrencyColumn.story.js';

<Meta title="Modules/Listview/Components/CurrencyColumn" component={CurrencyColumn} />

# Overview

`CurrencyColumn` is a column that renders the number passed as value.

### This is an example with the CurrencyColumn.

<Preview>
<Story name="CurrencyColumn">{stories.currencyColumn()}</Story>
</Preview>

# Component props

<Props of={CurrencyColumn} />
125 changes: 125 additions & 0 deletions packages/listview/docs/stories/CurrencyColumn.story.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import React from 'react';
import styled from 'styled-components';
import { Table, Column, Application } from 'react-rainbow-components';
import CurrencyColumn from '../../src/components/CurrencyColumn';
import ColoredStatusColumn from '../../src/components/ColoredStatusColumn';

const initialData = [
{
name: 'Carls Smith',
status: 'canceled',
company: 'Google',
amout: 0.1,
createdAt: '09/06/2020 09:00 AM',
},
{
name: 'John Snow',
status: 'delivered',
company: 'Google',
amout: 0.25,
createdAt: '09/06/2020 09:00 AM',
},
{
name: 'Anna Adams',
status: 'pending',
company: 'Google',
amout: 3045,
createdAt: '09/06/2020 09:00 AM',
},
{
name: 'William Adams',
status: 'arrived',
company: 'Google',
amout: 0,
createdAt: '09/06/2020 09:00 AM',
},
{
name: 'Joe Smith',
status: 'arrived',
company: 'Google',
amout: 100.85,
createdAt: '09/06/2020 09:00 AM',
},
{
name: 'John Doe',
status: 'arrived',
company: 'Google',
amout: 1,
createdAt: '09/06/2020 09:00 AM',
},
{
name: 'Jane Adams',
status: 'arrived',
company: 'Google',
amout: 120000.65,
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 currencyColumn = () => {
return (
<Application>
<Container>
<Table data={initialData} keyField="id" variant="listview" showCheckboxColumn>
<Column header="Created At" field="createdAt" />
<Column header="Name" field="name" />
<Column header="Company" field="company" />
<Column header="Amout" field="amout" component={CurrencyColumn} />
<Column
header="Status"
field="status"
colors={colors}
component={ColoredStatusColumn}
/>
</Table>
</Container>
</Application>
);
};

export const currencyWithIntlOptionColumn = () => {
return (
<Application>
<Container>
<Table data={initialData} keyField="id" variant="listview" showCheckboxColumn>
<Column header="Created At" field="createdAt" />
<Column header="Name" field="name" />
<Column header="Company" field="company" />
<Column
header="Amout"
field="amout"
component={CurrencyColumn}
currency="EUR"
currencyDisplay="name"
/>
<Column
header="Status"
field="status"
colors={colors}
component={ColoredStatusColumn}
/>
</Table>
</Container>
</Application>
);
};

export default {
title: 'Modules/Listview/Stories/CurrencyColumn',
parameters: {
viewOnGithub: {
fileName: __filename,
},
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React from 'react';
import { mount } from 'enzyme';
import CurrencyColumn from '../index';
import { StyledCellContainer } from '../styled';

describe('<CurrencyColumn />', () => {
it('should render a CurrencyColumn with the value passed', () => {
const wrapper = mount(<CurrencyColumn value={1} />);
const output = wrapper.find(StyledCellContainer);
expect(output.exists()).toBe(true);
expect(output.text()).toBe('$1.00');
});

it('should render a CurrencyColumn with the value passed and intl options', () => {
const wrapper = mount(
<CurrencyColumn value={5025} currency="EUR" currencyDisplay="name" />,
);
const output = wrapper.find(StyledCellContainer);
expect(output.exists()).toBe(true);
expect(output.text()).toBe('5,025.00 euros');
});
it('should render a center text whne cellAlignment is center', () => {
const wrapper = mount(<CurrencyColumn value={1} cellAlignment="center" />);
const output = wrapper.find(StyledCellContainer);
expect(output.exists()).toBe(true);
expect(output.prop('cellAlignment')).toBe('center');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
interface Options {
currency?: string;
currencyDisplay?: 'symbol' | 'narrowSymbol' | 'code' | 'name';
currencySign?: 'standard' | 'accounting';
minimumIntegerDigits?: number;
minimumFractionDigits?: number;
maximumFractionDigits?: number;
minimumSignificantDigits?: number;
maximumSignificantDigits?: number;
}

const formatCurrency = (value: number, locale: string, options: Options): string => {
return new Intl.NumberFormat(locale, {
style: 'currency',
...options,
}).format(value);
};

export default formatCurrency;
78 changes: 78 additions & 0 deletions packages/listview/src/components/CurrencyColumn/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import React from 'react';
import PropTypes from 'prop-types';
import { useLocale } from 'react-rainbow-components';
import formatCurrency from './helpers/formatCurrency';
import { StyledCellContainer } from './styled';
import { CurrencyColumnProps } from './types';

const CurrencyColumn: React.FC<CurrencyColumnProps> = (props: CurrencyColumnProps) => {
const { value, locale: localeProp, className, style, cellAlignment, ...rest } = props;
const locale = useLocale(localeProp);
const content = formatCurrency(value ?? 0, locale, rest);

return (
<StyledCellContainer
className={className}
style={style}
title={content}
cellAlignment={cellAlignment}
>
{content}
</StyledCellContainer>
);
};

CurrencyColumn.propTypes = {
/** A number that comes from the data and is displayed in the table cell */
value: PropTypes.number,
/** The CurrencyColumn locale. Defaults to browser's language. */
locale: PropTypes.string,
/** The currency to use in currency formatting. Possible values are the ISO 4217 currency codes. The default is "USD" */
currency: PropTypes.string,
/** How to display the currency in currency formatting. The default is "symbol". */
currencyDisplay: PropTypes.oneOf(['symbol', 'narrowSymbol', 'code', 'name']),
/** In many locales, accounting format means to wrap the number with parentheses instead of appending a minus sign.
* You can enable this formatting by setting the currencySign option to "accounting". The default value is "standard". */
currencySign: PropTypes.oneOf(['standard', 'accounting']),
/** 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 currency formatting is the number of minor unit digits provided by the ISO 4217 currency code list
* (2 if the list doesn't provide that information). */
minimumFractionDigits: PropTypes.number,
/** The maximum number of fraction digits to use. Possible values are from 0 to 20;
* the default for currency formatting is the larger of minimumFractionDigits and
* the number of minor unit digits provided by the ISO 4217 currency code list
* (2 if the list doesn't provide that information); */
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']),
};

CurrencyColumn.defaultProps = {
value: undefined,
locale: undefined,
currency: 'USD',
currencyDisplay: undefined,
currencySign: undefined,
minimumIntegerDigits: undefined,
minimumFractionDigits: undefined,
maximumFractionDigits: undefined,
minimumSignificantDigits: undefined,
maximumSignificantDigits: undefined,
className: undefined,
style: undefined,
cellAlignment: 'right',
};

export default CurrencyColumn;
9 changes: 9 additions & 0 deletions packages/listview/src/components/CurrencyColumn/styled.ts
Original file line number Diff line number Diff line change
@@ -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};
`;
17 changes: 17 additions & 0 deletions packages/listview/src/components/CurrencyColumn/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { CSSProperties } from 'react';

export interface CurrencyColumnProps {
value?: number;
locale?: string;
currency?: string;
currencyDisplay?: 'symbol' | 'narrowSymbol' | 'code' | 'name';
currencySign?: 'standard' | 'accounting';
minimumIntegerDigits?: number;
minimumFractionDigits?: number;
maximumFractionDigits?: number;
minimumSignificantDigits?: number;
maximumSignificantDigits?: number;
className?: string;
style?: CSSProperties;
cellAlignment?: 'left' | 'right' | 'center';
}