mirror of
https://github.com/facebook/docusaurus.git
synced 2025-06-25 05:57:53 +02:00
feat(v2): add edit url in post page (#2524)
* Add edit page url in docs * feat(v2): Create EditPage component * feat(v2): Improve component * Add test * çhore(v2): update description * çhore(v2): update url * fix(v2): test * fix(v2): test * feat(v2): change the prop of EditPage component * chore(v2): Remove packages * feat(v2): Update old tests * chore(v2): fix package * fix(v2): fix editUrl * docs(v2): document editUrl * -- * -- Co-authored-by: Yangshun Tay <tay.yang.shun@gmail.com>
This commit is contained in:
parent
7bc7241ee4
commit
5e664a1f26
9 changed files with 72 additions and 12 deletions
|
@ -28,6 +28,8 @@ describe('loadBlog', () => {
|
||||||
} as LoadContext,
|
} as LoadContext,
|
||||||
{
|
{
|
||||||
path: pluginPath,
|
path: pluginPath,
|
||||||
|
editUrl:
|
||||||
|
'https://github.com/facebook/docusaurus/edit/master/website-1x',
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
const {blogPosts} = await plugin.loadContent();
|
const {blogPosts} = await plugin.loadContent();
|
||||||
|
@ -50,12 +52,15 @@ describe('loadBlog', () => {
|
||||||
...blogPosts.find(v => v.metadata.title === 'date-matter').metadata,
|
...blogPosts.find(v => v.metadata.title === 'date-matter').metadata,
|
||||||
...{prevItem: undefined},
|
...{prevItem: undefined},
|
||||||
}).toEqual({
|
}).toEqual({
|
||||||
|
editUrl:
|
||||||
|
'https://github.com/facebook/docusaurus/edit/master/website-1x/blog/date-matter.md',
|
||||||
permalink: '/blog/2019/01/01/date-matter',
|
permalink: '/blog/2019/01/01/date-matter',
|
||||||
readingTime: 0.02,
|
readingTime: 0.02,
|
||||||
source: path.join('@site', pluginPath, 'date-matter.md'),
|
source: path.join('@site', pluginPath, 'date-matter.md'),
|
||||||
title: 'date-matter',
|
title: 'date-matter',
|
||||||
description: `date inside front matter`,
|
description: `date inside front matter`,
|
||||||
date: new Date('2019-01-01'),
|
date: new Date('2019-01-01'),
|
||||||
|
prevItem: undefined,
|
||||||
tags: [],
|
tags: [],
|
||||||
nextItem: {
|
nextItem: {
|
||||||
permalink: '/blog/2018/12/14/Happy-First-Birthday-Slash',
|
permalink: '/blog/2018/12/14/Happy-First-Birthday-Slash',
|
||||||
|
@ -68,6 +73,8 @@ describe('loadBlog', () => {
|
||||||
blogPosts.find(v => v.metadata.title === 'Happy 1st Birthday Slash!')
|
blogPosts.find(v => v.metadata.title === 'Happy 1st Birthday Slash!')
|
||||||
.metadata,
|
.metadata,
|
||||||
).toEqual({
|
).toEqual({
|
||||||
|
editUrl:
|
||||||
|
'https://github.com/facebook/docusaurus/edit/master/website-1x/blog/2018-12-14-Happy-First-Birthday-Slash.md',
|
||||||
permalink: '/blog/2018/12/14/Happy-First-Birthday-Slash',
|
permalink: '/blog/2018/12/14/Happy-First-Birthday-Slash',
|
||||||
readingTime: 0.01,
|
readingTime: 0.01,
|
||||||
source: path.join(
|
source: path.join(
|
||||||
|
@ -90,6 +97,8 @@ describe('loadBlog', () => {
|
||||||
...blogPosts.find(v => v.metadata.title === 'no date').metadata,
|
...blogPosts.find(v => v.metadata.title === 'no date').metadata,
|
||||||
...{prevItem: undefined},
|
...{prevItem: undefined},
|
||||||
}).toEqual({
|
}).toEqual({
|
||||||
|
editUrl:
|
||||||
|
'https://github.com/facebook/docusaurus/edit/master/website-1x/blog/no date.md',
|
||||||
permalink: noDatePermalink,
|
permalink: noDatePermalink,
|
||||||
readingTime: 0.01,
|
readingTime: 0.01,
|
||||||
source: noDateSource,
|
source: noDateSource,
|
||||||
|
@ -97,6 +106,7 @@ describe('loadBlog', () => {
|
||||||
description: `no date`,
|
description: `no date`,
|
||||||
date: noDateSourceBirthTime,
|
date: noDateSourceBirthTime,
|
||||||
tags: [],
|
tags: [],
|
||||||
|
prevItem: undefined,
|
||||||
nextItem: {
|
nextItem: {
|
||||||
permalink: '/blog/2020/02/27/draft',
|
permalink: '/blog/2020/02/27/draft',
|
||||||
title: 'draft',
|
title: 'draft',
|
||||||
|
|
|
@ -11,7 +11,12 @@ import path from 'path';
|
||||||
import readingTime from 'reading-time';
|
import readingTime from 'reading-time';
|
||||||
import {Feed} from 'feed';
|
import {Feed} from 'feed';
|
||||||
import {PluginOptions, BlogPost, DateLink} from './types';
|
import {PluginOptions, BlogPost, DateLink} from './types';
|
||||||
import {parse, normalizeUrl, aliasedSitePath} from '@docusaurus/utils';
|
import {
|
||||||
|
parse,
|
||||||
|
normalizeUrl,
|
||||||
|
aliasedSitePath,
|
||||||
|
getEditUrl,
|
||||||
|
} from '@docusaurus/utils';
|
||||||
import {LoadContext} from '@docusaurus/types';
|
import {LoadContext} from '@docusaurus/types';
|
||||||
|
|
||||||
export function truncate(fileString: string, truncateMarker: RegExp) {
|
export function truncate(fileString: string, truncateMarker: RegExp) {
|
||||||
|
@ -86,7 +91,13 @@ export async function generateBlogPosts(
|
||||||
{siteConfig, siteDir}: LoadContext,
|
{siteConfig, siteDir}: LoadContext,
|
||||||
options: PluginOptions,
|
options: PluginOptions,
|
||||||
) {
|
) {
|
||||||
const {include, routeBasePath, truncateMarker, showReadingTime} = options;
|
const {
|
||||||
|
include,
|
||||||
|
routeBasePath,
|
||||||
|
truncateMarker,
|
||||||
|
showReadingTime,
|
||||||
|
editUrl,
|
||||||
|
} = options;
|
||||||
|
|
||||||
if (!fs.existsSync(blogDir)) {
|
if (!fs.existsSync(blogDir)) {
|
||||||
return [];
|
return [];
|
||||||
|
@ -103,8 +114,12 @@ export async function generateBlogPosts(
|
||||||
blogFiles.map(async (relativeSource: string) => {
|
blogFiles.map(async (relativeSource: string) => {
|
||||||
const source = path.join(blogDir, relativeSource);
|
const source = path.join(blogDir, relativeSource);
|
||||||
const aliasedSource = aliasedSitePath(source, siteDir);
|
const aliasedSource = aliasedSitePath(source, siteDir);
|
||||||
|
const refDir = path.parse(blogDir).dir;
|
||||||
|
const relativePath = path.relative(refDir, source);
|
||||||
const blogFileName = path.basename(relativeSource);
|
const blogFileName = path.basename(relativeSource);
|
||||||
|
|
||||||
|
const editBlogUrl = getEditUrl(relativePath, editUrl);
|
||||||
|
|
||||||
const fileString = await fs.readFile(source, 'utf-8');
|
const fileString = await fs.readFile(source, 'utf-8');
|
||||||
const {frontMatter, content, excerpt} = parse(fileString);
|
const {frontMatter, content, excerpt} = parse(fileString);
|
||||||
|
|
||||||
|
@ -140,6 +155,7 @@ export async function generateBlogPosts(
|
||||||
routeBasePath,
|
routeBasePath,
|
||||||
frontMatter.id || toUrl({date, link: linkName}),
|
frontMatter.id || toUrl({date, link: linkName}),
|
||||||
]),
|
]),
|
||||||
|
editUrl: editBlogUrl,
|
||||||
source: aliasedSource,
|
source: aliasedSource,
|
||||||
description: frontMatter.description || excerpt,
|
description: frontMatter.description || excerpt,
|
||||||
date,
|
date,
|
||||||
|
|
|
@ -43,6 +43,7 @@ const DEFAULT_OPTIONS: PluginOptions = {
|
||||||
showReadingTime: true,
|
showReadingTime: true,
|
||||||
remarkPlugins: [],
|
remarkPlugins: [],
|
||||||
rehypePlugins: [],
|
rehypePlugins: [],
|
||||||
|
editUrl: undefined,
|
||||||
truncateMarker: /<!--\s*(truncate)\s*-->/, // Regex.
|
truncateMarker: /<!--\s*(truncate)\s*-->/, // Regex.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,7 @@ export interface PluginOptions {
|
||||||
copyright: string;
|
copyright: string;
|
||||||
language?: string;
|
language?: string;
|
||||||
};
|
};
|
||||||
|
editUrl?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BlogTags {
|
export interface BlogTags {
|
||||||
|
@ -82,6 +83,7 @@ export interface MetaData {
|
||||||
prevItem?: Paginator;
|
prevItem?: Paginator;
|
||||||
nextItem?: Paginator;
|
nextItem?: Paginator;
|
||||||
truncated: boolean;
|
truncated: boolean;
|
||||||
|
editUrl?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Paginator {
|
export interface Paginator {
|
||||||
|
|
|
@ -11,7 +11,7 @@ import {
|
||||||
parse,
|
parse,
|
||||||
aliasedSitePath,
|
aliasedSitePath,
|
||||||
normalizeUrl,
|
normalizeUrl,
|
||||||
posixPath,
|
getEditUrl,
|
||||||
} from '@docusaurus/utils';
|
} from '@docusaurus/utils';
|
||||||
import {LoadContext} from '@docusaurus/types';
|
import {LoadContext} from '@docusaurus/types';
|
||||||
|
|
||||||
|
@ -90,9 +90,7 @@ export default async function processMetadata({
|
||||||
|
|
||||||
const relativePath = path.relative(siteDir, filePath);
|
const relativePath = path.relative(siteDir, filePath);
|
||||||
|
|
||||||
const docsEditUrl = editUrl
|
const docsEditUrl = getEditUrl(relativePath, editUrl);
|
||||||
? normalizeUrl([editUrl, posixPath(relativePath)])
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
const {frontMatter = {}, excerpt} = parse(await fileStringPromise);
|
const {frontMatter = {}, excerpt} = parse(await fileStringPromise);
|
||||||
const {sidebar_label, custom_edit_url} = frontMatter;
|
const {sidebar_label, custom_edit_url} = frontMatter;
|
||||||
|
|
|
@ -14,8 +14,10 @@ import BlogPostPaginator from '@theme/BlogPostPaginator';
|
||||||
function BlogPostPage(props) {
|
function BlogPostPage(props) {
|
||||||
const {content: BlogPostContents} = props;
|
const {content: BlogPostContents} = props;
|
||||||
const {frontMatter, metadata} = BlogPostContents;
|
const {frontMatter, metadata} = BlogPostContents;
|
||||||
|
const {title, description, nextItem, prevItem, editUrl} = metadata;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout title={metadata.title} description={metadata.description}>
|
<Layout title={title} description={description}>
|
||||||
{BlogPostContents && (
|
{BlogPostContents && (
|
||||||
<div className="container margin-vert--xl">
|
<div className="container margin-vert--xl">
|
||||||
<div className="row">
|
<div className="row">
|
||||||
|
@ -26,12 +28,30 @@ function BlogPostPage(props) {
|
||||||
isBlogPostPage>
|
isBlogPostPage>
|
||||||
<BlogPostContents />
|
<BlogPostContents />
|
||||||
</BlogPostItem>
|
</BlogPostItem>
|
||||||
{(metadata.nextItem || metadata.prevItem) && (
|
<div>
|
||||||
|
{editUrl && (
|
||||||
|
<a href={editUrl} target="_blank" rel="noreferrer noopener">
|
||||||
|
<svg
|
||||||
|
fill="currentColor"
|
||||||
|
height="1.2em"
|
||||||
|
width="1.2em"
|
||||||
|
preserveAspectRatio="xMidYMid meet"
|
||||||
|
viewBox="0 0 40 40"
|
||||||
|
style={{
|
||||||
|
marginRight: '0.3em',
|
||||||
|
verticalAlign: 'sub',
|
||||||
|
}}>
|
||||||
|
<g>
|
||||||
|
<path d="m34.5 11.7l-3 3.1-6.3-6.3 3.1-3q0.5-0.5 1.2-0.5t1.1 0.5l3.9 3.9q0.5 0.4 0.5 1.1t-0.5 1.2z m-29.5 17.1l18.4-18.5 6.3 6.3-18.4 18.4h-6.3v-6.2z" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
Edit this page
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{(nextItem || prevItem) && (
|
||||||
<div className="margin-vert--xl">
|
<div className="margin-vert--xl">
|
||||||
<BlogPostPaginator
|
<BlogPostPaginator nextItem={nextItem} prevItem={prevItem} />
|
||||||
nextItem={metadata.nextItem}
|
|
||||||
prevItem={metadata.prevItem}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -273,3 +273,9 @@ export function aliasedSitePath(filePath: string, siteDir: string) {
|
||||||
// the '@site'. Let webpack loader resolve it.
|
// the '@site'. Let webpack loader resolve it.
|
||||||
return `@site/${relativePath}`;
|
return `@site/${relativePath}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getEditUrl(fileRelativePath: string, editUrl?: string) {
|
||||||
|
return editUrl
|
||||||
|
? normalizeUrl([editUrl, posixPath(fileRelativePath)])
|
||||||
|
: undefined;
|
||||||
|
}
|
||||||
|
|
|
@ -149,6 +149,11 @@ module.exports = {
|
||||||
* relative to site dir
|
* relative to site dir
|
||||||
*/
|
*/
|
||||||
path: 'blog',
|
path: 'blog',
|
||||||
|
/**
|
||||||
|
* URL for editing a blog post, example: 'https://github.com/facebook/docusaurus/edit/master/website/blog/'
|
||||||
|
*/
|
||||||
|
editUrl:
|
||||||
|
'https://github.com/facebook/docusaurus/edit/master/website/blog/',
|
||||||
/**
|
/**
|
||||||
* URL route for the blog section of your site
|
* URL route for the blog section of your site
|
||||||
* do not include trailing slash
|
* do not include trailing slash
|
||||||
|
|
|
@ -46,6 +46,8 @@ module.exports = {
|
||||||
},
|
},
|
||||||
blog: {
|
blog: {
|
||||||
path: '../website-1.x/blog',
|
path: '../website-1.x/blog',
|
||||||
|
editUrl:
|
||||||
|
'https://github.com/facebook/docusaurus/edit/master/website-1.x/',
|
||||||
postsPerPage: 3,
|
postsPerPage: 3,
|
||||||
feedOptions: {
|
feedOptions: {
|
||||||
type: 'all',
|
type: 'all',
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue