Skip to content

Commit

Permalink
Merge pull request #41 from storybookjs/jsomsanith/feat/button_wrapper
Browse files Browse the repository at this point in the history
feat: ButtonWrapper prop in Button to customise rendering
  • Loading branch information
kylesuss authored Jun 29, 2019
2 parents 52cbbd3 + 75a6d3d commit bd82b09
Show file tree
Hide file tree
Showing 2 changed files with 261 additions and 11 deletions.
43 changes: 32 additions & 11 deletions src/components/Button.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const SIZES = {
MEDIUM: 'medium',
};

const ButtonWrapper = styled.button`
const StyledButton = styled.button`
border: 0;
border-radius: 3em;
cursor: pointer;
Expand Down Expand Up @@ -305,27 +305,46 @@ const ButtonWrapper = styled.button`
`;

const ButtonLink = ButtonWrapper.withComponent('a');
const ButtonLink = StyledButton.withComponent('a');

export function Button({ isDisabled, isLoading, loadingText, isLink, children, ...props }) {
const applyStyle = ButtonWrapper => {
return (
ButtonWrapper &&
StyledButton.withComponent(({ containsIcon, isLoading, isUnclickable, ...rest }) => (
<ButtonWrapper {...rest} />
))
);
};

export function Button({
isDisabled,
isLoading,
loadingText,
isLink,
children,
ButtonWrapper,
...props
}) {
const buttonInner = (
<Fragment>
<Text>{children}</Text>
{isLoading && <Loading>{loadingText || 'Loading...'}</Loading>}
</Fragment>
);

if (isLink) {
return (
<ButtonLink isLoading={isLoading} disabled={isDisabled} {...props}>
{buttonInner}
</ButtonLink>
);
const StyledButtonWrapper = React.useMemo(() => applyStyle(ButtonWrapper), [ButtonWrapper]);

let SelectedButton = StyledButton;
if (ButtonWrapper) {
SelectedButton = StyledButtonWrapper;
} else if (isLink) {
SelectedButton = ButtonLink;
}

return (
<ButtonWrapper isLoading={isLoading} disabled={isDisabled} {...props}>
<SelectedButton isLoading={isLoading} disabled={isDisabled} {...props}>
{buttonInner}
</ButtonWrapper>
</SelectedButton>
);
}

Expand All @@ -351,6 +370,7 @@ Button.propTypes = {
*/
containsIcon: PropTypes.bool,
size: PropTypes.oneOf(Object.values(SIZES)),
ButtonWrapper: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
};

Button.defaultProps = {
Expand All @@ -362,4 +382,5 @@ Button.defaultProps = {
isUnclickable: false,
containsIcon: false,
size: SIZES.MEDIUM,
ButtonWrapper: undefined,
};
229 changes: 229 additions & 0 deletions src/components/Button.stories.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,23 @@
import React from 'react';
import { storiesOf } from '@storybook/react';
import styled from 'styled-components';
import { action } from '@storybook/addon-actions';

import { Button } from './Button';
import { Icon } from './Icon';
import { StoryLinkWrapper } from './StoryLinkWrapper';

const CustomButton = styled.button`
border: 1px solid green;
background: lightgreen;
color: rebeccapurple;
padding: 1em;
font-size: 1.2em;
`;

function ButtonWrapper(props) {
return <CustomButton onClick={action('button action click')} {...props} />;
}

storiesOf('Design System|Button', module)
.addParameters({ component: Button })
Expand Down Expand Up @@ -56,4 +72,217 @@ storiesOf('Design System|Button', module)
Link
</Button>
</div>
))
.add('button wrapper', () => (
<div>
<ButtonWrapper>Original Button Wrapper</ButtonWrapper>
<br />
<Button ButtonWrapper={ButtonWrapper} appearance="primary">
Primary
</Button>
<Button ButtonWrapper={ButtonWrapper} appearance="secondary">
Secondary
</Button>
<Button ButtonWrapper={ButtonWrapper} appearance="tertiary">
Tertiary
</Button>
<Button ButtonWrapper={ButtonWrapper} appearance="outline">
Outline
</Button>
<Button ButtonWrapper={ButtonWrapper} appearance="primaryOutline">
Outline primary
</Button>
<Button ButtonWrapper={ButtonWrapper} appearance="secondaryOutline">
Outline secondary
</Button>
<Button ButtonWrapper={ButtonWrapper} appearance="primary" isDisabled>
Disabled
</Button>
<br />
<Button ButtonWrapper={ButtonWrapper} appearance="primary" isLoading>
Primary
</Button>
<Button ButtonWrapper={ButtonWrapper} appearance="secondary" isLoading>
Secondary
</Button>
<Button ButtonWrapper={ButtonWrapper} appearance="tertiary" isLoading>
Tertiary
</Button>
<Button ButtonWrapper={ButtonWrapper} appearance="outline" isLoading>
Outline
</Button>
<Button ButtonWrapper={ButtonWrapper} appearance="outline" isLoading loadingText="Custom...">
Outline
</Button>
<br />
<Button ButtonWrapper={ButtonWrapper} appearance="primary" size="small">
Primary
</Button>
<Button ButtonWrapper={ButtonWrapper} appearance="secondary" size="small">
Secondary
</Button>
<Button ButtonWrapper={ButtonWrapper} appearance="tertiary" size="small">
Tertiary
</Button>
<Button ButtonWrapper={ButtonWrapper} appearance="outline" size="small">
Outline
</Button>
<Button ButtonWrapper={ButtonWrapper} appearance="primary" isDisabled size="small">
Disabled
</Button>
<Button ButtonWrapper={ButtonWrapper} appearance="outline" size="small" containsIcon>
<Icon icon="link" aria-label="Link" />
</Button>
<Button ButtonWrapper={ButtonWrapper} appearance="outline" size="small">
<Icon icon="link" />
Link
</Button>
</div>
))

.add('anchor wrapper', () => (
<div>
<StoryLinkWrapper to="http://storybook.js.org">Original Link Wrapper</StoryLinkWrapper>
<br />
<Button ButtonWrapper={StoryLinkWrapper} appearance="primary" href="http://storybook.js.org">
Primary
</Button>
<Button
ButtonWrapper={StoryLinkWrapper}
appearance="secondary"
href="http://storybook.js.org"
>
Secondary
</Button>
<Button ButtonWrapper={StoryLinkWrapper} appearance="tertiary" href="http://storybook.js.org">
Tertiary
</Button>
<Button ButtonWrapper={StoryLinkWrapper} appearance="outline" href="http://storybook.js.org">
Outline
</Button>
<Button
ButtonWrapper={StoryLinkWrapper}
appearance="primaryOutline"
href="http://storybook.js.org"
>
Outline primary
</Button>
<Button
ButtonWrapper={StoryLinkWrapper}
appearance="secondaryOutline"
href="http://storybook.js.org"
>
Outline secondary
</Button>
<Button
ButtonWrapper={StoryLinkWrapper}
appearance="primary"
isDisabled
href="http://storybook.js.org"
>
Disabled
</Button>
<br />
<Button
ButtonWrapper={StoryLinkWrapper}
appearance="primary"
isLoading
href="http://storybook.js.org"
>
Primary
</Button>
<Button
ButtonWrapper={StoryLinkWrapper}
appearance="secondary"
isLoading
href="http://storybook.js.org"
>
Secondary
</Button>
<Button
ButtonWrapper={StoryLinkWrapper}
appearance="tertiary"
isLoading
href="http://storybook.js.org"
>
Tertiary
</Button>
<Button
ButtonWrapper={StoryLinkWrapper}
appearance="outline"
isLoading
href="http://storybook.js.org"
>
Outline
</Button>
<Button
ButtonWrapper={StoryLinkWrapper}
appearance="outline"
isLoading
loadingText="Custom..."
href="http://storybook.js.org"
>
Outline
</Button>
<br />
<Button
ButtonWrapper={StoryLinkWrapper}
appearance="primary"
size="small"
href="http://storybook.js.org"
>
Primary
</Button>
<Button
ButtonWrapper={StoryLinkWrapper}
appearance="secondary"
size="small"
href="http://storybook.js.org"
>
Secondary
</Button>
<Button
ButtonWrapper={StoryLinkWrapper}
appearance="tertiary"
size="small"
href="http://storybook.js.org"
>
Tertiary
</Button>
<Button
ButtonWrapper={StoryLinkWrapper}
appearance="outline"
size="small"
href="http://storybook.js.org"
>
Outline
</Button>
<Button
ButtonWrapper={StoryLinkWrapper}
appearance="primary"
isDisabled
size="small"
href="http://storybook.js.org"
>
Disabled
</Button>
<Button
ButtonWrapper={StoryLinkWrapper}
appearance="outline"
size="small"
containsIcon
href="http://storybook.js.org"
>
<Icon icon="link" aria-label="Link" />
</Button>
<Button
ButtonWrapper={StoryLinkWrapper}
appearance="outline"
size="small"
href="http://storybook.js.org"
>
<Icon icon="link" />
Link
</Button>
</div>
));

0 comments on commit bd82b09

Please sign in to comment.