Skip to content

Commit 6055c2b

Browse files
bitionairesapegin
authored andcommitted
Feat: Page per section (#835)
Closes #494, #768.
1 parent fb1d2ec commit 6055c2b

22 files changed

+398
-61
lines changed

docs/Configuration.md

+17
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ By default, Styleguidist will look for `styleguide.config.js` file in your proje
2020
* [`handlers`](#handlers)
2121
* [`ignore`](#ignore)
2222
* [`logger`](#logger)
23+
* [`pagePerSection`](#pagepersection)
2324
* [`previewDelay`](#previewdelay)
2425
* [`propsParser`](#propsparser)
2526
* [`require`](#require)
@@ -261,6 +262,22 @@ module.exports = {
261262
}
262263
```
263264

265+
#### `pagePerSection`
266+
267+
Type: `Boolean`, default: `false`
268+
269+
Render one section or component per page, starting with the first.
270+
271+
If set to `true`, the sidebar will be visible on each page, except for the examples.
272+
273+
The value may be differ on each environment.
274+
275+
```javascript
276+
module.exports = {
277+
pagePerSection: process.env.NODE_ENV !== 'production'
278+
}
279+
```
280+
264281
#### `previewDelay`
265282

266283
Type: `Number`, default: 500

loaders/styleguide-loader.js

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ const CLIENT_CONFIG_OPTIONS = [
2424
'styles',
2525
'compilerConfig',
2626
'editorConfig',
27+
'pagePerSection',
2728
];
2829

2930
module.exports = function() {};

scripts/schemas/config.js

+4
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,10 @@ module.exports = {
124124
logger: {
125125
type: 'object',
126126
},
127+
pagePerSection: {
128+
type: 'boolean',
129+
default: false,
130+
},
127131
previewDelay: {
128132
type: 'number',
129133
default: 500,

src/index.js

+9-1
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,28 @@
11
/* eslint-disable import/first */
2-
32
import './polyfills';
43
import './styles';
54
import ReactDOM from 'react-dom';
65
import renderStyleguide from './utils/renderStyleguide';
76

87
// Examples code revision to rerender only code examples (not the whole page) when code changes
8+
// eslint-disable-next-line no-unused-vars
99
let codeRevision = 0;
1010

11+
/** Scrolls to origin when current window location hash points to an isolated view. */
12+
const scrollToOrigin = () => {
13+
if (window.location.hash.indexOf('#!/') === 0) {
14+
window.scrollTo(0, 0);
15+
}
16+
};
17+
1118
const render = () => {
1219
// eslint-disable-next-line import/no-unresolved
1320
const styleguide = require('!!../loaders/styleguide-loader!./index.js');
1421
ReactDOM.render(renderStyleguide(styleguide, codeRevision), document.getElementById('app'));
1522
};
1623

1724
window.addEventListener('hashchange', render);
25+
window.addEventListener('hashchange', scrollToOrigin);
1826

1927
/* istanbul ignore if */
2028
if (module.hot) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import React from 'react';
2+
import ComponentsListRenderer from 'rsg-components/ComponentsList/ComponentsListRenderer';
3+
import PropTypes from 'prop-types';
4+
import getUrl from '../../utils/getUrl';
5+
6+
function ComponentsList({ classes, items, useIsolatedLinks = false }) {
7+
const mappedItems = items.map(item => ({
8+
...item,
9+
href: getUrl({
10+
name: item.name,
11+
slug: item.slug,
12+
anchor: !useIsolatedLinks,
13+
isolated: useIsolatedLinks,
14+
}),
15+
}));
16+
return <ComponentsListRenderer classes={classes} items={mappedItems} />;
17+
}
18+
19+
ComponentsList.propTypes = {
20+
items: PropTypes.array.isRequired,
21+
classes: PropTypes.object,
22+
useIsolatedLinks: PropTypes.bool,
23+
};
24+
25+
export default ComponentsList;

src/rsg-components/ComponentsList/ComponentsList.spec.js

+37
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,50 @@
11
import React from 'react';
2+
import ComponentsList from './ComponentsList';
23
import { ComponentsListRenderer } from './ComponentsListRenderer';
34

5+
it('should set the correct href for items', () => {
6+
const components = [
7+
{
8+
name: 'Button',
9+
slug: 'button',
10+
},
11+
{
12+
name: 'Input',
13+
slug: 'input',
14+
},
15+
];
16+
17+
const actual = shallow(<ComponentsList items={components} classes={{}} />);
18+
expect(actual).toMatchSnapshot();
19+
});
20+
21+
it('should set the correct href for items when isolated links should be used', () => {
22+
const components = [
23+
{
24+
name: 'Button',
25+
slug: 'button',
26+
},
27+
{
28+
name: 'Input',
29+
slug: 'input',
30+
},
31+
];
32+
33+
const actual = shallow(<ComponentsList items={components} classes={{}} useIsolatedLinks />);
34+
expect(actual).toMatchSnapshot();
35+
});
36+
437
it('should render sections with nested components', () => {
538
const components = [
639
{
740
name: 'Button',
841
slug: 'button',
42+
href: '#button',
943
},
1044
{
1145
name: 'Input',
1246
slug: 'input',
47+
href: '#input',
1348
},
1449
];
1550
const actual = shallow(<ComponentsListRenderer items={components} classes={{}} />);
@@ -28,9 +63,11 @@ it('should ignore items without name', () => {
2863
{
2964
name: 'Button',
3065
slug: 'button',
66+
href: '#button',
3167
},
3268
{
3369
slug: 'input',
70+
href: '#input',
3471
},
3572
];
3673
const actual = shallow(<ComponentsListRenderer items={components} classes={{}} />);

src/rsg-components/ComponentsList/ComponentsListRenderer.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,12 @@ export function ComponentsListRenderer({ classes, items }) {
4242

4343
return (
4444
<ul className={classes.list}>
45-
{items.map(({ heading, name, slug, content }) => (
45+
{items.map(({ heading, name, href, content }) => (
4646
<li
4747
className={cx(classes.item, (!content || !content.props.items.length) && classes.isChild)}
4848
key={name}
4949
>
50-
<Link className={cx(heading && classes.heading)} href={`#${slug}`}>
50+
<Link className={cx(heading && classes.heading)} href={href}>
5151
{name}
5252
</Link>
5353
{content}
@@ -60,6 +60,7 @@ export function ComponentsListRenderer({ classes, items }) {
6060
ComponentsListRenderer.propTypes = {
6161
items: PropTypes.array.isRequired,
6262
classes: PropTypes.object.isRequired,
63+
useIsolatedLinks: PropTypes.bool,
6364
};
6465

6566
export default Styled(styles)(ComponentsListRenderer);

src/rsg-components/ComponentsList/__snapshots__/ComponentsList.spec.js.snap

+40
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,43 @@ exports[`should render sections with nested components 1`] = `
4242
</li>
4343
</ul>
4444
`;
45+
46+
exports[`should set the correct href for items 1`] = `
47+
<Styled(ComponentsList)
48+
classes={Object {}}
49+
items={
50+
Array [
51+
Object {
52+
"href": "blank#button",
53+
"name": "Button",
54+
"slug": "button",
55+
},
56+
Object {
57+
"href": "blank#input",
58+
"name": "Input",
59+
"slug": "input",
60+
},
61+
]
62+
}
63+
/>
64+
`;
65+
66+
exports[`should set the correct href for items when isolated links should be used 1`] = `
67+
<Styled(ComponentsList)
68+
classes={Object {}}
69+
items={
70+
Array [
71+
Object {
72+
"href": "blank#!/Button",
73+
"name": "Button",
74+
"slug": "button",
75+
},
76+
Object {
77+
"href": "blank#!/Input",
78+
"name": "Input",
79+
"slug": "input",
80+
},
81+
]
82+
}
83+
/>
84+
`;
+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
export { default } from 'rsg-components/ComponentsList/ComponentsListRenderer';
1+
export { default } from 'rsg-components/ComponentsList/ComponentsList';

src/rsg-components/Playground/Playground.spec.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ const options = {
2323
highlightTheme: 'base16-light',
2424
},
2525
codeRevision: 0,
26-
slots,
26+
slots: slots({}),
2727
},
2828
childContextTypes: {
2929
slots: PropTypes.object.isRequired,

src/rsg-components/StyleGuide/StyleGuide.js

+36-3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,29 @@ import Error from 'rsg-components/Error';
88
import { HOMEPAGE } from '../../../scripts/consts';
99
import { DisplayModes } from '../../consts';
1010

11+
/**
12+
* This function will return true, if the sidebar should be visible and false otherwise.
13+
*
14+
* These sorted conditions (highest precedence first) define the visibility
15+
* state of the sidebar.
16+
*
17+
* - Sidebar is hidden for isolated example views
18+
* - Sidebar is always visible when pagePerSection
19+
* - Sidebar is hidden when showSidebar is set to false
20+
* - Sidebar is visible when showSidebar is set to true for non-isolated views
21+
*
22+
* @param {boolean} displayMode
23+
* @param {boolean} showSidebar
24+
* @param {boolean} pagePerSection
25+
* @returns {boolean}
26+
*/
27+
function hasSidebar(displayMode, showSidebar, pagePerSection = false) {
28+
return (
29+
(pagePerSection && displayMode !== DisplayModes.example) ||
30+
(showSidebar && displayMode === DisplayModes.all)
31+
);
32+
}
33+
1134
export default class StyleGuide extends Component {
1235
static propTypes = {
1336
codeRevision: PropTypes.number.isRequired,
@@ -17,6 +40,8 @@ export default class StyleGuide extends Component {
1740
welcomeScreen: PropTypes.bool,
1841
patterns: PropTypes.array,
1942
displayMode: PropTypes.string,
43+
allSections: PropTypes.array.isRequired,
44+
pagePerSection: PropTypes.bool,
2045
};
2146

2247
static childContextTypes = {
@@ -52,7 +77,15 @@ export default class StyleGuide extends Component {
5277
}
5378

5479
render() {
55-
const { config, sections, welcomeScreen, patterns, displayMode } = this.props;
80+
const {
81+
config,
82+
sections,
83+
welcomeScreen,
84+
patterns,
85+
displayMode,
86+
allSections,
87+
pagePerSection,
88+
} = this.props;
5689

5790
if (this.state.error) {
5891
return <Error error={this.state.error} info={this.state.info} />;
@@ -66,8 +99,8 @@ export default class StyleGuide extends Component {
6699
<StyleGuideRenderer
67100
title={config.title}
68101
homepageUrl={HOMEPAGE}
69-
toc={<TableOfContents sections={sections} />}
70-
hasSidebar={config.showSidebar && displayMode === DisplayModes.all}
102+
toc={<TableOfContents sections={allSections} useIsolatedLinks={pagePerSection} />}
103+
hasSidebar={hasSidebar(displayMode, config.showSidebar, pagePerSection)}
71104
>
72105
<Sections sections={sections} depth={1} />
73106
</StyleGuideRenderer>

0 commit comments

Comments
 (0)