@transferwise/components 0.0.0-experimental-d1715ff → 0.0.0-experimental-3064bdb

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/build/i18n/en.json +2 -0
  2. package/build/i18n/en.json.js +2 -0
  3. package/build/i18n/en.json.js.map +1 -1
  4. package/build/i18n/en.json.mjs +2 -0
  5. package/build/i18n/en.json.mjs.map +1 -1
  6. package/build/main.css +214 -0
  7. package/build/styles/main.css +214 -0
  8. package/build/styles/table/Table.css +214 -0
  9. package/build/types/table/Table.d.ts +23 -0
  10. package/build/types/table/Table.d.ts.map +1 -0
  11. package/build/types/table/Table.messages.d.ts +12 -0
  12. package/build/types/table/Table.messages.d.ts.map +1 -0
  13. package/build/types/table/TableCell.d.ts +37 -0
  14. package/build/types/table/TableCell.d.ts.map +1 -0
  15. package/build/types/table/TableHeader.d.ts +12 -0
  16. package/build/types/table/TableHeader.d.ts.map +1 -0
  17. package/build/types/table/TableRow.d.ts +17 -0
  18. package/build/types/table/TableRow.d.ts.map +1 -0
  19. package/build/types/table/TableStatusText.d.ts +9 -0
  20. package/build/types/table/TableStatusText.d.ts.map +1 -0
  21. package/build/types/table/index.d.ts +6 -0
  22. package/build/types/table/index.d.ts.map +1 -0
  23. package/package.json +5 -5
  24. package/src/i18n/en.json +2 -0
  25. package/src/main.css +214 -0
  26. package/src/main.less +1 -0
  27. package/src/table/Table.css +214 -0
  28. package/src/table/Table.less +253 -0
  29. package/src/table/Table.messages.ts +12 -0
  30. package/src/table/Table.spec.tsx +87 -0
  31. package/src/table/Table.story.tsx +352 -0
  32. package/src/table/Table.tsx +121 -0
  33. package/src/table/TableCell.spec.tsx +298 -0
  34. package/src/table/TableCell.tsx +153 -0
  35. package/src/table/TableHeader.spec.tsx +58 -0
  36. package/src/table/TableHeader.tsx +50 -0
  37. package/src/table/TableRow.spec.tsx +104 -0
  38. package/src/table/TableRow.tsx +62 -0
  39. package/src/table/TableStatusText.spec.tsx +53 -0
  40. package/src/table/TableStatusText.tsx +35 -0
  41. package/src/table/index.ts +11 -0
  42. package/src/test-utils/assets/avatar-rectangle-fox.webp +0 -0
  43. package/src/test-utils/assets/avatar-square-dude.webp +0 -0
  44. package/src/test-utils/assets/tapestry-01.png +0 -0
@@ -0,0 +1,104 @@
1
+ import { render, screen, within } from '@testing-library/react';
2
+ import TableRow, { TableRowType, TableRowClickableType } from './TableRow';
3
+ import { IntlProvider } from 'react-intl';
4
+ import { userEvent } from '@testing-library/user-event';
5
+
6
+ describe('TableRow Component', () => {
7
+ const mockData = {
8
+ rowContent: [{ content: { text: 'Cell content 1' } }, { content: { text: 'Cell content 2' } }],
9
+ } satisfies TableRowType;
10
+
11
+ const mockDataClickable = {
12
+ id: 1,
13
+ rowContent: mockData.rowContent,
14
+ } satisfies TableRowClickableType;
15
+
16
+ const handleClick = jest.fn();
17
+
18
+ const renderWithIntl = (component: React.ReactElement) => {
19
+ return render(<IntlProvider locale="en">{component}</IntlProvider>);
20
+ };
21
+
22
+ it('renders without crashing', () => {
23
+ renderWithIntl(<TableRow />);
24
+ expect(screen.getByTestId('np-table-row')).toBeInTheDocument();
25
+ });
26
+
27
+ it('renders children when data is not provided', () => {
28
+ renderWithIntl(
29
+ <TableRow>
30
+ <td>Cell text</td>
31
+ </TableRow>,
32
+ );
33
+ expect(screen.getByText('Cell text')).toBeInTheDocument();
34
+ });
35
+
36
+ it('renders TableCell components when data is provided', () => {
37
+ renderWithIntl(<TableRow rowData={mockData} />);
38
+ expect(screen.getByText('Cell content 1')).toBeInTheDocument();
39
+ expect(screen.getByText('Cell content 2')).toBeInTheDocument();
40
+ });
41
+
42
+ it('renders correct number of TableCell components', () => {
43
+ renderWithIntl(<TableRow rowData={mockData} />);
44
+ expect(screen.getAllByRole('cell')).toHaveLength(mockData.rowContent.length);
45
+ });
46
+
47
+ it('renders correct number of TableCell components with chevron when clickable', () => {
48
+ renderWithIntl(<TableRow rowData={mockDataClickable} onRowClick={handleClick} />);
49
+ expect(screen.getAllByRole('cell')).toHaveLength(mockDataClickable.rowContent.length + 1);
50
+ });
51
+
52
+ it('renders a separator row when hasSeparator is passed', () => {
53
+ renderWithIntl(<TableRow rowData={mockData} hasSeparator />);
54
+ expect(screen.getAllByTestId('np-table-row')).toHaveLength(1);
55
+ expect(screen.getAllByTestId('np-table-row--separator')).toHaveLength(1);
56
+ });
57
+
58
+ it('renders correct colSpan for separator row', () => {
59
+ renderWithIntl(<TableRow rowData={mockData} hasSeparator />);
60
+ const separatorCell = within(screen.getByTestId('np-table-row--separator')).getByRole('cell');
61
+ expect(separatorCell).toHaveAttribute('colSpan', mockData.rowContent.length.toString());
62
+ });
63
+
64
+ it('renders correct colSpan for separator row with clickable row', () => {
65
+ renderWithIntl(<TableRow rowData={mockDataClickable} hasSeparator onRowClick={handleClick} />);
66
+ const separatorCell = within(screen.getByTestId('np-table-row--separator')).getByRole('cell');
67
+ expect(separatorCell).toHaveAttribute(
68
+ 'colSpan',
69
+ (mockDataClickable.rowContent.length + 1).toString(),
70
+ );
71
+ });
72
+
73
+ it('does not render separator row when hasSeparator is false', () => {
74
+ renderWithIntl(<TableRow rowData={mockData} />);
75
+ expect(screen.queryByTestId('np-table-row--separator')).not.toBeInTheDocument();
76
+ });
77
+
78
+ it('does not call onRowClick when row is not clickable', async () => {
79
+ renderWithIntl(<TableRow rowData={mockData} />);
80
+ await userEvent.click(screen.getByTestId('np-table-row'));
81
+ expect(handleClick).not.toHaveBeenCalled();
82
+ });
83
+
84
+ it('applies the np-table-row--clickable class when row is clickable', () => {
85
+ renderWithIntl(<TableRow rowData={mockDataClickable} onRowClick={handleClick} />);
86
+ expect(screen.getAllByTestId('np-table-row')[0]).toHaveClass('np-table-row--clickable');
87
+ });
88
+
89
+ it('calls onRowClick when row is clicked', async () => {
90
+ renderWithIntl(<TableRow rowData={mockDataClickable} onRowClick={handleClick} />);
91
+ await userEvent.click(screen.getByTestId('np-table-row'));
92
+ expect(handleClick).toHaveBeenCalledWith(mockDataClickable);
93
+ });
94
+
95
+ it('renders a chevron icon when row is clickable', () => {
96
+ renderWithIntl(<TableRow rowData={mockDataClickable} onRowClick={handleClick} />);
97
+ expect(screen.getByTestId('chevron-up-icon')).toBeInTheDocument();
98
+ });
99
+
100
+ it('does not render a chevron icon when row is not clickable', () => {
101
+ renderWithIntl(<TableRow rowData={mockData} />);
102
+ expect(screen.queryByTestId('chevron-up-icon')).not.toBeInTheDocument();
103
+ });
104
+ });
@@ -0,0 +1,62 @@
1
+ import React from 'react';
2
+ import TableCell, { TableCellProps } from './TableCell';
3
+ import Chevron from '../chevron';
4
+ import { Position } from '../common';
5
+ import clsx from 'clsx';
6
+
7
+ export interface TableRowType {
8
+ rowContent?: TableCellProps[];
9
+ }
10
+
11
+ export interface TableRowClickableType extends TableRowType {
12
+ id: number | string; // `id` is mandatory for clickable rows
13
+ }
14
+
15
+ export interface TableRowProps {
16
+ rowData?: TableRowType | TableRowClickableType;
17
+ hasSeparator?: boolean;
18
+ children?: React.ReactNode;
19
+ onRowClick?: (rowData: TableRowType | TableRowClickableType) => void;
20
+ }
21
+
22
+ const TableRow = ({ rowData, hasSeparator = false, children, onRowClick }: TableRowProps) => {
23
+ return (
24
+ <>
25
+ <tr
26
+ className={clsx('np-table-row', { 'np-table-row--clickable': !!onRowClick })}
27
+ data-testid="np-table-row"
28
+ onClick={onRowClick && rowData ? () => onRowClick(rowData) : undefined}
29
+ >
30
+ {rowData?.rowContent
31
+ ? rowData?.rowContent?.map((cellItem, index) => {
32
+ return (
33
+ <TableCell
34
+ key={(
35
+ cellItem.content?.text ?? cellItem.content?.primaryText?.toString()
36
+ )?.concat(index.toString())}
37
+ {...cellItem}
38
+ />
39
+ );
40
+ })
41
+ : children}
42
+ {onRowClick && (
43
+ <TableCell>
44
+ <Chevron orientation={Position.RIGHT} />
45
+ </TableCell>
46
+ )}
47
+ </tr>
48
+ {hasSeparator && (
49
+ <tr className="np-table-row np-table-row--separator" data-testid="np-table-row--separator">
50
+ <TableCell
51
+ hasSeparator
52
+ colSpan={
53
+ onRowClick ? Number(rowData?.rowContent?.length) + 1 : rowData?.rowContent?.length
54
+ }
55
+ />
56
+ </tr>
57
+ )}
58
+ </>
59
+ );
60
+ };
61
+
62
+ export default TableRow;
@@ -0,0 +1,53 @@
1
+ import { render, screen } from '@testing-library/react';
2
+ import TableStatusText, { TableStatusTextProps } from './TableStatusText';
3
+
4
+ describe('TableStatusText Component', () => {
5
+ const renderComponent = (props: Partial<TableStatusTextProps> = {}) => {
6
+ const defaultProps: TableStatusTextProps = {
7
+ text: 'Status Text',
8
+ ...props,
9
+ };
10
+ return render(<TableStatusText {...defaultProps} />);
11
+ };
12
+
13
+ it('renders the text and applies the default class', () => {
14
+ renderComponent();
15
+ expect(screen.getByText('Status Text')).toBeInTheDocument();
16
+ expect(screen.getByText('Status Text')).toHaveClass('np-text-body-default');
17
+ });
18
+
19
+ it('applies the custom class name', () => {
20
+ renderComponent({ className: 'custom-class' });
21
+ expect(screen.getByText('Status Text')).toHaveClass('custom-class');
22
+ });
23
+
24
+ it('applies the typography class when typography is `large-bold`', () => {
25
+ renderComponent({ typography: 'large-bold' });
26
+ expect(screen.getByText('Status Text')).toHaveClass('np-text-body-large-bold');
27
+ });
28
+
29
+ it('applies the default typography class when typography is not provided', () => {
30
+ renderComponent();
31
+ expect(screen.getByText('Status Text')).toHaveClass('np-text-body-default');
32
+ });
33
+
34
+ it('applies the typography and status classes, renders the error icon when status is error', () => {
35
+ renderComponent({ status: 'error' });
36
+ expect(screen.getByText('Status Text')).toHaveClass('np-text-body-large-bold');
37
+ expect(screen.getByText('Status Text')).toHaveClass('np-table-content--error');
38
+ expect(screen.getByTestId('alert-icon')).toBeInTheDocument();
39
+ });
40
+
41
+ it('does not render any icon when status is not provided', () => {
42
+ renderComponent();
43
+ expect(screen.queryByTestId('check-icon')).not.toBeInTheDocument();
44
+ expect(screen.queryByTestId('alert-icon')).not.toBeInTheDocument();
45
+ });
46
+
47
+ it('applies the typography and status classes, renders the success icon when status is success', () => {
48
+ renderComponent({ status: 'success' });
49
+ expect(screen.getByText('Status Text')).toHaveClass('np-text-body-large-bold');
50
+ expect(screen.getByText('Status Text')).toHaveClass('np-table-content--success');
51
+ expect(screen.getByTestId('check-icon')).toBeInTheDocument();
52
+ });
53
+ });
@@ -0,0 +1,35 @@
1
+ import { AlertCircle, CheckCircle } from '@transferwise/icons';
2
+ import clsx from 'clsx';
3
+
4
+ export interface TableStatusTextProps {
5
+ text: string;
6
+ className?: string;
7
+ status?: 'success' | 'error';
8
+ typography?: 'default' | 'large-bold';
9
+ }
10
+
11
+ const TableStatusText: React.FC<TableStatusTextProps> = ({
12
+ text,
13
+ status,
14
+ className,
15
+ typography = 'default',
16
+ }: TableStatusTextProps) => {
17
+ return (
18
+ <div
19
+ className={clsx(
20
+ className,
21
+ `np-text-body-${(status ?? typography === 'large-bold') ? 'large-bold' : 'default'}`,
22
+ {
23
+ 'np-table-content--success': status === 'success',
24
+ 'np-table-content--error': status === 'error',
25
+ },
26
+ )}
27
+ >
28
+ {text}
29
+ {status === 'success' && <CheckCircle className="tw-icon--status" data-testid="check-icon" />}
30
+ {status === 'error' && <AlertCircle className="tw-icon--status" data-testid="alert-icon" />}
31
+ </div>
32
+ );
33
+ };
34
+
35
+ export default TableStatusText;
@@ -0,0 +1,11 @@
1
+ export { default } from './Table';
2
+ export type { TableProps } from './Table';
3
+ export type { TableRowType, TableRowClickableType } from './TableRow';
4
+ export type { TableHeaderType } from './TableHeader';
5
+ export type {
6
+ LeadingContentType,
7
+ TextContentType,
8
+ CurrencyContentType,
9
+ StatusContentType,
10
+ TableCellProps,
11
+ } from './TableCell';