@primer/gatsby-theme-doctocat 2.1.0 → 3.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +34 -0
- package/gatsby-node.js +37 -45
- package/package.json +2 -4
- package/src/components/clipboard-copy.js +2 -2
- package/src/components/code.js +1 -1
- package/src/components/contributors.js +1 -2
- package/src/components/do-dont.js +1 -1
- package/src/components/drawer.js +2 -10
- package/src/components/hero-layout.js +2 -2
- package/src/components/hero.js +1 -1
- package/src/components/image-container.js +2 -4
- package/src/components/layout.js +23 -17
- package/src/components/live-code.js +2 -2
- package/src/components/mobile-search.js +4 -9
- package/src/components/nav-drawer.js +17 -19
- package/src/components/nav-dropdown.js +1 -1
- package/src/components/nav-items.js +15 -7
- package/src/components/page-footer.js +3 -3
- package/src/components/search.js +9 -7
- package/src/components/sidebar.js +9 -10
- package/src/components/status-label.js +1 -1
- package/src/components/wrap-root-element.js +4 -2
- package/src/mdx-components.js +2 -0
- package/src/__tests__/validate-checklists.test.js +0 -96
- package/src/checklists/component.schema.js +0 -13
- package/src/components/checklist.tsx +0 -58
- package/src/components/checklists.tsx +0 -48
- package/src/validate-checklists.js +0 -62
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,39 @@
|
|
|
1
1
|
# @primer/gatsby-theme-doctocat
|
|
2
2
|
|
|
3
|
+
## 3.1.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`45e8748`](https://github.com/primer/doctocat/commit/45e8748a547903b8f7091fafb5b13e5c197b0dae) [#324](https://github.com/primer/doctocat/pull/324) Thanks [@rezrah](https://github.com/rezrah)! - Only add pages that have `componentId` and `status` to `components.json`
|
|
8
|
+
|
|
9
|
+
## 3.1.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- [`3767651`](https://github.com/primer/doctocat/commit/37676515f1d7485ca7b5e932e115d96e3ef0285b) [#318](https://github.com/primer/doctocat/pull/318) Thanks [@colebemis](https://github.com/colebemis)! - Add a step to build process that will output a static `components.json` file with component status info if the site that its building has markdown files containing `componentId` frontmatter.
|
|
14
|
+
|
|
15
|
+
## 3.0.1
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- [`ff74ece`](https://github.com/primer/doctocat/commit/ff74ecea1b373469034c2122d94a54ca3e964158) [#301](https://github.com/primer/doctocat/pull/301) Thanks [@jfuchs](https://github.com/jfuchs)! - Removed styled system prop usage on Primer React components.
|
|
20
|
+
|
|
21
|
+
## 3.0.0
|
|
22
|
+
|
|
23
|
+
### Major Changes
|
|
24
|
+
|
|
25
|
+
- [`c2037d8`](https://github.com/primer/doctocat/commit/c2037d8352fca4ce3055e801b365d4ff1b5eefe9) [#295](https://github.com/primer/doctocat/pull/295) Thanks [@colebemis](https://github.com/colebemis)! - Remove checklist feature
|
|
26
|
+
|
|
27
|
+
### Minor Changes
|
|
28
|
+
|
|
29
|
+
- [`a94e0a9`](https://github.com/primer/doctocat/commit/a94e0a962e8013901e4285c7a3cabc3a0b691a92) [#294](https://github.com/primer/doctocat/pull/294) Thanks [@colebemis](https://github.com/colebemis)! - You can now make custom React components globally available (no import required) to all markdown files in your site.
|
|
30
|
+
|
|
31
|
+
### Patch Changes
|
|
32
|
+
|
|
33
|
+
- [`31a62df`](https://github.com/primer/doctocat/commit/31a62dfb7eb7a467a4e03a991adad995564ae4d1) [#262](https://github.com/primer/doctocat/pull/262) Thanks [@SferaDev](https://github.com/SferaDev)! - Fix scroll flickering of table of contents
|
|
34
|
+
|
|
35
|
+
* [`9972de8`](https://github.com/primer/doctocat/commit/9972de88224e1b868dad051abe62a92daf8796ef) [#291](https://github.com/primer/doctocat/pull/291) Thanks [@jonrohan](https://github.com/jonrohan)! - Fix release
|
|
36
|
+
|
|
3
37
|
## 2.1.0
|
|
4
38
|
|
|
5
39
|
### Minor Changes
|
package/gatsby-node.js
CHANGED
|
@@ -1,41 +1,11 @@
|
|
|
1
1
|
const path = require('path')
|
|
2
|
+
const fs = require('fs')
|
|
2
3
|
const readPkgUp = require('read-pkg-up')
|
|
3
4
|
const getPkgRepo = require('get-pkg-repo')
|
|
4
5
|
const axios = require('axios')
|
|
5
6
|
const uniqBy = require('lodash.uniqby')
|
|
6
7
|
const extractExports = require(`gatsby-plugin-mdx/utils/extract-exports`)
|
|
7
8
|
const mdx = require(`gatsby-plugin-mdx/utils/mdx`)
|
|
8
|
-
const requireGlob = require('require-glob')
|
|
9
|
-
const {validateChecklistSchema, validateChecklist} = require('./src/validate-checklists')
|
|
10
|
-
const checklistSchemas = requireGlob.sync('./src/checklists/*.schema.js')
|
|
11
|
-
|
|
12
|
-
exports.sourceNodes = ({actions, createNodeId, createContentDigest}) => {
|
|
13
|
-
const {createNode} = actions
|
|
14
|
-
|
|
15
|
-
// Add checklist schemas to GraphQL API
|
|
16
|
-
for (const schemaKey in checklistSchemas) {
|
|
17
|
-
try {
|
|
18
|
-
validateChecklistSchema(checklistSchemas[schemaKey])
|
|
19
|
-
} catch (error) {
|
|
20
|
-
throw new Error(`Invalid checklist schema: ${schemaKey.replace('Schema', '')}.schema.js\n${error.message}`)
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const name = schemaKey.replace('Schema', '')
|
|
24
|
-
const data = {name, ...checklistSchemas[schemaKey]}
|
|
25
|
-
|
|
26
|
-
const node = {
|
|
27
|
-
id: createNodeId(schemaKey),
|
|
28
|
-
parent: null,
|
|
29
|
-
children: [],
|
|
30
|
-
internal: {
|
|
31
|
-
type: 'ChecklistSchema',
|
|
32
|
-
contentDigest: createContentDigest(data)
|
|
33
|
-
},
|
|
34
|
-
...data
|
|
35
|
-
}
|
|
36
|
-
createNode(node)
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
9
|
|
|
40
10
|
const CONTRIBUTOR_CACHE = new Map()
|
|
41
11
|
|
|
@@ -43,7 +13,7 @@ exports.createPages = async ({graphql, actions}, themeOptions) => {
|
|
|
43
13
|
const repo = getPkgRepo(readPkgUp.sync().package)
|
|
44
14
|
|
|
45
15
|
const {data} = await graphql(`
|
|
46
|
-
{
|
|
16
|
+
query {
|
|
47
17
|
allMdx {
|
|
48
18
|
nodes {
|
|
49
19
|
fileAbsolutePath
|
|
@@ -65,7 +35,7 @@ exports.createPages = async ({graphql, actions}, themeOptions) => {
|
|
|
65
35
|
}
|
|
66
36
|
|
|
67
37
|
// Turn every MDX file into a page.
|
|
68
|
-
|
|
38
|
+
await Promise.all(
|
|
69
39
|
data.allMdx.nodes.map(async node => {
|
|
70
40
|
const pagePath = path
|
|
71
41
|
.join(node.parent.relativeDirectory, node.parent.name === 'index' ? '/' : node.parent.name)
|
|
@@ -87,18 +57,6 @@ exports.createPages = async ({graphql, actions}, themeOptions) => {
|
|
|
87
57
|
const code = await mdx(node.rawBody)
|
|
88
58
|
const {frontmatter} = extractExports(code)
|
|
89
59
|
|
|
90
|
-
// Validate checklist frontmatter
|
|
91
|
-
for (const schemaKey in checklistSchemas) {
|
|
92
|
-
const checklistKey = schemaKey.replace('Schema', 'Checklist')
|
|
93
|
-
if (checklistKey in frontmatter) {
|
|
94
|
-
try {
|
|
95
|
-
validateChecklist(frontmatter[checklistKey], checklistSchemas[schemaKey])
|
|
96
|
-
} catch (error) {
|
|
97
|
-
throw new Error(`Invalid checklist: ${checklistKey} in ${fileRelativePath}\n${error.message}`)
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
60
|
actions.createPage({
|
|
103
61
|
path: pagePath,
|
|
104
62
|
component: node.fileAbsolutePath,
|
|
@@ -117,6 +75,40 @@ exports.createPages = async ({graphql, actions}, themeOptions) => {
|
|
|
117
75
|
)
|
|
118
76
|
}
|
|
119
77
|
|
|
78
|
+
exports.onPostBuild = async ({graphql}) => {
|
|
79
|
+
try {
|
|
80
|
+
const {data} = await graphql(`
|
|
81
|
+
query {
|
|
82
|
+
allSitePage(filter: {context: {frontmatter: {componentId: {ne: null}, status: {ne: null}}}}) {
|
|
83
|
+
nodes {
|
|
84
|
+
path
|
|
85
|
+
context {
|
|
86
|
+
frontmatter {
|
|
87
|
+
componentId
|
|
88
|
+
status
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
`)
|
|
95
|
+
|
|
96
|
+
const components = data.allSitePage.nodes.map(node => {
|
|
97
|
+
return {
|
|
98
|
+
id: node.context.frontmatter.componentId,
|
|
99
|
+
path: node.path,
|
|
100
|
+
status: node.context.frontmatter.status.toLowerCase()
|
|
101
|
+
}
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
fs.writeFileSync(path.resolve(process.cwd(), 'public/components.json'), JSON.stringify(components))
|
|
105
|
+
} catch (error) {
|
|
106
|
+
// This is not necessarily an error, so we just log a warning instead of failing the build.
|
|
107
|
+
// Some sites won't have any markdown files with `componentId` frontmatter and that's okay.
|
|
108
|
+
console.warn('Unable to build components.json')
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
120
112
|
function getEditUrl(repo, filePath, defaultBranch) {
|
|
121
113
|
return `https://github.com/${repo.user}/${repo.project}/edit/${defaultBranch}/${filePath}`
|
|
122
114
|
}
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@primer/gatsby-theme-doctocat",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.1.1",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"test": "jest",
|
|
8
|
-
"lint": "eslint '**/*.js'",
|
|
8
|
+
"lint": "eslint '**/*.{js,ts,tsx}'",
|
|
9
9
|
"lint:fix": "yarn lint -- --fix"
|
|
10
10
|
},
|
|
11
11
|
"prettier": "@github/prettier-config",
|
|
@@ -60,7 +60,6 @@
|
|
|
60
60
|
"github-slugger": "^1.2.1",
|
|
61
61
|
"html-react-parser": "^1.4.0",
|
|
62
62
|
"jest": "^24.9.0",
|
|
63
|
-
"jsonschema": "^1.4.0",
|
|
64
63
|
"lodash.debounce": "4.0.8",
|
|
65
64
|
"lodash.uniqby": "^4.7.0",
|
|
66
65
|
"pkg-up": "^3.1.0",
|
|
@@ -76,7 +75,6 @@
|
|
|
76
75
|
"react-live": "^2.1.2",
|
|
77
76
|
"react-measure": "^2.3.0",
|
|
78
77
|
"read-pkg-up": "^6.0.0",
|
|
79
|
-
"require-glob": "^4.0.0",
|
|
80
78
|
"sentence-case": "^2.1.1",
|
|
81
79
|
"styled-components": "^4.3.2",
|
|
82
80
|
"styled-system": "^5.0.18",
|
|
@@ -24,9 +24,9 @@ function ClipboardCopy({value}) {
|
|
|
24
24
|
sx={{px: 2}}
|
|
25
25
|
>
|
|
26
26
|
{copied ? (
|
|
27
|
-
<StyledOcticon icon={CheckIcon} color
|
|
27
|
+
<StyledOcticon icon={CheckIcon} sx={{color: 'success.fg'}} />
|
|
28
28
|
) : (
|
|
29
|
-
<StyledOcticon icon={CopyIcon} color
|
|
29
|
+
<StyledOcticon icon={CopyIcon} sx={{color: 'fg.muted'}} />
|
|
30
30
|
)}
|
|
31
31
|
</Button>
|
|
32
32
|
)
|
package/src/components/code.js
CHANGED
|
@@ -24,7 +24,7 @@ function Code({className, children, live, noinline}) {
|
|
|
24
24
|
width: '100%'
|
|
25
25
|
}}
|
|
26
26
|
>
|
|
27
|
-
<Absolute
|
|
27
|
+
<Absolute sx={{top: 0, right: 0, p: 2}}>
|
|
28
28
|
<ClipboardCopy value={code} />
|
|
29
29
|
</Absolute>
|
|
30
30
|
<Highlight {...defaultProps} Prism={Prism} code={code} language={language} theme={githubTheme}>
|
|
@@ -20,8 +20,7 @@ function Contributors({contributors}) {
|
|
|
20
20
|
<Link
|
|
21
21
|
key={contributor.login}
|
|
22
22
|
href={`https://github.com/${contributor.login}`}
|
|
23
|
-
lineHeight
|
|
24
|
-
sx={{mr: 2}}
|
|
23
|
+
sx={{mr: 2, lineHeight: 'condensedUltra'}}
|
|
25
24
|
>
|
|
26
25
|
<Tooltip key={contributor.login} aria-label={contributor.login}>
|
|
27
26
|
<Avatar src={`https://github.com/${contributor.login}.png?size=40`} alt={contributor.login} />
|
|
@@ -26,7 +26,7 @@ function DoDontBase({children, title, icon: Icon, iconBg}) {
|
|
|
26
26
|
return (
|
|
27
27
|
<Box display="flex" flexDirection="column">
|
|
28
28
|
<Box display="flex" alignSelf="start" flexDirection="row" alignItems="center" mb="2">
|
|
29
|
-
<StyledOcticon icon={Icon}
|
|
29
|
+
<StyledOcticon icon={Icon} sx={{color: iconBg}} />
|
|
30
30
|
<Text fontWeight="bold" color="fg.default" ml={2}>
|
|
31
31
|
{title}
|
|
32
32
|
</Text>
|
package/src/components/drawer.js
CHANGED
|
@@ -23,12 +23,8 @@ function Drawer({isOpen, onDismiss, children}) {
|
|
|
23
23
|
animate={{opacity: 1}}
|
|
24
24
|
exit={{opacity: 0}}
|
|
25
25
|
transition={{type: 'tween'}}
|
|
26
|
-
top={0}
|
|
27
|
-
right={0}
|
|
28
|
-
bottom={0}
|
|
29
|
-
left={0}
|
|
30
|
-
bg="rgba(0, 0, 0, 0.5)"
|
|
31
26
|
onClick={() => onDismiss()}
|
|
27
|
+
sx={{top: 0, right: 0, bottom: 0, left: 0, bg: 'rgba(0, 0, 0, 0.5)'}}
|
|
32
28
|
/>
|
|
33
29
|
|
|
34
30
|
<Fixed
|
|
@@ -38,12 +34,8 @@ function Drawer({isOpen, onDismiss, children}) {
|
|
|
38
34
|
animate={{x: 0}}
|
|
39
35
|
exit={{x: '100%'}}
|
|
40
36
|
transition={{type: 'tween', duration: 0.2}}
|
|
41
|
-
width={300}
|
|
42
|
-
top={0}
|
|
43
|
-
right={0}
|
|
44
|
-
bottom={0}
|
|
45
|
-
bg="gray.0"
|
|
46
37
|
style={{zIndex: 1}}
|
|
38
|
+
sx={{width: 300, top: 0, right: 0, bottom: 0, bg: 'gray.0'}}
|
|
47
39
|
>
|
|
48
40
|
{children}
|
|
49
41
|
</Fixed>
|
|
@@ -15,10 +15,10 @@ function HeroLayout({children, pageContext}) {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
return (
|
|
18
|
-
<Flex flexDirection
|
|
18
|
+
<Flex sx={{flexDirection: 'column', minHeight: '100vh'}}>
|
|
19
19
|
<Head />
|
|
20
20
|
<Header />
|
|
21
|
-
<Flex flex
|
|
21
|
+
<Flex sx={{flex: '1 1 auto', flexDirection: 'row'}}>
|
|
22
22
|
<Box display={['none', null, null, 'block']}>
|
|
23
23
|
<Sidebar />
|
|
24
24
|
</Box>
|
package/src/components/hero.js
CHANGED
|
@@ -10,7 +10,7 @@ function Hero() {
|
|
|
10
10
|
<ThemeProvider colorMode="night" nightScheme="dark_dimmed">
|
|
11
11
|
<Box bg="canvas.default" py={6}>
|
|
12
12
|
<Container>
|
|
13
|
-
<Heading as="h1" color
|
|
13
|
+
<Heading as="h1" sx={{color: 'accent.fg', fontSize: 7, m: 0}}>
|
|
14
14
|
{title}
|
|
15
15
|
</Heading>
|
|
16
16
|
<Text as="p" m={0} color="fg.default" fontSize={4}>
|
|
@@ -3,10 +3,8 @@ import React from 'react'
|
|
|
3
3
|
|
|
4
4
|
function ImageContainer({children}) {
|
|
5
5
|
return (
|
|
6
|
-
<BorderBox
|
|
7
|
-
<Flex
|
|
8
|
-
{children}
|
|
9
|
-
</Flex>
|
|
6
|
+
<BorderBox sx={{p: 6, bg: 'gray.1'}}>
|
|
7
|
+
<Flex sx={{img: {maxWidth: '100%'}, justifyContent: 'center'}}>{children}</Flex>
|
|
10
8
|
</BorderBox>
|
|
11
9
|
)
|
|
12
10
|
}
|
package/src/components/layout.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import componentMetadata from '@primer/component-metadata'
|
|
2
2
|
import {BorderBox, Box, Flex, Grid, Heading, Position, Text} from '@primer/components'
|
|
3
3
|
import React from 'react'
|
|
4
|
-
import Checklists from './checklists'
|
|
5
4
|
import Head from './head'
|
|
6
5
|
import Header, {HEADER_HEIGHT} from './header'
|
|
7
6
|
import PageFooter from './page-footer'
|
|
@@ -27,10 +26,10 @@ function Layout({children, pageContext}) {
|
|
|
27
26
|
}
|
|
28
27
|
|
|
29
28
|
return (
|
|
30
|
-
<Flex flexDirection
|
|
29
|
+
<Flex sx={{flexDirection: 'column', minHeight: '100vh'}}>
|
|
31
30
|
<Head title={title} description={description} />
|
|
32
31
|
<Header />
|
|
33
|
-
<Flex flex
|
|
32
|
+
<Flex css={{zIndex: 0}} sx={{flex: '1 1 auto', flexDirection: 'row'}}>
|
|
34
33
|
<Box display={['none', null, null, 'block']}>
|
|
35
34
|
<Sidebar />
|
|
36
35
|
</Box>
|
|
@@ -46,12 +45,16 @@ function Layout({children, pageContext}) {
|
|
|
46
45
|
>
|
|
47
46
|
{pageContext.tableOfContents.items ? (
|
|
48
47
|
<Position
|
|
49
|
-
sx={{
|
|
50
|
-
|
|
48
|
+
sx={{
|
|
49
|
+
width: 220,
|
|
50
|
+
flex: '0 0 auto',
|
|
51
|
+
marginLeft: 6,
|
|
52
|
+
display: ['none', null, 'block'],
|
|
53
|
+
position: 'sticky',
|
|
54
|
+
top: HEADER_HEIGHT + 48,
|
|
55
|
+
maxHeight: `calc(100vh - ${HEADER_HEIGHT}px - 48px)`
|
|
56
|
+
}}
|
|
51
57
|
css={{gridArea: 'table-of-contents', overflow: 'auto'}}
|
|
52
|
-
position="sticky"
|
|
53
|
-
top={HEADER_HEIGHT + 24}
|
|
54
|
-
maxHeight={`calc(100vh - ${HEADER_HEIGHT}px - 24px)`}
|
|
55
58
|
>
|
|
56
59
|
<Text display="inline-block" fontWeight="bold" mb={1}>
|
|
57
60
|
On this page
|
|
@@ -62,7 +65,7 @@ function Layout({children, pageContext}) {
|
|
|
62
65
|
<Box width="100%" maxWidth="960px">
|
|
63
66
|
<Box mb={4}>
|
|
64
67
|
<Flex sx={{alignItems: 'center'}}>
|
|
65
|
-
<Heading as="h1"
|
|
68
|
+
<Heading as="h1" sx={{mr: 2}}>
|
|
66
69
|
{title}
|
|
67
70
|
</Heading>{' '}
|
|
68
71
|
{status ? <StatusLabel status={status} /> : null}
|
|
@@ -74,11 +77,13 @@ function Layout({children, pageContext}) {
|
|
|
74
77
|
) : null}
|
|
75
78
|
{source || storybook ? (
|
|
76
79
|
<Grid
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
80
|
+
sx={{
|
|
81
|
+
py: 2,
|
|
82
|
+
gridGap: [1, null, 3],
|
|
83
|
+
gridAutoFlow: ['row', null, 'column'],
|
|
84
|
+
gridAutoColumns: 'max-content',
|
|
85
|
+
gridAutoRows: 'max-content'
|
|
86
|
+
}}
|
|
82
87
|
>
|
|
83
88
|
{source ? <SourceLink href={source} /> : null}
|
|
84
89
|
{storybook ? <StorybookLink href={storybook} /> : null}
|
|
@@ -86,9 +91,11 @@ function Layout({children, pageContext}) {
|
|
|
86
91
|
) : null}
|
|
87
92
|
</Box>
|
|
88
93
|
{pageContext.tableOfContents.items ? (
|
|
89
|
-
<BorderBox
|
|
94
|
+
<BorderBox
|
|
95
|
+
sx={{display: ['block', null, 'none'], mb: 5, borderColor: 'border.muted', bg: 'canvas.subtle'}}
|
|
96
|
+
>
|
|
90
97
|
<Box p={3}>
|
|
91
|
-
<Flex flexDirection
|
|
98
|
+
<Flex sx={{flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center'}}>
|
|
92
99
|
<Text fontWeight="bold">On this page</Text>
|
|
93
100
|
</Flex>
|
|
94
101
|
</Box>
|
|
@@ -98,7 +105,6 @@ function Layout({children, pageContext}) {
|
|
|
98
105
|
</BorderBox>
|
|
99
106
|
) : null}
|
|
100
107
|
{children}
|
|
101
|
-
<Checklists frontmatter={pageContext.frontmatter} />
|
|
102
108
|
<PageFooter
|
|
103
109
|
editUrl={pageContext.editUrl}
|
|
104
110
|
contributors={pageContext.contributors.concat(additionalContributors.map(login => ({login})))}
|
|
@@ -40,7 +40,7 @@ function LiveCode({code, language, noinline}) {
|
|
|
40
40
|
const handleChange = updatedLiveCode => setLiveCode(updatedLiveCode)
|
|
41
41
|
|
|
42
42
|
return (
|
|
43
|
-
<Flex flexDirection
|
|
43
|
+
<Flex sx={{flexDirection: 'column', mb: 3}}>
|
|
44
44
|
<LiveProvider scope={scope} code={liveCode} transformCode={languageTransformers[language]} noInline={noinline}>
|
|
45
45
|
<Flex
|
|
46
46
|
sx={{
|
|
@@ -70,7 +70,7 @@ function LiveCode({code, language, noinline}) {
|
|
|
70
70
|
borderColor: theme.colors.border.default
|
|
71
71
|
}}
|
|
72
72
|
/>
|
|
73
|
-
<Absolute
|
|
73
|
+
<Absolute sx={{top: 0, right: 0, p: 2}}>
|
|
74
74
|
<ClipboardCopy value={liveCode} />
|
|
75
75
|
</Absolute>
|
|
76
76
|
</Relative>
|
|
@@ -38,19 +38,14 @@ function MobileSearch({isOpen, onDismiss}) {
|
|
|
38
38
|
<AnimatePresence>
|
|
39
39
|
{isOpen ? (
|
|
40
40
|
<FocusOn returnFocus={true} onEscapeKey={() => handleDismiss()}>
|
|
41
|
-
<Fixed
|
|
41
|
+
<Fixed sx={{top: 0, left: 0, right: 0, bottom: 0, zIndex: 1}}>
|
|
42
42
|
<Absolute
|
|
43
43
|
as={motion.div}
|
|
44
44
|
initial={{opacity: 0}}
|
|
45
45
|
animate={{opacity: 1}}
|
|
46
46
|
exit={{opacity: 0}}
|
|
47
|
-
top={0}
|
|
48
|
-
left={0}
|
|
49
|
-
right={0}
|
|
50
|
-
bottom={0}
|
|
51
|
-
bg="primer.canvas.backdrop"
|
|
52
|
-
zIndex={-1}
|
|
53
47
|
onClick={handleDismiss}
|
|
48
|
+
sx={{top: 0, left: 0, right: 0, bottom: 0, bg: 'primer.canvas.backdrop', zIndex: -1}}
|
|
54
49
|
/>
|
|
55
50
|
<Downshift
|
|
56
51
|
inputValue={query}
|
|
@@ -72,7 +67,7 @@ function MobileSearch({isOpen, onDismiss}) {
|
|
|
72
67
|
height: isMenuOpen ? '100%' : 'auto'
|
|
73
68
|
})}
|
|
74
69
|
>
|
|
75
|
-
<Flex bg
|
|
70
|
+
<Flex sx={{bg: 'canvas.default', color: 'fg.default', p: 3, flex: '0 0 auto'}}>
|
|
76
71
|
<motion.div
|
|
77
72
|
initial={{scaleX: 0.1}}
|
|
78
73
|
animate={{scaleX: 1}}
|
|
@@ -83,7 +78,7 @@ function MobileSearch({isOpen, onDismiss}) {
|
|
|
83
78
|
<TextInput
|
|
84
79
|
{...getInputProps({
|
|
85
80
|
placeholder: `Search`,
|
|
86
|
-
width: '100%'
|
|
81
|
+
sx: {width: '100%'}
|
|
87
82
|
})}
|
|
88
83
|
/>
|
|
89
84
|
</motion.div>
|
|
@@ -44,15 +44,13 @@ function NavDrawer({isOpen, onDismiss}) {
|
|
|
44
44
|
return (
|
|
45
45
|
<Drawer isOpen={isOpen} onDismiss={onDismiss}>
|
|
46
46
|
<Flex
|
|
47
|
-
flexDirection="column"
|
|
48
|
-
height="100%"
|
|
49
|
-
bg="canvas.default"
|
|
50
47
|
style={{overflow: 'auto', WebkitOverflowScrolling: 'touch'}}
|
|
48
|
+
sx={{flexDirection: 'column', height: '100%', bg: 'canvas.default'}}
|
|
51
49
|
>
|
|
52
|
-
<Flex flexDirection
|
|
53
|
-
<BorderBox
|
|
54
|
-
<Flex
|
|
55
|
-
<Link href="https://primer.style" fontFamily
|
|
50
|
+
<Flex sx={{flexDirection: 'column', flex: '0 0 auto', color: 'fg.default', bg: 'canvas.default'}}>
|
|
51
|
+
<BorderBox sx={{borderWidth: 0, borderRadius: 0, borderBottomWidth: 1, borderColor: 'border.muted'}}>
|
|
52
|
+
<Flex sx={{py: 3, pl: 4, pr: 3, alignItems: 'center', justifyContent: 'space-between'}}>
|
|
53
|
+
<Link href="https://primer.style" sx={{fontFamily: 'mono', color: 'inherit'}}>
|
|
56
54
|
Primer
|
|
57
55
|
</Link>
|
|
58
56
|
<Button aria-label="Close" onClick={onDismiss}>
|
|
@@ -60,14 +58,18 @@ function NavDrawer({isOpen, onDismiss}) {
|
|
|
60
58
|
</Button>
|
|
61
59
|
</Flex>
|
|
62
60
|
</BorderBox>
|
|
63
|
-
<Flex flexDirection
|
|
61
|
+
<Flex sx={{flexDirection: 'column'}}>
|
|
64
62
|
<PrimerNavItems items={primerNavItems} />
|
|
65
63
|
</Flex>
|
|
66
64
|
</Flex>
|
|
67
65
|
{navItems.length > 0 ? (
|
|
68
66
|
<ThemeProvider colorMode="day">
|
|
69
|
-
<Flex flexDirection
|
|
70
|
-
<Link
|
|
67
|
+
<Flex sx={{flexDirection: 'column', flex: '1 0 auto', color: 'fg.default', bg: 'canvas.default'}}>
|
|
68
|
+
<Link
|
|
69
|
+
as={GatsbyLink}
|
|
70
|
+
to="/"
|
|
71
|
+
sx={{display: 'inline-block', color: 'inherit', fontFamily: 'mono', mx: 4, my: 4}}
|
|
72
|
+
>
|
|
71
73
|
{siteMetadata.title}
|
|
72
74
|
</Link>
|
|
73
75
|
<NavItems items={navItems} />
|
|
@@ -84,25 +86,21 @@ function PrimerNavItems({items}) {
|
|
|
84
86
|
return (
|
|
85
87
|
<BorderBox
|
|
86
88
|
key={item.title}
|
|
87
|
-
|
|
88
|
-
borderRadius={0}
|
|
89
|
-
borderTopWidth={index !== 0 ? 1 : 0}
|
|
90
|
-
borderColor="border.muted"
|
|
91
|
-
p={4}
|
|
89
|
+
sx={{borderWidth: 0, borderRadius: 0, borderTopWidth: index !== 0 ? 1 : 0, borderColor: 'border.muted', p: 4}}
|
|
92
90
|
>
|
|
93
91
|
{item.children ? (
|
|
94
92
|
<Details key={index}>
|
|
95
93
|
{({open, toggle}) => (
|
|
96
94
|
<>
|
|
97
95
|
<summary onClick={toggle} style={{cursor: 'pointer'}}>
|
|
98
|
-
<Flex alignItems
|
|
96
|
+
<Flex sx={{alignItems: 'center', justifyContent: 'space-between'}}>
|
|
99
97
|
<Text>{item.title}</Text>
|
|
100
98
|
{open ? <ChevronUpIcon /> : <ChevronDownIcon />}
|
|
101
99
|
</Flex>
|
|
102
100
|
</summary>
|
|
103
|
-
<Flex flexDirection
|
|
101
|
+
<Flex sx={{flexDirection: 'column', mt: 2}}>
|
|
104
102
|
{item.children.map(child => (
|
|
105
|
-
<Link key={child.title} href={child.url}
|
|
103
|
+
<Link key={child.title} href={child.url} sx={{py: 1, mt: 2, fontSize: 1, color: 'inherit'}}>
|
|
106
104
|
{child.title}
|
|
107
105
|
</Link>
|
|
108
106
|
))}
|
|
@@ -111,7 +109,7 @@ function PrimerNavItems({items}) {
|
|
|
111
109
|
)}
|
|
112
110
|
</Details>
|
|
113
111
|
) : (
|
|
114
|
-
<Link key={index} href={item.url} color
|
|
112
|
+
<Link key={index} href={item.url} sx={{color: 'inherit', display: 'block'}}>
|
|
115
113
|
{item.title}
|
|
116
114
|
</Link>
|
|
117
115
|
)}
|
|
@@ -9,7 +9,7 @@ function NavDropdown({title, children}) {
|
|
|
9
9
|
<Details {...getDetailsProps()}>
|
|
10
10
|
<summary style={{cursor: 'pointer'}}>
|
|
11
11
|
<Text>{title}</Text>
|
|
12
|
-
<StyledOcticon icon={TriangleDownIcon}
|
|
12
|
+
<StyledOcticon icon={TriangleDownIcon} sx={{ml: 1}} />
|
|
13
13
|
</summary>
|
|
14
14
|
<Box position="absolute">
|
|
15
15
|
<Box
|
|
@@ -38,7 +38,13 @@ function NavItems({items}) {
|
|
|
38
38
|
p={4}
|
|
39
39
|
>
|
|
40
40
|
<Box display="flex" flexDirection="column">
|
|
41
|
-
<NavLink
|
|
41
|
+
<NavLink
|
|
42
|
+
as={GatsbyLink}
|
|
43
|
+
to={item.url}
|
|
44
|
+
activeClassName="active"
|
|
45
|
+
partiallyActive={true}
|
|
46
|
+
sx={{color: 'inherit'}}
|
|
47
|
+
>
|
|
42
48
|
{item.title}
|
|
43
49
|
</NavLink>
|
|
44
50
|
{item.children ? (
|
|
@@ -49,10 +55,12 @@ function NavItems({items}) {
|
|
|
49
55
|
as={GatsbyLink}
|
|
50
56
|
to={child.url}
|
|
51
57
|
activeClassName="active"
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
58
|
+
sx={{
|
|
59
|
+
display: 'block',
|
|
60
|
+
py: 1,
|
|
61
|
+
mt: 2,
|
|
62
|
+
fontSize: 1
|
|
63
|
+
}}
|
|
56
64
|
>
|
|
57
65
|
{child.title}
|
|
58
66
|
</NavLink>
|
|
@@ -64,10 +72,10 @@ function NavItems({items}) {
|
|
|
64
72
|
))}
|
|
65
73
|
{repositoryUrl ? (
|
|
66
74
|
<Box borderWidth={0} borderTopWidth={1} borderRadius={0} borderStyle="solid" borderColor="border.default" p={4}>
|
|
67
|
-
<Link href={repositoryUrl} color
|
|
75
|
+
<Link href={repositoryUrl} sx={{color: 'inherit'}}>
|
|
68
76
|
<Box display="flex" justifyContent="space-between" alignItems="center">
|
|
69
77
|
GitHub
|
|
70
|
-
<StyledOcticon icon={LinkExternalIcon} color
|
|
78
|
+
<StyledOcticon icon={LinkExternalIcon} sx={{color: 'fg.muted'}} />
|
|
71
79
|
</Box>
|
|
72
80
|
</Link>
|
|
73
81
|
</Box>
|
|
@@ -5,11 +5,11 @@ import Contributors from './contributors'
|
|
|
5
5
|
|
|
6
6
|
function PageFooter({editUrl, contributors}) {
|
|
7
7
|
return editUrl || contributors.length > 0 ? (
|
|
8
|
-
<BorderBox
|
|
9
|
-
<Grid
|
|
8
|
+
<BorderBox sx={{borderWidth: 0, borderTopWidth: 1, borderRadius: 0, mt: 8, py: 5}}>
|
|
9
|
+
<Grid sx={{gridGap: 4}}>
|
|
10
10
|
{editUrl ? (
|
|
11
11
|
<Link href={editUrl}>
|
|
12
|
-
<StyledOcticon icon={PencilIcon}
|
|
12
|
+
<StyledOcticon icon={PencilIcon} sx={{mr: 2}} />
|
|
13
13
|
Edit this page on GitHub
|
|
14
14
|
</Link>
|
|
15
15
|
) : null}
|
package/src/components/search.js
CHANGED
|
@@ -64,14 +64,16 @@ function Search() {
|
|
|
64
64
|
>
|
|
65
65
|
<ThemeProvider colorMode="day">
|
|
66
66
|
<BorderBox
|
|
67
|
-
minWidth={300}
|
|
68
|
-
maxHeight="70vh"
|
|
69
|
-
p={2}
|
|
70
|
-
boxShadow="shadow.large"
|
|
71
|
-
borderColor="border.muted"
|
|
72
|
-
bg="canvas.overlay"
|
|
73
|
-
borderRadius="12px"
|
|
74
67
|
style={{overflow: 'auto'}}
|
|
68
|
+
sx={{
|
|
69
|
+
minWidth: 300,
|
|
70
|
+
maxHeight: '70vh',
|
|
71
|
+
p: 2,
|
|
72
|
+
boxShadow: 'shadow.large',
|
|
73
|
+
borderColor: 'border.muted',
|
|
74
|
+
bg: 'canvas.overlay',
|
|
75
|
+
borderRadius: '12px'
|
|
76
|
+
}}
|
|
75
77
|
>
|
|
76
78
|
<SearchResults results={results} getItemProps={getItemProps} highlightedIndex={highlightedIndex} />
|
|
77
79
|
</BorderBox>
|
|
@@ -33,21 +33,20 @@ function Sidebar() {
|
|
|
33
33
|
|
|
34
34
|
return (
|
|
35
35
|
<Position
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
36
|
+
sx={{
|
|
37
|
+
position: 'sticky',
|
|
38
|
+
top: HEADER_HEIGHT,
|
|
39
|
+
height: `calc(100vh - ${HEADER_HEIGHT}px)`,
|
|
40
|
+
minWidth: 260,
|
|
41
|
+
bg: 'canvas.subtle'
|
|
42
|
+
}}
|
|
41
43
|
>
|
|
42
44
|
<BorderBox
|
|
43
45
|
{...scrollContainerProps}
|
|
44
|
-
borderWidth={0}
|
|
45
|
-
borderRightWidth={1}
|
|
46
|
-
borderRadius={0}
|
|
47
|
-
height="100%"
|
|
48
46
|
style={{overflow: 'auto'}}
|
|
47
|
+
sx={{borderWidth: 0, borderRightWidth: 1, borderRadius: 0, height: '100%'}}
|
|
49
48
|
>
|
|
50
|
-
<Flex flexDirection
|
|
49
|
+
<Flex sx={{flexDirection: 'column'}}>
|
|
51
50
|
<NavItems items={navItems} />
|
|
52
51
|
</Flex>
|
|
53
52
|
</BorderBox>
|
|
@@ -14,7 +14,7 @@ function getStatusColor(status) {
|
|
|
14
14
|
|
|
15
15
|
function StatusLabel({status}) {
|
|
16
16
|
return (
|
|
17
|
-
<Label outline
|
|
17
|
+
<Label outline sx={{color: getStatusColor(status), borderColor: getStatusColor(status)}}>
|
|
18
18
|
{status}
|
|
19
19
|
</Label>
|
|
20
20
|
)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {MDXProvider} from '@mdx-js/react'
|
|
2
2
|
import {Link, ThemeProvider} from '@primer/components'
|
|
3
3
|
import React from 'react'
|
|
4
|
+
import mdxComponents from '../mdx-components'
|
|
4
5
|
import Blockquote from './blockquote'
|
|
5
6
|
import Caption from './caption'
|
|
6
7
|
import Code from './code'
|
|
@@ -36,13 +37,14 @@ const components = {
|
|
|
36
37
|
ol: List.withComponent('ol'),
|
|
37
38
|
dl: DescriptionList,
|
|
38
39
|
|
|
39
|
-
//
|
|
40
|
+
// Custom components
|
|
40
41
|
Note,
|
|
41
42
|
Do,
|
|
42
43
|
Dont,
|
|
43
44
|
DoDontContainer,
|
|
44
45
|
Caption,
|
|
45
|
-
ImageContainer
|
|
46
|
+
ImageContainer,
|
|
47
|
+
...mdxComponents
|
|
46
48
|
}
|
|
47
49
|
|
|
48
50
|
function wrapRootElement({element}) {
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
import {validateChecklistSchema, validateChecklist} from '../validate-checklists'
|
|
2
|
-
|
|
3
|
-
describe('validateChecklistSchema()', () => {
|
|
4
|
-
const valid = [
|
|
5
|
-
{
|
|
6
|
-
title: 'Example checklist',
|
|
7
|
-
items: []
|
|
8
|
-
},
|
|
9
|
-
{
|
|
10
|
-
title: 'Example checklist',
|
|
11
|
-
items: [{id: 'example', description: 'Example item'}]
|
|
12
|
-
},
|
|
13
|
-
{
|
|
14
|
-
title: 'Example checklist',
|
|
15
|
-
items: [{id: 'example', description: 'Example item', someMetadata: 1}]
|
|
16
|
-
}
|
|
17
|
-
]
|
|
18
|
-
|
|
19
|
-
const invalid = [
|
|
20
|
-
{
|
|
21
|
-
title: 'Example checklist'
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
title: 'Example checklist',
|
|
25
|
-
items: [{description: 'Example item'}]
|
|
26
|
-
},
|
|
27
|
-
{
|
|
28
|
-
title: 1,
|
|
29
|
-
items: []
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
title: 'Example checklist',
|
|
33
|
-
items: 1
|
|
34
|
-
}
|
|
35
|
-
]
|
|
36
|
-
|
|
37
|
-
for (const checklistSchema of valid) {
|
|
38
|
-
test(`should validate ${JSON.stringify(checklistSchema)}`, () => {
|
|
39
|
-
expect(() => validateChecklistSchema(checklistSchema)).not.toThrow()
|
|
40
|
-
})
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
for (const checklistSchema of invalid) {
|
|
44
|
-
test(`should not validate ${JSON.stringify(checklistSchema)}`, () => {
|
|
45
|
-
expect(() => validateChecklistSchema(checklistSchema)).toThrow()
|
|
46
|
-
})
|
|
47
|
-
}
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
describe('validateChecklist()', () => {
|
|
51
|
-
const valid = [
|
|
52
|
-
{
|
|
53
|
-
checklist: {dependenciesReviewed: true},
|
|
54
|
-
checklistSchema: {
|
|
55
|
-
title: 'Example checklist',
|
|
56
|
-
items: [{id: 'dependenciesReviewed', description: 'Third-party dependencies have been reviewed'}]
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
]
|
|
60
|
-
|
|
61
|
-
const invalid = [
|
|
62
|
-
{
|
|
63
|
-
checklist: {reviewed: true},
|
|
64
|
-
checklistSchema: {
|
|
65
|
-
title: 'Example checklist',
|
|
66
|
-
items: [{id: 'dependenciesReviewed', description: 'Third-party dependencies have been reviewed'}]
|
|
67
|
-
}
|
|
68
|
-
},
|
|
69
|
-
{
|
|
70
|
-
checklist: {dependenciesReviewed: 1},
|
|
71
|
-
checklistSchema: {
|
|
72
|
-
title: 'Example checklist',
|
|
73
|
-
items: [{id: 'dependenciesReviewed', description: 'Third-party dependencies have been reviewed'}]
|
|
74
|
-
}
|
|
75
|
-
},
|
|
76
|
-
{
|
|
77
|
-
checklist: ['dependenciesReviewed'],
|
|
78
|
-
checklistSchema: {
|
|
79
|
-
title: 'Example checklist',
|
|
80
|
-
items: [{id: 'dependenciesReviewed', description: 'Third-party dependencies have been reviewed'}]
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
]
|
|
84
|
-
|
|
85
|
-
for (const {checklist, checklistSchema} of valid) {
|
|
86
|
-
test(`should validate ${JSON.stringify(checklist)}`, () => {
|
|
87
|
-
expect(() => validateChecklist(checklist, checklistSchema)).not.toThrow()
|
|
88
|
-
})
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
for (const {checklist, checklistSchema} of invalid) {
|
|
92
|
-
test(`should not validate ${JSON.stringify(checklist)}`, () => {
|
|
93
|
-
expect(() => validateChecklist(checklist, checklistSchema)).toThrow()
|
|
94
|
-
})
|
|
95
|
-
}
|
|
96
|
-
})
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
module.exports = {
|
|
2
|
-
title: 'Component checklist',
|
|
3
|
-
items: [
|
|
4
|
-
{
|
|
5
|
-
id: 'dependenciesReviewed',
|
|
6
|
-
description: 'All third-party dependencies have been reviewed.'
|
|
7
|
-
},
|
|
8
|
-
{
|
|
9
|
-
id: 'componentStatusDocumented',
|
|
10
|
-
description: 'Component status is documented.'
|
|
11
|
-
}
|
|
12
|
-
]
|
|
13
|
-
}
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import {H2} from './heading'
|
|
3
|
-
import {Box, StyledOcticon} from '@primer/components'
|
|
4
|
-
import {CheckCircleFillIcon, CircleIcon} from '@primer/octicons-react'
|
|
5
|
-
import GithubSlugger from 'github-slugger'
|
|
6
|
-
|
|
7
|
-
export type ChecklistSchema = {
|
|
8
|
-
title: string
|
|
9
|
-
items: Array<{id: string; description: string}>
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
type ChecklistProps = {
|
|
13
|
-
checklist: {[id: string]: boolean}
|
|
14
|
-
schema: ChecklistSchema
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function Checklist({checklist, schema}: ChecklistProps) {
|
|
18
|
-
const id = React.useMemo(() => {
|
|
19
|
-
const slugger = new GithubSlugger()
|
|
20
|
-
return slugger.slug(schema.title)
|
|
21
|
-
}, [schema.title])
|
|
22
|
-
return (
|
|
23
|
-
<>
|
|
24
|
-
<H2 id={id}>{schema.title}</H2>
|
|
25
|
-
<Box aria-describedby={id} as="ul" display="grid" gridGap={2} p={0} m={0}>
|
|
26
|
-
{schema.items.map(({id, description}) => {
|
|
27
|
-
const checked = Boolean(checklist[id])
|
|
28
|
-
return (
|
|
29
|
-
<ChecklistItem checked={checked} key={id}>
|
|
30
|
-
{description}
|
|
31
|
-
</ChecklistItem>
|
|
32
|
-
)
|
|
33
|
-
})}
|
|
34
|
-
</Box>
|
|
35
|
-
</>
|
|
36
|
-
)
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
type ChecklistItemProps = {
|
|
40
|
-
checked: boolean
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function ChecklistItem({checked, children}: React.PropsWithChildren<ChecklistItemProps>) {
|
|
44
|
-
return (
|
|
45
|
-
<Box as="li" display="grid" gridTemplateColumns="auto 1fr" gridGap={2} sx={{listStyleType: 'none'}}>
|
|
46
|
-
<Box height="24px" display="flex" alignItems="center">
|
|
47
|
-
{checked ? (
|
|
48
|
-
<StyledOcticon aria-label="Completed" icon={CheckCircleFillIcon} color="success.fg" />
|
|
49
|
-
) : (
|
|
50
|
-
<StyledOcticon aria-label="To do" icon={CircleIcon} color="border.default" />
|
|
51
|
-
)}
|
|
52
|
-
</Box>
|
|
53
|
-
<span>{children}</span>
|
|
54
|
-
</Box>
|
|
55
|
-
)
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export default Checklist
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import {graphql, useStaticQuery} from 'gatsby'
|
|
2
|
-
import React from 'react'
|
|
3
|
-
import Checklist from './checklist'
|
|
4
|
-
|
|
5
|
-
type ChecklistSchemaQuery = {
|
|
6
|
-
allChecklistSchema: {
|
|
7
|
-
nodes: Array<{
|
|
8
|
-
name: string
|
|
9
|
-
title: string
|
|
10
|
-
items: Array<{id: string; description: string}>
|
|
11
|
-
}>
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function Checklists({frontmatter}: {frontmatter: Record<string, any>}) {
|
|
16
|
-
const data = useStaticQuery<ChecklistSchemaQuery>(graphql`
|
|
17
|
-
query ChecklistSchemaQuery {
|
|
18
|
-
allChecklistSchema {
|
|
19
|
-
nodes {
|
|
20
|
-
name
|
|
21
|
-
title
|
|
22
|
-
items {
|
|
23
|
-
id
|
|
24
|
-
description
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
`)
|
|
30
|
-
|
|
31
|
-
const checklistSchemas = data.allChecklistSchema.nodes
|
|
32
|
-
|
|
33
|
-
const checklists = checklistSchemas
|
|
34
|
-
.map(schema => {
|
|
35
|
-
const key = `${schema.name}Checklist`
|
|
36
|
-
|
|
37
|
-
if (!(key in frontmatter)) {
|
|
38
|
-
return null
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
return <Checklist checklist={frontmatter[key]} schema={schema} />
|
|
42
|
-
})
|
|
43
|
-
.filter(Boolean)
|
|
44
|
-
|
|
45
|
-
return checklists
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export default Checklists
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
const validate = require('jsonschema').validate
|
|
2
|
-
|
|
3
|
-
exports.validateChecklistSchema = checklistSchema => {
|
|
4
|
-
const jsonSchema = {
|
|
5
|
-
type: 'object',
|
|
6
|
-
properties: {
|
|
7
|
-
title: {
|
|
8
|
-
type: 'string'
|
|
9
|
-
},
|
|
10
|
-
items: {
|
|
11
|
-
type: 'array',
|
|
12
|
-
items: {
|
|
13
|
-
type: 'object',
|
|
14
|
-
properties: {
|
|
15
|
-
id: {
|
|
16
|
-
type: 'string'
|
|
17
|
-
},
|
|
18
|
-
description: {
|
|
19
|
-
type: 'string'
|
|
20
|
-
}
|
|
21
|
-
},
|
|
22
|
-
required: ['id', 'description']
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
},
|
|
26
|
-
required: ['title', 'items']
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const {errors} = validate(checklistSchema, jsonSchema)
|
|
30
|
-
|
|
31
|
-
if (errors.length > 0) {
|
|
32
|
-
const errorMessage = errors.map(error => `• ${error.message}`).join('\n')
|
|
33
|
-
throw new Error(`Errors:\n${errorMessage}`)
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
exports.validateChecklist = (checklist, checklistSchema) => {
|
|
38
|
-
const jsonSchema = checklistSchemaToJsonSchema(checklistSchema)
|
|
39
|
-
|
|
40
|
-
const {errors} = validate(checklist, jsonSchema)
|
|
41
|
-
|
|
42
|
-
if (errors.length > 0) {
|
|
43
|
-
const errorMessage = errors.map(error => `• ${error.message}`).join('\n')
|
|
44
|
-
throw new Error(`Errors:\n${errorMessage}`)
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function checklistSchemaToJsonSchema(checklistSchema) {
|
|
49
|
-
const ids = checklistSchema.items.map(item => item.id)
|
|
50
|
-
|
|
51
|
-
const properties = ids.reduce((properties, id) => {
|
|
52
|
-
properties[id] = {type: 'boolean'}
|
|
53
|
-
return properties
|
|
54
|
-
}, {})
|
|
55
|
-
|
|
56
|
-
return {
|
|
57
|
-
type: 'object',
|
|
58
|
-
properties,
|
|
59
|
-
required: ids,
|
|
60
|
-
additionalProperties: false
|
|
61
|
-
}
|
|
62
|
-
}
|