@fluidframework/react 2.83.0 → 2.90.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/README.md +2 -0
  3. package/lib/index.d.ts +2 -0
  4. package/lib/index.d.ts.map +1 -1
  5. package/lib/index.js +2 -0
  6. package/lib/index.js.map +1 -1
  7. package/lib/propNode.js.map +1 -1
  8. package/lib/test/mochaHooks.js +13 -0
  9. package/lib/test/mochaHooks.js.map +1 -0
  10. package/lib/test/text/plainUtils.test.js +75 -0
  11. package/lib/test/text/plainUtils.test.js.map +1 -0
  12. package/lib/test/text/textEditor.test.js +704 -0
  13. package/lib/test/text/textEditor.test.js.map +1 -0
  14. package/lib/test/undoRedo.test.js +62 -0
  15. package/lib/test/undoRedo.test.js.map +1 -0
  16. package/lib/test/useObservation.spec.js +0 -1
  17. package/lib/test/useObservation.spec.js.map +1 -1
  18. package/lib/test/useTree.spec.js +0 -1
  19. package/lib/test/useTree.spec.js.map +1 -1
  20. package/lib/text/formatted/index.d.ts +6 -0
  21. package/lib/text/formatted/index.d.ts.map +1 -0
  22. package/lib/text/formatted/index.js +6 -0
  23. package/lib/text/formatted/index.js.map +1 -0
  24. package/lib/text/formatted/quillFormattedView.d.ts +54 -0
  25. package/lib/text/formatted/quillFormattedView.d.ts.map +1 -0
  26. package/lib/text/formatted/quillFormattedView.js +426 -0
  27. package/lib/text/formatted/quillFormattedView.js.map +1 -0
  28. package/lib/text/index.d.ts +7 -0
  29. package/lib/text/index.d.ts.map +1 -0
  30. package/lib/text/index.js +7 -0
  31. package/lib/text/index.js.map +1 -0
  32. package/lib/text/plain/index.d.ts +7 -0
  33. package/lib/text/plain/index.d.ts.map +1 -0
  34. package/lib/text/plain/index.js +7 -0
  35. package/lib/text/plain/index.js.map +1 -0
  36. package/lib/text/plain/plainTextView.d.ts +14 -0
  37. package/lib/text/plain/plainTextView.d.ts.map +1 -0
  38. package/lib/text/plain/plainTextView.js +75 -0
  39. package/lib/text/plain/plainTextView.js.map +1 -0
  40. package/lib/text/plain/plainUtils.d.ts +23 -0
  41. package/lib/text/plain/plainUtils.d.ts.map +1 -0
  42. package/lib/text/plain/plainUtils.js +51 -0
  43. package/lib/text/plain/plainUtils.js.map +1 -0
  44. package/lib/text/plain/quillView.d.ts +22 -0
  45. package/lib/text/plain/quillView.d.ts.map +1 -0
  46. package/lib/text/plain/quillView.js +112 -0
  47. package/lib/text/plain/quillView.js.map +1 -0
  48. package/lib/undoRedo.d.ts +51 -0
  49. package/lib/undoRedo.d.ts.map +1 -0
  50. package/lib/undoRedo.js +76 -0
  51. package/lib/undoRedo.js.map +1 -0
  52. package/package.json +26 -45
  53. package/react.test-files.tar +0 -0
  54. package/src/index.ts +10 -0
  55. package/src/propNode.ts +1 -1
  56. package/src/text/formatted/index.ts +11 -0
  57. package/src/text/formatted/quillFormattedView.tsx +509 -0
  58. package/src/text/index.ts +15 -0
  59. package/src/text/plain/index.ts +7 -0
  60. package/src/text/plain/plainTextView.tsx +110 -0
  61. package/src/text/plain/plainUtils.ts +68 -0
  62. package/src/text/plain/quillView.tsx +149 -0
  63. package/src/undoRedo.ts +117 -0
  64. package/tsconfig.json +6 -0
  65. package/api-extractor/api-extractor-lint-alpha.cjs.json +0 -5
  66. package/api-extractor/api-extractor-lint-beta.cjs.json +0 -5
  67. package/api-extractor/api-extractor-lint-public.cjs.json +0 -5
  68. package/dist/alpha.d.ts +0 -45
  69. package/dist/beta.d.ts +0 -15
  70. package/dist/index.d.ts +0 -16
  71. package/dist/index.d.ts.map +0 -1
  72. package/dist/index.js +0 -26
  73. package/dist/index.js.map +0 -1
  74. package/dist/package.json +0 -4
  75. package/dist/propNode.d.ts +0 -114
  76. package/dist/propNode.d.ts.map +0 -1
  77. package/dist/propNode.js +0 -43
  78. package/dist/propNode.js.map +0 -1
  79. package/dist/public.d.ts +0 -15
  80. package/dist/reactSharedTreeView.d.ts +0 -119
  81. package/dist/reactSharedTreeView.d.ts.map +0 -1
  82. package/dist/reactSharedTreeView.js +0 -206
  83. package/dist/reactSharedTreeView.js.map +0 -1
  84. package/dist/simpleIdentifier.d.ts +0 -19
  85. package/dist/simpleIdentifier.d.ts.map +0 -1
  86. package/dist/simpleIdentifier.js +0 -33
  87. package/dist/simpleIdentifier.js.map +0 -1
  88. package/dist/useObservation.d.ts +0 -83
  89. package/dist/useObservation.d.ts.map +0 -1
  90. package/dist/useObservation.js +0 -295
  91. package/dist/useObservation.js.map +0 -1
  92. package/dist/useTree.d.ts +0 -80
  93. package/dist/useTree.d.ts.map +0 -1
  94. package/dist/useTree.js +0 -137
  95. package/dist/useTree.js.map +0 -1
  96. package/tsconfig.cjs.json +0 -7
@@ -0,0 +1 @@
1
+ {"version":3,"file":"textEditor.test.js","sourceRoot":"","sources":["../../../src/test/text/textEditor.test.tsx"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { strict as assert } from \"node:assert\";\n\nimport { TreeViewConfiguration, type TreeView } from \"@fluidframework/tree\";\nimport { TreeAlpha } from \"@fluidframework/tree/alpha\";\nimport { independentView, TextAsTree } from \"@fluidframework/tree/internal\";\nimport { render } from \"@testing-library/react\";\nimport globalJsdom from \"global-jsdom\";\nimport DeltaPackage from \"quill-delta\";\nimport * as React from \"react\";\n\nimport { toPropTreeNode } from \"../../propNode.js\";\nimport {\n\tclipboardFormatMatcher,\n\tFormattedTextAsTree,\n\tFormattedMainView,\n\ttype FormattedEditorHandle,\n\tparseCssFontFamily,\n\tparseCssFontSize,\n\t// Allow import of files being tested\n\t// eslint-disable-next-line import-x/no-internal-modules\n} from \"../../text/formatted/quillFormattedView.js\";\nimport {\n\tPlainTextMainView,\n\tQuillMainView,\n\ttype MainViewProps,\n\t// Allow import of files being tested\n\t// eslint-disable-next-line import-x/no-internal-modules\n} from \"../../text/plain/index.js\";\nimport { UndoRedoStacks } from \"../../undoRedo.js\";\n\n// Workaround for quill-delta's export style not working well with node16 module resolution.\ntype Delta = DeltaPackage.default;\nconst Delta = DeltaPackage.default;\n\n// Configuration for creating formatted text views\nconst formattedTreeConfig = new TreeViewConfiguration({ schema: FormattedTextAsTree.Tree });\n\n/**\n * Creates a TreeView for formatted text, initialized with the provided initial value.\n */\nfunction createFormattedTreeView(initialValue = \"\"): {\n\ttree: FormattedTextAsTree.Tree;\n} {\n\tconst treeView = independentView(formattedTreeConfig);\n\ttreeView.initialize(FormattedTextAsTree.Tree.fromString(initialValue));\n\treturn { tree: treeView.root };\n}\n\n/**\n * Creates a TreeView for formatted text with events access (needed for undo/redo tests).\n */\nfunction createFormattedTreeViewWithEvents(\n\tinitialValue = \"\",\n): TreeView<typeof FormattedTextAsTree.Tree> {\n\tconst treeView = independentView(formattedTreeConfig);\n\ttreeView.initialize(FormattedTextAsTree.Tree.fromString(initialValue));\n\treturn treeView;\n}\n\nconst views: { name: string; component: React.FC<MainViewProps> }[] = [\n\t{ name: \"Quill\", component: QuillMainView },\n\t{ name: \"Plain TextArea\", component: PlainTextMainView },\n];\n\n// TODO add collaboration tests when rich formatting is supported using TestContainerRuntimeFactory from\n// @fluidframework/test-utils to test rich formatting data sync between multiple collaborators\ndescribe(\"textEditor\", () => {\n\t// Note: JSDOM is initialized once in mochaHooks.ts before Quill is imported,\n\t// since Quill requires document at import time. See src/test/mochaHooks.ts.\n\t// These tests reset up a clean DOM.\n\n\tlet cleanup: () => void;\n\n\t// TODO: why does making this beforeEach/afterEach instead of before/after cause cleanup to crash?\n\t// It seems like each test should be able to have its own clean DOM.\n\tbefore(() => {\n\t\tcleanup = globalJsdom();\n\t});\n\n\tafter(() => {\n\t\tcleanup();\n\t});\n\n\t// Loop through all registered views\n\tfor (const view of views) {\n\t\tdescribe(`${view.name} view`, () => {\n\t\t\tdescribe(\"dom tests\", () => {\n\t\t\t\t// Run without strict mode to make sure it works in a normal production setup.\n\t\t\t\t// Run with strict mode to potentially detect additional issues.\n\t\t\t\tfor (const reactStrictMode of [false, true]) {\n\t\t\t\t\tdescribe(`StrictMode: ${reactStrictMode}`, () => {\n\t\t\t\t\t\tconst ViewComponent = view.component;\n\n\t\t\t\t\t\tit(\"renders MainView with editor container\", () => {\n\t\t\t\t\t\t\tconst text = TextAsTree.Tree.fromString(\"\");\n\t\t\t\t\t\t\tconst content = <ViewComponent root={toPropTreeNode(text)} />;\n\t\t\t\t\t\t\tconst rendered = render(content, { reactStrictMode });\n\n\t\t\t\t\t\t\tassert.match(\n\t\t\t\t\t\t\t\trendered.baseElement.textContent ?? \"\",\n\t\t\t\t\t\t\t\t/Collaborative Text Editor/,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tit(\"renders MainView with initial text content\", () => {\n\t\t\t\t\t\t\tconst text = TextAsTree.Tree.fromString(\"Hello World\");\n\t\t\t\t\t\t\tconst content = <ViewComponent root={toPropTreeNode(text)} />;\n\t\t\t\t\t\t\tconst rendered = render(content, { reactStrictMode });\n\n\t\t\t\t\t\t\tassert.match(rendered.baseElement.textContent ?? \"\", /Hello World/);\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tit(\"invalidates view when tree is mutated\", () => {\n\t\t\t\t\t\t\tconst text = TextAsTree.Tree.fromString(\"Hello\");\n\t\t\t\t\t\t\tconst content = <ViewComponent root={toPropTreeNode(text)} />;\n\t\t\t\t\t\t\tconst rendered = render(content, { reactStrictMode });\n\n\t\t\t\t\t\t\t// Mutate the tree by inserting text\n\t\t\t\t\t\t\ttext.insertAt(5, \" World\");\n\n\t\t\t\t\t\t\t// Rerender and verify the view updates\n\t\t\t\t\t\t\trendered.rerender(content);\n\t\t\t\t\t\t\tassert.match(rendered.baseElement.textContent ?? \"\", /Hello World/);\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tit(\"invalidates view when text is removed\", () => {\n\t\t\t\t\t\t\tconst text = TextAsTree.Tree.fromString(\"Hello World\");\n\t\t\t\t\t\t\tconst content = <ViewComponent root={toPropTreeNode(text)} />;\n\t\t\t\t\t\t\tconst rendered = render(content, { reactStrictMode });\n\n\t\t\t\t\t\t\t// Mutate the tree by removing \" World\" (indices 5 to 11)\n\t\t\t\t\t\t\ttext.removeRange(5, 11);\n\n\t\t\t\t\t\t\t// Rerender and verify the view updates\n\t\t\t\t\t\t\trendered.rerender(content);\n\t\t\t\t\t\t\tassert.match(rendered.baseElement.textContent ?? \"\", /Hello/);\n\t\t\t\t\t\t\tassert(rendered.baseElement.textContent !== null);\n\t\t\t\t\t\t\tassert.doesNotMatch(rendered.baseElement.textContent, /World/);\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tit(\"invalidates view when text is cleared and replaced\", () => {\n\t\t\t\t\t\t\tconst text = TextAsTree.Tree.fromString(\"Original\");\n\t\t\t\t\t\t\tconst content = <ViewComponent root={toPropTreeNode(text)} />;\n\t\t\t\t\t\t\tconst rendered = render(content, { reactStrictMode });\n\n\t\t\t\t\t\t\t// Clear all text\n\t\t\t\t\t\t\tconst length = [...text.characters()].length;\n\t\t\t\t\t\t\ttext.removeRange(0, length);\n\n\t\t\t\t\t\t\t// Insert new text\n\t\t\t\t\t\t\ttext.insertAt(0, \"Replaced\");\n\n\t\t\t\t\t\t\t// Rerender and verify the view updates\n\t\t\t\t\t\t\trendered.rerender(content);\n\t\t\t\t\t\t\tassert.match(rendered.baseElement.textContent ?? \"\", /Replaced/);\n\t\t\t\t\t\t\tassert(rendered.baseElement.textContent !== null);\n\t\t\t\t\t\t\tassert.doesNotMatch(rendered.baseElement.textContent, /Original/);\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\t// Tests for surrogate pair characters (emojis use 2 UTF-16 code units)\n\t\t\t\t\t\t// These verify correct handling where editor indexing may differ from iteration.\n\n\t\t\t\t\t\tit(\"renders MainView with surrogate pair characters\", () => {\n\t\t\t\t\t\t\t// 😀 is a surrogate pair: \"😀\".length === 2, but [...\"😀\"].length === 1\n\t\t\t\t\t\t\tconst text = TextAsTree.Tree.fromString(\"Hello 😀 World\");\n\t\t\t\t\t\t\tconst content = <ViewComponent root={toPropTreeNode(text)} />;\n\t\t\t\t\t\t\tconst rendered = render(content, { reactStrictMode });\n\n\t\t\t\t\t\t\tassert.match(rendered.baseElement.textContent ?? \"\", /Hello 😀 World/);\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tit(\"inserts text after surrogate pair characters\", () => {\n\t\t\t\t\t\t\tconst text = TextAsTree.Tree.fromString(\"A😀B\");\n\t\t\t\t\t\t\tconst content = <ViewComponent root={toPropTreeNode(text)} />;\n\t\t\t\t\t\t\tconst rendered = render(content, { reactStrictMode });\n\n\t\t\t\t\t\t\t// Insert after the emoji (index 2 in character count: A, 😀, B)\n\t\t\t\t\t\t\ttext.insertAt(2, \"X\");\n\n\t\t\t\t\t\t\trendered.rerender(content);\n\t\t\t\t\t\t\tassert.match(rendered.baseElement.textContent ?? \"\", /A😀XB/);\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tit(\"removes surrogate pair characters\", () => {\n\t\t\t\t\t\t\tconst text = TextAsTree.Tree.fromString(\"A😀B\");\n\t\t\t\t\t\t\tconst content = <ViewComponent root={toPropTreeNode(text)} />;\n\t\t\t\t\t\t\tconst rendered = render(content, { reactStrictMode });\n\n\t\t\t\t\t\t\t// Remove the emoji (index 1, length 1 in character count)\n\t\t\t\t\t\t\ttext.removeRange(1, 2);\n\n\t\t\t\t\t\t\trendered.rerender(content);\n\t\t\t\t\t\t\tassert.match(rendered.baseElement.textContent ?? \"\", /AB/);\n\t\t\t\t\t\t\tassert(rendered.baseElement.textContent !== null);\n\t\t\t\t\t\t\tassert.doesNotMatch(rendered.baseElement.textContent, /😀/);\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tit(\"handles multiple surrogate pair characters\", () => {\n\t\t\t\t\t\t\tconst text = TextAsTree.Tree.fromString(\"👋🌍🎉\");\n\t\t\t\t\t\t\tconst content = <ViewComponent root={toPropTreeNode(text)} />;\n\t\t\t\t\t\t\tconst rendered = render(content, { reactStrictMode });\n\n\t\t\t\t\t\t\t// Insert between emojis\n\t\t\t\t\t\t\ttext.insertAt(2, \"!\");\n\n\t\t\t\t\t\t\trendered.rerender(content);\n\t\t\t\t\t\t\tassert.match(rendered.baseElement.textContent ?? \"\", /👋🌍!🎉/);\n\t\t\t\t\t\t});\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\t}\n\n\t// Formatted text view tests - Initial view rendering (matching plain text test structure)\n\tdescribe(\"Formatted Quill view\", () => {\n\t\tdescribe(\"dom tests\", () => {\n\t\t\tfor (const reactStrictMode of [false, true]) {\n\t\t\t\tdescribe(`StrictMode: ${reactStrictMode}`, () => {\n\t\t\t\t\tit(\"renders FormattedMainView with editor container\", () => {\n\t\t\t\t\t\tconst { tree } = createFormattedTreeView();\n\t\t\t\t\t\tconst content = <FormattedMainView root={toPropTreeNode(tree)} />;\n\t\t\t\t\t\tconst rendered = render(content, { reactStrictMode });\n\n\t\t\t\t\t\tassert.match(\n\t\t\t\t\t\t\trendered.baseElement.textContent ?? \"\",\n\t\t\t\t\t\t\t/Collaborative Formatted Text Editor/,\n\t\t\t\t\t\t);\n\t\t\t\t\t});\n\n\t\t\t\t\tit(\"renders FormattedMainView with initial text content\", () => {\n\t\t\t\t\t\tconst { tree } = createFormattedTreeView(\"Hello World\");\n\t\t\t\t\t\tconst content = <FormattedMainView root={toPropTreeNode(tree)} />;\n\t\t\t\t\t\tconst rendered = render(content, { reactStrictMode });\n\n\t\t\t\t\t\tassert.match(rendered.baseElement.textContent ?? \"\", /Hello World/);\n\t\t\t\t\t});\n\n\t\t\t\t\tit(\"invalidates view when tree is mutated\", () => {\n\t\t\t\t\t\tconst { tree: text } = createFormattedTreeView(\"Hello\");\n\t\t\t\t\t\tconst content = <FormattedMainView root={toPropTreeNode(text)} />;\n\t\t\t\t\t\tconst rendered = render(content, { reactStrictMode });\n\n\t\t\t\t\t\t// Mutate the tree by inserting text\n\t\t\t\t\t\ttext.insertAt(5, \" World\");\n\n\t\t\t\t\t\t// Rerender and verify the view updates\n\t\t\t\t\t\trendered.rerender(content);\n\t\t\t\t\t\tassert.match(rendered.baseElement.textContent ?? \"\", /Hello World/);\n\t\t\t\t\t});\n\n\t\t\t\t\tit(\"invalidates view when text is removed\", () => {\n\t\t\t\t\t\tconst { tree: text } = createFormattedTreeView(\"Hello World\");\n\t\t\t\t\t\tconst content = <FormattedMainView root={toPropTreeNode(text)} />;\n\t\t\t\t\t\tconst rendered = render(content, { reactStrictMode });\n\n\t\t\t\t\t\t// Mutate the tree by removing \" World\" (indices 5 to 11)\n\t\t\t\t\t\ttext.removeRange(5, 11);\n\n\t\t\t\t\t\t// Rerender and verify the view updates\n\t\t\t\t\t\trendered.rerender(content);\n\t\t\t\t\t\tassert.match(rendered.baseElement.textContent ?? \"\", /Hello/);\n\t\t\t\t\t\tassert(rendered.baseElement.textContent !== null);\n\t\t\t\t\t\tassert.doesNotMatch(rendered.baseElement.textContent, /World/);\n\t\t\t\t\t});\n\n\t\t\t\t\tit(\"invalidates view when text is cleared and replaced\", () => {\n\t\t\t\t\t\tconst { tree: text } = createFormattedTreeView(\"Original\");\n\t\t\t\t\t\tconst content = <FormattedMainView root={toPropTreeNode(text)} />;\n\t\t\t\t\t\tconst rendered = render(content, { reactStrictMode });\n\n\t\t\t\t\t\t// Clear all text\n\t\t\t\t\t\tconst length = [...text.characters()].length;\n\t\t\t\t\t\ttext.removeRange(0, length);\n\n\t\t\t\t\t\t// Insert new text\n\t\t\t\t\t\ttext.insertAt(0, \"Replaced\");\n\n\t\t\t\t\t\t// Rerender and verify the view updates\n\t\t\t\t\t\trendered.rerender(content);\n\t\t\t\t\t\tassert.match(rendered.baseElement.textContent ?? \"\", /Replaced/);\n\t\t\t\t\t\tassert(rendered.baseElement.textContent !== null);\n\t\t\t\t\t\tassert.doesNotMatch(rendered.baseElement.textContent, /Original/);\n\t\t\t\t\t});\n\n\t\t\t\t\t// Tests for surrogate pair characters (emojis use 2 UTF-16 code units)\n\t\t\t\t\t// These verify correct handling where editor indexing may differ from iteration.\n\n\t\t\t\t\tit(\"renders FormattedMainView with surrogate pair characters\", () => {\n\t\t\t\t\t\t// 😀 is a surrogate pair: \"😀\".length === 2, but [...\"😀\"].length === 1\n\t\t\t\t\t\tconst { tree: text } = createFormattedTreeView(\"Hello 😀 World\");\n\t\t\t\t\t\tconst content = <FormattedMainView root={toPropTreeNode(text)} />;\n\t\t\t\t\t\tconst rendered = render(content, { reactStrictMode });\n\n\t\t\t\t\t\tassert.match(rendered.baseElement.textContent ?? \"\", /Hello 😀 World/);\n\t\t\t\t\t});\n\n\t\t\t\t\tit(\"inserts text after surrogate pair characters\", () => {\n\t\t\t\t\t\tconst { tree: text } = createFormattedTreeView(\"A😀B\");\n\t\t\t\t\t\tconst content = <FormattedMainView root={toPropTreeNode(text)} />;\n\t\t\t\t\t\tconst rendered = render(content, { reactStrictMode });\n\n\t\t\t\t\t\t// Insert after the emoji (index 2 in character count: A, 😀, B)\n\t\t\t\t\t\ttext.insertAt(2, \"X\");\n\n\t\t\t\t\t\trendered.rerender(content);\n\t\t\t\t\t\tassert.match(rendered.baseElement.textContent ?? \"\", /A😀XB/);\n\t\t\t\t\t});\n\n\t\t\t\t\tit(\"removes surrogate pair characters\", () => {\n\t\t\t\t\t\tconst { tree: text } = createFormattedTreeView(\"A😀B\");\n\t\t\t\t\t\tconst content = <FormattedMainView root={toPropTreeNode(text)} />;\n\t\t\t\t\t\tconst rendered = render(content, { reactStrictMode });\n\n\t\t\t\t\t\t// Remove the emoji (index 1, length 1 in character count)\n\t\t\t\t\t\ttext.removeRange(1, 2);\n\n\t\t\t\t\t\trendered.rerender(content);\n\t\t\t\t\t\tassert.match(rendered.baseElement.textContent ?? \"\", /AB/);\n\t\t\t\t\t\tassert(rendered.baseElement.textContent !== null);\n\t\t\t\t\t\tassert.doesNotMatch(rendered.baseElement.textContent, /😀/);\n\t\t\t\t\t});\n\n\t\t\t\t\tit(\"handles multiple surrogate pair characters\", () => {\n\t\t\t\t\t\tconst { tree: text } = createFormattedTreeView(\"👋🌍🎉\");\n\t\t\t\t\t\tconst content = <FormattedMainView root={toPropTreeNode(text)} />;\n\t\t\t\t\t\tconst rendered = render(content, { reactStrictMode });\n\n\t\t\t\t\t\t// Insert between emojis\n\t\t\t\t\t\ttext.insertAt(2, \"!\");\n\n\t\t\t\t\t\trendered.rerender(content);\n\t\t\t\t\t\tassert.match(rendered.baseElement.textContent ?? \"\", /👋🌍!🎉/);\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t}\n\t\t});\n\n\t\t// Helper to create default format\n\t\tfunction createPlainFormat(): FormattedTextAsTree.CharacterFormat {\n\t\t\treturn new FormattedTextAsTree.CharacterFormat({\n\t\t\t\tbold: false,\n\t\t\t\titalic: false,\n\t\t\t\tunderline: false,\n\t\t\t\tsize: 12,\n\t\t\t\tfont: \"Arial\",\n\t\t\t});\n\t\t}\n\n\t\t// Essential tests for character attributes\n\t\t// Each attribute needs: insert, delete, and formatRange tests\n\t\tdescribe(\"character attribute tests\", () => {\n\t\t\tfor (const reactStrictMode of [false, true]) {\n\t\t\t\tdescribe(`StrictMode: ${reactStrictMode}`, () => {\n\t\t\t\t\tit(\"delete on empty string does not throw\", () => {\n\t\t\t\t\t\tconst { tree: text } = createFormattedTreeView();\n\t\t\t\t\t\tconst content = <FormattedMainView root={toPropTreeNode(text)} />;\n\t\t\t\t\t\tconst rendered = render(content, { reactStrictMode });\n\n\t\t\t\t\t\tassert.doesNotThrow(() => {\n\t\t\t\t\t\t\ttext.removeRange(0, 0);\n\t\t\t\t\t\t\trendered.rerender(content);\n\t\t\t\t\t\t});\n\t\t\t\t\t});\n\n\t\t\t\t\tdescribe(\"bold\", () => {\n\t\t\t\t\t\tit(\"inserts bold text and renders with <strong> tag\", () => {\n\t\t\t\t\t\t\tconst { tree: text } = createFormattedTreeView(\"Hello\");\n\t\t\t\t\t\t\tconst content = <FormattedMainView root={toPropTreeNode(text)} />;\n\t\t\t\t\t\t\tconst rendered = render(content, { reactStrictMode });\n\n\t\t\t\t\t\t\tassert.ok(!rendered.container.querySelector(\"strong\"), \"Initially: no <strong>\");\n\n\t\t\t\t\t\t\ttext.defaultFormat = new FormattedTextAsTree.CharacterFormat({\n\t\t\t\t\t\t\t\tbold: true,\n\t\t\t\t\t\t\t\titalic: false,\n\t\t\t\t\t\t\t\tunderline: false,\n\t\t\t\t\t\t\t\tsize: 12,\n\t\t\t\t\t\t\t\tfont: \"Arial\",\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\ttext.insertAt(2, \"BOLD\");\n\n\t\t\t\t\t\t\trendered.rerender(content);\n\t\t\t\t\t\t\tconst el = rendered.container.querySelector(\"strong\");\n\t\t\t\t\t\t\tassert.ok(el, \"Expected <strong> tag\");\n\t\t\t\t\t\t\tassert.match(el.textContent ?? \"\", /BOLD/);\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tit(\"deletes bold text and removes <strong> tag\", () => {\n\t\t\t\t\t\t\tconst { tree: text } = createFormattedTreeView();\n\t\t\t\t\t\t\ttext.defaultFormat = new FormattedTextAsTree.CharacterFormat({\n\t\t\t\t\t\t\t\tbold: true,\n\t\t\t\t\t\t\t\titalic: false,\n\t\t\t\t\t\t\t\tunderline: false,\n\t\t\t\t\t\t\t\tsize: 12,\n\t\t\t\t\t\t\t\tfont: \"Arial\",\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\ttext.insertAt(0, \"BOLD\");\n\t\t\t\t\t\t\ttext.defaultFormat = createPlainFormat();\n\t\t\t\t\t\t\ttext.insertAt(4, \"plain\");\n\n\t\t\t\t\t\t\tconst content = <FormattedMainView root={toPropTreeNode(text)} />;\n\t\t\t\t\t\t\tconst rendered = render(content, { reactStrictMode });\n\n\t\t\t\t\t\t\tassert.ok(rendered.container.querySelector(\"strong\"), \"Initially: has <strong>\");\n\n\t\t\t\t\t\t\ttext.removeRange(0, 4);\n\t\t\t\t\t\t\trendered.rerender(content);\n\n\t\t\t\t\t\t\tassert.ok(\n\t\t\t\t\t\t\t\t!rendered.container.querySelector(\"strong\"),\n\t\t\t\t\t\t\t\t\"After delete: no <strong>\",\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tit(\"applies bold via formatRange\", () => {\n\t\t\t\t\t\t\tconst { tree: text } = createFormattedTreeView(\"Hello World\");\n\t\t\t\t\t\t\tconst content = <FormattedMainView root={toPropTreeNode(text)} />;\n\t\t\t\t\t\t\tconst rendered = render(content, { reactStrictMode });\n\n\t\t\t\t\t\t\ttext.formatRange(6, 11, { bold: true });\n\t\t\t\t\t\t\trendered.rerender(content);\n\n\t\t\t\t\t\t\tconst el = rendered.container.querySelector(\"strong\");\n\t\t\t\t\t\t\tassert.ok(el, \"Expected <strong> after formatRange\");\n\t\t\t\t\t\t\tassert.match(el.textContent ?? \"\", /World/);\n\t\t\t\t\t\t});\n\t\t\t\t\t});\n\n\t\t\t\t\tdescribe(\"italic\", () => {\n\t\t\t\t\t\tit(\"inserts italic text and renders with <em> tag\", () => {\n\t\t\t\t\t\t\tconst { tree: text } = createFormattedTreeView(\"Hello\");\n\t\t\t\t\t\t\tconst content = <FormattedMainView root={toPropTreeNode(text)} />;\n\t\t\t\t\t\t\tconst rendered = render(content, { reactStrictMode });\n\n\t\t\t\t\t\t\tassert.ok(!rendered.container.querySelector(\"em\"), \"Initially: no <em>\");\n\n\t\t\t\t\t\t\ttext.defaultFormat = new FormattedTextAsTree.CharacterFormat({\n\t\t\t\t\t\t\t\tbold: false,\n\t\t\t\t\t\t\t\titalic: true,\n\t\t\t\t\t\t\t\tunderline: false,\n\t\t\t\t\t\t\t\tsize: 12,\n\t\t\t\t\t\t\t\tfont: \"Arial\",\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\ttext.insertAt(2, \"ITAL\");\n\n\t\t\t\t\t\t\trendered.rerender(content);\n\t\t\t\t\t\t\tconst el = rendered.container.querySelector(\"em\");\n\t\t\t\t\t\t\tassert.ok(el, \"Expected <em> tag\");\n\t\t\t\t\t\t\tassert.match(el.textContent ?? \"\", /ITAL/);\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tit(\"deletes italic text and removes <em> tag\", () => {\n\t\t\t\t\t\t\tconst { tree: text } = createFormattedTreeView();\n\t\t\t\t\t\t\ttext.defaultFormat = new FormattedTextAsTree.CharacterFormat({\n\t\t\t\t\t\t\t\tbold: false,\n\t\t\t\t\t\t\t\titalic: true,\n\t\t\t\t\t\t\t\tunderline: false,\n\t\t\t\t\t\t\t\tsize: 12,\n\t\t\t\t\t\t\t\tfont: \"Arial\",\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\ttext.insertAt(0, \"ITAL\");\n\t\t\t\t\t\t\ttext.defaultFormat = createPlainFormat();\n\t\t\t\t\t\t\ttext.insertAt(4, \"plain\");\n\n\t\t\t\t\t\t\tconst content = <FormattedMainView root={toPropTreeNode(text)} />;\n\t\t\t\t\t\t\tconst rendered = render(content, { reactStrictMode });\n\n\t\t\t\t\t\t\tassert.ok(rendered.container.querySelector(\"em\"), \"Initially: has <em>\");\n\n\t\t\t\t\t\t\ttext.removeRange(0, 4);\n\t\t\t\t\t\t\trendered.rerender(content);\n\n\t\t\t\t\t\t\tassert.ok(!rendered.container.querySelector(\"em\"), \"After delete: no <em>\");\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tit(\"applies italic via formatRange\", () => {\n\t\t\t\t\t\t\tconst { tree: text } = createFormattedTreeView(\"Hello World\");\n\t\t\t\t\t\t\tconst content = <FormattedMainView root={toPropTreeNode(text)} />;\n\t\t\t\t\t\t\tconst rendered = render(content, { reactStrictMode });\n\n\t\t\t\t\t\t\ttext.formatRange(6, 11, { italic: true });\n\t\t\t\t\t\t\trendered.rerender(content);\n\n\t\t\t\t\t\t\tconst el = rendered.container.querySelector(\"em\");\n\t\t\t\t\t\t\tassert.ok(el, \"Expected <em> after formatRange\");\n\t\t\t\t\t\t\tassert.match(el.textContent ?? \"\", /World/);\n\t\t\t\t\t\t});\n\t\t\t\t\t});\n\n\t\t\t\t\tdescribe(\"underline\", () => {\n\t\t\t\t\t\tit(\"inserts underlined text and renders with <u> tag\", () => {\n\t\t\t\t\t\t\tconst { tree: text } = createFormattedTreeView(\"Hello\");\n\t\t\t\t\t\t\tconst content = <FormattedMainView root={toPropTreeNode(text)} />;\n\t\t\t\t\t\t\tconst rendered = render(content, { reactStrictMode });\n\n\t\t\t\t\t\t\tassert.ok(!rendered.container.querySelector(\"u\"), \"Initially: no <u>\");\n\n\t\t\t\t\t\t\ttext.defaultFormat = new FormattedTextAsTree.CharacterFormat({\n\t\t\t\t\t\t\t\tbold: false,\n\t\t\t\t\t\t\t\titalic: false,\n\t\t\t\t\t\t\t\tunderline: true,\n\t\t\t\t\t\t\t\tsize: 12,\n\t\t\t\t\t\t\t\tfont: \"Arial\",\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\ttext.insertAt(2, \"UNDER\");\n\n\t\t\t\t\t\t\trendered.rerender(content);\n\t\t\t\t\t\t\tconst el = rendered.container.querySelector(\"u\");\n\t\t\t\t\t\t\tassert.ok(el, \"Expected <u> tag\");\n\t\t\t\t\t\t\tassert.match(el.textContent ?? \"\", /UNDER/);\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tit(\"deletes underlined text and removes <u> tag\", () => {\n\t\t\t\t\t\t\tconst { tree: text } = createFormattedTreeView();\n\t\t\t\t\t\t\ttext.defaultFormat = new FormattedTextAsTree.CharacterFormat({\n\t\t\t\t\t\t\t\tbold: false,\n\t\t\t\t\t\t\t\titalic: false,\n\t\t\t\t\t\t\t\tunderline: true,\n\t\t\t\t\t\t\t\tsize: 12,\n\t\t\t\t\t\t\t\tfont: \"Arial\",\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\ttext.insertAt(0, \"UNDER\");\n\t\t\t\t\t\t\ttext.defaultFormat = createPlainFormat();\n\t\t\t\t\t\t\ttext.insertAt(5, \"plain\");\n\n\t\t\t\t\t\t\tconst content = <FormattedMainView root={toPropTreeNode(text)} />;\n\t\t\t\t\t\t\tconst rendered = render(content, { reactStrictMode });\n\n\t\t\t\t\t\t\tassert.ok(rendered.container.querySelector(\"u\"), \"Initially: has <u>\");\n\n\t\t\t\t\t\t\ttext.removeRange(0, 5);\n\t\t\t\t\t\t\trendered.rerender(content);\n\n\t\t\t\t\t\t\tassert.ok(!rendered.container.querySelector(\"u\"), \"After delete: no <u>\");\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tit(\"applies underline via formatRange\", () => {\n\t\t\t\t\t\t\tconst { tree: text } = createFormattedTreeView(\"Hello World\");\n\t\t\t\t\t\t\tconst content = <FormattedMainView root={toPropTreeNode(text)} />;\n\t\t\t\t\t\t\tconst rendered = render(content, { reactStrictMode });\n\n\t\t\t\t\t\t\ttext.formatRange(6, 11, { underline: true });\n\t\t\t\t\t\t\trendered.rerender(content);\n\n\t\t\t\t\t\t\tconst el = rendered.container.querySelector(\"u\");\n\t\t\t\t\t\t\tassert.ok(el, \"Expected <u> after formatRange\");\n\t\t\t\t\t\t\tassert.match(el.textContent ?? \"\", /World/);\n\t\t\t\t\t\t});\n\t\t\t\t\t});\n\n\t\t\t\t\tdescribe(\"size\", () => {\n\t\t\t\t\t\tit(\"inserts huge size text and renders with .ql-size-huge\", () => {\n\t\t\t\t\t\t\tconst { tree: text } = createFormattedTreeView(\"Hello\");\n\t\t\t\t\t\t\tconst content = <FormattedMainView root={toPropTreeNode(text)} />;\n\t\t\t\t\t\t\tconst rendered = render(content, { reactStrictMode });\n\n\t\t\t\t\t\t\tassert.ok(\n\t\t\t\t\t\t\t\t!rendered.container.querySelector(\".ql-size-huge\"),\n\t\t\t\t\t\t\t\t\"Initially: no .ql-size-huge\",\n\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\ttext.defaultFormat = new FormattedTextAsTree.CharacterFormat({\n\t\t\t\t\t\t\t\tbold: false,\n\t\t\t\t\t\t\t\titalic: false,\n\t\t\t\t\t\t\t\tunderline: false,\n\t\t\t\t\t\t\t\tsize: 24,\n\t\t\t\t\t\t\t\tfont: \"Arial\",\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\ttext.insertAt(2, \"HUGE\");\n\n\t\t\t\t\t\t\trendered.rerender(content);\n\t\t\t\t\t\t\tconst el = rendered.container.querySelector(\".ql-size-huge\");\n\t\t\t\t\t\t\tassert.ok(el, \"Expected .ql-size-huge\");\n\t\t\t\t\t\t\tassert.match(el.textContent ?? \"\", /HUGE/);\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tit(\"deletes huge size text and removes .ql-size-huge\", () => {\n\t\t\t\t\t\t\tconst { tree: text } = createFormattedTreeView();\n\t\t\t\t\t\t\ttext.defaultFormat = new FormattedTextAsTree.CharacterFormat({\n\t\t\t\t\t\t\t\tbold: false,\n\t\t\t\t\t\t\t\titalic: false,\n\t\t\t\t\t\t\t\tunderline: false,\n\t\t\t\t\t\t\t\tsize: 24,\n\t\t\t\t\t\t\t\tfont: \"Arial\",\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\ttext.insertAt(0, \"HUGE\");\n\t\t\t\t\t\t\ttext.defaultFormat = createPlainFormat();\n\t\t\t\t\t\t\ttext.insertAt(4, \"plain\");\n\n\t\t\t\t\t\t\tconst content = <FormattedMainView root={toPropTreeNode(text)} />;\n\t\t\t\t\t\t\tconst rendered = render(content, { reactStrictMode });\n\n\t\t\t\t\t\t\tassert.ok(\n\t\t\t\t\t\t\t\trendered.container.querySelector(\".ql-size-huge\"),\n\t\t\t\t\t\t\t\t\"Initially: has .ql-size-huge\",\n\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\ttext.removeRange(0, 4);\n\t\t\t\t\t\t\trendered.rerender(content);\n\n\t\t\t\t\t\t\tassert.ok(\n\t\t\t\t\t\t\t\t!rendered.container.querySelector(\".ql-size-huge\"),\n\t\t\t\t\t\t\t\t\"After delete: no .ql-size-huge\",\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tit(\"applies size via formatRange\", () => {\n\t\t\t\t\t\t\tconst { tree: text } = createFormattedTreeView(\"Hello World\");\n\t\t\t\t\t\t\tconst content = <FormattedMainView root={toPropTreeNode(text)} />;\n\t\t\t\t\t\t\tconst rendered = render(content, { reactStrictMode });\n\n\t\t\t\t\t\t\ttext.formatRange(6, 11, { size: 24 });\n\t\t\t\t\t\t\trendered.rerender(content);\n\n\t\t\t\t\t\t\tconst el = rendered.container.querySelector(\".ql-size-huge\");\n\t\t\t\t\t\t\tassert.ok(el, \"Expected .ql-size-huge after formatRange\");\n\t\t\t\t\t\t\tassert.match(el.textContent ?? \"\", /World/);\n\t\t\t\t\t\t});\n\t\t\t\t\t});\n\n\t\t\t\t\tdescribe(\"font\", () => {\n\t\t\t\t\t\tit(\"inserts monospace font text and renders with .ql-font-monospace\", () => {\n\t\t\t\t\t\t\tconst { tree: text } = createFormattedTreeView(\"Hello\");\n\t\t\t\t\t\t\tconst content = <FormattedMainView root={toPropTreeNode(text)} />;\n\t\t\t\t\t\t\tconst rendered = render(content, { reactStrictMode });\n\n\t\t\t\t\t\t\tassert.ok(\n\t\t\t\t\t\t\t\t!rendered.container.querySelector(\".ql-font-monospace\"),\n\t\t\t\t\t\t\t\t\"Initially: no .ql-font-monospace\",\n\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\ttext.defaultFormat = new FormattedTextAsTree.CharacterFormat({\n\t\t\t\t\t\t\t\tbold: false,\n\t\t\t\t\t\t\t\titalic: false,\n\t\t\t\t\t\t\t\tunderline: false,\n\t\t\t\t\t\t\t\tsize: 12,\n\t\t\t\t\t\t\t\tfont: \"monospace\",\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\ttext.insertAt(2, \"MONO\");\n\n\t\t\t\t\t\t\trendered.rerender(content);\n\t\t\t\t\t\t\tconst el = rendered.container.querySelector(\".ql-font-monospace\");\n\t\t\t\t\t\t\tassert.ok(el, \"Expected .ql-font-monospace\");\n\t\t\t\t\t\t\tassert.match(el.textContent ?? \"\", /MONO/);\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tit(\"deletes monospace font text and removes .ql-font-monospace\", () => {\n\t\t\t\t\t\t\tconst { tree: text } = createFormattedTreeView();\n\t\t\t\t\t\t\ttext.defaultFormat = new FormattedTextAsTree.CharacterFormat({\n\t\t\t\t\t\t\t\tbold: false,\n\t\t\t\t\t\t\t\titalic: false,\n\t\t\t\t\t\t\t\tunderline: false,\n\t\t\t\t\t\t\t\tsize: 12,\n\t\t\t\t\t\t\t\tfont: \"monospace\",\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\ttext.insertAt(0, \"MONO\");\n\t\t\t\t\t\t\ttext.defaultFormat = createPlainFormat();\n\t\t\t\t\t\t\ttext.insertAt(4, \"plain\");\n\n\t\t\t\t\t\t\tconst content = <FormattedMainView root={toPropTreeNode(text)} />;\n\t\t\t\t\t\t\tconst rendered = render(content, { reactStrictMode });\n\n\t\t\t\t\t\t\tassert.ok(\n\t\t\t\t\t\t\t\trendered.container.querySelector(\".ql-font-monospace\"),\n\t\t\t\t\t\t\t\t\"Initially: has .ql-font-monospace\",\n\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\ttext.removeRange(0, 4);\n\t\t\t\t\t\t\trendered.rerender(content);\n\n\t\t\t\t\t\t\tassert.ok(\n\t\t\t\t\t\t\t\t!rendered.container.querySelector(\".ql-font-monospace\"),\n\t\t\t\t\t\t\t\t\"After delete: no .ql-font-monospace\",\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tit(\"applies font via formatRange\", () => {\n\t\t\t\t\t\t\tconst { tree: text } = createFormattedTreeView(\"Hello World\");\n\t\t\t\t\t\t\tconst content = <FormattedMainView root={toPropTreeNode(text)} />;\n\t\t\t\t\t\t\tconst rendered = render(content, { reactStrictMode });\n\n\t\t\t\t\t\t\ttext.formatRange(6, 11, { font: \"monospace\" });\n\t\t\t\t\t\t\trendered.rerender(content);\n\n\t\t\t\t\t\t\tconst el = rendered.container.querySelector(\".ql-font-monospace\");\n\t\t\t\t\t\t\tassert.ok(el, \"Expected .ql-font-monospace after formatRange\");\n\t\t\t\t\t\t\tassert.match(el.textContent ?? \"\", /World/);\n\t\t\t\t\t\t});\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t}\n\t\t});\n\n\t\t// Undo/Redo tests for non-transactional edits\n\t\tdescribe(\"undo/redo\", () => {\n\t\t\tfor (const reactStrictMode of [false, true]) {\n\t\t\t\tdescribe(`StrictMode: ${reactStrictMode}`, () => {\n\t\t\t\t\tit(\"insert character, undo removes it, redo restores it\", () => {\n\t\t\t\t\t\tconst treeView = createFormattedTreeViewWithEvents();\n\t\t\t\t\t\tconst text = treeView.root;\n\t\t\t\t\t\tconst undoRedo = new UndoRedoStacks(treeView.events);\n\t\t\t\t\t\tconst editorRef = React.createRef<FormattedEditorHandle>();\n\t\t\t\t\t\tconst content = (\n\t\t\t\t\t\t\t<FormattedMainView\n\t\t\t\t\t\t\t\tref={editorRef}\n\t\t\t\t\t\t\t\troot={toPropTreeNode(text)}\n\t\t\t\t\t\t\t\tundoRedo={undoRedo}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst rendered = render(content, { reactStrictMode });\n\n\t\t\t\t\t\t// Insert a character\n\t\t\t\t\t\ttext.insertAt(0, \"A\");\n\t\t\t\t\t\trendered.rerender(content);\n\t\t\t\t\t\tassert.match(rendered.baseElement.textContent ?? \"\", /A/);\n\n\t\t\t\t\t\t// Undo - character should be removed\n\t\t\t\t\t\teditorRef.current?.undo();\n\t\t\t\t\t\trendered.rerender(content);\n\t\t\t\t\t\tassert(rendered.baseElement.textContent !== null);\n\t\t\t\t\t\tassert.doesNotMatch(rendered.baseElement.textContent, /A/);\n\n\t\t\t\t\t\t// Redo - character should be restored\n\t\t\t\t\t\teditorRef.current?.redo();\n\t\t\t\t\t\trendered.rerender(content);\n\t\t\t\t\t\tassert.match(rendered.baseElement.textContent ?? \"\", /A/);\n\t\t\t\t\t});\n\n\t\t\t\t\tit(\"insert character, make bold, undo removes bold but keeps character\", () => {\n\t\t\t\t\t\tconst treeView = createFormattedTreeViewWithEvents();\n\t\t\t\t\t\tconst text = treeView.root;\n\t\t\t\t\t\tconst undoRedo = new UndoRedoStacks(treeView.events);\n\t\t\t\t\t\tconst editorRef = React.createRef<FormattedEditorHandle>();\n\t\t\t\t\t\tconst content = (\n\t\t\t\t\t\t\t<FormattedMainView\n\t\t\t\t\t\t\t\tref={editorRef}\n\t\t\t\t\t\t\t\troot={toPropTreeNode(text)}\n\t\t\t\t\t\t\t\tundoRedo={undoRedo}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst rendered = render(content, { reactStrictMode });\n\n\t\t\t\t\t\t// Insert a character\n\t\t\t\t\t\ttext.insertAt(0, \"B\");\n\t\t\t\t\t\trendered.rerender(content);\n\t\t\t\t\t\tassert.match(rendered.baseElement.textContent ?? \"\", /B/);\n\t\t\t\t\t\tassert.ok(!rendered.container.querySelector(\"strong\"), \"Initially: no <strong>\");\n\n\t\t\t\t\t\t// Make it bold\n\t\t\t\t\t\ttext.formatRange(0, 1, { bold: true });\n\t\t\t\t\t\trendered.rerender(content);\n\t\t\t\t\t\tassert.ok(\n\t\t\t\t\t\t\trendered.container.querySelector(\"strong\"),\n\t\t\t\t\t\t\t\"After format: has <strong>\",\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\t// Undo - bold should be removed, character should remain\n\t\t\t\t\t\teditorRef.current?.undo();\n\t\t\t\t\t\trendered.rerender(content);\n\t\t\t\t\t\tassert.match(rendered.baseElement.textContent ?? \"\", /B/);\n\t\t\t\t\t\tassert.ok(\n\t\t\t\t\t\t\t!rendered.container.querySelector(\"strong\"),\n\t\t\t\t\t\t\t\"After undo: no <strong>, character remains\",\n\t\t\t\t\t\t);\n\t\t\t\t\t});\n\n\t\t\t\t\tit(\"multiple operations in transaction undo together as one unit\", () => {\n\t\t\t\t\t\tconst treeView = createFormattedTreeViewWithEvents();\n\t\t\t\t\t\tconst text = treeView.root;\n\t\t\t\t\t\tconst undoRedo = new UndoRedoStacks(treeView.events);\n\t\t\t\t\t\tconst editorRef = React.createRef<FormattedEditorHandle>();\n\t\t\t\t\t\tconst content = (\n\t\t\t\t\t\t\t<FormattedMainView\n\t\t\t\t\t\t\t\tref={editorRef}\n\t\t\t\t\t\t\t\troot={toPropTreeNode(text)}\n\t\t\t\t\t\t\t\tundoRedo={undoRedo}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst rendered = render(content, { reactStrictMode });\n\n\t\t\t\t\t\t// Two operations in one transaction\n\t\t\t\t\t\tTreeAlpha.branch(text)?.runTransaction(() => {\n\t\t\t\t\t\t\ttext.insertAt(0, \"A\");\n\t\t\t\t\t\t\ttext.insertAt(1, \"B\");\n\t\t\t\t\t\t});\n\t\t\t\t\t\trendered.rerender(content);\n\t\t\t\t\t\tassert.match(rendered.baseElement.textContent ?? \"\", /AB/);\n\n\t\t\t\t\t\t// Single undo should remove both characters\n\t\t\t\t\t\teditorRef.current?.undo();\n\t\t\t\t\t\trendered.rerender(content);\n\t\t\t\t\t\tassert(rendered.baseElement.textContent !== null);\n\t\t\t\t\t\tassert.doesNotMatch(rendered.baseElement.textContent, /A/);\n\t\t\t\t\t\tassert.doesNotMatch(rendered.baseElement.textContent, /B/);\n\n\t\t\t\t\t\t// Single redo should restore both characters\n\t\t\t\t\t\teditorRef.current?.redo();\n\t\t\t\t\t\trendered.rerender(content);\n\t\t\t\t\t\tassert.match(rendered.baseElement.textContent ?? \"\", /AB/);\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t}\n\t\t});\n\t\tdescribe(\"copy-paste helpers\", () => {\n\t\t\t/** Helper to create an HTMLElement with inline styles. */\n\t\t\tfunction styledElement(styles: Partial<CSSStyleDeclaration>): HTMLElement {\n\t\t\t\tconst el = document.createElement(\"span\");\n\t\t\t\tObject.assign(el.style, styles);\n\t\t\t\treturn el;\n\t\t\t}\n\n\t\t\tdescribe(\"parseCssFontSize\", () => {\n\t\t\t\tit(\"returns undefined when no fontSize is set\", () => {\n\t\t\t\t\tassert.equal(parseCssFontSize(styledElement({})), undefined);\n\t\t\t\t});\n\n\t\t\t\tit(\"returns Quill size name for supported pixel values\", () => {\n\t\t\t\t\tassert.equal(parseCssFontSize(styledElement({ fontSize: \"10px\" })), \"small\");\n\t\t\t\t\tassert.equal(parseCssFontSize(styledElement({ fontSize: \"18px\" })), \"large\");\n\t\t\t\t\tassert.equal(parseCssFontSize(styledElement({ fontSize: \"24px\" })), \"huge\");\n\t\t\t\t});\n\n\t\t\t\tit(\"returns undefined for default or unrecognized sizes\", () => {\n\t\t\t\t\tassert.equal(parseCssFontSize(styledElement({ fontSize: \"12px\" })), undefined);\n\t\t\t\t\tassert.equal(parseCssFontSize(styledElement({ fontSize: \"42px\" })), undefined);\n\t\t\t\t});\n\t\t\t});\n\n\t\t\tdescribe(\"parseCssFontFamily\", () => {\n\t\t\t\tit(\"returns undefined when no fontFamily is set\", () => {\n\t\t\t\t\tassert.equal(parseCssFontFamily(styledElement({})), undefined);\n\t\t\t\t});\n\n\t\t\t\tit(\"returns first recognized font in a comma-separated stack\", () => {\n\t\t\t\t\tassert.equal(\n\t\t\t\t\t\tparseCssFontFamily(styledElement({ fontFamily: \"monospace\" })),\n\t\t\t\t\t\t\"monospace\",\n\t\t\t\t\t);\n\t\t\t\t\tassert.equal(\n\t\t\t\t\t\tparseCssFontFamily(styledElement({ fontFamily: '\"Courier New\", monospace' })),\n\t\t\t\t\t\t\"monospace\",\n\t\t\t\t\t);\n\t\t\t\t\tassert.equal(\n\t\t\t\t\t\tparseCssFontFamily(\n\t\t\t\t\t\t\tstyledElement({ fontFamily: '\"Times New Roman\", \"Arial\", serif' }),\n\t\t\t\t\t\t),\n\t\t\t\t\t\t\"Arial\",\n\t\t\t\t\t);\n\t\t\t\t});\n\t\t\t\tit(\"strips single quotes around recognized font names\", () => {\n\t\t\t\t\tassert.equal(parseCssFontFamily(styledElement({ fontFamily: \"'Arial'\" })), \"Arial\");\n\t\t\t\t});\n\t\t\t\tit(\"returns undefined for unrecognized fonts\", () => {\n\t\t\t\t\tassert.equal(\n\t\t\t\t\t\tparseCssFontFamily(styledElement({ fontFamily: '\"Courier New\", fantasy' })),\n\t\t\t\t\t\tundefined,\n\t\t\t\t\t);\n\t\t\t\t});\n\t\t\t});\n\n\t\t\tdescribe(\"clipboardFormatMatcher\", () => {\n\t\t\t\tit(\"returns delta unchanged for non-HTMLElement nodes\", () => {\n\t\t\t\t\tconst delta = new Delta().insert(\"hello\");\n\t\t\t\t\tconst text = document.createTextNode(\"hello\");\n\t\t\t\t\tconst result = clipboardFormatMatcher(text, delta);\n\t\t\t\t\tassert.deepEqual(result.ops, delta.ops);\n\t\t\t\t});\n\n\t\t\t\tit(\"applies size and font attributes from inline styles\", () => {\n\t\t\t\t\tconst delta = new Delta().insert(\"hello\");\n\t\t\t\t\tconst el = styledElement({ fontSize: \"18px\", fontFamily: \"serif\" });\n\t\t\t\t\tconst result = clipboardFormatMatcher(el, delta);\n\t\t\t\t\tassert.equal(result.ops[0]?.attributes?.size, \"large\");\n\t\t\t\t\tassert.equal(result.ops[0]?.attributes?.font, \"serif\");\n\t\t\t\t});\n\n\t\t\t\tit(\"returns delta unchanged when no recognized styles\", () => {\n\t\t\t\t\tconst delta = new Delta().insert(\"hello\");\n\t\t\t\t\tconst el = styledElement({});\n\t\t\t\t\tconst result = clipboardFormatMatcher(el, delta);\n\t\t\t\t\tassert.deepEqual(result.ops, delta.ops);\n\t\t\t\t});\n\t\t\t});\n\t\t});\n\n\t\t// Unicode 16+ (joined emojis) section - test attribute cycling\n\t\tdescribe(\"Unicode 16+ joined emoji attribute cycling\", () => {\n\t\t\t// ZWJ (Zero Width Joiner) emoji sequence: 👨‍👩‍👧‍👦 = family emoji\n\t\t\tconst joinedEmoji = \"👨‍👩‍👧‍👦\";\n\n\t\t\tfor (const reactStrictMode of [false, true]) {\n\t\t\t\tdescribe(`StrictMode: ${reactStrictMode}`, () => {\n\t\t\t\t\tit(\"applies bold to joined emoji and removes it preserving text\", () => {\n\t\t\t\t\t\tconst { tree: text } = createFormattedTreeView(`Test ${joinedEmoji} Text`);\n\t\t\t\t\t\tconst content = <FormattedMainView root={toPropTreeNode(text)} />;\n\t\t\t\t\t\tconst rendered = render(content, { reactStrictMode });\n\n\t\t\t\t\t\tconst emojiStart = 5; // \"Test \" is 5 chars\n\t\t\t\t\t\tconst emojiLength = [...joinedEmoji].length;\n\t\t\t\t\t\ttext.formatRange(emojiStart, emojiStart + emojiLength, { bold: true });\n\t\t\t\t\t\trendered.rerender(content);\n\n\t\t\t\t\t\tassert.ok(\n\t\t\t\t\t\t\trendered.container.querySelector(\"strong\"),\n\t\t\t\t\t\t\t\"After bold: expected <strong>\",\n\t\t\t\t\t\t);\n\t\t\t\t\t\tassert.ok(\n\t\t\t\t\t\t\t(rendered.baseElement.textContent ?? \"\").includes(joinedEmoji),\n\t\t\t\t\t\t\t\"After bold: emoji should be preserved\",\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\ttext.formatRange(emojiStart, emojiStart + emojiLength, { bold: false });\n\t\t\t\t\t\trendered.rerender(content);\n\n\t\t\t\t\t\tassert.ok(\n\t\t\t\t\t\t\t!rendered.container.querySelector(\"strong\"),\n\t\t\t\t\t\t\t\"After remove bold: no <strong> expected\",\n\t\t\t\t\t\t);\n\t\t\t\t\t\tassert.ok(\n\t\t\t\t\t\t\t(rendered.baseElement.textContent ?? \"\").includes(joinedEmoji),\n\t\t\t\t\t\t\t\"After remove bold: emoji should still be preserved\",\n\t\t\t\t\t\t);\n\t\t\t\t\t});\n\n\t\t\t\t\tit(\"applies size to joined emoji and removes it preserving text\", () => {\n\t\t\t\t\t\tconst { tree: text } = createFormattedTreeView(`Test ${joinedEmoji} Text`);\n\t\t\t\t\t\tconst content = <FormattedMainView root={toPropTreeNode(text)} />;\n\t\t\t\t\t\tconst rendered = render(content, { reactStrictMode });\n\n\t\t\t\t\t\tconst emojiStart = 5;\n\t\t\t\t\t\tconst emojiLength = [...joinedEmoji].length;\n\t\t\t\t\t\ttext.formatRange(emojiStart, emojiStart + emojiLength, { size: 24 });\n\t\t\t\t\t\trendered.rerender(content);\n\n\t\t\t\t\t\tassert.ok(\n\t\t\t\t\t\t\trendered.container.querySelector(\".ql-size-huge\"),\n\t\t\t\t\t\t\t\"After size: expected .ql-size-huge\",\n\t\t\t\t\t\t);\n\t\t\t\t\t\tassert.ok(\n\t\t\t\t\t\t\t(rendered.baseElement.textContent ?? \"\").includes(joinedEmoji),\n\t\t\t\t\t\t\t\"Emoji preserved\",\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\ttext.formatRange(emojiStart, emojiStart + emojiLength, { size: 12 });\n\t\t\t\t\t\trendered.rerender(content);\n\n\t\t\t\t\t\tassert.ok(\n\t\t\t\t\t\t\t!rendered.container.querySelector(\".ql-size-huge\"),\n\t\t\t\t\t\t\t\"After remove: no .ql-size-huge\",\n\t\t\t\t\t\t);\n\t\t\t\t\t\tassert.ok(\n\t\t\t\t\t\t\t(rendered.baseElement.textContent ?? \"\").includes(joinedEmoji),\n\t\t\t\t\t\t\t\"Emoji still preserved\",\n\t\t\t\t\t\t);\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t}\n\t\t});\n\t});\n});\n"]}
@@ -0,0 +1,62 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+ import { strict as assert } from "node:assert";
6
+ import { CommitKind, RevertibleStatus, } from "@fluidframework/tree";
7
+ import { UndoRedoStacks } from "../undoRedo.js";
8
+ function createMockListenable() {
9
+ let handler;
10
+ const listenable = {
11
+ on: (_event, cb) => {
12
+ handler = cb;
13
+ return () => { };
14
+ },
15
+ };
16
+ function fireCommit(kind, revertible) {
17
+ assert(handler !== undefined, "handler should be registered");
18
+ const commit = { kind, isLocal: true };
19
+ handler(commit, () => revertible);
20
+ }
21
+ return { listenable, fireCommit };
22
+ }
23
+ function createMockRevertible() {
24
+ return {
25
+ revert: () => { },
26
+ dispose: () => { },
27
+ status: RevertibleStatus.Valid,
28
+ };
29
+ }
30
+ describe("UndoRedoStacks", () => {
31
+ it("commit, undo, and redo notify listeners", () => {
32
+ const { listenable, fireCommit } = createMockListenable();
33
+ const stacks = new UndoRedoStacks(listenable);
34
+ let notifyCount = 0;
35
+ stacks.onStateChange(() => {
36
+ notifyCount++;
37
+ });
38
+ // Adding a change notifies
39
+ fireCommit(CommitKind.Default, createMockRevertible());
40
+ assert.equal(notifyCount, 1);
41
+ // Undo notifies
42
+ stacks.undo();
43
+ assert.equal(notifyCount, 2);
44
+ // Set up redo stack (also notifies)
45
+ fireCommit(CommitKind.Undo, createMockRevertible());
46
+ assert.equal(notifyCount, 3);
47
+ // Redo notifies
48
+ stacks.redo();
49
+ assert.equal(notifyCount, 4);
50
+ });
51
+ it("canUndo/canRedo reflect stack state", () => {
52
+ const { listenable, fireCommit } = createMockListenable();
53
+ const stacks = new UndoRedoStacks(listenable);
54
+ assert.equal(stacks.canUndo(), false);
55
+ assert.equal(stacks.canRedo(), false);
56
+ fireCommit(CommitKind.Default, createMockRevertible());
57
+ assert.equal(stacks.canUndo(), true);
58
+ fireCommit(CommitKind.Undo, createMockRevertible());
59
+ assert.equal(stacks.canRedo(), true);
60
+ });
61
+ });
62
+ //# sourceMappingURL=undoRedo.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"undoRedo.test.js","sourceRoot":"","sources":["../../src/test/undoRedo.test.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { strict as assert } from \"node:assert\";\n\nimport type { Listenable } from \"@fluidframework/core-interfaces\";\nimport {\n\tCommitKind,\n\ttype CommitMetadata,\n\ttype Revertible,\n\ttype RevertibleFactory,\n\tRevertibleStatus,\n\ttype TreeViewEvents,\n} from \"@fluidframework/tree\";\n\nimport { UndoRedoStacks } from \"../undoRedo.js\";\n\ntype CommitAppliedHandler = (\n\tcommit: CommitMetadata,\n\tgetRevertible?: RevertibleFactory,\n) => void;\n\nfunction createMockListenable(): {\n\tlistenable: Listenable<TreeViewEvents>;\n\tfireCommit: (kind: CommitKind, revertible: Revertible) => void;\n} {\n\tlet handler: CommitAppliedHandler | undefined;\n\tconst listenable = {\n\t\ton: (_event: string, cb: CommitAppliedHandler) => {\n\t\t\thandler = cb;\n\t\t\treturn () => {};\n\t\t},\n\t} as unknown as Listenable<TreeViewEvents>;\n\n\tfunction fireCommit(kind: CommitKind, revertible: Revertible): void {\n\t\tassert(handler !== undefined, \"handler should be registered\");\n\t\tconst commit: CommitMetadata = { kind, isLocal: true };\n\t\thandler(commit, () => revertible);\n\t}\n\n\treturn { listenable, fireCommit };\n}\n\nfunction createMockRevertible(): Revertible {\n\treturn {\n\t\trevert: () => {},\n\t\tdispose: () => {},\n\t\tstatus: RevertibleStatus.Valid,\n\t};\n}\n\ndescribe(\"UndoRedoStacks\", () => {\n\tit(\"commit, undo, and redo notify listeners\", () => {\n\t\tconst { listenable, fireCommit } = createMockListenable();\n\t\tconst stacks = new UndoRedoStacks(listenable);\n\n\t\tlet notifyCount = 0;\n\t\tstacks.onStateChange(() => {\n\t\t\tnotifyCount++;\n\t\t});\n\n\t\t// Adding a change notifies\n\t\tfireCommit(CommitKind.Default, createMockRevertible());\n\t\tassert.equal(notifyCount, 1);\n\n\t\t// Undo notifies\n\t\tstacks.undo();\n\t\tassert.equal(notifyCount, 2);\n\n\t\t// Set up redo stack (also notifies)\n\t\tfireCommit(CommitKind.Undo, createMockRevertible());\n\t\tassert.equal(notifyCount, 3);\n\n\t\t// Redo notifies\n\t\tstacks.redo();\n\t\tassert.equal(notifyCount, 4);\n\t});\n\n\tit(\"canUndo/canRedo reflect stack state\", () => {\n\t\tconst { listenable, fireCommit } = createMockListenable();\n\t\tconst stacks = new UndoRedoStacks(listenable);\n\n\t\tassert.equal(stacks.canUndo(), false);\n\t\tassert.equal(stacks.canRedo(), false);\n\n\t\tfireCommit(CommitKind.Default, createMockRevertible());\n\t\tassert.equal(stacks.canUndo(), true);\n\n\t\tfireCommit(CommitKind.Undo, createMockRevertible());\n\t\tassert.equal(stacks.canRedo(), true);\n\t});\n});\n"]}
@@ -31,7 +31,6 @@ describe("useObservation", () => {
31
31
  *
32
32
  * @remarks When in StrictMode, React may double render, so that case is not checked for an exact match.
33
33
  */
34
- // eslint-disable-next-line no-inner-declarations
35
34
  function checkRenderLog(log, expected) {
36
35
  if (reactStrictMode) {
37
36
  assert.deepEqual(new Set(log), new Set(expected));
@@ -1 +1 @@
1
- {"version":3,"file":"useObservation.spec.js","sourceRoot":"","sources":["../../src/test/useObservation.spec.tsx"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { strict as assert } from \"node:assert\";\n\nimport { render } from \"@testing-library/react\";\nimport globalJsdom from \"global-jsdom\";\nimport * as React from \"react\";\n\nimport {\n\tuseObservation,\n\tuseObservationStrict,\n\tuseObservationWithEffects,\n} from \"../useObservation.js\";\n\n// There is much more coverage of useObservation via useTree tests.\n\ndescribe(\"useObservation\", () => {\n\tdescribe(\"dom tests\", () => {\n\t\tlet cleanup: () => void;\n\n\t\tbefore(() => {\n\t\t\tcleanup = globalJsdom();\n\t\t});\n\n\t\tafter(() => {\n\t\t\tcleanup();\n\t\t});\n\n\t\tconst observationHooks: (typeof useObservation)[] = [\n\t\t\tuseObservation,\n\t\t\tuseObservationStrict,\n\t\t\tuseObservationWithEffects,\n\t\t] as const;\n\n\t\tfor (const useObservationHook of observationHooks) {\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\n\t\t\tdescribe((useObservationHook as Function).name, () => {\n\t\t\t\tfor (const reactStrictMode of [false, true]) {\n\t\t\t\t\t/**\n\t\t\t\t\t * Check then clear the contents of `log`.\n\t\t\t\t\t *\n\t\t\t\t\t * @remarks When in StrictMode, React may double render, so that case is not checked for an exact match.\n\t\t\t\t\t */\n\t\t\t\t\t// eslint-disable-next-line no-inner-declarations\n\t\t\t\t\tfunction checkRenderLog(log: string[], expected: readonly string[]): void {\n\t\t\t\t\t\tif (reactStrictMode) {\n\t\t\t\t\t\t\tassert.deepEqual(new Set(log), new Set(expected));\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tassert.deepEqual(log, expected);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tlog.length = 0;\n\t\t\t\t\t}\n\n\t\t\t\t\tdescribe(`StrictMode: ${reactStrictMode}`, () => {\n\t\t\t\t\t\tit(\"useObservation\", async () => {\n\t\t\t\t\t\t\tconst log: string[] = [];\n\n\t\t\t\t\t\t\tconst unsubscribe = (): void => {\n\t\t\t\t\t\t\t\tlog.push(\"unsubscribe\");\n\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\tfunction TestComponent(): JSX.Element {\n\t\t\t\t\t\t\t\tlog.push(\"render\");\n\t\t\t\t\t\t\t\treturn useObservationHook(\n\t\t\t\t\t\t\t\t\t(invalidate) => {\n\t\t\t\t\t\t\t\t\t\tlog.push(`useObservation`);\n\t\t\t\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\t\t\t\tresult: (\n\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t// In real usage, this would unsubscribe from any events.\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tlog.push(\"click\");\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tinvalidate();\n\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\tInvalidate\n\t\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t\t\tunsubscribe,\n\t\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t{ onInvalidation: () => log.push(\"invalidated\") },\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tconst content = <TestComponent />;\n\n\t\t\t\t\t\t\tconst rendered = render(content, { reactStrictMode });\n\t\t\t\t\t\t\tcheckRenderLog(log, [\"render\", \"useObservation\"]);\n\n\t\t\t\t\t\t\trendered.rerender(content);\n\t\t\t\t\t\t\tassertLogEmpty(log);\n\n\t\t\t\t\t\t\tconst button =\n\t\t\t\t\t\t\t\trendered.baseElement.querySelector(\"button\") ??\n\t\t\t\t\t\t\t\tassert.fail(\"button not found\");\n\t\t\t\t\t\t\tbutton.click();\n\n\t\t\t\t\t\t\tcheckRenderLog(log, [\"click\", \"invalidated\"]);\n\n\t\t\t\t\t\t\trendered.rerender(content);\n\n\t\t\t\t\t\t\tcheckRenderLog(log, [\n\t\t\t\t\t\t\t\t\"render\",\n\t\t\t\t\t\t\t\t...(reactStrictMode && useObservationHook !== useObservationWithEffects\n\t\t\t\t\t\t\t\t\t? [\"unsubscribe\"]\n\t\t\t\t\t\t\t\t\t: []),\n\t\t\t\t\t\t\t\t\"useObservation\",\n\t\t\t\t\t\t\t]);\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\t// This requires waiting for finalizers.\n\t\t\t\t\t\t// Forcing two async GCs seems to work robustly, so this is enabled, but if it becomes flakey, it can be tweaked and/or skipped.\n\t\t\t\t\t\tit(`unsubscribe on unmount`, async () => {\n\t\t\t\t\t\t\tassert(global.gc);\n\n\t\t\t\t\t\t\tconst log: string[] = [];\n\n\t\t\t\t\t\t\tconst unsubscribe = (): void => {\n\t\t\t\t\t\t\t\tlog.push(\"unsubscribe\");\n\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\tfunction TestComponent(this: unknown): JSX.Element {\n\t\t\t\t\t\t\t\treturn useObservationHook((invalidate) => ({\n\t\t\t\t\t\t\t\t\tresult: <br />,\n\t\t\t\t\t\t\t\t\tunsubscribe,\n\t\t\t\t\t\t\t\t}));\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tconst rendered = render(<TestComponent />, { reactStrictMode });\n\n\t\t\t\t\t\t\tassertLogEmpty(log);\n\t\t\t\t\t\t\trendered.unmount();\n\n\t\t\t\t\t\t\t// Unsubscribe on unmount is done via FinalizationRegistry, so force a GC and wait for it.\n\t\t\t\t\t\t\t// For this to pass on NodeJs experimentally is has been found that this must either do:\n\t\t\t\t\t\t\t// 1. a sync GC then a wait of 8 seconds (but this sometimes fails after multiple runs unless a debugger takes a heap snapshot, possible due to some JIT optimization that breaks it).\n\t\t\t\t\t\t\t// 2. two async GCs in a row.\n\t\t\t\t\t\t\t// Since the second option is both more robust and faster, that is what is used here.\n\t\t\t\t\t\t\tfor (let index = 0; index < 2; index++) {\n\t\t\t\t\t\t\t\tawait global.gc({ type: \"major\", execution: \"async\" });\n\t\t\t\t\t\t\t\tif (log.length > 0) {\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tcheckRenderLog(log, [\"unsubscribe\"]);\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tit(\"invalidate after unmount\", () => {\n\t\t\t\t\t\t\tconst log: string[] = [];\n\n\t\t\t\t\t\t\tlet logUnsubscribe = true;\n\n\t\t\t\t\t\t\tconst unsubscribe = (): void => {\n\t\t\t\t\t\t\t\tif (logUnsubscribe) log.push(\"unsubscribe\");\n\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\tconst invalidateCallbacks: (() => void)[] = [];\n\n\t\t\t\t\t\t\tfunction TestComponent(): JSX.Element {\n\t\t\t\t\t\t\t\tlog.push(\"render\");\n\t\t\t\t\t\t\t\treturn useObservationHook(\n\t\t\t\t\t\t\t\t\t(invalidate) => {\n\t\t\t\t\t\t\t\t\t\tinvalidateCallbacks.push(invalidate);\n\t\t\t\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\t\t\t\tresult: <br />,\n\t\t\t\t\t\t\t\t\t\t\tunsubscribe,\n\t\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t{ onInvalidation: () => log.push(\"invalidated\") },\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tconst rendered = render(<TestComponent />, { reactStrictMode });\n\n\t\t\t\t\t\t\tcheckRenderLog(log, [\"render\"]);\n\n\t\t\t\t\t\t\t// After unmount, unsubscribe could happen at any time due to finalizer,so suppress logging it to prevent the test from possibly becoming flaky.\n\t\t\t\t\t\t\tlogUnsubscribe = false;\n\n\t\t\t\t\t\t\trendered.unmount();\n\n\t\t\t\t\t\t\tassert(invalidateCallbacks.length === (reactStrictMode ? 2 : 1));\n\n\t\t\t\t\t\t\t// Invalidate after unmount.\n\t\t\t\t\t\t\t// Since this can happen in real use, due to unsubscribe delay while waiting for finalizer, ensure it does not cause issues.\n\t\t\t\t\t\t\t// This should be a no-op, but since it does a React SetState after unmount, React could object to it.\n\t\t\t\t\t\t\tfor (const callback of invalidateCallbacks) {\n\t\t\t\t\t\t\t\tcallback();\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// Confirm the invalidation happened..\n\t\t\t\t\t\t\t// If we didn't suppress unsubscribe logging, and the finalizer had run, this could fail (which is why we suppress it).\n\t\t\t\t\t\t\tcheckRenderLog(log, [\"invalidated\"]);\n\t\t\t\t\t\t});\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t});\n});\n\n/**\n * Assert that an array is empty.\n *\n * @remarks\n * Not inlined because doing so causes TypeScript to infer the array type as never[] afterwards and breaks push.\n * Better than asserting length is 0 as this gets a better error message on failure.\n */\nfunction assertLogEmpty(log: string[]): void {\n\tassert.deepEqual(log, []);\n}\n"]}
1
+ {"version":3,"file":"useObservation.spec.js","sourceRoot":"","sources":["../../src/test/useObservation.spec.tsx"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { strict as assert } from \"node:assert\";\n\nimport { render } from \"@testing-library/react\";\nimport globalJsdom from \"global-jsdom\";\nimport * as React from \"react\";\n\nimport {\n\tuseObservation,\n\tuseObservationStrict,\n\tuseObservationWithEffects,\n} from \"../useObservation.js\";\n\n// There is much more coverage of useObservation via useTree tests.\n\ndescribe(\"useObservation\", () => {\n\tdescribe(\"dom tests\", () => {\n\t\tlet cleanup: () => void;\n\n\t\tbefore(() => {\n\t\t\tcleanup = globalJsdom();\n\t\t});\n\n\t\tafter(() => {\n\t\t\tcleanup();\n\t\t});\n\n\t\tconst observationHooks: (typeof useObservation)[] = [\n\t\t\tuseObservation,\n\t\t\tuseObservationStrict,\n\t\t\tuseObservationWithEffects,\n\t\t] as const;\n\n\t\tfor (const useObservationHook of observationHooks) {\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\n\t\t\tdescribe((useObservationHook as Function).name, () => {\n\t\t\t\tfor (const reactStrictMode of [false, true]) {\n\t\t\t\t\t/**\n\t\t\t\t\t * Check then clear the contents of `log`.\n\t\t\t\t\t *\n\t\t\t\t\t * @remarks When in StrictMode, React may double render, so that case is not checked for an exact match.\n\t\t\t\t\t */\n\t\t\t\t\tfunction checkRenderLog(log: string[], expected: readonly string[]): void {\n\t\t\t\t\t\tif (reactStrictMode) {\n\t\t\t\t\t\t\tassert.deepEqual(new Set(log), new Set(expected));\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tassert.deepEqual(log, expected);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tlog.length = 0;\n\t\t\t\t\t}\n\n\t\t\t\t\tdescribe(`StrictMode: ${reactStrictMode}`, () => {\n\t\t\t\t\t\tit(\"useObservation\", async () => {\n\t\t\t\t\t\t\tconst log: string[] = [];\n\n\t\t\t\t\t\t\tconst unsubscribe = (): void => {\n\t\t\t\t\t\t\t\tlog.push(\"unsubscribe\");\n\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\tfunction TestComponent(): JSX.Element {\n\t\t\t\t\t\t\t\tlog.push(\"render\");\n\t\t\t\t\t\t\t\treturn useObservationHook(\n\t\t\t\t\t\t\t\t\t(invalidate) => {\n\t\t\t\t\t\t\t\t\t\tlog.push(`useObservation`);\n\t\t\t\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\t\t\t\tresult: (\n\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t// In real usage, this would unsubscribe from any events.\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tlog.push(\"click\");\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tinvalidate();\n\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\tInvalidate\n\t\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t\t\tunsubscribe,\n\t\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t{ onInvalidation: () => log.push(\"invalidated\") },\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tconst content = <TestComponent />;\n\n\t\t\t\t\t\t\tconst rendered = render(content, { reactStrictMode });\n\t\t\t\t\t\t\tcheckRenderLog(log, [\"render\", \"useObservation\"]);\n\n\t\t\t\t\t\t\trendered.rerender(content);\n\t\t\t\t\t\t\tassertLogEmpty(log);\n\n\t\t\t\t\t\t\tconst button =\n\t\t\t\t\t\t\t\trendered.baseElement.querySelector(\"button\") ??\n\t\t\t\t\t\t\t\tassert.fail(\"button not found\");\n\t\t\t\t\t\t\tbutton.click();\n\n\t\t\t\t\t\t\tcheckRenderLog(log, [\"click\", \"invalidated\"]);\n\n\t\t\t\t\t\t\trendered.rerender(content);\n\n\t\t\t\t\t\t\tcheckRenderLog(log, [\n\t\t\t\t\t\t\t\t\"render\",\n\t\t\t\t\t\t\t\t...(reactStrictMode && useObservationHook !== useObservationWithEffects\n\t\t\t\t\t\t\t\t\t? [\"unsubscribe\"]\n\t\t\t\t\t\t\t\t\t: []),\n\t\t\t\t\t\t\t\t\"useObservation\",\n\t\t\t\t\t\t\t]);\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\t// This requires waiting for finalizers.\n\t\t\t\t\t\t// Forcing two async GCs seems to work robustly, so this is enabled, but if it becomes flakey, it can be tweaked and/or skipped.\n\t\t\t\t\t\tit(`unsubscribe on unmount`, async () => {\n\t\t\t\t\t\t\tassert(global.gc);\n\n\t\t\t\t\t\t\tconst log: string[] = [];\n\n\t\t\t\t\t\t\tconst unsubscribe = (): void => {\n\t\t\t\t\t\t\t\tlog.push(\"unsubscribe\");\n\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\tfunction TestComponent(this: unknown): JSX.Element {\n\t\t\t\t\t\t\t\treturn useObservationHook((invalidate) => ({\n\t\t\t\t\t\t\t\t\tresult: <br />,\n\t\t\t\t\t\t\t\t\tunsubscribe,\n\t\t\t\t\t\t\t\t}));\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tconst rendered = render(<TestComponent />, { reactStrictMode });\n\n\t\t\t\t\t\t\tassertLogEmpty(log);\n\t\t\t\t\t\t\trendered.unmount();\n\n\t\t\t\t\t\t\t// Unsubscribe on unmount is done via FinalizationRegistry, so force a GC and wait for it.\n\t\t\t\t\t\t\t// For this to pass on NodeJs experimentally is has been found that this must either do:\n\t\t\t\t\t\t\t// 1. a sync GC then a wait of 8 seconds (but this sometimes fails after multiple runs unless a debugger takes a heap snapshot, possible due to some JIT optimization that breaks it).\n\t\t\t\t\t\t\t// 2. two async GCs in a row.\n\t\t\t\t\t\t\t// Since the second option is both more robust and faster, that is what is used here.\n\t\t\t\t\t\t\tfor (let index = 0; index < 2; index++) {\n\t\t\t\t\t\t\t\tawait global.gc({ type: \"major\", execution: \"async\" });\n\t\t\t\t\t\t\t\tif (log.length > 0) {\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tcheckRenderLog(log, [\"unsubscribe\"]);\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tit(\"invalidate after unmount\", () => {\n\t\t\t\t\t\t\tconst log: string[] = [];\n\n\t\t\t\t\t\t\tlet logUnsubscribe = true;\n\n\t\t\t\t\t\t\tconst unsubscribe = (): void => {\n\t\t\t\t\t\t\t\tif (logUnsubscribe) log.push(\"unsubscribe\");\n\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\tconst invalidateCallbacks: (() => void)[] = [];\n\n\t\t\t\t\t\t\tfunction TestComponent(): JSX.Element {\n\t\t\t\t\t\t\t\tlog.push(\"render\");\n\t\t\t\t\t\t\t\treturn useObservationHook(\n\t\t\t\t\t\t\t\t\t(invalidate) => {\n\t\t\t\t\t\t\t\t\t\tinvalidateCallbacks.push(invalidate);\n\t\t\t\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\t\t\t\tresult: <br />,\n\t\t\t\t\t\t\t\t\t\t\tunsubscribe,\n\t\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t{ onInvalidation: () => log.push(\"invalidated\") },\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tconst rendered = render(<TestComponent />, { reactStrictMode });\n\n\t\t\t\t\t\t\tcheckRenderLog(log, [\"render\"]);\n\n\t\t\t\t\t\t\t// After unmount, unsubscribe could happen at any time due to finalizer,so suppress logging it to prevent the test from possibly becoming flaky.\n\t\t\t\t\t\t\tlogUnsubscribe = false;\n\n\t\t\t\t\t\t\trendered.unmount();\n\n\t\t\t\t\t\t\tassert(invalidateCallbacks.length === (reactStrictMode ? 2 : 1));\n\n\t\t\t\t\t\t\t// Invalidate after unmount.\n\t\t\t\t\t\t\t// Since this can happen in real use, due to unsubscribe delay while waiting for finalizer, ensure it does not cause issues.\n\t\t\t\t\t\t\t// This should be a no-op, but since it does a React SetState after unmount, React could object to it.\n\t\t\t\t\t\t\tfor (const callback of invalidateCallbacks) {\n\t\t\t\t\t\t\t\tcallback();\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// Confirm the invalidation happened..\n\t\t\t\t\t\t\t// If we didn't suppress unsubscribe logging, and the finalizer had run, this could fail (which is why we suppress it).\n\t\t\t\t\t\t\tcheckRenderLog(log, [\"invalidated\"]);\n\t\t\t\t\t\t});\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t});\n});\n\n/**\n * Assert that an array is empty.\n *\n * @remarks\n * Not inlined because doing so causes TypeScript to infer the array type as never[] afterwards and breaks push.\n * Better than asserting length is 0 as this gets a better error message on failure.\n */\nfunction assertLogEmpty(log: string[]): void {\n\tassert.deepEqual(log, []);\n}\n"]}
@@ -37,7 +37,6 @@ describe("useTree", () => {
37
37
  *
38
38
  * When in StrictMode, React may double render, so that case is not checked for an exact match.
39
39
  */
40
- // eslint-disable-next-line no-inner-declarations
41
40
  function checkRenderLog(log, expected) {
42
41
  if (reactStrictMode) {
43
42
  assert.deepEqual(new Set(log), new Set(expected));
@@ -1 +1 @@
1
- {"version":3,"file":"useTree.spec.js","sourceRoot":"","sources":["../../src/test/useTree.spec.tsx"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { strict as assert } from \"node:assert\";\n\nimport { SchemaFactory } from \"@fluidframework/tree\";\nimport { render } from \"@testing-library/react\";\nimport globalJsdom from \"global-jsdom\";\nimport * as React from \"react\";\n\nimport { toPropTreeNode, type PropTreeNode } from \"../propNode.js\";\nimport { objectIdNumber } from \"../simpleIdentifier.js\";\nimport {\n\tusePropTreeNode,\n\twithMemoizedTreeObservations,\n\twithTreeObservations,\n} from \"../useTree.js\";\n\ndescribe(\"useTree\", () => {\n\tdescribe(\"dom tests\", () => {\n\t\tlet cleanup: () => void;\n\n\t\tbefore(() => {\n\t\t\tcleanup = globalJsdom();\n\t\t});\n\n\t\tafter(() => {\n\t\t\tcleanup();\n\t\t});\n\n\t\tit(\"withTreeObservations example\", () => {\n\t\t\tconst builder = new SchemaFactory(\"example\");\n\t\t\tclass Item extends builder.object(\"Item\", { text: SchemaFactory.string }) {}\n\t\t\tconst ItemComponentBug = ({ item }: { item: Item }): JSX.Element => (\n\t\t\t\t<span>{item.text}</span> // Reading `text`, a mutable value from a React prop, causes an invalidation bug.\n\t\t\t);\n\n\t\t\tconst ItemComponent = withTreeObservations(\n\t\t\t\t({ item }: { item: Item }): JSX.Element => <span>{item.text}</span>,\n\t\t\t);\n\n\t\t\tconst ItemParentComponent = ({ item }: { item: PropTreeNode<Item> }): JSX.Element => (\n\t\t\t\t<ItemComponent item={item} />\n\t\t\t);\n\n\t\t\tconst InvalidItemParentComponent = ({\n\t\t\t\titem,\n\t\t\t}: {\n\t\t\t\titem: PropTreeNode<Item>;\n\t\t\t}): JSX.Element => (\n\t\t\t\t// @ts-expect-error PropTreeNode turns this invalidation bug into a compile error\n\t\t\t\t<span>{item.text}</span>\n\t\t\t);\n\t\t});\n\n\t\tfor (const reactStrictMode of [false, true]) {\n\t\t\t/**\n\t\t\t * Check then clear, the contents of `log`.\n\t\t\t *\n\t\t\t * When in StrictMode, React may double render, so that case is not checked for an exact match.\n\t\t\t */\n\t\t\t// eslint-disable-next-line no-inner-declarations\n\t\t\tfunction checkRenderLog(log: string[], expected: readonly string[]): void {\n\t\t\t\tif (reactStrictMode) {\n\t\t\t\t\tassert.deepEqual(new Set(log), new Set(expected));\n\t\t\t\t} else {\n\t\t\t\t\tassert.deepEqual(log, expected);\n\t\t\t\t}\n\t\t\t\tlog.length = 0;\n\t\t\t}\n\n\t\t\tdescribe(`StrictMode: ${reactStrictMode}`, () => {\n\t\t\t\tit(\"usePropTreeNode\", () => {\n\t\t\t\t\tconst builder = new SchemaFactory(\"tree-react-api\");\n\n\t\t\t\t\tclass Point extends builder.object(\"Point\", {\n\t\t\t\t\t\tx: builder.number,\n\t\t\t\t\t\ty: builder.number,\n\t\t\t\t\t}) {}\n\n\t\t\t\t\tconst log: string[] = [];\n\n\t\t\t\t\tfunction PointComponent(props: { node: PropTreeNode<Point> }): JSX.Element {\n\t\t\t\t\t\tlog.push(\"render\");\n\t\t\t\t\t\tconst { x, y } = usePropTreeNode(props.node, (node) => {\n\t\t\t\t\t\t\tlog.push(`usePropTreeNode`);\n\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\tx: node.x,\n\t\t\t\t\t\t\t\ty: node.y,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t});\n\t\t\t\t\t\treturn <span>{`x: ${x}, y: ${y}`}</span>;\n\t\t\t\t\t}\n\n\t\t\t\t\tfunction ParentComponent(props: { node: PropTreeNode<Point> }): JSX.Element {\n\t\t\t\t\t\tlog.push(\"parent\");\n\t\t\t\t\t\treturn <PointComponent node={props.node} />;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst point = new Point({ x: 1, y: 1 });\n\t\t\t\t\tconst propPoint = toPropTreeNode(point);\n\n\t\t\t\t\tconst content = <ParentComponent node={propPoint} />;\n\n\t\t\t\t\tconst rendered = render(content, { reactStrictMode });\n\t\t\t\t\tassert.equal(rendered.baseElement.textContent, \"x: 1, y: 1\");\n\t\t\t\t\tcheckRenderLog(log, [\"parent\", \"render\", \"usePropTreeNode\"]);\n\n\t\t\t\t\trendered.rerender(content);\n\t\t\t\t\tassertLogEmpty(log);\n\n\t\t\t\t\tpoint.x = 2;\n\t\t\t\t\tassertLogEmpty(log);\n\t\t\t\t\trendered.rerender(content);\n\t\t\t\t\t// Parent which passed node down did not rerender, but PointComponent which read from it did:\n\t\t\t\t\tcheckRenderLog(log, [\"render\", \"usePropTreeNode\"]);\n\t\t\t\t\tassert.equal(rendered.baseElement.textContent, \"x: 2, y: 1\");\n\t\t\t\t});\n\t\t\t});\n\n\t\t\tdescribe(\"withTreeObservations and array\", () => {\n\t\t\t\tconst builder = new SchemaFactory(\"tree-react-api\");\n\n\t\t\t\tclass Item extends builder.object(\"Item\", {\n\t\t\t\t\tx: SchemaFactory.number,\n\t\t\t\t}) {}\n\n\t\t\t\tclass Collection extends builder.array(\"Collection\", Item) {}\n\n\t\t\t\tbeforeEach(() => {\n\t\t\t\t\t// Ensure the log starts empty for each test.\n\t\t\t\t\tlog.length = 0;\n\t\t\t\t});\n\n\t\t\t\tconst log: string[] = [];\n\n\t\t\t\tconst ItemComponent = withMemoizedTreeObservations(\n\t\t\t\t\t(props: { item: Item }): JSX.Element => {\n\t\t\t\t\t\tlog.push(`Item: ${props.item.x}`);\n\t\t\t\t\t\treturn <span>{`${props.item.x}`}</span>;\n\t\t\t\t\t},\n\t\t\t\t\t{ onInvalidation: () => log.push(\"Item invalidated\") },\n\t\t\t\t);\n\n\t\t\t\tconst CollectionComponent = withTreeObservations(\n\t\t\t\t\t(props: { collection: Collection }): JSX.Element => {\n\t\t\t\t\t\tlog.push(\"Collection\");\n\n\t\t\t\t\t\tconst items = props.collection.map((item) => (\n\t\t\t\t\t\t\t<ItemComponent key={objectIdNumber(item)} item={item} />\n\t\t\t\t\t\t));\n\n\t\t\t\t\t\treturn <div>{items}</div>;\n\t\t\t\t\t},\n\t\t\t\t\t{ onInvalidation: () => log.push(\"Collection invalidated\") },\n\t\t\t\t);\n\n\t\t\t\tconst ParentComponent = withTreeObservations(\n\t\t\t\t\t(props: { node: Collection }): JSX.Element => {\n\t\t\t\t\t\tlog.push(\"Parent\");\n\t\t\t\t\t\treturn <CollectionComponent collection={props.node} />;\n\t\t\t\t\t},\n\t\t\t\t\t{ onInvalidation: () => log.push(\"Parent invalidated\") },\n\t\t\t\t);\n\n\t\t\t\tit(\"empty\", () => {\n\t\t\t\t\tconst collection = new Collection([]);\n\t\t\t\t\tconst content = <ParentComponent node={collection} />;\n\t\t\t\t\trender(content, { reactStrictMode });\n\t\t\t\t\tcheckRenderLog(log, [\"Parent\", \"Collection\"]);\n\t\t\t\t});\n\n\t\t\t\t// This confirms that modifying an array does not needlessly invalid parents and reuses children (if they use memo)\n\t\t\t\tit(\"array editing: insertion\", () => {\n\t\t\t\t\tconst collection = new Collection([{ x: 1 }, { x: 2 }, { x: 3 }]);\n\t\t\t\t\tconst content = <ParentComponent node={collection} />;\n\t\t\t\t\tconst rendered = render(content, { reactStrictMode });\n\t\t\t\t\tcheckRenderLog(log, [\"Parent\", \"Collection\", \"Item: 1\", \"Item: 2\", \"Item: 3\"]);\n\t\t\t\t\tcollection.insertAtEnd(new Item({ x: 4 }));\n\t\t\t\t\tcheckRenderLog(log, [\"Collection invalidated\"]);\n\t\t\t\t\trendered.rerender(content);\n\t\t\t\t\tcheckRenderLog(log, [\"Collection\", \"Item: 4\"]);\n\n\t\t\t\t\tassert.equal(rendered.baseElement.textContent, \"1234\");\n\t\t\t\t});\n\n\t\t\t\t// This confirms the same as the above, but testes some harder cases.\n\t\t\t\t// For example this one depends on stable keys to reusing children due to indexes changing.\n\t\t\t\tit(\"array editing: general\", () => {\n\t\t\t\t\tconst collection = new Collection([{ x: 1 }, { x: 2 }, { x: 3 }]);\n\t\t\t\t\tconst content = <ParentComponent node={collection} />;\n\t\t\t\t\tconst rendered = render(content, { reactStrictMode });\n\t\t\t\t\tcheckRenderLog(log, [\"Parent\", \"Collection\", \"Item: 1\", \"Item: 2\", \"Item: 3\"]);\n\t\t\t\t\tcollection.moveToEnd(0);\n\t\t\t\t\tcheckRenderLog(log, [\"Collection invalidated\"]);\n\t\t\t\t\trendered.rerender(content);\n\t\t\t\t\tcheckRenderLog(log, [\"Collection\"]);\n\t\t\t\t\tcollection.removeAt(1);\n\t\t\t\t\tcollection.insertAtStart(new Item({ x: 4 }));\n\t\t\t\t\tcheckRenderLog(log, [\"Collection invalidated\"]);\n\t\t\t\t\trendered.rerender(content);\n\t\t\t\t\tcheckRenderLog(log, [\"Collection\", \"Item: 4\"]);\n\n\t\t\t\t\tassert.equal(rendered.baseElement.textContent, \"421\");\n\t\t\t\t});\n\t\t\t});\n\t\t}\n\t});\n});\n\n/**\n * Assert that an array is empty.\n *\n * Not inlined because doing so causes TypeScript to infer the array type as never[] afterwards and breaks push.\n * Better than asserting length is 0 as this gets a better error message on failure.\n */\nfunction assertLogEmpty(log: string[]): void {\n\tassert.deepEqual(log, []);\n}\n"]}
1
+ {"version":3,"file":"useTree.spec.js","sourceRoot":"","sources":["../../src/test/useTree.spec.tsx"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { strict as assert } from \"node:assert\";\n\nimport { SchemaFactory } from \"@fluidframework/tree\";\nimport { render } from \"@testing-library/react\";\nimport globalJsdom from \"global-jsdom\";\nimport * as React from \"react\";\n\nimport { toPropTreeNode, type PropTreeNode } from \"../propNode.js\";\nimport { objectIdNumber } from \"../simpleIdentifier.js\";\nimport {\n\tusePropTreeNode,\n\twithMemoizedTreeObservations,\n\twithTreeObservations,\n} from \"../useTree.js\";\n\ndescribe(\"useTree\", () => {\n\tdescribe(\"dom tests\", () => {\n\t\tlet cleanup: () => void;\n\n\t\tbefore(() => {\n\t\t\tcleanup = globalJsdom();\n\t\t});\n\n\t\tafter(() => {\n\t\t\tcleanup();\n\t\t});\n\n\t\tit(\"withTreeObservations example\", () => {\n\t\t\tconst builder = new SchemaFactory(\"example\");\n\t\t\tclass Item extends builder.object(\"Item\", { text: SchemaFactory.string }) {}\n\t\t\tconst ItemComponentBug = ({ item }: { item: Item }): JSX.Element => (\n\t\t\t\t<span>{item.text}</span> // Reading `text`, a mutable value from a React prop, causes an invalidation bug.\n\t\t\t);\n\n\t\t\tconst ItemComponent = withTreeObservations(\n\t\t\t\t({ item }: { item: Item }): JSX.Element => <span>{item.text}</span>,\n\t\t\t);\n\n\t\t\tconst ItemParentComponent = ({ item }: { item: PropTreeNode<Item> }): JSX.Element => (\n\t\t\t\t<ItemComponent item={item} />\n\t\t\t);\n\n\t\t\tconst InvalidItemParentComponent = ({\n\t\t\t\titem,\n\t\t\t}: {\n\t\t\t\titem: PropTreeNode<Item>;\n\t\t\t}): JSX.Element => (\n\t\t\t\t// @ts-expect-error PropTreeNode turns this invalidation bug into a compile error\n\t\t\t\t<span>{item.text}</span>\n\t\t\t);\n\t\t});\n\n\t\tfor (const reactStrictMode of [false, true]) {\n\t\t\t/**\n\t\t\t * Check then clear, the contents of `log`.\n\t\t\t *\n\t\t\t * When in StrictMode, React may double render, so that case is not checked for an exact match.\n\t\t\t */\n\t\t\tfunction checkRenderLog(log: string[], expected: readonly string[]): void {\n\t\t\t\tif (reactStrictMode) {\n\t\t\t\t\tassert.deepEqual(new Set(log), new Set(expected));\n\t\t\t\t} else {\n\t\t\t\t\tassert.deepEqual(log, expected);\n\t\t\t\t}\n\t\t\t\tlog.length = 0;\n\t\t\t}\n\n\t\t\tdescribe(`StrictMode: ${reactStrictMode}`, () => {\n\t\t\t\tit(\"usePropTreeNode\", () => {\n\t\t\t\t\tconst builder = new SchemaFactory(\"tree-react-api\");\n\n\t\t\t\t\tclass Point extends builder.object(\"Point\", {\n\t\t\t\t\t\tx: builder.number,\n\t\t\t\t\t\ty: builder.number,\n\t\t\t\t\t}) {}\n\n\t\t\t\t\tconst log: string[] = [];\n\n\t\t\t\t\tfunction PointComponent(props: { node: PropTreeNode<Point> }): JSX.Element {\n\t\t\t\t\t\tlog.push(\"render\");\n\t\t\t\t\t\tconst { x, y } = usePropTreeNode(props.node, (node) => {\n\t\t\t\t\t\t\tlog.push(`usePropTreeNode`);\n\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\tx: node.x,\n\t\t\t\t\t\t\t\ty: node.y,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t});\n\t\t\t\t\t\treturn <span>{`x: ${x}, y: ${y}`}</span>;\n\t\t\t\t\t}\n\n\t\t\t\t\tfunction ParentComponent(props: { node: PropTreeNode<Point> }): JSX.Element {\n\t\t\t\t\t\tlog.push(\"parent\");\n\t\t\t\t\t\treturn <PointComponent node={props.node} />;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst point = new Point({ x: 1, y: 1 });\n\t\t\t\t\tconst propPoint = toPropTreeNode(point);\n\n\t\t\t\t\tconst content = <ParentComponent node={propPoint} />;\n\n\t\t\t\t\tconst rendered = render(content, { reactStrictMode });\n\t\t\t\t\tassert.equal(rendered.baseElement.textContent, \"x: 1, y: 1\");\n\t\t\t\t\tcheckRenderLog(log, [\"parent\", \"render\", \"usePropTreeNode\"]);\n\n\t\t\t\t\trendered.rerender(content);\n\t\t\t\t\tassertLogEmpty(log);\n\n\t\t\t\t\tpoint.x = 2;\n\t\t\t\t\tassertLogEmpty(log);\n\t\t\t\t\trendered.rerender(content);\n\t\t\t\t\t// Parent which passed node down did not rerender, but PointComponent which read from it did:\n\t\t\t\t\tcheckRenderLog(log, [\"render\", \"usePropTreeNode\"]);\n\t\t\t\t\tassert.equal(rendered.baseElement.textContent, \"x: 2, y: 1\");\n\t\t\t\t});\n\t\t\t});\n\n\t\t\tdescribe(\"withTreeObservations and array\", () => {\n\t\t\t\tconst builder = new SchemaFactory(\"tree-react-api\");\n\n\t\t\t\tclass Item extends builder.object(\"Item\", {\n\t\t\t\t\tx: SchemaFactory.number,\n\t\t\t\t}) {}\n\n\t\t\t\tclass Collection extends builder.array(\"Collection\", Item) {}\n\n\t\t\t\tbeforeEach(() => {\n\t\t\t\t\t// Ensure the log starts empty for each test.\n\t\t\t\t\tlog.length = 0;\n\t\t\t\t});\n\n\t\t\t\tconst log: string[] = [];\n\n\t\t\t\tconst ItemComponent = withMemoizedTreeObservations(\n\t\t\t\t\t(props: { item: Item }): JSX.Element => {\n\t\t\t\t\t\tlog.push(`Item: ${props.item.x}`);\n\t\t\t\t\t\treturn <span>{`${props.item.x}`}</span>;\n\t\t\t\t\t},\n\t\t\t\t\t{ onInvalidation: () => log.push(\"Item invalidated\") },\n\t\t\t\t);\n\n\t\t\t\tconst CollectionComponent = withTreeObservations(\n\t\t\t\t\t(props: { collection: Collection }): JSX.Element => {\n\t\t\t\t\t\tlog.push(\"Collection\");\n\n\t\t\t\t\t\tconst items = props.collection.map((item) => (\n\t\t\t\t\t\t\t<ItemComponent key={objectIdNumber(item)} item={item} />\n\t\t\t\t\t\t));\n\n\t\t\t\t\t\treturn <div>{items}</div>;\n\t\t\t\t\t},\n\t\t\t\t\t{ onInvalidation: () => log.push(\"Collection invalidated\") },\n\t\t\t\t);\n\n\t\t\t\tconst ParentComponent = withTreeObservations(\n\t\t\t\t\t(props: { node: Collection }): JSX.Element => {\n\t\t\t\t\t\tlog.push(\"Parent\");\n\t\t\t\t\t\treturn <CollectionComponent collection={props.node} />;\n\t\t\t\t\t},\n\t\t\t\t\t{ onInvalidation: () => log.push(\"Parent invalidated\") },\n\t\t\t\t);\n\n\t\t\t\tit(\"empty\", () => {\n\t\t\t\t\tconst collection = new Collection([]);\n\t\t\t\t\tconst content = <ParentComponent node={collection} />;\n\t\t\t\t\trender(content, { reactStrictMode });\n\t\t\t\t\tcheckRenderLog(log, [\"Parent\", \"Collection\"]);\n\t\t\t\t});\n\n\t\t\t\t// This confirms that modifying an array does not needlessly invalid parents and reuses children (if they use memo)\n\t\t\t\tit(\"array editing: insertion\", () => {\n\t\t\t\t\tconst collection = new Collection([{ x: 1 }, { x: 2 }, { x: 3 }]);\n\t\t\t\t\tconst content = <ParentComponent node={collection} />;\n\t\t\t\t\tconst rendered = render(content, { reactStrictMode });\n\t\t\t\t\tcheckRenderLog(log, [\"Parent\", \"Collection\", \"Item: 1\", \"Item: 2\", \"Item: 3\"]);\n\t\t\t\t\tcollection.insertAtEnd(new Item({ x: 4 }));\n\t\t\t\t\tcheckRenderLog(log, [\"Collection invalidated\"]);\n\t\t\t\t\trendered.rerender(content);\n\t\t\t\t\tcheckRenderLog(log, [\"Collection\", \"Item: 4\"]);\n\n\t\t\t\t\tassert.equal(rendered.baseElement.textContent, \"1234\");\n\t\t\t\t});\n\n\t\t\t\t// This confirms the same as the above, but testes some harder cases.\n\t\t\t\t// For example this one depends on stable keys to reusing children due to indexes changing.\n\t\t\t\tit(\"array editing: general\", () => {\n\t\t\t\t\tconst collection = new Collection([{ x: 1 }, { x: 2 }, { x: 3 }]);\n\t\t\t\t\tconst content = <ParentComponent node={collection} />;\n\t\t\t\t\tconst rendered = render(content, { reactStrictMode });\n\t\t\t\t\tcheckRenderLog(log, [\"Parent\", \"Collection\", \"Item: 1\", \"Item: 2\", \"Item: 3\"]);\n\t\t\t\t\tcollection.moveToEnd(0);\n\t\t\t\t\tcheckRenderLog(log, [\"Collection invalidated\"]);\n\t\t\t\t\trendered.rerender(content);\n\t\t\t\t\tcheckRenderLog(log, [\"Collection\"]);\n\t\t\t\t\tcollection.removeAt(1);\n\t\t\t\t\tcollection.insertAtStart(new Item({ x: 4 }));\n\t\t\t\t\tcheckRenderLog(log, [\"Collection invalidated\"]);\n\t\t\t\t\trendered.rerender(content);\n\t\t\t\t\tcheckRenderLog(log, [\"Collection\", \"Item: 4\"]);\n\n\t\t\t\t\tassert.equal(rendered.baseElement.textContent, \"421\");\n\t\t\t\t});\n\t\t\t});\n\t\t}\n\t});\n});\n\n/**\n * Assert that an array is empty.\n *\n * Not inlined because doing so causes TypeScript to infer the array type as never[] afterwards and breaks push.\n * Better than asserting length is 0 as this gets a better error message on failure.\n */\nfunction assertLogEmpty(log: string[]): void {\n\tassert.deepEqual(log, []);\n}\n"]}
@@ -0,0 +1,6 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+ export { FormattedTextAsTree, FormattedMainView, type FormattedMainViewProps, type FormattedEditorHandle, } from "./quillFormattedView.js";
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/text/formatted/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACN,mBAAmB,EACnB,iBAAiB,EACjB,KAAK,sBAAsB,EAC3B,KAAK,qBAAqB,GAC1B,MAAM,yBAAyB,CAAC"}
@@ -0,0 +1,6 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+ export { FormattedTextAsTree, FormattedMainView, } from "./quillFormattedView.js";
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/text/formatted/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACN,mBAAmB,EACnB,iBAAiB,GAGjB,MAAM,yBAAyB,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nexport {\n\tFormattedTextAsTree,\n\tFormattedMainView,\n\ttype FormattedMainViewProps,\n\ttype FormattedEditorHandle,\n} from \"./quillFormattedView.js\";\n"]}
@@ -0,0 +1,54 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+ import { FormattedTextAsTree } from "@fluidframework/tree/internal";
6
+ export { FormattedTextAsTree } from "@fluidframework/tree/internal";
7
+ import DeltaPackage from "quill-delta";
8
+ import * as React from "react";
9
+ import { type PropTreeNode } from "../../propNode.js";
10
+ import type { UndoRedo } from "../../undoRedo.js";
11
+ type Delta = DeltaPackage.default;
12
+ declare const Delta: typeof DeltaPackage.default;
13
+ /**
14
+ * Props for the FormattedMainView component.
15
+ * @input @internal
16
+ */
17
+ export interface FormattedMainViewProps {
18
+ readonly root: PropTreeNode<FormattedTextAsTree.Tree>;
19
+ /** Optional undo/redo stack for the editor. */
20
+ readonly undoRedo?: UndoRedo;
21
+ }
22
+ /**
23
+ * Ref handle exposing undo/redo methods for the formatted editor.
24
+ * @input @internal
25
+ */
26
+ export type FormattedEditorHandle = Pick<UndoRedo, "undo" | "redo">;
27
+ /**
28
+ * A React component for formatted text editing.
29
+ * @remarks
30
+ * Uses {@link @fluidframework/tree#FormattedTextAsTree.Tree} for the data-model and Quill for the rich text editor UI.
31
+ * @internal
32
+ */
33
+ export declare const FormattedMainView: React.ForwardRefExoticComponent<FormattedMainViewProps & React.RefAttributes<FormattedEditorHandle>>;
34
+ /**
35
+ * Parse CSS font-size from a pasted HTML element's inline style.
36
+ * Returns a Quill size name if the pixel value matches a supported size, undefined otherwise.
37
+ * 12px is the default size and returns undefined (no Quill attribute needed).
38
+ */
39
+ export declare function parseCssFontSize(node: HTMLElement): string | undefined;
40
+ /**
41
+ * Parse CSS font-family from a pasted HTML element's inline style.
42
+ * Tries fonts in priority order (first to last per CSS spec) and returns
43
+ * the first recognized Quill font value.
44
+ */
45
+ export declare function parseCssFontFamily(node: HTMLElement): string | undefined;
46
+ /**
47
+ * Clipboard matcher that preserves recognized font-size and font-family
48
+ * from pasted HTML elements. Applies each format independently via
49
+ * compose/retain so new attributes can be added without risk of an
50
+ * early return skipping them.
51
+ * @see https://quilljs.com/docs/modules/clipboard#addmatcher
52
+ */
53
+ export declare function clipboardFormatMatcher(node: Node, delta: Delta): Delta;
54
+ //# sourceMappingURL=quillFormattedView.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quillFormattedView.d.ts","sourceRoot":"","sources":["../../../src/text/formatted/quillFormattedView.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAmB,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AACrF,OAAO,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AAEpE,OAAO,YAAY,MAAM,aAAa,CAAC;AACvC,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,OAAO,EAAE,KAAK,YAAY,EAAsB,MAAM,mBAAmB,CAAC;AAC1E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAGlD,KAAK,KAAK,GAAG,YAAY,CAAC,OAAO,CAAC;AAElC,QAAA,MAAM,KAAK,6BAAuB,CAAC;AAEnC;;;GAGG;AACH,MAAM,WAAW,sBAAsB;IACtC,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC;IACtD,+CAA+C;IAC/C,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC;CAC7B;AAED;;;GAGG;AACH,MAAM,MAAM,qBAAqB,GAAG,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC;AAEpE;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,sGAK5B,CAAC;AAaH;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,GAAG,SAAS,CAiBtE;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,GAAG,SAAS,CAkBxE;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,GAAG,KAAK,CActE"}