From 8dac2f9047705331bff1499c0bc45d5cedc838c9 Mon Sep 17 00:00:00 2001 From: atanasster Date: Tue, 9 Jun 2020 17:19:35 -0400 Subject: [PATCH] feat: list pages, date, tags fields --- README.md | 2 + core/instrument/src/babel/mdx-stories.ts | 16 +++- core/instrument/src/index.ts | 1 + core/loader/src/store.ts | 24 +++--- core/specification/src/configuration.ts | 64 ++++++++------- core/specification/src/stories.ts | 42 ++++++---- core/store/src/Store/Store.ts | 66 +++++++-------- core/store/src/types.ts | 14 ++-- examples/gatsby/.config/main.js | 9 +- .../stories/src/blogs/custom-docs-pages.mdx | 6 ++ .../storybook-6-no-docs/.storybook/main.js | 1 + .../src/components/Layout.tsx | 10 +-- .../gatsby-theme-stories/src/gatsby-node.ts | 82 +++++++++---------- .../src/templates/BlogPage.tsx | 1 - .../src/templates/DocPage.tsx | 1 - .../src/templates/PageList.tsx | 23 ++++++ .../src/templates/PagePage.tsx | 1 - ui/app/package.json | 1 + ui/app/src/Header/Header.tsx | 27 +++--- ui/app/src/Links/DocLink.tsx | 2 +- ui/app/src/Links/DocsLink.tsx | 4 +- ui/app/src/PageList/PageList.tsx | 30 +++++++ ui/app/src/PageList/PageListItem.tsx | 27 ++++++ ui/app/src/PageList/index.ts | 1 + ui/app/src/Sidebar/Sidebar.tsx | 32 ++++++-- ui/app/src/TagsList/TagsList.tsx | 19 +++++ ui/app/src/TagsList/index.ts | 1 + ui/app/src/index.ts | 2 + .../src/ThemeContext/ThemeContext.tsx | 10 ++- ui/components/src/typings.d.ts | 2 +- 30 files changed, 338 insertions(+), 183 deletions(-) create mode 100644 integrations/gatsby-theme-stories/src/templates/PageList.tsx create mode 100644 ui/app/src/PageList/PageList.tsx create mode 100644 ui/app/src/PageList/PageListItem.tsx create mode 100644 ui/app/src/PageList/index.ts create mode 100644 ui/app/src/TagsList/TagsList.tsx create mode 100644 ui/app/src/TagsList/index.ts diff --git a/README.md b/README.md index a234d8ff4..df43cc7b9 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,8 @@ There are many developments that have contributed to the creation of `component- - [theme-ui](https://theme-ui.com) is the driving force for standardizing `react` theming and design systems. `theme-ui` is used by our project as the theming and components founding block. +- [mdx](https://mdxjs.com) is driving the adoption of JSX in Markdown and allows writing rich, interactive documentation pages. + # Roadmap - [x] Core packages diff --git a/core/instrument/src/babel/mdx-stories.ts b/core/instrument/src/babel/mdx-stories.ts index b4edb0167..5508c9dea 100644 --- a/core/instrument/src/babel/mdx-stories.ts +++ b/core/instrument/src/babel/mdx-stories.ts @@ -22,6 +22,19 @@ import { import { componentsFromParams } from '../misc/component-attributes'; +const serialzeProp = (prop: any): string => { + if (prop instanceof Date) { + return `"${prop.toString()}"`; + } + if (Array.isArray(prop)) { + return prop.map(p => serialzeProp(p)); + } + if (typeof prop === 'string') { + return `"${jsStringEscape(prop)}"`; + } + return prop; +}; + export const extractMDXStories = (props: any) => ( ast: File, _options: Required, @@ -79,13 +92,14 @@ export const extractMDXStories = (props: any) => ( exports: {}, packages: {}, }; + if (props) { store.exports.default = { story: Object.keys(props).reduce((acc: object, key: string) => { const prop = props[key]; return { ...acc, - [key]: typeof prop === 'string' ? `"${jsStringEscape(prop)}"` : prop, + [key]: serialzeProp(prop), }; }, {}), }; diff --git a/core/instrument/src/index.ts b/core/instrument/src/index.ts index 877df081d..d7aa775a5 100644 --- a/core/instrument/src/index.ts +++ b/core/instrument/src/index.ts @@ -176,6 +176,7 @@ export const parseStories = async ( mergedOptions, ); const { stories, doc, components, exports, packages } = store; + debugger; const exportsSource = extractStoryExports(exports); let transformed = ` diff --git a/core/loader/src/store.ts b/core/loader/src/store.ts index ab656bc2a..1b6d744b3 100644 --- a/core/loader/src/store.ts +++ b/core/loader/src/store.ts @@ -4,6 +4,9 @@ import { BuildConfiguration, RunConfiguration, StoriesDoc, + defPageType, + Pages, + PageType, } from '@component-controls/specification'; import { LoadingDocStore } from '@component-controls/instrument'; export interface LoadingStore { @@ -31,8 +34,7 @@ export interface LoadingStore { stores: (Partial> & { filePath: string; })[]; - getDocs: () => StoriesDoc[]; - getBlogs: () => StoriesDoc[]; + getDocs: (pageType: PageType) => Pages; } class Store implements LoadingStore { @@ -41,17 +43,15 @@ class Store implements LoadingStore { packages: LoadingStore['packages'] = {}; config: LoadingStore['config'] = {}; buildConfig: LoadingStore['buildConfig'] = {}; - getDocs = () => + getDocs = (pageType: PageType) => this.stores - .filter( - store => - store?.doc && - (store.doc?.type === undefined || store.doc?.type === 'story'), - ) - .map(store => store.doc as StoriesDoc); - getBlogs = () => - this.stores - .filter(store => store?.doc && store.doc?.type === 'blog') + .filter(store => { + if (store?.doc) { + const { type = defPageType } = store.doc; + return type === pageType; + } + return false; + }) .map(store => store.doc as StoriesDoc); } diff --git a/core/specification/src/configuration.ts b/core/specification/src/configuration.ts index 75f8aa99c..4e1ffad34 100644 --- a/core/specification/src/configuration.ts +++ b/core/specification/src/configuration.ts @@ -1,5 +1,23 @@ import { StoryRenderFn } from './utility'; +export type PageType = 'story' | 'blog' | 'page'; + +export interface PageConfiguration { + /** + * base url path for the page + */ + basePath?: string; + + /** + * label - used for menu labels + */ + label?: string; +} + +export interface PagesConfiguration { + [key: string]: PageConfiguration; +} + /** * global configuration used at build time * stored in a file named main.js/main.ts @@ -14,11 +32,7 @@ export interface BuildConfiguration { /** * base url path for API documentation pages. Default is "docs/" */ - docsPath?: string; - /** - * base url path for blogs pages. Default is "blogs/" - */ - blogsPath?: string; + pages?: { [key: string]: Pick }; } /** @@ -70,31 +84,13 @@ export interface RunConfiguration { */ siteImage?: string; - /** - * Label for docs menu. Default is Docs - */ - docsLabel?: string; + pages?: PagesConfiguration; - /** - * Label for blog menu. Default is Blog - */ blogsLabel?: string; /** * story sorting function */ storySort?: (a: string, b: string) => number; - - /** - * the following used in both run time and build time. Should be set in the build config and - * they will be carried to the run config - /** - * base url path for API documentation pages. Default is "docs/" - */ - docsPath?: string; - /** - * base url path for blogs pages. Default is "blogs/" - */ - blogsPath?: string; } export const defaultRunConfig: RunConfiguration = { @@ -107,11 +103,23 @@ export const defaultRunConfig: RunConfiguration = { 'Component controls stories. Write your components documentation with MDX and JSX. Design, develop, test and review in a single site.', siteLanguage: 'en', author: '@component-controls', - docsLabel: 'Docs', - blogsLabel: 'Blog', + pages: { + story: { + label: 'Docs', + }, + blog: { + label: 'Blog', + }, + }, }; export const defaultBuildConfig: BuildConfiguration = { - docsPath: 'docs/', - blogsPath: 'blogs/', + pages: { + story: { + basePath: 'docs/', + }, + blog: { + basePath: 'blogs/', + }, + }, }; diff --git a/core/specification/src/stories.ts b/core/specification/src/stories.ts index e0c3e6a74..65d5c3b02 100644 --- a/core/specification/src/stories.ts +++ b/core/specification/src/stories.ts @@ -1,7 +1,7 @@ import { CodeLocation, PackageInfo, StoryRenderFn } from './utility'; import { StoryComponent } from './components'; import { ComponentControls } from './controls'; -import { RunConfiguration } from './configuration'; +import { RunConfiguration, PageType } from './configuration'; /** * an identifier/variable.argument in the source code */ @@ -149,6 +149,7 @@ export interface Stories { [id: string]: Story; } +export const defPageType: PageType = 'story'; /** * a group of stories. Usually multiple stories are in one csf file * and the 'group' is the default export @@ -162,7 +163,7 @@ export interface StoriesDoc { /** * document type - blogs a and stories. By default - storie */ - type?: 'story' | 'blog' | 'page'; + type?: PageType; /** * list of stories contained in the file/groups */ @@ -253,6 +254,22 @@ export interface StoriesDoc { * side menu - hide */ menu?: boolean | string; + + /** + * optional date the document was created + */ + date?: string; + + /** + * comma-separated list of document tags, used for search + */ + tags?: string; + + /** + * document author + */ + author?: string; + [name: string]: any; } @@ -271,6 +288,8 @@ export interface StoryDocs { [title: string]: StoriesDoc; } +export type Pages = StoriesDoc[]; + /** * list of stories */ @@ -314,20 +333,14 @@ export interface StoriesStore { } export const getDocPath = ( + pageType: PageType, doc?: StoriesDoc, config?: RunConfiguration, ): string => { - const { docsPath = '' } = config || {}; - return doc ? doc.route || `/${docsPath}${doc.title?.toLowerCase()}/` : ''; + const { basePath = '' } = config?.pages?.[pageType] || {}; + return doc ? doc.route || `/${basePath}${doc.title?.toLowerCase()}/` : ''; }; -export const getBlogPath = ( - doc?: StoriesDoc, - config?: RunConfiguration, -): string => { - const { blogsPath = '' } = config || {}; - return doc ? doc.route || `/${blogsPath}${doc.title?.toLowerCase()}/` : ''; -}; export const getStoryPath = ( story?: Story, doc?: StoriesDoc, @@ -336,8 +349,7 @@ export const getStoryPath = ( if (!story) { return ''; } - const docsPath = config?.docsPath || ''; - return doc - ? doc.route || `/${docsPath}${doc.title?.toLowerCase()}/#${story.id}` - : ''; + + const docsPath = getDocPath('story', doc, config); + return `${docsPath}/#${story.id}`; }; diff --git a/core/store/src/Store/Store.ts b/core/store/src/Store/Store.ts index ed5634594..671456ed1 100644 --- a/core/store/src/Store/Store.ts +++ b/core/store/src/Store/Store.ts @@ -1,11 +1,12 @@ import { StoriesStore, - StoryDocs, + Pages, StoriesDoc, RunConfiguration, getDocPath, getStoryPath, - getBlogPath, + PageType, + defPageType, } from '@component-controls/specification'; import { BroadcastChannel } from 'broadcast-channel'; import { @@ -38,9 +39,7 @@ export class Store implements StoryStore { private channel: BroadcastChannel | undefined; private observers: StoreObserver[]; private moduleId: number; - private _docs: StoryDocs = {}; - private _blogs: StoryDocs = {}; - + private _cachedPages: { [key: string]: Pages } = {}; private _firstStory: string | undefined; private _firstDoc: string | undefined; /** @@ -108,28 +107,6 @@ export class Store implements StoryStore { //point to first story of first doc this._firstStory = doc.stories?.[0]; } - if (this.loadedStore.docs) { - this._docs = Object.keys(this.loadedStore.docs).reduce( - (acc: StoryDocs, key: string) => { - const doc: StoriesDoc | undefined = this.loadedStore?.docs[key]; - if (doc && (doc.type === undefined || doc.type === 'story')) { - return { ...acc, [key]: doc }; - } - return acc; - }, - {}, - ); - this._blogs = Object.keys(this.loadedStore.docs).reduce( - (acc: StoryDocs, key: string) => { - const doc: StoriesDoc | undefined = this.loadedStore?.docs[key]; - if (doc && doc.type === 'blog') { - return { ...acc, [key]: doc }; - } - return acc; - }, - {}, - ); - } } }; /** @@ -191,9 +168,31 @@ export class Store implements StoryStore { /** * returns all the documentation files */ - getDocs = () => this._docs; + getDocs = (): Pages => this.getPageList('story'); - getBlogs = () => this._blogs; + getBlogs = (): Pages => this.getPageList('blog'); + + getPageList = (type: PageType = defPageType): Pages => { + if (this.loadedStore?.docs) { + if (!this._cachedPages[type]) { + this._cachedPages[type] = Object.keys(this.loadedStore.docs).reduce( + (acc: StoriesDoc[], key: string) => { + const doc: StoriesDoc | undefined = this.loadedStore?.docs[key]; + if (doc) { + const { type: docTYpe = defPageType } = doc; + if (docTYpe === type) { + return [...acc, doc]; + } + } + return acc; + }, + [], + ); + } + return this._cachedPages[type]; + } + return []; + }; get config(): RunConfiguration | undefined { return this.loadedStore?.config; @@ -205,14 +204,11 @@ export class Store implements StoryStore { return this._firstDoc; } - getDocPath = (name: string): string => { + getPagePath = (pageType: PageType, name: string): string => { const doc = this.getStoryDoc(name); - return getDocPath(doc, this.config); - }; - getBlogPath = (name: string): string => { - const doc = this.getStoryDoc(name); - return getBlogPath(doc, this.config); + return getDocPath(pageType, doc, this.config); }; + getStoryPath = (storyId: string): string => { const story = this.getStory(storyId); if (!story) { diff --git a/core/store/src/types.ts b/core/store/src/types.ts index 8c4325298..ec6abdadf 100644 --- a/core/store/src/types.ts +++ b/core/store/src/types.ts @@ -1,8 +1,10 @@ import { StoriesStore, Story, - StoryDocs, + StoriesDoc, + Pages, RunConfiguration, + PageType, } from '@component-controls/specification'; /** @@ -15,14 +17,14 @@ export type StoreObserver = (storyId?: string, propName?: string) => void; export interface StoryStore { getStore: () => StoriesStore | undefined; getStory: (storyId: string) => Story | undefined; - getStoryDoc: (name: string) => StoryDocs | undefined; - getDocs: () => StoryDocs; - getBlogs: () => StoryDocs; + getStoryDoc: (name: string) => StoriesDoc | undefined; + getDocs: () => Pages; + getBlogs: () => Pages; + getPageList: (type: PageType) => Pages; config: RunConfiguration | undefined; firstStory: string | undefined; firstDoc: string | undefined; - getDocPath: (name: string) => string; - getBlogPath: (name: string) => string; + getPagePath: (pageType: PageType, name: string) => string; getStoryPath: (storyId: string) => string; updateStoryProp: ( storyId: string, diff --git a/examples/gatsby/.config/main.js b/examples/gatsby/.config/main.js index b8dc884c9..9e8e6833f 100644 --- a/examples/gatsby/.config/main.js +++ b/examples/gatsby/.config/main.js @@ -5,5 +5,12 @@ module.exports = { '../src/stories/*.stories.(js|jsx|tsx|mdx)', '../../../ui/components/src/**/*.stories.(js|jsx|tsx|mdx)', ], - docsPath: 'page/', + pages: { + story: { + basePath: 'page/', + }, + blog: { + basePath: 'blogs/', + }, + }, }; diff --git a/examples/stories/src/blogs/custom-docs-pages.mdx b/examples/stories/src/blogs/custom-docs-pages.mdx index 8049ef79d..eed06a333 100644 --- a/examples/stories/src/blogs/custom-docs-pages.mdx +++ b/examples/stories/src/blogs/custom-docs-pages.mdx @@ -1,6 +1,12 @@ --- title: Custom documentation pages for storybookjs type: blog +date: 2019-11-07 +author: atanasster +description: Storybookjs plugin that overcomes some "docs page" storybook architecture flaws to display multiple documentation pages. +tags: + - storybook + - stories --- diff --git a/examples/storybook-6-no-docs/.storybook/main.js b/examples/storybook-6-no-docs/.storybook/main.js index 2be7bf609..1d0f4507f 100644 --- a/examples/storybook-6-no-docs/.storybook/main.js +++ b/examples/storybook-6-no-docs/.storybook/main.js @@ -34,6 +34,7 @@ module.exports = { '../../../plugins/axe-plugin/src/stories/**/*.stories.(js|jsx|tsx|mdx)', '../../stories/src/**/*.stories.(js|jsx|tsx|mdx)', '../stories/**/*.stories.(js|jsx|tsx|mdx)', + // '../../stories/src/blogs/*.mdx', ], webpackFinal: (config = {}, options = {}) => { return { diff --git a/integrations/gatsby-theme-stories/src/components/Layout.tsx b/integrations/gatsby-theme-stories/src/components/Layout.tsx index 116ccff55..949ad5131 100644 --- a/integrations/gatsby-theme-stories/src/components/Layout.tsx +++ b/integrations/gatsby-theme-stories/src/components/Layout.tsx @@ -1,7 +1,6 @@ /** @jsx jsx */ import { FC, useMemo } from 'react'; import { jsx } from 'theme-ui'; -import { Global } from '@emotion/core'; import { ThemeProvider } from '@component-controls/components'; import { App } from '@component-controls/app'; import { @@ -14,7 +13,7 @@ const bundle = require('@component-controls/webpack-compile/bundle'); import { GatsbyLink } from './GatsbyLink'; interface LayoutProps { - docId: string; + docId?: string; } export const Layout: FC = ({ docId, children }) => { @@ -29,13 +28,6 @@ export const Layout: FC = ({ docId, children }) => { return ( - ({ - a: { - transition: `all 0.3s ease-in-out`, - }, - })} - /> diff --git a/integrations/gatsby-theme-stories/src/gatsby-node.ts b/integrations/gatsby-theme-stories/src/gatsby-node.ts index 0a10eb3c5..a7a32a54d 100644 --- a/integrations/gatsby-theme-stories/src/gatsby-node.ts +++ b/integrations/gatsby-theme-stories/src/gatsby-node.ts @@ -1,4 +1,4 @@ -import { getDocPath, getBlogPath } from '@component-controls/specification'; +import { getDocPath, PageType } from '@component-controls/specification'; import { compile, @@ -20,61 +20,53 @@ exports.createPages = async ( presets: defaultPresets, configPath: options.configPath, }; + const pageTemplates: Record = { + story: require.resolve(`../src/templates/DocPage.tsx`), + blog: require.resolve(`../src/templates/BlogPage.tsx`), + page: require.resolve(`../src/templates/PagePage.tsx`), + }; + const listTemplates: Record = { + story: require.resolve(`../src/templates/DocPage.tsx`), + blog: require.resolve(`../src/templates/PageList.tsx`), + page: require.resolve(`../src/templates/PagePage.tsx`), + }; + const { store } = process.env.NODE_ENV === 'development' ? await watch(config) : await compile(config); if (store) { - const docTemplate = require.resolve(`../src/templates/DocPage.tsx`); - const { docsPath = '', blogsPath = '' } = store.buildConfig || {}; - const docs = store.getDocs(); - docs.forEach(doc => { - createPage({ - path: getDocPath(doc, store.buildConfig), - component: docTemplate, - context: { - doc: doc.title, - }, + const { pages = {} } = store.buildConfig || {}; + Object.keys(pages).forEach(type => { + const page = pages[type]; + const pageType = type as PageType; + const docs = store.getDocs(pageType); + docs.forEach(doc => { + createPage({ + path: getDocPath(pageType, doc, store.buildConfig), + component: pageTemplates[pageType], + context: { + doc: doc.title, + }, + }); }); + if (docs.length) { + const docsPage = docs.find(doc => doc?.route === `/${page.basePath}`); + createPage({ + path: `/${page.basePath}`, + component: listTemplates[pageType], + context: { + type: pageType, + doc: docsPage?.title, + }, + }); + } }); - if (docs.length) { - const docsPage = docs.find(doc => doc?.route === `/${docsPath}`); - createPage({ - path: `/${docsPath}`, - component: docTemplate, - context: { - doc: docsPage?.title, - }, - }); - } - const blogTemplate = require.resolve(`../src/templates/BlogPage.tsx`); - const blogs = store.getBlogs(); - - blogs.forEach(blog => { - createPage({ - path: getBlogPath(blog, store.buildConfig), - component: blogTemplate, - context: { - doc: blog.title, - }, - }); - }); - if (blogs.length) { - const blogsPage = blogs.find(blog => blog?.route === `/${blogsPath}`); - createPage({ - path: `/${blogsPath}`, - component: blogTemplate, - context: { - doc: blogsPage?.doc?.title, - }, - }); - } - const pageTemplate = require.resolve(`../src/templates/PagePage.tsx`); const homePage = store.stores.find(s => s.doc?.route === '/'); createPage({ path: `/`, - component: pageTemplate, + component: pageTemplates['page'], context: { doc: homePage?.doc?.title, }, diff --git a/integrations/gatsby-theme-stories/src/templates/BlogPage.tsx b/integrations/gatsby-theme-stories/src/templates/BlogPage.tsx index 5c81a3a38..007d8486d 100644 --- a/integrations/gatsby-theme-stories/src/templates/BlogPage.tsx +++ b/integrations/gatsby-theme-stories/src/templates/BlogPage.tsx @@ -4,7 +4,6 @@ import { Layout } from '../components/Layout'; interface BlogPageProps { pathContext: { - title: string; doc: string; }; } diff --git a/integrations/gatsby-theme-stories/src/templates/DocPage.tsx b/integrations/gatsby-theme-stories/src/templates/DocPage.tsx index 7c144d422..b78168b17 100644 --- a/integrations/gatsby-theme-stories/src/templates/DocPage.tsx +++ b/integrations/gatsby-theme-stories/src/templates/DocPage.tsx @@ -5,7 +5,6 @@ import { pages } from '../config/pages'; interface DocPageProps { pathContext: { - title: string; doc: string; }; } diff --git a/integrations/gatsby-theme-stories/src/templates/PageList.tsx b/integrations/gatsby-theme-stories/src/templates/PageList.tsx new file mode 100644 index 000000000..10d25e6b5 --- /dev/null +++ b/integrations/gatsby-theme-stories/src/templates/PageList.tsx @@ -0,0 +1,23 @@ +import React, { FC } from 'react'; + +import { PageType, defPageType } from '@component-controls/specification'; +import { PageList } from '@component-controls/app'; +import { Layout } from '../components/Layout'; + +interface PageListProps { + pathContext: { + type: PageType; + }; +} + +const PageListTemplate: FC = ({ + pathContext: { type = defPageType }, +}) => { + return ( + + + + ); +}; + +export default PageListTemplate; diff --git a/integrations/gatsby-theme-stories/src/templates/PagePage.tsx b/integrations/gatsby-theme-stories/src/templates/PagePage.tsx index e61a739c4..184a7b010 100644 --- a/integrations/gatsby-theme-stories/src/templates/PagePage.tsx +++ b/integrations/gatsby-theme-stories/src/templates/PagePage.tsx @@ -4,7 +4,6 @@ import { Layout } from '../components/Layout'; interface BlogPageProps { pathContext: { - title: string; doc: string; }; } diff --git a/ui/app/package.json b/ui/app/package.json index bceb47f2d..6da4db6fc 100644 --- a/ui/app/package.json +++ b/ui/app/package.json @@ -32,6 +32,7 @@ "dependencies": { "@component-controls/app-components": "^1.2.0", "@component-controls/blocks": "^1.2.0", + "@component-controls/components": "^1.2.0", "@component-controls/specification": "^1.2.0", "qs": "^6.9.4", "react": "^16.13.1", diff --git a/ui/app/src/Header/Header.tsx b/ui/app/src/Header/Header.tsx index e47d13df9..83b5f29c1 100644 --- a/ui/app/src/Header/Header.tsx +++ b/ui/app/src/Header/Header.tsx @@ -1,6 +1,8 @@ /** @jsx jsx */ import { FC, useContext } from 'react'; import { jsx, Flex, Text } from 'theme-ui'; +import { PageType } from '@component-controls/specification'; + import { Link, ColorMode, @@ -16,8 +18,7 @@ export const Header: FC = () => { const { SidebarToggle, collapsed, responsive } = useContext(SidebarContext); const { storeProvider } = useContext(BlockContext); const config = storeProvider.config; - const { docsPath, docsLabel, blogsPath, blogsLabel } = config || {}; - + const { pages = {} } = config || {}; return ( = () => { Home - {Object.keys(storeProvider.getDocs()).length > 0 && ( - - {docsLabel} - - )} - {Object.keys(storeProvider.getBlogs()).length > 0 && ( - - {blogsLabel} - - )} + {Object.keys(pages).map(type => { + const pageType = type as PageType; + if (Object.keys(storeProvider.getPageList(pageType)).length > 0) { + const page = pages[pageType]; + return ( + + {page.label} + + ); + } + return null; + })} {!responsive && } diff --git a/ui/app/src/Links/DocLink.tsx b/ui/app/src/Links/DocLink.tsx index f436b0173..396889ab2 100644 --- a/ui/app/src/Links/DocLink.tsx +++ b/ui/app/src/Links/DocLink.tsx @@ -15,7 +15,7 @@ export const DocLink: FC> = ({ ...props }) => { const { storeProvider } = useContext(BlockContext); - const href = storeProvider.getDocPath(id); + const href = storeProvider.getPagePath('story', id); return ( {children} diff --git a/ui/app/src/Links/DocsLink.tsx b/ui/app/src/Links/DocsLink.tsx index 71ab44085..9ad79a085 100644 --- a/ui/app/src/Links/DocsLink.tsx +++ b/ui/app/src/Links/DocsLink.tsx @@ -12,10 +12,10 @@ export const DocsLink: FC> = ({ }) => { const { storeProvider } = useContext(BlockContext); const config = storeProvider.config; - const { docsPath = '' } = config || {}; + const { basePath = '' } = config?.pages?.['story'] || {}; return ( - + {children} ); diff --git a/ui/app/src/PageList/PageList.tsx b/ui/app/src/PageList/PageList.tsx new file mode 100644 index 000000000..e038db9e3 --- /dev/null +++ b/ui/app/src/PageList/PageList.tsx @@ -0,0 +1,30 @@ +/** @jsx jsx */ +import { FC, useContext } from 'react'; +import { jsx } from 'theme-ui'; +import { PageType } from '@component-controls/specification'; +import { Title } from '@component-controls/components'; +import { PageContainer, BlockContext } from '@component-controls/blocks'; +import { PageListItem } from './PageListItem'; + +export interface PageListProps { + type: PageType; +} +export const PageList: FC = ({ type }) => { + const { storeProvider } = useContext(BlockContext); + const pages = storeProvider?.getPageList(type) || []; + const pageConfig = storeProvider?.config?.pages?.[type] || {}; + return ( + + {pageConfig.label} +
+ {pages.map(page => ( + + ))} +
+
+ ); +}; diff --git a/ui/app/src/PageList/PageListItem.tsx b/ui/app/src/PageList/PageListItem.tsx new file mode 100644 index 000000000..1c61ec92e --- /dev/null +++ b/ui/app/src/PageList/PageListItem.tsx @@ -0,0 +1,27 @@ +/** @jsx jsx */ +import { FC } from 'react'; +import { jsx, Flex } from 'theme-ui'; +import { StoriesDoc } from '@component-controls/specification'; +import { Subtitle } from '@component-controls/components'; +import { Link } from '@component-controls/app-components'; +import { TagsList } from '../TagsList'; + +export interface PageListItemProps { + link: string; + page: StoriesDoc; +} +export const PageListItem: FC = ({ page, link }) => { + const { tags = '', date } = page; + console.log(tags); + return ( + + + {page.title} + + +
{date ? new Date(date).toDateString() : ''}
+ +
+
+ ); +}; diff --git a/ui/app/src/PageList/index.ts b/ui/app/src/PageList/index.ts new file mode 100644 index 000000000..3bd5425d6 --- /dev/null +++ b/ui/app/src/PageList/index.ts @@ -0,0 +1 @@ +export * from './PageList'; diff --git a/ui/app/src/Sidebar/Sidebar.tsx b/ui/app/src/Sidebar/Sidebar.tsx index bad06c7fc..a4ccfedb1 100644 --- a/ui/app/src/Sidebar/Sidebar.tsx +++ b/ui/app/src/Sidebar/Sidebar.tsx @@ -12,7 +12,12 @@ import { MenuItem, Header, } from '@component-controls/app-components'; -import { StoryDocs, StoriesDoc } from '@component-controls/specification'; +import { + StoriesDoc, + PageType, + Pages, + defPageType, +} from '@component-controls/specification'; import { StoryStore } from '@component-controls/store'; export interface SidebarProps { @@ -20,11 +25,17 @@ export interface SidebarProps { * title element */ title?: React.ReactNode; + + /** + * page type + */ + type?: PageType; } const createMenuItem = ( storeProvider: StoryStore, doc: StoriesDoc, + type: PageType, levels: string[], parent?: MenuItems, item?: MenuItem, @@ -42,19 +53,23 @@ const createMenuItem = ( newItem.items = []; } else { newItem.id = doc.title; - newItem.href = storeProvider.getDocPath(doc.title); + newItem.href = storeProvider.getPagePath(type, doc.title); } parent.push(newItem); } return createMenuItem( storeProvider, doc, + type, levels.slice(1), sibling ? sibling.items : newItem.items, newItem, ); }; -export const SidebarBase: FC = ({ title: propsTitle }) => { +export const SidebarBase: FC = ({ + title: propsTitle, + type = defPageType, +}) => { const { doc } = useStoryContext({ id: '.' }); if (doc && doc.fullPage) { return null; @@ -65,20 +80,19 @@ export const SidebarBase: FC = ({ title: propsTitle }) => { const { siteTitle } = config || {}; const menuItems = useMemo(() => { if (storeProvider) { - const docs: StoryDocs = storeProvider.getDocs() || {}; - const docTitles: string[] = Object.keys(docs); - const menuItems = docTitles.reduce((acc: MenuItems, title: string) => { + const docs: Pages = storeProvider.getPageList(type); + const menuItems = docs.reduce((acc: MenuItems, doc: StoriesDoc) => { + const { title } = doc; const levels = title.split('/'); - const doc = docs[title]; if (doc.menu !== false) { - createMenuItem(storeProvider, doc, levels, acc); + createMenuItem(storeProvider, doc, type, levels, acc); } return acc; }, []); return menuItems; } return []; - }, [storeProvider]); + }, [type, storeProvider]); const [search, setSearch] = useState(undefined); return ( diff --git a/ui/app/src/TagsList/TagsList.tsx b/ui/app/src/TagsList/TagsList.tsx new file mode 100644 index 000000000..3968f37e1 --- /dev/null +++ b/ui/app/src/TagsList/TagsList.tsx @@ -0,0 +1,19 @@ +/** @jsx jsx */ +import { FC } from 'react'; +import { jsx, Flex } from 'theme-ui'; +import { Tag } from '@component-controls/components'; + +export interface TagsListProps { + tags?: string[]; +} +export const TagsList: FC = ({ tags }) => { + return tags ? ( + + {tags.map(tag => ( + + {tag} + + ))} + + ) : null; +}; diff --git a/ui/app/src/TagsList/index.ts b/ui/app/src/TagsList/index.ts new file mode 100644 index 000000000..88ab5e645 --- /dev/null +++ b/ui/app/src/TagsList/index.ts @@ -0,0 +1 @@ +export * from './TagsList'; diff --git a/ui/app/src/index.ts b/ui/app/src/index.ts index f0157f6a8..8fcb73e89 100644 --- a/ui/app/src/index.ts +++ b/ui/app/src/index.ts @@ -4,7 +4,9 @@ export * from './DocPage'; export * from './Footer'; export * from './Header'; export * from './Links'; +export * from './PageList'; export * from './PagePage'; export * from './SEO'; export * from './Sidebar'; export * from './SideContext'; +export * from './TagsList'; diff --git a/ui/components/src/ThemeContext/ThemeContext.tsx b/ui/components/src/ThemeContext/ThemeContext.tsx index ebf2f6d09..082e019db 100644 --- a/ui/components/src/ThemeContext/ThemeContext.tsx +++ b/ui/components/src/ThemeContext/ThemeContext.tsx @@ -1,7 +1,7 @@ import React from 'react'; -import { polaris } from '@theme-ui/presets'; +import polaris from '@theme-ui/preset-polaris'; import { get } from '@theme-ui/css'; -import { merge } from '@theme-ui/core'; +import { merge } from 'theme-ui'; import { ThemeProvider as ThemeUIProvider, Theme, @@ -47,7 +47,7 @@ const applyColorMode = (theme: Theme, dark?: boolean) => { return theme; } const modes = get(theme, 'colors.modes', {}); - return merge.all({}, theme, { + return merge(theme, { colors: get(modes, 'dark', {}), }); }; @@ -83,6 +83,10 @@ export const ThemeProvider: React.FC = ({ fontSizes: [12, 14, 16, 20, 24, 32, 42, 64, 96], styles: { ...defTheme.styles, + a: { + ...defTheme.styles.a, + transition: `all 0.3s ease-in-out`, + }, img: { ...defTheme.styles.img, maxWidth: '100%', diff --git a/ui/components/src/typings.d.ts b/ui/components/src/typings.d.ts index 8feb11e8a..a411ba8da 100644 --- a/ui/components/src/typings.d.ts +++ b/ui/components/src/typings.d.ts @@ -1,4 +1,4 @@ -declare module '@theme-ui/presets'; +declare module '@theme-ui/preset-polaris'; declare module '@theme-ui/core'; declare module '@mdx-js/runtime'; declare module '@theme-ui/css';