Skip to content
This repository was archived by the owner on Mar 6, 2025. It is now read-only.

Commit c1390f1

Browse files
Merge pull request #127 from Ultimaker/close-drop-down-menus-on-item-click
close drop down menus on item click
2 parents e146c7d + 4d8ee75 commit c1390f1

File tree

5 files changed

+56
-29
lines changed

5 files changed

+56
-29
lines changed

src/components/__tests__/drop_down_menu_item.test.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,18 @@ import * as React from 'react';
33
import { shallow } from 'enzyme';
44

55
// component
6-
import DropDownMenuItem from '../drop_down_menu_item';
6+
import { DropDownMenuItem, DropDownMenuItemProps } from '../drop_down_menu_item';
77
import Button from '../button';
88

99
describe('The DropDownMenuItem component', () => {
10-
let props;
10+
let props: DropDownMenuItemProps;
1111
let wrapper;
1212

1313
beforeEach(() => {
1414
props = {
1515
active: false,
1616
onClickHandler: jest.fn(),
17+
onCloseMenuHandler: jest.fn(),
1718
};
1819
wrapper = shallow(<DropDownMenuItem {...props}>Menu item</DropDownMenuItem>);
1920
});
@@ -26,6 +27,7 @@ describe('The DropDownMenuItem component', () => {
2627
it('should call the click handler when item is clicked', () => {
2728
wrapper.find(Button).props().onClickHandler();
2829
expect(props.onClickHandler).toHaveBeenCalled();
30+
expect(props.onCloseMenuHandler).toHaveBeenCalled();
2931
});
3032

3133
it('should render item as disabled', () => {

src/components/drop_down_menu.tsx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,13 @@ export default class DropDownMenu extends React.Component<DropDownMenuProps, Dro
2525
showMenu: false,
2626
};
2727

28+
constructor(props) {
29+
super(props);
30+
31+
// bind callbacks once
32+
this._onToggleMenuHandler = this._onToggleMenuHandler.bind(this);
33+
}
34+
2835
private _onToggleMenuHandler(showMenu: boolean) {
2936
this.setState({ showMenu });
3037
}
@@ -61,7 +68,12 @@ export default class DropDownMenu extends React.Component<DropDownMenuProps, Dro
6168
triggerElement={this._renderTrigger()}
6269
onToggleMenuHandler={newShowMenu => this._onToggleMenuHandler(newShowMenu)}
6370
>
64-
{children}
71+
{React.Children.map(children, (child: JSX.Element) => (
72+
React.cloneElement(
73+
child,
74+
{ onCloseMenuHandler: () => this._onToggleMenuHandler(false) },
75+
)
76+
))}
6577
</DropDownMenuBase>
6678
</div>
6779
);

src/components/drop_down_menu_base.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,9 @@ export class DropDownMenuBase extends React.Component<DropDownMenuBaseProps, {}>
4141
private _menuRef;
4242

4343
private _onOutsideFocusHandler(event): void {
44-
if (this._menuRef && !this._menuRef.current.contains(event.target)) {
44+
if (this._menuRef
45+
&& this._menuRef.current
46+
&& !this._menuRef.current.contains(event.target)) {
4547
this._onToggleMenuHandler(false);
4648
}
4749

src/components/drop_down_menu_item.tsx

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,23 +15,35 @@ export interface DropDownMenuItemProps {
1515
id?: string;
1616
/** Optional class for the menu item */
1717
className?: string;
18+
/** Passed by the DropDownMenu component */
19+
onCloseMenuHandler?: () => void;
1820
}
1921

20-
const DropDownMenuItem: React.StatelessComponent<DropDownMenuItemProps> = ({
21-
active, disabled, onClickHandler, id, className, children,
22-
}) => (
23-
<li>
24-
<Button
25-
id={id}
26-
className={classNames('drop-down-menu-base__item', { disabled, active }, className)}
27-
appearance="no-style"
28-
onClickHandler={() => { onClickHandler(); }}
29-
disabled={disabled}
30-
>
31-
{children}
32-
</Button>
33-
</li>
34-
);
22+
export const DropDownMenuItem: React.StatelessComponent<DropDownMenuItemProps> = ({
23+
active, disabled, onClickHandler, id, className, onCloseMenuHandler, children,
24+
}) => {
25+
const _onClickHandler = () => {
26+
onClickHandler();
27+
28+
if (onCloseMenuHandler) {
29+
onCloseMenuHandler();
30+
}
31+
};
32+
33+
return (
34+
<li>
35+
<Button
36+
id={id}
37+
className={classNames('drop-down-menu-base__item', { disabled, active }, className)}
38+
appearance="no-style"
39+
onClickHandler={_onClickHandler}
40+
disabled={disabled}
41+
>
42+
{children}
43+
</Button>
44+
</li>
45+
);
46+
};
3547

3648
DropDownMenuItem.displayName = 'DropDownMenuItem';
3749

src/stories/menu.stories.tsx

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import * as React from 'react';
22
import { storiesOf } from '@storybook/react';
3-
import { action } from '@storybook/addon-actions';
43
import {
54
withKnobs, text, boolean, selectV2,
65
} from '@storybook/addon-knobs/react';
@@ -47,19 +46,19 @@ stories.add('Context menu', withState({ showMenu: false })(withInfo(
4746
menuOffsetDirection={selectV2('Menu Offset Direction', offsetOptions, offsetDefaultValue)}
4847
>
4948
<ContextMenuItem
50-
onClickHandler={() => { store.set({ showMenu: false }); action('clicked 1'); }}
49+
onClickHandler={() => { store.set({ showMenu: false }); }}
5150
disabled={boolean('Disabled', false)}
5251
>
5352
{text('Text 1', 'Context menu item 1')}
5453
</ContextMenuItem>
5554
<ContextMenuItem
56-
onClickHandler={() => { store.set({ showMenu: false }); action('clicked 2'); }}
55+
onClickHandler={() => { store.set({ showMenu: false }); }}
5756
disabled={boolean('Disabled', false)}
5857
>
5958
{text('Text 2', 'Context menu item 2')}
6059
</ContextMenuItem>
6160
<ContextMenuItem
62-
onClickHandler={() => { store.set({ showMenu: false }); action('clicked 3'); }}
61+
onClickHandler={() => { store.set({ showMenu: false }); }}
6362
disabled={boolean('Disabled', false)}
6463
>
6564
{text('Text 3', 'Context menu item 3')}
@@ -71,16 +70,16 @@ stories.add('Drop down menu', withInfo(
7170
'Drop down menu',
7271
)(() => (
7372
<DropDownMenu activeLabel="10">
74-
<DropDownMenuItem active onClickHandler={action('clicked')}>
73+
<DropDownMenuItem active onClickHandler={() => {}}>
7574
10
7675
</DropDownMenuItem>
77-
<DropDownMenuItem active={false} onClickHandler={action('clicked')}>
76+
<DropDownMenuItem active={false} onClickHandler={() => {}}>
7877
20
7978
</DropDownMenuItem>
80-
<DropDownMenuItem active={false} onClickHandler={action('clicked')}>
79+
<DropDownMenuItem active={false} onClickHandler={() => {}}>
8180
40
8281
</DropDownMenuItem>
83-
<DropDownMenuItem active={false} onClickHandler={action('clicked')}>
82+
<DropDownMenuItem active={false} onClickHandler={() => {}}>
8483
80
8584
</DropDownMenuItem>
8685
</DropDownMenu>
@@ -90,12 +89,12 @@ stories.add('User account menu', withInfo(
9089
'User account menu',
9190
)(() => (
9291
<UserAccountMenu
93-
onSignOutClickHandler={action('clicked')}
92+
onSignOutClickHandler={() => {}}
9493
manageAccountURL={text('Account Management URL', 'https://account.ultimaker.com')}
9594
displayName="Test User"
9695
triggerWidth={text('Trigger width', null)}
9796
triggerHeight={text('Trigger height', null)}
9897
signedOut={boolean('Sign out', false)}
99-
onSignInClickHandler={action('clicked')}
98+
onSignInClickHandler={() => {}}
10099
/>
101100
)));

0 commit comments

Comments
 (0)