From bea75b95320cabc0be26bf37f9e90bdaea039ec8 Mon Sep 17 00:00:00 2001 From: Baptiste Devessier Date: Thu, 3 Apr 2025 08:58:56 +0200 Subject: [PATCH] Set failed node's output as red (#11358) | Error | Success | |--------|--------| | ![CleanShot 2025-04-02 at 18 18 45@2x](https://github.com/user-attachments/assets/6674d4d2-344a-4e16-9608-a70cde07a376) | ![CleanShot 2025-04-02 at 18 20 23@2x](https://github.com/user-attachments/assets/55b5a467-528f-4f07-9166-40ed14943ee2) | Closes https://github.com/twentyhq/core-team-issues/issues/716 --- .../components/WorkflowRunStepInputDetail.tsx | 5 ++- .../WorkflowRunStepOutputDetail.tsx | 14 ++++++- .../__stories__/JsonTree.stories.tsx | 32 +++++++++++++++ .../json-visualizer/components/JsonNode.tsx | 12 +++--- .../json-visualizer/components/JsonTree.tsx | 7 ++-- .../components/JsonValueNode.tsx | 7 ++-- .../components/internal/JsonNodeLabel.tsx | 41 +++++++++++++++---- .../components/internal/JsonNodeValue.tsx | 19 ++++++--- .../contexts/JsonTreeContext.tsx | 3 +- .../twenty-ui/src/json-visualizer/index.ts | 2 + .../types/GetJsonNodeHighlighting.ts | 5 +++ .../types/JsonNodeHighlighting.ts | 3 ++ 12 files changed, 119 insertions(+), 31 deletions(-) create mode 100644 packages/twenty-ui/src/json-visualizer/types/GetJsonNodeHighlighting.ts create mode 100644 packages/twenty-ui/src/json-visualizer/types/JsonNodeHighlighting.ts 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 f86e36d55..83584f72b 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 @@ -11,6 +11,7 @@ import { getActionIcon } from '@/workflow/workflow-steps/workflow-actions/utils/ import { getActionIconColorOrThrow } from '@/workflow/workflow-steps/workflow-actions/utils/getActionIconColorOrThrow'; import { useTheme } from '@emotion/react'; import { useLingui } from '@lingui/react/macro'; +import { isDefined } from 'twenty-shared/utils'; import { IconBrackets, JsonNestedNode, @@ -18,7 +19,6 @@ import { ShouldExpandNodeInitiallyProps, useIcons, } from 'twenty-ui'; -import { isDefined } from 'twenty-shared/utils'; export const WorkflowRunStepInputDetail = ({ stepId }: { stepId: string }) => { const { t, i18n } = useLingui(); @@ -105,7 +105,8 @@ export const WorkflowRunStepInputDetail = ({ stepId }: { stepId: string }) => { emptyStringLabel: t`[empty string]`, arrowButtonCollapsedLabel: t`Expand`, arrowButtonExpandedLabel: t`Collapse`, - shouldHighlightNode: (keyPath) => variablesUsedInStep.has(keyPath), + getNodeHighlighting: (keyPath) => + variablesUsedInStep.has(keyPath) ? 'blue' : undefined, shouldExpandNodeInitially: isFirstNodeDepthOfPreviousStep, }} > diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/components/WorkflowRunStepOutputDetail.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/components/WorkflowRunStepOutputDetail.tsx index 8009bae8e..f8b529bac 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/components/WorkflowRunStepOutputDetail.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/components/WorkflowRunStepOutputDetail.tsx @@ -9,7 +9,12 @@ import { getActionIconColorOrThrow } from '@/workflow/workflow-steps/workflow-ac import { useTheme } from '@emotion/react'; import { useLingui } from '@lingui/react/macro'; import { isDefined } from 'twenty-shared/utils'; -import { isTwoFirstDepths, JsonTree, useIcons } from 'twenty-ui'; +import { + GetJsonNodeHighlighting, + isTwoFirstDepths, + JsonTree, + useIcons, +} from 'twenty-ui'; export const WorkflowRunStepOutputDetail = ({ stepId }: { stepId: string }) => { const { t, i18n } = useLingui(); @@ -42,6 +47,8 @@ export const WorkflowRunStepOutputDetail = ({ stepId }: { stepId: string }) => { }); const headerType = getActionHeaderTypeOrThrow(stepDefinition.definition.type); + const setRedHighlightingForEveryNode: GetJsonNodeHighlighting = () => 'red'; + return ( <> { emptyStringLabel={t`[empty string]`} arrowButtonCollapsedLabel={t`Expand`} arrowButtonExpandedLabel={t`Collapse`} + getNodeHighlighting={ + isDefined(stepOutput.error) + ? setRedHighlightingForEveryNode + : undefined + } /> 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 369b82128..822b3185d 100644 --- a/packages/twenty-ui/src/json-visualizer/__stories__/JsonTree.stories.tsx +++ b/packages/twenty-ui/src/json-visualizer/__stories__/JsonTree.stories.tsx @@ -430,3 +430,35 @@ export const LongText: Story = { }, }, }; + +export const BlueHighlighting: Story = { + args: { + value: { + name: 'John Doe', + age: 30, + }, + getNodeHighlighting: () => 'blue', + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + + const ageElement = await canvas.findByText('age'); + expect(ageElement).toBeVisible(); + }, +}; + +export const RedHighlighting: Story = { + args: { + value: { + name: 'John Doe', + age: 30, + }, + getNodeHighlighting: () => 'red', + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + + const ageElement = await canvas.findByText('age'); + expect(ageElement).toBeVisible(); + }, +}; diff --git a/packages/twenty-ui/src/json-visualizer/components/JsonNode.tsx b/packages/twenty-ui/src/json-visualizer/components/JsonNode.tsx index 0adb38e26..6812354d1 100644 --- a/packages/twenty-ui/src/json-visualizer/components/JsonNode.tsx +++ b/packages/twenty-ui/src/json-visualizer/components/JsonNode.tsx @@ -29,9 +29,9 @@ export const JsonNode = ({ depth: number; keyPath: string; }) => { - const { shouldHighlightNode, emptyStringLabel } = useJsonTreeContextOrThrow(); + const { getNodeHighlighting, emptyStringLabel } = useJsonTreeContextOrThrow(); - const isHighlighted = shouldHighlightNode?.(keyPath) ?? false; + const highlighting = getNodeHighlighting?.(keyPath); if (isNull(value)) { return ( @@ -39,7 +39,7 @@ export const JsonNode = ({ label={label} valueAsString="null" Icon={IconCircleOff} - isHighlighted={isHighlighted} + highlighting={highlighting} /> ); } @@ -50,7 +50,7 @@ export const JsonNode = ({ label={label} valueAsString={isNonEmptyString(value) ? value : emptyStringLabel} Icon={IconTypography} - isHighlighted={isHighlighted} + highlighting={highlighting} /> ); } @@ -61,7 +61,7 @@ export const JsonNode = ({ label={label} valueAsString={String(value)} Icon={IconNumber9} - isHighlighted={isHighlighted} + highlighting={highlighting} /> ); } @@ -72,7 +72,7 @@ export const JsonNode = ({ label={label} valueAsString={String(value)} Icon={IconCheckbox} - isHighlighted={isHighlighted} + highlighting={highlighting} /> ); } diff --git a/packages/twenty-ui/src/json-visualizer/components/JsonTree.tsx b/packages/twenty-ui/src/json-visualizer/components/JsonTree.tsx index 7de9d4aef..596e278b6 100644 --- a/packages/twenty-ui/src/json-visualizer/components/JsonTree.tsx +++ b/packages/twenty-ui/src/json-visualizer/components/JsonTree.tsx @@ -2,11 +2,12 @@ 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 { GetJsonNodeHighlighting } from '@ui/json-visualizer/types/GetJsonNodeHighlighting'; import { JsonValue } from 'type-fest'; export const JsonTree = ({ value, - shouldHighlightNode, + getNodeHighlighting, shouldExpandNodeInitially, emptyArrayLabel, emptyObjectLabel, @@ -15,7 +16,7 @@ export const JsonTree = ({ arrowButtonExpandedLabel, }: { value: JsonValue; - shouldHighlightNode?: (keyPath: string) => boolean; + getNodeHighlighting?: GetJsonNodeHighlighting; shouldExpandNodeInitially: ( params: ShouldExpandNodeInitiallyProps, ) => boolean; @@ -28,7 +29,7 @@ export const JsonTree = ({ return ( theme.spacing(2)}; @@ -10,7 +11,7 @@ const StyledListItem = styled(JsonListItem)` type JsonValueNodeProps = { valueAsString: string; - isHighlighted: boolean; + highlighting: JsonNodeHighlighting | undefined; } & ( | { label: string; @@ -29,13 +30,13 @@ export const JsonValueNode = (props: JsonValueNodeProps) => { )} ); diff --git a/packages/twenty-ui/src/json-visualizer/components/internal/JsonNodeLabel.tsx b/packages/twenty-ui/src/json-visualizer/components/internal/JsonNodeLabel.tsx index 70fa5722b..576d85b82 100644 --- a/packages/twenty-ui/src/json-visualizer/components/internal/JsonNodeLabel.tsx +++ b/packages/twenty-ui/src/json-visualizer/components/internal/JsonNodeLabel.tsx @@ -1,13 +1,30 @@ import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import { IconComponent } from '@ui/display'; +import { JsonNodeHighlighting } from '@ui/json-visualizer/types/JsonNodeHighlighting'; -const StyledLabelContainer = styled.span<{ isHighlighted?: boolean }>` +const StyledLabelContainer = styled.span<{ + highlighting?: JsonNodeHighlighting; +}>` align-items: center; - background-color: ${({ theme }) => theme.background.transparent.lighter}; - border-color: ${({ theme }) => theme.border.color.medium}; - color: ${({ theme, isHighlighted }) => - isHighlighted ? theme.color.blue : theme.font.color.primary}; + background-color: ${({ theme, highlighting }) => + highlighting === 'blue' + ? theme.adaptiveColors.blue1 + : highlighting === 'red' + ? theme.background.danger + : theme.background.transparent.lighter}; + border-color: ${({ theme, highlighting }) => + highlighting === 'blue' + ? theme.adaptiveColors.blue2 + : highlighting === 'red' + ? theme.border.color.danger + : theme.border.color.medium}; + color: ${({ theme, highlighting }) => + highlighting === 'blue' + ? theme.color.blue + : highlighting === 'red' + ? theme.font.color.danger + : theme.font.color.primary}; border-radius: ${({ theme }) => theme.border.radius.sm}; border-style: solid; border-width: 1px; @@ -25,19 +42,25 @@ const StyledLabelContainer = styled.span<{ isHighlighted?: boolean }>` export const JsonNodeLabel = ({ label, Icon, - isHighlighted, + highlighting, }: { label: string; Icon: IconComponent; - isHighlighted?: boolean; + highlighting?: JsonNodeHighlighting | undefined; }) => { const theme = useTheme(); return ( - + {label} diff --git a/packages/twenty-ui/src/json-visualizer/components/internal/JsonNodeValue.tsx b/packages/twenty-ui/src/json-visualizer/components/internal/JsonNodeValue.tsx index 56a33e899..9dfcb4958 100644 --- a/packages/twenty-ui/src/json-visualizer/components/internal/JsonNodeValue.tsx +++ b/packages/twenty-ui/src/json-visualizer/components/internal/JsonNodeValue.tsx @@ -1,16 +1,23 @@ import styled from '@emotion/styled'; +import { JsonNodeHighlighting } from '@ui/json-visualizer/types/JsonNodeHighlighting'; -const StyledText = styled.span<{ isHighlighted?: boolean }>` - color: ${({ theme, isHighlighted }) => - isHighlighted ? theme.adaptiveColors.blue4 : theme.font.color.tertiary}; +const StyledText = styled.span<{ + highlighting: JsonNodeHighlighting | undefined; +}>` + color: ${({ theme, highlighting }) => + highlighting === 'blue' + ? theme.adaptiveColors.blue4 + : highlighting === 'red' + ? theme.font.color.danger + : theme.font.color.tertiary}; `; export const JsonNodeValue = ({ valueAsString, - isHighlighted, + highlighting, }: { valueAsString: string; - isHighlighted?: boolean; + highlighting?: JsonNodeHighlighting | undefined; }) => { - return {valueAsString}; + return {valueAsString}; }; diff --git a/packages/twenty-ui/src/json-visualizer/contexts/JsonTreeContext.tsx b/packages/twenty-ui/src/json-visualizer/contexts/JsonTreeContext.tsx index 4e32dd71d..a5cdaea2f 100644 --- a/packages/twenty-ui/src/json-visualizer/contexts/JsonTreeContext.tsx +++ b/packages/twenty-ui/src/json-visualizer/contexts/JsonTreeContext.tsx @@ -1,9 +1,10 @@ +import { GetJsonNodeHighlighting } from '@ui/json-visualizer/types/GetJsonNodeHighlighting'; import { createContext } from 'react'; export type ShouldExpandNodeInitiallyProps = { keyPath: string; depth: number }; export type JsonTreeContextType = { - shouldHighlightNode?: (keyPath: string) => boolean; + getNodeHighlighting?: GetJsonNodeHighlighting; shouldExpandNodeInitially: ( params: ShouldExpandNodeInitiallyProps, ) => boolean; diff --git a/packages/twenty-ui/src/json-visualizer/index.ts b/packages/twenty-ui/src/json-visualizer/index.ts index cbed5362a..c5432fb2e 100644 --- a/packages/twenty-ui/src/json-visualizer/index.ts +++ b/packages/twenty-ui/src/json-visualizer/index.ts @@ -7,5 +7,7 @@ export * from './components/JsonTreeContextProvider'; export * from './components/JsonValueNode'; export * from './contexts/JsonTreeContext'; export * from './hooks/useJsonTreeContextOrThrow'; +export * from './types/GetJsonNodeHighlighting'; +export * from './types/JsonNodeHighlighting'; export * from './utils/isArray'; export * from './utils/isTwoFirstDepths'; diff --git a/packages/twenty-ui/src/json-visualizer/types/GetJsonNodeHighlighting.ts b/packages/twenty-ui/src/json-visualizer/types/GetJsonNodeHighlighting.ts new file mode 100644 index 000000000..300412232 --- /dev/null +++ b/packages/twenty-ui/src/json-visualizer/types/GetJsonNodeHighlighting.ts @@ -0,0 +1,5 @@ +import { JsonNodeHighlighting } from '@ui/json-visualizer/types/JsonNodeHighlighting'; + +export type GetJsonNodeHighlighting = ( + keyPath: string, +) => JsonNodeHighlighting | undefined; diff --git a/packages/twenty-ui/src/json-visualizer/types/JsonNodeHighlighting.ts b/packages/twenty-ui/src/json-visualizer/types/JsonNodeHighlighting.ts new file mode 100644 index 000000000..fe0465505 --- /dev/null +++ b/packages/twenty-ui/src/json-visualizer/types/JsonNodeHighlighting.ts @@ -0,0 +1,3 @@ +import { ThemeColor } from '@ui/theme'; + +export type JsonNodeHighlighting = Extract;