Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(CloseButton): add close button #2536

Merged
merged 3 commits into from
Jun 7, 2022
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
18 changes: 12 additions & 6 deletions src/Button.js
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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';
davidacevedo marked this conversation as resolved.
Show resolved Hide resolved
export { default as CloseButtonDisabled } from './examples/CloseButtonDisabled';
export { default as Props } from './examples/CloseButtonProps';
14 changes: 14 additions & 0 deletions stories/examples/CloseButton.js
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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;