Skip to content
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
7 changes: 7 additions & 0 deletions example/src/components/ButtonDemo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,13 @@ export const ButtonDemo = () => {
/>
}
/>
<h1>Button with children element</h1>
<Button isLoading={isLoading} onClick={() => {}} label="">
<span>
this is the content
<strong> passed as children</strong>
</span>
</Button>
</>
);
};
14 changes: 13 additions & 1 deletion src/button/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ type ButtonProps = React.HTMLAttributes<HTMLButtonElement> & {
* allows to specify a variant
*/
variant?: 'primary' | 'secondary' | 'tertiary';
/**
* allow to specify a custom content
*/
children?: string | number | JSX.Element | JSX.Element[];
};

export const Button = ({
Expand All @@ -96,6 +100,7 @@ export const Button = ({
value,
className,
variant,
children,
...props
}: ButtonProps) => {
const [disable, setDisable] = React.useState<boolean>(disabled);
Expand Down Expand Up @@ -141,10 +146,16 @@ export const Button = ({
'dcx-button',
className,
{
[`dcx-button--${variant}`]: variant !== undefined,
[`dcx-button--${variant}`]: variant !== undefined,
},
]);

if (label !== undefined && children !== undefined) {
throw new Error(
'You can use label or children but not both at the same time'
);
}

return (
<button
onClick={handleClick}
Expand All @@ -159,6 +170,7 @@ export const Button = ({
>
{prefix}
{isLoading && loadingLabel ? loadingLabel : label}
{!isLoading && children}
{postfix}
</button>
);
Expand Down
46 changes: 42 additions & 4 deletions src/button/__tests__/Button.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -271,15 +271,53 @@ describe('Button', () => {

it('should render the secondary variant to the className', () => {
const handleClick = jest.fn();
render(<Button onClick={handleClick} variant="secondary" label="Register" />);
render(
<Button onClick={handleClick} variant="secondary" label="Register" />
);
const button: any = screen.getByRole('button');
expect(button.getAttribute('class')).toBe('dcx-button dcx-button--secondary');
expect(button.getAttribute('class')).toBe(
'dcx-button dcx-button--secondary'
);
});

it('should render the default, user specified and variant className', () => {
const handleClick = jest.fn();
render(<Button onClick={handleClick} className="active" variant="tertiary" label="Register" />);
render(
<Button
onClick={handleClick}
className="active"
variant="tertiary"
label="Register"
/>
);
const button: any = screen.getByRole('button');
expect(button.getAttribute('class')).toBe('dcx-button active dcx-button--tertiary');
expect(button.getAttribute('class')).toBe(
'dcx-button active dcx-button--tertiary'
);
});

it('renders JSX elements as children', () => {
const childElement = <span>Child Element</span>;
const { getByText } = render(<Button>{childElement}</Button>);
expect(getByText('Child Element')).toBeInTheDocument();
});

it('renders multiple JSX elements as children', () => {
const childElement1 = <span>Child Element 1</span>;
const childElement2 = <span>Child Element 2</span>;
const { getByText } = render(
<Button>
{childElement1}
{childElement2}
</Button>
);
expect(getByText('Child Element 1')).toBeInTheDocument();
expect(getByText('Child Element 2')).toBeInTheDocument();
});

it('should throws an error when both value and children are provided', () => {
expect(() => render(<Button label="test">Children test</Button>)).toThrow(
'You can use label or children but not both at the same time'
);
});
});
35 changes: 23 additions & 12 deletions stories/Button/ClassBased.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Button } from '../../src/button/Button';
import { useArgs } from '@storybook/preview-api';

/**
* In this section we're using the button component providing the **GovUk style** passing the relative `className.
* In this section we're using the button component providing the **GovUk style** passing the relative `className.
* Feel free to use your own css to style the formInput as you prefer.
*/
export default {
Expand All @@ -13,24 +13,24 @@ export default {
showPanel: true,
},
},
tags: ['autodocs']
tags: ['autodocs'],
};

export const Basic = {
export const Basic = {
name: 'Basic',
args: {
label: "Save and continue",
className: "govuk-button",
label: 'Save and continue',
className: 'govuk-button',
},
argTypes: { onClick: { action: 'clicked' } },
};

export const WithImage = {
name: 'With Image',
args: {
label: "Start now",
className: "govuk-button",
customPostfixImg:
label: 'Start now',
className: 'govuk-button',
customPostfixImg: (
<svg
class="govuk-button__start-icon"
xmlns="http://www.w3.org/2000/svg"
Expand All @@ -42,16 +42,17 @@ export const WithImage = {
>
<path fill="currentColor" d="M0 0h13l20 20-20 20H0l20-20z"></path>
</svg>
),
},
argTypes: { onClick: { action: 'clicked' } },
};

export const Disabled = {
name: 'Disabled',
args: {
label: "Start now",
className: "govuk-button",
disabled: true
label: 'Start now',
className: 'govuk-button',
disabled: true,
},
argTypes: { onClick: { action: 'clicked' } },
};
Expand All @@ -78,3 +79,13 @@ export const Loading = {
},
argTypes: { onClick: { action: 'onClick' } },
};

/**
* Button allows to pass a simple value or if needed a custom one as children property
*/
export const CustomContent = {
args: {
className: 'govuk-button',
children: [<strong>Login</strong>],
},
};
11 changes: 9 additions & 2 deletions stories/Button/Documentation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import * as ButtonStories from './UnStyled.stories';

A Button component ready to use in your form with support for delayClick and add an image before or after your label.
Button is UI/UX agnostic so you need to provide your style to have the look and feel you prefer.
It accepts a label or a more complex content.

When you import the button component without providing any className or style associated will looks as following:

<Canvas of={ButtonStories.Unstyled} />
Expand All @@ -19,7 +21,7 @@ A more complex example with all the possible properties is:
onClick={buttonHandler}
disableClickForMs={2000}
disable={true}
isLoading={true},
isLoading={true}
loadingLabel="loading..."
customLoadingPreImage={<span>spinner</span>}
customLoadingPostImage={<span>spinner</span>}
Expand Down Expand Up @@ -52,7 +54,12 @@ A more complex example with all the possible properties is:
value="value"
className="class"
variant="primary"
/>
>
/**
* if you use label you can't pass this extra content
*/
<strong>Value</strong>
</>
```

Below a list of all the available properties:
Expand Down
10 changes: 9 additions & 1 deletion stories/Button/UnStyled.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,18 @@ export default {
showPanel: true,
},
},
argTypes: {
children: {
description: 'allows to add an element as children',
},
},
};

export const Unstyled = {
args: {
label: 'Button',
children: [
'This is the simple custom-content of the ',
<strong>button</strong>,
],
},
};
10 changes: 9 additions & 1 deletion stories/Button/design-system/AccessibleTheme.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export default {
title: 'DCXLibrary/Form/Button/Design system/Accessible',
component: Button,
decorators: [
getStory => {
(getStory) => {
require('../../../dist/design-system/index.css');
require('../../themes/accessible.theme.css');
return getStory();
Expand Down Expand Up @@ -174,3 +174,11 @@ export const TertiaryDisabled = {
disabled: true,
},
};

export const CustomContent = {
name: 'Custom content',
args: {
children: [<strong>Login</strong>],
variant: 'primary',
},
};
19 changes: 18 additions & 1 deletion stories/Button/design-system/DarkTheme.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export default {
title: 'DCXLibrary/Form/Button/Design system/Dark',
component: Button,
decorators: [
getStory => {
(getStory) => {
require('../../../dist/design-system/index.css');
require('../../themes/dark.theme.css');
return getStory();
Expand Down Expand Up @@ -309,3 +309,20 @@ export const TertiaryDisabled = {
disabled: true,
},
};

export const CustomContent = {
name: 'Custom content',
parameters: {
backgrounds: {
default: 'dark',
values: [
{ name: 'dark', value: '#333131' },
{ name: 'light', value: '#fff' },
],
},
},
args: {
children: [<strong>My Button</strong>],
variant: 'primary',
},
};
9 changes: 8 additions & 1 deletion stories/Button/design-system/Default.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export default {
title: 'DCXLibrary/Form/Button/Design system/Default',
component: Button,
decorators: [
getStory => {
(getStory) => {
require('../../../dist/design-system/index.css');
return getStory();
},
Expand Down Expand Up @@ -57,3 +57,10 @@ export const DefaultDisabled = {
disabled: true,
},
};

export const CustomContent = {
name: 'Custom Content',
args: {
children: [<strong>My Button</strong>],
},
};
9 changes: 8 additions & 1 deletion stories/Button/design-system/MaterialTheme.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export default {
title: 'DCXLibrary/Form/Button/Design system/Material',
component: Button,
decorators: [
getStory => {
(getStory) => {
require('../../../dist/design-system/index.css');
require('../../themes/material.theme.css');
return getStory();
Expand Down Expand Up @@ -175,3 +175,10 @@ export const TertiaryDisabled = {
disabled: true,
},
};

export const CustomContent = {
name: 'Custom Content',
args: {
children: [<strong>My Button</strong>],
},
};
6 changes: 1 addition & 5 deletions stories/Button/design-system/Playground.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,7 @@ export default {
title: 'DCXLibrary/Form/Button/Design system',
component: Button,
decorators: [
getStory => (
<TokensDecorator style={style}>
{getStory()}
</TokensDecorator>
),
(getStory) => <TokensDecorator style={style}>{getStory()}</TokensDecorator>,
],
parameters: {
options: { showPanel: true },
Expand Down
10 changes: 9 additions & 1 deletion stories/Button/design-system/Primary.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export default {
title: 'DCXLibrary/Form/Button/Design system/Primary',
component: Button,
decorators: [
getStory => {
(getStory) => {
require('../../../dist/design-system/index.css');
return getStory();
},
Expand Down Expand Up @@ -62,3 +62,11 @@ export const PrimaryDisabled = {
disabled: true,
},
};

export const CustomContent = {
name: 'Custom Content',
args: {
variant: 'primary',
children: [<strong>My Button</strong>],
},
};
10 changes: 9 additions & 1 deletion stories/Button/design-system/Secondary.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export default {
title: 'DCXLibrary/Form/Button/Design system/Secondary',
component: Button,
decorators: [
getStory => {
(getStory) => {
require('../../../dist/design-system/index.css');
return getStory();
},
Expand Down Expand Up @@ -62,3 +62,11 @@ export const SecondaryDisabled = {
disabled: true,
},
};

export const CustomContent = {
name: 'Custom Content',
args: {
variant: 'secondary',
children: [<strong>My Button</strong>],
},
};
Loading