Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions packages/manager/.changeset/pr-13230-added-1767073910229.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Added
---

New feature marker in navigation menu and primary breadcrumbs of `CloudPulse metrics` ([#13230](https://github.com/linode/manager/pull/13230))
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,15 @@
aclp: {
beta: true,
enabled: true,
new: true,
},
}).as('getFeatureFlags');

cy.visitWithLogin('/linodes');
cy.wait('@getFeatureFlags');

cy.get('[data-testid="menu-item-Metrics"]').within(() => {

Check warning on line 33 in packages/manager/cypress/e2e/core/cloudpulse/cloudpulse-navigation.spec.ts

View workflow job for this annotation

GitHub Actions / ESLint Review (manager)

[eslint] reported by reviewdog 🐢 Define a constant instead of duplicating this literal 3 times. Raw Output: {"ruleId":"sonarjs/no-duplicate-string","severity":1,"message":"Define a constant instead of duplicating this literal 3 times.","line":33,"column":12,"nodeType":"Literal","endLine":33,"endColumn":47}
cy.get('[data-testid="newFeatureChip"]').should('be.visible'); // check for new feature chip

Check warning on line 34 in packages/manager/cypress/e2e/core/cloudpulse/cloudpulse-navigation.spec.ts

View workflow job for this annotation

GitHub Actions / ESLint Review (manager)

[eslint] reported by reviewdog 🐢 Define a constant instead of duplicating this literal 5 times. Raw Output: {"ruleId":"sonarjs/no-duplicate-string","severity":1,"message":"Define a constant instead of duplicating this literal 5 times.","line":34,"column":55,"nodeType":"Literal","endLine":34,"endColumn":67}
});
cy.get('[data-testid="menu-item-Metrics"]').should('be.visible').click();
cy.url().should('endWith', '/metrics');
});
Expand Down
48 changes: 47 additions & 1 deletion packages/manager/src/components/PrimaryNav/PrimaryNav.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ describe('PrimaryNav', () => {
aclp: {
beta: true,
enabled: true,
new: true,
},
aclpAlerting: {
accountAlertLimit: 10,
Expand All @@ -239,7 +240,7 @@ describe('PrimaryNav', () => {
},
};

const { findAllByTestId, findByText } = renderWithTheme(
const { findAllByTestId, findByText, queryByTestId } = renderWithTheme(
<PrimaryNav {...props} />,
{
flags,
Expand All @@ -249,12 +250,57 @@ describe('PrimaryNav', () => {
const monitorMetricsDisplayItem = await findByText('Metrics');
const monitorAlertsDisplayItem = await findByText('Alerts');
const betaChip = await findAllByTestId('betaChip');
const newFeatureChip = queryByTestId('newFeatureChip');
expect(newFeatureChip).toBeNull(); // when beta is true, only beta chip is shown not new chip

expect(monitorMetricsDisplayItem).toBeVisible();
expect(monitorAlertsDisplayItem).toBeVisible();
expect(betaChip).toHaveLength(2);
});

it('shoud show beta chip next to Metrics menu item if the user has the account capability and aclp feature flag has new true', async () => {
const account = accountFactory.build({
capabilities: ['Akamai Cloud Pulse'],
});

queryMocks.useAccount.mockReturnValue({
data: account,
isLoading: false,
error: null,
});

const flags = {
aclp: {
beta: false,
enabled: true,
new: true,
},
aclpAlerting: {
accountAlertLimit: 10,
accountMetricLimit: 10,
alertDefinitions: true,
beta: true,
notificationChannels: false,
recentActivity: false,
},
};

const { findByText, findByTestId } = renderWithTheme(
<PrimaryNav {...props} />,
{
flags,
}
);

const monitorMetricsDisplayItem = await findByText('Metrics');
const monitorAlertsDisplayItem = await findByText('Alerts');
const newFeatureChip = await findByTestId('newFeatureChip');

expect(monitorMetricsDisplayItem).toBeVisible();
expect(monitorAlertsDisplayItem).toBeVisible();
expect(newFeatureChip).toBeVisible();
});

it('should not show Metrics and Alerts menu items if the user has the account capability but the aclp feature flag is not enabled', async () => {
const account = accountFactory.build({
capabilities: ['Akamai Cloud Pulse'],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ export const PrimaryNav = (props: PrimaryNavProps) => {
hide: !isACLPEnabled,
to: '/metrics',
isBeta: flags.aclp?.beta,
isNew: !flags.aclp?.beta && flags.aclp?.new,
},
{
display: 'Alerts',
Expand Down
5 changes: 5 additions & 0 deletions packages/manager/src/featureFlags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,11 @@ interface AclpFlag {
*/
humanizableUnits?: string[];

/**
* This property indicates whether the feature is new or not
*/
new?: boolean;

/**
* This property indicates whether to show widget dimension filters or not
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,15 @@ vi.spyOn(utils, 'getAllDashboards').mockReturnValue({
});
describe('CloudPulseDashboardFilterBuilder component tests', () => {
it('should render error placeholder if dashboard not selected', () => {
renderWithTheme(<CloudPulseDashboardLanding />);
renderWithTheme(<CloudPulseDashboardLanding />, {
flags: {
aclp: {
new: true,
beta: false,
enabled: true,
},
},
});
const text = screen.getByText('metrics');
expect(text).toBeInTheDocument();

Expand All @@ -61,6 +69,9 @@ describe('CloudPulseDashboardFilterBuilder component tests', () => {

const messageComponent = screen.getByText(message);
expect(messageComponent).toBeDefined();

const newFeatureChip = screen.getByTestId('newFeatureChip');
expect(newFeatureChip).toBeVisible();
});

it('should render error placeholder if some dashboard is selected and filter config is not present', async () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { useProfile } from '@linode/queries';
import { Box, Paper } from '@linode/ui';
import { Box, NewFeatureChip, Paper } from '@linode/ui';
import { GridLegacy } from '@mui/material';
import { DateTime } from 'luxon';
import * as React from 'react';

import { DocumentTitleSegment } from 'src/components/DocumentTitle';
import { LandingHeader } from 'src/components/LandingHeader';
import { SuspenseLoader } from 'src/components/SuspenseLoader';
import { useFlags } from 'src/hooks/useFlags';

import { GlobalFilters } from '../Overview/GlobalFilters';
import { CloudPulseAppliedFilterRenderer } from '../shared/CloudPulseAppliedFilterRenderer';
Expand All @@ -33,6 +34,7 @@ export interface DashboardProp {

export const CloudPulseDashboardLanding = () => {
const { data: profile } = useProfile();
const flags = useFlags();
const [filterData, setFilterData] = React.useState<FilterData>({
id: {},
label: {},
Expand Down Expand Up @@ -101,7 +103,12 @@ export const CloudPulseDashboardLanding = () => {
<React.Suspense fallback={<SuspenseLoader />}>
<DocumentTitleSegment segment="Dashboards" />
<LandingHeader
breadcrumbProps={{ pathname: '/metrics' }}
breadcrumbProps={{
pathname: '/metrics',
labelOptions: {
suffixComponent: flags.aclp?.new ? <NewFeatureChip /> : undefined,
},
}}
docsLabel="Docs"
docsLink="https://techdocs.akamai.com/cloud-computing/docs/akamai-cloud-pulse"
/>
Expand Down