Skip to content

Commit f040067

Browse files
authored
Merge pull request #2361 from dewanshDT/dewanshmobile/mobilenav
`MobileNav` component for smaller devices
2 parents f398e76 + cef29cf commit f040067

27 files changed

+1102
-221
lines changed

client/common/icons.jsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import DropdownArrow from '../images/down-filled-triangle.svg';
1313
import Preferences from '../images/preferences.svg';
1414
import Play from '../images/triangle-arrow-right.svg';
1515
import More from '../images/more.svg';
16+
import Editor from '../images/editor.svg';
17+
import Account from '../images/account.svg';
1618
import Code from '../images/code.svg';
1719
import Save from '../images/save.svg';
1820
import Terminal from '../images/terminal.svg';
@@ -83,6 +85,8 @@ export const GoogleIcon = withLabel(Google);
8385
export const PlusIcon = withLabel(Plus);
8486
export const CloseIcon = withLabel(Close);
8587
export const ExitIcon = withLabel(Exit);
88+
export const EditorIcon = withLabel(Editor);
89+
export const AccountIcon = withLabel(Account);
8690
export const DropdownArrowIcon = withLabel(DropdownArrow);
8791
export const PreferencesIcon = withLabel(Preferences);
8892
export const PlayIcon = withLabel(Play);

client/components/Nav/NavBar.jsx

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import React, {
88
} from 'react';
99
import { MenuOpenContext, NavBarContext } from './contexts';
1010

11-
function NavBar({ children }) {
11+
function NavBar({ children, className }) {
1212
const [dropdownOpen, setDropdownOpen] = useState('none');
1313

1414
const timerRef = useRef(null);
@@ -55,6 +55,15 @@ function NavBar({ children }) {
5555
timerRef.current = setTimeout(() => setDropdownOpen('none'), 10);
5656
}, [timerRef, setDropdownOpen]);
5757

58+
const toggleDropdownOpen = useCallback(
59+
(dropdown) => {
60+
setDropdownOpen((prevState) =>
61+
prevState === dropdown ? 'none' : dropdown
62+
);
63+
},
64+
[setDropdownOpen]
65+
);
66+
5867
const contextValue = useMemo(
5968
() => ({
6069
createDropdownHandlers: (dropdown) => ({
@@ -64,9 +73,7 @@ function NavBar({ children }) {
6473
);
6574
},
6675
onClick: () => {
67-
setDropdownOpen((prevState) =>
68-
prevState === 'none' ? dropdown : 'none'
69-
);
76+
toggleDropdownOpen(dropdown);
7077
},
7178
onBlur: handleBlur,
7279
onFocus: clearHideTimeout
@@ -80,15 +87,16 @@ function NavBar({ children }) {
8087
clearHideTimeout();
8188
setDropdownOpen(dropdown);
8289
}
83-
})
90+
}),
91+
toggleDropdownOpen
8492
}),
85-
[setDropdownOpen, clearHideTimeout, handleBlur]
93+
[setDropdownOpen, toggleDropdownOpen, clearHideTimeout, handleBlur]
8694
);
8795

8896
return (
8997
<NavBarContext.Provider value={contextValue}>
9098
<header>
91-
<nav className="nav" ref={nodeRef}>
99+
<nav className={className} ref={nodeRef}>
92100
<MenuOpenContext.Provider value={dropdownOpen}>
93101
{children}
94102
</MenuOpenContext.Provider>
@@ -99,11 +107,13 @@ function NavBar({ children }) {
99107
}
100108

101109
NavBar.propTypes = {
102-
children: PropTypes.node
110+
children: PropTypes.node,
111+
className: PropTypes.string
103112
};
104113

105114
NavBar.defaultProps = {
106-
children: null
115+
children: null,
116+
className: 'nav'
107117
};
108118

109119
export default NavBar;

client/components/Nav/NavDropdownMenu.jsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import React, { useContext, useMemo } from 'react';
44
import TriangleIcon from '../../images/down-filled-triangle.svg';
55
import { MenuOpenContext, NavBarContext, ParentMenuContext } from './contexts';
66

7-
function NavDropdownMenu({ id, title, children }) {
7+
export function useMenuProps(id) {
88
const activeMenu = useContext(MenuOpenContext);
99

1010
const isOpen = id === activeMenu;
@@ -16,6 +16,12 @@ function NavDropdownMenu({ id, title, children }) {
1616
id
1717
]);
1818

19+
return { isOpen, handlers };
20+
}
21+
22+
function NavDropdownMenu({ id, title, children }) {
23+
const { isOpen, handlers } = useMenuProps(id);
24+
1925
return (
2026
<li className={classNames('nav__item', isOpen && 'nav__item--open')}>
2127
<button {...handlers}>

client/components/Nav/NavMenuItem.jsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import React, { useContext, useMemo } from 'react';
33
import ButtonOrLink from '../../common/ButtonOrLink';
44
import { NavBarContext, ParentMenuContext } from './contexts';
55

6-
function NavMenuItem({ hideIf, ...rest }) {
6+
function NavMenuItem({ hideIf, className, ...rest }) {
77
const parent = useContext(ParentMenuContext);
88

99
const { createMenuItemHandlers } = useContext(NavBarContext);
@@ -18,7 +18,7 @@ function NavMenuItem({ hideIf, ...rest }) {
1818
}
1919

2020
return (
21-
<li className="nav__dropdown-item">
21+
<li className={className}>
2222
<ButtonOrLink {...rest} {...handlers} />
2323
</li>
2424
);
@@ -31,13 +31,15 @@ NavMenuItem.propTypes = {
3131
/**
3232
* Provides a way to deal with optional items.
3333
*/
34-
hideIf: PropTypes.bool
34+
hideIf: PropTypes.bool,
35+
className: PropTypes.string
3536
};
3637

3738
NavMenuItem.defaultProps = {
3839
onClick: null,
3940
value: null,
40-
hideIf: false
41+
hideIf: false,
42+
className: 'nav__dropdown-item'
4143
};
4244

4345
export default NavMenuItem;

client/components/Nav/contexts.jsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@ export const MenuOpenContext = createContext('none');
66

77
export const NavBarContext = createContext({
88
createDropdownHandlers: () => ({}),
9-
createMenuItemHandlers: () => ({})
9+
createMenuItemHandlers: () => ({}),
10+
toggleDropdownOpen: () => {}
1011
});

client/constants.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,6 @@ export const PROJECT_SAVE_SUCCESS = 'PROJECT_SAVE_SUCCESS';
3030
export const PROJECT_SAVE_FAIL = 'PROJECT_SAVE_FAIL';
3131
export const NEW_PROJECT = 'NEW_PROJECT';
3232
export const RESET_PROJECT = 'RESET_PROJECT';
33-
export const SHOW_EDIT_PROJECT_NAME = 'SHOW_EDIT_PROJECT_NAME';
34-
export const HIDE_EDIT_PROJECT_NAME = 'HIDE_EDIT_PROJECT_NAME';
3533

3634
export const SET_PROJECT = 'SET_PROJECT';
3735
export const SET_PROJECTS = 'SET_PROJECTS';

client/images/account.svg

Lines changed: 3 additions & 0 deletions
Loading

client/images/editor.svg

Lines changed: 4 additions & 0 deletions
Loading

client/images/more.svg

Lines changed: 3 additions & 3 deletions
Loading

client/modules/IDE/actions/project.js

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -351,18 +351,6 @@ export function cloneProject(project) {
351351
};
352352
}
353353

354-
export function showEditProjectName() {
355-
return {
356-
type: ActionTypes.SHOW_EDIT_PROJECT_NAME
357-
};
358-
}
359-
360-
export function hideEditProjectName() {
361-
return {
362-
type: ActionTypes.HIDE_EDIT_PROJECT_NAME
363-
};
364-
}
365-
366354
export function setProjectSavedTime(updatedAt) {
367355
return {
368356
type: ActionTypes.SET_PROJECT_SAVED_TIME,

client/modules/IDE/components/EditableInput.jsx

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,27 +11,36 @@ function EditableInput({
1111
emptyPlaceholder,
1212
InputComponent,
1313
inputProps,
14-
onChange
14+
onChange,
15+
disabled,
16+
'aria-label': ariaLabel
1517
}) {
1618
const [isEditing, setIsEditing] = React.useState(false);
1719
const [currentValue, setCurrentValue] = React.useState(value || '');
1820
const displayValue = currentValue || emptyPlaceholder;
1921
const hasValue = currentValue !== '';
2022
const classes = `editable-input editable-input--${
2123
isEditing ? 'is-editing' : 'is-not-editing'
22-
} editable-input--${hasValue ? 'has-value' : 'has-placeholder'}`;
23-
const inputRef = React.createRef();
24+
} editable-input--${hasValue ? 'has-value' : 'has-placeholder'} ${
25+
disabled ? 'editable-input--disabled' : ''
26+
}`;
27+
const inputRef = React.useRef();
2428
const { t } = useTranslation();
2529
React.useEffect(() => {
2630
if (isEditing) {
27-
inputRef.current.focus();
31+
inputRef.current?.focus();
2832
}
2933
}, [isEditing]);
3034

3135
function beginEditing() {
3236
setIsEditing(true);
3337
}
3438

39+
function cancelEditing() {
40+
setIsEditing(false);
41+
setCurrentValue(value);
42+
}
43+
3544
function doneEditing() {
3645
setIsEditing(false);
3746

@@ -51,6 +60,8 @@ function EditableInput({
5160
function checkForKeyAction(event) {
5261
if (event.key === 'Enter') {
5362
doneEditing();
63+
} else if (event.key === 'Escape' || event.key === 'Esc') {
64+
cancelEditing();
5465
}
5566
}
5667

@@ -59,7 +70,11 @@ function EditableInput({
5970
<button
6071
className="editable-input__label"
6172
onClick={beginEditing}
62-
aria-label={t('EditableInput.EditValue', { display: displayValue })}
73+
aria-label={
74+
ariaLabel ?? t('EditableInput.EditValue', { display: displayValue })
75+
}
76+
aria-hidden={isEditing}
77+
disabled={disabled}
6378
>
6479
<span>{displayValue}</span>
6580
<EditIcon
@@ -74,9 +89,10 @@ function EditableInput({
7489
type="text"
7590
{...inputProps}
7691
disabled={!isEditing}
92+
aria-hidden={!isEditing}
7793
onBlur={doneEditing}
7894
onChange={updateValue}
79-
onKeyPress={checkForKeyAction}
95+
onKeyDown={checkForKeyAction}
8096
ref={inputRef}
8197
value={currentValue}
8298
/>
@@ -89,7 +105,9 @@ EditableInput.defaultProps = {
89105
InputComponent: 'input',
90106
inputProps: {},
91107
validate: () => true,
92-
value: ''
108+
value: '',
109+
disabled: false,
110+
'aria-label': undefined
93111
};
94112

95113
EditableInput.propTypes = {
@@ -99,7 +117,9 @@ EditableInput.propTypes = {
99117
inputProps: PropTypes.object, // eslint-disable-line
100118
onChange: PropTypes.func.isRequired,
101119
validate: PropTypes.func,
102-
value: PropTypes.string
120+
value: PropTypes.string,
121+
disabled: PropTypes.bool,
122+
'aria-label': PropTypes.string
103123
};
104124

105125
export default EditableInput;

0 commit comments

Comments
 (0)