@scality/data-browser-library 1.0.0-preview.11

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 (256) hide show
  1. package/dist/components/Editor.d.ts +12 -0
  2. package/dist/components/Editor.js +28 -0
  3. package/dist/components/__tests__/BucketCreate.test.d.ts +1 -0
  4. package/dist/components/__tests__/BucketCreate.test.js +408 -0
  5. package/dist/components/__tests__/BucketLifecycleFormPage.test.d.ts +1 -0
  6. package/dist/components/__tests__/BucketLifecycleFormPage.test.js +618 -0
  7. package/dist/components/__tests__/BucketLifecycleList.test.d.ts +1 -0
  8. package/dist/components/__tests__/BucketLifecycleList.test.js +325 -0
  9. package/dist/components/__tests__/BucketList.test.d.ts +1 -0
  10. package/dist/components/__tests__/BucketList.test.js +415 -0
  11. package/dist/components/__tests__/BucketNotificationCreatePage.test.d.ts +1 -0
  12. package/dist/components/__tests__/BucketNotificationCreatePage.test.js +316 -0
  13. package/dist/components/__tests__/BucketOverview.test.d.ts +1 -0
  14. package/dist/components/__tests__/BucketOverview.test.js +769 -0
  15. package/dist/components/__tests__/BucketPolicyPage.test.d.ts +1 -0
  16. package/dist/components/__tests__/BucketPolicyPage.test.js +268 -0
  17. package/dist/components/__tests__/BucketReplicationFormPage.test.d.ts +1 -0
  18. package/dist/components/__tests__/BucketReplicationFormPage.test.js +1757 -0
  19. package/dist/components/__tests__/BucketReplicationList.test.d.ts +1 -0
  20. package/dist/components/__tests__/BucketReplicationList.test.js +344 -0
  21. package/dist/components/__tests__/CreateFolderButton.test.d.ts +1 -0
  22. package/dist/components/__tests__/CreateFolderButton.test.js +147 -0
  23. package/dist/components/__tests__/DeleteBucketButton.test.d.ts +1 -0
  24. package/dist/components/__tests__/DeleteBucketButton.test.js +272 -0
  25. package/dist/components/__tests__/DeleteBucketConfigRuleButton.test.d.ts +1 -0
  26. package/dist/components/__tests__/DeleteBucketConfigRuleButton.test.js +196 -0
  27. package/dist/components/__tests__/DeleteObjectButton.test.d.ts +1 -0
  28. package/dist/components/__tests__/DeleteObjectButton.test.js +302 -0
  29. package/dist/components/__tests__/EmptyBucketButton.test.d.ts +1 -0
  30. package/dist/components/__tests__/EmptyBucketButton.test.js +302 -0
  31. package/dist/components/__tests__/MetadataSearch.test.d.ts +1 -0
  32. package/dist/components/__tests__/MetadataSearch.test.js +201 -0
  33. package/dist/components/__tests__/ObjectList.test.d.ts +1 -0
  34. package/dist/components/__tests__/ObjectList.test.js +283 -0
  35. package/dist/components/__tests__/UploadButton.test.d.ts +1 -0
  36. package/dist/components/__tests__/UploadButton.test.js +144 -0
  37. package/dist/components/buckets/BucketCreate.d.ts +49 -0
  38. package/dist/components/buckets/BucketCreate.js +237 -0
  39. package/dist/components/buckets/BucketDetails.d.ts +1 -0
  40. package/dist/components/buckets/BucketDetails.js +106 -0
  41. package/dist/components/buckets/BucketLifecycleFormPage.d.ts +15 -0
  42. package/dist/components/buckets/BucketLifecycleFormPage.js +1085 -0
  43. package/dist/components/buckets/BucketLifecycleList.d.ts +10 -0
  44. package/dist/components/buckets/BucketLifecycleList.js +270 -0
  45. package/dist/components/buckets/BucketList.d.ts +15 -0
  46. package/dist/components/buckets/BucketList.js +146 -0
  47. package/dist/components/buckets/BucketLocation.d.ts +3 -0
  48. package/dist/components/buckets/BucketLocation.js +16 -0
  49. package/dist/components/buckets/BucketOverview.d.ts +88 -0
  50. package/dist/components/buckets/BucketOverview.js +291 -0
  51. package/dist/components/buckets/BucketPage.d.ts +2 -0
  52. package/dist/components/buckets/BucketPage.js +47 -0
  53. package/dist/components/buckets/BucketPolicyButton.d.ts +7 -0
  54. package/dist/components/buckets/BucketPolicyButton.js +18 -0
  55. package/dist/components/buckets/BucketPolicyPage.d.ts +1 -0
  56. package/dist/components/buckets/BucketPolicyPage.js +236 -0
  57. package/dist/components/buckets/BucketReplicationFormPage.d.ts +1 -0
  58. package/dist/components/buckets/BucketReplicationFormPage.js +834 -0
  59. package/dist/components/buckets/BucketReplicationList.d.ts +11 -0
  60. package/dist/components/buckets/BucketReplicationList.js +189 -0
  61. package/dist/components/buckets/BucketVersioning.d.ts +4 -0
  62. package/dist/components/buckets/BucketVersioning.js +73 -0
  63. package/dist/components/buckets/DeleteBucketButton.d.ts +8 -0
  64. package/dist/components/buckets/DeleteBucketButton.js +78 -0
  65. package/dist/components/buckets/DeleteBucketConfigRuleButton.d.ts +18 -0
  66. package/dist/components/buckets/DeleteBucketConfigRuleButton.js +53 -0
  67. package/dist/components/buckets/EmptyBucketButton.d.ts +5 -0
  68. package/dist/components/buckets/EmptyBucketButton.js +232 -0
  69. package/dist/components/buckets/EmptyBucketSummary.d.ts +9 -0
  70. package/dist/components/buckets/EmptyBucketSummary.js +60 -0
  71. package/dist/components/buckets/EmptyBucketSummaryList.d.ts +13 -0
  72. package/dist/components/buckets/EmptyBucketSummaryList.js +140 -0
  73. package/dist/components/buckets/__tests__/BucketVersioning.test.d.ts +1 -0
  74. package/dist/components/buckets/__tests__/BucketVersioning.test.js +163 -0
  75. package/dist/components/buckets/notifications/BucketNotificationCreatePage.d.ts +1 -0
  76. package/dist/components/buckets/notifications/BucketNotificationCreatePage.js +234 -0
  77. package/dist/components/buckets/notifications/EventsSection.d.ts +1 -0
  78. package/dist/components/buckets/notifications/EventsSection.js +123 -0
  79. package/dist/components/buckets/notifications/events.d.ts +12 -0
  80. package/dist/components/buckets/notifications/events.js +27 -0
  81. package/dist/components/index.d.ts +21 -0
  82. package/dist/components/index.js +22 -0
  83. package/dist/components/layouts/ArrowNavigation.d.ts +4 -0
  84. package/dist/components/layouts/ArrowNavigation.js +16 -0
  85. package/dist/components/layouts/BrowserPageLayout.d.ts +12 -0
  86. package/dist/components/layouts/BrowserPageLayout.js +51 -0
  87. package/dist/components/objects/CreateFolderButton.d.ts +29 -0
  88. package/dist/components/objects/CreateFolderButton.js +118 -0
  89. package/dist/components/objects/DeleteObjectButton.d.ts +8 -0
  90. package/dist/components/objects/DeleteObjectButton.js +191 -0
  91. package/dist/components/objects/ObjectDetails/ObjectMetadata.d.ts +2 -0
  92. package/dist/components/objects/ObjectDetails/ObjectMetadata.js +356 -0
  93. package/dist/components/objects/ObjectDetails/ObjectSummary.d.ts +3 -0
  94. package/dist/components/objects/ObjectDetails/ObjectSummary.js +241 -0
  95. package/dist/components/objects/ObjectDetails/ObjectTags.d.ts +3 -0
  96. package/dist/components/objects/ObjectDetails/ObjectTags.js +272 -0
  97. package/dist/components/objects/ObjectDetails/index.d.ts +9 -0
  98. package/dist/components/objects/ObjectDetails/index.js +75 -0
  99. package/dist/components/objects/ObjectList.d.ts +40 -0
  100. package/dist/components/objects/ObjectList.js +453 -0
  101. package/dist/components/objects/ObjectLock/EditRetentionButton.d.ts +4 -0
  102. package/dist/components/objects/ObjectLock/EditRetentionButton.js +32 -0
  103. package/dist/components/objects/ObjectLock/ObjectLockRetentionSettings.d.ts +3 -0
  104. package/dist/components/objects/ObjectLock/ObjectLockRetentionSettings.js +211 -0
  105. package/dist/components/objects/ObjectLock/ObjectLockSettings.d.ts +9 -0
  106. package/dist/components/objects/ObjectLock/ObjectLockSettings.js +158 -0
  107. package/dist/components/objects/ObjectLock/ObjectLockSettingsUtils.d.ts +8 -0
  108. package/dist/components/objects/ObjectLock/ObjectLockSettingsUtils.js +39 -0
  109. package/dist/components/objects/ObjectLock/__tests__/EditRetentionButton.test.d.ts +1 -0
  110. package/dist/components/objects/ObjectLock/__tests__/EditRetentionButton.test.js +204 -0
  111. package/dist/components/objects/ObjectLock/__tests__/ObjectLockSettings.test.d.ts +1 -0
  112. package/dist/components/objects/ObjectLock/__tests__/ObjectLockSettings.test.js +374 -0
  113. package/dist/components/objects/ObjectPage.d.ts +1 -0
  114. package/dist/components/objects/ObjectPage.js +45 -0
  115. package/dist/components/objects/UploadButton.d.ts +34 -0
  116. package/dist/components/objects/UploadButton.js +229 -0
  117. package/dist/components/providers/DataBrowserProvider.d.ts +20 -0
  118. package/dist/components/providers/DataBrowserProvider.js +42 -0
  119. package/dist/components/search/MetadataSearch.d.ts +5 -0
  120. package/dist/components/search/MetadataSearch.js +162 -0
  121. package/dist/components/search/SearchHints.d.ts +8 -0
  122. package/dist/components/search/SearchHints.js +21 -0
  123. package/dist/components/ui/ArrayFieldActions.d.ts +36 -0
  124. package/dist/components/ui/ArrayFieldActions.js +43 -0
  125. package/dist/components/ui/ConfirmDeleteRuleModal.d.ts +16 -0
  126. package/dist/components/ui/ConfirmDeleteRuleModal.js +43 -0
  127. package/dist/components/ui/DeleteObjectModalContent.d.ts +5 -0
  128. package/dist/components/ui/DeleteObjectModalContent.js +71 -0
  129. package/dist/components/ui/FilterFormSection.d.ts +44 -0
  130. package/dist/components/ui/FilterFormSection.js +159 -0
  131. package/dist/components/ui/Search.elements.d.ts +17 -0
  132. package/dist/components/ui/Search.elements.js +59 -0
  133. package/dist/components/ui/Table.elements.d.ts +36 -0
  134. package/dist/components/ui/Table.elements.js +87 -0
  135. package/dist/config/factory.d.ts +63 -0
  136. package/dist/config/factory.js +74 -0
  137. package/dist/config/types.d.ts +46 -0
  138. package/dist/config/types.js +0 -0
  139. package/dist/hooks/__tests__/useISVBucketDetection.test.d.ts +1 -0
  140. package/dist/hooks/__tests__/useISVBucketDetection.test.js +188 -0
  141. package/dist/hooks/__tests__/useIsBucketEmpty.test.d.ts +1 -0
  142. package/dist/hooks/__tests__/useIsBucketEmpty.test.js +122 -0
  143. package/dist/hooks/bucketConfiguration.d.ts +168 -0
  144. package/dist/hooks/bucketConfiguration.js +67 -0
  145. package/dist/hooks/bucketOperations.d.ts +36 -0
  146. package/dist/hooks/bucketOperations.js +12 -0
  147. package/dist/hooks/factories/__tests__/useCreateS3FunctionMutationHook.test.d.ts +1 -0
  148. package/dist/hooks/factories/__tests__/useCreateS3FunctionMutationHook.test.js +276 -0
  149. package/dist/hooks/factories/__tests__/useCreateS3InfiniteQueryHook.test.d.ts +1 -0
  150. package/dist/hooks/factories/__tests__/useCreateS3InfiniteQueryHook.test.js +259 -0
  151. package/dist/hooks/factories/__tests__/useCreateS3LoginHook.test.d.ts +1 -0
  152. package/dist/hooks/factories/__tests__/useCreateS3LoginHook.test.js +166 -0
  153. package/dist/hooks/factories/__tests__/useCreateS3MutationHook.test.d.ts +1 -0
  154. package/dist/hooks/factories/__tests__/useCreateS3MutationHook.test.js +200 -0
  155. package/dist/hooks/factories/__tests__/useCreateS3QueryHook.test.d.ts +1 -0
  156. package/dist/hooks/factories/__tests__/useCreateS3QueryHook.test.js +179 -0
  157. package/dist/hooks/factories/index.d.ts +18 -0
  158. package/dist/hooks/factories/index.js +5 -0
  159. package/dist/hooks/factories/useCreateS3InfiniteQueryHook.d.ts +13 -0
  160. package/dist/hooks/factories/useCreateS3InfiniteQueryHook.js +76 -0
  161. package/dist/hooks/factories/useCreateS3LoginHook.d.ts +8 -0
  162. package/dist/hooks/factories/useCreateS3LoginHook.js +22 -0
  163. package/dist/hooks/factories/useCreateS3MutationHook.d.ts +5 -0
  164. package/dist/hooks/factories/useCreateS3MutationHook.js +50 -0
  165. package/dist/hooks/factories/useCreateS3QueryHook.d.ts +3 -0
  166. package/dist/hooks/factories/useCreateS3QueryHook.js +51 -0
  167. package/dist/hooks/index.d.ts +13 -0
  168. package/dist/hooks/index.js +13 -0
  169. package/dist/hooks/loginOperations.d.ts +21 -0
  170. package/dist/hooks/loginOperations.js +9 -0
  171. package/dist/hooks/objectOperations.d.ts +190 -0
  172. package/dist/hooks/objectOperations.js +67 -0
  173. package/dist/hooks/presignedOperations.d.ts +73 -0
  174. package/dist/hooks/presignedOperations.js +72 -0
  175. package/dist/hooks/useBatchObjectLegalHold.d.ts +11 -0
  176. package/dist/hooks/useBatchObjectLegalHold.js +45 -0
  177. package/dist/hooks/useDeleteBucketConfigRule.d.ts +26 -0
  178. package/dist/hooks/useDeleteBucketConfigRule.js +46 -0
  179. package/dist/hooks/useEmptyBucket.d.ts +27 -0
  180. package/dist/hooks/useEmptyBucket.js +116 -0
  181. package/dist/hooks/useISVBucketDetection.d.ts +15 -0
  182. package/dist/hooks/useISVBucketDetection.js +27 -0
  183. package/dist/hooks/useIsBucketEmpty.d.ts +7 -0
  184. package/dist/hooks/useIsBucketEmpty.js +36 -0
  185. package/dist/hooks/useLoginMutation.d.ts +21 -0
  186. package/dist/hooks/useLoginMutation.js +9 -0
  187. package/dist/hooks/useS3Client.d.ts +1 -0
  188. package/dist/hooks/useS3Client.js +13 -0
  189. package/dist/hooks/useTableRowSelection.d.ts +9 -0
  190. package/dist/hooks/useTableRowSelection.js +45 -0
  191. package/dist/index.d.ts +6 -0
  192. package/dist/index.js +6 -0
  193. package/dist/schemas/bucketPolicySchema.json +321 -0
  194. package/dist/test/msw/handlers/deleteBucket.d.ts +1 -0
  195. package/dist/test/msw/handlers/deleteBucket.js +14 -0
  196. package/dist/test/msw/handlers/getBucketAcl.d.ts +1 -0
  197. package/dist/test/msw/handlers/getBucketAcl.js +96 -0
  198. package/dist/test/msw/handlers/getBucketLocation.d.ts +1 -0
  199. package/dist/test/msw/handlers/getBucketLocation.js +23 -0
  200. package/dist/test/msw/handlers/getBucketPolicy.d.ts +11 -0
  201. package/dist/test/msw/handlers/getBucketPolicy.js +72 -0
  202. package/dist/test/msw/handlers/headObject.d.ts +1 -0
  203. package/dist/test/msw/handlers/headObject.js +17 -0
  204. package/dist/test/msw/handlers/listBuckets.d.ts +1 -0
  205. package/dist/test/msw/handlers/listBuckets.js +24 -0
  206. package/dist/test/msw/handlers/listObjectVersions.d.ts +1 -0
  207. package/dist/test/msw/handlers/listObjectVersions.js +83 -0
  208. package/dist/test/msw/handlers/listObjects.d.ts +1 -0
  209. package/dist/test/msw/handlers/listObjects.js +66 -0
  210. package/dist/test/msw/handlers/objectLegalHold.d.ts +1 -0
  211. package/dist/test/msw/handlers/objectLegalHold.js +24 -0
  212. package/dist/test/msw/handlers/objectRetention.d.ts +1 -0
  213. package/dist/test/msw/handlers/objectRetention.js +27 -0
  214. package/dist/test/msw/handlers/putBucketAcl.d.ts +1 -0
  215. package/dist/test/msw/handlers/putBucketAcl.js +18 -0
  216. package/dist/test/msw/handlers/putObject.d.ts +1 -0
  217. package/dist/test/msw/handlers/putObject.js +16 -0
  218. package/dist/test/msw/handlers.d.ts +4 -0
  219. package/dist/test/msw/handlers.js +109 -0
  220. package/dist/test/msw/index.d.ts +2 -0
  221. package/dist/test/msw/index.js +3 -0
  222. package/dist/test/msw/server.d.ts +4 -0
  223. package/dist/test/msw/server.js +20 -0
  224. package/dist/test/msw/utils.d.ts +2 -0
  225. package/dist/test/msw/utils.js +13 -0
  226. package/dist/test/setup.d.ts +1 -0
  227. package/dist/test/setup.js +90 -0
  228. package/dist/test/testUtils.d.ts +181 -0
  229. package/dist/test/testUtils.js +310 -0
  230. package/dist/test/utils/errorHandling.test.d.ts +1 -0
  231. package/dist/test/utils/errorHandling.test.js +423 -0
  232. package/dist/types/index.d.ts +51 -0
  233. package/dist/types/index.js +0 -0
  234. package/dist/utils/constants.d.ts +12 -0
  235. package/dist/utils/constants.js +9 -0
  236. package/dist/utils/deletion/index.d.ts +2 -0
  237. package/dist/utils/deletion/index.js +2 -0
  238. package/dist/utils/deletion/messages.d.ts +5 -0
  239. package/dist/utils/deletion/messages.js +29 -0
  240. package/dist/utils/deletion/types.d.ts +11 -0
  241. package/dist/utils/deletion/types.js +0 -0
  242. package/dist/utils/errorHandling.d.ts +63 -0
  243. package/dist/utils/errorHandling.js +84 -0
  244. package/dist/utils/hooks.d.ts +2 -0
  245. package/dist/utils/hooks.js +26 -0
  246. package/dist/utils/index.d.ts +4 -0
  247. package/dist/utils/index.js +4 -0
  248. package/dist/utils/proxyMiddleware.d.ts +18 -0
  249. package/dist/utils/proxyMiddleware.js +56 -0
  250. package/dist/utils/s3Client.d.ts +5 -0
  251. package/dist/utils/s3Client.js +36 -0
  252. package/dist/utils/s3RuleUtils.d.ts +53 -0
  253. package/dist/utils/s3RuleUtils.js +101 -0
  254. package/dist/utils/useFeatures.d.ts +1 -0
  255. package/dist/utils/useFeatures.js +7 -0
  256. package/package.json +84 -0
@@ -0,0 +1,374 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { fireEvent, render, screen, waitFor } from "@testing-library/react";
3
+ import user_event from "@testing-library/user-event";
4
+ import { MemoryRouter, Route, Routes } from "react-router-dom";
5
+ import { createTestWrapper } from "../../../../test/testUtils.js";
6
+ import { ObjectLockSettings } from "../ObjectLockSettings.js";
7
+ import { useGetBucketObjectLockConfiguration, useSetBucketObjectLockConfiguration } from "../../../../hooks/index.js";
8
+ jest.mock("../../../../hooks", ()=>({
9
+ useGetBucketObjectLockConfiguration: jest.fn(),
10
+ useSetBucketObjectLockConfiguration: jest.fn()
11
+ }));
12
+ const mockUseGetBucketObjectLockConfiguration = jest.mocked(useGetBucketObjectLockConfiguration);
13
+ const mockUseSetBucketObjectLockConfiguration = jest.mocked(useSetBucketObjectLockConfiguration);
14
+ const mockNavigate = jest.fn();
15
+ jest.mock("react-router-dom", ()=>({
16
+ ...jest.requireActual("react-router-dom"),
17
+ useNavigate: ()=>mockNavigate
18
+ }));
19
+ const renderObjectLockSettings = (bucketName = "test-bucket")=>{
20
+ const Wrapper = createTestWrapper();
21
+ return render(/*#__PURE__*/ jsx(MemoryRouter, {
22
+ initialEntries: [
23
+ `/buckets/${bucketName}/objects/settings`
24
+ ],
25
+ children: /*#__PURE__*/ jsx(Wrapper, {
26
+ children: /*#__PURE__*/ jsx(Routes, {
27
+ children: /*#__PURE__*/ jsx(Route, {
28
+ path: "/buckets/:bucketName/objects/settings",
29
+ element: /*#__PURE__*/ jsx(ObjectLockSettings, {})
30
+ })
31
+ })
32
+ })
33
+ }));
34
+ };
35
+ describe("ObjectLockSettings", ()=>{
36
+ const mockMutate = jest.fn();
37
+ beforeEach(()=>{
38
+ jest.clearAllMocks();
39
+ mockNavigate.mockClear();
40
+ mockUseGetBucketObjectLockConfiguration.mockReturnValue({
41
+ data: void 0,
42
+ status: "success"
43
+ });
44
+ mockUseSetBucketObjectLockConfiguration.mockReturnValue({
45
+ mutate: mockMutate,
46
+ isPending: false
47
+ });
48
+ });
49
+ it("displays loading state while fetching bucket configuration", ()=>{
50
+ mockUseGetBucketObjectLockConfiguration.mockReturnValue({
51
+ data: void 0,
52
+ status: "pending"
53
+ });
54
+ renderObjectLockSettings();
55
+ expect(screen.getByText("Loading retention settings...")).toBeInTheDocument();
56
+ });
57
+ it("renders form with Object Lock checkbox", async ()=>{
58
+ renderObjectLockSettings();
59
+ await waitFor(()=>{
60
+ expect(screen.getByText("Object-lock settings")).toBeInTheDocument();
61
+ });
62
+ expect(screen.getByLabelText(/object-lock/i)).toBeInTheDocument();
63
+ });
64
+ it("disables save button when Object Lock is not enabled", async ()=>{
65
+ renderObjectLockSettings();
66
+ await waitFor(()=>{
67
+ const saveButton = screen.getByRole("button", {
68
+ name: /save/i
69
+ });
70
+ expect(saveButton).toBeDisabled();
71
+ });
72
+ });
73
+ it("shows Default Retention fields when Object Lock is enabled", async ()=>{
74
+ mockUseGetBucketObjectLockConfiguration.mockReturnValue({
75
+ data: {
76
+ ObjectLockConfiguration: {
77
+ ObjectLockEnabled: "Enabled"
78
+ }
79
+ },
80
+ status: "success"
81
+ });
82
+ renderObjectLockSettings();
83
+ await waitFor(()=>{
84
+ expect(screen.getByText("Default Retention")).toBeInTheDocument();
85
+ expect(screen.getByText("Retention mode")).toBeInTheDocument();
86
+ expect(screen.getByText("Retention period")).toBeInTheDocument();
87
+ }, {
88
+ timeout: 3000
89
+ });
90
+ });
91
+ it("enables save button when Object Lock is enabled", async ()=>{
92
+ mockUseGetBucketObjectLockConfiguration.mockReturnValue({
93
+ data: {
94
+ ObjectLockConfiguration: {
95
+ ObjectLockEnabled: "Enabled"
96
+ }
97
+ },
98
+ status: "success"
99
+ });
100
+ renderObjectLockSettings();
101
+ await waitFor(()=>{
102
+ const saveButton = screen.getByRole("button", {
103
+ name: /save/i
104
+ });
105
+ expect(saveButton).toBeEnabled();
106
+ }, {
107
+ timeout: 3000
108
+ });
109
+ });
110
+ it("enables retention fields when Default Retention is checked", async ()=>{
111
+ mockUseGetBucketObjectLockConfiguration.mockReturnValue({
112
+ data: {
113
+ ObjectLockConfiguration: {
114
+ ObjectLockEnabled: "Enabled"
115
+ }
116
+ },
117
+ status: "success"
118
+ });
119
+ renderObjectLockSettings();
120
+ await waitFor(()=>{
121
+ expect(screen.getByText("Default Retention")).toBeInTheDocument();
122
+ });
123
+ const defaultRetentionCheckbox = screen.getByLabelText(/default retention/i);
124
+ expect(defaultRetentionCheckbox).not.toBeChecked();
125
+ fireEvent.click(defaultRetentionCheckbox);
126
+ await waitFor(()=>{
127
+ const governanceRadio = screen.getByLabelText(/governance/i);
128
+ expect(governanceRadio).not.toBeDisabled();
129
+ }, {
130
+ timeout: 3000
131
+ });
132
+ });
133
+ it("disables retention fields when Default Retention is unchecked", async ()=>{
134
+ mockUseGetBucketObjectLockConfiguration.mockReturnValue({
135
+ data: {
136
+ ObjectLockConfiguration: {
137
+ ObjectLockEnabled: "Enabled",
138
+ Rule: {
139
+ DefaultRetention: {
140
+ Mode: "GOVERNANCE",
141
+ Days: 30
142
+ }
143
+ }
144
+ }
145
+ },
146
+ status: "success"
147
+ });
148
+ renderObjectLockSettings();
149
+ await waitFor(()=>{
150
+ const defaultRetentionCheckbox = screen.getByLabelText(/default retention/i);
151
+ fireEvent.click(defaultRetentionCheckbox);
152
+ });
153
+ await waitFor(()=>{
154
+ const governanceRadio = screen.getByLabelText(/governance/i);
155
+ expect(governanceRadio).toBeDisabled();
156
+ });
157
+ });
158
+ it("shows validation error when retention period is invalid", async ()=>{
159
+ mockUseGetBucketObjectLockConfiguration.mockReturnValue({
160
+ data: {
161
+ ObjectLockConfiguration: {
162
+ ObjectLockEnabled: "Enabled"
163
+ }
164
+ },
165
+ status: "success"
166
+ });
167
+ renderObjectLockSettings();
168
+ await waitFor(()=>{
169
+ expect(screen.getByText("Default Retention")).toBeInTheDocument();
170
+ });
171
+ const defaultRetentionCheckbox = screen.getByLabelText(/default retention/i);
172
+ fireEvent.click(defaultRetentionCheckbox);
173
+ await waitFor(()=>{
174
+ const retentionPeriodInput = screen.getByLabelText(/retention period/i);
175
+ expect(retentionPeriodInput).toBeInTheDocument();
176
+ });
177
+ const retentionPeriodInput = screen.getByLabelText(/retention period/i);
178
+ await user_event.clear(retentionPeriodInput);
179
+ await user_event.type(retentionPeriodInput, "0");
180
+ await waitFor(()=>{
181
+ const saveButton = screen.getByRole("button", {
182
+ name: /save/i
183
+ });
184
+ expect(saveButton).toBeDisabled();
185
+ }, {
186
+ timeout: 3000
187
+ });
188
+ });
189
+ it("saves Object Lock configuration with Default Retention and navigates on success", async ()=>{
190
+ mockUseGetBucketObjectLockConfiguration.mockReturnValue({
191
+ data: {
192
+ ObjectLockConfiguration: {
193
+ ObjectLockEnabled: "Enabled"
194
+ }
195
+ },
196
+ status: "success"
197
+ });
198
+ renderObjectLockSettings();
199
+ await waitFor(()=>{
200
+ expect(screen.getByText("Default Retention")).toBeInTheDocument();
201
+ });
202
+ const defaultRetentionCheckbox = screen.getByLabelText(/default retention/i);
203
+ fireEvent.click(defaultRetentionCheckbox);
204
+ await waitFor(()=>{
205
+ const retentionPeriodInput = screen.getByLabelText(/retention period/i);
206
+ expect(retentionPeriodInput).toBeInTheDocument();
207
+ });
208
+ const retentionPeriodInput = screen.getByLabelText(/retention period/i);
209
+ await user_event.clear(retentionPeriodInput);
210
+ await user_event.type(retentionPeriodInput, "30");
211
+ const governanceRadio = screen.getByLabelText(/governance/i);
212
+ fireEvent.click(governanceRadio);
213
+ mockMutate.mockImplementation((_, options)=>{
214
+ options?.onSuccess?.();
215
+ });
216
+ const saveButton = screen.getByRole("button", {
217
+ name: /save/i
218
+ });
219
+ await waitFor(()=>expect(saveButton).toBeEnabled(), {
220
+ timeout: 3000
221
+ });
222
+ fireEvent.click(saveButton);
223
+ await waitFor(()=>{
224
+ expect(mockMutate).toHaveBeenCalled();
225
+ const callArgs = mockMutate.mock.calls[0][0];
226
+ expect(callArgs.Bucket).toBe("test-bucket");
227
+ expect(callArgs.ObjectLockConfiguration.ObjectLockEnabled).toBe("Enabled");
228
+ expect(callArgs.ObjectLockConfiguration.Rule.DefaultRetention.Mode).toBe("GOVERNANCE");
229
+ expect(callArgs.ObjectLockConfiguration.Rule.DefaultRetention.Days || callArgs.ObjectLockConfiguration.Rule.DefaultRetention.Years).toBe(30);
230
+ expect(mockNavigate).toHaveBeenCalledWith("/buckets/test-bucket");
231
+ });
232
+ });
233
+ it("saves Object Lock configuration with Years frequency", async ()=>{
234
+ mockUseGetBucketObjectLockConfiguration.mockReturnValue({
235
+ data: {
236
+ ObjectLockConfiguration: {
237
+ ObjectLockEnabled: "Enabled"
238
+ }
239
+ },
240
+ status: "success"
241
+ });
242
+ renderObjectLockSettings();
243
+ await waitFor(()=>{
244
+ expect(screen.getByText("Default Retention")).toBeInTheDocument();
245
+ });
246
+ const defaultRetentionCheckbox = screen.getByLabelText(/default retention/i);
247
+ fireEvent.click(defaultRetentionCheckbox);
248
+ await waitFor(()=>{
249
+ const retentionPeriodInput = screen.getByLabelText(/retention period/i);
250
+ expect(retentionPeriodInput).toBeInTheDocument();
251
+ });
252
+ const retentionPeriodInput = screen.getByLabelText(/retention period/i);
253
+ await user_event.clear(retentionPeriodInput);
254
+ await user_event.type(retentionPeriodInput, "2");
255
+ mockMutate.mockImplementation((_, options)=>{
256
+ options?.onSuccess?.();
257
+ });
258
+ const saveButton = screen.getByRole("button", {
259
+ name: /save/i
260
+ });
261
+ await waitFor(()=>expect(saveButton).toBeEnabled(), {
262
+ timeout: 3000
263
+ });
264
+ fireEvent.click(saveButton);
265
+ await waitFor(()=>{
266
+ expect(mockMutate).toHaveBeenCalled();
267
+ const callArgs = mockMutate.mock.calls[0][0];
268
+ expect(callArgs.Bucket).toBe("test-bucket");
269
+ expect(callArgs.ObjectLockConfiguration.ObjectLockEnabled).toBe("Enabled");
270
+ expect(callArgs.ObjectLockConfiguration.Rule.DefaultRetention.Mode).toBe("GOVERNANCE");
271
+ expect(callArgs.ObjectLockConfiguration.Rule.DefaultRetention.Days || callArgs.ObjectLockConfiguration.Rule.DefaultRetention.Years).toBe(2);
272
+ });
273
+ });
274
+ it("saves Object Lock configuration without Default Retention", async ()=>{
275
+ mockUseGetBucketObjectLockConfiguration.mockReturnValue({
276
+ data: {
277
+ ObjectLockConfiguration: {
278
+ ObjectLockEnabled: "Enabled"
279
+ }
280
+ },
281
+ status: "success"
282
+ });
283
+ renderObjectLockSettings();
284
+ await waitFor(()=>{
285
+ const saveButton = screen.getByRole("button", {
286
+ name: /save/i
287
+ });
288
+ expect(saveButton).toBeEnabled();
289
+ }, {
290
+ timeout: 3000
291
+ });
292
+ mockMutate.mockImplementation((_, options)=>{
293
+ options?.onSuccess?.();
294
+ });
295
+ const saveButton = screen.getByRole("button", {
296
+ name: /save/i
297
+ });
298
+ fireEvent.click(saveButton);
299
+ await waitFor(()=>{
300
+ expect(mockMutate).toHaveBeenCalled();
301
+ const callArgs = mockMutate.mock.calls[0][0];
302
+ expect(callArgs.Bucket).toBe("test-bucket");
303
+ expect(callArgs.ObjectLockConfiguration.ObjectLockEnabled).toBe("Enabled");
304
+ expect(callArgs.ObjectLockConfiguration.Rule).toBeUndefined();
305
+ });
306
+ });
307
+ it("displays error toast when save fails", async ()=>{
308
+ mockUseGetBucketObjectLockConfiguration.mockReturnValue({
309
+ data: {
310
+ ObjectLockConfiguration: {
311
+ ObjectLockEnabled: "Enabled"
312
+ }
313
+ },
314
+ status: "success"
315
+ });
316
+ renderObjectLockSettings();
317
+ await waitFor(()=>{
318
+ const saveButton = screen.getByRole("button", {
319
+ name: /save/i
320
+ });
321
+ expect(saveButton).toBeEnabled();
322
+ }, {
323
+ timeout: 3000
324
+ });
325
+ const error = new Error("Access Denied");
326
+ mockMutate.mockImplementation((_, options)=>{
327
+ options?.onError?.(error);
328
+ });
329
+ const saveButton = screen.getByRole("button", {
330
+ name: /save/i
331
+ });
332
+ fireEvent.click(saveButton);
333
+ await waitFor(()=>{
334
+ expect(mockNavigate).not.toHaveBeenCalled();
335
+ });
336
+ });
337
+ it("navigates back when cancel button is clicked", async ()=>{
338
+ renderObjectLockSettings();
339
+ await waitFor(()=>{
340
+ const cancelButton = screen.getByRole("button", {
341
+ name: /cancel/i
342
+ });
343
+ fireEvent.click(cancelButton);
344
+ });
345
+ expect(mockNavigate).toHaveBeenCalledWith("/buckets/test-bucket");
346
+ });
347
+ it("populates form with existing bucket configuration", async ()=>{
348
+ mockUseGetBucketObjectLockConfiguration.mockReturnValue({
349
+ data: {
350
+ ObjectLockConfiguration: {
351
+ ObjectLockEnabled: "Enabled",
352
+ Rule: {
353
+ DefaultRetention: {
354
+ Mode: "COMPLIANCE",
355
+ Days: 60
356
+ }
357
+ }
358
+ }
359
+ },
360
+ status: "success"
361
+ });
362
+ renderObjectLockSettings();
363
+ await waitFor(()=>{
364
+ const objectLockCheckbox = screen.getByLabelText(/object-lock/i);
365
+ expect(objectLockCheckbox).toBeChecked();
366
+ });
367
+ const defaultRetentionCheckbox = screen.getByLabelText(/default retention/i);
368
+ expect(defaultRetentionCheckbox).toBeChecked();
369
+ const complianceRadio = screen.getByLabelText(/compliance/i);
370
+ expect(complianceRadio).toBeChecked();
371
+ const retentionPeriodInput = screen.getByLabelText(/retention period/i);
372
+ expect(retentionPeriodInput).toHaveValue(60);
373
+ });
374
+ });
@@ -0,0 +1 @@
1
+ export declare const ObjectPage: () => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,45 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { useNavigate, useParams, useSearchParams } from "react-router-dom";
3
+ import { useCallback, useState } from "react";
4
+ import { ObjectList } from "./ObjectList.js";
5
+ import { ObjectDetails } from "./ObjectDetails/index.js";
6
+ import { BrowserPageLayout } from "../layouts/BrowserPageLayout.js";
7
+ const ObjectPage = ()=>{
8
+ const { bucketName } = useParams();
9
+ const navigate = useNavigate();
10
+ const [searchParams] = useSearchParams();
11
+ const [item, setItem] = useState(null);
12
+ const prefix = searchParams.get("prefix") || "";
13
+ const handlePrefixChange = useCallback((newPrefix)=>{
14
+ const newSearchParams = new URLSearchParams(searchParams);
15
+ newSearchParams.set("prefix", newPrefix);
16
+ setItem(null);
17
+ navigate(`?${newSearchParams.toString()}`, {
18
+ replace: true
19
+ });
20
+ }, [
21
+ navigate,
22
+ searchParams
23
+ ]);
24
+ if (!bucketName) return /*#__PURE__*/ jsx("div", {
25
+ children: "Bucket name is required"
26
+ });
27
+ const handleObjectSelect = (object)=>{
28
+ setItem(object);
29
+ };
30
+ return /*#__PURE__*/ jsx(BrowserPageLayout, {
31
+ title: bucketName,
32
+ leftPanel: /*#__PURE__*/ jsx(ObjectList, {
33
+ bucketName: bucketName,
34
+ prefix: prefix,
35
+ onObjectSelect: handleObjectSelect,
36
+ onPrefixChange: handlePrefixChange
37
+ }),
38
+ rightPanel: /*#__PURE__*/ jsx(ObjectDetails, {
39
+ item: item
40
+ }),
41
+ withArrowNavigation: true,
42
+ arrowNavigationPath: `/buckets/${bucketName}`
43
+ });
44
+ };
45
+ export { ObjectPage };
@@ -0,0 +1,34 @@
1
+ import React from "react";
2
+ import type { PutObjectCommandInput } from "@aws-sdk/client-s3";
3
+ interface UploadButtonProps {
4
+ /**
5
+ * The S3 bucket name where files will be uploaded
6
+ */
7
+ bucket: string;
8
+ /**
9
+ * The prefix/path where files will be uploaded within the bucket
10
+ */
11
+ prefix?: string;
12
+ /**
13
+ * Button label text
14
+ */
15
+ label?: string;
16
+ /**
17
+ * Button variant
18
+ */
19
+ variant?: "primary" | "secondary" | "outline";
20
+ /**
21
+ * Upload options to configure metadata, encryption, etc.
22
+ */
23
+ uploadOptions?: Partial<PutObjectCommandInput>;
24
+ /**
25
+ * Called when upload is successful
26
+ */
27
+ onUploadSuccess?: (files: File[]) => void;
28
+ /**
29
+ * Called when upload fails
30
+ */
31
+ onUploadError?: (error: Error, files: File[]) => void;
32
+ }
33
+ export declare const UploadButton: React.FC<UploadButtonProps>;
34
+ export default UploadButton;
@@ -0,0 +1,229 @@
1
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
+ import { useCallback, useState } from "react";
3
+ import { useDropzone } from "react-dropzone";
4
+ import { Icon, Modal, PrettyBytes, Stack, Wrap, spacing } from "@scality/core-ui";
5
+ import { Button } from "@scality/core-ui/dist/components/buttonv2/Buttonv2.component";
6
+ import styled_components from "styled-components";
7
+ import { useUploadObjects } from "../../hooks/index.js";
8
+ const DropZone = styled_components.div`
9
+ flex: 1;
10
+ display: flex;
11
+ flex-direction: column;
12
+ height: 300px;
13
+ width: 500px;
14
+ padding: ${spacing.r20};
15
+ border-width: ${spacing.r2};
16
+ border-radius: ${spacing.r2};
17
+ border-color: ${(props)=>props.theme.border};
18
+ border-style: dashed;
19
+ `;
20
+ const Files = styled_components.div`
21
+ height: 250px;
22
+ overflow-y: scroll;
23
+ margin: ${spacing.r8} 0px;
24
+ `;
25
+ const EmptyFile = styled_components.div`
26
+ text-align: center;
27
+ margin-top: 60px;
28
+ & > * {
29
+ margin-bottom: ${spacing.r16};
30
+ }
31
+ `;
32
+ const FileRow = styled_components.div`
33
+ display: flex;
34
+ justify-content: space-between;
35
+ align-items: center;
36
+ padding: ${spacing.r8};
37
+ border-bottom: 1px solid ${(props)=>props.theme.border};
38
+ `;
39
+ const FileInfo = styled_components.div`
40
+ flex: 1;
41
+ `;
42
+ const RemoveButton = styled_components.button`
43
+ background: none;
44
+ border: none;
45
+ cursor: pointer;
46
+ padding: ${spacing.r4};
47
+ color: ${(props)=>props.theme.textPrimary};
48
+
49
+ &:hover {
50
+ color: ${(props)=>props.theme.textSecondary};
51
+ }
52
+ `;
53
+ const maybePluralize = (count, word)=>1 === count ? `1 ${word}` : `${count} ${word}s`;
54
+ const getTitle = (fileCount)=>0 === fileCount ? "Upload Files" : `Upload ${maybePluralize(fileCount, "file")}`;
55
+ const FileList = ({ acceptedFiles, open, removeFile })=>/*#__PURE__*/ jsxs("div", {
56
+ children: [
57
+ /*#__PURE__*/ jsx(Button, {
58
+ icon: /*#__PURE__*/ jsx(Icon, {
59
+ name: "Create-add"
60
+ }),
61
+ label: "Add more files",
62
+ variant: "secondary",
63
+ onClick: open
64
+ }),
65
+ /*#__PURE__*/ jsx(Files, {
66
+ children: acceptedFiles.map((file)=>/*#__PURE__*/ jsxs(FileRow, {
67
+ children: [
68
+ /*#__PURE__*/ jsx(FileInfo, {
69
+ children: /*#__PURE__*/ jsxs("div", {
70
+ children: [
71
+ file.name,
72
+ /*#__PURE__*/ jsx("br", {}),
73
+ /*#__PURE__*/ jsx("small", {
74
+ children: /*#__PURE__*/ jsx(PrettyBytes, {
75
+ bytes: file.size
76
+ })
77
+ })
78
+ ]
79
+ })
80
+ }),
81
+ /*#__PURE__*/ jsx(RemoveButton, {
82
+ onClick: ()=>removeFile(file.name),
83
+ children: /*#__PURE__*/ jsx(Icon, {
84
+ name: "Close"
85
+ })
86
+ })
87
+ ]
88
+ }, file.name))
89
+ })
90
+ ]
91
+ });
92
+ const NoFile = ({ open })=>/*#__PURE__*/ jsxs(EmptyFile, {
93
+ children: [
94
+ /*#__PURE__*/ jsx(Icon, {
95
+ name: "Upload",
96
+ size: "3x"
97
+ }),
98
+ /*#__PURE__*/ jsx("div", {
99
+ children: "Drag and drop files and folders here"
100
+ }),
101
+ /*#__PURE__*/ jsx("div", {
102
+ children: "OR"
103
+ }),
104
+ /*#__PURE__*/ jsx(Button, {
105
+ icon: /*#__PURE__*/ jsx(Icon, {
106
+ name: "Create-add"
107
+ }),
108
+ label: "Add files",
109
+ variant: "secondary",
110
+ onClick: open
111
+ })
112
+ ]
113
+ });
114
+ const UploadButton_UploadButton = ({ bucket, prefix = "", uploadOptions = {}, onUploadSuccess, onUploadError })=>{
115
+ const [isModalOpen, setIsModalOpen] = useState(false);
116
+ const [acceptedFiles, setAcceptedFiles] = useState([]);
117
+ const uploadMutation = useUploadObjects();
118
+ const onDrop = useCallback((accepted)=>{
119
+ if (accepted.length > 0) setAcceptedFiles((prevFiles)=>{
120
+ const filtered = accepted.filter((newFile)=>!prevFiles.find((existingFile)=>existingFile.name === newFile.name));
121
+ return filtered.length > 0 ? [
122
+ ...prevFiles,
123
+ ...filtered
124
+ ] : prevFiles;
125
+ });
126
+ }, []);
127
+ const { getRootProps, getInputProps, open } = useDropzone({
128
+ noClick: true,
129
+ noKeyboard: true,
130
+ onDrop
131
+ });
132
+ const openModal = useCallback(()=>{
133
+ setIsModalOpen(true);
134
+ }, []);
135
+ const closeModal = useCallback(()=>{
136
+ setAcceptedFiles([]);
137
+ setIsModalOpen(false);
138
+ }, []);
139
+ const removeFile = useCallback((fileName)=>{
140
+ setAcceptedFiles((prevFiles)=>prevFiles.filter((file)=>file.name !== fileName));
141
+ }, []);
142
+ const handleUpload = useCallback(async ()=>{
143
+ if (0 === acceptedFiles.length) return;
144
+ const successfulFiles = [];
145
+ const failedFiles = [];
146
+ for (const file of acceptedFiles)try {
147
+ const fileBuffer = await file.arrayBuffer();
148
+ await uploadMutation.mutateAsync({
149
+ Bucket: bucket,
150
+ Key: prefix ? `${prefix}/${file.name}` : file.name,
151
+ Body: new Uint8Array(fileBuffer),
152
+ ContentType: file.type,
153
+ ...uploadOptions
154
+ });
155
+ successfulFiles.push(file);
156
+ } catch (error) {
157
+ failedFiles.push(file);
158
+ if (1 === failedFiles.length) onUploadError?.(error, [
159
+ file
160
+ ]);
161
+ }
162
+ if (successfulFiles.length > 0) onUploadSuccess?.(successfulFiles);
163
+ setAcceptedFiles([]);
164
+ setIsModalOpen(false);
165
+ }, [
166
+ acceptedFiles,
167
+ bucket,
168
+ prefix,
169
+ uploadMutation,
170
+ uploadOptions,
171
+ onUploadSuccess,
172
+ onUploadError
173
+ ]);
174
+ return /*#__PURE__*/ jsxs(Fragment, {
175
+ children: [
176
+ /*#__PURE__*/ jsx(Button, {
177
+ icon: /*#__PURE__*/ jsx(Icon, {
178
+ name: "Simple-upload"
179
+ }),
180
+ label: "Upload",
181
+ variant: "secondary",
182
+ onClick: openModal
183
+ }),
184
+ isModalOpen && /*#__PURE__*/ jsx(Modal, {
185
+ close: closeModal,
186
+ footer: /*#__PURE__*/ jsxs(Wrap, {
187
+ children: [
188
+ /*#__PURE__*/ jsx("p", {}),
189
+ /*#__PURE__*/ jsxs(Stack, {
190
+ children: [
191
+ /*#__PURE__*/ jsx(Button, {
192
+ variant: "outline",
193
+ onClick: closeModal,
194
+ label: "Cancel",
195
+ disabled: uploadMutation.isPending
196
+ }),
197
+ /*#__PURE__*/ jsx(Button, {
198
+ disabled: 0 === acceptedFiles.length || uploadMutation.isPending,
199
+ variant: "secondary",
200
+ onClick: handleUpload,
201
+ label: uploadMutation.isPending ? "Uploading..." : "Upload"
202
+ })
203
+ ]
204
+ })
205
+ ]
206
+ }),
207
+ isOpen: true,
208
+ title: getTitle(acceptedFiles.length),
209
+ children: /*#__PURE__*/ jsxs(DropZone, {
210
+ ...getRootProps(),
211
+ children: [
212
+ /*#__PURE__*/ jsx("input", {
213
+ ...getInputProps()
214
+ }),
215
+ acceptedFiles.length > 0 ? /*#__PURE__*/ jsx(FileList, {
216
+ acceptedFiles: acceptedFiles,
217
+ open: open,
218
+ removeFile: removeFile
219
+ }) : /*#__PURE__*/ jsx(NoFile, {
220
+ open: open
221
+ })
222
+ ]
223
+ })
224
+ })
225
+ ]
226
+ });
227
+ };
228
+ const UploadButton = UploadButton_UploadButton;
229
+ export { UploadButton_UploadButton as UploadButton, UploadButton as default };