Skip to content

Commit d01dccc

Browse files
upcoming: [UIE-10425] - Add the new feature "Reserved IPs" to side nav (#13486)
1 parent f18fce5 commit d01dccc

13 files changed

Lines changed: 157 additions & 0 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@linode/manager": Upcoming Features
3+
---
4+
5+
Reserve IP: Add the new feature Reserved IPs to side nav ([#13486](https://github.com/linode/manager/pull/13486))

packages/manager/src/GoTo.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { useIsDatabasesEnabled } from './features/Databases/utilities';
77
import { useIsMarketplaceV2Enabled } from './features/Marketplace/shared';
88
import { useIsNetworkLoadBalancerEnabled } from './features/NetworkLoadBalancers/utils';
99
import { useIsPlacementGroupsEnabled } from './features/PlacementGroups/utils';
10+
import { useIsReserveIpEnabled } from './features/ReservedIps/utils';
1011
import { useGlobalKeyboardListener } from './hooks/useGlobalKeyboardListener';
1112

1213
import type { SelectOption } from '@linode/ui';
@@ -22,6 +23,7 @@ export const GoTo = React.memo(() => {
2223
const { isDatabasesEnabled } = useIsDatabasesEnabled();
2324
const { isMarketplaceV2FeatureEnabled } = useIsMarketplaceV2Enabled();
2425
const { isNetworkLoadBalancerEnabled } = useIsNetworkLoadBalancerEnabled();
26+
const { isReserveIpEnabled } = useIsReserveIpEnabled();
2527

2628
const { goToOpen, setGoToOpen } = useGlobalKeyboardListener();
2729

@@ -63,6 +65,11 @@ export const GoTo = React.memo(() => {
6365
display: 'NodeBalancers',
6466
href: '/nodebalancers',
6567
},
68+
{
69+
display: 'Reserved IPs',
70+
hide: !isReserveIpEnabled,
71+
href: '/reserved-ips',
72+
},
6673
{
6774
display: 'Firewalls',
6875
href: '/firewalls',
@@ -130,6 +137,7 @@ export const GoTo = React.memo(() => {
130137
isMarketplaceV2FeatureEnabled,
131138
isNetworkLoadBalancerEnabled,
132139
isPlacementGroupsEnabled,
140+
isReserveIpEnabled,
133141
]
134142
);
135143

packages/manager/src/components/PrimaryNav/PrimaryNav.test.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -604,4 +604,18 @@ describe('PrimaryNav', () => {
604604

605605
expect(partnerReferralNavItem).toBeVisible();
606606
});
607+
608+
it('should show Reserved IPs menu item if the corresponding flag is enabled', async () => {
609+
const flags: Partial<Flags> = {
610+
reserveIp: true,
611+
};
612+
613+
const { findByTestId } = renderWithTheme(<PrimaryNav {...props} />, {
614+
flags,
615+
});
616+
617+
const reservedIpsNavItem = await findByTestId('menu-item-Reserved IPs');
618+
619+
expect(reservedIpsNavItem).toBeVisible();
620+
});
607621
});

packages/manager/src/components/PrimaryNav/PrimaryNav.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import { useIsIAMEnabled } from 'src/features/IAM/hooks/useIsIAMEnabled';
2525
import { useIsMarketplaceV2Enabled } from 'src/features/Marketplace/shared';
2626
import { useIsNetworkLoadBalancerEnabled } from 'src/features/NetworkLoadBalancers/utils';
2727
import { useIsPlacementGroupsEnabled } from 'src/features/PlacementGroups/utils';
28+
import { useIsReserveIpEnabled } from 'src/features/ReservedIps/utils';
2829
import { useFlags } from 'src/hooks/useFlags';
2930

3031
import PrimaryLink from './PrimaryLink';
@@ -65,6 +66,7 @@ export type NavEntity =
6566
| 'Placement Groups'
6667
| 'Quick Deploy Apps'
6768
| 'Quotas'
69+
| 'Reserved IPs'
6870
| 'Service Transfers'
6971
| 'StackScripts'
7072
| 'Users & Grants'
@@ -127,6 +129,8 @@ export const PrimaryNav = (props: PrimaryNavProps) => {
127129

128130
const { isMarketplaceV2FeatureEnabled } = useIsMarketplaceV2Enabled();
129131

132+
const { isReserveIpEnabled } = useIsReserveIpEnabled();
133+
130134
const {
131135
data: preferences,
132136
error: preferencesError,
@@ -234,6 +238,11 @@ export const PrimaryNav = (props: PrimaryNavProps) => {
234238
display: 'NodeBalancers',
235239
to: '/nodebalancers',
236240
},
241+
{
242+
display: 'Reserved IPs',
243+
hide: !isReserveIpEnabled,
244+
to: '/reserved-ips',
245+
},
237246
{
238247
display: 'Domains',
239248
to: '/domains',
@@ -357,6 +366,7 @@ export const PrimaryNav = (props: PrimaryNavProps) => {
357366
isIAMEnabled,
358367
isMarketplaceV2FeatureEnabled,
359368
isNetworkLoadBalancerEnabled,
369+
isReserveIpEnabled,
360370
limitsEvolution,
361371
]
362372
);

packages/manager/src/dev-tools/FeatureFlagTool.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ const options: { flag: keyof Flags; label: string }[] = [
9595
},
9696
{ flag: 'objSummaryPage', label: 'OBJ Summary Page' },
9797
{ flag: 'vpcIpv6', label: 'VPC IPv6' },
98+
{ flag: 'reserveIp', label: 'Reserve IP' },
9899
{ flag: 'marketplaceV2GlobalBanner', label: 'Marketplace V2 Global Banner' },
99100
];
100101

packages/manager/src/featureFlags.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,7 @@ export interface Flags {
274274
promos: boolean;
275275
promotionalOffers: PromotionalOffer[];
276276
referralBannerText: BannerContent;
277+
reserveIp: boolean;
277278
resourceLock: ResourceLockFlag;
278279
secureVmCopy: SecureVMCopy;
279280
selfServeBetas: boolean;
@@ -392,6 +393,7 @@ export type ProductInformationBannerLocation =
392393
| 'NodeBalancers'
393394
| 'Object Storage'
394395
| 'Placement Groups'
396+
| 'Reserved IPs'
395397
| 'StackScripts'
396398
| 'Volumes'
397399
| 'VPC';
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { Notice } from '@linode/ui';
2+
import * as React from 'react';
3+
4+
import { LandingHeader } from 'src/components/LandingHeader';
5+
6+
export const ReservedIpsLanding = () => {
7+
return (
8+
<>
9+
<LandingHeader
10+
breadcrumbProps={{
11+
pathname: 'Reserved IPs',
12+
removeCrumbX: 1,
13+
}}
14+
spacingBottom={16}
15+
title="Reserved IPs"
16+
/>
17+
<Notice variant="info">Reserved IPs is coming soon...</Notice>
18+
</>
19+
);
20+
};
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { createLazyRoute } from '@tanstack/react-router';
2+
3+
import { ReservedIpsLanding } from './ReservedIpsLanding';
4+
5+
export const reservedIpsLazyRoute = createLazyRoute('/reserved-ips')({
6+
component: ReservedIpsLanding,
7+
});
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { renderHook, waitFor } from '@testing-library/react';
2+
3+
import { wrapWithTheme } from 'src/utilities/testHelpers';
4+
5+
import { useIsReserveIpEnabled } from './utils';
6+
7+
describe('useIsReserveIpEnabled', () => {
8+
it('returns true if the feature is enabled', async () => {
9+
const options = { flags: { reserveIp: true } };
10+
11+
const { result } = renderHook(() => useIsReserveIpEnabled(), {
12+
wrapper: (ui) => wrapWithTheme(ui, options),
13+
});
14+
15+
await waitFor(() => {
16+
expect(result.current.isReserveIpEnabled).toBe(true);
17+
});
18+
});
19+
20+
it('returns false if the feature is NOT enabled', async () => {
21+
const options = { flags: { reserveIp: false } };
22+
23+
const { result } = renderHook(() => useIsReserveIpEnabled(), {
24+
wrapper: (ui) => wrapWithTheme(ui, options),
25+
});
26+
27+
await waitFor(() => {
28+
expect(result.current.isReserveIpEnabled).toBe(false);
29+
});
30+
});
31+
});
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { useFlags } from 'src/hooks/useFlags';
2+
3+
/**
4+
*
5+
* @returns an object that contains boolean property to check whether Reserved IP is enabled or not
6+
*/
7+
export const useIsReserveIpEnabled = () => {
8+
const flags = useFlags();
9+
10+
// @TODO ReservedIps: check for customer tag/account capability when it exists
11+
12+
return { isReserveIpEnabled: flags.reserveIp ?? false };
13+
};

0 commit comments

Comments
 (0)