diff --git a/.changeset/purple-timers-whisper.md b/.changeset/purple-timers-whisper.md new file mode 100644 index 0000000000..765dc4bbc5 --- /dev/null +++ b/.changeset/purple-timers-whisper.md @@ -0,0 +1,6 @@ +--- +'react-json-tree': minor +'react-json-tree-example': minor +--- + +Changed to using buttons for a11y compatibility, and added ability to override Arrow Component. diff --git a/packages/react-json-tree/README.md b/packages/react-json-tree/README.md index 678a5b6a64..07964b4c06 100644 --- a/packages/react-json-tree/README.md +++ b/packages/react-json-tree/README.md @@ -137,6 +137,34 @@ Their full signatures are: - `labelRenderer: function(keyPath, nodeType, expanded, expandable)` - `valueRenderer: function(valueAsString, value, ...keyPath)` +Additionally, it is possible to override the arrows for expanding, for example with a `+` and `-` button. + +```tsx +const ArrowOverride = ({ expanded, ...rest }: JSONArrowProps) => { + if (expanded) { + return + } + return +} + + +``` + +The default `JSONArrow` component will literally check if an `ArrowComponentOverride` exists and pass it's props there. The typescript for these props is as follows. + +```ts +interface JSONArrowProps { + styling: StylingFunction; + arrowStyle?: 'single' | 'double'; + expanded: boolean; + nodeType: string; + onClick: React.MouseEventHandler; + ariaControls?: string; + ariaLabel?: string + OverrideComponent?: ComponentType; +} +``` + #### More Options - `shouldExpandNodeInitially: function(keyPath, data, level)` - determines if node should be expanded when it first renders (root is expanded by default) diff --git a/packages/react-json-tree/examples/src/App.tsx b/packages/react-json-tree/examples/src/App.tsx index a972ce5888..a47d8e907f 100644 --- a/packages/react-json-tree/examples/src/App.tsx +++ b/packages/react-json-tree/examples/src/App.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { Map } from 'immutable'; -import { JSONTree, StylingValue } from 'react-json-tree'; +import { JSONArrowProps, JSONTree, StylingValue } from 'react-json-tree'; const getLabelStyle: StylingValue = ({ style }, nodeType, expanded) => ({ style: { @@ -125,6 +125,13 @@ const theme = { base0F: '#cc6633', }; +const ArrowOverride = ({ expanded, ...rest }: JSONArrowProps) => { + if (expanded) { + return + } + return +} + const App = () => (
@@ -168,10 +175,12 @@ const App = () => (

Pass labelRenderer or valueRenderer.

+

Additionally, you may pass an ArrowComponentOverride

(({raw})):} valueRenderer={(raw) => ( diff --git a/packages/react-json-tree/src/ItemRange.tsx b/packages/react-json-tree/src/ItemRange.tsx index a051a01598..9b40c36129 100644 --- a/packages/react-json-tree/src/ItemRange.tsx +++ b/packages/react-json-tree/src/ItemRange.tsx @@ -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,8 @@ export default function ItemRange(props: Props) { expanded={false} onClick={handleClick} arrowStyle="double" + ariaLabel={`Expand Array from ${from} to ${to}`} + OverrideComponent={props.ArrowComponentOverride} /> {`${from} ... ${to}`}
diff --git a/packages/react-json-tree/src/JSONArrow.tsx b/packages/react-json-tree/src/JSONArrow.tsx index e00fe456c4..f0e7aaaf29 100644 --- a/packages/react-json-tree/src/JSONArrow.tsx +++ b/packages/react-json-tree/src/JSONArrow.tsx @@ -1,29 +1,39 @@ -import React from 'react'; +import React, { ComponentType } from 'react'; import type { StylingFunction } from 'react-base16-styling'; -interface Props { +export interface JSONArrowProps { styling: StylingFunction; arrowStyle?: 'single' | 'double'; expanded: boolean; nodeType: string; - onClick: React.MouseEventHandler; + onClick: React.MouseEventHandler; + ariaControls?: string; + ariaLabel?: string + OverrideComponent?: ComponentType; } -export default function JSONArrow({ - styling, - arrowStyle = 'single', - expanded, - nodeType, - onClick, -}: Props) { +export default function JSONArrow({OverrideComponent, ...props}: JSONArrowProps) { + const { + styling, + arrowStyle = 'single', + expanded, + nodeType, + onClick, + ariaControls, + ariaLabel + } = props + if(OverrideComponent) { + return + } + return ( -
+
+ ); } diff --git a/packages/react-json-tree/src/JSONNestedNode.tsx b/packages/react-json-tree/src/JSONNestedNode.tsx index fbc9a47b61..58b468899c 100644 --- a/packages/react-json-tree/src/JSONNestedNode.tsx +++ b/packages/react-json-tree/src/JSONNestedNode.tsx @@ -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 ? (
    • @@ -155,6 +159,9 @@ export default function JSONNestedNode(props: Props) { nodeType={nodeType} expanded={expanded} onClick={handleClick} + ariaControls={ariaControls} + ariaLabel={ariaLabel} + OverrideComponent={props.ArrowComponentOverride} /> )}