From 144a3267092ddb5cd75e0efb4648cdd2dd5f8c6e Mon Sep 17 00:00:00 2001 From: Baptiste Devessier Date: Thu, 3 Apr 2025 12:11:19 +0200 Subject: [PATCH] JSON visualizer: Highlight the parent nodes of in-use nodes (#11373) https://github.com/user-attachments/assets/5f31023d-b24f-40c8-a061-ffc0d02b63b0 Closes https://github.com/twentyhq/core-team-issues/issues/715 --- .../components/WorkflowRunStepInputDetail.tsx | 20 +++++++++++++++++-- .../__stories__/JsonTree.stories.tsx | 20 +++++++++++++++++++ .../components/JsonArrayNode.tsx | 4 ++++ .../components/JsonNestedNode.tsx | 11 ++++++++-- .../json-visualizer/components/JsonNode.tsx | 2 ++ .../components/JsonObjectNode.tsx | 4 ++++ .../components/internal/JsonArrow.tsx | 6 +++++- .../components/internal/JsonNodeValue.tsx | 2 +- .../types/JsonNodeHighlighting.ts | 4 +++- 9 files changed, 66 insertions(+), 7 deletions(-) diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/components/WorkflowRunStepInputDetail.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/components/WorkflowRunStepInputDetail.tsx index 85c7b08e6..0eabbc866 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/components/WorkflowRunStepInputDetail.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/components/WorkflowRunStepInputDetail.tsx @@ -14,6 +14,7 @@ import { useLingui } from '@lingui/react/macro'; import { isDefined } from 'twenty-shared/utils'; import { IconBrackets, useIcons } from 'twenty-ui/display'; import { + GetJsonNodeHighlighting, JsonNestedNode, JsonTreeContextProvider, ShouldExpandNodeInitiallyProps, @@ -70,6 +71,7 @@ export const WorkflowRunStepInputDetail = ({ stepId }: { stepId: string }) => { const variablesUsedInStep = getWorkflowVariablesUsedInStep({ step, }); + const allVariablesUsedInStep = Array.from(variablesUsedInStep); const stepContext = getWorkflowRunStepContext({ context: workflowRun.context, @@ -80,6 +82,21 @@ export const WorkflowRunStepInputDetail = ({ stepId }: { stepId: string }) => { throw new Error('The input tab must be rendered with a non-empty context.'); } + const getNodeHighlighting: GetJsonNodeHighlighting = (keyPath: string) => { + if (variablesUsedInStep.has(keyPath)) { + return 'blue'; + } + + const isUsedVariableParent = allVariablesUsedInStep.some((variable) => + variable.startsWith(keyPath), + ); + if (isUsedVariableParent) { + return 'partial-blue'; + } + + return undefined; + }; + const isFirstNodeDepthOfPreviousStep = ({ keyPath, depth, @@ -104,8 +121,7 @@ export const WorkflowRunStepInputDetail = ({ stepId }: { stepId: string }) => { emptyStringLabel: t`[empty string]`, arrowButtonCollapsedLabel: t`Expand`, arrowButtonExpandedLabel: t`Collapse`, - getNodeHighlighting: (keyPath) => - variablesUsedInStep.has(keyPath) ? 'blue' : undefined, + getNodeHighlighting, shouldExpandNodeInitially: isFirstNodeDepthOfPreviousStep, }} > diff --git a/packages/twenty-ui/src/json-visualizer/__stories__/JsonTree.stories.tsx b/packages/twenty-ui/src/json-visualizer/__stories__/JsonTree.stories.tsx index 822b3185d..25af1430e 100644 --- a/packages/twenty-ui/src/json-visualizer/__stories__/JsonTree.stories.tsx +++ b/packages/twenty-ui/src/json-visualizer/__stories__/JsonTree.stories.tsx @@ -447,6 +447,26 @@ export const BlueHighlighting: Story = { }, }; +export const PartialBlueHighlighting: Story = { + args: { + value: { + name: 'John Doe', + age: 30, + address: { + city: 'Paris', + }, + }, + getNodeHighlighting: (keyPath: string) => + keyPath === 'address' ? 'partial-blue' : undefined, + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + + const ageElement = await canvas.findByText('age'); + expect(ageElement).toBeVisible(); + }, +}; + export const RedHighlighting: Story = { args: { value: { diff --git a/packages/twenty-ui/src/json-visualizer/components/JsonArrayNode.tsx b/packages/twenty-ui/src/json-visualizer/components/JsonArrayNode.tsx index d17da3ab6..1919b7adf 100644 --- a/packages/twenty-ui/src/json-visualizer/components/JsonArrayNode.tsx +++ b/packages/twenty-ui/src/json-visualizer/components/JsonArrayNode.tsx @@ -1,6 +1,7 @@ import { IconBrackets } from '@ui/display'; import { JsonNestedNode } from '@ui/json-visualizer/components/JsonNestedNode'; import { useJsonTreeContextOrThrow } from '@ui/json-visualizer/hooks/useJsonTreeContextOrThrow'; +import { JsonNodeHighlighting } from '@ui/json-visualizer/types/JsonNodeHighlighting'; import { JsonArray } from 'type-fest'; export const JsonArrayNode = ({ @@ -8,11 +9,13 @@ export const JsonArrayNode = ({ value, depth, keyPath, + highlighting, }: { label?: string; value: JsonArray; depth: number; keyPath: string; + highlighting: JsonNodeHighlighting | undefined; }) => { const { emptyArrayLabel } = useJsonTreeContextOrThrow(); @@ -29,6 +32,7 @@ export const JsonArrayNode = ({ depth={depth} emptyElementsText={emptyArrayLabel} keyPath={keyPath} + highlighting={highlighting} /> ); }; diff --git a/packages/twenty-ui/src/json-visualizer/components/JsonNestedNode.tsx b/packages/twenty-ui/src/json-visualizer/components/JsonNestedNode.tsx index 4adbb4fad..7b6fbc814 100644 --- a/packages/twenty-ui/src/json-visualizer/components/JsonNestedNode.tsx +++ b/packages/twenty-ui/src/json-visualizer/components/JsonNestedNode.tsx @@ -6,11 +6,12 @@ import { JsonList } from '@ui/json-visualizer/components/internal/JsonList'; import { JsonNodeLabel } from '@ui/json-visualizer/components/internal/JsonNodeLabel'; import { JsonNode } from '@ui/json-visualizer/components/JsonNode'; import { useJsonTreeContextOrThrow } from '@ui/json-visualizer/hooks/useJsonTreeContextOrThrow'; +import { JsonNodeHighlighting } from '@ui/json-visualizer/types/JsonNodeHighlighting'; import { ANIMATION } from '@ui/theme'; import { AnimatePresence, motion } from 'framer-motion'; import { useState } from 'react'; -import { JsonValue } from 'type-fest'; import { isDefined } from 'twenty-shared/utils'; +import { JsonValue } from 'type-fest'; const StyledContainer = styled.li` display: grid; @@ -41,6 +42,7 @@ export const JsonNestedNode = ({ emptyElementsText, depth, keyPath, + highlighting, }: { label?: string; Icon: IconComponent; @@ -49,6 +51,7 @@ export const JsonNestedNode = ({ emptyElementsText: string; depth: number; keyPath: string; + highlighting?: JsonNodeHighlighting | undefined; }) => { const { shouldExpandNodeInitially } = useJsonTreeContextOrThrow(); @@ -115,7 +118,11 @@ export const JsonNestedNode = ({ return ( - + diff --git a/packages/twenty-ui/src/json-visualizer/components/JsonNode.tsx b/packages/twenty-ui/src/json-visualizer/components/JsonNode.tsx index 6812354d1..b58aa6c80 100644 --- a/packages/twenty-ui/src/json-visualizer/components/JsonNode.tsx +++ b/packages/twenty-ui/src/json-visualizer/components/JsonNode.tsx @@ -84,6 +84,7 @@ export const JsonNode = ({ value={value} depth={depth} keyPath={keyPath} + highlighting={highlighting} /> ); } @@ -94,6 +95,7 @@ export const JsonNode = ({ value={value} depth={depth} keyPath={keyPath} + highlighting={highlighting} /> ); }; diff --git a/packages/twenty-ui/src/json-visualizer/components/JsonObjectNode.tsx b/packages/twenty-ui/src/json-visualizer/components/JsonObjectNode.tsx index e3f32fd34..29c6a1786 100644 --- a/packages/twenty-ui/src/json-visualizer/components/JsonObjectNode.tsx +++ b/packages/twenty-ui/src/json-visualizer/components/JsonObjectNode.tsx @@ -1,6 +1,7 @@ import { IconCube } from '@ui/display'; import { JsonNestedNode } from '@ui/json-visualizer/components/JsonNestedNode'; import { useJsonTreeContextOrThrow } from '@ui/json-visualizer/hooks/useJsonTreeContextOrThrow'; +import { JsonNodeHighlighting } from '@ui/json-visualizer/types/JsonNodeHighlighting'; import { JsonObject } from 'type-fest'; export const JsonObjectNode = ({ @@ -8,11 +9,13 @@ export const JsonObjectNode = ({ value, depth, keyPath, + highlighting, }: { label?: string; value: JsonObject; depth: number; keyPath: string; + highlighting: JsonNodeHighlighting | undefined; }) => { const { emptyObjectLabel } = useJsonTreeContextOrThrow(); @@ -29,6 +32,7 @@ export const JsonObjectNode = ({ depth={depth} emptyElementsText={emptyObjectLabel} keyPath={keyPath} + highlighting={highlighting} /> ); }; diff --git a/packages/twenty-ui/src/json-visualizer/components/internal/JsonArrow.tsx b/packages/twenty-ui/src/json-visualizer/components/internal/JsonArrow.tsx index 46ed51793..46da248dc 100644 --- a/packages/twenty-ui/src/json-visualizer/components/internal/JsonArrow.tsx +++ b/packages/twenty-ui/src/json-visualizer/components/internal/JsonArrow.tsx @@ -27,9 +27,11 @@ const MotionIconChevronDown = motion.create(IconChevronDown); export const JsonArrow = ({ isOpen, onClick, + variant, }: { isOpen: boolean; onClick: () => void; + variant?: 'blue'; }) => { const theme = useTheme(); @@ -44,7 +46,9 @@ export const JsonArrow = ({ ; +export type JsonNodeHighlighting = + | Extract + | 'partial-blue';