Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
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
2 changes: 2 additions & 0 deletions src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ declare module render {
shallow:boolean;
xml:boolean;
pretty:boolean;
alwaysRenderedComponents: Array[String];
}

function render(vnode:VNode, context?:any, options?:Options):string;
function shallowRender(vnode:VNode, context?:any):string;
function mixedRender(vnode: VNode, alwaysRenderedComponents: Array[String], context?: any): string;
}

export = render;
26 changes: 23 additions & 3 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,27 @@ renderToString.render = renderToString;
let shallowRender = (vnode, context) => renderToString(vnode, context, SHALLOW);


/**
* Only render elements, leaving Components inline as `<ComponentName ... />`
* except those specified in fullyRenderedComponents.
* This method is just a convenience alias for `render(vnode, context, { shallow:true, alwaysRenderedComponented: [] })`
* @param {VNode} vnode JSX VNode to render.
* @param {Array} alwaysRenderedComponents List of components that should be rendered with shallow rendering
* @param {Object} [context={}] Optionally pass an initial context object through the render path.
*/
let mixedRender = (vnode, alwaysRenderedComponents = [], context) => {
Copy link
Member

Choose a reason for hiding this comment

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

This file is size-constrained - would it be alright to add the alwaysRenderedComponents option, but not export another method (mixedRender)? That would keep things reasonably minimal.

Copy link
Author

Choose a reason for hiding this comment

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

Sure! I'll update the PR.

const opts = Object.assign({ alwaysRenderedComponents }, SHALLOW);
return renderToString(vnode, context, opts);
};


/** The default export is an alias of `render()`. */
export default function renderToString(vnode, context, opts, inner, isSvgMode) {
let { nodeName, attributes, children } = vnode || EMPTY,
isComponent = false;
context = context || {};
opts = opts || {};
opts.alwaysRenderedComponents = opts.alwaysRenderedComponents || [];

let pretty = opts.pretty,
indentChar = typeof pretty==='string' ? pretty : '\t';
Expand All @@ -69,9 +84,12 @@ export default function renderToString(vnode, context, opts, inner, isSvgMode) {

// components
if (typeof nodeName==='function') {
let componentName = getComponentName(nodeName);
isComponent = true;
if (opts.shallow && (inner || opts.renderRootComponent===false)) {
nodeName = getComponentName(nodeName);
if (opts.shallow &&
(inner || opts.renderRootComponent===false) &&
!opts.alwaysRenderedComponents.includes(componentName)) {
Copy link
Member

Choose a reason for hiding this comment

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

Needs to use indexOf()!==-1 because this lib supports IE9+.

nodeName = componentName;
}
else {
let props = getNodeProps(vnode),
Expand Down Expand Up @@ -230,10 +248,12 @@ function getFallbackComponentName(component) {
return name;
}
renderToString.shallowRender = shallowRender;
renderToString.mixedRender = mixedRender;


export {
renderToString as render,
renderToString,
shallowRender
shallowRender,
mixedRender
};
84 changes: 84 additions & 0 deletions test/mixedRender.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { mixedRender } from '../src';
import { h, Component } from 'preact';
import chai, { expect } from 'chai';
import { spy, match } from 'sinon';
import sinonChai from 'sinon-chai';
chai.use(sinonChai);

describe('mixedRender()', () => {
it('should not render nested components when not white listed', () => {
let Test = spy( ({ foo, children }) => <div bar={foo}><b>test child</b>{ children }</div> );
Test.displayName = 'Test';

let rendered = mixedRender(
<section>
<Test foo={1}><span>asdf</span></Test>
</section>
);

expect(rendered).to.equal(`<section><Test foo="1"><span>asdf</span></Test></section>`);
expect(Test).not.to.have.been.called;
});

it('should always render root component', () => {
let Test = spy( ({ foo, children }) => <div bar={foo}><b>test child</b>{ children }</div> );
Test.displayName = 'Test';

let rendered = mixedRender(
<Test foo={1}>
<span>asdf</span>
</Test>
);

expect(rendered).to.equal(`<div bar="1"><b>test child</b><span>asdf</span></div>`);
expect(Test).to.have.been.calledOnce;
});

it('should render nested components when they are white listed', () => {
let Test = spy( ({ foo, children }) => <div bar={foo}><b>test child</b>{ children }</div> );
Test.displayName = 'Test';

let rendered = mixedRender(
<section>
<Test foo={1}><span>asdf</span></Test>
</section>
, ['Test']);

expect(rendered).to.equal(`<section><div bar="1"><b>test child</b><span>asdf</span></div></section>`);
expect(Test).to.have.been.called;
});

it('should not render nested components inside a whitelisted component', () => {
let Test = spy( ({ foo, children }) => <div bar={foo}><b>test child</b>{ children }</div> );
let Ignored = spy( ({ title, children }) => <h2>This {title} should not be rendered</h2> );
Test.displayName = 'Test';
Ignored.displayName = 'Ignored';

let rendered = mixedRender(
<section>
<Test foo={1}><Ignored title={'FooBarTitle'}/></Test>
</section>
, ['Test']);

expect(rendered).to.equal(`<section><div bar="1"><b>test child</b><Ignored title="FooBarTitle"></Ignored></div></section>`);
expect(Test).to.have.been.called;
expect(Ignored).to.not.have.been.called;
});

it('should render deeply nested components when all are white listed', () => {
let Test = spy( ({ foo, children }) => <div bar={foo}><b>test child</b>{ children }</div> );
let Ignored = spy( ({ title, children }) => <h2>This {title} should be rendered</h2> );
Test.displayName = 'Test';
Ignored.displayName = 'Ignored';

let rendered = mixedRender(
<section>
<Test foo={1}><Ignored title={'FooBarTitle'}/></Test>
</section>
, ['Test', 'Ignored']);

expect(rendered).to.equal(`<section><div bar="1"><b>test child</b><h2>This FooBarTitle should be rendered</h2></div></section>`);
expect(Test).to.have.been.called;
expect(Ignored).to.have.been.called;
});
});