@axinom/mosaic-ui 0.66.0-rc.9 → 0.66.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 (146) hide show
  1. package/dist/components/DynamicDataList/DynamicListHeader/DynamicListHeader.d.ts.map +1 -1
  2. package/dist/components/FieldSelection/FieldSelection.d.ts.map +1 -1
  3. package/dist/components/Filters/Filter/Filter.d.ts.map +1 -1
  4. package/dist/components/Filters/Filters.model.d.ts +5 -0
  5. package/dist/components/Filters/Filters.model.d.ts.map +1 -1
  6. package/dist/components/Filters/SelectionTypes/DateTimeFilter/DateTimeFilter.d.ts +2 -0
  7. package/dist/components/Filters/SelectionTypes/DateTimeFilter/DateTimeFilter.d.ts.map +1 -1
  8. package/dist/components/Filters/SelectionTypes/FreeTextFilter/FreeTextFilter.d.ts +2 -0
  9. package/dist/components/Filters/SelectionTypes/FreeTextFilter/FreeTextFilter.d.ts.map +1 -1
  10. package/dist/components/Filters/SelectionTypes/MultiOptionFilter/MultiOptionFilter.d.ts +2 -0
  11. package/dist/components/Filters/SelectionTypes/MultiOptionFilter/MultiOptionFilter.d.ts.map +1 -1
  12. package/dist/components/Filters/SelectionTypes/NumericTextFilter/NumericTextFilter.d.ts +2 -0
  13. package/dist/components/Filters/SelectionTypes/NumericTextFilter/NumericTextFilter.d.ts.map +1 -1
  14. package/dist/components/Filters/SelectionTypes/OptionsFilter/OptionsFilter.d.ts +2 -0
  15. package/dist/components/Filters/SelectionTypes/OptionsFilter/OptionsFilter.d.ts.map +1 -1
  16. package/dist/components/Filters/SelectionTypes/SearcheableOptionsFilter/SearcheableOptionsFilter.d.ts +2 -0
  17. package/dist/components/Filters/SelectionTypes/SearcheableOptionsFilter/SearcheableOptionsFilter.d.ts.map +1 -1
  18. package/dist/components/FormElements/Radio/Radio.d.ts.map +1 -1
  19. package/dist/components/FormElements/ToggleButton/ToggleButton.d.ts.map +1 -1
  20. package/dist/components/Hub/Tile/Tile.d.ts.map +1 -1
  21. package/dist/components/Icons/Icons.d.ts +4 -9
  22. package/dist/components/Icons/Icons.d.ts.map +1 -1
  23. package/dist/components/LandingPageTiles/TileLarge/TileLarge.d.ts.map +1 -1
  24. package/dist/components/LandingPageTiles/TileSmall/TileSmall.d.ts.map +1 -1
  25. package/dist/components/List/ListCheckBox/ListCheckBox.d.ts.map +1 -1
  26. package/dist/components/List/ListHeader/ColumnLabel/ColumnLabel.d.ts.map +1 -1
  27. package/dist/components/List/ListHeader/ListHeader.d.ts.map +1 -1
  28. package/dist/components/List/ListRow/ListRow.d.ts.map +1 -1
  29. package/dist/components/List/ListRow/ListRowCell/ListRowCell.d.ts +15 -0
  30. package/dist/components/List/ListRow/ListRowCell/ListRowCell.d.ts.map +1 -0
  31. package/dist/components/List/ListRow/ListRowCell/renderData.d.ts +9 -0
  32. package/dist/components/List/ListRow/ListRowCell/renderData.d.ts.map +1 -0
  33. package/dist/components/List/ListRow/Renderers/TagsRenderer/TagsRenderer.d.ts.map +1 -1
  34. package/dist/components/Loaders/ImageLoader/ImageLoader.d.ts.map +1 -1
  35. package/dist/components/PageHeader/PageHeaderAction/PageHeaderAction.d.ts.map +1 -1
  36. package/dist/components/Utils/Postgraphile/CreateConnectionRenderer.d.ts +1 -1
  37. package/dist/components/Utils/Postgraphile/CreateConnectionRenderer.d.ts.map +1 -1
  38. package/dist/components/VisualElements/ImgElement.d.ts +50 -0
  39. package/dist/components/VisualElements/ImgElement.d.ts.map +1 -0
  40. package/dist/components/VisualElements/SvgElement.d.ts +14 -0
  41. package/dist/components/VisualElements/SvgElement.d.ts.map +1 -0
  42. package/dist/components/VisualElements/index.d.ts +3 -0
  43. package/dist/components/VisualElements/index.d.ts.map +1 -0
  44. package/dist/components/index.d.ts +1 -0
  45. package/dist/components/index.d.ts.map +1 -1
  46. package/dist/helpers/idleCallbackHelpers.d.ts +42 -0
  47. package/dist/helpers/idleCallbackHelpers.d.ts.map +1 -0
  48. package/dist/helpers/index.d.ts +1 -0
  49. package/dist/helpers/index.d.ts.map +1 -1
  50. package/dist/hooks/useResize/ResizeIndicator.d.ts +8 -0
  51. package/dist/hooks/useResize/ResizeIndicator.d.ts.map +1 -0
  52. package/dist/hooks/useResize/useResize.d.ts +5 -2
  53. package/dist/hooks/useResize/useResize.d.ts.map +1 -1
  54. package/dist/index.d.ts +1 -0
  55. package/dist/index.d.ts.map +1 -1
  56. package/dist/index.es.js +4 -4
  57. package/dist/index.es.js.map +1 -1
  58. package/dist/index.js +4 -4
  59. package/dist/index.js.map +1 -1
  60. package/package.json +2 -2
  61. package/src/components/Accordion/Accordion.scss +1 -1
  62. package/src/components/Accordion/AccordionItem/AccordionItem.scss +2 -2
  63. package/src/components/Buttons/Button/Button.scss +5 -5
  64. package/src/components/Buttons/CompositeButton/CompositeButton.scss +7 -7
  65. package/src/components/Buttons/TextButton/TextButton.scss +6 -6
  66. package/src/components/ConfirmDialog/ConfirmDialog.scss +1 -1
  67. package/src/components/DateTime/DatePicker/DatePicker.scss +12 -12
  68. package/src/components/DateTime/TimePicker/ScrollColumn/ScrollColumn.scss +7 -7
  69. package/src/components/DateTime/TimePicker/TimePicker.scss +1 -1
  70. package/src/components/DynamicDataList/DynamicListHeader/DynamicListHeader.scss +2 -2
  71. package/src/components/DynamicDataList/DynamicListHeader/DynamicListHeader.spec.tsx +2 -0
  72. package/src/components/DynamicDataList/DynamicListHeader/DynamicListHeader.tsx +62 -50
  73. package/src/components/DynamicDataList/DynamicListRow/DynamicListRow.scss +2 -2
  74. package/src/components/FieldSelection/FieldSelection.scss +4 -0
  75. package/src/components/FieldSelection/FieldSelection.tsx +1 -0
  76. package/src/components/Filters/Filter/Filter.scss +34 -15
  77. package/src/components/Filters/Filter/Filter.spec.tsx +1 -1
  78. package/src/components/Filters/Filter/Filter.tsx +46 -34
  79. package/src/components/Filters/Filters.model.ts +6 -0
  80. package/src/components/Filters/SelectionTypes/DateTimeFilter/DateTimeFilter.tsx +5 -0
  81. package/src/components/Filters/SelectionTypes/FreeTextFilter/FreeTextFilter.tsx +4 -0
  82. package/src/components/Filters/SelectionTypes/MultiOptionFilter/MultiOptionFilter.scss +1 -1
  83. package/src/components/Filters/SelectionTypes/MultiOptionFilter/MultiOptionFilter.tsx +9 -1
  84. package/src/components/Filters/SelectionTypes/NumericTextFilter/NumericTextFilter.tsx +5 -0
  85. package/src/components/Filters/SelectionTypes/OptionsFilter/OptionsFilter.scss +6 -10
  86. package/src/components/Filters/SelectionTypes/OptionsFilter/OptionsFilter.tsx +8 -0
  87. package/src/components/Filters/SelectionTypes/SearcheableOptionsFilter/SearcheableOptionsFilter.tsx +6 -1
  88. package/src/components/FormElements/Checkbox/Checkbox.scss +4 -4
  89. package/src/components/FormElements/CustomTags/CustomTags.scss +1 -1
  90. package/src/components/FormElements/FileUploadControl/FileUploadControl.scss +4 -4
  91. package/src/components/FormElements/Radio/Radio.scss +5 -5
  92. package/src/components/FormElements/Radio/Radio.tsx +3 -2
  93. package/src/components/FormElements/Select/Select.scss +11 -6
  94. package/src/components/FormElements/ToggleButton/ToggleButton.scss +7 -7
  95. package/src/components/FormElements/ToggleButton/ToggleButton.tsx +32 -27
  96. package/src/components/Hub/Hub.stories.tsx +3 -2
  97. package/src/components/Hub/Tile/Tile.spec.tsx +7 -2
  98. package/src/components/Hub/Tile/Tile.tsx +2 -1
  99. package/src/components/Icons/Icons.scss +1 -0
  100. package/src/components/Icons/Icons.spec.tsx +90 -41
  101. package/src/components/Icons/Icons.tsx +357 -765
  102. package/src/components/InfoTooltip/InfoTooltip.scss +1 -1
  103. package/src/components/InlineMenu/InlineMenu.scss +2 -2
  104. package/src/components/LandingPageTiles/LandingPageTiles.stories.tsx +3 -2
  105. package/src/components/LandingPageTiles/TileLarge/TileLarge.spec.tsx +5 -1
  106. package/src/components/LandingPageTiles/TileLarge/TileLarge.tsx +2 -1
  107. package/src/components/LandingPageTiles/TileSmall/TileSmall.spec.tsx +7 -2
  108. package/src/components/LandingPageTiles/TileSmall/TileSmall.tsx +2 -1
  109. package/src/components/List/List.scss +4 -4
  110. package/src/components/List/ListCheckBox/ListCheckBox.tsx +1 -0
  111. package/src/components/List/ListHeader/ColumnLabel/ColumnLabel.spec.tsx +6 -6
  112. package/src/components/List/ListHeader/ColumnLabel/ColumnLabel.tsx +10 -13
  113. package/src/components/List/ListHeader/ListHeader.scss +2 -3
  114. package/src/components/List/ListHeader/ListHeader.spec.tsx +2 -0
  115. package/src/components/List/ListHeader/ListHeader.tsx +57 -51
  116. package/src/components/List/ListRow/ListRow.scss +1 -28
  117. package/src/components/List/ListRow/ListRow.spec.tsx +10 -8
  118. package/src/components/List/ListRow/ListRow.tsx +20 -152
  119. package/src/components/List/ListRow/ListRowCell/ListRowCell.scss +26 -0
  120. package/src/components/List/ListRow/ListRowCell/ListRowCell.spec.tsx +491 -0
  121. package/src/components/List/ListRow/ListRowCell/ListRowCell.tsx +57 -0
  122. package/src/components/List/ListRow/ListRowCell/renderData.tsx +124 -0
  123. package/src/components/List/ListRow/Renderers/TagsRenderer/TagsRenderer.spec.tsx +191 -80
  124. package/src/components/List/ListRow/Renderers/TagsRenderer/TagsRenderer.tsx +63 -34
  125. package/src/components/Loaders/ImageLoader/ImageLoader.spec.tsx +13 -14
  126. package/src/components/Loaders/ImageLoader/ImageLoader.tsx +5 -3
  127. package/src/components/PageHeader/PageHeader.scss +1 -1
  128. package/src/components/PageHeader/PageHeaderAction/PageHeaderAction.tsx +13 -2
  129. package/src/components/Tabs/Tab/CustomTab.scss +4 -4
  130. package/src/components/Tabs/TabList/CustomTabList.scss +2 -2
  131. package/src/components/Utils/Postgraphile/CreateConnectionRenderer.spec.ts +48 -12
  132. package/src/components/Utils/Postgraphile/CreateConnectionRenderer.tsx +5 -4
  133. package/src/components/VisualElements/ImgElement.spec.tsx +92 -0
  134. package/src/components/VisualElements/ImgElement.tsx +72 -0
  135. package/src/components/VisualElements/SvgElement.spec.tsx +160 -0
  136. package/src/components/VisualElements/SvgElement.tsx +40 -0
  137. package/src/components/VisualElements/index.ts +7 -0
  138. package/src/components/index.ts +1 -0
  139. package/src/helpers/idleCallbackHelpers.ts +66 -0
  140. package/src/helpers/index.ts +5 -0
  141. package/src/hooks/useResize/ResizeIndicator.scss +7 -0
  142. package/src/hooks/useResize/ResizeIndicator.tsx +39 -0
  143. package/src/hooks/useResize/{useResize.ts → useResize.tsx} +38 -6
  144. package/src/index.ts +2 -0
  145. package/src/styles/branding.scss +89 -0
  146. package/src/styles/variables.scss +245 -187
@@ -0,0 +1,491 @@
1
+ import { mount, shallow } from 'enzyme';
2
+ import React from 'react';
3
+ import { Column } from '../..';
4
+ import { Data } from '../../../..';
5
+ import { ListRowCell } from './ListRowCell';
6
+
7
+ interface TestData extends Data {
8
+ id: string;
9
+ name: string;
10
+ age: number;
11
+ date: Date;
12
+ }
13
+
14
+ const mockData: TestData = {
15
+ id: '123',
16
+ name: 'Test User',
17
+ age: 30,
18
+ date: new Date('2023-01-15T10:30:00'),
19
+ };
20
+
21
+ const dateFormatter = new Intl.DateTimeFormat('en-US', {
22
+ year: 'numeric',
23
+ month: 'numeric',
24
+ day: 'numeric',
25
+ hour: 'numeric',
26
+ minute: 'numeric',
27
+ second: 'numeric',
28
+ });
29
+
30
+ const numberFormatter = new Intl.NumberFormat('en-US');
31
+
32
+ describe('ListRowCell', () => {
33
+ it('renders the component without crashing', () => {
34
+ const column: Column<TestData> = {
35
+ propertyName: 'name',
36
+ label: 'Name',
37
+ };
38
+
39
+ const wrapper = shallow(
40
+ <ListRowCell
41
+ column={column}
42
+ data={mockData}
43
+ dateFormatter={dateFormatter}
44
+ numberFormatter={numberFormatter}
45
+ />,
46
+ );
47
+
48
+ expect(wrapper).toBeTruthy();
49
+ });
50
+
51
+ it('renders a string value correctly', () => {
52
+ const column: Column<TestData> = {
53
+ propertyName: 'name',
54
+ label: 'Name',
55
+ };
56
+
57
+ const wrapper = mount(
58
+ <ListRowCell
59
+ column={column}
60
+ data={mockData}
61
+ dateFormatter={dateFormatter}
62
+ numberFormatter={numberFormatter}
63
+ />,
64
+ );
65
+
66
+ expect(wrapper.text()).toBe('Test User');
67
+ });
68
+
69
+ it('renders a number value with number formatter', () => {
70
+ const column: Column<TestData> = {
71
+ propertyName: 'age',
72
+ label: 'Age',
73
+ };
74
+
75
+ const wrapper = mount(
76
+ <ListRowCell
77
+ column={column}
78
+ data={mockData}
79
+ dateFormatter={dateFormatter}
80
+ numberFormatter={numberFormatter}
81
+ />,
82
+ );
83
+
84
+ expect(wrapper.text()).toBe(numberFormatter.format(30));
85
+ });
86
+
87
+ it('renders a date value with date formatter', () => {
88
+ const column: Column<TestData> = {
89
+ propertyName: 'date',
90
+ label: 'Date',
91
+ };
92
+
93
+ const wrapper = mount(
94
+ <ListRowCell
95
+ column={column}
96
+ data={mockData}
97
+ dateFormatter={dateFormatter}
98
+ numberFormatter={numberFormatter}
99
+ />,
100
+ );
101
+
102
+ expect(wrapper.text()).toBe(dateFormatter.format(mockData.date));
103
+ });
104
+
105
+ it('uses custom renderer when provided', () => {
106
+ const column: Column<TestData> = {
107
+ propertyName: 'name',
108
+ label: 'Name',
109
+ render: (value) => `Custom: ${value}`,
110
+ };
111
+
112
+ const wrapper = mount(
113
+ <ListRowCell
114
+ column={column}
115
+ data={mockData}
116
+ dateFormatter={dateFormatter}
117
+ numberFormatter={numberFormatter}
118
+ />,
119
+ );
120
+
121
+ expect(wrapper.text()).toBe('Custom: Test User');
122
+ });
123
+
124
+ it('handles null values', () => {
125
+ const nullData = { ...mockData, name: null as unknown as string };
126
+ const column: Column<TestData> = {
127
+ propertyName: 'name',
128
+ label: 'Name',
129
+ };
130
+
131
+ const wrapper = mount(
132
+ <ListRowCell
133
+ column={column}
134
+ data={nullData}
135
+ dateFormatter={dateFormatter}
136
+ numberFormatter={numberFormatter}
137
+ />,
138
+ );
139
+
140
+ expect(wrapper.text()).toBe('');
141
+ });
142
+
143
+ it('handles undefined values', () => {
144
+ const undefinedData = { ...mockData, name: undefined as unknown as string };
145
+ const column: Column<TestData> = {
146
+ propertyName: 'name',
147
+ label: 'Name',
148
+ };
149
+
150
+ const wrapper = mount(
151
+ <ListRowCell
152
+ column={column}
153
+ data={undefinedData}
154
+ dateFormatter={dateFormatter}
155
+ numberFormatter={numberFormatter}
156
+ />,
157
+ );
158
+
159
+ expect(wrapper.text()).toBe('');
160
+ });
161
+
162
+ it('renders tooltip with column text by default', () => {
163
+ const column: Column<TestData> = {
164
+ propertyName: 'name',
165
+ label: 'Name',
166
+ };
167
+
168
+ const wrapper = mount(
169
+ <ListRowCell
170
+ column={column}
171
+ data={mockData}
172
+ dateFormatter={dateFormatter}
173
+ numberFormatter={numberFormatter}
174
+ />,
175
+ );
176
+
177
+ const cell = wrapper.find('.cell');
178
+ expect(cell.prop('title')).toBe('Test User');
179
+ });
180
+
181
+ it('does not render tooltip when column.tooltip is false', () => {
182
+ const column: Column<TestData> = {
183
+ propertyName: 'name',
184
+ label: 'Name',
185
+ tooltip: false,
186
+ };
187
+
188
+ const wrapper = mount(
189
+ <ListRowCell
190
+ column={column}
191
+ data={mockData}
192
+ dateFormatter={dateFormatter}
193
+ numberFormatter={numberFormatter}
194
+ />,
195
+ );
196
+
197
+ const cell = wrapper.find('.cell');
198
+ expect(cell.prop('title')).toBeUndefined();
199
+ });
200
+
201
+ describe('text alignment', () => {
202
+ it('applies default horizontal text alignment (left)', () => {
203
+ const column: Column<TestData> = {
204
+ propertyName: 'name',
205
+ label: 'Name',
206
+ };
207
+
208
+ const wrapper = mount(
209
+ <ListRowCell
210
+ column={column}
211
+ data={mockData}
212
+ dateFormatter={dateFormatter}
213
+ numberFormatter={numberFormatter}
214
+ />,
215
+ );
216
+
217
+ const cell = wrapper.find('.cell');
218
+ expect(cell.prop('style')).toHaveProperty('textAlign', 'left');
219
+ });
220
+
221
+ it('applies custom horizontal text alignment', () => {
222
+ const column: Column<TestData> = {
223
+ propertyName: 'name',
224
+ label: 'Name',
225
+ };
226
+
227
+ const wrapper = mount(
228
+ <ListRowCell
229
+ column={column}
230
+ data={mockData}
231
+ horizontalTextAlign="right"
232
+ dateFormatter={dateFormatter}
233
+ numberFormatter={numberFormatter}
234
+ />,
235
+ );
236
+
237
+ const cell = wrapper.find('.cell');
238
+ expect(cell.prop('style')).toHaveProperty('textAlign', 'right');
239
+ });
240
+
241
+ it('applies default vertical text alignment (center)', () => {
242
+ const column: Column<TestData> = {
243
+ propertyName: 'name',
244
+ label: 'Name',
245
+ };
246
+
247
+ const wrapper = mount(
248
+ <ListRowCell
249
+ column={column}
250
+ data={mockData}
251
+ dateFormatter={dateFormatter}
252
+ numberFormatter={numberFormatter}
253
+ />,
254
+ );
255
+
256
+ const cell = wrapper.find('.cell');
257
+ expect(cell.prop('style')).toHaveProperty('alignSelf', 'center');
258
+ });
259
+
260
+ it('applies custom vertical text alignment', () => {
261
+ const column: Column<TestData> = {
262
+ propertyName: 'name',
263
+ label: 'Name',
264
+ };
265
+
266
+ const wrapper = mount(
267
+ <ListRowCell
268
+ column={column}
269
+ data={mockData}
270
+ verticalTextAlign="start"
271
+ dateFormatter={dateFormatter}
272
+ numberFormatter={numberFormatter}
273
+ />,
274
+ );
275
+
276
+ const cell = wrapper.find('.cell');
277
+ expect(cell.prop('style')).toHaveProperty('alignSelf', 'start');
278
+ });
279
+
280
+ it('applies horizontal column alignment from column config', () => {
281
+ const column: Column<TestData> = {
282
+ propertyName: 'name',
283
+ label: 'Name',
284
+ horizontalColumnAlign: 'right',
285
+ };
286
+
287
+ const wrapper = mount(
288
+ <ListRowCell
289
+ column={column}
290
+ data={mockData}
291
+ dateFormatter={dateFormatter}
292
+ numberFormatter={numberFormatter}
293
+ />,
294
+ );
295
+
296
+ const cell = wrapper.find('.cell');
297
+ expect(cell.prop('style')).toHaveProperty('justifySelf', 'right');
298
+ });
299
+ });
300
+
301
+ describe('text wrapping', () => {
302
+ it('applies nowrap class by default (textWrap = false)', () => {
303
+ const column: Column<TestData> = {
304
+ propertyName: 'name',
305
+ label: 'Name',
306
+ };
307
+
308
+ const wrapper = mount(
309
+ <ListRowCell
310
+ column={column}
311
+ data={mockData}
312
+ dateFormatter={dateFormatter}
313
+ numberFormatter={numberFormatter}
314
+ />,
315
+ );
316
+
317
+ const cell = wrapper.find('.cell');
318
+ expect(cell.hasClass('nowrap')).toBe(true);
319
+ });
320
+
321
+ it('does not apply nowrap class when textWrap is true', () => {
322
+ const column: Column<TestData> = {
323
+ propertyName: 'name',
324
+ label: 'Name',
325
+ };
326
+
327
+ const wrapper = mount(
328
+ <ListRowCell
329
+ column={column}
330
+ data={mockData}
331
+ textWrap={true}
332
+ dateFormatter={dateFormatter}
333
+ numberFormatter={numberFormatter}
334
+ />,
335
+ );
336
+
337
+ const cell = wrapper.find('.cell');
338
+ expect(cell.hasClass('nowrap')).toBe(false);
339
+ });
340
+ });
341
+
342
+ describe('data-test-id', () => {
343
+ it('sets data-test-id based on propertyName when render returns a string', () => {
344
+ const column: Column<TestData> = {
345
+ propertyName: 'name',
346
+ label: 'Name',
347
+ render: (value) => `Custom: ${value}`,
348
+ };
349
+
350
+ const wrapper = mount(
351
+ <ListRowCell
352
+ column={column}
353
+ data={mockData}
354
+ dateFormatter={dateFormatter}
355
+ numberFormatter={numberFormatter}
356
+ />,
357
+ );
358
+
359
+ const cell = wrapper.find('.cell');
360
+ expect(cell.prop('data-test-id')).toBeNull();
361
+ });
362
+
363
+ it('sets data-test-id on inner span for standard values', () => {
364
+ const column: Column<TestData> = {
365
+ propertyName: 'name',
366
+ label: 'Name',
367
+ };
368
+
369
+ const wrapper = mount(
370
+ <ListRowCell
371
+ column={column}
372
+ data={mockData}
373
+ dateFormatter={dateFormatter}
374
+ numberFormatter={numberFormatter}
375
+ />,
376
+ );
377
+
378
+ // For standard values, data-test-id is on the inner span, not the cell div
379
+ const cell = wrapper.find('.cell');
380
+ expect(cell.prop('data-test-id')).toBeNull();
381
+
382
+ // Check that the span inside has the data-test-id
383
+ const span = wrapper.find(
384
+ 'span[data-test-id="list-entry-property:name"]',
385
+ );
386
+ expect(span).toHaveLength(1);
387
+ });
388
+
389
+ it('sets data-test-id on cell when custom renderer returns JSX', () => {
390
+ const column: Column<TestData> = {
391
+ propertyName: 'name',
392
+ label: 'Name',
393
+ render: (value) => <div className="custom-render">{value}</div>,
394
+ };
395
+
396
+ const wrapper = mount(
397
+ <ListRowCell
398
+ column={column}
399
+ data={mockData}
400
+ dateFormatter={dateFormatter}
401
+ numberFormatter={numberFormatter}
402
+ />,
403
+ );
404
+
405
+ // For custom JSX renderers, data-test-id is on the cell div
406
+ const cell = wrapper.find('.cell');
407
+ expect(cell.prop('data-test-id')).toBe('list-entry-property:name');
408
+ });
409
+ });
410
+
411
+ describe('column key handling', () => {
412
+ it('uses propertyName as key when key is not provided', () => {
413
+ const column: Column<TestData> = {
414
+ propertyName: 'name',
415
+ label: 'Name',
416
+ };
417
+
418
+ const wrapper = shallow(
419
+ <ListRowCell
420
+ column={column}
421
+ data={mockData}
422
+ dateFormatter={dateFormatter}
423
+ numberFormatter={numberFormatter}
424
+ />,
425
+ );
426
+
427
+ const cellWrapper = wrapper.find('.cellWrapper');
428
+ expect(cellWrapper.key()).toBe('name');
429
+ });
430
+
431
+ it('uses custom key when provided', () => {
432
+ const column: Column<TestData> = {
433
+ propertyName: 'name',
434
+ label: 'Name',
435
+ key: 'custom-key',
436
+ };
437
+
438
+ const wrapper = shallow(
439
+ <ListRowCell
440
+ column={column}
441
+ data={mockData}
442
+ dateFormatter={dateFormatter}
443
+ numberFormatter={numberFormatter}
444
+ />,
445
+ );
446
+
447
+ const cellWrapper = wrapper.find('.cellWrapper');
448
+ expect(cellWrapper.key()).toBe('custom-key');
449
+ });
450
+ });
451
+
452
+ describe('unbound columns (no propertyName)', () => {
453
+ it('renders custom renderer for unbound column', () => {
454
+ const column: Column<TestData> = {
455
+ key: 'actions',
456
+ label: 'Actions',
457
+ render: (_value, data) => <button>Edit {data.name}</button>,
458
+ };
459
+
460
+ const wrapper = mount(
461
+ <ListRowCell
462
+ column={column}
463
+ data={mockData}
464
+ dateFormatter={dateFormatter}
465
+ numberFormatter={numberFormatter}
466
+ />,
467
+ );
468
+
469
+ expect(wrapper.find('button').text()).toBe('Edit Test User');
470
+ });
471
+
472
+ it('handles unbound column with string render result', () => {
473
+ const column: Column<TestData> = {
474
+ key: 'status',
475
+ label: 'Status',
476
+ render: () => 'Active',
477
+ };
478
+
479
+ const wrapper = mount(
480
+ <ListRowCell
481
+ column={column}
482
+ data={mockData}
483
+ dateFormatter={dateFormatter}
484
+ numberFormatter={numberFormatter}
485
+ />,
486
+ );
487
+
488
+ expect(wrapper.text()).toBe('Active');
489
+ });
490
+ });
491
+ });
@@ -0,0 +1,57 @@
1
+ import clsx from 'clsx';
2
+ import React from 'react';
3
+ import type { Data } from '../../../../types/data';
4
+ import type { Column } from '../../List.model';
5
+ import classes from './ListRowCell.scss';
6
+ import { renderData } from './renderData';
7
+
8
+ interface ListRowCellProps<T extends Data> {
9
+ column: Column<T>;
10
+ data: T;
11
+ horizontalTextAlign?: 'left' | 'right' | 'center';
12
+ verticalTextAlign?: 'start' | 'center' | 'end';
13
+ textWrap?: boolean;
14
+ dateFormatter: Intl.DateTimeFormat;
15
+ numberFormatter: Intl.NumberFormat;
16
+ }
17
+
18
+ export const ListRowCell = <T extends Data>({
19
+ column,
20
+ data,
21
+ horizontalTextAlign = 'left',
22
+ verticalTextAlign = 'center',
23
+ textWrap = false,
24
+ dateFormatter,
25
+ numberFormatter,
26
+ }: ListRowCellProps<T>): JSX.Element => {
27
+ const { columnData, tooltip, isDataTestIdSet } = renderData<T>(
28
+ column,
29
+ data,
30
+ dateFormatter,
31
+ numberFormatter,
32
+ );
33
+
34
+ return (
35
+ <div
36
+ key={column.key ?? (column.propertyName as string)}
37
+ className={classes.cellWrapper}
38
+ >
39
+ <div
40
+ className={clsx(classes.cell, { [classes.nowrap]: !textWrap })}
41
+ title={tooltip}
42
+ style={{
43
+ justifySelf: column.horizontalColumnAlign, // Horizontal alignment based on column config
44
+ alignSelf: verticalTextAlign, // Vertical alignment based on props
45
+ textAlign: horizontalTextAlign, // Additional text alignment inside the cell
46
+ }}
47
+ data-test-id={
48
+ isDataTestIdSet
49
+ ? null
50
+ : `list-entry-property:${column.propertyName as string}`
51
+ }
52
+ >
53
+ {columnData}
54
+ </div>
55
+ </div>
56
+ );
57
+ };
@@ -0,0 +1,124 @@
1
+ import React from 'react';
2
+ import { Data } from '../../../../types';
3
+ import { getTooltipText } from '../../../../utils/ToolTipHelpers';
4
+ import { Column } from '../../List.model';
5
+
6
+ export const renderData = <T extends Data>(
7
+ column: Column<T>,
8
+ rowData: T,
9
+ dateFormatter: Intl.DateTimeFormat,
10
+ numberFormatter: Intl.NumberFormat,
11
+ ): {
12
+ columnData: React.ReactNode;
13
+ tooltip: string | undefined;
14
+ isDataTestIdSet: boolean;
15
+ } => {
16
+ const getTooltip = (value: unknown): string | undefined =>
17
+ column.tooltip !== false ? getTooltipText(value) : undefined;
18
+
19
+ if (!column.propertyName) {
20
+ const columnData = column.render?.(undefined, rowData);
21
+
22
+ if (typeof columnData === 'string') {
23
+ return {
24
+ columnData: (
25
+ <span
26
+ data-test-id={`list-entry-property:${
27
+ column.propertyName as string
28
+ }`}
29
+ >
30
+ {columnData}
31
+ </span>
32
+ ),
33
+ isDataTestIdSet: true,
34
+ tooltip: getTooltip(columnData),
35
+ };
36
+ }
37
+
38
+ return {
39
+ columnData,
40
+ tooltip: getTooltip(columnData),
41
+ isDataTestIdSet: false,
42
+ };
43
+ }
44
+ const value: unknown = rowData[column.propertyName];
45
+ if (column.render) {
46
+ const columnData = column.render(value, rowData);
47
+
48
+ if (typeof columnData === 'string') {
49
+ return {
50
+ columnData: (
51
+ <span
52
+ data-test-id={`list-entry-property:${
53
+ column.propertyName as string
54
+ }`}
55
+ >
56
+ {columnData}
57
+ </span>
58
+ ),
59
+ isDataTestIdSet: true,
60
+ tooltip: getTooltip(columnData),
61
+ };
62
+ }
63
+
64
+ return {
65
+ columnData,
66
+ tooltip: getTooltip(columnData),
67
+ isDataTestIdSet: false,
68
+ };
69
+ }
70
+
71
+ if (value === null || value === undefined) {
72
+ return {
73
+ columnData: (
74
+ <span
75
+ data-test-id={`list-entry-property:${column.propertyName as string}`}
76
+ />
77
+ ),
78
+ tooltip: undefined,
79
+ isDataTestIdSet: true,
80
+ };
81
+ }
82
+
83
+ if (typeof value === 'number') {
84
+ const columnData = numberFormatter.format(value);
85
+ return {
86
+ columnData: (
87
+ <span
88
+ data-test-id={`list-entry-property:${column.propertyName as string}`}
89
+ >
90
+ {columnData}
91
+ </span>
92
+ ),
93
+ tooltip: getTooltip(columnData),
94
+ isDataTestIdSet: true,
95
+ };
96
+ }
97
+
98
+ if (value instanceof Date) {
99
+ const columnData = dateFormatter.format(value);
100
+ return {
101
+ columnData: (
102
+ <span
103
+ data-test-id={`list-entry-property:${column.propertyName as string}`}
104
+ >
105
+ {columnData}
106
+ </span>
107
+ ),
108
+ tooltip: getTooltip(columnData),
109
+ isDataTestIdSet: true,
110
+ };
111
+ }
112
+
113
+ return {
114
+ columnData: (
115
+ <span
116
+ data-test-id={`list-entry-property:${column.propertyName as string}`}
117
+ >
118
+ {String(value)}
119
+ </span>
120
+ ),
121
+ tooltip: getTooltip(String(value)),
122
+ isDataTestIdSet: true,
123
+ };
124
+ };