Skip to content

Commit

Permalink
feat: [XD-47]: Initial effort for component library docs
Browse files Browse the repository at this point in the history
  • Loading branch information
knagurski committed Dec 19, 2024
1 parent 71a1f2f commit f621827
Show file tree
Hide file tree
Showing 79 changed files with 1,020 additions and 347 deletions.
4 changes: 3 additions & 1 deletion apps/design-system/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@
"@harnessio/ui": "workspace:*",
"@harnessio/yaml-editor": "workspace:*",
"clsx": "^2.1.1",
"monaco-editor": "0.50.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-live": "^4.1.8",
"react-router-dom": "^6.26.0"
},
"devDependencies": {
Expand All @@ -30,7 +32,7 @@
"typescript": "~5.6.2",
"typescript-eslint": "^8.15.0",
"vite": "^6.0.3",
"monaco-editor": "0.50.0"
"vite-tsconfig-paths": "^5.1.4"
},
"lint-staged": {
"*.{js,jsx,ts,tsx}": [
Expand Down
12 changes: 5 additions & 7 deletions apps/design-system/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
import { FC } from 'react'
import { createBrowserRouter, Navigate, RouterProvider } from 'react-router-dom'

import { ExitConfirmProvider } from './context/exit-confirm-context.tsx'
import ViewPreview from './pages/view-preview/view-preview.tsx'
import ComponentPage from '@/pages/component-page.tsx'
import ViewPreview from '@/pages/view-preview/view-preview.tsx'
import DocsLayout from '@components/docs-layout/docs-layout.tsx'

const router = createBrowserRouter([
{ path: '/view-preview/*', element: <ViewPreview /> },
{ path: '/docs/*', element: <DocsLayout />, children: [{ path: 'components/*', element: <ComponentPage /> }] },
{ path: '/*', element: <Navigate to="/view-preview" /> } // temp redirect to view preview
])

const App: FC = () => {
return (
<ExitConfirmProvider>
<RouterProvider router={router} />
</ExitConfirmProvider>
)
return <RouterProvider router={router} />
}

export default App
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.layout {
display: grid;
grid-template-columns: auto 1fr;
}

.navbar {
position: sticky;
top: 0;
left: 0;
}
22 changes: 22 additions & 0 deletions apps/design-system/src/components/docs-layout/docs-layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { FC, useEffect } from 'react'
import { Outlet } from 'react-router-dom'

import '@harnessio/ui/styles.css'

import DocsNavbar from '@components/docs-navbar/docs-navbar'

import css from './docs-layout.module.css'

const DocsLayout: FC = () => {
// TODO: expose this via a UI element to switch between light-std-std and dark-std-std
useEffect(() => document.body.classList.add('light-std-std'), [])

return (
<main className={css.layout}>
<DocsNavbar className={css.navbar} />
<Outlet />
</main>
)
}

export default DocsLayout
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.content {
overflow: hidden;
}

.viewLink {
display: flex;
align-items: baseline;
gap: 0.5rem;
}
56 changes: 56 additions & 0 deletions apps/design-system/src/components/docs-navbar/docs-navbar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { FC } from 'react'
import { Link, useLocation } from 'react-router-dom'

import { viewPreviews } from '@/pages/view-preview/view-preview.tsx'
import { componentPages } from '@subjects/components/componentPages.ts'

import { Icon, NavbarSkeleton, ScrollArea } from '@harnessio/ui/components'

import css from './docs-navbar.module.css'

export interface DocsNavbarProps {
className?: string
}

const DocsNavbar: FC<DocsNavbarProps> = ({ className }) => {
const location = useLocation()

return (
<NavbarSkeleton.Root className={className}>
<NavbarSkeleton.Header>
<Link className="flex items-center gap-1.5 p-5" to="/docs">
<Icon name="harness" size={18} className="text-foreground-1" />
<Icon name="harness-logo-text" width={65} height={15} className="text-foreground-1 mb-0.5" />
</Link>
</NavbarSkeleton.Header>

<NavbarSkeleton.Content className={css.content}>
<ScrollArea>
<NavbarSkeleton.Group title="Components">
{componentPages.map(({ name, path }) => (
<Link key={path} to={`/docs/components/${path}`}>
<NavbarSkeleton.Item text={name} active={location.pathname.includes(`/docs/components/${path}`)} />
</Link>
))}
</NavbarSkeleton.Group>
<NavbarSkeleton.Group title="View previews" topBorder>
{Object.keys(viewPreviews).map(path => (
<Link key={path} to={`/view-preview/${path}`} target="_blank" className={css.viewLink}>
<NavbarSkeleton.Item text={path} />
<Icon name="supply-chain" size={12} />
</Link>
))}
</NavbarSkeleton.Group>
</ScrollArea>
</NavbarSkeleton.Content>
<NavbarSkeleton.Footer>
<Link to="/view-preview/" target="_blank" className={css.viewLink}>
<NavbarSkeleton.Item text="View Preview" />
<Icon name="supply-chain" size={12} />
</Link>
</NavbarSkeleton.Footer>
</NavbarSkeleton.Root>
)
}

export default DocsNavbar
17 changes: 17 additions & 0 deletions apps/design-system/src/components/docs-page/component-example.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { FC, useMemo } from 'react'

import Example, { ExampleProps } from '@components/docs-page/example.tsx'

import * as components from '@harnessio/ui/components'

export type ComponentExampleProps = Omit<ExampleProps, 'scope'> & {
scope?: ExampleProps['scope']
}

const ComponentExample: FC<ComponentExampleProps> = ({ code, scope }) => {
const combinedScope = useMemo<ExampleProps['scope']>(() => ({ ...components, ...scope }), [scope])

return <Example code={code} scope={combinedScope} />
}

export default ComponentExample
13 changes: 13 additions & 0 deletions apps/design-system/src/components/docs-page/docs-page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import ComponentExample from './component-example.tsx'
import Example from './example.tsx'
import Root from './root'
import Section from './section.tsx'
import Summary from './summary.tsx'

export const DocsPage = {
Root,
Summary,
Section,
Example,
ComponentExample
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.layout {
display: flex;
flex-direction: column;
gap: 0.5rem;
justify-content: center;
align-items: center;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { FC, PropsWithChildren } from 'react'

import css from './example-layout.module.css'

const ExampleLayout: FC<PropsWithChildren> = ({ children }) => <div className={css.layout}>{children}</div>

export default ExampleLayout
48 changes: 48 additions & 0 deletions apps/design-system/src/components/docs-page/example.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
.root {
border: 1px solid var(--preview-border, #eaeaea);
border-radius: 6px;
margin-top: 20px;
margin-bottom: 20px;
overflow: hidden;
}

.preview {
padding: 24px;
}

.editor {
background: var(--preview-background, #f7f7f7);
border-top: 1px solid var(--preview-border, #eaeaea);
list-style-type: none;

summary {
padding: 6px 24px;
color: var(--preview-text, #666);
cursor: pointer;
user-select: none;
list-style-type: none;
line-height: 24px;
display: flex;
align-items: center;
}

summary:before {
content: '';
background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiM2NjYiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBjbGFzcz0ibHVjaWRlIGx1Y2lkZS11bmZvbGQtdmVydGljYWwiPjxwYXRoIGQ9Ik0xMiAyMnYtNiIvPjxwYXRoIGQ9Ik0xMiA4VjIiLz48cGF0aCBkPSJNNCAxMkgyIi8+PHBhdGggZD0iTTEwIDEySDgiLz48cGF0aCBkPSJNMTYgMTJoLTIiLz48cGF0aCBkPSJNMjIgMTJoLTIiLz48cGF0aCBkPSJtMTUgMTktMyAzLTMtMyIvPjxwYXRoIGQ9Im0xNSA1LTMtMy0zIDMiLz48L3N2Zz4=);
width: 16px;
height: 16px;
display: inline-flex;
margin-right: 6px;
}

&[open] summary:before {
background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiM2NjYiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBjbGFzcz0ibHVjaWRlIGx1Y2lkZS1mb2xkLXZlcnRpY2FsIj48cGF0aCBkPSJNMTIgMjJ2LTYiLz48cGF0aCBkPSJNMTIgOFYyIi8+PHBhdGggZD0iTTQgMTJIMiIvPjxwYXRoIGQ9Ik0xMCAxMkg4Ii8+PHBhdGggZD0iTTE2IDEyaC0yIi8+PHBhdGggZD0iTTIyIDEyaC0yIi8+PHBhdGggZD0ibTE1IDE5LTMtMy0zIDMiLz48cGF0aCBkPSJtMTUgNS0zIDMtMy0zIi8+PC9zdmc+);
}
}

body[theme='dark'] {
--preview-border: #2f2e30;
--preview-background: #202023;
--preview-text: #a1a1aa;
--preview-text: #666666;
}
141 changes: 141 additions & 0 deletions apps/design-system/src/components/docs-page/example.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import { ComponentProps, FC, useMemo } from 'react'
import { LiveEditor, LiveError, LivePreview, LiveProvider } from 'react-live'

import ExampleLayout from '@components/docs-page/example-layout.tsx'

import css from './example.module.css'

type LiveProviderProps = ComponentProps<typeof LiveProvider>

export type ExampleProps = Pick<LiveProviderProps, 'code' | 'scope'>

const Example: FC<ExampleProps> = ({ code, scope }) => {
const scopeWithLayout = useMemo<ExampleProps['scope']>(() => ({ ...scope, ExampleLayout }), [scope])

return (
<div className={css.root}>
<LiveProvider code={`<ExampleLayout>${code}</ExampleLayout>`} scope={scopeWithLayout} theme={theme}>
<div className={css.preview}>
<LivePreview />
<LiveError />
</div>
<details className={css.editor}>
<summary tabIndex={-1}>Code Editor</summary>
<LiveEditor code={code} />
</details>
</LiveProvider>
</div>
)
}

export default Example

const theme: LiveProviderProps['theme'] = {
plain: {
backgroundColor: '#fff',
color: '#1f2937', // ?
fontWeight: '400',
fontStyle: 'normal',
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
fontFamily: 'Roboto Mono, JetBrains Mono, monospace',
fontSize: '13px',
textRendering: 'geometricPrecision'
},
styles: [
{
types: ['comment', 'prolog', 'doctype', 'cdata', 'punctuation'],
style: {
color: '#374151'
}
},
{
types: ['namespace'],
style: {
opacity: 1
}
},
{
types: ['tag', 'operator', 'number'],
style: {
color: '#1d4ed8',
fontWeight: '500'
}
},
{
types: ['property', 'function'],
style: {
color: '#a21caf'
}
},
{
types: ['tag-id', 'selector', 'atrule-id'],
style: {
color: '#eeebff'
}
},
{
types: ['attr-name'],
style: {
color: '#a21caf'
}
},
{
types: [
'boolean',
'string',
'entity',
'url',
'attr-value',
'keyword',
'control',
'directive',
'unit',
'statement',
'regex',
'at-rule',
'placeholder',
'variable'
],
style: {
color: '#06b6d4'
}
},
{
types: ['deleted'],
style: {
textDecorationLine: 'line-through'
}
},
{
types: ['language-javascript', 'script'],
style: {
color: '#1d4ed8'
}
},
{
types: ['inserted'],
style: {
textDecorationLine: 'underline'
}
},
{
types: ['italic'],
style: {
fontStyle: 'italic'
}
},
{
types: ['important', 'bold'],
style: {
fontWeight: 'bold'
}
},
{
types: ['important'],
style: {
color: '#c4b9fe'
}
}
]
}
13 changes: 13 additions & 0 deletions apps/design-system/src/components/docs-page/root.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
.root {
padding: 4rem;
display: flex;
justify-content: center;
}

.content {
width: 100%;
max-width: 960px;
display: flex;
flex-direction: column;
gap: 2rem;
}
Loading

0 comments on commit f621827

Please sign in to comment.