Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
a2f0390
chore: initial commit - making elements focusable and react to enter
kbangelov Dec 15, 2025
06d2d62
feat: added some accessability with arrow logic
kbangelov Dec 19, 2025
34b18df
chore: addressed copilot stuff
kbangelov Dec 19, 2025
1c05d24
feat: refactored dropdown menu logic
kbangelov Dec 22, 2025
9914e61
feat: more dropdowns in new files and stuff
kbangelov Dec 23, 2025
328b234
chore: completed functionality and refactored code inconsistencies
kbangelov Dec 29, 2025
0c20327
chore: added aria-expanded everywhere on dropdowns
kbangelov Dec 29, 2025
6f3a80d
chore: code redacting
kbangelov Dec 29, 2025
05fb1da
chore: cleanup of isOpen logic for menus, since it is reimplemented
kbangelov Dec 29, 2025
333f1f8
chore: adjusted key press logic to match wanted functionality
kbangelov Dec 29, 2025
f1ea8f6
Merge branch 'develop' into task/uepr-445-ensure-navigable-toolbar
kbangelov Dec 30, 2025
2ce4eac
chore: post-merge accessibility readjustments
kbangelov Dec 30, 2025
fdac2f4
chore: minor code changes
kbangelov Dec 30, 2025
77f2ac6
chore: translated labels and some more adjustments
kbangelov Dec 30, 2025
3f518b5
chore: undeleted a line
kbangelov Jan 5, 2026
83bdad7
chore: moved file
kbangelov Jan 6, 2026
5640a76
chore: beginning to remove now obsolete old logic related to opening …
kbangelov Jan 7, 2026
4f92988
chore: refactored menu navigation logic via a hook
kbangelov Jan 8, 2026
287e1a5
chore: more refactoring
kbangelov Jan 8, 2026
19fef13
chore: removed old menus logic
kbangelov Jan 8, 2026
d2fd053
chore: package-lock.json back to original
kbangelov Jan 8, 2026
016f46f
chore: deleting rows
kbangelov Jan 8, 2026
d75933d
chore: refactored some menuRef, itemRef code
kbangelov Jan 9, 2026
e186ba9
chore: passing down remix message
kbangelov Jan 9, 2026
37c9974
chore: added some isRequired-s
kbangelov Jan 9, 2026
fa75484
chore: brought some elements inside file menu
kbangelov Jan 9, 2026
8626a1a
chore: fixed some aria labels
kbangelov Jan 9, 2026
965434b
chore: context is function instead of class now
kbangelov Jan 9, 2026
dbaa015
chore: more refactoring and semantic improvements
kbangelov Jan 12, 2026
824f352
chore: tiny bit more refactoring
kbangelov Jan 12, 2026
bad3449
Merge branch 'develop' into task/uepr-445-ensure-navigable-toolbar
kbangelov Jan 12, 2026
8c76688
chore: fix pipeline fail
kbangelov Jan 12, 2026
58b77bf
chore: fixing test
kbangelov Jan 12, 2026
408f6e2
chore: package-lock.json
kbangelov Jan 12, 2026
c4c59b1
chore: fixed unit test
kbangelov Jan 12, 2026
0ddeddb
chore: completed nav bar with all optional buttons
kbangelov Jan 15, 2026
7599d05
chore: refactored code, addressed comments
kbangelov Jan 15, 2026
7618f41
chore: changed accessibility messages and updated jsdoc
kbangelov Jan 15, 2026
a0985ea
chore: more code refactoring and comment addressing
kbangelov Jan 16, 2026
3a935c4
chore: removed redundant aria-label usage
kbangelov Jan 16, 2026
6864586
chore: fixing more things
kbangelov Jan 16, 2026
84124af
chore: removed old unnecessary code
kbangelov Jan 16, 2026
67c14ac
Merge branch 'develop' into task/uepr-445-ensure-navigable-toolbar
kbangelov Jan 16, 2026
3290043
chore: restore enter functionality thorugh hook
kbangelov Jan 16, 2026
f80c5d7
feat: reimplemented algorithm for finding inner menus via HTML tag BFS
kbangelov Jan 19, 2026
734a948
chore: fix unit tests
kbangelov Jan 19, 2026
f17c40f
chore: fixed integration tests
kbangelov Jan 19, 2026
ed3395d
Merge branch 'develop' into task/uepr-445-ensure-navigable-toolbar
kbangelov Jan 19, 2026
dd8b41c
chore: renamed methods
kbangelov Jan 19, 2026
c539ae6
chore: addressing some copilot comments
kbangelov Jan 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 55 additions & 58 deletions packages/scratch-gui/src/components/gui/gui.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,63 +46,64 @@ import DebugModal from '../debug-modal/debug-modal.jsx';
import {setPlatform} from '../../reducers/platform.js';
import {setTheme} from '../../reducers/settings.js';
import {PLATFORM} from '../../lib/platform.js';
import {MenuRefProvider} from '../../contexts/menu-ref-context.jsx';
import {ModalFocusProvider} from '../../contexts/modal-focus-context.jsx';

const ariaMessages = defineMessages({
menuBar: {
id: 'gui.aria.menuBar',
defaultMessage: 'Menu topbar',
description: 'ARIA label for the top menu bar'
description: 'accessibility label for the top menu bar'
},
editor: {
id: 'gui.aria.editor',
defaultMessage: 'Editor',
description: 'ARIA label for the main editor area'
description: 'accessibility label for the main editor area'
},
tabList: {
id: 'gui.aria.tabList',
defaultMessage: 'Tab list',
description: 'ARIA label for the editor tab list'
description: 'accessibility label for the editor tab list'
},
codePanel: {
id: 'gui.aria.codePanel',
defaultMessage: 'Code editor panel',
description: 'ARIA label for the code editor panel'
description: 'accessibility label for the code editor panel'
},
costumesPanel: {
id: 'gui.aria.costumesPanel',
defaultMessage: 'Costumes editor panel',
description: 'ARIA label for the costumes editor panel'
description: 'accessibility label for the costumes editor panel'
},
backdropsPanel: {
id: 'gui.aria.backdropsPanel',
defaultMessage: 'Backdrops editor panel',
description: 'ARIA label for the backdrops editor panel'
description: 'accessibility label for the backdrops editor panel'
},
soundsPanel: {
id: 'gui.aria.soundsPanel',
defaultMessage: 'Sounds editor panel',
description: 'ARIA label for the sounds editor panel'
description: 'accessibility label for the sounds editor panel'
},
backpack: {
id: 'gui.aria.backpack',
defaultMessage: 'Backpack',
description: 'ARIA label for the backpack'
description: 'accessibility label for the backpack'
},
stageAndTarget: {
id: 'gui.aria.stageAndTarget',
defaultMessage: 'Stage and target',
description: 'ARIA label for stage and target area'
description: 'accessibility label for stage and target area'
},
stage: {
id: 'gui.aria.stage',
defaultMessage: 'Stage',
description: 'ARIA label for the stage'
description: 'accessibility label for the stage'
},
targetPane: {
id: 'gui.aria.targetPane',
defaultMessage: 'Target pane',
description: 'ARIA label for the target pane'
description: 'accessibility label for the target pane'
}
});

Expand All @@ -114,7 +115,6 @@ const GUIComponent = props => {
const intl = useIntl();
const {
accountMenuOptions,
accountNavOpen,
activeTabIndex,
alertsVisible,
authorId,
Expand Down Expand Up @@ -162,8 +162,6 @@ const GUIComponent = props => {
menuBarHidden,
renderLogin,
onClickAbout,
onClickAccountNav,
onCloseAccountNav,
onLogOut,
onOpenRegistration,
onToggleLoginOpen,
Expand Down Expand Up @@ -332,47 +330,49 @@ const GUIComponent = props => {
onRequestClose={onRequestCloseBackdropLibrary}
/>
) : null}
{!menuBarHidden && <MenuBar
ariaRole="banner"
ariaLabel={intl.formatMessage(ariaMessages.menuBar)}
accountNavOpen={accountNavOpen}
authorId={authorId}
authorThumbnailUrl={authorThumbnailUrl}
authorUsername={authorUsername}
authorAvatarBadge={authorAvatarBadge}
canChangeLanguage={canChangeLanguage}
canChangeColorMode={canChangeColorMode}
canChangeTheme={canChangeTheme}
canCreateCopy={canCreateCopy}
canCreateNew={canCreateNew}
canEditTitle={canEditTitle}
canManageFiles={canManageFiles}
canRemix={canRemix}
canSave={canSave}
canShare={canShare}
className={styles.menuBarPosition}
enableCommunity={enableCommunity}
hasActiveMembership={hasActiveMembership}
isShared={isShared}
isTotallyNormal={isTotallyNormal}
logo={logo}
renderLogin={renderLogin}
showComingSoon={showComingSoon}
onClickAbout={onClickAbout}
onClickAccountNav={onClickAccountNav}
onClickLogo={onClickLogo}
onCloseAccountNav={onCloseAccountNav}
onLogOut={onLogOut}
onOpenRegistration={onOpenRegistration}
onProjectTelemetryEvent={onProjectTelemetryEvent}
onSeeCommunity={onSeeCommunity}
onShare={onShare}
onStartSelectingFileUpload={onStartSelectingFileUpload}
onToggleLoginOpen={onToggleLoginOpen}
userOwnsProject={userOwnsProject}
username={username}
accountMenuOptions={accountMenuOptions}
/>}
{/* TODO - in case of moving MenuRefProvider which seems likely,
make sure to move it from tests as well */}
{!menuBarHidden && <MenuRefProvider>
<MenuBar
ariaRole="banner"
ariaLabel={intl.formatMessage(ariaMessages.menuBar)}
authorId={authorId}
authorThumbnailUrl={authorThumbnailUrl}
authorUsername={authorUsername}
authorAvatarBadge={authorAvatarBadge}
canChangeLanguage={canChangeLanguage}
canChangeColorMode={canChangeColorMode}
canChangeTheme={canChangeTheme}
canCreateCopy={canCreateCopy}
canCreateNew={canCreateNew}
canEditTitle={canEditTitle}
canManageFiles={canManageFiles}
canRemix={canRemix}
canSave={canSave}
canShare={canShare}
className={styles.menuBarPosition}
enableCommunity={enableCommunity}
hasActiveMembership={hasActiveMembership}
isShared={isShared}
isTotallyNormal={isTotallyNormal}
logo={logo}
renderLogin={renderLogin}
showComingSoon={showComingSoon}
onClickAbout={onClickAbout}
onClickLogo={onClickLogo}
onLogOut={onLogOut}
onOpenRegistration={onOpenRegistration}
onProjectTelemetryEvent={onProjectTelemetryEvent}
onSeeCommunity={onSeeCommunity}
onShare={onShare}
onStartSelectingFileUpload={onStartSelectingFileUpload}
onToggleLoginOpen={onToggleLoginOpen}
userOwnsProject={userOwnsProject}
username={username}
accountMenuOptions={accountMenuOptions}
/>
</MenuRefProvider>
}
<Box className={classNames(boxStyles, styles.flexWrapper)}>
<Box
role="main"
Expand Down Expand Up @@ -573,7 +573,6 @@ const GUIComponent = props => {
};

GUIComponent.propTypes = {
accountNavOpen: PropTypes.bool,
accountMenuOptions: AccountMenuOptionsPropTypes,
activeTabIndex: PropTypes.number,
authorId: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]), // can be false
Expand Down Expand Up @@ -620,9 +619,7 @@ GUIComponent.propTypes = {
onActivateCostumesTab: PropTypes.func,
onActivateSoundsTab: PropTypes.func,
onActivateTab: PropTypes.func,
onClickAccountNav: PropTypes.func,
onClickLogo: PropTypes.func,
onCloseAccountNav: PropTypes.func,
onExtensionButtonClick: PropTypes.func,
onLogOut: PropTypes.func,
onNewSpriteClick: PropTypes.func,
Expand Down
131 changes: 131 additions & 0 deletions packages/scratch-gui/src/components/menu-bar/about-menu.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import React, {useCallback} from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import {useIntl, defineMessage} from 'react-intl';
import {connect} from 'react-redux';

import MenuBarMenu from './menu-bar-menu.jsx';
import Button from '../button/button.jsx';
import {MenuItem} from '../menu/menu.jsx';
import useMenuNavigation from '../../hooks/use-menu-navigation.jsx';

import styles from './menu-bar.css';
import aboutIcon from './icon--about.svg';

const aboutMenuMessage = defineMessage({
id: 'gui.aria.aboutMenu',
defaultMessage: 'About menu',
description: 'accessibility label for About menu'
});

const AboutButton = props => {
const intl = useIntl();

return (<Button
className={classNames(styles.menuBarItem, styles.hoverable)}
iconClassName={styles.aboutIcon}
iconSrc={aboutIcon}
onClick={props.onClick}
aria-label={intl.formatMessage(aboutMenuMessage)}
/>);
};

AboutButton.propTypes = {
onClick: PropTypes.func.isRequired
};

const AboutMenu = ({
onClick,
isRtl
}) => {
if (!onClick) {
// hide the button
return null;
}
if (typeof onClick === 'function') {
// make a button which calls a function
return <AboutButton onClick={onClick} />;
}

// assume it's an array of objects
// each item must have a 'title' FormattedMessage and a 'handleClick' function
// generate a menu with items for each object in the array

const intl = useIntl();

const {
menuRef,
isExpanded,
handleOnOpen,
handleOnClose,
handleKeyDown,
handleKeyDownOpenMenu
} = useMenuNavigation({
depth: 1
});

const wrapAboutMenuCallback = useCallback(
callback => () => {
callback();
handleOnClose();
},
[handleOnClose]
);

return (
<button
className={classNames(styles.menuBarItem, styles.hoverable, {
[styles.active]: isExpanded()
})}
onClick={handleOnOpen}
onKeyDown={handleKeyDown}
aria-label={intl.formatMessage(aboutMenuMessage)}
aria-expanded={isExpanded()}
ref={menuRef}
>
<img
className={styles.aboutIcon}
src={aboutIcon}
/>
<MenuBarMenu
className={classNames(styles.menuBarMenu)}
open={isExpanded()}
place={isRtl ? 'right' : 'left'}
onRequestClose={handleOnClose}
>
{
onClick.map(itemProps => (
<MenuItem
key={itemProps.title}
isRtl={isRtl}
onClick={wrapAboutMenuCallback(itemProps.onClick)}
onParentKeyPress={handleKeyDownOpenMenu}
data-menu-item="true"
>
{itemProps.title}
</MenuItem>
))
}
</MenuBarMenu>
</button>
);
};

AboutMenu.propTypes = {
isRtl: PropTypes.bool.isRequired,
onClick: PropTypes.oneOfType([
PropTypes.func, // button mode: call this callback when the About button is clicked
PropTypes.arrayOf( // menu mode: list of items in the About menu
PropTypes.shape({
title: PropTypes.string, // text for the menu item
onClick: PropTypes.func // call this callback when the menu item is clicked
})
)
])
};

const mapStateToProps = state => ({
isRtl: state.locales.isRtl
});

export default connect(mapStateToProps)(AboutMenu);
Loading
Loading