Skip to content

Commit

Permalink
fix class importance weights callout truncated in small view for acce…
Browse files Browse the repository at this point in the history
…ssibility (#2462)
  • Loading branch information
imatiach-msft authored Dec 18, 2023
1 parent 6cb95c0 commit 2a6ff82
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 157 deletions.
17 changes: 14 additions & 3 deletions libs/core-ui/src/lib/components/LabelWithCallout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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;
Expand All @@ -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" ? (
Expand All @@ -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}
Expand All @@ -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}>
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,15 @@ import {
Dropdown,
IChoiceGroupOption,
IDropdownOption,
Stack,
Text
Stack
} from "@fluentui/react";
import {
Cohort,
IExplanationModelMetadata,
IsClassifier,
WeightVectorOption,
ChartTypes,
LabelWithCallout,
ITelemetryEvent,
TelemetryEventName,
ifEnableLargeData,
ModelAssessmentContext,
defaultModelAssessmentContext
Expand All @@ -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";

Expand Down Expand Up @@ -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>
)}
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit 2a6ff82

Please sign in to comment.