Skip to content

Support displaying limited number of array groups #88

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ import ReactJsonView from '@microlink/react-json-view'
| `collapseStringsAfterLength` | `integer` | `false` | When an integer value is assigned, strings will be cut off at that length. Collapsed strings are followed by an ellipsis. String content can be expanded and collapsed by clicking on the string value. |
| `shouldCollapse` | `(field)=>{}` | `false` | Callback function to provide control over what objects and arrays should be collapsed by default. An object is passed to the callback containing `name`, `src`, `type` ("array" or "object") and `namespace`. |
| `groupArraysAfterLength` | `integer` | 100 | When an integer value is assigned, arrays will be displayed in groups by count of the value. Groups are displayed with bracket notation and can be expanded and collapsed by clicking on the brackets. |
| `numberOfArrayGroupsToDisplay` | `integer` or `null` | `null` | When an integer value is assigned, only the specified number of array groups will be displayed. If there are more groups, an ellipsis will be shown. This works in conjunction with groupArraysAfterLength. When null (default), all groups are displayed (no change in behavior). |
| `enableClipboard` | `boolean` or `(copy)=>{}` | `true` | When prop is not `false`, the user can copy objects and arrays to clipboard by clicking on the clipboard icon. Copy callbacks are supported. |
| `displayObjectSize` | `boolean` | `true` | When set to `true`, objects and arrays are labeled with size. |
| `displayDataTypes` | `boolean` | `true` | When set to `true`, data type labels prefix values. |
Expand Down
44 changes: 44 additions & 0 deletions dev-server/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,26 @@ ReactDom.render(
src={getExampleJson4()}
/>

{/* Demo of numberOfArrayGroupsToDisplay */}
<JsonViewer
theme='monokai'
collapsed={false}
name='array_with_limited_groups'
groupArraysAfterLength={5}
numberOfArrayGroupsToDisplay={3}
src={getLargeArrayForDemo()}
/>

{/* Demo of numberOfArrayGroupsToDisplay with nested objects */}
<JsonViewer
theme='apathy:inverted'
collapsed={false}
name='nested_arrays_with_limited_groups'
groupArraysAfterLength={5}
numberOfArrayGroupsToDisplay={2}
src={getNestedArraysForDemo()}
/>

{/* Name as colored react component */}
<JsonViewer
collapsed
Expand Down Expand Up @@ -368,3 +388,27 @@ function getExampleArray () {
function getExampleWithStringEscapeSequences () {
return { '\\\n\t\r\f\\n': '\\\n\t\r\f\\n' }
}

function getLargeArrayForDemo () {
return new Array(25).fill().map((_, i) => `Item ${i + 1}`)
}

function getNestedArraysForDemo () {
return {
users: new Array(25).fill().map((_, i) => ({
id: i + 1,
name: `User ${i + 1}`,
email: `user${i + 1}@example.com`
})),
products: new Array(25).fill().map((_, i) => ({
id: i + 1,
name: `Product ${i + 1}`,
price: Math.random() * 100
})),
categories: new Array(25).fill().map((_, i) => ({
id: i + 1,
name: `Category ${i + 1}`,
description: `Description for category ${i + 1}`
}))
}
}
2 changes: 1 addition & 1 deletion docs/src/js/entry.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ require('./../style/scss/global.scss')

const app = document.getElementById('mac-react-container')

//app entrypoint
// app entrypoint
ReactDom.render(
<div className='app-entry'>
<Index />
Expand Down
8 changes: 8 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,14 @@ export interface ReactJsonViewProps {
* Default: 100
*/
groupArraysAfterLength?: number
/**
* When an integer value is assigned, only the specified number of array groups will be displayed.
* If there are more groups, an ellipsis will be shown. This works in conjunction with groupArraysAfterLength.
* When null (default), all groups are displayed (no change in behavior).
*
* Default: null
*/
numberOfArrayGroupsToDisplay?: number | null
/**
* When prop is not false, the user can copy objects and arrays to clipboard by clicking on the clipboard icon.
* Copy callbacks are supported.
Expand Down
32 changes: 25 additions & 7 deletions src/js/components/ArrayGroup.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export default class extends React.PureComponent {
const {
src,
groupArraysAfterLength,
numberOfArrayGroupsToDisplay,
depth,
name,
theme,
Expand All @@ -53,36 +54,38 @@ export default class extends React.PureComponent {
...rest
} = this.props

let object_padding_left = 0
let objectPaddingLeft = 0

const array_group_padding_left = this.props.indentWidth * SINGLE_INDENT
const arrayGroupPaddingLeft = this.props.indentWidth * SINGLE_INDENT

if (!jsvRoot) {
object_padding_left = this.props.indentWidth * SINGLE_INDENT
objectPaddingLeft = this.props.indentWidth * SINGLE_INDENT
}

const size = groupArraysAfterLength
const groups = Math.ceil(src.length / size)
const displayGroups = numberOfArrayGroupsToDisplay !== null && numberOfArrayGroupsToDisplay !== undefined ? Math.min(groups, numberOfArrayGroupsToDisplay) : groups
const hasMoreGroups = groups > displayGroups

return (
<div
className='object-key-val'
{...Theme(theme, jsvRoot ? 'jsv-root' : 'objectKeyVal', {
paddingLeft: object_padding_left
paddingLeft: objectPaddingLeft
})}
>
<ObjectName {...this.props} />

<span>
<VariableMeta size={src.length} {...this.props} />
</span>
{[...Array(groups)].map((_, i) => (
{[...Array(displayGroups)].map((_, i) => (
<div
key={i}
className='object-key-val array-group'
{...Theme(theme, 'objectKeyVal', {
marginLeft: 6,
paddingLeft: array_group_padding_left
paddingLeft: arrayGroupPaddingLeft
})}
>
<span {...Theme(theme, 'brace-row')}>
Expand Down Expand Up @@ -110,7 +113,7 @@ export default class extends React.PureComponent {
parent_type='array_group'
theme={theme}
showComma={this.props.showComma}
isLast={i === groups - 1}
isLast={i === displayGroups - 1 && !hasMoreGroups}
{...rest}
/>
)
Expand Down Expand Up @@ -141,6 +144,21 @@ export default class extends React.PureComponent {
</span>
</div>
))}
{hasMoreGroups && (
<div
className='object-key-val array-group-ellipsis'
{...Theme(theme, 'objectKeyVal', {
marginLeft: 6,
paddingLeft: arrayGroupPaddingLeft
})}
>
<span {...Theme(theme, 'brace-row')}>
<span {...Theme(theme, 'brace')} className='array-group-ellipsis'>
...
</span>
</span>
</div>
)}
</div>
)
}
Expand Down
5 changes: 4 additions & 1 deletion src/js/components/DataTypes/Object.js
Original file line number Diff line number Diff line change
Expand Up @@ -239,8 +239,9 @@ class RjvObject extends React.PureComponent {
parent_type,
index_offset,
groupArraysAfterLength,
numberOfArrayGroupsToDisplay,
namespace,
showComma,
showComma
} = this.props
const { object_type } = this.state
const elements = []
Expand Down Expand Up @@ -269,6 +270,7 @@ class RjvObject extends React.PureComponent {
parent_type={object_type}
isLast={isLast}
showComma={showComma}
numberOfArrayGroupsToDisplay={numberOfArrayGroupsToDisplay}
{...props}
/>
)
Expand All @@ -293,6 +295,7 @@ class RjvObject extends React.PureComponent {
parent_type={object_type}
isLast={isLast}
showComma={showComma}
numberOfArrayGroupsToDisplay={numberOfArrayGroupsToDisplay}
{...props}
/>
)
Expand Down
2 changes: 1 addition & 1 deletion src/js/components/VariableEditor.js
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@ class VariableEditor extends React.PureComponent {
if (BigNumber && parsedInput.type === 'bigNumber') {
new_value = new BigNumber(new_value)
}
}
}
this.setState({
editMode: false
})
Expand Down
4 changes: 2 additions & 2 deletions src/js/helpers/dispatcher.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
class Dispatcher {
handler = () => {}

register(handler) {
register (handler) {
this.handler = handler
}

dispatch(data) {
dispatch (data) {
this.handler?.(data)
}
}
Expand Down
1 change: 0 additions & 1 deletion src/js/helpers/util.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// returns a string "type" of input object
export function toType (obj, bigNumber) {

/* Check if the object is an instance of the custom BigNumber class passed in as a prop
* If it matches, return 'bigNumber' type so it can be displayed appropriately
*/
Expand Down
1 change: 1 addition & 0 deletions src/js/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class ReactJsonView extends React.PureComponent {
sortKeys: false,
quotesOnKeys: true,
groupArraysAfterLength: 100,
numberOfArrayGroupsToDisplay: null,
indentWidth: 4,
enableClipboard: true,
escapeStrings: true,
Expand Down
2 changes: 1 addition & 1 deletion src/js/themes/styleConstants.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,4 +96,4 @@ export default {
commaColor: '#666',
commaFontSize: '12px',
commaMarginRight: '4px'
};
}
85 changes: 78 additions & 7 deletions test/tests/js/components/ArrayGroup-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ import JsonObject from './../../../../src/js/components/DataTypes/Object'
import JsonString from './../../../../src/js/components/DataTypes/String'

describe('<ArrayGroup />', function () {
const large_array = new Array(15).fill('test')
const largeArray = new Array(15).fill('test')

it('ArrayGroup mount', function () {
const wrapper = render(
<ArrayGroup
groupArraysAfterLength={5}
namespace='test'
name='test'
src={large_array}
src={largeArray}
theme='rjv-default'
jsvRoot={false}
indentWidth={4}
Expand All @@ -31,7 +31,7 @@ describe('<ArrayGroup />', function () {
groupArraysAfterLength={5}
namespace={['test']}
name='test'
src={large_array}
src={largeArray}
theme='rjv-default'
jsvRoot={false}
indentWidth={4}
Expand All @@ -57,7 +57,7 @@ describe('<ArrayGroup />', function () {
groupArraysAfterLength={5}
namespace={['test']}
name='test'
src={large_array}
src={largeArray}
theme='rjv-default'
jsvRoot={false}
indentWidth={4}
Expand All @@ -72,14 +72,14 @@ describe('<ArrayGroup />', function () {
})

it('ArrayGroup paginates groups accurately', function () {
const test_array = new Array(17).fill('test')
const testArray = new Array(17).fill('test')

const wrapper = mount(
<ArrayGroup
groupArraysAfterLength={5}
namespace={['test']}
name='test'
src={test_array}
src={testArray}
theme='rjv-default'
jsvRoot={false}
indentWidth={4}
Expand All @@ -101,7 +101,7 @@ describe('<ArrayGroup />', function () {
groupArraysAfterLength={5}
namespace={['test']}
name='test'
src={large_array}
src={largeArray}
theme='rjv-default'
jsvRoot
indentWidth={4}
Expand All @@ -110,4 +110,75 @@ describe('<ArrayGroup />', function () {

expect(wrapper.find('.array-group').length).to.equal(3)
})

it('ArrayGroup limits displayed groups with numberOfArrayGroupsToDisplay', function () {
const testArray = new Array(25).fill('test')

const wrapper = render(
<ArrayGroup
groupArraysAfterLength={5}
numberOfArrayGroupsToDisplay={2}
namespace={['test']}
name='test'
src={testArray}
theme='rjv-default'
jsvRoot={false}
indentWidth={4}
/>
)

// Should only display 2 groups instead of 5
expect(wrapper.find('.array-group').length).to.equal(2)

// Should show ellipsis
expect(wrapper.find('.object-key-val.array-group-ellipsis').length).to.equal(1)
})

it('ArrayGroup shows no ellipsis when all groups are displayed', function () {
const testArray = new Array(10).fill('test')

const wrapper = render(
<ArrayGroup
groupArraysAfterLength={5}
numberOfArrayGroupsToDisplay={3}
namespace={['test']}
name='test'
src={testArray}
theme='rjv-default'
jsvRoot={false}
indentWidth={4}
/>
)

// Should display all 2 groups
expect(wrapper.find('.array-group').length).to.equal(2)

// Should not show ellipsis
expect(wrapper.find('.object-key-val.array-group-ellipsis').length).to.equal(0)
})

it('ArrayGroup shows all groups when numberOfArrayGroupsToDisplay is null (default behavior)', function () {
const testArray = new Array(25).fill('test')

const wrapper = render(
<ArrayGroup
groupArraysAfterLength={5}
numberOfArrayGroupsToDisplay={null}
namespace={['test']}
name='test'
src={testArray}
theme='rjv-default'
jsvRoot={false}
indentWidth={4}
/>
)

// Should display all 5 groups
expect(wrapper.find('.array-group').length).to.equal(5)

// Should not show ellipsis
expect(wrapper.find('.object-key-val.array-group-ellipsis').length).to.equal(0)
})


})
Loading