Skip to content

Commit

Permalink
188150453 v3 DI Handle Slate Objects in API Requests (#1421)
Browse files Browse the repository at this point in the history
* Start accepting objects rather than strings for text values.

---------

Co-authored-by: Kirk Swenson <[email protected]>
  • Loading branch information
tealefristoe and kswenson authored Aug 24, 2024
1 parent 8abb1d9 commit cc4987c
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 15 deletions.
8 changes: 7 additions & 1 deletion v3/src/components/text/text-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,14 @@ export const TextModel = TileContentModel
}))
.actions(self => ({
setValue(value: EditorValue) {
self.isSettingValue = true
self.value = editorValueToModelValue(value)
}
}))
.actions(self => ({
setValueFromEditor(value: EditorValue) {
// The component will not update when isSettingValue is true
self.isSettingValue = true
self.setValue(value)
self.isSettingValue = false
}
}))
Expand Down
52 changes: 52 additions & 0 deletions v3/src/components/text/text-registration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,4 +140,56 @@ describe("text component handler", () => {
documentContent.deleteTile(tileId)
expect(documentContent.tileMap.size).toBe(0)
})

it("can create and then update a text tile with slate type text content and retrieve its contents", () => {
expect(documentContent.tileMap.size).toBe(0)
// creating empty text tile
const emptyTextTileResult = diHandler.create!({}, {
type: "text",
text: {
object: "value",
document: {
children: [{
type: "paragraph",
children: [{
text: "To be, or not to be"
}]
}]
}
}
})
expect(emptyTextTileResult.success).toBe(true)
expect(documentContent.tileMap.size).toBe(1)
const [tileId, tile] = Array.from(documentContent.tileMap.entries())[0]
expect(isTextModel(tile.content)).toBe(true)
expect((tile.content as ITextModel).textContent).toBe("To be, or not to be")

testGetComponent(tile, diHandler, (textTile, values) => {
const { text } = values as V2Text
expect(isTextModel(textTile.content)).toBe(true)
const textContent = textTile.content as ITextModel
expect(textContent.value).toBe(text)
})

const newValues: Partial<V2Text> = {
text: {
object: "value",
document: {
children: [{
type: "paragraph",
children: [{
text: "To be, or not to be, that is the question."
}]
}]
}
}
}
testUpdateComponent(tile, diHandler, newValues, (textTile, values) => {
expect(isTextModel(tile.content)).toBe(true)
expect((tile.content as ITextModel).textContent).toBe("To be, or not to be, that is the question.")
})

documentContent.deleteTile(tileId)
expect(documentContent.tileMap.size).toBe(0)
})
})
22 changes: 13 additions & 9 deletions v3/src/components/text/text-registration.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { textToSlate } from "@concord-consortium/slate-editor"
import { SlateExchangeValue, textToSlate } from "@concord-consortium/slate-editor"
import { SetRequired } from "type-fest"
import { V2Text } from "../../data-interactive/data-interactive-component-types"
import { registerComponentHandler } from "../../data-interactive/handlers/component-handler"
Expand Down Expand Up @@ -45,14 +45,17 @@ registerTileComponentInfo({
defaultHeight: 100
})

function importTextToModelValue(text?: string) {
function importTextToModelValue(text?: string | SlateExchangeValue) {
// According to a comment in the v2 code: "Prior to build 0535 this was simple text.
// As of 0535 it is a JSON representation of the rich text content."
// For v3, we make sure we're always dealing with rich-text JSON.
const json = safeJsonParse(text)
return text != null && json != null && typeof json === "object"
? text
: editorValueToModelValue(textToSlate(text ?? ""))
if (typeof text === "string") {
const json = safeJsonParse(text)
return text != null && json != null && typeof json === "object"
? text
: editorValueToModelValue(textToSlate(text))
}
return editorValueToModelValue(text?.document?.children ?? [])
}

registerV2TileImporter("DG.TextView", ({ v2Component, insertTile }) => {
Expand Down Expand Up @@ -91,9 +94,10 @@ registerComponentHandler(kV2TextType, {
update(content, values) {
if (isTextModel(content)) {
const { text } = values as V2Text
if (text) {
const modelValue = importTextToModelValue(text)
content.setValue(modelValueToEditorValue(modelValue))
if (typeof text === "string") {
content.setValue(modelValueToEditorValue(importTextToModelValue(text)))
} else if (text?.document?.children) {
content.setValue(text.document.children)
}
}

Expand Down
8 changes: 4 additions & 4 deletions v3/src/components/text/text-tile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,15 @@ export const TextTile = observer(function TextTile({ tile }: ITileBaseProps) {
const textOnFocus = useRef("")

const [initialValue, setInitialValue] = useState(() => modelValueToEditorValue(textModel?.value))
// changes to this value trigger a remount of the slate editor
const mountKey = useRef(0)
const editor = useMemo(() => {
// slate doesn't have a convenient API for replacing the value in an existing editor,
// so we create a new editor instance when the value changes externally (e.g. undo/redo).
initialValue // eslint-disable-line no-unused-expressions
++mountKey.current
return createEditor()
}, [initialValue])
// changes to this value trigger a remount of the slate editor
const mountKey = useRef(0)

useEffect(() => {
return tile && mstReaction(
Expand All @@ -48,7 +49,6 @@ export const TextTile = observer(function TextTile({ tile }: ITileBaseProps) {
if (!textModel.isSettingValue) {
// set the new value and remount the editor
setInitialValue(modelValueToEditorValue(textModel?.value))
++mountKey.current
}
}
}))
Expand All @@ -67,7 +67,7 @@ export const TextTile = observer(function TextTile({ tile }: ITileBaseProps) {
if (textModel && !textModel.isEquivalent(editor.children)) {
const textDidChange = textOnFocus.current !== textModel.textContent
textModel?.applyModelChange(() => {
textModel.setValue(editor.children)
textModel.setValueFromEditor(editor.children)
}, {
// log only when the text actually changed, e.g. not on style changes
// Note that logging of text changes was commented out in v2 in build 0601. ¯\_(ツ)_/¯
Expand Down
3 changes: 2 additions & 1 deletion v3/src/data-interactive/data-interactive-component-types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { SlateExchangeValue } from "@concord-consortium/slate-editor"
import { kCalculatorTileType, kV2CalculatorType } from "../components/calculator/calculator-defs"
import { kCaseCardTileType, kV2CaseCardType } from "../components/case-card/case-card-defs"
import { kCaseTableTileType, kV2CaseTableType } from "../components/case-table/case-table-defs"
Expand Down Expand Up @@ -113,7 +114,7 @@ export interface V2GetSlider extends V2Slider {
value?: number
}
export interface V2Text extends V2Component {
text?: string
text?: string | SlateExchangeValue
type: "text"
}
export interface V2WebView extends V2Component {
Expand Down

0 comments on commit cc4987c

Please sign in to comment.