diff --git a/packages/manager/.changeset/pr-13164-upcoming-features-1764838771826.md b/packages/manager/.changeset/pr-13164-upcoming-features-1764838771826.md new file mode 100644 index 00000000000..8276febb6be --- /dev/null +++ b/packages/manager/.changeset/pr-13164-upcoming-features-1764838771826.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Upcoming Features +--- + +Add Beta/New feature Chip support for RuleSets and Prefix Lists ([#13164](https://github.com/linode/manager/pull/13164)) diff --git a/packages/manager/src/features/Firewalls/FirewallDetail/Rules/FirewallPrefixListDrawer.tsx b/packages/manager/src/features/Firewalls/FirewallDetail/Rules/FirewallPrefixListDrawer.tsx index a1292f39f11..083634c3063 100644 --- a/packages/manager/src/features/Firewalls/FirewallDetail/Rules/FirewallPrefixListDrawer.tsx +++ b/packages/manager/src/features/Firewalls/FirewallDetail/Rules/FirewallPrefixListDrawer.tsx @@ -7,7 +7,10 @@ import ArrowLeftIcon from 'src/assets/icons/arrow-left.svg'; import { CopyTooltip } from 'src/components/CopyTooltip/CopyTooltip'; import { DateTimeDisplay } from 'src/components/DateTimeDisplay'; -import { useIsFirewallRulesetsPrefixlistsEnabled } from '../../shared'; +import { + getFeatureChip, + useIsFirewallRulesetsPrefixlistsEnabled, +} from '../../shared'; import { getPrefixListType, PREFIXLIST_MARKED_FOR_DELETION_TEXT, @@ -42,8 +45,11 @@ export const FirewallPrefixListDrawer = React.memo( const { category, context, onClose, isOpen, selectedPrefixListLabel } = props; - const { isFirewallRulesetsPrefixlistsFeatureEnabled } = - useIsFirewallRulesetsPrefixlistsEnabled(); + const { + isFirewallRulesetsPrefixlistsFeatureEnabled, + isFirewallRulesetsPrefixListsBetaEnabled, + isFirewallRulesetsPrefixListsGAEnabled, + } = useIsFirewallRulesetsPrefixlistsEnabled(); const { classes } = useStyles(); const { data, error, isFetching } = useAllFirewallPrefixListsQuery( @@ -118,6 +124,13 @@ export const FirewallPrefixListDrawer = React.memo( onClose={() => onClose({ closeAll: true })} open={isOpen} title={titleText} + titleSuffix={ + getFeatureChip({ + isFirewallRulesetsPrefixlistsFeatureEnabled, + isFirewallRulesetsPrefixListsBetaEnabled, + isFirewallRulesetsPrefixListsGAEnabled, + }) ?? undefined + } > {prefixListDetails && ( diff --git a/packages/manager/src/features/Firewalls/FirewallDetail/Rules/FirewallRuleDrawer.test.tsx b/packages/manager/src/features/Firewalls/FirewallDetail/Rules/FirewallRuleDrawer.test.tsx index 57b88c41801..7e63e292910 100644 --- a/packages/manager/src/features/Firewalls/FirewallDetail/Rules/FirewallRuleDrawer.test.tsx +++ b/packages/manager/src/features/Firewalls/FirewallDetail/Rules/FirewallRuleDrawer.test.tsx @@ -1,4 +1,5 @@ import { capitalize } from '@linode/utilities'; +import { within } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import * as React from 'react'; @@ -272,6 +273,26 @@ describe('ViewRuleSetDetailsDrawer', () => { ); }); +describe('EditRuleDrawer', () => { + it('should not show the Firewall RS & PL feature chip in the title in Edit mode', () => { + spy.mockReturnValue({ + isFirewallRulesetsPrefixlistsFeatureEnabled: true, + isFirewallRulesetsPrefixListsBetaEnabled: true, + isFirewallRulesetsPrefixListsLAEnabled: false, + isFirewallRulesetsPrefixListsGAEnabled: false, + }); + + const { getByTestId } = renderWithTheme( + + ); + + const titleContainer = getByTestId('drawer-title-container'); + + // The beta (chip) should NOT be in the title area + expect(within(titleContainer).queryByText('beta')).not.toBeInTheDocument(); + }); +}); + describe('utilities', () => { describe('formValueToIPs', () => { it('returns a complete set of IPs given a string form value', () => { diff --git a/packages/manager/src/features/Firewalls/FirewallDetail/Rules/FirewallRuleDrawer.tsx b/packages/manager/src/features/Firewalls/FirewallDetail/Rules/FirewallRuleDrawer.tsx index ec7d525a2f0..91a6ba75d78 100644 --- a/packages/manager/src/features/Firewalls/FirewallDetail/Rules/FirewallRuleDrawer.tsx +++ b/packages/manager/src/features/Firewalls/FirewallDetail/Rules/FirewallRuleDrawer.tsx @@ -6,7 +6,10 @@ import * as React from 'react'; import { SelectionCard } from 'src/components/SelectionCard/SelectionCard'; -import { useIsFirewallRulesetsPrefixlistsEnabled } from '../../shared'; +import { + getFeatureChip, + useIsFirewallRulesetsPrefixlistsEnabled, +} from '../../shared'; import { formValueToIPs, getInitialFormValues, @@ -50,8 +53,11 @@ export const FirewallRuleDrawer = React.memo( ruleToModifyOrView, } = props; - const { isFirewallRulesetsPrefixlistsFeatureEnabled } = - useIsFirewallRulesetsPrefixlistsEnabled(); + const { + isFirewallRulesetsPrefixlistsFeatureEnabled, + isFirewallRulesetsPrefixListsBetaEnabled, + isFirewallRulesetsPrefixListsGAEnabled, + } = useIsFirewallRulesetsPrefixlistsEnabled(); /** * State for the type of entity being created: either a firewall 'rule' or @@ -189,8 +195,23 @@ export const FirewallRuleDrawer = React.memo( return errors; }; + const featureChip = + getFeatureChip({ + isFirewallRulesetsPrefixlistsFeatureEnabled, + isFirewallRulesetsPrefixListsBetaEnabled, + isFirewallRulesetsPrefixListsGAEnabled, + }) ?? undefined; + + // Do not show the Firewall RS & PL feature chip in Edit mode drawer title + const titleSuffix = mode === 'edit' ? undefined : featureChip; + return ( - + {mode === 'create' && isFirewallRulesetsPrefixlistsFeatureEnabled && ( {firewallRuleCreateOptions.map((option) => ( diff --git a/packages/manager/src/features/Firewalls/FirewallDetail/Rules/MultiplePrefixListSelect.tsx b/packages/manager/src/features/Firewalls/FirewallDetail/Rules/MultiplePrefixListSelect.tsx index 91525de7216..67e3312427f 100644 --- a/packages/manager/src/features/Firewalls/FirewallDetail/Rules/MultiplePrefixListSelect.tsx +++ b/packages/manager/src/features/Firewalls/FirewallDetail/Rules/MultiplePrefixListSelect.tsx @@ -1,7 +1,6 @@ import { useAllFirewallPrefixListsQuery } from '@linode/queries'; import { Autocomplete, - BetaChip, Box, Button, Checkbox, @@ -15,7 +14,10 @@ import Grid from '@mui/material/Grid'; import * as React from 'react'; import { makeStyles } from 'tss-react/mui'; -import { useIsFirewallRulesetsPrefixlistsEnabled } from 'src/features/Firewalls/shared'; +import { + getFeatureChip, + useIsFirewallRulesetsPrefixlistsEnabled, +} from 'src/features/Firewalls/shared'; import { getPrefixListType, groupPriority } from './shared'; @@ -121,7 +123,9 @@ export const MultiplePrefixListSelect = React.memo( const { isFirewallRulesetsPrefixlistsFeatureEnabled, isFirewallRulesetsPrefixListsBetaEnabled, + isFirewallRulesetsPrefixListsGAEnabled, } = useIsFirewallRulesetsPrefixlistsEnabled(); + const { data, isLoading } = useAllFirewallPrefixListsQuery( isFirewallRulesetsPrefixlistsFeatureEnabled ); @@ -326,7 +330,11 @@ export const MultiplePrefixListSelect = React.memo( {pls.length > 0 && ( Prefix List - {isFirewallRulesetsPrefixListsBetaEnabled && } + {getFeatureChip({ + isFirewallRulesetsPrefixlistsFeatureEnabled, + isFirewallRulesetsPrefixListsBetaEnabled, + isFirewallRulesetsPrefixListsGAEnabled, + })} )} diff --git a/packages/manager/src/features/Firewalls/shared.test.tsx b/packages/manager/src/features/Firewalls/shared.test.tsx index 76ea95c6fe5..9a3605af7a0 100644 --- a/packages/manager/src/features/Firewalls/shared.test.tsx +++ b/packages/manager/src/features/Firewalls/shared.test.tsx @@ -11,6 +11,7 @@ import { buildPrefixListReferenceMap, generateAddressesLabel, generateAddressesLabelV2, + getFeatureChip, predefinedFirewallFromRule, useIsFirewallRulesetsPrefixlistsEnabled, } from './shared'; @@ -442,3 +443,43 @@ describe('useIsFirewallRulesetsPrefixlistsEnabled', () => { }); }); }); + +describe('getFeatureChip', () => { + it('returns null if RS & PL feature is disabled', () => { + const result = getFeatureChip({ + isFirewallRulesetsPrefixlistsFeatureEnabled: false, + isFirewallRulesetsPrefixListsBetaEnabled: false, + isFirewallRulesetsPrefixListsGAEnabled: false, + }); + expect(result).toBeNull(); + }); + + it('returns BetaChip if Firewall RS & PL feature is enabled and Beta is true', () => { + const result = getFeatureChip({ + isFirewallRulesetsPrefixlistsFeatureEnabled: true, + isFirewallRulesetsPrefixListsBetaEnabled: true, + isFirewallRulesetsPrefixListsGAEnabled: false, + }); + const { getByText } = renderWithTheme(<>{result}); + expect(getByText('beta')).toBeVisible(); + }); + + it('returns NewFeatureChip if Firewall RS & PL feature is enabled, GA is true, and Beta is false', () => { + const result = getFeatureChip({ + isFirewallRulesetsPrefixlistsFeatureEnabled: true, + isFirewallRulesetsPrefixListsBetaEnabled: false, + isFirewallRulesetsPrefixListsGAEnabled: true, + }); + const { getByText } = renderWithTheme(<>{result}); + expect(getByText('new')).toBeVisible(); + }); + + it('returns null if feature is enabled but neither Beta nor GA', () => { + const result = getFeatureChip({ + isFirewallRulesetsPrefixlistsFeatureEnabled: true, + isFirewallRulesetsPrefixListsBetaEnabled: false, + isFirewallRulesetsPrefixListsGAEnabled: false, + }); + expect(result).toBeNull(); + }); +}); diff --git a/packages/manager/src/features/Firewalls/shared.tsx b/packages/manager/src/features/Firewalls/shared.tsx index f9696d68960..289f0db6e39 100644 --- a/packages/manager/src/features/Firewalls/shared.tsx +++ b/packages/manager/src/features/Firewalls/shared.tsx @@ -1,4 +1,4 @@ -import { Box, Chip, Tooltip } from '@linode/ui'; +import { BetaChip, Box, Chip, NewFeatureChip, Tooltip } from '@linode/ui'; import { capitalize, truncateAndJoinList } from '@linode/utilities'; import React from 'react'; @@ -580,3 +580,24 @@ export const useIsFirewallRulesetsPrefixlistsEnabled = () => { flags.fwRulesetsPrefixLists?.ga ?? false, }; }; + +/** + * Returns the feature chip for Firewall Rulesets & Prefix Lists. + * + * - Shows `` if the feature is in Beta. + * - Shows `` if the feature is in GA. + * - Returns `null` if the feature is disabled OR if the feature is enabled but no chip applies. + */ +export const getFeatureChip = ({ + isFirewallRulesetsPrefixlistsFeatureEnabled, + isFirewallRulesetsPrefixListsBetaEnabled, + isFirewallRulesetsPrefixListsGAEnabled, +}: Omit< + ReturnType, + 'isFirewallRulesetsPrefixListsLAEnabled' +>) => { + if (!isFirewallRulesetsPrefixlistsFeatureEnabled) return null; + if (isFirewallRulesetsPrefixListsBetaEnabled) return ; + if (isFirewallRulesetsPrefixListsGAEnabled) return ; + return null; +}; diff --git a/packages/ui/src/components/Drawer/Drawer.tsx b/packages/ui/src/components/Drawer/Drawer.tsx index 8caad7bdd8a..eb9f423a13b 100644 --- a/packages/ui/src/components/Drawer/Drawer.tsx +++ b/packages/ui/src/components/Drawer/Drawer.tsx @@ -154,7 +154,11 @@ export const Drawer = React.forwardRef( > {isFetching ? null : ( - +