Compare commits

...

4 Commits

Author SHA1 Message Date
Vyacheslav Matyukhin
9e2eace05e
Merge pull request #1231 from quantified-uncertainty/project-in-editors
Project in editors and remove warnings
2022-10-14 18:06:49 +03:00
Vyacheslav Matyukhin
a0000cd179
Merge branch 'develop' into project-in-editors 2022-10-13 03:36:08 +04:00
Sam Nolan
98454a87b5 Get projects working in Playgrounds 2022-10-10 14:23:04 +11:00
Sam Nolan
0f8e7ce6b6 Project in editors and remove warnings 2022-10-08 16:23:58 +11:00
8 changed files with 89 additions and 51 deletions

View File

@ -24,7 +24,7 @@ export const Alert: React.FC<{
children, children,
}) => { }) => {
return ( return (
<div className={clsx("rounded-md p-4", backgroundColor)}> <div className={clsx("rounded-md p-4", backgroundColor)} role="status">
<div className="flex"> <div className="flex">
<Icon <Icon
className={clsx("h-5 w-5 flex-shrink-0", iconColor)} className={clsx("h-5 w-5 flex-shrink-0", iconColor)}

View File

@ -55,10 +55,7 @@ export const CodeEditor: FC<CodeEditorProps> = ({
editorProps={{ editorProps={{
$blockScrolling: true, $blockScrolling: true,
}} }}
setOptions={{ setOptions={{}}
enableBasicAutocompletion: false,
enableLiveAutocompletion: false,
}}
commands={[ commands={[
{ {
name: "submit", name: "submit",

View File

@ -1,5 +1,10 @@
import * as React from "react"; import * as React from "react";
import { SqValue, environment, SqProject } from "@quri/squiggle-lang"; import {
SqValue,
environment,
SqProject,
defaultEnvironment,
} from "@quri/squiggle-lang";
import { useSquiggle } from "../lib/hooks"; import { useSquiggle } from "../lib/hooks";
import { SquiggleViewer } from "./SquiggleViewer"; import { SquiggleViewer } from "./SquiggleViewer";
import { JsImports } from "../lib/jsImports"; import { JsImports } from "../lib/jsImports";
@ -66,7 +71,6 @@ type ProjectExecutionProps = {
}; };
const defaultOnChange = () => {}; const defaultOnChange = () => {};
const defaultImports: JsImports = {}; const defaultImports: JsImports = {};
const defaultContinues: string[] = [];
export const splitSquiggleChartSettings = (props: SquiggleChartProps) => { export const splitSquiggleChartSettings = (props: SquiggleChartProps) => {
const { const {
@ -120,24 +124,15 @@ export const SquiggleChart: React.FC<SquiggleChartProps> = React.memo(
width, width,
height = 200, height = 200,
enableLocalSettings = false, enableLocalSettings = false,
continues = defaultContinues, continues,
project,
environment,
} = props; } = props;
const p = React.useMemo(() => {
if (props.project) {
return props.project;
} else {
const p = SqProject.create();
if (props.environment) {
p.setEnvironment(props.environment);
}
return p;
}
}, [props.project, props.environment]);
const resultAndBindings = useSquiggle({ const resultAndBindings = useSquiggle({
environment,
continues, continues,
project: p, project,
code, code,
jsImports, jsImports,
onChange, onChange,
@ -153,7 +148,9 @@ export const SquiggleChart: React.FC<SquiggleChartProps> = React.memo(
height={height} height={height}
distributionPlotSettings={distributionPlotSettings} distributionPlotSettings={distributionPlotSettings}
chartSettings={chartSettings} chartSettings={chartSettings}
environment={p.getEnvironment()} environment={
project ? project.getEnvironment() : environment ?? defaultEnvironment
}
enableLocalSettings={enableLocalSettings} enableLocalSettings={enableLocalSettings}
/> />
); );

View File

@ -54,17 +54,13 @@ export const SquiggleEditor: React.FC<SquiggleEditorProps> = (props) => {
width, width,
height = 200, height = 200,
enableLocalSettings = false, enableLocalSettings = false,
continues,
project,
} = props; } = props;
const project = React.useMemo(() => {
const p = SqProject.create();
if (environment) {
p.setEnvironment(environment);
}
return p;
}, [environment]);
const resultAndBindings = useSquiggle({ const resultAndBindings = useSquiggle({
environment,
continues,
code, code,
project, project,
jsImports, jsImports,

View File

@ -251,6 +251,8 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
onSettingsChange, onSettingsChange,
showEditor = true, showEditor = true,
showShareButton = false, showShareButton = false,
continues,
project,
}) => { }) => {
const [code, setCode] = useMaybeControlledValue({ const [code, setCode] = useMaybeControlledValue({
value: controlledCode, value: controlledCode,
@ -305,15 +307,9 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
executionId, executionId,
} = useRunnerState(code); } = useRunnerState(code);
const project = React.useMemo(() => {
const p = SqProject.create();
if (environment) {
p.setEnvironment(environment);
}
return p;
}, [environment]);
const resultAndBindings = useSquiggle({ const resultAndBindings = useSquiggle({
environment,
continues,
code: renderedCode, code: renderedCode,
project, project,
jsImports: imports, jsImports: imports,

View File

@ -45,7 +45,7 @@ export const VariableBox: React.FC<VariableBoxProps> = ({
: location.path.items[location.path.items.length - 1]; : location.path.items[location.path.items.length - 1];
return ( return (
<div> <div role={isTopLevel ? "status" : undefined}>
<header className="inline-flex space-x-1"> <header className="inline-flex space-x-1">
<Tooltip text={heading}> <Tooltip text={heading}>
<span <span

View File

@ -4,16 +4,18 @@ import {
SqProject, SqProject,
SqRecord, SqRecord,
SqValue, SqValue,
environment,
} from "@quri/squiggle-lang"; } from "@quri/squiggle-lang";
import { useEffect, useMemo } from "react"; import { useEffect, useMemo } from "react";
import { JsImports, jsImportsToSquiggleCode } from "../jsImports"; import { JsImports, jsImportsToSquiggleCode } from "../jsImports";
import * as uuid from "uuid"; import * as uuid from "uuid";
type SquiggleArgs = { type SquiggleArgs = {
environment?: environment;
code: string; code: string;
executionId?: number; executionId?: number;
jsImports?: JsImports; jsImports?: JsImports;
project: SqProject; project?: SqProject;
continues?: string[]; continues?: string[];
onChange?: (expr: SqValue | undefined, sourceName: string) => void; onChange?: (expr: SqValue | undefined, sourceName: string) => void;
}; };
@ -27,15 +29,25 @@ const importSourceName = (sourceName: string) => "imports-" + sourceName;
const defaultContinues = []; const defaultContinues = [];
export const useSquiggle = (args: SquiggleArgs): ResultAndBindings => { export const useSquiggle = (args: SquiggleArgs): ResultAndBindings => {
const project = useMemo(() => {
if (args.project) {
return args.project;
} else {
const p = SqProject.create();
if (args.environment) {
p.setEnvironment(args.environment);
}
return p;
}
}, [args.project, args.environment]);
const sourceName = useMemo(() => uuid.v4(), []); const sourceName = useMemo(() => uuid.v4(), []);
const env = args.project.getEnvironment(); const env = project.getEnvironment();
const continues = args.continues || defaultContinues; const continues = args.continues || defaultContinues;
const result = useMemo( const result = useMemo(
() => { () => {
const project = args.project;
project.setSource(sourceName, args.code); project.setSource(sourceName, args.code);
let fullContinues = continues; let fullContinues = continues;
if (args.jsImports && Object.keys(args.jsImports).length) { if (args.jsImports && Object.keys(args.jsImports).length) {
@ -59,7 +71,7 @@ export const useSquiggle = (args: SquiggleArgs): ResultAndBindings => {
args.executionId, args.executionId,
sourceName, sourceName,
continues, continues,
args.project, project,
env, env,
] ]
); );
@ -75,11 +87,11 @@ export const useSquiggle = (args: SquiggleArgs): ResultAndBindings => {
useEffect(() => { useEffect(() => {
return () => { return () => {
args.project.removeSource(sourceName); project.removeSource(sourceName);
if (args.project.getSource(importSourceName(sourceName))) if (project.getSource(importSourceName(sourceName)))
args.project.removeSource(importSourceName(sourceName)); project.removeSource(importSourceName(sourceName));
}; };
}, [args.project, sourceName]); }, [project, sourceName]);
return result; return result;
}; };

View File

@ -1,9 +1,14 @@
import { render } from "@testing-library/react"; import { render, screen } from "@testing-library/react";
import React from "react"; import React from "react";
import "@testing-library/jest-dom"; import "@testing-library/jest-dom";
import { SquiggleChart } from "../src/index"; import {
SquiggleChart,
SquiggleEditor,
SquigglePlayground,
} from "../src/index";
import { SqProject } from "@quri/squiggle-lang";
test("Logs nothing on render", async () => { test("Chart logs nothing on render", async () => {
const { unmount } = render(<SquiggleChart code={"normal(0, 1)"} />); const { unmount } = render(<SquiggleChart code={"normal(0, 1)"} />);
unmount(); unmount();
@ -11,3 +16,38 @@ test("Logs nothing on render", async () => {
expect(console.warn).not.toBeCalled(); expect(console.warn).not.toBeCalled();
expect(console.error).not.toBeCalled(); expect(console.error).not.toBeCalled();
}); });
test("Editor logs nothing on render", async () => {
const { unmount } = render(<SquiggleEditor code={"normal(0, 1)"} />);
unmount();
expect(console.log).not.toBeCalled();
expect(console.warn).not.toBeCalled();
expect(console.error).not.toBeCalled();
});
test("Project dependencies work in editors", async () => {
const project = SqProject.create();
render(<SquiggleEditor code={"x = 1"} project={project} />);
const source = project.getSourceIds()[0];
const { container } = render(
<SquiggleEditor code={"x + 1"} project={project} continues={[source]} />
);
expect(container).toHaveTextContent("2");
});
test("Project dependencies work in playgrounds", async () => {
const project = SqProject.create();
project.setSource("depend", "x = 1");
render(
<SquigglePlayground
code={"x + 1"}
project={project}
continues={["depend"]}
/>
);
// We must await here because SquigglePlayground loads results asynchronously
expect(await screen.findByRole("status")).toHaveTextContent("2");
});