diff --git a/packages/manager/.changeset/pr-9932-tech-stories-1700688400660.md b/packages/manager/.changeset/pr-9932-tech-stories-1700688400660.md new file mode 100644 index 00000000000..987413f2225 --- /dev/null +++ b/packages/manager/.changeset/pr-9932-tech-stories-1700688400660.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Tech Stories +--- + +Dismissible Banner V7 story migration ([#9932](https://github.com/linode/manager/pull/9932)) diff --git a/packages/manager/src/components/AbuseTicketBanner/AbuseTicketBanner.tsx b/packages/manager/src/components/AbuseTicketBanner/AbuseTicketBanner.tsx index e424526575a..aab45644f4c 100644 --- a/packages/manager/src/components/AbuseTicketBanner/AbuseTicketBanner.tsx +++ b/packages/manager/src/components/AbuseTicketBanner/AbuseTicketBanner.tsx @@ -3,7 +3,7 @@ import { DateTime } from 'luxon'; import * as React from 'react'; import { Link, useLocation } from 'react-router-dom'; -import { DismissibleBanner } from 'src/components/DismissibleBanner'; +import { DismissibleBanner } from 'src/components/DismissibleBanner/DismissibleBanner'; import { Typography } from 'src/components/Typography'; import { useNotificationsQuery } from 'src/queries/accountNotifications'; import { getAbuseTickets } from 'src/store/selectors/getAbuseTicket'; diff --git a/packages/manager/src/components/DismissibleBanner/DismissibleBanner.stories.mdx b/packages/manager/src/components/DismissibleBanner/DismissibleBanner.stories.mdx deleted file mode 100644 index e7c963079b7..00000000000 --- a/packages/manager/src/components/DismissibleBanner/DismissibleBanner.stories.mdx +++ /dev/null @@ -1,126 +0,0 @@ -import { ArgsTable, Canvas, Meta, Story } from '@storybook/addon-docs'; -import { Button } from 'src/components/Button/Button'; -import { DismissibleBanner } from './DismissibleBanner'; -import Grid from '@mui/material/Unstable_Grid2'; -import { Link } from 'src/components/Link'; -import { Typography } from 'src/components/Typography'; - - - -# Dismissible Banners - -### Usage - -Banners appear between the top nav and page content and are a visual interruption of the normal page layout. Their use must be approved by all project stakeholders. - -### Design - -- Banners are dismissible using an ’X’ icon. -- Consider adding a link to a doc or a guide for users to learn more. -- Banners should be considered as one part of a larger communications plan; messaging should be developed with the marketing team. - -### Variants - -Under the hood, banners use the [Notice](/docs/components-notifications-notices--success) component so they have the same variants such as: - -- Success: Informs users of a new feature or improved service. -- Warning: Informs users of an impending change that will have an impact on their service(s). -- Call to action: Primary Button or text link allows a user to take action directly from the banner. - -export const DismissibleBannerTemplate = (args) => { - return ( - - This is an example of a dismissible banner. - - ); -}; - -export const CallToActionBanner = () => { - return ( - - - - A new version of Kubernetes is available. - - - - - - - ); -}; - -export const StatusBannersTemplate = (args) => { - const { message, title, status, impact, href } = args; - return ; -}; - -export const BetaBanner = () => { - return ( - - - Managed Database for MySQL is available in a free, open beta period - until May 2nd, 2022. This is a beta environment and should not be used - to support production workloads. Review the{' '} - - Early Adopter Program SLA - - . - - - ); -}; - -#### Beta Banner - -Beta banners, along with [beta chips](/docs/elements-chips-beta-chips--default-story), provide notice to users about beta features. - - - {BetaBanner.bind({})} - - -#### Call to Action Banner - - - {CallToActionBanner.bind({})} - - -### Component API - - - - {DismissibleBannerTemplate.bind({})} - - - -#### Beta - -Beta banners, along with [beta chips](/docs/elements-chips-beta-chips--default-story), provide notice to users about beta features. - - - {BetaBanner.bind({})} - - - diff --git a/packages/manager/src/components/DismissibleBanner/DismissibleBanner.stories.tsx b/packages/manager/src/components/DismissibleBanner/DismissibleBanner.stories.tsx new file mode 100644 index 00000000000..25f38245c01 --- /dev/null +++ b/packages/manager/src/components/DismissibleBanner/DismissibleBanner.stories.tsx @@ -0,0 +1,65 @@ +import * as React from 'react'; + +import { Button } from 'src/components/Button/Button'; +import { Link } from 'src/components/Link'; +import { Typography } from 'src/components/Typography'; + +import { DismissibleBanner } from './DismissibleBanner'; + +import type { Meta, StoryObj } from '@storybook/react'; + +type Story = StoryObj; + +export const Default: Story = { + render: (args) => ( + + This is an example of a dismissible banner. + + ), +}; + +/** + * Example of a dismissible banner with an associated action + */ +export const CallToActionBanner: Story = { + render: () => ( + null}> + Upgrade Version + + } + preferenceKey="cluster-v1" + variant="info" + > + A new version of Kubernetes is available. + + ), +}; + +/** + * Beta banners, along with [beta chips](/docs/elements-chips-beta-chips--default-story), provide notice to users about beta features. + */ +export const BetaBanner: Story = { + render: () => ( + + + Managed Database for MySQL is available in a free, open beta period + until May 2nd, 2022. This is a beta environment and should not be used + to support production workloads. Review the{' '} + + Early Adopter Program SLA + + . + + + ), +}; + +const meta: Meta = { + args: { preferenceKey: 'dismissible-banner' }, + component: DismissibleBanner, + title: 'Components/Notifications/Dismissible Banners', +}; + +export default meta; diff --git a/packages/manager/src/components/DismissibleBanner/DismissibleBanner.styles.ts b/packages/manager/src/components/DismissibleBanner/DismissibleBanner.styles.ts new file mode 100644 index 00000000000..7ac4261bf71 --- /dev/null +++ b/packages/manager/src/components/DismissibleBanner/DismissibleBanner.styles.ts @@ -0,0 +1,31 @@ +import { styled } from '@mui/material/styles'; + +import { Notice } from 'src/components/Notice/Notice'; + +import { StyledLinkButton } from '../Button/StyledLinkButton'; + +export const StyledNotice = styled(Notice, { label: 'StyledNotice' })( + ({ theme }) => ({ + '&&': { + p: { + lineHeight: '1.25rem', + }, + }, + alignItems: 'center', + background: theme.bg.bgPaper, + borderRadius: 1, + display: 'flex', + flexFlow: 'row nowrap', + justifyContent: 'space-between', + marginBottom: theme.spacing(), + padding: theme.spacing(2), + }) +); + +export const StyledButton = styled(StyledLinkButton, { label: 'StyledButton' })( + ({ theme }) => ({ + color: theme.textColors.tableStatic, + display: 'flex', + marginLeft: 20, + }) +); diff --git a/packages/manager/src/components/DismissibleBanner/DismissibleBanner.test.tsx b/packages/manager/src/components/DismissibleBanner/DismissibleBanner.test.tsx new file mode 100644 index 00000000000..cba9ba38d02 --- /dev/null +++ b/packages/manager/src/components/DismissibleBanner/DismissibleBanner.test.tsx @@ -0,0 +1,50 @@ +import { fireEvent } from '@testing-library/react'; +import * as React from 'react'; + +import { Button } from 'src/components/Button/Button'; +import { Typography } from 'src/components/Typography'; +import { renderWithTheme } from 'src/utilities/testHelpers'; + +import { DismissibleBanner } from './DismissibleBanner'; + +describe('Dismissible banner', () => { + const props = { + preferenceKey: 'dismissible-banner', + }; + + it('renders a dismissible banner and can dismiss it', () => { + const { getByLabelText, getByText } = renderWithTheme( + + This is a dismissible banner + + ); + const text = getByText('This is a dismissible banner'); + expect(text).toBeVisible(); + const dismissButton = getByLabelText('Dismiss dismissible-banner banner'); + expect(dismissButton).toBeVisible(); + fireEvent.click(dismissButton); + expect(dismissButton).not.toBeInTheDocument(); + }); + it('renders a dismissible banner with an action button', () => { + const onClickProp = { + onClick: vi.fn(), + }; + + const { getByText } = renderWithTheme( + + Action button + + } + > + Banner with action button + + ); + const button = getByText('Action button'); + expect(button).toBeVisible(); + fireEvent.click(button); + expect(onClickProp.onClick).toHaveBeenCalled(); + }); +}); diff --git a/packages/manager/src/components/DismissibleBanner/DismissibleBanner.tsx b/packages/manager/src/components/DismissibleBanner/DismissibleBanner.tsx index b20a25167a9..53207a8ad60 100644 --- a/packages/manager/src/components/DismissibleBanner/DismissibleBanner.tsx +++ b/packages/manager/src/components/DismissibleBanner/DismissibleBanner.tsx @@ -1,29 +1,63 @@ import Close from '@mui/icons-material/Close'; import Grid from '@mui/material/Unstable_Grid2'; -import { styled } from '@mui/material/styles'; import { SxProps } from '@mui/system'; import * as React from 'react'; import { Box } from 'src/components/Box'; -import { Notice } from 'src/components/Notice/Notice'; import { DismissibleNotificationOptions, useDismissibleNotifications, } from 'src/hooks/useDismissibleNotifications'; +import { StyledButton, StyledNotice } from './DismissibleBanner.styles'; + import type { NoticeProps } from 'src/components/Notice/Notice'; interface Props { + /** + * Optional element to pass to the banner to trigger actions + */ actionButton?: JSX.Element; + /** + * Child element to pass to the banner + */ children: JSX.Element; + /** + * Additional classes to the root element + */ className?: string; + /** + * Additional controls to pass to the Dismissible Banner + */ options?: DismissibleNotificationOptions; + /** + * Used to check if this banner has already been dismissed + */ preferenceKey: string; + /** + * Additional styles to apply to the root element + */ sx?: SxProps; } type CombinedProps = Props & Partial; +/** + * ## Usage + * + * Banners appear between the top nav and page content and are a visual interruption of the normal page layout. Their use must be approved by all project stakeholders. + * + * ## Design + * - Banners are dismissible using an 'X' icon. + * - Consider adding a link to a doc or a guide for users to learn more. + * - Banners should be considered as one part of a larger communications plan; messaging should be developed with the marketing team. + * + * ## Variants + * Under the hood, banners use the [Notice](/docs/components-notifications-notices--success) component so they have the same variants such as: + * - Success: Informs users of a new feature or improved service. + * - Warning: Informs users of an impending change that will have an impact on their service(s). + * - Call to action: Primary Button or text link allows a user to take action directly from the banner. + */ export const DismissibleBanner = (props: CombinedProps) => { const { actionButton, @@ -92,26 +126,3 @@ export const useDismissibleBanner = ( return { handleDismiss, hasDismissedBanner }; }; - -const StyledNotice = styled(Notice)(({ theme }) => ({ - '&&': { - p: { - lineHeight: '1.25rem', - }, - }, - alignItems: 'center', - background: theme.bg.bgPaper, - borderRadius: 1, - display: 'flex', - flexFlow: 'row nowrap', - justifyContent: 'space-between', - marginBottom: theme.spacing(), - padding: theme.spacing(2), -})); - -const StyledButton = styled('button')(({ theme }) => ({ - ...theme.applyLinkStyles, - color: theme.textColors.tableStatic, - display: 'flex', - marginLeft: 20, -})); diff --git a/packages/manager/src/components/DismissibleBanner/index.ts b/packages/manager/src/components/DismissibleBanner/index.ts deleted file mode 100644 index 19bfe9df306..00000000000 --- a/packages/manager/src/components/DismissibleBanner/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { DismissibleBanner } from './DismissibleBanner'; diff --git a/packages/manager/src/components/ProductInformationBanner/ProductInformationBanner.tsx b/packages/manager/src/components/ProductInformationBanner/ProductInformationBanner.tsx index 21e67df23b0..c0b9eec83c8 100644 --- a/packages/manager/src/components/ProductInformationBanner/ProductInformationBanner.tsx +++ b/packages/manager/src/components/ProductInformationBanner/ProductInformationBanner.tsx @@ -6,7 +6,7 @@ import { ProductInformationBannerLocation } from 'src/featureFlags'; import { useFlags } from 'src/hooks/useFlags'; import { isAfter } from 'src/utilities/date'; -import { DismissibleBanner } from '../DismissibleBanner'; +import { DismissibleBanner } from '../DismissibleBanner/DismissibleBanner'; import type { NoticeProps } from 'src/components/Notice/Notice'; diff --git a/packages/manager/src/features/Domains/DomainBanner.tsx b/packages/manager/src/features/Domains/DomainBanner.tsx index 1aa60481d29..911be5f9f45 100644 --- a/packages/manager/src/features/Domains/DomainBanner.tsx +++ b/packages/manager/src/features/Domains/DomainBanner.tsx @@ -2,7 +2,7 @@ import { styled } from '@mui/material/styles'; import { DateTime } from 'luxon'; import * as React from 'react'; -import { DismissibleBanner } from 'src/components/DismissibleBanner'; +import { DismissibleBanner } from 'src/components/DismissibleBanner/DismissibleBanner'; import { Link } from 'src/components/Link'; import { Typography } from 'src/components/Typography'; diff --git a/packages/manager/src/features/GlobalNotifications/APIMaintenanceBanner.tsx b/packages/manager/src/features/GlobalNotifications/APIMaintenanceBanner.tsx index 561d756115d..4e8316e03d1 100644 --- a/packages/manager/src/features/GlobalNotifications/APIMaintenanceBanner.tsx +++ b/packages/manager/src/features/GlobalNotifications/APIMaintenanceBanner.tsx @@ -1,8 +1,8 @@ -import { Stack } from 'src/components/Stack'; import * as React from 'react'; -import { DismissibleBanner } from 'src/components/DismissibleBanner'; +import { DismissibleBanner } from 'src/components/DismissibleBanner/DismissibleBanner'; import { Link } from 'src/components/Link'; +import { Stack } from 'src/components/Stack'; import { Typography } from 'src/components/Typography'; import { SuppliedMaintenanceData } from 'src/featureFlags'; import { queryPresets } from 'src/queries/base'; diff --git a/packages/manager/src/features/GlobalNotifications/ComplianceBanner.tsx b/packages/manager/src/features/GlobalNotifications/ComplianceBanner.tsx index 0c3f5392307..cda10f1c5ad 100644 --- a/packages/manager/src/features/GlobalNotifications/ComplianceBanner.tsx +++ b/packages/manager/src/features/GlobalNotifications/ComplianceBanner.tsx @@ -3,7 +3,7 @@ import * as React from 'react'; import { Box } from 'src/components/Box'; import { Button } from 'src/components/Button/Button'; -import { DismissibleBanner } from 'src/components/DismissibleBanner'; +import { DismissibleBanner } from 'src/components/DismissibleBanner/DismissibleBanner'; import { Typography } from 'src/components/Typography'; import { complianceUpdateContext } from 'src/context/complianceUpdateContext'; import { useNotificationsQuery } from 'src/queries/accountNotifications'; diff --git a/packages/manager/src/features/GlobalNotifications/TaxCollectionBanner.tsx b/packages/manager/src/features/GlobalNotifications/TaxCollectionBanner.tsx index d96a9ad9a05..55ddbbf2b9e 100644 --- a/packages/manager/src/features/GlobalNotifications/TaxCollectionBanner.tsx +++ b/packages/manager/src/features/GlobalNotifications/TaxCollectionBanner.tsx @@ -3,7 +3,7 @@ import * as React from 'react'; import { useHistory } from 'react-router-dom'; import { Button } from 'src/components/Button/Button'; -import { DismissibleBanner } from 'src/components/DismissibleBanner'; +import { DismissibleBanner } from 'src/components/DismissibleBanner/DismissibleBanner'; import { Link } from 'src/components/Link'; import { Typography } from 'src/components/Typography'; import { useFlags } from 'src/hooks/useFlags'; diff --git a/packages/manager/src/features/Help/StatusBanners.tsx b/packages/manager/src/features/Help/StatusBanners.tsx index 445961d71a4..5dea406c15a 100644 --- a/packages/manager/src/features/Help/StatusBanners.tsx +++ b/packages/manager/src/features/Help/StatusBanners.tsx @@ -3,7 +3,7 @@ import { DateTime } from 'luxon'; import * as React from 'react'; import { Box } from 'src/components/Box'; -import { DismissibleBanner } from 'src/components/DismissibleBanner'; +import { DismissibleBanner } from 'src/components/DismissibleBanner/DismissibleBanner'; import { Link } from 'src/components/Link'; import { Typography } from 'src/components/Typography'; import { diff --git a/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/UpgradeKubernetesVersionBanner.tsx b/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/UpgradeKubernetesVersionBanner.tsx index 6030d1992f7..56cbf4734d9 100644 --- a/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/UpgradeKubernetesVersionBanner.tsx +++ b/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/UpgradeKubernetesVersionBanner.tsx @@ -2,7 +2,7 @@ import Grid from '@mui/material/Unstable_Grid2'; import * as React from 'react'; import { Button } from 'src/components/Button/Button'; -import { DismissibleBanner } from 'src/components/DismissibleBanner'; +import { DismissibleBanner } from 'src/components/DismissibleBanner/DismissibleBanner'; import { Typography } from 'src/components/Typography'; import { useKubernetesVersionQuery } from 'src/queries/kubernetes'; diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodesDetailNavigation.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodesDetailNavigation.tsx index a73b3d6f441..69addb34d5b 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodesDetailNavigation.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodesDetailNavigation.tsx @@ -8,7 +8,7 @@ import { } from 'react-router-dom'; import { CircleProgress } from 'src/components/CircleProgress'; -import { DismissibleBanner } from 'src/components/DismissibleBanner'; +import { DismissibleBanner } from 'src/components/DismissibleBanner/DismissibleBanner'; import { DocumentTitleSegment } from 'src/components/DocumentTitle'; import { ErrorState } from 'src/components/ErrorState/ErrorState'; import { TabPanels } from 'src/components/ReachTabPanels'; diff --git a/packages/manager/src/features/ObjectStorage/ObjectStorageLanding.tsx b/packages/manager/src/features/ObjectStorage/ObjectStorageLanding.tsx index eaae1dff96b..2c1679d8097 100644 --- a/packages/manager/src/features/ObjectStorage/ObjectStorageLanding.tsx +++ b/packages/manager/src/features/ObjectStorage/ObjectStorageLanding.tsx @@ -4,7 +4,7 @@ import * as React from 'react'; import { useHistory, useParams } from 'react-router-dom'; import { StyledLinkButton } from 'src/components/Button/StyledLinkButton'; -import { DismissibleBanner } from 'src/components/DismissibleBanner'; +import { DismissibleBanner } from 'src/components/DismissibleBanner/DismissibleBanner'; import { DocumentTitleSegment } from 'src/components/DocumentTitle'; import { LandingHeader } from 'src/components/LandingHeader'; import { Link } from 'src/components/Link'; diff --git a/packages/manager/src/features/VPCs/VPCDetail/VPCDetail.tsx b/packages/manager/src/features/VPCs/VPCDetail/VPCDetail.tsx index 9f342a5814e..5025209e915 100644 --- a/packages/manager/src/features/VPCs/VPCDetail/VPCDetail.tsx +++ b/packages/manager/src/features/VPCs/VPCDetail/VPCDetail.tsx @@ -6,7 +6,7 @@ import { useParams } from 'react-router-dom'; import { Box } from 'src/components/Box'; import { StyledLinkButton } from 'src/components/Button/StyledLinkButton'; import { CircleProgress } from 'src/components/CircleProgress/CircleProgress'; -import { DismissibleBanner } from 'src/components/DismissibleBanner'; +import { DismissibleBanner } from 'src/components/DismissibleBanner/DismissibleBanner'; import { DocumentTitleSegment } from 'src/components/DocumentTitle'; import { EntityHeader } from 'src/components/EntityHeader/EntityHeader'; import { ErrorState } from 'src/components/ErrorState/ErrorState';