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

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 (40) hide show
  1. package/dist/components/DataBrowserUI.d.ts +20 -0
  2. package/dist/components/DataBrowserUI.js +64 -0
  3. package/dist/components/__tests__/BucketDetails.test.d.ts +1 -0
  4. package/dist/components/__tests__/BucketDetails.test.js +421 -0
  5. package/dist/components/__tests__/BucketList.test.js +389 -164
  6. package/dist/components/__tests__/BucketOverview.test.js +19 -63
  7. package/dist/components/__tests__/ObjectList.test.js +719 -219
  8. package/dist/components/buckets/BucketDetails.d.ts +40 -0
  9. package/dist/components/buckets/BucketDetails.js +194 -86
  10. package/dist/components/buckets/BucketList.d.ts +5 -6
  11. package/dist/components/buckets/BucketList.js +152 -97
  12. package/dist/components/buckets/BucketOverview.d.ts +6 -0
  13. package/dist/components/buckets/BucketOverview.js +363 -179
  14. package/dist/components/buckets/BucketPage.js +1 -5
  15. package/dist/components/buckets/BucketVersioning.js +3 -0
  16. package/dist/components/buckets/EmptyBucketButton.js +1 -1
  17. package/dist/components/index.d.ts +2 -1
  18. package/dist/components/index.js +2 -1
  19. package/dist/components/layouts/ArrowNavigation.js +20 -8
  20. package/dist/components/objects/CreateFolderButton.js +1 -1
  21. package/dist/components/objects/ObjectDetails/ObjectSummary.js +287 -157
  22. package/dist/components/objects/ObjectDetails/__tests__/ObjectDetails.test.d.ts +1 -0
  23. package/dist/components/objects/ObjectDetails/__tests__/ObjectDetails.test.js +516 -0
  24. package/dist/components/objects/ObjectDetails/__tests__/ObjectSummary.test.d.ts +1 -0
  25. package/dist/components/objects/ObjectDetails/__tests__/ObjectSummary.test.js +813 -0
  26. package/dist/components/objects/ObjectDetails/index.d.ts +16 -0
  27. package/dist/components/objects/ObjectDetails/index.js +132 -46
  28. package/dist/components/objects/ObjectList.d.ts +7 -5
  29. package/dist/components/objects/ObjectList.js +566 -286
  30. package/dist/components/objects/UploadButton.js +1 -1
  31. package/dist/config/types.d.ts +117 -0
  32. package/dist/contexts/DataBrowserUICustomizationContext.d.ts +27 -0
  33. package/dist/contexts/DataBrowserUICustomizationContext.js +13 -0
  34. package/dist/test/testUtils.d.ts +64 -0
  35. package/dist/test/testUtils.js +100 -1
  36. package/dist/types/index.d.ts +5 -3
  37. package/dist/utils/constants.d.ts +7 -0
  38. package/dist/utils/constants.js +8 -1
  39. package/dist/utils/useFeatures.js +1 -1
  40. package/package.json +2 -2
@@ -1 +1,41 @@
1
+ import { useNavigate } from "react-router-dom";
2
+ interface BucketDetailsContextValue {
3
+ bucketName: string;
4
+ navigate: ReturnType<typeof useNavigate>;
5
+ }
6
+ /**
7
+ * Context for BucketDetails component.
8
+ *
9
+ * **Important**: Always use `useBucketDetailsContext()` hook instead of accessing this context directly.
10
+ * The hook provides proper error handling if the context is not available.
11
+ *
12
+ * This context is exported for testing purposes only.
13
+ */
14
+ export declare const BucketDetailsContext: import("react").Context<BucketDetailsContextValue | null>;
15
+ export declare function useBucketDetailsContext(): BucketDetailsContextValue;
16
+ declare const OverviewTab: React.FC;
17
+ declare const LifecycleTab: React.FC;
18
+ declare const ReplicationTab: React.FC;
19
+ interface CustomTabProps {
20
+ config: {
21
+ id: string;
22
+ title: string;
23
+ path?: string;
24
+ withoutPadding?: boolean;
25
+ render: () => React.ReactNode;
26
+ };
27
+ }
28
+ declare const CustomTab: React.FC<CustomTabProps>;
29
+ interface BucketDetailsProps {
30
+ children?: React.ReactNode;
31
+ }
32
+ declare const BucketDetailsRoot: React.FC<BucketDetailsProps>;
1
33
  export declare function BucketDetails(): import("react/jsx-runtime").JSX.Element;
34
+ type BucketDetailsComponent = typeof BucketDetailsRoot & {
35
+ OverviewTab: typeof OverviewTab;
36
+ LifecycleTab: typeof LifecycleTab;
37
+ ReplicationTab: typeof ReplicationTab;
38
+ CustomTab: typeof CustomTab;
39
+ };
40
+ declare const BucketDetailsCompound: BucketDetailsComponent;
41
+ export { BucketDetailsCompound };
@@ -1,106 +1,214 @@
1
1
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
- import { useQueryParams } from "../../utils/hooks.js";
2
+ import { createContext, memo, useCallback, useContext, useMemo } from "react";
3
3
  import { useNavigate, useParams } from "react-router-dom";
4
4
  import { Tabs } from "@scality/core-ui/dist/next";
5
- import { BucketOverview } from "./BucketOverview.js";
6
- import { DeleteBucketButton } from "./DeleteBucketButton.js";
7
- import { EmptyBucketButton } from "./EmptyBucketButton.js";
8
- import { BucketLifecycleList } from "./BucketLifecycleList.js";
9
- import { BucketReplicationList } from "./BucketReplicationList.js";
5
+ import { useDataBrowserUICustomization } from "../../contexts/DataBrowserUICustomizationContext.js";
10
6
  import { useGetBucketLifecycle, useGetBucketReplication } from "../../hooks/bucketConfiguration.js";
7
+ import { BUCKET_ROUTES } from "../../utils/constants.js";
11
8
  import { isNotFoundError } from "../../utils/errorHandling.js";
9
+ import { BucketLifecycleList } from "./BucketLifecycleList.js";
10
+ import { BucketOverview } from "./BucketOverview.js";
11
+ import { BucketReplicationList } from "./BucketReplicationList.js";
12
12
  import { BucketVersioning } from "./BucketVersioning.js";
13
- function BucketDetails() {
14
- const query = useQueryParams();
15
- const queryObject = Object.fromEntries(query.entries());
16
- const { bucketName } = useParams();
17
- const navigate = useNavigate();
13
+ import { DeleteBucketButton } from "./DeleteBucketButton.js";
14
+ import { EmptyBucketButton } from "./EmptyBucketButton.js";
15
+ const BucketDetailsContext = /*#__PURE__*/ createContext(null);
16
+ function useBucketDetailsContext() {
17
+ const context = useContext(BucketDetailsContext);
18
+ if (!context) throw new Error("BucketDetails components must be used within BucketDetails");
19
+ return context;
20
+ }
21
+ const getEffectiveStatus = (status, error)=>{
22
+ if (isNotFoundError(error)) return "success";
23
+ if ("pending" === status) return "loading";
24
+ if ("error" === status) return "error";
25
+ return "success";
26
+ };
27
+ const OverviewTab = /*#__PURE__*/ memo(()=>{
28
+ const { bucketName, navigate } = useBucketDetailsContext();
29
+ const renderDeleteButton = useCallback((name)=>/*#__PURE__*/ jsx(DeleteBucketButton, {
30
+ bucketName: name
31
+ }), []);
32
+ const renderEmptyButton = useCallback((name)=>/*#__PURE__*/ jsx(EmptyBucketButton, {
33
+ bucketName: name
34
+ }), []);
35
+ const renderVersioning = useCallback(()=>/*#__PURE__*/ jsx(BucketVersioning, {}), []);
36
+ const handleEditPolicy = useCallback((name)=>navigate(BUCKET_ROUTES.bucketPolicy(name)), [
37
+ navigate
38
+ ]);
39
+ return /*#__PURE__*/ jsxs(BucketOverview, {
40
+ bucketName: bucketName,
41
+ children: [
42
+ /*#__PURE__*/ jsx(BucketOverview.Actions, {
43
+ renderDeleteButton: renderDeleteButton,
44
+ renderEmptyButton: renderEmptyButton
45
+ }),
46
+ /*#__PURE__*/ jsxs(BucketOverview.Sections, {
47
+ children: [
48
+ /*#__PURE__*/ jsx(BucketOverview.GeneralSection, {
49
+ renderVersioning: renderVersioning
50
+ }),
51
+ /*#__PURE__*/ jsx(BucketOverview.DataProtectionSection, {}),
52
+ /*#__PURE__*/ jsx(BucketOverview.PermissionsSection, {
53
+ onEditPolicy: handleEditPolicy
54
+ })
55
+ ]
56
+ })
57
+ ]
58
+ });
59
+ });
60
+ OverviewTab.displayName = "BucketDetails.OverviewTab";
61
+ const LifecycleTab = /*#__PURE__*/ memo(()=>{
62
+ const { bucketName, navigate } = useBucketDetailsContext();
18
63
  const { data: lifecycleData, status: lifecycleStatus, error: lifecycleError } = useGetBucketLifecycle({
19
64
  Bucket: bucketName
20
65
  });
66
+ const lifecycleRules = lifecycleData?.Rules || [];
67
+ const effectiveStatus = getEffectiveStatus(lifecycleStatus, lifecycleError);
68
+ const handleCreateRule = useCallback(()=>navigate(BUCKET_ROUTES.lifecycleCreate(bucketName)), [
69
+ navigate,
70
+ bucketName
71
+ ]);
72
+ const handleEditRule = useCallback((ruleId)=>navigate(BUCKET_ROUTES.lifecycleEdit(bucketName, ruleId)), [
73
+ navigate,
74
+ bucketName
75
+ ]);
76
+ return /*#__PURE__*/ jsx(BucketLifecycleList, {
77
+ bucketName: bucketName,
78
+ lifecycleRules: lifecycleRules,
79
+ lifecycleStatus: effectiveStatus,
80
+ onCreateRule: handleCreateRule,
81
+ onEditRule: handleEditRule
82
+ });
83
+ });
84
+ LifecycleTab.displayName = "BucketDetails.LifecycleTab";
85
+ const ReplicationTab = /*#__PURE__*/ memo(()=>{
86
+ const { bucketName, navigate } = useBucketDetailsContext();
21
87
  const { data: replicationData, status: replicationStatus, error: replicationError } = useGetBucketReplication({
22
88
  Bucket: bucketName
23
89
  });
24
- const lifecycleRules = lifecycleData?.Rules || [];
25
90
  const replicationRules = replicationData?.ReplicationConfiguration?.Rules || [];
26
91
  const replicationRole = replicationData?.ReplicationConfiguration?.Role || "";
27
- const effectiveLifecycleStatus = isNotFoundError(lifecycleError) ? "success" : "pending" === lifecycleStatus ? "loading" : "error" === lifecycleStatus ? "error" : "success";
28
- const effectiveReplicationStatus = isNotFoundError(replicationError) ? "success" : "pending" === replicationStatus ? "loading" : "error" === replicationStatus ? "error" : "success";
92
+ const effectiveStatus = getEffectiveStatus(replicationStatus, replicationError);
93
+ const handleCreateRule = useCallback(()=>navigate(BUCKET_ROUTES.replicationCreate(bucketName)), [
94
+ navigate,
95
+ bucketName
96
+ ]);
97
+ const handleEditRule = useCallback((ruleId)=>navigate(BUCKET_ROUTES.replicationEdit(bucketName, ruleId)), [
98
+ navigate,
99
+ bucketName
100
+ ]);
101
+ return /*#__PURE__*/ jsx(BucketReplicationList, {
102
+ bucketName: bucketName,
103
+ replicationRules: replicationRules,
104
+ replicationRole: replicationRole,
105
+ replicationStatus: effectiveStatus,
106
+ onCreateRule: handleCreateRule,
107
+ onEditRule: handleEditRule
108
+ });
109
+ });
110
+ ReplicationTab.displayName = "BucketDetails.ReplicationTab";
111
+ const CustomTab = ({ config })=>/*#__PURE__*/ jsx(Fragment, {
112
+ children: config.render()
113
+ });
114
+ CustomTab.displayName = "BucketDetails.CustomTab";
115
+ const BucketDetailsRoot = ({ children })=>{
116
+ const { bucketName } = useParams();
117
+ const navigate = useNavigate();
29
118
  if (!bucketName) return /*#__PURE__*/ jsx("div", {
30
119
  children: "No bucket selected"
31
120
  });
32
- return /*#__PURE__*/ jsx(Fragment, {
33
- children: /*#__PURE__*/ jsxs(Tabs, {
34
- children: [
35
- /*#__PURE__*/ jsx(Tabs.Tab, {
36
- label: "Overview",
37
- path: "",
38
- query: {
39
- ...queryObject,
40
- tab: ""
41
- },
42
- children: /*#__PURE__*/ jsxs(BucketOverview, {
43
- bucketName: bucketName,
44
- children: [
45
- /*#__PURE__*/ jsx(BucketOverview.Actions, {
46
- renderDeleteButton: ()=>/*#__PURE__*/ jsx(DeleteBucketButton, {
47
- bucketName: bucketName
48
- }),
49
- renderEmptyButton: ()=>/*#__PURE__*/ jsx(EmptyBucketButton, {
50
- bucketName: bucketName
51
- })
52
- }),
53
- /*#__PURE__*/ jsxs(BucketOverview.Sections, {
54
- children: [
55
- /*#__PURE__*/ jsx(BucketOverview.GeneralSection, {
56
- renderVersioning: ()=>/*#__PURE__*/ jsx(BucketVersioning, {})
57
- }),
58
- /*#__PURE__*/ jsx(BucketOverview.DataProtectionSection, {}),
59
- /*#__PURE__*/ jsx(BucketOverview.PermissionsSection, {
60
- onEditPolicy: ()=>{
61
- navigate(`/buckets/${bucketName}/policy`);
62
- }
63
- })
64
- ]
65
- })
66
- ]
67
- })
68
- }),
69
- /*#__PURE__*/ jsx(Tabs.Tab, {
70
- label: "Lifecycle",
71
- path: "",
72
- query: {
73
- ...queryObject,
74
- tab: "lifecycle"
75
- },
76
- withoutPadding: true,
77
- children: /*#__PURE__*/ jsx(BucketLifecycleList, {
78
- bucketName: bucketName,
79
- lifecycleRules: lifecycleRules,
80
- lifecycleStatus: effectiveLifecycleStatus,
81
- onCreateRule: ()=>navigate(`/buckets/${bucketName}/lifecycle/create`),
82
- onEditRule: (ruleId)=>navigate(`/buckets/${bucketName}/lifecycle/edit/${encodeURIComponent(ruleId)}`)
83
- })
84
- }),
85
- /*#__PURE__*/ jsx(Tabs.Tab, {
86
- label: "Replication",
87
- path: "",
88
- query: {
89
- ...queryObject,
90
- tab: "replication"
91
- },
92
- withoutPadding: true,
93
- children: /*#__PURE__*/ jsx(BucketReplicationList, {
94
- bucketName: bucketName,
95
- replicationRules: replicationRules,
96
- replicationRole: replicationRole,
97
- replicationStatus: effectiveReplicationStatus,
98
- onCreateRule: ()=>navigate(`/buckets/${bucketName}/replication/create`),
99
- onEditRule: (ruleId)=>navigate(`/buckets/${bucketName}/replication/edit/${encodeURIComponent(ruleId)}`)
100
- })
121
+ const contextValue = useMemo(()=>({
122
+ bucketName,
123
+ navigate
124
+ }), [
125
+ bucketName,
126
+ navigate
127
+ ]);
128
+ return /*#__PURE__*/ jsx(BucketDetailsContext.Provider, {
129
+ value: contextValue,
130
+ children: children || /*#__PURE__*/ jsx(DefaultTabs, {})
131
+ });
132
+ };
133
+ BucketDetailsRoot.displayName = "BucketDetails";
134
+ const DefaultTabs = ()=>{
135
+ const { extraBucketTabs } = useDataBrowserUICustomization();
136
+ const defaultTabsMap = useMemo(()=>({
137
+ overview: {
138
+ id: "overview",
139
+ label: "Overview",
140
+ path: "",
141
+ withoutPadding: false,
142
+ query: {
143
+ tab: ""
144
+ },
145
+ type: "default",
146
+ Component: OverviewTab
147
+ },
148
+ lifecycle: {
149
+ id: "lifecycle",
150
+ label: "Lifecycle",
151
+ path: "",
152
+ withoutPadding: true,
153
+ query: {
154
+ tab: "lifecycle"
155
+ },
156
+ type: "default",
157
+ Component: LifecycleTab
158
+ },
159
+ replication: {
160
+ id: "replication",
161
+ label: "Replication",
162
+ path: "",
163
+ withoutPadding: true,
164
+ query: {
165
+ tab: "replication"
166
+ },
167
+ type: "default",
168
+ Component: ReplicationTab
169
+ }
170
+ }), []);
171
+ const allTabs = useMemo(()=>{
172
+ const customTabConfigs = extraBucketTabs ? extraBucketTabs.map((tabConfig)=>({
173
+ id: tabConfig.id,
174
+ label: tabConfig.title,
175
+ path: tabConfig.path || "",
176
+ query: {
177
+ tab: tabConfig.id
178
+ },
179
+ withoutPadding: tabConfig.withoutPadding || false,
180
+ type: "custom",
181
+ config: tabConfig
182
+ })) : [];
183
+ const overrideIds = new Set(customTabConfigs.filter((t)=>t.id in defaultTabsMap).map((t)=>t.id));
184
+ const defaults = Object.values(defaultTabsMap).filter((tab)=>!overrideIds.has(tab.id));
185
+ return [
186
+ ...defaults,
187
+ ...customTabConfigs
188
+ ];
189
+ }, [
190
+ extraBucketTabs,
191
+ defaultTabsMap
192
+ ]);
193
+ return /*#__PURE__*/ jsx(Tabs, {
194
+ children: allTabs.map((tab)=>/*#__PURE__*/ jsx(Tabs.Tab, {
195
+ label: tab.label,
196
+ path: tab.path,
197
+ query: tab.query,
198
+ withoutPadding: tab.withoutPadding,
199
+ children: "default" === tab.type ? /*#__PURE__*/ jsx(tab.Component, {}) : /*#__PURE__*/ jsx(CustomTab, {
200
+ config: tab.config
101
201
  })
102
- ]
103
- })
202
+ }, tab.id))
104
203
  });
204
+ };
205
+ DefaultTabs.displayName = "BucketDetails.DefaultTabs";
206
+ function BucketDetails() {
207
+ return /*#__PURE__*/ jsx(BucketDetailsRoot, {});
105
208
  }
106
- export { BucketDetails };
209
+ const BucketDetailsCompound = BucketDetailsRoot;
210
+ BucketDetailsCompound.OverviewTab = OverviewTab;
211
+ BucketDetailsCompound.LifecycleTab = LifecycleTab;
212
+ BucketDetailsCompound.ReplicationTab = ReplicationTab;
213
+ BucketDetailsCompound.CustomTab = CustomTab;
214
+ export { BucketDetails, BucketDetailsCompound, BucketDetailsContext, useBucketDetailsContext };
@@ -1,15 +1,14 @@
1
1
  import type { Bucket } from "@aws-sdk/client-s3";
2
- import type { Column } from "@scality/core-ui/dist/components/tablev2/Tablev2.component";
3
- interface BucketListProps<TData extends Bucket & Record<string, unknown> = Bucket & Record<string, unknown>> {
2
+ interface BucketListProps {
4
3
  buckets: Bucket[];
5
4
  bucketStatus?: "idle" | "loading" | "error" | "success";
6
5
  selectedBucketName?: string | null;
7
6
  onBucketSelect?: (bucketName: string) => void;
8
7
  onCreateBucket?: () => void;
9
8
  onNavigateToBucket?: (bucketName: string) => void;
10
- renderBucketLocation?: (bucketName: string) => React.ReactNode;
11
- additionalColumns?: Column<TData>[];
12
- transformBucketData?: (bucket: Bucket) => TData;
13
9
  }
14
- export declare function BucketList<TData extends Bucket & Record<string, unknown> = Bucket & Record<string, unknown>>({ buckets, bucketStatus, selectedBucketName, onBucketSelect, onCreateBucket, onNavigateToBucket, renderBucketLocation, additionalColumns, transformBucketData, }: BucketListProps<TData>): import("react/jsx-runtime").JSX.Element;
10
+ export declare function BucketList({ buckets, bucketStatus, selectedBucketName, onBucketSelect, onCreateBucket, onNavigateToBucket, }: BucketListProps): import("react/jsx-runtime").JSX.Element;
11
+ export declare namespace BucketList {
12
+ var displayName: string;
13
+ }
15
14
  export {};
@@ -1,101 +1,162 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { Fragment, useCallback, useMemo } from "react";
2
3
  import { ConstrainedText, FormattedDateTime, Icon, Link, Wrap, spacing } from "@scality/core-ui";
3
4
  import { Box, Button, Table } from "@scality/core-ui/dist/next";
4
- import { useMemo } from "react";
5
+ import { useDataBrowserUICustomization } from "../../contexts/DataBrowserUICustomizationContext.js";
6
+ import { BucketLocation } from "./BucketLocation.js";
5
7
  const SEARCH_QUERY_PARAM = "search";
6
- function BucketList({ buckets, bucketStatus, selectedBucketName, onBucketSelect, onCreateBucket, onNavigateToBucket, renderBucketLocation, additionalColumns, transformBucketData }) {
8
+ const LOCATION_COLUMN_FLEX = "1.2";
9
+ const createBucketNameColumn = (onNavigateToBucket)=>({
10
+ Header: "Bucket Name",
11
+ accessor: "Name",
12
+ id: "name",
13
+ Cell: ({ value })=>{
14
+ if (!value) return /*#__PURE__*/ jsx("span", {
15
+ children: "-"
16
+ });
17
+ return /*#__PURE__*/ jsx(ConstrainedText, {
18
+ text: /*#__PURE__*/ jsx(Link, {
19
+ onClick: (e)=>{
20
+ e.stopPropagation();
21
+ onNavigateToBucket?.(value);
22
+ },
23
+ children: value
24
+ }),
25
+ lineClamp: 2
26
+ });
27
+ },
28
+ cellStyle: {
29
+ flex: "1",
30
+ width: "unset"
31
+ }
32
+ });
33
+ const createLocationColumn = ()=>({
34
+ Header: "Storage Location",
35
+ accessor: "Name",
36
+ id: "location",
37
+ Cell: ({ value })=>{
38
+ if (!value) return /*#__PURE__*/ jsx("span", {
39
+ children: "-"
40
+ });
41
+ return /*#__PURE__*/ jsx(BucketLocation, {
42
+ bucketName: value
43
+ });
44
+ },
45
+ cellStyle: {
46
+ width: "unset",
47
+ flex: LOCATION_COLUMN_FLEX
48
+ }
49
+ });
50
+ const createDateColumn = ()=>({
51
+ Header: "Created on",
52
+ accessor: "CreationDate",
53
+ id: "date",
54
+ cellStyle: {
55
+ flex: "1",
56
+ textAlign: "right",
57
+ paddingRight: spacing.r16,
58
+ width: "unset"
59
+ },
60
+ Cell: ({ value })=>{
61
+ if (!value) return /*#__PURE__*/ jsx("span", {
62
+ children: "-"
63
+ });
64
+ return /*#__PURE__*/ jsx(FormattedDateTime, {
65
+ format: "date-time-second",
66
+ value: new Date(value)
67
+ });
68
+ }
69
+ });
70
+ const buildCustomColumn = (columnConfig)=>({
71
+ Header: columnConfig.header,
72
+ id: String(columnConfig.id),
73
+ Cell: ({ row })=>{
74
+ const RenderComponent = columnConfig.render;
75
+ return /*#__PURE__*/ jsx(RenderComponent, {
76
+ data: row.original
77
+ });
78
+ },
79
+ cellStyle: {
80
+ width: columnConfig.width ?? "unset",
81
+ flex: columnConfig.width ? void 0 : "1"
82
+ }
83
+ });
84
+ function createOverrideMap(customItems) {
85
+ return new Map(customItems.filter((item)=>item.id).map((item)=>[
86
+ String(item.id),
87
+ item
88
+ ]));
89
+ }
90
+ function BucketList({ buckets, bucketStatus, selectedBucketName, onBucketSelect, onCreateBucket, onNavigateToBucket }) {
91
+ const { extraBucketListColumns, extraBucketListActions } = useDataBrowserUICustomization();
7
92
  const columns = useMemo(()=>{
8
- const baseColumns = [
9
- {
10
- Header: "Bucket Name",
11
- accessor: "Name",
12
- id: "name",
13
- Cell: ({ value, row })=>{
14
- const name = value || row.original?.Name;
15
- if (!name) return /*#__PURE__*/ jsx("span", {
16
- children: "-"
17
- });
18
- return /*#__PURE__*/ jsx(ConstrainedText, {
19
- text: /*#__PURE__*/ jsx(Link, {
20
- onClick: (e)=>{
21
- e.stopPropagation();
22
- onNavigateToBucket?.(name);
23
- },
24
- children: name
25
- }),
26
- lineClamp: 2
27
- });
28
- },
29
- cellStyle: {
30
- flex: "1",
31
- width: "unset"
32
- }
33
- },
34
- {
35
- Header: "Storage Location",
36
- accessor: "Name",
37
- id: "location",
38
- Cell ({ value }) {
39
- return renderBucketLocation ? renderBucketLocation(value) : /*#__PURE__*/ jsx("span", {
40
- children: "-"
41
- });
42
- },
43
- cellStyle: {
44
- width: "unset",
45
- flex: "1.2"
46
- }
47
- }
93
+ const defaultColumnsMap = {
94
+ name: createBucketNameColumn(onNavigateToBucket),
95
+ location: createLocationColumn(),
96
+ date: createDateColumn()
97
+ };
98
+ const customColumns = (extraBucketListColumns || []).map((config)=>buildCustomColumn(config));
99
+ const customColumnsMap = createOverrideMap(customColumns);
100
+ const getColumn = (id)=>customColumnsMap.get(id) || defaultColumnsMap[id];
101
+ const extraColumns = customColumns.filter((col)=>col.id && !(col.id in defaultColumnsMap));
102
+ return [
103
+ getColumn("name"),
104
+ getColumn("location"),
105
+ ...extraColumns,
106
+ getColumn("date")
48
107
  ];
49
- if (additionalColumns) baseColumns.push(...additionalColumns);
50
- baseColumns.push({
51
- Header: "Created on",
52
- accessor: "CreationDate",
53
- id: "date",
54
- cellStyle: {
55
- flex: "1",
56
- textAlign: "right",
57
- paddingRight: spacing.r16,
58
- width: "unset"
59
- },
60
- Cell: ({ value, row })=>{
61
- const date = value || row.original?.CreationDate;
62
- if (!date) return /*#__PURE__*/ jsx("span", {
63
- children: "-"
64
- });
65
- return /*#__PURE__*/ jsx(FormattedDateTime, {
66
- format: "date-time-second",
67
- value: new Date(date)
68
- });
69
- }
70
- });
71
- return baseColumns;
72
108
  }, [
73
109
  onNavigateToBucket,
74
- renderBucketLocation,
75
- additionalColumns
76
- ]);
77
- const tableData = useMemo(()=>buckets.map((bucket)=>{
78
- if (transformBucketData) return transformBucketData(bucket);
79
- const { Name, CreationDate, ...rest } = bucket;
80
- return {
81
- Name,
82
- CreationDate,
83
- ...rest
84
- };
85
- }), [
86
- buckets,
87
- transformBucketData
110
+ extraBucketListColumns
88
111
  ]);
89
112
  const selectedId = useMemo(()=>{
90
- if (buckets && selectedBucketName) return buckets.findIndex((bucket)=>bucket.Name === selectedBucketName);
91
- return null;
113
+ if (!buckets || !selectedBucketName) return;
114
+ const index = buckets.findIndex((bucket)=>bucket.Name === selectedBucketName);
115
+ return index >= 0 ? String(index) : void 0;
92
116
  }, [
93
117
  selectedBucketName,
94
118
  buckets
95
119
  ]);
120
+ const actions = useMemo(()=>{
121
+ const defaultActionsMap = {
122
+ createBucket: {
123
+ id: "createBucket",
124
+ render: ()=>/*#__PURE__*/ jsx(Button, {
125
+ icon: /*#__PURE__*/ jsx(Icon, {
126
+ name: "Create-add"
127
+ }),
128
+ label: "Create Bucket",
129
+ variant: "primary",
130
+ onClick: onCreateBucket,
131
+ type: "submit"
132
+ })
133
+ }
134
+ };
135
+ const customActions = (extraBucketListActions || []).map((config)=>({
136
+ id: config.id,
137
+ render: config.render
138
+ }));
139
+ const customActionsMap = createOverrideMap(customActions);
140
+ const getAction = (id)=>customActionsMap.get(id) || defaultActionsMap[id];
141
+ const extraActions = customActions.filter((action)=>!(action.id in defaultActionsMap));
142
+ return [
143
+ getAction("createBucket"),
144
+ ...extraActions
145
+ ];
146
+ }, [
147
+ onCreateBucket,
148
+ extraBucketListActions
149
+ ]);
150
+ const handleRowSelected = useCallback((row)=>{
151
+ const isSelected = selectedBucketName === row.original.Name;
152
+ if (!isSelected && row.original.Name) onBucketSelect?.(row.original.Name);
153
+ }, [
154
+ selectedBucketName,
155
+ onBucketSelect
156
+ ]);
96
157
  return /*#__PURE__*/ jsxs(Table, {
97
158
  columns: columns,
98
- data: tableData,
159
+ data: buckets,
99
160
  status: bucketStatus,
100
161
  defaultSortingKey: "CreationDate",
101
162
  entityName: {
@@ -118,29 +179,23 @@ function BucketList({ buckets, bucketStatus, selectedBucketName, onBucketSelect,
118
179
  })
119
180
  }),
120
181
  /*#__PURE__*/ jsx(Box, {
121
- gap: "r16",
122
- children: /*#__PURE__*/ jsx(Button, {
123
- icon: /*#__PURE__*/ jsx(Icon, {
124
- name: "Create-add"
125
- }),
126
- label: "Create Bucket",
127
- variant: "primary",
128
- onClick: onCreateBucket,
129
- type: "submit"
130
- })
182
+ display: "flex",
183
+ gap: spacing.r16,
184
+ alignItems: "center",
185
+ children: actions.map((action)=>/*#__PURE__*/ jsx(Fragment, {
186
+ children: action.render()
187
+ }, action.id))
131
188
  })
132
189
  ]
133
190
  }),
134
191
  /*#__PURE__*/ jsx(Table.SingleSelectableContent, {
135
192
  rowHeight: "h40",
136
- selectedId: selectedId?.toString(),
137
- onRowSelected: (row)=>{
138
- const isSelected = selectedBucketName === row.original.Name;
139
- if (!isSelected && row.original.Name) onBucketSelect?.(row.original.Name);
140
- },
193
+ selectedId: selectedId,
194
+ onRowSelected: handleRowSelected,
141
195
  separationLineVariant: "backgroundLevel1"
142
196
  })
143
197
  ]
144
198
  });
145
199
  }
200
+ BucketList.displayName = "BucketList";
146
201
  export { BucketList };
@@ -1,4 +1,10 @@
1
1
  import React from "react";
2
+ export type FieldItem = {
3
+ id: string;
4
+ label: string;
5
+ value: React.ReactNode;
6
+ actions?: React.ReactNode;
7
+ };
2
8
  interface BucketOverviewContextValue {
3
9
  bucketName: string;
4
10
  }