@transferwise/components 46.80.0 → 46.82.0

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 (137) hide show
  1. package/build/avatar/Avatar.js +3 -0
  2. package/build/avatar/Avatar.js.map +1 -1
  3. package/build/avatar/Avatar.mjs +3 -0
  4. package/build/avatar/Avatar.mjs.map +1 -1
  5. package/build/avatarView/AvatarView.js +175 -0
  6. package/build/avatarView/AvatarView.js.map +1 -0
  7. package/build/avatarView/AvatarView.mjs +173 -0
  8. package/build/avatarView/AvatarView.mjs.map +1 -0
  9. package/build/avatarView/NotificationDot.js +59 -0
  10. package/build/avatarView/NotificationDot.js.map +1 -0
  11. package/build/avatarView/NotificationDot.mjs +57 -0
  12. package/build/avatarView/NotificationDot.mjs.map +1 -0
  13. package/build/avatarWrapper/AvatarWrapper.js +10 -4
  14. package/build/avatarWrapper/AvatarWrapper.js.map +1 -1
  15. package/build/avatarWrapper/AvatarWrapper.mjs +10 -4
  16. package/build/avatarWrapper/AvatarWrapper.mjs.map +1 -1
  17. package/build/badge/Badge.js +16 -4
  18. package/build/badge/Badge.js.map +1 -1
  19. package/build/badge/Badge.mjs +15 -3
  20. package/build/badge/Badge.mjs.map +1 -1
  21. package/build/badge/BadgeAssets.js +60 -0
  22. package/build/badge/BadgeAssets.js.map +1 -0
  23. package/build/badge/BadgeAssets.mjs +58 -0
  24. package/build/badge/BadgeAssets.mjs.map +1 -0
  25. package/build/common/circle/Circle.js +19 -1
  26. package/build/common/circle/Circle.js.map +1 -1
  27. package/build/common/circle/Circle.mjs +19 -1
  28. package/build/common/circle/Circle.mjs.map +1 -1
  29. package/build/i18n/en.json +5 -0
  30. package/build/i18n/en.json.js +5 -0
  31. package/build/i18n/en.json.js.map +1 -1
  32. package/build/i18n/en.json.mjs +5 -0
  33. package/build/i18n/en.json.mjs.map +1 -1
  34. package/build/i18n/zh-HK.json +5 -0
  35. package/build/i18n/zh-HK.json.js +5 -0
  36. package/build/i18n/zh-HK.json.js.map +1 -1
  37. package/build/i18n/zh-HK.json.mjs +5 -0
  38. package/build/i18n/zh-HK.json.mjs.map +1 -1
  39. package/build/index.js +18 -13
  40. package/build/index.js.map +1 -1
  41. package/build/index.mjs +10 -7
  42. package/build/index.mjs.map +1 -1
  43. package/build/main.css +348 -5
  44. package/build/money/Money.js +5 -2
  45. package/build/money/Money.js.map +1 -1
  46. package/build/money/Money.mjs +5 -2
  47. package/build/money/Money.mjs.map +1 -1
  48. package/build/styles/avatarView/AvatarView.css +36 -0
  49. package/build/styles/avatarView/NotificationDot.css +20 -0
  50. package/build/styles/badge/Badge.css +6 -5
  51. package/build/styles/common/circle/Circle.css +32 -0
  52. package/build/styles/main.css +348 -5
  53. package/build/styles/table/Table.css +274 -0
  54. package/build/types/avatar/Avatar.d.ts +3 -0
  55. package/build/types/avatar/Avatar.d.ts.map +1 -1
  56. package/build/types/avatarView/AvatarView.d.ts +26 -0
  57. package/build/types/avatarView/AvatarView.d.ts.map +1 -0
  58. package/build/types/avatarView/NotificationDot.d.ts +8 -0
  59. package/build/types/avatarView/NotificationDot.d.ts.map +1 -0
  60. package/build/types/avatarView/index.d.ts +3 -0
  61. package/build/types/avatarView/index.d.ts.map +1 -0
  62. package/build/types/avatarWrapper/AvatarWrapper.d.ts +3 -0
  63. package/build/types/avatarWrapper/AvatarWrapper.d.ts.map +1 -1
  64. package/build/types/badge/Badge.d.ts +9 -4
  65. package/build/types/badge/Badge.d.ts.map +1 -1
  66. package/build/types/badge/BadgeAssets.d.ts +14 -0
  67. package/build/types/badge/BadgeAssets.d.ts.map +1 -0
  68. package/build/types/badge/index.d.ts +2 -0
  69. package/build/types/badge/index.d.ts.map +1 -1
  70. package/build/types/common/circle/Circle.d.ts +2 -0
  71. package/build/types/common/circle/Circle.d.ts.map +1 -1
  72. package/build/types/index.d.ts +3 -1
  73. package/build/types/index.d.ts.map +1 -1
  74. package/build/types/money/Money.d.ts +2 -1
  75. package/build/types/money/Money.d.ts.map +1 -1
  76. package/build/types/table/Table.d.ts +23 -0
  77. package/build/types/table/Table.d.ts.map +1 -0
  78. package/build/types/table/Table.messages.d.ts +24 -0
  79. package/build/types/table/Table.messages.d.ts.map +1 -0
  80. package/build/types/table/TableCell.d.ts +40 -0
  81. package/build/types/table/TableCell.d.ts.map +1 -0
  82. package/build/types/table/TableHeader.d.ts +13 -0
  83. package/build/types/table/TableHeader.d.ts.map +1 -0
  84. package/build/types/table/TableRow.d.ts +17 -0
  85. package/build/types/table/TableRow.d.ts.map +1 -0
  86. package/build/types/table/TableStatusText.d.ts +10 -0
  87. package/build/types/table/TableStatusText.d.ts.map +1 -0
  88. package/build/types/table/index.d.ts +6 -0
  89. package/build/types/table/index.d.ts.map +1 -0
  90. package/build/types/test-utils/index.d.ts +10 -0
  91. package/build/types/test-utils/index.d.ts.map +1 -1
  92. package/package.json +3 -3
  93. package/src/avatar/Avatar.tsx +3 -0
  94. package/src/avatarView/AvatarView.css +36 -0
  95. package/src/avatarView/AvatarView.less +27 -0
  96. package/src/avatarView/AvatarView.story.tsx +467 -0
  97. package/src/avatarView/AvatarView.tsx +171 -0
  98. package/src/avatarView/NotificationDot.css +20 -0
  99. package/src/avatarView/NotificationDot.less +24 -0
  100. package/src/avatarView/NotificationDot.tsx +35 -0
  101. package/src/avatarView/index.ts +2 -0
  102. package/src/avatarWrapper/AvatarWrapper.story.tsx +19 -0
  103. package/src/avatarWrapper/AvatarWrapper.tsx +3 -0
  104. package/src/badge/Badge.css +6 -5
  105. package/src/badge/Badge.less +4 -3
  106. package/src/badge/Badge.tsx +20 -6
  107. package/src/badge/BadgeAssets.tsx +61 -0
  108. package/src/badge/index.ts +3 -0
  109. package/src/circularButton/CircularButton.spec.tsx +0 -36
  110. package/src/common/circle/Circle.css +32 -0
  111. package/src/common/circle/Circle.less +35 -0
  112. package/src/common/circle/Circle.tsx +24 -1
  113. package/src/flowNavigation/FlowNavigation.story.tsx +19 -52
  114. package/src/i18n/en.json +5 -0
  115. package/src/i18n/zh-HK.json +5 -0
  116. package/src/index.ts +3 -0
  117. package/src/listItem/ListItem.story.tsx +5 -47
  118. package/src/main.css +348 -5
  119. package/src/main.less +2 -0
  120. package/src/money/Money.tsx +9 -2
  121. package/src/overlayHeader/OverlayHeader.story.tsx +6 -14
  122. package/src/table/Table.css +274 -0
  123. package/src/table/Table.less +334 -0
  124. package/src/table/Table.messages.ts +24 -0
  125. package/src/table/Table.spec.tsx +82 -0
  126. package/src/table/Table.story.tsx +356 -0
  127. package/src/table/Table.tsx +167 -0
  128. package/src/table/TableCell.spec.tsx +298 -0
  129. package/src/table/TableCell.tsx +149 -0
  130. package/src/table/TableHeader.spec.tsx +50 -0
  131. package/src/table/TableHeader.tsx +74 -0
  132. package/src/table/TableRow.spec.tsx +112 -0
  133. package/src/table/TableRow.tsx +70 -0
  134. package/src/table/TableStatusText.spec.tsx +53 -0
  135. package/src/table/TableStatusText.tsx +40 -0
  136. package/src/table/index.ts +11 -0
  137. package/src/circularButton/__snapshots__/CircularButton.spec.tsx.snap +0 -381
@@ -0,0 +1,82 @@
1
+ import React from 'react';
2
+ import { render, screen, mockMatchMedia } from '../test-utils';
3
+ import { userEvent } from '@testing-library/user-event';
4
+ import '@testing-library/jest-dom';
5
+ import Table, { TableProps } from './Table';
6
+
7
+ mockMatchMedia();
8
+
9
+ describe('Table Component', () => {
10
+ const defaultProps: TableProps = {
11
+ data: {
12
+ headers: [{ header: 'Header 1' }, { header: 'Header 2' }],
13
+ rows: [
14
+ {
15
+ cells: [
16
+ { cell: { type: 'text', text: 'Cell content 1' } },
17
+ { cell: { type: 'text', text: 'Cell content 2' } },
18
+ ],
19
+ },
20
+ {
21
+ cells: [
22
+ { cell: { type: 'text', text: 'Cell content 3' } },
23
+ { cell: { type: 'text', text: 'Cell content 4' } },
24
+ ],
25
+ },
26
+ ],
27
+ onRowClick: jest.fn(),
28
+ },
29
+ loading: false,
30
+ className: '',
31
+ fullWidth: true,
32
+ error: undefined,
33
+ };
34
+
35
+ it('renders table', () => {
36
+ render(<Table {...defaultProps} />);
37
+ expect(screen.getByText('Header 1')).toBeInTheDocument();
38
+ expect(screen.getByText('Header 2')).toBeInTheDocument();
39
+ expect(screen.getByText('Cell content 1')).toBeInTheDocument();
40
+ expect(screen.getByText('Cell content 2')).toBeInTheDocument();
41
+ expect(screen.getByText('Cell content 3')).toBeInTheDocument();
42
+ expect(screen.getByText('Cell content 4')).toBeInTheDocument();
43
+ });
44
+
45
+ it('renders `loading` state on loading', () => {
46
+ render(<Table {...defaultProps} loading />);
47
+ expect(screen.getAllByTestId('np-table-loader')).toHaveLength(1);
48
+ });
49
+
50
+ it('renders `empty data` message for empty table`s data', () => {
51
+ render(<Table {...defaultProps} data={{ headers: [], rows: [] }} />);
52
+ expect(screen.getAllByTestId('np-table-empty-data')).toHaveLength(1);
53
+ });
54
+
55
+ it('renders `error` message if `error` is passed', () => {
56
+ const errorProps: TableProps = {
57
+ ...defaultProps,
58
+ error: { message: 'Error occurred', action: { text: 'Retry' } },
59
+ };
60
+ render(<Table {...errorProps} />);
61
+ expect(screen.getByText('Error occurred')).toBeInTheDocument();
62
+ expect(screen.getByText('Retry')).toBeInTheDocument();
63
+ });
64
+
65
+ it('renders with custom class name', () => {
66
+ render(<Table {...defaultProps} className="custom-class" />);
67
+ expect(screen.getByTestId('np-table-outer-container')).toHaveClass('custom-class');
68
+ });
69
+
70
+ it('renders with `fullWidth` set to false', () => {
71
+ render(<Table {...defaultProps} fullWidth={false} />);
72
+ expect(screen.getByTestId('np-table-outer-container')).toHaveClass(
73
+ 'np-table-outer-container--center',
74
+ );
75
+ });
76
+
77
+ it('calls `onRowClick` when a row is clicked', async () => {
78
+ render(<Table {...defaultProps} />);
79
+ await userEvent.click(screen.getByText('Cell content 1'));
80
+ expect(defaultProps.data.onRowClick).toHaveBeenCalled();
81
+ });
82
+ });
@@ -0,0 +1,356 @@
1
+ import { Meta, StoryObj } from '@storybook/react';
2
+ import Table from './Table';
3
+ import { TableHeaderType } from './TableHeader';
4
+ import { TableRowType, TableRowClickableType } from './TableRow';
5
+
6
+ export default {
7
+ component: Table,
8
+ title: 'Option/Table',
9
+ tags: ['autodocs'],
10
+ } satisfies Meta<typeof Table>;
11
+
12
+ type Story = StoryObj<typeof Table>;
13
+
14
+ const tableData = {
15
+ headers: [
16
+ {
17
+ header: 'Employee',
18
+ width: '230px',
19
+ },
20
+ {
21
+ header: 'Location',
22
+ },
23
+ {
24
+ header: 'Emplpyment Date',
25
+ className: 'np-table-header--custom-class',
26
+ },
27
+ {
28
+ header: 'Payment',
29
+ alignment: 'right',
30
+ },
31
+ {
32
+ header: 'Status',
33
+ status: 'error',
34
+ },
35
+ ] satisfies TableHeaderType[],
36
+ rows: [
37
+ {
38
+ id: 0,
39
+ cells: [
40
+ {
41
+ cell: {
42
+ type: 'leading',
43
+ primaryText: 'Bob Brown',
44
+ secondaryText: 'Software Engineer',
45
+ avatar: {
46
+ profileName: 'Bob Brown',
47
+ },
48
+ },
49
+ },
50
+ {
51
+ cell: {
52
+ type: 'text',
53
+ text: 'FR, Paris',
54
+ },
55
+ },
56
+ {
57
+ cell: {
58
+ type: 'text',
59
+ text: '11 Oct 2022',
60
+ },
61
+ },
62
+ {
63
+ cell: {
64
+ type: 'currency',
65
+ primaryCurrency: {
66
+ amount: 12345.67,
67
+ currency: 'eur',
68
+ },
69
+ secondaryCurrency: {
70
+ amount: 11000.0,
71
+ currency: 'gbp',
72
+ },
73
+ },
74
+ alignment: 'right',
75
+ },
76
+ {
77
+ cell: {
78
+ type: 'status',
79
+ primaryText: 'Cancelled',
80
+ secondaryText: '1 day ago',
81
+ sentiment: 'negative',
82
+ },
83
+ },
84
+ ],
85
+ },
86
+ {
87
+ id: 1,
88
+ cells: [
89
+ {
90
+ cell: {
91
+ type: 'leading',
92
+ primaryText: 'Jan Kowalski',
93
+ secondaryText: 'Product Manager',
94
+ avatar: {
95
+ profileName: 'Jan Kowalski',
96
+ },
97
+ },
98
+ },
99
+ {
100
+ cell: {
101
+ type: 'text',
102
+ text: 'US, New York',
103
+ },
104
+ },
105
+ {
106
+ cell: {
107
+ type: 'text',
108
+ text: '24 Jan 2024',
109
+ },
110
+ },
111
+ {
112
+ cell: {
113
+ type: 'currency',
114
+ primaryCurrency: {
115
+ amount: 23456.78,
116
+ currency: 'usd',
117
+ },
118
+ secondaryCurrency: {
119
+ amount: 21000.0,
120
+ currency: 'gbp',
121
+ },
122
+ },
123
+ alignment: 'right',
124
+ },
125
+ {
126
+ cell: {
127
+ type: 'status',
128
+ primaryText: 'Scheduled',
129
+ secondaryText: 'Tomorrow',
130
+ sentiment: 'neutral',
131
+ },
132
+ },
133
+ ],
134
+ },
135
+ {
136
+ id: 2,
137
+ cells: [
138
+ {
139
+ cell: {
140
+ type: 'leading',
141
+ primaryText: 'William Davis',
142
+ secondaryText: 'Data Scientist',
143
+ avatar: {
144
+ profileName: 'William Davis',
145
+ },
146
+ },
147
+ },
148
+ {
149
+ cell: {
150
+ type: 'text',
151
+ text: 'UK, London',
152
+ },
153
+ },
154
+ {
155
+ cell: {
156
+ type: 'text',
157
+ text: '9 Sept 2023',
158
+ },
159
+ },
160
+ {
161
+ cell: {
162
+ type: 'currency',
163
+ primaryCurrency: {
164
+ amount: 34567.89,
165
+ currency: 'gbp',
166
+ },
167
+ secondaryCurrency: {
168
+ amount: 31000.0,
169
+ currency: 'eur',
170
+ },
171
+ },
172
+ alignment: 'right',
173
+ },
174
+ {
175
+ cell: {
176
+ type: 'status',
177
+ primaryText: 'Paid',
178
+ secondaryText: 'Yesterday',
179
+ sentiment: 'positive',
180
+ },
181
+ },
182
+ ],
183
+ },
184
+ {
185
+ id: 3,
186
+ cells: [
187
+ {
188
+ cell: {
189
+ type: 'leading',
190
+ primaryText: 'Jane Smith',
191
+ secondaryText: 'IT Support Specialist',
192
+ status: 'success',
193
+ avatar: {
194
+ src: 'avatar-square-dude.webp',
195
+ },
196
+ },
197
+ },
198
+ {
199
+ cell: {
200
+ type: 'text',
201
+ text: 'NO, Oslo',
202
+ },
203
+ },
204
+ {
205
+ cell: {
206
+ type: 'text',
207
+ text: '12 Apr 2024',
208
+ status: 'success',
209
+ },
210
+ },
211
+ {
212
+ cell: {
213
+ type: 'currency',
214
+ primaryCurrency: {
215
+ amount: 45678.9,
216
+ currency: 'nok',
217
+ },
218
+ secondaryCurrency: {
219
+ amount: 41000.0,
220
+ currency: 'gbp',
221
+ },
222
+ status: 'success',
223
+ },
224
+ alignment: 'right',
225
+ },
226
+ {
227
+ cell: {
228
+ type: 'status',
229
+ primaryText: 'Overdue',
230
+ secondaryText: '3 days ago',
231
+ sentiment: 'warning',
232
+ },
233
+ },
234
+ ],
235
+ },
236
+ {
237
+ id: 4,
238
+ cells: [
239
+ {
240
+ cell: {
241
+ type: 'leading',
242
+ primaryText: 'Alice Johnson',
243
+ secondaryText: 'Frontend Developer',
244
+ status: 'error',
245
+ avatar: {
246
+ src: 'avatar-square-dude.webp',
247
+ },
248
+ },
249
+ },
250
+ {
251
+ cell: {
252
+ type: 'text',
253
+ text: 'JP, Tokyo',
254
+ },
255
+ },
256
+ {
257
+ cell: {
258
+ type: 'text',
259
+ text: '27 Nov 2023',
260
+ status: 'error',
261
+ },
262
+ },
263
+ {
264
+ cell: {
265
+ type: 'currency',
266
+ primaryCurrency: {
267
+ amount: 56789.01, // JPY currency's value is always cropped to zero decimals, you'll see `56789` instead of `56789.01` in screen/tests
268
+ currency: 'jpy',
269
+ },
270
+ secondaryCurrency: {
271
+ amount: 51000.0,
272
+ currency: 'gbp',
273
+ },
274
+ status: 'error',
275
+ },
276
+ alignment: 'right',
277
+ },
278
+ {
279
+ cell: {
280
+ type: 'status',
281
+ primaryText: 'Overdue',
282
+ secondaryText: '6 days ago',
283
+ sentiment: 'pending',
284
+ },
285
+ },
286
+ ],
287
+ },
288
+ ] satisfies TableRowType[] | TableRowClickableType[],
289
+ };
290
+
291
+ export const Basic: Story = {
292
+ args: {
293
+ data: tableData,
294
+ },
295
+ render: (args) => {
296
+ return <Table {...args} />;
297
+ },
298
+ };
299
+
300
+ export const WithClickableRow: Story = {
301
+ args: {
302
+ data: {
303
+ ...tableData,
304
+ onRowClick: (rowData: TableRowType | TableRowClickableType) => {
305
+ // eslint-disable-next-line no-console
306
+ console.log(`Row clicked, data:`, rowData);
307
+ },
308
+ },
309
+ },
310
+ render: (args) => {
311
+ return <Table {...args} />;
312
+ },
313
+ };
314
+
315
+ export const WithLoading: Story = {
316
+ args: {
317
+ loading: true,
318
+ },
319
+ };
320
+
321
+ export const WithEmptyData: Story = {
322
+ args: {
323
+ data: {
324
+ headers: tableData.headers,
325
+ rows: [],
326
+ },
327
+ },
328
+ };
329
+
330
+ export const WithError: Story = {
331
+ args: {
332
+ error: {
333
+ message: 'Something went wrong during data fetching',
334
+ action: {
335
+ href: '/?path=/story/option-table--with-error',
336
+ text: 'To Refresh page, click here',
337
+ },
338
+ },
339
+ },
340
+ };
341
+
342
+ export const WithAFewColums: Story = {
343
+ args: {
344
+ data: {
345
+ headers: tableData.headers
346
+ .slice(0, 1)
347
+ .concat(tableData.headers[3])
348
+ .concat(tableData.headers[4]),
349
+ rows: tableData.rows.map((row) => ({
350
+ id: row.id,
351
+ cells: row.cells.slice(0, 1).concat(row.cells[3]).concat(row.cells[4]),
352
+ })),
353
+ },
354
+ fullWidth: false,
355
+ },
356
+ };
@@ -0,0 +1,167 @@
1
+ import { useIntl } from 'react-intl';
2
+ import TableCell from './TableCell';
3
+ import TableHeader, { TableHeaderType } from './TableHeader';
4
+ import TableRow, { TableRowClickableType, TableRowType } from './TableRow';
5
+ import Alert from '../alert';
6
+
7
+ import messages from './Table.messages';
8
+ import Loader from '../loader';
9
+ import { Sentiment, Size } from '../common';
10
+ import StatusIcon from '../statusIcon';
11
+ import { clsx } from 'clsx';
12
+ import { useTheme } from '@wise/components-theming';
13
+ import Body from '../body';
14
+
15
+ export interface TableProps {
16
+ 'aria-labelledby'?: string;
17
+ data: {
18
+ headers?: TableHeaderType[];
19
+ rows?: TableRowType[] | TableRowClickableType[];
20
+ onRowClick?: (rowData: TableRowType | TableRowClickableType) => void;
21
+ };
22
+ loading?: boolean;
23
+ className?: string | undefined;
24
+ fullWidth?: boolean;
25
+ error?: {
26
+ message?: string;
27
+ action?: {
28
+ href?: string;
29
+ text?: string;
30
+ };
31
+ };
32
+ }
33
+
34
+ const Table = ({
35
+ 'aria-labelledby': ariaLabelledBy,
36
+ data,
37
+ loading,
38
+ className,
39
+ fullWidth = true,
40
+ error,
41
+ }: TableProps) => {
42
+ const { formatMessage } = useIntl();
43
+ const { theme } = useTheme();
44
+
45
+ const getRowLength = () => {
46
+ const columnsLength = data?.headers?.length ?? 0;
47
+ return data?.onRowClick ? columnsLength + 1 : columnsLength;
48
+ };
49
+
50
+ const getTableContent = () => {
51
+ if (loading) {
52
+ return (
53
+ <TableRow>
54
+ <TableCell>
55
+ <Loader data-testid="np-table-loader" />
56
+ </TableCell>
57
+ </TableRow>
58
+ );
59
+ }
60
+
61
+ // Shows the `emptyData` message when there is no data to display
62
+ if (!data?.rows?.length) {
63
+ return (
64
+ <TableRow>
65
+ <TableCell colSpan={data?.headers?.length}>
66
+ <div className="np-table-empty-data" data-testid="np-table-empty-data">
67
+ <StatusIcon sentiment={Sentiment.WARNING} size={Size.MEDIUM} />
68
+ <Body type="body-default-bold">{formatMessage(messages.emptyData)}</Body>
69
+ </div>
70
+ </TableCell>
71
+ </TableRow>
72
+ );
73
+ }
74
+
75
+ return data?.rows?.map((rowData, rowIndex) => {
76
+ return (
77
+ <TableRow
78
+ key={'table-row-'.concat(rowIndex.toString())}
79
+ rowData={rowData}
80
+ hasSeparator={data?.rows?.length ? data.rows.length - 1 !== rowIndex : false}
81
+ onRowClick={data?.onRowClick}
82
+ />
83
+ );
84
+ });
85
+ };
86
+
87
+ if (error) {
88
+ return (
89
+ <Alert
90
+ className="np-table-error"
91
+ message={error.message}
92
+ type={Sentiment.NEGATIVE}
93
+ action={{
94
+ href: error?.action?.href ?? '/',
95
+ text: error?.action?.text ?? formatMessage(messages.refreshPage),
96
+ }}
97
+ data-testid="np-table-error"
98
+ />
99
+ );
100
+ }
101
+
102
+ return (
103
+ <>
104
+ <div aria-live="polite" className="sr-only">
105
+ {formatMessage(messages[loading ? 'loading' : 'loaded'])}
106
+ </div>
107
+ <div
108
+ role={loading ? 'presentation' : 'region'}
109
+ aria-labelledby={ariaLabelledBy}
110
+ className={clsx('np-table-outer-container', className, {
111
+ 'np-theme-personal': theme === 'bright-green',
112
+ 'np-table-outer-container--center': !fullWidth,
113
+ 'np-table-outer-container--full-width': fullWidth,
114
+ })}
115
+ // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
116
+ tabIndex={0}
117
+ data-testid="np-table-outer-container"
118
+ >
119
+ <div
120
+ className={clsx('np-table-container', {
121
+ 'np-table-container--loading': loading,
122
+ })}
123
+ data-testid="np-table-container"
124
+ >
125
+ <div className="np-table-inner-container">
126
+ <table className="np-table">
127
+ <thead role={loading ? 'presentation' : undefined}>
128
+ <tr>
129
+ {(loading ?? (data?.headers && !data?.headers.length)) ? (
130
+ <TableHeader />
131
+ ) : (
132
+ data?.headers?.map((headerItem: TableHeaderType, index) => (
133
+ <TableHeader
134
+ key={headerItem.header?.concat(index.toString())}
135
+ {...headerItem}
136
+ />
137
+ ))
138
+ )}
139
+ {data?.onRowClick && <TableHeader isActionHeader />}
140
+ </tr>
141
+ </thead>
142
+ <tbody>
143
+ <tr
144
+ key="first-np-table-row--cosmetic"
145
+ aria-hidden="true"
146
+ className="np-table-row np-table-row--cosmetic"
147
+ >
148
+ <td className="np-table-cell" colSpan={getRowLength()} />
149
+ </tr>
150
+ {getTableContent()}
151
+ <tr
152
+ key="last-np-table-row--cosmetic"
153
+ aria-hidden="true"
154
+ className="np-table-row np-table-row--cosmetic"
155
+ >
156
+ <td className="np-table-cell" colSpan={getRowLength()} />
157
+ </tr>
158
+ </tbody>
159
+ </table>
160
+ </div>
161
+ </div>
162
+ </div>
163
+ </>
164
+ );
165
+ };
166
+
167
+ export default Table;