Skip to content

Commit 8a0d14b

Browse files
authored
feat: add tab navigation in component pages (#17)
* feat: add tab navigation in component pages * refactor: change tabs' styles
1 parent e9a1262 commit 8a0d14b

File tree

4 files changed

+196
-34
lines changed

4 files changed

+196
-34
lines changed

gatsby-theme-woly/gatsby-node.js

Lines changed: 46 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
const pathsPath = require.resolve('./src/paths.js');
88
const { paths } = require(pathsPath);
99
const fs = require('fs');
10+
const path = require('path');
1011

1112
try {
1213
require.resolve(`babel-plugin-extract-react-types`);
@@ -46,23 +47,55 @@ async function createUsagePages({ actions, graphql, reporter }) {
4647

4748
const component = require.resolve('./src/templates/usage.js');
4849

49-
result.data.usages.nodes.forEach(({ id, fileAbsolutePath }) => {
50-
const [_, name, category, packageName] = fileAbsolutePath
51-
.split('/')
52-
.reverse();
50+
const groupedMdx = result.data.usages.nodes.reduce(
51+
(all, { id, fileAbsolutePath }) => {
52+
const [filename, name, category, packageName] = fileAbsolutePath
53+
.split('/')
54+
.reverse();
55+
56+
const key = `${packageName}-${category}-${name}`;
57+
const type = path.basename(filename, '.mdx');
58+
59+
if (!all[key]) {
60+
all[key] = {
61+
id,
62+
name,
63+
category,
64+
package: packageName,
65+
ids: [],
66+
pages: {},
67+
};
68+
}
5369

54-
actions.createPage({
55-
id,
56-
path: paths.componentPage({ package: packageName, category, name }),
57-
component,
58-
context: {
59-
pageID: id,
70+
all[key].ids.push(id);
71+
all[key].pages[id] = {
6072
name,
6173
category,
6274
package: packageName,
63-
},
64-
});
65-
});
75+
type,
76+
};
77+
78+
return all;
79+
},
80+
{},
81+
);
82+
83+
Object.values(groupedMdx).forEach(
84+
({ id, name, category, package: p, ids, pages }) => {
85+
actions.createPage({
86+
id,
87+
path: paths.componentPage({ package: p, category, name }),
88+
component,
89+
context: {
90+
ids,
91+
package: p,
92+
category,
93+
name,
94+
pages,
95+
},
96+
});
97+
},
98+
);
6699
}
67100

68101
exports.onPostBuild = async (gatsby) => {
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import React from 'react';
2+
import styled from 'styled-components';
3+
/**
4+
* TODO: set active tab from url
5+
*/
6+
7+
export const Tabs = ({ data }) => {
8+
const [activeIndex, setActiveIndex] = React.useState(0);
9+
10+
if (data.length === 0) {
11+
console.warn('pass at least one tab data element');
12+
return null;
13+
}
14+
15+
if (data.length === 1) {
16+
const [{ header, content, meta }] = data;
17+
18+
return (
19+
<>
20+
{header?.(meta)}
21+
{content}
22+
</>
23+
);
24+
}
25+
26+
const renderTabLabels = () => {
27+
return (
28+
<TabLabels>
29+
{data.map(({ label }, index) => (
30+
<TabLabel
31+
key={label}
32+
data-active={index === activeIndex}
33+
onClick={() => setActiveIndex(index)}
34+
>
35+
{label}
36+
</TabLabel>
37+
))}
38+
</TabLabels>
39+
);
40+
};
41+
42+
const renderTabContent = () => {
43+
const tabContent = data[activeIndex];
44+
45+
if (tabContent) {
46+
const { meta, content, header } = tabContent;
47+
48+
return (
49+
<TabContent>
50+
{header?.(meta)}
51+
{content}
52+
</TabContent>
53+
);
54+
}
55+
56+
return null;
57+
};
58+
59+
return (
60+
<>
61+
{renderTabLabels()}
62+
{renderTabContent()}
63+
</>
64+
);
65+
};
66+
67+
const TabLabels = styled.div`
68+
display: flex;
69+
70+
& > * + * {
71+
margin-left: 1rem;
72+
}
73+
`;
74+
75+
const TabLabel = styled.button`
76+
padding: 9px 15px;
77+
font-size: inherit;
78+
font-family: inherit;
79+
background: none;
80+
border: none;
81+
border-bottom: var(--highlight-border);
82+
83+
&[data-active='true'] {
84+
border-bottom-color: var(--accent);
85+
}
86+
`;
87+
88+
const TabContent = styled.div`
89+
padding-top: 12px;
90+
91+
&[data-active='false'] {
92+
display: none;
93+
}
94+
`;
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import React from 'react';
2+
import { pascalCase } from 'change-case';
3+
4+
export const pageSections = [
5+
{
6+
code: 'usage',
7+
label: 'Usage',
8+
renderHeader: (meta) => {
9+
return (
10+
<pre>
11+
import {'{'} {pascalCase(meta.name)} {'}'} from "{meta.package}
12+
";
13+
</pre>
14+
);
15+
},
16+
},
17+
{ code: 'specification', label: 'Specification' },
18+
{ code: 'state', label: 'State Map' },
19+
];

gatsby-theme-woly/src/templates/usage.js

Lines changed: 37 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,57 @@
11
import React from 'react';
22
import { graphql } from 'gatsby';
33
import { MDXRenderer } from 'gatsby-plugin-mdx';
4-
import { pascalCase } from 'change-case';
54
import { Layout } from '../components/layout';
5+
import { Tabs } from '../components/tabs';
66

7-
const installation = ({ package: p }) =>
8-
`npm install ${p}
9-
# or
10-
yarn add ${p}`;
7+
import { pageSections } from '../lib/constants';
8+
9+
const getTabData = ({ pages, mapper }) => {
10+
return mapper.reduce((all, { code, label, renderHeader }) => {
11+
const page = pages.find((page) => page.meta.type === code);
12+
13+
if (page) {
14+
all.push({
15+
label,
16+
meta: page.meta,
17+
content: <MDXRenderer>{page.body}</MDXRenderer>,
18+
header: renderHeader,
19+
});
20+
}
21+
22+
return all;
23+
}, []);
24+
};
1125

1226
const ComponentPage = ({ data, pageContext }) => {
13-
const { body } = data.usage;
14-
const { name, category, package: packageName } = pageContext;
27+
const pages = data.allMdx.nodes.map(({ id, body }) => {
28+
const meta = pageContext.pages[id];
29+
30+
return {
31+
meta,
32+
body,
33+
};
34+
});
35+
36+
const tabData = getTabData({ pages, mapper: pageSections });
1537

1638
return (
1739
<Layout>
1840
<div>
19-
<h2>{name}</h2>
20-
<pre>
21-
import {'{'} {pascalCase(name)} {'}'} from "{packageName}";
22-
</pre>
23-
{/* <h3>Installation</h3> */}
24-
{/* prettier-ignore */}
25-
{/* <pre>
26-
{installation(frontmatter)}
27-
</pre> */}
28-
<MDXRenderer>{body}</MDXRenderer>
41+
<h2>{pageContext.name}</h2>
42+
<Tabs data={tabData} />
2943
</div>
3044
</Layout>
3145
);
3246
};
3347

3448
export const pageQuery = graphql`
35-
query($pageID: String!) {
36-
usage: mdx(id: { eq: $pageID }) {
37-
fileAbsolutePath
38-
body
49+
query($ids: [String]) {
50+
allMdx(filter: { id: { in: $ids } }) {
51+
nodes {
52+
id
53+
body
54+
}
3955
}
4056
}
4157
`;

0 commit comments

Comments
 (0)