Skip to content

Commit

Permalink
Fix/cartouche map counter (#5920)
Browse files Browse the repository at this point in the history
This PR makes sure the cartouche is showing the array length badge in
the navigator, data picker, and the List Section's List Source Picker.
This PR also makes it so that the number is only pink when there is an
active override in the navigator
  • Loading branch information
balazsbajorics authored Jun 14, 2024
1 parent 8b3a883 commit a4ea2ae
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 144 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ interface MapListSourceCartoucheProps {
target: ElementPath
selected: boolean
openOn: 'single-click' | 'double-click'
countChildren?: boolean
}

export const MapListSourceCartoucheNavigator = React.memo((props: MapListSourceCartoucheProps) => {
Expand All @@ -45,7 +44,7 @@ export const MapListSourceCartoucheNavigator = React.memo((props: MapListSourceC
minWidth: 0,
}}
>
<MapListSourceCartoucheInner {...props} />
<MapListSourceCartoucheInner {...props} source='navigator' />
</div>
)
})
Expand All @@ -54,143 +53,144 @@ MapListSourceCartoucheNavigator.displayName = 'MapListSourceCartoucheNavigator'
export const MapListSourceCartoucheInspector = React.memo((props: MapListSourceCartoucheProps) => {
return (
<CartoucheInspectorWrapper>
<MapListSourceCartoucheInner {...props} />
<MapListSourceCartoucheInner {...props} source='inspector' />
</CartoucheInspectorWrapper>
)
})
MapListSourceCartoucheInspector.displayName = 'MapListSourceCartoucheInspector'

const MapListSourceCartoucheInner = React.memo((props: MapListSourceCartoucheProps) => {
const { target, openOn } = props

const originalMapExpression = useEditorState(
Substores.metadata,
(store) => mapExpressionValueToMapSelector(store, [target]),
'ConditionalSection condition expression',
)

const metadataForElement = useEditorState(
Substores.metadata,
(store) => MetadataUtils.findElementByElementPath(store.editor.jsxMetadata, target),
'ConditionalSection metadata',
)

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 pathToMappedExpression = React.useMemo(
() =>
originalMapExpression === 'multiselect' || originalMapExpression === 'not-a-mapexpression'
? null
: jsxElementChildToValuePath(originalMapExpression.valueToMap),
[originalMapExpression],
)

const { popupIsOpen, DataPickerComponent, setReferenceElement, openPopup } = useDataPickerButton(
variableNamesInScope,
onPickMappedElement,
pathToMappedExpression,
target,
)

const onClick = React.useCallback(() => {
if (openOn === 'single-click') {
openPopup()
}
}, [openOn, openPopup])

const onDoubleClick = React.useCallback(() => {
if (openOn === 'double-click') {
openPopup()
}
}, [openOn, openPopup])

const isDataComingFromHookResult = useEditorState(
Substores.projectContentsAndMetadata,
(store) => {
if (
originalMapExpression === 'multiselect' ||
originalMapExpression === 'not-a-mapexpression'
) {
return false
} else {
const traceDataResult = traceDataFromElement(
originalMapExpression.valueToMap,
target,
store.editor.jsxMetadata,
store.editor.projectContents,
dataPathSuccess([]),
)
return traceDataResult.type === 'hook-result'
const MapListSourceCartoucheInner = React.memo(
(props: MapListSourceCartoucheProps & { source: 'inspector' | 'navigator' }) => {
const { target, openOn } = props

const originalMapExpression = useEditorState(
Substores.metadata,
(store) => mapExpressionValueToMapSelector(store, [target]),
'ConditionalSection condition expression',
)

const metadataForElement = useEditorState(
Substores.metadata,
(store) => MetadataUtils.findElementByElementPath(store.editor.jsxMetadata, target),
'ConditionalSection metadata',
)

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 pathToMappedExpression = React.useMemo(
() =>
originalMapExpression === 'multiselect' || originalMapExpression === 'not-a-mapexpression'
? null
: jsxElementChildToValuePath(originalMapExpression.valueToMap),
[originalMapExpression],
)

const { popupIsOpen, DataPickerComponent, setReferenceElement, openPopup } =
useDataPickerButton(variableNamesInScope, onPickMappedElement, pathToMappedExpression, target)

const onClick = React.useCallback(() => {
if (openOn === 'single-click') {
openPopup()
}
},
'ListSection isDataComingFromHookResult',
)
}, [openOn, openPopup])

const cartoucheDataType: CartoucheDataType = useEditorState(
Substores.projectContentsAndMetadataAndVariablesInScope,
(store) => {
if (
originalMapExpression === 'multiselect' ||
originalMapExpression === 'not-a-mapexpression'
) {
return 'unknown'
const onDoubleClick = React.useCallback(() => {
if (openOn === 'double-click') {
openPopup()
}
return getCartoucheDataTypeForExpression(
target,
originalMapExpression.valueToMap,
store.editor.variablesInScope,
)
},
'MapListSourceCartouche cartoucheDataType',
)

if (originalMapExpression === 'multiselect' || originalMapExpression === 'not-a-mapexpression') {
return null
}

const contentsToDisplay = getTextContentOfElement(
originalMapExpression.valueToMap,
metadataForElement,
)

return (
<React.Fragment>
{popupIsOpen ? DataPickerComponent : null}
<DataCartoucheInner
ref={setReferenceElement}
contentsToDisplay={contentsToDisplay}
onClick={onClick}
onDoubleClick={onDoubleClick}
onDelete={NO_OP}
selected={props.selected}
safeToDelete={false}
testId='list-source-cartouche'
contentIsComingFromServer={isDataComingFromHookResult}
datatype={cartoucheDataType}
badge={
props.countChildren ? <MapCounter selected={props.selected} elementPath={target} /> : null
}, [openOn, openPopup])

const isDataComingFromHookResult = useEditorState(
Substores.projectContentsAndMetadata,
(store) => {
if (
originalMapExpression === 'multiselect' ||
originalMapExpression === 'not-a-mapexpression'
) {
return false
} else {
const traceDataResult = traceDataFromElement(
originalMapExpression.valueToMap,
target,
store.editor.jsxMetadata,
store.editor.projectContents,
dataPathSuccess([]),
)
return traceDataResult.type === 'hook-result'
}
/>
</React.Fragment>
)
})
},
'ListSection isDataComingFromHookResult',
)

const cartoucheDataType: CartoucheDataType = useEditorState(
Substores.projectContentsAndMetadataAndVariablesInScope,
(store) => {
if (
originalMapExpression === 'multiselect' ||
originalMapExpression === 'not-a-mapexpression'
) {
return 'unknown'
}
return getCartoucheDataTypeForExpression(
target,
originalMapExpression.valueToMap,
store.editor.variablesInScope,
)
},
'MapListSourceCartouche cartoucheDataType',
)

if (
originalMapExpression === 'multiselect' ||
originalMapExpression === 'not-a-mapexpression'
) {
return null
}

const contentsToDisplay = getTextContentOfElement(
originalMapExpression.valueToMap,
metadataForElement,
)

return (
<React.Fragment>
{popupIsOpen ? DataPickerComponent : null}
<DataCartoucheInner
ref={setReferenceElement}
contentsToDisplay={contentsToDisplay}
onClick={onClick}
onDoubleClick={onDoubleClick}
onDelete={NO_OP}
selected={props.selected}
safeToDelete={false}
testId='list-source-cartouche'
contentIsComingFromServer={isDataComingFromHookResult}
datatype={cartoucheDataType}
badge={
<MapCounter selected={props.selected} elementPath={target} source={props.source} />
}
/>
</React.Fragment>
)
},
)
12 changes: 8 additions & 4 deletions editor/src/components/navigator/navigator-item/map-counter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@ import { colorTheme } from '../../../uuiui'

export const MapCounterTestIdPrefix = 'map-counter-'

export function getMapCounterTestId(path: ElementPath): string {
return `${MapCounterTestIdPrefix}${EP.toString(path)}`
export function getMapCounterTestId(
path: ElementPath,
source: 'navigator' | 'inspector' | 'data-picker',
): string {
return `${MapCounterTestIdPrefix}${EP.toString(path)}-${source}`
}

type OverrideStatus = 'no-override' | 'overridden' | 'override-failed'
Expand All @@ -27,6 +30,7 @@ type SelectedStatus = true | false
interface MapCounterProps {
elementPath: ElementPath
selected: boolean
source: 'navigator' | 'inspector' | 'data-picker'
}

export const MapCounter = React.memo((props: MapCounterProps) => {
Expand Down Expand Up @@ -101,7 +105,7 @@ export const MapCounter = React.memo((props: MapCounterProps) => {

return (
<MapCounterUi
data-testid={getMapCounterTestId(props.elementPath)}
data-testid={getMapCounterTestId(props.elementPath, props.source)}
counterValue={shownCounterValue}
overrideStatus={overrideStatus}
selectedStatus={selectedStatus}
Expand Down Expand Up @@ -132,7 +136,7 @@ function getMapCounterStyleProps(
return {
...stylePropsBase,
backgroundColor: selectedStatus ? colorTheme.whiteOpacity30.value : colorTheme.bg1.value,
color: selectedStatus ? colorTheme.white.value : colorTheme.brandNeonPink.value,
color: selectedStatus ? colorTheme.white.value : undefined,
}
case 'overridden':
return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1090,7 +1090,6 @@ export const NavigatorRowLabel = React.memo((props: NavigatorRowLabelProps) => {
target={props.navigatorEntry.elementPath}
selected={props.selected}
openOn='double-click'
countChildren={true}
/>
<ComponentPreview
key={`preview-${props.label}`}
Expand Down
13 changes: 7 additions & 6 deletions editor/src/components/navigator/navigator-map.spec.browser2.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint jest/expect-expect: ["error", { "assertFunctionNames": ["expect", "expectRegularCounterWithCount"] }] */
import {
TestSceneUID,
formatTestProjectCode,
Expand Down Expand Up @@ -103,7 +104,7 @@ describe('maps in the navigator', () => {
)

const mapPath = EP.fromString('utopia-storyboard-uid/scene-aaa/containing-div/map')
const counterTestId = getMapCounterTestId(mapPath)
const counterTestId = getMapCounterTestId(mapPath, 'navigator')
const counter = await renderResult.renderedDOM.findByTestId(counterTestId)

expectRegularCounterWithCount(counter, arr.length)
Expand Down Expand Up @@ -227,7 +228,7 @@ describe('maps in the navigator', () => {
mapParent,
)[0]

const counterTestId = getMapCounterTestId(mapElement.elementPath)
const counterTestId = getMapCounterTestId(mapElement.elementPath, 'navigator')
const counter = await renderResult.renderedDOM.findByTestId(counterTestId)

expectOverriddenCounterWithCount(counter, overrideCount, overrideSuccess)
Expand All @@ -246,7 +247,7 @@ describe('maps in the navigator', () => {
)

const mapPath = EP.fromString('utopia-storyboard-uid/scene-aaa/containing-div/map')
const counterTestId = getMapCounterTestId(mapPath)
const counterTestId = getMapCounterTestId(mapPath, 'navigator')
let counter = await renderResult.renderedDOM.findByTestId(counterTestId)
const counterBounds = counter.getBoundingClientRect()
const counterCentre = {
Expand Down Expand Up @@ -279,7 +280,7 @@ describe('maps in the navigator', () => {
)

const mapPath = EP.fromString('utopia-storyboard-uid/scene-aaa/containing-div/map')
const counterTestId = getMapCounterTestId(mapPath)
const counterTestId = getMapCounterTestId(mapPath, 'navigator')
let counter = await renderResult.renderedDOM.findByTestId(counterTestId)
const counterBounds = counter.getBoundingClientRect()
const counterCentre = {
Expand Down Expand Up @@ -312,7 +313,7 @@ describe('maps in the navigator', () => {
)

const mapPath = EP.fromString('utopia-storyboard-uid/scene-aaa/containing-div/map')
const counterTestId = getMapCounterTestId(mapPath)
const counterTestId = getMapCounterTestId(mapPath, 'navigator')
let counter = await renderResult.renderedDOM.findByTestId(counterTestId)
const counterBounds = counter.getBoundingClientRect()
const counterCentre = {
Expand Down Expand Up @@ -341,7 +342,7 @@ describe('maps in the navigator', () => {
)

const mapPath = EP.fromString('utopia-storyboard-uid/scene-aaa/containing-div/map')
const counterTestId = getMapCounterTestId(mapPath)
const counterTestId = getMapCounterTestId(mapPath, 'navigator')
let counter = await renderResult.renderedDOM.findByTestId(counterTestId)
const counterBounds = counter.getBoundingClientRect()
const counterCentre = {
Expand Down

0 comments on commit a4ea2ae

Please sign in to comment.