From f4c6247e7c427baeb98a03044bf424674e8fd890 Mon Sep 17 00:00:00 2001 From: Fran McDade Date: Wed, 20 Dec 2023 16:48:16 +1000 Subject: [PATCH 1/3] feat: create dismissible cookie banner (#543) --- .../src/components/common/Banner/banner.tsx | 19 ++-- .../CookieBanner/cookieBanner.styles.ts | 48 +++++++++++ .../components/CookieBanner/cookieBanner.tsx | 86 +++++++++++++++++++ .../DismissibleBanner/dismissibleBanner.tsx | 39 +++++++++ .../ButtonOutline/buttonOutline.styles.ts | 18 ++++ .../ButtonOutline/buttonOutline.tsx | 23 +++++ .../types/data-explorer-ui.d.ts | 1 + 7 files changed, 226 insertions(+), 8 deletions(-) create mode 100644 packages/data-explorer-ui/src/components/common/Banner/components/CookieBanner/cookieBanner.styles.ts create mode 100644 packages/data-explorer-ui/src/components/common/Banner/components/CookieBanner/cookieBanner.tsx create mode 100644 packages/data-explorer-ui/src/components/common/Banner/components/DismissibleBanner/dismissibleBanner.tsx create mode 100644 packages/data-explorer-ui/src/components/common/Button/components/ButtonOutline/buttonOutline.styles.ts create mode 100644 packages/data-explorer-ui/src/components/common/Button/components/ButtonOutline/buttonOutline.tsx diff --git a/packages/data-explorer-ui/src/components/common/Banner/banner.tsx b/packages/data-explorer-ui/src/components/common/Banner/banner.tsx index d2f23ade..7436d71f 100644 --- a/packages/data-explorer-ui/src/components/common/Banner/banner.tsx +++ b/packages/data-explorer-ui/src/components/common/Banner/banner.tsx @@ -1,19 +1,22 @@ import { Alert as MAlert, AlertProps as MAlertProps } from "@mui/material"; -import React, { ReactNode } from "react"; +import React, { forwardRef, ReactNode } from "react"; export interface BannerProps extends MAlertProps { children: ReactNode; className?: string; } -export const Banner = ({ - children, - className, - ...props /* Spread props to allow for Mui AlertProps specific prop overrides. */ -}: BannerProps): JSX.Element => { +export const Banner = forwardRef(function Banner( + { + children, + className, + ...props /* Spread props to allow for Mui AlertProps specific prop overrides. */ + }: BannerProps, + ref +): JSX.Element { return ( - + {children} ); -}; +}); diff --git a/packages/data-explorer-ui/src/components/common/Banner/components/CookieBanner/cookieBanner.styles.ts b/packages/data-explorer-ui/src/components/common/Banner/components/CookieBanner/cookieBanner.styles.ts new file mode 100644 index 00000000..31a598b9 --- /dev/null +++ b/packages/data-explorer-ui/src/components/common/Banner/components/CookieBanner/cookieBanner.styles.ts @@ -0,0 +1,48 @@ +import styled from "@emotion/styled"; +import { mediaTabletUp } from "../../../../../styles/common/mixins/breakpoints"; +import { white } from "../../../../../styles/common/mixins/colors"; +import { textBody400 } from "../../../../../styles/common/mixins/fonts"; +import { shadows02 } from "../../../../../styles/common/mixins/shadows"; +import { Banner } from "../../banner"; + +export const CookieBanner = styled(Banner)` + bottom: 0; + box-shadow: ${shadows02}; + color: ${white}; + flex-direction: column; + gap: 16px; + left: 0; + margin: 8px; + padding: 16px; + position: fixed; + width: calc(100vw - 16px); + z-index: 1400; // Above support fab. + + .MuiAlert-message { + ${textBody400}; + + .MuiLink-root { + color: inherit; + text-decoration: underline; + + &:hover { + text-decoration: none; + } + } + } + + .MuiAlert-action { + flex-wrap: wrap; + gap: 8px; + justify-content: flex-start; + margin: 0; + padding: 0; + } + + ${mediaTabletUp} { + box-sizing: content-box; + margin: 16px; + max-width: 400px; + width: unset; + } +`; diff --git a/packages/data-explorer-ui/src/components/common/Banner/components/CookieBanner/cookieBanner.tsx b/packages/data-explorer-ui/src/components/common/Banner/components/CookieBanner/cookieBanner.tsx new file mode 100644 index 00000000..479199ad --- /dev/null +++ b/packages/data-explorer-ui/src/components/common/Banner/components/CookieBanner/cookieBanner.tsx @@ -0,0 +1,86 @@ +import { AlertProps as MAlertProps, ButtonProps } from "@mui/material"; +import React, { forwardRef, Fragment, ReactNode } from "react"; +import { FLAG } from "../../../../../hooks/useFeatureFlag/common/entities"; +import { + getLocalStorage, + setLocalStorage, +} from "../../../../../hooks/useFeatureFlag/common/utils"; +import { ButtonPrimary } from "../../../Button/components/ButtonPrimary/buttonPrimary"; +import { DismissibleBanner } from "../DismissibleBanner/dismissibleBanner"; +import { CookieBanner as Banner } from "./cookieBanner.styles"; + +export interface CookieBannerProps extends MAlertProps { + className?: string; + localStorageKey: string; + message?: ReactNode; + secondaryAction?: ReactNode; +} + +export const CookieBanner = ({ + className, + localStorageKey, + message, + secondaryAction, +}: CookieBannerProps): JSX.Element => { + const isCookieAccepted = getLocalStorage(localStorageKey) === FLAG.TRUE; + + // Callback fired when the banner requests to be closed. + const onDismiss = (): void => { + setLocalStorage(localStorageKey, FLAG.TRUE); + }; + + return ( + renderCloseButton(props, secondaryAction), + }} + > + {message} + + ); +}; + +/** + * Return the cookie banner alert. + * @param props - Alert props e.g. "onClick" to close banner. + * @returns alert element. + */ +const Alert = forwardRef(function Alert( + { ...props }, + ref +): JSX.Element { + return ( + + {props.children} + + ); +}); + +/** + * Returns the close action component(s). + * @param buttonProps - Button props e.g. "onClick" to close banner. + * @param secondaryAction - Secondary action component. + * @returns close button element(s). + */ +function renderCloseButton( + buttonProps: ButtonProps, + secondaryAction?: ReactNode +): JSX.Element { + return ( + + Ok, Got It + {secondaryAction} + + ); +} diff --git a/packages/data-explorer-ui/src/components/common/Banner/components/DismissibleBanner/dismissibleBanner.tsx b/packages/data-explorer-ui/src/components/common/Banner/components/DismissibleBanner/dismissibleBanner.tsx new file mode 100644 index 00000000..cabcac34 --- /dev/null +++ b/packages/data-explorer-ui/src/components/common/Banner/components/DismissibleBanner/dismissibleBanner.tsx @@ -0,0 +1,39 @@ +import { + Alert as MAlert, + AlertProps as MAlertProps, + Fade, +} from "@mui/material"; +import React, { ElementType, ReactNode, useState } from "react"; + +export interface CookieBannerProps extends MAlertProps { + Alert?: ElementType; + children: ReactNode; + className?: string; + onDismiss?: () => void; + open: boolean; +} + +export const DismissibleBanner = ({ + Alert = MAlert /* Requires forwardRef to be used as a child of Fade. */, + className, + children, + onDismiss, + open, + ...props /* Spread props to allow for Mui AlertProps specific prop overrides. */ +}: CookieBannerProps): JSX.Element => { + const [isIn, setIsIn] = useState(open); + + // Callback fired when the component requests to be closed. + const onClose = (): void => { + onDismiss?.(); + setIsIn(false); + }; + + return ( + + + {children} + + + ); +}; diff --git a/packages/data-explorer-ui/src/components/common/Button/components/ButtonOutline/buttonOutline.styles.ts b/packages/data-explorer-ui/src/components/common/Button/components/ButtonOutline/buttonOutline.styles.ts new file mode 100644 index 00000000..cd9a7be7 --- /dev/null +++ b/packages/data-explorer-ui/src/components/common/Button/components/ButtonOutline/buttonOutline.styles.ts @@ -0,0 +1,18 @@ +import styled from "@emotion/styled"; +import { white } from "../../../../../styles/common/mixins/colors"; +import { alpha32, alpha64 } from "../../../../../theme/common/palette"; +import { Button } from "../../button"; + +export const ButtonOutline = styled(Button)` + box-shadow: inset 0 0 0 1px ${white}${alpha32}; + color: ${white}; + + &:hover { + box-shadow: inset 0 0 0 1px ${white}${alpha64}; + } + + &:disabled { + box-shadow: inset 0 0 0 1px ${white}${alpha32}; + color: ${white}; + } +`; diff --git a/packages/data-explorer-ui/src/components/common/Button/components/ButtonOutline/buttonOutline.tsx b/packages/data-explorer-ui/src/components/common/Button/components/ButtonOutline/buttonOutline.tsx new file mode 100644 index 00000000..bcc471fc --- /dev/null +++ b/packages/data-explorer-ui/src/components/common/Button/components/ButtonOutline/buttonOutline.tsx @@ -0,0 +1,23 @@ +import React, { forwardRef } from "react"; +import { ButtonProps } from "../../button"; +import { ButtonOutline as Button } from "./buttonOutline.styles"; + +export const ButtonOutline = forwardRef( + function ButtonOutline( + { + className, + ...props /* Spread props to allow for Mui ButtonProps specific prop overrides e.g. "onClick". */ + }: ButtonProps, + ref + ): JSX.Element { + return ( +