Skip to content
Open
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
11 changes: 11 additions & 0 deletions .chronus/changes/playground-mobile-2026-2-13-15-5-48.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
# Change versionKind to one of: internal, fix, dependencies, feature, deprecation, breaking
changeKind: feature
packages:
- "@typespec/playground"
---

Make UI more mobile friendly.
- Add a new switch to toggle between TypeSpec and output panels
- Command bar hides less important tools behind `...`
- [API] Update custom toolbar to take a menu item instead of generic react node.
80 changes: 39 additions & 41 deletions packages/playground-website/src/import.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,6 @@ import {
DialogTitle,
Input,
Label,
Menu,
MenuItem,
MenuList,
MenuPopover,
MenuTrigger,
ToolbarButton,
Tooltip,
} from "@fluentui/react-components";
import { ArrowUploadFilled } from "@fluentui/react-icons";
import { combineProjectIntoFile, createRemoteHost } from "@typespec/pack";
Expand All @@ -22,46 +15,51 @@ import {
Editor,
useMonacoModel,
usePlaygroundContext,
type CommandBarItem,
} from "@typespec/playground/react";
import { ReactNode, useState } from "react";
import { useState, type FunctionComponent, type ReactNode } from "react";
import { parse } from "yaml";
import style from "./import.module.css";

export const ImportToolbarButton = () => {
const [open, setOpen] = useState<"openapi3" | "tsp" | undefined>();
type ImportType = "openapi3" | "tsp";

return (
<>
<Menu>
<MenuTrigger disableButtonEnhancement>
<Tooltip content="Import" relationship="description" withArrow>
<ToolbarButton
appearance="subtle"
aria-label="File Bug Report"
icon={<ArrowUploadFilled />}
/>
</Tooltip>
</MenuTrigger>
<MenuPopover>
<MenuList>
<MenuItem onClick={() => setOpen("tsp")}>Remote TypeSpec</MenuItem>
<MenuItem onClick={() => setOpen("openapi3")}>From OpenAPI 3 spec</MenuItem>
</MenuList>
</MenuPopover>
</Menu>
/** Hook that creates a CommandBarItem for the Import action with sub-menu items. */
export function useImportCommandBarItem(): CommandBarItem {
const [open, setOpen] = useState<ImportType>();

return {
id: "import",
label: "Import",
icon: <ArrowUploadFilled />,
align: "right",
children: [
{ id: "import-tsp", label: "Remote TypeSpec", onClick: () => setOpen("tsp") },
{
id: "import-openapi3",
label: "From OpenAPI 3 spec",
onClick: () => setOpen("openapi3"),
},
],
content: <ImportDialog open={open} onClose={() => setOpen(undefined)} />,
};
}

<Dialog open={open !== undefined} onOpenChange={(event, data) => setOpen(undefined)}>
<DialogSurface>
<DialogBody>
<DialogTitle>Settings</DialogTitle>
<DialogContent>
{open === "openapi3" && <ImportOpenAPI3 onImport={() => setOpen(undefined)} />}
{open === "tsp" && <ImportTsp onImport={() => setOpen(undefined)} />}
</DialogContent>
</DialogBody>
</DialogSurface>
</Dialog>
</>
const ImportDialog: FunctionComponent<{
open: "openapi3" | "tsp" | undefined;
onClose: () => void;
}> = ({ open, onClose }) => {
return (
<Dialog open={open !== undefined} onOpenChange={() => onClose()}>
<DialogSurface>
<DialogBody>
<DialogTitle>Settings</DialogTitle>
<DialogContent>
{open === "openapi3" && <ImportOpenAPI3 onImport={onClose} />}
{open === "tsp" && <ImportTsp onImport={onClose} />}
</DialogContent>
</DialogBody>
</DialogSurface>
</Dialog>
);
};

Expand Down
2 changes: 1 addition & 1 deletion packages/playground-website/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export { TypeSpecPlaygroundConfig } from "./config.js";
export { ImportToolbarButton } from "./import.js";
export { useImportCommandBarItem } from "./import.js";
51 changes: 32 additions & 19 deletions packages/playground-website/src/main.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import { FluentProvider, webLightTheme } from "@fluentui/react-components";
import { MANIFEST } from "@typespec/compiler";
import { registerMonacoDefaultWorkersForVite } from "@typespec/playground";
import PlaygroundManifest from "@typespec/playground/manifest";
import {
Footer,
FooterItem,
FooterVersionItem,
renderReactPlayground,
StandalonePlayground,
} from "@typespec/playground/react";
import { SwaggerUIViewer } from "@typespec/playground/react/viewers";
import "@typespec/playground/styles.css";
import { createRoot } from "react-dom/client";
import samples from "../samples/dist/samples.js";
import { ImportToolbarButton } from "./import.js";
import { useImportCommandBarItem } from "./import.js";
import "./style.css";

registerMonacoDefaultWorkersForVite();
Expand Down Expand Up @@ -42,20 +44,31 @@ const PlaygroundFooter = () => {
);
};

await renderReactPlayground({
...PlaygroundManifest,
samples,
emitterViewers: {
"@typespec/openapi3": [SwaggerUIViewer],
},
importConfig: {
useShim: true,
},
footer: <PlaygroundFooter />,
commandBarButtons: <ImportToolbarButton />,
onFileBug: () => {
const bodyPayload = encodeURIComponent(`\n\n\n[Playground Link](${document.location.href})`);
const url = `https://github.com/microsoft/typespec/issues/new?body=${bodyPayload}`;
window.open(url, "_blank");
},
});
const onFileBug = () => {
const bodyPayload = encodeURIComponent(`\n\n\n[Playground Link](${document.location.href})`);
const url = `https://github.com/microsoft/typespec/issues/new?body=${bodyPayload}`;
window.open(url, "_blank");
};

const App = () => {
const importItem = useImportCommandBarItem();

return (
<StandalonePlayground
{...PlaygroundManifest}
samples={samples}
emitterViewers={{ "@typespec/openapi3": [SwaggerUIViewer] }}
importConfig={{ useShim: true }}
footer={<PlaygroundFooter />}
commandBarItems={[importItem]}
onFileBug={onFileBug}
/>
);
};

const root = createRoot(document.getElementById("root")!);
root.render(
<FluentProvider theme={webLightTheme} style={{ height: "100vh" }}>
<App />
</FluentProvider>,
);
10 changes: 7 additions & 3 deletions packages/playground/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@
"build:storybook": "sb build",
"preview": "pnpm build && vite preview",
"start": "vite",
"test": "echo 'no tests'",
"test:ci": "echo 'no tests'",
"test": "vitest run",
"test:ci": "vitest run --coverage --reporter=junit --reporter=default",
"lint": "eslint . --max-warnings=0",
"lint:fix": "eslint . --fix"
},
Expand Down Expand Up @@ -100,6 +100,9 @@
"@playwright/test": "^1.57.0",
"@storybook/cli": "^10.1.8",
"@storybook/react-vite": "^10.1.8",
"@testing-library/dom": "^10.4.0",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.2.0",
"@types/debounce": "~1.2.4",
"@types/node": "~25.3.0",
"@types/react": "~19.2.7",
Expand All @@ -116,6 +119,7 @@
"typescript": "~5.9.3",
"vite": "^7.2.7",
"vite-plugin-checker": "^0.12.0",
"vite-plugin-dts": "4.5.4"
"vite-plugin-dts": "4.5.4",
"vitest": "^4.0.18"
}
}
Loading
Loading