Skip to content

Commit 337b3ef

Browse files
montezumekodiakhq[bot]
authored andcommitted
feat(secondary-icon-button): add as prop (#1168)
* feat(secondary-icon-button): add as * chore: add tests
1 parent a92589c commit 337b3ef

File tree

4 files changed

+106
-14
lines changed

4 files changed

+106
-14
lines changed

src/components/buttons/accessible-button/accessible-button.js

+19-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import PropTypes from 'prop-types';
21
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
import { oneLine } from 'common-tags';
34
import styled from '@emotion/styled';
45
import vars from '../../../../materials/custom-properties';
56

@@ -67,7 +68,23 @@ AccessibleButton.displayName = 'AccessibleButton';
6768
AccessibleButton.propTypes = {
6869
as: PropTypes.oneOfType([PropTypes.string, PropTypes.elementType]),
6970
id: PropTypes.string,
70-
type: PropTypes.oneOf(['submit', 'reset', 'button']),
71+
type: (props, propName, componentName, ...rest) => {
72+
// the type defaults to `button`, so we don't need to handle undefined
73+
if (props.as && props.type !== 'button') {
74+
throw new Error(
75+
oneLine`
76+
${componentName}: "${propName}" does not have any effect when
77+
"as" is set.
78+
`
79+
);
80+
}
81+
return PropTypes.oneOf(['submit', 'reset', 'button'])(
82+
props,
83+
propName,
84+
componentName,
85+
...rest
86+
);
87+
},
7188
label: PropTypes.string.isRequired,
7289
children: PropTypes.node.isRequired,
7390
// set to true or false to indicate a toggle button

src/components/buttons/secondary-icon-button/README.md

+11-8
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,17 @@ also pass a label for accessibility reasons.
2424

2525
#### Properties
2626

27-
| Props | Type | Required | Values | Default | Description |
28-
| ------------ | -------- | :------: | --------------------------- | -------- | -------------------------------------------------------------------------------------- |
29-
| `type` | `string` | - | `submit`, `reset`, `button` | `button` | Used as the HTML `type` attribute. |
30-
| `label` | `string` || - | - | Should describe what the button does, for accessibility purposes (screen-reader users) |
31-
| `icon` | `node` || - | - | An `Icon` component |
32-
| `isDisabled` | `bool` | - | - | `false` | Tells when the button should present a disabled state |
33-
| `onClick` | `func` || - | - | What the button will trigger when clicked |
34-
| `color` | `oneOf` | - | `solid`, `primary` | `solid` | Sets the color of the icon |
27+
| Props | Type | Required | Values | Default | Description |
28+
| ------------ | --------------------- | :------: | --------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
29+
| `type` | `string` | - | `submit`, `reset`, `button` | `button` | Used as the HTML `type` attribute. |
30+
| `label` | `string` || - | - | Should describe what the button does, for accessibility purposes (screen-reader users) |
31+
| `icon` | `node` || - | - | An `Icon` component |
32+
| `isDisabled` | `bool` | - | - | `false` | Tells when the button should present a disabled state |
33+
| `onClick` | `func` || - | - | What the button will trigger when clicked |
34+
| `color` | `oneOf` | - | `solid`, `primary` | `solid` | Sets the color of the icon |
35+
| `as` | `string` or `element` | - | - | - | You may pass in a string like "a" to have the button render as an anchor tag instead. Or you could pass in a React Component, like a `Link`. |
36+
37+
The component further forwards all valid HTML attributes to the underlying `button` component.
3538

3639
#### Note
3740

src/components/buttons/secondary-icon-button/secondary-icon-button.js

+11-4
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,23 @@
11
import React from 'react';
22
import PropTypes from 'prop-types';
3-
import filterAriaAttributes from '../../../utils/filter-aria-attributes';
4-
import filterDataAttributes from '../../../utils/filter-data-attributes';
3+
import omit from 'lodash/omit';
4+
import filterInvalidAttributes from '../../../utils/filter-invalid-attributes';
55
import AccessibleButton from '../accessible-button';
66
import { getBaseStyles } from './secondary-icon-button.styles';
77

8+
const propsToOmit = ['type'];
9+
810
export const SecondaryIconButton = props => {
911
const buttonAttributes = {
12+
...filterInvalidAttributes(omit(props, propsToOmit)),
1013
'data-track-component': 'SecondaryIconButton',
11-
...filterAriaAttributes(props),
12-
...filterDataAttributes(props),
14+
// if there is a divergence between `isDisabled` and `disabled`,
15+
// we fall back to `isDisabled`
16+
disabled: props.isDisabled,
1317
};
1418
return (
1519
<AccessibleButton
20+
as={props.as}
1621
type={props.type}
1722
buttonAttributes={buttonAttributes}
1823
label={props.label}
@@ -26,7 +31,9 @@ export const SecondaryIconButton = props => {
2631
};
2732

2833
SecondaryIconButton.displayName = 'SecondaryIconButton';
34+
2935
SecondaryIconButton.propTypes = {
36+
as: PropTypes.oneOfType([PropTypes.string, PropTypes.elementType]),
3037
type: PropTypes.oneOf(['submit', 'reset', 'button']),
3138
icon: PropTypes.element.isRequired,
3239
color: PropTypes.oneOf(['solid', 'primary']),

src/components/buttons/secondary-icon-button/secondary-icon-button.spec.js

+65
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import React from 'react';
2+
import { Link } from 'react-router-dom';
23
import { render } from '../../../test-utils';
34
import { PlusBoldIcon } from '../../icons';
45
import SecondaryIconButton from './secondary-icon-button';
@@ -43,6 +44,32 @@ describe('rendering', () => {
4344
'true'
4445
);
4546
});
47+
describe('when there is a divergence between `disabled` and `isDisabled`', () => {
48+
describe('when `isDisabled` and not `disabled`', () => {
49+
it('should favour `isDisabled`', () => {
50+
const { getByLabelText } = render(
51+
<SecondaryIconButton {...props} isDisabled={true} disabled={false} />
52+
);
53+
expect(getByLabelText('test-button')).toHaveAttribute('disabled');
54+
expect(getByLabelText('test-button')).toHaveAttribute(
55+
'aria-disabled',
56+
'true'
57+
);
58+
});
59+
});
60+
describe('when not `isDisabled` and `disabled`', () => {
61+
it('should favour `isDisabled`', () => {
62+
const { getByLabelText } = render(
63+
<SecondaryIconButton {...props} isDisabled={false} disabled={true} />
64+
);
65+
expect(getByLabelText('test-button')).not.toHaveAttribute('disabled');
66+
expect(getByLabelText('test-button')).not.toHaveAttribute(
67+
'aria-disabled',
68+
'true'
69+
);
70+
});
71+
});
72+
});
4673
describe('type variations', () => {
4774
it('should render a button of type "button"', () => {
4875
const { getByLabelText } = render(<SecondaryIconButton {...props} />);
@@ -61,4 +88,42 @@ describe('rendering', () => {
6188
expect(getByLabelText('test-button')).toHaveAttribute('type', 'reset');
6289
});
6390
});
91+
describe('when used with `as`', () => {
92+
describe('when as is a valid HTML element', () => {
93+
it('should render as that HTML element', () => {
94+
const { container } = render(
95+
<SecondaryIconButton
96+
{...props}
97+
as="a"
98+
href="https://www.kanyetothe.com"
99+
target="_BLANK"
100+
/>
101+
);
102+
const linkButton = container.querySelector('a');
103+
expect(linkButton).toHaveAttribute(
104+
'href',
105+
'https://www.kanyetothe.com'
106+
);
107+
expect(linkButton).not.toHaveAttribute('type', 'button');
108+
expect(linkButton).toHaveAttribute('target', '_BLANK');
109+
});
110+
});
111+
describe('when as is a React component', () => {
112+
it('should render as that component', () => {
113+
const { getByLabelText } = render(
114+
<SecondaryIconButton
115+
{...props}
116+
as={Link}
117+
to="foo/bar"
118+
target="_BLANK"
119+
/>
120+
);
121+
122+
const linkButton = getByLabelText('test-button');
123+
expect(linkButton).toHaveAttribute('href', '/foo/bar');
124+
expect(linkButton).toHaveAttribute('target', '_BLANK');
125+
expect(linkButton).not.toHaveAttribute('type', 'button');
126+
});
127+
});
128+
});
64129
});

0 commit comments

Comments
 (0)