docusaurus
docusaurus copied to clipboard
Show profile of all contributors to a doc page
Have you read the Contributing Guidelines on issues?
- [X] I have read the Contributing Guidelines on issues.
Description
More details here https://github.com/facebook/docusaurus/discussions/6194#discussion-3765566
Has this been requested on Canny?
No response
Motivation
We want to show the picture of contributors to our docs so that users get more motivation to contribute to the doc
API design
No response
Have you tried building it?
No
Self-service
- [ ] I'd be willing to contribute this feature to Docusaurus myself.
So I think displaying the pictures of all contributors is going to be too opinionated. However, we can do this:
- Instead of including the last update author & time, we include all authors and their last update timestamps in the metadata;
- You can wrap the doc container component to render the list of authors beneath the main content, e.g. by querying the GitHub API and getting their profile pictures.
@slorber Let's talk about how the API will look like. Our DocMetadata
type has the shape:
type DocMetadataBase = LastUpdateData & {
id: string;
version: VersionName;
title: string;
//...
};
Should we add another field
type NewDocMetadataBase = DocMetadataBase & {
updateHistory: Array<{author: string, time: number, formattedTime: string}>
}
to the metadata but preserve the existing lastUpdatedAt
and lastUpdatedBy
metadata (make it safe for existing swizzled components & works well with existing options showLastUpdate*
), or should we release this as a breaking change, change LastUpdateData
to a generic UpdateHistory
field, and move the determination of what to display to the theme side?
I'd rather have an updateHistory breaking change instead of duplicating fields
Note it seems looking up for the git history might have performance implications, we can try this on our own site first maybe and see, eventually providing an option to control how much we look back into the history. Is it worth it to include all history entries? Probably not, as that would lead to multiple times the same author, so it's not really an history anymore but rather the last unique users that updated a doc.
Also the git author is a string, and it does not necessarily match your github username (in your case it's author: "Joshua Chen"
=> I guess GitHub is matching on the email too, so we probably want to include the user email in this author type.
Yes, we need to figure out how GitHub matches git usernames with users. For a while I used username Josh-Cena
, and my avatars etc. are still loaded correctly, so I think email also plays a part.
About duplicated authors, I don't know if the full history is useful to keep, but in any case we can first go with filtering out unique author names
This command does not seem too slow to run locally: git log --format=%ct,%an,%ae
🤷♂️ but it's hard to tell for a lot of files.
Maybe we should first write a way to log lifecycle execution duration of all plugins to be able to measure the impact?
About duplicated authors, I don't know if the full history is useful to keep, but in any case we can first go with filtering out unique author names
That would also be a lot of data to send to the frontend for each file, probably not worth it or at least not something to do by default
Isn't this data tree-shakable if unused?
Isn't this data tree-shakable if unused?
If this is imported/required it won't be tree-shaked even if it's not rendered
IE if it's part of docs metadata it will add weight, so you'd rather find a way to limit the json size.
It's also possible to pass the component an optional json module as props, but that's quite similar in terms of tradeoffs anyway
From #3592, the following would be helpful:
- ability to specify maximum number of last contributors from 1 to 5 (hardcoded: 1)
- ability to specify the handle by which user is represented (hardcoded: name + surname)
- as possible value it would be nice to have GitHub username, email or one of names used alone
- ability to display user avatar or profile picture from GitHub (or Gravatar) with a nice fallback
Yes, we need to figure out how GitHub matches git usernames with users. For a while I used username
Josh-Cena
, and my avatars etc. are still loaded correctly, so I think email also plays a part.
GitHub doesn't match on username at all, only email address (of which a GitHub user can have multiple associated with their account): https://docs.github.com/en/account-and-profile/setting-up-and-managing-your-github-user-account/managing-email-preferences/setting-your-commit-email-address
I've mocked that in a UI design..
We can get the last 3-7 contributor and have the others on request when clicking on "N contributors"
I've mocked that in a UI design..
Love the mockup, great work @osamajandali 👏 Are you thinking that the pop-up window would also indicate who was the original creator of the page and timestamps when contributions were made?
I also wanted to ask if anyone has already started implementing this? And whether it would become part of core docusaurus or a standalone module instead?
Is this still under development? We would love that too!
👍 also interested in this feature
any updates? I'm looking forward to this feature
Technically this can now be implemented in userland thanks to the v3.1 parseFrontMatter hook: https://docusaurus.io/blog/releases/3.1#parsefrontmatter-hook
It notably allows you to automatically add a frontMatter.authors
field to your docs
(in case defining the list of authors manually is not an option)
async function getAuthors(filePath: string) {
// Do whatever you want here to create a list of authors for each file
// you can read Git history if you want, or any other source
return [
{name: "Seb",picture: "https://github.com/slorber.png"},
{name: "Joshua",picture: "https://github.com/Josh-Cena.png"}
]
}
const config: Config = {
markdown: {
parseFrontMatter: async (params) => {
const result = await params.defaultParseFrontMatter(params);
const authors = await getAuthors(params.filePath);
return {
...result,
frontMatter: {
...result.frontMatter,
authors,
}
}
}
},
};
export default config;
Then you can swizzle the doc component you want and render that front matter:
yarn docusaurus swizzle @docusaurus/theme-classic DocItem/Footer --wrap --typescript
import React from 'react';
import Footer from '@theme-original/DocItem/Footer';
import type FooterType from '@theme/DocItem/Footer';
import type { WrapperProps } from '@docusaurus/types';
import { useDoc } from '@docusaurus/theme-common/internal';
type Props = WrapperProps<typeof FooterType>;
export default function FooterWrapper(props: Props): JSX.Element {
const doc = useDoc();
const authors = (doc.frontMatter as any).authors as {
name: string;
picture: string;
}[];
return (
<>
<div style={{marginTop: 20, marginBottom: 20}}>
Authors:
<ul>
{authors.map((author, i) => (
<li key={i} style={{display: "flex",flexDirection: "row", alignItems: "center", padding: 5}}>
<img
src={author.picture}
style={{ width: 40, height: 40, borderRadius: 40, marginRight: 10}}
/>
<span>{author.name}</span>
</li>
))}
</ul>
</div>
<Footer {...props} />
</>
);
}
Result:
Runnable sandbox: https://stackblitz.com/edit/github-revkph?file=src%2Ftheme%2FDocItem%2FFooter%2Findex.tsx,docusaurus.config.ts
Considering it is possible to implement in userland thanks to existing Docusaurus primitives, if we ever add this feature to Docusaurus core, this would only be an "opinionated shortcut" to the above solution.
By opinionated I mean: limited to reading history on Git (and not other VCS), and displaying the authors in some predefined place such as the footer. This also means our opinions won't please everyone.
For this reason, there's no reason to rush implementing this feature officially. I'd rather see a few successful userland implementations first, so that we understand the common denominator of the solutions, which would help us craft a good opinionated solution that please a maximum number of users.
This is also related to https://github.com/facebook/docusaurus/issues/8657 which asks us to render some extra metadata in the blog post footer. Similarly parseFrontMatter
can be used.