From 37fcaf163e5b9195a1cbe78aa5a660ad10a4ea86 Mon Sep 17 00:00:00 2001 From: Linda Paiste Date: Sat, 3 Jun 2023 18:14:41 -0500 Subject: [PATCH] Create sub-components of Nav --- client/common/ButtonOrLink.jsx | 48 ++ client/common/ButtonOrLink.test.jsx | 32 + client/components/Nav.jsx | 791 ++++-------------- client/components/Nav.unit.test.jsx | 16 +- client/components/Nav/NavBar.jsx | 109 +++ client/components/Nav/NavDropdownMenu.jsx | 48 ++ client/components/Nav/NavMenuItem.jsx | 43 + client/components/Nav/contexts.jsx | 10 + .../__snapshots__/Nav.unit.test.jsx.snap | 271 +++++- 9 files changed, 713 insertions(+), 655 deletions(-) create mode 100644 client/common/ButtonOrLink.jsx create mode 100644 client/common/ButtonOrLink.test.jsx create mode 100644 client/components/Nav/NavBar.jsx create mode 100644 client/components/Nav/NavDropdownMenu.jsx create mode 100644 client/components/Nav/NavMenuItem.jsx create mode 100644 client/components/Nav/contexts.jsx diff --git a/client/common/ButtonOrLink.jsx b/client/common/ButtonOrLink.jsx new file mode 100644 index 0000000000..f2c31e1c6e --- /dev/null +++ b/client/common/ButtonOrLink.jsx @@ -0,0 +1,48 @@ +import React from 'react'; +import { Link } from 'react-router'; +import PropTypes from 'prop-types'; + +/** + * Helper for switching between ; +}; + +/** + * Accepts all the props of an HTML or '); + fireEvent.click(button); + expect(clickHandler).toHaveBeenCalled(); + }); + + it('can render an external link', () => { + render(p5); + const link = screen.getByRole('link'); + expect(link).toBeInstanceOf(HTMLAnchorElement); + expect(link).toHaveAttribute('href', 'https://p5js.org'); + }); + + it('can render an internal link with react-router', () => { + render(About); + // TODO: how can this be tested? Needs a router provider? + }); +}); diff --git a/client/components/Nav.jsx b/client/components/Nav.jsx index 241be0149d..2320111bce 100644 --- a/client/components/Nav.jsx +++ b/client/components/Nav.jsx @@ -1,4 +1,3 @@ -import classNames from 'classnames'; import { sortBy } from 'lodash'; import PropTypes from 'prop-types'; import React from 'react'; @@ -21,82 +20,19 @@ import { getIsUserOwner } from '../modules/IDE/selectors/users'; import { selectSketchPath } from '../modules/IDE/selectors/project'; import CaretLeftIcon from '../images/left-arrow.svg'; -import TriangleIcon from '../images/down-filled-triangle.svg'; import LogoIcon from '../images/p5js-logo-small.svg'; +import NavDropdownMenu from './Nav/NavDropdownMenu'; +import NavMenuItem from './Nav/NavMenuItem'; +import NavBar from './Nav/NavBar'; class Nav extends React.PureComponent { constructor(props) { super(props); - this.state = { - dropdownOpen: 'none' - }; - this.handleFocus = this.handleFocus.bind(this); - this.handleBlur = this.handleBlur.bind(this); - this.clearHideTimeout = this.clearHideTimeout.bind(this); - this.handleClick = this.handleClick.bind(this); - this.handleClickOutside = this.handleClickOutside.bind(this); this.handleSave = this.handleSave.bind(this); this.handleNew = this.handleNew.bind(this); - this.handleDuplicate = this.handleDuplicate.bind(this); this.handleShare = this.handleShare.bind(this); this.handleDownload = this.handleDownload.bind(this); - this.handleFind = this.handleFind.bind(this); - this.handleAddFile = this.handleAddFile.bind(this); - this.handleAddFolder = this.handleAddFolder.bind(this); - this.handleRun = this.handleRun.bind(this); - this.handleReplace = this.handleReplace.bind(this); - this.handleStop = this.handleStop.bind(this); - this.handleStartAccessible = this.handleStartAccessible.bind(this); - this.handleStopAccessible = this.handleStopAccessible.bind(this); - this.handleKeyboardShortcuts = this.handleKeyboardShortcuts.bind(this); - this.handleLogout = this.handleLogout.bind(this); - this.toggleDropdownForFile = this.toggleDropdown.bind(this, 'file'); - this.handleFocusForFile = this.handleFocus.bind(this, 'file'); - this.setDropdownForNone = this.setDropdown.bind(this, 'none'); - this.toggleDropdownForEdit = this.toggleDropdown.bind(this, 'edit'); - this.handleFocusForEdit = this.handleFocus.bind(this, 'edit'); - this.toggleDropdownForSketch = this.toggleDropdown.bind(this, 'sketch'); - this.handleFocusForSketch = this.handleFocus.bind(this, 'sketch'); - this.toggleDropdownForHelp = this.toggleDropdown.bind(this, 'help'); - this.handleFocusForHelp = this.handleFocus.bind(this, 'help'); - this.toggleDropdownForAccount = this.toggleDropdown.bind(this, 'account'); - this.handleFocusForAccount = this.handleFocus.bind(this, 'account'); - this.toggleDropdownForLang = this.toggleDropdown.bind(this, 'lang'); - this.handleFocusForLang = this.handleFocus.bind(this, 'lang'); this.handleLangSelection = this.handleLangSelection.bind(this); - - this.closeDropDown = this.closeDropDown.bind(this); - } - - componentDidMount() { - document.addEventListener('mousedown', this.handleClick, false); - document.addEventListener('keydown', this.closeDropDown, false); - } - componentWillUnmount() { - document.removeEventListener('mousedown', this.handleClick, false); - document.removeEventListener('keydown', this.closeDropDown, false); - } - setDropdown(dropdown) { - this.setState({ - dropdownOpen: dropdown - }); - } - - closeDropDown(e) { - if (e.keyCode === 27) { - this.setDropdown('none'); - } - } - - handleClick(e) { - if (!this.node) { - return; - } - if (this.node && this.node.contains(e.target)) { - return; - } - - this.handleClickOutside(); } handleNew() { @@ -110,7 +46,6 @@ class Nav extends React.PureComponent { this.props.setToastText('Toast.OpenedNewSketch'); this.props.newProject(); } - this.setDropdown('none'); } handleSave() { @@ -119,75 +54,17 @@ class Nav extends React.PureComponent { } else { this.props.showErrorModal('forceAuthentication'); } - this.setDropdown('none'); - } - - handleFind() { - this.props.cmController.showFind(); - this.setDropdown('none'); - } - - handleReplace() { - this.props.cmController.showReplace(); - this.setDropdown('none'); - } - - handleAddFile() { - this.props.newFile(this.props.rootFile.id); - this.setDropdown('none'); - } - - handleAddFolder() { - this.props.newFolder(this.props.rootFile.id); - this.setDropdown('none'); - } - - handleRun() { - this.props.startSketch(); - this.setDropdown('none'); - } - - handleStop() { - this.props.stopSketch(); - this.setDropdown('none'); - } - - handleStartAccessible() { - this.props.setAllAccessibleOutput(true); - this.setDropdown('none'); - } - - handleStopAccessible() { - this.props.setAllAccessibleOutput(false); - this.setDropdown('none'); - } - - handleKeyboardShortcuts() { - this.props.showKeyboardShortcutModal(); - this.setDropdown('none'); } handleLangSelection(event) { this.props.setLanguage(event.target.value); this.props.showToast(1500); this.props.setToastText('Toast.LangChange'); - this.setDropdown('none'); - } - - handleLogout() { - this.props.logoutUser(); - this.setDropdown('none'); } handleDownload() { this.props.autosaveProject(); projectActions.exportProjectAsZip(this.props.project.id); - this.setDropdown('none'); - } - - handleDuplicate() { - this.props.cloneProject(); - this.setDropdown('none'); } handleShare() { @@ -197,44 +74,9 @@ class Nav extends React.PureComponent { this.props.project.name, username ); - this.setDropdown('none'); - } - - handleClickOutside() { - this.setState({ - dropdownOpen: 'none' - }); - } - - toggleDropdown(dropdown) { - if (this.state.dropdownOpen === 'none') { - this.setState({ - dropdownOpen: dropdown - }); - } else { - this.setState({ - dropdownOpen: 'none' - }); - } - } - - handleFocus(dropdown) { - this.clearHideTimeout(); - this.setDropdown(dropdown); - } - - clearHideTimeout() { - if (this.timer) { - clearTimeout(this.timer); - this.timer = null; - } } - handleBlur() { - this.timer = setTimeout(this.setDropdown.bind(this, 'none'), 10); - } - - renderDashboardMenu(navDropdownState) { + renderDashboardMenu() { return ( ); } - renderLanguageMenu(navDropdownState) { + renderLanguageMenu() { return ( - -
  • - -
      - {sortBy(availableLanguages).map((key) => ( -
    • - -
    • - ))} -
    -
  • -
    + + {sortBy(availableLanguages).map((key) => ( + + {languageKeyToLabel(key)} + + ))} + ); } - renderUnauthenticatedUserMenu(navDropdownState) { + renderUnauthenticatedUserMenu() { return (