Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

15087: Add Button Base #15998

Merged
merged 13 commits into from
Oct 4, 2022
8 changes: 8 additions & 0 deletions test/env.js
Original file line number Diff line number Diff line change
@@ -1 +1,9 @@
process.env.METAMASK_ENV = 'test';

/**
* Used for testing components that use the Icon component
* 'ui/components/component-library/icon/icon.js'
*/
process.env.ICON_NAMES = {
LOADING_FILLED: 'loading-filled',
};
garrettbear marked this conversation as resolved.
Show resolved Hide resolved
128 changes: 128 additions & 0 deletions ui/components/component-library/button-base/README.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import { Story, Canvas, ArgsTable } from '@storybook/addon-docs';
import { ButtonBase } from './button-base';

### This is a base component. It should not be used in your feature code directly but as a "base" for other UI components

# ButtonBase

The `ButtonBase` is the base component for buttons.

<Canvas>
<Story id="ui-components-component-library-button-base-button-base-stories-js--default-story" />
</Canvas>

## Props

The `ButtonBase` accepts all props below as well as all [Box](/docs/ui-components-ui-box-box-stories-js--default-story#props) component props

<ArgsTable of={ButtonBase} />

### Size

Use the `size` prop and the `SIZES` object from `./ui/helpers/constants/design-system.js`
to change the size of `ButtonBase`. Defaults to `SIZES.MD`

Optional: `BUTTON_SIZES` from `./button-base` object can be used instead of `SIZES`.
georgewrmarshall marked this conversation as resolved.
Show resolved Hide resolved

Possible sizes include:

- `SIZES.AUTO` inherits the font-size of the parent element.
- `SIZES.SM` 32px
- `SIZES.MD` 40px
- `SIZES.LG` 48px

<Canvas>
<Story id="ui-components-component-library-button-base-button-base-stories-js--size" />
</Canvas>

```jsx
import { SIZES } from '../../../helpers/constants/design-system';
import { ButtonBase } from '../ui/component-library';

<ButtonBase size={SIZES.AUTO} />
<ButtonBase size={SIZES.SM} />
<ButtonBase size={SIZES.MD} />
<ButtonBase size={SIZES.LG} />
```

### Block

Use boolean `block` prop to quickly enable a full width block button

<Canvas>
<Story id="ui-components-component-library-button-base-button-base-stories-js--block" />
</Canvas>

```jsx
import { DISPLAY } from '../../../helpers/constants/design-system';
import { ButtonBase } from '../ui/component-library';

<ButtonBase>Default Button</ButtonBase>
<ButtonBase block>Block Button</ButtonBase>
```

### As

Use the `as` box prop to change the element of `ButtonBase`. Defaults to `button`.

Button `as` options:

- `button`
- `a`

<Canvas>
<Story id="ui-components-component-library-button-base-button-base-stories-js--as" />
</Canvas>

```jsx
import { ButtonBase } from '../ui/component-library';


<ButtonBase as="button">Button Element</ButtonBase>
<ButtonBase as="a" href="#">
Anchor Element
</ButtonBase>
```

### Disabled

Use the boolean `disabled` prop to disable button

<Canvas>
<Story id="ui-components-component-library-button-base-button-base-stories-js--disabled" />
</Canvas>

```jsx
import { ButtonBase } from '../ui/component-library';

<ButtonBase disabled>Disabled Button</ButtonBase>;
```

### Loading

Use the boolean `loading` prop to set loading spinner

<Canvas>
<Story id="ui-components-component-library-button-base-button-base-stories-js--loading" />
</Canvas>

```jsx
import { ButtonBase } from '../ui/component-library';

<ButtonBase loading>Loading Button</ButtonBase>;
```

### Icon

Use the `icon` prop and the `ICON_NAMES` object from `./ui/components/component-library/icon` to select icon.

<Canvas>
<Story id="ui-components-component-library-button-base-button-base-stories-js--icon" />
</Canvas>

```jsx
import { ButtonBase } from '../ui/component-library';
import { ICON_NAMES } from '../icon';

<ButtonBase icon={ICON_NAMES.ADD_SQUARE_FILLED}>Button</ButtonBase>;
```
138 changes: 138 additions & 0 deletions ui/components/component-library/button-base/button-base.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import React from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';

import Box from '../../ui/box';
import { Icon, ICON_NAMES } from '../icon';
import { Text } from '../text';

import {
ALIGN_ITEMS,
DISPLAY,
JUSTIFY_CONTENT,
TEXT_COLORS,
TEXT,
SIZES,
FLEX_DIRECTION,
} from '../../../helpers/constants/design-system';
import { BUTTON_SIZES } from './button.constants';

export const ButtonBase = ({
as = 'button',
block,
children,
className,
size = BUTTON_SIZES.MD,
icon,
iconPositionRight,
loading,
disabled,
iconProps,
...props
}) => {
return (
<Box
as={as}
paddingLeft={size === BUTTON_SIZES.AUTO ? 0 : 4}
paddingRight={size === BUTTON_SIZES.AUTO ? 0 : 4}
className={classnames(
'mm-button',
`mm-button--size-${size}`,
{
'mm-button--loading': Boolean(loading),
'mm-button--disabled': Boolean(disabled),
'mm-button--block': Boolean(block),
},
className,
)}
disabled={disabled}
display={DISPLAY.INLINE_FLEX}
justifyContent={JUSTIFY_CONTENT.CENTER}
alignItems={ALIGN_ITEMS.CENTER}
{...props}
>
<Text
as="span"
className="mm-button__content"
alignItems={ALIGN_ITEMS.CENTER}
justifyContent={JUSTIFY_CONTENT.CENTER}
flexDirection={
iconPositionRight ? FLEX_DIRECTION.ROW_REVERSE : FLEX_DIRECTION.ROW
}
gap={2}
variant={size === BUTTON_SIZES.AUTO ? TEXT.INHERIT : TEXT.BODY_MD}
color={TEXT_COLORS.INHERIT}
>
{icon && (
<Icon
name={icon}
size={size === BUTTON_SIZES.AUTO ? SIZES.AUTO : SIZES.SM}
{...iconProps}
/>
)}
{children}
</Text>
{loading && (
<Icon
className="mm-button__icon-loading"
name={ICON_NAMES.LOADING_FILLED}
size={size === BUTTON_SIZES.AUTO ? SIZES.AUTO : SIZES.MD}
/>
)}
</Box>
);
};

ButtonBase.propTypes = {
/**
* The polymorphic `as` prop allows you to change the root HTML element of the Button component between `button` and `a` tag
*/
as: PropTypes.string,
/**
* Boolean prop to quickly activate box prop display block
*/
block: PropTypes.bool,
/**
* The children to be rendered inside the ButtonBase
*/
children: PropTypes.node,
/**
* An additional className to apply to the ButtonBase.
*/
className: PropTypes.string,
/**
* Boolean to disable button
*/
disabled: PropTypes.bool,
/**
* Add icon to left side of button text passing icon name
* The name of the icon to display. Should be one of ICON_NAMES
*/
icon: PropTypes.string, // Can't set PropTypes.oneOf(ICON_NAMES) because ICON_NAMES is an environment variable
/**
* Boolean that when true will position the icon on right of children
* Icon default position left
*/
iconPositionRight: PropTypes.bool,
/**
* iconProps accepts all the props from Icon
*/
iconProps: Icon.propTypes,
/**
* Boolean to show loading spinner in button
*/
loading: PropTypes.bool,
/**
* The size of the ButtonBase.
* Possible values could be 'SIZES.AUTO', 'SIZES.SM', 'SIZES.MD', 'SIZES.LG',
*/
size: PropTypes.oneOf(Object.values(BUTTON_SIZES)),
/**
* Addition style properties to apply to the button.
*/
style: PropTypes.object,
/**
* ButtonBase accepts all the props from Box
*/
...Box.propTypes,
};
79 changes: 79 additions & 0 deletions ui/components/component-library/button-base/button-base.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
.mm-button {
position: relative;
height: 40px;
padding: 0;
border-radius: 999px;
cursor: pointer;
color: var(--color-text-default);
background-color: var(--brand-colors-grey-grey100);
vertical-align: middle;
user-select: none;

&:active,
&:hover {
color: var(--color-text-default);
}

&--block {
display: block;
width: 100%;
}

&__content {
height: 100%;
}


&--size-sm {
height: 32px;
}

&--size-md {
height: 40px;
}

&--size-lg {
height: 48px;
}

&--size-auto {
height: auto;
background-color: transparent;
border-radius: 0;
vertical-align: top;
font-family: inherit;
font-weight: inherit;
font-size: inherit;
line-height: inherit;
letter-spacing: inherit;
}

&--loading {
cursor: not-allowed;
}

&--loading &__content {
color: transparent;
}

&--disabled,
&:disabled {
opacity: 0.3;
cursor: not-allowed;
}

&__icon-loading {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
animation: spinner 1.2s linear infinite;
}
}



@keyframes spinner {
to { transform: translate(-50%, -50%) rotate(360deg); }
}

Loading