Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
ded4e01
Save progress...
pmakode-akamai Jun 12, 2025
401b02d
Few updates..
pmakode-akamai Jun 12, 2025
0395a79
More updates
pmakode-akamai Jun 12, 2025
faa6505
Add unit tests for alerts and some fixes
pmakode-akamai Jun 12, 2025
b824564
Merge branch 'develop' into poc/M3-10153-update-legacy-beta-preferenc…
pmakode-akamai Jun 12, 2025
d1cd824
Merge branch 'develop' into poc/M3-10153-update-legacy-beta-preferenc…
pmakode-akamai Jun 13, 2025
1807fd2
Remove Alerts preference from ManagerPreferences
pmakode-akamai Jun 13, 2025
56f3ae3
Refactor and resolve conflicts
pmakode-akamai Jul 1, 2025
8994a48
Resolve merge conflicts
pmakode-akamai Jul 2, 2025
6f3949f
Rename props
pmakode-akamai Jul 2, 2025
ab848aa
Rename more props
pmakode-akamai Jul 2, 2025
0a2ac5e
Huge refactor
pmakode-akamai Jul 2, 2025
55a61ae
More refactor
pmakode-akamai Jul 2, 2025
0d94e85
Merge branch 'develop' into poc/M3-10153-update-legacy-beta-preferenc…
pmakode-akamai Jul 2, 2025
e7ca60f
Add useIsLinodeAclpSubscribed hook and tests
pmakode-akamai Jul 7, 2025
bb8a4f5
Merge branch 'develop' into poc/M3-10153-update-legacy-beta-preferenc…
pmakode-akamai Jul 7, 2025
ed57853
Some clean up
pmakode-akamai Jul 7, 2025
a561546
Merge branch 'M3-10288-add-linode-aclp-subscription-status-hook' into…
pmakode-akamai Jul 7, 2025
ec262f5
Add useIsLinodeAclpSubscribed hook and tests
pmakode-akamai Jul 7, 2025
dc9529b
Minor refactor
pmakode-akamai Jul 7, 2025
49488df
Update comment
pmakode-akamai Jul 7, 2025
ff87f87
Added changeset: Update legacy/beta toggle behavior for Metrics, Aler…
pmakode-akamai Jul 7, 2025
013547f
Added changeset: Add a `useIsLinodeAclpSubscribed` hook and its unit …
pmakode-akamai Jul 7, 2025
2225f12
Minor change
pmakode-akamai Jul 7, 2025
c38cc02
Merge branch 'develop' into M3-10153-M3-10288-new-aclp-toggle-behavio…
pmakode-akamai Jul 7, 2025
32bf886
Update hook return type and rename props
pmakode-akamai Jul 8, 2025
9ee8e1e
Merge branch 'develop' into M3-10153-M3-10288-new-aclp-toggle-behavio…
pmakode-akamai Jul 8, 2025
2db29b3
Use '
pmakode-akamai Jul 8, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"test:search": "pnpm run --filter @linode/search test",
"test:ui": "pnpm run --filter @linode/ui test",
"test:utilities": "pnpm run --filter @linode/utilities test",
"test:shared": "pnpm run --filter @linode/shared test",
"coverage": "pnpm run --filter linode-manager coverage",
"coverage:summary": "pnpm run --filter linode-manager coverage:summary",
"cy:run": "pnpm run --filter linode-manager cy:run",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Upcoming Features
---

Update legacy/beta toggle behavior for Metrics, Alerts and Banners ([#12479](https://github.com/linode/manager/pull/12479))
107 changes: 42 additions & 65 deletions packages/manager/src/features/Linodes/AclpPreferenceToggle.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ vi.mock('@linode/queries', async () => {

describe('AclpPreferenceToggle', () => {
/**
* ACLP Preference Toggle for Metrics
* ACLP Preference Toggle tests for Metrics
*/
it('should display loading state for Metrics preference correctly', () => {
queryMocks.usePreferences.mockReturnValue({
Expand Down Expand Up @@ -165,110 +165,87 @@ describe('AclpPreferenceToggle', () => {
});

/**
* ACLP Preference Toggle for Alerts
* ACLP Preference Toggle tests for Alerts
*/
it('should display loading state for Alerts preference correctly', () => {
queryMocks.usePreferences.mockReturnValue({
data: undefined,
isLoading: true,
});
queryMocks.useMutatePreferences.mockReturnValue({
mutateAsync: vi.fn().mockResolvedValue(undefined),
});

renderWithTheme(<AclpPreferenceToggle type="alerts" />);

const skeleton = screen.getByTestId('alerts-preference-skeleton');
expect(skeleton).toBeInTheDocument();
});

it('should display the correct legacy mode banner and button text for Alerts when isAclpAlertsBeta preference is disabled', () => {
queryMocks.usePreferences.mockReturnValue({
data: false,
isLoading: false,
});

renderWithTheme(<AclpPreferenceToggle type="alerts" />);
it('should display the correct legacy mode banner and button text for Alerts when isAlertsBetaMode is false', () => {
renderWithTheme(
<AclpPreferenceToggle
isAlertsBetaMode={false}
onAlertsModeChange={vi.fn()}
type="alerts"
/>
);

// Check if the banner content and button text is correct in legacy mode
const typography = screen.getByTestId('alerts-preference-banner-text');
expect(typography).toHaveTextContent(
expectedAclpPreferences.alerts.legacyModeBannerText
);

const expectedLegacyModeButtonText = screen.getByText(
const button = screen.getByText(
expectedAclpPreferences.alerts.legacyModeButtonText
);
expect(expectedLegacyModeButtonText).toBeInTheDocument();
expect(button).toBeInTheDocument();
});

it('should display the correct beta mode banner and button text for Alerts when isAclpAlertsBeta preference is enabled', () => {
queryMocks.usePreferences.mockReturnValue({
data: expectedAclpPreferences.alerts.preference,
isLoading: false,
});

renderWithTheme(<AclpPreferenceToggle type="alerts" />);
it('should display the correct beta mode banner and button text for Alerts when isAlertsBetaMode is true', () => {
renderWithTheme(
<AclpPreferenceToggle
isAlertsBetaMode={true}
onAlertsModeChange={vi.fn()}
type="alerts"
/>
);

// Check if the banner content and button text is correct in beta mode
const typography = screen.getByTestId('alerts-preference-banner-text');
expect(typography).toHaveTextContent(
expectedAclpPreferences.alerts.betaModeBannertext
);

const expectedLegacyModeButtonText = screen.getByText(
const button = screen.getByText(
expectedAclpPreferences.alerts.betaModeButtonText
);
expect(expectedLegacyModeButtonText).toBeInTheDocument();
expect(button).toBeInTheDocument();
});

it('should update ACLP Alerts preference to beta mode when toggling from legacy mode', async () => {
queryMocks.usePreferences.mockReturnValue({
data: false,
isLoading: false,
});
const mockUpdatePreferences = vi.fn().mockResolvedValue({
isAclpMetricsBeta: false,
});
queryMocks.useMutatePreferences.mockReturnValue({
mutateAsync: mockUpdatePreferences,
});
it('should call onAlertsModeChange with true when switching from legacy to beta mode', async () => {
const mockSetIsAclpBetaLocal = vi.fn();

renderWithTheme(<AclpPreferenceToggle type="alerts" />);
renderWithTheme(
<AclpPreferenceToggle
isAlertsBetaMode={false}
onAlertsModeChange={mockSetIsAclpBetaLocal}
type="alerts"
/>
);

// Click the button to switch from legacy to beta
const button = screen.getByText(
expectedAclpPreferences.alerts.legacyModeButtonText
);
await userEvent.click(button);

expect(mockUpdatePreferences).toHaveBeenCalledWith({
isAclpAlertsBeta: true,
});
expect(mockSetIsAclpBetaLocal).toHaveBeenCalledWith(true);
});

it('should update ACLP Alerts preference to legacy mode when toggling from beta mode', async () => {
queryMocks.usePreferences.mockReturnValue({
data: expectedAclpPreferences.alerts.preference,
isLoading: false,
});
const mockUpdatePreferences = vi.fn().mockResolvedValue({
isAclpMetricsBeta: true,
});
queryMocks.useMutatePreferences.mockReturnValue({
mutateAsync: mockUpdatePreferences,
});
it('should call onAlertsModeChange with false when switching from beta to legacy mode', async () => {
const mockSetIsAclpBetaLocal = vi.fn();

renderWithTheme(<AclpPreferenceToggle type="alerts" />);
renderWithTheme(
<AclpPreferenceToggle
isAlertsBetaMode={true}
onAlertsModeChange={mockSetIsAclpBetaLocal}
type="alerts"
/>
);

// Click the button to switch from beta to legacy
const button = screen.getByText(
expectedAclpPreferences.alerts.betaModeButtonText
);
await userEvent.click(button);

expect(mockUpdatePreferences).toHaveBeenCalledWith({
isAclpAlertsBeta: false,
});
expect(mockSetIsAclpBetaLocal).toHaveBeenCalledWith(false);
});
});
53 changes: 32 additions & 21 deletions packages/manager/src/features/Linodes/AclpPreferenceToggle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,32 @@ import React, { type JSX } from 'react';
import { DismissibleBanner } from 'src/components/DismissibleBanner/DismissibleBanner';
import { Skeleton } from 'src/components/Skeleton';

import type { ManagerPreferences } from '@linode/utilities';

export interface AclpPreferenceToggleType {
/**
* Alerts toggle state. Use only when type is `alerts`
*/
isAlertsBetaMode?: boolean;
/**
* Handler for alerts toggle. Use only when type is `alerts`
*/
onAlertsModeChange?: (isBeta: boolean) => void;
/**
* Toggle type: `alerts` or `metrics`
*/
type: 'alerts' | 'metrics';
}

interface PreferenceConfigItem {
getBannerText: (isBeta: boolean | undefined) => JSX.Element;
getButtonText: (isBeta: boolean | undefined) => string;
preferenceKey: string;
updateKey: keyof ManagerPreferences;
usePreferenceSelector: (
preferences: ManagerPreferences | undefined
) => boolean | undefined;
}

const preferenceConfig: Record<
AclpPreferenceToggleType['type'],
PreferenceConfigItem
> = {
metrics: {
usePreferenceSelector: (preferences) => preferences?.isAclpMetricsBeta,
updateKey: 'isAclpMetricsBeta',
preferenceKey: 'metrics-preference',
getButtonText: (isBeta) =>
isBeta ? 'Switch to legacy Metrics' : 'Try the Metrics (Beta)',
Expand All @@ -46,8 +49,6 @@ const preferenceConfig: Record<
),
},
alerts: {
usePreferenceSelector: (preferences) => preferences?.isAclpAlertsBeta,
updateKey: 'isAclpAlertsBeta',
preferenceKey: 'alerts-preference',
getButtonText: (isBeta) =>
isBeta ? 'Switch to legacy Alerts' : 'Try Alerts (Beta)',
Expand All @@ -66,37 +67,47 @@ const preferenceConfig: Record<
},
};

export const AclpPreferenceToggle = ({ type }: AclpPreferenceToggleType) => {
export const AclpPreferenceToggle = (props: AclpPreferenceToggleType) => {
const { isAlertsBetaMode, onAlertsModeChange, type } = props;

const config = preferenceConfig[type];

const { data: isBeta, isLoading } = usePreferences(
config.usePreferenceSelector
);
// -------------------- Metrics related logic ------------------------
const { data: isAclpMetricsBeta, isLoading: isAclpMetricsBetaLoading } =
usePreferences((preferences) => {
return preferences?.isAclpMetricsBeta;
}, type === 'metrics');

const { mutateAsync: updatePreferences } = useMutatePreferences();

if (isLoading) {
if (isAclpMetricsBetaLoading) {
return (
<Skeleton
data-testid={`${type}-preference-skeleton`}
data-testid="metrics-preference-skeleton"
height="90px"
sx={(theme) => ({
marginTop: `-${theme.tokens.spacing.S20}`,
})}
/>
);
}
// -------------------------------------------------------------------

const isBeta = type === 'alerts' ? isAlertsBetaMode : isAclpMetricsBeta;
const handleBetaToggle = () => {
if (type === 'alerts' && onAlertsModeChange) {
onAlertsModeChange(!isBeta);
} else {
updatePreferences({ isAclpMetricsBeta: !isBeta });
}
};

return (
<DismissibleBanner
actionButton={
<Button
buttonType="primary"
onClick={() =>
updatePreferences({
[config.updateKey]: !isBeta,
})
}
onClick={handleBetaToggle}
sx={{ textTransform: 'none' }}
>
{config.getButtonText(isBeta)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ describe('Actions', () => {
expect(button).toBeEnabled();
});

it("should render a ' View Code Snippets' button", () => {
it("should render a 'View Code Snippets' button", () => {
const { getByText } = renderWithThemeAndHookFormContext({
component: <Actions />,
});
Expand Down
12 changes: 6 additions & 6 deletions packages/manager/src/features/Linodes/LinodeCreate/Actions.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { usePreferences } from '@linode/queries';
import { Box, Button } from '@linode/ui';
import { scrollErrorIntoView } from '@linode/utilities';
import React, { useState } from 'react';
Expand All @@ -19,15 +18,16 @@ import {

import type { LinodeCreateFormValues } from './utilities';

export const Actions = () => {
interface ActionProps {
isAlertsBetaMode?: boolean;
}

export const Actions = ({ isAlertsBetaMode }: ActionProps) => {
const { params } = useLinodeCreateQueryParams();
const [isAPIAwarenessModalOpen, setIsAPIAwarenessModalOpen] = useState(false);

const { isLinodeInterfacesEnabled } = useIsLinodeInterfacesEnabled();
const { aclpBetaServices } = useFlags();
const { data: isAclpAlertsPreferenceBeta } = usePreferences(
(preferences) => preferences?.isAclpAlertsBeta
);

const { formState, getValues, trigger, control } =
useFormContext<LinodeCreateFormValues>();
Expand Down Expand Up @@ -83,7 +83,7 @@ export const Actions = () => {
payLoad={getLinodeCreatePayload(structuredClone(getValues()), {
isShowingNewNetworkingUI: isLinodeInterfacesEnabled,
isAclpIntegration: aclpBetaServices?.linode?.alerts,
isAclpAlertsPreferenceBeta,
isAclpAlertsPreferenceBeta: isAlertsBetaMode,
})}
/>
</Box>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,22 @@ import React from 'react';
import { useWatch } from 'react-hook-form';

import { useVMHostMaintenanceEnabled } from 'src/features/Account/utils';
import { MaintenancePolicy } from 'src/features/Linodes/LinodeCreate/AdditionalOptions/MaintenancePolicy';
import { useFlags } from 'src/hooks/useFlags';

import { Alerts } from './Alerts/Alerts';
import { Alerts } from './Alerts';
import { MaintenancePolicy } from './MaintenancePolicy';

import type { CreateLinodeRequest } from '@linode/api-v4';

export const AdditionalOptions = () => {
interface AdditionalOptionProps {
isAlertsBetaMode: boolean;
onAlertsModeChange: (isBeta: boolean) => void;
}

export const AdditionalOptions = ({
onAlertsModeChange,
isAlertsBetaMode,
}: AdditionalOptionProps) => {
const { aclpBetaServices } = useFlags();
const { data: regions } = useRegionsQuery();
const { isVMHostMaintenanceEnabled } = useVMHostMaintenanceEnabled();
Expand Down Expand Up @@ -46,7 +54,12 @@ export const AdditionalOptions = () => {
Additional Options
</Typography>
<Stack divider={<Divider />}>
{showAlerts && <Alerts />}
{showAlerts && (
<Alerts
isAlertsBetaMode={isAlertsBetaMode}
onAlertsModeChange={onAlertsModeChange}
/>
)}
{isVMHostMaintenanceEnabled && <MaintenancePolicy />}
</Stack>
</Paper>
Expand Down
Loading