ndla-ui
Version:
UI component library for NDLA.
194 lines (179 loc) • 6.12 kB
JSX
import React, { Component, Fragment } from 'react';
import BEMHelper from 'react-bem-helper';
import PropTypes from 'prop-types';
import { Back } from 'ndla-icons/common';
import debounce from 'lodash/debounce';
import { getCurrentBreakpoint, breakpoints } from 'ndla-util';
import Modal, { ModalHeader, ModalBody, ModalCloseButton } from 'ndla-modal';
import Button from 'ndla-button';
import { injectT } from 'ndla-i18n';
import SearchField from './SearchField';
import ActiveFilters from './ActiveFilters';
const classes = BEMHelper('c-search-page');
const filterClasses = BEMHelper('c-filter');
class SearchPage extends Component {
constructor(props) {
super(props);
this.state = {
isNarrowScreen: false,
};
this.filterCloseButton = null;
this.checkScreenSize = this.checkScreenSize.bind(this);
this.checkScreenSizeDebounce = debounce(() => this.checkScreenSize(), 100);
}
componentDidMount() {
window.addEventListener('resize', this.checkScreenSizeDebounce);
this.checkScreenSize();
}
componentWillUnmount() {
window.removeEventListener('resize', this.checkScreenSizeDebounce);
}
checkScreenSize() {
const currentBreakpoint = getCurrentBreakpoint();
const isNarrowScreen =
currentBreakpoint === breakpoints.mobile ||
currentBreakpoint === breakpoints.tablet;
/* eslint react/no-did-mount-set-state: 0 */
if (isNarrowScreen !== this.state.isNarrowScreen) {
this.setState({
isNarrowScreen,
});
}
}
render() {
const {
searchString,
onSearchFieldChange,
onSearchFieldFilterRemove,
searchFieldFilters,
onSearch,
// only on narrow screen
activeFilters,
resourceToLinkProps,
filters,
children,
messages,
author,
hideResultText,
t,
} = this.props;
return (
<main {...classes()}>
<div {...classes('search-field-wrapper')}>
<SearchField
value={searchString}
onChange={onSearchFieldChange}
onSearch={onSearch}
placeholder={t('searchPage.searchFieldPlaceholder')}
filters={searchFieldFilters}
onFilterRemove={onSearchFieldFilterRemove}
resourceToLinkProps={resourceToLinkProps}
messages={{
searchFieldTitle: t('searchPage.search'),
}}
/>
</div>
{author}
<div {...classes('filter-result-wrapper')}>
<aside {...classes('filter-wrapper')}>
<h1 {...classes('filter-heading')}>
{t('searchPage.searchPageMessages.filterHeading')}
</h1>
<div {...classes('filters')}>
{!this.state.isNarrowScreen && filters}
</div>
</aside>
<div {...classes('result-wrapper')}>
<h2 aria-hidden="true" {...classes('result-label', 'large-screen')}>
{!hideResultText ? messages.resultHeading : '\u00A0'}
</h2>
<div {...classes('active-filters')}>
<ActiveFilters
filters={activeFilters}
onFilterRemove={(value, filterName) =>
onSearchFieldFilterRemove(value, filterName)
}
/>
</div>
<div {...classes('toggle-filter')}>
<Modal
animation="subtle"
animationDuration={150}
size="fullscreen"
backgroundColor="grey"
activateButton={
<Button outline>
{t('searchPage.searchPageMessages.filterHeading')}
</Button>
}>
{onClose => (
<Fragment>
<ModalHeader modifier="white left-align">
<ModalCloseButton
title={
<Fragment>
<Back /> {messages.narrowScreenFilterHeading}
</Fragment>
}
onClick={onClose}>
Close
</ModalCloseButton>
</ModalHeader>
<ModalBody modifier="slide-in-left no-side-padding-mobile">
{filters}
<div {...filterClasses('usefilter-wrapper')}>
<Button outline onClick={onClose}>
{t('searchPage.searchFilterMessages.useFilter')}
</Button>
</div>
</ModalBody>
</Fragment>
)}
</Modal>
</div>
<h2 aria-hidden="true" {...classes('result-label', 'small-screen')}>
{!hideResultText ? messages.resultHeading : '\u00A0'}
</h2>
{children}
</div>
</div>
</main>
);
}
}
SearchPage.propTypes = {
// should be <Fragment />
filters: PropTypes.node.isRequired,
children: PropTypes.node.isRequired,
searchString: PropTypes.string.isRequired,
onSearchFieldChange: PropTypes.func.isRequired,
onSearch: PropTypes.func.isRequired,
onSearchFieldFilterRemove: PropTypes.func.isRequired,
resourceToLinkProps: PropTypes.func.isRequired,
searchFieldFilters: PropTypes.arrayOf(
PropTypes.shape({
value: PropTypes.string.isRequired,
title: PropTypes.string.isRequired,
filterName: PropTypes.string.isRequired,
}),
),
activeFilters: PropTypes.arrayOf(
PropTypes.shape({
value: PropTypes.string.isRequired,
title: PropTypes.string.isRequired,
filterName: PropTypes.string.isRequired,
}),
),
messages: PropTypes.shape({
narrowScreenFilterHeading: PropTypes.string.isRequired,
resultHeading: PropTypes.string,
}).isRequired,
author: PropTypes.node,
hideResultText: PropTypes.bool,
filterScreenChange: PropTypes.func,
t: PropTypes.func.isRequired,
};
SearchPage.defaultProps = {
author: null,
};
export default injectT(SearchPage);