Skip to content

Commit

Permalink
refactor(portable-text-editor): make sure editor content has normaliz…
Browse files Browse the repository at this point in the history
…ed style prop

When a PT value is loaded into the editor, the blocks should be normalized with a style
property. This prop is not required in the TS type or PT spec, but we should put the default
style on the in-memory value so that it's initialized and set when editing that block
with the editor.
  • Loading branch information
skogsmaskin committed Feb 22, 2022
1 parent e94633d commit ad91bf2
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 69 deletions.
17 changes: 13 additions & 4 deletions packages/@sanity/portable-text-editor/src/editor/Editable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ export const PortableTextEditable = forwardRef(function PortableTextEditable(
() =>
toSlateValue(
getValueOrInitialValue(value, [placeHolderBlock]),
blockType.name,
portableTextEditor,
KEY_TO_SLATE_ELEMENT.get(slateEditor)
),
// eslint-disable-next-line react-hooks/exhaustive-deps
Expand Down Expand Up @@ -246,13 +246,13 @@ export const PortableTextEditable = forwardRef(function PortableTextEditable(
const defaultValue = [placeHolderBlock]
const slateValueFromProps = toSlateValue(
getValueOrInitialValue(value, defaultValue),
blockType.name,
portableTextEditor,
KEY_TO_SLATE_ELEMENT.get(slateEditor)
)
const val: PortableTextBlock[] = value || defaultValue
val.forEach((blk, index) => {
if (slateEditor.isTextBlock(blk)) {
if (!isEqual(toSlateValue([blk], blockType.name)[0], slateEditor.children[index])) {
if (!isEqual(toSlateValue([blk], portableTextEditor)[0], slateEditor.children[index])) {
equal = false
}
} else {
Expand Down Expand Up @@ -281,7 +281,16 @@ export const PortableTextEditable = forwardRef(function PortableTextEditable(
}
change$.next({type: 'value', value})
}
}, [change$, isSelecting, isThrottling, placeHolderBlock, blockType.name, slateEditor, value])
}, [
change$,
isSelecting,
isThrottling,
placeHolderBlock,
blockType.name,
slateEditor,
value,
portableTextEditor,
])

// Restore selection from props
useEffect(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ export function createWithEditableAPI(
],
},
],
portableTextFeatures.types.block.name
portableTextEditor
)[0] as unknown) as SlateElement
const child = block.children[0]
Editor.insertNode(editor, child as Node)
Expand All @@ -172,7 +172,7 @@ export function createWithEditableAPI(
...(value ? value : {}),
},
],
portableTextFeatures.types.block.name
portableTextEditor
)[0] as unknown) as Node
Editor.insertNode(editor, block)
editor.onChange()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export function createWithHotkeys(
],
},
],
portableTextFeatures.types.block.name
portableTextEditor
)[0]
return function withHotKeys(editor: PortableTextSlateEditor & ReactEditor) {
editor.pteWithHotKeys = (event: React.KeyboardEvent<HTMLDivElement>): void => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ export function createWithInsertData(
if (Array.isArray(parsed) && parsed.length > 0) {
const slateValue = regenerateKeys(
editor,
toSlateValue(parsed, blockTypeName),
toSlateValue(parsed, {portableTextFeatures}),
keyGenerator,
spanTypeName
)
Expand Down Expand Up @@ -177,7 +177,7 @@ export function createWithInsertData(
html,
portableTextFeatures.types.portableText
).map((block: PortableTextBlock) => normalizeBlock(block, {blockTypeName}))
fragment = toSlateValue(portableText, blockTypeName)
fragment = toSlateValue(portableText, {portableTextFeatures})
insertedType = 'HTML'
} else {
// plain text
Expand All @@ -189,9 +189,9 @@ export function createWithInsertData(
.join('')
const textToHtml = `<html><body>${blocks}</body></html>`
portableText = htmlToBlocks(textToHtml, portableTextFeatures.types.portableText)
fragment = toSlateValue(portableText, blockTypeName).map((block: PortableTextBlock) =>
normalizeBlock(block, {blockTypeName})
)
fragment = toSlateValue(portableText, {
portableTextFeatures,
}).map((block: PortableTextBlock) => normalizeBlock(block, {blockTypeName}))
insertedType = 'text'
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import {fromSlateValue, toSlateValue} from '../values'
import {type} from '../../editor/__tests__/PortableTextEditorTester'
import {getPortableTextFeatures} from '../getPortableTextFeatures'

const portableTextFeatures = getPortableTextFeatures(type)

describe('toSlateValue', () => {
it('checks undefined', () => {
const result = toSlateValue(undefined, 'image')
const result = toSlateValue(undefined, {portableTextFeatures})
expect(result).toHaveLength(0)
})

it('runs given empty array', () => {
const result = toSlateValue([], 'image')
const result = toSlateValue([], {portableTextFeatures})
expect(result).toHaveLength(0)
})

Expand All @@ -19,7 +23,7 @@ describe('toSlateValue', () => {
_key: '123',
},
],
'block'
{portableTextFeatures}
)

expect(result).toMatchObject([
Expand All @@ -40,39 +44,43 @@ describe('toSlateValue', () => {
const result = toSlateValue(
[
{
_type: 'block',
_type: portableTextFeatures.types.block.name,
_key: '123',
children: [
{
_type: 'span',
_key: '1231',
value: '123',
text: '123',
},
],
},
],
'block'
{portableTextFeatures}
)
expect(result).toEqual([
{
_key: '123',
_type: 'block',
children: [
{
_key: '1231',
_type: 'span',
value: '123',
},
],
},
])
expect(result).toMatchInlineSnapshot(`
Array [
Object {
"_key": "123",
"_type": "myTestBlockType",
"children": Array [
Object {
"_key": "1231",
"_type": "span",
"text": "123",
},
],
"markDefs": Array [],
"style": "normal",
},
]
`)
})

it('given type is block and has custom object in children', () => {
const result = toSlateValue(
[
{
_type: 'block',
_type: portableTextFeatures.types.block.name,
_key: '123',
children: [
{
Expand All @@ -90,39 +98,43 @@ describe('toSlateValue', () => {
],
},
],
'block'
{portableTextFeatures}
)
expect(result).toEqual([
{
_key: '123',
_type: 'block',
children: [
{
_key: '1231',
_type: 'span',
text: '123',
},
{
_key: '1232',
_type: 'image',
__inline: true,
children: [
{
_key: '123-void-child',
_type: 'span',
marks: [],
text: '',
},
],
value: {
asset: {
_ref: 'ref-123',
expect(result).toMatchInlineSnapshot(`
Array [
Object {
"_key": "123",
"_type": "myTestBlockType",
"children": Array [
Object {
"_key": "1231",
"_type": "span",
"text": "123",
},
Object {
"__inline": true,
"_key": "1232",
"_type": "image",
"children": Array [
Object {
"_key": "123-void-child",
"_type": "span",
"marks": Array [],
"text": "",
},
],
"value": Object {
"asset": Object {
"_ref": "ref-123",
},
},
},
},
],
},
])
],
"markDefs": Array [],
"style": "normal",
},
]
`)
})
})

Expand Down Expand Up @@ -225,8 +237,8 @@ describe('fromSlateValue', () => {
style: 'normal',
},
]
const toSlate1 = toSlateValue(value, 'block', keyMap)
const toSlate2 = toSlateValue(value, 'block', keyMap)
const toSlate1 = toSlateValue(value, {portableTextFeatures}, keyMap)
const toSlate2 = toSlateValue(value, {portableTextFeatures}, keyMap)
expect(toSlate1[0]).toBe(toSlate2[0])
expect(toSlate1[1]).toBe(toSlate2[1])
const fromSlate1 = fromSlateValue(toSlate1, 'block', keyMap)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ import {KEY_TO_SLATE_ELEMENT} from './weakMaps'

const debug = debugWithName('operationToPatches')

export function createPatchToOperations(portableTextFeatures: PortableTextFeatures) {
export function createPatchToOperations(
portableTextFeatures: PortableTextFeatures
): (editor: Editor, patch: Patch) => boolean {
function insertPatch(editor: Editor, patch: InsertPatch) {
if (patch.path.length === 1) {
const {items, position} = patch
const blocksToInsert = (toSlateValue(
items as PortableTextBlock[],
portableTextFeatures.types.block.name,
{portableTextFeatures},
KEY_TO_SLATE_ELEMENT.get(editor)
) as unknown) as Node[]
const posKey = findLastKey(patch.path)
Expand Down
17 changes: 14 additions & 3 deletions packages/@sanity/portable-text-editor/src/utils/values.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@ import {isEqual} from 'lodash'
import {Node, Element, Text, Descendant} from 'slate'
import {PathSegment} from '@sanity/types'
import {
MarkDef,
PortableTextBlock,
PortableTextChild,
PortableTextFeatures,
TextBlock,
} from '../types/portableText'

const EMPTY_MARKDEFS: MarkDef[] = []

type Partial<T> = {
[P in keyof T]?: T[P]
}
Expand All @@ -26,17 +29,19 @@ function keepObjectEquality(

export function toSlateValue(
value: PortableTextBlock[] | undefined,
textBlockType: string,
{portableTextFeatures}: {portableTextFeatures: PortableTextFeatures},
keyMap: Record<string, any> = {}
): Descendant[] {
if (value && Array.isArray(value)) {
return value.map((block) => {
const {_type, _key, ...rest} = block
const voidChildren = [{_key: `${_key}-void-child`, _type: 'span', text: '', marks: []}]
const isPortableText = block && block._type === textBlockType
const isPortableText = block && block._type === portableTextFeatures.types.block.name
if (isPortableText) {
const textBlock = block as TextBlock
let hasInlines = false
const hasMissingStyle = typeof textBlock.style === 'undefined'
const hasMissingMarkDefs = typeof textBlock.markDefs === 'undefined'
const children = textBlock.children.map((child) => {
const {_type: cType, _key: cKey, ...cRest} = child
if (cType !== 'span') {
Expand All @@ -55,10 +60,16 @@ export function toSlateValue(
// Original object
return child
})
if (!hasInlines && Element.isElement(block)) {
if (!hasMissingStyle && !hasMissingMarkDefs && !hasInlines && Element.isElement(block)) {
// Original object
return block
}
if (hasMissingStyle) {
rest.style = portableTextFeatures.styles[0].value
}
if (hasMissingMarkDefs) {
rest.markDefs = EMPTY_MARKDEFS
}
return keepObjectEquality({_type, _key, ...rest, children}, keyMap)
}
return keepObjectEquality(
Expand Down

0 comments on commit ad91bf2

Please sign in to comment.