Skip to content

Commit

Permalink
feat(Pill): add onClose prop (#198)
Browse files Browse the repository at this point in the history
  • Loading branch information
eszthoff authored Dec 10, 2019
1 parent 38f01d8 commit ccd928e
Show file tree
Hide file tree
Showing 9 changed files with 90 additions and 7 deletions.
17 changes: 16 additions & 1 deletion src/components/Pill/Pill.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const Pill = props => {
dropdownRef: dropdownRefFromProps,
noPaddingInDropdown,
additionalDropdownProps,
onClose,
...rest
} = props;

Expand All @@ -27,6 +28,9 @@ const Pill = props => {
// eslint-disable-next-line react/display-name, react/prop-types
const buttonRenderer = ({ setPopupVisibility, isOpen }) => {
const toggleDropdown = () => {
if (isOpen && onClose) {
onClose();
}
setPopupVisibility(!isOpen);
};

Expand All @@ -43,10 +47,17 @@ const Pill = props => {
);
};

const closeDropdown = setPopupVisibility => {
setPopupVisibility(false);
if (onClose) {
onClose();
}
};

// eslint-disable-next-line react/display-name, react/prop-types
const dropdownRenderer = ({ setPopupVisibility }) => (
<PillDropdown
close={() => setPopupVisibility(false)}
close={() => closeDropdown(setPopupVisibility)}
noPadding={noPaddingInDropdown}
doneLabel={doneLabel}
{...additionalDropdownProps}
Expand All @@ -61,6 +72,7 @@ const Pill = props => {
popupRenderer={dropdownRenderer}
anchorRef={buttonRef}
popupRef={dropdownRef}
onClose={onClose}
/>
);
};
Expand Down Expand Up @@ -94,6 +106,8 @@ Pill.propTypes = {
noPaddingInDropdown: PropTypes.bool,
/** other props that need to be applied to the dropdown container */
additionalDropdownProps: PropTypes.object, // eslint-disable-line react/forbid-prop-types
/** a function that is called when the dropdown closes via done-button-click, window-click or ESC */
onClose: PropTypes.func,
};

Pill.defaultProps = {
Expand All @@ -103,6 +117,7 @@ Pill.defaultProps = {
dropdownRef: null,
noPaddingInDropdown: false,
additionalDropdownProps: {},
onClose: null,
};

export default Pill;
30 changes: 25 additions & 5 deletions src/components/Pill/__tests__/Pill.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,27 @@ import Pill from '../Pill';
describe('<Pill> component', () => {
const childrenMock = jest.fn();
const onClearMock = jest.fn();
const onCloseMock = jest.fn();
const nameMock = 'Pill name';
const contentMock = 'Pill content';

let wrapper;
let clickPillButton;

beforeEach(() => {
wrapper = mount(
<Pill onClear={onClearMock} name={nameMock} content={contentMock} doneLabel="Done">
<Pill
onClear={onClearMock}
onClose={onCloseMock}
name={nameMock}
content={contentMock}
doneLabel="Done"
>
{childrenMock}
</Pill>
);

clickPillButton = () => wrapper.find('.PillButton__pill').simulate('click');
});

afterEach(() => {
Expand All @@ -28,23 +38,33 @@ describe('<Pill> component', () => {
expect(wrapper.find('PillDropdown')).toHaveLength(0);
});
it('should open dropdown when button is clicked', () => {
wrapper.find('.PillButton__pill').simulate('click');
clickPillButton();
expect(toJson(wrapper)).toMatchSnapshot();
expect(wrapper.find('PillButton')).toHaveLength(1);
expect(wrapper.find('PillDropdown')).toHaveLength(1);
});
it('should close dropdown when button is clicked again', () => {
wrapper.find('.PillButton__pill').simulate('click');
clickPillButton();
expect(wrapper.find('PillDropdown')).toHaveLength(1);

wrapper.find('.PillButton__pill').simulate('click');
clickPillButton();
expect(wrapper.find('PillDropdown')).toHaveLength(0);
});
it('should render children when dropdown is open', () => {
expect(childrenMock).not.toHaveBeenCalled();

wrapper.find('.PillButton__pill').simulate('click');
clickPillButton();
expect(wrapper.find('PillDropdown')).toHaveLength(1);
expect(childrenMock).toHaveBeenCalledTimes(1);
});
it('should call onClose when dropdown is closed via pill-button click', () => {
clickPillButton();
clickPillButton();
expect(onCloseMock).toHaveBeenCalledTimes(1);
});
it('should call onClose when dropdown is closed via done-button click', () => {
clickPillButton();
wrapper.find('.PillDropdown__footer button').simulate('click');
expect(onCloseMock).toHaveBeenCalledTimes(1);
});
});
4 changes: 4 additions & 0 deletions src/components/Pill/__tests__/__snapshots__/Pill.spec.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ exports[`<Pill> component should open dropdown when button is clicked 1`] = `
name="Pill name"
noPaddingInDropdown={false}
onClear={[MockFunction]}
onClose={[MockFunction]}
ref={null}
>
<PopupBase
Expand Down Expand Up @@ -55,6 +56,7 @@ exports[`<Pill> component should open dropdown when button is clicked 1`] = `
}
}
anchorRenderer={[Function]}
onClose={[MockFunction]}
placement="bottom-start"
popupRef={
Object {
Expand Down Expand Up @@ -211,6 +213,7 @@ exports[`<Pill> component should render correctly 1`] = `
name="Pill name"
noPaddingInDropdown={false}
onClear={[MockFunction]}
onClose={[MockFunction]}
ref={null}
>
<PopupBase
Expand Down Expand Up @@ -247,6 +250,7 @@ exports[`<Pill> component should render correctly 1`] = `
}
}
anchorRenderer={[Function]}
onClose={[MockFunction]}
placement="bottom-start"
popupRef={
Object {
Expand Down
8 changes: 8 additions & 0 deletions src/components/PopupBase/PopupBase.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,14 @@ class PopupBase extends React.Component {
}

close() {
const { onClose } = this.props;
const { isOpen } = this.state;

if (isOpen) {
this.setState({ isOpen: false });
if (onClose) {
onClose();
}
}
}

Expand Down Expand Up @@ -162,13 +167,16 @@ PopupBase.propTypes = {
placement: PropTypes.oneOf(POPUP_PLACEMENTS),
/** To render the popup in a portal. Useful if the anchor element has overflow hidden and similar cases */
renderInPortal: PropTypes.bool,
/** a function to be called when the popup closes */
onClose: PropTypes.func,
};

PopupBase.defaultProps = {
anchorRef: null,
popupRef: null,
placement: 'bottom-start',
renderInPortal: false,
onClose: null,
};

export default PopupBase;
27 changes: 26 additions & 1 deletion src/components/PopupBase/__tests__/PopupBase.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ describe('<PopupBase> that adds basic anchor/popup functionality to rendered com

describe('click and keydown event handling', () => {
let togglePopup;
const onCloseMock = jest.fn();

// see: https://medium.com/@DavideRama/testing-global-event-listener-within-a-react-component-b9d661e59953
const mockDocumentEventListener = {};
Expand All @@ -71,7 +72,11 @@ describe('<PopupBase> that adds basic anchor/popup functionality to rendered com

beforeEach(() => {
wrapper = mount(
<PopupBase anchorRenderer={anchorRendererMock} popupRenderer={popupRendererMock} />
<PopupBase
anchorRenderer={anchorRendererMock}
popupRenderer={popupRendererMock}
onClose={onCloseMock}
/>
);

togglePopup = () => {
Expand All @@ -82,6 +87,10 @@ describe('<PopupBase> that adds basic anchor/popup functionality to rendered com
};
});

afterEach(() => {
onCloseMock.mockReset();
});

it('should close open popup if outside is clicked', () => {
togglePopup();
expect(wrapper.find('Popover')).toHaveLength(1);
Expand All @@ -91,6 +100,14 @@ describe('<PopupBase> that adds basic anchor/popup functionality to rendered com

expect(wrapper.find('Popover')).toHaveLength(0);
});
it('should call onClose if outside is clicked', () => {
togglePopup();

mockDocumentEventListener.click({ target: document.body });
wrapper.update();

expect(onCloseMock).toHaveBeenCalled();
});
it('should not close open popup if popup is clicked', () => {
togglePopup();
expect(wrapper.find('Popover')).toHaveLength(1);
Expand Down Expand Up @@ -132,5 +149,13 @@ describe('<PopupBase> that adds basic anchor/popup functionality to rendered com

expect(wrapper.find('Popover')).toHaveLength(0);
});
it('should call onClose on Escape press', () => {
togglePopup();

mockDocumentEventListener.keydown({ key: ESCAPE_KEY });
wrapper.update();

expect(onCloseMock).toHaveBeenCalled();
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ exports[`<PopupBase> that adds basic anchor/popup functionality to rendered comp
<PopupBase
anchorRef={null}
anchorRenderer={[Function]}
onClose={null}
placement="bottom-start"
popupRef={null}
popupRenderer={[Function]}
Expand Down Expand Up @@ -98,6 +99,7 @@ exports[`<PopupBase> that adds basic anchor/popup functionality to rendered comp
<PopupBase
anchorRef={null}
anchorRenderer={[Function]}
onClose={null}
placement="bottom-start"
popupRef={null}
popupRenderer={[Function]}
Expand Down Expand Up @@ -169,6 +171,7 @@ exports[`<PopupBase> that adds basic anchor/popup functionality to rendered comp
<PopupBase
anchorRef={null}
anchorRenderer={[Function]}
onClose={null}
placement="bottom-start"
popupRef={null}
popupRenderer={[Function]}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ exports[`<Tooltip> that renders a Tooltip should display a tooltip on hover 1`]
<PopupBase
anchorRef={null}
anchorRenderer={[Function]}
onClose={null}
placement="bottom"
popupRef={null}
popupRenderer={[Function]}
Expand Down Expand Up @@ -43,6 +44,7 @@ exports[`<Tooltip> that renders a Tooltip should render default Tooltip correctl
<PopupBase
anchorRef={null}
anchorRenderer={[Function]}
onClose={null}
placement="bottom"
popupRef={null}
popupRenderer={[Function]}
Expand Down
3 changes: 3 additions & 0 deletions stories/Pill.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ storiesOf('Molecules|Pill', module)
false
)}
noPaddingInDropdown={boolean('no padding in dropdown', false)}
onClose={() => {
console.log('onClose has been called');
}}
>
{({ close }) => <DummyComponent close={close} />}
</Pill>
Expand Down
3 changes: 3 additions & 0 deletions stories/PopupBase.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ storiesOf('Atoms|PopupBase', module)
POPUP_PLACEMENTS,
'bottom-start'
)}
onClose={() => {
console.log('onClose has been called');
}}
/>
</div>
));

0 comments on commit ccd928e

Please sign in to comment.