Skip to content

Commit 2d7a934

Browse files
authored
Fix bug #349 typings for Placeholder prop functions render, renderEach, renderEmpty (#350)
1 parent 110a3dc commit 2d7a934

File tree

4 files changed

+112
-12
lines changed

4 files changed

+112
-12
lines changed

packages/sitecore-jss-react/src/components/Placeholder.test.tsx

+85-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ const componentFactory: ComponentFactory = (componentName: string) => {
1515
const components = new Map<string, any>();
1616

1717
// pass otherProps to page-content to test property cascading through the Placeholder
18-
const Home: React.SFC<any> = ({ rendering, ...otherProps }) => (
18+
const Home: React.SFC<any> = ({ rendering, render, renderEach, renderEmpty, ...otherProps }) => (
1919
<div className="home-mock">
2020
<Placeholder name="page-header" rendering={rendering} />
2121
<Placeholder name="page-content" rendering={rendering} {...otherProps} />
@@ -85,6 +85,90 @@ describe('<Placeholder />', () => {
8585
expect(renderedComponent.find('.download-callout-mock').length).to.equal(1);
8686
});
8787

88+
it('should render components based on the rendereach function', () => {
89+
const component: any = dataSet.data.sitecore.route;
90+
const phKey = 'main';
91+
92+
const renderedComponent = mount(
93+
<SitecoreContext componentFactory={componentFactory}>
94+
<Placeholder
95+
name={phKey}
96+
rendering={component}
97+
renderEach={(comp) => <div className="wrapper">{comp}</div>}
98+
/>
99+
</SitecoreContext>
100+
);
101+
102+
expect(renderedComponent.find('.wrapper').length).to.equal(1);
103+
});
104+
105+
it('should render components based on the render function', () => {
106+
const component: any = dataSet.data.sitecore.route;
107+
const phKey = 'main';
108+
109+
const renderedComponent = mount(
110+
<SitecoreContext componentFactory={componentFactory}>
111+
<Placeholder
112+
name={phKey}
113+
rendering={component}
114+
render={(comp) => <div className="wrapper">{comp}</div>}
115+
/>
116+
</SitecoreContext>
117+
);
118+
119+
expect(renderedComponent.find('.wrapper').length).to.equal(1);
120+
});
121+
122+
it('when null passed to render function', () => {
123+
it('should render empty placeholder', () => {
124+
const component: any = dataSet.data.sitecore.route;
125+
const phKey = 'main';
126+
127+
const renderedComponent = mount(
128+
<SitecoreContext componentFactory={componentFactory}>
129+
<Placeholder
130+
name={phKey}
131+
rendering={component}
132+
render={() => null}
133+
/>
134+
</SitecoreContext>
135+
);
136+
137+
const placeholder = renderedComponent.find(Placeholder)
138+
expect(placeholder.length).to.equal(1);
139+
expect(placeholder.children()).to.be.empty;
140+
});
141+
})
142+
143+
it('should render output based on the renderEmpty function in case of no renderings', () => {
144+
let component: any = dataSet.data.sitecore.route;
145+
const renderings = component.placeholders.main.filter(({ componentName }: any) => !componentName);
146+
const myComponent = {
147+
...component,
148+
placeholders: {
149+
...component.placeholders,
150+
main: [...renderings],
151+
},
152+
};
153+
154+
const phKey = 'main';
155+
156+
const renderedComponent = mount(
157+
<SitecoreContext componentFactory={componentFactory}>
158+
<Placeholder
159+
name={phKey}
160+
rendering={myComponent}
161+
renderEmpty={(comp) => <div className="wrapper">{comp}</div>}
162+
/>
163+
</SitecoreContext>
164+
);
165+
166+
expect(renderedComponent.find('.wrapper').length).to.equal(1);
167+
expect(renderedComponent.find('.download-callout-mock').length).to.equal(0);
168+
expect(renderedComponent.find('.home-mock').length).to.equal(0);
169+
expect(renderedComponent.find('.jumbotron-mock').length).to.equal(0);
170+
});
171+
88172
it('should pass properties to nested components', () => {
89173
const component = dataSet.data.sitecore.route as any;
90174
const phKey = 'main';

packages/sitecore-jss-react/src/components/Placeholder.tsx

+22-6
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,38 @@ import { withComponentFactory } from '../enhancers/withComponentFactory';
44
import { ComponentRendering, HtmlElementRendering } from '@sitecore-jss/sitecore-jss';
55

66
export interface PlaceholderComponentProps extends PlaceholderProps {
7+
/**
8+
* Render props function that is called when the placeholder contains no content components.
9+
* Can be used to wrap the Sitecore EE empty placeholder markup in something that's visually correct
10+
*/
11+
renderEmpty?: (
12+
components: React.ReactNode[]
13+
) => React.ComponentClass<any> | React.SFC<any> | React.ReactNode;
714
/**
815
* Render props function that enables control over the rendering of the components in the placeholder.
916
* Useful for techniques like wrapping each child in a wrapper component.
1017
*/
11-
render?: (components: React.ReactNode[], data: (ComponentRendering | HtmlElementRendering)[], props: PlaceholderProps) => React.ComponentClass<any> | React.SFC<any>;
18+
render?: (
19+
components: React.ReactNode[],
20+
data: (ComponentRendering | HtmlElementRendering)[],
21+
props: PlaceholderProps
22+
) => React.ComponentClass<any> | React.SFC<any> | React.ReactNode;
1223

1324
/**
1425
* Render props function that is called for each non-system component added to the placeholder.
1526
* Mutually exclusive with `render`. System components added during Experience Editor are automatically rendered as-is.
1627
*/
17-
renderEach?: (components: React.ReactNode[], data: (ComponentRendering | HtmlElementRendering)[], props: PlaceholderProps) => React.ComponentClass<any> | React.SFC<any>;
28+
renderEach?: (
29+
component: React.ReactNode,
30+
index: number
31+
) => React.ComponentClass<any> | React.SFC<any> | React.ReactNode;
1832
}
1933

2034
function isRawRendering(rendering: HtmlElementRendering | ComponentRendering): rendering is HtmlElementRendering {
2135
return !(rendering as ComponentRendering).componentName && (rendering as HtmlElementRendering).name !== undefined;
2236
}
2337

24-
class PlaceholderComponent extends PlaceholderCommon {
38+
class PlaceholderComponent extends PlaceholderCommon<PlaceholderComponentProps> {
2539
static propTypes = PlaceholderCommon.propTypes;
2640

2741
constructor(props: PlaceholderComponentProps) {
@@ -48,19 +62,21 @@ class PlaceholderComponent extends PlaceholderCommon {
4862
const renderingData = childProps.rendering;
4963

5064
const placeholderData = PlaceholderCommon.getPlaceholderDataFromRenderingData(renderingData, this.props.name);
51-
const components = this.getComponentsForRenderingData(placeholderData);
65+
const components = this.getComponentsForRenderingData(placeholderData);
5266

5367
if (this.props.renderEmpty && placeholderData.every((rendering: ComponentRendering | HtmlElementRendering) => isRawRendering(rendering))) {
54-
return this.props.renderEmpty(components, placeholderData, childProps);
68+
return this.props.renderEmpty(components);
5569
} else if (this.props.render) {
5670
return this.props.render(components, placeholderData, childProps);
5771
} else if (this.props.renderEach) {
72+
const renderEach = this.props.renderEach;
73+
5874
return components.map((component, index) => {
5975
if (component && component.props && component.props.type === 'text/sitecore') {
6076
return component;
6177
}
6278

63-
return this.props.renderEach(component, index);
79+
return renderEach(component, index);
6480
});
6581
} else {
6682
return components;

packages/sitecore-jss-react/src/components/PlaceholderCommon.tsx

+4-4
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ export interface PlaceholderProps {
4949
[key: string]: any;
5050
}
5151

52-
export class PlaceholderCommon extends React.Component<PlaceholderProps> {
52+
export class PlaceholderCommon<T extends PlaceholderProps> extends React.Component<T> {
5353
static propTypes = {
5454
rendering: PropTypes.oneOfType([
5555
PropTypes.object as Requireable<RouteData>,
@@ -93,7 +93,7 @@ export class PlaceholderCommon extends React.Component<PlaceholderProps> {
9393
return result;
9494
}
9595

96-
constructor(props: PlaceholderProps) {
96+
constructor(props: T) {
9797
super(props);
9898
this.nodeRefs = [];
9999
this.state = {};
@@ -191,8 +191,8 @@ export class PlaceholderCommon extends React.Component<PlaceholderProps> {
191191
};
192192

193193
/* Since we can't set the "key" attribute via React, stash it
194-
* so we can set in the DOM after render.
195-
*/
194+
* so we can set in the DOM after render.
195+
*/
196196
if (attributes && attributes.chrometype === 'placeholder') {
197197
props.phkey = elem.attributes.key; // props that get rendered as dom attribute names need to be lowercase, otherwise React complains.
198198
props.ref = this.addRef; // only need ref for placeholder containers, trying to add it to other components (e.g. stateless components) may result in a warning.

packages/sitecore-jss-react/src/enhancers/withPlaceholder.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export type WithPlaceholderSpec = (string | PlaceholderToPropMapping) | (string
3434

3535
export function withPlaceholder(placeholders: WithPlaceholderSpec, options?: WithPlaceholderOptions) {
3636
return (WrappedComponent: React.ComponentClass<any> | React.SFC<any>) => {
37-
class WithPlaceholder extends PlaceholderCommon {
37+
class WithPlaceholder extends PlaceholderCommon<PlaceholderProps> {
3838
static propTypes = PlaceholderCommon.propTypes;
3939

4040
constructor(props: any) {

0 commit comments

Comments
 (0)