Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/concrete-utopia/utopia in…
Browse files Browse the repository at this point in the history
…to tests/pizza-fake-user
  • Loading branch information
bkrmendy committed Jan 3, 2024
2 parents b44a8dc + 7cd24e9 commit 0e7ffe6
Show file tree
Hide file tree
Showing 32 changed files with 875 additions and 221 deletions.
17 changes: 11 additions & 6 deletions editor/src/components/canvas/controls/comment-indicator.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
/** @jsxRuntime classic */
/** @jsx jsx */
import { jsx } from '@emotion/react'
import type { CSSObject, Interpolation } from '@emotion/react'
import type { Interpolation } from '@emotion/react'
import type { ThreadData } from '@liveblocks/client'
import React from 'react'
import type { ThreadMetadata } from '../../../../liveblocks.config'
import { useEditThreadMetadata, useStorage, useThreads } from '../../../../liveblocks.config'
import { useEditThreadMetadata, useStorage } from '../../../../liveblocks.config'
import {
useCanvasLocationOfThread,
useActiveThreads,
Expand Down Expand Up @@ -40,6 +40,7 @@ import { optionalMap } from '../../../core/shared/optional-utils'
import { setRightMenuTab } from '../../editor/actions/action-creators'
import { RightMenuTab, getCurrentTheme } from '../../editor/store/editor-state'
import { when } from '../../../utils/react-conditionals'
import { CommentRepliesCounter } from './comment-replies-counter'

const IndicatorSize = 24
const MagnifyScale = 1.15
Expand Down Expand Up @@ -452,6 +453,12 @@ const HoveredCommentIndicator = React.memo((props: HoveredCommentIndicatorProps)
return (
<div
style={{
display: 'flex',
flexDirection: 'column',
borderRadius: '18px 18px 18px 0px',
width: 250,
boxShadow: UtopiaStyles.shadowStyles.mid.boxShadow,
background: colorTheme.bg1.value,
position: 'fixed',
// temporarily moving the hovered comment indicator to align with the not hovered version
top: position.y - 40.5,
Expand All @@ -462,17 +469,15 @@ const HoveredCommentIndicator = React.memo((props: HoveredCommentIndicatorProps)
>
<CommentWrapper
style={{
borderRadius: '18px 18px 18px 0px',
width: 250,
boxShadow: UtopiaStyles.shadowStyles.mid.boxShadow,
background: colorTheme.bg1.value,
overflow: 'auto',
}}
data-theme={theme}
user={user}
comment={comment}
showActions={false}
/>
<CommentRepliesCounter thread={thread} />
<div style={{ height: 8 }} />
</div>
)
})
Expand Down
3 changes: 2 additions & 1 deletion editor/src/components/canvas/controls/comment-popup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,8 @@ const CommentThread = React.memo(({ comment }: CommentThreadProps) => {
return
}
resolveThread(thread)
}, [thread, resolveThread])
dispatch([switchEditorMode(EditorModes.commentMode(null, 'not-dragging'))])
}, [thread, resolveThread, dispatch])

const onClickClose = React.useCallback(() => {
dispatch([switchEditorMode(EditorModes.commentMode(null, 'not-dragging'))])
Expand Down
31 changes: 31 additions & 0 deletions editor/src/components/canvas/controls/comment-replies-counter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React from 'react'
import type { ThreadData } from '@liveblocks/client'
import type { ThreadMetadata } from '../../../../liveblocks.config'
import { useColorTheme } from '../../../uuiui'

interface CommentRepliesCounterProps {
thread: ThreadData<ThreadMetadata>
}

export const CommentRepliesCounter = React.memo((props: CommentRepliesCounterProps) => {
const colorTheme = useColorTheme()

const repliesCount = props.thread.comments.filter((c) => c.deletedAt == null).length - 1

if (repliesCount <= 0) {
return <div />
}

return (
<div
style={{
paddingLeft: 44,
fontSize: 9,
color: colorTheme.fg6.value,
}}
>
{repliesCount} {repliesCount > 1 ? 'replies' : 'reply'}
</div>
)
})
CommentRepliesCounter.displayName = 'CommentRepliesCounter'
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* eslint jest/expect-expect: ["error", { "assertFunctionNames": ["expect", "checkFocusedPath", "checkSelectedPaths"] }] */
/* eslint jest/expect-expect: ["error", { "assertFunctionNames": ["expect", "checkFocusedPath", "checkSelectedPaths", "selectInjectedDivAndCheckAllPaths"] }] */
/// <reference types="karma-viewport" />
import { BakedInStoryboardUID } from '../../../../core/model/scene-utils'
import * as EP from '../../../../core/shared/element-path'
Expand Down Expand Up @@ -1736,6 +1736,86 @@ describe('mouseup selection', () => {
})
})

describe('Problematic selection', () => {
// This was a real world bug caused by the unholy combination of these things

async function selectInjectedDivAndCheckAllPaths(
renderResult: EditorRenderResult,
injectedDiv: HTMLElement,
desiredPaths: Array<ElementPath>,
) {
const injectedDivBounds = injectedDiv.getBoundingClientRect()

const canvasControlsLayer = renderResult.renderedDOM.getByTestId(CanvasControlsContainerID)
const doubleClick = createDoubleClicker(
canvasControlsLayer,
injectedDivBounds.left + injectedDivBounds.width / 2,
injectedDivBounds.top + injectedDivBounds.height / 2,
)

await fireSingleClickEvents(
canvasControlsLayer,
injectedDivBounds.left + injectedDivBounds.width / 2,
injectedDivBounds.top + injectedDivBounds.height / 2,
)

checkFocusedPath(renderResult, null)
checkSelectedPaths(renderResult, [desiredPaths[0]])

await doubleClick()
checkFocusedPath(renderResult, null)
checkSelectedPaths(renderResult, [desiredPaths[1]])

await doubleClick()
checkFocusedPath(renderResult, desiredPaths[1])
checkSelectedPaths(renderResult, [desiredPaths[1]])

await doubleClick()
checkFocusedPath(renderResult, desiredPaths[1])
checkSelectedPaths(renderResult, [desiredPaths[2]])
}

it('Can keep clicking to select the parent of a dangerouslySetInnerHTML element, inside a component with a fragment', async () => {
// prettier-ignore
const desiredPaths = createConsecutivePaths(
'sb' + // Skipped as it's the storyboard
'/div-a', // Single click to select
'/FragmentAtRoot', // Double click to select, double click again to focus
':FragmentAtRoot-root' + // Skipped as it's locked
'/FragmentAtRoot-target' // Third double click to select
)

const renderResult = await renderTestEditorWithCode(
DangerouslySetInnerHTMLProject,
'await-first-dom-report',
)

const injectedDiv = renderResult.renderedDOM.getAllByTestId('injected-div')[0]

await selectInjectedDivAndCheckAllPaths(renderResult, injectedDiv, desiredPaths)
})

it('Can keep clicking to select the parent of a dangerouslySetInnerHTML element, inside a component with a div', async () => {
// prettier-ignore
const desiredPaths = createConsecutivePaths(
'sb' + // Skipped as it's the storyboard
'/div-b', // Single click to select
'/DivAtRoot', // Double click to select, double click again to focus
':DivAtRoot-root' + // Skipped as it's locked
'/DivAtRoot-target' // Third double click
)

const renderResult = await renderTestEditorWithCode(
DangerouslySetInnerHTMLProject,
'await-first-dom-report',
)

const injectedDiv = renderResult.renderedDOM.getAllByTestId('injected-div')[1]

await selectInjectedDivAndCheckAllPaths(renderResult, injectedDiv, desiredPaths)
})
})

function createConsecutivePaths(...partialPathStrings: Array<string>): Array<ElementPath> {
return partialPathStrings.map((_value, index, arr) => {
const joinedParts = arr.slice(0, index + 1).join('')
Expand All @@ -1761,6 +1841,80 @@ function checkWithKey<T>(key: string, actual: T, expected: T) {
})
}

const DangerouslySetInnerHTMLProject = `
import * as React from 'react'
import { Scene, Storyboard } from 'utopia-api'
const content =
'<div ' +
' style=" ' +
' background-color: lightblue;' +
' width: 150px;' +
' height: 150px;' +
' " ' +
' data-testid="injected-div" ' +
' >Click me if you can!</div> '
const UnSelectable = () => {
return (
<React.Fragment data-uid='FragmentAtRoot-root'>
You can't select this
<div
dangerouslySetInnerHTML={{ __html: content }}
data-uid='FragmentAtRoot-target'
/>
</React.Fragment>
)
}
const Selectable = () => {
return (
<div data-uid='DivAtRoot-root'>
You can select this
<div
dangerouslySetInnerHTML={{ __html: content }}
data-uid='DivAtRoot-target'
/>
</div>
)
}
const Card = (props) => {
return <div style={props.style}>{props.children}</div>
}
export var storyboard = (
<Storyboard data-uid='sb'>
<div
style={{
backgroundColor: '#aaaaaa33',
position: 'absolute',
left: 400,
top: 50,
width: 300,
height: 300,
}}
data-uid='div-a'
>
<UnSelectable data-uid='FragmentAtRoot' />
</div>
<div
style={{
backgroundColor: '#aaaaaa33',
position: 'absolute',
left: 400,
top: 364,
width: 300,
height: 300,
}}
data-uid='div-b'
>
<Selectable data-uid='DivAtRoot' />
</div>
</Storyboard>
)
`

const generateTestProjectAlpineClimb = (conditional: boolean, conditionalSiblings: boolean) => `
import * as React from "react";
import { Scene, Storyboard } from "utopia-api";
Expand Down
8 changes: 4 additions & 4 deletions editor/src/components/canvas/design-panel-root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ import { useRoom, useStatus } from '../../../liveblocks.config'
import { MultiplayerWrapper } from '../../utils/multiplayer-wrapper'
import { isFeatureEnabled } from '../../utils/feature-switches'
import { CommentsPane } from '../inspector/comments-pane'
import { useIsViewer } from '../editor/store/project-server-state-hooks'
import { EditorModes, isCommentMode } from '../editor/editor-modes'
import { useAllowedToEditProject } from '../editor/store/collaborative-editing'

function isCodeEditorEnabled(): boolean {
if (typeof window !== 'undefined') {
Expand Down Expand Up @@ -170,7 +170,7 @@ export const RightPane = React.memo<ResizableRightPaneProps>((props) => {
onClickTab(RightMenuTab.Settings)
}, [onClickTab])

const isViewer = useIsViewer()
const allowedToEdit = useAllowedToEditProject()

if (!isRightMenuExpanded) {
return null
Expand All @@ -196,8 +196,8 @@ export const RightPane = React.memo<ResizableRightPaneProps>((props) => {
selected={selectedTab === RightMenuTab.Inspector}
onClick={onClickInspectorTab}
/>
{unless(
isViewer,
{when(
allowedToEdit,
<>
<MenuTab
label={'Insert'}
Expand Down
4 changes: 4 additions & 0 deletions editor/src/components/canvas/dom-lookup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ export function firstAncestorOrItselfWithValidElementPath(
): ElementPath | null {
const staticAndDynamicTargetElementPaths = getStaticAndDynamicElementPathsForDomElement(target)

if (staticAndDynamicTargetElementPaths.length === 0) {
return null
}

const validStaticElementPaths = getValidStaticElementPathsForDomElement(
target,
parentSceneValidPathsCache,
Expand Down
12 changes: 6 additions & 6 deletions editor/src/components/editor/canvas-toolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ import {
} from '../canvas/ui/floating-insert-menu'
import { isFeatureEnabled } from '../../utils/feature-switches'
import { RightMenuTab, floatingInsertMenuStateSwap } from './store/editor-state'
import { useIsViewer } from './store/project-server-state-hooks'
import { useStatus } from '../../../liveblocks.config'
import { useAllowedToEditProject } from './store/collaborative-editing'

export const InsertMenuButtonTestId = 'insert-menu-button'
export const PlayModeButtonTestId = 'canvas-toolbar-play-mode'
Expand Down Expand Up @@ -429,7 +429,7 @@ export const CanvasToolbar = React.memo(() => {
roomStatus !== 'connected'
? CommentModeButtonTestId('disconnected')
: CommentModeButtonTestId('connected')
const isViewer = useIsViewer()
const allowedToEdit = useAllowedToEditProject()

return (
<FlexColumn
Expand Down Expand Up @@ -463,8 +463,8 @@ export const CanvasToolbar = React.memo(() => {
style={{ width: 36 }}
/>
</Tooltip>
{unless(
isViewer,
{when(
allowedToEdit,
<>
<Tooltip title='Insert or Edit Text' placement='bottom'>
<InsertModeButton
Expand Down Expand Up @@ -548,7 +548,7 @@ export const CanvasToolbar = React.memo(() => {
{when(
canvasToolbarMode.primary === 'edit' &&
canvasToolbarMode.secondary === 'selected' &&
!isViewer,
allowedToEdit,
<>
{when(
insertMenuMode === 'closed',
Expand Down Expand Up @@ -677,7 +677,7 @@ export const CanvasToolbar = React.memo(() => {
{when(
canvasToolbarMode.primary === 'edit' &&
canvasToolbarMode.secondary === 'strategy-active' &&
!isViewer,
allowedToEdit,
<StrategyIndicator />,
)}
{/* Insert Mode */}
Expand Down
11 changes: 4 additions & 7 deletions editor/src/components/editor/editor-component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,7 @@ import { useDispatch } from './store/dispatch-context'
import type { EditorAction } from './action-types'
import { EditorCommon } from './editor-component-common'
import { notice } from '../common/notice'
import {
ProjectServerStateUpdater,
isProjectViewer,
isProjectViewerFromState,
} from './store/project-server-state'
import { ProjectServerStateUpdater } from './store/project-server-state'
import { RoomProvider, initialPresence, useRoom, initialStorage } from '../../../liveblocks.config'
import { generateUUID } from '../../utils/utils'
import { isLiveblocksEnabled } from './liveblocks-utils'
Expand All @@ -72,6 +68,7 @@ import LiveblocksProvider from '@liveblocks/yjs'
import { isRoomId, projectIdToRoomId } from '../../core/shared/multiplayer'
import { useDisplayOwnershipWarning } from './project-owner-hooks'
import { EditorModes } from './editor-modes'
import { allowedToEditProject } from './store/collaborative-editing'

const liveModeToastId = 'play-mode-toast'

Expand Down Expand Up @@ -359,9 +356,9 @@ export const EditorComponentInner = React.memo((props: EditorProps) => {

useSelectorWithCallback(
Substores.projectServerState,
(store) => store.projectServerState.isMyProject,
(store) => store.projectServerState,
(isMyProject) => {
if (isProjectViewer(isMyProject)) {
if (!allowedToEditProject(isMyProject)) {
dispatch([
EditorActions.switchEditorMode(EditorModes.commentMode(null, 'not-dragging')),
EditorActions.setRightMenuTab(RightMenuTab.Comments),
Expand Down
Loading

0 comments on commit 0e7ffe6

Please sign in to comment.