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
67 changes: 67 additions & 0 deletions src/library/GithubPermalink/github-permalink.css
Original file line number Diff line number Diff line change
Expand Up @@ -400,3 +400,70 @@ svg.github-logo{
margin: 0 2px;
}
}

/* Repository Link Styles */
.react-github-repository-link {
border: 1px solid var(--rgp-color-border);
border-radius: 6px;
background-color: var(--rgp-color-bg-stark);
color: var(--rgp-color-text-stark);
font-family: "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif;
padding: 16px;
margin: 8px 0;
}

.react-github-repository-link-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
}

.react-github-repository-link-repo {
display: flex;
align-items: center;
gap: 8px;
}

.react-github-repository-link-name {
color: var(--rgp-color-file-link);
font-weight: 600;
font-size: 16px;
text-decoration: none;
}

.react-github-repository-link-name:hover {
text-decoration: underline;
}

.react-github-repository-link-stats {
display: flex;
gap: 16px;
color: var(--rgp-color-text-frame);
font-size: 14px;
}

.react-github-repository-link-description {
color: var(--rgp-color-text-frame);
font-size: 14px;
line-height: 1.5;
}

.react-github-repository-link-inline {
display: inline-flex;
align-items: center;
gap: 4px;
color: var(--rgp-color-file-link);
text-decoration: none;
font-size: 14px;
}

.react-github-repository-link-inline:hover {
text-decoration: underline;
}

.react-github-repository-link-counts {
margin-left: 8px;
color: var(--rgp-color-text-frame);
font-size: 12px;
}
68 changes: 68 additions & 0 deletions src/library/GithubRepositoryLink/GithubRepositoryLink.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import type { Meta, StoryObj } from "@storybook/react";

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

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

export default meta;

type Story = StoryObj<typeof GithubRepositoryLink>;

export const Primary: Story = {
render: () => (
<GithubRepositoryLink repositoryLink="https://github.com/dwjohnston/react-github-permalink" />
),
};

export const Inline: Story = {
render: () => (
<div>
<p>Check out this repository: <GithubRepositoryLink
repositoryLink="https://github.com/dwjohnston/react-github-permalink"
variant="inline"
/> for more details.</p>
</div>
),
};

export const WithToken: Story = {
render: () => (
<GithubPermalinkProvider githubToken={process.env.STORYBOOK_GITHUB_TOKEN}>
<GithubRepositoryLink repositoryLink="https://github.com/dwjohnston/react-github-permalink" />
</GithubPermalinkProvider>
),
};

export const CustomDataFn: Story = {
render: () => (
<GithubPermalinkProvider
getRepositoryFn={(repositoryLink) => {
return Promise.resolve({
owner: "example",
repo: "test-repo",
name: "test-repo",
fullName: "example/test-repo",
description: "A test repository with custom data",
stargazersCount: 100,
forksCount: 25,
htmlUrl: "https://github.com/example/test-repo",
status: "ok"
});
}}
>
<GithubRepositoryLink repositoryLink="https://github.com/example/test-repo" />
</GithubPermalinkProvider>
),
};

export const ErrorReporting: Story = {
render: () => (
<GithubPermalinkProvider onError={(err) => console.error(err)}>
<GithubRepositoryLink repositoryLink="https://github.com/nonexistent/repo" />
</GithubPermalinkProvider>
),
};
25 changes: 25 additions & 0 deletions src/library/GithubRepositoryLink/GithubRepositoryLink.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { useContext, useEffect, useState } from "react";
import { GithubPermalinkContext } from "../config/GithubPermalinkContext";
import { GithubRepositoryLinkDataResponse } from "../config/GithubPermalinkContext";
import { GithubRepositoryLinkBase, GithubRepositoryLinkBaseProps } from "./GithubRepositoryLinkBase";

export type GithubRepositoryLinkProps = Omit<GithubRepositoryLinkBaseProps, "data"> & {
repositoryLink: string;
};

export function GithubRepositoryLink(props: GithubRepositoryLinkProps) {
const { getRepositoryFn, githubToken, onError } = useContext(GithubPermalinkContext);
const [data, setData] = useState<GithubRepositoryLinkDataResponse>({ status: "other-error" });
const { repositoryLink } = props;

useEffect(() => {
getRepositoryFn(repositoryLink, githubToken, onError).then((v) => {
setData(v);
}).catch((err) => {
onError?.(err);
setData({ status: "other-error" });
});
}, [repositoryLink, getRepositoryFn, githubToken, onError]);

return <GithubRepositoryLinkBase {...props} data={data} />;
}
123 changes: 123 additions & 0 deletions src/library/GithubRepositoryLink/GithubRepositoryLinkBase.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import type { Meta, StoryObj } from "@storybook/react";

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

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

export default meta;

type Story = StoryObj<typeof GithubRepositoryLinkBase>;

export const Primary: Story = {
render: () => (
<GithubRepositoryLinkBase
repositoryLink="https://github.com/dwjohnston/react-github-permalink"
data={{
owner: "dwjohnston",
repo: "react-github-permalink",
name: "react-github-permalink",
fullName: "dwjohnston/react-github-permalink",
description: "Display Github permalinks as codeblocks. Display Github issue links.",
stargazersCount: 25,
forksCount: 3,
htmlUrl: "https://github.com/dwjohnston/react-github-permalink",
status: "ok"
}}
/>
),
};

export const Inline: Story = {
render: () => (
<div>
<p>Check out this repository: <GithubRepositoryLinkBase
repositoryLink="https://github.com/dwjohnston/react-github-permalink"
variant="inline"
data={{
owner: "dwjohnston",
repo: "react-github-permalink",
name: "react-github-permalink",
fullName: "dwjohnston/react-github-permalink",
description: "Display Github permalinks as codeblocks. Display Github issue links.",
stargazersCount: 25,
forksCount: 3,
htmlUrl: "https://github.com/dwjohnston/react-github-permalink",
status: "ok"
}}
/> for more details.</p>
</div>
),
};

export const WithoutDescription: Story = {
render: () => (
<GithubRepositoryLinkBase
repositoryLink="https://github.com/dwjohnston/react-github-permalink"
showDescription={false}
data={{
owner: "dwjohnston",
repo: "react-github-permalink",
name: "react-github-permalink",
fullName: "dwjohnston/react-github-permalink",
description: "Display Github permalinks as codeblocks. Display Github issue links.",
stargazersCount: 25,
forksCount: 3,
htmlUrl: "https://github.com/dwjohnston/react-github-permalink",
status: "ok"
}}
/>
),
};

export const WithoutCounts: Story = {
render: () => (
<GithubRepositoryLinkBase
repositoryLink="https://github.com/dwjohnston/react-github-permalink"
showCounts={false}
data={{
owner: "dwjohnston",
repo: "react-github-permalink",
name: "react-github-permalink",
fullName: "dwjohnston/react-github-permalink",
description: "Display Github permalinks as codeblocks. Display Github issue links.",
stargazersCount: 25,
forksCount: 3,
htmlUrl: "https://github.com/dwjohnston/react-github-permalink",
status: "ok"
}}
/>
),
};

export const ErrorState: Story = {
render: () => (
<GithubRepositoryLinkBase
repositoryLink="https://github.com/nonexistent/repo"
data={{
status: "404"
}}
/>
),
};

export const NoDescription: Story = {
render: () => (
<GithubRepositoryLinkBase
repositoryLink="https://github.com/dwjohnston/react-github-permalink"
data={{
owner: "dwjohnston",
repo: "react-github-permalink",
name: "react-github-permalink",
fullName: "dwjohnston/react-github-permalink",
description: null,
stargazersCount: 25,
forksCount: 3,
htmlUrl: "https://github.com/dwjohnston/react-github-permalink",
status: "ok"
}}
/>
),
};
77 changes: 77 additions & 0 deletions src/library/GithubRepositoryLink/GithubRepositoryLinkBase.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { PropsWithChildren } from "react";
import { GithubSvg } from "../GithubSvg/GithubSvg";
import { GithubRepositoryLinkDataResponse } from "../config/GithubPermalinkContext";
import { ErrorMessages } from "../ErrorMessages/ErrorMessages";

export type GithubRepositoryLinkBaseProps = {
className?: string;
repositoryLink: string;
data: GithubRepositoryLinkDataResponse;
variant?: "inline" | "block";
showDescription?: boolean;
showCounts?: boolean;
}

export function GithubRepositoryLinkBase(props: GithubRepositoryLinkBaseProps) {
const {
data,
variant = "block",
repositoryLink,
showDescription = true,
showCounts = true
} = props;

if (data.status === "ok") {
if (variant === "inline") {
return (
<a href={data.htmlUrl} className="react-github-repository-link-inline">
<GithubSvg />
<span>{data.fullName}</span>
{showCounts && (
<span className="react-github-repository-link-counts">
⭐ {data.stargazersCount} 🍴 {data.forksCount}
</span>
)}
</a>
);
}

return (
<GithubRepositoryLinkInner {...props}>
<div className="react-github-repository-link-header">
<div className="react-github-repository-link-repo">
<GithubSvg />
<a href={data.htmlUrl} className="react-github-repository-link-name">
{data.fullName}
</a>
</div>
{showCounts && (
<div className="react-github-repository-link-stats">
<span className="react-github-repository-link-stars">
⭐ {data.stargazersCount}
</span>
<span className="react-github-repository-link-forks">
🍴 {data.forksCount}
</span>
</div>
)}
</div>
{showDescription && data.description && (
<div className="react-github-repository-link-description">
{data.description}
</div>
)}
</GithubRepositoryLinkInner>
);
}

return <ErrorMessages data={data} />;
}

export function GithubRepositoryLinkInner(props: PropsWithChildren<GithubRepositoryLinkBaseProps>) {
return (
<div className={`react-github-repository-link ${props.className ?? ""}`}>
{props.children}
</div>
);
}
21 changes: 21 additions & 0 deletions src/library/GithubRepositoryLink/GithubRepositoryLinkRsc.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { GithubRepositoryLinkBase, GithubRepositoryLinkBaseProps } from "./GithubRepositoryLinkBase";
import { githubPermalinkRscConfig } from "../config/GithubPermalinkRscConfig";

export type GithubRepositoryLinkRscProps = Omit<GithubRepositoryLinkBaseProps, "data"> & {
repositoryLink: string;
};

export async function GithubRepositoryLinkRsc(props: GithubRepositoryLinkRscProps) {
const { repositoryLink } = props;
const { getRepositoryFn, githubToken, onError } = githubPermalinkRscConfig.getConfig();

let data;
try {
data = await getRepositoryFn(repositoryLink, githubToken, onError);
} catch (err) {
onError?.(err);
data = { status: "other-error" as const };
}

return <GithubRepositoryLinkBase {...props} data={data} />;
}
Loading