Respond to PR comments

This commit is contained in:
Sam Nolan 2022-07-05 17:32:02 +10:00
parent df3608d9b2
commit c4d2ad922a
6 changed files with 61 additions and 40 deletions

View File

@ -20,7 +20,7 @@ Add to `App.js`:
```jsx ```jsx
import { SquiggleEditor } from "@quri/squiggle-components"; import { SquiggleEditor } from "@quri/squiggle-components";
<SquiggleEditor <SquiggleEditor
squiggleString="x = beta($alpha, 10); x + $shift" defaultCode="x = beta($alpha, 10); x + $shift"
jsImports={{ alpha: 3, shift: 20 }} jsImports={{ alpha: 3, shift: 20 }}
/>; />;
``` ```
@ -50,7 +50,7 @@ export function DynamicSquiggleChart({ squiggleString }) {
} else { } else {
return ( return (
<SquiggleChart <SquiggleChart
squiggleString={squiggleString} defaultCode={squiggleString}
width={445} width={445}
height={200} height={200}
showSummary={true} showSummary={true}

View File

@ -1,10 +1,10 @@
import React, { useState } from "react"; import React from "react";
import { CodeEditor } from "./CodeEditor"; import { CodeEditor } from "./CodeEditor";
import { environment, bindings, jsImports } from "@quri/squiggle-lang"; import { environment, bindings, jsImports } from "@quri/squiggle-lang";
import { defaultImports, defaultBindings } from "@quri/squiggle-lang"; import { defaultImports, defaultBindings } from "@quri/squiggle-lang";
import { SquiggleContainer } from "./SquiggleContainer"; import { SquiggleContainer } from "./SquiggleContainer";
import { SquiggleChart, SquiggleChartProps } from "./SquiggleChart"; import { SquiggleChart, SquiggleChartProps } from "./SquiggleChart";
import { useSquigglePartial } from "../lib/hooks"; import { useSquigglePartial, useMaybeControlledValue } from "../lib/hooks";
import { SquiggleErrorAlert } from "./SquiggleErrorAlert"; import { SquiggleErrorAlert } from "./SquiggleErrorAlert";
const WrappedCodeEditor: React.FC<{ const WrappedCodeEditor: React.FC<{
@ -28,21 +28,16 @@ export type SquiggleEditorProps = SquiggleChartProps & {
}; };
export const SquiggleEditor: React.FC<SquiggleEditorProps> = (props) => { export const SquiggleEditor: React.FC<SquiggleEditorProps> = (props) => {
let defaultCode = props.defaultCode ?? ""; const [code, setCode] = useMaybeControlledValue({
const [uncontrolledCode, setCode] = useState(defaultCode); value: props.code,
let code = props.code ?? uncontrolledCode; defaultValue: props.defaultCode,
onChange: props.onCodeChange,
});
let chartProps = { ...props, code }; let chartProps = { ...props, code };
return ( return (
<SquiggleContainer> <SquiggleContainer>
<WrappedCodeEditor <WrappedCodeEditor code={code} setCode={setCode} />
code={code}
setCode={(code) => {
if (props.onCodeChange) props.onCodeChange(code);
if (props.code === undefined) setCode(code);
}}
/>
<SquiggleChart {...chartProps} /> <SquiggleChart {...chartProps} />
</SquiggleContainer> </SquiggleContainer>
); );
@ -66,7 +61,7 @@ export interface SquigglePartialProps {
} }
export const SquigglePartial: React.FC<SquigglePartialProps> = ({ export const SquigglePartial: React.FC<SquigglePartialProps> = ({
code, code: controlledCode,
defaultCode = "", defaultCode = "",
onChange, onChange,
onCodeChange, onCodeChange,
@ -74,11 +69,14 @@ export const SquigglePartial: React.FC<SquigglePartialProps> = ({
environment, environment,
jsImports = defaultImports, jsImports = defaultImports,
}: SquigglePartialProps) => { }: SquigglePartialProps) => {
const [uncontrolledCode, setCode] = useState(defaultCode); const [code, setCode] = useMaybeControlledValue<string>({
let codeProp = code ?? uncontrolledCode; value: controlledCode,
defaultValue: defaultCode,
onChange: onCodeChange,
});
const result = useSquigglePartial({ const result = useSquigglePartial({
code: codeProp, code,
bindings, bindings,
environment, environment,
jsImports, jsImports,
@ -87,14 +85,7 @@ export const SquigglePartial: React.FC<SquigglePartialProps> = ({
return ( return (
<SquiggleContainer> <SquiggleContainer>
<WrappedCodeEditor <WrappedCodeEditor code={code} setCode={setCode} />
code={codeProp}
setCode={(code) => {
if (onCodeChange) onCodeChange(code);
if (code === undefined) setCode(code);
}}
/>
{result.tag !== "Ok" ? <SquiggleErrorAlert error={result.value} /> : null} {result.tag !== "Ok" ? <SquiggleErrorAlert error={result.value} /> : null}
</SquiggleContainer> </SquiggleContainer>
); );

View File

@ -1,6 +1,7 @@
import React, { FC, Fragment, useState, useEffect } from "react"; import React, { FC, Fragment, useState, useEffect } from "react";
import { Path, useForm, UseFormRegister, useWatch } from "react-hook-form"; import { Path, useForm, UseFormRegister, useWatch } from "react-hook-form";
import * as yup from "yup"; import * as yup from "yup";
import { useMaybeControlledValue } from "../lib/hooks";
import { yupResolver } from "@hookform/resolvers/yup"; import { yupResolver } from "@hookform/resolvers/yup";
import { Tab } from "@headlessui/react"; import { Tab } from "@headlessui/react";
import { import {
@ -216,7 +217,11 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
onSettingsChange, onSettingsChange,
showEditor = true, showEditor = true,
}) => { }) => {
const [uncontrolledCode, setUncontrolledCode] = useState(defaultCode); const [code, setCode] = useMaybeControlledValue({
value: controlledCode,
defaultValue: defaultCode,
onChange: onCodeChange,
});
const [importString, setImportString] = useState("{}"); const [importString, setImportString] = useState("{}");
const [imports, setImports] = useState({}); const [imports, setImports] = useState({});
const [importsAreValid, setImportsAreValid] = useState(true); const [importsAreValid, setImportsAreValid] = useState(true);
@ -261,8 +266,6 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
} }
}; };
const code = controlledCode ?? uncontrolledCode;
const samplingSettings = ( const samplingSettings = (
<div className="space-y-6 p-3 max-w-xl"> <div className="space-y-6 p-3 max-w-xl">
<div> <div>
@ -434,14 +437,8 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
const firstTab = vars.showEditor ? ( const firstTab = vars.showEditor ? (
<div className="border border-slate-200"> <div className="border border-slate-200">
<CodeEditor <CodeEditor
value={code} value={code ?? ""}
onChange={(newCode) => { onChange={setCode}
if (controlledCode === undefined) {
// uncontrolled mode
setUncontrolledCode(newCode);
}
onCodeChange?.(newCode);
}}
oneLine={false} oneLine={false}
showGutter={true} showGutter={true}
height={height - 1} height={height - 1}

View File

@ -5,7 +5,7 @@ import {
run, run,
runPartial, runPartial,
} from "@quri/squiggle-lang"; } from "@quri/squiggle-lang";
import { useEffect, useMemo } from "react"; import { useEffect, useMemo, useState } from "react";
type SquiggleArgs<T extends ReturnType<typeof run | typeof runPartial>> = { type SquiggleArgs<T extends ReturnType<typeof run | typeof runPartial>> = {
code: string; code: string;
@ -42,3 +42,23 @@ export const useSquigglePartial = (
export const useSquiggle = (args: SquiggleArgs<ReturnType<typeof run>>) => { export const useSquiggle = (args: SquiggleArgs<ReturnType<typeof run>>) => {
return useSquiggleAny(args, run); return useSquiggleAny(args, run);
}; };
type ControlledValueArgs<T> = {
value?: T;
defaultValue: T;
onChange?: (x: T) => void;
};
export function useMaybeControlledValue<T>(
args: ControlledValueArgs<T>
): [T, (x: T) => void] {
let [uncontrolledValue, setUncontrolledValue] = useState(args.defaultValue);
let value = args.value ?? uncontrolledValue;
let onChange = (newValue: T) => {
if (args.value === undefined) {
// uncontrolled mode
setUncontrolledValue(newValue);
}
args.onChange?.(newValue);
};
return [value, onChange];
}

View File

@ -21,6 +21,19 @@ the distribution.
</Story> </Story>
</Canvas> </Canvas>
It's also possible to create a controlled version of the same component
<Canvas>
<Story
name="Controlled"
args={{
code: "normal(5,2)",
}}
>
{Template.bind({})}
</Story>
</Canvas>
You can also name variables like so: You can also name variables like so:
<Canvas> <Canvas>

View File

@ -14,7 +14,7 @@ including sampling settings, in squiggle.
<Story <Story
name="Normal" name="Normal"
args={{ args={{
initialSquiggleString: "normal(5,2)", defaultCode: "normal(5,2)",
height: 800, height: 800,
}} }}
> >