diff --git a/README.md b/README.md index 560e475..e4b23b8 100644 --- a/README.md +++ b/README.md @@ -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. | diff --git a/dev-server/src/index.js b/dev-server/src/index.js index 9fee0bc..6415d8a 100644 --- a/dev-server/src/index.js +++ b/dev-server/src/index.js @@ -232,6 +232,26 @@ ReactDom.render( src={getExampleJson4()} /> + {/* Demo of numberOfArrayGroupsToDisplay */} + + + {/* Demo of numberOfArrayGroupsToDisplay with nested objects */} + + {/* Name as colored react component */} `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}` + })) + } +} diff --git a/docs/src/js/entry.js b/docs/src/js/entry.js index 59dd2e9..fe02c9d 100644 --- a/docs/src/js/entry.js +++ b/docs/src/js/entry.js @@ -5,7 +5,7 @@ require('./../style/scss/global.scss') const app = document.getElementById('mac-react-container') -//app entrypoint +// app entrypoint ReactDom.render(
diff --git a/index.d.ts b/index.d.ts index 3d49b19..ee88510 100644 --- a/index.d.ts +++ b/index.d.ts @@ -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. diff --git a/src/js/components/ArrayGroup.js b/src/js/components/ArrayGroup.js index f819c92..c427b98 100644 --- a/src/js/components/ArrayGroup.js +++ b/src/js/components/ArrayGroup.js @@ -44,6 +44,7 @@ export default class extends React.PureComponent { const { src, groupArraysAfterLength, + numberOfArrayGroupsToDisplay, depth, name, theme, @@ -53,22 +54,24 @@ 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 (
@@ -76,13 +79,13 @@ export default class extends React.PureComponent { - {[...Array(groups)].map((_, i) => ( + {[...Array(displayGroups)].map((_, i) => (
@@ -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} /> ) @@ -141,6 +144,21 @@ export default class extends React.PureComponent {
))} + {hasMoreGroups && ( +
+ + + ... + + +
+ )}
) } diff --git a/src/js/components/DataTypes/Object.js b/src/js/components/DataTypes/Object.js index 0334d4e..121b534 100644 --- a/src/js/components/DataTypes/Object.js +++ b/src/js/components/DataTypes/Object.js @@ -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 = [] @@ -269,6 +270,7 @@ class RjvObject extends React.PureComponent { parent_type={object_type} isLast={isLast} showComma={showComma} + numberOfArrayGroupsToDisplay={numberOfArrayGroupsToDisplay} {...props} /> ) @@ -293,6 +295,7 @@ class RjvObject extends React.PureComponent { parent_type={object_type} isLast={isLast} showComma={showComma} + numberOfArrayGroupsToDisplay={numberOfArrayGroupsToDisplay} {...props} /> ) diff --git a/src/js/components/VariableEditor.js b/src/js/components/VariableEditor.js index 2d14b10..c555f30 100644 --- a/src/js/components/VariableEditor.js +++ b/src/js/components/VariableEditor.js @@ -338,7 +338,7 @@ class VariableEditor extends React.PureComponent { if (BigNumber && parsedInput.type === 'bigNumber') { new_value = new BigNumber(new_value) } - } + } this.setState({ editMode: false }) diff --git a/src/js/helpers/dispatcher.js b/src/js/helpers/dispatcher.js index 364a74a..a6e1222 100644 --- a/src/js/helpers/dispatcher.js +++ b/src/js/helpers/dispatcher.js @@ -1,11 +1,11 @@ class Dispatcher { handler = () => {} - register(handler) { + register (handler) { this.handler = handler } - dispatch(data) { + dispatch (data) { this.handler?.(data) } } diff --git a/src/js/helpers/util.js b/src/js/helpers/util.js index 04e332b..3e537e9 100644 --- a/src/js/helpers/util.js +++ b/src/js/helpers/util.js @@ -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 */ diff --git a/src/js/index.js b/src/js/index.js index b2ed403..ce9d848 100644 --- a/src/js/index.js +++ b/src/js/index.js @@ -44,6 +44,7 @@ class ReactJsonView extends React.PureComponent { sortKeys: false, quotesOnKeys: true, groupArraysAfterLength: 100, + numberOfArrayGroupsToDisplay: null, indentWidth: 4, enableClipboard: true, escapeStrings: true, diff --git a/src/js/themes/styleConstants.js b/src/js/themes/styleConstants.js index 4b921a6..fe9574a 100644 --- a/src/js/themes/styleConstants.js +++ b/src/js/themes/styleConstants.js @@ -96,4 +96,4 @@ export default { commaColor: '#666', commaFontSize: '12px', commaMarginRight: '4px' -}; +} diff --git a/test/tests/js/components/ArrayGroup-test.js b/test/tests/js/components/ArrayGroup-test.js index 9c4749b..5143072 100644 --- a/test/tests/js/components/ArrayGroup-test.js +++ b/test/tests/js/components/ArrayGroup-test.js @@ -7,7 +7,7 @@ import JsonObject from './../../../../src/js/components/DataTypes/Object' import JsonString from './../../../../src/js/components/DataTypes/String' describe('', function () { - const large_array = new Array(15).fill('test') + const largeArray = new Array(15).fill('test') it('ArrayGroup mount', function () { const wrapper = render( @@ -15,7 +15,7 @@ describe('', function () { groupArraysAfterLength={5} namespace='test' name='test' - src={large_array} + src={largeArray} theme='rjv-default' jsvRoot={false} indentWidth={4} @@ -31,7 +31,7 @@ describe('', function () { groupArraysAfterLength={5} namespace={['test']} name='test' - src={large_array} + src={largeArray} theme='rjv-default' jsvRoot={false} indentWidth={4} @@ -57,7 +57,7 @@ describe('', function () { groupArraysAfterLength={5} namespace={['test']} name='test' - src={large_array} + src={largeArray} theme='rjv-default' jsvRoot={false} indentWidth={4} @@ -72,14 +72,14 @@ describe('', 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( ', function () { groupArraysAfterLength={5} namespace={['test']} name='test' - src={large_array} + src={largeArray} theme='rjv-default' jsvRoot indentWidth={4} @@ -110,4 +110,75 @@ describe('', 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( + + ) + + // 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( + + ) + + // 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( + + ) + + // 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) + }) + + }) diff --git a/test/tests/js/components/DataTypes/Object-test.js b/test/tests/js/components/DataTypes/Object-test.js index 7c01c0b..840601d 100644 --- a/test/tests/js/components/DataTypes/Object-test.js +++ b/test/tests/js/components/DataTypes/Object-test.js @@ -466,4 +466,32 @@ describe('', function () { ) expect(wrapper.find('span').someWhere(node => node.text() === ',')).to.be.false }) + + it('Object with nested arrays respects numberOfArrayGroupsToDisplay', function () { + let src = { + nestedArray: new Array(25).fill('test'), + otherProp: 'value' + } + + const wrapper = render( + + ) + + // Should only display 2 groups for the nested array + expect(wrapper.find('.array-group').length).to.equal(2) + + // Should show ellipsis for the nested array + expect(wrapper.find('.object-key-val.array-group-ellipsis').length).to.equal(1) + }) }) diff --git a/test/tests/js/components/VariableEditor-test.js b/test/tests/js/components/VariableEditor-test.js index 075a998..23e4107 100644 --- a/test/tests/js/components/VariableEditor-test.js +++ b/test/tests/js/components/VariableEditor-test.js @@ -446,10 +446,10 @@ describe('', function () { }} /> ) - + // Check that comma exists expect(wrapper.find('span').someWhere(node => node.text() === ',')).to.be.true - + // Check that tools (edit icon) exist const editIcon = wrapper.find('.click-to-edit-icon') expect(editIcon).to.have.length(1)