Skip to content

Commit

Permalink
refactor: [M3-8059] - Clean up Main Content Banner (#10430)
Browse files Browse the repository at this point in the history
* clean up main content banner

* remove old tests and add changeset

* fix test name

---------

Co-authored-by: Banks Nussman <banks@nussman.us>
  • Loading branch information
bnussman-akamai and bnussman authored May 3, 2024
1 parent 8a37043 commit 020e084
Show file tree
Hide file tree
Showing 5 changed files with 230 additions and 242 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Tech Stories
---

Clean up Main Content Banner ([#10430](https://github.com/linode/manager/pull/10430))
45 changes: 0 additions & 45 deletions packages/manager/src/MainContent.test.ts

This file was deleted.

195 changes: 74 additions & 121 deletions packages/manager/src/MainContent.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { Theme } from '@mui/material/styles';
import Grid from '@mui/material/Unstable_Grid2';
import { isEmpty } from 'ramda';
import * as React from 'react';
import { Redirect, Route, Switch } from 'react-router-dom';
import { makeStyles } from 'tss-react/mui';
Expand All @@ -25,13 +24,11 @@ import { useAccountManagement } from 'src/hooks/useAccountManagement';
import { useFlags } from 'src/hooks/useFlags';
import { useDatabaseEnginesQuery } from 'src/queries/databases';
import { useMutatePreferences, usePreferences } from 'src/queries/preferences';
import { ManagerPreferences } from 'src/types/ManagerPreferences';
import { isFeatureEnabled } from 'src/utilities/accountCapabilities';

import { ENABLE_MAINTENANCE_MODE } from './constants';
import { complianceUpdateContext } from './context/complianceUpdateContext';
import { switchAccountSessionContext } from './context/switchAccountSessionContext';
import { FlagSet } from './featureFlags';
import { useIsACLBEnabled } from './features/LoadBalancers/utils';
import { useIsPlacementGroupsEnabled } from './features/PlacementGroups/utils';
import { useGlobalErrors } from './hooks/useGlobalErrors';
Expand Down Expand Up @@ -210,8 +207,6 @@ export const MainContent = () => {

const username = profile?.username || '';

const [bannerDismissed, setBannerDismissed] = React.useState<boolean>(false);

const checkRestrictedUser = !Boolean(flags.databases) && !!accountError;
const {
error: enginesError,
Expand All @@ -231,14 +226,6 @@ export const MainContent = () => {

const defaultRoot = _isManagedAccount ? '/managed' : '/linodes';

const shouldDisplayMainContentBanner =
!bannerDismissed &&
checkFlagsForMainContentBanner(flags) &&
!checkPreferencesForBannerDismissal(
preferences ?? {},
flags?.mainContentBanner?.key
);

/**
* this is the case where the user has successfully completed signup
* but needs a manual review from Customer Support. In this case,
Expand Down Expand Up @@ -292,130 +279,96 @@ export const MainContent = () => {
});
};

/**
* otherwise just show the rest of the app.
*/
return (
<div className={classes.appFrame}>
<SwitchAccountSessionProvider value={switchAccountSessionContextValue}>
<ComplianceUpdateProvider value={complianceUpdateContextValue}>
<NotificationProvider value={contextValue}>
<>
{shouldDisplayMainContentBanner ? (
<MainContentBanner
bannerKey={flags.mainContentBanner?.key ?? ''}
bannerText={flags.mainContentBanner?.text ?? ''}
linkText={flags.mainContentBanner?.link?.text ?? ''}
onClose={() => setBannerDismissed(true)}
url={flags.mainContentBanner?.link?.url ?? ''}
/>
) : null}
<SideMenu
closeMenu={() => toggleMenu(false)}
collapse={desktopMenuIsOpen || false}
open={menuIsOpen}
<SideMenu
closeMenu={() => toggleMenu(false)}
collapse={desktopMenuIsOpen || false}
open={menuIsOpen}
/>
<div
className={cx(classes.content, {
[classes.fullWidthContent]:
desktopMenuIsOpen ||
(desktopMenuIsOpen && desktopMenuIsOpen === true),
})}
>
<MainContentBanner />
<TopMenu
desktopMenuToggle={desktopMenuToggle}
isSideMenuOpen={!desktopMenuIsOpen}
openSideMenu={() => toggleMenu(true)}
username={username}
/>
<div
className={cx(classes.content, {
[classes.fullWidthContent]:
desktopMenuIsOpen ||
(desktopMenuIsOpen && desktopMenuIsOpen === true),
})}
<main
className={classes.cmrWrapper}
id="main-content"
role="main"
>
<TopMenu
desktopMenuToggle={desktopMenuToggle}
isSideMenuOpen={!desktopMenuIsOpen}
openSideMenu={() => toggleMenu(true)}
username={username}
/>
<main
className={classes.cmrWrapper}
id="main-content"
role="main"
>
<Grid className={classes.grid} container spacing={0}>
<Grid className={cx(classes.switchWrapper, 'p0')}>
<GlobalNotifications />
<React.Suspense fallback={<SuspenseLoader />}>
<Switch>
<Route component={LinodesRoutes} path="/linodes" />
{isPlacementGroupsEnabled && (
<Route
component={PlacementGroups}
path="/placement-groups"
/>
)}
<Route component={Volumes} path="/volumes" />
<Redirect path="/volumes*" to="/volumes" />
{isACLBEnabled && (
<Route
component={LoadBalancers}
path="/loadbalancer*"
/>
)}
<Grid className={classes.grid} container spacing={0}>
<Grid className={cx(classes.switchWrapper, 'p0')}>
<GlobalNotifications />
<React.Suspense fallback={<SuspenseLoader />}>
<Switch>
<Route component={LinodesRoutes} path="/linodes" />
{isPlacementGroupsEnabled && (
<Route
component={NodeBalancers}
path="/nodebalancers"
component={PlacementGroups}
path="/placement-groups"
/>
<Route component={Domains} path="/domains" />
<Route component={Managed} path="/managed" />
<Route component={Longview} path="/longview" />
<Route component={Images} path="/images" />
)}
<Route component={Volumes} path="/volumes" />
<Redirect path="/volumes*" to="/volumes" />
{isACLBEnabled && (
<Route
component={StackScripts}
path="/stackscripts"
component={LoadBalancers}
path="/loadbalancer*"
/>
<Route
component={ObjectStorage}
path="/object-storage"
/>
<Route component={Kubernetes} path="/kubernetes" />
<Route component={Account} path="/account" />
<Route component={Profile} path="/profile" />
<Route component={Help} path="/support" />
<Route component={SearchLanding} path="/search" />
<Route component={EventsLanding} path="/events" />
<Route component={Firewalls} path="/firewalls" />
{showDatabases && (
<Route component={Databases} path="/databases" />
)}
{flags.selfServeBetas && (
<Route component={BetaRoutes} path="/betas" />
)}
<Route component={VPC} path="/vpcs" />
<Redirect exact from="/" to={defaultRoot} />
{/** We don't want to break any bookmarks. This can probably be removed eventually. */}
<Redirect from="/dashboard" to={defaultRoot} />
<Route component={NotFound} />
</Switch>
</React.Suspense>
</Grid>
)}
<Route
component={NodeBalancers}
path="/nodebalancers"
/>
<Route component={Domains} path="/domains" />
<Route component={Managed} path="/managed" />
<Route component={Longview} path="/longview" />
<Route component={Images} path="/images" />
<Route component={StackScripts} path="/stackscripts" />
<Route
component={ObjectStorage}
path="/object-storage"
/>
<Route component={Kubernetes} path="/kubernetes" />
<Route component={Account} path="/account" />
<Route component={Profile} path="/profile" />
<Route component={Help} path="/support" />
<Route component={SearchLanding} path="/search" />
<Route component={EventsLanding} path="/events" />
<Route component={Firewalls} path="/firewalls" />
{showDatabases && (
<Route component={Databases} path="/databases" />
)}
{flags.selfServeBetas && (
<Route component={BetaRoutes} path="/betas" />
)}
<Route component={VPC} path="/vpcs" />
<Redirect exact from="/" to={defaultRoot} />
{/** We don't want to break any bookmarks. This can probably be removed eventually. */}
<Redirect from="/dashboard" to={defaultRoot} />
<Route component={NotFound} />
</Switch>
</React.Suspense>
</Grid>
</main>
</div>
</>
</Grid>
</main>
</div>
</NotificationProvider>
<Footer desktopMenuIsOpen={desktopMenuIsOpen} />
</ComplianceUpdateProvider>
</SwitchAccountSessionProvider>
</div>
);
};

// =============================================================================
// Utilities
// =============================================================================
export const checkFlagsForMainContentBanner = (flags: FlagSet) => {
return Boolean(
flags.mainContentBanner &&
!isEmpty(flags.mainContentBanner) &&
flags.mainContentBanner.key
);
};

export const checkPreferencesForBannerDismissal = (
preferences: ManagerPreferences,
key = 'defaultKey'
) => {
return Boolean(preferences?.main_content_banner_dismissal?.[key]);
};
82 changes: 82 additions & 0 deletions packages/manager/src/components/MainContentBanner.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { waitFor } from '@testing-library/react';
import { userEvent } from '@testing-library/user-event';
import React from 'react';

import { HttpResponse, http, server } from 'src/mocks/testServer';
import { renderWithTheme } from 'src/utilities/testHelpers';

import { MainContentBanner } from './MainContentBanner';

import type { ManagerPreferences } from 'src/types/ManagerPreferences';

describe('MainContentBanner', () => {
const mainContentBanner = {
key: 'test-banner-1',
link: {
text: 'Learn more.',
url: 'https://akamai.com',
},
text: 'Linode is now Akamai 🤯',
};

it('should render a banner from feature flags', () => {
const { getByText } = renderWithTheme(<MainContentBanner />, {
flags: { mainContentBanner },
});

expect(getByText('Linode is now Akamai 🤯')).toBeVisible();
});

it('should render a link from feature flags', () => {
const { getByText } = renderWithTheme(<MainContentBanner />, {
flags: { mainContentBanner },
});

const link = getByText('Learn more.');

expect(link).toBeVisible();
expect(link).toBeEnabled();
expect(link).toHaveRole('link');
expect(link).toHaveAttribute('href', 'https://akamai.com');
});

it('should be dismissable', async () => {
const { container, getByLabelText } = renderWithTheme(
<MainContentBanner />,
{
flags: { mainContentBanner },
}
);

const closeButton = getByLabelText('Close');

expect(closeButton).toBeVisible();
expect(closeButton).toBeEnabled();

await userEvent.click(closeButton);

expect(container).toBeEmptyDOMElement();
});

it('should not render if the user dismissed the banner', async () => {
const preferences: ManagerPreferences = {
main_content_banner_dismissal: {
'test-banner-1': true,
},
};

server.use(
http.get('*/v4/profile/preferences', () => {
return HttpResponse.json(preferences);
})
);

const { container } = renderWithTheme(<MainContentBanner />, {
flags: { mainContentBanner },
});

await waitFor(() => {
expect(container).toBeEmptyDOMElement();
});
});
});
Loading

0 comments on commit 020e084

Please sign in to comment.