From b16dada82ed848b2e3f89f80147702bee21d49de Mon Sep 17 00:00:00 2001 From: Tristan Espinoza <tristan.m.espinoza@gmail.com> Date: Fri, 15 Nov 2024 16:22:23 -0800 Subject: [PATCH 01/39] fix: renamed files, components, imports, exports, contexts, and methods --- .../{Nav/NavBar.jsx => Menubar/Menubar.jsx} | 54 ++++---- .../MenubarItem.jsx} | 12 +- .../MenubarMenu.jsx} | 16 +-- .../components/{Nav => Menubar}/contexts.jsx | 6 +- .../IDE/components/Header/MobileNav.jsx | 10 +- client/modules/IDE/components/Header/Nav.jsx | 124 +++++++++--------- 6 files changed, 109 insertions(+), 113 deletions(-) rename client/components/{Nav/NavBar.jsx => Menubar/Menubar.jsx} (51%) rename client/components/{Nav/NavMenuItem.jsx => Menubar/MenubarItem.jsx} (73%) rename client/components/{Nav/NavDropdownMenu.jsx => Menubar/MenubarMenu.jsx} (75%) rename client/components/{Nav => Menubar}/contexts.jsx (62%) diff --git a/client/components/Nav/NavBar.jsx b/client/components/Menubar/Menubar.jsx similarity index 51% rename from client/components/Nav/NavBar.jsx rename to client/components/Menubar/Menubar.jsx index c8ae7c6377..ebd78b97d5 100644 --- a/client/components/Nav/NavBar.jsx +++ b/client/components/Menubar/Menubar.jsx @@ -1,16 +1,16 @@ import PropTypes from 'prop-types'; import React, { useCallback, useMemo, useRef, useState } from 'react'; import useModalClose from '../../common/useModalClose'; -import { MenuOpenContext, NavBarContext } from './contexts'; +import { MenuOpenContext, MenubarContext } from './contexts'; -function NavBar({ children, className }) { - const [dropdownOpen, setDropdownOpen] = useState('none'); +function Menubar({ children, className }) { + const [menuOpen, setMenuOpen] = useState('none'); const timerRef = useRef(null); const handleClose = useCallback(() => { - setDropdownOpen('none'); - }, [setDropdownOpen]); + setMenuOpen('none'); + }, [setMenuOpen]); const nodeRef = useModalClose(handleClose); @@ -22,71 +22,67 @@ function NavBar({ children, className }) { }, [timerRef]); const handleBlur = useCallback(() => { - timerRef.current = setTimeout(() => setDropdownOpen('none'), 10); - }, [timerRef, setDropdownOpen]); + timerRef.current = setTimeout(() => setMenuOpen('none'), 10); + }, [timerRef, setMenuOpen]); - const toggleDropdownOpen = useCallback( - (dropdown) => { - setDropdownOpen((prevState) => - prevState === dropdown ? 'none' : dropdown - ); + const toggleMenuOpen = useCallback( + (menu) => { + setMenuOpen((prevState) => (prevState === menu ? 'none' : menu)); }, - [setDropdownOpen] + [setMenuOpen] ); const contextValue = useMemo( () => ({ - createDropdownHandlers: (dropdown) => ({ + createMenuHandlers: (menu) => ({ onMouseOver: () => { - setDropdownOpen((prevState) => - prevState === 'none' ? 'none' : dropdown - ); + setMenuOpen((prevState) => (prevState === 'none' ? 'none' : menu)); }, onClick: () => { - toggleDropdownOpen(dropdown); + toggleMenuOpen(menu); }, onBlur: handleBlur, onFocus: clearHideTimeout }), - createMenuItemHandlers: (dropdown) => ({ + createMenuItemHandlers: (menu) => ({ onMouseUp: (e) => { if (e.button === 2) { return; } - setDropdownOpen('none'); + setMenuOpen('none'); }, onBlur: handleBlur, onFocus: () => { clearHideTimeout(); - setDropdownOpen(dropdown); + setMenuOpen(menu); } }), - toggleDropdownOpen + toggleMenuOpen }), - [setDropdownOpen, toggleDropdownOpen, clearHideTimeout, handleBlur] + [setMenuOpen, toggleMenuOpen, clearHideTimeout, handleBlur] ); return ( - <NavBarContext.Provider value={contextValue}> + <MenubarContext.Provider value={contextValue}> <header> <div className={className} ref={nodeRef}> - <MenuOpenContext.Provider value={dropdownOpen}> + <MenuOpenContext.Provider value={menuOpen}> {children} </MenuOpenContext.Provider> </div> </header> - </NavBarContext.Provider> + </MenubarContext.Provider> ); } -NavBar.propTypes = { +Menubar.propTypes = { children: PropTypes.node, className: PropTypes.string }; -NavBar.defaultProps = { +Menubar.defaultProps = { children: null, className: 'nav' }; -export default NavBar; +export default Menubar; diff --git a/client/components/Nav/NavMenuItem.jsx b/client/components/Menubar/MenubarItem.jsx similarity index 73% rename from client/components/Nav/NavMenuItem.jsx rename to client/components/Menubar/MenubarItem.jsx index 09436e43ee..8c08aa9c03 100644 --- a/client/components/Nav/NavMenuItem.jsx +++ b/client/components/Menubar/MenubarItem.jsx @@ -1,12 +1,12 @@ import PropTypes from 'prop-types'; import React, { useContext, useMemo } from 'react'; import ButtonOrLink from '../../common/ButtonOrLink'; -import { NavBarContext, ParentMenuContext } from './contexts'; +import { MenubarContext, ParentMenuContext } from './contexts'; -function NavMenuItem({ hideIf, className, ...rest }) { +function MenubarItem({ hideIf, className, ...rest }) { const parent = useContext(ParentMenuContext); - const { createMenuItemHandlers } = useContext(NavBarContext); + const { createMenuItemHandlers } = useContext(MenubarContext); const handlers = useMemo(() => createMenuItemHandlers(parent), [ createMenuItemHandlers, @@ -24,7 +24,7 @@ function NavMenuItem({ hideIf, className, ...rest }) { ); } -NavMenuItem.propTypes = { +MenubarItem.propTypes = { ...ButtonOrLink.propTypes, onClick: PropTypes.func, value: PropTypes.string, @@ -35,11 +35,11 @@ NavMenuItem.propTypes = { className: PropTypes.string }; -NavMenuItem.defaultProps = { +MenubarItem.defaultProps = { onClick: null, value: null, hideIf: false, className: 'nav__dropdown-item' }; -export default NavMenuItem; +export default MenubarItem; diff --git a/client/components/Nav/NavDropdownMenu.jsx b/client/components/Menubar/MenubarMenu.jsx similarity index 75% rename from client/components/Nav/NavDropdownMenu.jsx rename to client/components/Menubar/MenubarMenu.jsx index d2c5744c46..4dd47d3fb5 100644 --- a/client/components/Nav/NavDropdownMenu.jsx +++ b/client/components/Menubar/MenubarMenu.jsx @@ -2,24 +2,24 @@ import classNames from 'classnames'; import PropTypes from 'prop-types'; import React, { useContext, useMemo } from 'react'; import TriangleIcon from '../../images/down-filled-triangle.svg'; -import { MenuOpenContext, NavBarContext, ParentMenuContext } from './contexts'; +import { MenuOpenContext, MenubarContext, ParentMenuContext } from './contexts'; export function useMenuProps(id) { const activeMenu = useContext(MenuOpenContext); const isOpen = id === activeMenu; - const { createDropdownHandlers } = useContext(NavBarContext); + const { createMenuHandlers } = useContext(MenubarContext); - const handlers = useMemo(() => createDropdownHandlers(id), [ - createDropdownHandlers, + const handlers = useMemo(() => createMenuHandlers(id), [ + createMenuHandlers, id ]); return { isOpen, handlers }; } -function NavDropdownMenu({ id, title, children }) { +function MenubarMenu({ id, title, children }) { const { isOpen, handlers } = useMenuProps(id); return ( @@ -46,14 +46,14 @@ function NavDropdownMenu({ id, title, children }) { ); } -NavDropdownMenu.propTypes = { +MenubarMenu.propTypes = { id: PropTypes.string.isRequired, title: PropTypes.node.isRequired, children: PropTypes.node }; -NavDropdownMenu.defaultProps = { +MenubarMenu.defaultProps = { children: null }; -export default NavDropdownMenu; +export default MenubarMenu; diff --git a/client/components/Nav/contexts.jsx b/client/components/Menubar/contexts.jsx similarity index 62% rename from client/components/Nav/contexts.jsx rename to client/components/Menubar/contexts.jsx index 896d7283f4..ab3bb9ffcf 100644 --- a/client/components/Nav/contexts.jsx +++ b/client/components/Menubar/contexts.jsx @@ -4,8 +4,8 @@ export const ParentMenuContext = createContext('none'); export const MenuOpenContext = createContext('none'); -export const NavBarContext = createContext({ - createDropdownHandlers: () => ({}), +export const MenubarContext = createContext({ + createMenuHandlers: () => ({}), createMenuItemHandlers: () => ({}), - toggleDropdownOpen: () => {} + toggleMenuOpen: () => {} }); diff --git a/client/modules/IDE/components/Header/MobileNav.jsx b/client/modules/IDE/components/Header/MobileNav.jsx index 37fa16bed3..c1fd718a8f 100644 --- a/client/modules/IDE/components/Header/MobileNav.jsx +++ b/client/modules/IDE/components/Header/MobileNav.jsx @@ -5,10 +5,10 @@ import { useTranslation } from 'react-i18next'; import { Link } from 'react-router-dom'; import { sortBy } from 'lodash'; import classNames from 'classnames'; -import { ParentMenuContext } from '../../../../components/Nav/contexts'; -import NavBar from '../../../../components/Nav/NavBar'; -import { useMenuProps } from '../../../../components/Nav/NavDropdownMenu'; -import NavMenuItem from '../../../../components/Nav/NavMenuItem'; +import { ParentMenuContext } from '../../../../components/Menubar/contexts'; +import Menubar from '../../../../components/Menubar/Menubar'; +import { useMenuProps } from '../../../../components/Menubar/MenubarMenu'; +import NavMenuItem from '../../../../components/Menubar/MenubarItem'; import { prop, remSize } from '../../../../theme'; import AsteriskIcon from '../../../../images/p5-asterisk.svg'; import IconButton from '../../../../common/IconButton'; @@ -36,7 +36,7 @@ import Overlay from '../../../App/components/Overlay'; import ProjectName from './ProjectName'; import CollectionCreate from '../../../User/components/CollectionCreate'; -const Nav = styled(NavBar)` +const Nav = styled(Menubar)` background: ${prop('MobilePanel.default.background')}; color: ${prop('primaryTextColor')}; padding: ${remSize(8)} 0; diff --git a/client/modules/IDE/components/Header/Nav.jsx b/client/modules/IDE/components/Header/Nav.jsx index 3492c4388a..dc6b0c6b71 100644 --- a/client/modules/IDE/components/Header/Nav.jsx +++ b/client/modules/IDE/components/Header/Nav.jsx @@ -4,13 +4,13 @@ import { sortBy } from 'lodash'; import { Link } from 'react-router-dom'; import PropTypes from 'prop-types'; import { useTranslation } from 'react-i18next'; -import NavDropdownMenu from '../../../../components/Nav/NavDropdownMenu'; -import NavMenuItem from '../../../../components/Nav/NavMenuItem'; +import MenubarMenu from '../../../../components/Menubar/MenubarMenu'; +import MenubarItem from '../../../../components/Menubar/MenubarItem'; import { availableLanguages, languageKeyToLabel } from '../../../../i18n'; import getConfig from '../../../../utils/getConfig'; import { showToast } from '../../actions/toast'; import { setLanguage } from '../../actions/preferences'; -import NavBar from '../../../../components/Nav/NavBar'; +import Menubar from '../../../../components/Menubar/Menubar'; import CaretLeftIcon from '../../../../images/left-arrow.svg'; import LogoIcon from '../../../../images/p5js-logo-small.svg'; import { selectRootFile } from '../../selectors/files'; @@ -37,10 +37,10 @@ const Nav = ({ layout }) => { return isMobile ? ( <MobileNav /> ) : ( - <NavBar> + <Menubar> <LeftLayout layout={layout} /> <UserMenu /> - </NavBar> + </Menubar> ); }; @@ -159,9 +159,9 @@ const ProjectMenu = () => { </a> )} </li> - <NavDropdownMenu id="file" title={t('Nav.File.Title')}> - <NavMenuItem onClick={newSketch}>{t('Nav.File.New')}</NavMenuItem> - <NavMenuItem + <MenubarMenu id="file" title={t('Nav.File.Title')}> + <MenubarItem onClick={newSketch}>{t('Nav.File.New')}</MenubarItem> + <MenubarItem hideIf={ !getConfig('LOGIN_ENABLED') || (project?.owner && !isUserOwner) } @@ -169,26 +169,26 @@ const ProjectMenu = () => { > {t('Common.Save')} <span className="nav__keyboard-shortcut">{metaKeyName}+S</span> - </NavMenuItem> - <NavMenuItem + </MenubarItem> + <MenubarItem hideIf={isUnsaved || !user.authenticated} onClick={() => dispatch(cloneProject())} > {t('Nav.File.Duplicate')} - </NavMenuItem> - <NavMenuItem hideIf={isUnsaved} onClick={shareSketch}> + </MenubarItem> + <MenubarItem hideIf={isUnsaved} onClick={shareSketch}> {t('Nav.File.Share')} - </NavMenuItem> - <NavMenuItem hideIf={isUnsaved} onClick={downloadSketch}> + </MenubarItem> + <MenubarItem hideIf={isUnsaved} onClick={downloadSketch}> {t('Nav.File.Download')} - </NavMenuItem> - <NavMenuItem + </MenubarItem> + <MenubarItem hideIf={!user.authenticated} href={`/${user.username}/sketches`} > {t('Nav.File.Open')} - </NavMenuItem> - <NavMenuItem + </MenubarItem> + <MenubarItem hideIf={ !getConfig('UI_COLLECTIONS_ENABLED') || !user.authenticated || @@ -197,56 +197,56 @@ const ProjectMenu = () => { href={`/${user.username}/sketches/${project?.id}/add-to-collection`} > {t('Nav.File.AddToCollection')} - </NavMenuItem> - <NavMenuItem + </MenubarItem> + <MenubarItem hideIf={!getConfig('EXAMPLES_ENABLED')} href="/p5/sketches" > {t('Nav.File.Examples')} - </NavMenuItem> - </NavDropdownMenu> - <NavDropdownMenu id="edit" title={t('Nav.Edit.Title')}> - <NavMenuItem onClick={cmRef.current?.tidyCode}> + </MenubarItem> + </MenubarMenu> + <MenubarMenu id="edit" title={t('Nav.Edit.Title')}> + <MenubarItem onClick={cmRef.current?.tidyCode}> {t('Nav.Edit.TidyCode')} <span className="nav__keyboard-shortcut">{metaKeyName}+Shift+F</span> - </NavMenuItem> - <NavMenuItem onClick={cmRef.current?.showFind}> + </MenubarItem> + <MenubarItem onClick={cmRef.current?.showFind}> {t('Nav.Edit.Find')} <span className="nav__keyboard-shortcut">{metaKeyName}+F</span> - </NavMenuItem> - <NavMenuItem onClick={cmRef.current?.showReplace}> + </MenubarItem> + <MenubarItem onClick={cmRef.current?.showReplace}> {t('Nav.Edit.Replace')} <span className="nav__keyboard-shortcut">{replaceCommand}</span> - </NavMenuItem> - </NavDropdownMenu> - <NavDropdownMenu id="sketch" title={t('Nav.Sketch.Title')}> - <NavMenuItem onClick={() => dispatch(newFile(rootFile.id))}> + </MenubarItem> + </MenubarMenu> + <MenubarMenu id="sketch" title={t('Nav.Sketch.Title')}> + <MenubarItem onClick={() => dispatch(newFile(rootFile.id))}> {t('Nav.Sketch.AddFile')} <span className="nav__keyboard-shortcut">{newFileCommand}</span> - </NavMenuItem> - <NavMenuItem onClick={() => dispatch(newFolder(rootFile.id))}> + </MenubarItem> + <MenubarItem onClick={() => dispatch(newFolder(rootFile.id))}> {t('Nav.Sketch.AddFolder')} - </NavMenuItem> - <NavMenuItem onClick={() => dispatch(startSketch())}> + </MenubarItem> + <MenubarItem onClick={() => dispatch(startSketch())}> {t('Nav.Sketch.Run')} <span className="nav__keyboard-shortcut">{metaKeyName}+Enter</span> - </NavMenuItem> - <NavMenuItem onClick={() => dispatch(stopSketch())}> + </MenubarItem> + <MenubarItem onClick={() => dispatch(stopSketch())}> {t('Nav.Sketch.Stop')} <span className="nav__keyboard-shortcut"> Shift+{metaKeyName}+Enter </span> - </NavMenuItem> - </NavDropdownMenu> - <NavDropdownMenu id="help" title={t('Nav.Help.Title')}> - <NavMenuItem onClick={() => dispatch(showKeyboardShortcutModal())}> + </MenubarItem> + </MenubarMenu> + <MenubarMenu id="help" title={t('Nav.Help.Title')}> + <MenubarItem onClick={() => dispatch(showKeyboardShortcutModal())}> {t('Nav.Help.KeyboardShortcuts')} - </NavMenuItem> - <NavMenuItem href="https://p5js.org/reference/"> + </MenubarItem> + <MenubarItem href="https://p5js.org/reference/"> {t('Nav.Help.Reference')} - </NavMenuItem> - <NavMenuItem href="/about">{t('Nav.Help.About')}</NavMenuItem> - </NavDropdownMenu> + </MenubarItem> + <MenubarItem href="/about">{t('Nav.Help.About')}</MenubarItem> + </MenubarMenu> </ul> ); }; @@ -261,14 +261,14 @@ const LanguageMenu = () => { } return ( - <NavDropdownMenu id="lang" title={languageKeyToLabel(language)}> + <MenubarMenu id="lang" title={languageKeyToLabel(language)}> {sortBy(availableLanguages).map((key) => ( // eslint-disable-next-line react/jsx-no-bind - <NavMenuItem key={key} value={key} onClick={handleLangSelection}> + <MenubarItem key={key} value={key} onClick={handleLangSelection}> {languageKeyToLabel(key)} - </NavMenuItem> + </MenubarItem> ))} - </NavDropdownMenu> + </MenubarMenu> ); }; @@ -305,7 +305,7 @@ const AuthenticatedUserMenu = () => { return ( <ul className="nav__items-right" title="user-menu" role="navigation"> {getConfig('TRANSLATIONS_ENABLED') && <LanguageMenu />} - <NavDropdownMenu + <MenubarMenu id="account" title={ <span> @@ -313,23 +313,23 @@ const AuthenticatedUserMenu = () => { </span> } > - <NavMenuItem href={`/${username}/sketches`}> + <MenubarItem href={`/${username}/sketches`}> {t('Nav.Auth.MySketches')} - </NavMenuItem> - <NavMenuItem + </MenubarItem> + <MenubarItem href={`/${username}/collections`} hideIf={!getConfig('UI_COLLECTIONS_ENABLED')} > {t('Nav.Auth.MyCollections')} - </NavMenuItem> - <NavMenuItem href={`/${username}/assets`}> + </MenubarItem> + <MenubarItem href={`/${username}/assets`}> {t('Nav.Auth.MyAssets')} - </NavMenuItem> - <NavMenuItem href="/account">{t('Preferences.Settings')}</NavMenuItem> - <NavMenuItem onClick={() => dispatch(logoutUser())}> + </MenubarItem> + <MenubarItem href="/account">{t('Preferences.Settings')}</MenubarItem> + <MenubarItem onClick={() => dispatch(logoutUser())}> {t('Nav.Auth.LogOut')} - </NavMenuItem> - </NavDropdownMenu> + </MenubarItem> + </MenubarMenu> </ul> ); }; From 0f9cf65c9c050ebed8c86bd9070f41ac1b29a3f0 Mon Sep 17 00:00:00 2001 From: Tristan Espinoza <tristan.m.espinoza@gmail.com> Date: Mon, 18 Nov 2024 14:05:14 -0800 Subject: [PATCH 02/39] refactor: separated UserMenu from the menubar component, wrapped both in a header container, removed navigation role from UserMenu --- client/components/Menubar/Menubar.jsx | 12 +++++------- client/modules/IDE/components/Header/Nav.jsx | 16 ++++++++++------ 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/client/components/Menubar/Menubar.jsx b/client/components/Menubar/Menubar.jsx index ebd78b97d5..b806246515 100644 --- a/client/components/Menubar/Menubar.jsx +++ b/client/components/Menubar/Menubar.jsx @@ -64,13 +64,11 @@ function Menubar({ children, className }) { return ( <MenubarContext.Provider value={contextValue}> - <header> - <div className={className} ref={nodeRef}> - <MenuOpenContext.Provider value={menuOpen}> - {children} - </MenuOpenContext.Provider> - </div> - </header> + <div className={className} ref={nodeRef}> + <MenuOpenContext.Provider value={menuOpen}> + {children} + </MenuOpenContext.Provider> + </div> </MenubarContext.Provider> ); } diff --git a/client/modules/IDE/components/Header/Nav.jsx b/client/modules/IDE/components/Header/Nav.jsx index dc6b0c6b71..0e5f2cdac7 100644 --- a/client/modules/IDE/components/Header/Nav.jsx +++ b/client/modules/IDE/components/Header/Nav.jsx @@ -37,10 +37,14 @@ const Nav = ({ layout }) => { return isMobile ? ( <MobileNav /> ) : ( - <Menubar> - <LeftLayout layout={layout} /> - <UserMenu /> - </Menubar> + <> + <header className="nav__header"> + <Menubar> + <LeftLayout layout={layout} /> + </Menubar> + <UserMenu /> + </header> + </> ); }; @@ -275,7 +279,7 @@ const LanguageMenu = () => { const UnauthenticatedUserMenu = () => { const { t } = useTranslation(); return ( - <ul className="nav__items-right" title="user-menu" role="navigation"> + <ul className="nav__items-right" title="user-menu"> {getConfig('TRANSLATIONS_ENABLED') && <LanguageMenu />} <li className="nav__item"> <Link to="/login" className="nav__auth-button" role="menuitem"> @@ -303,7 +307,7 @@ const AuthenticatedUserMenu = () => { const dispatch = useDispatch(); return ( - <ul className="nav__items-right" title="user-menu" role="navigation"> + <ul className="nav__items-right" title="user-menu"> {getConfig('TRANSLATIONS_ENABLED') && <LanguageMenu />} <MenubarMenu id="account" From ab818ad0cb5ed1ad8c3f39cb3fa3ae62897a47db Mon Sep 17 00:00:00 2001 From: Tristan Espinoza <tristan.m.espinoza@gmail.com> Date: Mon, 18 Nov 2024 14:07:06 -0800 Subject: [PATCH 03/39] style: updated header and menubar styles --- client/styles/components/_nav.scss | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/client/styles/components/_nav.scss b/client/styles/components/_nav.scss index 1f4a7fc364..4ddc96b327 100644 --- a/client/styles/components/_nav.scss +++ b/client/styles/components/_nav.scss @@ -6,23 +6,31 @@ flex-direction: row; justify-content: space-between; - @include themify() { - border-bottom: 1px dashed map-get($theme-map, 'nav-border-color'); - } - & button { padding: 0; } } +.nav__header { + display: flex; + flex-direction: row; + justify-content: space-between; + height: 100%; + align-items: center; + + @include themify() { + border-bottom: 1px dashed map-get($theme-map, 'nav-border-color'); + } + // padding-left: #{math.div(20, $base-font-size)}rem; +} + .nav__items-left, .nav__items-right { list-style: none; display: flex; flex-direction: row; - justify-content: flex-end; - height: 100%; align-items: center; + height: 100%; } .preview-nav__editor-svg { From 246597f9442dfc70d83a80125d740fb05c6a503e Mon Sep 17 00:00:00 2001 From: Tristan Espinoza <tristan.m.espinoza@gmail.com> Date: Mon, 18 Nov 2024 15:52:26 -0800 Subject: [PATCH 04/39] refactor: moved LanguageMenu into left items --- client/modules/IDE/components/Header/Nav.jsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/client/modules/IDE/components/Header/Nav.jsx b/client/modules/IDE/components/Header/Nav.jsx index 0e5f2cdac7..6721209761 100644 --- a/client/modules/IDE/components/Header/Nav.jsx +++ b/client/modules/IDE/components/Header/Nav.jsx @@ -251,6 +251,7 @@ const ProjectMenu = () => { </MenubarItem> <MenubarItem href="/about">{t('Nav.Help.About')}</MenubarItem> </MenubarMenu> + {getConfig('TRANSLATIONS_ENABLED') && <LanguageMenu />} </ul> ); }; @@ -280,7 +281,6 @@ const UnauthenticatedUserMenu = () => { const { t } = useTranslation(); return ( <ul className="nav__items-right" title="user-menu"> - {getConfig('TRANSLATIONS_ENABLED') && <LanguageMenu />} <li className="nav__item"> <Link to="/login" className="nav__auth-button" role="menuitem"> <span className="nav__item-header" title="Login"> @@ -308,7 +308,6 @@ const AuthenticatedUserMenu = () => { return ( <ul className="nav__items-right" title="user-menu"> - {getConfig('TRANSLATIONS_ENABLED') && <LanguageMenu />} <MenubarMenu id="account" title={ From 9f3096cf7f905e0582cfb6e16f831f59e94155cd Mon Sep 17 00:00:00 2001 From: Tristan Espinoza <tristan.m.espinoza@gmail.com> Date: Mon, 18 Nov 2024 16:06:30 -0800 Subject: [PATCH 05/39] refactor: split new MenubarMenu component into Trigger, List, and Menu components --- client/components/Menubar/MenubarMenu.jsx | 65 ++++++++++++++++------- 1 file changed, 47 insertions(+), 18 deletions(-) diff --git a/client/components/Menubar/MenubarMenu.jsx b/client/components/Menubar/MenubarMenu.jsx index 4dd47d3fb5..5453e6c8d8 100644 --- a/client/components/Menubar/MenubarMenu.jsx +++ b/client/components/Menubar/MenubarMenu.jsx @@ -19,29 +19,58 @@ export function useMenuProps(id) { return { isOpen, handlers }; } +function MenubarTrigger({ id, title, ...props }) { + const { isOpen, handlers } = useMenuProps(id); + + return ( + <button + {...handlers} + {...props} + role="menuitem" + aria-haspopup="menu" + aria-expanded={isOpen} + > + <span className="nav__item-header">{title}</span> + <TriangleIcon + className="nav__item-header-triangle" + focusable="false" + aria-hidden="true" + /> + </button> + ); +} + +MenubarTrigger.propTypes = { + id: PropTypes.string.isRequired, + title: PropTypes.node.isRequired +}; + +function MenubarList({ id, children }) { + return ( + <ul className="nav__dropdown" role="menu"> + <ParentMenuContext.Provider value={id}> + {children} + </ParentMenuContext.Provider> + </ul> + ); +} + +MenubarList.propTypes = { + id: PropTypes.string.isRequired, + children: PropTypes.node +}; + +MenubarList.defaultProps = { + children: null +}; + function MenubarMenu({ id, title, children }) { const { isOpen, handlers } = useMenuProps(id); return ( <li className={classNames('nav__item', isOpen && 'nav__item--open')}> - <button - {...handlers} - role="menuitem" - aria-haspopup="menu" - aria-expanded={isOpen} - > - <span className="nav__item-header">{title}</span> - <TriangleIcon - className="nav__item-header-triangle" - focusable="false" - aria-hidden="true" - /> - </button> - <ul className="nav__dropdown" role="menu"> - <ParentMenuContext.Provider value={id}> - {children} - </ParentMenuContext.Provider> - </ul> + <MenubarTrigger id={id} title={title} /> + <MenubarList id={id}>{children}</MenubarList> </li> ); } From 7c3b896800f5828cf99eaa02565b760736887417 Mon Sep 17 00:00:00 2001 From: Tristan Espinoza <tristan.m.espinoza@gmail.com> Date: Mon, 18 Nov 2024 16:08:52 -0800 Subject: [PATCH 06/39] test: updated snapshots --- .../__snapshots__/Nav.unit.test.jsx.snap | 732 +++++++++--------- 1 file changed, 366 insertions(+), 366 deletions(-) diff --git a/client/modules/IDE/components/Header/__snapshots__/Nav.unit.test.jsx.snap b/client/modules/IDE/components/Header/__snapshots__/Nav.unit.test.jsx.snap index af56a1a418..d9140653c1 100644 --- a/client/modules/IDE/components/Header/__snapshots__/Nav.unit.test.jsx.snap +++ b/client/modules/IDE/components/Header/__snapshots__/Nav.unit.test.jsx.snap @@ -2,7 +2,9 @@ exports[`Nav renders dashboard version for desktop 1`] = ` <DocumentFragment> - <header> + <header + class="nav__header" + > <div class="nav" > @@ -265,71 +267,54 @@ exports[`Nav renders dashboard version for mobile 1`] = ` color: #FFF; } -<header> +<div + class="c0" + > <div - class="c0" + class="c1" > - <div - class="c1" - > - <test-file-stub /> - </div> - <div - class="c2" - > - <h1> - <span - class="editable-input editable-input--is-not-editing editable-input--has-value " + <test-file-stub /> + </div> + <div + class="c2" + > + <h1> + <span + class="editable-input editable-input--is-not-editing editable-input--has-value " + > + <button + aria-hidden="false" + aria-label="Edit sketch name" + class="editable-input__label" > - <button - aria-hidden="false" - aria-label="Edit sketch name" - class="editable-input__label" - > - <span> - Test project name - </span> - <test-file-stub - aria-hidden="true" - classname="editable-input__icon" - focusable="false" - /> - </button> - <input + <span> + Test project name + </span> + <test-file-stub aria-hidden="true" - aria-label="New sketch name" - class="editable-input__input" - disabled="" - maxlength="128" - type="text" - value="Test project name" - /> - </span> - </h1> - </div> - <div - class="c3" - > - <div> - <a - href="/login" - > - <button - class="c4 c5" - display="inline" + classname="editable-input__icon" focusable="false" - kind="primary" - type="button" - > - <test-file-stub - aria-hidden="true" - classname="sc-fujyAs cSTVlM" - focusable="false" - /> - </button> - </a> - </div> - <div> + /> + </button> + <input + aria-hidden="true" + aria-label="New sketch name" + class="editable-input__input" + disabled="" + maxlength="128" + type="text" + value="Test project name" + /> + </span> + </h1> + </div> + <div + class="c3" + > + <div> + <a + href="/login" + > <button class="c4 c5" display="inline" @@ -339,152 +324,169 @@ exports[`Nav renders dashboard version for mobile 1`] = ` > <test-file-stub aria-hidden="true" - classname="sc-iqAclL iOZiVo" + classname="sc-fujyAs cSTVlM" focusable="false" /> </button> - <ul - class="" + </a> + </div> + <div> + <button + class="c4 c5" + display="inline" + focusable="false" + kind="primary" + type="button" + > + <test-file-stub + aria-hidden="true" + classname="sc-iqAclL iOZiVo" + focusable="false" + /> + </button> + <ul + class="" + > + <b> + File + </b> + <li + class="nav__dropdown-item" > - <b> - File - </b> - <li - class="nav__dropdown-item" + <button + role="menuitem" > - <button - role="menuitem" - > - New - </button> - </li> - <li - class="nav__dropdown-item" + New + </button> + </li> + <li + class="nav__dropdown-item" + > + <button + role="menuitem" > - <button - role="menuitem" - > - Save - </button> - </li> - <li - class="nav__dropdown-item" + Save + </button> + </li> + <li + class="nav__dropdown-item" + > + <a + href="/p5/sketches" + role="menuitem" > - <a - href="/p5/sketches" - role="menuitem" - > - Examples - </a> - </li> - <b> - Edit - </b> - <li - class="nav__dropdown-item" + Examples + </a> + </li> + <b> + Edit + </b> + <li + class="nav__dropdown-item" + > + <button + role="menuitem" > - <button - role="menuitem" - > - Tidy Code - </button> - </li> - <li - class="nav__dropdown-item" + Tidy Code + </button> + </li> + <li + class="nav__dropdown-item" + > + <button + role="menuitem" > - <button - role="menuitem" - > - Find - </button> - </li> - <b> - Sketch - </b> - <li - class="nav__dropdown-item" + Find + </button> + </li> + <b> + Sketch + </b> + <li + class="nav__dropdown-item" + > + <button + role="menuitem" > - <button - role="menuitem" - > - Add File - </button> - </li> - <li - class="nav__dropdown-item" + Add File + </button> + </li> + <li + class="nav__dropdown-item" + > + <button + role="menuitem" > - <button - role="menuitem" - > - Add Folder - </button> - </li> - <b> - Settings - </b> - <li - class="nav__dropdown-item" + Add Folder + </button> + </li> + <b> + Settings + </b> + <li + class="nav__dropdown-item" + > + <button + role="menuitem" > - <button - role="menuitem" - > - Preferences - </button> - </li> - <li - class="nav__dropdown-item" + Preferences + </button> + </li> + <li + class="nav__dropdown-item" + > + <button + role="menuitem" > - <button - role="menuitem" - > - Language - </button> - </li> - <b> - Help - </b> - <li - class="nav__dropdown-item" + Language + </button> + </li> + <b> + Help + </b> + <li + class="nav__dropdown-item" + > + <button + role="menuitem" > - <button - role="menuitem" - > - Keyboard Shortcuts - </button> - </li> - <li - class="nav__dropdown-item" + Keyboard Shortcuts + </button> + </li> + <li + class="nav__dropdown-item" + > + <a + href="https://p5js.org/reference/" + rel="noopener noreferrer" + role="menuitem" + target="_blank" > - <a - href="https://p5js.org/reference/" - rel="noopener noreferrer" - role="menuitem" - target="_blank" - > - Reference - </a> - </li> - <li - class="nav__dropdown-item" + Reference + </a> + </li> + <li + class="nav__dropdown-item" + > + <a + href="/about" + role="menuitem" > - <a - href="/about" - role="menuitem" - > - About - </a> - </li> - </ul> - </div> + About + </a> + </li> + </ul> </div> </div> - </header> + </div> </DocumentFragment> `; exports[`Nav renders editor version for desktop 1`] = ` <DocumentFragment> - <header> + <header + class="nav__header" + > <div class="nav" > @@ -966,71 +968,54 @@ exports[`Nav renders editor version for mobile 1`] = ` color: #FFF; } -<header> +<div + class="c0" + > <div - class="c0" + class="c1" > - <div - class="c1" - > - <test-file-stub /> - </div> - <div - class="c2" - > - <h1> - <span - class="editable-input editable-input--is-not-editing editable-input--has-value " + <test-file-stub /> + </div> + <div + class="c2" + > + <h1> + <span + class="editable-input editable-input--is-not-editing editable-input--has-value " + > + <button + aria-hidden="false" + aria-label="Edit sketch name" + class="editable-input__label" > - <button - aria-hidden="false" - aria-label="Edit sketch name" - class="editable-input__label" - > - <span> - Test project name - </span> - <test-file-stub - aria-hidden="true" - classname="editable-input__icon" - focusable="false" - /> - </button> - <input + <span> + Test project name + </span> + <test-file-stub aria-hidden="true" - aria-label="New sketch name" - class="editable-input__input" - disabled="" - maxlength="128" - type="text" - value="Test project name" - /> - </span> - </h1> - </div> - <div - class="c3" - > - <div> - <a - href="/login" - > - <button - class="c4 c5" - display="inline" + classname="editable-input__icon" focusable="false" - kind="primary" - type="button" - > - <test-file-stub - aria-hidden="true" - classname="sc-fujyAs cSTVlM" - focusable="false" - /> - </button> - </a> - </div> - <div> + /> + </button> + <input + aria-hidden="true" + aria-label="New sketch name" + class="editable-input__input" + disabled="" + maxlength="128" + type="text" + value="Test project name" + /> + </span> + </h1> + </div> + <div + class="c3" + > + <div> + <a + href="/login" + > <button class="c4 c5" display="inline" @@ -1040,145 +1025,160 @@ exports[`Nav renders editor version for mobile 1`] = ` > <test-file-stub aria-hidden="true" - classname="sc-iqAclL iOZiVo" + classname="sc-fujyAs cSTVlM" focusable="false" /> </button> - <ul - class="" + </a> + </div> + <div> + <button + class="c4 c5" + display="inline" + focusable="false" + kind="primary" + type="button" + > + <test-file-stub + aria-hidden="true" + classname="sc-iqAclL iOZiVo" + focusable="false" + /> + </button> + <ul + class="" + > + <b> + File + </b> + <li + class="nav__dropdown-item" > - <b> - File - </b> - <li - class="nav__dropdown-item" + <button + role="menuitem" > - <button - role="menuitem" - > - New - </button> - </li> - <li - class="nav__dropdown-item" + New + </button> + </li> + <li + class="nav__dropdown-item" + > + <button + role="menuitem" > - <button - role="menuitem" - > - Save - </button> - </li> - <li - class="nav__dropdown-item" + Save + </button> + </li> + <li + class="nav__dropdown-item" + > + <a + href="/p5/sketches" + role="menuitem" > - <a - href="/p5/sketches" - role="menuitem" - > - Examples - </a> - </li> - <b> - Edit - </b> - <li - class="nav__dropdown-item" + Examples + </a> + </li> + <b> + Edit + </b> + <li + class="nav__dropdown-item" + > + <button + role="menuitem" > - <button - role="menuitem" - > - Tidy Code - </button> - </li> - <li - class="nav__dropdown-item" + Tidy Code + </button> + </li> + <li + class="nav__dropdown-item" + > + <button + role="menuitem" > - <button - role="menuitem" - > - Find - </button> - </li> - <b> - Sketch - </b> - <li - class="nav__dropdown-item" + Find + </button> + </li> + <b> + Sketch + </b> + <li + class="nav__dropdown-item" + > + <button + role="menuitem" > - <button - role="menuitem" - > - Add File - </button> - </li> - <li - class="nav__dropdown-item" + Add File + </button> + </li> + <li + class="nav__dropdown-item" + > + <button + role="menuitem" > - <button - role="menuitem" - > - Add Folder - </button> - </li> - <b> - Settings - </b> - <li - class="nav__dropdown-item" + Add Folder + </button> + </li> + <b> + Settings + </b> + <li + class="nav__dropdown-item" + > + <button + role="menuitem" > - <button - role="menuitem" - > - Preferences - </button> - </li> - <li - class="nav__dropdown-item" + Preferences + </button> + </li> + <li + class="nav__dropdown-item" + > + <button + role="menuitem" > - <button - role="menuitem" - > - Language - </button> - </li> - <b> - Help - </b> - <li - class="nav__dropdown-item" + Language + </button> + </li> + <b> + Help + </b> + <li + class="nav__dropdown-item" + > + <button + role="menuitem" > - <button - role="menuitem" - > - Keyboard Shortcuts - </button> - </li> - <li - class="nav__dropdown-item" + Keyboard Shortcuts + </button> + </li> + <li + class="nav__dropdown-item" + > + <a + href="https://p5js.org/reference/" + rel="noopener noreferrer" + role="menuitem" + target="_blank" > - <a - href="https://p5js.org/reference/" - rel="noopener noreferrer" - role="menuitem" - target="_blank" - > - Reference - </a> - </li> - <li - class="nav__dropdown-item" + Reference + </a> + </li> + <li + class="nav__dropdown-item" + > + <a + href="/about" + role="menuitem" > - <a - href="/about" - role="menuitem" - > - About - </a> - </li> - </ul> - </div> + About + </a> + </li> + </ul> </div> </div> - </header> + </div> </DocumentFragment> `; From b2eb033a463be90db1aba6daaa503d3e32094dfc Mon Sep 17 00:00:00 2001 From: Tristan Espinoza <tristan.m.espinoza@gmail.com> Date: Tue, 19 Nov 2024 10:06:35 -0800 Subject: [PATCH 07/39] refactor: removed menuitem roles from sign up and login, added role=presentation to or --- client/modules/IDE/components/Header/Nav.jsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/client/modules/IDE/components/Header/Nav.jsx b/client/modules/IDE/components/Header/Nav.jsx index 6721209761..6ddc38bda2 100644 --- a/client/modules/IDE/components/Header/Nav.jsx +++ b/client/modules/IDE/components/Header/Nav.jsx @@ -282,15 +282,17 @@ const UnauthenticatedUserMenu = () => { return ( <ul className="nav__items-right" title="user-menu"> <li className="nav__item"> - <Link to="/login" className="nav__auth-button" role="menuitem"> + <Link to="/login" className="nav__auth-button"> <span className="nav__item-header" title="Login"> {t('Nav.Login')} </span> </Link> </li> - <li className="nav__item-or">{t('Nav.LoginOr')}</li> + <li className="nav__item-or" role="presentation"> + {t('Nav.LoginOr')} + </li> <li className="nav__item"> - <Link to="/signup" className="nav__auth-button" role="menuitem"> + <Link to="/signup" className="nav__auth-button"> <span className="nav__item-header" title="SignUp"> {t('Nav.SignUp')} </span> From 06e915277cbb3471cb9c2523c2853c43a0b6637c Mon Sep 17 00:00:00 2001 From: Tristan Espinoza <tristan.m.espinoza@gmail.com> Date: Tue, 19 Nov 2024 10:08:02 -0800 Subject: [PATCH 08/39] fix: lint fixes --- client/components/Menubar/MenubarMenu.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/components/Menubar/MenubarMenu.jsx b/client/components/Menubar/MenubarMenu.jsx index 5453e6c8d8..67c7eb91e3 100644 --- a/client/components/Menubar/MenubarMenu.jsx +++ b/client/components/Menubar/MenubarMenu.jsx @@ -65,7 +65,7 @@ MenubarList.defaultProps = { }; function MenubarMenu({ id, title, children }) { - const { isOpen, handlers } = useMenuProps(id); + const { isOpen } = useMenuProps(id); return ( <li className={classNames('nav__item', isOpen && 'nav__item--open')}> From 905ac20605f183b91779c638e59a59fc757219c8 Mon Sep 17 00:00:00 2001 From: Tristan Espinoza <tristan.m.espinoza@gmail.com> Date: Tue, 19 Nov 2024 10:18:19 -0800 Subject: [PATCH 09/39] chore: added separators between components within MenubarMenu --- client/components/Menubar/MenubarMenu.jsx | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/client/components/Menubar/MenubarMenu.jsx b/client/components/Menubar/MenubarMenu.jsx index 67c7eb91e3..34192b237b 100644 --- a/client/components/Menubar/MenubarMenu.jsx +++ b/client/components/Menubar/MenubarMenu.jsx @@ -19,6 +19,10 @@ export function useMenuProps(id) { return { isOpen, handlers }; } +/* ------------------------------------------------------------------------------------------------- + * MenubarTrigger + * -----------------------------------------------------------------------------------------------*/ + function MenubarTrigger({ id, title, ...props }) { const { isOpen, handlers } = useMenuProps(id); @@ -45,6 +49,10 @@ MenubarTrigger.propTypes = { title: PropTypes.node.isRequired }; +/* ------------------------------------------------------------------------------------------------- + * MenubarList + * -----------------------------------------------------------------------------------------------*/ + function MenubarList({ id, children }) { return ( <ul className="nav__dropdown" role="menu"> @@ -64,6 +72,10 @@ MenubarList.defaultProps = { children: null }; +/* ------------------------------------------------------------------------------------------------- + * MenubarMenu + * -----------------------------------------------------------------------------------------------*/ + function MenubarMenu({ id, title, children }) { const { isOpen } = useMenuProps(id); From b8a45d5ba22f14f961e0ae7938606e6e8140fdf9 Mon Sep 17 00:00:00 2001 From: Pratyush Raj <pratyushraj@AGLS-MLT-536.local> Date: Sat, 2 Nov 2024 19:42:32 +0530 Subject: [PATCH 10/39] Fix#3241 Icon Alligment issue in Password Field --- client/styles/components/_forms.scss | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/styles/components/_forms.scss b/client/styles/components/_forms.scss index d1237935cc..cf2d8df31f 100644 --- a/client/styles/components/_forms.scss +++ b/client/styles/components/_forms.scss @@ -75,13 +75,15 @@ .form__field__password { position: relative; + display: flex; + align-items: center; } .form__eye__icon { font-size: 28px; position: absolute; right: 0px; - top: 4px; + display: flex; vertical-align: middle; } From dbc98dd28c6fb0916434102dd360c4b858457dfc Mon Sep 17 00:00:00 2001 From: pratyushsawan <dev@pratyushsawan.co.in> Date: Sat, 23 Nov 2024 21:42:57 +0530 Subject: [PATCH 11/39] added some fixes on the eye-svg --- client/styles/components/_forms.scss | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/client/styles/components/_forms.scss b/client/styles/components/_forms.scss index cf2d8df31f..daf5fa0d08 100644 --- a/client/styles/components/_forms.scss +++ b/client/styles/components/_forms.scss @@ -80,11 +80,13 @@ } .form__eye__icon { - font-size: 28px; + font-size: #{math.div(30, $base-font-size)}rem; position: absolute; right: 0px; - display: flex; - vertical-align: middle; + + & svg { + transform: translateY(10%); + } } From 72daf4d1ded685bf38613efa8836e8a9b7231b6b Mon Sep 17 00:00:00 2001 From: raclim <raclim@nyu.edu> Date: Tue, 26 Nov 2024 12:25:51 -0500 Subject: [PATCH 12/39] missing dispatch import --- client/modules/IDE/components/ConsoleInput.jsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/modules/IDE/components/ConsoleInput.jsx b/client/modules/IDE/components/ConsoleInput.jsx index c026d29d4b..daac76839c 100644 --- a/client/modules/IDE/components/ConsoleInput.jsx +++ b/client/modules/IDE/components/ConsoleInput.jsx @@ -1,5 +1,6 @@ import PropTypes from 'prop-types'; import React, { useRef, useEffect, useState } from 'react'; +import { useDispatch } from 'react-redux'; import CodeMirror from 'codemirror'; import { Encode } from 'console-feed'; @@ -15,6 +16,7 @@ function ConsoleInput({ theme, fontSize }) { const [commandCursor, setCommandCursor] = useState(-1); const codemirrorContainer = useRef(null); const cmInstance = useRef(null); + const dispatch = useDispatch(); useEffect(() => { cmInstance.current = CodeMirror(codemirrorContainer.current, { @@ -45,7 +47,7 @@ function ConsoleInput({ theme, fontSize }) { payload: { source: 'console', messages } }); - dispatchConsoleEvent(consoleEvent); + dispatch(dispatchConsoleEvent(consoleEvent)); cm.setValue(''); setCommandHistory([value, ...commandHistory]); setCommandCursor(-1); From e5e2ec12dbea69bc79d93c3b152adbe54169d141 Mon Sep 17 00:00:00 2001 From: raclim <raclim@nyu.edu> Date: Wed, 27 Nov 2024 17:04:40 -0500 Subject: [PATCH 13/39] wrap actions with dispatch --- client/modules/IDE/components/FileNode.jsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/client/modules/IDE/components/FileNode.jsx b/client/modules/IDE/components/FileNode.jsx index e589fb3cff..04d7223bdb 100644 --- a/client/modules/IDE/components/FileNode.jsx +++ b/client/modules/IDE/components/FileNode.jsx @@ -1,7 +1,7 @@ import PropTypes from 'prop-types'; import classNames from 'classnames'; import React, { useState, useRef } from 'react'; -import { connect } from 'react-redux'; +import { connect, useDispatch } from 'react-redux'; import { useTranslation } from 'react-i18next'; import * as IDEActions from '../actions/ide'; @@ -87,6 +87,7 @@ const FileNode = ({ const [isEditingName, setIsEditingName] = useState(false); const [isDeleting, setIsDeleting] = useState(false); const [updatedName, setUpdatedName] = useState(name); + const dispatch = useDispatch(); const { t } = useTranslation(); const fileNameInput = useRef(null); @@ -122,17 +123,17 @@ const FileNode = ({ }; const handleClickAddFile = () => { - newFile(id); + dispatch(newFile(id)); setTimeout(() => hideFileOptions(), 0); }; const handleClickAddFolder = () => { - newFolder(id); + dispatch(newFolder(id)); setTimeout(() => hideFileOptions(), 0); }; const handleClickUploadFile = () => { - openUploadFileModal(id); + dispatch(openUploadFileModal(id)); setTimeout(hideFileOptions, 0); }; From f122ec62d0b5a337d46578c2596f0a84a951c5e0 Mon Sep 17 00:00:00 2001 From: raclim <raclim@nyu.edu> Date: Wed, 27 Nov 2024 18:46:21 -0500 Subject: [PATCH 14/39] update test to add useDispatch --- .../IDE/components/FileNode.unit.test.jsx | 64 ++++++++++++++----- 1 file changed, 48 insertions(+), 16 deletions(-) diff --git a/client/modules/IDE/components/FileNode.unit.test.jsx b/client/modules/IDE/components/FileNode.unit.test.jsx index 8676a817d8..86d3504626 100644 --- a/client/modules/IDE/components/FileNode.unit.test.jsx +++ b/client/modules/IDE/components/FileNode.unit.test.jsx @@ -1,4 +1,7 @@ import React from 'react'; +import { Provider } from 'react-redux'; +import configureStore from 'redux-mock-store'; +import { useDispatch } from 'react-redux'; import { fireEvent, @@ -9,7 +12,19 @@ import { } from '../../../test-utils'; import { FileNode } from './FileNode'; +jest.mock('react-redux', () => ({ + ...jest.requireActual('react-redux'), + useDispatch: jest.fn() +})); + describe('<FileNode />', () => { + const mockDispatch = jest.fn(); + const mockStore = configureStore([]); + + beforeEach(() => { + useDispatch.mockReturnValue(mockDispatch); + }); + const changeName = (newFileName) => { const renameButton = screen.getByText(/Rename/i); fireEvent.click(renameButton); @@ -25,6 +40,19 @@ describe('<FileNode />', () => { }; const renderFileNode = (fileType, extraProps = {}) => { + const initialState = { + files: [ + { + id: '0', + name: fileType === 'folder' ? 'afolder' : 'test.jsx', + fileType + } + ], + user: { authenticated: false } + }; + + const store = mockStore(initialState); + const props = { ...extraProps, id: '0', @@ -45,24 +73,28 @@ describe('<FileNode />', () => { setProjectName: jest.fn() }; - render(<FileNode {...props} />); + render( + <Provider store={store}> + <FileNode {...props} /> + </Provider> + ); - return props; + return { store, props }; }; describe('fileType: file', () => { it('cannot change to an empty name', async () => { - const props = renderFileNode('file'); + const { props } = renderFileNode('file'); changeName(''); - await waitFor(() => expect(props.updateFileName).not.toHaveBeenCalled()); + await waitFor(() => expect(mockDispatch).not.toHaveBeenCalled()); await expectFileNameToBe(props.name); }); it('can change to a valid filename', async () => { const newName = 'newname.jsx'; - const props = renderFileNode('file'); + const { props } = renderFileNode('file'); changeName(newName); @@ -74,11 +106,11 @@ describe('<FileNode />', () => { it('must have an extension', async () => { const newName = 'newname'; - const props = renderFileNode('file'); + const { props } = renderFileNode('file'); changeName(newName); - await waitFor(() => expect(props.updateFileName).not.toHaveBeenCalled()); + await waitFor(() => expect(mockDispatch).not.toHaveBeenCalled()); await expectFileNameToBe(props.name); }); @@ -87,7 +119,7 @@ describe('<FileNode />', () => { window.confirm = mockConfirm; const newName = 'newname.gif'; - const props = renderFileNode('file'); + const { props } = renderFileNode('file'); changeName(newName); @@ -95,33 +127,33 @@ describe('<FileNode />', () => { await waitFor(() => expect(props.updateFileName).toHaveBeenCalledWith(props.id, newName) ); - await expectFileNameToBe(props.name); + await expectFileNameToBe(newName); }); it('cannot be just an extension', async () => { const newName = '.jsx'; - const props = renderFileNode('file'); + const { props } = renderFileNode('file'); changeName(newName); - await waitFor(() => expect(props.updateFileName).not.toHaveBeenCalled()); + await waitFor(() => expect(mockDispatch).not.toHaveBeenCalled()); await expectFileNameToBe(props.name); }); }); describe('fileType: folder', () => { it('cannot change to an empty name', async () => { - const props = renderFileNode('folder'); + const { props } = renderFileNode('folder'); changeName(''); - await waitFor(() => expect(props.updateFileName).not.toHaveBeenCalled()); + await waitFor(() => expect(mockDispatch).not.toHaveBeenCalled()); await expectFileNameToBe(props.name); }); it('can change to another name', async () => { const newName = 'foldername'; - const props = renderFileNode('folder'); + const { props } = renderFileNode('folder'); changeName(newName); @@ -133,11 +165,11 @@ describe('<FileNode />', () => { it('cannot have a file extension', async () => { const newName = 'foldername.jsx'; - const props = renderFileNode('folder'); + const { props } = renderFileNode('folder'); changeName(newName); - await waitFor(() => expect(props.updateFileName).not.toHaveBeenCalled()); + await waitFor(() => expect(mockDispatch).not.toHaveBeenCalled()); await expectFileNameToBe(props.name); }); }); From 5c8a91249baf29b96ff20833b277107e21e79db4 Mon Sep 17 00:00:00 2001 From: raclim <raclim@nyu.edu> Date: Thu, 28 Nov 2024 10:06:18 -0500 Subject: [PATCH 15/39] update dispatch and add selectors --- client/modules/IDE/components/FileNode.jsx | 132 +++++---------------- 1 file changed, 31 insertions(+), 101 deletions(-) diff --git a/client/modules/IDE/components/FileNode.jsx b/client/modules/IDE/components/FileNode.jsx index 04d7223bdb..cd6caa3255 100644 --- a/client/modules/IDE/components/FileNode.jsx +++ b/client/modules/IDE/components/FileNode.jsx @@ -1,43 +1,17 @@ import PropTypes from 'prop-types'; import classNames from 'classnames'; import React, { useState, useRef } from 'react'; -import { connect, useDispatch } from 'react-redux'; +import { useDispatch, useSelector } from 'react-redux'; import { useTranslation } from 'react-i18next'; import * as IDEActions from '../actions/ide'; import * as FileActions from '../actions/files'; +import parseFileName from '../utils/parseFileName'; import DownArrowIcon from '../../../images/down-filled-triangle.svg'; import FolderRightIcon from '../../../images/triangle-arrow-right.svg'; import FolderDownIcon from '../../../images/triangle-arrow-down.svg'; import FileTypeIcon from './FileTypeIcon'; -function parseFileName(name) { - const nameArray = name.split('.'); - if (nameArray.length > 1) { - const extension = `.${nameArray[nameArray.length - 1]}`; - const baseName = nameArray.slice(0, -1).join('.'); - const firstLetter = baseName[0]; - const lastLetter = baseName[baseName.length - 1]; - const middleText = baseName.slice(1, -1); - return { - baseName, - firstLetter, - lastLetter, - middleText, - extension - }; - } - const firstLetter = name[0]; - const lastLetter = name[name.length - 1]; - const middleText = name.slice(1, -1); - return { - baseName: name, - firstLetter, - lastLetter, - middleText - }; -} - function FileName({ name }) { const { baseName, @@ -62,41 +36,35 @@ FileName.propTypes = { name: PropTypes.string.isRequired }; -const FileNode = ({ - id, - parentId, - children, - name, - fileType, - isSelectedFile, - isFolderClosed, - setSelectedFile, - deleteFile, - updateFileName, - resetSelectedFile, - newFile, - newFolder, - showFolderChildren, - hideFolderChildren, - canEdit, - openUploadFileModal, - authenticated, - onClickFile -}) => { +const FileNode = ({ id, canEdit, onClickFile }) => { + const dispatch = useDispatch(); + const { t } = useTranslation(); + + const fileNode = + useSelector((state) => state.files.find((file) => file.id === id)) || {}; + const authenticated = useSelector((state) => state.user.authenticated); + + const { + name = '', + parentId = null, + children = [], + fileType = 'file', + isSelectedFile = false, + isFolderClosed = false + } = fileNode; + const [isOptionsOpen, setIsOptionsOpen] = useState(false); const [isEditingName, setIsEditingName] = useState(false); const [isDeleting, setIsDeleting] = useState(false); const [updatedName, setUpdatedName] = useState(name); - const dispatch = useDispatch(); - const { t } = useTranslation(); const fileNameInput = useRef(null); const fileOptionsRef = useRef(null); const handleFileClick = (event) => { event.stopPropagation(); if (name !== 'root' && !isDeleting) { - setSelectedFile(id); + dispatch(IDEActions.setSelectedFile(id)); } if (onClickFile) { onClickFile(); @@ -123,17 +91,17 @@ const FileNode = ({ }; const handleClickAddFile = () => { - dispatch(newFile(id)); + dispatch(IDEActions.newFile(id)); setTimeout(() => hideFileOptions(), 0); }; const handleClickAddFolder = () => { - dispatch(newFolder(id)); + dispatch(IDEActions.newFolder(id)); setTimeout(() => hideFileOptions(), 0); }; const handleClickUploadFile = () => { - dispatch(openUploadFileModal(id)); + dispatch(IDEActions.openUploadFileModal(id)); setTimeout(hideFileOptions, 0); }; @@ -142,8 +110,8 @@ const FileNode = ({ if (window.confirm(prompt)) { setIsDeleting(true); - resetSelectedFile(id); - setTimeout(() => deleteFile(id, parentId), 100); + dispatch(IDEActions.resetSelectedFile(id)); + setTimeout(() => dispatch(FileActions.deleteFile(id, parentId), 100)); } }; @@ -159,7 +127,7 @@ const FileNode = ({ const saveUpdatedFileName = () => { if (updatedName !== name) { - updateFileName(id, updatedName); + dispatch(FileActions.updateFileName(id, updatedName)); } }; @@ -244,7 +212,7 @@ const FileNode = ({ <div className="sidebar__file-item--folder"> <button className="sidebar__file-item-closed" - onClick={() => showFolderChildren(id)} + onClick={() => dispatch(FileActions.showFolderChildren(id))} aria-label={t('FileNode.OpenFolderARIA')} title={t('FileNode.OpenFolderARIA')} > @@ -256,7 +224,7 @@ const FileNode = ({ </button> <button className="sidebar__file-item-open" - onClick={() => hideFolderChildren(id)} + onClick={() => dispatch(FileActions.hideFolderChildren(id))} aria-label={t('FileNode.CloseFolderARIA')} title={t('FileNode.CloseFolderARIA')} > @@ -354,7 +322,7 @@ const FileNode = ({ <ul className="file-item__children"> {children.map((childId) => ( <li key={childId}> - <ConnectedFileNode + <FileNode id={childId} parentId={id} canEdit={canEdit} @@ -370,50 +338,12 @@ const FileNode = ({ FileNode.propTypes = { id: PropTypes.string.isRequired, - parentId: PropTypes.string, - children: PropTypes.arrayOf(PropTypes.string.isRequired).isRequired, - name: PropTypes.string.isRequired, - fileType: PropTypes.string.isRequired, - isSelectedFile: PropTypes.bool, - isFolderClosed: PropTypes.bool, - setSelectedFile: PropTypes.func.isRequired, - deleteFile: PropTypes.func.isRequired, - updateFileName: PropTypes.func.isRequired, - resetSelectedFile: PropTypes.func.isRequired, - newFile: PropTypes.func.isRequired, - newFolder: PropTypes.func.isRequired, - showFolderChildren: PropTypes.func.isRequired, - hideFolderChildren: PropTypes.func.isRequired, canEdit: PropTypes.bool.isRequired, - openUploadFileModal: PropTypes.func.isRequired, - authenticated: PropTypes.bool.isRequired, onClickFile: PropTypes.func }; FileNode.defaultProps = { - onClickFile: null, - parentId: '0', - isSelectedFile: false, - isFolderClosed: false + onClickFile: null }; -function mapStateToProps(state, ownProps) { - // this is a hack, state is updated before ownProps - const fileNode = state.files.find((file) => file.id === ownProps.id) || { - name: 'test', - fileType: 'file' - }; - return Object.assign({}, fileNode, { - authenticated: state.user.authenticated - }); -} - -const mapDispatchToProps = { ...FileActions, ...IDEActions }; - -const ConnectedFileNode = connect( - mapStateToProps, - mapDispatchToProps -)(FileNode); - -export { FileNode }; -export default ConnectedFileNode; +export default FileNode; From 2177976d9f480185c3ac44cd2f3278252caf83fa Mon Sep 17 00:00:00 2001 From: raclim <raclim@nyu.edu> Date: Thu, 28 Nov 2024 10:06:42 -0500 Subject: [PATCH 16/39] create parseFileName util --- client/modules/IDE/utils/parseFileName.js | 28 +++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 client/modules/IDE/utils/parseFileName.js diff --git a/client/modules/IDE/utils/parseFileName.js b/client/modules/IDE/utils/parseFileName.js new file mode 100644 index 0000000000..1bde7be0f6 --- /dev/null +++ b/client/modules/IDE/utils/parseFileName.js @@ -0,0 +1,28 @@ +function parseFileName(name) { + const nameArray = name.split('.'); + if (nameArray.length > 1) { + const extension = `.${nameArray[nameArray.length - 1]}`; + const baseName = nameArray.slice(0, -1).join('.'); + const firstLetter = baseName[0]; + const lastLetter = baseName[baseName.length - 1]; + const middleText = baseName.slice(1, -1); + return { + baseName, + firstLetter, + lastLetter, + middleText, + extension + }; + } + const firstLetter = name[0]; + const lastLetter = name[name.length - 1]; + const middleText = name.slice(1, -1); + return { + baseName: name, + firstLetter, + lastLetter, + middleText + }; +} + +export default parseFileName; From 0e9a96712716fca8c5d29510bd7cfc2d80a752eb Mon Sep 17 00:00:00 2001 From: raclim <raclim@nyu.edu> Date: Thu, 28 Nov 2024 10:07:04 -0500 Subject: [PATCH 17/39] update FileNode tests --- .../IDE/components/FileNode.unit.test.jsx | 78 +++++++++---------- 1 file changed, 35 insertions(+), 43 deletions(-) diff --git a/client/modules/IDE/components/FileNode.unit.test.jsx b/client/modules/IDE/components/FileNode.unit.test.jsx index 86d3504626..4f75d9a272 100644 --- a/client/modules/IDE/components/FileNode.unit.test.jsx +++ b/client/modules/IDE/components/FileNode.unit.test.jsx @@ -2,6 +2,7 @@ import React from 'react'; import { Provider } from 'react-redux'; import configureStore from 'redux-mock-store'; import { useDispatch } from 'react-redux'; +import * as FileActions from '../actions/files'; import { fireEvent, @@ -10,19 +11,25 @@ import { waitFor, within } from '../../../test-utils'; -import { FileNode } from './FileNode'; +import FileNode from './FileNode'; jest.mock('react-redux', () => ({ ...jest.requireActual('react-redux'), useDispatch: jest.fn() })); +jest.mock('../actions/files', () => ({ + updateFileName: jest.fn() +})); + +const mockStore = configureStore([]); + describe('<FileNode />', () => { const mockDispatch = jest.fn(); - const mockStore = configureStore([]); beforeEach(() => { useDispatch.mockReturnValue(mockDispatch); + jest.clearAllMocks(); }); const changeName = (newFileName) => { @@ -39,79 +46,64 @@ describe('<FileNode />', () => { await waitFor(() => within(name).queryByText(expectedName)); }; - const renderFileNode = (fileType, extraProps = {}) => { + const renderFileNode = (fileType, extraState = {}) => { const initialState = { files: [ { id: '0', name: fileType === 'folder' ? 'afolder' : 'test.jsx', - fileType + fileType, + parentId: 'root', + children: [], + isSelectedFile: false, + isFolderClosed: false } ], - user: { authenticated: false } + user: { authenticated: false }, + ...extraState }; const store = mockStore(initialState); - const props = { - ...extraProps, - id: '0', - name: fileType === 'folder' ? 'afolder' : 'test.jsx', - fileType, - canEdit: true, - children: [], - authenticated: false, - setSelectedFile: jest.fn(), - deleteFile: jest.fn(), - updateFileName: jest.fn(), - resetSelectedFile: jest.fn(), - newFile: jest.fn(), - newFolder: jest.fn(), - showFolderChildren: jest.fn(), - hideFolderChildren: jest.fn(), - openUploadFileModal: jest.fn(), - setProjectName: jest.fn() - }; - render( <Provider store={store}> - <FileNode {...props} /> + <FileNode id="0" canEdit /> </Provider> ); - return { store, props }; + return { store }; }; describe('fileType: file', () => { it('cannot change to an empty name', async () => { - const { props } = renderFileNode('file'); + renderFileNode('file'); changeName(''); await waitFor(() => expect(mockDispatch).not.toHaveBeenCalled()); - await expectFileNameToBe(props.name); + await expectFileNameToBe('test.jsx'); }); it('can change to a valid filename', async () => { const newName = 'newname.jsx'; - const { props } = renderFileNode('file'); + renderFileNode('file'); changeName(newName); await waitFor(() => - expect(props.updateFileName).toHaveBeenCalledWith(props.id, newName) + expect(FileActions.updateFileName).toHaveBeenCalledWith('0', newName) ); await expectFileNameToBe(newName); }); it('must have an extension', async () => { const newName = 'newname'; - const { props } = renderFileNode('file'); + renderFileNode('file'); changeName(newName); await waitFor(() => expect(mockDispatch).not.toHaveBeenCalled()); - await expectFileNameToBe(props.name); + await expectFileNameToBe('test.jsx'); }); it('can change to a different extension', async () => { @@ -119,58 +111,58 @@ describe('<FileNode />', () => { window.confirm = mockConfirm; const newName = 'newname.gif'; - const { props } = renderFileNode('file'); + renderFileNode('file'); changeName(newName); expect(mockConfirm).toHaveBeenCalled(); await waitFor(() => - expect(props.updateFileName).toHaveBeenCalledWith(props.id, newName) + expect(FileActions.updateFileName).toHaveBeenCalledWith('0', newName) ); await expectFileNameToBe(newName); }); it('cannot be just an extension', async () => { const newName = '.jsx'; - const { props } = renderFileNode('file'); + renderFileNode('file'); changeName(newName); await waitFor(() => expect(mockDispatch).not.toHaveBeenCalled()); - await expectFileNameToBe(props.name); + await expectFileNameToBe('test.jsx'); }); }); describe('fileType: folder', () => { it('cannot change to an empty name', async () => { - const { props } = renderFileNode('folder'); + renderFileNode('folder'); changeName(''); await waitFor(() => expect(mockDispatch).not.toHaveBeenCalled()); - await expectFileNameToBe(props.name); + await expectFileNameToBe('afolder'); }); it('can change to another name', async () => { const newName = 'foldername'; - const { props } = renderFileNode('folder'); + renderFileNode('folder'); changeName(newName); await waitFor(() => - expect(props.updateFileName).toHaveBeenCalledWith(props.id, newName) + expect(FileActions.updateFileName).toHaveBeenCalledWith('0', newName) ); await expectFileNameToBe(newName); }); it('cannot have a file extension', async () => { const newName = 'foldername.jsx'; - const { props } = renderFileNode('folder'); + renderFileNode('folder'); changeName(newName); await waitFor(() => expect(mockDispatch).not.toHaveBeenCalled()); - await expectFileNameToBe(props.name); + await expectFileNameToBe('afolder'); }); }); }); From fd3e3a6e98da361250c596a19b6bafd7588ec771 Mon Sep 17 00:00:00 2001 From: raclim <raclim@nyu.edu> Date: Thu, 28 Nov 2024 10:07:30 -0500 Subject: [PATCH 18/39] update FileNode import --- client/modules/IDE/components/Sidebar.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/modules/IDE/components/Sidebar.jsx b/client/modules/IDE/components/Sidebar.jsx index 79ffed3b86..20f0104caf 100644 --- a/client/modules/IDE/components/Sidebar.jsx +++ b/client/modules/IDE/components/Sidebar.jsx @@ -13,7 +13,7 @@ import { import { selectRootFile } from '../selectors/files'; import { getAuthenticated, selectCanEditSketch } from '../selectors/users'; -import ConnectedFileNode from './FileNode'; +import FileNode from './FileNode'; import { PlusIcon } from '../../../common/icons'; import { FileDrawer } from './Editor/MobileEditor'; @@ -130,7 +130,7 @@ export default function SideBar() { </ul> </div> </header> - <ConnectedFileNode id={rootFile.id} canEdit={canEditProject} /> + <FileNode id={rootFile.id} canEdit={canEditProject} /> </section> </FileDrawer> ); From 48f9ad8ab90e9abbcc2bc35b563a0289a7b8b39e Mon Sep 17 00:00:00 2001 From: Stef Tervelde <stef@steftervelde.nl> Date: Sat, 30 Nov 2024 20:01:07 +0100 Subject: [PATCH 19/39] added donation banner --- client/index.jsx | 14 ++++++++++++++ client/styles/base/_base.scss | 7 +++++++ 2 files changed, 21 insertions(+) diff --git a/client/index.jsx b/client/index.jsx index 6c12fa511b..313334dc20 100644 --- a/client/index.jsx +++ b/client/index.jsx @@ -20,6 +20,20 @@ const initialState = window.__INITIAL_STATE__; const store = configureStore(initialState); +// Add a banner to the page +const banner = document.createElement('div'); +banner.id = 'processing-banner'; +document.body.appendChild(banner); + +const link = document.createElement('link'); +link.rel = 'stylesheet'; +link.href = 'https://foundation-donate-banner.netlify.app/static/css/main.css'; +document.head.appendChild(link); + +const script = document.createElement('script'); +script.src = 'https://foundation-donate-banner.netlify.app/static/js/main.js'; +document.body.appendChild(script); + const App = () => ( <> <Router history={browserHistory}> diff --git a/client/styles/base/_base.scss b/client/styles/base/_base.scss index 3e283d7ab9..ad17249932 100644 --- a/client/styles/base/_base.scss +++ b/client/styles/base/_base.scss @@ -151,3 +151,10 @@ textarea:focus { white-space: nowrap; width: 1px; } + + +// Donate banner custom properties +body { + --donate-banner-dark: #c01c4c; + --donate-banner-background: url('https://foundation-donate-banner.netlify.app/p5.png'); +} \ No newline at end of file From f84ebd9e05a01e1b4283783e7a6bfb37916f6a2c Mon Sep 17 00:00:00 2001 From: raclim <raclim@nyu.edu> Date: Mon, 2 Dec 2024 20:45:14 -0500 Subject: [PATCH 20/39] 2.15.4 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index de6eb98b9d..0d1b8cecf9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "p5.js-web-editor", - "version": "2.15.3", + "version": "2.15.4", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "p5.js-web-editor", - "version": "2.15.3", + "version": "2.15.4", "license": "LGPL-2.1", "dependencies": { "@auth0/s3": "^1.0.0", diff --git a/package.json b/package.json index 40c364e6a8..03c93a5be7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "p5.js-web-editor", - "version": "2.15.3", + "version": "2.15.4", "description": "The web editor for p5.js.", "scripts": { "clean": "rimraf dist", From a361329db1856eea016c82c3ce1a627e696849e4 Mon Sep 17 00:00:00 2001 From: raclim <43053081+raclim@users.noreply.github.com> Date: Tue, 3 Dec 2024 00:42:08 -0500 Subject: [PATCH 21/39] Revert "Fix Console Errors and Update Hooks in FileNode" --- .../modules/IDE/components/ConsoleInput.jsx | 4 +- client/modules/IDE/components/FileNode.jsx | 131 +++++++++++++----- .../IDE/components/FileNode.unit.test.jsx | 112 ++++++--------- client/modules/IDE/components/Sidebar.jsx | 4 +- client/modules/IDE/utils/parseFileName.js | 28 ---- 5 files changed, 147 insertions(+), 132 deletions(-) delete mode 100644 client/modules/IDE/utils/parseFileName.js diff --git a/client/modules/IDE/components/ConsoleInput.jsx b/client/modules/IDE/components/ConsoleInput.jsx index daac76839c..c026d29d4b 100644 --- a/client/modules/IDE/components/ConsoleInput.jsx +++ b/client/modules/IDE/components/ConsoleInput.jsx @@ -1,6 +1,5 @@ import PropTypes from 'prop-types'; import React, { useRef, useEffect, useState } from 'react'; -import { useDispatch } from 'react-redux'; import CodeMirror from 'codemirror'; import { Encode } from 'console-feed'; @@ -16,7 +15,6 @@ function ConsoleInput({ theme, fontSize }) { const [commandCursor, setCommandCursor] = useState(-1); const codemirrorContainer = useRef(null); const cmInstance = useRef(null); - const dispatch = useDispatch(); useEffect(() => { cmInstance.current = CodeMirror(codemirrorContainer.current, { @@ -47,7 +45,7 @@ function ConsoleInput({ theme, fontSize }) { payload: { source: 'console', messages } }); - dispatch(dispatchConsoleEvent(consoleEvent)); + dispatchConsoleEvent(consoleEvent); cm.setValue(''); setCommandHistory([value, ...commandHistory]); setCommandCursor(-1); diff --git a/client/modules/IDE/components/FileNode.jsx b/client/modules/IDE/components/FileNode.jsx index cd6caa3255..e589fb3cff 100644 --- a/client/modules/IDE/components/FileNode.jsx +++ b/client/modules/IDE/components/FileNode.jsx @@ -1,17 +1,43 @@ import PropTypes from 'prop-types'; import classNames from 'classnames'; import React, { useState, useRef } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; +import { connect } from 'react-redux'; import { useTranslation } from 'react-i18next'; import * as IDEActions from '../actions/ide'; import * as FileActions from '../actions/files'; -import parseFileName from '../utils/parseFileName'; import DownArrowIcon from '../../../images/down-filled-triangle.svg'; import FolderRightIcon from '../../../images/triangle-arrow-right.svg'; import FolderDownIcon from '../../../images/triangle-arrow-down.svg'; import FileTypeIcon from './FileTypeIcon'; +function parseFileName(name) { + const nameArray = name.split('.'); + if (nameArray.length > 1) { + const extension = `.${nameArray[nameArray.length - 1]}`; + const baseName = nameArray.slice(0, -1).join('.'); + const firstLetter = baseName[0]; + const lastLetter = baseName[baseName.length - 1]; + const middleText = baseName.slice(1, -1); + return { + baseName, + firstLetter, + lastLetter, + middleText, + extension + }; + } + const firstLetter = name[0]; + const lastLetter = name[name.length - 1]; + const middleText = name.slice(1, -1); + return { + baseName: name, + firstLetter, + lastLetter, + middleText + }; +} + function FileName({ name }) { const { baseName, @@ -36,35 +62,40 @@ FileName.propTypes = { name: PropTypes.string.isRequired }; -const FileNode = ({ id, canEdit, onClickFile }) => { - const dispatch = useDispatch(); - const { t } = useTranslation(); - - const fileNode = - useSelector((state) => state.files.find((file) => file.id === id)) || {}; - const authenticated = useSelector((state) => state.user.authenticated); - - const { - name = '', - parentId = null, - children = [], - fileType = 'file', - isSelectedFile = false, - isFolderClosed = false - } = fileNode; - +const FileNode = ({ + id, + parentId, + children, + name, + fileType, + isSelectedFile, + isFolderClosed, + setSelectedFile, + deleteFile, + updateFileName, + resetSelectedFile, + newFile, + newFolder, + showFolderChildren, + hideFolderChildren, + canEdit, + openUploadFileModal, + authenticated, + onClickFile +}) => { const [isOptionsOpen, setIsOptionsOpen] = useState(false); const [isEditingName, setIsEditingName] = useState(false); const [isDeleting, setIsDeleting] = useState(false); const [updatedName, setUpdatedName] = useState(name); + const { t } = useTranslation(); const fileNameInput = useRef(null); const fileOptionsRef = useRef(null); const handleFileClick = (event) => { event.stopPropagation(); if (name !== 'root' && !isDeleting) { - dispatch(IDEActions.setSelectedFile(id)); + setSelectedFile(id); } if (onClickFile) { onClickFile(); @@ -91,17 +122,17 @@ const FileNode = ({ id, canEdit, onClickFile }) => { }; const handleClickAddFile = () => { - dispatch(IDEActions.newFile(id)); + newFile(id); setTimeout(() => hideFileOptions(), 0); }; const handleClickAddFolder = () => { - dispatch(IDEActions.newFolder(id)); + newFolder(id); setTimeout(() => hideFileOptions(), 0); }; const handleClickUploadFile = () => { - dispatch(IDEActions.openUploadFileModal(id)); + openUploadFileModal(id); setTimeout(hideFileOptions, 0); }; @@ -110,8 +141,8 @@ const FileNode = ({ id, canEdit, onClickFile }) => { if (window.confirm(prompt)) { setIsDeleting(true); - dispatch(IDEActions.resetSelectedFile(id)); - setTimeout(() => dispatch(FileActions.deleteFile(id, parentId), 100)); + resetSelectedFile(id); + setTimeout(() => deleteFile(id, parentId), 100); } }; @@ -127,7 +158,7 @@ const FileNode = ({ id, canEdit, onClickFile }) => { const saveUpdatedFileName = () => { if (updatedName !== name) { - dispatch(FileActions.updateFileName(id, updatedName)); + updateFileName(id, updatedName); } }; @@ -212,7 +243,7 @@ const FileNode = ({ id, canEdit, onClickFile }) => { <div className="sidebar__file-item--folder"> <button className="sidebar__file-item-closed" - onClick={() => dispatch(FileActions.showFolderChildren(id))} + onClick={() => showFolderChildren(id)} aria-label={t('FileNode.OpenFolderARIA')} title={t('FileNode.OpenFolderARIA')} > @@ -224,7 +255,7 @@ const FileNode = ({ id, canEdit, onClickFile }) => { </button> <button className="sidebar__file-item-open" - onClick={() => dispatch(FileActions.hideFolderChildren(id))} + onClick={() => hideFolderChildren(id)} aria-label={t('FileNode.CloseFolderARIA')} title={t('FileNode.CloseFolderARIA')} > @@ -322,7 +353,7 @@ const FileNode = ({ id, canEdit, onClickFile }) => { <ul className="file-item__children"> {children.map((childId) => ( <li key={childId}> - <FileNode + <ConnectedFileNode id={childId} parentId={id} canEdit={canEdit} @@ -338,12 +369,50 @@ const FileNode = ({ id, canEdit, onClickFile }) => { FileNode.propTypes = { id: PropTypes.string.isRequired, + parentId: PropTypes.string, + children: PropTypes.arrayOf(PropTypes.string.isRequired).isRequired, + name: PropTypes.string.isRequired, + fileType: PropTypes.string.isRequired, + isSelectedFile: PropTypes.bool, + isFolderClosed: PropTypes.bool, + setSelectedFile: PropTypes.func.isRequired, + deleteFile: PropTypes.func.isRequired, + updateFileName: PropTypes.func.isRequired, + resetSelectedFile: PropTypes.func.isRequired, + newFile: PropTypes.func.isRequired, + newFolder: PropTypes.func.isRequired, + showFolderChildren: PropTypes.func.isRequired, + hideFolderChildren: PropTypes.func.isRequired, canEdit: PropTypes.bool.isRequired, + openUploadFileModal: PropTypes.func.isRequired, + authenticated: PropTypes.bool.isRequired, onClickFile: PropTypes.func }; FileNode.defaultProps = { - onClickFile: null + onClickFile: null, + parentId: '0', + isSelectedFile: false, + isFolderClosed: false }; -export default FileNode; +function mapStateToProps(state, ownProps) { + // this is a hack, state is updated before ownProps + const fileNode = state.files.find((file) => file.id === ownProps.id) || { + name: 'test', + fileType: 'file' + }; + return Object.assign({}, fileNode, { + authenticated: state.user.authenticated + }); +} + +const mapDispatchToProps = { ...FileActions, ...IDEActions }; + +const ConnectedFileNode = connect( + mapStateToProps, + mapDispatchToProps +)(FileNode); + +export { FileNode }; +export default ConnectedFileNode; diff --git a/client/modules/IDE/components/FileNode.unit.test.jsx b/client/modules/IDE/components/FileNode.unit.test.jsx index 4f75d9a272..8676a817d8 100644 --- a/client/modules/IDE/components/FileNode.unit.test.jsx +++ b/client/modules/IDE/components/FileNode.unit.test.jsx @@ -1,8 +1,4 @@ import React from 'react'; -import { Provider } from 'react-redux'; -import configureStore from 'redux-mock-store'; -import { useDispatch } from 'react-redux'; -import * as FileActions from '../actions/files'; import { fireEvent, @@ -11,27 +7,9 @@ import { waitFor, within } from '../../../test-utils'; -import FileNode from './FileNode'; - -jest.mock('react-redux', () => ({ - ...jest.requireActual('react-redux'), - useDispatch: jest.fn() -})); - -jest.mock('../actions/files', () => ({ - updateFileName: jest.fn() -})); - -const mockStore = configureStore([]); +import { FileNode } from './FileNode'; describe('<FileNode />', () => { - const mockDispatch = jest.fn(); - - beforeEach(() => { - useDispatch.mockReturnValue(mockDispatch); - jest.clearAllMocks(); - }); - const changeName = (newFileName) => { const renameButton = screen.getByText(/Rename/i); fireEvent.click(renameButton); @@ -46,64 +24,62 @@ describe('<FileNode />', () => { await waitFor(() => within(name).queryByText(expectedName)); }; - const renderFileNode = (fileType, extraState = {}) => { - const initialState = { - files: [ - { - id: '0', - name: fileType === 'folder' ? 'afolder' : 'test.jsx', - fileType, - parentId: 'root', - children: [], - isSelectedFile: false, - isFolderClosed: false - } - ], - user: { authenticated: false }, - ...extraState + const renderFileNode = (fileType, extraProps = {}) => { + const props = { + ...extraProps, + id: '0', + name: fileType === 'folder' ? 'afolder' : 'test.jsx', + fileType, + canEdit: true, + children: [], + authenticated: false, + setSelectedFile: jest.fn(), + deleteFile: jest.fn(), + updateFileName: jest.fn(), + resetSelectedFile: jest.fn(), + newFile: jest.fn(), + newFolder: jest.fn(), + showFolderChildren: jest.fn(), + hideFolderChildren: jest.fn(), + openUploadFileModal: jest.fn(), + setProjectName: jest.fn() }; - const store = mockStore(initialState); + render(<FileNode {...props} />); - render( - <Provider store={store}> - <FileNode id="0" canEdit /> - </Provider> - ); - - return { store }; + return props; }; describe('fileType: file', () => { it('cannot change to an empty name', async () => { - renderFileNode('file'); + const props = renderFileNode('file'); changeName(''); - await waitFor(() => expect(mockDispatch).not.toHaveBeenCalled()); - await expectFileNameToBe('test.jsx'); + await waitFor(() => expect(props.updateFileName).not.toHaveBeenCalled()); + await expectFileNameToBe(props.name); }); it('can change to a valid filename', async () => { const newName = 'newname.jsx'; - renderFileNode('file'); + const props = renderFileNode('file'); changeName(newName); await waitFor(() => - expect(FileActions.updateFileName).toHaveBeenCalledWith('0', newName) + expect(props.updateFileName).toHaveBeenCalledWith(props.id, newName) ); await expectFileNameToBe(newName); }); it('must have an extension', async () => { const newName = 'newname'; - renderFileNode('file'); + const props = renderFileNode('file'); changeName(newName); - await waitFor(() => expect(mockDispatch).not.toHaveBeenCalled()); - await expectFileNameToBe('test.jsx'); + await waitFor(() => expect(props.updateFileName).not.toHaveBeenCalled()); + await expectFileNameToBe(props.name); }); it('can change to a different extension', async () => { @@ -111,58 +87,58 @@ describe('<FileNode />', () => { window.confirm = mockConfirm; const newName = 'newname.gif'; - renderFileNode('file'); + const props = renderFileNode('file'); changeName(newName); expect(mockConfirm).toHaveBeenCalled(); await waitFor(() => - expect(FileActions.updateFileName).toHaveBeenCalledWith('0', newName) + expect(props.updateFileName).toHaveBeenCalledWith(props.id, newName) ); - await expectFileNameToBe(newName); + await expectFileNameToBe(props.name); }); it('cannot be just an extension', async () => { const newName = '.jsx'; - renderFileNode('file'); + const props = renderFileNode('file'); changeName(newName); - await waitFor(() => expect(mockDispatch).not.toHaveBeenCalled()); - await expectFileNameToBe('test.jsx'); + await waitFor(() => expect(props.updateFileName).not.toHaveBeenCalled()); + await expectFileNameToBe(props.name); }); }); describe('fileType: folder', () => { it('cannot change to an empty name', async () => { - renderFileNode('folder'); + const props = renderFileNode('folder'); changeName(''); - await waitFor(() => expect(mockDispatch).not.toHaveBeenCalled()); - await expectFileNameToBe('afolder'); + await waitFor(() => expect(props.updateFileName).not.toHaveBeenCalled()); + await expectFileNameToBe(props.name); }); it('can change to another name', async () => { const newName = 'foldername'; - renderFileNode('folder'); + const props = renderFileNode('folder'); changeName(newName); await waitFor(() => - expect(FileActions.updateFileName).toHaveBeenCalledWith('0', newName) + expect(props.updateFileName).toHaveBeenCalledWith(props.id, newName) ); await expectFileNameToBe(newName); }); it('cannot have a file extension', async () => { const newName = 'foldername.jsx'; - renderFileNode('folder'); + const props = renderFileNode('folder'); changeName(newName); - await waitFor(() => expect(mockDispatch).not.toHaveBeenCalled()); - await expectFileNameToBe('afolder'); + await waitFor(() => expect(props.updateFileName).not.toHaveBeenCalled()); + await expectFileNameToBe(props.name); }); }); }); diff --git a/client/modules/IDE/components/Sidebar.jsx b/client/modules/IDE/components/Sidebar.jsx index 20f0104caf..79ffed3b86 100644 --- a/client/modules/IDE/components/Sidebar.jsx +++ b/client/modules/IDE/components/Sidebar.jsx @@ -13,7 +13,7 @@ import { import { selectRootFile } from '../selectors/files'; import { getAuthenticated, selectCanEditSketch } from '../selectors/users'; -import FileNode from './FileNode'; +import ConnectedFileNode from './FileNode'; import { PlusIcon } from '../../../common/icons'; import { FileDrawer } from './Editor/MobileEditor'; @@ -130,7 +130,7 @@ export default function SideBar() { </ul> </div> </header> - <FileNode id={rootFile.id} canEdit={canEditProject} /> + <ConnectedFileNode id={rootFile.id} canEdit={canEditProject} /> </section> </FileDrawer> ); diff --git a/client/modules/IDE/utils/parseFileName.js b/client/modules/IDE/utils/parseFileName.js deleted file mode 100644 index 1bde7be0f6..0000000000 --- a/client/modules/IDE/utils/parseFileName.js +++ /dev/null @@ -1,28 +0,0 @@ -function parseFileName(name) { - const nameArray = name.split('.'); - if (nameArray.length > 1) { - const extension = `.${nameArray[nameArray.length - 1]}`; - const baseName = nameArray.slice(0, -1).join('.'); - const firstLetter = baseName[0]; - const lastLetter = baseName[baseName.length - 1]; - const middleText = baseName.slice(1, -1); - return { - baseName, - firstLetter, - lastLetter, - middleText, - extension - }; - } - const firstLetter = name[0]; - const lastLetter = name[name.length - 1]; - const middleText = name.slice(1, -1); - return { - baseName: name, - firstLetter, - lastLetter, - middleText - }; -} - -export default parseFileName; From b63c031c6f42498b2512a1a12a06eacb88eff5a9 Mon Sep 17 00:00:00 2001 From: raclim <raclim@nyu.edu> Date: Tue, 3 Dec 2024 00:51:56 -0500 Subject: [PATCH 22/39] add back in consoleinput changes --- client/modules/IDE/components/ConsoleInput.jsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/modules/IDE/components/ConsoleInput.jsx b/client/modules/IDE/components/ConsoleInput.jsx index c026d29d4b..62e7da13a9 100644 --- a/client/modules/IDE/components/ConsoleInput.jsx +++ b/client/modules/IDE/components/ConsoleInput.jsx @@ -1,6 +1,7 @@ import PropTypes from 'prop-types'; import React, { useRef, useEffect, useState } from 'react'; import CodeMirror from 'codemirror'; +import { useDispatch } from 'react-redux'; import { Encode } from 'console-feed'; import RightArrowIcon from '../../../images/right-arrow.svg'; @@ -15,6 +16,7 @@ function ConsoleInput({ theme, fontSize }) { const [commandCursor, setCommandCursor] = useState(-1); const codemirrorContainer = useRef(null); const cmInstance = useRef(null); + const dispatch = useDispatch(); useEffect(() => { cmInstance.current = CodeMirror(codemirrorContainer.current, { @@ -45,7 +47,7 @@ function ConsoleInput({ theme, fontSize }) { payload: { source: 'console', messages } }); - dispatchConsoleEvent(consoleEvent); + dispatch(dispatchConsoleEvent(consoleEvent)); cm.setValue(''); setCommandHistory([value, ...commandHistory]); setCommandCursor(-1); From c8118f639550c738d8df72542539259d939b2118 Mon Sep 17 00:00:00 2001 From: raclim <raclim@nyu.edu> Date: Tue, 3 Dec 2024 00:55:50 -0500 Subject: [PATCH 23/39] 2.15.5 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0d1b8cecf9..3235c91550 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "p5.js-web-editor", - "version": "2.15.4", + "version": "2.15.5", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "p5.js-web-editor", - "version": "2.15.4", + "version": "2.15.5", "license": "LGPL-2.1", "dependencies": { "@auth0/s3": "^1.0.0", diff --git a/package.json b/package.json index 03c93a5be7..8d26a46a4a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "p5.js-web-editor", - "version": "2.15.4", + "version": "2.15.5", "description": "The web editor for p5.js.", "scripts": { "clean": "rimraf dist", From 16644f3de1904bcf8418e99d6a6027e661afc87a Mon Sep 17 00:00:00 2001 From: raclim <raclim@nyu.edu> Date: Wed, 4 Dec 2024 10:42:18 -0500 Subject: [PATCH 24/39] add apple pay id --- static/.well-known/apple-developer-merchantid-domain-association | 1 + 1 file changed, 1 insertion(+) create mode 100644 static/.well-known/apple-developer-merchantid-domain-association diff --git a/static/.well-known/apple-developer-merchantid-domain-association b/static/.well-known/apple-developer-merchantid-domain-association new file mode 100644 index 0000000000..2ff95c9628 --- /dev/null +++ b/static/.well-known/apple-developer-merchantid-domain-association @@ -0,0 +1 @@ +7B227073704964223A2239373943394538343346343131343044463144313834343232393232313734313034353044314339464446394437384337313531303944334643463542433731222C2276657273696F6E223A312C22637265617465644F6E223A313536363233343735303036312C227369676E6174757265223A22333038303036303932613836343838366637306430313037303261303830333038303032303130313331306633303064303630393630383634383031363530333034303230313035303033303830303630393261383634383836663730643031303730313030303061303830333038323033653333303832303338386130303330323031303230323038346333303431343935313964353433363330306130363038326138363438636533643034303330323330376133313265333032633036303335353034303330633235343137303730366336353230343137303730366336393633363137343639366636653230343936653734363536373732363137343639366636653230343334313230326432303437333333313236333032343036303335353034306230633164343137303730366336353230343336353732373436393636363936333631373436393666366532303431373537343638366637323639373437393331313333303131303630333535303430613063306134313730373036633635323034393665363332653331306233303039303630333535303430363133303235353533333031653137306433313339333033353331333833303331333333323335333735613137306433323334333033353331333633303331333333323335333735613330356633313235333032333036303335353034303330633163363536333633326437333664373032643632373236663662363537323264373336393637366535663535343333343264353035323466343433313134333031323036303335353034306230633062363934663533323035333739373337343635366437333331313333303131303630333535303430613063306134313730373036633635323034393665363332653331306233303039303630333535303430363133303235353533333035393330313330363037326138363438636533643032303130363038326138363438636533643033303130373033343230303034633231353737656465626436633762323231386636386464373039306131323138646337623062643666326332383364383436303935643934616634613534313162383334323065643831316633343037653833333331663163353463336637656233323230643662616435643465666634393238393839336537633066313361333832303231313330383230323064333030633036303335353164313330313031666630343032333030303330316630363033353531643233303431383330313638303134323366323439633434663933653465663237653663346636323836633366613262626664326534623330343530363038326230363031303530353037303130313034333933303337333033353036303832623036303130353035303733303031383632393638373437343730336132663266366636333733373032653631373037303663363532653633366636643266366636333733373033303334326436313730373036633635363136393633363133333330333233303832303131643036303335353164323030343832303131343330383230313130333038323031306330363039326138363438383666373633363430353031333038316665333038316333303630383262303630313035303530373032303233303831623630633831623335323635366336393631366536333635323036663665323037343638363937333230363336353732373436393636363936333631373436353230363237393230363136653739323037303631373237343739323036313733373337353664363537333230363136333633363537303734363136653633363532303666363632303734363836353230373436383635366532303631373037303663363936333631363236633635323037333734363136653634363137323634323037343635373236643733323036313665363432303633366636653634363937343639366636653733323036663636323037353733363532633230363336353732373436393636363936333631373436353230373036663663363936333739323036313665363432303633363537323734363936363639363336313734363936663665323037303732363136333734363936333635323037333734363137343635366436353665373437333265333033363036303832623036303130353035303730323031313632613638373437343730336132663266373737373737326536313730373036633635326536333666366432663633363537323734363936363639363336313734363536313735373436383666373236393734373932663330333430363033353531643166303432643330326233303239613032376130323538363233363837343734373033613266326636333732366332653631373037303663363532653633366636643266363137303730366336353631363936333631333332653633373236633330316430363033353531643065303431363034313439343537646236666435373438313836383938393736326637653537383530376537396235383234333030653036303335353164306630313031666630343034303330323037383033303066303630393261383634383836663736333634303631643034303230353030333030613036303832613836343863653364303430333032303334393030333034363032323130306265303935373166653731653165373335623535653561666163623463373266656234343566333031383532323263373235313030326236316562643666353530323231303064313862333530613564643664643665623137343630333562313165623263653837636661336536616636636264383338303839306463383263646461613633333038323032656533303832303237356130303330323031303230323038343936643266626633613938646139373330306130363038326138363438636533643034303330323330363733313162333031393036303335353034303330633132343137303730366336353230353236663666373432303433343132303264323034373333333132363330323430363033353530343062306331643431373037303663363532303433363537323734363936363639363336313734363936663665323034313735373436383666373236393734373933313133333031313036303335353034306130633061343137303730366336353230343936653633326533313062333030393036303335353034303631333032353535333330316531373064333133343330333533303336333233333334333633333330356131373064333233393330333533303336333233333334333633333330356133303761333132653330326330363033353530343033306332353431373037303663363532303431373037303663363936333631373436393666366532303439366537343635363737323631373436393666366532303433343132303264323034373333333132363330323430363033353530343062306331643431373037303663363532303433363537323734363936363639363336313734363936663665323034313735373436383666373236393734373933313133333031313036303335353034306130633061343137303730366336353230343936653633326533313062333030393036303335353034303631333032353535333330353933303133303630373261383634386365336430323031303630383261383634386365336430333031303730333432303030346630313731313834313964373634383564353161356532353831303737366538383061326566646537626165346465303864666334623933653133333536643536363562333561653232643039373736306432323465376262613038666437363137636538386362373662623636373062656338653832393834666635343435613338316637333038316634333034363036303832623036303130353035303730313031303433613330333833303336303630383262303630313035303530373330303138363261363837343734373033613266326636663633373337303265363137303730366336353265363336663664326636663633373337303330333432643631373037303663363537323666366637343633363136373333333031643036303335353164306530343136303431343233663234396334346639336534656632376536633466363238366333666132626266643265346233303066303630333535316431333031303166663034303533303033303130316666333031663036303335353164323330343138333031363830313462626230646561313538333338383961613438613939646562656264656261666461636232346162333033373036303335353164316630343330333032653330326361303261613032383836323636383734373437303361326632663633373236633265363137303730366336353265363336663664326636313730373036633635373236663666373436333631363733333265363337323663333030653036303335353164306630313031666630343034303330323031303633303130303630613261383634383836663736333634303630323065303430323035303033303061303630383261383634386365336430343033303230333637303033303634303233303361636637323833353131363939623138366662333563333536636136326266663431376564643930663735346461323865626566313963383135653432623738396638393866373962353939663938643534313064386639646539633266653032333033323264643534343231623061333035373736633564663333383362393036376664313737633263323136643936346663363732363938323132366635346638376137643162393963623962303938393231363130363939306630393932316430303030333138323031386233303832303138373032303130313330383138363330376133313265333032633036303335353034303330633235343137303730366336353230343137303730366336393633363137343639366636653230343936653734363536373732363137343639366636653230343334313230326432303437333333313236333032343036303335353034306230633164343137303730366336353230343336353732373436393636363936333631373436393666366532303431373537343638366637323639373437393331313333303131303630333535303430613063306134313730373036633635323034393665363332653331306233303039303630333535303430363133303235353533303230383463333034313439353139643534333633303064303630393630383634383031363530333034303230313035303061303831393533303138303630393261383634383836663730643031303930333331306230363039326138363438383666373064303130373031333031633036303932613836343838366637306430313039303533313066313730643331333933303338333133393331333733313332333333303561333032613036303932613836343838366637306430313039333433313164333031623330306430363039363038363438303136353033303430323031303530306131306130363038326138363438636533643034303330323330326630363039326138363438383666373064303130393034333132323034323062303731303365313430613462386231376262613230316130336163643036396234653431366232613263383066383661383338313435633239373566633131333030613036303832613836343863653364303430333032303434363330343430323230343639306264636637626461663833636466343934396534633035313039656463663334373665303564373261313264376335666538633033303033343464663032323032363764353863393365626233353031333836363062353730373938613064643731313734316262353864626436613138363633353038353431656565393035303030303030303030303030227D \ No newline at end of file From 944447001789bad56c64241ee61d41309bc28335 Mon Sep 17 00:00:00 2001 From: raclim <raclim@nyu.edu> Date: Wed, 4 Dec 2024 12:19:24 -0500 Subject: [PATCH 25/39] remove static folder --- .well-known/apple-developer-merchantid-domain-association | 1 + 1 file changed, 1 insertion(+) create mode 100644 .well-known/apple-developer-merchantid-domain-association diff --git a/.well-known/apple-developer-merchantid-domain-association b/.well-known/apple-developer-merchantid-domain-association new file mode 100644 index 0000000000..2ff95c9628 --- /dev/null +++ b/.well-known/apple-developer-merchantid-domain-association @@ -0,0 +1 @@ +7B227073704964223A2239373943394538343346343131343044463144313834343232393232313734313034353044314339464446394437384337313531303944334643463542433731222C2276657273696F6E223A312C22637265617465644F6E223A313536363233343735303036312C227369676E6174757265223A22333038303036303932613836343838366637306430313037303261303830333038303032303130313331306633303064303630393630383634383031363530333034303230313035303033303830303630393261383634383836663730643031303730313030303061303830333038323033653333303832303338386130303330323031303230323038346333303431343935313964353433363330306130363038326138363438636533643034303330323330376133313265333032633036303335353034303330633235343137303730366336353230343137303730366336393633363137343639366636653230343936653734363536373732363137343639366636653230343334313230326432303437333333313236333032343036303335353034306230633164343137303730366336353230343336353732373436393636363936333631373436393666366532303431373537343638366637323639373437393331313333303131303630333535303430613063306134313730373036633635323034393665363332653331306233303039303630333535303430363133303235353533333031653137306433313339333033353331333833303331333333323335333735613137306433323334333033353331333633303331333333323335333735613330356633313235333032333036303335353034303330633163363536333633326437333664373032643632373236663662363537323264373336393637366535663535343333343264353035323466343433313134333031323036303335353034306230633062363934663533323035333739373337343635366437333331313333303131303630333535303430613063306134313730373036633635323034393665363332653331306233303039303630333535303430363133303235353533333035393330313330363037326138363438636533643032303130363038326138363438636533643033303130373033343230303034633231353737656465626436633762323231386636386464373039306131323138646337623062643666326332383364383436303935643934616634613534313162383334323065643831316633343037653833333331663163353463336637656233323230643662616435643465666634393238393839336537633066313361333832303231313330383230323064333030633036303335353164313330313031666630343032333030303330316630363033353531643233303431383330313638303134323366323439633434663933653465663237653663346636323836633366613262626664326534623330343530363038326230363031303530353037303130313034333933303337333033353036303832623036303130353035303733303031383632393638373437343730336132663266366636333733373032653631373037303663363532653633366636643266366636333733373033303334326436313730373036633635363136393633363133333330333233303832303131643036303335353164323030343832303131343330383230313130333038323031306330363039326138363438383666373633363430353031333038316665333038316333303630383262303630313035303530373032303233303831623630633831623335323635366336393631366536333635323036663665323037343638363937333230363336353732373436393636363936333631373436353230363237393230363136653739323037303631373237343739323036313733373337353664363537333230363136333633363537303734363136653633363532303666363632303734363836353230373436383635366532303631373037303663363936333631363236633635323037333734363136653634363137323634323037343635373236643733323036313665363432303633366636653634363937343639366636653733323036663636323037353733363532633230363336353732373436393636363936333631373436353230373036663663363936333739323036313665363432303633363537323734363936363639363336313734363936663665323037303732363136333734363936333635323037333734363137343635366436353665373437333265333033363036303832623036303130353035303730323031313632613638373437343730336132663266373737373737326536313730373036633635326536333666366432663633363537323734363936363639363336313734363536313735373436383666373236393734373932663330333430363033353531643166303432643330326233303239613032376130323538363233363837343734373033613266326636333732366332653631373037303663363532653633366636643266363137303730366336353631363936333631333332653633373236633330316430363033353531643065303431363034313439343537646236666435373438313836383938393736326637653537383530376537396235383234333030653036303335353164306630313031666630343034303330323037383033303066303630393261383634383836663736333634303631643034303230353030333030613036303832613836343863653364303430333032303334393030333034363032323130306265303935373166653731653165373335623535653561666163623463373266656234343566333031383532323263373235313030326236316562643666353530323231303064313862333530613564643664643665623137343630333562313165623263653837636661336536616636636264383338303839306463383263646461613633333038323032656533303832303237356130303330323031303230323038343936643266626633613938646139373330306130363038326138363438636533643034303330323330363733313162333031393036303335353034303330633132343137303730366336353230353236663666373432303433343132303264323034373333333132363330323430363033353530343062306331643431373037303663363532303433363537323734363936363639363336313734363936663665323034313735373436383666373236393734373933313133333031313036303335353034306130633061343137303730366336353230343936653633326533313062333030393036303335353034303631333032353535333330316531373064333133343330333533303336333233333334333633333330356131373064333233393330333533303336333233333334333633333330356133303761333132653330326330363033353530343033306332353431373037303663363532303431373037303663363936333631373436393666366532303439366537343635363737323631373436393666366532303433343132303264323034373333333132363330323430363033353530343062306331643431373037303663363532303433363537323734363936363639363336313734363936663665323034313735373436383666373236393734373933313133333031313036303335353034306130633061343137303730366336353230343936653633326533313062333030393036303335353034303631333032353535333330353933303133303630373261383634386365336430323031303630383261383634386365336430333031303730333432303030346630313731313834313964373634383564353161356532353831303737366538383061326566646537626165346465303864666334623933653133333536643536363562333561653232643039373736306432323465376262613038666437363137636538386362373662623636373062656338653832393834666635343435613338316637333038316634333034363036303832623036303130353035303730313031303433613330333833303336303630383262303630313035303530373330303138363261363837343734373033613266326636663633373337303265363137303730366336353265363336663664326636663633373337303330333432643631373037303663363537323666366637343633363136373333333031643036303335353164306530343136303431343233663234396334346639336534656632376536633466363238366333666132626266643265346233303066303630333535316431333031303166663034303533303033303130316666333031663036303335353164323330343138333031363830313462626230646561313538333338383961613438613939646562656264656261666461636232346162333033373036303335353164316630343330333032653330326361303261613032383836323636383734373437303361326632663633373236633265363137303730366336353265363336663664326636313730373036633635373236663666373436333631363733333265363337323663333030653036303335353164306630313031666630343034303330323031303633303130303630613261383634383836663736333634303630323065303430323035303033303061303630383261383634386365336430343033303230333637303033303634303233303361636637323833353131363939623138366662333563333536636136326266663431376564643930663735346461323865626566313963383135653432623738396638393866373962353939663938643534313064386639646539633266653032333033323264643534343231623061333035373736633564663333383362393036376664313737633263323136643936346663363732363938323132366635346638376137643162393963623962303938393231363130363939306630393932316430303030333138323031386233303832303138373032303130313330383138363330376133313265333032633036303335353034303330633235343137303730366336353230343137303730366336393633363137343639366636653230343936653734363536373732363137343639366636653230343334313230326432303437333333313236333032343036303335353034306230633164343137303730366336353230343336353732373436393636363936333631373436393666366532303431373537343638366637323639373437393331313333303131303630333535303430613063306134313730373036633635323034393665363332653331306233303039303630333535303430363133303235353533303230383463333034313439353139643534333633303064303630393630383634383031363530333034303230313035303061303831393533303138303630393261383634383836663730643031303930333331306230363039326138363438383666373064303130373031333031633036303932613836343838366637306430313039303533313066313730643331333933303338333133393331333733313332333333303561333032613036303932613836343838366637306430313039333433313164333031623330306430363039363038363438303136353033303430323031303530306131306130363038326138363438636533643034303330323330326630363039326138363438383666373064303130393034333132323034323062303731303365313430613462386231376262613230316130336163643036396234653431366232613263383066383661383338313435633239373566633131333030613036303832613836343863653364303430333032303434363330343430323230343639306264636637626461663833636466343934396534633035313039656463663334373665303564373261313264376335666538633033303033343464663032323032363764353863393365626233353031333836363062353730373938613064643731313734316262353864626436613138363633353038353431656565393035303030303030303030303030227D \ No newline at end of file From 060b13adf3c2ba37aa83b613b3a30d9647008984 Mon Sep 17 00:00:00 2001 From: raclim <raclim@nyu.edu> Date: Wed, 4 Dec 2024 12:20:38 -0500 Subject: [PATCH 26/39] actually delete the static folder --- static/.well-known/apple-developer-merchantid-domain-association | 1 - 1 file changed, 1 deletion(-) delete mode 100644 static/.well-known/apple-developer-merchantid-domain-association diff --git a/static/.well-known/apple-developer-merchantid-domain-association b/static/.well-known/apple-developer-merchantid-domain-association deleted file mode 100644 index 2ff95c9628..0000000000 --- a/static/.well-known/apple-developer-merchantid-domain-association +++ /dev/null @@ -1 +0,0 @@ -7B227073704964223A2239373943394538343346343131343044463144313834343232393232313734313034353044314339464446394437384337313531303944334643463542433731222C2276657273696F6E223A312C22637265617465644F6E223A313536363233343735303036312C227369676E6174757265223A22333038303036303932613836343838366637306430313037303261303830333038303032303130313331306633303064303630393630383634383031363530333034303230313035303033303830303630393261383634383836663730643031303730313030303061303830333038323033653333303832303338386130303330323031303230323038346333303431343935313964353433363330306130363038326138363438636533643034303330323330376133313265333032633036303335353034303330633235343137303730366336353230343137303730366336393633363137343639366636653230343936653734363536373732363137343639366636653230343334313230326432303437333333313236333032343036303335353034306230633164343137303730366336353230343336353732373436393636363936333631373436393666366532303431373537343638366637323639373437393331313333303131303630333535303430613063306134313730373036633635323034393665363332653331306233303039303630333535303430363133303235353533333031653137306433313339333033353331333833303331333333323335333735613137306433323334333033353331333633303331333333323335333735613330356633313235333032333036303335353034303330633163363536333633326437333664373032643632373236663662363537323264373336393637366535663535343333343264353035323466343433313134333031323036303335353034306230633062363934663533323035333739373337343635366437333331313333303131303630333535303430613063306134313730373036633635323034393665363332653331306233303039303630333535303430363133303235353533333035393330313330363037326138363438636533643032303130363038326138363438636533643033303130373033343230303034633231353737656465626436633762323231386636386464373039306131323138646337623062643666326332383364383436303935643934616634613534313162383334323065643831316633343037653833333331663163353463336637656233323230643662616435643465666634393238393839336537633066313361333832303231313330383230323064333030633036303335353164313330313031666630343032333030303330316630363033353531643233303431383330313638303134323366323439633434663933653465663237653663346636323836633366613262626664326534623330343530363038326230363031303530353037303130313034333933303337333033353036303832623036303130353035303733303031383632393638373437343730336132663266366636333733373032653631373037303663363532653633366636643266366636333733373033303334326436313730373036633635363136393633363133333330333233303832303131643036303335353164323030343832303131343330383230313130333038323031306330363039326138363438383666373633363430353031333038316665333038316333303630383262303630313035303530373032303233303831623630633831623335323635366336393631366536333635323036663665323037343638363937333230363336353732373436393636363936333631373436353230363237393230363136653739323037303631373237343739323036313733373337353664363537333230363136333633363537303734363136653633363532303666363632303734363836353230373436383635366532303631373037303663363936333631363236633635323037333734363136653634363137323634323037343635373236643733323036313665363432303633366636653634363937343639366636653733323036663636323037353733363532633230363336353732373436393636363936333631373436353230373036663663363936333739323036313665363432303633363537323734363936363639363336313734363936663665323037303732363136333734363936333635323037333734363137343635366436353665373437333265333033363036303832623036303130353035303730323031313632613638373437343730336132663266373737373737326536313730373036633635326536333666366432663633363537323734363936363639363336313734363536313735373436383666373236393734373932663330333430363033353531643166303432643330326233303239613032376130323538363233363837343734373033613266326636333732366332653631373037303663363532653633366636643266363137303730366336353631363936333631333332653633373236633330316430363033353531643065303431363034313439343537646236666435373438313836383938393736326637653537383530376537396235383234333030653036303335353164306630313031666630343034303330323037383033303066303630393261383634383836663736333634303631643034303230353030333030613036303832613836343863653364303430333032303334393030333034363032323130306265303935373166653731653165373335623535653561666163623463373266656234343566333031383532323263373235313030326236316562643666353530323231303064313862333530613564643664643665623137343630333562313165623263653837636661336536616636636264383338303839306463383263646461613633333038323032656533303832303237356130303330323031303230323038343936643266626633613938646139373330306130363038326138363438636533643034303330323330363733313162333031393036303335353034303330633132343137303730366336353230353236663666373432303433343132303264323034373333333132363330323430363033353530343062306331643431373037303663363532303433363537323734363936363639363336313734363936663665323034313735373436383666373236393734373933313133333031313036303335353034306130633061343137303730366336353230343936653633326533313062333030393036303335353034303631333032353535333330316531373064333133343330333533303336333233333334333633333330356131373064333233393330333533303336333233333334333633333330356133303761333132653330326330363033353530343033306332353431373037303663363532303431373037303663363936333631373436393666366532303439366537343635363737323631373436393666366532303433343132303264323034373333333132363330323430363033353530343062306331643431373037303663363532303433363537323734363936363639363336313734363936663665323034313735373436383666373236393734373933313133333031313036303335353034306130633061343137303730366336353230343936653633326533313062333030393036303335353034303631333032353535333330353933303133303630373261383634386365336430323031303630383261383634386365336430333031303730333432303030346630313731313834313964373634383564353161356532353831303737366538383061326566646537626165346465303864666334623933653133333536643536363562333561653232643039373736306432323465376262613038666437363137636538386362373662623636373062656338653832393834666635343435613338316637333038316634333034363036303832623036303130353035303730313031303433613330333833303336303630383262303630313035303530373330303138363261363837343734373033613266326636663633373337303265363137303730366336353265363336663664326636663633373337303330333432643631373037303663363537323666366637343633363136373333333031643036303335353164306530343136303431343233663234396334346639336534656632376536633466363238366333666132626266643265346233303066303630333535316431333031303166663034303533303033303130316666333031663036303335353164323330343138333031363830313462626230646561313538333338383961613438613939646562656264656261666461636232346162333033373036303335353164316630343330333032653330326361303261613032383836323636383734373437303361326632663633373236633265363137303730366336353265363336663664326636313730373036633635373236663666373436333631363733333265363337323663333030653036303335353164306630313031666630343034303330323031303633303130303630613261383634383836663736333634303630323065303430323035303033303061303630383261383634386365336430343033303230333637303033303634303233303361636637323833353131363939623138366662333563333536636136326266663431376564643930663735346461323865626566313963383135653432623738396638393866373962353939663938643534313064386639646539633266653032333033323264643534343231623061333035373736633564663333383362393036376664313737633263323136643936346663363732363938323132366635346638376137643162393963623962303938393231363130363939306630393932316430303030333138323031386233303832303138373032303130313330383138363330376133313265333032633036303335353034303330633235343137303730366336353230343137303730366336393633363137343639366636653230343936653734363536373732363137343639366636653230343334313230326432303437333333313236333032343036303335353034306230633164343137303730366336353230343336353732373436393636363936333631373436393666366532303431373537343638366637323639373437393331313333303131303630333535303430613063306134313730373036633635323034393665363332653331306233303039303630333535303430363133303235353533303230383463333034313439353139643534333633303064303630393630383634383031363530333034303230313035303061303831393533303138303630393261383634383836663730643031303930333331306230363039326138363438383666373064303130373031333031633036303932613836343838366637306430313039303533313066313730643331333933303338333133393331333733313332333333303561333032613036303932613836343838366637306430313039333433313164333031623330306430363039363038363438303136353033303430323031303530306131306130363038326138363438636533643034303330323330326630363039326138363438383666373064303130393034333132323034323062303731303365313430613462386231376262613230316130336163643036396234653431366232613263383066383661383338313435633239373566633131333030613036303832613836343863653364303430333032303434363330343430323230343639306264636637626461663833636466343934396534633035313039656463663334373665303564373261313264376335666538633033303033343464663032323032363764353863393365626233353031333836363062353730373938613064643731313734316262353864626436613138363633353038353431656565393035303030303030303030303030227D \ No newline at end of file From 096edb816280fcfd1b32726a6b57ced654626e82 Mon Sep 17 00:00:00 2001 From: raclim <raclim@nyu.edu> Date: Wed, 4 Dec 2024 12:53:07 -0500 Subject: [PATCH 27/39] move to public folder --- .../.well-known}/apple-developer-merchantid-domain-association | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {.well-known => public/.well-known}/apple-developer-merchantid-domain-association (100%) diff --git a/.well-known/apple-developer-merchantid-domain-association b/public/.well-known/apple-developer-merchantid-domain-association similarity index 100% rename from .well-known/apple-developer-merchantid-domain-association rename to public/.well-known/apple-developer-merchantid-domain-association From 89a81d8638f513b900ad80d46f5725be26bac898 Mon Sep 17 00:00:00 2001 From: raclim <raclim@nyu.edu> Date: Wed, 4 Dec 2024 13:42:45 -0500 Subject: [PATCH 28/39] ensure content type is set correctly in server --- server/server.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/server/server.js b/server/server.js index 8b4d196451..cfa0c69a15 100644 --- a/server/server.js +++ b/server/server.js @@ -125,6 +125,27 @@ if (process.env.BASIC_USERNAME && process.env.BASIC_PASSWORD) { ); } +// routing to serve files in .well-known with specific content type +// temporary addition for the apple pay integration with donorbox +app.use( + '/.well-known/apple-developer-merchantid-domain-association', + (req, res, next) => { + const filePath = path.join( + __dirname, + '../public/.well-known/apple-developer-merchantid-domain-association' + ); + + res.setHeader('Content-Type', 'text/plain'); + + res.sendFile(filePath, (err) => { + if (err) { + console.error('Error serving file:', err); + next(err); + } + }); + } +); + // Body parser, cookie parser, sessions, serve public assets app.use( '/locales', From 41b10f9393efc03ef9044720d2a4738d010cc0a6 Mon Sep 17 00:00:00 2001 From: raclim <raclim@nyu.edu> Date: Wed, 4 Dec 2024 15:09:59 -0500 Subject: [PATCH 29/39] set absolute path --- server/server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/server.js b/server/server.js index cfa0c69a15..ce33b047d9 100644 --- a/server/server.js +++ b/server/server.js @@ -130,7 +130,7 @@ if (process.env.BASIC_USERNAME && process.env.BASIC_PASSWORD) { app.use( '/.well-known/apple-developer-merchantid-domain-association', (req, res, next) => { - const filePath = path.join( + const filePath = path.resolve( __dirname, '../public/.well-known/apple-developer-merchantid-domain-association' ); From 50077b63f422f511ddb63ab361cd4a8913c0d5c8 Mon Sep 17 00:00:00 2001 From: raclim <raclim@nyu.edu> Date: Wed, 4 Dec 2024 15:38:40 -0500 Subject: [PATCH 30/39] explicitly set root path --- server/server.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/server/server.js b/server/server.js index ce33b047d9..2226a42749 100644 --- a/server/server.js +++ b/server/server.js @@ -130,14 +130,12 @@ if (process.env.BASIC_USERNAME && process.env.BASIC_PASSWORD) { app.use( '/.well-known/apple-developer-merchantid-domain-association', (req, res, next) => { - const filePath = path.resolve( - __dirname, - '../public/.well-known/apple-developer-merchantid-domain-association' - ); + const rootPath = path.resolve(__dirname, '../public/.well-known'); + const fileName = 'apple-developer-merchantid-domain-association'; res.setHeader('Content-Type', 'text/plain'); - res.sendFile(filePath, (err) => { + res.sendFile(fileName, { root: rootPath }, (err) => { if (err) { console.error('Error serving file:', err); next(err); From 043eb44c23c3265c50edeff191d17539311bf25d Mon Sep 17 00:00:00 2001 From: raclim <raclim@nyu.edu> Date: Thu, 5 Dec 2024 09:30:18 -0500 Subject: [PATCH 31/39] remove apple pay setup in server --- server/server.js | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/server/server.js b/server/server.js index 2226a42749..8b4d196451 100644 --- a/server/server.js +++ b/server/server.js @@ -125,25 +125,6 @@ if (process.env.BASIC_USERNAME && process.env.BASIC_PASSWORD) { ); } -// routing to serve files in .well-known with specific content type -// temporary addition for the apple pay integration with donorbox -app.use( - '/.well-known/apple-developer-merchantid-domain-association', - (req, res, next) => { - const rootPath = path.resolve(__dirname, '../public/.well-known'); - const fileName = 'apple-developer-merchantid-domain-association'; - - res.setHeader('Content-Type', 'text/plain'); - - res.sendFile(fileName, { root: rootPath }, (err) => { - if (err) { - console.error('Error serving file:', err); - next(err); - } - }); - } -); - // Body parser, cookie parser, sessions, serve public assets app.use( '/locales', From 71fd59e9f15e7f2eac58529012113f0f58974094 Mon Sep 17 00:00:00 2001 From: raclim <raclim@nyu.edu> Date: Thu, 5 Dec 2024 09:47:15 -0500 Subject: [PATCH 32/39] 2.15.6 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3235c91550..83bed02c1d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "p5.js-web-editor", - "version": "2.15.5", + "version": "2.15.6", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "p5.js-web-editor", - "version": "2.15.5", + "version": "2.15.6", "license": "LGPL-2.1", "dependencies": { "@auth0/s3": "^1.0.0", diff --git a/package.json b/package.json index 8d26a46a4a..5bf70189ea 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "p5.js-web-editor", - "version": "2.15.5", + "version": "2.15.6", "description": "The web editor for p5.js.", "scripts": { "clean": "rimraf dist", From d42872fb495e52eb2f9b8968803678e56593feb8 Mon Sep 17 00:00:00 2001 From: raclim <43053081+raclim@users.noreply.github.com> Date: Fri, 6 Dec 2024 11:30:16 -0500 Subject: [PATCH 33/39] Remove Icon from README.md --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 36d7195f88..1314340425 100644 --- a/README.md +++ b/README.md @@ -46,5 +46,3 @@ Hosting and technical support has come from: <a href="https://releasehub.com/" target="_blank"><img width="100" src="https://assets.website-files.com/603dd147c5b0a480611bd348/603dd147c5b0a469bc1bd451_logo--dark.svg" /></a> <br /> <a href="https://www.browserstack.com/" target="_blank"><img width="100" src="https://user-images.githubusercontent.com/6063380/46976166-ab280a80-d096-11e8-983b-18dd38c8cc9b.png" /></a> -<br /> -<a href="https://www.fastly.com/" target="_blank"><img width="100" src="https://cdn-assets-us.frontify.com/s3/frontify-enterprise-files-us/eyJwYXRoIjoiZmFzdGx5XC9hY2NvdW50c1wvYzJcLzQwMDEwMjNcL3Byb2plY3RzXC8xMVwvYXNzZXRzXC80ZVwvNzc0XC9lZTZmYzlkOWYzNWE1NjBkNjUzNjFkNGI0NGQ2MTNmZi0xNjIxNTIyODg4LnBuZyJ9:fastly:nVuY3PxyFqQMI6elJsMzxAGLH3IFlmiuMdacHAGRMkE?width=2400" /></a> From e1a4a97477269460f62889aaf0d7622b78307175 Mon Sep 17 00:00:00 2001 From: Tristan Espinoza <tristan.m.espinoza@gmail.com> Date: Wed, 8 Jan 2025 13:16:29 -0800 Subject: [PATCH 34/39] fix: moved usermenu back into menubar to reenable dropdown behavior --- client/modules/IDE/components/Header/Nav.jsx | 2 +- client/styles/components/_nav.scss | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/client/modules/IDE/components/Header/Nav.jsx b/client/modules/IDE/components/Header/Nav.jsx index 6ddc38bda2..a608c2d214 100644 --- a/client/modules/IDE/components/Header/Nav.jsx +++ b/client/modules/IDE/components/Header/Nav.jsx @@ -41,8 +41,8 @@ const Nav = ({ layout }) => { <header className="nav__header"> <Menubar> <LeftLayout layout={layout} /> + <UserMenu /> </Menubar> - <UserMenu /> </header> </> ); diff --git a/client/styles/components/_nav.scss b/client/styles/components/_nav.scss index 4ddc96b327..b2dacee5e8 100644 --- a/client/styles/components/_nav.scss +++ b/client/styles/components/_nav.scss @@ -3,6 +3,7 @@ .nav { height: #{math.div(42, $base-font-size)}rem; display: flex; + width: 100%; flex-direction: row; justify-content: space-between; From 3022bb81637200c36f5b8e5202e0a28ba2331fab Mon Sep 17 00:00:00 2001 From: Tristan Espinoza <tristan.m.espinoza@gmail.com> Date: Wed, 8 Jan 2025 13:35:38 -0800 Subject: [PATCH 35/39] chore: added reference article as comment --- client/components/Menubar/MenubarMenu.jsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/components/Menubar/MenubarMenu.jsx b/client/components/Menubar/MenubarMenu.jsx index 34192b237b..8ad7f4cfef 100644 --- a/client/components/Menubar/MenubarMenu.jsx +++ b/client/components/Menubar/MenubarMenu.jsx @@ -1,3 +1,5 @@ +// https://blog.logrocket.com/building-accessible-menubar-component-react + import classNames from 'classnames'; import PropTypes from 'prop-types'; import React, { useContext, useMemo } from 'react'; From d7e2dc1a93ff07547e1ea395fb276dd71a78a29c Mon Sep 17 00:00:00 2001 From: Tristan Espinoza <tristan.m.espinoza@gmail.com> Date: Sat, 11 Jan 2025 17:25:53 -0800 Subject: [PATCH 36/39] fix: removed layout shift on user and example pages caused by height:100% --- client/styles/components/_nav.scss | 1 - 1 file changed, 1 deletion(-) diff --git a/client/styles/components/_nav.scss b/client/styles/components/_nav.scss index b2dacee5e8..58691ff251 100644 --- a/client/styles/components/_nav.scss +++ b/client/styles/components/_nav.scss @@ -16,7 +16,6 @@ display: flex; flex-direction: row; justify-content: space-between; - height: 100%; align-items: center; @include themify() { From cca3bd5eaab174f8251285442527e24195c07f46 Mon Sep 17 00:00:00 2001 From: Tristan Espinoza <tristan.m.espinoza@gmail.com> Date: Sat, 11 Jan 2025 18:29:51 -0800 Subject: [PATCH 37/39] refactor: renamed MenubarMenu to MenubarSubmenu for clarity --- .../Menubar/{MenubarMenu.jsx => MenubarSubmenu.jsx} | 10 +++++----- client/modules/IDE/components/Header/MobileNav.jsx | 2 +- client/modules/IDE/components/Header/Nav.jsx | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) rename client/components/Menubar/{MenubarMenu.jsx => MenubarSubmenu.jsx} (94%) diff --git a/client/components/Menubar/MenubarMenu.jsx b/client/components/Menubar/MenubarSubmenu.jsx similarity index 94% rename from client/components/Menubar/MenubarMenu.jsx rename to client/components/Menubar/MenubarSubmenu.jsx index 8ad7f4cfef..ae278bdf4f 100644 --- a/client/components/Menubar/MenubarMenu.jsx +++ b/client/components/Menubar/MenubarSubmenu.jsx @@ -75,10 +75,10 @@ MenubarList.defaultProps = { }; /* ------------------------------------------------------------------------------------------------- - * MenubarMenu + * MenubarSubmenu * -----------------------------------------------------------------------------------------------*/ -function MenubarMenu({ id, title, children }) { +function MenubarSubmenu({ id, title, children }) { const { isOpen } = useMenuProps(id); return ( @@ -89,14 +89,14 @@ function MenubarMenu({ id, title, children }) { ); } -MenubarMenu.propTypes = { +MenubarSubmenu.propTypes = { id: PropTypes.string.isRequired, title: PropTypes.node.isRequired, children: PropTypes.node }; -MenubarMenu.defaultProps = { +MenubarSubmenu.defaultProps = { children: null }; -export default MenubarMenu; +export default MenubarSubmenu; diff --git a/client/modules/IDE/components/Header/MobileNav.jsx b/client/modules/IDE/components/Header/MobileNav.jsx index c1fd718a8f..349d3d1709 100644 --- a/client/modules/IDE/components/Header/MobileNav.jsx +++ b/client/modules/IDE/components/Header/MobileNav.jsx @@ -7,7 +7,7 @@ import { sortBy } from 'lodash'; import classNames from 'classnames'; import { ParentMenuContext } from '../../../../components/Menubar/contexts'; import Menubar from '../../../../components/Menubar/Menubar'; -import { useMenuProps } from '../../../../components/Menubar/MenubarMenu'; +import { useMenuProps } from '../../../../components/Menubar/MenubarSubmenu'; import NavMenuItem from '../../../../components/Menubar/MenubarItem'; import { prop, remSize } from '../../../../theme'; import AsteriskIcon from '../../../../images/p5-asterisk.svg'; diff --git a/client/modules/IDE/components/Header/Nav.jsx b/client/modules/IDE/components/Header/Nav.jsx index a608c2d214..a20fe1ac09 100644 --- a/client/modules/IDE/components/Header/Nav.jsx +++ b/client/modules/IDE/components/Header/Nav.jsx @@ -4,7 +4,7 @@ import { sortBy } from 'lodash'; import { Link } from 'react-router-dom'; import PropTypes from 'prop-types'; import { useTranslation } from 'react-i18next'; -import MenubarMenu from '../../../../components/Menubar/MenubarMenu'; +import MenubarMenu from '../../../../components/Menubar/MenubarSubmenu'; import MenubarItem from '../../../../components/Menubar/MenubarItem'; import { availableLanguages, languageKeyToLabel } from '../../../../i18n'; import getConfig from '../../../../utils/getConfig'; From d4b76729e96cc4a1d9ae55cfea3c78b966bb48b0 Mon Sep 17 00:00:00 2001 From: Tristan Espinoza <tristan.m.espinoza@gmail.com> Date: Sat, 11 Jan 2025 18:48:38 -0800 Subject: [PATCH 38/39] fix: renamed MenubarMenu components to MenubarSubmenu --- client/modules/IDE/components/Header/Nav.jsx | 26 ++++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/client/modules/IDE/components/Header/Nav.jsx b/client/modules/IDE/components/Header/Nav.jsx index a20fe1ac09..95bfaccd85 100644 --- a/client/modules/IDE/components/Header/Nav.jsx +++ b/client/modules/IDE/components/Header/Nav.jsx @@ -4,7 +4,7 @@ import { sortBy } from 'lodash'; import { Link } from 'react-router-dom'; import PropTypes from 'prop-types'; import { useTranslation } from 'react-i18next'; -import MenubarMenu from '../../../../components/Menubar/MenubarSubmenu'; +import MenubarSubmenu from '../../../../components/Menubar/MenubarSubmenu'; import MenubarItem from '../../../../components/Menubar/MenubarItem'; import { availableLanguages, languageKeyToLabel } from '../../../../i18n'; import getConfig from '../../../../utils/getConfig'; @@ -163,7 +163,7 @@ const ProjectMenu = () => { </a> )} </li> - <MenubarMenu id="file" title={t('Nav.File.Title')}> + <MenubarSubmenu id="file" title={t('Nav.File.Title')}> <MenubarItem onClick={newSketch}>{t('Nav.File.New')}</MenubarItem> <MenubarItem hideIf={ @@ -208,8 +208,8 @@ const ProjectMenu = () => { > {t('Nav.File.Examples')} </MenubarItem> - </MenubarMenu> - <MenubarMenu id="edit" title={t('Nav.Edit.Title')}> + </MenubarSubmenu> + <MenubarSubmenu id="edit" title={t('Nav.Edit.Title')}> <MenubarItem onClick={cmRef.current?.tidyCode}> {t('Nav.Edit.TidyCode')} <span className="nav__keyboard-shortcut">{metaKeyName}+Shift+F</span> @@ -222,8 +222,8 @@ const ProjectMenu = () => { {t('Nav.Edit.Replace')} <span className="nav__keyboard-shortcut">{replaceCommand}</span> </MenubarItem> - </MenubarMenu> - <MenubarMenu id="sketch" title={t('Nav.Sketch.Title')}> + </MenubarSubmenu> + <MenubarSubmenu id="sketch" title={t('Nav.Sketch.Title')}> <MenubarItem onClick={() => dispatch(newFile(rootFile.id))}> {t('Nav.Sketch.AddFile')} <span className="nav__keyboard-shortcut">{newFileCommand}</span> @@ -241,8 +241,8 @@ const ProjectMenu = () => { Shift+{metaKeyName}+Enter </span> </MenubarItem> - </MenubarMenu> - <MenubarMenu id="help" title={t('Nav.Help.Title')}> + </MenubarSubmenu> + <MenubarSubmenu id="help" title={t('Nav.Help.Title')}> <MenubarItem onClick={() => dispatch(showKeyboardShortcutModal())}> {t('Nav.Help.KeyboardShortcuts')} </MenubarItem> @@ -250,7 +250,7 @@ const ProjectMenu = () => { {t('Nav.Help.Reference')} </MenubarItem> <MenubarItem href="/about">{t('Nav.Help.About')}</MenubarItem> - </MenubarMenu> + </MenubarSubmenu> {getConfig('TRANSLATIONS_ENABLED') && <LanguageMenu />} </ul> ); @@ -266,14 +266,14 @@ const LanguageMenu = () => { } return ( - <MenubarMenu id="lang" title={languageKeyToLabel(language)}> + <MenubarSubmenu id="lang" title={languageKeyToLabel(language)}> {sortBy(availableLanguages).map((key) => ( // eslint-disable-next-line react/jsx-no-bind <MenubarItem key={key} value={key} onClick={handleLangSelection}> {languageKeyToLabel(key)} </MenubarItem> ))} - </MenubarMenu> + </MenubarSubmenu> ); }; @@ -310,7 +310,7 @@ const AuthenticatedUserMenu = () => { return ( <ul className="nav__items-right" title="user-menu"> - <MenubarMenu + <MenubarSubmenu id="account" title={ <span> @@ -334,7 +334,7 @@ const AuthenticatedUserMenu = () => { <MenubarItem onClick={() => dispatch(logoutUser())}> {t('Nav.Auth.LogOut')} </MenubarItem> - </MenubarMenu> + </MenubarSubmenu> </ul> ); }; From de201ffad0024550e859b0657134c255d7b0b282 Mon Sep 17 00:00:00 2001 From: Tristan Espinoza <tristan.m.espinoza@gmail.com> Date: Sat, 11 Jan 2025 19:43:32 -0800 Subject: [PATCH 39/39] feat: supprt listbox role for language selection menu --- client/components/Menubar/MenubarItem.jsx | 21 +++++-- client/components/Menubar/MenubarSubmenu.jsx | 59 +++++++++++++++----- client/modules/IDE/components/Header/Nav.jsx | 15 ++++- 3 files changed, 76 insertions(+), 19 deletions(-) diff --git a/client/components/Menubar/MenubarItem.jsx b/client/components/Menubar/MenubarItem.jsx index 8c08aa9c03..8d595bb5cd 100644 --- a/client/components/Menubar/MenubarItem.jsx +++ b/client/components/Menubar/MenubarItem.jsx @@ -3,7 +3,13 @@ import React, { useContext, useMemo } from 'react'; import ButtonOrLink from '../../common/ButtonOrLink'; import { MenubarContext, ParentMenuContext } from './contexts'; -function MenubarItem({ hideIf, className, ...rest }) { +function MenubarItem({ + hideIf, + className, + role: customRole, + selected, + ...rest +}) { const parent = useContext(ParentMenuContext); const { createMenuItemHandlers } = useContext(MenubarContext); @@ -17,9 +23,12 @@ function MenubarItem({ hideIf, className, ...rest }) { return null; } + const role = customRole || 'menuitem'; + const ariaSelected = role === 'option' ? { 'aria-selected': selected } : {}; + return ( <li className={className}> - <ButtonOrLink {...rest} {...handlers} role="menuitem" /> + <ButtonOrLink {...rest} {...handlers} {...ariaSelected} role={role} /> </li> ); } @@ -32,14 +41,18 @@ MenubarItem.propTypes = { * Provides a way to deal with optional items. */ hideIf: PropTypes.bool, - className: PropTypes.string + className: PropTypes.string, + role: PropTypes.oneOf(['menuitem', 'option']), + selected: PropTypes.bool }; MenubarItem.defaultProps = { onClick: null, value: null, hideIf: false, - className: 'nav__dropdown-item' + className: 'nav__dropdown-item', + role: 'menuitem', + selected: false }; export default MenubarItem; diff --git a/client/components/Menubar/MenubarSubmenu.jsx b/client/components/Menubar/MenubarSubmenu.jsx index ae278bdf4f..13b0e33177 100644 --- a/client/components/Menubar/MenubarSubmenu.jsx +++ b/client/components/Menubar/MenubarSubmenu.jsx @@ -25,15 +25,15 @@ export function useMenuProps(id) { * MenubarTrigger * -----------------------------------------------------------------------------------------------*/ -function MenubarTrigger({ id, title, ...props }) { +function MenubarTrigger({ id, title, role, hasPopup, ...props }) { const { isOpen, handlers } = useMenuProps(id); return ( <button {...handlers} {...props} - role="menuitem" - aria-haspopup="menu" + role={role} + aria-haspopup={hasPopup} aria-expanded={isOpen} > <span className="nav__item-header">{title}</span> @@ -48,16 +48,23 @@ function MenubarTrigger({ id, title, ...props }) { MenubarTrigger.propTypes = { id: PropTypes.string.isRequired, - title: PropTypes.node.isRequired + title: PropTypes.node.isRequired, + role: PropTypes.string, + hasPopup: PropTypes.oneOf(['menu', 'listbox', 'true']) +}; + +MenubarTrigger.defaultProps = { + role: 'menuitem', + hasPopup: 'menu' }; /* ------------------------------------------------------------------------------------------------- * MenubarList * -----------------------------------------------------------------------------------------------*/ -function MenubarList({ id, children }) { +function MenubarList({ id, children, role, ...props }) { return ( - <ul className="nav__dropdown" role="menu"> + <ul className="nav__dropdown" role={role} {...props}> <ParentMenuContext.Provider value={id}> {children} </ParentMenuContext.Provider> @@ -67,24 +74,46 @@ function MenubarList({ id, children }) { MenubarList.propTypes = { id: PropTypes.string.isRequired, - children: PropTypes.node + children: PropTypes.node, + role: PropTypes.oneOf(['menu', 'listbox']) }; MenubarList.defaultProps = { - children: null + children: null, + role: 'menu' }; /* ------------------------------------------------------------------------------------------------- * MenubarSubmenu * -----------------------------------------------------------------------------------------------*/ -function MenubarSubmenu({ id, title, children }) { +function MenubarSubmenu({ + id, + title, + children, + triggerRole: customTriggerRole, + listRole: customListRole, + ...props +}) { const { isOpen } = useMenuProps(id); + const triggerRole = customTriggerRole || 'menuitem'; + const listRole = customListRole || 'menu'; + + const hasPopup = listRole === 'listbox' ? 'listbox' : 'menu'; + return ( <li className={classNames('nav__item', isOpen && 'nav__item--open')}> - <MenubarTrigger id={id} title={title} /> - <MenubarList id={id}>{children}</MenubarList> + <MenubarTrigger + id={id} + title={title} + role={triggerRole} + hasPopup={hasPopup} + {...props} + /> + <MenubarList id={id} role={listRole}> + {children} + </MenubarList> </li> ); } @@ -92,11 +121,15 @@ function MenubarSubmenu({ id, title, children }) { MenubarSubmenu.propTypes = { id: PropTypes.string.isRequired, title: PropTypes.node.isRequired, - children: PropTypes.node + children: PropTypes.node, + triggerRole: PropTypes.string, + listRole: PropTypes.string }; MenubarSubmenu.defaultProps = { - children: null + children: null, + triggerRole: 'menuitem', + listRole: 'menu' }; export default MenubarSubmenu; diff --git a/client/modules/IDE/components/Header/Nav.jsx b/client/modules/IDE/components/Header/Nav.jsx index 95bfaccd85..f40e9137bf 100644 --- a/client/modules/IDE/components/Header/Nav.jsx +++ b/client/modules/IDE/components/Header/Nav.jsx @@ -266,10 +266,21 @@ const LanguageMenu = () => { } return ( - <MenubarSubmenu id="lang" title={languageKeyToLabel(language)}> + <MenubarSubmenu + id="lang" + title={languageKeyToLabel(language)} + triggerRole="button" + listRole="listbox" + > {sortBy(availableLanguages).map((key) => ( // eslint-disable-next-line react/jsx-no-bind - <MenubarItem key={key} value={key} onClick={handleLangSelection}> + <MenubarItem + key={key} + value={key} + onClick={handleLangSelection} + role="option" + selected={key === language} + > {languageKeyToLabel(key)} </MenubarItem> ))}