mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-02-16 17:25:58 +00:00
[accessibility-2262594]:[Keyboard Navigation - Azure Cosmos DB - Data Explorer]: Keyboard focus is not retaining back to 'more' button after closing 'Delete container' dialog.
This commit is contained in:
parent
bf225f91c4
commit
81a4c8039b
@ -52,12 +52,15 @@ export const createDatabaseContextMenu = (container: Explorer, databaseId: strin
|
|||||||
if (userContext.apiType !== "Tables" || userContext.features.enableSDKoperations) {
|
if (userContext.apiType !== "Tables" || userContext.features.enableSDKoperations) {
|
||||||
items.push({
|
items.push({
|
||||||
iconSrc: DeleteDatabaseIcon,
|
iconSrc: DeleteDatabaseIcon,
|
||||||
onClick: () =>
|
onClick: (lastFocusedElement?: React.RefObject<HTMLElement>) =>
|
||||||
useSidePanel
|
useSidePanel
|
||||||
.getState()
|
.getState()
|
||||||
.openSidePanel(
|
.openSidePanel(
|
||||||
"Delete " + getDatabaseName(),
|
"Delete " + getDatabaseName(),
|
||||||
<DeleteDatabaseConfirmationPanel refreshDatabases={() => container.refreshAllDatabases()} />,
|
<DeleteDatabaseConfirmationPanel
|
||||||
|
lastFocusedElement={lastFocusedElement}
|
||||||
|
refreshDatabases={() => container.refreshAllDatabases()}
|
||||||
|
/>,
|
||||||
),
|
),
|
||||||
label: `Delete ${getDatabaseName()}`,
|
label: `Delete ${getDatabaseName()}`,
|
||||||
styleClass: "deleteDatabaseMenuItem",
|
styleClass: "deleteDatabaseMenuItem",
|
||||||
@ -132,13 +135,16 @@ export const createCollectionContextMenuButton = (
|
|||||||
if (configContext.platform !== Platform.Fabric) {
|
if (configContext.platform !== Platform.Fabric) {
|
||||||
items.push({
|
items.push({
|
||||||
iconSrc: DeleteCollectionIcon,
|
iconSrc: DeleteCollectionIcon,
|
||||||
onClick: () => {
|
onClick: (lastFocusedElement?: React.RefObject<HTMLElement>) => {
|
||||||
useSelectedNode.getState().setSelectedNode(selectedCollection);
|
useSelectedNode.getState().setSelectedNode(selectedCollection);
|
||||||
useSidePanel
|
useSidePanel
|
||||||
.getState()
|
.getState()
|
||||||
.openSidePanel(
|
.openSidePanel(
|
||||||
"Delete " + getCollectionName(),
|
"Delete " + getCollectionName(),
|
||||||
<DeleteCollectionConfirmationPane refreshDatabases={() => container.refreshAllDatabases()} />,
|
<DeleteCollectionConfirmationPane
|
||||||
|
lastFocusedElement={lastFocusedElement}
|
||||||
|
refreshDatabases={() => container.refreshAllDatabases()}
|
||||||
|
/>,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
label: `Delete ${getCollectionName()}`,
|
label: `Delete ${getCollectionName()}`,
|
||||||
|
@ -21,7 +21,7 @@ import { useCallback } from "react";
|
|||||||
|
|
||||||
export interface TreeNodeMenuItem {
|
export interface TreeNodeMenuItem {
|
||||||
label: string;
|
label: string;
|
||||||
onClick: () => void;
|
onClick: (value?: React.RefObject<HTMLElement>) => void;
|
||||||
iconSrc?: string;
|
iconSrc?: string;
|
||||||
isDisabled?: boolean;
|
isDisabled?: boolean;
|
||||||
styleClass?: string;
|
styleClass?: string;
|
||||||
@ -73,6 +73,7 @@ export const TreeNodeComponent: React.FC<TreeNodeComponentProps> = ({
|
|||||||
treeNodeId,
|
treeNodeId,
|
||||||
}: TreeNodeComponentProps): JSX.Element => {
|
}: TreeNodeComponentProps): JSX.Element => {
|
||||||
const [isLoading, setIsLoading] = React.useState<boolean>(false);
|
const [isLoading, setIsLoading] = React.useState<boolean>(false);
|
||||||
|
const contextMenuRef = React.useRef<HTMLButtonElement>(null);
|
||||||
|
|
||||||
const getSortedChildren = (treeNode: TreeNode): TreeNode[] => {
|
const getSortedChildren = (treeNode: TreeNode): TreeNode[] => {
|
||||||
if (!treeNode || !treeNode.children) {
|
if (!treeNode || !treeNode.children) {
|
||||||
@ -139,7 +140,7 @@ export const TreeNodeComponent: React.FC<TreeNodeComponentProps> = ({
|
|||||||
data-test={`TreeNode/ContextMenuItem:${menuItem.label}`}
|
data-test={`TreeNode/ContextMenuItem:${menuItem.label}`}
|
||||||
disabled={menuItem.isDisabled}
|
disabled={menuItem.isDisabled}
|
||||||
key={menuItem.label}
|
key={menuItem.label}
|
||||||
onClick={menuItem.onClick}
|
onClick={() => menuItem.onClick(contextMenuRef)}
|
||||||
>
|
>
|
||||||
{menuItem.label}
|
{menuItem.label}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
@ -158,11 +159,12 @@ export const TreeNodeComponent: React.FC<TreeNodeComponentProps> = ({
|
|||||||
actions={
|
actions={
|
||||||
contextMenuItems.length > 0 && (
|
contextMenuItems.length > 0 && (
|
||||||
<Menu onOpenChange={onMenuOpenChange}>
|
<Menu onOpenChange={onMenuOpenChange}>
|
||||||
<MenuTrigger disableButtonEnhancement>
|
<MenuTrigger disableButtonEnhancement={true}>
|
||||||
<Button
|
<Button
|
||||||
aria-label="More options"
|
aria-label="More options"
|
||||||
data-test="TreeNode/ContextMenuTrigger"
|
data-test="TreeNode/ContextMenuTrigger"
|
||||||
appearance="subtle"
|
appearance="subtle"
|
||||||
|
ref={contextMenuRef}
|
||||||
icon={<MoreHorizontal20Regular />}
|
icon={<MoreHorizontal20Regular />}
|
||||||
/>
|
/>
|
||||||
</MenuTrigger>
|
</MenuTrigger>
|
||||||
|
@ -1560,14 +1560,14 @@ exports[`TreeNodeComponent renders a node with a menu 1`] = `
|
|||||||
<MenuList>
|
<MenuList>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
data-test="TreeNode/ContextMenuItem:enabledItem"
|
data-test="TreeNode/ContextMenuItem:enabledItem"
|
||||||
onClick={[MockFunction enabledItemClick]}
|
onClick={[Function]}
|
||||||
>
|
>
|
||||||
enabledItem
|
enabledItem
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
data-test="TreeNode/ContextMenuItem:disabledItem"
|
data-test="TreeNode/ContextMenuItem:disabledItem"
|
||||||
disabled={true}
|
disabled={true}
|
||||||
onClick={[MockFunction disabledItemClick]}
|
onClick={[Function]}
|
||||||
>
|
>
|
||||||
disabledItem
|
disabledItem
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
@ -1604,7 +1604,7 @@ exports[`TreeNodeComponent renders a node with a menu 1`] = `
|
|||||||
<MenuItem
|
<MenuItem
|
||||||
data-test="TreeNode/ContextMenuItem:enabledItem"
|
data-test="TreeNode/ContextMenuItem:enabledItem"
|
||||||
key="enabledItem"
|
key="enabledItem"
|
||||||
onClick={[MockFunction enabledItemClick]}
|
onClick={[Function]}
|
||||||
>
|
>
|
||||||
enabledItem
|
enabledItem
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
@ -1612,7 +1612,7 @@ exports[`TreeNodeComponent renders a node with a menu 1`] = `
|
|||||||
data-test="TreeNode/ContextMenuItem:disabledItem"
|
data-test="TreeNode/ContextMenuItem:disabledItem"
|
||||||
disabled={true}
|
disabled={true}
|
||||||
key="disabledItem"
|
key="disabledItem"
|
||||||
onClick={[MockFunction disabledItemClick]}
|
onClick={[Function]}
|
||||||
>
|
>
|
||||||
disabledItem
|
disabledItem
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
@ -12,17 +12,19 @@ import { getCollectionName } from "Utils/APITypeUtils";
|
|||||||
import * as NotificationConsoleUtils from "Utils/NotificationConsoleUtils";
|
import * as NotificationConsoleUtils from "Utils/NotificationConsoleUtils";
|
||||||
import { useSidePanel } from "hooks/useSidePanel";
|
import { useSidePanel } from "hooks/useSidePanel";
|
||||||
import { useTabs } from "hooks/useTabs";
|
import { useTabs } from "hooks/useTabs";
|
||||||
import React, { FunctionComponent, useState } from "react";
|
import React, { FunctionComponent, useEffect, useState } from "react";
|
||||||
import { useDatabases } from "../../useDatabases";
|
import { useDatabases } from "../../useDatabases";
|
||||||
import { useSelectedNode } from "../../useSelectedNode";
|
import { useSelectedNode } from "../../useSelectedNode";
|
||||||
import { RightPaneForm, RightPaneFormProps } from "../RightPaneForm/RightPaneForm";
|
import { RightPaneForm, RightPaneFormProps } from "../RightPaneForm/RightPaneForm";
|
||||||
|
|
||||||
export interface DeleteCollectionConfirmationPaneProps {
|
export interface DeleteCollectionConfirmationPaneProps {
|
||||||
refreshDatabases: () => Promise<void>;
|
refreshDatabases: () => Promise<void>;
|
||||||
|
lastFocusedElement: React.MutableRefObject<HTMLElement>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DeleteCollectionConfirmationPane: FunctionComponent<DeleteCollectionConfirmationPaneProps> = ({
|
export const DeleteCollectionConfirmationPane: FunctionComponent<DeleteCollectionConfirmationPaneProps> = ({
|
||||||
refreshDatabases,
|
refreshDatabases,
|
||||||
|
lastFocusedElement,
|
||||||
}: DeleteCollectionConfirmationPaneProps) => {
|
}: DeleteCollectionConfirmationPaneProps) => {
|
||||||
const closeSidePanel = useSidePanel((state) => state.closeSidePanel);
|
const closeSidePanel = useSidePanel((state) => state.closeSidePanel);
|
||||||
const [deleteCollectionFeedback, setDeleteCollectionFeedback] = useState<string>("");
|
const [deleteCollectionFeedback, setDeleteCollectionFeedback] = useState<string>("");
|
||||||
@ -35,7 +37,7 @@ export const DeleteCollectionConfirmationPane: FunctionComponent<DeleteCollectio
|
|||||||
|
|
||||||
const collectionName = getCollectionName().toLocaleLowerCase();
|
const collectionName = getCollectionName().toLocaleLowerCase();
|
||||||
const paneTitle = "Delete " + collectionName;
|
const paneTitle = "Delete " + collectionName;
|
||||||
|
const lastItemElement = lastFocusedElement?.current;
|
||||||
const onSubmit = async (): Promise<void> => {
|
const onSubmit = async (): Promise<void> => {
|
||||||
const collection = useSelectedNode.getState().findSelectedCollection();
|
const collection = useSelectedNode.getState().findSelectedCollection();
|
||||||
if (!collection || inputCollectionName !== collection.id()) {
|
if (!collection || inputCollectionName !== collection.id()) {
|
||||||
@ -111,6 +113,14 @@ export const DeleteCollectionConfirmationPane: FunctionComponent<DeleteCollectio
|
|||||||
};
|
};
|
||||||
const confirmContainer = `Confirm by typing the ${collectionName.toLowerCase()} id`;
|
const confirmContainer = `Confirm by typing the ${collectionName.toLowerCase()} id`;
|
||||||
const reasonInfo = `Help us improve Azure Cosmos DB! What is the reason why you are deleting this ${collectionName}?`;
|
const reasonInfo = `Help us improve Azure Cosmos DB! What is the reason why you are deleting this ${collectionName}?`;
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
if (lastItemElement) {
|
||||||
|
lastItemElement.focus();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, [lastItemElement]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RightPaneForm {...props}>
|
<RightPaneForm {...props}>
|
||||||
<div className="panelFormWrapper">
|
<div className="panelFormWrapper">
|
||||||
|
@ -13,7 +13,7 @@ import { getDatabaseName } from "Utils/APITypeUtils";
|
|||||||
import { logConsoleError } from "Utils/NotificationConsoleUtils";
|
import { logConsoleError } from "Utils/NotificationConsoleUtils";
|
||||||
import { useSidePanel } from "hooks/useSidePanel";
|
import { useSidePanel } from "hooks/useSidePanel";
|
||||||
import { useTabs } from "hooks/useTabs";
|
import { useTabs } from "hooks/useTabs";
|
||||||
import React, { FunctionComponent, useState } from "react";
|
import React, { FunctionComponent, useEffect, useState } from "react";
|
||||||
import { useDatabases } from "../useDatabases";
|
import { useDatabases } from "../useDatabases";
|
||||||
import { useSelectedNode } from "../useSelectedNode";
|
import { useSelectedNode } from "../useSelectedNode";
|
||||||
import { PanelInfoErrorComponent, PanelInfoErrorProps } from "./PanelInfoErrorComponent";
|
import { PanelInfoErrorComponent, PanelInfoErrorProps } from "./PanelInfoErrorComponent";
|
||||||
@ -21,10 +21,12 @@ import { RightPaneForm, RightPaneFormProps } from "./RightPaneForm/RightPaneForm
|
|||||||
|
|
||||||
interface DeleteDatabaseConfirmationPanelProps {
|
interface DeleteDatabaseConfirmationPanelProps {
|
||||||
refreshDatabases: () => Promise<void>;
|
refreshDatabases: () => Promise<void>;
|
||||||
|
lastFocusedElement: React.MutableRefObject<HTMLElement>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DeleteDatabaseConfirmationPanel: FunctionComponent<DeleteDatabaseConfirmationPanelProps> = ({
|
export const DeleteDatabaseConfirmationPanel: FunctionComponent<DeleteDatabaseConfirmationPanelProps> = ({
|
||||||
refreshDatabases,
|
refreshDatabases,
|
||||||
|
lastFocusedElement,
|
||||||
}: DeleteDatabaseConfirmationPanelProps): JSX.Element => {
|
}: DeleteDatabaseConfirmationPanelProps): JSX.Element => {
|
||||||
const closeSidePanel = useSidePanel((state) => state.closeSidePanel);
|
const closeSidePanel = useSidePanel((state) => state.closeSidePanel);
|
||||||
const isLastNonEmptyDatabase = useDatabases((state) => state.isLastNonEmptyDatabase);
|
const isLastNonEmptyDatabase = useDatabases((state) => state.isLastNonEmptyDatabase);
|
||||||
@ -34,7 +36,7 @@ export const DeleteDatabaseConfirmationPanel: FunctionComponent<DeleteDatabaseCo
|
|||||||
const [databaseInput, setDatabaseInput] = useState<string>("");
|
const [databaseInput, setDatabaseInput] = useState<string>("");
|
||||||
const [databaseFeedbackInput, setDatabaseFeedbackInput] = useState<string>("");
|
const [databaseFeedbackInput, setDatabaseFeedbackInput] = useState<string>("");
|
||||||
const selectedDatabase: Database = useDatabases.getState().findSelectedDatabase();
|
const selectedDatabase: Database = useDatabases.getState().findSelectedDatabase();
|
||||||
|
const lastItemElement = lastFocusedElement?.current;
|
||||||
const submit = async (): Promise<void> => {
|
const submit = async (): Promise<void> => {
|
||||||
if (selectedDatabase?.id() && databaseInput !== selectedDatabase.id()) {
|
if (selectedDatabase?.id() && databaseInput !== selectedDatabase.id()) {
|
||||||
setFormError(
|
setFormError(
|
||||||
@ -126,6 +128,13 @@ export const DeleteDatabaseConfirmationPanel: FunctionComponent<DeleteDatabaseCo
|
|||||||
};
|
};
|
||||||
const confirmDatabase = `Confirm by typing the ${getDatabaseName()} id`;
|
const confirmDatabase = `Confirm by typing the ${getDatabaseName()} id`;
|
||||||
const reasonInfo = `Help us improve Azure Cosmos DB! What is the reason why you are deleting this ${getDatabaseName()}?`;
|
const reasonInfo = `Help us improve Azure Cosmos DB! What is the reason why you are deleting this ${getDatabaseName()}?`;
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
if (lastItemElement) {
|
||||||
|
lastItemElement.focus();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, [lastItemElement]);
|
||||||
return (
|
return (
|
||||||
<RightPaneForm {...props}>
|
<RightPaneForm {...props}>
|
||||||
{!formError && <PanelInfoErrorComponent {...errorProps} />}
|
{!formError && <PanelInfoErrorComponent {...errorProps} />}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user