Skip to content

Commit 9640f18

Browse files
author
Mat Brown
committed
Use createMenu for project picker
Ports the project picker (formerly project list) component to use createMenu, giving it proper click-outside behavior, etc. Required enhancing the `createMenu` factory to take an optional `isVisible` function, which takes in props and determines whether the menu should appear at all.
1 parent 5e2435c commit 9640f18

9 files changed

Lines changed: 78 additions & 94 deletions

File tree

src/components/ProjectPreview.jsx

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,12 @@
11
import React from 'react';
22
import PropTypes from 'prop-types';
3-
import classnames from 'classnames';
43
import moment from 'moment';
54

65
const MAX_LENGTH = 50;
76

8-
export default function ProjectPreview({
9-
isSelected,
10-
preview,
11-
project,
12-
onProjectSelected,
13-
}) {
7+
export default function ProjectPreview({preview, project}) {
148
return (
15-
<div
16-
className={classnames(
17-
'project-preview',
18-
'top-bar__menu-item',
19-
{'top-bar__menu-item_active': isSelected},
20-
)}
21-
key={project.projectKey}
22-
onClick={onProjectSelected}
23-
>
9+
<div>
2410
<div className="project-preview__label">
2511
{preview.slice(0, MAX_LENGTH)}
2612
</div>
@@ -32,8 +18,6 @@ export default function ProjectPreview({
3218
}
3319

3420
ProjectPreview.propTypes = {
35-
isSelected: PropTypes.bool.isRequired,
3621
preview: PropTypes.string.isRequired,
3722
project: PropTypes.object.isRequired,
38-
onProjectSelected: PropTypes.func.isRequired,
3923
};

src/components/TopBar/ProjectList.jsx

Lines changed: 0 additions & 30 deletions
This file was deleted.
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import map from 'lodash/map';
2+
import PropTypes from 'prop-types';
3+
import ProjectPreview from '../../containers/ProjectPreview';
4+
import ProjectPickerButton from './ProjectPickerButton';
5+
import createMenu from './createMenu';
6+
7+
const ProjectPicker = createMenu({
8+
name: 'projectPicker',
9+
10+
isVisible({currentProjectKey, projectKeys}) {
11+
return currentProjectKey && projectKeys.length > 1;
12+
},
13+
14+
mapPropsToItems({currentProjectKey, projectKeys}) {
15+
return map(projectKeys, projectKey => ({
16+
key: projectKey,
17+
isEnabled: projectKey === currentProjectKey,
18+
props: {projectKey},
19+
}));
20+
},
21+
})(ProjectPickerButton, ProjectPreview);
22+
23+
ProjectPicker.propTypes = {
24+
currentProjectKey: PropTypes.string,
25+
projectKeys: PropTypes.arrayOf(PropTypes.string).isRequired,
26+
};
27+
28+
ProjectPicker.defaultProps = {
29+
currentProjectKey: null,
30+
};
31+
32+
export default ProjectPicker;
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import React from 'react';
2+
import {t} from 'i18next';
3+
4+
export default function ProjectPickerButton() {
5+
return (
6+
<span>
7+
{t('top-bar.load-project')}
8+
{' '}
9+
<span className="u__icon">
10+
&#xf0d7;
11+
</span>
12+
</span>
13+
);
14+
}
15+
16+
ProjectPickerButton.propTypes = {};

src/components/TopBar/ProjectsButton.jsx

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

src/components/TopBar/createMenu.jsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import classnames from 'classnames';
22
import {connect} from 'react-redux';
3+
import constant from 'lodash/constant';
34
import onClickOutside from 'react-onclickoutside';
45
import partial from 'lodash/partial';
56
import preventClickthrough from 'react-prevent-clickthrough';
@@ -9,7 +10,11 @@ import React from 'react';
910
import {closeTopBarMenu, toggleTopBarMenu} from '../../actions';
1011
import {getOpenTopBarMenu} from '../../selectors';
1112

12-
export default function createMenu({mapPropsToItems, name}) {
13+
export default function createMenu({
14+
isVisible = constant(true),
15+
mapPropsToItems,
16+
name,
17+
}) {
1318
function mapStateToProps(state) {
1419
const isOpen = getOpenTopBarMenu(state) === name;
1520
return {
@@ -32,6 +37,10 @@ export default function createMenu({mapPropsToItems, name}) {
3237

3338
return function createMenuWithMappedProps(Label, Item) {
3439
function Menu(props) {
40+
if (!isVisible(props)) {
41+
return null;
42+
}
43+
3544
const {isOpen, onClickItem, onToggle} = props;
3645
const items = mapPropsToItems(props);
3746
const menu = isOpen ?

src/components/TopBar/index.jsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import CurrentUser from './CurrentUser';
88
import HamburgerMenuButton from './HamburgerMenuButton';
99
import LibraryPicker from './LibraryPicker';
1010
import NewProjectButton from './NewProjectButton';
11-
import ProjectsButton from './ProjectsButton';
11+
import ProjectPicker from './ProjectPicker';
1212
import SnapshotButton from './SnapshotButton';
1313
import TextSize from './TextSize';
1414

@@ -38,6 +38,7 @@ export default function TopBar({
3838
openMenu,
3939
projectKeys,
4040
validationState,
41+
onChangeCurrentProject,
4142
onClickMenu,
4243
onCloseMenu,
4344
onCreateNewProject,
@@ -73,10 +74,10 @@ export default function TopBar({
7374
isUserAuthenticated={isUserAuthenticated}
7475
onClick={onCreateNewProject}
7576
/>
76-
<ProjectsButton
77-
isOpen={openMenu === 'projectPicker'}
77+
<ProjectPicker
78+
currentProjectKey={currentProjectKey}
7879
projectKeys={projectKeys}
79-
onClick={partial(onClickMenu, 'projectPicker')}
80+
onClickItem={onChangeCurrentProject}
8081
/>
8182
<LibraryPicker
8283
enabledLibraries={enabledLibraries}
@@ -112,6 +113,7 @@ TopBar.propTypes = {
112113
openMenu: PropTypes.string,
113114
projectKeys: PropTypes.arrayOf(PropTypes.string).isRequired,
114115
validationState: PropTypes.string.isRequired,
116+
onChangeCurrentProject: PropTypes.func.isRequired,
115117
onClickMenu: PropTypes.func.isRequired,
116118
onCloseMenu: PropTypes.func.isRequired,
117119
onCreateNewProject: PropTypes.func.isRequired,

src/containers/TopBar.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
isUserTyping,
1919
} from '../selectors';
2020
import {
21+
changeCurrentProject,
2122
closeTopBarMenu,
2223
createProject,
2324
createSnapshot,
@@ -52,6 +53,10 @@ function mapStateToProps(state) {
5253

5354
function mapDispatchToProps(dispatch) {
5455
return {
56+
onChangeCurrentProject(projectKey) {
57+
dispatch(changeCurrentProject(projectKey));
58+
},
59+
5560
onClickMenu(menuKey) {
5661
dispatch(toggleTopBarMenu(menuKey));
5762
},

src/reducers/ui.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,13 @@ export default function ui(stateIn, action) {
4040

4141
switch (action.type) {
4242
case 'CHANGE_CURRENT_PROJECT':
43+
return state.
44+
set('workspace', DEFAULT_WORKSPACE).
45+
updateIn(
46+
['topBar', 'openMenu'],
47+
menu => menu === 'projectPicker' ? null : menu,
48+
);
49+
4350
case 'PROJECT_CREATED':
4451
return state.set('workspace', DEFAULT_WORKSPACE);
4552

0 commit comments

Comments
 (0)