diff --git a/ui/components/component-library/banner-base/README.mdx b/ui/components/component-library/banner-base/README.mdx new file mode 100644 index 000000000..2d005bf5f --- /dev/null +++ b/ui/components/component-library/banner-base/README.mdx @@ -0,0 +1,121 @@ +import { Story, Canvas, ArgsTable } from '@storybook/addon-docs'; +import { BannerBase } from './banner-base'; + +### This is a base component. It should not be used in your feature code directly but as a "base" for other UI components + +# BannerBase + +The `BannerBase` is the base component for banners + + + + + +## Props + +The `BannerBase` accepts all props below as well as all [Box](/docs/ui-components-ui-box-box-stories-js--default-story#props) component props + + + +### Title + +Use the `title` prop to pass a string that is sentence case no period. Use the `titleProps` prop to pass additional props to the `Text` component. + + + + + +```jsx +import { BannerBase } from '../../component-library'; + + + Pass only a string through the title prop +; +``` + +### Children + +The `children` is the description area of the `BannerBase` that can be a text or react node. Description shouldn't repeat title and only 1-3 lines. + + + + + +```jsx +import { SIZES } from '../../../helpers/constants/design-system'; +import { BannerBase } from '../../component-library'; + + + {`Description shouldn't repeat title. 1-3 lines. Can contain a `} + + hyperlink. + +; +``` + +### Action Button Label, onClick, & Props + +Use the `actionButtonLabel` prop to pass text, `actionButtonOnClick` prop to pass an onClick handler, and `actionButtonProps` prop to pass an object of [ButtonLink props](/docs/ui-components-component-library-button-link-button-link-stories-js--default-story) for the action + + + + + +```jsx +import { BannerBase, ICON_NAMES } from '../../component-library'; + + console.log('ButtonLink actionButtonOnClick demo')} +> + Use actionButtonLabel for action text, actionButtonOnClick for the onClick + handler, and actionButtonProps to pass any ButtonLink prop types such as + iconName +; +``` + +### On Close + +Use the `onClose` prop to pass a function to the close button. The close button will appear when this prop is used. + +Additional props can be passed to the close button with `closeButtonProps` + + + + + +```jsx +import { BannerBase } from '../../component-library'; + + console.log('close button clicked')} +> + Click the close button icon to hide this notifcation +; +``` + +### Start Accessory + +Use the `startAccessory` prop to add components such as icons or fox image to the start (default: left) of the `BannerBase` content + + + + + +```jsx +import { SIZES } from '../../../helpers/constants/design-system'; +import { BannerBase, Icon, ICON_NAMES } from '../../component-library'; + +} +> + The info icon on the left is passed through the startAccessory prop +; +``` diff --git a/ui/components/component-library/banner-base/__snapshots__/banner-base.test.js.snap b/ui/components/component-library/banner-base/__snapshots__/banner-base.test.js.snap new file mode 100644 index 000000000..13440fa40 --- /dev/null +++ b/ui/components/component-library/banner-base/__snapshots__/banner-base.test.js.snap @@ -0,0 +1,23 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`BannerBase should render bannerbase element correctly 1`] = ` +
+
+
+
+ Bannerbase test +
+

+ should render bannerbase element correctly +

+
+
+
+`; diff --git a/ui/components/component-library/banner-base/banner-base.js b/ui/components/component-library/banner-base/banner-base.js new file mode 100644 index 000000000..1544ec73e --- /dev/null +++ b/ui/components/component-library/banner-base/banner-base.js @@ -0,0 +1,133 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import classnames from 'classnames'; + +import { ButtonIcon, ButtonLink, ICON_NAMES, Text } from '..'; + +import Box from '../../ui/box'; + +import { + COLORS, + DISPLAY, + SIZES, + TEXT, +} from '../../../helpers/constants/design-system'; + +export const BannerBase = ({ + className, + title, + titleProps, + children, + + actionButtonLabel, + actionButtonOnClick, + actionButtonProps, + startAccessory, + onClose, + closeButtonProps, + ...props +}) => { + return ( + + {startAccessory && <>{startAccessory}} + +
+ {title && ( + + {title} + + )} + {children && typeof children === 'object' ? ( + children + ) : ( + {children} + )} + {actionButtonLabel && ( + + {actionButtonLabel} + + )} +
+ {onClose && ( + + )} +
+ ); +}; + +BannerBase.propTypes = { + /** + * The title of the BannerBase + */ + title: PropTypes.string, + /** + * Additional props to pass to the `Text` component used for the `title` text + */ + titleProps: PropTypes.shape(Text.PropTypes), + /** + * The children is the description area of the BannerBase below the title + */ + children: PropTypes.node, + /** + * The action of the BannerBase below the children + */ + action: PropTypes.node, + /** + * Label for action button (ButtonLink) of the BannerBase below the children + */ + actionButtonLabel: PropTypes.string, + /** + * Props for action button (ButtonLink) of the BannerBase below the children + */ + actionButtonProps: PropTypes.shape(ButtonLink.PropTypes), + /** + * The onClick handler for the action button (ButtonLink) + */ + actionButtonOnClick: PropTypes.func, + /** + * The start(defualt left) content area of BannerBase + */ + startAccessory: PropTypes.node, + /** + * The onClick handler for the close button + * When passed this will allow for the close button to show + */ + onClose: PropTypes.func, + /** + * The props to pass to the close button + */ + closeButtonProps: PropTypes.shape(ButtonIcon.PropTypes), + /** + * An additional className to apply to the BannerBase + */ + className: PropTypes.string, + /** + * BannerBase accepts all the props from Box + */ + ...Box.propTypes, +}; diff --git a/ui/components/component-library/banner-base/banner-base.scss b/ui/components/component-library/banner-base/banner-base.scss new file mode 100644 index 000000000..83ebfe6b7 --- /dev/null +++ b/ui/components/component-library/banner-base/banner-base.scss @@ -0,0 +1,4 @@ +.mm-banner-base { + border-left: 4px solid var(--color-icon-default); +} + diff --git a/ui/components/component-library/banner-base/banner-base.stories.js b/ui/components/component-library/banner-base/banner-base.stories.js new file mode 100644 index 000000000..faf9533d3 --- /dev/null +++ b/ui/components/component-library/banner-base/banner-base.stories.js @@ -0,0 +1,170 @@ +import React from 'react'; +import { useState } from '@storybook/addons'; +import { SIZES } from '../../../helpers/constants/design-system'; +import { Icon, ICON_NAMES, ButtonLink, ButtonPrimary } from '..'; +import { BannerBase } from './banner-base'; +import README from './README.mdx'; + +const marginSizeControlOptions = [ + undefined, + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 'auto', +]; + +export default { + title: 'Components/ComponentLibrary/BannerBase', + id: __filename, + component: BannerBase, + parameters: { + docs: { + page: README, + }, + backgrounds: { default: 'alternative' }, + }, + argTypes: { + className: { + control: 'text', + }, + title: { + control: 'text', + }, + children: { + control: 'text', + }, + action: { + control: 'func', + }, + actionButtonLabel: { + control: 'text', + }, + actionButtonOnClick: { + control: 'func', + }, + actionButtonProps: { + control: 'object', + }, + startAccessory: { + control: 'text', + }, + onClose: { + action: 'onClose', + }, + marginTop: { + options: marginSizeControlOptions, + control: 'select', + table: { category: 'box props' }, + }, + marginRight: { + options: marginSizeControlOptions, + control: 'select', + table: { category: 'box props' }, + }, + marginBottom: { + options: marginSizeControlOptions, + control: 'select', + table: { category: 'box props' }, + }, + marginLeft: { + options: marginSizeControlOptions, + control: 'select', + table: { category: 'box props' }, + }, + }, +}; + +export const DefaultStory = (args) => { + const onClose = () => console.log('BannerBase onClose trigger'); + return ; +}; + +DefaultStory.args = { + title: 'Title is sentence case no period', + children: "Description shouldn't repeat title. 1-3 lines.", + actionButtonLabel: 'Action', + startAccessory: , +}; + +DefaultStory.storyName = 'Default'; + +export const Title = (args) => { + return ; +}; + +Title.args = { + title: 'Title is sentence case no period', + children: 'Pass only a string through the title prop', +}; + +export const Children = (args) => { + return ( + + {`Description shouldn't repeat title. 1-3 lines. Can contain a `} + + hyperlink. + + + ); +}; + +export const ActionButton = (args) => { + return ; +}; + +ActionButton.args = { + title: 'Action prop demo', + actionButtonLabel: 'Action', + actionButtonOnClick: () => console.log('ButtonLink actionButtonOnClick demo'), + actionButtonProps: { + icon: ICON_NAMES.ARROW_2_RIGHT, // TODO: change to iconName + iconPositionRight: true, + }, + children: + 'Use actionButtonLabel for action text, actionButtonOnClick for the onClick handler, and actionButtonProps to pass any ButtonLink prop types such as iconName', +}; + +export const OnClose = (args) => { + const [isShown, setShown] = useState(true); + const bannerToggle = () => { + if (isShown) { + console.log('close button clicked'); + } + setShown(!isShown); + }; + return ( + <> + {isShown ? ( + + ) : ( + View BannerBase + )} + + ); +}; + +OnClose.args = { + title: 'onClose demo', + children: 'Click the close button icon to hide this notifcation', +}; + +export const StartAccessory = (args) => { + return ; +}; + +StartAccessory.args = { + title: 'Start accessory demo', + children: + 'The info icon on the left is passed through the startAccessory prop', + startAccessory: , +}; diff --git a/ui/components/component-library/banner-base/banner-base.test.js b/ui/components/component-library/banner-base/banner-base.test.js new file mode 100644 index 000000000..5e19a10c5 --- /dev/null +++ b/ui/components/component-library/banner-base/banner-base.test.js @@ -0,0 +1,98 @@ +/* eslint-disable jest/require-top-level-describe */ +import { render } from '@testing-library/react'; +import React from 'react'; + +import { renderWithUserEvent } from '../../../../test/lib/render-helpers'; + +import { Icon, ICON_NAMES } from '..'; +import { BannerBase } from './banner-base'; + +describe('BannerBase', () => { + it('should render bannerbase element correctly', () => { + const { getByTestId, container } = render( + + should render bannerbase element correctly + , + ); + expect(getByTestId('banner-base')).toHaveClass('mm-banner-base'); + expect(container).toMatchSnapshot(); + }); + + it('should render with added classname', () => { + const { getByTestId } = render( + + should render bannerbase element correctly + , + ); + expect(getByTestId('banner-base')).toHaveClass('mm-banner-base--test'); + }); + + it('should render bannerbase title', () => { + const { getByText } = render(); + expect(getByText('Bannerbase title test')).toHaveClass( + 'mm-banner-base__title', + ); + }); + + it('should render bannerbase description', () => { + const { getByText } = render( + Bannerbase description test, + ); + expect(getByText('Bannerbase description test')).toBeDefined(); + }); + + it('should render bannerbase action button', () => { + const { getByTestId } = render( + + console.log('ButtonLink actionButtonOnClick demo') + } + > + Use actionButtonLabel for action text, actionButtonOnClick for the + onClick handler, and actionButtonProps to pass any ButtonLink prop types + such as iconName + , + ); + expect(getByTestId('action')).toHaveClass('mm-banner-base__action'); + }); + + it('should render bannerbase startAccessory', () => { + const { getByTestId } = render( + + } + />, + ); + + expect(getByTestId('start-accessory')).toBeDefined(); + }); + + it('should render and fire onClose event', async () => { + const onClose = jest.fn(); + const { user, getByTestId } = renderWithUserEvent( + , + ); + await user.click(getByTestId('close-button')); + expect(onClose).toHaveBeenCalledTimes(1); + }); +}); diff --git a/ui/components/component-library/banner-base/index.js b/ui/components/component-library/banner-base/index.js new file mode 100644 index 000000000..e286559ae --- /dev/null +++ b/ui/components/component-library/banner-base/index.js @@ -0,0 +1 @@ +export { BannerBase } from './banner-base'; diff --git a/ui/components/component-library/component-library-components.scss b/ui/components/component-library/component-library-components.scss index 75508aa69..96a05a086 100644 --- a/ui/components/component-library/component-library-components.scss +++ b/ui/components/component-library/component-library-components.scss @@ -26,3 +26,4 @@ @import 'text-field-base/text-field-base'; @import 'text-field-search/text-field-search'; @import 'form-text-field/form-text-field'; +@import 'banner-base/banner-base'; diff --git a/ui/components/component-library/index.js b/ui/components/component-library/index.js index 70b4810ad..637408d12 100644 --- a/ui/components/component-library/index.js +++ b/ui/components/component-library/index.js @@ -34,3 +34,6 @@ export { TEXT_FIELD_BASE_TYPES, } from './text-field-base'; export { TextFieldSearch } from './text-field-search'; + +// Molecules +export { BannerBase } from './banner-base';