@scality/data-browser-library 1.0.4 → 1.0.6
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.
- package/dist/components/DataBrowserUI.js +18 -8
- package/dist/components/__tests__/BucketCreate.test.js +60 -20
- package/dist/components/__tests__/BucketList.test.js +91 -6
- package/dist/components/__tests__/BucketNotificationFormPage.test.js +54 -19
- package/dist/components/__tests__/BucketReplicationFormPage.test.js +183 -61
- package/dist/components/__tests__/MetadataSearch.test.js +18 -12
- package/dist/components/__tests__/ObjectList.test.js +94 -2
- package/dist/components/buckets/BucketCreate.d.ts +1 -0
- package/dist/components/buckets/BucketCreate.js +57 -7
- package/dist/components/buckets/BucketDetails.js +0 -1
- package/dist/components/buckets/BucketLifecycleFormPage.js +209 -213
- package/dist/components/buckets/BucketList.js +25 -4
- package/dist/components/buckets/BucketReplicationFormPage.js +9 -3
- package/dist/components/buckets/DeleteBucketConfigRuleButton.js +1 -1
- package/dist/components/buckets/notifications/BucketNotificationList.js +1 -1
- package/dist/components/objects/DeleteObjectButton.d.ts +1 -0
- package/dist/components/objects/DeleteObjectButton.js +11 -5
- package/dist/components/objects/ObjectDetails/FormComponents.d.ts +9 -0
- package/dist/components/objects/ObjectDetails/FormComponents.js +37 -0
- package/dist/components/objects/ObjectDetails/ObjectMetadata.js +182 -204
- package/dist/components/objects/ObjectDetails/ObjectSummary.js +22 -5
- package/dist/components/objects/ObjectDetails/ObjectTags.js +109 -154
- package/dist/components/objects/ObjectDetails/__tests__/ObjectDetails.test.js +3 -3
- package/dist/components/objects/ObjectDetails/__tests__/ObjectMetadata.test.d.ts +1 -0
- package/dist/components/objects/ObjectDetails/__tests__/ObjectMetadata.test.js +230 -0
- package/dist/components/objects/ObjectDetails/__tests__/ObjectTags.test.d.ts +1 -0
- package/dist/components/objects/ObjectDetails/__tests__/ObjectTags.test.js +342 -0
- package/dist/components/objects/ObjectDetails/__tests__/formUtils.test.d.ts +1 -0
- package/dist/components/objects/ObjectDetails/__tests__/formUtils.test.js +202 -0
- package/dist/components/objects/ObjectDetails/index.d.ts +2 -1
- package/dist/components/objects/ObjectDetails/index.js +12 -16
- package/dist/components/objects/ObjectList.d.ts +3 -2
- package/dist/components/objects/ObjectList.js +204 -104
- package/dist/components/objects/ObjectLock/__tests__/ObjectLockSettings.test.js +78 -26
- package/dist/components/objects/ObjectPage.js +22 -5
- package/dist/components/ui/ArrayFieldActions.js +0 -2
- package/dist/components/ui/FilterFormSection.js +17 -36
- package/dist/components/ui/FormGrid.d.ts +7 -0
- package/dist/components/ui/FormGrid.js +37 -0
- package/dist/components/ui/Table.elements.js +1 -0
- package/dist/config/__tests__/factory.test.js +29 -1
- package/dist/config/factory.d.ts +2 -0
- package/dist/config/factory.js +3 -1
- package/dist/config/types.d.ts +45 -2
- package/dist/hooks/__tests__/usePresigningS3Client.test.d.ts +1 -0
- package/dist/hooks/__tests__/usePresigningS3Client.test.js +104 -0
- package/dist/hooks/factories/index.d.ts +1 -1
- package/dist/hooks/factories/index.js +2 -2
- package/dist/hooks/factories/useCreateS3MutationHook.d.ts +2 -1
- package/dist/hooks/factories/useCreateS3MutationHook.js +10 -3
- package/dist/hooks/factories/useCreateS3QueryHook.d.ts +1 -0
- package/dist/hooks/factories/useCreateS3QueryHook.js +9 -6
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.js +2 -1
- package/dist/hooks/presignedOperations.js +4 -4
- package/dist/hooks/useBucketLocations.d.ts +6 -0
- package/dist/hooks/useBucketLocations.js +45 -0
- package/dist/hooks/usePresigningS3Client.d.ts +13 -0
- package/dist/hooks/usePresigningS3Client.js +21 -0
- package/package.json +4 -3
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { fireEvent, render, renderHook, screen, waitFor } from "@testing-library/react";
|
|
2
|
+
import { act, fireEvent, render, renderHook, screen, waitFor } from "@testing-library/react";
|
|
3
3
|
import { MemoryRouter } from "react-router";
|
|
4
4
|
import { DataBrowserUICustomizationProvider } from "../../contexts/DataBrowserUICustomizationContext.js";
|
|
5
5
|
import { useListObjectVersions, useListObjects } from "../../hooks/index.js";
|
|
@@ -281,14 +281,42 @@ describe('ObjectList', ()=>{
|
|
|
281
281
|
expect(screen.getByText('Version ID')).toBeInTheDocument();
|
|
282
282
|
});
|
|
283
283
|
});
|
|
284
|
+
it('notifies parent to clear selection when toggling versions', async ()=>{
|
|
285
|
+
const onObjectSelect = jest.fn();
|
|
286
|
+
renderObjectList({
|
|
287
|
+
onObjectSelect
|
|
288
|
+
});
|
|
289
|
+
await waitFor(()=>{
|
|
290
|
+
expect(screen.getByText('file1.txt')).toBeInTheDocument();
|
|
291
|
+
});
|
|
292
|
+
const rows = screen.getAllByRole('row');
|
|
293
|
+
const objectRow = rows.find((row)=>row.textContent?.includes('file1.txt'));
|
|
294
|
+
expect(objectRow).toBeDefined();
|
|
295
|
+
fireEvent.click(objectRow);
|
|
296
|
+
expect(onObjectSelect).toHaveBeenCalledWith(expect.objectContaining({
|
|
297
|
+
Key: 'file1.txt',
|
|
298
|
+
type: 'object'
|
|
299
|
+
}));
|
|
300
|
+
onObjectSelect.mockClear();
|
|
301
|
+
const toggleLabel = screen.getByText(/list versions/i);
|
|
302
|
+
const toggle = toggleLabel.closest('label')?.querySelector('input[type="checkbox"]');
|
|
303
|
+
fireEvent.click(toggle);
|
|
304
|
+
await waitFor(()=>{
|
|
305
|
+
expect(screen.getByText('Version ID')).toBeInTheDocument();
|
|
306
|
+
});
|
|
307
|
+
expect(onObjectSelect).toHaveBeenCalledWith(null);
|
|
308
|
+
});
|
|
284
309
|
});
|
|
285
310
|
describe('Search Features', ()=>{
|
|
286
|
-
it('renders
|
|
311
|
+
it('renders SearchInput when metadata-search feature is disabled', async ()=>{
|
|
287
312
|
jest.spyOn(__rspack_external__hooks_useFeatures_js_a6a84786, 'useFeatures').mockReturnValue(false);
|
|
288
313
|
renderObjectList();
|
|
289
314
|
await waitFor(()=>{
|
|
290
315
|
expect(screen.getByRole('grid')).toBeInTheDocument();
|
|
291
316
|
});
|
|
317
|
+
expect(screen.getByRole('searchbox', {
|
|
318
|
+
name: 'search'
|
|
319
|
+
})).toBeInTheDocument();
|
|
292
320
|
expect(screen.queryByPlaceholderText(/Metadata Search/i)).not.toBeInTheDocument();
|
|
293
321
|
});
|
|
294
322
|
it('renders MetadataSearch component when metadata-search feature is enabled', async ()=>{
|
|
@@ -299,6 +327,70 @@ describe('ObjectList', ()=>{
|
|
|
299
327
|
});
|
|
300
328
|
expect(screen.getByPlaceholderText(/Metadata Search/i)).toBeInTheDocument();
|
|
301
329
|
});
|
|
330
|
+
it('filters table rows when typing in search input', async ()=>{
|
|
331
|
+
jest.useFakeTimers();
|
|
332
|
+
jest.spyOn(__rspack_external__hooks_useFeatures_js_a6a84786, 'useFeatures').mockReturnValue(false);
|
|
333
|
+
renderObjectList();
|
|
334
|
+
await waitFor(()=>{
|
|
335
|
+
expect(screen.getByText('file1.txt')).toBeInTheDocument();
|
|
336
|
+
expect(screen.getByText('file2.txt')).toBeInTheDocument();
|
|
337
|
+
expect(screen.getByText('folder1/')).toBeInTheDocument();
|
|
338
|
+
});
|
|
339
|
+
const searchInput = screen.getByRole('searchbox', {
|
|
340
|
+
name: 'search'
|
|
341
|
+
});
|
|
342
|
+
fireEvent.change(searchInput, {
|
|
343
|
+
target: {
|
|
344
|
+
value: 'file1'
|
|
345
|
+
}
|
|
346
|
+
});
|
|
347
|
+
act(()=>{
|
|
348
|
+
jest.advanceTimersByTime(300);
|
|
349
|
+
});
|
|
350
|
+
await waitFor(()=>{
|
|
351
|
+
expect(screen.getByText('file1.txt')).toBeInTheDocument();
|
|
352
|
+
expect(screen.queryByText('file2.txt')).not.toBeInTheDocument();
|
|
353
|
+
expect(screen.queryByText('folder1/')).not.toBeInTheDocument();
|
|
354
|
+
});
|
|
355
|
+
jest.useRealTimers();
|
|
356
|
+
});
|
|
357
|
+
it('shows all rows when search is cleared', async ()=>{
|
|
358
|
+
jest.useFakeTimers();
|
|
359
|
+
jest.spyOn(__rspack_external__hooks_useFeatures_js_a6a84786, 'useFeatures').mockReturnValue(false);
|
|
360
|
+
renderObjectList();
|
|
361
|
+
await waitFor(()=>{
|
|
362
|
+
expect(screen.getByText('file1.txt')).toBeInTheDocument();
|
|
363
|
+
expect(screen.getByText('file2.txt')).toBeInTheDocument();
|
|
364
|
+
});
|
|
365
|
+
const searchInput = screen.getByRole('searchbox', {
|
|
366
|
+
name: 'search'
|
|
367
|
+
});
|
|
368
|
+
fireEvent.change(searchInput, {
|
|
369
|
+
target: {
|
|
370
|
+
value: 'file1'
|
|
371
|
+
}
|
|
372
|
+
});
|
|
373
|
+
act(()=>{
|
|
374
|
+
jest.advanceTimersByTime(300);
|
|
375
|
+
});
|
|
376
|
+
await waitFor(()=>{
|
|
377
|
+
expect(screen.queryByText('file2.txt')).not.toBeInTheDocument();
|
|
378
|
+
});
|
|
379
|
+
fireEvent.change(searchInput, {
|
|
380
|
+
target: {
|
|
381
|
+
value: ''
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
act(()=>{
|
|
385
|
+
jest.advanceTimersByTime(300);
|
|
386
|
+
});
|
|
387
|
+
await waitFor(()=>{
|
|
388
|
+
expect(screen.getByText('file1.txt')).toBeInTheDocument();
|
|
389
|
+
expect(screen.getByText('file2.txt')).toBeInTheDocument();
|
|
390
|
+
expect(screen.getByText('folder1/')).toBeInTheDocument();
|
|
391
|
+
});
|
|
392
|
+
jest.useRealTimers();
|
|
393
|
+
});
|
|
302
394
|
});
|
|
303
395
|
describe('Multi-Selection', ()=>{
|
|
304
396
|
it('integrates with MultiSelectableContent', async ()=>{
|
|
@@ -5,6 +5,7 @@ export declare const bucketNameValidationSchema: Joi.StringSchema<string>;
|
|
|
5
5
|
export declare const baseBucketCreateSchema: Joi.ObjectSchema<any>;
|
|
6
6
|
export type BucketCreateFormData = {
|
|
7
7
|
name: string;
|
|
8
|
+
locationConstraint?: string;
|
|
8
9
|
isVersioning: boolean;
|
|
9
10
|
isEncryptionEnabled: boolean;
|
|
10
11
|
isObjectLockEnabled: boolean;
|
|
@@ -4,7 +4,9 @@ import { Checkbox, Form, FormGroup, FormSection, Stack, useToast } from "@scalit
|
|
|
4
4
|
import { Button, Input } from "@scality/core-ui/dist/next";
|
|
5
5
|
import { convertRemToPixels } from "@scality/core-ui/dist/utils";
|
|
6
6
|
import joi from "joi";
|
|
7
|
-
import {
|
|
7
|
+
import { useMemo } from "react";
|
|
8
|
+
import { Controller, FormProvider, useForm } from "react-hook-form";
|
|
9
|
+
import { useDataBrowserUICustomization } from "../../contexts/DataBrowserUICustomizationContext.js";
|
|
8
10
|
import { useCreateBucket, useSetBucketEncryption, useSetBucketObjectLockConfiguration, useSetBucketVersioning } from "../../hooks/index.js";
|
|
9
11
|
import { useDataBrowserNavigate } from "../../hooks/useDataBrowserNavigate.js";
|
|
10
12
|
import ObjectLockRetentionSettings from "../objects/ObjectLock/ObjectLockRetentionSettings.js";
|
|
@@ -58,8 +60,12 @@ const baseBucketCreateSchema = joi.object({
|
|
|
58
60
|
const BucketCreate = ({ subTitle, validationSchema, validationContext, defaultValues: customDefaultValues, children, onSubmit: onSubmitProp, onCancel: onCancelProp })=>{
|
|
59
61
|
const navigate = useDataBrowserNavigate();
|
|
60
62
|
const { showToast } = useToast();
|
|
63
|
+
const { locationSelector: LocationSelector, bucketCreateExtraFields, transformBucketCreateData, bucketCreateVersioning: VersioningRenderer } = useDataBrowserUICustomization();
|
|
61
64
|
const baseDefaultValues = {
|
|
62
65
|
name: '',
|
|
66
|
+
...LocationSelector ? {
|
|
67
|
+
locationConstraint: ''
|
|
68
|
+
} : {},
|
|
63
69
|
isVersioning: false,
|
|
64
70
|
isEncryptionEnabled: false,
|
|
65
71
|
isObjectLockEnabled: false,
|
|
@@ -69,15 +75,32 @@ const BucketCreate = ({ subTitle, validationSchema, validationContext, defaultVa
|
|
|
69
75
|
retentionPeriodFrequencyChoice: 'DAYS',
|
|
70
76
|
...customDefaultValues
|
|
71
77
|
};
|
|
78
|
+
const hasExtraFields = !!bucketCreateExtraFields;
|
|
79
|
+
const resolvedSchema = useMemo(()=>{
|
|
80
|
+
let schema = validationSchema || baseBucketCreateSchema;
|
|
81
|
+
if (LocationSelector) schema = schema.keys({
|
|
82
|
+
locationConstraint: joi.string().required().messages({
|
|
83
|
+
'string.empty': 'Location is required'
|
|
84
|
+
})
|
|
85
|
+
});
|
|
86
|
+
if (hasExtraFields) schema = schema.options({
|
|
87
|
+
allowUnknown: true
|
|
88
|
+
});
|
|
89
|
+
return schema;
|
|
90
|
+
}, [
|
|
91
|
+
validationSchema,
|
|
92
|
+
LocationSelector,
|
|
93
|
+
hasExtraFields
|
|
94
|
+
]);
|
|
72
95
|
const useFormMethods = useForm({
|
|
73
96
|
mode: 'all',
|
|
74
|
-
resolver: joiResolver(
|
|
97
|
+
resolver: joiResolver(resolvedSchema, {
|
|
75
98
|
context: validationContext || {}
|
|
76
99
|
}),
|
|
77
100
|
defaultValues: baseDefaultValues,
|
|
78
101
|
context: validationContext
|
|
79
102
|
});
|
|
80
|
-
const { register, handleSubmit, formState, watch } = useFormMethods;
|
|
103
|
+
const { register, handleSubmit, formState, watch, control } = useFormMethods;
|
|
81
104
|
const { isValid, errors } = formState;
|
|
82
105
|
const isObjectLockEnabled = watch('isObjectLockEnabled');
|
|
83
106
|
const isEncryptionEnabled = watch('isEncryptionEnabled');
|
|
@@ -106,15 +129,21 @@ const BucketCreate = ({ subTitle, validationSchema, validationContext, defaultVa
|
|
|
106
129
|
status: 'error'
|
|
107
130
|
});
|
|
108
131
|
};
|
|
109
|
-
const onSubmit = (
|
|
110
|
-
if (onSubmitProp) return void onSubmitProp(
|
|
132
|
+
const onSubmit = (rawData)=>{
|
|
133
|
+
if (onSubmitProp) return void onSubmitProp(rawData);
|
|
134
|
+
const data = transformBucketCreateData ? transformBucketCreateData(rawData) : rawData;
|
|
111
135
|
const bucketName = data.name;
|
|
112
136
|
const needsExplicitVersioning = data.isVersioning && !data.isObjectLockEnabled;
|
|
113
137
|
const needsRetentionConfig = data.isObjectLockEnabled && data.isDefaultRetentionEnabled;
|
|
114
138
|
const needsEncryption = data.isEncryptionEnabled;
|
|
115
139
|
createBucket({
|
|
116
140
|
Bucket: bucketName,
|
|
117
|
-
ObjectLockEnabledForBucket: data.isObjectLockEnabled || void 0
|
|
141
|
+
ObjectLockEnabledForBucket: data.isObjectLockEnabled || void 0,
|
|
142
|
+
...data.locationConstraint ? {
|
|
143
|
+
CreateBucketConfiguration: {
|
|
144
|
+
LocationConstraint: data.locationConstraint
|
|
145
|
+
}
|
|
146
|
+
} : {}
|
|
118
147
|
}, {
|
|
119
148
|
onSuccess: ()=>{
|
|
120
149
|
if (needsExplicitVersioning) setBucketVersioning({
|
|
@@ -236,6 +265,24 @@ const BucketCreate = ({ subTitle, validationSchema, validationContext, defaultVa
|
|
|
236
265
|
helpErrorPosition: "bottom",
|
|
237
266
|
error: errors.name ? errors.name?.type === 'string.pattern.base' ? bucketErrorMessage : errors.name?.message : void 0
|
|
238
267
|
}),
|
|
268
|
+
LocationSelector && /*#__PURE__*/ jsx(FormGroup, {
|
|
269
|
+
id: "locationConstraint",
|
|
270
|
+
label: "Storage Service Location",
|
|
271
|
+
required: true,
|
|
272
|
+
direction: "horizontal",
|
|
273
|
+
labelHelpTooltip: "Cannot be modified after creation",
|
|
274
|
+
helpErrorPosition: "bottom",
|
|
275
|
+
error: errors?.locationConstraint?.message,
|
|
276
|
+
content: /*#__PURE__*/ jsx(Controller, {
|
|
277
|
+
name: "locationConstraint",
|
|
278
|
+
control: control,
|
|
279
|
+
render: ({ field })=>/*#__PURE__*/ jsx(LocationSelector, {
|
|
280
|
+
value: field.value ?? '',
|
|
281
|
+
onChange: field.onChange
|
|
282
|
+
})
|
|
283
|
+
})
|
|
284
|
+
}),
|
|
285
|
+
bucketCreateExtraFields,
|
|
239
286
|
children,
|
|
240
287
|
s3Capabilities?.bucketEncryption && /*#__PURE__*/ jsx(FormGroup, {
|
|
241
288
|
id: "isEncryptionEnabled",
|
|
@@ -248,7 +295,10 @@ const BucketCreate = ({ subTitle, validationSchema, validationContext, defaultVa
|
|
|
248
295
|
...register('isEncryptionEnabled')
|
|
249
296
|
})
|
|
250
297
|
}),
|
|
251
|
-
/*#__PURE__*/ jsx(
|
|
298
|
+
VersioningRenderer ? /*#__PURE__*/ jsx(VersioningRenderer, {
|
|
299
|
+
isVersioning: isVersioning,
|
|
300
|
+
isObjectLockEnabled: isObjectLockEnabled
|
|
301
|
+
}) : /*#__PURE__*/ jsx(FormGroup, {
|
|
252
302
|
id: "isVersioning",
|
|
253
303
|
label: "Versioning",
|
|
254
304
|
disabled: isObjectLockEnabled,
|