mirror of
https://github.com/reduxjs/redux-devtools.git
synced 2025-07-27 08:30:02 +03:00
feat(rtk-query): improve a11y of rtk-query-monitor tab panel #1126
This commit is contained in:
parent
d9ecb32efc
commit
8154bde35b
|
@ -127,6 +127,7 @@ export class QueryForm extends React.PureComponent<
|
||||||
{({ styling, base16Theme }) => {
|
{({ styling, base16Theme }) => {
|
||||||
return (
|
return (
|
||||||
<form
|
<form
|
||||||
|
id="rtk-query-monitor-query-selection-form"
|
||||||
action="#"
|
action="#"
|
||||||
onSubmit={this.handleSubmit}
|
onSubmit={this.handleSubmit}
|
||||||
{...styling('queryForm')}
|
{...styling('queryForm')}
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
import { createSelector, Selector } from '@reduxjs/toolkit';
|
import { createSelector, Selector } from '@reduxjs/toolkit';
|
||||||
import React, { ReactNode, PureComponent } from 'react';
|
import React, { ReactNode, PureComponent } from 'react';
|
||||||
import { Action, AnyAction } from 'redux';
|
import { Action, AnyAction } from 'redux';
|
||||||
|
import { QueryPreviewTabs } from '../types';
|
||||||
|
import { renderTabPanelButtonId, renderTabPanelId } from '../utils/a11y';
|
||||||
import { emptyRecord, identity } from '../utils/object';
|
import { emptyRecord, identity } from '../utils/object';
|
||||||
import { TreeView } from './TreeView';
|
import { TreeView, TreeViewProps } from './TreeView';
|
||||||
|
|
||||||
export interface QueryPreviewActionsProps {
|
export interface QueryPreviewActionsProps {
|
||||||
isWideLayout: boolean;
|
isWideLayout: boolean;
|
||||||
|
@ -11,6 +13,12 @@ export interface QueryPreviewActionsProps {
|
||||||
|
|
||||||
const keySep = ' - ';
|
const keySep = ' - ';
|
||||||
|
|
||||||
|
const rootProps: TreeViewProps['rootProps'] = {
|
||||||
|
'aria-labelledby': renderTabPanelButtonId(QueryPreviewTabs.actions),
|
||||||
|
id: renderTabPanelId(QueryPreviewTabs.actions),
|
||||||
|
role: 'tabpanel',
|
||||||
|
};
|
||||||
|
|
||||||
export class QueryPreviewActions extends PureComponent<QueryPreviewActionsProps> {
|
export class QueryPreviewActions extends PureComponent<QueryPreviewActionsProps> {
|
||||||
selectFormattedActions: Selector<
|
selectFormattedActions: Selector<
|
||||||
AnyAction[],
|
AnyAction[],
|
||||||
|
@ -74,6 +82,7 @@ export class QueryPreviewActions extends PureComponent<QueryPreviewActionsProps>
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TreeView
|
<TreeView
|
||||||
|
rootProps={rootProps}
|
||||||
data={this.selectFormattedActions(actionsOfQuery)}
|
data={this.selectFormattedActions(actionsOfQuery)}
|
||||||
isWideLayout={isWideLayout}
|
isWideLayout={isWideLayout}
|
||||||
shouldExpandNode={this.shouldExpandNode}
|
shouldExpandNode={this.shouldExpandNode}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import React, { ReactNode, PureComponent } from 'react';
|
import React, { ReactNode, PureComponent } from 'react';
|
||||||
import { ApiStats, RtkQueryApiState } from '../types';
|
import { ApiStats, QueryPreviewTabs, RtkQueryApiState } from '../types';
|
||||||
import { StyleUtilsContext } from '../styles/createStylingFromTheme';
|
import { StyleUtilsContext } from '../styles/createStylingFromTheme';
|
||||||
import { TreeView } from './TreeView';
|
import { TreeView, TreeViewProps } from './TreeView';
|
||||||
|
import { renderTabPanelId, renderTabPanelButtonId } from '../utils/a11y';
|
||||||
|
|
||||||
export interface QueryPreviewApiProps {
|
export interface QueryPreviewApiProps {
|
||||||
apiStats: ApiStats | null;
|
apiStats: ApiStats | null;
|
||||||
|
@ -9,6 +10,12 @@ export interface QueryPreviewApiProps {
|
||||||
isWideLayout: boolean;
|
isWideLayout: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const rootProps: TreeViewProps['rootProps'] = {
|
||||||
|
'aria-labelledby': renderTabPanelButtonId(QueryPreviewTabs.apiConfig),
|
||||||
|
id: renderTabPanelId(QueryPreviewTabs.apiConfig),
|
||||||
|
role: 'tabpanel',
|
||||||
|
};
|
||||||
|
|
||||||
export class QueryPreviewApi extends PureComponent<QueryPreviewApiProps> {
|
export class QueryPreviewApi extends PureComponent<QueryPreviewApiProps> {
|
||||||
shouldExpandApiStateNode = (
|
shouldExpandApiStateNode = (
|
||||||
keyPath: (string | number)[],
|
keyPath: (string | number)[],
|
||||||
|
@ -33,7 +40,7 @@ export class QueryPreviewApi extends PureComponent<QueryPreviewApiProps> {
|
||||||
return (
|
return (
|
||||||
<StyleUtilsContext.Consumer>
|
<StyleUtilsContext.Consumer>
|
||||||
{({ styling }) => (
|
{({ styling }) => (
|
||||||
<article {...styling('tabContent')}>
|
<article {...rootProps} {...styling('tabContent')}>
|
||||||
<h2>{apiState.config.reducerPath}</h2>
|
<h2>{apiState.config.reducerPath}</h2>
|
||||||
<TreeView
|
<TreeView
|
||||||
before={<h3>State</h3>}
|
before={<h3>State</h3>}
|
||||||
|
|
|
@ -1,20 +1,26 @@
|
||||||
import React, { ReactNode, PureComponent } from 'react';
|
import React, { ReactNode, PureComponent } from 'react';
|
||||||
import { RtkResourceInfo } from '../types';
|
import { QueryPreviewTabs, RtkResourceInfo } from '../types';
|
||||||
import { TreeView } from './TreeView';
|
import { renderTabPanelButtonId, renderTabPanelId } from '../utils/a11y';
|
||||||
|
import { TreeView, TreeViewProps } from './TreeView';
|
||||||
|
|
||||||
export interface QueryPreviewDataProps {
|
export interface QueryPreviewDataProps {
|
||||||
data: RtkResourceInfo['state']['data'];
|
data: RtkResourceInfo['state']['data'];
|
||||||
isWideLayout: boolean;
|
isWideLayout: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const rootProps: TreeViewProps['rootProps'] = {
|
||||||
|
'aria-labelledby': renderTabPanelButtonId(QueryPreviewTabs.data),
|
||||||
|
id: renderTabPanelId(QueryPreviewTabs.data),
|
||||||
|
role: 'tabpanel',
|
||||||
|
};
|
||||||
|
|
||||||
export class QueryPreviewData extends PureComponent<QueryPreviewDataProps> {
|
export class QueryPreviewData extends PureComponent<QueryPreviewDataProps> {
|
||||||
shouldExpandNode = (
|
shouldExpandNode = (
|
||||||
keyPath: (string | number)[],
|
keyPath: (string | number)[],
|
||||||
value: unknown,
|
value: unknown,
|
||||||
layer: number
|
layer: number
|
||||||
): boolean => {
|
): boolean => {
|
||||||
const lastKey = keyPath[keyPath.length - 1];
|
return layer <= 1;
|
||||||
|
|
||||||
return layer <= 1 && lastKey !== 'query' && lastKey !== 'mutation';
|
|
||||||
};
|
};
|
||||||
|
|
||||||
render(): ReactNode {
|
render(): ReactNode {
|
||||||
|
@ -22,6 +28,7 @@ export class QueryPreviewData extends PureComponent<QueryPreviewDataProps> {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TreeView
|
<TreeView
|
||||||
|
rootProps={rootProps}
|
||||||
data={data}
|
data={data}
|
||||||
isWideLayout={isWideLayout}
|
isWideLayout={isWideLayout}
|
||||||
shouldExpandNode={this.shouldExpandNode}
|
shouldExpandNode={this.shouldExpandNode}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import React, { ReactNode } from 'react';
|
import React, { ReactNode } from 'react';
|
||||||
import { StyleUtilsContext } from '../styles/createStylingFromTheme';
|
import { StyleUtilsContext } from '../styles/createStylingFromTheme';
|
||||||
import { QueryPreviewTabs, TabOption } from '../types';
|
import { QueryPreviewTabs, TabOption } from '../types';
|
||||||
|
import { renderTabPanelButtonId, renderTabPanelId } from '../utils/a11y';
|
||||||
import { emptyArray } from '../utils/object';
|
import { emptyArray } from '../utils/object';
|
||||||
|
|
||||||
export interface QueryPreviewHeaderProps {
|
export interface QueryPreviewHeaderProps {
|
||||||
|
@ -28,7 +29,11 @@ export class QueryPreviewHeader extends React.Component<QueryPreviewHeaderProps>
|
||||||
<div {...styling('previewHeader')}>
|
<div {...styling('previewHeader')}>
|
||||||
<div {...styling('tabSelector')}>
|
<div {...styling('tabSelector')}>
|
||||||
{tabs.map((tab) => (
|
{tabs.map((tab) => (
|
||||||
<div
|
<button
|
||||||
|
type="button"
|
||||||
|
id={renderTabPanelButtonId(tab.value)}
|
||||||
|
aria-selected={tab.value === selectedTab}
|
||||||
|
role={'tab'}
|
||||||
onClick={() => this.handleTabClick(tab)}
|
onClick={() => this.handleTabClick(tab)}
|
||||||
key={tab.value}
|
key={tab.value}
|
||||||
{...styling(
|
{...styling(
|
||||||
|
@ -42,7 +47,7 @@ export class QueryPreviewHeader extends React.Component<QueryPreviewHeaderProps>
|
||||||
<span>
|
<span>
|
||||||
{renderTabLabel ? renderTabLabel(tab) : tab.label}
|
{renderTabLabel ? renderTabLabel(tab) : tab.label}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
import { createSelector, Selector } from '@reduxjs/toolkit';
|
import { createSelector, Selector } from '@reduxjs/toolkit';
|
||||||
import { QueryStatus } from '@reduxjs/toolkit/dist/query';
|
import { QueryStatus } from '@reduxjs/toolkit/dist/query';
|
||||||
import React, { ReactNode, PureComponent } from 'react';
|
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 { formatMs } from '../utils/formatters';
|
||||||
import { identity } from '../utils/object';
|
import { identity } from '../utils/object';
|
||||||
import { getQueryStatusFlags } from '../utils/rtk-query';
|
import { getQueryStatusFlags } from '../utils/rtk-query';
|
||||||
import { TreeView } from './TreeView';
|
import { TreeView, TreeViewProps } from './TreeView';
|
||||||
|
|
||||||
type QueryTimings = {
|
type QueryTimings = {
|
||||||
startedAt: string;
|
startedAt: string;
|
||||||
|
@ -23,6 +24,12 @@ type FormattedQuery = {
|
||||||
| { query: RtkResourceInfo['state'] }
|
| { query: RtkResourceInfo['state'] }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const rootProps: TreeViewProps['rootProps'] = {
|
||||||
|
'aria-labelledby': renderTabPanelButtonId(QueryPreviewTabs.queryinfo),
|
||||||
|
id: renderTabPanelId(QueryPreviewTabs.queryinfo),
|
||||||
|
role: 'tabpanel',
|
||||||
|
};
|
||||||
|
|
||||||
export interface QueryPreviewInfoProps {
|
export interface QueryPreviewInfoProps {
|
||||||
resInfo: RtkResourceInfo;
|
resInfo: RtkResourceInfo;
|
||||||
isWideLayout: boolean;
|
isWideLayout: boolean;
|
||||||
|
@ -97,6 +104,7 @@ export class QueryPreviewInfo extends PureComponent<QueryPreviewInfoProps> {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TreeView
|
<TreeView
|
||||||
|
rootProps={rootProps}
|
||||||
data={formattedQuery}
|
data={formattedQuery}
|
||||||
isWideLayout={isWideLayout}
|
isWideLayout={isWideLayout}
|
||||||
shouldExpandNode={this.shouldExpandNode}
|
shouldExpandNode={this.shouldExpandNode}
|
||||||
|
|
|
@ -1,6 +1,15 @@
|
||||||
import React, { ReactNode, PureComponent } from 'react';
|
import React, { ReactNode, PureComponent } from 'react';
|
||||||
import { RtkQueryApiState } from '../types';
|
import { QueryPreviewTabs, RtkQueryApiState } from '../types';
|
||||||
import { TreeView } from './TreeView';
|
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 {
|
export interface QueryPreviewSubscriptionsProps {
|
||||||
subscriptions: RtkQueryApiState['subscriptions'][keyof RtkQueryApiState['subscriptions']];
|
subscriptions: RtkQueryApiState['subscriptions'][keyof RtkQueryApiState['subscriptions']];
|
||||||
|
@ -12,7 +21,11 @@ export class QueryPreviewSubscriptions extends PureComponent<QueryPreviewSubscri
|
||||||
const { subscriptions } = this.props;
|
const { subscriptions } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TreeView data={subscriptions} isWideLayout={this.props.isWideLayout} />
|
<TreeView
|
||||||
|
rootProps={rootProps}
|
||||||
|
data={subscriptions}
|
||||||
|
isWideLayout={this.props.isWideLayout}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,18 @@
|
||||||
import React, { ReactNode, PureComponent } from 'react';
|
import React, { ReactNode, PureComponent } from 'react';
|
||||||
import { RtkQueryTag } from '../types';
|
import { QueryPreviewTabs, RtkQueryTag } from '../types';
|
||||||
import { TreeView } from './TreeView';
|
import { renderTabPanelButtonId, renderTabPanelId } from '../utils/a11y';
|
||||||
|
import { TreeView, TreeViewProps } from './TreeView';
|
||||||
|
|
||||||
interface QueryPreviewTagsState {
|
interface QueryPreviewTagsState {
|
||||||
data: { tags: RtkQueryTag[] };
|
data: { tags: RtkQueryTag[] };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const rootProps: TreeViewProps['rootProps'] = {
|
||||||
|
'aria-labelledby': renderTabPanelButtonId(QueryPreviewTabs.queryTags),
|
||||||
|
id: renderTabPanelId(QueryPreviewTabs.queryTags),
|
||||||
|
role: 'tabpanel',
|
||||||
|
};
|
||||||
|
|
||||||
export interface QueryPreviewTagsProps {
|
export interface QueryPreviewTagsProps {
|
||||||
tags: RtkQueryTag[];
|
tags: RtkQueryTag[];
|
||||||
isWideLayout: boolean;
|
isWideLayout: boolean;
|
||||||
|
@ -26,6 +33,8 @@ export class QueryPreviewTags extends PureComponent<
|
||||||
render(): ReactNode {
|
render(): ReactNode {
|
||||||
const { isWideLayout, tags } = this.props;
|
const { isWideLayout, tags } = this.props;
|
||||||
|
|
||||||
return <TreeView data={tags} isWideLayout={isWideLayout} />;
|
return (
|
||||||
|
<TreeView rootProps={rootProps} data={tags} isWideLayout={isWideLayout} />
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,9 @@ export interface TreeViewProps
|
||||||
before?: ReactNode;
|
before?: ReactNode;
|
||||||
after?: ReactNode;
|
after?: ReactNode;
|
||||||
children?: ReactNode;
|
children?: ReactNode;
|
||||||
|
rootProps?: Partial<
|
||||||
|
Omit<React.HTMLAttributes<HTMLDivElement>, 'className' | 'style'>
|
||||||
|
>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TreeView extends React.PureComponent<TreeViewProps> {
|
export class TreeView extends React.PureComponent<TreeViewProps> {
|
||||||
|
@ -80,13 +83,14 @@ export class TreeView extends React.PureComponent<TreeViewProps> {
|
||||||
keyPath,
|
keyPath,
|
||||||
shouldExpandNode,
|
shouldExpandNode,
|
||||||
hideRoot,
|
hideRoot,
|
||||||
|
rootProps,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyleUtilsContext.Consumer>
|
<StyleUtilsContext.Consumer>
|
||||||
{({ styling, invertTheme, base16Theme }) => {
|
{({ styling, invertTheme, base16Theme }) => {
|
||||||
return (
|
return (
|
||||||
<div {...styling('treeWrapper')}>
|
<div {...rootProps} {...styling('treeWrapper')}>
|
||||||
{before}
|
{before}
|
||||||
<JSONTree
|
<JSONTree
|
||||||
keyPath={keyPath}
|
keyPath={keyPath}
|
||||||
|
|
|
@ -217,6 +217,8 @@ const getSheetFromColorMap = (map: ColorMap) => {
|
||||||
padding: '0 8px',
|
padding: '0 8px',
|
||||||
display: 'inline-flex',
|
display: 'inline-flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
|
boxShadow: 'none',
|
||||||
|
outline: 'none',
|
||||||
color: map.TEXT_COLOR,
|
color: map.TEXT_COLOR,
|
||||||
'border-style': 'solid',
|
'border-style': 'solid',
|
||||||
'border-width': '1px',
|
'border-width': '1px',
|
||||||
|
|
|
@ -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';
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user