Skip to content

Control the expansion of individual nodes #59

Description

@TomsBicans

Hello. I found this library for json data display, but i do not see a way how to calculate the initial expansion and collapse state of each individual node. Ideally, there could be a function that can be implemented and passed to the JsonView component called:

      shouldExpandNodeInitially={(
        keyPath: (string | number)[],      // denotes the keypath of the current node in the json structure
      ): boolean => {}

if the result is true - node should be expanded
if the result is false - node should be collapsed

This way the initial expansion could be calculated for each node separately and it would be possible to decide for each node if it should be opened or not. The current attribute 'collapsed' lets me control the collapsion state of the whole tree, which does not seem to allow to configure each node separately and limits my needs.

Any idea on how this issue could be solved?

Activity

  1. TomsBicans commented on Sep 4, 2024

    @TomsBicans
    Author

    To help understand the problem, here is the current code i am trying to implement:

            <JsonView
              id={rootNode}
              key={treeKey}
              value={tranformedTree}
              style={customTheme}
              keyName={rootNode}
              // collapsed={
              //   searchQuery || selectedDatapoints.length > 0 ? false : true
              // }
              collapsed={true}
              enableClipboard={false}
              displayObjectSize={false}
              displayDataTypes={false}
              onExpand={({ expand, value, keyid, keyName }) => {
                console.log('expand', expand, value, keyid, keyName);
              }}
            >
              <JsonView.Colon render={() => <></>} />
              <JsonView.Quote render={() => <></>} />
              <JsonView.BraceLeft render={() => <></>} />
              <JsonView.BraceRight render={() => <></>} />
              <JsonView.Ellipsis render={() => <></>} />
              <JsonView.KeyName
                render={(
                  { children, ...props },
                  { keyName, value, keys, parentValue },
                ) => {
                  // Define whether the current node is a leaf node
                  const isLeafNode = isValueALeafNode(value);
    
                  // if (!keys) {
                  //   return children;
                  // }
                  // const reversedKeys = [...keys].reverse();
    
                  // console.log('reversedKeys', reversedKeys);
                  // console.log('precomputedPaths', precomputedPaths);
    
                  // const shouldBeExpanded = precomputedPaths.some(
                  //   (precomputedPath) =>
                  //     reversedKeys.every(
                  //       (key, index) =>
                  //         precomputedPath[
                  //           precomputedPath.length - reversedKeys.length + index
                  //         ] === key,
                  //     ),
                  // );
    
                  // props['data-expanded'] = shouldBeExpanded;
    
                  // Console log every incoming parameter
                  // console.log('children', children);
                  // console.log('props', props);
                  // console.log('keyName', keyName);
                  // console.log('keys', keys);
                  // console.log('value', value);
                  // console.log('parentValue', parentValue);
                  // console.log('');
    
                  if (!isLeafNode || !keyName || !keys) {
                    return children;
                  }
    
                  const selection = keypathToMPIDDPID(
                    [...(keys || [])].reverse(),
                    tranformedTree,
                  );
    
                  if (!selection) {
                    return children;
                  }
    
                  const assets = findAssetMemberAndMeasuredValue(
                    monitoringSiteDetails,
                    selection,
                  );
                  const isSelected = isDatapointSelected(selection);
                  const isAllowedToSelectForThisDatapoint = isAllowedToSelect(
                    selection,
                    selectedDatapoints,
                  );
    
                  const displayValue = `${keyName as string} (${
                    assets.measuredValue?.uomCode || '1'
                  })`;
    
                  return (
                    <span
                      onClick={() =>
                        isAllowedToSelectForThisDatapoint &&
                        handleValueClick(selection.dpid, selection.mpid)
                      }
                      style={{
                        cursor: isAllowedToSelectForThisDatapoint
                          ? 'pointer'
                          : 'not-allowed',
                        color: isSelected
                          ? 'var(--sdx-color-int-blue)'
                          : isAllowedToSelectForThisDatapoint
                            ? 'var(--sdx-color-int-blue)'
                            : 'gray',
                        fontWeight: isSelected ? 'bold' : 'normal',
                      }}
                    >
                      {highlightMatch(displayValue, searchQuery || '')}
                    </span>
                  );
                }}
              />
              <JsonView.Arrow
                // eslint-disable-next-line @typescript-eslint/no-explicit-any
                render={(props: any) => {
                  const isExpanded = props['data-expanded'];
                  return (
                    <img
                      style={{
                        cursor: 'pointer',
                        width: '0.7rem',
                        marginRight: 10,
                        rotate: isExpanded ? '0deg' : '270deg',
                      }}
                      src="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/icons/forms_selector.svg"
                    />
                  );
                }}
              />
              <JsonView.ValueQuote render={() => <></>} />
            </JsonView>

    If it would be possible to implement such function and pass it to the JsonView component, then it would be great. Here is an example implementation:

    shouldExpandNodeInitially={( // called for each node
                keyPath: (string | number)[],
              // data: unknown,
              // level: number,
            ) => {
              if (searchQuery) {
                return true;
              }
              console.log('keyPath', keyPath); //
              console.log('precomputedPaths', precomputedPaths); //<- keypaths to currently selected elements
              const res = precomputedPaths.some((precomputedPath) =>
                precomputedPath
                  .slice(0, keyPath.length)
                  .every((key, index) => key === keyPath[index]),
              );
              return res; // boolean result
            }}
  2. jaywcjlove commented on Sep 10, 2024

    @jaywcjlove
    Member

    @TomsBicans My understanding is that you might need an onExpand event?

    onExpand?: (props: { expand: boolean; value?: T; keyid: string; keyName?: string | number }) => void;

  3. TomsBicans commented on Sep 12, 2024

    @TomsBicans
    Author

    Hello. It is not exactly what i mean. The onExpand event is triggered through user interaction, which is not exactly what i am talking about. I can try to describe the problem i am facing.

    I have a problem, that i need to open up the json tree in a specific way for the initial state - some nodes of the json tree should be opened, some nodes should be closed. Currently i see no way on how to control this mechanism without user interaction.

    Another library i found has this kind of function i am talking about, but it lacks in other areas unrelated to the node expansion - it lets the developer implement the ShouldExpandNodeInitially function and the function is called automatically on the initial render for each node, so it is kind of easy to control the initial open/closed state for each individual node of the json tree. Have a look here: https://github.com/reduxjs/redux-devtools/blob/3c39eb49f2e5cb5ee18dcf8bc0cb9d66ec75573b/packages/react-json-tree/src/types.ts#L29

    Maybe something like this can be implemented also in this project.

    Thank you for your time.

  4. jaywcjlove commented on Oct 16, 2024

    @jaywcjlove
    Member

    @TomsBicans upgrade v2.0.0-alpha.29

    <JsonView
    value={object}
    collapsed={2}
    shouldExpandNodeInitially={(isExpanded, { value, keys, level }) => {
    if (keys.length > 0 && keys[0] == "object") {
    return true
    }
    return isExpanded
    }}

  5. KDean-Dolphin commented on Mar 24, 2025

    @KDean-Dolphin

    @jaywcjlove This doesn't appear to be working the way the name suggests.

    <JsonView
        value={obj}
        shouldExpandNodeInitially={() => true}
    />

    My interpretation of this is that every node should be expanded. Instead, every node is collapsed. When I change true to false, every node is expanded.

    <JsonView
        value={obj}
        shouldExpandNodeInitially={() => false}
    />
  6. JosephGalante commented on Jul 3, 2025

    @JosephGalante

    @jaywcjlove This doesn't appear to be working the way the name suggests.

    <JsonView
    value={obj}
    shouldExpandNodeInitially={() => true}
    />
    My interpretation of this is that every node should be expanded. Instead, every node is collapsed. When I change true to false, every node is expanded.

    <JsonView
    value={obj}
    shouldExpandNodeInitially={() => false}
    />

    I also run into the same issue, when I mark the return as false, it expands the node initially. One would think however, that the node should be collapsed when false due to the inherent name shouldExpandNodeInitially. I believe this is issue is still open and should be addressed.

  7. jaywcjlove commented on Jul 4, 2025

    @jaywcjlove
    Member

    @JosephGalante @KDean-Dolphin Would it make more sense if I rename shouldExpandNodeInitially to shouldCollapseNodeInitially to better reflect the current behavior?

  8. KDean-Dolphin commented on Jul 6, 2025

    @KDean-Dolphin

    @jaywcjlove If you rename it, that will likely break existing code using the name. It would be better to fix the behaviour to match the name, rather than change the name to match the behaviour.

  9. jaywcjlove commented on Jul 7, 2025

    @jaywcjlove
    Member

    @KDean-Dolphin I’ve adjusted the return value of shouldExpandNodeInitially based on your suggestion.
    Feel free to update to v2.0.0-alpha.33. Thanks again!

  10. toxik commented on Jul 8, 2025

    @toxik

    @KDean-Dolphin I’ve adjusted the return value of shouldExpandNodeInitially based on your suggestion. Feel free to update to v2.0.0-alpha.33. Thanks again!

    hey, looks like the package didn't make it to npm :)

  11. jaywcjlove commented on Jul 10, 2025

    @jaywcjlove
    Member

    @KDean-Dolphin I’ve adjusted the return value of shouldExpandNodeInitially based on your suggestion. Feel free to update to v2.0.0-alpha.33. Thanks again!

    hey, looks like the package didn't make it to npm :)

    @toxik The issue has been resolved. The version failed to publish earlier because a test case did not pass.

  12. artdiniz-rs commented on Sep 16, 2025

    @artdiniz-rs

    there's a regression to this issue starting in v2.0.0-alpha.35, probably introduced in #76

  13. jaywcjlove commented on Sep 17, 2025

    @jaywcjlove
    Member

    there's a regression to this issue starting in v2.0.0-alpha.35, probably introduced in #76

    @artdiniz-rs thx! Upgrade v2.0.0-alpha.38

  14. toxsick commented on Sep 22, 2025

    @toxsick

    Hey @jaywcjlove in 87a29ec you have introduced some log statements that show up all over the place now (testsuite, console, etc). For example see test output here:

    https://github.com/fuf-stack/pixels/actions/runs/17930534099/job/50986651382?pr=1208

    Maybe better use https://www.npmjs.com/package/debug for this?

    Greetings!

  15. jaywcjlove commented on Sep 23, 2025

    @jaywcjlove
    Member

    @toxik Thanks for the reminder, I’ve removed the logs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions