From 958cfe65a0ecf23e0fb74dd10f503902f7870c9e Mon Sep 17 00:00:00 2001 From: Garrett Bear Date: Fri, 7 Oct 2022 08:50:28 -0700 Subject: [PATCH] Feat/15951/add button secondary (#16097) * add button secondary * revert change to shadow stories * test and docs update * Update ui/components/component-library/button-secondary/README.mdx Co-authored-by: George Marshall * remove unused fragment Co-authored-by: George Marshall --- .../button-primary/button-primary.stories.js | 6 +- .../button-secondary/README.mdx | 57 ++++++++ .../button-secondary.constants.js | 7 + .../button-secondary/button-secondary.js | 45 ++++++ .../button-secondary/button-secondary.scss | 51 +++++++ .../button-secondary.stories.js | 133 ++++++++++++++++++ .../button-secondary/button-secondary.test.js | 101 +++++++++++++ .../button-secondary/index.js | 1 + .../component-library-components.scss | 1 + 9 files changed, 397 insertions(+), 5 deletions(-) create mode 100644 ui/components/component-library/button-secondary/README.mdx create mode 100644 ui/components/component-library/button-secondary/button-secondary.constants.js create mode 100644 ui/components/component-library/button-secondary/button-secondary.js create mode 100644 ui/components/component-library/button-secondary/button-secondary.scss create mode 100644 ui/components/component-library/button-secondary/button-secondary.stories.js create mode 100644 ui/components/component-library/button-secondary/button-secondary.test.js create mode 100644 ui/components/component-library/button-secondary/index.js diff --git a/ui/components/component-library/button-primary/button-primary.stories.js b/ui/components/component-library/button-primary/button-primary.stories.js index fff0966b0..8f9c3cafc 100644 --- a/ui/components/component-library/button-primary/button-primary.stories.js +++ b/ui/components/component-library/button-primary/button-primary.stories.js @@ -104,11 +104,7 @@ export default { }, }; -export const DefaultStory = (args) => ( - <> - - -); +export const DefaultStory = (args) => ; DefaultStory.storyName = 'Default'; diff --git a/ui/components/component-library/button-secondary/README.mdx b/ui/components/component-library/button-secondary/README.mdx new file mode 100644 index 000000000..16a5a74a6 --- /dev/null +++ b/ui/components/component-library/button-secondary/README.mdx @@ -0,0 +1,57 @@ +import { Story, Canvas, ArgsTable } from '@storybook/addon-docs'; + +import { ButtonSecondary } from './button-secondary'; + +# ButtonSecondary + +The `ButtonSecondary` is an extension of `ButtonBase` to support secondary styles. + + + + + +## Props + +The `ButtonSecondary` accepts all props below as well as all [Box](/docs/ui-components-ui-box-box-stories-js--default-story#props) and [ButtonBase](/docs/ui-components-component-library-button-base-button-base-stories-js--default-story#props) component props + + + +### Size + +Use the `size` prop and the `SIZES` object from `./ui/helpers/constants/design-system.js` to change the size of `ButtonSecondary`. Defaults to `SIZES.MD` + +Optional: `BUTTON_SIZES` from `./button-base` object can be used instead of `SIZES`. + +Possible sizes include: + +- `SIZES.SM` 32px +- `SIZES.MD` 40px +- `SIZES.LG` 48px + + + + + +```jsx +import { SIZES } from '../../../helpers/constants/design-system'; +import { ButtonSecondary } from '../ui/component-library/button/button-secondary/button-secondary'; + + + + +``` + +### Type + +Use the `type` prop and the `BUTTON_TYPES` object from `./ui/helpers/constants/design-system.js` to change the context of `ButtonSecondary`. + + + + + +```jsx +import { ButtonSecondary } from '../ui/component-library/button/button-secondary/button-secondary'; + +Normal +Danger +``` diff --git a/ui/components/component-library/button-secondary/button-secondary.constants.js b/ui/components/component-library/button-secondary/button-secondary.constants.js new file mode 100644 index 000000000..0e8af938e --- /dev/null +++ b/ui/components/component-library/button-secondary/button-secondary.constants.js @@ -0,0 +1,7 @@ +import { SIZES } from '../../../helpers/constants/design-system'; + +export const BUTTON_SECONDARY_SIZES = { + SM: SIZES.SM, + MD: SIZES.MD, + LG: SIZES.LG, +}; diff --git a/ui/components/component-library/button-secondary/button-secondary.js b/ui/components/component-library/button-secondary/button-secondary.js new file mode 100644 index 000000000..c8d4ed608 --- /dev/null +++ b/ui/components/component-library/button-secondary/button-secondary.js @@ -0,0 +1,45 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import classnames from 'classnames'; + +import { ButtonBase } from '../button-base'; +import { BUTTON_SECONDARY_SIZES } from './button-secondary.constants'; + +export const ButtonSecondary = ({ + className, + danger, + size = BUTTON_SECONDARY_SIZES.MD, + ...props +}) => { + return ( + + ); +}; + +ButtonSecondary.propTypes = { + /** + * An additional className to apply to the ButtonSecondary. + */ + className: PropTypes.string, + /** + * Boolean to change button type to Danger when true + */ + danger: PropTypes.bool, + /** + * The possible size values for ButtonSecondary: 'SIZES.SM', 'SIZES.MD', 'SIZES.LG', + * Default value is 'SIZES.MD'. + */ + size: PropTypes.oneOf(Object.values(BUTTON_SECONDARY_SIZES)), + /** + * ButtonSecondary accepts all the props from ButtonBase + */ + ...ButtonBase.propTypes, +}; + +export default ButtonSecondary; diff --git a/ui/components/component-library/button-secondary/button-secondary.scss b/ui/components/component-library/button-secondary/button-secondary.scss new file mode 100644 index 000000000..7724e5701 --- /dev/null +++ b/ui/components/component-library/button-secondary/button-secondary.scss @@ -0,0 +1,51 @@ +.mm-button-secondary { + color: var(--color-primary-default); + border: 1px solid var(--color-primary-default); + background-color: transparent; + + &:hover { + color: var(--color-primary-inverse); + background-color: var(--color-primary-default); + box-shadow: var(--component-button-primary-shadow); + } + + &:active { + color: var(--color-primary-inverse); + background-color: var(--color-primary-alternative); + border-color: var(--color-primary-alternative); + } + + &.mm-button--disabled { + &:hover { + color: var(--color-primary-default); + box-shadow: none; + background-color: transparent; + } + + &:active { + background-color: transparent; + } + } + + &--type-danger { + color: var(--color-error-default); + border: 1px solid var(--color-error-default); + background-color: transparent; + + &:hover { + color: var(--color-error-inverse); + background-color: var(--color-error-default); + box-shadow: var(--component-button-danger-shadow); + } + + &:active { + color: var(--color-error-inverse); + background-color: var(--color-error-alternative); + border-color: var(--color-error-alternative); + } + + &.mm-button--disabled:hover { + color: var(--color-error-default); + } + } +} diff --git a/ui/components/component-library/button-secondary/button-secondary.stories.js b/ui/components/component-library/button-secondary/button-secondary.stories.js new file mode 100644 index 000000000..399cb412e --- /dev/null +++ b/ui/components/component-library/button-secondary/button-secondary.stories.js @@ -0,0 +1,133 @@ +import React from 'react'; +import { ALIGN_ITEMS, DISPLAY } from '../../../helpers/constants/design-system'; +import Box from '../../ui/box/box'; +import { ICON_NAMES } from '../icon'; +import { ButtonSecondary } from './button-secondary'; +import { BUTTON_SECONDARY_SIZES } from './button-secondary.constants'; +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/ButtonSecondary', + id: __filename, + component: ButtonSecondary, + parameters: { + docs: { + page: README, + }, + }, + argTypes: { + as: { + control: 'select', + options: ['button', 'a'], + table: { category: 'button base props' }, + }, + block: { + control: 'boolean', + table: { category: 'button base props' }, + }, + children: { + control: 'text', + }, + className: { + control: 'text', + }, + danger: { + control: 'boolean', + }, + disabled: { + control: 'boolean', + table: { category: 'button base props' }, + }, + icon: { + control: 'select', + options: Object.values(ICON_NAMES), + table: { category: 'button base props' }, + }, + iconPositionRight: { + control: 'boolean', + table: { category: 'button base props' }, + }, + iconProps: { + control: 'object', + table: { category: 'button base props' }, + }, + + loading: { + control: 'boolean', + table: { category: 'button base props' }, + }, + size: { + control: 'select', + options: Object.values(BUTTON_SECONDARY_SIZES), + }, + 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' }, + }, + }, + args: { + children: 'Button Secondary', + }, +}; + +export const DefaultStory = (args) => ; + +DefaultStory.storyName = 'Default'; + +export const Size = (args) => ( + + + Small Button + + + Medium (Default) Button + + + Large Button + + +); + +export const Type = (args) => ( + + Normal + {/* Test Anchor tag to match exactly as button */} + + Danger + + +); diff --git a/ui/components/component-library/button-secondary/button-secondary.test.js b/ui/components/component-library/button-secondary/button-secondary.test.js new file mode 100644 index 000000000..61f910ab9 --- /dev/null +++ b/ui/components/component-library/button-secondary/button-secondary.test.js @@ -0,0 +1,101 @@ +/* eslint-disable jest/require-top-level-describe */ +import { render } from '@testing-library/react'; +import React from 'react'; +import { ButtonSecondary } from './button-secondary'; +import { BUTTON_SECONDARY_SIZES } from './button-secondary.constants'; + +describe('ButtonSecondary', () => { + it('should render button element correctly', () => { + const { getByText, getByTestId, container } = render( + + Button Secondary + , + ); + expect(getByText('Button Secondary')).toBeDefined(); + expect(container.querySelector('button')).toBeDefined(); + expect(getByTestId('button-secondary')).toHaveClass('mm-button'); + }); + + it('should render anchor element correctly', () => { + const { getByTestId, container } = render( + , + ); + expect(getByTestId('button-secondary')).toHaveClass('mm-button'); + const anchor = container.getElementsByTagName('a').length; + expect(anchor).toBe(1); + }); + + it('should render button as block', () => { + const { getByTestId } = render( + , + ); + expect(getByTestId('block')).toHaveClass(`mm-button--block`); + }); + + it('should render with added classname', () => { + const { getByTestId } = render( + , + ); + expect(getByTestId('classname')).toHaveClass('mm-button--test'); + }); + + it('should render with different size classes', () => { + const { getByTestId } = render( + <> + + + + , + ); + + expect(getByTestId(BUTTON_SECONDARY_SIZES.SM)).toHaveClass( + `mm-button--size-${BUTTON_SECONDARY_SIZES.SM}`, + ); + expect(getByTestId(BUTTON_SECONDARY_SIZES.MD)).toHaveClass( + `mm-button--size-${BUTTON_SECONDARY_SIZES.MD}`, + ); + expect(getByTestId(BUTTON_SECONDARY_SIZES.LG)).toHaveClass( + `mm-button--size-${BUTTON_SECONDARY_SIZES.LG}`, + ); + }); + + it('should render with different types', () => { + const { getByTestId } = render( + <> + + , + ); + + expect(getByTestId('danger')).toHaveClass( + 'mm-button-secondary--type-danger', + ); + }); + + it('should render with different button states', () => { + const { getByTestId } = render( + <> + + + , + ); + expect(getByTestId('loading')).toHaveClass(`mm-button--loading`); + expect(getByTestId('disabled')).toHaveClass(`mm-button--disabled`); + }); + it('should render with icon', () => { + const { container } = render( + , + ); + + const icons = container.getElementsByClassName('icon').length; + expect(icons).toBe(1); + }); +}); diff --git a/ui/components/component-library/button-secondary/index.js b/ui/components/component-library/button-secondary/index.js new file mode 100644 index 000000000..6b1d2869f --- /dev/null +++ b/ui/components/component-library/button-secondary/index.js @@ -0,0 +1 @@ +export { ButtonSecondary } from './button-secondary'; diff --git a/ui/components/component-library/component-library-components.scss b/ui/components/component-library/component-library-components.scss index 85c2f84c6..2a9363272 100644 --- a/ui/components/component-library/component-library-components.scss +++ b/ui/components/component-library/component-library-components.scss @@ -4,6 +4,7 @@ @import 'base-avatar/base-avatar'; @import 'button-base/button-base'; @import 'button-primary/button-primary'; +@import 'button-secondary/button-secondary'; @import 'icon/icon'; @import 'text/text'; @import 'text-field-base/text-field-base';