diff --git a/apis/nucleus/src/__tests__/viz.spec.js b/apis/nucleus/src/__tests__/viz.spec.js index 0ffc51a63..21a578fca 100644 --- a/apis/nucleus/src/__tests__/viz.spec.js +++ b/apis/nucleus/src/__tests__/viz.spec.js @@ -22,17 +22,20 @@ describe('viz', () => { let setSnOptions; let setSnContext; let takeSnapshot; + let exportImage; before(() => { sandbox = sinon.createSandbox(); unmount = sandbox.spy(); setSnOptions = sandbox.spy(); setSnContext = sandbox.spy(); takeSnapshot = sandbox.spy(); + exportImage = sandbox.spy(); cellRef = { current: { setSnOptions, setSnContext, takeSnapshot, + exportImage, }, }; glue = sandbox.stub().returns([unmount, cellRef]); @@ -61,6 +64,10 @@ describe('viz', () => { it('should have a setTemporaryProperties method', () => { expect(api.setTemporaryProperties).to.be.a('function'); }); + + it('should have an exportImage method', () => { + expect(api.exportImage).to.be.a('function'); + }); }); describe('mounting', () => { @@ -133,4 +140,11 @@ describe('viz', () => { expect(cellRef.current.takeSnapshot).to.have.been.calledWithExactly(); }); }); + + describe('export', () => { + it('should export image', async () => { + api.exportImage(); + expect(cellRef.current.exportImage).to.have.been.calledWithExactly(); + }); + }); }); diff --git a/apis/nucleus/src/components/Cell.jsx b/apis/nucleus/src/components/Cell.jsx index 179ea71c7..9318dd253 100644 --- a/apis/nucleus/src/components/Cell.jsx +++ b/apis/nucleus/src/components/Cell.jsx @@ -233,29 +233,15 @@ const Cell = forwardRef(({ nebulaContext, model, initialSnContext, initialSnOpti setSnContext, setSnOptions, async takeSnapshot() { - const snapshot = { - ...layout, - snapshotData: { - object: { - size: { - w: contentRect.width, - h: contentRect.height, - }, - }, - }, - }; + const { width, height } = cellRef.current.getBoundingClientRect(); + + // clone layout to avoid mutation + let clonedLayout = JSON.parse(JSON.stringify(layout)); if (typeof state.sn.component.setSnapshotData === 'function') { - return (await state.sn.component.setSnapshotData(snapshot)) || snapshot; + clonedLayout = (await state.sn.component.setSnapshotData(clonedLayout)) || clonedLayout; } - return snapshot; - }, - async exportImage() { - if (!nebulaContext.snapshot.capture) { - throw new Error('Nebula has not been configured with snapshot.capture'); - } - const lyt = await this.takeSnapshot(); // eslint-disable-line - const { width, height } = cellRef.current.getBoundingClientRect(); - const s = { + return { + key: String(+Date.now()), meta: { language: translator.language(), theme: theme.name, @@ -265,10 +251,15 @@ const Cell = forwardRef(({ nebulaContext, model, initialSnContext, initialSnOpti height: Math.round(height), }, }, - layout: lyt, + layout: clonedLayout, }; - - return nebulaContext.snapshot.capture(s); + }, + async exportImage() { + if (!nebulaContext.snapshot.capture) { + throw new Error('Nebula has not been configured with snapshot.capture'); + } + const snapshot = await this.takeSnapshot(); // eslint-disable-line + return nebulaContext.snapshot.capture(snapshot); }, }), [state.sn, contentRect, layout, theme.name] diff --git a/apis/nucleus/src/components/__tests__/cell.spec.jsx b/apis/nucleus/src/components/__tests__/cell.spec.jsx index fcb92f4fd..31f8b64f4 100644 --- a/apis/nucleus/src/components/__tests__/cell.spec.jsx +++ b/apis/nucleus/src/components/__tests__/cell.spec.jsx @@ -75,7 +75,7 @@ describe('', () => { await act(async () => { renderer = create( - s }}> + s, language: () => 'sv' }}> ', () => { }); global.window.addEventListener.callArg(1); const snapshot = await cellRef.current.takeSnapshot(); + const { key } = snapshot; + delete snapshot.key; + expect(key).to.be.a('string'); expect(snapshot).to.deep.equal({ - visualization: 'sn', - snapshotData: { object: { size: { w: 300, h: 400 } } }, + layout: { + visualization: 'sn', + }, + meta: { + language: 'sv', + theme: 'dark', + size: { + height: 400, + width: 300, + }, + }, }); }); @@ -460,7 +472,7 @@ describe('', () => { }); global.window.addEventListener.callArg(1); const snapshot = await cellRef.current.takeSnapshot(); - expect(snapshot).to.deep.equal({ + expect(snapshot.layout).to.deep.equal({ foo: 'bar', }); }); diff --git a/apis/nucleus/src/viz.js b/apis/nucleus/src/viz.js index 91814888b..0520e94eb 100644 --- a/apis/nucleus/src/viz.js +++ b/apis/nucleus/src/viz.js @@ -112,13 +112,15 @@ export default function viz({ model, context: nebulaContext } = {}) { setSnContext(ctx); return api; }, + exportImage() { + return cellRef.current.exportImage(); + }, + + // DEBUG MODE ? + // TODO - decide if this method is useful as part of public API takeSnapshot() { - // TODO - decide if this method is useful at all return cellRef.current.takeSnapshot(); }, - exportImage(settings) { - return cellRef.current.exportImage(settings); - }, // QVisualization API // close() {}, diff --git a/commands/serve/web/components/Cell.jsx b/commands/serve/web/components/Cell.jsx index 88ad7af18..638d192cf 100644 --- a/commands/serve/web/components/Cell.jsx +++ b/commands/serve/web/components/Cell.jsx @@ -74,15 +74,19 @@ export default function({ id, expandable, minHeight }) { }); } else { const containerSize = vizRef.current.el.getBoundingClientRect(); - vizRef.current.viz.exportImage().then(res => { - if (res && res.url) { - const key = /\/([A-z0-9_]+)$/.exec(res.url)[1]; - window.open( - `/render?snapshot=${key}`, - 'snapshot', - `height=${Math.round(containerSize.height)},width=${Math.round(containerSize.width)}` - ); - } + vizRef.current.viz.takeSnapshot().then(snapshot => { + fetch('/njs/snapshot', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(snapshot), + }); + window.open( + `/render?snapshot=${snapshot.key}`, + 'snapshot', + `height=${Math.round(containerSize.height)},width=${Math.round(containerSize.width)}` + ); }); } }, diff --git a/commands/serve/web/eRender.js b/commands/serve/web/eRender.js index 9c6ff9b56..9d3037b93 100644 --- a/commands/serve/web/eRender.js +++ b/commands/serve/web/eRender.js @@ -85,9 +85,12 @@ async function renderSnapshot() { }, ], }); - snapshooter({ - nucleus: n, - element, + + window.onHotChange(supernova.name, async () => { + snapshooter({ + nucleus: n, + element, + }); }); }