feat(rtk-query): improve a11y of rtk-query-monitor tab panel #1126

This commit is contained in:
FaberVitale 2022-04-09 12:57:17 +02:00
parent d9ecb32efc
commit 8154bde35b
11 changed files with 94 additions and 20 deletions

View File

@ -127,6 +127,7 @@ export class QueryForm extends React.PureComponent<
{({ styling, base16Theme }) => {
return (
<form
id="rtk-query-monitor-query-selection-form"
action="#"
onSubmit={this.handleSubmit}
{...styling('queryForm')}

View File

@ -1,8 +1,10 @@
import { createSelector, Selector } from '@reduxjs/toolkit';
import React, { ReactNode, PureComponent } from 'react';
import { Action, AnyAction } from 'redux';
import { QueryPreviewTabs } from '../types';
import { renderTabPanelButtonId, renderTabPanelId } from '../utils/a11y';
import { emptyRecord, identity } from '../utils/object';
import { TreeView } from './TreeView';
import { TreeView, TreeViewProps } from './TreeView';
export interface QueryPreviewActionsProps {
isWideLayout: boolean;
@ -11,6 +13,12 @@ export interface QueryPreviewActionsProps {
const keySep = ' - ';
const rootProps: TreeViewProps['rootProps'] = {
'aria-labelledby': renderTabPanelButtonId(QueryPreviewTabs.actions),
id: renderTabPanelId(QueryPreviewTabs.actions),
role: 'tabpanel',
};
export class QueryPreviewActions extends PureComponent<QueryPreviewActionsProps> {
selectFormattedActions: Selector<
AnyAction[],
@ -74,6 +82,7 @@ export class QueryPreviewActions extends PureComponent<QueryPreviewActionsProps>
return (
<TreeView
rootProps={rootProps}
data={this.selectFormattedActions(actionsOfQuery)}
isWideLayout={isWideLayout}
shouldExpandNode={this.shouldExpandNode}

View File

@ -1,7 +1,8 @@
import React, { ReactNode, PureComponent } from 'react';
import { ApiStats, RtkQueryApiState } from '../types';
import { ApiStats, QueryPreviewTabs, RtkQueryApiState } from '../types';
import { StyleUtilsContext } from '../styles/createStylingFromTheme';
import { TreeView } from './TreeView';
import { TreeView, TreeViewProps } from './TreeView';
import { renderTabPanelId, renderTabPanelButtonId } from '../utils/a11y';
export interface QueryPreviewApiProps {
apiStats: ApiStats | null;
@ -9,6 +10,12 @@ export interface QueryPreviewApiProps {
isWideLayout: boolean;
}
const rootProps: TreeViewProps['rootProps'] = {
'aria-labelledby': renderTabPanelButtonId(QueryPreviewTabs.apiConfig),
id: renderTabPanelId(QueryPreviewTabs.apiConfig),
role: 'tabpanel',
};
export class QueryPreviewApi extends PureComponent<QueryPreviewApiProps> {
shouldExpandApiStateNode = (
keyPath: (string | number)[],
@ -33,7 +40,7 @@ export class QueryPreviewApi extends PureComponent<QueryPreviewApiProps> {
return (
<StyleUtilsContext.Consumer>
{({ styling }) => (
<article {...styling('tabContent')}>
<article {...rootProps} {...styling('tabContent')}>
<h2>{apiState.config.reducerPath}</h2>
<TreeView
before={<h3>State</h3>}

View File

@ -1,20 +1,26 @@
import React, { ReactNode, PureComponent } from 'react';
import { RtkResourceInfo } from '../types';
import { TreeView } from './TreeView';
import { QueryPreviewTabs, RtkResourceInfo } from '../types';
import { renderTabPanelButtonId, renderTabPanelId } from '../utils/a11y';
import { TreeView, TreeViewProps } from './TreeView';
export interface QueryPreviewDataProps {
data: RtkResourceInfo['state']['data'];
isWideLayout: boolean;
}
const rootProps: TreeViewProps['rootProps'] = {
'aria-labelledby': renderTabPanelButtonId(QueryPreviewTabs.data),
id: renderTabPanelId(QueryPreviewTabs.data),
role: 'tabpanel',
};
export class QueryPreviewData extends PureComponent<QueryPreviewDataProps> {
shouldExpandNode = (
keyPath: (string | number)[],
value: unknown,
layer: number
): boolean => {
const lastKey = keyPath[keyPath.length - 1];
return layer <= 1 && lastKey !== 'query' && lastKey !== 'mutation';
return layer <= 1;
};
render(): ReactNode {
@ -22,6 +28,7 @@ export class QueryPreviewData extends PureComponent<QueryPreviewDataProps> {
return (
<TreeView
rootProps={rootProps}
data={data}
isWideLayout={isWideLayout}
shouldExpandNode={this.shouldExpandNode}

View File

@ -1,6 +1,7 @@
import React, { ReactNode } from 'react';
import { StyleUtilsContext } from '../styles/createStylingFromTheme';
import { QueryPreviewTabs, TabOption } from '../types';
import { renderTabPanelButtonId, renderTabPanelId } from '../utils/a11y';
import { emptyArray } from '../utils/object';
export interface QueryPreviewHeaderProps {
@ -28,7 +29,11 @@ export class QueryPreviewHeader extends React.Component<QueryPreviewHeaderProps>
<div {...styling('previewHeader')}>
<div {...styling('tabSelector')}>
{tabs.map((tab) => (
<div
<button
type="button"
id={renderTabPanelButtonId(tab.value)}
aria-selected={tab.value === selectedTab}
role={'tab'}
onClick={() => this.handleTabClick(tab)}
key={tab.value}
{...styling(
@ -42,7 +47,7 @@ export class QueryPreviewHeader extends React.Component<QueryPreviewHeaderProps>
<span>
{renderTabLabel ? renderTabLabel(tab) : tab.label}
</span>
</div>
</button>
))}
</div>
</div>

View File

@ -1,11 +1,12 @@
import { createSelector, Selector } from '@reduxjs/toolkit';
import { QueryStatus } from '@reduxjs/toolkit/dist/query';
import React, { ReactNode, PureComponent } from 'react';
import { RtkResourceInfo, RTKStatusFlags } from '../types';
import { QueryPreviewTabs, RtkResourceInfo, RTKStatusFlags } from '../types';
import { renderTabPanelButtonId, renderTabPanelId } from '../utils/a11y';
import { formatMs } from '../utils/formatters';
import { identity } from '../utils/object';
import { getQueryStatusFlags } from '../utils/rtk-query';
import { TreeView } from './TreeView';
import { TreeView, TreeViewProps } from './TreeView';
type QueryTimings = {
startedAt: string;
@ -23,6 +24,12 @@ type FormattedQuery = {
| { query: RtkResourceInfo['state'] }
);
const rootProps: TreeViewProps['rootProps'] = {
'aria-labelledby': renderTabPanelButtonId(QueryPreviewTabs.queryinfo),
id: renderTabPanelId(QueryPreviewTabs.queryinfo),
role: 'tabpanel',
};
export interface QueryPreviewInfoProps {
resInfo: RtkResourceInfo;
isWideLayout: boolean;
@ -97,6 +104,7 @@ export class QueryPreviewInfo extends PureComponent<QueryPreviewInfoProps> {
return (
<TreeView
rootProps={rootProps}
data={formattedQuery}
isWideLayout={isWideLayout}
shouldExpandNode={this.shouldExpandNode}

View File

@ -1,6 +1,15 @@
import React, { ReactNode, PureComponent } from 'react';
import { RtkQueryApiState } from '../types';
import { TreeView } from './TreeView';
import { QueryPreviewTabs, RtkQueryApiState } from '../types';
import { renderTabPanelButtonId, renderTabPanelId } from '../utils/a11y';
import { TreeView, TreeViewProps } from './TreeView';
const rootProps: TreeViewProps['rootProps'] = {
'aria-labelledby': renderTabPanelButtonId(
QueryPreviewTabs.querySubscriptions
),
id: renderTabPanelId(QueryPreviewTabs.querySubscriptions),
role: 'tabpanel',
};
export interface QueryPreviewSubscriptionsProps {
subscriptions: RtkQueryApiState['subscriptions'][keyof RtkQueryApiState['subscriptions']];
@ -12,7 +21,11 @@ export class QueryPreviewSubscriptions extends PureComponent<QueryPreviewSubscri
const { subscriptions } = this.props;
return (
<TreeView data={subscriptions} isWideLayout={this.props.isWideLayout} />
<TreeView
rootProps={rootProps}
data={subscriptions}
isWideLayout={this.props.isWideLayout}
/>
);
}
}

View File

@ -1,11 +1,18 @@
import React, { ReactNode, PureComponent } from 'react';
import { RtkQueryTag } from '../types';
import { TreeView } from './TreeView';
import { QueryPreviewTabs, RtkQueryTag } from '../types';
import { renderTabPanelButtonId, renderTabPanelId } from '../utils/a11y';
import { TreeView, TreeViewProps } from './TreeView';
interface QueryPreviewTagsState {
data: { tags: RtkQueryTag[] };
}
const rootProps: TreeViewProps['rootProps'] = {
'aria-labelledby': renderTabPanelButtonId(QueryPreviewTabs.queryTags),
id: renderTabPanelId(QueryPreviewTabs.queryTags),
role: 'tabpanel',
};
export interface QueryPreviewTagsProps {
tags: RtkQueryTag[];
isWideLayout: boolean;
@ -26,6 +33,8 @@ export class QueryPreviewTags extends PureComponent<
render(): ReactNode {
const { isWideLayout, tags } = this.props;
return <TreeView data={tags} isWideLayout={isWideLayout} />;
return (
<TreeView rootProps={rootProps} data={tags} isWideLayout={isWideLayout} />
);
}
}

View File

@ -22,6 +22,9 @@ export interface TreeViewProps
before?: ReactNode;
after?: ReactNode;
children?: ReactNode;
rootProps?: Partial<
Omit<React.HTMLAttributes<HTMLDivElement>, 'className' | 'style'>
>;
}
export class TreeView extends React.PureComponent<TreeViewProps> {
@ -80,13 +83,14 @@ export class TreeView extends React.PureComponent<TreeViewProps> {
keyPath,
shouldExpandNode,
hideRoot,
rootProps,
} = this.props;
return (
<StyleUtilsContext.Consumer>
{({ styling, invertTheme, base16Theme }) => {
return (
<div {...styling('treeWrapper')}>
<div {...rootProps} {...styling('treeWrapper')}>
{before}
<JSONTree
keyPath={keyPath}

View File

@ -217,6 +217,8 @@ const getSheetFromColorMap = (map: ColorMap) => {
padding: '0 8px',
display: 'inline-flex',
alignItems: 'center',
boxShadow: 'none',
outline: 'none',
color: map.TEXT_COLOR,
'border-style': 'solid',
'border-width': '1px',

View File

@ -0,0 +1,9 @@
import { QueryPreviewTabs } from '../types';
export function renderTabPanelId(value: QueryPreviewTabs): string {
return `rtk-query-monitor-tab-panel-${value}`;
}
export function renderTabPanelButtonId(value: QueryPreviewTabs): string {
return renderTabPanelId(value) + '-button';
}