From 185eddcf3d6f001ece984aae671c5a9faeff30f4 Mon Sep 17 00:00:00 2001 From: Le Roux Bodenstein Date: Fri, 21 Feb 2025 16:11:42 +0000 Subject: [PATCH 1/3] set a consistent key for output elements rather than using the array index --- .../src/components/shell-output-line.tsx | 1 + .../browser-repl/src/components/shell-output.tsx | 5 ++++- packages/browser-repl/src/components/shell.tsx | 3 +++ packages/browser-repl/src/sandbox.tsx | 16 +++++++++++++--- 4 files changed, 21 insertions(+), 4 deletions(-) diff --git a/packages/browser-repl/src/components/shell-output-line.tsx b/packages/browser-repl/src/components/shell-output-line.tsx index 7367c1d7c2..0f43359abc 100644 --- a/packages/browser-repl/src/components/shell-output-line.tsx +++ b/packages/browser-repl/src/components/shell-output-line.tsx @@ -35,6 +35,7 @@ type ShellOutputEntryValue = any; type Glyph = 'ChevronRight' | 'XWithCircle' | 'ChevronLeft'; export interface ShellOutputEntry { + key?: number | string; format: 'input' | 'output' | 'error'; type?: string | null; value: ShellOutputEntryValue; diff --git a/packages/browser-repl/src/components/shell-output.tsx b/packages/browser-repl/src/components/shell-output.tsx index e8f331df84..657c6e899c 100644 --- a/packages/browser-repl/src/components/shell-output.tsx +++ b/packages/browser-repl/src/components/shell-output.tsx @@ -16,7 +16,10 @@ export class ShellOutput extends Component { renderLine = (entry: ShellOutputEntry, index: number): JSX.Element => { return ( - + ); }; diff --git a/packages/browser-repl/src/components/shell.tsx b/packages/browser-repl/src/components/shell.tsx index d7a220a65c..692d87a949 100644 --- a/packages/browser-repl/src/components/shell.tsx +++ b/packages/browser-repl/src/components/shell.tsx @@ -184,6 +184,8 @@ const capLengthStart = (elements: unknown[], maxLength: number) => { elements.splice(maxLength); }; +let lastKey = 0; + const _Shell: ForwardRefRenderFunction = ( { runtime, @@ -262,6 +264,7 @@ const _Shell: ForwardRefRenderFunction = ( ...(outputRef.current ?? []), ...result.map( (entry): ShellOutputEntry => ({ + key: lastKey++, format: 'output', type: entry.type, value: entry.printable, diff --git a/packages/browser-repl/src/sandbox.tsx b/packages/browser-repl/src/sandbox.tsx index 1e2e87975b..fa170b08d7 100644 --- a/packages/browser-repl/src/sandbox.tsx +++ b/packages/browser-repl/src/sandbox.tsx @@ -151,16 +151,26 @@ class DemoServiceProvider { const runtime = new IframeRuntime(new DemoServiceProvider() as any); +const lotsOfLines: ShellOutputEntry[] = []; +for (let i = 0; i < 99; i++) { + lotsOfLines.push({ key: `entry-${i}`, format: 'output', value: { i } }); +} + const IframeRuntimeExample: React.FunctionComponent = () => { const [darkMode, setDarkMode] = useState(true); const [redactInfo, setRedactInfo] = useState(false); - const [maxOutputLength, setMaxOutputLength] = useState(1000); - const [maxHistoryLength, setMaxHistoryLength] = useState(1000); + const [maxOutputLength, setMaxOutputLength] = useState(100); + const [maxHistoryLength, setMaxHistoryLength] = useState(100); const [initialEvaluate, setInitialEvaluate] = useState([]); const [initialText, setInitialText] = useState(''); const [output, setOutput] = useState([ - { format: 'output', value: { foo: 1, bar: true, buz: function () {} } }, + ...lotsOfLines, + { + key: 'test', + format: 'output', + value: { foo: 1, bar: true, buz: function () {} }, + }, ]); const [history, setHistory] = useState([ 'show dbs', From 2632debc661c6454c5dac54b4fdaa37ac3f7ecfe Mon Sep 17 00:00:00 2001 From: Le Roux Bodenstein Date: Fri, 21 Feb 2025 16:34:28 +0000 Subject: [PATCH 2/3] make key required --- packages/browser-repl/src/components/shell-output-line.tsx | 2 +- packages/browser-repl/src/components/shell-output.tsx | 7 ++----- packages/browser-repl/src/components/shell.spec.tsx | 6 +++++- packages/browser-repl/src/components/shell.tsx | 4 ++++ 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/packages/browser-repl/src/components/shell-output-line.tsx b/packages/browser-repl/src/components/shell-output-line.tsx index 0f43359abc..11615d1357 100644 --- a/packages/browser-repl/src/components/shell-output-line.tsx +++ b/packages/browser-repl/src/components/shell-output-line.tsx @@ -35,7 +35,7 @@ type ShellOutputEntryValue = any; type Glyph = 'ChevronRight' | 'XWithCircle' | 'ChevronLeft'; export interface ShellOutputEntry { - key?: number | string; + key: number | string; format: 'input' | 'output' | 'error'; type?: string | null; value: ShellOutputEntryValue; diff --git a/packages/browser-repl/src/components/shell-output.tsx b/packages/browser-repl/src/components/shell-output.tsx index 657c6e899c..ced5dda409 100644 --- a/packages/browser-repl/src/components/shell-output.tsx +++ b/packages/browser-repl/src/components/shell-output.tsx @@ -14,12 +14,9 @@ export class ShellOutput extends Component { output: PropTypes.arrayOf(PropTypes.any).isRequired, }; - renderLine = (entry: ShellOutputEntry, index: number): JSX.Element => { + renderLine = (entry: ShellOutputEntry): JSX.Element => { return ( - + ); }; diff --git a/packages/browser-repl/src/components/shell.spec.tsx b/packages/browser-repl/src/components/shell.spec.tsx index dfe4804ca6..0bc6f89a6c 100644 --- a/packages/browser-repl/src/components/shell.spec.tsx +++ b/packages/browser-repl/src/components/shell.spec.tsx @@ -37,6 +37,8 @@ function filterEvaluateCalls(calls: any) { }); } +let lastKey = 0; + describe('shell', function () { let fakeRuntime; let scrollIntoView; @@ -79,7 +81,7 @@ describe('shell', function () { it('takes output', function () { const output: ShellOutputEntry[] = [ - { format: 'output', value: 'Welcome message goes here' }, + { key: lastKey++, format: 'output', value: 'Welcome message goes here' }, ]; render(); @@ -312,6 +314,7 @@ describe('shell', function () { let output: ShellOutputEntry[] = []; for (let i = 0; i < 1000; i++) { output.push({ + key: lastKey++, format: 'output', type: undefined, value: 'some result', @@ -425,6 +428,7 @@ describe('shell', function () { it('clears the output when onClearCommand is called', async function () { let output: ShellOutputEntry[] = [ { + key: lastKey++, format: 'output', type: undefined, value: 'some result', diff --git a/packages/browser-repl/src/components/shell.tsx b/packages/browser-repl/src/components/shell.tsx index 692d87a949..4233b4ac73 100644 --- a/packages/browser-repl/src/components/shell.tsx +++ b/packages/browser-repl/src/components/shell.tsx @@ -357,12 +357,14 @@ const _Shell: ForwardRefRenderFunction = ( runtime.setEvaluationListener(listener); const result = await runtime.evaluate(code); outputLine = { + key: lastKey++, format: 'output', type: result.type, value: result.printable, }; } catch (error) { outputLine = { + key: lastKey++, format: 'error', value: error, }; @@ -383,6 +385,7 @@ const _Shell: ForwardRefRenderFunction = ( // don't evaluate empty input, but do add it to the output if (!code || code.trim() === '') { newOutputBeforeEval.push({ + key: lastKey++, format: 'input', value: ' ', }); @@ -394,6 +397,7 @@ const _Shell: ForwardRefRenderFunction = ( // add input to output newOutputBeforeEval.push({ + key: lastKey++, format: 'input', value: code, }); From 0eae9d80ca270004ae006c40ebeec520607a6d59 Mon Sep 17 00:00:00 2001 From: Le Roux Bodenstein Date: Mon, 24 Feb 2025 09:16:27 +0000 Subject: [PATCH 3/3] fix tests --- .../src/components/shell.spec.tsx | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/packages/browser-repl/src/components/shell.spec.tsx b/packages/browser-repl/src/components/shell.spec.tsx index 0bc6f89a6c..17f9e8764c 100644 --- a/packages/browser-repl/src/components/shell.spec.tsx +++ b/packages/browser-repl/src/components/shell.spec.tsx @@ -1,3 +1,4 @@ +import _ from 'lodash'; import React, { useState, useEffect } from 'react'; import sinon from 'sinon'; import { render, screen, waitFor, configure } from '@testing-library/react'; @@ -39,6 +40,12 @@ function filterEvaluateCalls(calls: any) { let lastKey = 0; +function stripKeys(output: ShellOutputEntry[]) { + return output.map((entry) => { + return _.omit(entry, ['key']); + }); +} + describe('shell', function () { let fakeRuntime; let scrollIntoView; @@ -107,7 +114,7 @@ describe('shell', function () { ); await waitFor(() => { - expect(output).to.deep.equal([ + expect(stripKeys(output)).to.deep.equal([ { format: 'input', value: 'my command', @@ -264,7 +271,7 @@ describe('shell', function () { ); await waitFor(() => { - expect(output).to.deep.equal([ + expect(stripKeys(output)).to.deep.equal([ { format: 'input', value: ' ', @@ -295,7 +302,7 @@ describe('shell', function () { ); await waitFor(() => { - expect(output).to.deep.equal([ + expect(stripKeys(output)).to.deep.equal([ { format: 'input', value: 'my command', @@ -336,7 +343,7 @@ describe('shell', function () { ); await waitFor(() => { - expect(output).to.deep.equal([ + expect(stripKeys(output)).to.deep.equal([ { format: 'output', type: undefined, @@ -408,14 +415,14 @@ describe('shell', function () { ); await waitFor(() => { - expect(output[output.length - 1]).to.deep.equal({ + expect(stripKeys(output)[output.length - 1]).to.deep.equal({ format: 'output', type: undefined, value: 'some result', }); }); - expect(output).to.deep.equal([ + expect(stripKeys(output)).to.deep.equal([ // we typed "my command" { format: 'input', value: 'my command' }, // while evaluating it printed something