From 91caeff6c38a10b05a1383fed8fc7210b1f3d5de Mon Sep 17 00:00:00 2001 From: deme Date: Fri, 5 May 2023 15:33:17 -0300 Subject: [PATCH] [#809,docs][xl]: add LHS sidebar to docs --- site/components/DocsPagination.tsx | 44 +++ site/components/Layout.tsx | 8 +- site/components/MDXPage.tsx | 7 +- site/components/SiteToc.tsx | 36 +-- site/content/docs/creating-new-datasets.md | 66 +++++ .../docs/deploying-your-portaljs-app.md | 56 ++++ .../docs/{ => examples}/example-ckan.md | 0 .../{ => examples}/example-data-catalog.md | 0 .../example-frictionless-dataset.md.bkp | 0 site/content/docs/index.md | 264 +----------------- site/content/docs/searching-datasets.md | 105 +++++++ site/content/docs/showing-metadata.md | 36 +++ site/content/docs/sidebar.json | 27 ++ site/content/docs/sidebarTree.json | 19 -- site/pages/[...slug].tsx | 20 +- 15 files changed, 375 insertions(+), 313 deletions(-) create mode 100644 site/components/DocsPagination.tsx create mode 100644 site/content/docs/creating-new-datasets.md create mode 100644 site/content/docs/deploying-your-portaljs-app.md rename site/content/docs/{ => examples}/example-ckan.md (100%) rename site/content/docs/{ => examples}/example-data-catalog.md (100%) rename site/content/docs/{ => examples}/example-frictionless-dataset.md.bkp (100%) create mode 100644 site/content/docs/searching-datasets.md create mode 100644 site/content/docs/showing-metadata.md create mode 100644 site/content/docs/sidebar.json delete mode 100644 site/content/docs/sidebarTree.json diff --git a/site/components/DocsPagination.tsx b/site/components/DocsPagination.tsx new file mode 100644 index 00000000..d6db19c4 --- /dev/null +++ b/site/components/DocsPagination.tsx @@ -0,0 +1,44 @@ +export default function DocsPagination({ prev = '', next = '' }) { + return ( +
+ {prev && ( + + + + + Prev + + )} + {next && ( + + Next Lesson + + + + + )} +
+ ); +} diff --git a/site/components/Layout.tsx b/site/components/Layout.tsx index 300fa2a5..002c59f9 100644 --- a/site/components/Layout.tsx +++ b/site/components/Layout.tsx @@ -55,13 +55,13 @@ export default function Layout({ title, tableOfContents = [], urlPath, - siteMap = [] + sidebarTree = [] }: { children; title?: string; tableOfContents?; urlPath?: string; - siteMap?: []; + sidebarTree?: []; }) { // const { toc } = children.props; const { theme, setTheme } = useTheme(); @@ -134,7 +134,7 @@ export default function Layout({ href={`#${subSection.id}`} className={ isActive(subSection) - ? 'text-sky-500' + ? 'text-secondary' : 'hover:text-slate-600 dark:hover:text-slate-300' } > @@ -153,7 +153,7 @@ export default function Layout({ {/* LHS NAVIGATION */} {/* {showSidebar && ( */}
- +
{/* )} */} diff --git a/site/components/MDXPage.tsx b/site/components/MDXPage.tsx index e4cd130b..d9ae1566 100644 --- a/site/components/MDXPage.tsx +++ b/site/components/MDXPage.tsx @@ -1,5 +1,6 @@ -import { MDXRemote } from "next-mdx-remote"; -import layouts from "layouts"; +import { MDXRemote } from 'next-mdx-remote'; +import layouts from 'layouts'; +import DocsPagination from './DocsPagination'; export default function MDXPage({ source, frontMatter }) { const Layout = ({ children }) => { @@ -32,7 +33,7 @@ export default function MDXPage({ source, frontMatter }) {
- +
diff --git a/site/components/SiteToc.tsx b/site/components/SiteToc.tsx index 9d58c3b3..908f5c6d 100644 --- a/site/components/SiteToc.tsx +++ b/site/components/SiteToc.tsx @@ -1,6 +1,6 @@ -import Link from "next/link.js"; -import clsx from "clsx"; -import { Disclosure, Transition } from "@headlessui/react"; +import Link from 'next/link.js'; +import clsx from 'clsx'; +import { Disclosure, Transition } from '@headlessui/react'; export interface NavItem { name: string; @@ -40,15 +40,11 @@ function sortNavGroupChildren(items: Array) { } export const SiteToc: React.FC = ({ currentPath, nav }) => { - function isActiveItem(item: NavItem) { - return item.href === currentPath; - } - return ( ); @@ -56,30 +52,34 @@ export const SiteToc: React.FC = ({ currentPath, nav }) => { const NavComponent: React.FC<{ item: NavItem | NavGroup; - isActive: boolean; -}> = ({ item, isActive }) => { + currentPath: string; +}> = ({ item, currentPath }) => { + function isActiveItem(item: NavItem) { + return item.href === "/" + currentPath; + } + return !isNavGroup(item) ? ( {item.name} ) : ( - + {({ open }) => (
+ +> [!tip] +> In this tutorial we opted for storing content as markdown files and data as CSV files in the app, but PortalJS can have metadata, data and content stored anywhere. + +## Create an index page + +Now, let's create an index page. First, create a new folder `content/my-awesome-dataset/` and move `content/index.md` to it. Then, create a new file `content/index.md` and put the following content inside it: + +```markdown +# Welcome to my data portal! + +List of available datasets: + +- [My Awesome Dataset](/my-awesome-dataset) +- [My Incredible Dataset](/my-incredible-dataset) + +``` + +From the browser, access http://localhost:3000. You should see the following: + + + + \ No newline at end of file diff --git a/site/content/docs/deploying-your-portaljs-app.md b/site/content/docs/deploying-your-portaljs-app.md new file mode 100644 index 00000000..33e4f433 --- /dev/null +++ b/site/content/docs/deploying-your-portaljs-app.md @@ -0,0 +1,56 @@ +# Deploying your PortalJS app + +Finally, let's learn how to deploy PortalJS apps to Vercel or Cloudflare Pages. + +> [!tip] +> Although we are using Vercel and Cloudflare Pages in this tutorial, you can deploy apps in any hosting solution you want as a static website by running `npm run export` and distributing the contents of the `out/` folder. + +## Push to a GitHub repo + +The PortalJS app we built up to this point is stored locally. To allow Vercel or Cloudflare Pages to deploy it, we have to push it to GitHub (or another SCM supported by these hosting solutions). + +- Create a new repository under your GitHub account +- Add the new remote origin to your PortalJS app +- Push the app to the repository + +If you are not sure about how to do it, follow this guide: https://nextjs.org/learn/basics/deploying-nextjs-app/github + +> [!tip] +> You can also deploy using our Vercel deploy button. In this case, a new repository will be created under your GitHub account automatically. +> [Click here](#one-click-deploy) to scroll to the deploy button. + +## Deploy to Vercel + +The easiest way to deploy a PortalJS app is to use Vercel, a serverless platform for static and hybrid applications developed by the creators of Next.js. + +To deploy your PortalJS app: + +- Create a Vercel account by going to https://vercel.com/signup and choosing "Continue with GitHub" +- Import the repository you created for the PortalJS app at https://vercel.com/new +- During the setup process you can use the default settings - no need to change anything. + +When you deploy, your PortalJS app will start building. It should finish in under a minute. + +When it’s done, you’ll get deployment URLs. Click on one of the URLs and you should see your PortaJS app live. + +>[!tip] +> You can find a more in-depth explanation about this process at https://nextjs.org/learn/basics/deploying-nextjs-app/deploy + +### One-Click Deploy + +You can instantly deploy our example app to your Vercel account by clicking the button below: + +[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fdatopian%2Fportaljs%2Ftree%2Fmain%2Fexamples%2Flearn-example&project-name=my-data-portal&repository-name=my-data-portal&demo-title=PortalJS%20Learn%20Example&demo-description=PortalJS%20Learn%20Example%20-%20https%3A%2F%2Fportaljs.org%2Fdocs&demo-url=learn-example.portaljs.org&demo-image=https%3A%2F%2Fportaljs.org%2Fassets%2Fexamples%2Fbasic-example.png) + +This will create a new repository on your GitHub account and deploy it to Vercel. If you are following the tutorial, you can replicate the changes done on your local app to this new repository. + +## Deploy to Cloudflare Pages + +To deploy your PortalJS app to Cloudflare Pages, follow this guide: + +https://developers.cloudflare.com/pages/framework-guides/deploy-a-nextjs-site/#deploy-with-cloudflare-pages-1 + +Note that you don't have to change anything - just follow the steps, choosing the repository you created. + + + diff --git a/site/content/docs/example-ckan.md b/site/content/docs/examples/example-ckan.md similarity index 100% rename from site/content/docs/example-ckan.md rename to site/content/docs/examples/example-ckan.md diff --git a/site/content/docs/example-data-catalog.md b/site/content/docs/examples/example-data-catalog.md similarity index 100% rename from site/content/docs/example-data-catalog.md rename to site/content/docs/examples/example-data-catalog.md diff --git a/site/content/docs/example-frictionless-dataset.md.bkp b/site/content/docs/examples/example-frictionless-dataset.md.bkp similarity index 100% rename from site/content/docs/example-frictionless-dataset.md.bkp rename to site/content/docs/examples/example-frictionless-dataset.md.bkp diff --git a/site/content/docs/index.md b/site/content/docs/index.md index 94988fed..91d1f079 100644 --- a/site/content/docs/index.md +++ b/site/content/docs/index.md @@ -1,7 +1,3 @@ ---- -showSidebar: true ---- - # Getting Started Welcome to the PortalJS documentation! @@ -55,262 +51,4 @@ As soon as you save the file, the browser automatically updates the page with th -## Creating new datasets - -So far, the PortalJS app we created only has a single page displaying a dataset. Data catalogs and data portals generally showcase many different datasets. - -Let's explore how to add and display more datasets to our portal. - -### Pages in PortalJS - -As you have seen, in this example a dataset page is just a markdown file on disk plus a data file. - -To create a new data showcase page we just create a new markdown file in the `content/` folder and a new data file in the `public/` folder. - -Let's do that now. Create a `content/my-incredible-dataset` folder, and inside this new folder create a `index.md` file with the following content: - -```markdown -# My Incredible Dataset - -This is my incredible dataset. - -## Chart - - -``` - -Now, create a file in `public/` named `my-incredible-data.csv` and put the following content inside it: - -```bash -Year,Population (mi) -1980,227 -1990,249 -2000,281 -2010,309 -2020,331 -``` - -Note that pages are associated with a route based on their pathname, so, to see the new data page, access http://localhost:3000/my-incredible-dataset from the browser. You should see the following: - - - -> [!tip] -> In this tutorial we opted for storing content as markdown files and data as CSV files in the app, but PortalJS can have metadata, data and content stored anywhere. - -### Create an index page - -Now, let's create an index page. First, create a new folder `content/my-awesome-dataset/` and move `content/index.md` to it. Then, create a new file `content/index.md` and put the following content inside it: - -```markdown -# Welcome to my data portal! - -List of available datasets: - -- [My Awesome Dataset](/my-awesome-dataset) -- [My Incredible Dataset](/my-incredible-dataset) - -``` - -From the browser, access http://localhost:3000. You should see the following: - - - -## Search page - -Typing out every link in the index page will get cumbersome eventually, and as the portal grows, finding the datasets you are looking for on the index page will become harder and harder, for that we will need search functionality. - -### Creating a search page - -Luckily we have a component for that. Change your `content/index.md` file to this: - -``` -# Welcome to my data portal! - -List of available datasets: - - -``` - -Before you refresh the page, however, you will need to run the following command: - -``` -npm run mddb -``` - -This example makes use of the [markdowndb](https://github.com/datopian/markdowndb) library. For now the only thing you need to know is that you should run the command above everytime you make some change to `/content`. - -From the browser, access http://localhost:3000. You should see the following, you now have a searchable automatic list of your datasets: - -![](https://i.imgur.com/9HfSPIx.png) - -To make this catalog look even better, we can change the text that is being displayed for each dataset to a title. Let's do that by adding the "title" [frontmatter field](https://daily-dev-tips.com/posts/what-exactly-is-frontmatter/) to the first dataset in the list. Change `content/my-awesome-dataset/index.md` to the following: - -``` ---- -title: 'My awesome dataset' ---- - -# My Awesome Dataset - -Built with PortalJS - -## Table - - -``` - -Rerun `npm run mddb` and, from the browser, access http://localhost:3000. You should see the title appearing instead of the folder name: - -![](https://i.imgur.com/nvmSnJ5.png) - -Any frontmatter attribute that you add will automatically get indexed and be usable in the search box. - -### Adding filters - -Sometimes contextual search is not enough. Let's add a filter. To do so, lets add a new metadata field called "group", add it to your `content/my-incredible-dataset/index.md` like so: - - -``` ---- -group: 'Incredible' ---- - -# My Incredible Dataset - -This is my incredible dataset. - -## Chart - - -``` - -Also add it to your `content/my-awesome-dataset/index.md` like so: - -``` ---- -title: 'My awesome dataset' -group: 'Awesome' ---- - -# My Awesome Dataset - -Built with PortalJS - -## Table - -
-``` - -Now on your `content/index.md` you can add a "facet" to the `Catalog` component, like so: - -``` -# Welcome to my data portal! - -List of available datasets: - - -``` - -You now have a filter in your page with all possible values automatically added to it. - -![](https://i.imgur.com/p2miSdg.png) - -## Showing metadata - -If you go now to `http://localhost:3000/my-awesome-dataset`, you will see that we now have two titles on the page. That's because `title` is one of the default metadata fields supported by PortalJS. - -![](https://i.imgur.com/O145uuc.png) - -Change the content inside `/content/my-awesome-dataset/index.md` to this. - -``` ---- -title: 'My awesome dataset' -author: 'Rufus Pollock' -description: 'An awesome dataset displaying some awesome data' -modified: '2023-05-04' -files: ['data.csv'] -groups: ['Awesome'] ---- - -Built with PortalJS - -## Table - -
-``` - -Once you refresh the page at `http://localhost:3000/my-awesome-dataset` you should see something like this at the top: - -![](https://i.imgur.com/nvDYJQT.png) - -These are the standard metadata fields that will be shown at the top of the page if you add them. - -- `title` that gets displayed as a big header at the top of the page -- `author`, `description`, and `modified` which gets displayed below the title -- `files` that get displayed as a table with two columns: `File` which is linked directly to the file, and `Format` which show the file format. - -## Deploying your PortalJS app - -Finally, let's learn how to deploy PortalJS apps to Vercel or Cloudflare Pages. - -> [!tip] -> Although we are using Vercel and Cloudflare Pages in this tutorial, you can deploy apps in any hosting solution you want as a static website by running `npm run export` and distributing the contents of the `out/` folder. - -### Push to a GitHub repo - -The PortalJS app we built up to this point is stored locally. To allow Vercel or Cloudflare Pages to deploy it, we have to push it to GitHub (or another SCM supported by these hosting solutions). - -- Create a new repository under your GitHub account -- Add the new remote origin to your PortalJS app -- Push the app to the repository - -If you are not sure about how to do it, follow this guide: https://nextjs.org/learn/basics/deploying-nextjs-app/github - -> [!tip] -> You can also deploy using our Vercel deploy button. In this case, a new repository will be created under your GitHub account automatically. -> [Click here](#one-click-deploy) to scroll to the deploy button. - -### Deploy to Vercel - -The easiest way to deploy a PortalJS app is to use Vercel, a serverless platform for static and hybrid applications developed by the creators of Next.js. - -To deploy your PortalJS app: - -- Create a Vercel account by going to https://vercel.com/signup and choosing "Continue with GitHub" -- Import the repository you created for the PortalJS app at https://vercel.com/new -- During the setup process you can use the default settings - no need to change anything. - -When you deploy, your PortalJS app will start building. It should finish in under a minute. - -When it’s done, you’ll get deployment URLs. Click on one of the URLs and you should see your PortaJS app live. - ->[!tip] -> You can find a more in-depth explanation about this process at https://nextjs.org/learn/basics/deploying-nextjs-app/deploy - -#### One-Click Deploy - -You can instantly deploy our example app to your Vercel account by clicking the button below: - -[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fdatopian%2Fportaljs%2Ftree%2Fmain%2Fexamples%2Flearn-example&project-name=my-data-portal&repository-name=my-data-portal&demo-title=PortalJS%20Learn%20Example&demo-description=PortalJS%20Learn%20Example%20-%20https%3A%2F%2Fportaljs.org%2Fdocs&demo-url=learn-example.portaljs.org&demo-image=https%3A%2F%2Fportaljs.org%2Fassets%2Fexamples%2Fbasic-example.png) - -This will create a new repository on your GitHub account and deploy it to Vercel. If you are following the tutorial, you can replicate the changes done on your local app to this new repository. - -### Deploy to Cloudflare Pages - -To deploy your PortalJS app to Cloudflare Pages, follow this guide: - -https://developers.cloudflare.com/pages/framework-guides/deploy-a-nextjs-site/#deploy-with-cloudflare-pages-1 - -Note that you don't have to change anything - just follow the steps, choosing the repository you created. - + \ No newline at end of file diff --git a/site/content/docs/searching-datasets.md b/site/content/docs/searching-datasets.md new file mode 100644 index 00000000..a98f59bf --- /dev/null +++ b/site/content/docs/searching-datasets.md @@ -0,0 +1,105 @@ +# Searching datasets + +Typing out every link in the index page will get cumbersome eventually, and as the portal grows, finding the datasets you are looking for on the index page will become harder and harder, for that we will need search functionality. + +## Creating a search page + +Luckily we have a component for that. Change your `content/index.md` file to this: + +``` +# Welcome to my data portal! + +List of available datasets: + + +``` + +Before you refresh the page, however, you will need to run the following command: + +``` +npm run mddb +``` + +This example makes use of the [markdowndb](https://github.com/datopian/markdowndb) library. For now the only thing you need to know is that you should run the command above everytime you make some change to `/content`. + +From the browser, access http://localhost:3000. You should see the following, you now have a searchable automatic list of your datasets: + +![](https://i.imgur.com/9HfSPIx.png) + +To make this catalog look even better, we can change the text that is being displayed for each dataset to a title. Let's do that by adding the "title" [frontmatter field](https://daily-dev-tips.com/posts/what-exactly-is-frontmatter/) to the first dataset in the list. Change `content/my-awesome-dataset/index.md` to the following: + +``` +--- +title: 'My awesome dataset' +--- + +# My Awesome Dataset + +Built with PortalJS + +## Table + +
+``` + +Rerun `npm run mddb` and, from the browser, access http://localhost:3000. You should see the title appearing instead of the folder name: + +![](https://i.imgur.com/nvmSnJ5.png) + +Any frontmatter attribute that you add will automatically get indexed and be usable in the search box. + +## Adding filters + +Sometimes contextual search is not enough. Let's add a filter. To do so, lets add a new metadata field called "group", add it to your `content/my-incredible-dataset/index.md` like so: + +``` +--- +group: 'Incredible' +--- + +# My Incredible Dataset + +This is my incredible dataset. + +## Chart + + +``` + +Also add it to your `content/my-awesome-dataset/index.md` like so: + +``` +--- +title: 'My awesome dataset' +group: 'Awesome' +--- + +# My Awesome Dataset + +Built with PortalJS + +## Table + +
+``` + +Now on your `content/index.md` you can add a "facet" to the `Catalog` component, like so: + +``` +# Welcome to my data portal! + +List of available datasets: + + +``` + +You now have a filter in your page with all possible values automatically added to it. + +![](https://i.imgur.com/p2miSdg.png) + + diff --git a/site/content/docs/showing-metadata.md b/site/content/docs/showing-metadata.md new file mode 100644 index 00000000..c27e8cf7 --- /dev/null +++ b/site/content/docs/showing-metadata.md @@ -0,0 +1,36 @@ +# Showing metadata + +If you go now to `http://localhost:3000/my-awesome-dataset`, you will see that we now have two titles on the page. That's because `title` is one of the default metadata fields supported by PortalJS. + +![](https://i.imgur.com/O145uuc.png) + +Change the content inside `/content/my-awesome-dataset/index.md` to this. + +``` +--- +title: 'My awesome dataset' +author: 'Rufus Pollock' +description: 'An awesome dataset displaying some awesome data' +modified: '2023-05-04' +files: ['data.csv'] +groups: ['Awesome'] +--- + +Built with PortalJS + +## Table + +
+``` + +Once you refresh the page at `http://localhost:3000/my-awesome-dataset` you should see something like this at the top: + +![](https://i.imgur.com/nvDYJQT.png) + +These are the standard metadata fields that will be shown at the top of the page if you add them. + +- `title` that gets displayed as a big header at the top of the page +- `author`, `description`, and `modified` which gets displayed below the title +- `files` that get displayed as a table with two columns: `File` which is linked directly to the file, and `Format` which show the file format. + + diff --git a/site/content/docs/sidebar.json b/site/content/docs/sidebar.json new file mode 100644 index 00000000..3288439f --- /dev/null +++ b/site/content/docs/sidebar.json @@ -0,0 +1,27 @@ +[ + { + "name": "Getting started", + "children": [ + { + "name": "Setup", + "href": "/docs" + }, + { + "name": "Creating new datasets", + "href": "/docs/creating-new-datasets" + }, + { + "name": "Searching datasets", + "href": "/docs/searching-datasets" + }, + { + "name": "Showing metadata", + "href": "/docs/showing-metadata" + }, + { + "name": "Deploying your PortalJS app", + "href": "/docs/deploying-your-portaljs-app" + } + ] + } +] diff --git a/site/content/docs/sidebarTree.json b/site/content/docs/sidebarTree.json deleted file mode 100644 index 01843152..00000000 --- a/site/content/docs/sidebarTree.json +++ /dev/null @@ -1,19 +0,0 @@ -[ - { - "name": "Docs", - "href": "/docs", - "level": 0, - "children": [ - { - "name": "Getting Started", - "href": "/docs", - "level": 1 - }, - { - "name": "Creating new datasets", - "href": "/tutorial-i-creating-new-datasets", - "level": 1 - } - ] - } -] diff --git a/site/pages/[...slug].tsx b/site/pages/[...slug].tsx index 78a17969..1b7e3b3e 100644 --- a/site/pages/[...slug].tsx +++ b/site/pages/[...slug].tsx @@ -13,7 +13,7 @@ import { CustomAppProps } from './_app.jsx'; import computeFields from '@/lib/computeFields'; import { getAuthorsDetails } from '@/lib/getAuthorsDetails'; -export default function DRDPage({ source, meta, siteMap }) { +export default function Page({ source, meta, sidebarTree }) { source = JSON.parse(source); const router = useRouter(); @@ -32,7 +32,8 @@ export default function DRDPage({ source, meta, siteMap }) { @@ -61,6 +62,12 @@ export const getStaticProps: GetStaticProps = async ({ ); } + // Temporary, docs pages should present the LHS sidebar + if (dbFile.url_path.startsWith('docs')) { + frontMatter.showSidebar = true; + frontMatter.sidebarTreeFile = 'content/docs/sidebar.json'; + } + const source = fs.readFileSync(filePath, { encoding: 'utf-8' }); const { mdxSource } = await parse(source, 'mdx', {}); @@ -77,11 +84,12 @@ export const getStaticProps: GetStaticProps = async ({ if (frontMatterWithComputedFields?.showSidebar) { let sidebarTreeFile = frontMatterWithComputedFields?.sidebarTreeFile; + // Added this file funcionality so that we can control + // which items appear in the sidebar and the order via + // a json file if (sidebarTreeFile) { - const tree = fs.readFileSync(sidebarTreeFile, { encoding: "utf-8" }); - + const tree = fs.readFileSync(sidebarTreeFile, { encoding: 'utf-8' }); sidebarTree = JSON.parse(tree); - } else { const allPages = await mddb.getFiles({ extensions: ['md', 'mdx'] }); const pages = allPages.filter((p) => !p.metadata?.isDraft); @@ -95,7 +103,7 @@ export const getStaticProps: GetStaticProps = async ({ props: { source: JSON.stringify(mdxSource), meta: frontMatterWithComputedFields, - siteMap: sidebarTree, + sidebarTree, }, }; };