Skip to content

Page per section #835

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 28 commits into from
Mar 21, 2018
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
49631b2
feat(*): allow to show one component per page
Jan 9, 2018
80135ba
fix(*): fix according sapegin recomendations
Jan 9, 2018
b546d05
docs(*): add docs for oneComponentPerPage prop
Jan 9, 2018
9a2f8cb
test(*): add tests for oneComponentPerPage functionality
stepancar Jan 9, 2018
85158da
Merge branches 'feature/allow-to-show-one-component-per-page' and 'ma…
bitionaire Feb 21, 2018
15975dc
Replaced 'oneComponentPerPage' with 'pagePerSection' property and add…
bitionaire Feb 21, 2018
b4a6780
Changed test name
bitionaire Feb 21, 2018
98931f4
Fixed section route
bitionaire Feb 21, 2018
48a585b
Use isolated links extracted from props instead of context
bitionaire Feb 21, 2018
fca7e0c
Added option to configure pagePerSection as function
bitionaire Feb 21, 2018
12d823c
Added scrollTo(0,0) on sections change
bitionaire Feb 21, 2018
fa0e3e8
Forward props correctly
bitionaire Feb 21, 2018
b4f94fe
Removed unintended change
bitionaire Feb 21, 2018
42396ca
Fixed test
bitionaire Feb 21, 2018
3f7c42a
Added scroll tests
bitionaire Feb 22, 2018
fecaf2a
Added getRouteData test for single section
bitionaire Feb 22, 2018
ad76446
Coverage fix
bitionaire Feb 22, 2018
bfe0c46
Modifications by PR review
bitionaire Feb 23, 2018
26c56cf
Modifications by PR review
bitionaire Feb 23, 2018
dd74215
Splitted URL generation and renderer
bitionaire Feb 26, 2018
d2a38c0
Replaced renderStyleguide SFC by <App /> and moved scrollTo logic int…
bitionaire Feb 26, 2018
5eb2744
Removed unused snapshot file
bitionaire Feb 26, 2018
aaac44b
Reverted changes
bitionaire Mar 2, 2018
c2984df
Added snapshot
bitionaire Mar 2, 2018
5bd367f
Fixes by PR review
bitionaire Mar 2, 2018
0c03a66
Replaced startsWith with indexOf === 0
bitionaire Mar 3, 2018
6c6e5a7
Hide IsolateButton when pagePerSection is true
bitionaire Mar 9, 2018
55b81ef
Merge branch 'master' into feature/page-per-section
sapegin Mar 19, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@

**Isolated React component development environment with a living style guide**

[![Build Status](https://travis-ci.org/styleguidist/react-styleguidist.svg)](https://travis-ci.org/styleguidist/react-styleguidist) [![npm](https://img.shields.io/npm/v/react-styleguidist.svg)](https://www.npmjs.com/package/react-styleguidist) [![Join the chat at https://gitter.im/styleguidist/styleguidist](https://badges.gitter.im/styleguidist/styleguidist.svg)](https://gitter.im/styleguidist/styleguidist?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Join the community on Spectrum](https://withspectrum.github.io/badge/badge.svg)](https://spectrum.chat/styleguidist) [![Codecov](https://codecov.io/gh/styleguidist/react-styleguidist/branch/master/graph/badge.svg)](https://codecov.io/gh/styleguidist/react-styleguidist)
[![Open Source Helpers](https://www.codetriage.com/styleguidist/react-styleguidist/badges/users.svg)](https://www.codetriage.com/styleguidist/react-styleguidist)
[![Build Status](https://travis-ci.org/styleguidist/react-styleguidist.svg)](https://travis-ci.org/styleguidist/react-styleguidist) [![npm](https://img.shields.io/npm/v/react-styleguidist.svg)](https://www.npmjs.com/package/react-styleguidist) [![Join the chat at https://gitter.im/styleguidist/styleguidist](https://badges.gitter.im/styleguidist/styleguidist.svg)](https://gitter.im/styleguidist/styleguidist?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Join the community on Spectrum](https://withspectrum.github.io/badge/badge.svg)](https://spectrum.chat/styleguidist) [![Codecov](https://codecov.io/gh/styleguidist/react-styleguidist/branch/master/graph/badge.svg)](https://codecov.io/gh/styleguidist/react-styleguidist) [![Open Source Helpers](https://www.codetriage.com/styleguidist/react-styleguidist/badges/users.svg)](https://www.codetriage.com/styleguidist/react-styleguidist)

</div>

Expand Down
17 changes: 17 additions & 0 deletions docs/Configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ By default, Styleguidist will look for `styleguide.config.js` file in your proje
* [`handlers`](#handlers)
* [`ignore`](#ignore)
* [`logger`](#logger)
* [`pagePerSection`](#pagepersection)
* [`previewDelay`](#previewdelay)
* [`propsParser`](#propsparser)
* [`require`](#require)
Expand Down Expand Up @@ -259,6 +260,22 @@ module.exports = {
}
```

#### `pagePerSection`

Type: `Boolean`, default: `false`

Render one section or component per page, starting with the first.

If set to `true`, the sidebar will be visible on each page, except for the examples.

The value may be differ on each environment.

```javascript
module.exports = {
pagePerSection: process.env.NODE_ENV !== 'production'
}
```

#### `previewDelay`

Type: `Number`, default: 500
Expand Down
1 change: 0 additions & 1 deletion docs/Cookbook.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,6 @@ const iconElements = Object.keys(icons).map(iconName => {
</span>
)
})

;<div>{iconElements}</div>
```

Expand Down
1 change: 1 addition & 0 deletions loaders/styleguide-loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const CLIENT_CONFIG_OPTIONS = [
'styles',
'compilerConfig',
'editorConfig',
'pagePerSection',
];

module.exports = function() {};
Expand Down
4 changes: 4 additions & 0 deletions scripts/schemas/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,10 @@ module.exports = {
logger: {
type: 'object',
},
pagePerSection: {
type: 'boolean',
default: false,
},
previewDelay: {
type: 'number',
default: 500,
Expand Down
9 changes: 7 additions & 2 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,21 @@

import './polyfills';
import './styles';
import React from 'react';
import ReactDOM from 'react-dom';
import renderStyleguide from './utils/renderStyleguide';
import App from './rsg-components/App';

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

const render = () => {
// eslint-disable-next-line import/no-unresolved
const styleguide = require('!!../loaders/styleguide-loader!./index.js');
ReactDOM.render(renderStyleguide(styleguide, codeRevision), document.getElementById('app'));
ReactDOM.render(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it was necessary: it should rerender everything on hash change, so you can scroll in renderStyleguide.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't want to introduce any kind of global variable; either the hash of the last location or a boolean that will indicate if an event listener for hashchange was already registered.
I think it'll be easier to handle the component (or event handler) lifecycle in the <App />. But it's your project so you decide. Should I revert the changes and add an event listener for the first time renderStyleguide will be called?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, please. I think it makes this part much more complex then it was.

What kind of an event handler you want to add? There's a hashchange handler already that rerenders everything.

<App styleguide={styleguide} codeRevision={codeRevision} />,
document.getElementById('app')
);
};

window.addEventListener('hashchange', render);
Expand Down
78 changes: 78 additions & 0 deletions src/rsg-components/App/App.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { isFunction } from 'lodash';
import slots from 'rsg-components/slots';
import StyleGuide from 'rsg-components/StyleGuide';
import getPageTitle from '../../utils/getPageTitle';
import getRouteData from '../../utils/getRouteData';
import globalizeComponents from '../../utils/globalizeComponents';
import processSections from '../../utils/processSections';

export default class App extends Component {
static propTypes = {
/** Object returned by styleguide-loader */
styleguide: PropTypes.object.isRequired,
codeRevision: PropTypes.number.isRequired,
loc: PropTypes.object,
doc: PropTypes.object,
hist: PropTypes.object,
};

static defaultProps = {
loc: window.location,
doc: document,
hist: window.history,
};

constructor(props) {
super(props);
this.lastHash = props.loc.hash;
}

componentDidUpdate() {
// scroll to top of styleguide container when sections changed
if (this.props.styleguide.config.pagePerSection && this.props.loc.hash !== this.lastHash) {
if (window && isFunction(window.scrollTo)) {
window.scrollTo(0, 0);
}
}
this.currentHash = this.props.loc.hash;
}

render() {
const { styleguide, codeRevision, loc, doc, hist } = this.props;

const allSections = processSections(styleguide.sections);

// Globalize all components, not just ones we see on the screen, to make
// all components accessible to all examples
globalizeComponents(allSections);

const { title, pagePerSection } = styleguide.config;
const { sections, displayMode } = getRouteData(allSections, loc.hash, pagePerSection);

// Update page title
doc.title = getPageTitle(sections, title, displayMode);

// If the current hash location was set to just `/` (e.g. when navigating back from isolated view to overview)
// replace the URL with one without hash, to present the user with a single address of the overview screen
if (loc.hash === '#/') {
const url = loc.pathname + loc.search;
hist.replaceState('', doc.title, url);
}

return (
<StyleGuide
codeRevision={codeRevision}
config={styleguide.config}
slots={slots}
welcomeScreen={styleguide.welcomeScreen}
patterns={styleguide.patterns}
sections={sections}
allSections={allSections}
displayMode={displayMode}
pagePerSection={pagePerSection}
/>
);
}
}
180 changes: 180 additions & 0 deletions src/rsg-components/App/App.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
import React from 'react';
import { cloneDeep } from 'lodash';
import App from './App';

const styleguide = {
config: {
title: 'My Style Guide',
},
sections: [
{
components: [
{
props: {
displayName: 'Button',
},
module: 'ButtonModule',
},
{
props: {
displayName: 'Image',
},
module: 'ImageModule',
},
],
},
],
welcomeScreen: false,
patterns: ['button', 'input'],
};
const codeRevision = 1;
const location = {
hash: '',
};
const doc = {
title: () => {},
};
const history = {
replaceState: () => {},
};

afterEach(() => {
delete global.Button;
delete global.Image;
});

describe('<App />', () => {
it('should render StyleGuide component', () => {
const result = shallow(
<App
styleguide={styleguide}
codeRevision={codeRevision}
loc={location}
doc={doc}
hist={history}
/>
);
expect(result).toMatchSnapshot();
});

it('should globalize all components', () => {
shallow(
<App
styleguide={styleguide}
codeRevision={codeRevision}
loc={location}
doc={doc}
hist={history}
/>
);
expect(global.Button).toBe('ButtonModule');
expect(global.Image).toBe('ImageModule');
});

it('should globalize all components in isolated mode', () => {
shallow(
<App
styleguide={styleguide}
codeRevision={codeRevision}
loc={{ hash: '#!/Button' }}
doc={doc}
hist={history}
/>
);
expect(global.Button).toBe('ButtonModule');
expect(global.Image).toBe('ImageModule');
});

it('should change document title', () => {
const title = jest.fn();
const location = { hash: '' };
Object.defineProperty(location, 'title', {
set: title,
});
shallow(
<App
styleguide={styleguide}
codeRevision={codeRevision}
loc={location}
doc={location}
hist={history}
/>
);
expect(title).toBeCalledWith('My Style Guide');
});

it('should change document title in isolated mode', () => {
const title = jest.fn();
const location = { hash: '#!/Button' };
Object.defineProperty(location, 'title', {
set: title,
});
shallow(
<App
styleguide={styleguide}
codeRevision={codeRevision}
loc={location}
doc={location}
hist={history}
/>
);
expect(title).toBeCalledWith('Button — My Style Guide');
});

it('should remove #/ from the address bar', () => {
const location = { hash: '#/', pathname: '/pizza', search: '?foo=bar' };
const history = { replaceState: jest.fn() };
shallow(
<App
styleguide={styleguide}
codeRevision={codeRevision}
loc={location}
doc={doc}
hist={history}
/>
);
expect(history.replaceState).toBeCalledWith('', 'My Style Guide', '/pizza?foo=bar');
});

it('should scroll to top on sections change, when pagePerSections is true', () => {
const scrollTo = jest.fn();
global.window.scrollTo = scrollTo;

const styleguideCopy = cloneDeep(styleguide);
styleguideCopy.config.pagePerSection = true;

const props = {
styleguide: styleguideCopy,
codeRevision,
loc: { hash: '#A' },
doc,
hist: history,
};

const wrapper = shallow(<App {...props} />);
wrapper.setProps({ ...props, loc: { hash: '#B' } });

expect(scrollTo).toHaveBeenCalled();
});

it('should not scroll to top on sections change, when pagePerSections is false', () => {
const scrollTo = jest.fn();
global.window.scrollTo = scrollTo;

const styleguideCopy = cloneDeep(styleguide);
styleguideCopy.config.pagePerSection = false;

const props = {
styleguide: styleguideCopy,
codeRevision,
loc: { hash: '#A' },
doc,
hist: history,
};

const wrapper = shallow(<App {...props} />);
wrapper.setProps({ ...props, loc: { hash: '#B' } });

expect(scrollTo).not.toHaveBeenCalled();
});
});
Loading