diff --git a/app/images/fox-chat.png b/app/images/fox-chat.png new file mode 100644 index 000000000..985ad7010 Binary files /dev/null and b/app/images/fox-chat.png differ diff --git a/app/images/fox-greeting.png b/app/images/fox-greeting.png new file mode 100644 index 000000000..d9260e698 Binary files /dev/null and b/app/images/fox-greeting.png differ diff --git a/ui/components/component-library/banner-tip/README.mdx b/ui/components/component-library/banner-tip/README.mdx new file mode 100644 index 000000000..e92bc9519 --- /dev/null +++ b/ui/components/component-library/banner-tip/README.mdx @@ -0,0 +1,165 @@ +import { Story, Canvas, ArgsTable } from '@storybook/addon-docs'; +import { BannerTip } from './banner-tip'; +import { BannerBase } from '..'; + +# BannerTip + +`BannerTip` is an inline notification that offers users educational tips, knowledge, and helpful links + + + + + +## Props + +The `BannerTip` accepts all props below as well as all [Box](/docs/components-ui-box--default-story#props) component props + + + +The `BannerTip` accepts all `BannerBase` component props below + + + +### Logo Type + +Use the `logoType` prop with the `BannerTipLogoType` enum from `../../component-library` to change the context of `BannerTip`. + +Possible options: + +- `BannerTipLogoType.Greeting` Default +- `BannerTipLogoType.Chat` + + + + + +```jsx +import { BannerTip } from '../../component-library'; + + + This is a demo of greeting. + + + This is a demo of chat. + +``` + +### 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 { BannerTip } from '../../component-library'; + + + Pass only a string through the title prop +; +``` + +### Description + +The `description` is the content area of the `BannerTip` that must be a string. Description shouldn't repeat title and only 1-3 lines. + +If content requires more than a string, see `children` prop below. + + + + + +```jsx +import { BannerTip } from '../../component-library'; +; +``` + +### Children + +The `children` prop is an alternative to `description` for `BannerTip` when more than a string is needed. Children content shouldn't repeat title and only 1-3 lines. + + + + + +```jsx +import { Size } from '../../../helpers/constants/design-system'; +import { BannerTip } 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/components-componentlibrary-buttonlink--default-story) for the action + + + + + +```jsx +import { BannerTip, 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 { BannerTip } from '../../component-library'; + + console.log('close button clicked')} +> + Click the close button icon to hide this notifcation +; +``` + +### Start Accessory + +Use the `startAccessory` prop to pass a ReactNode to the start of the `BannerTip`. This is useful for overriding the defaults defined by `BannerTip`. + + + + + +```jsx +import { BannerTip } from '../../component-library'; + +} + title="StartAccessory" +> + This is a demo of startAccessory override. +; +``` diff --git a/ui/components/component-library/banner-tip/__snapshots__/banner-tip.test.js.snap b/ui/components/component-library/banner-tip/__snapshots__/banner-tip.test.js.snap new file mode 100644 index 000000000..c44c22219 --- /dev/null +++ b/ui/components/component-library/banner-tip/__snapshots__/banner-tip.test.js.snap @@ -0,0 +1,32 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`BannerTip should render BannerTip element correctly 1`] = ` +
+
+
+ +
+
+
+ BannerTip test +
+

+ should render BannerTip element correctly +

+
+
+
+`; diff --git a/ui/components/component-library/banner-tip/banner-tip.constants.ts b/ui/components/component-library/banner-tip/banner-tip.constants.ts new file mode 100644 index 000000000..088c6fcdf --- /dev/null +++ b/ui/components/component-library/banner-tip/banner-tip.constants.ts @@ -0,0 +1,4 @@ +export enum BannerTipLogoType { + Greeting = 'greeting', + Chat = 'chat', +} diff --git a/ui/components/component-library/banner-tip/banner-tip.js b/ui/components/component-library/banner-tip/banner-tip.js new file mode 100644 index 000000000..8864a6e21 --- /dev/null +++ b/ui/components/component-library/banner-tip/banner-tip.js @@ -0,0 +1,76 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import classnames from 'classnames'; +import { + AlignItems, + BorderColor, + DISPLAY, +} from '../../../helpers/constants/design-system'; +import Box from '../../ui/box'; +import { BannerBase } from '..'; +import { BannerTipLogoType } from './banner-tip.constants'; + +export const BannerTip = ({ + children, + className, + logoType = BannerTipLogoType.Greeting, + logoWrapperProps, + logoProps, + startAccessory, + ...props +}) => { + return ( + + + + ) + } + borderColor={BorderColor.borderDefault} + className={classnames('mm-banner-tip', className)} + {...props} + > + {children} + + ); +}; + +BannerTip.propTypes = { + /** + * An additional className to apply to the Banner + */ + className: PropTypes.string, + /** + * Use the `logoType` prop with the `BannerTipLogoType` enum from `../../component-library` to change the logo image of `BannerTip`. + * Possible options: `BannerTipLogoType.Greeting`(Default), `BannerTipLogoType.Chat`, + */ + logoType: PropTypes.oneOf(Object.values(BannerTipLogoType)), + /** + * logoProps accepts all the props from Box + */ + logoProps: PropTypes.shape(Box.propTypes), + /** + * logoWrapperProps accepts all the props from Box + */ + logoWrapperProps: PropTypes.shape(Box.propTypes), + /** + * The start(defualt left) content area of BannerBase + */ + startAccessory: PropTypes.node, + /** + * BannerTip accepts all the props from BannerBase + */ + ...BannerBase.propTypes, +}; diff --git a/ui/components/component-library/banner-tip/banner-tip.scss b/ui/components/component-library/banner-tip/banner-tip.scss new file mode 100644 index 000000000..355e8515a --- /dev/null +++ b/ui/components/component-library/banner-tip/banner-tip.scss @@ -0,0 +1,5 @@ +.mm-banner-tip { + &--logo { + width: 60px; + } +} diff --git a/ui/components/component-library/banner-tip/banner-tip.stories.js b/ui/components/component-library/banner-tip/banner-tip.stories.js new file mode 100644 index 000000000..fe6ec1ffb --- /dev/null +++ b/ui/components/component-library/banner-tip/banner-tip.stories.js @@ -0,0 +1,185 @@ +import React, { useState } from 'react'; +import { + DISPLAY, + FLEX_DIRECTION, + Size, +} from '../../../helpers/constants/design-system'; +import Box from '../../ui/box/box'; +import { ButtonLink, ButtonPrimary, Icon, ICON_NAMES } from '..'; +import README from './README.mdx'; +import { BannerTip, BannerTipLogoType } from '.'; + +const marginSizeControlOptions = [ + undefined, + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 'auto', +]; + +export default { + title: 'Components/ComponentLibrary/BannerTip', + component: BannerTip, + parameters: { + docs: { + page: README, + }, + backgrounds: { default: 'alternative' }, + }, + argTypes: { + logoType: { + options: Object.values(BannerTipLogoType), + control: 'select', + }, + className: { + control: 'text', + }, + 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('BannerTip onClose trigger'); + return ; +}; + +DefaultStory.args = { + title: 'Title is sentence case no period', + children: "Description shouldn't repeat title. 1-3 lines.", + actionButtonLabel: 'Action', +}; + +DefaultStory.storyName = 'Default'; + +export const LogoType = (args) => { + return ( + + + This is a demo of greeting. + + + This is a demo of chat. + + + ); +}; + +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 Description = (args) => { + return ; +}; + +Description.args = { + title: 'Description vs children', + description: + 'Pass only a string through the description prop or you can use children if the contents require more', +}; + +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: { + iconName: ICON_NAMES.ARROW_2_RIGHT, + 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 bannerTipToggle = () => { + if (isShown) { + console.log('close button clicked'); + } + setShown(!isShown); + }; + return ( + <> + {isShown ? ( + + ) : ( + View BannerTip + )} + + ); +}; + +OnClose.args = { + title: 'onClose demo', + children: 'Click the close button icon to hide this notifcation', +}; + +export const StartAccessory = (args) => { + return ( + } + title="StartAccessory" + onClose={() => console.log('close button clicked')} + > + This is a demo of startAccessory override. + + ); +}; diff --git a/ui/components/component-library/banner-tip/banner-tip.test.js b/ui/components/component-library/banner-tip/banner-tip.test.js new file mode 100644 index 000000000..772acf722 --- /dev/null +++ b/ui/components/component-library/banner-tip/banner-tip.test.js @@ -0,0 +1,105 @@ +/* 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 { BannerTip, BannerTipLogoType } from '.'; + +describe('BannerTip', () => { + it('should render BannerTip element correctly', () => { + const { getByTestId, container } = render( + + should render BannerTip element correctly + , + ); + expect(getByTestId('bannerTip')).toHaveClass('mm-banner-tip'); + expect(container).toMatchSnapshot(); + }); + + it('should render with different logo types', () => { + const { getByTestId } = render( + <> + + should render BannerTip element correctly + + + should render BannerTip element correctly + + , + ); + const imageElement = getByTestId('banner-tip-greeting'); + expect(imageElement.tagName).toBe('IMG'); + expect(getByTestId('banner-tip-greeting')).toHaveClass( + 'mm-banner-tip--logo', + ); + expect(getByTestId('banner-tip-chat')).toHaveClass('mm-banner-tip--logo'); + }); + + it('should render with added classname', () => { + const { getByTestId } = render( + + should render BannerTip element correctly + , + ); + expect(getByTestId('bannerTip')).toHaveClass('mm-banner-tip--test'); + }); + + it('should render BannerTip title', () => { + const { getByText } = render(); + expect(getByText('BannerTip title test')).toHaveClass( + 'mm-banner-base__title', + ); + }); + + it('should render BannerTip description', () => { + const { getByText } = render( + , + ); + expect(getByText('BannerTip description test')).toBeDefined(); + }); + + it('should render BannerTip 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 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-tip/index.js b/ui/components/component-library/banner-tip/index.js new file mode 100644 index 000000000..d49ed487c --- /dev/null +++ b/ui/components/component-library/banner-tip/index.js @@ -0,0 +1,2 @@ +export { BannerTip } from './banner-tip'; +export { BannerTipLogoType } from './banner-tip.constants'; diff --git a/ui/components/component-library/component-library-components.scss b/ui/components/component-library/component-library-components.scss index 700eacd37..6a10d8f99 100644 --- a/ui/components/component-library/component-library-components.scss +++ b/ui/components/component-library/component-library-components.scss @@ -28,3 +28,4 @@ @import 'text-field-search/text-field-search'; @import 'form-text-field/form-text-field'; @import 'banner-alert/banner-alert'; +@import 'banner-tip/banner-tip'; diff --git a/ui/components/component-library/index.js b/ui/components/component-library/index.js index d39a8de59..b26ea86ca 100644 --- a/ui/components/component-library/index.js +++ b/ui/components/component-library/index.js @@ -35,3 +35,4 @@ export { TextFieldSearch } from './text-field-search'; // Molecules export { BannerBase } from './banner-base'; export { BannerAlert, BANNER_ALERT_SEVERITIES } from './banner-alert'; +export { BannerTip, BannerTipLogoType } from './banner-tip';