@scality/data-browser-library 1.0.0-preview.7 → 1.0.0-preview.9

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 (90) hide show
  1. package/dist/components/__tests__/BucketCreate.test.d.ts +1 -0
  2. package/dist/components/__tests__/BucketCreate.test.js +408 -0
  3. package/dist/components/__tests__/BucketLifecycleFormPage.test.d.ts +1 -0
  4. package/dist/components/__tests__/BucketLifecycleFormPage.test.js +618 -0
  5. package/dist/components/__tests__/BucketLifecycleList.test.d.ts +1 -0
  6. package/dist/components/__tests__/BucketLifecycleList.test.js +325 -0
  7. package/dist/components/__tests__/BucketList.test.js +190 -0
  8. package/dist/components/__tests__/BucketOverview.test.js +298 -8
  9. package/dist/components/__tests__/BucketReplicationFormPage.test.d.ts +1 -0
  10. package/dist/components/__tests__/BucketReplicationFormPage.test.js +1757 -0
  11. package/dist/components/__tests__/BucketReplicationList.test.d.ts +1 -0
  12. package/dist/components/__tests__/BucketReplicationList.test.js +344 -0
  13. package/dist/components/__tests__/DeleteBucketConfigRuleButton.test.d.ts +1 -0
  14. package/dist/components/__tests__/DeleteBucketConfigRuleButton.test.js +196 -0
  15. package/dist/components/__tests__/EmptyBucketButton.test.d.ts +1 -0
  16. package/dist/components/__tests__/EmptyBucketButton.test.js +302 -0
  17. package/dist/components/buckets/BucketCreate.d.ts +49 -0
  18. package/dist/components/buckets/BucketCreate.js +237 -0
  19. package/dist/components/buckets/BucketDetails.js +62 -10
  20. package/dist/components/buckets/BucketLifecycleFormPage.d.ts +15 -0
  21. package/dist/components/buckets/BucketLifecycleFormPage.js +1070 -0
  22. package/dist/components/buckets/BucketLifecycleList.d.ts +10 -0
  23. package/dist/components/buckets/BucketLifecycleList.js +270 -0
  24. package/dist/components/buckets/BucketList.d.ts +5 -2
  25. package/dist/components/buckets/BucketList.js +38 -28
  26. package/dist/components/buckets/BucketOverview.d.ts +65 -4
  27. package/dist/components/buckets/BucketOverview.js +261 -179
  28. package/dist/components/buckets/BucketPage.js +1 -1
  29. package/dist/components/buckets/BucketReplicationFormPage.d.ts +1 -0
  30. package/dist/components/buckets/BucketReplicationFormPage.js +834 -0
  31. package/dist/components/buckets/BucketReplicationList.d.ts +11 -0
  32. package/dist/components/buckets/BucketReplicationList.js +189 -0
  33. package/dist/components/buckets/DeleteBucketConfigRuleButton.d.ts +18 -0
  34. package/dist/components/buckets/DeleteBucketConfigRuleButton.js +53 -0
  35. package/dist/components/buckets/EmptyBucketButton.d.ts +5 -0
  36. package/dist/components/buckets/EmptyBucketButton.js +232 -0
  37. package/dist/components/buckets/EmptyBucketSummary.d.ts +9 -0
  38. package/dist/components/buckets/EmptyBucketSummary.js +60 -0
  39. package/dist/components/buckets/EmptyBucketSummaryList.d.ts +13 -0
  40. package/dist/components/buckets/EmptyBucketSummaryList.js +140 -0
  41. package/dist/components/buckets/notifications/BucketNotificationCreatePage.js +8 -8
  42. package/dist/components/index.d.ts +8 -1
  43. package/dist/components/index.js +9 -2
  44. package/dist/components/objects/ObjectLock/EditRetentionButton.d.ts +4 -0
  45. package/dist/components/objects/ObjectLock/EditRetentionButton.js +32 -0
  46. package/dist/components/objects/ObjectLock/ObjectLockRetentionSettings.d.ts +3 -0
  47. package/dist/components/objects/ObjectLock/ObjectLockRetentionSettings.js +211 -0
  48. package/dist/components/objects/ObjectLock/ObjectLockSettings.d.ts +9 -0
  49. package/dist/components/objects/ObjectLock/ObjectLockSettings.js +158 -0
  50. package/dist/components/objects/ObjectLock/ObjectLockSettingsUtils.d.ts +8 -0
  51. package/dist/components/objects/ObjectLock/ObjectLockSettingsUtils.js +39 -0
  52. package/dist/components/objects/ObjectLock/__tests__/EditRetentionButton.test.d.ts +1 -0
  53. package/dist/components/objects/ObjectLock/__tests__/EditRetentionButton.test.js +204 -0
  54. package/dist/components/objects/ObjectLock/__tests__/ObjectLockSettings.test.d.ts +1 -0
  55. package/dist/components/objects/ObjectLock/__tests__/ObjectLockSettings.test.js +374 -0
  56. package/dist/components/ui/ArrayFieldActions.d.ts +36 -0
  57. package/dist/components/ui/ArrayFieldActions.js +38 -0
  58. package/dist/components/ui/ConfirmDeleteRuleModal.d.ts +16 -0
  59. package/dist/components/ui/ConfirmDeleteRuleModal.js +43 -0
  60. package/dist/components/ui/FilterFormSection.d.ts +44 -0
  61. package/dist/components/ui/FilterFormSection.js +159 -0
  62. package/dist/config/factory.d.ts +13 -2
  63. package/dist/config/factory.js +9 -6
  64. package/dist/hooks/__tests__/useISVBucketDetection.test.d.ts +1 -0
  65. package/dist/hooks/__tests__/useISVBucketDetection.test.js +188 -0
  66. package/dist/hooks/factories/__tests__/useCreateS3QueryHook.test.js +44 -1
  67. package/dist/hooks/factories/useCreateS3QueryHook.js +22 -1
  68. package/dist/hooks/index.d.ts +4 -0
  69. package/dist/hooks/index.js +5 -1
  70. package/dist/hooks/useDeleteBucketConfigRule.d.ts +26 -0
  71. package/dist/hooks/useDeleteBucketConfigRule.js +46 -0
  72. package/dist/hooks/useEmptyBucket.d.ts +27 -0
  73. package/dist/hooks/useEmptyBucket.js +116 -0
  74. package/dist/hooks/useISVBucketDetection.d.ts +15 -0
  75. package/dist/hooks/useISVBucketDetection.js +27 -0
  76. package/dist/hooks/useTableRowSelection.d.ts +9 -0
  77. package/dist/hooks/useTableRowSelection.js +45 -0
  78. package/dist/test/setup.js +8 -0
  79. package/dist/test/testUtils.d.ts +99 -17
  80. package/dist/test/testUtils.js +64 -16
  81. package/dist/test/utils/errorHandling.test.js +39 -1
  82. package/dist/utils/constants.d.ts +12 -0
  83. package/dist/utils/constants.js +9 -0
  84. package/dist/utils/errorHandling.d.ts +9 -0
  85. package/dist/utils/errorHandling.js +6 -1
  86. package/dist/utils/index.d.ts +2 -0
  87. package/dist/utils/index.js +2 -0
  88. package/dist/utils/s3RuleUtils.d.ts +53 -0
  89. package/dist/utils/s3RuleUtils.js +101 -0
  90. package/package.json +1 -1
@@ -0,0 +1,325 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { fireEvent, render, screen, within } from "@testing-library/react";
3
+ import { MemoryRouter } from "react-router";
4
+ import { createTestWrapper, mockOffsetSize } from "../../test/testUtils.js";
5
+ import { BucketLifecycleList } from "../buckets/BucketLifecycleList.js";
6
+ const renderBucketLifecycleList = (props = {})=>{
7
+ const Wrapper = createTestWrapper();
8
+ return render(/*#__PURE__*/ jsx(MemoryRouter, {
9
+ children: /*#__PURE__*/ jsx(Wrapper, {
10
+ children: /*#__PURE__*/ jsx(BucketLifecycleList, {
11
+ bucketName: "test-bucket",
12
+ lifecycleRules: [],
13
+ ...props
14
+ })
15
+ })
16
+ }));
17
+ };
18
+ const mockLifecycleRules = [
19
+ {
20
+ ID: "rule-1",
21
+ Status: "Enabled",
22
+ Expiration: {
23
+ Days: 30
24
+ }
25
+ },
26
+ {
27
+ ID: "rule-2",
28
+ Status: "Disabled",
29
+ NoncurrentVersionExpiration: {
30
+ NoncurrentDays: 90
31
+ }
32
+ },
33
+ {
34
+ ID: "rule-3",
35
+ Status: "Enabled",
36
+ AbortIncompleteMultipartUpload: {
37
+ DaysAfterInitiation: 7
38
+ },
39
+ Expiration: {
40
+ Days: 365
41
+ },
42
+ NoncurrentVersionExpiration: {
43
+ NoncurrentDays: 30
44
+ }
45
+ }
46
+ ];
47
+ const mockRuleWithDate = {
48
+ ID: "rule-with-date",
49
+ Status: "Enabled",
50
+ Expiration: {
51
+ Date: new Date("2025-12-31T00:00:00Z")
52
+ }
53
+ };
54
+ const mockRuleWithSingleDay = {
55
+ ID: "rule-single-day",
56
+ Status: "Enabled",
57
+ Expiration: {
58
+ Days: 1
59
+ }
60
+ };
61
+ describe("BucketLifecycleList", ()=>{
62
+ beforeEach(()=>{
63
+ jest.clearAllMocks();
64
+ mockOffsetSize(800, 600);
65
+ });
66
+ it("shows a table with proper headers", ()=>{
67
+ renderBucketLifecycleList({
68
+ lifecycleRules: mockLifecycleRules
69
+ });
70
+ expect(screen.getByRole("grid")).toBeInTheDocument();
71
+ expect(screen.getByText("Rule ID")).toBeInTheDocument();
72
+ expect(screen.getByText("Status")).toBeInTheDocument();
73
+ expect(screen.getByText("Actions")).toBeInTheDocument();
74
+ });
75
+ it("displays lifecycle rules with their IDs", ()=>{
76
+ renderBucketLifecycleList({
77
+ lifecycleRules: mockLifecycleRules
78
+ });
79
+ expect(screen.getByText("rule-1")).toBeInTheDocument();
80
+ expect(screen.getByText("rule-2")).toBeInTheDocument();
81
+ expect(screen.getByText("rule-3")).toBeInTheDocument();
82
+ });
83
+ it("displays status as Active for Enabled rules", ()=>{
84
+ renderBucketLifecycleList({
85
+ lifecycleRules: mockLifecycleRules
86
+ });
87
+ const activeStatuses = screen.getAllByText("Active");
88
+ expect(activeStatuses.length).toBe(2);
89
+ });
90
+ it("displays status as Inactive for Disabled rules", ()=>{
91
+ renderBucketLifecycleList({
92
+ lifecycleRules: mockLifecycleRules
93
+ });
94
+ expect(screen.getByText("Inactive")).toBeInTheDocument();
95
+ });
96
+ it("displays single action correctly", ()=>{
97
+ const singleActionRule = [
98
+ {
99
+ ID: "single-action-rule",
100
+ Status: "Enabled",
101
+ Expiration: {
102
+ Days: 30
103
+ }
104
+ }
105
+ ];
106
+ renderBucketLifecycleList({
107
+ lifecycleRules: singleActionRule
108
+ });
109
+ expect(screen.getByText(/Expire current \(30 days\)/)).toBeInTheDocument();
110
+ });
111
+ it("displays multiple actions with +N more indicator", ()=>{
112
+ renderBucketLifecycleList({
113
+ lifecycleRules: mockLifecycleRules
114
+ });
115
+ expect(screen.getByText(/\+2/)).toBeInTheDocument();
116
+ });
117
+ it("pluralizes days correctly - singular", ()=>{
118
+ renderBucketLifecycleList({
119
+ lifecycleRules: [
120
+ mockRuleWithSingleDay
121
+ ]
122
+ });
123
+ expect(screen.getByText(/1 day/)).toBeInTheDocument();
124
+ });
125
+ it("pluralizes days correctly - plural", ()=>{
126
+ renderBucketLifecycleList({
127
+ lifecycleRules: mockLifecycleRules
128
+ });
129
+ expect(screen.getByText(/30 days/)).toBeInTheDocument();
130
+ });
131
+ it("displays Abort Incomplete MPU action", ()=>{
132
+ const mpuRule = [
133
+ {
134
+ ID: "mpu-rule",
135
+ Status: "Enabled",
136
+ AbortIncompleteMultipartUpload: {
137
+ DaysAfterInitiation: 7
138
+ }
139
+ }
140
+ ];
141
+ renderBucketLifecycleList({
142
+ lifecycleRules: mpuRule
143
+ });
144
+ expect(screen.getByText(/Abort Incomplete MPU \(7 days\)/)).toBeInTheDocument();
145
+ });
146
+ it("displays dates in formatted format", ()=>{
147
+ renderBucketLifecycleList({
148
+ lifecycleRules: [
149
+ mockRuleWithDate
150
+ ]
151
+ });
152
+ const gridElement = screen.getByRole("grid");
153
+ expect(gridElement).toHaveTextContent("2025");
154
+ });
155
+ it("handles edit button click", ()=>{
156
+ const onEditRule = jest.fn();
157
+ renderBucketLifecycleList({
158
+ lifecycleRules: mockLifecycleRules,
159
+ onEditRule
160
+ });
161
+ const editButtons = screen.getAllByRole("button", {
162
+ name: /edit rule/i
163
+ });
164
+ expect(editButtons.length).toBeGreaterThanOrEqual(3);
165
+ fireEvent.click(editButtons[0]);
166
+ expect(onEditRule).toHaveBeenCalled();
167
+ expect(onEditRule).toHaveBeenCalledWith(expect.any(String));
168
+ });
169
+ it("renders delete buttons for each rule", ()=>{
170
+ renderBucketLifecycleList({
171
+ lifecycleRules: mockLifecycleRules
172
+ });
173
+ const deleteButtons = screen.getAllByRole("button", {
174
+ name: /delete rule/i
175
+ });
176
+ expect(deleteButtons.length).toBeGreaterThanOrEqual(3);
177
+ });
178
+ it("handles empty lifecycle rules list", ()=>{
179
+ renderBucketLifecycleList({
180
+ lifecycleRules: []
181
+ });
182
+ expect(screen.getByRole("grid")).toBeInTheDocument();
183
+ expect(screen.getByText("Rule ID")).toBeInTheDocument();
184
+ });
185
+ it("handles lifecycle rules without IDs", ()=>{
186
+ const rulesWithoutIds = [
187
+ {
188
+ ID: void 0,
189
+ Status: "Enabled"
190
+ },
191
+ {
192
+ Status: "Disabled"
193
+ }
194
+ ];
195
+ renderBucketLifecycleList({
196
+ lifecycleRules: rulesWithoutIds
197
+ });
198
+ const dashes = screen.getAllByText("-");
199
+ expect(dashes.length).toBeGreaterThan(0);
200
+ });
201
+ it("shows loading state when lifecycleStatus is loading", ()=>{
202
+ renderBucketLifecycleList({
203
+ lifecycleRules: [],
204
+ lifecycleStatus: "loading"
205
+ });
206
+ expect(screen.getByRole("grid")).toBeInTheDocument();
207
+ expect(screen.queryByText("rule-1")).not.toBeInTheDocument();
208
+ });
209
+ it("shows error state when lifecycleStatus is error", ()=>{
210
+ renderBucketLifecycleList({
211
+ lifecycleRules: [],
212
+ lifecycleStatus: "error"
213
+ });
214
+ expect(screen.getByRole("grid")).toBeInTheDocument();
215
+ expect(screen.queryByText("rule-1")).not.toBeInTheDocument();
216
+ });
217
+ it("shows data when lifecycleStatus is success", ()=>{
218
+ renderBucketLifecycleList({
219
+ lifecycleRules: mockLifecycleRules,
220
+ lifecycleStatus: "success"
221
+ });
222
+ expect(screen.getByRole("grid")).toBeInTheDocument();
223
+ expect(screen.getByText("rule-1")).toBeInTheDocument();
224
+ expect(screen.getByText("rule-2")).toBeInTheDocument();
225
+ });
226
+ it("works when no callbacks are provided", ()=>{
227
+ renderBucketLifecycleList({
228
+ lifecycleRules: mockLifecycleRules
229
+ });
230
+ expect(()=>{
231
+ const createButton = screen.getByRole("button", {
232
+ name: /create rule/i
233
+ });
234
+ fireEvent.click(createButton);
235
+ const editButtons = screen.getAllByLabelText("Edit rule");
236
+ fireEvent.click(editButtons[0]);
237
+ const deleteButtons = screen.getAllByLabelText("Delete rule");
238
+ fireEvent.click(deleteButtons[0]);
239
+ }).not.toThrow();
240
+ });
241
+ it("renders rows that can be selected", ()=>{
242
+ renderBucketLifecycleList({
243
+ lifecycleRules: mockLifecycleRules
244
+ });
245
+ const rows = screen.getAllByRole("row");
246
+ expect(rows.length).toBeGreaterThan(1);
247
+ const firstDataRow = rows[1];
248
+ expect(firstDataRow).toHaveAttribute("aria-selected");
249
+ });
250
+ it("displays noncurrent version expiration with keep versions", ()=>{
251
+ const ruleWithKeep = [
252
+ {
253
+ ID: "rule-with-keep",
254
+ Status: "Enabled",
255
+ NoncurrentVersionExpiration: {
256
+ NoncurrentDays: 90,
257
+ NewerNoncurrentVersions: 3
258
+ }
259
+ }
260
+ ];
261
+ renderBucketLifecycleList({
262
+ lifecycleRules: ruleWithKeep
263
+ });
264
+ expect(screen.getByText(/90 days, keep 3/)).toBeInTheDocument();
265
+ });
266
+ it("displays transition actions", ()=>{
267
+ const ruleWithTransition = [
268
+ {
269
+ ID: "transition-rule",
270
+ Status: "Enabled",
271
+ Transitions: [
272
+ {
273
+ Days: 60,
274
+ StorageClass: "GLACIER"
275
+ }
276
+ ]
277
+ }
278
+ ];
279
+ renderBucketLifecycleList({
280
+ lifecycleRules: ruleWithTransition
281
+ });
282
+ expect(screen.getByText(/Transition current \(60 days\)/)).toBeInTheDocument();
283
+ });
284
+ it("displays noncurrent version transition actions", ()=>{
285
+ const ruleWithNoncurrentTransition = [
286
+ {
287
+ ID: "noncurrent-transition-rule",
288
+ Status: "Enabled",
289
+ NoncurrentVersionTransitions: [
290
+ {
291
+ NoncurrentDays: 30,
292
+ StorageClass: "GLACIER"
293
+ }
294
+ ]
295
+ }
296
+ ];
297
+ renderBucketLifecycleList({
298
+ lifecycleRules: ruleWithNoncurrentTransition
299
+ });
300
+ expect(screen.getByText(/Transition noncurrent \(30 days\)/)).toBeInTheDocument();
301
+ });
302
+ it("displays expired object delete marker action", ()=>{
303
+ const ruleWithDeleteMarker = [
304
+ {
305
+ ID: "delete-marker-rule",
306
+ Status: "Enabled",
307
+ Expiration: {
308
+ ExpiredObjectDeleteMarker: true
309
+ }
310
+ }
311
+ ];
312
+ renderBucketLifecycleList({
313
+ lifecycleRules: ruleWithDeleteMarker
314
+ });
315
+ expect(screen.getByText("Delete expired markers")).toBeInTheDocument();
316
+ });
317
+ it("shows info icon for multiple actions", ()=>{
318
+ renderBucketLifecycleList({
319
+ lifecycleRules: mockLifecycleRules
320
+ });
321
+ const rows = screen.getAllByRole("row");
322
+ const ruleWithMultipleActions = rows.find((row)=>within(row).queryByText(/\+2/));
323
+ expect(ruleWithMultipleActions).toBeInTheDocument();
324
+ });
325
+ });
@@ -222,4 +222,194 @@ describe("BucketList", ()=>{
222
222
  fireEvent.click(bucketName.closest('[role="row"]') || bucketName);
223
223
  expect(onBucketSelect).not.toHaveBeenCalled();
224
224
  });
225
+ describe("additionalColumns support", ()=>{
226
+ it("renders additional columns when additionalColumns prop is provided", ()=>{
227
+ const additionalColumns = [
228
+ {
229
+ Header: "Metrics",
230
+ accessor: "metrics",
231
+ id: "metrics",
232
+ Cell: ()=>/*#__PURE__*/ jsx("span", {
233
+ children: "Metrics Data"
234
+ })
235
+ }
236
+ ];
237
+ renderBucketList({
238
+ buckets: mockBuckets,
239
+ additionalColumns
240
+ });
241
+ expect(screen.getByText("Metrics")).toBeInTheDocument();
242
+ expect(screen.getAllByText("Metrics Data")).toHaveLength(3);
243
+ });
244
+ it("renders multiple additional columns in correct order", ()=>{
245
+ const additionalColumns = [
246
+ {
247
+ Header: "Column A",
248
+ accessor: "columnA",
249
+ id: "columnA",
250
+ Cell: ()=>/*#__PURE__*/ jsx("span", {
251
+ children: "A"
252
+ })
253
+ },
254
+ {
255
+ Header: "Column B",
256
+ accessor: "columnB",
257
+ id: "columnB",
258
+ Cell: ()=>/*#__PURE__*/ jsx("span", {
259
+ children: "B"
260
+ })
261
+ }
262
+ ];
263
+ renderBucketList({
264
+ buckets: mockBuckets,
265
+ additionalColumns
266
+ });
267
+ const headers = screen.getAllByRole("columnheader");
268
+ const headerTexts = headers.map((h)=>h.textContent);
269
+ expect(headerTexts).toContain("Bucket Name");
270
+ expect(headerTexts).toContain("Storage Location");
271
+ expect(headerTexts).toContain("Column A");
272
+ expect(headerTexts).toContain("Column B");
273
+ expect(headerTexts).toContain("Created on");
274
+ const bucketNameIndex = headerTexts.indexOf("Bucket Name");
275
+ const storageLocationIndex = headerTexts.indexOf("Storage Location");
276
+ const columnAIndex = headerTexts.indexOf("Column A");
277
+ const columnBIndex = headerTexts.indexOf("Column B");
278
+ const createdOnIndex = headerTexts.indexOf("Created on");
279
+ expect(bucketNameIndex).toBeLessThan(storageLocationIndex);
280
+ expect(storageLocationIndex).toBeLessThan(columnAIndex);
281
+ expect(columnAIndex).toBeLessThan(columnBIndex);
282
+ expect(columnBIndex).toBeLessThan(createdOnIndex);
283
+ });
284
+ it("works without additional columns (backward compatibility)", ()=>{
285
+ renderBucketList({
286
+ buckets: mockBuckets
287
+ });
288
+ const headers = screen.getAllByRole("columnheader");
289
+ const headerTexts = headers.map((h)=>h.textContent);
290
+ expect(headerTexts).toContain("Bucket Name");
291
+ expect(headerTexts).toContain("Storage Location");
292
+ expect(headerTexts).toContain("Created on");
293
+ expect(headerTexts).toHaveLength(3);
294
+ });
295
+ });
296
+ describe("transformBucketData support", ()=>{
297
+ it("transforms bucket data when transformBucketData is provided", ()=>{
298
+ const transformBucketData = jest.fn((bucket)=>({
299
+ ...bucket,
300
+ metrics: {
301
+ value: 100
302
+ }
303
+ }));
304
+ const additionalColumns = [
305
+ {
306
+ Header: "Metrics",
307
+ accessor: "metrics",
308
+ id: "metrics",
309
+ Cell: ({ value })=>/*#__PURE__*/ jsxs("span", {
310
+ children: [
311
+ "Value: ",
312
+ value.value
313
+ ]
314
+ })
315
+ }
316
+ ];
317
+ renderBucketList({
318
+ buckets: mockBuckets,
319
+ additionalColumns,
320
+ transformBucketData
321
+ });
322
+ expect(transformBucketData).toHaveBeenCalledTimes(3);
323
+ expect(transformBucketData).toHaveBeenCalledWith(mockBuckets[0]);
324
+ expect(transformBucketData).toHaveBeenCalledWith(mockBuckets[1]);
325
+ expect(transformBucketData).toHaveBeenCalledWith(mockBuckets[2]);
326
+ });
327
+ it("renders transformed data in additional columns", ()=>{
328
+ const transformBucketData = (bucket)=>({
329
+ ...bucket,
330
+ metrics: {
331
+ value: "test-bucket-1" === bucket.Name ? 50 : 100
332
+ }
333
+ });
334
+ const additionalColumns = [
335
+ {
336
+ Header: "Metrics",
337
+ accessor: "metrics",
338
+ id: "metrics",
339
+ Cell: ({ value })=>/*#__PURE__*/ jsxs("span", {
340
+ children: [
341
+ "Metric: ",
342
+ value.value
343
+ ]
344
+ })
345
+ }
346
+ ];
347
+ renderBucketList({
348
+ buckets: mockBuckets,
349
+ additionalColumns,
350
+ transformBucketData
351
+ });
352
+ expect(screen.getByText("Metric: 50")).toBeInTheDocument();
353
+ expect(screen.getAllByText("Metric: 100")).toHaveLength(2);
354
+ });
355
+ it("works without transformBucketData (uses default behavior)", ()=>{
356
+ const additionalColumns = [
357
+ {
358
+ Header: "Test Column",
359
+ accessor: "Name",
360
+ id: "testColumn",
361
+ Cell: ({ value })=>/*#__PURE__*/ jsxs("span", {
362
+ children: [
363
+ "Name: ",
364
+ value
365
+ ]
366
+ })
367
+ }
368
+ ];
369
+ renderBucketList({
370
+ buckets: mockBuckets,
371
+ additionalColumns
372
+ });
373
+ expect(screen.getByText("Name: test-bucket-1")).toBeInTheDocument();
374
+ expect(screen.getByText("Name: test-bucket-2")).toBeInTheDocument();
375
+ expect(screen.getByText("Name: test-bucket-3")).toBeInTheDocument();
376
+ });
377
+ });
378
+ describe("generic type support", ()=>{
379
+ it("maintains type safety with extended bucket types", ()=>{
380
+ const transformBucketData = (bucket)=>({
381
+ ...bucket,
382
+ customField: `Custom ${bucket.Name}`,
383
+ numericField: 42
384
+ });
385
+ const additionalColumns = [
386
+ {
387
+ Header: "Custom",
388
+ accessor: "customField",
389
+ id: "custom",
390
+ Cell: ({ value })=>/*#__PURE__*/ jsx("span", {
391
+ children: value
392
+ })
393
+ },
394
+ {
395
+ Header: "Number",
396
+ accessor: "numericField",
397
+ id: "numeric",
398
+ Cell: ({ value })=>/*#__PURE__*/ jsxs("span", {
399
+ children: [
400
+ "Num: ",
401
+ value
402
+ ]
403
+ })
404
+ }
405
+ ];
406
+ renderBucketList({
407
+ buckets: mockBuckets,
408
+ additionalColumns,
409
+ transformBucketData
410
+ });
411
+ expect(screen.getByText("Custom test-bucket-1")).toBeInTheDocument();
412
+ expect(screen.getAllByText("Num: 42")).toHaveLength(3);
413
+ });
414
+ });
225
415
  });