diff --git a/README.md b/README.md index fed04f2f..d6ac3744 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,7 @@ Name|Type|Default|Description `collapsed`|`boolean` or `integer`|`false`|When set to `true`, all nodes will be collapsed by default. Use an integer value to collapse at a particular depth. `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`. +`onToggleCollapse`|`(collapse)=>{}`|`false`|Callback function to hook into when an object or array has been expanded or collapsed. An object is passed to the callback containing `expanded`, `name`, 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 brakcet notation and can be expanded and collapsed by clickong on the brackets. `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 diff --git a/dev-server/src/index.js b/dev-server/src/index.js index 168bb3a2..a87f8d1d 100644 --- a/dev-server/src/index.js +++ b/dev-server/src/index.js @@ -37,6 +37,9 @@ ReactDom.render( console.log("select callback", e) console.log(e.namespace) }} + onToggleCollapse={e => { + console.log("toggle collapse callback", e) + }} displayObjectSize={true} name={"dev-server"} enableClipboard={copy => { diff --git a/index.d.ts b/index.d.ts index 4a91dc29..d927a085 100644 --- a/index.d.ts +++ b/index.d.ts @@ -117,6 +117,12 @@ export interface ReactJsonViewProps { * Default: false */ onSelect?: ((select: OnSelectProps) => void) | false; + /** + * When a function is passed in, toggling an expand/collapse object triggers the onToggleCollapse method to be called. + * + * Default: false + */ + onToggleCollapse?: ((collapse: onToggleCollapseProps) => void) | false; /** * Custom message for validation failures to onEdit, onAdd, or onDelete callbacks. * @@ -219,6 +225,22 @@ export interface OnSelectProps { } +export interface onToggleCollapseProps { + /** + * The name of the currently selected entry. + */ + name: string | null; + /** + * The new expanded state of the expandable/collapsable object. + */ + expanded: boolean; + /** + * List of keys representing the scopes above the selected entry. + */ + namespace: Array; + +} + export type TypeDefaultValue = string | number | boolean | object; export interface ThemeObject { diff --git a/src/js/components/DataTypes/Object.js b/src/js/components/DataTypes/Object.js index c14a3ff5..1842f57d 100644 --- a/src/js/components/DataTypes/Object.js +++ b/src/js/components/DataTypes/Object.js @@ -80,15 +80,20 @@ class RjvObject extends React.PureComponent { } toggleCollapsed = () => { + const { onToggleCollapse, name, namespace, rjvId } = this.props; + this.setState({ expanded: !this.state.expanded }, () => { AttributeStore.set( - this.props.rjvId, - this.props.namespace, + rjvId, + namespace, 'expanded', this.state.expanded ); + if (typeof onToggleCollapse === 'function') { + onToggleCollapse({ expanded: this.state.expanded, name, namespace }); + } }); } diff --git a/test/tests/js/components/DataTypes/Object-test.js b/test/tests/js/components/DataTypes/Object-test.js index 02ce4b18..ff8a7ef0 100644 --- a/test/tests/js/components/DataTypes/Object-test.js +++ b/test/tests/js/components/DataTypes/Object-test.js @@ -1,5 +1,6 @@ import React from "react" import { shallow, render, mount } from "enzyme" +import sinon from "sinon" import { expect } from "chai" import JsonObject from "./../../../../../src/js/components/DataTypes/Object" @@ -325,6 +326,7 @@ describe("", function() { expect(wrapper.state("expanded")).to.equal(true) }) + it("sort object keys", () => { let src = { d: 'd', @@ -365,4 +367,59 @@ describe("", function() { ) expect(wrapper.text()).to.equal('"":{"d":"d""b":"b""a":"a""c":"c"}'); }) + + describe("callbacks", function() { + let sandbox; + + beforeEach(function() { + sandbox = sinon.sandbox.create() + }) + + afterEach(function() { + sandbox.restore() + }) + + it("should call onToggleCollapse callback when collapsing", function() { + let src = { prop1: 1, prop2: 2, prop3: 3 } + const mockToggleCollapseCallback = sandbox.spy(); + + const wrapper = mount( + + ) + + expect(wrapper.state("expanded"), "should start collapsed").to.equal(false) + expect(wrapper.find("CollapsedIcon").length, "should have one collapsed icon").to.equal(1) + + wrapper.find("CollapsedIcon").simulate("click"); + expect(mockToggleCollapseCallback.calledWithMatch({ expanded: true }), + "should call callback with object with property expanded as true").to.equal(true) + }) + + it("should call onToggleCollapse callback when expanding", function() { + let src = { prop1: 1, prop2: 2, prop3: 3 } + const mockToggleCollapseCallback = sandbox.spy(); + + const wrapper = mount( + + ) + + expect(wrapper.state("expanded"), "should start expanded").to.equal(true) + expect(wrapper.find("ExpandedIcon").length, "should have one expanded icon").to.equal(1) + + wrapper.find("ExpandedIcon").simulate("click"); + expect(mockToggleCollapseCallback.calledWithMatch({ expanded: false }), + "should call callback with object with property expanded as false").to.equal(true) + }) + }) })