Skip to content
This repository was archived by the owner on Jul 9, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import throttle from 'lodash/throttle';
import { useRecoilValue } from 'recoil';
import { extractSchemaProperties, groupTriggersByPropertyReference, NoGroupingTriggerGroupName } from '@bfc/indexers';
import isEqual from 'lodash/isEqual';
import { Announced } from 'office-ui-fabric-react/lib/Announced';
import { useId } from '@uifabric/react-hooks';

import {
dispatcherState,
Expand Down Expand Up @@ -175,6 +177,8 @@ export const ProjectTree: React.FC<Props> = ({
const projectCollection: TreeDataPerProject[] = useRecoilValue(projectTreeSelectorFamily);
const jsonSchemaFilesByProjectId = useRecoilValue(jsonSchemaFilesByProjectIdSelector);

const projectTreeNavLabelId = useId('project-tree-nav-label');

// TODO Refactor to make sure tree is not generated until a new trigger/dialog is added. #5462
const createSubtree = useCallback(() => {
return projectCollection.map(createBotSubtree);
Expand Down Expand Up @@ -773,34 +777,39 @@ export const ProjectTree: React.FC<Props> = ({
return (
<div
ref={treeRef}
aria-label={formatMessage('Navigation pane')}
aria-labelledby={projectTreeNavLabelId}
className="ProjectTree"
css={root}
data-testid="ProjectTree"
>
<ProjectTreeHeader
ariaLabel={headerAriaLabel}
filterValue={filter}
menu={headerMenu}
placeholder={headerPlaceholder}
onFilter={onFilter}
/>
<FocusZone isCircularNavigation css={focusStyle} direction={FocusZoneDirection.vertical}>
<div
aria-label={formatMessage(
`{
<Announced
id={projectTreeNavLabelId}
message={formatMessage(
`Navigation pane, {
hasFilter, select,
false {}
other {search results for "{filter}":}
} {
dialogNum, plural,
=0 {No bots have}
=1 {One bot has}
=0 {no bots have}
=1 {one bot has}
other {# bots have}
} been found.
{
dialogNum, select,
0 {}
other {Press down arrow key to navigate the search results}
}`,
{ dialogNum: projectCollection.length }
{ dialogNum: projectCollection.length, filter: filter, hasFilter: !!filter }
)}
aria-live={'polite'}
/>
<div css={tree}>{projectTree}</div>
</FocusZone>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,12 @@ export interface ProjectTreeHeaderProps {
placeholder?: string;
ariaLabel?: string;
onFilter?: (newValue?: string) => void;
filterValue: string;
}

export const ProjectTreeHeader: React.FC<ProjectTreeHeaderProps> = ({
menu,
filterValue,
onFilter = () => {},
placeholder = '',
ariaLabel = '',
Expand Down Expand Up @@ -94,8 +96,10 @@ export const ProjectTreeHeader: React.FC<ProjectTreeHeaderProps> = ({
}) as IOverflowSetItemProps[];

const handleSearchBoxBlur = () => {
onFilter('');
setShowFilter(false);
if (!filterValue) {
onFilter('');
setShowFilter(false);
}
};

// Move focus back to the filter button after the filter search box gets closed
Expand Down Expand Up @@ -141,11 +145,7 @@ export const ProjectTreeHeader: React.FC<ProjectTreeHeaderProps> = ({
styles={searchBox}
onBlur={handleSearchBoxBlur}
onChange={(_e, value) => onFilter(value)}
onClear={(ev) => {
if (!ev.target.value) {
handleSearchBoxBlur();
}
}}
onClear={handleSearchBoxBlur}
/>
) : (
<div css={commands}>
Expand Down