Initial Basic Accessibility Added

This commit is contained in:
Michael Adsit 2024-09-03 17:04:26 -05:00
parent c5aef77b85
commit e8b07a9c11
5 changed files with 43 additions and 5 deletions

View File

@ -1,6 +1,6 @@
import React, { useCallback, useState } from 'react';
import JSONArrow from './JSONArrow.js';
import type { CircularCache, CommonInternalProps } from './types.js';
import type { CircularCache, CommonInternalProps, KeyPath } from './types.js';
interface Props extends CommonInternalProps {
data: unknown;
@ -10,6 +10,7 @@ interface Props extends CommonInternalProps {
renderChildNodes: (props: Props, from: number, to: number) => React.ReactNode;
circularCache: CircularCache;
level: number;
keyPath: KeyPath;
}
export default function ItemRange(props: Props) {
@ -32,6 +33,7 @@ export default function ItemRange(props: Props) {
expanded={false}
onClick={handleClick}
arrowStyle="double"
ariaLabel={`Expand Array from ${from} to ${to}`}
/>
{`${from} ... ${to}`}
</div>

View File

@ -6,7 +6,9 @@ interface Props {
arrowStyle?: 'single' | 'double';
expanded: boolean;
nodeType: string;
onClick: React.MouseEventHandler<HTMLDivElement>;
onClick: React.MouseEventHandler<HTMLButtonElement>;
ariaControls?: string;
ariaLabel?: string
}
export default function JSONArrow({
@ -15,15 +17,17 @@ export default function JSONArrow({
expanded,
nodeType,
onClick,
ariaControls,
ariaLabel
}: Props) {
return (
<div {...styling('arrowContainer', arrowStyle)} onClick={onClick}>
<button {...styling('arrowContainer', arrowStyle)} aria-label={ariaLabel} aria-expanded={expanded} aria-controls={ariaControls} onClick={onClick}>
<div {...styling(['arrow', 'arrowSign'], nodeType, expanded, arrowStyle)}>
{'\u25B6'}
{arrowStyle === 'double' && (
<div {...styling(['arrowSign', 'arrowSignInner'])}>{'\u25B6'}</div>
)}
</div>
</div>
</button>
);
}

View File

@ -4,6 +4,7 @@ import getCollectionEntries from './getCollectionEntries.js';
import JSONNode from './JSONNode.js';
import ItemRange from './ItemRange.js';
import type { CircularCache, CommonInternalProps } from './types.js';
import getAriaPropsFromKeyPath from './getAriaPropsFromKeyPath.js';
/**
* Renders nested values (eg. objects, arrays, lists, etc.)
@ -62,6 +63,7 @@ function renderChildNodes(
from={entry.from}
to={entry.to}
renderChildNodes={renderChildNodes}
keyPath={[entry.from, ...keyPath]}
/>,
);
} else {
@ -141,6 +143,8 @@ export default function JSONNestedNode(props: Props) {
);
const stylingArgs = [keyPath, nodeType, expanded, expandable] as const;
const {ariaControls, ariaLabel} = getAriaPropsFromKeyPath(keyPath)
return hideRoot ? (
<li {...styling('rootNode', ...stylingArgs)}>
<ul {...styling('rootNodeChildren', ...stylingArgs)}>
@ -155,6 +159,8 @@ export default function JSONNestedNode(props: Props) {
nodeType={nodeType}
expanded={expanded}
onClick={handleClick}
ariaControls={ariaControls}
ariaLabel={ariaLabel}
/>
)}
<label
@ -169,7 +175,7 @@ export default function JSONNestedNode(props: Props) {
>
{renderedItemString}
</span>
<ul {...styling('nestedNodeChildren', ...stylingArgs)}>
<ul {...styling('nestedNodeChildren', ...stylingArgs)} id={expandable ? ariaControls : undefined}>
{renderedChildren}
</ul>
</li>

View File

@ -122,6 +122,11 @@ const getDefaultThemeStyling = (theme: Base16Theme): StylingConfig => {
arrowContainer: ({ style }, arrowStyle) => ({
style: {
...style,
background: 'none',
color: 'inherit',
border: 'none',
padding: 0,
font: 'inherit',
display: 'inline-block',
paddingRight: '0.5em',
paddingLeft: arrowStyle === 'double' ? '1em' : 0,

View File

@ -0,0 +1,21 @@
import { KeyPath } from "./types.js";
const replaceSpacesRegex = / /g;
const getAriaPropsFromKeyPath = (
keyPath: KeyPath
) => {
let ariaControls = '';
let ariaLabel = 'JSON Tree Node: ';
for(let i = keyPath.length - 1; i >= 0; i--) {
const key = keyPath[i];
ariaControls += `${key}`.replace(replaceSpacesRegex, '-');
ariaLabel += `${key} `;
}
ariaLabel = ariaLabel.trim();
return { ariaControls, ariaLabel };
}
export default getAriaPropsFromKeyPath;