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

Feature/add button full width prop #129

Merged
merged 15 commits into from
Jul 8, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions .storybook/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,16 @@ function removeDefaultStorybookSvgRule(config) {
);
}

module.exports = ({ config }) => {
module.exports = webpackSettings => {
// ESLint fix for `Resolve error: Cannot destructure property" `config` of 'undefined' or 'null'` in /stories/**/index.js files.
if (!webpackSettings) return {};
const { config } = webpackSettings;

removeDefaultStorybookSvgRule(config);

config.resolve = {
modules: [path.resolve(__dirname, '..'), 'node_modules'],
extensions: ['.js', '.jsx'],
extensions: ['.js', '.jsx', '.md'],
};

const customRules = [
Expand Down
31 changes: 21 additions & 10 deletions docs/button/button.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { CheckmarkIcon } from 'radiance-ui/lib/icons';
<Button buttonType="tertiary">Tertiary Button</Button>
<Button buttonType="quaternary">Quaternary Button</Button>
<Button disabled>Disabled Button</Button>
<Button isFullWidth>Full Width Button</Button>

<Button isLoading>Primary Loading</Button>
<Button isLoading buttonType="secondary">
Expand All @@ -24,6 +25,9 @@ import { CheckmarkIcon } from 'radiance-ui/lib/icons';
<Button buttonType="quaternary" isLoading>
Quaternary Loading
</Button>
<Button isFullWidth isLoading>
Full Width Loading
</Button>
</Button.Container>

<Button.Container>
Expand All @@ -37,10 +41,13 @@ import { CheckmarkIcon } from 'radiance-ui/lib/icons';
<Button buttonType="quaternary" icon={<CheckmarkIcon />}>
Quaternary Button
</Button>

<Button disabled icon={<CheckmarkIcon />}>
Disabled Button
</Button>
<Button isFullWidth icon={<CheckmarkIcon />}>
Full Width Button
</Button>

<Button isLoading icon={<CheckmarkIcon />}>
Primary Loading
</Button>
Expand All @@ -53,6 +60,9 @@ import { CheckmarkIcon } from 'radiance-ui/lib/icons';
<Button isLoading buttonType="quaternary" icon={<CheckmarkIcon />}>
Quaternary Loading
</Button>
<Button isLoading isFullWidth icon={<CheckmarkIcon />}>
Full Width Loading
</Button>
</Button.Container>
</React.Fragment>;
```
Expand All @@ -61,15 +71,16 @@ import { CheckmarkIcon } from 'radiance-ui/lib/icons';

### Proptypes

| prop | propType | required | default | description |
| ---------- | -------- | -------- | -------- | ---------------------------------------------------------------------------------------------------------------------------- |
| buttonType | string | no | primary | Determines the button's main style theme. Must be one of `primary`, `secondary`, `tertiary`, `quaternary`. |
| children | node | yes | - | node to be rendered inside the button. Recommended to be the button text |
| disabled | bool | no | false | when disabled, click listener will not be called and the UI will look disabled |
| icon | node | no | null | icon to render in the button. Recommended to use one of Radiance's icons |
| isLoading | bool | no | false | renders loading state and prevents click listener from being called |
| onClick | func | no | () => {} | callback function called on click of the button |
| textColor | string | no | '' | color (as a string) that will override existing text, icon, and loading colors for the button (except when disabled is true) |
| prop | propType | required | default | description |
| ----------- | -------- | -------- | -------- | ---------------------------------------------------------------------------------------------------------------------------- |
| buttonType | string | no | primary | Determines the button's main style theme. Must be one of `primary`, `secondary`, `tertiary`, `quaternary`. |
| children | node | yes | - | node to be rendered inside the button. Recommended to be the button text |
| disabled | bool | no | false | when disabled, click listener will not be called and the UI will look disabled |
| icon | node | no | null | icon to render in the button. Recommended to use one of Radiance's icons |
| isLoading | bool | no | false | renders loading state and prevents click listener from being called |
| onClick | func | no | () => {} | callback function called on click of the button |
| textColor | string | no | '' | color (as a string) that will override existing text, icon, and loading colors for the button (except when disabled is true) |
| isFullWidth | bool | no | false | adjusts the default button styles so that the button takes the full width of the container. Intended for mobile-use only. |

### Notes

Expand Down
10 changes: 5 additions & 5 deletions src/shared-components/button/__snapshots__/test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -110,24 +110,24 @@ exports[`<Button /> UI snapshots renders with props 1`] = `
cursor: pointer;
display: block;
margin: 0;
max-width: 325px;
min-height: 52px;
min-width: 208px;
opacity: 1;
padding: 0 1.5rem;
position: relative;
-webkit-transition: all 350ms ease-in-out;
transition: all 350ms ease-in-out;
-webkit-text-decoration: none;
text-decoration: none;
width: -webkit-max-content;
width: -moz-max-content;
width: max-content;
background-color: #ededf0;
border-color: #ededf0;
color: #c3c0cd;
cursor: not-allowed;
fill: #c3c0cd;
min-width: 208px;
max-width: 325px;
width: -webkit-max-content;
width: -moz-max-content;
width: max-content;
}

.emotion-6:hover {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,23 @@ exports[`<LinkButton/> UI snapshots renders with props 1`] = `
cursor: pointer;
display: block;
margin: 0;
max-width: 325px;
min-height: 52px;
min-width: 208px;
opacity: 1;
padding: 0 1.5rem;
position: relative;
-webkit-transition: all 350ms ease-in-out;
transition: all 350ms ease-in-out;
-webkit-text-decoration: none;
text-decoration: none;
width: -webkit-max-content;
width: -moz-max-content;
width: max-content;
background-color: #332e54;
border-color: #332e54;
color: #ffffff;
fill: #ffffff;
min-width: 208px;
max-width: 325px;
width: -webkit-max-content;
width: -moz-max-content;
width: max-content;
}

.emotion-4:hover {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,24 +128,24 @@ exports[`<RoundButton /> UI snapshots renders with props 1`] = `
cursor: pointer;
display: block;
margin: 0;
max-width: 325px;
min-height: 52px;
min-width: 208px;
opacity: 1;
padding: 0 1.5rem;
position: relative;
-webkit-transition: all 350ms ease-in-out;
transition: all 350ms ease-in-out;
-webkit-text-decoration: none;
text-decoration: none;
width: -webkit-max-content;
width: -moz-max-content;
width: max-content;
background-color: #ededf0;
border-color: #ededf0;
color: #c3c0cd;
cursor: not-allowed;
fill: #c3c0cd;
min-width: 208px;
max-width: 325px;
width: -webkit-max-content;
width: -moz-max-content;
width: max-content;
border-radius: 50%;
max-width: unset;
min-height: unset;
Expand Down
13 changes: 12 additions & 1 deletion src/shared-components/button/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ const isLoadingPropFunction = (props, propName, componentName) => {
}
};

// TODO: Move <Loader /> to be sibling of <ButtonContents /> for more consistent
// loading animation spacing
class Button extends React.Component {
static Container = Container;

Expand All @@ -36,6 +38,7 @@ class Button extends React.Component {
isLoading: PropTypes.bool,
icon: PropTypes.node,
textColor: PropTypes.string,
isFullWidth: PropTypes.bool,
};

static defaultProps = {
Expand All @@ -44,6 +47,7 @@ class Button extends React.Component {
isLoading: false,
onClick() {},
textColor: '',
isFullWidth: false,
};

render() {
Expand All @@ -56,6 +60,7 @@ class Button extends React.Component {
isLoading,
icon,
textColor,
isFullWidth,
...rest
} = this.props;

Expand All @@ -71,9 +76,14 @@ class Button extends React.Component {
isLoading={loadingVal}
type="button"
textColor={textColor}
isFullWidth={isFullWidth}
{...rest}
>
<ButtonContents isLoading={loadingVal} hasIcon={!!icon}>
<ButtonContents
isLoading={loadingVal}
hasIcon={!!icon}
isFullWidth={isFullWidth}
>
{icon}
<ButtonText
isLoading={loadingVal}
Expand All @@ -90,6 +100,7 @@ class Button extends React.Component {
disabled={disabled}
buttonType={buttonType}
textColor={textColor}
isFullWidth={isFullWidth}
/>
</ButtonBase>
);
Expand Down
15 changes: 12 additions & 3 deletions src/shared-components/button/shared-components/loader/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,21 @@ import PropTypes from 'prop-types';

import ButtonLoader from './style';

const Loader = ({ isLoading, disabled, buttonType, className, textColor }) => (
const Loader = ({
buttonType,
className,
disabled,
isFullWidth,
isLoading,
textColor,
}) => (
<ButtonLoader
isLoading={isLoading}
disabled={disabled}
buttonType={buttonType}
className={className}
textColor={textColor}
isFullWidth={isFullWidth}
>
<div>
<span />
Expand All @@ -20,8 +28,6 @@ const Loader = ({ isLoading, disabled, buttonType, className, textColor }) => (
);

Loader.propTypes = {
isLoading: PropTypes.bool,
disabled: PropTypes.bool,
buttonType: PropTypes.oneOf([
'primary',
'secondary',
Expand All @@ -30,6 +36,9 @@ Loader.propTypes = {
'action',
]),
className: PropTypes.string,
disabled: PropTypes.bool,
isFullWidth: PropTypes.bool,
isLoading: PropTypes.bool,
textColor: PropTypes.string,
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ const ButtonLoader = styled.div`
right: 0;
top: 0;
margin-top: -6px;
width: 38px;
width: ${({ isFullWidth }) => (isFullWidth ? `25%` : `38px`)};
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

25% was fairly arbitrary but was relatively close for very wide buttons (which isn't a pattern we want to use, since we're adding this for mobile), but also close enough with narrower viewports (320-400px).

opacity: ${({ isLoading }) => (isLoading ? 1 : 0)};

& span {
Expand Down
26 changes: 22 additions & 4 deletions src/shared-components/button/style.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ export const baseButtonStyles = ({
buttonType,
isLoading,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

Is it possible to move the loading spinner into the same area as the text here? It currently pushes way out to the side.

textColor,
isFullWidth,
}) => css`
${TYPOGRAPHY_STYLE.button};
appearance: none;
Expand All @@ -127,15 +128,12 @@ export const baseButtonStyles = ({
cursor: pointer;
display: block;
margin: 0;
max-width: 325px;
min-height: 52px;
min-width: 208px;
opacity: 1;
padding: 0 ${SPACER.large};
position: relative;
transition: all ${ANIMATION.defaultTiming} ease-in-out;
text-decoration: none;
width: max-content;

&:hover {
transition: all ${ANIMATION.defaultTiming} ease-in-out;
Expand All @@ -155,12 +153,32 @@ export const baseButtonStyles = ({
color: ${textColor};
fill: ${textColor};
`};

${isFullWidth
? `
width: 100%;
`
: `
min-width: 208px;
max-width: 325px;
width: max-content;

`};
`;

export const ButtonBase = styled.button(baseButtonStyles);

// align-items conditional fixes slight button height misalignment for truthy scenario
// See screenshot in: https://github.com/PocketDerm/radiance-ui/pull/129#issue-292994081
export const ButtonContents = styled.div`
align-items: center;
align-items: ${({ hasIcon, isFullWidth, isLoading }) => {
if (isFullWidth && isLoading && hasIcon) {
return 'baseline';
}

return 'center';
}};

display: flex;
height: 100%;
justify-content: center;
Expand Down
13 changes: 11 additions & 2 deletions stories/button/button/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ import { text, select, boolean } from '@storybook/addon-knobs';
import { withDocs } from 'storybook-readme';
import { action } from '@storybook/addon-actions';
import { css } from '@emotion/core';

import ButtonReadme from 'docs/button/button.md';
import ButtonReadme from 'docs/button/button';
import { CheckmarkIcon } from 'src/svgs/icons';
import { Button, Typography } from 'src/shared-components';
import { SPACER } from 'src/constants';
Expand All @@ -30,6 +29,7 @@ const ButtonStory = withDocs(ButtonReadme, () => (
<Button buttonType="tertiary">Tertiary Button</Button>
<Button buttonType="quaternary">Quaternary Button</Button>
<Button disabled>Disabled Button</Button>
<Button isFullWidth>Full Width Button</Button>

<Button isLoading>Primary Loading</Button>
<Button isLoading buttonType="secondary">
Expand All @@ -41,6 +41,9 @@ const ButtonStory = withDocs(ButtonReadme, () => (
<Button buttonType="quaternary" isLoading>
Quaternary Loading
</Button>
<Button isFullWidth isLoading>
Full Width Loading
</Button>
</Button.Container>

<Button.Container
Expand Down Expand Up @@ -70,6 +73,9 @@ const ButtonStory = withDocs(ButtonReadme, () => (
<Button disabled icon={<CheckmarkIcon />}>
Disabled Button
</Button>
<Button isFullWidth icon={<CheckmarkIcon />}>
Full Width Button
</Button>

<Button isLoading icon={<CheckmarkIcon />}>
Primary Loading
Expand All @@ -83,6 +89,9 @@ const ButtonStory = withDocs(ButtonReadme, () => (
<Button isLoading buttonType="quaternary" icon={<CheckmarkIcon />}>
Quaternary Loading
</Button>
<Button isLoading isFullWidth icon={<CheckmarkIcon />}>
Full Width Loading
</Button>
</Button.Container>
<Typography.Heading
css={css`
Expand Down