From 1ecc5e2bf600110419f30140f81066a510c5e3d0 Mon Sep 17 00:00:00 2001 From: Baptiste Devessier Date: Wed, 19 Mar 2025 11:44:34 +0100 Subject: [PATCH] Limit nodes opened by default in the JSON Tree component (#11002) - Add a parameter to choose which nodes to open by default - On the Admin Panel, open all nodes by default - On the Workflow Run step output, open only the two first depths - On the Workflow Run step input, open only the previous step first depth - Display `[empty string]` when a node is an empty string - Now, display `null` instead of `[null]` ## Demo https://github.com/user-attachments/assets/99b3078a-da3c-4330-b0ff-ddb2e360d933 Closes https://github.com/twentyhq/core-team-issues/issues/538 --- .../CommandMenuWorkflowRunViewStep.tsx | 10 ++++- .../JsonDataIndicatorHealthStatus.tsx | 4 ++ .../RightDrawerWorkflowRunViewStep.tsx | 10 ++++- .../components/WorkflowRunStepInputDetail.tsx | 20 +++++++++- .../WorkflowRunStepOutputDetail.tsx | 4 +- .../utils/getWorkflowPreviousStep.ts | 25 ++++++++++++ .../__stories__/JsonTree.stories.tsx | 38 +++++++++++++++++++ .../components/JsonNestedNode.tsx | 7 +++- .../json-visualizer/components/JsonNode.tsx | 14 +++++-- .../json-visualizer/components/JsonTree.tsx | 9 +++++ .../contexts/JsonTreeContext.tsx | 6 +++ .../twenty-ui/src/json-visualizer/index.ts | 1 + .../json-visualizer/utils/isTwoFirstDepths.ts | 4 ++ 13 files changed, 141 insertions(+), 11 deletions(-) create mode 100644 packages/twenty-front/src/modules/workflow/workflow-steps/utils/getWorkflowPreviousStep.ts create mode 100644 packages/twenty-ui/src/json-visualizer/utils/isTwoFirstDepths.ts diff --git a/packages/twenty-front/src/modules/command-menu/pages/workflow/step/view-run/components/CommandMenuWorkflowRunViewStep.tsx b/packages/twenty-front/src/modules/command-menu/pages/workflow/step/view-run/components/CommandMenuWorkflowRunViewStep.tsx index b9a2ba38e..9a47c3062 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/workflow/step/view-run/components/CommandMenuWorkflowRunViewStep.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/workflow/step/view-run/components/CommandMenuWorkflowRunViewStep.tsx @@ -85,11 +85,17 @@ export const CommandMenuWorkflowRunViewStep = () => { ) : null} {activeTabId === 'input' ? ( - + ) : null} {activeTabId === 'output' ? ( - + ) : null} ); diff --git a/packages/twenty-front/src/modules/settings/admin-panel/health-status/components/JsonDataIndicatorHealthStatus.tsx b/packages/twenty-front/src/modules/settings/admin-panel/health-status/components/JsonDataIndicatorHealthStatus.tsx index 68f594c0c..4247b4571 100644 --- a/packages/twenty-front/src/modules/settings/admin-panel/health-status/components/JsonDataIndicatorHealthStatus.tsx +++ b/packages/twenty-front/src/modules/settings/admin-panel/health-status/components/JsonDataIndicatorHealthStatus.tsx @@ -33,6 +33,8 @@ export const JsonDataIndicatorHealthStatus = () => { !indicatorHealth.status || indicatorHealth.status === AdminPanelHealthServiceStatus.OUTAGE; + const isAnyNode = () => true; + return (
{isDown && ( @@ -45,8 +47,10 @@ export const JsonDataIndicatorHealthStatus = () => { diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/components/RightDrawerWorkflowRunViewStep.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/components/RightDrawerWorkflowRunViewStep.tsx index 999183855..c617295a3 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/components/RightDrawerWorkflowRunViewStep.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/components/RightDrawerWorkflowRunViewStep.tsx @@ -85,11 +85,17 @@ export const RightDrawerWorkflowRunViewStep = () => { ) : null} {activeTabId === 'input' ? ( - + ) : null} {activeTabId === 'output' ? ( - + ) : null} ); 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 c703fb2ad..cb231ca0f 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 @@ -1,5 +1,6 @@ import { useWorkflowRun } from '@/workflow/hooks/useWorkflowRun'; import { useWorkflowRunIdOrThrow } from '@/workflow/hooks/useWorkflowRunIdOrThrow'; +import { getWorkflowPreviousStepId } from '@/workflow/workflow-steps/utils/getWorkflowPreviousStep'; import { getWorkflowRunStepContext } from '@/workflow/workflow-steps/utils/getWorkflowRunStepContext'; import { getWorkflowVariablesUsedInStep } from '@/workflow/workflow-steps/utils/getWorkflowVariablesUsedInStep'; import styled from '@emotion/styled'; @@ -9,6 +10,7 @@ import { IconBrackets, JsonNestedNode, JsonTreeContextProvider, + ShouldExpandNodeInitiallyProps, } from 'twenty-ui'; const StyledContainer = styled.div` @@ -38,6 +40,15 @@ export const WorkflowRunStepInputDetail = ({ stepId }: { stepId: string }) => { return null; } + const previousStepId = getWorkflowPreviousStepId({ + stepId, + steps: workflowRun.output.flow.steps, + }); + + if (previousStepId === undefined) { + return null; + } + const variablesUsedInStep = getWorkflowVariablesUsedInStep({ step, }); @@ -47,20 +58,27 @@ export const WorkflowRunStepInputDetail = ({ stepId }: { stepId: string }) => { flow: workflowRun.output.flow, stepId, }); - if (stepContext.length === 0) { throw new Error('The input tab must be rendered with a non-empty context.'); } + const isFirstNodeDepthOfPreviousStep = ({ + keyPath, + depth, + }: ShouldExpandNodeInitiallyProps) => + keyPath.startsWith(previousStepId) && depth < 2; + return ( variablesUsedInStep.has(keyPath), + shouldExpandNodeInitially: isFirstNodeDepthOfPreviousStep, }} > { diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/utils/getWorkflowPreviousStep.ts b/packages/twenty-front/src/modules/workflow/workflow-steps/utils/getWorkflowPreviousStep.ts new file mode 100644 index 000000000..5bf8bbf65 --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/utils/getWorkflowPreviousStep.ts @@ -0,0 +1,25 @@ +import { WorkflowStep } from '@/workflow/types/Workflow'; +import { TRIGGER_STEP_ID } from '@/workflow/workflow-trigger/constants/TriggerStepId'; + +export const getWorkflowPreviousStepId = ({ + stepId, + steps, +}: { + stepId: string; + steps: Array; +}) => { + if (stepId === TRIGGER_STEP_ID) { + return undefined; + } + + if (stepId === steps[0].id) { + return TRIGGER_STEP_ID; + } + + const stepIndex = steps.findIndex((step) => step.id === stepId); + if (stepIndex === -1) { + throw new Error('Step not found'); + } + + return steps[stepIndex - 1].id; +}; 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 198b1d207..369b82128 100644 --- a/packages/twenty-ui/src/json-visualizer/__stories__/JsonTree.stories.tsx +++ b/packages/twenty-ui/src/json-visualizer/__stories__/JsonTree.stories.tsx @@ -6,13 +6,16 @@ import { within, } from '@storybook/test'; import { JsonTree } from '@ui/json-visualizer/components/JsonTree'; +import { isTwoFirstDepths } from '@ui/json-visualizer/utils/isTwoFirstDepths'; const meta: Meta = { title: 'UI/JsonVisualizer/JsonTree', component: JsonTree, args: { + shouldExpandNodeInitially: () => true, emptyArrayLabel: 'Empty Array', emptyObjectLabel: 'Empty Object', + emptyStringLabel: '[empty string]', arrowButtonCollapsedLabel: 'Expand', arrowButtonExpandedLabel: 'Collapse', }, @@ -273,6 +276,41 @@ export const ExpandingElementExpandsAllItsDescendants: Story = { }, }; +export const ExpandTwoFirstDepths: Story = { + args: { + value: { + person: { + name: 'John Doe', + address: { + street: '123 Main St', + city: 'New York', + country: { + name: 'USA', + code: 'US', + }, + }, + }, + isActive: true, + }, + shouldExpandNodeInitially: isTwoFirstDepths, + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + + const nameElement = await canvas.findByText('name'); + expect(nameElement).toBeVisible(); + + const addressElement = await canvas.findByText('address'); + expect(addressElement).toBeVisible(); + + const streetElement = canvas.queryByText('street'); + expect(streetElement).not.toBeInTheDocument(); + + const countrCodeElement = canvas.queryByText('code'); + expect(countrCodeElement).not.toBeInTheDocument(); + }, +}; + export const ReallyDeepNestedObject: Story = { args: { value: { diff --git a/packages/twenty-ui/src/json-visualizer/components/JsonNestedNode.tsx b/packages/twenty-ui/src/json-visualizer/components/JsonNestedNode.tsx index fe9775f8f..71c0822bd 100644 --- a/packages/twenty-ui/src/json-visualizer/components/JsonNestedNode.tsx +++ b/packages/twenty-ui/src/json-visualizer/components/JsonNestedNode.tsx @@ -5,6 +5,7 @@ import { JsonArrow } from '@ui/json-visualizer/components/internal/JsonArrow'; 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 { ANIMATION } from '@ui/theme'; import { AnimatePresence, motion } from 'framer-motion'; import { useState } from 'react'; @@ -49,9 +50,13 @@ export const JsonNestedNode = ({ depth: number; keyPath: string; }) => { + const { shouldExpandNodeInitially } = useJsonTreeContextOrThrow(); + const hideRoot = !isDefined(label); - const [isOpen, setIsOpen] = useState(true); + const [isOpen, setIsOpen] = useState( + shouldExpandNodeInitially({ keyPath, depth }), + ); const renderedChildren = ( { - const { shouldHighlightNode } = useJsonTreeContextOrThrow(); + const { shouldHighlightNode, emptyStringLabel } = useJsonTreeContextOrThrow(); const isHighlighted = shouldHighlightNode?.(keyPath) ?? false; @@ -31,7 +37,7 @@ export const JsonNode = ({ return ( @@ -42,7 +48,7 @@ export const JsonNode = ({ return ( diff --git a/packages/twenty-ui/src/json-visualizer/components/JsonTree.tsx b/packages/twenty-ui/src/json-visualizer/components/JsonTree.tsx index ef92c44a6..7de9d4aef 100644 --- a/packages/twenty-ui/src/json-visualizer/components/JsonTree.tsx +++ b/packages/twenty-ui/src/json-visualizer/components/JsonTree.tsx @@ -1,20 +1,27 @@ import { JsonList } from '@ui/json-visualizer/components/internal/JsonList'; import { JsonNode } from '@ui/json-visualizer/components/JsonNode'; import { JsonTreeContextProvider } from '@ui/json-visualizer/components/JsonTreeContextProvider'; +import { ShouldExpandNodeInitiallyProps } from '@ui/json-visualizer/contexts/JsonTreeContext'; import { JsonValue } from 'type-fest'; export const JsonTree = ({ value, shouldHighlightNode, + shouldExpandNodeInitially, emptyArrayLabel, emptyObjectLabel, + emptyStringLabel, arrowButtonCollapsedLabel, arrowButtonExpandedLabel, }: { value: JsonValue; shouldHighlightNode?: (keyPath: string) => boolean; + shouldExpandNodeInitially: ( + params: ShouldExpandNodeInitiallyProps, + ) => boolean; emptyArrayLabel: string; emptyObjectLabel: string; + emptyStringLabel: string; arrowButtonCollapsedLabel: string; arrowButtonExpandedLabel: string; }) => { @@ -22,8 +29,10 @@ export const JsonTree = ({ boolean; + shouldExpandNodeInitially: ( + params: ShouldExpandNodeInitiallyProps, + ) => boolean; + emptyStringLabel: string; emptyArrayLabel: string; emptyObjectLabel: string; arrowButtonCollapsedLabel: string; diff --git a/packages/twenty-ui/src/json-visualizer/index.ts b/packages/twenty-ui/src/json-visualizer/index.ts index 649bc8032..cbed5362a 100644 --- a/packages/twenty-ui/src/json-visualizer/index.ts +++ b/packages/twenty-ui/src/json-visualizer/index.ts @@ -8,3 +8,4 @@ export * from './components/JsonValueNode'; export * from './contexts/JsonTreeContext'; export * from './hooks/useJsonTreeContextOrThrow'; export * from './utils/isArray'; +export * from './utils/isTwoFirstDepths'; diff --git a/packages/twenty-ui/src/json-visualizer/utils/isTwoFirstDepths.ts b/packages/twenty-ui/src/json-visualizer/utils/isTwoFirstDepths.ts new file mode 100644 index 000000000..0900df2d7 --- /dev/null +++ b/packages/twenty-ui/src/json-visualizer/utils/isTwoFirstDepths.ts @@ -0,0 +1,4 @@ +import { ShouldExpandNodeInitiallyProps } from '@ui/json-visualizer/contexts/JsonTreeContext'; + +export const isTwoFirstDepths = ({ depth }: ShouldExpandNodeInitiallyProps) => + depth <= 1;