⚠ This page is served via a proxy. Original site: https://github.com
This service does not collect credentials or authentication data.
Skip to content
Draft
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
15 changes: 12 additions & 3 deletions package-lock.json

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

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"release": "changeset publish"
},
"dependencies": {
"lz-string": "^1.5.0",
"react-responsive": "^10.0.0",
"react-syntax-highlighter": "^15.5.0"
},
Expand All @@ -44,6 +45,7 @@
"@testing-library/react": "^13.0.0",
"@testing-library/user-event": "^13.2.1",
"@types/jest": "^27.0.1",
"@types/lz-string": "^1.3.34",
"@types/node": "^20",
"@types/react": "^18.3.0",
"@types/react-dom": "^18.3.0",
Expand Down
11 changes: 10 additions & 1 deletion src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
import { TypeScriptPlayground } from "@/library/TypeScriptPlayground/TypeScriptPlayground";
import "@/library/GithubPermalink/github-permalink.css";

export default function Home() {
return (
<main>
<div >
<h1>React Github Permalink</h1>

<p>This library now supports TypeScript Playground links!</p>

See the <a href="/storybook-static/index.html">Storybook</a>
<h2>Example: TypeScript Playground</h2>
<TypeScriptPlayground playgroundUrl="https://www.typescriptlang.org/play/?ssl=44&ssc=1&pln=8&pc=1#code/C4TwDgpgBASg9gdwCIENgqgXlhAxnAJwBMAeAZ2AIEsA7AcwBooBXGgaxsRoD4oAyKAG8oVIgC4oFavSgBfANwAoRaEhQAwnAA2zALY1U6EgBV4yNBggAPYBBpEysRIZRNjAaQggo12-cdsXnAAZlCmzha82IKKUHFQgSASHl6x8QR2RBAELhIAFABuKDoQyWYuANopIAC6AJRYvFK0dEqyysGsuMBUcDRQYARwuBBkZMYoAEZaECblFj42mY7z6G6e3r7LCUGh4ebo3HlDCBIAggQEKCBzEYdM+Dr655fXJJpPBha3B65hG9xuA0Yop2spBsNRuMpjM8mk4hUYvFkSJxFAAEQARgATABmdEMeEolASTGElHxSYSJEUlFWUnk2nIpJQbFE5HtDk1JiI9nxRISdEoAl8uIZezZXJQQrFZgQBqYXg0plxAD0qulYBQV10EFsBAaRRKEhoekm2SgAB8hKKUeq6SazdklCr4vbmY7dOaCC7XVB7Zy-RlgMwCP0AAYAEkERrlsnDvopgbiskZcWVKIFGMmIqZ4qyOQs+Vj8saNttyPteS1Or12UNstKUFNXot1ozTPd8XpzadPorbo1Ht7rf7fv9GuTKvVAAUhpACKAoAByKzLqBEOCjZtwYCLKgUKB9KCqaDLlveq1CKA9i-OqAsu8+uTLgB0A7FetDEejJdfVlTX9G1fEB40TFEp1TRR6iUZRlCAA" />

<br />
<br />

See the <a href="/storybook-static/index.html">Storybook</a> for more examples
</div>
</main>
);
Expand Down
33 changes: 33 additions & 0 deletions src/library/TypeScriptPlayground/TypeScriptPlayground.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import type { Meta, StoryObj } from "@storybook/react";

import { TypeScriptPlayground } from "./TypeScriptPlayground";
import { GithubPermalinkProvider } from "../config/GithubPermalinkContext";
import "../GithubPermalink/github-permalink.css";

const meta: Meta<typeof TypeScriptPlayground> = {
component: TypeScriptPlayground,
};

export default meta;

type Story = StoryObj<typeof TypeScriptPlayground>;

export const Primary: Story = {
render: () => (
<TypeScriptPlayground playgroundUrl="https://www.typescriptlang.org/play/?ssl=44&ssc=1&pln=8&pc=1#code/C4TwDgpgBASg9gdwCIENgqgXlhAxnAJwBMAeAZ2AIEsA7AcwBooBXGgaxsRoD4oAyKAG8oVIgC4oFavSgBfANwAoRaEhQAwnAA2zALY1U6EgBV4yNBggAPYBBpEysRIZRNjAaQggo12-cdsXnAAZlCmzha82IKKUHFQgSASHl6x8QR2RBAELhIAFABuKDoQyWYuANopIAC6AJRYvFK0dEqyysGsuMBUcDRQYARwuBBkZMYoAEZaECblFj42mY7z6G6e3r7LCUGh4ebo3HlDCBIAggQEKCBzEYdM+Dr655fXJJpPBha3B65hG9xuA0Yop2spBsNRuMpjM8mk4hUYvFkSJxFAAEQARgATABmdEMeEolASTGElHxSYSJEUlFWUnk2nIpJQbFE5HtDk1JiI9nxRISdEoAl8uIZezZXJQQrFZgQBqYXg0plxAD0qulYBQV10EFsBAaRRKEhoekm2SgAB8hKKUeq6SazdklCr4vbmY7dOaCC7XVB7Zy-RlgMwCP0AAYAEkERrlsnDvopgbiskZcWVKIFGMmIqZ4qyOQs+Vj8saNttyPteS1Or12UNstKUFNXot1ozTPd8XpzadPorbo1Ht7rf7fv9GuTKvVAAUhpACKAoAByKzLqBEOCjZtwYCLKgUKB9KCqaDLlveq1CKA9i-OqAsu8+uTLgB0A7FetDEejJdfVlTX9G1fEB40TFEp1TRR6iUZRlCAA" />
),
};

export const SimpleCode: Story = {
render: () => (
<TypeScriptPlayground playgroundUrl="https://www.typescriptlang.org/play/#code/MYewdgzgLgBAtgUwhAhgcwQLhtATgSzDRgF4YByACQQBsaQAaGAFQE8AHBAZWAPagCE5ANwAoUJBA0EAOnpoAFImToEASmFA" />
),
};

export const WithProvider: Story = {
render: () => (
<GithubPermalinkProvider>
<TypeScriptPlayground playgroundUrl="https://www.typescriptlang.org/play/?ssl=44&ssc=1&pln=8&pc=1#code/C4TwDgpgBASg9gdwCIENgqgXlhAxnAJwBMAeAZ2AIEsA7AcwBooBXGgaxsRoD4oAyKAG8oVIgC4oFavSgBfANwAoRaEhQAwnAA2zALY1U6EgBV4yNBggAPYBBpEysRIZRNjAaQggo12-cdsXnAAZlCmzha82IKKUHFQgSASHl6x8QR2RBAELhIAFABuKDoQyWYuANopIAC6AJRYvFK0dEqyysGsuMBUcDRQYARwuBBkZMYoAEZaECblFj42mY7z6G6e3r7LCUGh4ebo3HlDCBIAggQEKCBzEYdM+Dr655fXJJpPBha3B65hG9xuA0Yop2spBsNRuMpjM8mk4hUYvFkSJxFAAEQARgATABmdEMeEolASTGElHxSYSJEUlFWUnk2nIpJQbFE5HtDk1JiI9nxRISdEoAl8uIZezZXJQQrFZgQBqYXg0plxAD0qulYBQV10EFsBAaRRKEhoekm2SgAB8hKKUeq6SazdklCr4vbmY7dOaCC7XVB7Zy-RlgMwCP0AAYAEkERrlsnDvopgbiskZcWVKIFGMmIqZ4qyOQs+Vj8saNttyPteS1Or12UNstKUFNXot1ozTPd8XpzadPorbo1Ht7rf7fv9GuTKvVAAUhpACKAoAByKzLqBEOCjZtwYCLKgUKB9KCqaDLlveq1CKA9i-OqAsu8+uTLgB0A7FetDEejJdfVlTX9G1fEB40TFEp1TRR6iUZRlCAA" />
</GithubPermalinkProvider>
),
};
30 changes: 30 additions & 0 deletions src/library/TypeScriptPlayground/TypeScriptPlayground.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
"use client"

import { useContext, useEffect, useState } from "react";
import { TypeScriptPlaygroundDataResponse, GithubPermalinkContext } from "../config/GithubPermalinkContext";
import { TypeScriptPlaygroundBase, TypeScriptPlaygroundBaseProps } from "./TypeScriptPlaygroundBase";

type TypeScriptPlaygroundProps = Omit<TypeScriptPlaygroundBaseProps, "data"> & { playgroundUrl: string };

export function TypeScriptPlayground(props: TypeScriptPlaygroundProps) {
const { playgroundUrl } = props;
const [data, setData] = useState(null as null | TypeScriptPlaygroundDataResponse);
const { getTypeScriptPlaygroundFn, githubToken, onError } = useContext(GithubPermalinkContext);
const [isLoading, setIsLoading] = useState(true);

useEffect(() => {
getTypeScriptPlaygroundFn(playgroundUrl, githubToken, onError).then((v) => {
setIsLoading(false);
setData(v);
})
}, [getTypeScriptPlaygroundFn, githubToken, onError, playgroundUrl])

if (isLoading) {
return null;
}
if (!data) {
throw new Error("Loading is complete, but no data was returned.")
}

return <TypeScriptPlaygroundBase data={data} {...props} />
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import type { Meta, StoryObj } from "@storybook/react";

import { TypeScriptPlaygroundBase } from "./TypeScriptPlaygroundBase";
import "../GithubPermalink/github-permalink.css";

const meta: Meta<typeof TypeScriptPlaygroundBase> = {
component: TypeScriptPlaygroundBase,
};

export default meta;

type Story = StoryObj<typeof TypeScriptPlaygroundBase>;

export const WithData: Story = {
render: () => (
<TypeScriptPlaygroundBase
playgroundUrl="https://www.typescriptlang.org/play/#code/PTAEAEFMCdoe2gZwFygEwGYAsBWAUAJYB2ALgKYBG0A7AC4CeADgE4mgA0oA3pKKK2JACUQgL5A"
data={{
lines: [
"const message: string = 'Hello, TypeScript!';",
"console.log(message);"
],
startLine: 1,
endLine: 2,
status: "ok"
}}
/>
),
};

export const ErrorState: Story = {
render: () => (
<TypeScriptPlaygroundBase
playgroundUrl="https://www.typescriptlang.org/play/#code/invalid"
data={{
status: "other-error"
}}
/>
),
};

export const LongCode: Story = {
render: () => (
<TypeScriptPlaygroundBase
playgroundUrl="https://www.typescriptlang.org/play/?ssl=44&ssc=1&pln=8&pc=1#code/C4TwDgpgBASg9gdwCIENgqgXlhAxnAJwBMAeAZ2AIEsA7AcwBooBXGgaxsRoD4oAyKAG8oVIgC4oFavSgBfANwAoRaEhQAwnAA2zALY1U6EgBV4yNBggAPYBBpEysRIZRNjAaQggo12-cdsXnAAZlCmzha82IKKUHFQgSASHl6x8QR2RBAELhIAFABuKDoQyWYuANopIAC6AJRYvFK0dEqyysGsuMBUcDRQYARwuBBkZMYoAEZaECblFj42mY7z6G6e3r7LCUGh4ebo3HlDCBIAggQEKCBzEYdM+Dr655fXJJpPBha3B65hG9xuA0Yop2spBsNRuMpjM8mk4hUYvFkSJxFAAEQARgATABmdEMeEolASTGElHxSYSJEUlFWUnk2nIpJQbFE5HtDk1JiI9nxRISdEoAl8uIZezZXJQQrFZgQBqYXg0plxAD0qulYBQV10EFsBAaRRKEhoekm2SgAB8hKKUeq6SazdklCr4vbmY7dOaCC7XVB7Zy-RlgMwCP0AAYAEkERrlsnDvopgbiskZcWVKIFGMmIqZ4qyOQs+Vj8saNttyPteS1Or12UNstKUFNXot1ozTPd8XpzadPorbo1Ht7rf7fv9GuTKvVAAUhpACKAoAByKzLqBEOCjZtwYCLKgUKB9KCqaDLlveq1CKA9i-OqAsu8+uTLgB0A7FetDEejJdfVlTX9G1fEB40TFEp1TRR6iUZRlCAA"
data={{
lines: [
"type RowData = Record<string, unknown> & { id: string };",
"",
"type ColumnData<TRowData extends RowData, TKey extends keyof TRowData> = {",
" key: TKey",
" renderData: (value: TRowData[TKey]) => string;",
"}",
"",
"function processTable<TRowData extends RowData, TKey extends keyof TRowData>(row: Array<TRowData>, column: Array<ColumnData<TRowData, TKey>>) {",
"",
"}",
"",
"",
"processTable(",
" [{",
" id: \"123\",",
" a: 1,",
" b: {",
" x: 1,",
" y: 2",
" }",
" }], [{",
" key: \"a\",",
" renderData: (value) => {",
" // (parameter) value: number | {",
" // x: number;",
" // y: number;",
" // }",
" return `${value}`;",
" }",
" },",
" {",
" key: \"b\",",
" renderData: (value) => {",
"",
" // (parameter) value: number | {",
" // x: number;",
" // y: number;",
" // }",
" //Property 'x' does not exist on type 'number | { x: number; y: number; }'.",
" return `${value.x},${value.y}`;",
" }",
" },",
"]);"
],
startLine: 44,
endLine: 8,
status: "ok"
}}
/>
),
};
67 changes: 67 additions & 0 deletions src/library/TypeScriptPlayground/TypeScriptPlaygroundBase.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { TypeScriptPlaygroundDataResponse } from "../config/GithubPermalinkContext";
import { ErrorMessages } from "../ErrorMessages/ErrorMessages";
import { PropsWithChildren } from "react";
import { SyntaxHighlight } from "../SyntaxHighlight/SyntaxHighlight";
import { CopyButton } from "../common/CopyButton/CopyButton";
import { AvailableLanguagesPrism } from "../SyntaxHighlight/availableLanguagesPrism";

export type TypeScriptPlaygroundBaseProps = {
className?: string;
playgroundUrl: string;
data: TypeScriptPlaygroundDataResponse;
language?: AvailableLanguagesPrism;
}

export function TypeScriptPlaygroundBase(props: TypeScriptPlaygroundBaseProps) {
const { data, playgroundUrl } = props;

if (data.status === "ok") {
const language = props.language ?? "typescript";
const clipboard = data.lines.join("\n");

// Determine which lines to highlight based on startLine and endLine
const startLineNumber = data.startLine ?? 1;

return <TypeScriptPlaygroundInner {...props} clipboard={clipboard} header={<>
<a href={playgroundUrl} className="file-link">TypeScript Playground</a>
{data.startLine != null && data.endLine != null && (
<p>Lines {data.startLine} to {data.endLine}</p>
)}
</>}>
<SyntaxHighlight
text={clipboard}
startingLineNumber={startLineNumber}
language={language}
/>
</TypeScriptPlaygroundInner>
}

return <TypeScriptPlaygroundInner {...props}>
<ErrorMessages data={data} />
</TypeScriptPlaygroundInner>
}

function TypeScriptPlaygroundInner(props: PropsWithChildren<{
header?: React.ReactNode
clipboard?: string;
} & TypeScriptPlaygroundBaseProps>) {
const { clipboard } = props;

return <div className={`rgp-base react-github-permalink typescript-playground ${props.className ?? ''}`}>
<div className="header">
<div>
<svg height="24" viewBox="0 0 24 24" width="24" aria-hidden="true">
<path fill="currentColor" d="M1.125 0C.502 0 0 .502 0 1.125v21.75C0 23.498.502 24 1.125 24h21.75c.623 0 1.125-.502 1.125-1.125V1.125C24 .502 23.498 0 22.875 0zm17.363 9.75c.612 0 1.154.037 1.627.111a6.38 6.38 0 0 1 1.306.34v2.458a3.95 3.95 0 0 0-.643-.361 5.093 5.093 0 0 0-.717-.26 5.453 5.453 0 0 0-1.426-.2c-.3 0-.573.028-.819.086a2.1 2.1 0 0 0-.623.242c-.17.104-.3.229-.393.374a.888.888 0 0 0-.14.49c0 .196.053.373.156.529.104.156.252.304.443.444s.423.276.696.41c.273.135.582.274.926.416.47.197.892.407 1.266.628.374.222.695.473.963.753.268.279.472.598.614.957.142.359.214.776.214 1.253 0 .657-.125 1.21-.373 1.656a3.033 3.033 0 0 1-1.012 1.085 4.38 4.38 0 0 1-1.487.596c-.566.12-1.163.18-1.79.18a9.916 9.916 0 0 1-1.84-.164 5.544 5.544 0 0 1-1.512-.493v-2.63a5.033 5.033 0 0 0 3.237 1.2c.333 0 .624-.03.872-.09.249-.06.456-.144.623-.25.166-.108.29-.234.373-.38a1.023 1.023 0 0 0-.074-1.089 2.12 2.12 0 0 0-.537-.5 5.597 5.597 0 0 0-.807-.444 27.72 27.72 0 0 0-1.007-.436c-.918-.383-1.602-.852-2.053-1.405-.45-.553-.676-1.222-.676-2.005 0-.614.123-1.141.369-1.582.246-.441.58-.804 1.004-1.089a4.494 4.494 0 0 1 1.47-.629 7.536 7.536 0 0 1 1.77-.201zm-15.113.188h9.563v2.166H9.506v9.646H6.789v-9.646H3.375z"/>
</svg>
</div>
<div className="link-wrapper">
{props.header ?? <a href={props.playgroundUrl} className="file-link">{props.playgroundUrl}</a>}
</div>

{clipboard && <div className="copy-button-container">
<CopyButton clipboard={clipboard} />
</div>}
</div>
{props.children}
</div>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import type { Meta, StoryObj } from "@storybook/react";

import { TypeScriptPlaygroundRsc } from "./TypeScriptPlaygroundRsc";
import "../GithubPermalink/github-permalink.css";

const meta: Meta<typeof TypeScriptPlaygroundRsc> = {
component: TypeScriptPlaygroundRsc,
};

export default meta;

type Story = StoryObj<typeof TypeScriptPlaygroundRsc>;

export const Primary: Story = {
render: () => (
<TypeScriptPlaygroundRsc playgroundUrl="https://www.typescriptlang.org/play/?ssl=44&ssc=1&pln=8&pc=1#code/C4TwDgpgBASg9gdwCIENgqgXlhAxnAJwBMAeAZ2AIEsA7AcwBooBXGgaxsRoD4oAyKAG8oVIgC4oFavSgBfANwAoRaEhQAwnAA2zALY1U6EgBV4yNBggAPYBBpEysRIZRNjAaQggo12-cdsXnAAZlCmzha82IKKUHFQgSASHl6x8QR2RBAELhIAFABuKDoQyWYuANopIAC6AJRYvFK0dEqyysGsuMBUcDRQYARwuBBkZMYoAEZaECblFj42mY7z6G6e3r7LCUGh4ebo3HlDCBIAggQEKCBzEYdM+Dr655fXJJpPBha3B65hG9xuA0Yop2spBsNRuMpjM8mk4hUYvFkSJxFAAEQARgATABmdEMeEolASTGElHxSYSJEUlFWUnk2nIpJQbFE5HtDk1JiI9nxRISdEoAl8uIZezZXJQQrFZgQBqYXg0plxAD0qulYBQV10EFsBAaRRKEhoekm2SgAB8hKKUeq6SazdklCr4vbmY7dOaCC7XVB7Zy-RlgMwCP0AAYAEkERrlsnDvopgbiskZcWVKIFGMmIqZ4qyOQs+Vj8saNttyPteS1Or12UNstKUFNXot1ozTPd8XpzadPorbo1Ht7rf7fv9GuTKvVAAUhpACKAoAByKzLqBEOCjZtwYCLKgUKB9KCqaDLlveq1CKA9i-OqAsu8+uTLgB0A7FetDEejJdfVlTX9G1fEB40TFEp1TRR6iUZRlCAA" />
),
};
15 changes: 15 additions & 0 deletions src/library/TypeScriptPlayground/TypeScriptPlaygroundRsc.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { TypeScriptPlaygroundBase, TypeScriptPlaygroundBaseProps } from "./TypeScriptPlaygroundBase";
import { githubPermalinkRscConfig } from "../config/GithubPermalinkRscConfig";

type TypeScriptPlaygroundRscProps = Omit<TypeScriptPlaygroundBaseProps, "data"> & { playgroundUrl: string };

export async function TypeScriptPlaygroundRsc(props: TypeScriptPlaygroundRscProps) {
const { playgroundUrl } = props;
const getTypeScriptPlaygroundFn = githubPermalinkRscConfig.getTypeScriptPlaygroundFn();
const githubToken = githubPermalinkRscConfig.getGithubToken();
const onError = githubPermalinkRscConfig.getOnError();

const data = await getTypeScriptPlaygroundFn(playgroundUrl, githubToken, onError);

return <TypeScriptPlaygroundBase data={data} {...props} />
}
4 changes: 4 additions & 0 deletions src/library/config/BaseConfiguration.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { defaultGetPermalinkFn } from "./defaultFunctions";
import { defaultGetIssueFn } from "./defaultFunctions";
import { defaultGetTypeScriptPlaygroundFn } from "./defaultFunctions";


export type BaseConfiguration = {
Expand All @@ -11,6 +12,9 @@ export type BaseConfiguration = {
/** Function to provide issue data payload */
getIssueFn: typeof defaultGetIssueFn;

/** Function to provide TypeScript playground data payload */
getTypeScriptPlaygroundFn: typeof defaultGetTypeScriptPlaygroundFn;

/**
* A github personal access token - will be passed to the data fetching functions
*/
Expand Down
Loading