Skip to content

Commit

Permalink
fix(pte): tools are active only when all blocks use the tool (#6524)
Browse files Browse the repository at this point in the history
* fix(pte): show toolbar as active only if all blocks have tool active

* fix(pte): show toolbar as active only if all blocks have tool active

* fix(pte): correct toggling of marks when part mark is selected

* fix(core): change scrollintoview block to be nearest (#6328)

* fix(core): set scroll boundary to nearest (#6310)

* fix(core): set scroll boundary to nearest

* fix(core): add smooth scroll behavior

* fix(sanity): remove smooth behavior

---------

Co-authored-by: RitaDias <[email protected]>

* fix(@sanity): issue where hidden unicode characters were bloating document in PTE (#6440)

* fix(portable-text-editor): issue shown in tests re stega. use duplicate code

* test(playwright-ct): add test

* chore(sanity): remove prettier linting

* test(sanity): fix missing snapshot

* test(sanity): update test after realising the test would pass always if comparing object number

* chore: test unicode removal

* chore: test unicode removal

* chore(@sanity): remove old solution

* fix(@sanity/block-tools): unicode issue. remove vercel/stega and move to block-tools

* test(@sanity/block-tools): for unicode

* fix(@sanity/block-tools): utf8 characters weren't beign filtered. using the vercel/stega

* chore: update lock file

* (chore): update pnpm lock

* chore: add codeowners to block-tools

* chore(deps): dedupe pnpm-lock.yaml (#6508)

Co-authored-by: github-merge-queue[bot] <118344674+github-merge-queue[bot]@users.noreply.github.com>

* fix(pte): tidying implementation

* fix(@sanity): issue where hidden unicode characters were bloating document in PTE (#6440)

* fix(portable-text-editor): issue shown in tests re stega. use duplicate code

* test(playwright-ct): add test

* chore(sanity): remove prettier linting

* test(sanity): fix missing snapshot

* test(sanity): update test after realising the test would pass always if comparing object number

* chore: test unicode removal

* chore: test unicode removal

* chore(@sanity): remove old solution

* fix(@sanity/block-tools): unicode issue. remove vercel/stega and move to block-tools

* test(@sanity/block-tools): for unicode

* fix(@sanity/block-tools): utf8 characters weren't beign filtered. using the vercel/stega

* chore: update lock file

* (chore): update pnpm lock

* chore(deps): dedupe pnpm-lock.yaml (#6508)

Co-authored-by: github-merge-queue[bot] <118344674+github-merge-queue[bot]@users.noreply.github.com>

* chore(deps): update dependency @sanity/pkg-utils to v6.8.8 (#6509)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* feat: use prefersLatestPublished parameter in DocumentPaneProvider (#6486)

* fix(@sanity): issue where hidden unicode characters were bloating document in PTE (#6440)

* fix(portable-text-editor): issue shown in tests re stega. use duplicate code

* test(playwright-ct): add test

* chore(sanity): remove prettier linting

* test(sanity): fix missing snapshot

* test(sanity): update test after realising the test would pass always if comparing object number

* chore: test unicode removal

* chore: test unicode removal

* chore(@sanity): remove old solution

* fix(@sanity/block-tools): unicode issue. remove vercel/stega and move to block-tools

* test(@sanity/block-tools): for unicode

* fix(@sanity/block-tools): utf8 characters weren't beign filtered. using the vercel/stega

* chore: update lock file

* (chore): update pnpm lock

* chore(deps): dedupe pnpm-lock.yaml (#6508)

Co-authored-by: github-merge-queue[bot] <118344674+github-merge-queue[bot]@users.noreply.github.com>

* chore(deps): update dependency @sanity/pkg-utils to v6.8.8 (#6509)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* fix(pte): simplifying activeAnnotations logic

* fix(pte): new isAnnotationActive static method

* fix(pte): retoring activeAnnotations method in editor

* fix(pte): retoring activeAnnotations method in editor

* fix(pte): fixing issue with multiple annotations in a single block

* fix(pte): fixing issue with multiple annotations in a single block

* fix(pte): fixing typing issue with PortableTextSpan

* fix(pte): fixing failing test since mark toggling has changed

* fix(pte): improving usage of isTextBlock for createWithPortableTextLists

* fix(portable-text-editor): fix issue where decoration would not target correctly (#6532)

* fix(pte): remove unused function in createWithPortableTextMarkModel

* exploration

* fix(pte): reverting incorrect merge

* fix(pte): testing new logic for tools in PTE

* fix(pte): reorg test cases

---------

Co-authored-by: Nina Andal Aarvik <[email protected]>
Co-authored-by: RitaDias <[email protected]>
Co-authored-by: RitaDias <[email protected]>
Co-authored-by: Per-Kristian Nordnes <[email protected]>
Co-authored-by: ecospark[bot] <128108030+ecospark[bot]@users.noreply.github.com>
Co-authored-by: github-merge-queue[bot] <118344674+github-merge-queue[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: cngonzalez <[email protected]>
  • Loading branch information
9 people authored May 2, 2024
1 parent 869b698 commit 169e5fd
Show file tree
Hide file tree
Showing 8 changed files with 261 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,12 @@ export class PortableTextEditor extends Component<PortableTextEditorProps> {
static activeAnnotations = (editor: PortableTextEditor): PortableTextObject[] => {
return editor && editor.editable ? editor.editable.activeAnnotations() : []
}
static isAnnotationActive = (
editor: PortableTextEditor,
annotationType: PortableTextObject['_type'],
): boolean => {
return editor && editor.editable ? editor.editable.isAnnotationActive(annotationType) : false
}
static addAnnotation = (
editor: PortableTextEditor,
type: ObjectSchemaType,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import {describe, expect, it, jest} from '@jest/globals'
import {render, waitFor} from '@testing-library/react'
import {createRef, type RefObject} from 'react'

import {PortableTextEditorTester, schemaType} from '../../__tests__/PortableTextEditorTester'
import {PortableTextEditor} from '../../PortableTextEditor'

describe('plugin:withPortableTextLists', () => {
it('should return active list styles that cover the whole selection', async () => {
const editorRef: RefObject<PortableTextEditor> = createRef()
const initialValue = [
{
_key: 'a',
_type: 'myTestBlockType',
children: [
{
_key: 'a1',
_type: 'span',
marks: [],
text: '12',
},
],
markDefs: [],
style: 'normal',
},
{
_key: 'b',
_type: 'myTestBlockType',
children: [
{
_key: '2',
_type: 'span',
marks: [],
text: '34',
level: 1,
listItem: 'bullet',
},
],
markDefs: [],
style: 'normal',
},
]
const onChange = jest.fn()
await waitFor(() => {
render(
<PortableTextEditorTester
onChange={onChange}
ref={editorRef}
schemaType={schemaType}
value={initialValue}
/>,
)
})
const editor = editorRef.current!
expect(editor).toBeDefined()
await waitFor(() => {
PortableTextEditor.focus(editor)
PortableTextEditor.select(editor, {
focus: {path: [{_key: 'a'}, 'children', {_key: 'a1'}], offset: 0},
anchor: {path: [{_key: '2'}, 'children', {_key: '2'}], offset: 2},
})
expect(PortableTextEditor.hasListStyle(editor, 'bullet')).toBe(false)
})
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -360,23 +360,25 @@ describe('plugin:withPortableTextMarksModel', () => {
})
PortableTextEditor.toggleMark(editor, 'bold')
expect(PortableTextEditor.getValue(editor)).toMatchInlineSnapshot(`
Array [
Object {
"_key": "a",
"_type": "myTestBlockType",
"children": Array [
Object {
"_key": "a1",
"_type": "span",
"marks": Array [],
"text": "1234",
},
],
"markDefs": Array [],
"style": "normal",
},
]
`)
Array [
Object {
"_key": "a",
"_type": "myTestBlockType",
"children": Array [
Object {
"_key": "a1",
"_type": "span",
"marks": Array [
"bold",
],
"text": "1234",
},
],
"markDefs": Array [],
"style": "normal",
},
]
`)
}
})
})
Expand Down Expand Up @@ -765,7 +767,110 @@ describe('plugin:withPortableTextMarksModel', () => {
expect(currentSelectionObject === nextSelectionObject).toBe(false)
expect(onChange).toHaveBeenCalledWith({type: 'selection', selection: nextSelectionObject})
})

it('should return active marks that cover the whole selection', async () => {
const editorRef: RefObject<PortableTextEditor> = createRef()
const initialValue = [
{
_key: 'a',
_type: 'myTestBlockType',
children: [
{
_key: 'a1',
_type: 'span',
marks: ['bold'],
text: '12',
},
{
_key: '2',
_type: 'span',
marks: [],
text: '34',
},
],
markDefs: [{_key: 'bold', _type: 'strong'}],
style: 'normal',
},
]
const onChange = jest.fn()
await waitFor(() => {
render(
<PortableTextEditorTester
onChange={onChange}
ref={editorRef}
schemaType={schemaType}
value={initialValue}
/>,
)
})
const editor = editorRef.current!
expect(editor).toBeDefined()
await waitFor(() => {
PortableTextEditor.focus(editor)
PortableTextEditor.select(editor, {
focus: {path: [{_key: 'a'}, 'children', {_key: 'a1'}], offset: 0},
anchor: {path: [{_key: 'a'}, 'children', {_key: '2'}], offset: 2},
})
expect(PortableTextEditor.isMarkActive(editor, 'bold')).toBe(false)
PortableTextEditor.toggleMark(editor, 'bold')
expect(PortableTextEditor.isMarkActive(editor, 'bold')).toBe(true)
})
})

it('should return active annotation types that cover the whole selection', async () => {
const editorRef: RefObject<PortableTextEditor> = createRef()
const initialValue = [
{
_key: 'a',
_type: 'myTestBlockType',
children: [
{
_key: 'a1',
_type: 'span',
marks: ['bab319ad3a9d'],
text: '12',
},
{
_key: '2',
_type: 'span',
marks: [],
text: '34',
},
],
markDefs: [
{
_key: 'bab319ad3a9d',
_type: 'link',
href: 'http://www.123.com',
},
],
style: 'normal',
},
]
const onChange = jest.fn()
await waitFor(() => {
render(
<PortableTextEditorTester
onChange={onChange}
ref={editorRef}
schemaType={schemaType}
value={initialValue}
/>,
)
})
const editor = editorRef.current!
expect(editor).toBeDefined()
await waitFor(() => {
PortableTextEditor.focus(editor)
PortableTextEditor.select(editor, {
focus: {path: [{_key: 'a'}, 'children', {_key: 'a1'}], offset: 0},
anchor: {path: [{_key: 'a'}, 'children', {_key: '2'}], offset: 2},
})
expect(PortableTextEditor.isAnnotationActive(editor, 'link')).toBe(false)
})
})
})

describe('removing annotations', () => {
it('removes the markDefs if the annotation is no longer in use', async () => {
const editorRef: RefObject<PortableTextEditor> = createRef()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
isPortableTextSpan,
type ObjectSchemaType,
type Path,
type PortableTextBlock,
Expand Down Expand Up @@ -295,6 +296,47 @@ export function createWithEditableAPI(
return []
}
},
isAnnotationActive: (annotationType: PortableTextObject['_type']): boolean => {
if (!editor.selection || editor.selection.focus.path.length < 2) {
return false
}

try {
const spans = [
...Editor.nodes(editor, {
at: editor.selection,
match: (node) => Text.isText(node),
}),
]

if (
spans.some(
([span]) => !isPortableTextSpan(span) || !span.marks || span.marks?.length === 0,
)
)
return false

const selectionMarkDefs = spans.reduce((accMarkDefs, [, path]) => {
const [block] = Editor.node(editor, path, {depth: 1})
if (editor.isTextBlock(block) && block.markDefs) {
return [...accMarkDefs, ...block.markDefs]
}
return accMarkDefs
}, [] as PortableTextObject[])

return spans.every(([span]) => {
if (!isPortableTextSpan(span)) return false

const spanMarkDefs = span.marks?.map(
(markKey) => selectionMarkDefs.find((def) => def?._key === markKey)?._type,
)

return spanMarkDefs?.includes(annotationType)
})
} catch (err) {
return false
}
},
addAnnotation: (
type: ObjectSchemaType,
value?: {[prop: string]: unknown},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,11 +143,14 @@ export function createWithPortableTextLists(types: PortableTextMemberSchemaTypes
const selectedBlocks = [
...Editor.nodes(editor, {
at: editor.selection,
match: (node) => editor.isListBlock(node) && node.listItem === listStyle,
match: (node) => editor.isTextBlock(node),
}),
]

if (selectedBlocks.length > 0) {
return true
return selectedBlocks.every(
([node]) => editor.isListBlock(node) && node.listItem === listStyle,
)
}
return false
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,9 @@
*
*/

import {flatten, isEqual, uniq} from 'lodash'
import {isEqual, uniq} from 'lodash'
import {type Subject} from 'rxjs'
import {
type Descendant,
Editor,
Element,
type NodeEntry,
Path,
Range,
Text,
Transforms,
} from 'slate'
import {type Descendant, Editor, Element, Path, Range, Text, Transforms} from 'slate'

import {
type EditorChange,
Expand Down Expand Up @@ -250,9 +241,8 @@ export function createWithPortableTextMarkModel(
const splitTextNodes = [
...Editor.nodes(editor, {at: editor.selection, match: Text.isText}),
]
const shouldRemoveMark = flatten(
splitTextNodes.map((item) => item[0]).map((node) => node.marks),
).includes(mark)
const shouldRemoveMark = splitTextNodes.every((node) => node[0].marks?.includes(mark))

if (shouldRemoveMark) {
editor.removeMark(mark)
return editor
Expand Down Expand Up @@ -345,19 +335,24 @@ export function createWithPortableTextMarkModel(
if (!editor.selection) {
return false
}
let existingMarks =

const selectedNodes = Array.from(
Editor.nodes(editor, {match: Text.isText, at: editor.selection}),
)

if (Range.isExpanded(editor.selection)) {
return selectedNodes.every((n) => {
const [node] = n

return node.marks?.includes(mark)
})
}

return (
{
...(Editor.marks(editor) || {}),
}.marks || []
if (Range.isExpanded(editor.selection)) {
Array.from(Editor.nodes(editor, {match: Text.isText, at: editor.selection})).forEach(
(n) => {
const [node] = n as NodeEntry<Text>
existingMarks = uniq([...existingMarks, ...((node.marks as string[]) || [])])
},
)
}
return existingMarks.includes(mark)
).includes(mark)
}

// Custom editor function to toggle a mark
Expand Down
1 change: 1 addition & 0 deletions packages/@sanity/portable-text-editor/src/types/editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export interface EditableAPIDeleteOptions {
/** @beta */
export interface EditableAPI {
activeAnnotations: () => PortableTextObject[]
isAnnotationActive: (annotationType: PortableTextObject['_type']) => boolean
addAnnotation: (
type: ObjectSchemaType,
value?: {[prop: string]: unknown},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,14 +77,10 @@ export function useActiveActionKeys({
return useUnique(
useMemo(
() => {
const activeAnnotationKeys = PortableTextEditor.activeAnnotations(editor).map(
(a) => a._type,
)

return actions
.filter((a) => {
if (a.type === 'annotation') {
return activeAnnotationKeys.includes(a.key)
return PortableTextEditor.isAnnotationActive(editor, a.key)
}

if (a.type === 'listStyle') {
Expand Down

0 comments on commit 169e5fd

Please sign in to comment.