Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

announce search results in RAI Vision dashboard toolbar for accessibility #2466

Merged
merged 1 commit into from
Dec 22, 2023
Merged
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
@@ -186,7 +186,8 @@ export class DataCharacteristics extends React.Component<
const filteredItems = getFilteredDataFromSearch(
this.props.searchValue,
this.props.items,
this.props.taskType
this.props.taskType,
this.props.onSearchUpdated
);
this.setState(
processItems(
@@ -262,7 +263,6 @@ export class DataCharacteristics extends React.Component<
showBackArrow[index] = true;
this.setState({ renderStartIndex, showBackArrow });
};

private loadPrevItems = (index: number) => (): void => {
const { renderStartIndex, showBackArrow } = this.state;
renderStartIndex[index] -= this.state.columnCount[index];
Original file line number Diff line number Diff line change
@@ -13,6 +13,7 @@ export interface IDataCharacteristicsProps extends ISearchable {
imageDim: number;
numRows: number;
taskType: string;
onSearchUpdated: (successCount: number, errorCount: number) => void;
selectItem(item: IVisionListItem): void;
}

@@ -188,7 +189,7 @@ export function processItems(
showBackArrow.push(false);
columnCount.push(0);
});
return {
const result = {
columnCount,
dropdownOptionsPredicted,
dropdownOptionsTrue,
@@ -201,6 +202,7 @@ export function processItems(
selectedKeysTrue,
showBackArrow
};
return result;
}

export function getLabelVisibility(
Original file line number Diff line number Diff line change
@@ -26,6 +26,7 @@ export interface IImageListProps extends ISearchable {
imageDim: number;
selectItem: (item: IVisionListItem) => void;
taskType: string;
onSearchUpdated: (successCount: number, errorCount: number) => void;
}

export interface IImageListState {
@@ -93,7 +94,8 @@ export class ImageList extends React.Component<
filteredItems = getFilteredDataFromSearch(
searchValue,
filteredItems,
this.props.taskType
this.props.taskType,
this.props.onSearchUpdated
);
}
return filteredItems;
Original file line number Diff line number Diff line change
@@ -187,7 +187,8 @@ export class TableList extends React.Component<
const filteredItems = getFilteredDataFromSearch(
searchValue,
items,
this.props.taskType
this.props.taskType,
this.props.onSearchUpdated
);
return filteredItems;
}
Original file line number Diff line number Diff line change
@@ -15,6 +15,7 @@ export interface ITableListProps extends ISearchable {
selectItem: (item: IVisionListItem) => void;
updateSelectedIndices: (indices: number[]) => void;
taskType: string;
onSearchUpdated: (successCount: number, errorCount: number) => void;
}

export interface ITableListState {
Original file line number Diff line number Diff line change
@@ -7,8 +7,10 @@ import {
ErrorCohort,
DatasetTaskType
} from "@responsible-ai/core-ui";
import { localization } from "@responsible-ai/localization";
import React from "react";

import { updateSearchTextAriaLabel } from "../utils/searchTextUtils";
import { visionExplanationDashboardStyles } from "../VisionExplanationDashboard.styles";
import {
TitleBarOptions,
@@ -31,6 +33,7 @@ export interface ITabsViewProps {
selectedItem: IVisionListItem | undefined;
selectedKey: string;
onItemSelect: (item: IVisionListItem) => void;
onSearchUpdated: (searchResultsAriaLabel: string) => void;
updateSelectedIndices: (indices: number[]) => void;
selectedCohort: ErrorCohort;
setSelectedCohort: (cohort: ErrorCohort) => void;
@@ -39,6 +42,8 @@ export interface ITabsViewProps {

export interface ITabViewState {
items: IVisionListItem[];
errorInstancesCount?: number;
successInstancesCount?: number;
}

const stackTokens = {
@@ -62,6 +67,17 @@ export class TabsView extends React.Component<ITabsViewProps, ITabViewState> {
items: this.props.errorInstances.concat(...this.props.successInstances)
});
}
if (
this.props.searchValue !== prevProps.searchValue &&
(!this.props.searchValue || this.props.searchValue === "")
) {
this.setState({
errorInstancesCount: undefined,
successInstancesCount: undefined
});
const label = localization.InterpretVision.Search.defaultSearchLabel;
this.props.onSearchUpdated(label);
}
}

public render(): React.ReactNode {
@@ -78,6 +94,7 @@ export class TabsView extends React.Component<ITabsViewProps, ITabViewState> {
items={this.state.items}
imageDim={this.props.imageDim}
numRows={this.props.numRows}
onSearchUpdated={this.onSearchCountUpdated}
searchValue={this.props.searchValue}
selectItem={this.props.onItemSelect}
taskType={this.props.taskType}
@@ -94,6 +111,7 @@ export class TabsView extends React.Component<ITabsViewProps, ITabViewState> {
successInstances={this.props.successInstances}
imageDim={this.props.imageDim}
otherMetadataFieldNames={this.props.otherMetadataFieldNames}
onSearchUpdated={this.onSearchCountUpdated}
searchValue={this.props.searchValue}
selectItem={this.props.onItemSelect}
updateSelectedIndices={this.props.updateSelectedIndices}
@@ -122,6 +140,7 @@ export class TabsView extends React.Component<ITabsViewProps, ITabViewState> {
<ImageList
items={this.state.items}
imageDim={this.props.imageDim}
onSearchUpdated={this.onSearchCountUpdated}
searchValue={this.props.searchValue}
selectItem={this.props.onItemSelect}
taskType={this.props.taskType}
@@ -151,6 +170,7 @@ export class TabsView extends React.Component<ITabsViewProps, ITabViewState> {
<ImageList
items={this.props.errorInstances}
imageDim={this.props.imageDim}
onSearchUpdated={this.onSearchUpdatedError}
searchValue={this.props.searchValue}
selectItem={this.props.onItemSelect}
taskType={this.props.taskType}
@@ -174,6 +194,7 @@ export class TabsView extends React.Component<ITabsViewProps, ITabViewState> {
<ImageList
items={this.props.successInstances}
imageDim={this.props.imageDim}
onSearchUpdated={this.onSearchUpdatedSuccess}
searchValue={this.props.searchValue}
selectItem={this.props.onItemSelect}
taskType={this.props.taskType}
@@ -187,4 +208,35 @@ export class TabsView extends React.Component<ITabsViewProps, ITabViewState> {
);
}
}

private onSearchCountUpdated = (
successCount: number,
errorCount: number
): void => {
updateSearchTextAriaLabel(
this.props.onSearchUpdated,
successCount,
errorCount,
this.props.searchValue
);
};

private updateSuccessErrorInstancesAriaLabel = (): void => {
this.onSearchCountUpdated(
this.state.successInstancesCount ?? 0,
this.state.errorInstancesCount ?? 0
);
};

private onSearchUpdatedError = (_: number, errorCount: number): void => {
this.setState({ errorInstancesCount: errorCount }, () => {
this.updateSuccessErrorInstancesAriaLabel();
});
};

private onSearchUpdatedSuccess = (successCount: number): void => {
this.setState({ successInstancesCount: successCount }, () => {
this.updateSuccessErrorInstancesAriaLabel();
});
};
}
Original file line number Diff line number Diff line change
@@ -22,6 +22,7 @@ export interface IToolBarProps {
newValue?: string
) => void;
selectedCohort: ErrorCohort;
searchResultsAriaLabel: string;
setSelectedCohort: (cohort: ErrorCohort) => void;
}

@@ -68,6 +69,7 @@ export class ToolBar extends React.Component<IToolBarProps> {
placeholder={localization.InterpretVision.Dashboard.search}
value={this.props.searchValue}
onChange={this.props.onSearch}
ariaLabel={this.props.searchResultsAriaLabel}
/>
</Stack.Item>
</Stack>
Original file line number Diff line number Diff line change
@@ -12,6 +12,7 @@ export interface IVisionExplanationDashboardState {
otherMetadataFieldNames: string[];
numRows: number;
panelOpen: boolean;
searchResultsAriaLabel: string;
searchValue: string;
selectedIndices: number[];
selectedItem: IVisionListItem | undefined;
Original file line number Diff line number Diff line change
@@ -17,7 +17,7 @@ import { TabsView } from "./Controls/TabsView";
import { IVisionExplanationDashboardProps } from "./Interfaces/IVisionExplanationDashboardProps";
import { IVisionExplanationDashboardState } from "./Interfaces/IVisionExplanationDashboardState";
import { visionExplanationDashboardStyles } from "./VisionExplanationDashboard.styles";
import { VisionExplanationDashboardCommon } from "./VisionExplanationDashboardCommon";
import { VisionExplanationDashboardHeader } from "./VisionExplanationDashboardHeader";
import {
preprocessData,
getItems,
@@ -35,10 +35,12 @@ export class VisionExplanationDashboard extends React.Component<
defaultModelAssessmentContext;
private originalErrorInstances: IVisionListItem[] = [];
private originalSuccessInstances: IVisionListItem[] = [];

public constructor(props: IVisionExplanationDashboardProps) {
super(props);
this.state = defaultState;
}

public componentDidMount(): void {
const data = preprocessData(this.props, this.context.dataset);
if (!data) {
@@ -48,6 +50,7 @@ export class VisionExplanationDashboard extends React.Component<
this.originalSuccessInstances = data.successInstances;
this.setState(data);
}

public componentDidUpdate(prevProps: IVisionExplanationDashboardProps): void {
if (this.props.selectedCohort !== prevProps.selectedCohort) {
this.setState(
@@ -59,6 +62,7 @@ export class VisionExplanationDashboard extends React.Component<
);
}
}

public render(): React.ReactNode {
const classNames = visionExplanationDashboardStyles();
const imageStyles = imageListStyles();
@@ -69,11 +73,22 @@ export class VisionExplanationDashboard extends React.Component<
id="VisionDataExplorer"
tokens={{ childrenGap: "l1", padding: "m 40px" }}
>
<VisionExplanationDashboardCommon
thisdashboard={this}
<VisionExplanationDashboardHeader
cohorts={this.props.cohorts}
selectedKey={this.state.selectedKey}
searchResultsAriaLabel={this.state.searchResultsAriaLabel}
searchValue={this.state.searchValue}
selectedCohort={this.props.selectedCohort}
imageStyles={imageStyles}
classNames={classNames}
taskType={this.context.dataset.task_type}
handleLinkClick={this.handleLinkClick}
setSelectedCohort={this.props.setSelectedCohort}
onSearch={this.onSearch}
selectedIndices={this.state.selectedIndices}
onSliderChange={this.onSliderChange}
onNumRowsSelect={this.onNumRowsSelect}
addCohortWrapper={this.addCohortWrapper}
/>
<Stack.Item>
<TabsView
@@ -87,6 +102,7 @@ export class VisionExplanationDashboard extends React.Component<
selectedItem={this.state.selectedItem}
selectedKey={this.state.selectedKey}
onItemSelect={this.onItemSelect}
onSearchUpdated={this.onSearchUpdated}
updateSelectedIndices={this.updateSelectedIndices}
selectedCohort={this.props.selectedCohort}
setSelectedCohort={this.props.setSelectedCohort}
@@ -118,25 +134,34 @@ export class VisionExplanationDashboard extends React.Component<
</Stack>
);
}

public updateSelectedIndices = (indices: number[]): void => {
this.setState({ selectedIndices: indices });
};

public addCohortWrapper = (name: string, switchCohort: boolean): void => {
this.context.addCohort(
getCohort(name, this.state.selectedIndices, this.context.jointDataset),
undefined,
switchCohort
);
};

public onPanelClose = (): void => {
this.setState({ panelOpen: !this.state.panelOpen });
};

public onSearch = (
_event?: React.ChangeEvent<HTMLInputElement>,
newValue?: string
): void => {
this.setState({ searchValue: newValue || "" });
};

public onSearchUpdated = (searchResultsAriaLabel: string): void => {
this.setState({ searchResultsAriaLabel });
};

public onItemSelect = (item: IVisionListItem): void => {
this.setState({ panelOpen: !this.state.panelOpen, selectedItem: item });
const index = item.index;
@@ -165,6 +190,7 @@ export class VisionExplanationDashboard extends React.Component<
});
}
};

public onItemSelectObjectDetection = (
item: IVisionListItem,
selectedObject = -1
@@ -202,6 +228,7 @@ export class VisionExplanationDashboard extends React.Component<
});
}
};

/* For onSliderChange, the max imageDims per tab (400 and 100) are selected arbitrary to look like the Figma.
For handleLinkClick, the default are half the max values chosen in onSliderChange. */
public onSliderChange = (value: number): void => {
@@ -214,12 +241,14 @@ export class VisionExplanationDashboard extends React.Component<
this.setState({ imageDim: Math.floor((value / 100) * 100) });
}
};

public onNumRowsSelect = (
_event: React.FormEvent<HTMLDivElement>,
item: IDropdownOption | undefined
): void => {
this.setState({ numRows: Number(item?.text) });
};

public handleLinkClick = (item?: PivotItem): void => {
if (item && item.props.itemKey !== undefined) {
this.setState({ selectedKey: item.props.itemKey });
Loading
Loading