Skip to content

Commit 52e8541

Browse files
kyletsangdavidjb
andauthored
feat(v5): Update CloseButton (#5460)
* feat(v5): Update CloseButton - Added white variant - Remove inner span label in favor of aria-label attrib - Remove inner &times label since it's now using a built-in svg - Enable CloseButton tests (previously not working) - Added closeVariant prop to Alert, ToastHeader, ModalHeader * docs(CloseButton): add component page (#5474) Co-authored-by: David Beitey <david@davidjb.com>
1 parent ce6230c commit 52e8541

18 files changed

Lines changed: 198 additions & 111 deletions

src/Alert.tsx

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { useUncontrolled } from 'uncontrollable';
66
import useEventCallback from '@restart/hooks/useEventCallback';
77
import { useBootstrapPrefix } from './ThemeProvider';
88
import Fade from './Fade';
9-
import CloseButton from './CloseButton';
9+
import CloseButton, { CloseButtonVariant } from './CloseButton';
1010
import { Variant } from './types';
1111
import divWithClassName from './divWithClassName';
1212
import createWithBsPrefix from './createWithBsPrefix';
@@ -20,6 +20,7 @@ export interface AlertProps extends React.HTMLProps<HTMLDivElement> {
2020
show?: boolean;
2121
onClose?: (a: any, b: any) => void;
2222
closeLabel?: string;
23+
closeVariant?: CloseButtonVariant;
2324
transition?: TransitionType;
2425
}
2526

@@ -77,6 +78,11 @@ const propTypes = {
7778
*/
7879
closeLabel: PropTypes.string,
7980

81+
/**
82+
* Sets the variant for close button.
83+
*/
84+
closeVariant: PropTypes.oneOf<CloseButtonVariant>(['white']),
85+
8086
/**
8187
* Animate the alert dismissal. Defaults to using `<Fade>` animation or use
8288
* `false` to disable. A custom `react-transition-group` Transition can also
@@ -97,6 +103,7 @@ const Alert = (React.forwardRef<HTMLDivElement, AlertProps>(
97103
bsPrefix,
98104
show,
99105
closeLabel,
106+
closeVariant,
100107
className,
101108
children,
102109
variant,
@@ -128,7 +135,11 @@ const Alert = (React.forwardRef<HTMLDivElement, AlertProps>(
128135
)}
129136
>
130137
{dismissible && (
131-
<CloseButton onClick={handleClose} label={closeLabel} />
138+
<CloseButton
139+
onClick={handleClose}
140+
aria-label={closeLabel}
141+
variant={closeVariant}
142+
/>
132143
)}
133144
{children}
134145
</div>

src/CloseButton.tsx

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,32 +2,41 @@ import PropTypes from 'prop-types';
22
import React from 'react';
33
import classNames from 'classnames';
44

5+
export type CloseButtonVariant = 'white';
6+
57
export interface CloseButtonProps
68
extends React.ButtonHTMLAttributes<HTMLButtonElement> {
7-
label?: string;
9+
variant?: CloseButtonVariant;
810
}
911

1012
const propTypes = {
11-
label: PropTypes.string.isRequired,
13+
'aria-label': PropTypes.string,
1214
onClick: PropTypes.func,
15+
16+
/**
17+
* Render different color variant for the button.
18+
*
19+
* Omitting this will render the default dark color.
20+
*/
21+
variant: PropTypes.oneOf<CloseButtonVariant>(['white']),
1322
};
1423

1524
const defaultProps = {
16-
label: 'Close',
25+
'aria-label': 'Close',
1726
};
1827

1928
const CloseButton = React.forwardRef<HTMLButtonElement, CloseButtonProps>(
20-
({ label, onClick, className, ...props }: CloseButtonProps, ref) => (
29+
({ className, variant, ...props }, ref) => (
2130
<button
2231
ref={ref}
2332
type="button"
24-
className={classNames('close', className)}
25-
onClick={onClick}
33+
className={classNames(
34+
'btn-close',
35+
variant && `btn-close-${variant}`,
36+
className,
37+
)}
2638
{...props}
27-
>
28-
<span aria-hidden="true">&times;</span>
29-
<span className="sr-only">{label}</span>
30-
</button>
39+
/>
3140
),
3241
);
3342

src/ModalHeader.tsx

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,15 @@ import React, { useContext } from 'react';
44
import useEventCallback from '@restart/hooks/useEventCallback';
55

66
import { useBootstrapPrefix } from './ThemeProvider';
7-
import CloseButton from './CloseButton';
7+
import CloseButton, { CloseButtonVariant } from './CloseButton';
88
import ModalContext from './ModalContext';
99
import { BsPrefixAndClassNameOnlyProps } from './helpers';
1010

1111
export interface ModalHeaderProps
1212
extends React.PropsWithChildren<BsPrefixAndClassNameOnlyProps>,
1313
React.ComponentProps<'div'> {
1414
closeLabel?: string;
15+
closeVariant?: CloseButtonVariant;
1516
closeButton?: boolean;
1617
onHide?: () => void;
1718
}
@@ -26,6 +27,11 @@ const propTypes = {
2627
*/
2728
closeLabel: PropTypes.string,
2829

30+
/**
31+
* Sets the variant for close button.
32+
*/
33+
closeVariant: PropTypes.oneOf<CloseButtonVariant>(['white']),
34+
2935
/**
3036
* Specify whether the Component should contain a close button
3137
*/
@@ -49,6 +55,7 @@ const ModalHeader = React.forwardRef<HTMLDivElement, ModalHeaderProps>(
4955
{
5056
bsPrefix,
5157
closeLabel,
58+
closeVariant,
5259
closeButton,
5360
onHide,
5461
className,
@@ -71,7 +78,11 @@ const ModalHeader = React.forwardRef<HTMLDivElement, ModalHeaderProps>(
7178
{children}
7279

7380
{closeButton && (
74-
<CloseButton label={closeLabel} onClick={handleClick} />
81+
<CloseButton
82+
aria-label={closeLabel}
83+
variant={closeVariant}
84+
onClick={handleClick}
85+
/>
7586
)}
7687
</div>
7788
);

src/ToastHeader.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import React, { useContext } from 'react';
44
import useEventCallback from '@restart/hooks/useEventCallback';
55

66
import { useBootstrapPrefix } from './ThemeProvider';
7-
import CloseButton from './CloseButton';
7+
import CloseButton, { CloseButtonVariant } from './CloseButton';
88
import ToastContext from './ToastContext';
99
import {
1010
BsPrefixAndClassNameOnlyProps,
@@ -14,6 +14,7 @@ import {
1414
export interface ToastHeaderProps
1515
extends React.PropsWithChildren<BsPrefixAndClassNameOnlyProps> {
1616
closeLabel?: string;
17+
closeVariant?: CloseButtonVariant;
1718
closeButton?: boolean;
1819
}
1920

@@ -29,6 +30,11 @@ const propTypes = {
2930
*/
3031
closeLabel: PropTypes.string,
3132

33+
/**
34+
* Sets the variant for close button.
35+
*/
36+
closeVariant: PropTypes.oneOf<CloseButtonVariant>(['white']),
37+
3238
/**
3339
* Specify whether the Component should contain a close button
3440
*/
@@ -48,6 +54,7 @@ const ToastHeader: ToastHeader = React.forwardRef<
4854
{
4955
bsPrefix,
5056
closeLabel,
57+
closeVariant,
5158
closeButton,
5259
className,
5360
children,
@@ -71,7 +78,8 @@ const ToastHeader: ToastHeader = React.forwardRef<
7178

7279
{closeButton && (
7380
<CloseButton
74-
label={closeLabel}
81+
aria-label={closeLabel}
82+
variant={closeVariant}
7583
onClick={handleClick}
7684
className="ml-2 mb-1"
7785
data-dismiss="toast"

src/helpers.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,8 @@ export interface BsPrefixAndClassNameOnlyProps {
1313
className?: string;
1414
}
1515

16-
export interface BsPrefixProps<
17-
As extends React.ElementType = React.ElementType
18-
> extends BsPrefixAndClassNameOnlyProps {
16+
export interface BsPrefixProps<As extends React.ElementType = React.ElementType>
17+
extends BsPrefixAndClassNameOnlyProps {
1918
as?: As;
2019
}
2120

test/AlertSpec.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,18 @@ describe('<Alert>', () => {
6565
expect(wrapper.isEmptyRender()).to.be.true;
6666
});
6767

68+
it('should render close button variant', () => {
69+
const wrapper = mount(
70+
<Alert dismissible closeVariant="white">
71+
Message
72+
</Alert>,
73+
);
74+
expect(wrapper.find('CloseButton').props()).to.have.property(
75+
'variant',
76+
'white',
77+
);
78+
});
79+
6880
describe('Web Accessibility', () => {
6981
it('Should have alert role', () => {
7082
mount(<Alert>Message</Alert>).assertSingle('[role="alert"]');

test/CloseButton.js

Lines changed: 0 additions & 91 deletions
This file was deleted.

test/CloseButtonSpec.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import React from 'react';
2+
import { mount } from 'enzyme';
3+
4+
import CloseButton from '../src/CloseButton';
5+
6+
describe('<CloseButton>', () => {
7+
it('Should output a button', () => {
8+
mount(<CloseButton />)
9+
.find('button')
10+
.should.have.length(1);
11+
});
12+
13+
it('Should have type=button by default', () => {
14+
mount(<CloseButton />)
15+
.find('button')
16+
.getDOMNode()
17+
.getAttribute('type')
18+
.should.equal('button');
19+
});
20+
21+
it('Should have class .btn-close', () => {
22+
mount(<CloseButton />).assertSingle('.btn-close');
23+
});
24+
25+
it('Should call onClick callback', (done) => {
26+
mount(<CloseButton onClick={() => done()} />).simulate('click');
27+
});
28+
29+
it('Should have a aria-label defaulted to "Close"', () => {
30+
mount(<CloseButton />)
31+
.find('button')
32+
.getDOMNode()
33+
.getAttribute('aria-label')
34+
.should.equal('Close');
35+
});
36+
37+
it('Should allow override of aria-label', () => {
38+
mount(<CloseButton aria-label="My Close" />)
39+
.find('button')
40+
.getDOMNode()
41+
.getAttribute('aria-label')
42+
.should.equal('My Close');
43+
});
44+
});

test/ModalHeaderSpec.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,12 @@ describe('Modal.Header', () => {
2424

2525
expect(onHideSpy).to.have.been.called;
2626
});
27+
28+
it('should render close button variant', () => {
29+
const wrapper = mount(<Modal.Header closeButton closeVariant="white" />);
30+
expect(wrapper.find('CloseButton').props()).to.have.property(
31+
'variant',
32+
'white',
33+
);
34+
});
2735
});

test/ModalSpec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ describe('<Modal>', () => {
129129
<strong>Message</strong>
130130
</Modal>,
131131
)
132-
.find('.close')
132+
.find('.btn-close')
133133
.simulate('click');
134134
});
135135

0 commit comments

Comments
 (0)