Implement Next.js MDX Separate the docs from the code and tests.tags/0.3.0
@@ -5,8 +5,8 @@ module.exports = { | |||||
'./jest.setup.ts', | './jest.setup.ts', | ||||
], | ], | ||||
collectCoverageFrom: [ | collectCoverageFrom: [ | ||||
'./lib/**/*.{ts,tsx}', | |||||
'!./lib/**/*.stories.{ts,tsx}' | |||||
'./packages/**/*.{ts,tsx}', | |||||
'!./packages/**/*.stories.{ts,tsx}' | |||||
], | ], | ||||
preset: 'ts-jest', | preset: 'ts-jest', | ||||
testTimeout: 30000, | testTimeout: 30000, | ||||
@@ -1,25 +0,0 @@ | |||||
--- | |||||
name: Checkbox | |||||
route: /components/checkbox | |||||
menu: Components | |||||
--- | |||||
import { Playground, Props, Link } from 'docz' | |||||
import Checkbox from './Checkbox' | |||||
# Checkbox | |||||
Component for values that have an on/off state. | |||||
<Playground> | |||||
<Checkbox label="Accept Terms" /> | |||||
</Playground> | |||||
## Props | |||||
<Props of={Checkbox} /> | |||||
## See Also | |||||
- <Link to="../select">Select</Link> for a similar component suitable for selecting more values. | |||||
- <Link to="../radiobutton">RadioButton</Link> for a similar component on selecting a single value among very few choices. |
@@ -1,20 +0,0 @@ | |||||
--- | |||||
name: Icon | |||||
route: /components/icon | |||||
menu: Components | |||||
--- | |||||
import { Playground, Props } from 'docz' | |||||
import Icon from './Icon' | |||||
# Icon | |||||
Component for displaying graphics. | |||||
<Playground> | |||||
<Icon name="check" label="OK" size="1.5rem" weight={0.125} /> | |||||
</Playground> | |||||
## Props | |||||
<Props of={Icon} /> |
@@ -1,32 +0,0 @@ | |||||
--- | |||||
name: RadioButton | |||||
route: /components/radiobutton | |||||
menu: Components | |||||
--- | |||||
import { Playground, Props, Link } from 'docz' | |||||
import RadioButton from './RadioButton' | |||||
# RadioButton | |||||
Component for values which are to be selected from a few list of options. | |||||
<Playground> | |||||
<div style={{ display: 'grid', gap: '1rem', }}> | |||||
<div> | |||||
<RadioButton name="flavor" label="Chocolate" /> | |||||
</div> | |||||
<div> | |||||
<RadioButton name="flavor" label="Vanilla" /> | |||||
</div> | |||||
</div> | |||||
</Playground> | |||||
## Props | |||||
<Props of={RadioButton} /> | |||||
## See Also | |||||
- <Link to="../checkbox">Checkbox</Link> for a similar component on selecting values among very few choices. | |||||
- <Link to="../select">Select</Link> for a similar component suitable for selecting more values. |
@@ -1,45 +0,0 @@ | |||||
--- | |||||
name: Select | |||||
route: /components/select | |||||
menu: Components | |||||
--- | |||||
import { Playground, Props, Link } from 'docz' | |||||
import Select from './Select' | |||||
# Select | |||||
Component for selecting values from a larger number of options. | |||||
<Playground> | |||||
<Select> | |||||
<optgroup | |||||
label="Fruits" | |||||
> | |||||
<option value="mango">Mango</option> | |||||
<option value="strawberry">Strawberry</option> | |||||
<option value="blueberry">Blueberry</option> | |||||
</optgroup> | |||||
<optgroup | |||||
label="Classic" | |||||
> | |||||
<option value="chocolate">Chocolate</option> | |||||
<option value="vanilla">Vanilla</option> | |||||
</optgroup> | |||||
</Select> | |||||
</Playground> | |||||
## Props | |||||
<Props of={Select} /> | |||||
## Usage Notes | |||||
The component will behave as `block`, i.e. it takes the remaining of the horizontal space. | |||||
To use the component together with layouts, see [TextInput](./textinput) for examples. Both `Select` and | |||||
`TextInput` have similar strategies on usage with layouts. | |||||
## See Also | |||||
- <Link to="../checkbox">Checkbox</Link> for a similar component on selecting values among very few choices. | |||||
- <Link to="../radiobutton">RadioButton</Link> for a similar component on selecting a single value among very few choices. |
@@ -1,71 +0,0 @@ | |||||
--- | |||||
name: TextInput | |||||
route: /components/textinput | |||||
menu: Components | |||||
--- | |||||
import { Playground, Props, Link } from 'docz' | |||||
import TextInput from './TextInput' | |||||
# TextInput | |||||
Component for inputting textual values. | |||||
<Playground> | |||||
<TextInput label="Username" placeholder="johndoe" hint="the name you use to log in" /> | |||||
</Playground> | |||||
## Props | |||||
<Props of={TextInput} /> | |||||
## Usage Notes | |||||
The component will behave as `block`, i.e. it takes the remaining of the horizontal space. | |||||
To use the component together with layouts, see the following examples. | |||||
### Inline | |||||
The components are surrounded by `inline-block` elements. These surrounding elements have specified widths, which could | |||||
act as guide to the user on how long the expected input values are. | |||||
<Playground> | |||||
<form> | |||||
I am <span style={{ display: 'inline-block', width: '16rem', }}><TextInput label="Full name" hint="given and family name" /></span> and I live in <span style={{ display: 'inline-block', width: '24rem', }}><TextInput label="Address" hint="city, state and country" /></span>. | |||||
</form> | |||||
</Playground> | |||||
### Grid | |||||
It is advisable to put surrounding elements instead of the `TextInput` components themselves as children of the | |||||
element specified as `grid`. This is to be able to add complementing content to the components, if for example there are | |||||
some content that is best displayed outside the component instead of putting in the `hint` prop. | |||||
<Playground> | |||||
<form | |||||
style={{ display: 'grid', gridTemplateColumns: '4fr 4fr 5fr', gap: '1rem', }} | |||||
> | |||||
<div style={{ gridColumnStart: 1, gridColumnEnd: 4, }}> | |||||
<TextInput label="Address line 1" hint="unit/house number, building" /> | |||||
</div> | |||||
<div style={{ gridColumnStart: 1, gridColumnEnd: 4, }}> | |||||
<TextInput label="Address line 2" hint="street, area" /> | |||||
</div> | |||||
<div> | |||||
<TextInput size="large" label="City/Town" /> | |||||
</div> | |||||
<div> | |||||
<TextInput size="large" label="State/Province" /> | |||||
</div> | |||||
<div> | |||||
<TextInput size="large" label="Country" hint="abbreviations are accepted" /> | |||||
<small> | |||||
Consult the <a href="#">fees table</a> for shipping fee details. | |||||
</small> | |||||
</div> | |||||
</form> | |||||
</Playground> | |||||
## See Also | |||||
- <Link to="../select">Select</Link> for a graphically-similar component suitable for selecting more values. |
@@ -1,42 +0,0 @@ | |||||
import * as fc from 'fast-check' | |||||
import isEmpty from './isEmpty' | |||||
describe('lib/services/isEmpty', () => { | |||||
it('should exist', () => { | |||||
expect(isEmpty).toBeDefined() | |||||
}) | |||||
it('should be a function', () => { | |||||
expect(isEmpty).toBeFunction() | |||||
}) | |||||
it('should accept 1 argument', () => { | |||||
expect(isEmpty).toHaveLength(1) | |||||
}) | |||||
it('should return a boolean value', () => { | |||||
fc.assert( | |||||
fc.property(fc.anything(), (v) => { | |||||
expect(typeof isEmpty(v)).toBe('boolean') | |||||
}), | |||||
) | |||||
}) | |||||
describe('on arguments', () => { | |||||
it('should return `true` on an argument with value of `undefined`', () => { | |||||
expect(isEmpty(undefined)).toBe(true) | |||||
}) | |||||
it('should return `true` on an argument with value of `null`', () => { | |||||
expect(isEmpty(null)).toBe(true) | |||||
}) | |||||
it('should return `false` on an argument with value that is neither `undefined` nor `null`', () => { | |||||
fc.assert( | |||||
fc.property(fc.anything().filter((v) => typeof v !== 'undefined' && v !== null), (v) => { | |||||
expect(isEmpty(v)).toBe(false) | |||||
}), | |||||
) | |||||
}) | |||||
}) | |||||
}) |
@@ -0,0 +1,30 @@ | |||||
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). | |||||
## Getting Started | |||||
First, run the development server: | |||||
```bash | |||||
npm run dev | |||||
# or | |||||
yarn dev | |||||
``` | |||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. | |||||
You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file. | |||||
## Learn More | |||||
To learn more about Next.js, take a look at the following resources: | |||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. | |||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. | |||||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! | |||||
## Deploy on Vercel | |||||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/import?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. | |||||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. |
@@ -0,0 +1,9 @@ | |||||
const Brand = () => ( | |||||
<div> | |||||
<strong> | |||||
Tesseract | |||||
</strong> | |||||
</div> | |||||
) | |||||
export default Brand |
@@ -0,0 +1,2 @@ | |||||
/// <reference types="next" /> | |||||
/// <reference types="next/types/global" /> |
@@ -0,0 +1,6 @@ | |||||
const withMDX = require('next-mdx-frontmatter')({ | |||||
extension: /\.mdx?$/ | |||||
}) | |||||
module.exports = withMDX({ | |||||
pageExtensions: ['js', 'jsx', 'ts', 'tsx', 'md', 'mdx'] | |||||
}) |
@@ -0,0 +1,19 @@ | |||||
{ | |||||
"name": "react-common-docs", | |||||
"version": "0.1.0", | |||||
"private": true, | |||||
"scripts": { | |||||
"dev": "next dev", | |||||
"build": "next build", | |||||
"start": "next start" | |||||
}, | |||||
"dependencies": { | |||||
"@mdx-js/loader": "^1.6.19", | |||||
"@tesseract-design/react-common": "^0.2.1", | |||||
"next": "10.0.1", | |||||
"next-mdx-frontmatter": "^0.0.3", | |||||
"pascalcase": "^1.0.0", | |||||
"react-live": "^2.2.3", | |||||
"styled-components": "^5.2.1" | |||||
} | |||||
} |
@@ -0,0 +1,52 @@ | |||||
body { | |||||
margin: 0; | |||||
} | |||||
h1 { | |||||
font-size: 3em; | |||||
text-transform: lowercase; | |||||
} | |||||
h2 { | |||||
font-size: 2em; | |||||
text-transform: lowercase; | |||||
} | |||||
h3 { | |||||
font-size: 1.75em; | |||||
text-transform: lowercase; | |||||
} | |||||
h4 { | |||||
text-transform: lowercase; | |||||
} | |||||
h5 { | |||||
text-transform: lowercase; | |||||
} | |||||
h6 { | |||||
text-transform: lowercase; | |||||
} | |||||
p { | |||||
margin: 2em 0; | |||||
} | |||||
small { | |||||
font-size: 0.75em; | |||||
} | |||||
a:focus { | |||||
color: var(--color-active); | |||||
outline: 0; | |||||
} | |||||
::selection { | |||||
background-color: var(--color-active); | |||||
color: var(--color-fg); | |||||
} | |||||
:root { | |||||
caret-color: var(--color-active); | |||||
} |
@@ -0,0 +1,127 @@ | |||||
@font-face { | |||||
font-family: 'Encode Sans'; | |||||
font-stretch: semi-expanded; | |||||
font-weight: 400; | |||||
src: | |||||
local('Encode Sans Semi Expanded'), | |||||
local('Encode Sans'); | |||||
} | |||||
@font-face { | |||||
font-family: 'Encode Sans'; | |||||
font-stretch: semi-expanded; | |||||
font-weight: 700; | |||||
src: | |||||
local('Encode Sans Semi Expanded Bold'), | |||||
local('Encode Sans Semi Expanded'), | |||||
local('Encode Sans'); | |||||
} | |||||
@font-face { | |||||
font-family: 'Encode Sans'; | |||||
font-stretch: condensed; | |||||
font-weight: 100; | |||||
src: | |||||
local('Encode Sans Condensed Thin'), | |||||
local('Encode Sans Condensed'), | |||||
local('Encode Sans'); | |||||
} | |||||
@font-face { | |||||
font-family: 'Encode Sans'; | |||||
font-stretch: condensed; | |||||
font-weight: 200; | |||||
src: | |||||
local('Encode Sans Condensed ExtraLight'), | |||||
local('Encode Sans Condensed Extra Light'), | |||||
local('Encode Sans Condensed'), | |||||
local('Encode Sans'); | |||||
} | |||||
@font-face { | |||||
font-family: 'Encode Sans'; | |||||
font-stretch: condensed; | |||||
font-weight: 300; | |||||
src: | |||||
local('Encode Sans Condensed Light'), | |||||
local('Encode Sans Condensed'), | |||||
local('Encode Sans'); | |||||
} | |||||
:root { | |||||
--color-active: #f90; | |||||
--font-family-base: 'Encode Sans Semi Expanded', 'Encode Sans', system-ui; | |||||
--font-stretch-base: semi-expanded; | |||||
--font-weight-base: 400; | |||||
--line-height-base: 2; | |||||
--font-family-headings:'Encode Sans Condensed', 'Encode Sans', system-ui; | |||||
--font-stretch-headings: condensed; | |||||
--font-weight-headings: 100; | |||||
--line-height-headings: 1.5; | |||||
--font-family-monospace: 'mononoki'; | |||||
--font-size-root: 16px; | |||||
--color-negative: #222; | |||||
--color-positive: #eee; | |||||
--color-accent: #C78AB3; | |||||
--opacity-light: 0.25; | |||||
--opacity-lighter: 0.5; | |||||
--opacity-lightest: 0.75; | |||||
} | |||||
:root { | |||||
--color-bg: var(--color-negative, white); | |||||
--color-fg: var(--color-positive, black); | |||||
background-color: var(--color-bg); | |||||
color: var(--color-fg); | |||||
font-size: var(--font-size-root); | |||||
font-family: var(--font-family-base), sans-serif; | |||||
font-stretch: var(--font-stretch-base, normal); | |||||
font-weight: var(--font-weight-base, 400); | |||||
line-height: var(--line-height-base, 2); | |||||
} | |||||
h1 { | |||||
font-family: var(--font-family-headings), sans-serif; | |||||
font-stretch: var(--font-stretch-headings, normal); | |||||
font-weight: var(--font-weight-headings, 400); | |||||
line-height: var(--line-height-headings, 1.5); | |||||
} | |||||
h2 { | |||||
font-family: var(--font-family-headings), sans-serif; | |||||
font-stretch: var(--font-stretch-headings, normal); | |||||
font-weight: var(--font-weight-headings, 400); | |||||
line-height: var(--line-height-headings, 1.5); | |||||
} | |||||
h3 { | |||||
font-family: var(--font-family-headings), sans-serif; | |||||
font-stretch: var(--font-stretch-headings, normal); | |||||
font-weight: var(--font-weight-headings, 400); | |||||
line-height: var(--line-height-headings, 1.5); | |||||
} | |||||
h4 { | |||||
font-family: var(--font-family-headings), sans-serif; | |||||
font-stretch: var(--font-stretch-headings, normal); | |||||
font-weight: var(--font-weight-headings, 400); | |||||
line-height: var(--line-height-headings, 1.5); | |||||
} | |||||
h5 { | |||||
font-family: var(--font-family-headings), sans-serif; | |||||
font-stretch: var(--font-stretch-headings, normal); | |||||
font-weight: var(--font-weight-headings, 400); | |||||
line-height: var(--line-height-headings, 1.5); | |||||
} | |||||
h6 { | |||||
font-family: var(--font-family-headings), sans-serif; | |||||
font-stretch: var(--font-stretch-headings, normal); | |||||
font-weight: var(--font-weight-headings, 400); | |||||
line-height: var(--line-height-headings, 1.5); | |||||
} | |||||
a { | |||||
color: var(--color-accent); | |||||
} |
@@ -0,0 +1,138 @@ | |||||
@font-face { | |||||
font-family: 'Encode Sans'; | |||||
font-stretch: semi-expanded; | |||||
font-weight: 400; | |||||
src: | |||||
local('Encode Sans Semi Expanded'), | |||||
local('Encode Sans'); | |||||
} | |||||
@font-face { | |||||
font-family: 'Encode Sans'; | |||||
font-stretch: semi-expanded; | |||||
font-weight: 700; | |||||
src: | |||||
local('Encode Sans Semi Expanded Bold'), | |||||
local('Encode Sans Semi Expanded'), | |||||
local('Encode Sans'); | |||||
} | |||||
@font-face { | |||||
font-family: 'Encode Sans'; | |||||
font-stretch: condensed; | |||||
font-weight: 100; | |||||
src: | |||||
local('Encode Sans Condensed Thin'), | |||||
local('Encode Sans Condensed'), | |||||
local('Encode Sans'); | |||||
} | |||||
@font-face { | |||||
font-family: 'Encode Sans'; | |||||
font-stretch: condensed; | |||||
font-weight: 200; | |||||
src: | |||||
local('Encode Sans Condensed ExtraLight'), | |||||
local('Encode Sans Condensed Extra Light'), | |||||
local('Encode Sans Condensed'), | |||||
local('Encode Sans'); | |||||
} | |||||
@font-face { | |||||
font-family: 'Encode Sans'; | |||||
font-stretch: condensed; | |||||
font-weight: 300; | |||||
src: | |||||
local('Encode Sans Condensed Light'), | |||||
local('Encode Sans Condensed'), | |||||
local('Encode Sans'); | |||||
} | |||||
:root { | |||||
--color-negative: #eee; | |||||
--color-positive: #222; | |||||
--color-accent: #ba6a9c; | |||||
--color-active: #f90; | |||||
--font-family-base: 'Encode Sans Semi Expanded', 'Encode Sans', system-ui; | |||||
--font-stretch-base: semi-expanded; | |||||
--font-weight-base: 400; | |||||
--line-height-base: 2; | |||||
--font-family-headings:'Encode Sans Condensed', 'Encode Sans', system-ui; | |||||
--font-stretch-headings: condensed; | |||||
--font-weight-headings: 100; | |||||
--line-height-headings: 1.5; | |||||
--font-family-monospace: 'mononoki'; | |||||
--font-size-root: 16px; | |||||
--opacity-light: 0.5; | |||||
--opacity-lighter: 0.75; | |||||
--opacity-lightest: 0.875; | |||||
} | |||||
/*@media (prefers-color-scheme: dark) {*/ | |||||
/* :root {*/ | |||||
/* --color-negative: #222;*/ | |||||
/* --color-positive: #eee;*/ | |||||
/* --color-accent: #C78AB3;*/ | |||||
/* --opacity-light: 0.25;*/ | |||||
/* --opacity-lighter: 0.5;*/ | |||||
/* --opacity-lightest: 0.75;*/ | |||||
/* }*/ | |||||
/*}*/ | |||||
:root { | |||||
--color-bg: var(--color-negative, white); | |||||
--color-fg: var(--color-positive, black); | |||||
background-color: var(--color-bg); | |||||
color: var(--color-fg); | |||||
font-size: var(--font-size-root); | |||||
font-family: var(--font-family-base), sans-serif; | |||||
font-stretch: var(--font-stretch-base, normal); | |||||
font-weight: var(--font-weight-base, 400); | |||||
line-height: var(--line-height-base, 2); | |||||
} | |||||
h1 { | |||||
font-family: var(--font-family-headings), sans-serif; | |||||
font-stretch: var(--font-stretch-headings, normal); | |||||
font-weight: var(--font-weight-headings, 400); | |||||
line-height: var(--line-height-headings, 1.5); | |||||
} | |||||
h2 { | |||||
font-family: var(--font-family-headings), sans-serif; | |||||
font-stretch: var(--font-stretch-headings, normal); | |||||
font-weight: var(--font-weight-headings, 400); | |||||
line-height: var(--line-height-headings, 1.5); | |||||
} | |||||
h3 { | |||||
font-family: var(--font-family-headings), sans-serif; | |||||
font-stretch: var(--font-stretch-headings, normal); | |||||
font-weight: var(--font-weight-headings, 400); | |||||
line-height: var(--line-height-headings, 1.5); | |||||
} | |||||
h4 { | |||||
font-family: var(--font-family-headings), sans-serif; | |||||
font-stretch: var(--font-stretch-headings, normal); | |||||
font-weight: var(--font-weight-headings, 400); | |||||
line-height: var(--line-height-headings, 1.5); | |||||
} | |||||
h5 { | |||||
font-family: var(--font-family-headings), sans-serif; | |||||
font-stretch: var(--font-stretch-headings, normal); | |||||
font-weight: var(--font-weight-headings, 400); | |||||
line-height: var(--line-height-headings, 1.5); | |||||
} | |||||
h6 { | |||||
font-family: var(--font-family-headings), sans-serif; | |||||
font-stretch: var(--font-stretch-headings, normal); | |||||
font-weight: var(--font-weight-headings, 400); | |||||
line-height: var(--line-height-headings, 1.5); | |||||
} | |||||
a { | |||||
color: var(--color-accent); | |||||
} |
@@ -0,0 +1,4 @@ | |||||
<svg width="283" height="64" viewBox="0 0 283 64" fill="none" | |||||
xmlns="http://www.w3.org/2000/svg"> | |||||
<path d="M141.04 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.46 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM248.72 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.45 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM200.24 34c0 6 3.92 10 10 10 4.12 0 7.21-1.87 8.8-4.92l7.68 4.43c-3.18 5.3-9.14 8.49-16.48 8.49-11.05 0-19-7.2-19-18s7.96-18 19-18c7.34 0 13.29 3.19 16.48 8.49l-7.68 4.43c-1.59-3.05-4.68-4.92-8.8-4.92-6.07 0-10 4-10 10zm82.48-29v46h-9V5h9zM36.95 0L73.9 64H0L36.95 0zm92.38 5l-27.71 48L73.91 5H84.3l17.32 30 17.32-30h10.39zm58.91 12v9.69c-1-.29-2.06-.49-3.2-.49-5.81 0-10 4-10 10V51h-9V17h9v9.2c0-5.08 5.91-9.2 13.2-9.2z" fill="#000"/> | |||||
</svg> |
@@ -0,0 +1,91 @@ | |||||
import * as React from 'react' | |||||
import Link from 'next/link' | |||||
import styled from 'styled-components' | |||||
const StyledLink = styled('a')({ | |||||
display: 'block', | |||||
textDecoration: 'none', | |||||
}) | |||||
const Container = styled('span')({ | |||||
display: 'flex', | |||||
alignItems: 'center', | |||||
padding: '0 1rem', | |||||
height: '2rem', | |||||
margin: '0 0 0 auto', | |||||
'@media (min-width: 720px)': { | |||||
maxWidth: 360, | |||||
}, | |||||
}) | |||||
const HeaderContainer = styled(Container)({ | |||||
marginTop: '2rem', | |||||
}) | |||||
const NavContainer = styled('div')({ | |||||
}) | |||||
const Nav = ({ | |||||
data, | |||||
}) => { | |||||
if (Array.isArray(data)) { | |||||
return ( | |||||
<NavContainer> | |||||
{ | |||||
data.map(d => ( | |||||
<Nav | |||||
data={d} | |||||
/> | |||||
)) | |||||
} | |||||
</NavContainer> | |||||
) | |||||
} | |||||
if (typeof data === 'object') { | |||||
const [entry] = Object.entries(data) | |||||
const [key, value] = entry | |||||
if (Array.isArray(value)) { | |||||
return ( | |||||
<NavContainer> | |||||
<HeaderContainer> | |||||
{key} | |||||
</HeaderContainer> | |||||
{ | |||||
value.map(v => ( | |||||
<Nav | |||||
data={v} | |||||
/> | |||||
)) | |||||
} | |||||
</NavContainer> | |||||
) | |||||
} | |||||
return ( | |||||
<Link | |||||
href={value} | |||||
passHref | |||||
> | |||||
<StyledLink> | |||||
<Container> | |||||
{key} | |||||
</Container> | |||||
</StyledLink> | |||||
</Link> | |||||
) | |||||
} | |||||
return ( | |||||
<Link | |||||
href={data} | |||||
passHref | |||||
> | |||||
<StyledLink> | |||||
<Container> | |||||
{data} | |||||
</Container> | |||||
</StyledLink> | |||||
</Link> | |||||
) | |||||
} | |||||
export default Nav |
@@ -0,0 +1,47 @@ | |||||
import * as React from 'react' | |||||
import { | |||||
LiveProvider, | |||||
LiveEditor, | |||||
LivePreview | |||||
} from 'react-live' | |||||
import styled from 'styled-components' | |||||
const Figure = styled('figure')({ | |||||
margin: 0, | |||||
}) | |||||
const Playground = ({ | |||||
components, | |||||
code, | |||||
label, | |||||
}) => { | |||||
const indentLevel = code.indexOf('<') - 1 | |||||
const normalizedCode = code | |||||
.split('\n') | |||||
.map((s: string) => s.slice(indentLevel)) | |||||
.filter((s: string) => s.trim().length > 0) | |||||
.join('\n') | |||||
return ( | |||||
<div> | |||||
<Figure> | |||||
{ | |||||
label | |||||
&& ( | |||||
<figcaption> | |||||
{label} | |||||
</figcaption> | |||||
) | |||||
} | |||||
<div> | |||||
<LiveProvider code={normalizedCode} scope={components}> | |||||
<LivePreview /> | |||||
<LiveEditor /> | |||||
</LiveProvider> | |||||
</div> | |||||
</Figure> | |||||
</div> | |||||
) | |||||
} | |||||
export default Playground |
@@ -0,0 +1,96 @@ | |||||
import styled from 'styled-components' | |||||
import Nav from './Nav' | |||||
import Link from 'next/link' | |||||
import * as React from 'react' | |||||
import { Checkbox } from '@tesseract-design/react-common' | |||||
const StyledLink = styled('a')({ | |||||
display: 'block', | |||||
textDecoration: 'none', | |||||
marginBottom: '2rem', | |||||
}) | |||||
const Container = styled('span')({ | |||||
display: 'flex', | |||||
alignItems: 'center', | |||||
padding: '0 1rem', | |||||
height: '2rem', | |||||
margin: '0 0 0 auto', | |||||
'@media (min-width: 720px)': { | |||||
maxWidth: 360, | |||||
}, | |||||
}) | |||||
const Base = styled('aside')({ | |||||
position: 'fixed', | |||||
top: 0, | |||||
left: '-100%', | |||||
width: '100%', | |||||
height: '100%', | |||||
backgroundColor: 'var(--color-bg)', | |||||
zIndex: 4, | |||||
'@media (min-width: 720px)': { | |||||
left: 0, | |||||
width: `${100 / 3}%`, | |||||
maxWidth: 'none', | |||||
height: '100%', | |||||
'+ *': { | |||||
paddingLeft: `${100 / 3}%`, | |||||
boxSizing: 'border-box', | |||||
}, | |||||
}, | |||||
}) | |||||
const Sidebar = ({ | |||||
data, | |||||
brand: Brand, | |||||
}) => { | |||||
const [dark, setDark] = React.useState(false) | |||||
const toggleDarkMode = b => () => { | |||||
setDark(b) | |||||
} | |||||
React.useEffect(() => { | |||||
const stylesheets = Array.from(window.document.querySelectorAll('link[title]')) | |||||
stylesheets.forEach(s => { | |||||
if (s.getAttribute('rel').includes('alternate')) { | |||||
s.setAttribute('rel', 'stylesheet') | |||||
s.removeAttribute('disabled') | |||||
} else { | |||||
s.setAttribute('rel', 'alternate stylesheet') | |||||
s.setAttribute('disabled', 'disabled') | |||||
} | |||||
}) | |||||
}, [dark]) | |||||
return ( | |||||
<Base> | |||||
<Container> | |||||
<input | |||||
type="checkbox" | |||||
defaultChecked={dark} | |||||
onChange={toggleDarkMode(!dark)} | |||||
/> | |||||
</Container> | |||||
<nav> | |||||
<Link | |||||
href="/" | |||||
passHref | |||||
> | |||||
<StyledLink> | |||||
<Container> | |||||
<Brand /> | |||||
</Container> | |||||
</StyledLink> | |||||
</Link> | |||||
<Nav | |||||
data={data.nav} | |||||
/> | |||||
</nav> | |||||
</Base> | |||||
) | |||||
} | |||||
export default Sidebar |
@@ -0,0 +1,35 @@ | |||||
import '../../public/global.css' | |||||
import '../../public/theme/dark.css' | |||||
import sidebar from '../sidebar.json' | |||||
import brand from '../../brand' | |||||
import Sidebar from '../components/Sidebar' | |||||
import styled from 'styled-components' | |||||
const Container = styled('div')({ | |||||
maxWidth: 720, | |||||
margin: '0 auto', | |||||
padding: '0 1rem', | |||||
boxSizing: 'border-box', | |||||
}) | |||||
const App = ({ | |||||
Component, | |||||
pageProps, | |||||
...etc | |||||
}) => ( | |||||
<div> | |||||
<Sidebar | |||||
brand={brand} | |||||
data={sidebar} | |||||
/> | |||||
<main> | |||||
<Container> | |||||
<Component | |||||
{...pageProps} | |||||
/> | |||||
</Container> | |||||
</main> | |||||
</div> | |||||
) | |||||
export default App |
@@ -0,0 +1,45 @@ | |||||
import Document, { Html, Head, Main, NextScript, DocumentContext } from 'next/document' | |||||
import { ServerStyleSheet } from 'styled-components' | |||||
export default class MyDocument extends Document { | |||||
static async getInitialProps(ctx: DocumentContext) { | |||||
const sheet = new ServerStyleSheet() | |||||
const originalRenderPage = ctx.renderPage | |||||
try { | |||||
ctx.renderPage = () => | |||||
originalRenderPage({ | |||||
enhanceApp: (App) => (props) => | |||||
sheet.collectStyles(<App {...props} />), | |||||
}) | |||||
const initialProps = await Document.getInitialProps(ctx) | |||||
return { | |||||
...initialProps, | |||||
styles: ( | |||||
<> | |||||
{initialProps.styles} | |||||
<link rel="stylesheet" href="/global.css" /> | |||||
<link rel="stylesheet" title="Dark" href="/theme/dark.css" /> | |||||
<link rel="alternate stylesheet" title="Light" href="/theme/light.css" /> | |||||
{sheet.getStyleElement()} | |||||
</> | |||||
), | |||||
} | |||||
} finally { | |||||
sheet.seal() | |||||
} | |||||
} | |||||
render() { | |||||
return ( | |||||
<Html lang="en-PH"> | |||||
<Head /> | |||||
<body> | |||||
<Main /> | |||||
<NextScript /> | |||||
</body> | |||||
</Html> | |||||
) | |||||
} | |||||
} |
@@ -1,23 +1,26 @@ | |||||
--- | --- | ||||
name: Button | |||||
route: /components/button | |||||
menu: Components | |||||
title: Button | |||||
--- | --- | ||||
import { Playground, Props } from 'docz' | |||||
import Button from './Button' | |||||
import { Button } from '@tesseract-design/react-common' | |||||
import Playground from '../../components/Playground' | |||||
# Button | # Button | ||||
Component for performing an action upon activation (e.g. when clicked). | Component for performing an action upon activation (e.g. when clicked). | ||||
<Playground> | |||||
<Button>Perform Action</Button> | |||||
</Playground> | |||||
<Playground | |||||
code={` | |||||
<Button> | |||||
Perform Action | |||||
</Button> | |||||
`} | |||||
components={{ Button }} | |||||
/> | |||||
## Props | ## Props | ||||
<Props of={Button} /> | |||||
Props | |||||
## Usage Notes | ## Usage Notes | ||||
@@ -0,0 +1,26 @@ | |||||
--- | |||||
name: Checkbox | |||||
--- | |||||
import { Checkbox } from '@tesseract-design/react-common' | |||||
import Playground from '../../components/Playground' | |||||
# Checkbox | |||||
Component for values that have an on/off state. | |||||
<Playground | |||||
components={{ Checkbox }} | |||||
code={` | |||||
<Checkbox label="Accept Terms" /> | |||||
`} | |||||
/> | |||||
## Props | |||||
Props | |||||
## See Also | |||||
- [Select](./Select) for a similar component suitable for selecting more values. | |||||
- [RadioButton](./RadioButton) for a similar component on selecting a single value among very few choices. |
@@ -0,0 +1,21 @@ | |||||
--- | |||||
name: Icon | |||||
--- | |||||
import { Icon } from '@tesseract-design/react-common' | |||||
import Playground from '../../components/Playground' | |||||
# Icon | |||||
Component for displaying graphics. | |||||
<Playground | |||||
components={{ Icon }} | |||||
code={` | |||||
<Icon name="check" label="OK" size="1.5rem" weight={0.125} /> | |||||
`} | |||||
/> | |||||
## Props | |||||
Props |
@@ -0,0 +1,33 @@ | |||||
--- | |||||
name: RadioButton | |||||
--- | |||||
import { RadioButton } from '@tesseract-design/react-common' | |||||
import Playground from '../../components/Playground' | |||||
# RadioButton | |||||
Component for values which are to be selected from a few list of options. | |||||
<Playground | |||||
components={{ RadioButton }} | |||||
code={` | |||||
<div style={{ display: 'grid', gap: '1rem', }}> | |||||
<div> | |||||
<RadioButton name="flavor" label="Chocolate" /> | |||||
</div> | |||||
<div> | |||||
<RadioButton name="flavor" label="Vanilla" /> | |||||
</div> | |||||
</div> | |||||
`} | |||||
/> | |||||
## Props | |||||
Props | |||||
## See Also | |||||
- [Checkbox](./Checkbox) for a similar component on selecting values among very few choices. | |||||
- [Select](./Select) for a similar component suitable for selecting more values. |
@@ -0,0 +1,46 @@ | |||||
--- | |||||
name: Select | |||||
--- | |||||
import { Select } from '@tesseract-design/react-common' | |||||
import Playground from '../../components/Playground' | |||||
# Select | |||||
Component for selecting values from a larger number of options. | |||||
<Playground | |||||
components={{ Select }} | |||||
code={` | |||||
<Select> | |||||
<optgroup | |||||
label="Fruits" | |||||
> | |||||
<option value="mango">Mango</option> | |||||
<option value="strawberry">Strawberry</option> | |||||
<option value="blueberry">Blueberry</option> | |||||
</optgroup> | |||||
<optgroup | |||||
label="Classic" | |||||
> | |||||
<option value="chocolate">Chocolate</option> | |||||
<option value="vanilla">Vanilla</option> | |||||
</optgroup> | |||||
</Select> | |||||
`} | |||||
/> | |||||
## Props | |||||
Props | |||||
## Usage Notes | |||||
The component will behave as `block`, i.e. it takes the remaining of the horizontal space. | |||||
To use the component together with layouts, see [TextInput](./TextInput) for examples. Both `Select` and | |||||
`TextInput` have similar strategies on usage with layouts. | |||||
## See Also | |||||
- [Checkbox](./Checkbox) for a similar component on selecting values among very few choices. | |||||
- [RadioButton](./RadioButton) for a similar component on selecting a single value among very few choices. |
@@ -1,11 +1,9 @@ | |||||
--- | --- | ||||
name: Slider | name: Slider | ||||
route: /components/slider | |||||
menu: Components | |||||
--- | --- | ||||
import { Playground, Props } from 'docz' | |||||
import Slider from './Slider' | |||||
import { Slider } from '@tesseract-design/react-common' | |||||
import Playground from '../../components/Playground' | |||||
# Slider | # Slider | ||||
@@ -14,13 +12,16 @@ Component for inputting numeric values in a graphical manner. | |||||
The component is styled using client-side scripts. When the component is rendered server-side, | The component is styled using client-side scripts. When the component is rendered server-side, | ||||
the component falls back into the original `<input type="range">` element. | the component falls back into the original `<input type="range">` element. | ||||
<Playground> | |||||
<Slider /> | |||||
</Playground> | |||||
<Playground | |||||
components={{ Slider }} | |||||
code={` | |||||
<Slider /> | |||||
`} | |||||
/> | |||||
## Props | ## Props | ||||
<Props of={Slider} /> | |||||
Props | |||||
## See Also | ## See Also | ||||
@@ -0,0 +1,78 @@ | |||||
--- | |||||
name: TextInput | |||||
--- | |||||
import { TextInput } from '@tesseract-design/react-common' | |||||
import Playground from '../../components/Playground' | |||||
# TextInput | |||||
Component for inputting textual values. | |||||
<Playground | |||||
components={{ TextInput }} | |||||
code={` | |||||
<TextInput label="Username" placeholder="johndoe" hint="the name you use to log in" /> | |||||
`} | |||||
/> | |||||
## Props | |||||
Props | |||||
## Usage Notes | |||||
The component will behave as `block`, i.e. it takes the remaining of the horizontal space. | |||||
To use the component together with layouts, see the following examples. | |||||
### Inline | |||||
The components are surrounded by `inline-block` elements. These surrounding elements have specified widths, which could | |||||
act as guide to the user on how long the expected input values are. | |||||
<Playground | |||||
components={{ TextInput }} | |||||
code={` | |||||
<form> | |||||
I am <span style={{ display: 'inline-block', width: '16rem', }}><TextInput label="Full name" hint="given and family name" /></span> and I live in <span style={{ display: 'inline-block', width: '24rem', }}><TextInput label="Address" hint="city, state and country" /></span>. | |||||
</form> | |||||
`} | |||||
/> | |||||
### Grid | |||||
It is advisable to put surrounding elements instead of the `TextInput` components themselves as children of the | |||||
element specified as `grid`. This is to be able to add complementing content to the components, if for example there are | |||||
some content that is best displayed outside the component instead of putting in the `hint` prop. | |||||
<Playground | |||||
components={{ TextInput }} | |||||
code={` | |||||
<form | |||||
style={{ display: 'grid', gridTemplateColumns: '4fr 4fr 5fr', gap: '1rem', }} | |||||
> | |||||
<div style={{ gridColumnStart: 1, gridColumnEnd: 4, }}> | |||||
<TextInput label="Address line 1" hint="unit/house number, building" /> | |||||
</div> | |||||
<div style={{ gridColumnStart: 1, gridColumnEnd: 4, }}> | |||||
<TextInput label="Address line 2" hint="street, area" /> | |||||
</div> | |||||
<div> | |||||
<TextInput size="large" label="City/Town" /> | |||||
</div> | |||||
<div> | |||||
<TextInput size="large" label="State/Province" /> | |||||
</div> | |||||
<div> | |||||
<TextInput size="large" label="Country" hint="abbreviations are accepted" /> | |||||
<small> | |||||
Consult the <a href="#">fees table</a> for shipping fee details. | |||||
</small> | |||||
</div> | |||||
</form> | |||||
`} | |||||
/> | |||||
## See Also | |||||
- [Select](./Select) for a graphically-similar component suitable for selecting more values. |
@@ -0,0 +1 @@ | |||||
../../../../README.md |
@@ -0,0 +1,18 @@ | |||||
{ | |||||
"nav": [ | |||||
{ | |||||
"Theming": "/theming" | |||||
}, | |||||
{ | |||||
"Components": [ | |||||
{ "Button": "/components/Button" }, | |||||
{ "Checkbox": "/components/Checkbox" }, | |||||
{ "Icon": "/components/Icon" }, | |||||
{ "RadioButton": "/components/RadioButton" }, | |||||
{ "Select": "/components/Select" }, | |||||
{ "Slider": "/components/Slider" }, | |||||
{ "TextInput": "/components/TextInput" } | |||||
] | |||||
} | |||||
] | |||||
} |
@@ -0,0 +1,37 @@ | |||||
{ | |||||
"compilerOptions": { | |||||
"target": "es5", | |||||
"lib": [ | |||||
"dom", | |||||
"dom.iterable", | |||||
"esnext" | |||||
], | |||||
"allowJs": true, | |||||
"skipLibCheck": true, | |||||
"esModuleInterop": true, | |||||
"allowSyntheticDefaultImports": true, | |||||
"strict": true, | |||||
"forceConsistentCasingInFileNames": true, | |||||
"module": "esnext", | |||||
"moduleResolution": "node", | |||||
"resolveJsonModule": true, | |||||
"isolatedModules": true, | |||||
"noEmit": true, | |||||
"jsx": "preserve", | |||||
"declaration": true, | |||||
"declarationDir": "./dist", | |||||
"sourceMap": true | |||||
}, | |||||
"exclude": [ | |||||
"node_modules", | |||||
"**/*.test.ts", | |||||
"**/*.test.tsx", | |||||
"utilities/**/*", | |||||
"jest.setup.ts" | |||||
], | |||||
"include": [ | |||||
"next-env.d.ts", | |||||
"**/*.ts", | |||||
"**/*.tsx" | |||||
] | |||||
} |
@@ -1,5 +1,5 @@ | |||||
/// <reference types="jest-enzyme" /> | /// <reference types="jest-enzyme" /> | ||||
/// <reference path="../../../utilities/jest/extensions.ts" /> | |||||
/// <reference path="../../../../utilities/jest/extensions.ts" /> | |||||
import * as fc from 'fast-check' | import * as fc from 'fast-check' | ||||
import * as Enzyme from 'enzyme' | import * as Enzyme from 'enzyme' |
@@ -1,5 +1,5 @@ | |||||
/// <reference types="jest-enzyme" /> | /// <reference types="jest-enzyme" /> | ||||
/// <reference path="../../../utilities/jest/extensions.ts" /> | |||||
/// <reference path="../../../../utilities/jest/extensions.ts" /> | |||||
import * as fc from 'fast-check' | import * as fc from 'fast-check' | ||||
import * as Enzyme from 'enzyme' | import * as Enzyme from 'enzyme' |
@@ -1,5 +1,5 @@ | |||||
/// <reference types="jest-enzyme" /> | /// <reference types="jest-enzyme" /> | ||||
/// <reference path="../../../utilities/jest/extensions.ts" /> | |||||
/// <reference path="../../../../utilities/jest/extensions.ts" /> | |||||
import * as fc from 'fast-check' | import * as fc from 'fast-check' | ||||
import * as Enzyme from 'enzyme' | import * as Enzyme from 'enzyme' |
@@ -1,5 +1,5 @@ | |||||
/// <reference types="jest-enzyme" /> | /// <reference types="jest-enzyme" /> | ||||
/// <reference path="../../../utilities/jest/extensions.ts" /> | |||||
/// <reference path="../../../../utilities/jest/extensions.ts" /> | |||||
import * as fc from 'fast-check' | import * as fc from 'fast-check' | ||||
import * as Enzyme from 'enzyme' | import * as Enzyme from 'enzyme' |
@@ -1,5 +1,5 @@ | |||||
/// <reference types="jest-enzyme" /> | /// <reference types="jest-enzyme" /> | ||||
/// <reference path="../../../utilities/jest/extensions.ts" /> | |||||
/// <reference path="../../../../utilities/jest/extensions.ts" /> | |||||
import * as fc from 'fast-check' | import * as fc from 'fast-check' | ||||
import * as Enzyme from 'enzyme' | import * as Enzyme from 'enzyme' |
@@ -1,5 +1,5 @@ | |||||
/// <reference types="jest-enzyme" /> | /// <reference types="jest-enzyme" /> | ||||
/// <reference path="../../../utilities/jest/extensions.ts" /> | |||||
/// <reference path="../../../../utilities/jest/extensions.ts" /> | |||||
import * as fc from 'fast-check' | import * as fc from 'fast-check' | ||||
import * as Enzyme from 'enzyme' | import * as Enzyme from 'enzyme' |
@@ -1,5 +1,5 @@ | |||||
/// <reference types="jest-enzyme" /> | /// <reference types="jest-enzyme" /> | ||||
/// <reference path="../../../utilities/jest/extensions.ts" /> | |||||
/// <reference path="../../../../utilities/jest/extensions.ts" /> | |||||
import * as fc from 'fast-check' | import * as fc from 'fast-check' | ||||
import * as Enzyme from 'enzyme' | import * as Enzyme from 'enzyme' |
@@ -1,5 +1,5 @@ | |||||
/// <reference types="jest-enzyme" /> | /// <reference types="jest-enzyme" /> | ||||
/// <reference path="../utilities/jest/extensions.ts" /> | |||||
/// <reference path="../../utilities/jest/extensions.ts" /> | |||||
import * as T from './index' | import * as T from './index' | ||||
@@ -0,0 +1,40 @@ | |||||
import * as fc from 'fast-check' | |||||
import isEmpty from './isEmpty' | |||||
it('should exist', () => { | |||||
expect(isEmpty).toBeDefined() | |||||
}) | |||||
it('should be a function', () => { | |||||
expect(typeof isEmpty).toBe('function') | |||||
}) | |||||
it('should accept 1 argument', () => { | |||||
expect(isEmpty).toHaveLength(1) | |||||
}) | |||||
it('should return a boolean value', () => { | |||||
fc.assert( | |||||
fc.property(fc.anything(), (v) => { | |||||
expect(typeof isEmpty(v)).toBe('boolean') | |||||
}), | |||||
) | |||||
}) | |||||
describe('on arguments', () => { | |||||
it('should return `true` on an argument with value of `undefined`', () => { | |||||
expect(isEmpty(undefined)).toBe(true) | |||||
}) | |||||
it('should return `true` on an argument with value of `null`', () => { | |||||
expect(isEmpty(null)).toBe(true) | |||||
}) | |||||
it('should return `false` on an argument with value that is neither `undefined` nor `null`', () => { | |||||
fc.assert( | |||||
fc.property(fc.anything().filter((v) => typeof v !== 'undefined' && v !== null), (v) => { | |||||
expect(isEmpty(v)).toBe(false) | |||||
}), | |||||
) | |||||
}) | |||||
}) |
@@ -1,5 +1,5 @@ | |||||
import * as fc from 'fast-check' | import * as fc from 'fast-check' | ||||
import * as fcArb from '../../utilities/fast-check/arbitraries' | |||||
import * as fcArb from '../../../utilities/fast-check/arbitraries' | |||||
import stringify from './stringify' | import stringify from './stringify' | ||||
it('should exist', () => { | it('should exist', () => { |
@@ -23,7 +23,7 @@ module.exports = plop => { | |||||
type: 'addMany', | type: 'addMany', | ||||
templateFiles: 'plop/templates/component/*', | templateFiles: 'plop/templates/component/*', | ||||
base: 'plop/templates/component', | base: 'plop/templates/component', | ||||
destination: 'lib/components/{{pascalCase name}}', | |||||
destination: 'packages/react-common/components/{{pascalCase name}}', | |||||
}, | }, | ||||
], | ], | ||||
}) | }) | ||||
@@ -4,7 +4,7 @@ import typescript from '@rollup/plugin-typescript' | |||||
import pkg from './package.json' | import pkg from './package.json' | ||||
const ENTRY_POINT = './lib/index.ts' | |||||
const ENTRY_POINT = './packages/react-common/index.ts' | |||||
export default { | export default { | ||||
input: ENTRY_POINT, | input: ENTRY_POINT, | ||||