Skip to content

Commit

Permalink
Add support for cascading titles (#37)
Browse files Browse the repository at this point in the history
Ref #11

This PR is cascading titles MVP. My project requires similar to helment
behaviour for titles.
  • Loading branch information
TrySound committed Jul 11, 2018
1 parent d48ca47 commit ead3722
Show file tree
Hide file tree
Showing 11 changed files with 368 additions and 76 deletions.
2 changes: 2 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,7 @@ module.exports = {
'react/forbid-prop-types': 0,
'react/prop-types': 0,
'react/no-did-mount-set-state': 0,
'react/sort-comp': 0,
'react/no-unused-state': 0,
},
};
24 changes: 12 additions & 12 deletions .size-snapshot.json
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
{
"dist/index.umd.js": {
"bundled": 5720,
"minified": 2672,
"gzipped": 1117
"bundled": 7135,
"minified": 3281,
"gzipped": 1319
},
"dist/index.cjs.js": {
"bundled": 4138,
"minified": 2642,
"gzipped": 942
"bundled": 5463,
"minified": 3247,
"gzipped": 1152
},
"dist/index.esm.js": {
"bundled": 3752,
"minified": 2327,
"gzipped": 853,
"bundled": 5073,
"minified": 2929,
"gzipped": 1062,
"treeshaked": {
"rollup": {
"code": 392,
"import_statements": 342
"code": 429,
"import_statements": 328
},
"webpack": {
"code": 1514
"code": 1565
}
}
}
Expand Down
62 changes: 47 additions & 15 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 7 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-head",
"version": "2.1.0",
"version": "3.0.0-0",
"description": "SSR-ready Document Head management for React 16+",
"main": "dist/index.cjs.js",
"module": "dist/index.esm.js",
Expand All @@ -9,15 +9,14 @@
"dist"
],
"scripts": {
"prepare": "npm run lint && npm run test && npm run build",
"prepare": "npm run test && npm run build",
"build:flow": "echo \"// @flow\n\nexport * from '../src'\" > dist/index.cjs.js.flow",
"build:watch": "rollup -c --watch",
"build": "rollup -c && npm run build:flow",
"example": "cd example && npm i && npm start",
"dev": "run-p build:watch example",
"pretest": "npm run lint",
"test": "jest",
"posttest": "npm run build",
"posttest": "npm run lint",
"test:watch": "jest --watch",
"typecheck:flow": "flow check --max-warnings=0",
"lint": "eslint ./src",
Expand Down Expand Up @@ -68,10 +67,11 @@
"lint-staged": "^7.1.0",
"npm-run-all": "^4.1.3",
"prettier": "^1.12.1",
"prop-types": "^15.6.1",
"prop-types": "^15.6.2",
"raf": "^3.4.0",
"react": "^16.3.2",
"react-dom": "^16.3.2",
"react": "^16.4.1",
"react-dom": "^16.4.1",
"react-powerplug": "^1.0.0-rc.1",
"react-test-renderer": "^16.4.1",
"rollup": "^0.61.2",
"rollup-plugin-babel": "^4.0.0-beta.7",
Expand Down
36 changes: 29 additions & 7 deletions src/HeadProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,37 @@ export default class HeadProvider extends React.Component {
children: PropTypes.node.isRequired,
};

headTags = {
add: tag => this.props.headTags.push(tag),
index = -1;

state = {
list: [],
addClientTag: tag => {
this.setState(state => ({
list: [...state.list, tag],
}));
this.index += 1;
return this.index;
},
removeClientTag: index => {
this.setState(state => {
const list = [...state.list];
list[index] = null;
return { list };
});
},
addServerTag: tag => {
const { headTags } = this.props;
if (tag.type === 'title') {
const index = headTags.findIndex(prev => prev.type === 'title');
if (index !== -1) {
headTags.splice(index, 1);
}
}
headTags.push(tag);
},
};

render() {
return (
<Provider value={this.headTags}>
{React.Children.only(this.props.children)}
</Provider>
);
return <Provider value={this.state}>{this.props.children}</Provider>;
}
}
29 changes: 23 additions & 6 deletions src/HeadTag.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ export default class HeadTag extends React.Component {
canUseDOM: false,
};

headTags = null;
index = -1;

componentDidMount() {
this.setState({ canUseDOM: true });

Expand All @@ -24,21 +27,35 @@ export default class HeadTag extends React.Component {
if (ssrTags) {
ssrTags.remove();
}
this.index = this.headTags.addClientTag(tag);
}

componentWillUnmount() {
this.headTags.removeClientTag(this.index);
}

render() {
const { tag: Tag, ...rest } = this.props;

if (this.state.canUseDOM) {
const Comp = <Tag {...rest} />;
return ReactDOM.createPortal(Comp, document.head);
}

return (
<Consumer>
{headTags => {
this.headTags = headTags;

if (this.state.canUseDOM) {
if (
Tag === 'title' &&
headTags.list.lastIndexOf(Tag) !== this.index
) {
return null;
}
const ClientComp = <Tag {...rest} />;
return ReactDOM.createPortal(ClientComp, document.head);
}

const ServerComp = <Tag data-rh="" {...rest} />;
headTags.add(ServerComp);
headTags.addServerTag(ServerComp);
return null;
}}
</Consumer>
);
Expand Down
56 changes: 35 additions & 21 deletions src/__tests__/HeadTags.node.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,44 @@ import { renderToStaticMarkup } from 'react-dom/server';
import { HeadProvider, HeadTag, Title, Style, Meta, Link } from '../';

describe('HeadTag during server rendering', () => {
const arr = [];
const globalCss = `p {
color: #121212;
}`;
const markup = renderToStaticMarkup(
<HeadProvider headTags={arr}>
<div>
Yes render
<HeadTag tag="test" content="testing tag">
No render
</HeadTag>
<Title>Title</Title>
<Style>{globalCss}</Style>
<Link href="index.css" />
<Meta charset="utf-8" />
</div>
</HeadProvider>
);

it('renders nothing', () => {
it('renders nothing and adds tags to headTags context array', () => {
const arr = [];
const globalCss = `p {
color: #121212;
}`;
const markup = renderToStaticMarkup(
<HeadProvider headTags={arr}>
<div>
Yes render
<HeadTag tag="test" content="testing tag">
No render
</HeadTag>
<Title>Title</Title>
<Style>{globalCss}</Style>
<Link href="index.css" />
<Meta charset="utf-8" />
</div>
</HeadProvider>
);
expect(markup).toMatchSnapshot();
expect(arr).toMatchSnapshot();
});

it('adds tags to headTags context array', () => {
it('render only the last title', () => {
const arr = [];
renderToStaticMarkup(
<HeadProvider headTags={arr}>
<div>
<Title>Title 1</Title>
</div>
<div>
<Title>Title 2</Title>
</div>
<div>
<Title>Title 3</Title>
</div>
</HeadProvider>
);
expect(arr).toMatchSnapshot();
});
});

0 comments on commit ead3722

Please sign in to comment.