diff --git a/teachertool/src/components/styling/ActionAnnouncer.module.scss b/react-common/styles/react-common-accessibility.less similarity index 99% rename from teachertool/src/components/styling/ActionAnnouncer.module.scss rename to react-common/styles/react-common-accessibility.less index dd16f6f581fa..a220069ea210 100644 --- a/teachertool/src/components/styling/ActionAnnouncer.module.scss +++ b/react-common/styles/react-common-accessibility.less @@ -10,4 +10,4 @@ border: 0; white-space: nowrap; border-width: 0; -} +} \ No newline at end of file diff --git a/react-common/styles/react-common.less b/react-common/styles/react-common.less index 5103d5465e83..deb1d61a9065 100644 --- a/react-common/styles/react-common.less +++ b/react-common/styles/react-common.less @@ -30,6 +30,7 @@ @import "controls/ThumbsFeedback.less"; @import "controls/AIFooter.less"; @import "theming/base-theme.less"; +@import "./react-common-accessibility.less"; @import "./react-common-variables.less"; @import "./semantic-ui-overrides.less"; diff --git a/teachertool/src/components/ScreenReaderAnnouncer.tsx b/teachertool/src/components/ScreenReaderAnnouncer.tsx index 2ac53aba18ba..e0f1538bcbfd 100644 --- a/teachertool/src/components/ScreenReaderAnnouncer.tsx +++ b/teachertool/src/components/ScreenReaderAnnouncer.tsx @@ -1,6 +1,5 @@ import { useContext } from "react"; import { AppStateContext } from "../state/appStateContext"; -import css from "./styling/ActionAnnouncer.module.scss"; export interface ScreenReaderAnnouncerProps {} export const ScreenReaderAnnouncer: React.FC = () => { @@ -8,7 +7,7 @@ export const ScreenReaderAnnouncer: React.FC = () => return ( <> {teacherTool.screenReaderAnnouncement && ( -
+
{teacherTool.screenReaderAnnouncement}
)} diff --git a/webapp/src/toolbox.tsx b/webapp/src/toolbox.tsx index ea08dfd227e8..1e33630031d1 100644 --- a/webapp/src/toolbox.tsx +++ b/webapp/src/toolbox.tsx @@ -10,8 +10,6 @@ import * as coretsx from "./coretsx"; import Util = pxt.Util; import { fireClickOnEnter } from "./util" import { DeleteConfirmationModal } from "../../react-common/components/extensions/DeleteConfirmationModal" - -import * as Blockly from "blockly"; import { classList } from "../../react-common/components/util" export const enum CategoryNameID { @@ -169,7 +167,13 @@ export class Toolbox extends data.Component { this.setState({ visible: true }) } - setSelectedItem(item: CategoryItem) { + setSelectedItem(item: CategoryItem, name?: string) { + for (const item of this.items) { + if (item.nameid === name) { + this.setSelection(item, this.items.indexOf(item), true); + return; + } + } this.selectedItem = item; } @@ -196,13 +200,14 @@ export class Toolbox extends data.Component { setSearch() { // Focus the search box if it exists const searchBox = this.refs.searchbox as ToolboxSearch; - if (searchBox) searchBox.focus(); + if (searchBox) { + this.clear(); + searchBox.focus(); + } } clear() { this.clearSelection(); - this.selectedIndex = 0; - this.selectedTreeRow = undefined; } clearSelection() { @@ -263,8 +268,15 @@ export class Toolbox extends data.Component { } } - onCategoryClick = (treeRow: ToolboxCategory, index: number) => { - this.setSelection(treeRow, index, undefined, true); + onCategoryClick = (treeRow: ToolboxCategory, index: number, isClick = true) => { + if (isClick) { + return this.setSelection(treeRow, index, undefined, isClick); + } + const { customClick } = treeRow; + const { parent } = this.props; + if (customClick) { + customClick(parent); + } } focus(itemToFocus?: string) { @@ -480,10 +492,6 @@ export class Toolbox extends data.Component { } if (!this.rootElement) return; if (this.selectedIndex !== undefined && this.selectedTreeRow) { - if (this.selectedTreeRow === this.selectedItem.props.treeRow) { - // The flyout is already open with appropriate content. - return; - } // 'Focus' the selected item this.setSelection(this.selectedTreeRow, this.selectedIndex, true); } else { @@ -518,11 +526,17 @@ export class Toolbox extends data.Component { const charCode = core.keyCodeFromEvent(e); if (charCode == 40 /* Down arrow key */) { - this.nextItem(); + if (this.state.selectedItem) { + this.nextItem(); + } else { + this.selectFirstItem(); + } // Don't trigger scroll behaviour inside the toolbox. e.preventDefault(); } else if (charCode == 38 /* Up arrow key */) { - this.previousItem(); + if (this.state.selectedItem) { + this.previousItem(); + } // Don't trigger scroll behaviour inside the toolbox. e.preventDefault(); } else if ((charCode == 39 /* Right arrow key */ && !isRtl) @@ -540,9 +554,9 @@ export class Toolbox extends data.Component { this.closeFlyout(); this.props.parent.focusWorkspace(); } else if (charCode == core.ENTER_KEY || charCode == core.SPACE_KEY) { - const {onCategoryClick, treeRow, index} = this.selectedItem.props; + const {onCategoryClick, treeRow, index } = this.selectedItem.props; if (onCategoryClick) { - onCategoryClick(treeRow, index); + onCategoryClick(treeRow, index, false); e.preventDefault(); e.stopPropagation(); } @@ -652,7 +666,10 @@ export class Toolbox extends data.Component {
{ selected={selectedItem == "search"} treeRow={searchTreeRow} onCategoryClick={this.onCategoryClick} - ariaLevel={1} /> } {nonAdvancedCategories.map(treeRow => + { shouldAnimate={this.state.shouldAnimate} hasDeleteButton={treeRow.allowDelete} onDeleteClick={this.handleRemoveExtension} - ariaLevel={1} - isExpanded={expandedItem == treeRow.nameid} > - {treeRow.subcategories && ( -
- {treeRow.subcategories.map(subTreeRow => - - )} -
- )}
+ {treeRow.subcategories?.map(subTreeRow => + + )} +
+ )} {hasAdvanced && <> @@ -732,44 +745,36 @@ export class Toolbox extends data.Component { }} onCategoryClick={this.advancedClicked} topRowIndex={topRowIndex++} - ariaLevel={1} - isExpanded={showAdvanced} - > -
- { - advancedCategories.map(treeRow => - - {treeRow.subcategories && ( -
- {treeRow.subcategories.map(subTreeRow => - - )} -
- )} -
- ) - } -
- + ariaHidden={true} + /> + {advancedCategories.map(treeRow => + + + + {treeRow.subcategories?.map(subTreeRow => + + )} + + )} }
@@ -781,14 +786,17 @@ export class Toolbox extends data.Component { export interface CategoryItemProps extends TreeRowProps { toolbox: Toolbox; - onCategoryClick?: (treeRow: ToolboxCategory, index: number) => void; + onCategoryClick?: (treeRow: ToolboxCategory, index: number, isClick?: boolean) => void; index?: number; selectedIndex?: number; topRowIndex?: number; hasDeleteButton?: boolean; onDeleteClick?: (ns: string) => void; - ariaLevel: number; + ariaHidden?: boolean; + ariaLabel?: string; + ariaLevel?: number; isExpanded?: boolean; + className?: string; } export interface CategoryItemState { @@ -865,13 +873,13 @@ export class CategoryItem extends data.Component + { renderCore() { - const { selected, id, ariaLevel, ariaExpanded } = this.props; + const { className, selected, id, ariaHidden, ariaLabel, ariaLevel, ariaExpanded } = this.props; return (
{this.props.children}
@@ -1133,9 +1147,9 @@ export class ToolboxSearch extends data.Component