diff --git a/packages/fuselage/.loki/reference/chrome_iphone7_Containers_Banner_Banner.png b/packages/fuselage/.loki/reference/chrome_iphone7_Containers_Banner_Banner.png new file mode 100644 index 0000000000000000000000000000000000000000..1ddd560b2d3a1c43de37c8e0e5a6cdc921f7ef33 Binary files /dev/null and b/packages/fuselage/.loki/reference/chrome_iphone7_Containers_Banner_Banner.png differ diff --git a/packages/fuselage/.loki/reference/chrome_iphone7_Containers_Banner_Inline.png b/packages/fuselage/.loki/reference/chrome_iphone7_Containers_Banner_Inline.png new file mode 100644 index 0000000000000000000000000000000000000000..20becab25e5694f404ab139817bbc33e2453a6c2 Binary files /dev/null and b/packages/fuselage/.loki/reference/chrome_iphone7_Containers_Banner_Inline.png differ diff --git a/packages/fuselage/.loki/reference/chrome_iphone7_Containers_Banner_Normal.png b/packages/fuselage/.loki/reference/chrome_iphone7_Containers_Banner_Normal.png new file mode 100644 index 0000000000000000000000000000000000000000..8258e8c780938a7be66dcc243a9f584d223c7f2e Binary files /dev/null and b/packages/fuselage/.loki/reference/chrome_iphone7_Containers_Banner_Normal.png differ diff --git a/packages/fuselage/.loki/reference/chrome_laptop_Containers_Banner_Banner.png b/packages/fuselage/.loki/reference/chrome_laptop_Containers_Banner_Banner.png new file mode 100644 index 0000000000000000000000000000000000000000..6831838c0903707e8dee584baccdf57c43af3fd7 Binary files /dev/null and b/packages/fuselage/.loki/reference/chrome_laptop_Containers_Banner_Banner.png differ diff --git a/packages/fuselage/.loki/reference/chrome_laptop_Containers_Banner_Inline.png b/packages/fuselage/.loki/reference/chrome_laptop_Containers_Banner_Inline.png new file mode 100644 index 0000000000000000000000000000000000000000..4f3370dd6988c234b9fcaa5c1a3e75a9225b7e03 Binary files /dev/null and b/packages/fuselage/.loki/reference/chrome_laptop_Containers_Banner_Inline.png differ diff --git a/packages/fuselage/.loki/reference/chrome_laptop_Containers_Banner_Normal.png b/packages/fuselage/.loki/reference/chrome_laptop_Containers_Banner_Normal.png new file mode 100644 index 0000000000000000000000000000000000000000..72d80ec9553a02984efc132443ae36c666a25b16 Binary files /dev/null and b/packages/fuselage/.loki/reference/chrome_laptop_Containers_Banner_Normal.png differ diff --git a/packages/fuselage/src/components/Banner/Banner.js b/packages/fuselage/src/components/Banner/Banner.js new file mode 100644 index 0000000000000000000000000000000000000000..7d02a7d5f5bf59f2b08d9f27c187dab9f44b5057 --- /dev/null +++ b/packages/fuselage/src/components/Banner/Banner.js @@ -0,0 +1,52 @@ +import React, { useMemo } from 'react'; + +import { composeClassNames as cx } from '../../helpers/composeClassNames'; +import { useStyleSheet } from '../../hooks/useStyleSheet'; +import Button from '../Button'; +import { Icon } from '../Icon'; + +const variants = ['neutral', 'info', 'success', 'warning', 'danger']; + +const Banner = ({ + inline = false, + children, + className, + closeable, + icon, + title, + variant = 'neutral', + onClose, + ...props +}) => { + useStyleSheet(); + + variant = variants.includes(variant) ? variant : variants[0]; + + const closeButtonProps = useMemo(() => { + return variant !== variants[0] ? { [variant]: true } : {}; + }, [variant]); + + return ( +
+ {icon &&
{icon}
} +
+ {title && ( +
{title}
+ )} + {children} +
+ {closeable && ( +
+ +
+ )} +
+ ); +}; + +export default Banner; diff --git a/packages/fuselage/src/components/Banner/Banner.stories.js b/packages/fuselage/src/components/Banner/Banner.stories.js new file mode 100644 index 0000000000000000000000000000000000000000..d537ac8c159d15e20205b11d860f02b9666bc704 --- /dev/null +++ b/packages/fuselage/src/components/Banner/Banner.stories.js @@ -0,0 +1,148 @@ +import { action } from '@storybook/addon-actions'; +import React from 'react'; + +import { Icon } from '../Icon'; +import Banner from './Banner'; + +export default { + title: 'Containers/Banner', + component: Banner, +}; + +export const _Banner = () => ( + } + title='Sed ut perspiciatis unde' + onClose={action('close')} + > + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod + tempor + +); +_Banner.storyName = 'Banner'; + +export const Normal = () => ( + <> + } + title='Sed ut perspiciatis unde' + variant='neutral' + onClose={action('close')} + > + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod + tempor + +
+ } + title='Sed ut perspiciatis unde' + variant='info' + onClose={action('close')} + > + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod + tempor + +
+ } + title='Sed ut perspiciatis unde' + variant='success' + onClose={action('close')} + > + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod + tempor + +
+ } + title='Sed ut perspiciatis unde' + variant='warning' + onClose={action('close')} + > + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod + tempor + +
+ } + title='Sed ut perspiciatis unde' + variant='danger' + onClose={action('close')} + > + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod + tempor + +
+ +); + +export const Inline = () => ( + <> + } + title='Sed ut perspiciatis unde' + variant='neutral' + onClose={action('close')} + > + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod + tempor + +
+ } + title='Sed ut perspiciatis unde' + variant='info' + onClose={action('close')} + > + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod + tempor + +
+ } + title='Sed ut perspiciatis unde' + variant='success' + onClose={action('close')} + > + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod + tempor + +
+ } + title='Sed ut perspiciatis unde' + variant='warning' + onClose={action('close')} + > + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod + tempor + +
+ } + title='Sed ut perspiciatis unde' + variant='danger' + onClose={action('close')} + > + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod + tempor + +
+ +); diff --git a/packages/fuselage/src/components/Banner/Banner.styles.scss b/packages/fuselage/src/components/Banner/Banner.styles.scss new file mode 100644 index 0000000000000000000000000000000000000000..307977c92dcf543b99b7295722c2f3a43a319f78 --- /dev/null +++ b/packages/fuselage/src/components/Banner/Banner.styles.scss @@ -0,0 +1,117 @@ +@use 'sass:map'; +@use '../../styles/colors.scss'; +@use '../../styles/typography.scss'; + +@mixin -variant($colors) { + color: map.get($colors, color); + background-color: map.get($colors, background-color); +} + +.rcx-banner { + display: flex; + flex-flow: row nowrap; + justify-content: space-between; + align-items: flex-start; + flex: 0 1 auto; + + box-sizing: border-box; + + font-family: typography.font-family('sans'); + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + + &--neutral { + @include -variant( + ( + background-color: colors.neutral(100), + color: colors.neutral(800), + ) + ); + } + + &--info { + @include -variant( + ( + background-color: colors.info(200), + color: colors.info(600), + ) + ); + } + + &--success { + @include -variant( + ( + background-color: colors.success(200), + color: colors.success(800), + ) + ); + } + + &--warning { + @include -variant( + ( + background-color: colors.warning(200), + color: colors.warning(900), + ) + ); + } + + &--danger { + @include -variant( + ( + background-color: colors.danger(200), + color: colors.danger(600), + ) + ); + } + + &__icon { + display: none; + + padding-block: 22px; + padding-inline-start: 16px; + + @include on-breakpoint(sm) { + display: unset; + } + + &--inline { + padding-block: 10px; + } + } + + &__content { + flex-grow: 1; + align-self: center; + + padding-block: 14px; + padding-inline: 12px; + @include typography.use-font-scale(p1); + + &--inline { + padding-block: 12px; + @include typography.use-with-truncated-text; + } + } + + &__title { + margin: 0; + padding: 0; + @include typography.use-font-scale(p2); + + &--inline { + float: inline-start; + + padding-inline-end: 8px; + } + } + + &__close-button { + padding-block: 20px; + padding-inline-end: 24px; + + &--inline { + padding-block: 8px; + } + } +} diff --git a/packages/fuselage/src/components/Banner/index.js b/packages/fuselage/src/components/Banner/index.js new file mode 100644 index 0000000000000000000000000000000000000000..b927b80912cf8f52346d55b5a94617d3dff663f1 --- /dev/null +++ b/packages/fuselage/src/components/Banner/index.js @@ -0,0 +1 @@ +export { default } from './Banner'; diff --git a/packages/fuselage/src/components/index.js b/packages/fuselage/src/components/index.js index 4e44423ceed9ca5203adaa9a173ccf23689d28aa..27f659367f69c181846b2684f29be23629bea4fa 100644 --- a/packages/fuselage/src/components/index.js +++ b/packages/fuselage/src/components/index.js @@ -2,6 +2,7 @@ export * from './Accordion'; export * from './AutoComplete'; export * from './Avatar'; export * from './Badge'; +export { default } from './Banner'; export * from './Box'; export { default as Button, ActionButton } from './Button'; export * from './ButtonGroup'; diff --git a/packages/fuselage/src/components/index.scss b/packages/fuselage/src/components/index.scss index 601bf82b5e46f15dcddce83b1df2a73ab854c7ad..0b777a67930e8efb75b80323b93bfc97b2de7b87 100644 --- a/packages/fuselage/src/components/index.scss +++ b/packages/fuselage/src/components/index.scss @@ -2,6 +2,7 @@ @import './AutoComplete/styles.scss'; @import './Avatar/styles.scss'; @import './Badge/styles.scss'; +@import './Banner/Banner.styles.scss'; @import './Box/styles.scss'; @import './Button/Button.styles.scss'; @import './ButtonGroup/styles.scss'; diff --git a/packages/fuselage/src/helpers/composeClassNames.js b/packages/fuselage/src/helpers/composeClassNames.js new file mode 100644 index 0000000000000000000000000000000000000000..45842118fc057f3493e2801e50647a7d379a0753 --- /dev/null +++ b/packages/fuselage/src/helpers/composeClassNames.js @@ -0,0 +1,49 @@ +const withPrefix = (prefix) => (modifier) => + prefix ? `${prefix}--${modifier}` : modifier; + +export const composeClassNames = (prefix) => (...args) => { + const addPrefix = withPrefix(prefix); + + const classNames = args + .map((arg) => { + if (typeof arg === 'string') { + return arg; + } + + if (typeof arg === 'object' && Array.isArray(arg)) { + return arg.filter(Boolean).map(addPrefix).join(' '); + } + + if (typeof arg === 'object' && arg !== null) { + return Object.entries(arg) + .map(([key, value]) => { + if (typeof value === 'boolean') { + if (value) { + return addPrefix(key); + } + + return null; + } + + if (typeof value === 'string' || typeof value === 'number') { + return addPrefix(`${key}-${value}`); + } + + return null; + }) + .filter(Boolean) + .join(' '); + } + + return null; + }) + .concat() + .filter(Boolean) + .join(' '); + + if (!prefix) { + return classNames; + } + + return `${prefix} ${classNames}`; +};