Skip to content

Commit

Permalink
fix(pte): cursor presence causes pte error (#6622)
Browse files Browse the repository at this point in the history
* fix(pte): cursor presence causes pte error

* test(playwright-ct): fix outdated test

Since the leaf is now properly rendered for the presence decorator, we are interested in the next nextSibling

* fix(pte): update tests

---------

Co-authored-by: Per-Kristian Nordnes <per.kristian.nordnes@gmail.com>
  • Loading branch information
2 people authored and ricokahler committed May 14, 2024
1 parent b63af70 commit b13fff4
Show file tree
Hide file tree
Showing 7 changed files with 109 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -298,13 +298,13 @@ describe('selection adjustment', () => {
],
},
{
_key: 'B-7',
_key: 'B-6',
_type: 'block',
markDefs: [],
style: 'normal',
children: [
{
_key: 'B-6',
_key: 'B-5',
_type: 'span',
text: '',
marks: [],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,65 @@ describe('plugin:withInsertBreak: "enter"', () => {
}
})
})
it('inserts the new block after if key enter is pressed at the start of the block, creating a new one in "after" position if the block is empty', async () => {
const initialSelection = {
focus: {path: [{_key: 'a'}, 'children', {_key: 'a1'}], offset: 0},
anchor: {path: [{_key: 'a'}, 'children', {_key: 'a1'}], offset: 0},
}
const emptyBlock = {
_key: 'a',
_type: 'myTestBlockType',
children: [
{
_key: 'a1',
_type: 'span',
marks: [],
text: '',
},
],
markDefs: [],
style: 'normal',
}

const editorRef: RefObject<PortableTextEditor> = createRef()
const onChange = jest.fn()
render(
<PortableTextEditorTester
onChange={onChange}
ref={editorRef}
schemaType={schemaType}
value={[emptyBlock]}
/>,
)
const editor = editorRef.current
const inlineType = editor?.schemaTypes.inlineObjects.find((t) => t.name === 'someObject')
await waitFor(async () => {
if (editor && inlineType) {
PortableTextEditor.focus(editor)
PortableTextEditor.select(editor, initialSelection)
PortableTextEditor.insertBreak(editor)

const value = PortableTextEditor.getValue(editor)
expect(value).toEqual([
emptyBlock,
{
_key: '2',
_type: 'myTestBlockType',
markDefs: [],
style: 'normal',
children: [
{
_key: '1',
_type: 'span',
marks: [],
text: '',
},
],
},
])
}
})
})
it('splits the text block key if enter is pressed at the middle of the block', async () => {
const initialSelection = {
focus: {path: [{_key: 'b'}, 'children', {_key: 'b1'}], offset: 2},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import {Editor, Node, Path, Range, Transforms} from 'slate'

import {type PortableTextSlateEditor} from '../../types/editor'
import {type PortableTextMemberSchemaTypes, type PortableTextSlateEditor} from '../../types/editor'
import {type SlateTextBlock, type VoidElement} from '../../types/slate'
import {isEqualToEmptyEditor} from '../../utils/values'

/**
* Changes default behavior of insertBreak to insert a new block instead of splitting current when the cursor is at the
* start of the block.
*/
export function createWithInsertBreak(): (
editor: PortableTextSlateEditor,
) => PortableTextSlateEditor {
export function createWithInsertBreak(
types: PortableTextMemberSchemaTypes,
): (editor: PortableTextSlateEditor) => PortableTextSlateEditor {
return function withInsertBreak(editor: PortableTextSlateEditor): PortableTextSlateEditor {
const {insertBreak} = editor

Expand All @@ -23,7 +24,8 @@ export function createWithInsertBreak(): (
const [, end] = Range.edges(editor.selection)
// If it's at the start of block, we want to preserve the current block key and insert a new one in the current position instead of splitting the node.
const isEndAtStartOfNode = Editor.isStart(editor, end, end.path)
if (isEndAtStartOfNode) {
const isEmptyTextBlock = focusBlock && isEqualToEmptyEditor([focusBlock], types)
if (isEndAtStartOfNode && !isEmptyTextBlock) {
Editor.insertNode(editor, editor.pteCreateEmptyBlock())
const [nextBlockPath] = Path.next(focusBlockPath)
Transforms.select(editor, {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export const withPlugins = <T extends Editor>(

const withPlaceholderBlock = createWithPlaceholderBlock()

const withInsertBreak = createWithInsertBreak()
const withInsertBreak = createWithInsertBreak(schemaTypes)

const withUtils = createWithUtils({keyGenerator, schemaTypes, portableTextEditor})
const withPortableTextSelections = createWithPortableTextSelections(change$, schemaTypes)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ async function getSiblingTextContent(page: Page) {
const cursorB = document.querySelector('[data-testid="presence-cursor-User-B"]')

return {
cursorA: cursorA?.nextElementSibling?.textContent,
cursorB: cursorB?.nextElementSibling?.textContent,
cursorA: cursorA?.nextElementSibling?.nextElementSibling?.textContent,
cursorB: cursorB?.nextElementSibling?.nextElementSibling?.textContent,
}
})
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,11 +108,12 @@ const UserText = styled(motion(Text))`
`

interface UserPresenceCursorProps {
children?: React.ReactNode
user: User
}

export function UserPresenceCursor(props: UserPresenceCursorProps): JSX.Element {
const {user} = props
const {children, user} = props
const {tints} = useUserColor(user.id)
const [hovered, setHovered] = useState<boolean>(false)

Expand All @@ -125,39 +126,42 @@ export function UserPresenceCursor(props: UserPresenceCursorProps): JSX.Element
)

return (
<CursorLine
$tints={tints}
contentEditable={false}
data-testid={testId}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
>
<AnimatePresence>
{hovered && (
<UserBox
animate="animate"
exit="exit"
flex={1}
initial="initial"
transition={CONTENT_BOX_TRANSITION}
variants={CONTENT_BOX_VARIANTS}
>
<UserText
<>
<CursorLine
$tints={tints}
contentEditable={false}
data-testid={testId}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
>
<AnimatePresence>
{hovered && (
<UserBox
animate="animate"
exit="exit"
flex={1}
initial="initial"
size={0}
transition={CONTENT_TEXT_TRANSITION}
variants={CONTENT_TEXT_VARIANTS}
weight="medium"
transition={CONTENT_BOX_TRANSITION}
variants={CONTENT_BOX_VARIANTS}
>
{user.displayName}
</UserText>
</UserBox>
)}
</AnimatePresence>

<CursorDot />
</CursorLine>
<UserText
animate="animate"
exit="exit"
initial="initial"
size={0}
transition={CONTENT_TEXT_TRANSITION}
variants={CONTENT_TEXT_VARIANTS}
weight="medium"
>
{user.displayName}
</UserText>
</UserBox>
)}
</AnimatePresence>

<CursorDot />
</CursorLine>
{children}
</>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,9 @@ export function usePresenceCursorDecorations(
const cursorPoint = {focus: presence.selection.focus, anchor: presence.selection.focus}

return {
component: () => <UserPresenceCursor user={presence.user} />,
component: ({children}) => (
<UserPresenceCursor user={presence.user}>{children}</UserPresenceCursor>
),
selection: cursorPoint,
onMoved: handleRangeDecorationMoved,
payload: {sessionId: presence.sessionId},
Expand Down

0 comments on commit b13fff4

Please sign in to comment.