fix class importance weights callout truncated in small view for accessibility (#2462)
This commit is contained in:
Родитель
6cb95c0dcc
Коммит
2a6ff82b4b
|
@ -5,7 +5,8 @@ import {
|
|||
Callout as FabricCallout,
|
||||
CommandBarButton,
|
||||
IconButton,
|
||||
Text
|
||||
Text,
|
||||
DirectionalHint
|
||||
} from "@fluentui/react";
|
||||
import { localization } from "@responsible-ai/localization";
|
||||
import React from "react";
|
||||
|
@ -24,6 +25,9 @@ export interface ILabelWithCalloutProps {
|
|||
renderOnNewLayer?: boolean;
|
||||
type?: "label" | "button";
|
||||
telemetryHook?: (message: ITelemetryEvent) => void;
|
||||
iconButtonId?: string;
|
||||
calloutTarget?: string;
|
||||
directionalHint?: DirectionalHint;
|
||||
}
|
||||
interface ILabelWithCalloutState {
|
||||
showCallout: boolean;
|
||||
|
@ -42,6 +46,12 @@ export class LabelWithCallout extends React.Component<
|
|||
public render(): React.ReactNode {
|
||||
const classNames = labelWithCalloutStyles();
|
||||
const id = `callout-${v4()}`;
|
||||
const iconButtonId = this.props.iconButtonId
|
||||
? this.props.iconButtonId
|
||||
: "label-callout-info";
|
||||
const calloutTarget = this.props.calloutTarget
|
||||
? this.props.calloutTarget
|
||||
: `#${id}`;
|
||||
return (
|
||||
<div className={classNames.calloutContainer}>
|
||||
{this.props.type === "button" ? (
|
||||
|
@ -58,7 +68,7 @@ export class LabelWithCallout extends React.Component<
|
|||
{this.props.label}
|
||||
</Text>
|
||||
<IconButton
|
||||
id={"label-callout-info"}
|
||||
id={iconButtonId}
|
||||
iconProps={{ iconName: "Info" }}
|
||||
title={localization.Interpret.calloutTitle}
|
||||
onClick={this.toggleCallout}
|
||||
|
@ -68,10 +78,11 @@ export class LabelWithCallout extends React.Component<
|
|||
{this.state.showCallout && (
|
||||
<FabricCallout
|
||||
doNotLayer={!this.props.renderOnNewLayer}
|
||||
target={`#${id}`}
|
||||
target={calloutTarget}
|
||||
setInitialFocus
|
||||
onDismiss={this.toggleCallout}
|
||||
role="alertdialog"
|
||||
directionalHint={this.props.directionalHint}
|
||||
styles={{ container: FluentUIStyles.calloutContainer }}
|
||||
>
|
||||
<div className={classNames.calloutWrapper}>
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import { IStyle, mergeStyleSets, IProcessedStyleSet } from "@fluentui/react";
|
||||
import { FluentUIStyles } from "@responsible-ai/core-ui";
|
||||
|
||||
export interface ILabelWithCalloutStyles {
|
||||
calloutHeader: IStyle;
|
||||
calloutInner: IStyle;
|
||||
calloutTitle: IStyle;
|
||||
calloutWrapper: IStyle;
|
||||
multiclassWeightLabel: IStyle;
|
||||
multiclassWeightLabelText: IStyle;
|
||||
}
|
||||
|
||||
export const classImportanceWeightsStyles: () => IProcessedStyleSet<ILabelWithCalloutStyles> =
|
||||
() => {
|
||||
return mergeStyleSets<ILabelWithCalloutStyles>({
|
||||
calloutHeader: [FluentUIStyles.calloutHeader],
|
||||
calloutInner: [FluentUIStyles.calloutInner],
|
||||
calloutTitle: [FluentUIStyles.calloutTitle],
|
||||
calloutWrapper: [FluentUIStyles.calloutWrapper],
|
||||
multiclassWeightLabel: {
|
||||
display: "inline-flex",
|
||||
paddingTop: "10px"
|
||||
},
|
||||
multiclassWeightLabelText: {
|
||||
fontWeight: "600",
|
||||
paddingTop: "5px"
|
||||
}
|
||||
});
|
||||
};
|
|
@ -2,40 +2,34 @@
|
|||
// Licensed under the MIT License.
|
||||
|
||||
import {
|
||||
Callout,
|
||||
DirectionalHint,
|
||||
Dropdown,
|
||||
IconButton,
|
||||
IDropdownOption,
|
||||
Text
|
||||
} from "@fluentui/react";
|
||||
import { WeightVectorOption, FluentUIStyles } from "@responsible-ai/core-ui";
|
||||
import {
|
||||
WeightVectorOption,
|
||||
LabelWithCallout,
|
||||
ITelemetryEvent,
|
||||
TelemetryEventName
|
||||
} from "@responsible-ai/core-ui";
|
||||
import { localization } from "@responsible-ai/localization";
|
||||
import { Dictionary } from "lodash";
|
||||
import React from "react";
|
||||
|
||||
import { classImportanceWeightsStyles } from "./ClassImportanceWeights.styles";
|
||||
|
||||
export interface IClassImportanceWeightsProps {
|
||||
onWeightChange: (option: WeightVectorOption) => void;
|
||||
selectedWeightVector: WeightVectorOption;
|
||||
weightOptions: WeightVectorOption[];
|
||||
weightLabels: any;
|
||||
weightLabels: Dictionary<string>;
|
||||
disabled?: boolean;
|
||||
}
|
||||
interface IClassImportanceWeightsState {
|
||||
crossClassInfoVisible: boolean;
|
||||
telemetryHook?: (message: ITelemetryEvent) => void;
|
||||
}
|
||||
|
||||
export class ClassImportanceWeights extends React.Component<
|
||||
IClassImportanceWeightsProps,
|
||||
IClassImportanceWeightsState
|
||||
> {
|
||||
export class ClassImportanceWeights extends React.Component<IClassImportanceWeightsProps> {
|
||||
private weightOptions: IDropdownOption[] | undefined;
|
||||
public constructor(props: IClassImportanceWeightsProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
crossClassInfoVisible: false
|
||||
};
|
||||
this.weightOptions = this.props.weightOptions.map((option) => {
|
||||
return {
|
||||
key: option,
|
||||
|
@ -44,74 +38,52 @@ export class ClassImportanceWeights extends React.Component<
|
|||
});
|
||||
}
|
||||
public render(): React.ReactNode {
|
||||
const classNames = classImportanceWeightsStyles();
|
||||
const iconButtonId = "cross-class-weight-info";
|
||||
const calloutTarget = `#${iconButtonId}`;
|
||||
return (
|
||||
<div id="ClassImportanceWeights">
|
||||
<div className={classNames.multiclassWeightLabel}>
|
||||
<Text
|
||||
variant={"medium"}
|
||||
className={classNames.multiclassWeightLabelText}
|
||||
>
|
||||
{localization.Interpret.GlobalTab.weightOptions}
|
||||
</Text>
|
||||
<IconButton
|
||||
id={"cross-class-weight-info"}
|
||||
iconProps={{ iconName: "Info" }}
|
||||
title={localization.Interpret.CrossClass.info}
|
||||
onClick={this.toggleCrossClassInfo}
|
||||
/>
|
||||
</div>
|
||||
{this.weightOptions && (
|
||||
<Dropdown
|
||||
options={this.weightOptions}
|
||||
selectedKey={this.props.selectedWeightVector}
|
||||
onChange={this.setWeightOption}
|
||||
ariaLabel={localization.Interpret.GlobalTab.weightOptions}
|
||||
disabled={this.props.disabled ?? false}
|
||||
/>
|
||||
)}
|
||||
{this.state.crossClassInfoVisible && (
|
||||
<Callout
|
||||
doNotLayer
|
||||
target={"#cross-class-weight-info"}
|
||||
setInitialFocus
|
||||
onDismiss={this.toggleCrossClassInfo}
|
||||
directionalHint={DirectionalHint.leftCenter}
|
||||
role="alertdialog"
|
||||
styles={{ container: FluentUIStyles.calloutContainer }}
|
||||
>
|
||||
<div className={classNames.calloutWrapper}>
|
||||
<div className={classNames.calloutHeader}>
|
||||
<Text className={classNames.calloutTitle}>
|
||||
{localization.Interpret.CrossClass.crossClassWeights}
|
||||
</Text>
|
||||
</div>
|
||||
<div className={classNames.calloutInner}>
|
||||
<Text>{localization.Interpret.CrossClass.overviewInfo}</Text>
|
||||
<ul>
|
||||
<li>
|
||||
<Text>
|
||||
{localization.Interpret.CrossClass.absoluteValInfo}
|
||||
</Text>
|
||||
</li>
|
||||
<li>
|
||||
<Text>
|
||||
{localization.Interpret.CrossClass.enumeratedClassInfo}
|
||||
</Text>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</Callout>
|
||||
<div>
|
||||
<LabelWithCallout
|
||||
calloutTitle={localization.Interpret.CrossClass.crossClassWeights}
|
||||
label={localization.Interpret.GlobalTab.weightOptions}
|
||||
telemetryHook={this.props.telemetryHook}
|
||||
calloutEventName={
|
||||
TelemetryEventName.FeatureImportancesCrossClassWeightsCalloutClick
|
||||
}
|
||||
iconButtonId={iconButtonId}
|
||||
calloutTarget={calloutTarget}
|
||||
renderOnNewLayer
|
||||
directionalHint={DirectionalHint.leftCenter}
|
||||
>
|
||||
<Text>{localization.Interpret.CrossClass.overviewInfo}</Text>
|
||||
<ul>
|
||||
<li>
|
||||
<Text>
|
||||
{localization.Interpret.CrossClass.absoluteValInfo}
|
||||
</Text>
|
||||
</li>
|
||||
<li>
|
||||
<Text>
|
||||
{localization.Interpret.CrossClass.enumeratedClassInfo}
|
||||
</Text>
|
||||
</li>
|
||||
</ul>
|
||||
</LabelWithCallout>
|
||||
<Dropdown
|
||||
id={"classWeightDropdown"}
|
||||
options={this.weightOptions}
|
||||
selectedKey={this.props.selectedWeightVector}
|
||||
onChange={this.setWeightOption}
|
||||
ariaLabel={localization.Interpret.GlobalTab.weightOptionsDropdown}
|
||||
disabled={this.props.disabled ?? false}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
private toggleCrossClassInfo = (): void => {
|
||||
this.setState({ crossClassInfoVisible: !this.state.crossClassInfoVisible });
|
||||
};
|
||||
|
||||
private setWeightOption = (
|
||||
_event: React.FormEvent<HTMLDivElement>,
|
||||
item?: IDropdownOption
|
||||
|
|
|
@ -6,8 +6,7 @@ import {
|
|||
Dropdown,
|
||||
IChoiceGroupOption,
|
||||
IDropdownOption,
|
||||
Stack,
|
||||
Text
|
||||
Stack
|
||||
} from "@fluentui/react";
|
||||
import {
|
||||
Cohort,
|
||||
|
@ -15,9 +14,7 @@ import {
|
|||
IsClassifier,
|
||||
WeightVectorOption,
|
||||
ChartTypes,
|
||||
LabelWithCallout,
|
||||
ITelemetryEvent,
|
||||
TelemetryEventName,
|
||||
ifEnableLargeData,
|
||||
ModelAssessmentContext,
|
||||
defaultModelAssessmentContext
|
||||
|
@ -26,6 +23,8 @@ import { localization } from "@responsible-ai/localization";
|
|||
import { Dictionary } from "lodash";
|
||||
import React from "react";
|
||||
|
||||
import { ClassImportanceWeights } from "../ClassImportanceWeights/ClassImportanceWeights";
|
||||
|
||||
import { globalTabStyles } from "./GlobalExplanationTab.styles";
|
||||
import { IGlobalSeries } from "./IGlobalSeries";
|
||||
|
||||
|
@ -106,39 +105,13 @@ export class SidePanel extends React.Component<
|
|||
{IsClassifier(this.props.metadata.modelType) &&
|
||||
this.state.weightOptions && (
|
||||
<div>
|
||||
<LabelWithCallout
|
||||
calloutTitle={
|
||||
localization.Interpret.CrossClass.crossClassWeights
|
||||
}
|
||||
label={localization.Interpret.GlobalTab.weightOptions}
|
||||
telemetryHook={this.props.telemetryHook}
|
||||
calloutEventName={
|
||||
TelemetryEventName.FeatureImportancesCrossClassWeightsCalloutClick
|
||||
}
|
||||
>
|
||||
<Text>{localization.Interpret.CrossClass.overviewInfo}</Text>
|
||||
<ul>
|
||||
<li>
|
||||
<Text>
|
||||
{localization.Interpret.CrossClass.absoluteValInfo}
|
||||
</Text>
|
||||
</li>
|
||||
<li>
|
||||
<Text>
|
||||
{localization.Interpret.CrossClass.enumeratedClassInfo}
|
||||
</Text>
|
||||
</li>
|
||||
</ul>
|
||||
</LabelWithCallout>
|
||||
<Dropdown
|
||||
id={"classWeightDropdown"}
|
||||
options={this.state.weightOptions}
|
||||
selectedKey={this.props.selectedWeightVector}
|
||||
onChange={this.setWeightOption}
|
||||
<ClassImportanceWeights
|
||||
onWeightChange={this.props.onWeightChange}
|
||||
selectedWeightVector={this.props.selectedWeightVector}
|
||||
weightOptions={this.props.weightOptions}
|
||||
weightLabels={this.props.weightLabels}
|
||||
disabled={this.props.loading}
|
||||
ariaLabel={
|
||||
localization.Interpret.GlobalTab.weightOptionsDropdown
|
||||
}
|
||||
telemetryHook={this.props.telemetryHook}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
@ -176,16 +149,6 @@ export class SidePanel extends React.Component<
|
|||
return undefined;
|
||||
}
|
||||
|
||||
private setWeightOption = (
|
||||
_event: React.FormEvent<HTMLDivElement>,
|
||||
item?: IDropdownOption
|
||||
): void => {
|
||||
if (item?.key !== undefined) {
|
||||
const newIndex = item.key as WeightVectorOption;
|
||||
this.props.onWeightChange(newIndex);
|
||||
}
|
||||
};
|
||||
|
||||
private getChartOptions(): IChoiceGroupOption[] {
|
||||
if (ifEnableLargeData(this.context.dataset)) {
|
||||
return this.largeDataChartOptions;
|
||||
|
|
Загрузка…
Ссылка в новой задаче