Skip to content

Commit

Permalink
fix: automated DocsButton (#1188)
Browse files Browse the repository at this point in the history
Co-authored-by: Alejandra Quetzalli <alejandra.olvera.novack@gmail.com>
Co-authored-by: Lukasz Gornicki <lpgornicki@gmail.com>
  • Loading branch information
3 people authored Jan 20, 2023
1 parent cd89336 commit 1b82874
Show file tree
Hide file tree
Showing 26 changed files with 111 additions and 386 deletions.
2 changes: 0 additions & 2 deletions components/MDX.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import Figure from '../components/Figure'
import Profile from '../components/Profile'
import GeneratorInstallation from '../components/GeneratorInstallation'
import NewsletterSubscribe from '../components/NewsletterSubscribe'
import DocsButton from '../components/buttons/DocsButton';
import { DocsCards } from '../components/docs/DocsCards'

let mermaidInitialized = false;
Expand Down Expand Up @@ -68,7 +67,6 @@ function getMDXComponents() {
CodeBlock,
ChapterSuggestions,
YouTube,
DocsButton,
Remember,
Warning,
Sponsors,
Expand Down
37 changes: 12 additions & 25 deletions components/buttons/DocsButton.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,12 @@
import Link from 'next/link';

export default function DocsButton({ suggestions = [], className }) {
if (suggestions.length === 0) {
return null;
}

const backPages = suggestions.filter(
(suggestion) => suggestion.type === 'back'
);
const nextPages = suggestions.filter(
(suggestion) => suggestion.type === 'next'
);

export default function DocsButton({ post, className='' }) {
return (
<div className={`flex flex-row ${className}`}>
<div className="pr-2 flex flex-col grow w-full">
{backPages.map((suggestion, key) => (
<Link href={suggestion.href} key={key} passHref>
<div className={`flex flex-row gap-4 mb-4 h-full ${className}`}>
<div className="w-1/2 h-auto">
{ post?.prevPage && <Link href={post.prevPage.href} passHref>
<a>
<div className="p-4 rounded shadow-md border border-gray-200 transition-all duration-300 ease-in-out hover:shadow-lg hover:border-gray-300 mb-4 text-center lg:text-left cursor-pointer">
<div className="p-4 rounded shadow-md border border-gray-200 transition-all duration-300 ease-in-out hover:shadow-lg hover:border-gray-300 text-center lg:text-left cursor-pointer">
<div className="text-secondary-500">
<svg
xmlns="http://www.w3.org/2000/svg"
Expand All @@ -38,17 +26,16 @@ export default function DocsButton({ suggestions = [], className }) {
Go Back
</div>
</div>
<div className="font-medium text-base my-2">{suggestion.title}</div>
<div className="font-medium text-base my-2">{post.prevPage.title}</div>
</div>
</a>
</Link>
))}
}
</div>
<div className="pl-2 flex flex-col grow w-full">
{nextPages.map((suggestion, key) => (
<Link href={suggestion.href} key={key} passHref>
<div className="w-1/2 h-auto">
{ post?.nextPage && <Link href={post.nextPage.href} className='h-auto' passHref>
<a>
<div className="p-4 rounded shadow-md border border-gray-200 transition-all duration-300 ease-in-out hover:shadow-lg hover:border-gray-300 mb-4 text-center lg:text-right cursor-pointer">
<div className="p-4 rounded shadow-md border border-gray-200 transition-all duration-300 ease-in-out hover:shadow-lg hover:border-gray-300 text-center lg:text-right cursor-pointer">
<div className="text-secondary-500">
<div className="font-bold my-auto text-sm inline uppercase">
Up Next
Expand All @@ -68,11 +55,11 @@ export default function DocsButton({ suggestions = [], className }) {
/>
</svg>
</div>
<div className="font-medium text-base my-2">{suggestion.title}</div>
<div className="font-medium text-base my-2">{post.nextPage.title}</div>
</div>
</a>
</Link>
))}
}
</div>
</div>
);
Expand Down
6 changes: 5 additions & 1 deletion components/layout/DocsLayout.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import DocsContext from '../../context/DocsContext'
import TOC from '../TOC'
import DocsNav from '../navigation/DocsNav'
import DocsMobileMenu from '../navigation/DocsMobileMenu'
import DocsButton from '../buttons/DocsButton'
import NavBar from '../navigation/NavBar'
import ArrowRight from '../icons/ArrowRight'
import Feedback from '../Feedback'
Expand All @@ -23,7 +24,7 @@ function generateEditLink(post) {
return <a target="_blank" rel="noopener noreferrer" href={`https://github.com/asyncapi/website/blob/master/pages${post.isIndex ? post.slug + '/index' : post.slug}.md`} className="ml-1 underline">Edit this page on GitHub</a>
}

function buildNavTree(navItems) {
export function buildNavTree(navItems) {
const tree = {
'welcome': {
item: { title: 'Welcome', weight: 0, isRootSection: true, isSection: true, rootSectionId: 'welcome', sectionWeight: 0, slug: '/docs' },
Expand Down Expand Up @@ -185,6 +186,9 @@ export default function DocsLayout({ post, navItems = {}, children }) {
/>
{ children }
</article>
<div>
<DocsButton post={post} />
</div>
<div className="">
<Feedback />
</div>
Expand Down
97 changes: 90 additions & 7 deletions components/layout/Layout.js
Original file line number Diff line number Diff line change
@@ -1,40 +1,123 @@
import { useRouter } from 'next/router'
import DocsLayout from './DocsLayout'
import DocsLayout, {buildNavTree} from './DocsLayout'
import BlogLayout from './BlogLayout'
import JobsLayout from './JobsLayout'
import GenericPostLayout from './GenericPostLayout'
import BlogContext from '../../context/BlogContext'
import JobsContext from '../../context/JobsContext'
import { getPostBySlug, getAllPosts } from '../../lib/api'
import { getPostBySlug, getAllPosts, getDocBySlug } from '../../lib/api'

export default function Layout({ children }) {
const { pathname } = useRouter()
const posts = getAllPosts()
const allDocPosts = posts.filter(p => p.slug.startsWith('/docs/'))
const treePosts = buildNavTree(allDocPosts) // builds a NavTree of all DocPosts to structurize the order of pages
let structuredPosts = [] // stores the list of DocPosts in the order it has to be shown

// A recursion function, works on the logic of Depth First Search to traverse all the root and child posts of the
// DocTree to get sequential order of the Doc Posts
const convertDocPosts = (docObject) => {
let docsArray = []
// certain entries in the DocPosts are either a parent to many posts or itself a post.
docsArray.push(docObject?.item || docObject)
if(docObject.children){
let children = docObject.children
Object.keys(children).forEach((child) => {
let docChildArray = convertDocPosts(children[child])
docsArray = [...docsArray, ...docChildArray]
})
}
return docsArray
}

// Traversing the whole DocTree and storing each post inside them in sequential order
Object.keys(treePosts).forEach((rootElement) => {
structuredPosts.push(treePosts[rootElement].item)
if(treePosts[rootElement].children){
let children = treePosts[rootElement].children
Object.keys(children).forEach((child) => {
let docChildArray = convertDocPosts(children[child])
structuredPosts = [...structuredPosts, ...docChildArray]
})
}
})
// Appending the content of welcome page pf Docs from the posts.json
structuredPosts[0] = posts.filter(p => p.slug === '/docs')[0]

// Traversing the strucutredPosts in order to add `nextPage` and `prevPage` details for each page
let countDocPages = structuredPosts.length
structuredPosts = structuredPosts.map((post, index) => {
// post item specifying the root Section or sub-section in the docs are excluded as
// they doesn't comprise any Doc Page or content to be shown in website.
if(post?.isRootElement || post?.isSection || index==0)
return post

let nextPage = {}, prevPage = {}
let docPost = post;

// checks whether the next page for the current docPost item exists or not
if(index+1<countDocPages){
// checks whether the next item inside structuredPosts is a rootElement or a sectionElement
// if yes, it goes again to a next to next item in structuredPosts to link the nextPage
if(!structuredPosts[index+1].isRootElement && !structuredPosts[index+1].isSection){
nextPage = {
title: structuredPosts[index+1].title,
href: structuredPosts[index+1].slug
}
}else{
nextPage = {
title: structuredPosts[index+2].title,
href: structuredPosts[index+2].slug
}
}
docPost = {...docPost, nextPage}
}

// checks whether the previous page for the current docPost item exists or not
if(index>0){
// checks whether the previous item inside structuredPosts is a rootElement or a sectionElement
// if yes, it goes again to a next previous item in structuredPosts to link the prevPage
if(!structuredPosts[index-1]?.isRootElement && !structuredPosts[index-1]?.isSection){
prevPage = {
title: structuredPosts[index-1].title,
href: structuredPosts[index-1].slug
}
docPost = {...docPost, prevPage}
}else{
// additonal check for the first page of Docs so that it doesn't give any Segementation fault
if(index-2>=0){
prevPage = {
title: structuredPosts[index-2].title,
href: structuredPosts[index-2].slug
}
docPost = {...docPost, prevPage}
}
}
}
return docPost
})

if (pathname.startsWith('/docs')) {
const posts = getAllPosts()
const post = getPostBySlug(pathname)
const post = getDocBySlug(structuredPosts, pathname)
return (
<DocsLayout post={post} navItems={posts.filter(p => p.slug.startsWith('/docs/'))}>
{children}
</DocsLayout>
)
} else if (pathname.startsWith('/blog/')) {
const posts = getAllPosts()
const post = getPostBySlug(pathname)
return (
<BlogLayout post={post} navItems={posts.filter(p => p.slug.startsWith('/blog/'))}>
{children}
</BlogLayout>
)
} else if (pathname === '/blog') {
const posts = getAllPosts()
return (
<BlogContext.Provider value={{ navItems: posts.filter(p => p.slug.startsWith('/blog/')) }}>
{children}
</BlogContext.Provider>
)
} else if (pathname === '/jobs') {
const posts = getAllPosts()
return (
<JobsContext.Provider value={{ navItems: posts.filter(p => p.slug.startsWith('/jobs/')) }}>
{children}
Expand Down
4 changes: 4 additions & 0 deletions lib/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,7 @@ export function getAllPosts() {
export function getPostBySlug(slug) {
return posts.find(post => post.slug === slug && !post.isSection)
}

export function getDocBySlug(structuredPosts, slug) {
return structuredPosts.find(post => post.slug === slug && !post.isSection)
}
15 changes: 0 additions & 15 deletions pages/docs/concepts/application.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,3 @@ The above diagram describes a message communication traveling through a channel
<Remember>
When writing your AsyncAPI document, make sure to describe what a user can do with your application; not what the application does. In other words, if your <em>application</em> is a <b>producer</b>, your AsyncAPI document should describe where users can subscribe to, to receive messages produced by your <b>producer</b> application.
</Remember>

<DocsButton
suggestions={[
{
href: '/docs/concepts/channel',
title: 'Channel',
type:'back',
},
{
href: '/docs/concepts/protocol',
title: 'Protocol',
type:'next',
}
]}
/>
16 changes: 0 additions & 16 deletions pages/docs/concepts/channel.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,3 @@ graph LR
C --> F[Consumer]
```
The diagram above shows the communication between a `producer` and `consumer`, with the producer sending a `message` through the `channel`. The channel then queues the message to the specific consumer.

---
<DocsButton
suggestions={[
{
href:'/docs/concepts/consumer',
type: 'back',
title: 'Consumer',
},
{
href:'/docs/concepts/application',
type: 'next',
title: 'Application',
}
]}
/>
16 changes: 0 additions & 16 deletions pages/docs/concepts/consumer.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,19 +30,3 @@ The above diagram depicts a sample flow of events from `producer` to `broker` to
<Remember>
<b>Subscribers</b> can also be <a href="https://www.asyncapi.com/docs/concepts/producer">producers</a>.
</Remember>

---
<DocsButton
suggestions={[
{
href:'/docs/concepts/producer',
type: 'back',
title: 'Producer',
},
{
href:'/docs/concepts/channel',
type: 'next',
title: 'Channel',
}
]}
/>
17 changes: 0 additions & 17 deletions pages/docs/concepts/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,3 @@ Do you have a documentation contributor question and you're wondering how to tag

Tag me in your AsyncAPI Doc PRs or [GitHub Discussions](https://github.com/asyncapi/community/discussions/categories/docs) via my GitHub handle, [`alequetzalli`](https://github.com/alequetzalli) 🐙.
</Remember>

---

<DocsButton
suggestions={[
{
href:'/docs',
type: 'back',
title: 'Welcome',
},
{
href:'/docs/concepts/server',
type: 'next',
title: 'Server',
}
]}
/>
16 changes: 0 additions & 16 deletions pages/docs/concepts/message.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,19 +33,3 @@ graph TD
```

Overall, `events` are `messages` but not all `messages` are `events`.

---
<DocsButton
suggestions={[
{
href:'/docs/concepts/protocol',
type: 'back',
title: 'Protocol',
},
{
href:'/docs/tutorials',
type: 'next',
title: 'Tutorials - Overview',
}
]}
/>
17 changes: 0 additions & 17 deletions pages/docs/concepts/producer.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,20 +32,3 @@ flowchart LR
```

In the diagram above, we see a producer publishing messages to a specific channel and a consumer subscribing to messages from that channel. We also have a second producer who publishes to one channel, but subscribes to messages from another.

---

<DocsButton
suggestions={[
{
href:'/docs/concepts/server',
type: 'back',
title: 'Server',
},
{
href:'/docs/concepts/consumer',
type: 'next',
title: 'Consumer',
}
]}
/>
15 changes: 0 additions & 15 deletions pages/docs/concepts/protocol.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,3 @@ The diagram above depicts the message exchange flow from `producer` to `broker`
The quality of service information rule is specified on a protocol level. Broker implementations and other involved actors must act accordingly.

In AsyncAPI documents, all protocol-specific details that the application follows can be described using [bindings](/docs/reference/specification/v2.5.0#definitionsBindings).

<DocsButton
suggestions={[
{
href:'/docs/concepts/application',
type: 'back',
title: 'Application',
},
{
href:'/docs/concepts/message',
type: 'next',
title: 'Message',
}
]}
/>
Loading

0 comments on commit 1b82874

Please sign in to comment.