diff --git a/src/constants.js b/src/constants.js index 7a823a3efd..1ad82b208e 100644 --- a/src/constants.js +++ b/src/constants.js @@ -31,3 +31,5 @@ export const NULL = null; export const UNDEFINED = undefined; export const EMPTY_OBJ = /** @type {any} */ ({}); export const EMPTY_ARR = []; + +export const MATHML_TOKEN_ELEMENTS = /(mi|mn|mo|ms$|mte|msp)/; diff --git a/src/diff/index.js b/src/diff/index.js index 73c4a01ebb..427d3cf314 100644 --- a/src/diff/index.js +++ b/src/diff/index.js @@ -12,7 +12,8 @@ import { RESET_MODE, SVG_NAMESPACE, UNDEFINED, - XHTML_NAMESPACE + XHTML_NAMESPACE, + MATHML_TOKEN_ELEMENTS } from '../constants'; import { BaseComponent, getDomSibling } from '../component'; import { Fragment } from '../create-element'; @@ -608,6 +609,13 @@ function diffElementNodes( } else { if (oldHtml) dom.innerHTML = ''; + if ( + nodeType == 'foreignObject' || + (namespace == MATH_NAMESPACE && MATHML_TOKEN_ELEMENTS.test(nodeType)) + ) { + namespace = XHTML_NAMESPACE; + } + diffChildren( // @ts-expect-error nodeType == 'template' ? dom.content : dom, @@ -615,7 +623,7 @@ function diffElementNodes( newVNode, oldVNode, globalContext, - nodeType == 'foreignObject' ? XHTML_NAMESPACE : namespace, + namespace, excessDomChildren, commitQueue, excessDomChildren diff --git a/test/browser/mathml.test.jsx b/test/browser/mathml.test.jsx index 79439f4016..0f5f43023e 100644 --- a/test/browser/mathml.test.jsx +++ b/test/browser/mathml.test.jsx @@ -2,6 +2,9 @@ import { createElement, Component, render } from 'preact'; import { setupRerender } from 'preact/test-utils'; import { setupScratch, teardown } from '../_util/helpers'; +const XHTML_NAMESPACE = 'http://www.w3.org/1999/xhtml'; +const MATH_NAMESPACE = 'http://www.w3.org/1998/Math/MathML'; + describe('mathml', () => { let scratch; @@ -18,7 +21,7 @@ describe('mathml', () => { let namespace = scratch.querySelector('math').namespaceURI; - expect(namespace).to.equal('http://www.w3.org/1998/Math/MathML'); + expect(namespace).to.equal(MATH_NAMESPACE); }); it('should render children with the correct namespace URI', () => { @@ -31,12 +34,12 @@ describe('mathml', () => { let namespace = scratch.querySelector('mrow').namespaceURI; - expect(namespace).to.equal('http://www.w3.org/1998/Math/MathML'); + expect(namespace).to.equal(MATH_NAMESPACE); }); it('should inherit correct namespace URI from parent', () => { const math = document.createElementNS( - 'http://www.w3.org/1998/Math/MathML', + MATH_NAMESPACE, 'math' ); scratch.appendChild(math); @@ -44,14 +47,14 @@ describe('mathml', () => { render(, scratch.firstChild); let namespace = scratch.querySelector('mrow').namespaceURI; - expect(namespace).to.equal('http://www.w3.org/1998/Math/MathML'); + expect(namespace).to.equal(MATH_NAMESPACE); }); it('should inherit correct namespace URI from parent upon updating', () => { setupRerender(); const math = document.createElementNS( - 'http://www.w3.org/1998/Math/MathML', + MATH_NAMESPACE, 'math' ); scratch.appendChild(math); @@ -62,7 +65,7 @@ describe('mathml', () => { // eslint-disable-next-line this.setState({ show: false }, () => { expect(scratch.querySelector('mo').namespaceURI).to.equal( - 'http://www.w3.org/1998/Math/MathML' + MATH_NAMESPACE ); }); } @@ -90,4 +93,24 @@ describe('mathml', () => { expect(scratch.firstChild).to.be.an('HTMLDivElement'); expect(scratch.firstChild.firstChild).to.be.an('MathMLElement'); }); + + it('should support XHTML phrasing content within MathML token elements', () => { + render( +
+ + + 123 + + +
, + scratch + ); + + expect(scratch.querySelector('mi').namespaceURI).to.equal( + MATH_NAMESPACE + ); + expect(scratch.querySelector('ins').namespaceURI).to.equal( + XHTML_NAMESPACE + ); + }); });