-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #144 from hypothesis/simplified-library-layout
Library layout components, simplified
- Loading branch information
Showing
3 changed files
with
285 additions
and
39 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
import { toChildArray } from 'preact'; | ||
import { useState } from 'preact/hooks'; | ||
|
||
import { LabeledButton } from '../../components/buttons'; | ||
|
||
import { jsxToString } from '../util/jsx-to-string'; | ||
|
||
/** | ||
* @typedef LibraryBaseProps | ||
* @prop {import("preact").ComponentChildren} [children] | ||
* @prop {string} [title] | ||
* | ||
*/ | ||
|
||
/** | ||
* Components for rendering patterns, examples and demos in the pattern-library | ||
* page. A pattern-library Page contains Patterns, which in turn contain | ||
* Examples. An Example _may_ contain one or more Demos. Child content (markup) | ||
* may also be rendered in these components, as desired. | ||
* | ||
* Example of structure: | ||
* | ||
* <Library.Page title="Elephants"> | ||
* <p>Any content you want on the page.</p> | ||
* More content: it can be any valid `ComponentChildren` | ||
* | ||
* <Library.Pattern title="Elephant"> | ||
* <p>The `Elephant` component is used to render information about elephant | ||
* personalities.</p> | ||
* <Library.Example title="Colored elephants"> | ||
* <p>You can change the color of your elephant.</p> | ||
* <Library.Demo withSource> | ||
* <Elephant color="pink" /> | ||
* </Library.Demo> | ||
* </Library.Example> | ||
* // More Examples if desired | ||
* </Library.Pattern> | ||
* | ||
* // more Patterns if desired... | ||
* </Library.Page> | ||
*/ | ||
|
||
/** | ||
* Render a pattern-library page. | ||
* | ||
* @param {LibraryBaseProps} props | ||
*/ | ||
function Page({ children, title }) { | ||
return ( | ||
<section className="LibraryPage"> | ||
<h1 className="LibraryPage__heading">{title}</h1> | ||
{children} | ||
</section> | ||
); | ||
} | ||
|
||
/** | ||
* Render info about a single pattern (or component) on a pattern-library page. | ||
* | ||
* @param {LibraryBaseProps} props | ||
*/ | ||
function Pattern({ children, title }) { | ||
return ( | ||
<section className="LibraryPattern"> | ||
<h2 className="LibraryPattern__heading">{title}</h2> | ||
{children} | ||
</section> | ||
); | ||
} | ||
|
||
/** | ||
* Render example content and optional Demo(s) for a pattern. | ||
* | ||
* @param {LibraryBaseProps} props | ||
*/ | ||
function Example({ children, title }) { | ||
const kids = toChildArray(children); | ||
|
||
// Extract Demo components out of any children | ||
const demos = kids.filter( | ||
kid => typeof kid === 'object' && kid?.type === Demo | ||
); | ||
// And everything else that is not a demo... | ||
const notDemos = kids.filter(kid => !demos.includes(kid)); | ||
|
||
return ( | ||
<div className="LibraryExample"> | ||
<div className="LibraryExample__content"> | ||
{title && <h3 className="LibraryExample__heading">{title}</h3>} | ||
{notDemos} | ||
</div> | ||
<div className="LibraryExample__content">{demos}</div> | ||
</div> | ||
); | ||
} | ||
|
||
/** | ||
* Render a "Demo", with optional source. This will render the children as | ||
* provided in a tabbed container. If `withSource` is `true`, the JSX source | ||
* of the children will be provided in a separate "Source" tab from the | ||
* rendered Demo content. | ||
* | ||
* @typedef DemoProps | ||
* @prop {import("preact").ComponentChildren} [children] | ||
* @prop {boolean} [withSource=false] - Should the demo also render the source? | ||
* When true, a "Source" tab will be rendered, which will display the JSX | ||
* source of the Demo's children | ||
* @prop {object} [style] - Inline styles to apply to the demo container | ||
*/ | ||
function Demo({ children, withSource = false, style = {} }) { | ||
const [visibleTab, setVisibleTab] = useState('demo'); | ||
const source = toChildArray(children).map((child, idx) => { | ||
return ( | ||
<li key={idx}> | ||
<code> | ||
<pre>{jsxToString(child)}</pre> | ||
</code> | ||
</li> | ||
); | ||
}); | ||
return ( | ||
<div className="LibraryDemo"> | ||
<div className="LibraryDemo__tabs"> | ||
<LabeledButton | ||
onClick={() => setVisibleTab('demo')} | ||
pressed={visibleTab === 'demo'} | ||
variant="dark" | ||
> | ||
Demo | ||
</LabeledButton> | ||
{withSource && ( | ||
<LabeledButton | ||
onClick={() => setVisibleTab('source')} | ||
pressed={visibleTab === 'source'} | ||
variant="dark" | ||
> | ||
Source | ||
</LabeledButton> | ||
)} | ||
</div> | ||
<div className="LibraryDemo__container"> | ||
{visibleTab === 'demo' && ( | ||
<div className="LibraryDemo__demo" style={style}> | ||
<div className="LibraryDemo__demo-content">{children}</div> | ||
</div> | ||
)} | ||
{visibleTab === 'source' && ( | ||
<div className="LibraryDemo__source"> | ||
<ul>{source}</ul> | ||
</div> | ||
)} | ||
</div> | ||
</div> | ||
); | ||
} | ||
|
||
export default { | ||
Page, | ||
Pattern, | ||
Example, | ||
Demo, | ||
}; |
80 changes: 42 additions & 38 deletions
80
src/pattern-library/components/patterns/ContainerComponents.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,76 +1,80 @@ | ||
import { Frame, Card, Actions } from '../../../components/containers'; | ||
import { LabeledButton } from '../../../components/buttons'; | ||
import { Frame, Card, Actions } from '../../..'; | ||
import { LabeledButton } from '../../..'; | ||
|
||
import { | ||
PatternPage, | ||
Pattern, | ||
PatternExamples, | ||
PatternExample, | ||
} from '../PatternPage'; | ||
import Library from '../Library'; | ||
|
||
export default function ContainerComponents() { | ||
return ( | ||
<PatternPage title="Containers"> | ||
<Pattern title="Frame"> | ||
<p> | ||
The <code>Frame</code> component pattern provides a framed layout with | ||
padding and vertical spacing of children. | ||
</p> | ||
<PatternExamples> | ||
<PatternExample details="Laying out content in a Frame"> | ||
<Library.Page title="Containers"> | ||
<Library.Pattern title="Frame"> | ||
<Library.Example title="Laying out content in a Frame"> | ||
<p> | ||
The <code>Frame</code> component renders content inside of a{' '} | ||
<code>frame</code> design pattern. | ||
</p> | ||
<Library.Demo withSource> | ||
<Frame> | ||
<div>This content is inside of a frame.</div> | ||
<div>This content is inside of a frame.</div> | ||
</Frame> | ||
</PatternExample> | ||
</PatternExamples> | ||
</Pattern> | ||
</Library.Demo> | ||
</Library.Example> | ||
</Library.Pattern> | ||
|
||
<Pattern title="Card"> | ||
<Library.Pattern title="Card"> | ||
<p> | ||
The <code>Card</code> component pattern provides a card-like layout | ||
that builds on <code>Frame</code>. | ||
using the <code>card</code> pattern. | ||
</p> | ||
<PatternExamples> | ||
<PatternExample details="Laying out content in a Card"> | ||
<Library.Example title="Laying out content in a Card"> | ||
<Library.Demo withSource> | ||
<Card> | ||
<div>This content is inside of a card.</div> | ||
<div>This content is inside of a card.</div> | ||
</Card> | ||
</PatternExample> | ||
|
||
<PatternExample details="Example of overriding background color"> | ||
</Library.Demo> | ||
</Library.Example> | ||
<Library.Example title="Overriding styles"> | ||
<p> | ||
This example shows overriding the background color of a{' '} | ||
<code>Card</code> using a utility class. | ||
</p> | ||
<Library.Demo withSource> | ||
<Card classes="hyp-u-bg-color--grey-3"> | ||
<div>This content is inside of a card.</div> | ||
<div>This content is inside of a card.</div> | ||
</Card> | ||
</PatternExample> | ||
</PatternExamples> | ||
</Pattern> | ||
</Library.Demo> | ||
</Library.Example> | ||
</Library.Pattern> | ||
|
||
<Pattern title="Actions"> | ||
<Library.Pattern title="Actions"> | ||
<p> | ||
The <code>Actions</code> component pattern lays out actions (buttons). | ||
</p> | ||
<PatternExamples> | ||
<PatternExample details="Laying out buttons with Actions"> | ||
|
||
<Library.Example title="Laying out buttons with Actions"> | ||
<Library.Demo withSource> | ||
<Actions> | ||
<LabeledButton>Cancel</LabeledButton> | ||
<LabeledButton>Maybe</LabeledButton> | ||
<LabeledButton variant="primary">OK</LabeledButton> | ||
</Actions> | ||
</PatternExample> | ||
<PatternExample details="Laying out buttons vertically with Actions"> | ||
</Library.Demo> | ||
</Library.Example> | ||
|
||
<Library.Example title="Laying out buttons vertically with Actions"> | ||
<Library.Demo withSource> | ||
<Actions direction="column"> | ||
<LabeledButton>This is one option</LabeledButton> | ||
<LabeledButton>This is another option</LabeledButton> | ||
<LabeledButton variant="primary"> | ||
This is the best option | ||
</LabeledButton> | ||
</Actions> | ||
</PatternExample> | ||
</PatternExamples> | ||
</Pattern> | ||
</PatternPage> | ||
</Library.Demo> | ||
</Library.Example> | ||
</Library.Pattern> | ||
</Library.Page> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters