Skip to content

Commit

Permalink
Merge pull request #2536 from illiteratewriter/closebutton
Browse files Browse the repository at this point in the history
feat(CloseButton): add close button
  • Loading branch information
davidacevedo committed Jun 7, 2022
2 parents 9ef40d1 + 681558a commit 2a96fba
Show file tree
Hide file tree
Showing 10 changed files with 184 additions and 6 deletions.
18 changes: 12 additions & 6 deletions src/Button.js
Expand Up @@ -2,6 +2,7 @@ import React, { useCallback } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { mapToCssModules, tagPropType } from './utils';
import CloseButton from './CloseButton';

const propTypes = {
active: PropTypes.bool,
Expand Down Expand Up @@ -54,13 +55,20 @@ function Button(props) {
...attributes
} = props;

if (close) {
return (
<CloseButton
{...attributes}
/>
)
}

const btnOutlineColor = `btn${outline ? '-outline' : ''}-${color}`;

const classes = mapToCssModules(classNames(
className,
close && 'btn-close',
close || 'btn',
close || btnOutlineColor,
'btn',
btnOutlineColor,
size ? `btn-${size}` : false,
block ? 'd-block w-100' : false,
{ active, disabled: props.disabled }
Expand All @@ -70,16 +78,14 @@ function Button(props) {
Tag = 'a';
}

const defaultAriaLabel = close ? 'Close' : null;

return (
<Tag
type={(Tag === 'button' && attributes.onClick) ? 'button' : undefined}
{...attributes}
className={classes}
ref={innerRef}
onClick={onClick}
aria-label={ariaLabel || defaultAriaLabel}
aria-label={ariaLabel}
/>
);
}
Expand Down
44 changes: 44 additions & 0 deletions src/CloseButton.js
@@ -0,0 +1,44 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { mapToCssModules } from './utils';

const propTypes = {
active: PropTypes.bool,
'aria-label': PropTypes.string,
onClick: PropTypes.func,
variant: PropTypes.oneOf(['white'])
}

const defaultProps = {
'aria-label': 'close'
}

const CloseButton = (props) => {
const {
className,
cssModule,
variant,
innerRef,
...attributes
} = props;

const classes = mapToCssModules(classNames(
className,
'btn-close',
variant && `btn-close-${variant}`
))

return (
<button
ref={innerRef}
type="button"
className={classes}
{...attributes} />
)
}

CloseButton.propTypes = propTypes;
CloseButton.defaultProps = defaultProps;

export default CloseButton;
43 changes: 43 additions & 0 deletions src/__tests__/CloseButton.spec.js
@@ -0,0 +1,43 @@
import React from 'react';
import { shallow, mount } from 'enzyme';
import CloseButton from '../CloseButton';

describe('CloseButton', () => {
it('should render a close button', () => {
const wrapper = shallow(<CloseButton />);
expect(wrapper.hasClass('btn-close')).toBe(true);
})

it('should render white variant', () => {
const wrapper = shallow(<CloseButton variant='white'/>);
expect(wrapper.hasClass('btn-close-white')).toBe(true);
})


describe('onClick', () => {
it('calls props.onClick if it exists', () => {
const onClick = jest.fn();
const wrapper = mount(<CloseButton onClick={onClick} />);

wrapper.find('button').hostNodes().simulate('click');
expect(onClick).toHaveBeenCalled();
});

it('returns the value returned by props.onClick', () => {
const onClick = jest.fn(() => 1234);
const wrapper = mount(<CloseButton onClick={onClick} />);

const result = wrapper.find('button').props().onClick();
expect(onClick).toHaveBeenCalled();
expect(result).toEqual(1234);
});

it('is not called when disabled', () => {
const onClick = jest.fn();
const wrapper = mount(<CloseButton onClick={onClick} disabled />);

wrapper.find('button').hostNodes().simulate('click');
expect(onClick).not.toHaveBeenCalled();
});
});
})
1 change: 1 addition & 0 deletions src/index.js
Expand Up @@ -15,6 +15,7 @@ export ButtonToggle from './ButtonToggle';
export ButtonDropdown from './ButtonDropdown';
export ButtonGroup from './ButtonGroup';
export ButtonToolbar from './ButtonToolbar';
export CloseButton from './CloseButton';
export Dropdown from './Dropdown';
export DropdownItem from './DropdownItem';
export DropdownMenu from './DropdownMenu';
Expand Down
21 changes: 21 additions & 0 deletions stories/CloseButton.stories.js
@@ -0,0 +1,21 @@
import React from 'react';

export default {
title: 'Components/CloseButton',
parameters: {
docs: {
description: {
component: `
[Bootstrap CloseButton](https://getbootstrap.com/docs/5.2/components/close-button/)
A generic close button for dismissing content like modals and alerts.
`,
}
}
}
};

export { default as CloseButton } from './examples/CloseButton';
export { default as CloseButtonWhite } from './examples/CloseButtonWhite';
export { default as CloseButtonDisabled } from './examples/CloseButtonDisabled';
export { default as Props } from './examples/CloseButtonProps';
14 changes: 14 additions & 0 deletions stories/examples/CloseButton.js
@@ -0,0 +1,14 @@
import React from 'react';
import { CloseButton } from 'reactstrap';

const Example = (args) => {
return (
<CloseButton {...args}/>
)
}

Example.args= {
disabled: false,
}

export default Example;
9 changes: 9 additions & 0 deletions stories/examples/CloseButtonDisabled.js
@@ -0,0 +1,9 @@
import React from 'react';
import { CloseButton } from 'reactstrap';

const Example = (args) => {
return (
<CloseButton disabled />
)
}
export default Example;
10 changes: 10 additions & 0 deletions stories/examples/CloseButtonProps.js
@@ -0,0 +1,10 @@

import React from 'react';
import { CloseButton } from 'reactstrap';
import Props from './Props';

const Example = () => (
<Props components={[CloseButton]} />
);

export default Example;
18 changes: 18 additions & 0 deletions stories/examples/CloseButtonWhite.js
@@ -0,0 +1,18 @@

import React from 'react';
import { CloseButton } from 'reactstrap';

const Example = (args) => {
return (
<div className='bg-dark p-3'>
<CloseButton {...args}/>
</div>
)
}

Example.args= {
disabled: false,
variant: 'white'
}

export default Example;
12 changes: 12 additions & 0 deletions types/lib/CloseButton.d.ts
@@ -0,0 +1,12 @@
import * as React from 'react';
import { CSSModule } from './utils';

export interface CloseButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement> {
active?: boolean;
variant?: 'white' | 'black';
cssModule?: CSSModule;
}

declare class CloseButton extends React.Component<CloseButtonProps> {}
export default CloseButton;

0 comments on commit 2a96fba

Please sign in to comment.