Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
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 dev/app/builtin/init-stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import initStatsStories from './stories/stats.stories';
import initToggleStories from './stories/toggleRefinement.stories';
import initConfigureStories from './stories/configure.stories';
import initPoweredByStories from './stories/powered-by.stories';
import initPanelStories from './stories/panel.stories';

export default () => {
initAnalyticsStories();
Expand Down Expand Up @@ -50,4 +51,5 @@ export default () => {
initToggleStories();
initConfigureStories();
initPoweredByStories();
initPanelStories();
};
27 changes: 27 additions & 0 deletions dev/app/builtin/stories/panel.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/* eslint-disable import/default */

import { storiesOf } from 'dev-novel';
import instantsearch from '../../../../index';
import { wrapWithHits } from '../../utils/wrap-with-hits.js';

const stories = storiesOf('Panel');

export default () => {
stories.add(
'with default',
wrapWithHits(container => {
window.search.addWidget(
instantsearch.widgets.panel({
templates: {
header: 'Header',
footer: 'Footer',
},
hidden: ({ canRefine }) => !canRefine,
})(instantsearch.widgets.refinementList)({
container,
attribute: 'brand',
})
);
})
);
};
58 changes: 58 additions & 0 deletions src/components/Panel/Panel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import React from 'react';
import PropTypes from 'prop-types';
import cx from 'classnames';
import Template from '../Template/Template';

const Panel = ({ cssClasses, hidden, templateProps, data, setBodyRef }) => (
<div
className={cx(cssClasses.root, {
[cssClasses.noRefinementRoot]: !data.canRefine,
})}
hidden={hidden}
>
{templateProps.templates.header && (
<Template
{...templateProps}
templateKey="header"
rootProps={{
className: cssClasses.header,
}}
data={data}
/>
)}

<div className={cssClasses.body} ref={setBodyRef} />

{templateProps.templates.footer && (
<Template
{...templateProps}
templateKey="footer"
rootProps={{
className: cssClasses.footer,
}}
data={data}
/>
)}
</div>
);

Panel.propTypes = {
// Prop to get the panel body reference to insert the widget
setBodyRef: PropTypes.func,
cssClasses: PropTypes.shape({
root: PropTypes.string.isRequired,
noRefinementRoot: PropTypes.string.isRequired,
body: PropTypes.string.isRequired,
header: PropTypes.string.isRequired,
footer: PropTypes.string.isRequired,
}).isRequired,
templateProps: PropTypes.shape({
templates: PropTypes.object.isRequired,
}).isRequired,
hidden: PropTypes.bool.isRequired,
data: PropTypes.shape({
canRefine: PropTypes.bool.isRequired,
}).isRequired,
};

export default Panel;
101 changes: 101 additions & 0 deletions src/components/Panel/__tests__/Panel-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import React from 'react';
import { mount } from 'enzyme';
import Panel from '../Panel';

describe('Panel', () => {
test('should render component with default props', () => {
const props = {
cssClasses: {
root: 'root',
noRefinementRoot: 'noRefinementRoot',
body: 'body',
header: 'header',
footer: 'footer',
},
hidden: false,
data: {
canRefine: true,
},
templateProps: {
templates: {
header: 'Header',
footer: 'Footer',
},
},
};

const wrapper = mount(<Panel {...props} />);

expect(wrapper.find('.root')).toHaveLength(1);
expect(wrapper.find('.noRefinementRoot')).toHaveLength(0);
expect(wrapper.find('.body')).toHaveLength(1);
expect(wrapper.find('.header')).toHaveLength(1);
expect(wrapper.find('.footer')).toHaveLength(1);
expect(wrapper.find('.header').text()).toBe('Header');
expect(wrapper.find('.footer').text()).toBe('Footer');
expect(wrapper).toMatchSnapshot();
});

test('should render component with `noRefinementRoot` CSS class', () => {
const props = {
cssClasses: {
root: 'root',
noRefinementRoot: 'noRefinementRoot',
body: 'body',
header: 'header',
footer: 'footer',
},
hidden: false,
data: {
canRefine: false,
},
templateProps: {
templates: {
header: 'Header',
footer: 'Footer',
},
},
};

const wrapper = mount(<Panel {...props} />);

expect(wrapper.find('.root')).toHaveLength(1);
expect(wrapper.find('.noRefinementRoot')).toHaveLength(1);
expect(wrapper.find('.body')).toHaveLength(1);
expect(wrapper.find('.header')).toHaveLength(1);
expect(wrapper.find('.footer')).toHaveLength(1);
expect(wrapper).toMatchSnapshot();
});

test('should render component with `hidden` prop', () => {
const props = {
cssClasses: {
root: 'root',
noRefinementRoot: 'noRefinementRoot',
body: 'body',
header: 'header',
footer: 'footer',
},
hidden: true,
data: {
canRefine: true,
},
templateProps: {
templates: {
header: 'Header',
footer: 'Footer',
},
},
};

const wrapper = mount(<Panel {...props} />);

expect(wrapper.find('.root')).toHaveLength(1);
expect(wrapper.find('.noRefinementRoot')).toHaveLength(0);
expect(wrapper.find('.body')).toHaveLength(1);
expect(wrapper.find('.header')).toHaveLength(1);
expect(wrapper.find('.footer')).toHaveLength(1);
expect(wrapper.props().hidden).toBe(true);
expect(wrapper).toMatchSnapshot();
});
});
82 changes: 82 additions & 0 deletions src/components/Panel/__tests__/__snapshots__/Panel-test.js.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Panel should render component with \`hidden\` prop 1`] = `
<div
className="root"
hidden={true}
>
<div
className="header"
dangerouslySetInnerHTML={
Object {
"__html": "Header",
}
}
/>
<div
className="body"
/>
<div
className="footer"
dangerouslySetInnerHTML={
Object {
"__html": "Footer",
}
}
/>
</div>
`;

exports[`Panel should render component with \`noRefinementRoot\` CSS class 1`] = `
<div
className="root noRefinementRoot"
hidden={false}
>
<div
className="header"
dangerouslySetInnerHTML={
Object {
"__html": "Header",
}
}
/>
<div
className="body"
/>
<div
className="footer"
dangerouslySetInnerHTML={
Object {
"__html": "Footer",
}
}
/>
</div>
`;

exports[`Panel should render component with default props 1`] = `
<div
className="root"
hidden={false}
>
<div
className="header"
dangerouslySetInnerHTML={
Object {
"__html": "Header",
}
}
/>
<div
className="body"
/>
<div
className="footer"
dangerouslySetInnerHTML={
Object {
"__html": "Footer",
}
}
/>
</div>
`;
1 change: 1 addition & 0 deletions src/widgets/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,4 @@ export { default as analytics } from './analytics/analytics.js';
export { default as breadcrumb } from './breadcrumb/breadcrumb.js';
export { default as menuSelect } from './menu-select/menu-select.js';
export { default as poweredBy } from './powered-by/powered-by.js';
export { default as panel } from './panel/panel.js';
39 changes: 39 additions & 0 deletions src/widgets/panel/__tests__/panel-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import panel from '../panel';

describe('panel call', () => {
test('without arguments does not throw', () => {
expect(() => panel()).not.toThrow();
});

test('with templates does not throw', () => {
expect(() =>
panel({
templates: { header: 'header' },
})
).not.toThrow();
});

test('with a `hidden` function does not throw', () => {
expect(() =>
panel({
hidden: () => true,
})
).not.toThrow();
});

test('with `hidden` as a boolean throws usage', () => {
expect(() =>
panel({
hidden: true,
})
).toThrow(/^\[InstantSearch.js\]/);
});

test('with a widget without `container` throws', () => {
const fakeWidget = () => {};

expect(() => panel()(fakeWidget)({})).toThrowErrorMatchingInlineSnapshot(
`"[InstantSearch.js] The \`container\` option is required in the widget \\"fakeWidget\\"."`
);
});
});
Loading