Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions packages/browser-repl/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@
"exports": {
".": {
"types": "./lib/index.d.ts",
"require": "./lib/index.js"
"default": "./lib/index.js"
},
"./shell": {
"types": "./lib/components/shell.d.ts",
"require": "./lib/components/shell.js"
"default": "./lib/components/shell.js"
},
"./package.json": {
"require": "./package.json"
"default": "./package.json"
}
},
"scripts": {
Expand Down
5 changes: 4 additions & 1 deletion packages/browser-repl/src/components/shell-input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ interface ShellInputProps {
prompt?: string;
onSigInt?(): Promise<boolean>;
editorRef?: (editor: EditorRef | null) => void;
initialText?: string;
onTextChange?: (text: string) => void;
}

interface ShellInputState {
Expand All @@ -29,7 +31,7 @@ interface ShellInputState {

export class ShellInput extends Component<ShellInputProps, ShellInputState> {
readonly state: ShellInputState = {
currentValue: '',
currentValue: this.props.initialText ?? '',
readOnly: false,
};

Expand Down Expand Up @@ -61,6 +63,7 @@ export class ShellInput extends Component<ShellInputProps, ShellInputState> {
}

private onChange = (value: string): void => {
this.props.onTextChange?.(value);
this.setState({ currentValue: value });
};

Expand Down
7 changes: 7 additions & 0 deletions packages/browser-repl/src/components/shell.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -516,4 +516,11 @@ describe('<Shell />', function () {
expect(wrapper.find('ShellInput').prop('prompt')).to.equal('abc>');
});
});

it('sets initial text for the shell input', function () {
wrapper = mount(
<Shell runtime={fakeRuntime} initialInput="db.coll.find({})" />
);
expect(wrapper.find('Editor').prop('value')).to.eq('db.coll.find({})');
});
});
32 changes: 29 additions & 3 deletions packages/browser-repl/src/components/shell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ interface ShellProps {
*/
onHistoryChanged: (history: readonly string[]) => void;

/**
* A function called each time the text in the shell input is changed
*/
onInputChanged?: (input: string) => void;

/* If set, the shell will omit or redact entries containing sensitive
* info from history. Defaults to `false`.
*/
Expand All @@ -85,6 +90,16 @@ interface ShellProps {
*/
onOperationEnd: () => void;

/**
* Initial value in the shell input field
*/
initialInput?: string;

/**
* A set of input strings to evaluate right after shell is mounted
*/
initialEvaluate?: string | string[];

/* An array of entries to be displayed in the output area.
*
* Can be used to restore the output between sessions, or to setup
Expand Down Expand Up @@ -128,6 +143,7 @@ export class Shell extends Component<ShellProps, ShellState> {
onOutputChanged: noop,
maxOutputLength: 1000,
maxHistoryLength: 1000,
initialInput: '',
initialOutput: [],
initialHistory: [],
};
Expand All @@ -147,8 +163,16 @@ export class Shell extends Component<ShellProps, ShellState> {

componentDidMount(): void {
this.scrollToBottom();
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.updateShellPrompt();
void this.updateShellPrompt().then(async () => {
if (this.props.initialEvaluate) {
const evalLines = Array.isArray(this.props.initialEvaluate)
? this.props.initialEvaluate
: [this.props.initialEvaluate];
for (const input of evalLines) {
await this.onInput(input);
}
}
});
}

componentDidUpdate(): void {
Expand Down Expand Up @@ -351,7 +375,7 @@ export class Shell extends Component<ShellProps, ShellState> {
this.editor = editor;
};

private focusEditor = (): void => {
focusEditor = (): void => {
this.editor?.focus();
};

Expand Down Expand Up @@ -379,6 +403,8 @@ export class Shell extends Component<ShellProps, ShellState> {

return (
<ShellInput
initialText={this.props.initialInput}
onTextChange={this.props.onInputChanged}
prompt={this.state.shellPrompt}
autocompleter={this.props.runtime}
history={this.state.history}
Expand Down
40 changes: 38 additions & 2 deletions packages/browser-repl/src/sandbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,8 @@ const IframeRuntimeExample: React.FunctionComponent = () => {
const [redactInfo, setRedactInfo] = useState(false);
const [maxOutputLength, setMaxOutputLength] = useState(1000);
const [maxHistoryLength, setMaxHistoryLength] = useState(1000);
const [initialInput, setInitialInput] = useState('');
const [initialEvaluate, setInitialEvaluate] = useState<string[]>([]);
const [initialOutput] = useState<ShellOutputEntry[]>([
{ format: 'output', value: { foo: 1, bar: true, buz: function () {} } },
]);
Expand All @@ -171,8 +173,8 @@ const IframeRuntimeExample: React.FunctionComponent = () => {
}, []);

const key = useMemo(() => {
return initialHistory.join('');
}, [initialHistory]);
return initialHistory.concat(initialInput, initialEvaluate).join('');
}, [initialHistory, initialInput, initialEvaluate]);

return (
<div className={sandboxContainer}>
Expand All @@ -183,6 +185,8 @@ const IframeRuntimeExample: React.FunctionComponent = () => {
redactInfo={redactInfo}
maxOutputLength={maxOutputLength}
maxHistoryLength={maxHistoryLength}
initialInput={initialInput}
initialEvaluate={initialEvaluate.filter(Boolean)}
initialOutput={initialOutput}
initialHistory={initialHistory.filter(Boolean)}
/>
Expand Down Expand Up @@ -263,6 +267,38 @@ const IframeRuntimeExample: React.FunctionComponent = () => {
className={cx(textarea, textInput)}
/>
</FormFieldContainer>

<FormFieldContainer className={formField}>
<Label id="initialInputLabel" htmlFor="initialInput">
initialInput
</Label>
<Description>Initial value in the shell input field</Description>
<TextInput
className={textInput}
aria-labelledby="initialInputLabel"
value={initialInput}
onChange={(evt) => {
setInitialInput(evt.currentTarget.value);
}}
/>
</FormFieldContainer>

<FormFieldContainer className={formField}>
<Label id="initialEvaluateLabel" htmlFor="initialEvaluate">
initialEvaluate
</Label>
<Description>
A set of input strings to evaluate right after shell is mounted
</Description>
<TextArea
aria-labelledby="initialEvaluate"
value={initialEvaluate.join('\n')}
onChange={(evt) => {
setInitialEvaluate(evt.currentTarget.value.split('\n'));
}}
className={cx(textarea, textInput)}
/>
</FormFieldContainer>
</div>
</div>
);
Expand Down