Skip to content

Commit

Permalink
Fix crash in program viewer when trying to display new value type (#3585
Browse files Browse the repository at this point in the history
)

fix Azure/typespec-azure#1016
### Fix crash in the program viewer
When we were trying to display a value(only case right now is using a
default value as property) it would blow up

### Add error boundary to only crash the output view and ability to
retry so the entire UI is not lost

![image](https://github.com/microsoft/typespec/assets/1031227/d856c8c9-41d9-4c86-9c6b-432e60a12d53)
  • Loading branch information
timotheeguerin authored Jun 14, 2024
1 parent d319e33 commit 216f423
Show file tree
Hide file tree
Showing 7 changed files with 68 additions and 11 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
# Change versionKind to one of: internal, fix, dependencies, feature, deprecation, breaking
changeKind: feature
packages:
- "@typespec/playground"
---

Add error recovery for viewer that crash
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
# Change versionKind to one of: internal, fix, dependencies, feature, deprecation, breaking
changeKind: fix
packages:
- "@typespec/html-program-viewer"
---

Fix crash in program viewer when trying to display new value type
22 changes: 16 additions & 6 deletions packages/html-program-viewer/src/ui.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { css } from "@emotion/react";
import {
Entity,
Enum,
EnumMember,
getNamespaceFullName,
Expand Down Expand Up @@ -132,12 +133,12 @@ const NamedTypeUI = <T extends NamedType>({ type, name, properties }: NamedTypeU
return undefined;
}

const render = (x: any) =>
action === "ref" ? <TypeReference type={value} /> : <TypeUI type={x} />;
const render = (x: Entity) =>
action === "ref" ? <TypeReference type={value} /> : <EntityUI entity={x} />;
let valueUI;
if (value === undefined) {
valueUI = value;
} else if (value.kind) {
} else if (value.entityKind) {
valueUI = render(value);
} else if (
typeof value === "object" &&
Expand All @@ -160,10 +161,19 @@ const NamedTypeUI = <T extends NamedType>({ type, name, properties }: NamedTypeU
};

interface TypeUIProps {
type: Type;
readonly entity: Entity;
}

const TypeUI: FunctionComponent<TypeUIProps> = ({ type }) => {
const EntityUI: FunctionComponent<TypeUIProps> = ({ entity }) => {
switch (entity.entityKind) {
case "Type":
return <TypeUI type={entity} />;
default:
return null;
}
};

const TypeUI: FunctionComponent<{ type: Type }> = ({ type }) => {
switch (type.kind) {
case "Namespace":
return <NamespaceUI type={type} />;
Expand Down Expand Up @@ -379,7 +389,7 @@ const TypeReference: FunctionComponent<{ type: Type }> = ({ type }) => {
if (type.name === "") {
return (
<KeyValueSection>
<TypeUI type={type} />
<EntityUI entity={type} />
</KeyValueSection>
);
} else {
Expand Down
1 change: 1 addition & 0 deletions packages/playground/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
"monaco-editor": "~0.46.0",
"react": "~18.3.1",
"react-dom": "~18.3.1",
"react-error-boundary": "^4.0.13",
"swagger-ui-dist": "^5.17.10",
"vscode-languageserver": "~9.0.1",
"vscode-languageserver-textdocument": "~1.0.11"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,7 @@
.viewer-tabs-container {
background-color: var(--colorNeutralBackground3);
}

.viewer-error {
padding: 20px;
}
23 changes: 18 additions & 5 deletions packages/playground/src/react/output-view/output-view.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Tab, TabList, type SelectTabEventHandler } from "@fluentui/react-components";
import { Button, Tab, TabList, type SelectTabEventHandler } from "@fluentui/react-components";
import { useCallback, useMemo, useState, type FunctionComponent } from "react";
import { ErrorBoundary, type FallbackProps } from "react-error-boundary";
import type { PlaygroundEditorsOptions } from "../playground.js";
import type { CompilationState, CompileResult, FileOutputViewer, ProgramViewer } from "../types.js";
import { createFileViewer } from "./file-viewer.js";
Expand Down Expand Up @@ -81,10 +82,12 @@ const OutputViewInternal: FunctionComponent<{
return (
<div className={style["output-view"]}>
<div className={style["output-content"]}>
<viewer.render
program={compilationResult.program}
outputFiles={compilationResult.outputFiles}
/>
<ErrorBoundary fallbackRender={fallbackRender}>
<viewer.render
program={compilationResult.program}
outputFiles={compilationResult.outputFiles}
/>
</ErrorBoundary>
</div>
<div className={style["viewer-tabs-container"]}>
<TabList
Expand All @@ -106,3 +109,13 @@ const OutputViewInternal: FunctionComponent<{
</div>
);
};

function fallbackRender({ error, resetErrorBoundary }: FallbackProps) {
return (
<div role="alert" className={style["viewer-error"]}>
<h2>Something went wrong:</h2>
<div style={{ color: "red" }}>{error.toString()}</div>
<Button onClick={resetErrorBoundary}>Try again</Button>
</div>
);
}
13 changes: 13 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 216f423

Please sign in to comment.