diff --git a/packages/rendermime/src/widgets.ts b/packages/rendermime/src/widgets.ts index 7c4e91d3b88f..8a1b7c9f168e 100644 --- a/packages/rendermime/src/widgets.ts +++ b/packages/rendermime/src/widgets.ts @@ -63,10 +63,21 @@ export abstract class RenderedCommon extends Widget * @param model - The mime model to render. * * @returns A promise which resolves when rendering is complete. + * + * #### Notes + * If the DOM node for this widget already has content, it is emptied + * before rendering. Subclasses that do not want this behavior + * (if, for instance, they are using DOM diffing), should override + * this method and not call `super.renderModel()`. */ async renderModel(model: IRenderMime.IMimeModel): Promise { // TODO compare model against old model for early bail? + // Empty any existing content in the node from previous renders + while (this.node.firstChild) { + this.node.removeChild(this.node.firstChild); + } + // Toggle the trusted class on the widget. this.toggleClass('jp-mod-trusted', model.trusted); diff --git a/tests/test-rendermime/src/factories.spec.ts b/tests/test-rendermime/src/factories.spec.ts index fc0b920ef8da..b846dc02bd3c 100644 --- a/tests/test-rendermime/src/factories.spec.ts +++ b/tests/test-rendermime/src/factories.spec.ts @@ -66,6 +66,16 @@ describe('rendermime/factories', () => { expect(w.node.innerHTML).to.equal('
x = 2 ** a
'); }); + it('should be re-renderable', async () => { + const f = textRendererFactory; + const mimeType = 'text/plain'; + const model = createModel(mimeType, 'x = 2 ** a'); + const w = f.createRenderer({ mimeType, ...defaultOptions }); + await w.renderModel(model); + await w.renderModel(model); + expect(w.node.innerHTML).to.equal('
x = 2 ** a
'); + }); + it('should output the correct HTML with ansi colors', async () => { const f = textRendererFactory; const source = 'There is no text but \x1b[01;41;32mtext\x1b[00m.\nWoo.'; @@ -116,6 +126,17 @@ describe('rendermime/factories', () => { await w.renderModel(model); expect(w.node.textContent).to.equal(source); }); + + it('should be re-renderable', async () => { + const source = 'sumlimits_{i=0}^{infty} \frac{1}{n^2}'; + const f = latexRendererFactory; + const mimeType = 'text/latex'; + const model = createModel(mimeType, source); + const w = f.createRenderer({ mimeType, ...defaultOptions }); + await w.renderModel(model); + await w.renderModel(model); + expect(w.node.textContent).to.equal(source); + }); }); }); @@ -174,6 +195,17 @@ describe('rendermime/factories', () => { expect(w.node.innerHTML).to.equal(source); }); + it('it should be re-renderable', async () => { + const f = markdownRendererFactory; + const source = '

hello

'; + const mimeType = 'text/markdown'; + const model = createModel(mimeType, source); + const w = f.createRenderer({ mimeType, ...defaultOptions }); + await w.renderModel(model); + await w.renderModel(model); + expect(w.node.innerHTML).to.equal(source); + }); + it('should add header anchors', async () => { const source = require('../../../examples/filebrowser/sample.md') .default as string; @@ -229,6 +261,17 @@ describe('rendermime/factories', () => { expect(w.node.innerHTML).to.equal('

This is great

'); }); + it('should be re-renderable', async () => { + const f = htmlRendererFactory; + const source = '

This is great

'; + const mimeType = 'text/html'; + const model = createModel(mimeType, source); + const w = f.createRenderer({ mimeType, ...defaultOptions }); + await w.renderModel(model); + await w.renderModel(model); + expect(w.node.innerHTML).to.equal('

This is great

'); + }); + // TODO we are disabling script execution for now. it.skip('should execute a script tag when attached', () => { const source = '';