Compare commits

..

2 Commits

Author SHA1 Message Date
MokireddySampath
57fa554178 Update TabComponent.tsx 2023-04-21 12:22:55 +05:30
Sampath
ce3a7debb3 Tabs have been changed to follow keyboard focus order 2023-04-21 11:01:30 +05:30
17 changed files with 81 additions and 46 deletions

View File

@@ -36,7 +36,7 @@ export const CollapsedResourceTree: FunctionComponent<CollapsedResourceTreeProps
id="collapseToggleLeftPaneButton"
role="button"
tabIndex={0}
aria-label={getApiShortDisplayName() + `Expand tree`}
aria-label="Expand Tree"
onClick={toggleLeftPaneExpanded}
onKeyPress={onKeyPressToggleLeftPaneExpanded}
ref={focusButton}

View File

@@ -55,6 +55,7 @@ export const EntityValue: FunctionComponent<TableEntityProps> = ({
label={entityValueLabel && entityValueLabel}
className="addEntityTextField"
id="entityValueId"
autoFocus
disabled={isEntityValueDisable}
type={entityValueType}
placeholder={entityValuePlaceholder}

View File

@@ -51,7 +51,7 @@ export const ResourceTreeContainer: FunctionComponent<ResourceTreeContainerProps
role="button"
data-bind="click: onRefreshResourcesClick, clickBubble: false, event: { keypress: onRefreshDatabasesKeyPress }"
tabIndex={0}
aria-label={getApiShortDisplayName() + `Refresh tree`}
aria-label="Refresh tree"
title="Refresh tree"
>
<img className="refreshcol" src={refreshImg} alt="Refresh Tree" />
@@ -63,7 +63,7 @@ export const ResourceTreeContainer: FunctionComponent<ResourceTreeContainerProps
onClick={toggleLeftPaneExpanded}
onKeyPress={onKeyPressToggleLeftPaneExpanded}
tabIndex={0}
aria-label={getApiShortDisplayName() + `Collapse Tree`}
aria-label="Collapse Tree"
title="Collapse Tree"
ref={focusButton}
>

View File

@@ -142,6 +142,7 @@ export const TableEntity: FunctionComponent<TableEntityProps> = ({
{...imageProps}
src={EditIcon}
alt="editEntity"
id="editEntity"
onClick={onEditEntity}
tabIndex={0}
onKeyPress={handleKeyPress}

View File

@@ -73,7 +73,7 @@ export class AccordionItemComponent extends React.Component<AccordionItemCompone
<img
className="expandCollapseIcon"
src={this.state.isExpanded ? TriangleDownIcon : TriangleRightIcon}
alt={this.state.isExpanded ? `${this.props.title} hide` : `${this.props.title} expand`}
alt="Hide"
tabIndex={0}
role="button"
/>

View File

@@ -24,8 +24,6 @@ import { calculateEstimateNumber, usageInGB } from "../../../../../Utils/Pricing
import { Int32 } from "../../../../Panes/Tables/Validators/EntityPropertyValidationCommon";
import {
AutoscaleEstimatedSpendingDisplayProps,
ManualEstimatedSpendingDisplayProps,
PriceBreakdown,
checkBoxAndInputStackProps,
getAutoPilotV3SpendElement,
getChoiceGroupStyles,
@@ -33,14 +31,16 @@ import {
getRuPriceBreakdown,
getTextFieldStyles,
getToolTipContainer,
ManualEstimatedSpendingDisplayProps,
manualToAutoscaleDisclaimerElement,
messageBarStyles,
noLeftPaddingCheckBoxStyle,
PriceBreakdown,
saveThroughputWarningMessage,
titleAndInputStackProps,
transparentDetailsHeaderStyle,
} from "../../SettingsRenderUtils";
import { IsComponentDirtyResult, getSanitizedInputValue, isDirty } from "../../SettingsUtils";
import { getSanitizedInputValue, IsComponentDirtyResult, isDirty } from "../../SettingsUtils";
import { ToolTipLabelComponent } from "../ToolTipLabelComponent";
export interface ThroughputInputAutoPilotV3Props {
@@ -218,7 +218,7 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
const estimatedSpendingColumns: IColumn[] = [
{
key: "costType",
name: "Cost Type",
name: "",
fieldName: "costType",
minWidth: 100,
maxWidth: 200,
@@ -312,7 +312,7 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
const estimatedSpendingColumns: IColumn[] = [
{
key: "costType",
name: "Cost type",
name: "",
fieldName: "costType",
minWidth: 100,
maxWidth: 200,

View File

@@ -311,7 +311,7 @@ exports[`ThroughputInputAutoPilotV3Component spendAck checkbox visible 1`] = `
"key": "costType",
"maxWidth": 200,
"minWidth": 100,
"name": "Cost type",
"name": "",
"styles": Object {
"root": Object {
"selectors": Object {
@@ -586,7 +586,7 @@ exports[`ThroughputInputAutoPilotV3Component throughput input visible 1`] = `
"key": "costType",
"maxWidth": 200,
"minWidth": 100,
"name": "Cost type",
"name": "",
"styles": Object {
"root": Object {
"selectors": Object {

View File

@@ -1,5 +1,4 @@
import * as React from "react";
import { AccessibleElement } from "../../Controls/AccessibleElement/AccessibleElement";
import "./TabComponent.less";
export interface TabContent {
@@ -19,11 +18,15 @@ interface TabComponentProps {
onTabIndexChange: (newIndex: number) => void;
hideHeader: boolean;
}
interface TabRefs {
[key: string]: HTMLElement;
}
/**
* We assume there's at least one tab
*/
export class TabComponent extends React.Component<TabComponentProps> {
private tabRefs: TabRefs = {};
public constructor(props: TabComponentProps) {
super(props);
@@ -33,11 +36,41 @@ export class TabComponent extends React.Component<TabComponentProps> {
throw new Error(msg);
}
}
state = {
activeTabIndex: this.props.currentTabIndex,
};
private setActiveTab(index: number): void {
this.setState({ activeTabIndex: index });
this.props.onTabIndexChange(index);
}
private setIndex = (index: number) => {
const tab = this.tabRefs[index];
if (tab) {
tab.focus();
this.setState({ activeTabIndex: index });
}
};
private handlekeypress = (event: React.KeyboardEvent<HTMLSpanElement>): void => {
const { tabs, onTabIndexChange } = this.props;
const { activeTabIndex } = this.state;
const count = tabs.length;
const prevTab = () => {
this.setIndex((activeTabIndex - 1 + count) % count);
onTabIndexChange((activeTabIndex - 1 + count) % count);
};
const nextTab = () => {
this.setIndex((activeTabIndex + 1) % count);
onTabIndexChange((activeTabIndex + 1) % count);
};
if (event.key === "ArrowLeft") {
prevTab();
} else if (event.key === "ArrowRight") {
nextTab();
}
};
private renderTabTitles(): JSX.Element[] {
return this.props.tabs.map((tab: Tab, index: number) => {
@@ -47,26 +80,32 @@ export class TabComponent extends React.Component<TabComponentProps> {
let className = "toggleSwitch";
let ariaselected;
let tabindex;
if (index === this.props.currentTabIndex) {
className += " selectedToggle";
ariaselected = true;
tabindex = 0;
} else {
className += " unselectedToggle";
ariaselected = false;
tabindex = -1;
}
return (
<div className="tab" key={index}>
<AccessibleElement
as="span"
<span
className={className}
role="tab"
onActivated={() => this.setActiveTab(index)}
onClick={() => this.setActiveTab(index)}
onKeyDown={(event: React.KeyboardEvent<HTMLSpanElement>) => this.handlekeypress(event)}
onFocus={() => this.setState({ activeTabIndex: index })}
aria-label={`Select tab: ${tab.title}`}
aria-selected={ariaselected}
tabIndex={tabindex}
ref={(element) => (this.tabRefs[index] = element)}
>
{tab.title}
</AccessibleElement>
</span>
</div>
);
});

View File

@@ -250,7 +250,7 @@ export const ThroughputInput: FunctionComponent<ThroughputInputProps> = ({
step={AutoPilotUtils.autoPilotIncrementStep}
min={AutoPilotUtils.autoPilotThroughput1K}
value={throughput.toString()}
ariaLabel={`${isDatabase ? "Database" : getCollectionName()} max RU/s`}
aria-label="Max request units per second"
required={true}
errorMessage={throughputError}
/>

View File

@@ -1640,7 +1640,7 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
</div>
</Stack>
<StyledTextFieldBase
ariaLabel="Container max RU/s"
aria-label="Max request units per second"
errorMessage=""
id="autoscaleRUValueField"
key=".0:$.2"
@@ -1663,7 +1663,7 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
value="4000"
>
<TextFieldBase
ariaLabel="Container max RU/s"
aria-label="Max request units per second"
deferredValidationTime={200}
errorMessage=""
id="autoscaleRUValueField"
@@ -1961,7 +1961,6 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
>
<input
aria-invalid={false}
aria-label="Container max RU/s"
className="ms-TextField-field field-64"
id="autoscaleRUValueField"
min={1000}

View File

@@ -729,7 +729,6 @@ export class D3ForceGraph implements GraphRenderer {
var iconGroup = newNodes
.append("g")
.attr("class", "iconContainer")
.attr("role", "group")
.attr("tabindex", 0)
.attr("aria-label", (d: D3Node) => {
return this.retrieveNodeCaption(d);

View File

@@ -23,9 +23,7 @@ export class MiddlePaneComponent extends React.Component<MiddlePaneComponentProp
onClick={this.props.toggleExpandGraph}
role="button"
aria-expanded={this.props.isTabsContentExpanded}
aria-label={
this.props.isTabsContentExpanded ? "Collapse graph to minimized view" : "View graph in full screen"
}
aria-name="View graph in full screen"
>
<img
src={this.props.isTabsContentExpanded ? CollapseArrowIcon : ExpandIcon}

View File

@@ -107,11 +107,11 @@ describe("New Vertex Component", () => {
it("should call onTypeChange method on type dropdown change", () => {
const DOWN_ARROW = { keyCode: 40 };
const onTypeChange = jest.fn();
const dropdown = screen.getByRole("combobox");
const dropdown = screen.getByRole("listbox");
dropdown.onclick = onTypeChange();
dropdown.onkeydown = onTypeChange();
fireEvent.keyDown(screen.getByRole("combobox"), DOWN_ARROW);
fireEvent.keyDown(screen.getByRole("listbox"), DOWN_ARROW);
fireEvent.click(screen.getByText(/number/));
expect(onTypeChange).toHaveBeenCalled();
});

View File

@@ -145,7 +145,6 @@ export const NewVertexComponent: FunctionComponent<INewVertexComponentProps> = (
type="text"
id="propertyKeyNewVertexPane"
componentRef={input}
aria-required="true"
placeholder="Key"
autoComplete="off"
aria-label={`Enter value for propery ${index + 1}`}
@@ -153,8 +152,6 @@ export const NewVertexComponent: FunctionComponent<INewVertexComponentProps> = (
onChange={(event: React.ChangeEvent<HTMLInputElement>) => onKeyChange(event, index)}
/>
</div>
<span className="mandatoryStar">*&nbsp;</span>
<div className="valueCol">
<TextField
className="edgeInput"
@@ -168,7 +165,7 @@ export const NewVertexComponent: FunctionComponent<INewVertexComponentProps> = (
</div>
<div>
<Dropdown
role="combobox"
role="listbox"
placeholder="Select an option"
defaultSelectedKey={data.values[0].type}
style={{ width: 100 }}

View File

@@ -48,7 +48,7 @@ export const PanelInfoErrorComponent: React.FunctionComponent<PanelInfoErrorProp
)}
</Text>
{showErrorDetails && (
<a className="paneErrorLink" role="button" onClick={expandConsole} tabIndex={0} onKeyPress={expandConsole}>
<a className="paneErrorLink" role="link" onClick={expandConsole} tabIndex={0} onKeyPress={expandConsole}>
More details
</a>
)}

View File

@@ -134,7 +134,6 @@
data-bind="click: selectQueryOptions, event: { keydown: onselectQueryOptionsKeyDown }"
tabindex="0"
role="button"
href=""
>
<span>Choose Columns... </span>
</a>

View File

@@ -7,10 +7,10 @@ import { SplashScreen } from "Explorer/SplashScreen/SplashScreen";
import { ConnectTab } from "Explorer/Tabs/ConnectTab";
import { PostgresConnectTab } from "Explorer/Tabs/PostgresConnectTab";
import { QuickstartTab } from "Explorer/Tabs/QuickstartTab";
import { userContext } from "UserContext";
import { useTeachingBubble } from "hooks/useTeachingBubble";
import ko from "knockout";
import React, { MutableRefObject, useEffect, useRef, useState } from "react";
import { userContext } from "UserContext";
import loadingIcon from "../../../images/circular_loader_black_16x16.gif";
import errorIcon from "../../../images/close-black.svg";
import { useObservable } from "../../hooks/useObservable";
@@ -102,20 +102,22 @@ function TabNav({ tab, active, tabKind }: { tab?: Tab; active: boolean; tabKind?
ref={focusTab}
>
<span className="tabNavContentContainer">
<div className="tab_Content">
<span className="statusIconContainer" style={{ width: tabKind === ReactTabKind.Home ? 0 : 18 }}>
{useObservable(tab?.isExecutionError || ko.observable(false)) && <ErrorIcon tab={tab} active={active} />}
{useObservable(tab?.isExecuting || ko.observable(false)) && (
<img className="loadingIcon" title="Loading" src={loadingIcon} alt="Loading" />
)}
</span>
<span className="tabNavText">{useObservable(tab?.tabTitle || ko.observable(ReactTabKind[tabKind]))}</span>
{tabKind !== ReactTabKind.Home && (
<span className="tabIconSection">
<CloseButton tab={tab} active={active} hovering={hovering} tabKind={tabKind} />
<a data-toggle="tab" href={"#" + tabId} tabIndex={-1}>
<div className="tab_Content">
<span className="statusIconContainer" style={{ width: tabKind === ReactTabKind.Home ? 0 : 18 }}>
{useObservable(tab?.isExecutionError || ko.observable(false)) && <ErrorIcon tab={tab} active={active} />}
{useObservable(tab?.isExecuting || ko.observable(false)) && (
<img className="loadingIcon" title="Loading" src={loadingIcon} alt="Loading" />
)}
</span>
)}
</div>
<span className="tabNavText">{useObservable(tab?.tabTitle || ko.observable(ReactTabKind[tabKind]))}</span>
{tabKind !== ReactTabKind.Home && (
<span className="tabIconSection">
<CloseButton tab={tab} active={active} hovering={hovering} tabKind={tabKind} />
</span>
)}
</div>
</a>
</span>
</li>
);