diff --git a/src/components/JsonViewer/JsonViewer.tsx b/src/components/JsonViewer/JsonViewer.tsx index 0ef0da51a2..f320473a96 100644 --- a/src/components/JsonViewer/JsonViewer.tsx +++ b/src/components/JsonViewer/JsonViewer.tsx @@ -34,11 +34,11 @@ class Json extends React.PureComponent { - {options => ( + {(options) => ( (this.node = node!)} + ref={(node) => (this.node = node!)} dangerouslySetInnerHTML={{ __html: jsonToHTML(this.props.data, options.jsonSampleExpandLevel), }} @@ -65,9 +65,8 @@ class Json extends React.PureComponent { } }; - clickListener = (event: MouseEvent) => { + collapseElement = (target: HTMLElement) => { let collapsed; - const target = event.target as HTMLElement; if (target.className === 'collapser') { collapsed = target.parentElement!.getElementsByClassName('collapsible')[0]; if (collapsed.parentElement.classList.contains('collapsed')) { @@ -78,12 +77,24 @@ class Json extends React.PureComponent { } }; + clickListener = (event: MouseEvent) => { + this.collapseElement(event.target as HTMLElement); + }; + + focusListener = (event: KeyboardEvent) => { + if (event.key === 'Enter') { + this.collapseElement(event.target as HTMLElement); + } + }; + componentDidMount() { this.node!.addEventListener('click', this.clickListener); + this.node!.addEventListener('focus', this.focusListener); } componentWillUnmount() { this.node!.removeEventListener('click', this.clickListener); + this.node!.removeEventListener('focus', this.focusListener); } } diff --git a/src/components/JsonViewer/style.ts b/src/components/JsonViewer/style.ts index 14ef649255..82b1677a76 100644 --- a/src/components/JsonViewer/style.ts +++ b/src/components/JsonViewer/style.ts @@ -1,12 +1,13 @@ import { css } from '../../styled-components'; export const jsonStyles = css` - .redoc-json > .collapser { + .redoc-json code > .collapser { display: none; + pointer-events: none; } - font-family: ${props => props.theme.typography.code.fontFamily}; - font-size: ${props => props.theme.typography.code.fontSize}; + font-family: ${(props) => props.theme.typography.code.fontFamily}; + font-size: ${(props) => props.theme.typography.code.fontSize}; white-space: ${({ theme }) => (theme.typography.code.wrap ? 'pre-wrap' : 'pre')}; contain: content; @@ -47,8 +48,32 @@ export const jsonStyles = css` } .collapser { + background-color: transparent; + border: 0; + color: #fff; + font-family: ${(props) => props.theme.typography.code.fontFamily}; + font-size: ${(props) => props.theme.typography.code.fontSize}; padding-right: 6px; padding-left: 6px; + padding-top: 0; + padding-bottom: 0; + display: flex; + align-items: center; + justify-content: center; + width: 15px; + height: 15px; + position: absolute; + top: 4px; + left: -1.5em; + cursor: default; + user-select: none; + -webkit-user-select: none; + padding: 2px; + &:focus { + outline-color: #fff; + outline-style: dotted; + outline-width: 1px; + } } ul { @@ -83,13 +108,4 @@ export const jsonStyles = css` .collapsed > .ellipsis { display: inherit; } - - .collapser { - position: absolute; - top: 1px; - left: -1.5em; - cursor: default; - user-select: none; - -webkit-user-select: none; - } `; diff --git a/src/utils/jsonToHtml.ts b/src/utils/jsonToHtml.ts index e950ee2cd5..65a7edfbe8 100644 --- a/src/utils/jsonToHtml.ts +++ b/src/utils/jsonToHtml.ts @@ -73,7 +73,7 @@ function valueToHTML(value, maxExpandLevel: number) { function arrayToHTML(json, maxExpandLevel: number) { const collapsed = level > maxExpandLevel ? 'collapsed' : ''; - let output = `
${punctuation( + let output = `${punctuation( '[', )}
    `; let hasContents = false; @@ -98,7 +98,7 @@ function objectToHTML(json, maxExpandLevel: number) { const collapsed = level > maxExpandLevel ? 'collapsed' : ''; const keys = Object.keys(json); const length = keys.length; - let output = `
    ${punctuation( + let output = `${punctuation( '{', )}
      `; let hasContents = false;