From d266b885db6bac7c26b75edaefe1af9484411de6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bertalan=20K=C3=B6rmendy?= Date: Tue, 21 May 2024 15:49:51 +0200 Subject: [PATCH] Data picker - code cleanup (#5705) * wip - refactor * update tests * remove todo * refactor if ladder in REPLACE_ELEMENT_IN_SCOPE * remove unused import --- editor/src/components/editor/action-types.ts | 20 +- .../editor/actions/action-creators.ts | 12 -- .../components/editor/actions/action-utils.ts | 1 - .../src/components/editor/actions/actions.tsx | 76 +++++-- .../components/editor/store/editor-update.tsx | 2 - .../component-section/component-section.tsx | 137 ++++++------ .../component-section/data-picker-popup.tsx | 200 ++---------------- .../data-reference-cartouche.tsx | 61 ++++-- .../property-control-controls.tsx | 1 - .../sections/layout-section/list-section.tsx | 130 +----------- .../layout-section/list-source-cartouche.tsx | 77 ++++++- ...performance-regression-tests.spec.tsx.snap | 56 +++++ .../performance-regression-tests.spec.tsx | 4 +- 13 files changed, 324 insertions(+), 453 deletions(-) diff --git a/editor/src/components/editor/action-types.ts b/editor/src/components/editor/action-types.ts index 2827b09858ca..a9332c51f524 100644 --- a/editor/src/components/editor/action-types.ts +++ b/editor/src/components/editor/action-types.ts @@ -170,11 +170,14 @@ export interface ReplaceMappedElement { importsToAdd: Imports } -export type ElementReplacementPath = { - type: 'replace-child-with-uid' - uid: string - replaceWith: JSXElementChild -} +export type ElementReplacementPath = + | { + type: 'replace-child-with-uid' + uid: string + replaceWith: JSXElementChild + } + | { type: 'update-map-expression'; valueToMap: JSExpression } + | { type: 'replace-property-value'; propertyPath: PropertyPath; replaceWith: JSExpression } export interface ReplaceElementInScope { action: 'REPLACE_ELEMENT_IN_SCOPE' @@ -850,12 +853,6 @@ export interface UpdateConditionalExpression { expression: string } -export interface UpdateMapExpression { - action: 'UPDATE_MAP_EXPRESSION' - target: ElementPath - expression: JSExpression -} - export interface AddImports { action: 'ADD_IMPORTS' target: ElementPath @@ -1361,7 +1358,6 @@ export type EditorAction = | SetMapCountOverride | SwitchConditionalBranches | UpdateConditionalExpression - | UpdateMapExpression | ExecutePostActionMenuChoice | ClearPostActionSession | StartPostActionSession diff --git a/editor/src/components/editor/actions/action-creators.ts b/editor/src/components/editor/actions/action-creators.ts index f04e27b8d90c..8fab22291e72 100644 --- a/editor/src/components/editor/actions/action-creators.ts +++ b/editor/src/components/editor/actions/action-creators.ts @@ -231,7 +231,6 @@ import type { IncreaseOnlineStateFailureCount, AddCollapsedViews, ReplaceMappedElement, - UpdateMapExpression, ReplaceTarget, InsertAsChildTarget, ReplaceKeepChildrenAndStyleTarget, @@ -1771,17 +1770,6 @@ export function updateConditionalExpression( } } -export function updateMapExpression( - target: ElementPath, - expression: JSExpression, -): UpdateMapExpression { - return { - action: 'UPDATE_MAP_EXPRESSION', - target: target, - expression: expression, - } -} - export function switchConditionalBranches(target: ElementPath): SwitchConditionalBranches { return { action: 'SWITCH_CONDITIONAL_BRANCHES', diff --git a/editor/src/components/editor/actions/action-utils.ts b/editor/src/components/editor/actions/action-utils.ts index 1717d6747ff6..6aba556d2798 100644 --- a/editor/src/components/editor/actions/action-utils.ts +++ b/editor/src/components/editor/actions/action-utils.ts @@ -209,7 +209,6 @@ export function isTransientAction(action: EditorAction): boolean { case 'SET_MAP_COUNT_OVERRIDE': case 'SWITCH_CONDITIONAL_BRANCHES': case 'UPDATE_CONIDTIONAL_EXPRESSION': - case 'UPDATE_MAP_EXPRESSION': case 'CUT_SELECTION_TO_CLIPBOARD': case 'UPDATE_TOP_LEVEL_ELEMENTS_FROM_COLLABORATION_UPDATE': case 'UPDATE_EXPORTS_DETAIL_FROM_COLLABORATION_UPDATE': diff --git a/editor/src/components/editor/actions/actions.tsx b/editor/src/components/editor/actions/actions.tsx index 34408136c64c..66e5f7928eda 100644 --- a/editor/src/components/editor/actions/actions.tsx +++ b/editor/src/components/editor/actions/actions.tsx @@ -56,7 +56,7 @@ import type { JSXElementChild, JSXConditionalExpression, JSXFragment, - JSXMapExpression, + JSExpression, } from '../../../core/shared/element-template' import { deleteJSXAttribute, @@ -127,6 +127,7 @@ import type { ParseSuccess, ProjectContents, ProjectFile, + PropertyPath, StaticElementPath, TextFile, } from '../../../core/shared/project-file-types' @@ -343,7 +344,6 @@ import type { RemoveFeaturedRoute, AddCollapsedViews, ReplaceMappedElement, - UpdateMapExpression, ReplaceElementInScope, ReplaceJSXElement, } from '../action-types' @@ -2430,17 +2430,67 @@ export const UPDATE_FNS = { } }, REPLACE_ELEMENT_IN_SCOPE: (action: ReplaceElementInScope, editor: EditorModel): EditorModel => { - return modifyUnderlyingTarget(action.target, editor, (element) => { + const replaceChildWithUid = ( + element: JSXElementChild, + uid: string, + replaceWith: JSXElementChild, + ): JSXElementChild => { if (element.type !== 'JSX_ELEMENT' && element.type !== 'JSX_FRAGMENT') { return element } return { ...element, - children: element.children.map((c) => - c.uid !== action.replacementPath.uid ? c : action.replacementPath.replaceWith, + children: element.children.map((c) => (c.uid !== uid ? c : replaceWith)), + } + } + + const updateMapExpression = ( + element: JSXElementChild, + valueToMap: JSExpression, + ): JSXElementChild => { + if (element.type !== 'JSX_MAP_EXPRESSION') { + return element + } + return { + ...element, + valueToMap: valueToMap, + } + } + + const replacePropertyValue = ( + element: JSXElementChild, + propertyPath: PropertyPath, + replaceWith: JSExpression, + ): JSXElementChild => { + if (element.type !== 'JSX_ELEMENT') { + return element + } + return { + ...element, + props: defaultEither( + element.props, + setJSXValueAtPath(element.props, propertyPath, replaceWith), ), } + } + + return modifyUnderlyingTarget(action.target, editor, (element) => { + const replacementPath = action.replacementPath + switch (replacementPath.type) { + case 'replace-child-with-uid': + return replaceChildWithUid(element, replacementPath.uid, replacementPath.replaceWith) + case 'replace-property-value': + return replacePropertyValue( + element, + replacementPath.propertyPath, + replacementPath.replaceWith, + ) + case 'update-map-expression': + return updateMapExpression(element, replacementPath.valueToMap) + default: + assertNever(replacementPath) + } }) }, INSERT_ATTRIBUTE_OTHER_JAVASCRIPT_INTO_ELEMENT: ( @@ -4386,22 +4436,6 @@ export const UPDATE_FNS = { editor, ) }, - UPDATE_MAP_EXPRESSION: (action: UpdateMapExpression, editor: EditorModel): EditorModel => { - return modifyOpenJsxChildAtPath( - action.target, - (element): JSXElementChild => { - if (isJSXMapExpression(element)) { - return { - ...element, - valueToMap: action.expression, - } - } else { - return element - } - }, - editor, - ) - }, UPDATE_CONDITIONAL_EXPRESSION: ( action: UpdateConditionalExpression, editor: EditorModel, diff --git a/editor/src/components/editor/store/editor-update.tsx b/editor/src/components/editor/store/editor-update.tsx index 766e889b86c2..9295548fe965 100644 --- a/editor/src/components/editor/store/editor-update.tsx +++ b/editor/src/components/editor/store/editor-update.tsx @@ -459,8 +459,6 @@ export function runSimpleLocalEditorAction( return UPDATE_FNS.SET_MAP_COUNT_OVERRIDE(action, state) case 'UPDATE_CONIDTIONAL_EXPRESSION': return UPDATE_FNS.UPDATE_CONDITIONAL_EXPRESSION(action, state) - case 'UPDATE_MAP_EXPRESSION': - return UPDATE_FNS.UPDATE_MAP_EXPRESSION(action, state) case 'SWITCH_CONDITIONAL_BRANCHES': return UPDATE_FNS.SWITCH_CONDITIONAL_BRANCHES(action, state) case 'UPDATE_TOP_LEVEL_ELEMENTS_FROM_COLLABORATION_UPDATE': diff --git a/editor/src/components/inspector/sections/component-section/component-section.tsx b/editor/src/components/inspector/sections/component-section/component-section.tsx index 30a3ce038ecb..df4891531389 100644 --- a/editor/src/components/inspector/sections/component-section/component-section.tsx +++ b/editor/src/components/inspector/sections/component-section/component-section.tsx @@ -1,7 +1,7 @@ /** @jsxRuntime classic */ /** @jsx jsx */ import React from 'react' -import { css, jsx } from '@emotion/react' +import { jsx } from '@emotion/react' import type { OptionsType } from 'react-select' import { animated } from 'react-spring' import type { @@ -14,7 +14,7 @@ import type { UnionControlDescription, } from '../../../custom-code/internal-property-controls' import { isBaseControlDescription } from '../../../custom-code/internal-property-controls' -import { PathForSceneProps, sceneMetadata } from '../../../../core/model/scene-utils' +import { PathForSceneProps } from '../../../../core/model/scene-utils' import { mapToArray } from '../../../../core/shared/object-utils' import type { PropertyPath } from '../../../../core/shared/project-file-types' import type { ElementPath } from '../../../../core/shared/project-file-types' @@ -23,7 +23,7 @@ import * as EP from '../../../../core/shared/element-path' import { useKeepReferenceEqualityIfPossible } from '../../../../utils/react-performance' import Utils from '../../../../utils/utils' import type { ParseError } from '../../../../utils/value-parser-utils' -import { getParseErrorDetails, ParseResult } from '../../../../utils/value-parser-utils' +import { getParseErrorDetails } from '../../../../utils/value-parser-utils' import { Tooltip, //TODO: switch last component to functional component and make use of 'useColorTheme': @@ -35,7 +35,6 @@ import { Icons, FlexRow, Icn, - OnClickOutsideHOC, iconForControlType, colorTheme, } from '../../../../uuiui' @@ -49,7 +48,6 @@ import { useInspectorInfoForPropertyControl, } from '../../common/property-controls-hooks' import type { ControlStyles } from '../../common/control-styles' -import { ControlStatus } from '../../common/control-status' import type { InspectorInfo } from '../../common/property-path-hooks' import { useArraySuperControl } from '../../controls/array-supercontrol' import type { SelectOption } from '../../controls/select-control' @@ -78,33 +76,28 @@ import { unless, when } from '../../../../utils/react-conditionals' import { PropertyControlsSection } from './property-controls-section' import type { ReactEventHandlers } from 'react-use-gesture/dist/types' import { normalisePathToUnderlyingTarget } from '../../../custom-code/code-file' -import { openCodeEditorFile, setProp_UNSAFE } from '../../../editor/actions/action-creators' -import { Substores, useEditorState, useRefEditorState } from '../../../editor/store/store-hook' +import { openCodeEditorFile, replaceElementInScope } from '../../../editor/actions/action-creators' +import { Substores, useEditorState } from '../../../editor/store/store-hook' import { MetadataUtils } from '../../../../core/model/element-metadata-utils' import { getFilePathForImportedComponent } from '../../../../core/model/project-file-utils' import { safeIndex } from '../../../../core/shared/array-utils' import { useDispatch } from '../../../editor/store/dispatch-context' import { usePopper } from 'react-popper' +import type { JSExpressionOtherJavaScript } from '../../../../core/shared/element-template' import { - emptyComments, - modifiableAttributeToValuePath, - jsExpressionOtherJavaScriptSimple, - jsIdentifier, getJSXElementNameAsString, isImportedOrigin, } from '../../../../core/shared/element-template' import { optionalMap } from '../../../../core/shared/optional-utils' -import type { VariableData } from '../../../canvas/ui-jsx-canvas' -import { array } from 'prop-types' -import { useVariablesInScopeForSelectedElement } from './variables-in-scope-utils' -import type { DataPickerType } from './data-picker-popup' -import { DataPickerPopup, dataPickerForAProperty } from './data-picker-popup' +import type { DataPickerCallback, VariableOption } from './data-picker-popup' +import { DataPickerPopup, DataPickerPreferredAllAtom } from './data-picker-popup' import { jsxElementChildToText } from '../../../canvas/ui-jsx-canvas-renderer/jsx-element-child-to-text' -import { foldEither } from '../../../../core/shared/either' import { stopPropagation } from '../../common/inspector-utils' -import { NO_OP, identity } from '../../../../core/shared/utils' import { IdentifierExpressionCartoucheControl } from './cartouche-control' import { getRegisteredComponent } from '../../../../core/property-controls/property-controls-utils' +import type { EditorAction } from '../../../editor/action-types' +import { useVariablesInScopeForSelectedElement } from './variables-in-scope-utils' +import { useAtom } from 'jotai' export const VariableFromScopeOptionTestId = (idx: string) => `variable-from-scope-${idx}` export const DataPickerPopupButtonTestId = `data-picker-popup-button-test-id` @@ -344,11 +337,8 @@ function getLabelControlStyle( const isBaseIndentationLevel = (props: AbstractRowForControlProps) => props.indentationLevel === 1 export function useDataPickerButton( - selectedElements: Array, - propPath: PropertyPath, - isScene: boolean, - controlDescription: RegularControlDescription, - dataPickerType?: DataPickerType, + variablesInScope: VariableOption[], + onPropertyPicked: DataPickerCallback, ) { const [referenceElement, setReferenceElement] = React.useState(null) const [popperElement, setPopperElement] = React.useState(null) @@ -367,28 +357,6 @@ export function useDataPickerButton( const openPopup = React.useCallback(() => setPopupIsOpen(true), []) const closePopup = React.useCallback(() => setPopupIsOpen(false), []) - const selectedElement = selectedElements.at(0) ?? EP.emptyElementPath - - const propMetadata = useComponentPropsInspectorInfo(propPath, isScene, controlDescription) - - const propExpressionPath: Array | null = React.useMemo(() => { - return propMetadata.attributeExpression == null - ? null - : foldEither( - () => { - return null - }, - (path) => { - return path - }, - modifiableAttributeToValuePath(propMetadata.attributeExpression), - ) - }, [propMetadata.attributeExpression]) - - const pickerType = React.useMemo(() => { - return dataPickerType ?? dataPickerForAProperty(selectedElement, propPath, propExpressionPath) - }, [dataPickerType, propExpressionPath, propPath, selectedElement]) - const DataPickerComponent = React.useMemo( () => ( ), - [closePopup, pickerType, popper.attributes.popper, popper.styles.popper], + [ + closePopup, + onPropertyPicked, + popper.attributes.popper, + popper.styles.popper, + variablesInScope, + ], ) return { @@ -416,6 +390,45 @@ interface RowForBaseControlProps extends AbstractRowForControlProps { controlDescription: BaseControlDescription } +function setPropertyFromDataPickerActions( + selectedViews: Array, + propertyPath: PropertyPath, + expression: JSExpressionOtherJavaScript, +): Array | null { + const target = selectedViews.at(0) + if (target == null) { + return null + } + + return [ + replaceElementInScope(target, { + type: 'replace-property-value', + propertyPath: propertyPath, + replaceWith: expression, + }), + ] +} + +function useDataPickerButtonInComponentSection( + selectedViews: Array, + propertyPath: PropertyPath, +) { + const dispatch = useDispatch() + + const [preferredAllState] = useAtom(DataPickerPreferredAllAtom) + const variableNamesInScope = useVariablesInScopeForSelectedElement( + selectedViews.at(0) ?? EP.emptyElementPath, + propertyPath, + preferredAllState, + ) + + const dataPickerButtonData = useDataPickerButton(variableNamesInScope, (e) => + optionalMap(dispatch, setPropertyFromDataPickerActions(selectedViews, propertyPath, e)), + ) + + return dataPickerButtonData +} + const RowForBaseControl = React.memo((props: RowForBaseControlProps) => { const { propPath, controlDescription, isScene } = props const title = labelForControl(propPath, controlDescription) @@ -440,12 +453,7 @@ const RowForBaseControl = React.memo((props: RowForBaseControlProps) => { [controlDescription, propMetadata], ) - const dataPickerButtonData = useDataPickerButton( - selectedViews, - props.propPath, - props.isScene, - props.controlDescription, - ) + const dataPickerButtonData = useDataPickerButtonInComponentSection(selectedViews, props.propPath) const handleMouseEnter = React.useCallback(() => { setIsHovered(true) @@ -607,12 +615,7 @@ const RowForArrayControl = React.memo((props: RowForArrayControlProps) => { 'RowForArrayControl selectedViews', ) - const dataPickerButtonData = useDataPickerButton( - selectedViews, - props.propPath, - props.isScene, - props.controlDescription, - ) + const dataPickerButtonData = useDataPickerButtonInComponentSection(selectedViews, props.propPath) return ( @@ -777,11 +780,7 @@ interface RowForTupleControlProps extends AbstractRowForControlProps { const RowForTupleControl = React.memo((props: RowForTupleControlProps) => { const { propPath, controlDescription, isScene } = props const title = labelForControl(propPath, controlDescription) - const { value, onSubmitValue, propertyStatus } = useComponentPropsInspectorInfo( - propPath, - isScene, - controlDescription, - ) + const { value } = useComponentPropsInspectorInfo(propPath, isScene, controlDescription) const rowHeight = UtopiaTheme.layout.rowHeight.normal const transformedValue = Array.isArray(value) ? value : [value] @@ -911,12 +910,8 @@ const RowForObjectControl = React.memo((props: RowForObjectControlProps) => { (store) => store.editor.selectedViews, 'RowForObjectControl selectedViews', ) - const dataPickerButtonData = useDataPickerButton( - selectedViews, - props.propPath, - props.isScene, - props.controlDescription, - ) + + const dataPickerButtonData = useDataPickerButtonInComponentSection(selectedViews, props.propPath) const [isHovered, setIsHovered] = React.useState(false) diff --git a/editor/src/components/inspector/sections/component-section/data-picker-popup.tsx b/editor/src/components/inspector/sections/component-section/data-picker-popup.tsx index 728f86a001ab..b58b0894e861 100644 --- a/editor/src/components/inspector/sections/component-section/data-picker-popup.tsx +++ b/editor/src/components/inspector/sections/component-section/data-picker-popup.tsx @@ -3,22 +3,13 @@ /** @jsx jsx */ import { jsx } from '@emotion/react' import React, { useCallback } from 'react' +import type { JSExpressionOtherJavaScript } from '../../../../core/shared/element-template' import { jsExpressionOtherJavaScriptSimple } from '../../../../core/shared/element-template' import { optionalMap } from '../../../../core/shared/optional-utils' -import type { ElementPath, PropertyPath } from '../../../../core/shared/project-file-types' +import type { PropertyPath } from '../../../../core/shared/project-file-types' import { useColorTheme, Button, FlexColumn, UtopiaStyles } from '../../../../uuiui' -import { - insertJSXElement, - replaceElementInScope, - setProp_UNSAFE, - updateMapExpression, -} from '../../../editor/actions/action-creators' -import { useDispatch } from '../../../editor/store/dispatch-context' -import { useRefEditorState } from '../../../editor/store/store-hook' import { UIGridRow } from '../../widgets/ui-grid-row' import { DataPickerPopupTestId, VariableFromScopeOptionTestId } from './component-section' -import * as EP from '../../../../core/shared/element-path' -import * as PP from '../../../../core/shared/property-path' import type { ArrayInfo, JSXInfo, @@ -26,16 +17,12 @@ import type { PrimitiveInfo, VariableInfo, } from './variables-in-scope-utils' -import { useVariablesInScopeForSelectedElement } from './variables-in-scope-utils' -import { NO_OP, arrayEqualsByValue, assertNever } from '../../../../core/shared/utils' -import { isPrefixOf } from '../../../../core/shared/array-utils' +import { assertNever } from '../../../../core/shared/utils' import { ExpandableIndicator } from '../../../navigator/navigator-item/expandable-indicator' import { FlexRow } from 'utopia-api' -import { is } from '../../../../core/shared/equality-utils' import { atom, useAtom } from 'jotai' import type { SelectOption } from '../../controls/select-control' import { InspectorModal } from '../../widgets/inspector-modal' -import { emptyImports } from '../../../../core/workers/common/project-file-utils' export interface PrimitiveOption { type: 'primitive' @@ -107,127 +94,28 @@ function valueToDisplay(option: VariableOption): string { } } -function isChildrenProp(path: PropertyPath): boolean { - return ( - path.propertyElements.length > 0 && - typeof path.propertyElements[0] === 'string' && - path.propertyElements[0] === 'children' - ) -} - -export interface DataPickerPopupProps { - closePopup: () => void - style: React.CSSProperties - pickerType: DataPickerType -} - -export interface DataPickerForAProperty { - type: 'FOR_A_PROPERTY' - elementPath: ElementPath - propPath: PropertyPath - propExpressionPath: Array | null -} - -export function dataPickerForAProperty( - elementPath: ElementPath, - propPath: PropertyPath, - propExpressionPath: Array | null, -): DataPickerForAProperty { - return { - type: 'FOR_A_PROPERTY', - elementPath: elementPath, - propPath: propPath, - propExpressionPath: propExpressionPath, - } -} - -export interface DataPickerForAMapExpressionValue { - type: 'FOR_A_MAP_EXPRESSION_VALUE' - elementPath: ElementPath -} - -export function dataPickerForAnElement(elementPath: ElementPath): DataPickerForAMapExpressionValue { - return { - type: 'FOR_A_MAP_EXPRESSION_VALUE', - elementPath: elementPath, - } -} - -export interface DataPickerForChildNode { - type: 'FOR_CHILD_NODE' - elementPath: ElementPath - childNodeUid: string -} - -export type DataPickerType = - | DataPickerForAProperty - | DataPickerForAMapExpressionValue - | DataPickerForChildNode - -export function dataPickerIgnoreClass(pickerType: DataPickerType): string { - switch (pickerType.type) { - case 'FOR_A_PROPERTY': - return `ignore-react-onclickoutside-data-picker-${PP.toString(pickerType.propPath)}` - case 'FOR_A_MAP_EXPRESSION_VALUE': - case 'FOR_CHILD_NODE': - return `ignore-react-onclickoutside-data-picker-${EP.toString(pickerType.elementPath)}` - default: - assertNever(pickerType) - } -} - export interface DataPickerPopupSubvariablesProps { preferredAllState: DataPickerFilterOption - pickerType: DataPickerType onTweakProperty: (name: string, definedElsewhere: string | null) => (e: React.MouseEvent) => void customizeOptions: (_: VariableOption[]) => VariableOption[] } -export const DataPickerPopupSubvariables = React.memo((props: DataPickerPopupSubvariablesProps) => { - const { pickerType, preferredAllState, onTweakProperty } = props - const variableNamesInScope = useVariablesInScopeForSelectedElement( - props.pickerType.elementPath, - props.pickerType.type === 'FOR_A_PROPERTY' ? props.pickerType.propPath : null, - preferredAllState, - ) - - const filteredVariableNamesInScope = React.useMemo( - () => props.customizeOptions(variableNamesInScope), - [props, variableNamesInScope], - ) - - return ( - <> - {filteredVariableNamesInScope.map((variableOption, idx) => { - return ( - - ) - })} - - ) -}) +export type DataPickerCallback = (e: JSExpressionOtherJavaScript) => void export interface DataPickerPopupProps { closePopup: () => void - customizeOptions: (_: VariableOption[]) => VariableOption[] style: React.CSSProperties - pickerType: DataPickerType + onTweakProperty: DataPickerCallback + variablesInScope: VariableOption[] } export const DataPickerPopup = React.memo( React.forwardRef((props, forwardedRef) => { - const { closePopup, pickerType } = props + const { closePopup, onTweakProperty, variablesInScope } = props const [preferredAllState, setPreferredAllState] = useAtom(DataPickerPreferredAllAtom) const colorTheme = useColorTheme() - const dispatch = useDispatch() const setMode = React.useCallback( (option: SelectOption) => { @@ -236,7 +124,7 @@ export const DataPickerPopup = React.memo( [setPreferredAllState], ) - const onTweakProperty = React.useCallback( + const onTweakPropertyInner = React.useCallback( (name: string, definedElsewhere: string | null) => (e: React.MouseEvent) => { e.stopPropagation() e.preventDefault() @@ -244,41 +132,9 @@ export const DataPickerPopup = React.memo( const definedElseWhereArray = optionalMap((d) => [d], definedElsewhere) ?? [] const expression = jsExpressionOtherJavaScriptSimple(name, definedElseWhereArray) - switch (pickerType.type) { - case 'FOR_A_PROPERTY': - const isTargetingChildrenProp = isChildrenProp(pickerType.propPath) - if (isTargetingChildrenProp) { - dispatch([ - { - action: 'INSERT_ATTRIBUTE_OTHER_JAVASCRIPT_INTO_ELEMENT', - expression: expression, - parent: props.pickerType.elementPath, - }, - ]) - return - } - - dispatch([ - setProp_UNSAFE(props.pickerType.elementPath, pickerType.propPath, expression), - ]) - break - case 'FOR_A_MAP_EXPRESSION_VALUE': - dispatch([updateMapExpression(props.pickerType.elementPath, expression)]) - break - case 'FOR_CHILD_NODE': - dispatch([ - replaceElementInScope(pickerType.elementPath, { - type: 'replace-child-with-uid', - uid: pickerType.childNodeUid, - replaceWith: expression, - }), - ]) - break - default: - assertNever(pickerType) - } + onTweakProperty(expression) }, - [dispatch, pickerType, props.pickerType.elementPath], + [onTweakProperty], ) const filterOptions = React.useMemo( @@ -307,7 +163,7 @@ export const DataPickerPopup = React.memo( zIndex: 1, }} closePopupOnUnmount={false} - outsideClickIgnoreClass={dataPickerIgnoreClass(pickerType)} + outsideClickIgnoreClass={'ignore-react-onclickoutside-data-picker'} >
- + {variablesInScope.map((variableOption, idx) => { + return ( + + ) + })}
@@ -396,20 +256,13 @@ interface ValueRowProps { variableOption: VariableOption idx: string onTweakProperty: (name: string, definedElsewhere: string | null) => (e: React.MouseEvent) => void - pickerType: DataPickerType overriddenTitle?: string } const anyObjectChildMatches = (info: VariableInfo): boolean => info.type === 'object' && info.props.some((c) => c.matches || anyObjectChildMatches(c)) -function ValueRow({ - variableOption, - idx, - onTweakProperty, - pickerType, - overriddenTitle, -}: ValueRowProps) { +function ValueRow({ variableOption, idx, onTweakProperty, overriddenTitle }: ValueRowProps) { const colorTheme = useColorTheme() const [selectedIndex, setSelectedIndex] = React.useState(0) @@ -442,11 +295,6 @@ function ValueRow({ const hasObjectChildren = variableOption.type === 'object' && variableOption.children.length > 0 - const currentExpressionExactMatch = - pickerType.type === 'FOR_A_PROPERTY' && - pickerType.propExpressionPath != null && - arrayEqualsByValue(variableOption.valuePath, pickerType.propExpressionPath, is) - const onClickTopLevelButton = React.useCallback( (e: React.MouseEvent) => { if (variableOption.disabled) { @@ -481,8 +329,6 @@ function ValueRow({ height: 28, marginTop: variableChildren != null && variableOption.depth === 0 ? 6 : 0, // add some space between top-level variables cursor: variableOption.variableInfo.matches ? 'pointer' : 'default', - background: currentExpressionExactMatch ? colorTheme.primary.value : undefined, - color: currentExpressionExactMatch ? colorTheme.white.value : undefined, paddingLeft: variableOption.depth * 8, }} onClick={onClickTopLevelButton} @@ -568,7 +414,6 @@ function ValueRow({ variableOption={variableChildren[selectedIndex]} idx={`${idx}-${selectedIndex}`} onTweakProperty={onTweakProperty} - pickerType={pickerType} overriddenTitle={`${variableOption.variableInfo.expressionPathPart}[${selectedIndex}]`} /> ) : childrenOpen ? ( @@ -579,7 +424,6 @@ function ValueRow({ variableOption={child} idx={`${idx}-${index}`} onTweakProperty={onTweakProperty} - pickerType={pickerType} /> ) }) diff --git a/editor/src/components/inspector/sections/component-section/data-reference-cartouche.tsx b/editor/src/components/inspector/sections/component-section/data-reference-cartouche.tsx index fc62097921eb..3667a739dfd2 100644 --- a/editor/src/components/inspector/sections/component-section/data-reference-cartouche.tsx +++ b/editor/src/components/inspector/sections/component-section/data-reference-cartouche.tsx @@ -3,6 +3,7 @@ import { MetadataUtils } from '../../../../core/model/element-metadata-utils' import * as EP from '../../../../core/shared/element-path' import type { ElementInstanceMetadata, + JSExpressionOtherJavaScript, JSXElementChild, } from '../../../../core/shared/element-template' import { getJSXElementNameLastPart } from '../../../../core/shared/element-template' @@ -17,8 +18,11 @@ import { useDispatch } from '../../../editor/store/dispatch-context' import { selectComponents } from '../../../editor/actions/meta-actions' import { when } from '../../../../utils/react-conditionals' import { dataPathSuccess, traceDataFromElement } from '../../../../core/data-tracing/data-tracing' -import type { DataPickerType } from './data-picker-popup' import type { RenderedAt } from '../../../editor/store/editor-state' +import { replaceElementInScope } from '../../../editor/actions/action-creators' +import { useVariablesInScopeForSelectedElement } from './variables-in-scope-utils' +import { DataPickerPreferredAllAtom } from './data-picker-popup' +import { useAtom } from 'jotai' interface DataReferenceCartoucheControlProps { elementPath: ElementPath @@ -58,32 +62,49 @@ export const DataReferenceCartoucheControl = React.memo( 'IdentifierExpressionCartoucheControl trace', ) - const renderedAt = + const updateDataWithDataPicker = React.useCallback( + (expression: JSExpressionOtherJavaScript) => { + switch (props.renderedAt.type) { + case 'element-property-path': + return dispatch([ + replaceElementInScope(props.renderedAt.elementPropertyPath.elementPath, { + type: 'replace-property-value', + propertyPath: props.renderedAt.elementPropertyPath.propertyPath, + replaceWith: expression, + }), + ]) + case 'child-node': + return dispatch([ + replaceElementInScope(props.renderedAt.parentPath, { + type: 'replace-child-with-uid', + uid: props.renderedAt.nodeUid, + replaceWith: expression, + }), + ]) + default: + assertNever(props.renderedAt) + } + }, + [dispatch, props.renderedAt], + ) + + const propertyPath = props.renderedAt.type === 'element-property-path' - ? props.renderedAt.elementPropertyPath + ? props.renderedAt.elementPropertyPath.propertyPath : props.renderedAt.type === 'child-node' - ? EPP.create(EP.parentPath(elementPath), PP.create('children')) + ? null : assertNever(props.renderedAt) - const pickerType: DataPickerType | undefined = - props.renderedAt.type !== 'child-node' - ? undefined - : { - type: 'FOR_CHILD_NODE', - childNodeUid: props.renderedAt.nodeUid, - elementPath: props.renderedAt.parentPath, - } + const [preferredAllState] = useAtom(DataPickerPreferredAllAtom) - const dataPickerButtonData = useDataPickerButton( - [renderedAt.elementPath], - renderedAt.propertyPath, - false, // TODO we need to kill this parameter - { - control: 'none', - }, - pickerType, + const variableNamesInScope = useVariablesInScopeForSelectedElement( + elementPath, + propertyPath, + preferredAllState, ) + const dataPickerButtonData = useDataPickerButton(variableNamesInScope, updateDataWithDataPicker) + const isDataComingFromHookResult = dataTraceResult.type === 'hook-result' const onClick = React.useCallback(() => { diff --git a/editor/src/components/inspector/sections/component-section/property-control-controls.tsx b/editor/src/components/inspector/sections/component-section/property-control-controls.tsx index 0b944d4ac173..638237158077 100644 --- a/editor/src/components/inspector/sections/component-section/property-control-controls.tsx +++ b/editor/src/components/inspector/sections/component-section/property-control-controls.tsx @@ -82,7 +82,6 @@ import { assertNever } from '../../../../core/shared/utils' import { preventDefault, stopPropagation } from '../../common/inspector-utils' import { unless, when } from '../../../../utils/react-conditionals' import { MetadataUtils } from '../../../../core/model/element-metadata-utils' -import { useDataPickerButton } from './component-section' import { parseNoneControlDescription } from '../../../../core/property-controls/property-controls-parser' import * as EP from '../../../../core/shared/element-path' diff --git a/editor/src/components/inspector/sections/layout-section/list-section.tsx b/editor/src/components/inspector/sections/layout-section/list-section.tsx index ab9444459cc3..aebcfa37862c 100644 --- a/editor/src/components/inspector/sections/layout-section/list-section.tsx +++ b/editor/src/components/inspector/sections/layout-section/list-section.tsx @@ -2,8 +2,6 @@ /** @jsx jsx */ import { jsx } from '@emotion/react' import createCachedSelector from 're-reselect' import React from 'react' -import { usePopper } from 'react-popper' -import type { ArrayControlDescription } from 'utopia-api/core' import { MetadataUtils } from '../../../../core/model/element-metadata-utils' import { mapDropNulls } from '../../../../core/shared/array-utils' import { isLeft } from '../../../../core/shared/either' @@ -15,25 +13,13 @@ import type { } from '../../../../core/shared/element-template' import { isJSXMapExpression } from '../../../../core/shared/element-template' import type { ElementPath } from '../../../../core/shared/project-file-types' -import { NO_OP, assertNever } from '../../../../core/shared/utils' import type { JSXParsedValue } from '../../../../utils/value-parser-utils' -import { - Button, - FlexRow, - Icn, - InspectorSectionIcons, - UtopiaTheme, - useColorTheme, -} from '../../../../uuiui' +import { FlexRow, InspectorSectionIcons, UtopiaTheme, useColorTheme } from '../../../../uuiui' import { Substores, useEditorState } from '../../../editor/store/store-hook' import type { MetadataSubstate } from '../../../editor/store/store-hook-substore-types' import { UIGridRow } from '../../widgets/ui-grid-row' -import { DataPickerPopupButtonTestId } from '../component-section/component-section' -import type { VariableOption } from '../component-section/data-picker-popup' -import { DataPickerPopup, dataPickerForAnElement } from '../component-section/data-picker-popup' import { getTextContentOfElement } from '../component-section/data-reference-cartouche' import { JSXPropertyControlForListSection } from '../component-section/property-control-controls' -import { useVariablesInScopeForSelectedElement } from '../component-section/variables-in-scope-utils' import { MapListSourceCartouche } from './list-source-cartouche' type MapExpression = JSXMapExpression | 'multiselect' | 'not-a-mapexpression' @@ -88,120 +74,6 @@ function getMapExpressionMetadata( export const ListSectionTestId = 'list-section' -function filterVariableOption(option: VariableOption): VariableOption | null { - switch (option.type) { - case 'array': - return { - ...option, - children: filterKeepArraysOnly(option.children), - disabled: false, - } - case 'object': - const children = filterKeepArraysOnly(option.children) - if (children.length === 0) { - // no array-valued children found - return null - } - return { - ...option, - children: children, - disabled: true, - } - case 'jsx': - case 'primitive': - return null - default: - assertNever(option) - } -} - -function filterKeepArraysOnly(options: VariableOption[]): VariableOption[] { - return mapDropNulls((o) => filterVariableOption(o), options) -} - -export function useDataPickerButtonListSource(selectedElements: Array) { - const [referenceElement, setReferenceElement] = React.useState(null) - const [popperElement, setPopperElement] = React.useState(null) - const popper = usePopper(referenceElement, popperElement, { - modifiers: [ - { - name: 'offset', - options: { - offset: [0, 8], - }, - }, - ], - }) - - const [popupIsOpen, setPopupIsOpen] = React.useState(false) - const togglePopup = React.useCallback(() => setPopupIsOpen((v) => !v), []) - const openPopup = React.useCallback(() => setPopupIsOpen(true), []) - const closePopup = React.useCallback(() => setPopupIsOpen(false), []) - - const onClick = React.useCallback( - (e: React.MouseEvent) => { - e.stopPropagation() - e.preventDefault() - - togglePopup() - }, - [togglePopup], - ) - - const selectedElement = selectedElements.at(0) ?? EP.emptyElementPath - - const variablePickerButtonAvailable = - useVariablesInScopeForSelectedElement(selectedElement, null, 'all').length > 0 - const variablePickerButtonTooltipText = variablePickerButtonAvailable - ? 'Pick data source' - : 'No data sources available' - - const pickerType = React.useMemo(() => { - return dataPickerForAnElement(selectedElement) - }, [selectedElement]) - - const DataPickerComponent = React.useMemo( - () => ( - - ), - [closePopup, pickerType, popper.attributes.popper, popper.styles.popper], - ) - - const DataPickerOpener = React.useMemo( - () => ( - - ), - [onClick, variablePickerButtonAvailable, variablePickerButtonTooltipText], - ) - - return { - popupIsOpen, - DataPickerOpener, - DataPickerComponent, - setReferenceElement, - openPopup, - } -} - export const ListSection = React.memo(({ paths }: { paths: ElementPath[] }) => { const target = paths.at(0) const colorTheme = useColorTheme() diff --git a/editor/src/components/inspector/sections/layout-section/list-source-cartouche.tsx b/editor/src/components/inspector/sections/layout-section/list-source-cartouche.tsx index 6a1b55833c65..63d1fe7ff213 100644 --- a/editor/src/components/inspector/sections/layout-section/list-source-cartouche.tsx +++ b/editor/src/components/inspector/sections/layout-section/list-source-cartouche.tsx @@ -3,15 +3,55 @@ import React from 'react' import { MetadataUtils } from '../../../../core/model/element-metadata-utils' import type { ElementPath } from '../../../../core/shared/project-file-types' -import { NO_OP } from '../../../../core/shared/utils' +import { NO_OP, assertNever } from '../../../../core/shared/utils' import { Substores, useEditorState } from '../../../editor/store/store-hook' import { DataCartoucheInner, getTextContentOfElement, } from '../component-section/data-reference-cartouche' -import { mapExpressionValueToMapSelector, useDataPickerButtonListSource } from './list-section' +import { mapExpressionValueToMapSelector } from './list-section' +import { useDataPickerButton } from '../component-section/component-section' +import { useDispatch } from '../../../editor/store/dispatch-context' +import type { JSExpressionOtherJavaScript } from '../../../../core/shared/element-template' +import { replaceElementInScope } from '../../../editor/actions/action-creators' +import { useAtom } from 'jotai' +import type { VariableOption } from '../component-section/data-picker-popup' +import { DataPickerPreferredAllAtom } from '../component-section/data-picker-popup' +import { useVariablesInScopeForSelectedElement } from '../component-section/variables-in-scope-utils' +import { mapDropNulls } from '../../../../core/shared/array-utils' import { traceDataFromElement, dataPathSuccess } from '../../../../core/data-tracing/data-tracing' +function filterVariableOption(option: VariableOption): VariableOption | null { + switch (option.type) { + case 'array': + return { + ...option, + children: filterKeepArraysOnly(option.children), + disabled: false, + } + case 'object': + const children = filterKeepArraysOnly(option.children) + if (children.length === 0) { + // no array-valued children found + return null + } + return { + ...option, + children: children, + disabled: true, + } + case 'jsx': + case 'primitive': + return null + default: + assertNever(option) + } +} + +function filterKeepArraysOnly(options: VariableOption[]): VariableOption[] { + return mapDropNulls((o) => filterVariableOption(o), options) +} + interface MapListSourceCartoucheProps { target: ElementPath inverted: boolean @@ -34,8 +74,37 @@ export const MapListSourceCartouche = React.memo((props: MapListSourceCartoucheP 'ConditionalSection metadata', ) - const { popupIsOpen, DataPickerComponent, setReferenceElement, openPopup } = - useDataPickerButtonListSource([target]) + const dispatch = useDispatch() + + const onPickMappedElement = React.useCallback( + (expression: JSExpressionOtherJavaScript) => { + dispatch([ + replaceElementInScope(target, { + type: 'update-map-expression', + valueToMap: expression, + }), + ]) + }, + [dispatch, target], + ) + + const [preferredAllState] = useAtom(DataPickerPreferredAllAtom) + + const variableNamesInScope = useVariablesInScopeForSelectedElement( + target, + null, + preferredAllState, + ) + + const filteredVariableNamesInScope = React.useMemo( + () => filterKeepArraysOnly(variableNamesInScope), + [variableNamesInScope], + ) + + const { popupIsOpen, DataPickerComponent, setReferenceElement, openPopup } = useDataPickerButton( + filteredVariableNamesInScope, + onPickMappedElement, + ) const onClick = React.useCallback(() => { if (openOn === 'single-click') { diff --git a/editor/src/core/performance/__snapshots__/performance-regression-tests.spec.tsx.snap b/editor/src/core/performance/__snapshots__/performance-regression-tests.spec.tsx.snap index 1387806a9d18..6466aa0712fe 100644 --- a/editor/src/core/performance/__snapshots__/performance-regression-tests.spec.tsx.snap +++ b/editor/src/core/performance/__snapshots__/performance-regression-tests.spec.tsx.snap @@ -411,6 +411,7 @@ Array [ "//UtopiaSpiedExoticType(Symbol(react.fragment))//UtopiaSpiedFunctionComponent(Menu)", "/UtopiaSpiedExoticType(Symbol(react.fragment))//UtopiaSpiedFunctionComponent(Menu)/UtopiaSpiedFunctionComponent(RefTrackerProvider)", "//UtopiaSpiedFunctionComponent(Menu)/UtopiaSpiedFunctionComponent(RefTrackerProvider)/UtopiaSpiedExoticType(Symbol(react.provider))", + "/null///Symbol(react.memo)(Symbol(react.forward_ref)())", "/null///UtopiaSpiedFunctionComponent(PropertyLabelAndPlusButton)", "/null///Symbol(react.memo)(PropertyLabel)", "/null///Symbol(react.memo)()", @@ -624,6 +625,13 @@ Array [ "/UtopiaSpiedExoticType(Symbol(react.fragment))/PropertyLabel/Symbol(react.forward_ref)(EmotionCssPropInternal)/div", "/Symbol(react.provider)///Symbol(react.memo)(Inspector)", "/Symbol(react.provider)///Symbol(react.memo)()", + "/null///Symbol(react.memo)(Symbol(react.forward_ref)())", + "/null///UtopiaSpiedFunctionComponent(PropertyLabelAndPlusButton)", + "/null///Symbol(react.memo)(PropertyLabel)", + "/null///Symbol(react.memo)()", + "/null///div", + "/null///UtopiaSpiedFunctionComponent(UIGridRow)", + "/null///UtopiaSpiedClass(InspectorContextMenuWrapper)", "/Symbol(react.forward_ref)(Styled(div))/div/IndicatorArrow/div:data-testid='indicator-elements-outside-visible-area'", ] `; @@ -1269,6 +1277,7 @@ Array [ "//UtopiaSpiedExoticType(Symbol(react.fragment))//UtopiaSpiedFunctionComponent(Menu)", "/UtopiaSpiedExoticType(Symbol(react.fragment))//UtopiaSpiedFunctionComponent(Menu)/UtopiaSpiedFunctionComponent(RefTrackerProvider)", "//UtopiaSpiedFunctionComponent(Menu)/UtopiaSpiedFunctionComponent(RefTrackerProvider)/UtopiaSpiedExoticType(Symbol(react.provider))", + "/null///Symbol(react.memo)(Symbol(react.forward_ref)())", "/null///UtopiaSpiedFunctionComponent(PropertyLabelAndPlusButton)", "/null///Symbol(react.memo)(PropertyLabel)", "/null///Symbol(react.memo)()", @@ -1745,6 +1754,7 @@ Array [ "//UtopiaSpiedExoticType(Symbol(react.fragment))//UtopiaSpiedFunctionComponent(Menu)", "/UtopiaSpiedExoticType(Symbol(react.fragment))//UtopiaSpiedFunctionComponent(Menu)/UtopiaSpiedFunctionComponent(RefTrackerProvider)", "//UtopiaSpiedFunctionComponent(Menu)/UtopiaSpiedFunctionComponent(RefTrackerProvider)/UtopiaSpiedExoticType(Symbol(react.provider))", + "/null///Symbol(react.memo)(Symbol(react.forward_ref)())", "/null///UtopiaSpiedFunctionComponent(PropertyLabelAndPlusButton)", "/null///Symbol(react.memo)(PropertyLabel)", "/null///Symbol(react.memo)()", @@ -1994,6 +2004,52 @@ Array [ "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)(SelectionAreaRectangle)", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/UtopiaSpiedExoticType(Symbol(react.fragment))", "/Symbol(react.forward_ref)(Styled(div))/div/Symbol(react.forward_ref)(Styled(div))/div", + "/null///Symbol(react.memo)(Symbol(react.forward_ref)())", + "/null///UtopiaSpiedFunctionComponent(PropertyLabelAndPlusButton)", + "/null///Symbol(react.memo)(PropertyLabel)", + "/null///Symbol(react.memo)()", + "/null///div", + "/null///UtopiaSpiedFunctionComponent(UIGridRow)", + "/null///UtopiaSpiedClass(InspectorContextMenuWrapper)", + "///UtopiaSpiedClass(InspectorContextMenuWrapper)/UtopiaSpiedFunctionComponent(MenuProvider)", + "///UtopiaSpiedClass(InspectorContextMenuWrapper)/UtopiaSpiedClass(MomentumContextMenu)", + "///UtopiaSpiedClass(InspectorContextMenuWrapper)/UtopiaSpiedExoticType(Symbol(react.fragment))", + "///UtopiaSpiedClass(InspectorContextMenuWrapper)/Symbol(react.forward_ref)(EmotionCssPropInternal):data-testid='context-menu-for-onMouseDown'", + "//UtopiaSpiedClass(InspectorContextMenuWrapper)/Symbol(react.forward_ref)(EmotionCssPropInternal)/div:data-testid='context-menu-for-onMouseDown'", + "/div/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedFunctionComponent(MenuProvider)/div", + "/UtopiaSpiedFunctionComponent(MenuProvider)/div/UtopiaSpiedFunctionComponent(UIGridRow)/Symbol(react.forward_ref)(EmotionCssPropInternal)", + "/div/UtopiaSpiedFunctionComponent(UIGridRow)/Symbol(react.forward_ref)(EmotionCssPropInternal)/div", + "/Symbol(react.forward_ref)(EmotionCssPropInternal)/div/PropertyLabel/Symbol(react.forward_ref)(EmotionCssPropInternal)", + "/div/PropertyLabel/Symbol(react.forward_ref)(EmotionCssPropInternal)/div", + "/Symbol(react.forward_ref)(EmotionCssPropInternal)/div/UtopiaSpiedFunctionComponent(PropertyLabelAndPlusButton)/span", + "/Symbol(react.forward_ref)(EmotionCssPropInternal)/div/UtopiaSpiedFunctionComponent(PropertyLabelAndPlusButton)/Symbol(react.memo)(Icon)", + "/Symbol(react.forward_ref)(EmotionCssPropInternal)/div/UtopiaSpiedFunctionComponent(PropertyLabelAndPlusButton)/div", + "/Symbol(react.forward_ref)(EmotionCssPropInternal)/div/UtopiaSpiedFunctionComponent(PropertyLabelAndPlusButton)/div", + "/Symbol(react.forward_ref)(EmotionCssPropInternal)/div/UtopiaSpiedFunctionComponent(PropertyLabelAndPlusButton)/UtopiaSpiedClass(Tooltip)", + "/div/UtopiaSpiedFunctionComponent(PropertyLabelAndPlusButton)/UtopiaSpiedClass(Tooltip)/Symbol(react.forward_ref)(EmotionCssPropInternal)", + "/UtopiaSpiedFunctionComponent(PropertyLabelAndPlusButton)/UtopiaSpiedClass(Tooltip)/Symbol(react.forward_ref)(EmotionCssPropInternal)/Symbol(react.forward_ref)(TippyWrapper)", + "/UtopiaSpiedClass(Tooltip)/Symbol(react.forward_ref)(EmotionCssPropInternal)/Symbol(react.forward_ref)(TippyWrapper)/UtopiaSpiedFunctionComponent(Tippy)", + "/Symbol(react.forward_ref)(EmotionCssPropInternal)/Symbol(react.forward_ref)(TippyWrapper)/UtopiaSpiedFunctionComponent(Tippy)/UtopiaSpiedExoticType(Symbol(react.fragment))", + "/div/div//Symbol(react.memo)()", + "/div/div//Symbol(react.memo)()", + "/div///Symbol(react.forward_ref)()", + "///Symbol(react.forward_ref)()/Symbol(react.memo)()", + "///Symbol(react.forward_ref)()/div", + "///Symbol(react.forward_ref)()/UtopiaSpiedClass(Tooltip)", + "///Symbol(react.forward_ref)()/Symbol(react.memo)(Icon):data-testid='delete-cartouche-onMouseDown'", + "///Symbol(react.forward_ref)()/Symbol(react.forward_ref)(Styled(div))", + "///Symbol(react.forward_ref)()/div", + "/Symbol(react.forward_ref)()/div/Symbol(react.forward_ref)(Styled(div))/div", + "/Symbol(react.forward_ref)(Styled(div))/div/UtopiaSpiedClass(Tooltip)/Symbol(react.forward_ref)(EmotionCssPropInternal)", + "/div/UtopiaSpiedClass(Tooltip)/Symbol(react.forward_ref)(EmotionCssPropInternal)/Symbol(react.forward_ref)(TippyWrapper)", + "/UtopiaSpiedClass(Tooltip)/Symbol(react.forward_ref)(EmotionCssPropInternal)/Symbol(react.forward_ref)(TippyWrapper)/UtopiaSpiedFunctionComponent(Tippy)", + "/Symbol(react.forward_ref)(EmotionCssPropInternal)/Symbol(react.forward_ref)(TippyWrapper)/UtopiaSpiedFunctionComponent(Tippy)/UtopiaSpiedExoticType(Symbol(react.fragment))", + "/div/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedClass(MomentumContextMenu)/span", + "/div/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedClass(MomentumContextMenu)/span", + "/div/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedClass(MomentumContextMenu)/UtopiaSpiedFunctionComponent(Item)", + "/div/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedClass(MomentumContextMenu)/UtopiaSpiedFunctionComponent(Menu)", + "/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedClass(MomentumContextMenu)/UtopiaSpiedFunctionComponent(Menu)/UtopiaSpiedFunctionComponent(RefTrackerProvider)", + "/UtopiaSpiedClass(MomentumContextMenu)/UtopiaSpiedFunctionComponent(Menu)/UtopiaSpiedFunctionComponent(RefTrackerProvider)/UtopiaSpiedExoticType(Symbol(react.provider))", "//UtopiaSpiedExoticType(Symbol(react.fragment))//Symbol(react.memo)()", "//UtopiaSpiedExoticType(Symbol(react.fragment))//UtopiaSpiedFunctionComponent(Menu)", "/UtopiaSpiedExoticType(Symbol(react.fragment))//UtopiaSpiedFunctionComponent(Menu)/UtopiaSpiedFunctionComponent(RefTrackerProvider)", diff --git a/editor/src/core/performance/performance-regression-tests.spec.tsx b/editor/src/core/performance/performance-regression-tests.spec.tsx index a509ee1e6682..9d20c74add22 100644 --- a/editor/src/core/performance/performance-regression-tests.spec.tsx +++ b/editor/src/core/performance/performance-regression-tests.spec.tsx @@ -127,7 +127,7 @@ describe('React Render Count Tests -', () => { const renderCountAfter = renderResult.getNumberOfRenders() // if this breaks, GREAT NEWS but update the test please :) - expect(renderCountAfter - renderCountBefore).toMatchInlineSnapshot(`859`) + expect(renderCountAfter - renderCountBefore).toMatchInlineSnapshot(`907`) expect(renderResult.getRenderInfo()).toMatchSnapshot() }) @@ -249,7 +249,7 @@ describe('React Render Count Tests -', () => { const renderCountAfter = renderResult.getNumberOfRenders() // if this breaks, GREAT NEWS but update the test please :) - expect(renderCountAfter - renderCountBefore).toMatchInlineSnapshot(`623`) + expect(renderCountAfter - renderCountBefore).toMatchInlineSnapshot(`631`) expect(renderResult.getRenderInfo()).toMatchSnapshot() }) })