From bd085335d4e5b495e71815c3de5012cde9f6308b Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 10 Jul 2025 06:01:20 +0000
Subject: [PATCH 1/2] Initial plan
From 4058e700521fc9f6e0e05ece4606807c3a1c9206 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 10 Jul 2025 06:20:33 +0000
Subject: [PATCH 2/2] Add complete GitHub PR Link support with all variants and
documentation
Co-authored-by: dwjohnston <2467377+dwjohnston@users.noreply.github.com>
---
README.md | 81 +++++++++++++-
.../GithubPRLink/GithubPRLink.stories.tsx | 43 ++++++++
src/library/GithubPRLink/GithubPRLink.tsx | 33 ++++++
.../GithubPRLink/GithubPRLinkBase.stories.tsx | 103 ++++++++++++++++++
src/library/GithubPRLink/GithubPRLinkBase.tsx | 98 +++++++++++++++++
src/library/GithubPRLink/GithubPRLinkRsc.tsx | 14 +++
.../GithubPermalink/github-permalink.css | 78 ++++++++++++-
src/library/config/BaseConfiguration.ts | 5 +-
src/library/config/GithubPermalinkContext.tsx | 29 ++++-
.../config/GithubPermalinkRscConfig.ts | 7 +-
src/library/config/defaultFunctions.ts | 36 +++++-
src/library/export.ts | 2 +
src/library/rsc.ts | 3 +-
src/library/utils/urlParsers.test.ts | 44 +++++++-
src/library/utils/urlParsers.ts | 12 ++
15 files changed, 574 insertions(+), 14 deletions(-)
create mode 100644 src/library/GithubPRLink/GithubPRLink.stories.tsx
create mode 100644 src/library/GithubPRLink/GithubPRLink.tsx
create mode 100644 src/library/GithubPRLink/GithubPRLinkBase.stories.tsx
create mode 100644 src/library/GithubPRLink/GithubPRLinkBase.tsx
create mode 100644 src/library/GithubPRLink/GithubPRLinkRsc.tsx
diff --git a/README.md b/README.md
index fb1b2b5..5e5dc6a 100644
--- a/README.md
+++ b/README.md
@@ -4,6 +4,8 @@ Display Github permalinks as codeblocks.
Display Github issue links.
+Display Github PR links.
+



@@ -27,9 +29,9 @@ This package is compatible with Next 13+ and the components can be used as RSCs
Three variants of each component are exported
- - GithubPermalink/GithubIssueLink - Client component - It fetches the data as on the client in a useEffect. ie. Data won't be retrieved until application has loaded in user's browser.
- - GithubPermalinkBase/GithubIssueLinkBase - this is the base component - it does no data fetching on its own.
- - GithubPermalinkRsc/GithubIssueLinkRsc - This is an RSC.
+ - GithubPermalink/GithubIssueLink/GithubPRLink - Client component - It fetches the data as on the client in a useEffect. ie. Data won't be retrieved until application has loaded in user's browser.
+ - GithubPermalinkBase/GithubIssueLinkBase/GithubPRLinkBase - this is the base component - it does no data fetching on its own.
+ - GithubPermalinkRsc/GithubIssueLinkRsc/GithubPRLinkRsc - This is an RSC.
@@ -112,6 +114,72 @@ export function MyApp() {
}
```
+## Github PR Link
+
+### Usage
+```jsx
+import { GithubPRLink } from 'react-github-permalink';
+import "react-github-permalink/dist/github-permalink.css"; // Or provide your own styles
+
+export function MyApp() {
+ return ,
+}
+```
+
+PR Link also has an inline variant:
+
+```jsx
+export function MyApp() {
+ return ,
+}
+```
+
+### PR Link with custom data
+
+```jsx
+import { GithubPRLinkBase } from 'react-github-permalink';
+import "react-github-permalink/dist/github-permalink.css"; // Or provide your own styles
+
+export function MyApp() {
+ return
+}
+```
+
+### PR Link RSC
+
+```jsx
+import { GithubPRLinkRsc } from 'react-github-permalink/dist/rsc';
+import "react-github-permalink/dist/github-permalink.css"; // Or provide your own styles
+
+export function MyApp() {
+ return
+}
+```
+
## Rate Limits and Authentication
By default the components make unauthenticated requests against Github's API. The rate limit for such requests is 60/hour and only publicly visible repositories are available.
@@ -126,6 +194,7 @@ The global configuration object has this signature
type BaseConfiguration = {
getDataFn: (permalink: string, githubToken?: string | undefined, onError?: ((err: unknown) => void) | undefined) => Promise;
getIssueFn: (issueLink: string, githubToken?: string | undefined, onError?: ((err: unknown) => void) | undefined) => Promise;
+ getPRFn: (prLink: string, githubToken?: string | undefined, onError?: ((err: unknown) => void) | undefined) => Promise;
githubToken: string | undefined;
onError: ((e: unknown) => void) | undefined;
}
@@ -136,7 +205,7 @@ type BaseConfiguration = {
Client components are configured via context provider:
```tsx
-import { GithubPermalink, GithubIssueLink GithubPermalinkProvider, } from 'react-github-permalink';
+import { GithubPermalink, GithubIssueLink, GithubPRLink, GithubPermalinkProvider } from 'react-github-permalink';
import "react-github-permalink/dist/github-permalink.css";
export function MyApp() {
@@ -147,6 +216,9 @@ export function MyApp() {
getIssueFn={(issueLink: string) => {
// Your implementation to retrieve issue links here
}}
+ getPRFn={(prLink: string) => {
+ // Your implementation to retrieve PR links here
+ }}
// Don't put a put a github token into the context provider in production! It will visible for all the world to see!
// Instead you will need to expose a data fetching function on the backend to do it for you
@@ -158,6 +230,7 @@ export function MyApp() {
>
+
}
```
diff --git a/src/library/GithubPRLink/GithubPRLink.stories.tsx b/src/library/GithubPRLink/GithubPRLink.stories.tsx
new file mode 100644
index 0000000..41286f2
--- /dev/null
+++ b/src/library/GithubPRLink/GithubPRLink.stories.tsx
@@ -0,0 +1,43 @@
+import type { Meta, StoryObj } from "@storybook/react";
+
+import { GithubPRLink } from "./GithubPRLink";
+import {
+ GithubPermalinkContext,
+ GithubPermalinkProvider,
+} from "../config/GithubPermalinkContext";
+import "../GithubPermalink/github-permalink.css";
+
+const meta: Meta = {
+ component: GithubPRLink,
+};
+
+export default meta;
+
+type Story = StoryObj;
+
+/*
+ *👇 Render functions are a framework specific feature to allow you control on how the component renders.
+ * See https://storybook.js.org/docs/react/api/csf
+ * to learn how to use render functions.
+ */
+export const Primary: Story = {
+ render: () => (
+
+ ),
+};
+
+export const WithBackground: Story = {
+ render: () => (
+
+
+
+ ),
+};
+
+export const WithToken: Story = {
+ render: () => (
+
+
+
+ ),
+};
\ No newline at end of file
diff --git a/src/library/GithubPRLink/GithubPRLink.tsx b/src/library/GithubPRLink/GithubPRLink.tsx
new file mode 100644
index 0000000..4ee69ef
--- /dev/null
+++ b/src/library/GithubPRLink/GithubPRLink.tsx
@@ -0,0 +1,33 @@
+"use client"
+
+import { useContext, useEffect, useState } from "react";
+
+import { GithubPermalinkContext, GithubPRLinkDataResponse } from "../config/GithubPermalinkContext";
+import { GithubPRLinkBase, GithubPRLinkBaseProps } from "./GithubPRLinkBase";
+
+type GithubPRLinkProps = Omit;
+
+export function GithubPRLink(props: GithubPRLinkProps) {
+
+ const { prLink } = props;
+ const [data, setData] = useState(null as null | GithubPRLinkDataResponse)
+ const { getPRFn, githubToken, onError} = useContext(GithubPermalinkContext);
+ const [isLoading, setIsLoading] = useState(true);
+
+ useEffect(() => {
+ getPRFn(prLink, githubToken, onError).then((v) => {
+ setIsLoading(false);
+ setData(v);
+ })
+ }, [getPRFn, githubToken, prLink, onError])
+
+ if (isLoading) {
+ return null;
+ }
+ if (!data) {
+ throw new Error("Loading is complete, but no data was returned.")
+ }
+
+ return
+
+}
\ No newline at end of file
diff --git a/src/library/GithubPRLink/GithubPRLinkBase.stories.tsx b/src/library/GithubPRLink/GithubPRLinkBase.stories.tsx
new file mode 100644
index 0000000..19dd8ec
--- /dev/null
+++ b/src/library/GithubPRLink/GithubPRLinkBase.stories.tsx
@@ -0,0 +1,103 @@
+import type { Meta, StoryObj } from "@storybook/react";
+
+import { GithubPRLinkBase } from "./GithubPRLinkBase";
+import "../GithubPermalink/github-permalink.css";
+
+const meta: Meta = {
+ component: GithubPRLinkBase,
+};
+
+export default meta;
+
+type Story = StoryObj;
+
+const sampleData = {
+ prTitle: "Add concurrent features to React",
+ prNumber: "24652",
+ prState: "closed" as const,
+ owner: "facebook",
+ repo: "react",
+ status: "ok" as const,
+ isDraft: false,
+ merged: true,
+ mergeable: null,
+ reactions: {
+ "+1": 42,
+ "-1": 0,
+ confused: 0,
+ eyes: 2,
+ heart: 8,
+ hooray: 15,
+ laugh: 0,
+ rocket: 5,
+ total_count: 72
+ }
+};
+
+const draftData = {
+ ...sampleData,
+ prTitle: "WIP: New feature implementation",
+ prNumber: "12345",
+ prState: "open" as const,
+ isDraft: true,
+ merged: false,
+};
+
+const openData = {
+ ...sampleData,
+ prTitle: "Fix bug in component rendering",
+ prNumber: "54321",
+ prState: "open" as const,
+ isDraft: false,
+ merged: false,
+};
+
+export const Primary: Story = {
+ render: () => (
+
+ ),
+};
+
+export const Draft: Story = {
+ render: () => (
+
+ ),
+};
+
+export const Open: Story = {
+ render: () => (
+
+ ),
+};
+
+export const Inline: Story = {
+ render: () => (
+