Skip to content

Commit

Permalink
Improved external API; fixes all around
Browse files Browse the repository at this point in the history
  • Loading branch information
riccardo-forina committed Nov 20, 2024
1 parent 173f937 commit b04c16c
Show file tree
Hide file tree
Showing 7 changed files with 115 additions and 89 deletions.
135 changes: 54 additions & 81 deletions packages/test-app/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@ import {
OpenApiEditorProps,
OpenApiEditorRef,
} from "@apicurio-editors/ui/src";
import { useMachine } from "@xstate/react";
import { Loading } from "../../ui/src/components/Loading.tsx";
import { appMachine } from "./AppMachine.ts";
import { SpecUploader } from "./components/SpecUploader";

import * as monaco from "monaco-editor";
Expand All @@ -28,8 +25,8 @@ import {
TextArea,
Title,
} from "@patternfly/react-core";
import { fromPromise } from "xstate";

// initialize Monaco's workers
self.MonacoEnvironment = {
getWorker(_, label) {
if (label === "json") {
Expand All @@ -50,20 +47,7 @@ self.MonacoEnvironment = {
loader.config({ monaco });

function App() {
const [state, send] = useMachine(
appMachine.provide({
actors: {
parseSpec: fromPromise(async ({ input }) => {
if (input.spec) {
await worker.parseOasSchema(input.spec);
return true;
}
return false;
}),
},
}),
{ input: { spec: undefined } }
);
const [spec, setSpec] = useState<string | null>(null);
const [captureChanges, setCaptureChanges] = useState(true);
const [output, setOutput] = useState("");

Expand All @@ -72,7 +56,7 @@ function App() {
const onDocumentChange: OpenApiEditorProps["onDocumentChange"] =
useCallback(() => {
console.log("DOCUMENT_CHANGE");
// this should be run in a debounce
// this should probably be run in a debounce
if (captureChanges && editorRef.current) {
editorRef.current.getDocumentAsYaml().then((v) => {
setOutput(v.substring(0, 1000));
Expand All @@ -84,78 +68,67 @@ function App() {
if (editorRef.current) {
const value = await editorRef.current.getDocumentAsYaml();
setOutput(value.substring(0, 1000));
editorRef.current.updateDocument(`{
setSpec(`{
"openapi": "3.0.3",
"info": {
"title": "Sample API"
}
}`);
// or, you could do
// editorRef.current.updateDocument(...newSpec...);
}
}, []);

switch (true) {
case state.matches("idle"):
return (
<SpecUploader
previousSpec={state.context.spec}
onSpec={(content) => send({ type: "SPEC", content })}
if (spec === null) {
return <SpecUploader onSpec={setSpec} />;
}
return (
<>
<PageSection
isFilled={true}
padding={{ default: "noPadding" }}
aria-label={"OpenApi designer"}
>
<OpenApiEditor
ref={editorRef}
spec={spec}
parseOpenApi={worker.parseOpenApi}
getEditorState={worker.getEditorState}
getDocumentRootSnapshot={worker.getDocumentRootSnapshot}
getPathSnapshot={worker.getPathSnapshot}
getDataTypeSnapshot={worker.getDataTypeSnapshot}
getResponseSnapshot={worker.getResponseSnapshot}
getNodeSource={worker.getNodeSource}
getDocumentNavigation={worker.getDocumentNavigation}
convertSource={worker.convertSource}
updateDocumentTitle={worker.updateDocumentTitle}
updateDocumentVersion={worker.updateDocumentVersion}
updateDocumentDescription={worker.updateDocumentDescription}
updateDocumentContactName={worker.updateDocumentContactName}
updateDocumentContactEmail={worker.updateDocumentContactEmail}
updateDocumentContactUrl={worker.updateDocumentContactUrl}
undoChange={worker.undoChange}
redoChange={worker.redoChange}
onDocumentChange={onDocumentChange}
/>
);
case state.matches("parsing"):
return <Loading />;
case state.matches("parsed"):
return (
<>
<PageSection
isFilled={true}
padding={{ default: "noPadding" }}
aria-label={"OpenApi designer"}
>
<OpenApiEditor
ref={editorRef}
getEditorState={worker.getEditorState}
getDocumentRootSnapshot={worker.getDocumentRootSnapshot}
getPathSnapshot={worker.getPathSnapshot}
getDataTypeSnapshot={worker.getDataTypeSnapshot}
getResponseSnapshot={worker.getResponseSnapshot}
getNodeSource={worker.getNodeSource}
getDocumentNavigation={worker.getDocumentNavigation}
convertSource={worker.convertSource}
updateDocumentTitle={worker.updateDocumentTitle}
updateDocumentVersion={worker.updateDocumentVersion}
updateDocumentDescription={worker.updateDocumentDescription}
updateDocumentContactName={worker.updateDocumentContactName}
updateDocumentContactEmail={worker.updateDocumentContactEmail}
updateDocumentContactUrl={worker.updateDocumentContactUrl}
undoChange={worker.undoChange}
redoChange={worker.redoChange}
onDocumentChange={onDocumentChange}
</PageSection>
<PageSection variant={"secondary"}>
<Flex>
<FlexItem>
<Button onClick={onSaveClick}>Save and update</Button>
</FlexItem>
<Title headingLevel={"h6"}>
<Switch
isChecked={captureChanges}
onChange={(_, v) => setCaptureChanges(v)}
label={"Listen to onDocumentChange events"}
/>
</PageSection>
<PageSection variant={"secondary"}>
<Flex>
<FlexItem>
<Button onClick={onSaveClick}>Save and update</Button>
</FlexItem>
<Title headingLevel={"h6"}>
<Switch
isChecked={captureChanges}
onChange={(_, v) => setCaptureChanges(v)}
label={"Listen to onDocumentChange events"}
/>
</Title>
<TextArea
aria-label="Output of the editor"
value={output}
rows={6}
/>
</Flex>
</PageSection>
</>
);
default:
return <>Unknown state: {state.value}</>;
}
</Title>
<TextArea aria-label="Output of the editor" value={output} rows={6} />
</Flex>
</PageSection>
</>
);
}

export default App;
2 changes: 1 addition & 1 deletion packages/test-app/src/AppMachine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ function Input(input: Input): Promise<Context> {
async function ParseSpec(input: Input): Promise<boolean> {
if (input.spec !== undefined) {
try {
await worker.parseOasSchema(input.spec);
await worker.parseOpenApi(input.spec);
return true;
} catch (e) {
console.error("ParseSpec", { e, input });
Expand Down
26 changes: 24 additions & 2 deletions packages/ui/src/OpenApiEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import { CodeEditor } from "./codeEditor/CodeEditor.tsx";
import {
ComponentProps,
forwardRef,
useEffect,
useImperativeHandle,
useLayoutEffect,
useRef,
Expand All @@ -64,6 +65,8 @@ const { inspect } = createBrowserInspector({
});

export type OpenApiEditorProps = {
spec: string;
parseOpenApi: (document: string) => Promise<void>;
getEditorState: (filter: string) => Promise<EditorModel>;
getDocumentRootSnapshot: () => Promise<DocumentRoot>;
getPathSnapshot: (path: NodePath) => Promise<DocumentPath>;
Expand Down Expand Up @@ -98,6 +101,8 @@ export interface OpenApiEditorRef {
export const OpenApiEditor = forwardRef<OpenApiEditorRef, OpenApiEditorProps>(
function OpenApiEditor(
{
spec,
parseOpenApi,
getEditorState,
getDocumentRootSnapshot,
getPathSnapshot,
Expand Down Expand Up @@ -189,6 +194,7 @@ export const OpenApiEditor = forwardRef<OpenApiEditorRef, OpenApiEditorProps>(

const editorLogic = OpenApiEditorMachine.provide({
actors: {
parseOpenApi: fromPromise(({ input }) => parseOpenApi(input)),
getEditorState: fromPromise(({ input }) => getEditorState(input)),
getDocumentNavigation: fromPromise(({ input }) =>
getDocumentNavigation(input)
Expand All @@ -209,7 +215,10 @@ export const OpenApiEditor = forwardRef<OpenApiEditorRef, OpenApiEditorProps>(
<OpenApiEditorMachineContext.Provider
logic={editorLogic}
options={{
inspect: document.location.host === "localhost" ? inspect : undefined,
input: {
spec,
},
inspect,
}}
>
<div
Expand All @@ -228,6 +237,7 @@ export const OpenApiEditor = forwardRef<OpenApiEditorRef, OpenApiEditorProps>(
>
<Editor
ref={ref}
spec={spec}
enableViewer={enableViewer}
enableDesigner={enableDesigner}
enableSource={enableSource}
Expand All @@ -245,6 +255,7 @@ export const OpenApiEditor = forwardRef<OpenApiEditorRef, OpenApiEditorProps>(
);

type EditorProps = {
spec: string;
enableViewer: boolean;
enableDesigner?: boolean;
enableSource: boolean;
Expand All @@ -254,6 +265,7 @@ type EditorProps = {

const Editor = forwardRef<OpenApiEditorRef, EditorProps>(function Editor(
{
spec,
enableViewer,
enableDesigner,
enableSource,
Expand All @@ -279,11 +291,21 @@ const Editor = forwardRef<OpenApiEditorRef, EditorProps>(function Editor(
}));
const actorRef = OpenApiEditorMachineContext.useActorRef();

const prevSpec = useRef(spec);
useEffect(() => {
if (prevSpec.current !== spec) {
actorRef.send({ type: "NEW_SPEC", spec });
prevSpec.current = spec;
}
}, [actorRef, spec]);

useImperativeHandle(
ref,
() => {
return {
updateDocument: (spec: string) => {},
updateDocument: (spec: string) => {
actorRef.send({ type: "NEW_SPEC", spec });
},
getDocumentAsYaml: () => {
return new Promise((resolve) => {
setTimeout(async () => {
Expand Down
32 changes: 31 additions & 1 deletion packages/ui/src/OpenApiEditorMachine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ type Context = EditorModel & {
type Events =
| {
readonly type: "xstate.init";
readonly input: {
readonly spec: string;
};
}
| {
readonly type: "NEW_SPEC";
readonly spec: string;
}
| {
readonly type: "SHOW_NAVIGATION";
Expand Down Expand Up @@ -156,8 +163,12 @@ export const OpenApiEditorMachine = setup({
types: {
context: {} as Context,
events: {} as Events,
input: {
spec: "" as string,
},
},
actors: {
parseOpenApi: fromPromise<void, string>(() => Promise.resolve()),
getEditorState: fromPromise<EditorModel, string>(() =>
Promise.resolve({} as EditorModel)
),
Expand Down Expand Up @@ -194,8 +205,26 @@ export const OpenApiEditorMachine = setup({
view: "visualize",
showNavigation: false,
},
initial: "viewChanged",
initial: "parsing",
states: {
parsing: {
invoke: {
src: "parseOpenApi",
input: ({ event }) => {
console.log("parseOpenApi actor", event);
if (event.type === "xstate.init" && event.input.spec) {
return event.input.spec;
}
if (event.type === "NEW_SPEC") {
return event.spec;
}
throw new Error("Invalid event type");
},
onDone: "viewChanged",
onError: "error",
},
},
error: {},
idle: {},
saving: {
after: {
Expand Down Expand Up @@ -521,5 +550,6 @@ export const OpenApiEditorMachine = setup({
showNavigation: false,
}),
},
NEW_SPEC: ".parsing",
},
});
3 changes: 2 additions & 1 deletion packages/ui/src/OpenApiEditorWorker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,8 @@ function securitySchemes(): DM.SecurityScheme[] {
}
}

export async function parseOasSchema(schema: string) {
export async function parseOpenApi(schema: string) {
console.log("parseOpenApi", { schema });
try {
document = DM.Library.readDocumentFromJSONString(schema) as DM.OasDocument;
otEngine = new DM.OtEngine(document);
Expand Down
4 changes: 2 additions & 2 deletions packages/ui/src/components/Toc.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
width: 100%;
}

@media (min-width: 1451px) {
@media (min-width: 993px) {
.content.content.content {
max-width: calc(100% - var(--toc-width));
}
Expand All @@ -28,7 +28,7 @@
box-shadow: var(--pf-t--global--box-shadow--lg--bottom);
}

@media (min-width: 1451px) {
@media (min-width: 993px) {
.toc.toc.toc, .toc:global(.pf-m-expanded) {
box-shadow: none;
}
Expand Down
2 changes: 1 addition & 1 deletion packages/ui/src/components/Toc.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export function Toc({ children }: PropsWithChildren) {
className={classes.toc}
scrollableSelector={".apicurio-editor .pf-v6-c-drawer__panel-main"}
isVertical={true}
expandable={{ default: "expandable", "2xl": "nonExpandable" }}
expandable={{ default: "expandable", lg: "nonExpandable" }}
label={"Table of contents"}
offset={60}
style={{ top: 0 }}
Expand Down

0 comments on commit b04c16c

Please sign in to comment.