Skip to content

Commit 3043445

Browse files
refactor: STORIF-335 - BucketDrawerOutlet component created.
1 parent e0b00db commit 3043445

8 files changed

Lines changed: 152 additions & 91 deletions

File tree

packages/manager/src/features/ObjectStorage/BucketLanding/BucketDetailsDrawer.test.tsx

Lines changed: 19 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ const queryMocks = vi.hoisted(() => ({
3434
useProfile: vi.fn().mockReturnValue({}),
3535
useRegionQuery: vi.fn().mockReturnValue({}),
3636
useRegionsQuery: vi.fn().mockReturnValue({}),
37+
useObjectStorageBucket: vi.fn().mockReturnValue({}),
3738
}));
3839

3940
// Mock the queries
@@ -59,6 +60,7 @@ vi.mock('src/queries/object-storage/queries', async () => {
5960
return {
6061
...actual,
6162
useObjectStorageClusters: queryMocks.useObjectStorageClusters,
63+
useObjectStorageBucket: queryMocks.useObjectStorageBucket,
6264
};
6365
});
6466

@@ -78,6 +80,7 @@ describe('BucketDetailsDrawer: Legacy UI', () => {
7880
queryMocks.useRegionQuery.mockReturnValue({ data: region });
7981
queryMocks.useRegionsQuery.mockReturnValue({ data: [region] });
8082
queryMocks.useObjectStorageClusters.mockReturnValue({ data: [] });
83+
queryMocks.useObjectStorageBucket.mockReturnValue({ data: bucket });
8184

8285
// These utils are used in the component
8386
vi.mocked(formatDate).mockReturnValue('2019-12-12');
@@ -91,13 +94,7 @@ describe('BucketDetailsDrawer: Legacy UI', () => {
9194

9295
it('renders correctly when open', () => {
9396
renderWithThemeAndHookFormContext({
94-
component: (
95-
<BucketDetailsDrawer
96-
onClose={mockOnClose}
97-
open={true}
98-
selectedBucket={bucket}
99-
/>
100-
),
97+
component: <BucketDetailsDrawer isOpen={true} onClose={mockOnClose} />,
10198
});
10299

103100
expect(screen.getByText(bucket.label)).toBeInTheDocument();
@@ -112,41 +109,25 @@ describe('BucketDetailsDrawer: Legacy UI', () => {
112109

113110
it('does not render when closed', () => {
114111
renderWithThemeAndHookFormContext({
115-
component: (
116-
<BucketDetailsDrawer
117-
onClose={mockOnClose}
118-
open={false}
119-
selectedBucket={bucket}
120-
/>
121-
),
112+
component: <BucketDetailsDrawer isOpen={false} onClose={mockOnClose} />,
122113
});
123114

124115
expect(screen.queryByText(bucket.label)).not.toBeInTheDocument();
125116
});
126117

127118
it('renders correctly with objMultiCluster disabled', () => {
128119
renderWithThemeAndHookFormContext({
129-
component: (
130-
<BucketDetailsDrawer
131-
onClose={mockOnClose}
132-
open={true}
133-
selectedBucket={bucket}
134-
/>
135-
),
120+
component: <BucketDetailsDrawer isOpen={true} onClose={mockOnClose} />,
136121
});
137122

138123
expect(screen.getByTestId('cluster')).toHaveTextContent(region.id);
139124
});
140125

141126
it('handles undefined selectedBucket gracefully', () => {
127+
queryMocks.useObjectStorageBucket.mockReturnValue({ data: undefined });
128+
142129
renderWithThemeAndHookFormContext({
143-
component: (
144-
<BucketDetailsDrawer
145-
onClose={mockOnClose}
146-
open={true}
147-
selectedBucket={undefined}
148-
/>
149-
),
130+
component: <BucketDetailsDrawer isOpen={true} onClose={mockOnClose} />,
150131
});
151132

152133
expect(screen.getByText('Bucket Detail')).toBeInTheDocument();
@@ -156,13 +137,7 @@ describe('BucketDetailsDrawer: Legacy UI', () => {
156137

157138
it('renders AccessSelect when cluster and bucketLabel are available', async () => {
158139
renderWithThemeAndHookFormContext({
159-
component: (
160-
<BucketDetailsDrawer
161-
onClose={mockOnClose}
162-
open={true}
163-
selectedBucket={bucket}
164-
/>
165-
),
140+
component: <BucketDetailsDrawer isOpen={true} onClose={mockOnClose} />,
166141
options: {
167142
flags: { objectStorageGen2: { enabled: true } },
168143
},
@@ -177,14 +152,10 @@ describe('BucketDetailsDrawer: Legacy UI', () => {
177152

178153
it('does not render AccessSelect when cluster or bucketLabel is missing', async () => {
179154
const bucketWithoutCluster = { ...bucket, cluster: '' };
155+
queryMocks.useObjectStorageBucket.mockReturnValue(bucketWithoutCluster);
156+
180157
renderWithThemeAndHookFormContext({
181-
component: (
182-
<BucketDetailsDrawer
183-
onClose={mockOnClose}
184-
open={true}
185-
selectedBucket={bucketWithoutCluster}
186-
/>
187-
),
158+
component: <BucketDetailsDrawer isOpen={true} onClose={mockOnClose} />,
188159
options: {
189160
flags: { objectStorageGen2: { enabled: true } },
190161
},
@@ -205,15 +176,13 @@ describe('BucketDetailDrawer: Gen2 UI', () => {
205176
id: e3Bucket.region,
206177
});
207178

179+
beforeEach(() => {
180+
queryMocks.useObjectStorageBucket.mockReturnValue({ data: e3Bucket });
181+
});
182+
208183
it('renders correctly when open', () => {
209184
renderWithThemeAndHookFormContext({
210-
component: (
211-
<BucketDetailsDrawer
212-
onClose={mockOnClose}
213-
open={true}
214-
selectedBucket={e3Bucket}
215-
/>
216-
),
185+
component: <BucketDetailsDrawer isOpen={true} onClose={mockOnClose} />,
217186
options: {
218187
flags: { objectStorageGen2: { enabled: true } },
219188
},
@@ -234,13 +203,7 @@ describe('BucketDetailDrawer: Gen2 UI', () => {
234203

235204
it("doesn't show the CORS switch for E2 and E3 buckets", async () => {
236205
const { getByText } = renderWithThemeAndHookFormContext({
237-
component: (
238-
<BucketDetailsDrawer
239-
onClose={mockOnClose}
240-
open={true}
241-
selectedBucket={e3Bucket}
242-
/>
243-
),
206+
component: <BucketDetailsDrawer isOpen={true} onClose={mockOnClose} />,
244207
options: {
245208
flags: { objectStorageGen2: { enabled: true } },
246209
},

packages/manager/src/features/ObjectStorage/BucketLanding/BucketDetailsDrawer.tsx

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,32 @@ import { useProfile, useRegionQuery, useRegionsQuery } from '@linode/queries';
22
import { Divider, Drawer, Typography } from '@linode/ui';
33
import { pluralize, readableBytes, truncateMiddle } from '@linode/utilities';
44
import { styled } from '@mui/material/styles';
5+
import { useParams } from '@tanstack/react-router';
56
import * as React from 'react';
67

78
import { CopyTooltip } from 'src/components/CopyTooltip/CopyTooltip';
89
import { Link } from 'src/components/Link';
910
import { MaskableText } from 'src/components/MaskableText/MaskableText';
10-
import { useObjectStorageClusters } from 'src/queries/object-storage/queries';
11+
import {
12+
useObjectStorageBucket,
13+
useObjectStorageClusters,
14+
} from 'src/queries/object-storage/queries';
1115
import { formatDate } from 'src/utilities/formatDate';
1216

1317
import { AccessSelect } from '../BucketDetail/AccessTab/AccessSelect';
1418
import { useIsObjMultiClusterEnabled } from '../hooks/useIsObjectStorageGen2Enabled';
1519

16-
import type { ObjectStorageBucket } from '@linode/api-v4/lib/object-storage';
17-
1820
export interface BucketDetailsDrawerProps {
21+
isOpen: boolean;
1922
onClose: () => void;
20-
open: boolean;
21-
selectedBucket: ObjectStorageBucket | undefined;
2223
}
2324

2425
export const BucketDetailsDrawer = React.memo(
2526
(props: BucketDetailsDrawerProps) => {
26-
const { onClose, open, selectedBucket } = props;
27+
const { onClose, isOpen } = props;
28+
const { regionId, bucketName } = useParams({ strict: false });
29+
30+
const { data: bucket } = useObjectStorageBucket(regionId, bucketName);
2731

2832
const {
2933
cluster,
@@ -34,7 +38,7 @@ export const BucketDetailsDrawer = React.memo(
3438
objects,
3539
region,
3640
size,
37-
} = selectedBucket ?? {};
41+
} = bucket ?? {};
3842

3943
const { isObjMultiClusterEnabled } = useIsObjMultiClusterEnabled();
4044

@@ -65,7 +69,7 @@ export const BucketDetailsDrawer = React.memo(
6569
return (
6670
<Drawer
6771
onClose={onClose}
68-
open={open}
72+
open={isOpen}
6973
title={truncateMiddle(label ?? 'Bucket Detail')}
7074
>
7175
{formattedCreated && (
@@ -109,7 +113,7 @@ export const BucketDetailsDrawer = React.memo(
109113
{typeof objects === 'number' && (
110114
<Link
111115
to={`/object-storage/buckets/${
112-
isObjMultiClusterEnabled && selectedBucket ? region : cluster
116+
isObjMultiClusterEnabled && bucket ? region : cluster
113117
}/${label}`}
114118
>
115119
{pluralize('object', 'objects', objects)}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import React from 'react';
2+
3+
import { BucketDetailsDrawer } from './BucketDetailsDrawer';
4+
import { CreateBucketDrawer } from './CreateBucketDrawer';
5+
import { useBucketDrawers } from './hooks/useBucketDrawers';
6+
7+
export const BucketDrawerOutlet = () => {
8+
const { drawer, closeDrawer } = useBucketDrawers();
9+
10+
return (
11+
<>
12+
<CreateBucketDrawer
13+
isOpen={drawer === 'create-bucket'}
14+
onClose={closeDrawer}
15+
/>
16+
17+
<BucketDetailsDrawer
18+
isOpen={drawer === 'bucket-details'}
19+
onClose={closeDrawer}
20+
/>
21+
</>
22+
);
23+
};

packages/manager/src/features/ObjectStorage/BucketLanding/OMC_BucketLanding.tsx

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ import {
2121
} from 'src/utilities/analytics/customEventAnalytics';
2222

2323
import { CancelNotice } from '../CancelNotice';
24-
import { BucketDetailsDrawer } from './BucketDetailsDrawer';
2524
import { BucketLandingEmptyState } from './BucketLandingEmptyState';
2625
import { BucketTable } from './BucketTable';
26+
import { useBucketDrawers } from './hooks/useBucketDrawers';
2727

2828
import type { APIError, ObjectStorageBucket } from '@linode/api-v4';
2929
import type { Theme } from '@mui/material/styles';
@@ -55,26 +55,17 @@ export const OMC_BucketLanding = (props: Props) => {
5555

5656
const { classes } = useStyles();
5757

58+
const { openDrawer } = useBucketDrawers();
59+
5860
const removeBucketConfirmationDialog = useOpenClose();
5961

6062
const [isLoading, setIsLoading] = React.useState<boolean>(false);
6163
const [error, setError] = React.useState<APIError[] | undefined>(undefined);
62-
const [bucketDetailDrawerOpen, setBucketDetailDrawerOpen] =
63-
React.useState<boolean>(false);
6464

6565
const [selectedBucket, setSelectedBucket] = React.useState<
6666
ObjectStorageBucket | undefined
6767
>(undefined);
6868

69-
const handleClickDetails = (bucket: ObjectStorageBucket) => {
70-
setBucketDetailDrawerOpen(true);
71-
setSelectedBucket(bucket);
72-
};
73-
74-
const closeBucketDetailDrawer = () => {
75-
setBucketDetailDrawerOpen(false);
76-
};
77-
7869
const handleClickRemove = (bucket: ObjectStorageBucket) => {
7970
setSelectedBucket(bucket);
8071
setError(undefined);
@@ -200,7 +191,9 @@ export const OMC_BucketLanding = (props: Props) => {
200191
<Grid size={12}>
201192
<BucketTable
202193
data={orderedData ?? []}
203-
handleClickDetails={handleClickDetails}
194+
handleClickDetails={(bucket) =>
195+
openDrawer('bucket-details', bucket.region, bucket.label)
196+
}
204197
handleClickRemove={handleClickRemove}
205198
handleOrderChange={handleOrderChange}
206199
order={order}
@@ -256,11 +249,6 @@ export const OMC_BucketLanding = (props: Props) => {
256249
Account Settings. */}
257250
{buckets.length === 1 && <CancelNotice className={classes.copy} />}
258251
</TypeToConfirmDialog>
259-
<BucketDetailsDrawer
260-
onClose={closeBucketDetailDrawer}
261-
open={bucketDetailDrawerOpen}
262-
selectedBucket={selectedBucket}
263-
/>
264252
</React.Fragment>
265253
);
266254
};
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { useMatch, useNavigate } from '@tanstack/react-router';
2+
3+
type BucketDrawers = 'bucket-details' | 'create-bucket';
4+
5+
const BUCKETS_BASE_URL = '/object-storage/buckets';
6+
7+
export const useBucketDrawers = () => {
8+
const navigate = useNavigate();
9+
const { routeId } = useMatch({ strict: false });
10+
11+
function getDrawer(): BucketDrawers | null {
12+
switch (routeId) {
13+
case `${BUCKETS_BASE_URL}/$regionId/$bucketName/details`:
14+
return 'bucket-details';
15+
case `${BUCKETS_BASE_URL}/create`:
16+
return 'create-bucket';
17+
default:
18+
return null;
19+
}
20+
}
21+
22+
function openDrawer(
23+
drawer: BucketDrawers,
24+
regionId?: string,
25+
bucketName?: string
26+
) {
27+
switch (drawer) {
28+
case 'bucket-details':
29+
navigate({
30+
to: `${BUCKETS_BASE_URL}/${regionId}/${bucketName}/details`,
31+
});
32+
break;
33+
case 'create-bucket':
34+
navigate({ to: `${BUCKETS_BASE_URL}/create` });
35+
break;
36+
}
37+
}
38+
39+
function closeDrawer() {
40+
navigate({ to: BUCKETS_BASE_URL });
41+
}
42+
43+
return {
44+
drawer: getDrawer(),
45+
openDrawer,
46+
closeDrawer,
47+
};
48+
};

packages/manager/src/features/ObjectStorage/ObjectStorageLanding.tsx

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { useObjectStorageBuckets } from 'src/queries/object-storage/queries';
1818

1919
import { getRestrictedResourceText } from '../Account/utils';
2020
import { BillingNotice } from './BillingNotice';
21-
import { CreateBucketDrawer } from './BucketLanding/CreateBucketDrawer';
21+
import { BucketDrawerOutlet } from './BucketLanding/BucketDrawerOutlet';
2222
import { OMC_BucketLanding } from './BucketLanding/OMC_BucketLanding';
2323

2424
import type { MODE } from './AccessKeyLanding/types';
@@ -123,7 +123,7 @@ export const ObjectStorageLanding = () => {
123123
}
124124

125125
return (
126-
<React.Fragment>
126+
<>
127127
<DocumentTitleSegment
128128
segment={`${
129129
isCreateBucketOpen && !objectStorageBucketsResponse?.buckets.length
@@ -189,13 +189,10 @@ export const ObjectStorageLanding = () => {
189189
</SafeTabPanel>
190190
</TabPanels>
191191
</React.Suspense>
192-
193-
<CreateBucketDrawer
194-
isOpen={isCreateBucketOpen}
195-
onClose={() => navigate({ to: '/object-storage/buckets' })}
196-
/>
197192
</Tabs>
198-
</React.Fragment>
193+
194+
<BucketDrawerOutlet />
195+
</>
199196
);
200197
};
201198

0 commit comments

Comments
 (0)