@scality/data-browser-library 1.0.9 → 1.1.1

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.
@@ -4,6 +4,7 @@ import user_event from "@testing-library/user-event";
4
4
  import { MemoryRouter, Route, Routes } from "react-router";
5
5
  import { useGetBucketReplication, useSetBucketReplication } from "../../hooks/bucketConfiguration.js";
6
6
  import { useBuckets } from "../../hooks/bucketOperations.js";
7
+ import { useFeatures } from "../../hooks/useFeatures.js";
7
8
  import { useISVBucketStatus } from "../../hooks/useISVBucketDetection.js";
8
9
  import { createTestWrapper, findToggleByLabel, mockErrorSubmit, mockOffsetSize, mockSuccessSubmit, submitForm } from "../../test/testUtils.js";
9
10
  import { BucketReplicationFormPage } from "../buckets/BucketReplicationFormPage.js";
@@ -14,12 +15,16 @@ jest.mock('../../hooks/bucketConfiguration', ()=>({
14
15
  jest.mock('../../hooks/bucketOperations', ()=>({
15
16
  useBuckets: jest.fn()
16
17
  }));
18
+ jest.mock('../../hooks/useFeatures', ()=>({
19
+ useFeatures: jest.fn()
20
+ }));
17
21
  jest.mock('../../hooks/useISVBucketDetection', ()=>({
18
22
  useISVBucketStatus: jest.fn()
19
23
  }));
20
24
  const mockUseGetBucketReplication = jest.mocked(useGetBucketReplication);
21
25
  const mockUseSetBucketReplication = jest.mocked(useSetBucketReplication);
22
26
  const mockUseBuckets = jest.mocked(useBuckets);
27
+ const mockUseFeatures = jest.mocked(useFeatures);
23
28
  const mockUseISVBucketStatus = jest.mocked(useISVBucketStatus);
24
29
  const mockNavigate = jest.fn();
25
30
  const mockShowToast = jest.fn();
@@ -107,6 +112,7 @@ describe('BucketReplicationFormPage', ()=>{
107
112
  },
108
113
  status: 'success'
109
114
  });
115
+ mockUseFeatures.mockReturnValue(true);
110
116
  mockUseISVBucketStatus.mockReturnValue({
111
117
  isVeeamBucket: false,
112
118
  isCommvaultBucket: false,
@@ -1946,4 +1952,31 @@ describe('BucketReplicationFormPage', ()=>{
1946
1952
  expect(screen.getByText(/bucket used for external integration with Kasten/i)).toBeInTheDocument();
1947
1953
  });
1948
1954
  });
1955
+ describe('Feature Flag - replicationAdvanced', ()=>{
1956
+ it('hides Additional Options section when replicationAdvanced is disabled', ()=>{
1957
+ mockUseFeatures.mockReturnValue(false);
1958
+ renderBucketReplicationFormPage();
1959
+ expect(screen.queryByText('Replication Time Control (RTC)')).not.toBeInTheDocument();
1960
+ expect(screen.queryByText('RTC metrics and notifications')).not.toBeInTheDocument();
1961
+ expect(screen.queryByText('Replica modification sync')).not.toBeInTheDocument();
1962
+ expect(screen.queryByText('Delete marker replication')).not.toBeInTheDocument();
1963
+ });
1964
+ it('hides switchObjectOwnership toggle when replicationAdvanced is disabled', async ()=>{
1965
+ mockUseFeatures.mockReturnValue(false);
1966
+ renderBucketReplicationFormPage();
1967
+ const sameAccountToggle = findToggleByLabel('Same account destination');
1968
+ await user_event.click(sameAccountToggle);
1969
+ await waitFor(()=>{
1970
+ expect(screen.queryByText('Switch Object ownership')).not.toBeInTheDocument();
1971
+ });
1972
+ });
1973
+ it('shows Additional Options section when replicationAdvanced is enabled', ()=>{
1974
+ mockUseFeatures.mockReturnValue(true);
1975
+ renderBucketReplicationFormPage();
1976
+ expect(screen.getByText('Replication Time Control (RTC)')).toBeInTheDocument();
1977
+ expect(screen.getByText('RTC metrics and notifications')).toBeInTheDocument();
1978
+ expect(screen.getByText('Replica modification sync')).toBeInTheDocument();
1979
+ expect(screen.getByText('Delete marker replication')).toBeInTheDocument();
1980
+ });
1981
+ });
1949
1982
  });
@@ -1 +1,2 @@
1
+ export declare function DefaultReplicationDestinationFields(): import("react/jsx-runtime").JSX.Element;
1
2
  export declare function BucketReplicationFormPage(): import("react/jsx-runtime").JSX.Element;
@@ -5,13 +5,14 @@ import { convertRemToPixels } from "@scality/core-ui/dist/components/tablev2/Tab
5
5
  import { Box, Button, Input, Select } from "@scality/core-ui/dist/next";
6
6
  import joi from "joi";
7
7
  import { useCallback, useEffect, useMemo, useRef } from "react";
8
- import { Controller, FormProvider, useFieldArray, useForm } from "react-hook-form";
8
+ import { Controller, FormProvider, useFieldArray, useForm, useFormContext } from "react-hook-form";
9
9
  import { useParams } from "react-router";
10
10
  import { useDataBrowserUICustomization } from "../../contexts/DataBrowserUICustomizationContext.js";
11
11
  import { useGetBucketReplication, useSetBucketReplication } from "../../hooks/bucketConfiguration.js";
12
12
  import { useBuckets } from "../../hooks/bucketOperations.js";
13
- import { useISVBucketStatus } from "../../hooks/useISVBucketDetection.js";
14
13
  import { useDataBrowserNavigate } from "../../hooks/useDataBrowserNavigate.js";
14
+ import { useFeatures } from "../../hooks/useFeatures.js";
15
+ import { useISVBucketStatus } from "../../hooks/useISVBucketDetection.js";
15
16
  import { AWS_RULE_LIMITS, STATUS_OPTIONS, buildS3Filter } from "../../utils/s3RuleUtils.js";
16
17
  import { FilterFormSection, createFilterValidationSchema } from "../ui/FilterFormSection.js";
17
18
  const storageClassOptions = [
@@ -44,7 +45,7 @@ const storageClassOptions = [
44
45
  label: 'Glacier Instant Retrieval'
45
46
  }
46
47
  ];
47
- const createSchema = (hasExistingRules)=>joi.object({
48
+ const createSchema = (hasExistingRules, hasCustomDestination)=>joi.object({
48
49
  role: hasExistingRules ? joi.string().optional() : joi.string().required().messages({
49
50
  'string.empty': 'Role ARN is required for first replication rule'
50
51
  }),
@@ -61,10 +62,10 @@ const createSchema = (hasExistingRules)=>joi.object({
61
62
  includeEncryptedObjects: joi.boolean(),
62
63
  replicaModifications: joi.boolean(),
63
64
  sameAccount: joi.boolean(),
64
- targetBucket: joi.string().required().messages({
65
+ targetBucket: hasCustomDestination ? joi.string().allow('').optional() : joi.string().required().messages({
65
66
  'string.empty': 'Target bucket is required'
66
67
  }),
67
- targetAccountId: joi.when('sameAccount', {
68
+ targetAccountId: hasCustomDestination ? joi.string().allow('').optional() : joi.when('sameAccount', {
68
69
  is: false,
69
70
  then: joi.string().required().messages({
70
71
  'string.empty': 'Target account ID is required for cross-account replication'
@@ -185,8 +186,10 @@ const ToggleFormField = ({ name, label, id, control, labelHelpTooltip })=>/*#__P
185
186
  })
186
187
  })
187
188
  });
188
- const buildDestination = (data)=>({
189
- Bucket: `arn:aws:s3:::${data.targetBucket}`,
189
+ const buildDestination = (data, sourceBucketName)=>{
190
+ const bucketName = data.targetBucket || sourceBucketName;
191
+ return {
192
+ Bucket: `arn:aws:s3:::${bucketName}`,
190
193
  ...!data.sameAccount && data.targetAccountId && '' !== data.targetAccountId.trim() && {
191
194
  Account: data.targetAccountId
192
195
  },
@@ -219,7 +222,8 @@ const buildDestination = (data)=>({
219
222
  Owner: 'Destination'
220
223
  }
221
224
  }
222
- });
225
+ };
226
+ };
223
227
  const buildSourceSelectionCriteria = (data)=>{
224
228
  if (data.includeEncryptedObjects || data.replicaModifications) return {
225
229
  ...data.includeEncryptedObjects && {
@@ -234,12 +238,12 @@ const buildSourceSelectionCriteria = (data)=>{
234
238
  }
235
239
  };
236
240
  };
237
- const buildReplicationRule = (data)=>{
241
+ const buildReplicationRule = (data, sourceBucketName)=>{
238
242
  const rule = {
239
243
  ID: data.ruleId,
240
244
  Status: data.status,
241
245
  Priority: data.priority ?? 0,
242
- Destination: buildDestination(data)
246
+ Destination: buildDestination(data, sourceBucketName)
243
247
  };
244
248
  const filter = buildS3Filter(data);
245
249
  if (filter) rule.Filter = filter;
@@ -250,18 +254,95 @@ const buildReplicationRule = (data)=>{
250
254
  };
251
255
  return rule;
252
256
  };
257
+ function DefaultReplicationDestinationFields() {
258
+ const { watch, register, control, formState: { errors } } = useFormContext();
259
+ const { bucketName } = useParams();
260
+ const { data: bucketsData } = useBuckets();
261
+ const availableBuckets = bucketsData?.Buckets || [];
262
+ const sameAccount = watch('sameAccount');
263
+ const showAdvancedReplication = useFeatures('replicationAdvanced');
264
+ return /*#__PURE__*/ jsxs(Fragment, {
265
+ children: [
266
+ /*#__PURE__*/ jsx(FormGroup, {
267
+ label: "Destination",
268
+ id: "sameAccount",
269
+ direction: "horizontal",
270
+ labelHelpTooltip: "Toggle between same-account and cross-account replication destination",
271
+ content: /*#__PURE__*/ jsx(Controller, {
272
+ name: "sameAccount",
273
+ control: control,
274
+ render: ({ field })=>/*#__PURE__*/ jsx(Toggle, {
275
+ toggle: field.value,
276
+ onChange: field.onChange,
277
+ label: field.value ? 'Same account destination' : 'Not same account destination'
278
+ })
279
+ })
280
+ }),
281
+ sameAccount ? /*#__PURE__*/ jsx(Fragment, {}) : /*#__PURE__*/ jsx("div", {
282
+ style: {
283
+ paddingLeft: spacing.r24
284
+ },
285
+ children: /*#__PURE__*/ jsx(FormGroup, {
286
+ label: "Target account ID",
287
+ id: "targetAccountId",
288
+ direction: "horizontal",
289
+ error: errors?.targetAccountId?.message,
290
+ helpErrorPosition: "bottom",
291
+ required: true,
292
+ content: /*#__PURE__*/ jsx(Input, {
293
+ id: "targetAccountId",
294
+ placeholder: "Account ID",
295
+ ...register('targetAccountId')
296
+ })
297
+ })
298
+ }),
299
+ /*#__PURE__*/ jsx(FormGroup, {
300
+ label: "Target Bucket",
301
+ id: "targetBucket",
302
+ direction: "horizontal",
303
+ error: errors?.targetBucket?.message,
304
+ helpErrorPosition: "bottom",
305
+ required: true,
306
+ content: sameAccount ? /*#__PURE__*/ jsx(Controller, {
307
+ name: "targetBucket",
308
+ control: control,
309
+ render: ({ field })=>/*#__PURE__*/ jsx(Select, {
310
+ id: "targetBucket",
311
+ value: field.value,
312
+ onChange: field.onChange,
313
+ placeholder: "Select a bucket",
314
+ children: availableBuckets.filter((bucket)=>bucket.Name !== bucketName).map((bucket)=>/*#__PURE__*/ jsx(Select.Option, {
315
+ value: bucket.Name || '',
316
+ children: bucket.Name
317
+ }, bucket.Name))
318
+ })
319
+ }) : /*#__PURE__*/ jsx(Input, {
320
+ id: "targetBucket",
321
+ placeholder: "Bucket name",
322
+ ...register('targetBucket')
323
+ })
324
+ }),
325
+ showAdvancedReplication && !sameAccount ? /*#__PURE__*/ jsx(ToggleFormField, {
326
+ name: "switchObjectOwnership",
327
+ label: "Switch Object ownership",
328
+ id: "switchObjectOwnership",
329
+ control: control,
330
+ labelHelpTooltip: "Change replica ownership to destination bucket owner (AccessControlTranslation)"
331
+ }) : /*#__PURE__*/ jsx(Fragment, {})
332
+ ]
333
+ });
334
+ }
253
335
  function BucketReplicationFormPage() {
254
- const { storageClassSelector: StorageClassSelector, storageClassLabel } = useDataBrowserUICustomization();
336
+ const { storageClassSelector: StorageClassSelector, storageClassLabel, replicationRoleDefault, replicationDestinationFields: CustomDestinationFields } = useDataBrowserUICustomization();
255
337
  const { bucketName, ruleId } = useParams();
256
338
  const navigate = useDataBrowserNavigate();
257
339
  const { showToast } = useToast();
258
340
  const isEditMode = !!ruleId;
341
+ const showAdvancedReplication = useFeatures('replicationAdvanced');
259
342
  const { isISVManaged, isvApplication, isLoading: isISVLoading } = useISVBucketStatus(bucketName);
260
343
  const { data: replicationData, status: replicationStatus } = useGetBucketReplication({
261
344
  Bucket: bucketName
262
345
  });
263
- const { data: bucketsData } = useBuckets();
264
- const availableBuckets = bucketsData?.Buckets || [];
265
346
  const existingRole = replicationData?.ReplicationConfiguration?.Role || '';
266
347
  const hasExistingRules = (replicationData?.ReplicationConfiguration?.Rules || []).length > 0;
267
348
  const existingRule = useMemo(()=>{
@@ -286,20 +367,21 @@ function BucketReplicationFormPage() {
286
367
  }, [
287
368
  replicationData
288
369
  ]);
289
- const dynamicSchema = useMemo(()=>createSchema(hasExistingRules).keys({
370
+ const dynamicSchema = useMemo(()=>createSchema(hasExistingRules, !!CustomDestinationFields).keys({
290
371
  ruleId: joi.string().required().invalid(...existingRuleIds).messages({
291
372
  'string.empty': 'Rule ID is required',
292
373
  'any.invalid': 'A rule with this ID already exists'
293
374
  })
294
375
  }), [
295
376
  existingRuleIds,
296
- hasExistingRules
377
+ hasExistingRules,
378
+ CustomDestinationFields
297
379
  ]);
298
380
  const methods = useForm({
299
381
  resolver: joiResolver(dynamicSchema),
300
382
  mode: 'onChange',
301
383
  defaultValues: {
302
- role: '',
384
+ role: replicationRoleDefault || '',
303
385
  ruleId: '',
304
386
  status: 'Enabled',
305
387
  priority: null,
@@ -327,11 +409,35 @@ function BucketReplicationFormPage() {
327
409
  name: 'tags'
328
410
  });
329
411
  const filterType = watch('filterType');
330
- const sameAccount = watch('sameAccount');
331
412
  const encryptReplicatedObjects = watch('encryptReplicatedObjects');
332
413
  const includeEncryptedObjects = watch('includeEncryptedObjects');
333
414
  const deleteMarkerReplication = watch('deleteMarkerReplication');
334
415
  const enforceRTC = watch('enforceRTC');
416
+ const storageClassField = /*#__PURE__*/ jsx(FormGroup, {
417
+ label: storageClassLabel || 'Storage Class',
418
+ id: "storageClass",
419
+ direction: "horizontal",
420
+ error: errors?.storageClass?.message,
421
+ helpErrorPosition: "bottom",
422
+ labelHelpTooltip: StorageClassSelector ? void 0 : 'Storage class for replicated objects in destination bucket',
423
+ content: /*#__PURE__*/ jsx(Controller, {
424
+ name: "storageClass",
425
+ control: control,
426
+ render: ({ field })=>StorageClassSelector ? /*#__PURE__*/ jsx(StorageClassSelector, {
427
+ value: field.value,
428
+ onChange: field.onChange,
429
+ context: "replication"
430
+ }) : /*#__PURE__*/ jsx(Select, {
431
+ id: "storageClass",
432
+ value: field.value,
433
+ onChange: field.onChange,
434
+ children: storageClassOptions.map((option)=>/*#__PURE__*/ jsx(Select.Option, {
435
+ value: option.value,
436
+ children: option.label
437
+ }, option.value))
438
+ })
439
+ })
440
+ });
335
441
  const { mutate: setReplication, isPending: isSaving } = useSetBucketReplication();
336
442
  const loadedRuleIdRef = useRef(null);
337
443
  useEffect(()=>{
@@ -344,7 +450,7 @@ function BucketReplicationFormPage() {
344
450
  } else if (!isEditMode && !isDirty) {
345
451
  reset((prev)=>({
346
452
  ...prev,
347
- role: existingRole,
453
+ role: existingRole || replicationRoleDefault || '',
348
454
  priority: nextAvailablePriority
349
455
  }));
350
456
  loadedRuleIdRef.current = null;
@@ -353,6 +459,7 @@ function BucketReplicationFormPage() {
353
459
  isEditMode,
354
460
  existingRule,
355
461
  existingRole,
462
+ replicationRoleDefault,
356
463
  nextAvailablePriority,
357
464
  isDirty,
358
465
  reset
@@ -425,7 +532,7 @@ function BucketReplicationFormPage() {
425
532
  ]);
426
533
  const onSubmit = useCallback((data)=>{
427
534
  if (!bucketName) return;
428
- const rule = buildReplicationRule(data);
535
+ const rule = buildReplicationRule(data, bucketName);
429
536
  const existingRules = replicationData?.ReplicationConfiguration?.Rules || [];
430
537
  const updatedRules = isEditMode ? existingRules.map((r)=>r.ID === existingRule?.ID ? rule : r) : [
431
538
  ...existingRules,
@@ -553,7 +660,7 @@ function BucketReplicationFormPage() {
553
660
  direction: "horizontal",
554
661
  error: errors?.role?.message,
555
662
  helpErrorPosition: "bottom",
556
- required: !hasExistingRules,
663
+ required: true,
557
664
  content: hasExistingRules ? /*#__PURE__*/ jsxs(Stack, {
558
665
  direction: "vertical",
559
666
  gap: "r8",
@@ -562,6 +669,7 @@ function BucketReplicationFormPage() {
562
669
  children: existingRole || 'Not set'
563
670
  }),
564
671
  /*#__PURE__*/ jsx(Text, {
672
+ variant: "Small",
565
673
  color: "textSecondary",
566
674
  children: "To change the Role, edit it through bucket configuration or delete all rules first"
567
675
  })
@@ -648,104 +756,22 @@ function BucketReplicationFormPage() {
648
756
  removeTag: removeTag,
649
757
  errors: errors
650
758
  }),
651
- /*#__PURE__*/ jsxs(FormSection, {
759
+ /*#__PURE__*/ jsx(FormSection, {
652
760
  title: {
653
761
  name: 'Destination'
654
762
  },
655
763
  forceLabelWidth: convertRemToPixels(15),
656
- children: [
657
- /*#__PURE__*/ jsx(FormGroup, {
658
- label: "Destination",
659
- id: "sameAccount",
660
- direction: "horizontal",
661
- labelHelpTooltip: "Toggle between same-account and cross-account replication destination",
662
- content: /*#__PURE__*/ jsx(Controller, {
663
- name: "sameAccount",
664
- control: control,
665
- render: ({ field })=>/*#__PURE__*/ jsx(Toggle, {
666
- toggle: field.value,
667
- onChange: field.onChange,
668
- label: field.value ? 'Same account destination' : 'Not same account destination'
669
- })
670
- })
671
- }),
672
- sameAccount ? /*#__PURE__*/ jsx(Fragment, {}) : /*#__PURE__*/ jsx("div", {
673
- style: {
674
- paddingLeft: spacing.r24
675
- },
676
- children: /*#__PURE__*/ jsx(FormGroup, {
677
- label: "Target account ID",
678
- id: "targetAccountId",
679
- direction: "horizontal",
680
- error: errors?.targetAccountId?.message,
681
- helpErrorPosition: "bottom",
682
- required: true,
683
- content: /*#__PURE__*/ jsx(Input, {
684
- id: "targetAccountId",
685
- placeholder: "Account ID",
686
- ...register('targetAccountId')
687
- })
688
- })
689
- }),
690
- /*#__PURE__*/ jsx(FormGroup, {
691
- label: "Target Bucket",
692
- id: "targetBucket",
693
- direction: "horizontal",
694
- error: errors?.targetBucket?.message,
695
- helpErrorPosition: "bottom",
696
- required: true,
697
- content: sameAccount ? /*#__PURE__*/ jsx(Controller, {
698
- name: "targetBucket",
699
- control: control,
700
- render: ({ field })=>/*#__PURE__*/ jsx(Select, {
701
- id: "targetBucket",
702
- value: field.value,
703
- onChange: field.onChange,
704
- placeholder: "Select a bucket",
705
- children: availableBuckets.filter((bucket)=>bucket.Name !== bucketName).map((bucket)=>/*#__PURE__*/ jsx(Select.Option, {
706
- value: bucket.Name || '',
707
- children: bucket.Name
708
- }, bucket.Name))
709
- })
710
- }) : /*#__PURE__*/ jsx(Input, {
711
- id: "targetBucket",
712
- placeholder: "Bucket name",
713
- ...register('targetBucket')
714
- })
715
- }),
716
- /*#__PURE__*/ jsx(FormGroup, {
717
- label: storageClassLabel || 'Storage Class',
718
- id: "storageClass",
719
- direction: "horizontal",
720
- error: errors?.storageClass?.message,
721
- helpErrorPosition: "bottom",
722
- labelHelpTooltip: StorageClassSelector ? void 0 : 'Storage class for replicated objects in destination bucket',
723
- content: /*#__PURE__*/ jsx(Controller, {
724
- name: "storageClass",
725
- control: control,
726
- render: ({ field })=>StorageClassSelector ? /*#__PURE__*/ jsx(StorageClassSelector, {
727
- value: field.value,
728
- onChange: field.onChange,
729
- context: "replication"
730
- }) : /*#__PURE__*/ jsx(Select, {
731
- id: "storageClass",
732
- value: field.value,
733
- onChange: field.onChange,
734
- children: storageClassOptions.map((option)=>/*#__PURE__*/ jsx(Select.Option, {
735
- value: option.value,
736
- children: option.label
737
- }, option.value))
738
- })
739
- })
740
- }),
741
- sameAccount ? /*#__PURE__*/ jsx(Fragment, {}) : /*#__PURE__*/ jsx(ToggleFormField, {
742
- name: "switchObjectOwnership",
743
- label: "Switch Object ownership",
744
- id: "switchObjectOwnership",
745
- control: control,
746
- labelHelpTooltip: "Change replica ownership to destination bucket owner (AccessControlTranslation)"
747
- })
748
- ]
764
+ children: CustomDestinationFields ? /*#__PURE__*/ jsxs(Fragment, {
765
+ children: [
766
+ storageClassField,
767
+ /*#__PURE__*/ jsx(CustomDestinationFields, {})
768
+ ]
769
+ }) : /*#__PURE__*/ jsxs(Fragment, {
770
+ children: [
771
+ /*#__PURE__*/ jsx(DefaultReplicationDestinationFields, {}),
772
+ storageClassField
773
+ ]
774
+ })
749
775
  }),
750
776
  /*#__PURE__*/ jsxs(FormSection, {
751
777
  title: {
@@ -811,7 +837,7 @@ function BucketReplicationFormPage() {
811
837
  }) : /*#__PURE__*/ jsx(Fragment, {})
812
838
  ]
813
839
  }),
814
- /*#__PURE__*/ jsxs(FormSection, {
840
+ showAdvancedReplication && /*#__PURE__*/ jsxs(FormSection, {
815
841
  title: {
816
842
  name: 'Additional Options'
817
843
  },
@@ -894,4 +920,4 @@ function BucketReplicationFormPage() {
894
920
  })
895
921
  });
896
922
  }
897
- export { BucketReplicationFormPage };
923
+ export { BucketReplicationFormPage, DefaultReplicationDestinationFields };
@@ -8,7 +8,7 @@ export { BucketList } from './buckets/BucketList';
8
8
  export { BucketOverview, BucketOverviewField, BucketOverviewSection, useBucketOverviewContext, } from './buckets/BucketOverview';
9
9
  export { BucketPage } from './buckets/BucketPage';
10
10
  export { BucketPolicyPage } from './buckets/BucketPolicyPage';
11
- export { BucketReplicationFormPage } from './buckets/BucketReplicationFormPage';
11
+ export { BucketReplicationFormPage, DefaultReplicationDestinationFields, } from './buckets/BucketReplicationFormPage';
12
12
  export { BucketVersioning } from './buckets/BucketVersioning';
13
13
  export { DeleteBucketButton } from './buckets/DeleteBucketButton';
14
14
  export { EmptyBucketButton } from './buckets/EmptyBucketButton';
@@ -8,7 +8,7 @@ import { BucketList } from "./buckets/BucketList.js";
8
8
  import { BucketOverview, BucketOverviewField, BucketOverviewSection, useBucketOverviewContext } from "./buckets/BucketOverview.js";
9
9
  import { BucketPage } from "./buckets/BucketPage.js";
10
10
  import { BucketPolicyPage } from "./buckets/BucketPolicyPage.js";
11
- import { BucketReplicationFormPage } from "./buckets/BucketReplicationFormPage.js";
11
+ import { BucketReplicationFormPage, DefaultReplicationDestinationFields } from "./buckets/BucketReplicationFormPage.js";
12
12
  import { BucketVersioning } from "./buckets/BucketVersioning.js";
13
13
  import { DeleteBucketButton } from "./buckets/DeleteBucketButton.js";
14
14
  import { EmptyBucketButton } from "./buckets/EmptyBucketButton.js";
@@ -25,4 +25,4 @@ import { DataBrowserProvider, useDataBrowserConfig, useDataBrowserContext, useIn
25
25
  import { QueryProvider } from "./providers/QueryProvider.js";
26
26
  import { MetadataSearch } from "./search/MetadataSearch.js";
27
27
  import { ArrayFieldActions } from "./ui/ArrayFieldActions.js";
28
- export { ArrayFieldActions, Breadcrumb, BucketAccessor, BucketCorsPage, BucketCreate, BucketLifecycleFormPage, BucketList, BucketNotificationFormPage, BucketOverview, BucketOverviewField, BucketOverviewSection, BucketPage, BucketPolicyPage, BucketReplicationFormPage, BucketVersioning, CreateFolderButton, DataBrowserBreadcrumb, DataBrowserProvider, DataBrowserUI, DeleteBucketButton, EditRetentionButton, EmptyBucketButton, MetadataSearch, ObjectDetails, ObjectList, ObjectLockSettings, ObjectPage, QueryProvider, UploadButton, baseBucketCreateSchema, bucketErrorMessage, bucketNameValidationSchema, useBreadcrumbPaths, useBucketOverviewContext, useDataBrowserConfig, useDataBrowserContext, useDataBrowserNavigate, useInvalidateQueries };
28
+ export { ArrayFieldActions, Breadcrumb, BucketAccessor, BucketCorsPage, BucketCreate, BucketLifecycleFormPage, BucketList, BucketNotificationFormPage, BucketOverview, BucketOverviewField, BucketOverviewSection, BucketPage, BucketPolicyPage, BucketReplicationFormPage, BucketVersioning, CreateFolderButton, DataBrowserBreadcrumb, DataBrowserProvider, DataBrowserUI, DefaultReplicationDestinationFields, DeleteBucketButton, EditRetentionButton, EmptyBucketButton, MetadataSearch, ObjectDetails, ObjectList, ObjectLockSettings, ObjectPage, QueryProvider, UploadButton, baseBucketCreateSchema, bucketErrorMessage, bucketNameValidationSchema, useBreadcrumbPaths, useBucketOverviewContext, useDataBrowserConfig, useDataBrowserContext, useDataBrowserNavigate, useInvalidateQueries };
@@ -262,6 +262,17 @@ export interface DataBrowserUIProps {
262
262
  * - "legalHold" - Legal Hold field
263
263
  */
264
264
  extraObjectSummaryDataProtection?: FieldConfig[];
265
+ /** Pre-fills the Role ARN input field with this value. */
266
+ replicationRoleDefault?: string;
267
+ /**
268
+ * When provided, replaces the default destination fields
269
+ * (sameAccount toggle, targetBucket, targetAccountId, switchObjectOwnership).
270
+ * The StorageClass/Location selector is rendered separately above.
271
+ *
272
+ * Use `DefaultReplicationDestinationFields` (exported from the library)
273
+ * to fall back to default behavior for non-custom cases.
274
+ */
275
+ replicationDestinationFields?: React.ComponentType;
265
276
  }
266
277
  export type S3EventType = 's3:ObjectCreated:*' | 's3:ObjectCreated:Put' | 's3:ObjectCreated:Post' | 's3:ObjectCreated:Copy' | 's3:ObjectCreated:CompleteMultipartUpload' | 's3:ObjectRemoved:*' | 's3:ObjectRemoved:Delete' | 's3:ObjectRemoved:DeleteMarkerCreated' | 's3:ObjectRestore:*' | 's3:ObjectRestore:Post' | 's3:ObjectRestore:Completed' | 's3:ObjectRestore:Delete' | 's3:LifecycleExpiration:*' | 's3:LifecycleExpiration:Delete' | 's3:LifecycleExpiration:DeleteMarkerCreated' | 's3:LifecycleTransition' | 's3:Replication:*' | 's3:Replication:OperationFailedReplication' | 's3:Replication:OperationMissedThreshold' | 's3:Replication:OperationReplicatedAfterThreshold' | 's3:Replication:OperationNotTracked' | 's3:ObjectTagging:*' | 's3:ObjectTagging:Put' | 's3:ObjectTagging:Delete' | 's3:ReducedRedundancyLostObject' | 's3:IntelligentTiering' | 's3:ObjectAcl:Put' | 's3:TestEvent';
267
278
  export type S3EventCategory = 'Object Creation' | 'Object Deletion' | 'Object Restoration' | 'Lifecycle' | 'Replication' | 'Object Tagging' | 'Storage & Access' | 'Testing';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scality/data-browser-library",
3
- "version": "1.0.9",
3
+ "version": "1.1.1",
4
4
  "description": "A modular React component library for browsing S3 buckets and objects",
5
5
  "type": "module",
6
6
  "types": "./dist/index.d.ts",