UNPKG

ndla-ui

Version:

UI component library for NDLA.

221 lines (198 loc) 5.78 kB
/* * Copyright (c) 2016-present, NDLA. * * This source code is licensed under the GPLv3 license found in the * LICENSE file in the root directory of this source tree. * */ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import BEMHelper from 'react-bem-helper'; import { Search as SearchIcon } from 'ndla-icons/common'; import { injectT } from 'ndla-i18n'; import SafeLink from '../common/SafeLink'; import ActiveFilters from './ActiveFilters'; import ContentTypeResult from './ContentTypeResult'; import { ContentTypeResultShape } from '../shapes'; const classes = new BEMHelper('c-search-field'); const messagesShape = PropTypes.shape({ // required if search result searchResultHeading: PropTypes.string, contentTypeResultShowMoreLabel: PropTypes.string, contentTypeResultShowLessLabel: PropTypes.string, contentTypeResultNoHit: PropTypes.string, }); const SearchResult = ({ result, allResultUrl, resourceToLinkProps, onNavigate, t, }) => ( <section {...classes('search-result')}> <h1 {...classes('search-result-heading')}> {t('searchPage.searchField.searchResultHeading')} </h1> <div {...classes('search-result-content')}> {result.map(contentTypeResult => ( <ContentTypeResult onNavigate={onNavigate} contentTypeResult={contentTypeResult} resourceToLinkProps={resourceToLinkProps} defaultCount={window.innerWidth > 980 ? 7 : 3} key={contentTypeResult.title} messages={{ allResultLabel: t( 'searchPage.searchField.contentTypeResultShowMoreLabel', ), showLessResultLabel: t( 'searchPage.searchField.contentTypeResultShowLessLabel', ), noHit: t('searchPage.searchField.contentTypeResultNoHit'), }} /> ))} </div> <div {...classes('go-to-search')}> <SafeLink to={allResultUrl}> {t('searchPage.searchField.allResultButtonText')} </SafeLink> </div> </section> ); SearchResult.propTypes = { result: PropTypes.arrayOf(ContentTypeResultShape), resourceToLinkProps: PropTypes.func.isRequired, allResultUrl: PropTypes.string.isRequired, onNavigate: PropTypes.func, t: PropTypes.func.isRequired, }; class SearchField extends Component { constructor(props) { super(props); this.state = { inputHasFocus: false, }; this.inputRef = null; this.handleOnFilterRemove = this.handleOnFilterRemove.bind(this); this.onInputBlur = this.onInputBlur.bind(this); this.onInputFocus = this.onInputFocus.bind(this); } onInputBlur() { this.setState({ inputHasFocus: false, }); } onInputFocus() { this.setState({ inputHasFocus: true, }); } handleOnFilterRemove(value, filterName) { this.props.onFilterRemove(value, filterName); this.inputRef.focus(); } render() { const { placeholder, value, onChange, filters, searchResult, messages, allResultUrl, onSearch, resourceToLinkProps, small, autofocus, onNavigate, t, } = this.props; const modifiers = []; const hasSearchResult = searchResult && searchResult.length > 0; let searchResultView = null; if (hasSearchResult) { modifiers.push('has-search-result'); searchResultView = ( <SearchResult result={searchResult} searchString={value} allResultUrl={allResultUrl} resourceToLinkProps={resourceToLinkProps} autofocus={autofocus} onNavigate={onNavigate} t={t} /> ); } if (filters && filters.length > 0) { modifiers.push('has-filter'); } if (this.state.inputHasFocus) { modifiers.push('input-has-focus'); } return ( <form action="/search/" {...classes('', modifiers)} onSubmit={onSearch}> <div {...classes('input-wrapper')}> <input ref={ref => { this.inputRef = ref; }} title={messages.searchFieldTitle} type="search" {...classes('input', { small })} aria-autocomplete="list" autoComplete="off" id="search" name="search" placeholder={placeholder} aria-label={placeholder} value={value} onChange={onChange} onBlur={this.onInputBlur} onFocus={this.onInputFocus} /> {filters && filters.length > 0 && ( <div {...classes('filters')}> <ActiveFilters filters={filters} onFilterRemove={this.handleOnFilterRemove} /> </div> )} <button tabIndex="-1" {...classes('button')} type="submit" value="Search"> <SearchIcon /> </button> </div> {searchResultView} </form> ); } } SearchField.propTypes = { value: PropTypes.string.isRequired, placeholder: PropTypes.string.isRequired, onChange: PropTypes.func.isRequired, onSearch: PropTypes.func.isRequired, filters: PropTypes.arrayOf( PropTypes.shape({ value: PropTypes.string.isRequired, title: PropTypes.string.isRequired, }), ), messages: messagesShape, searchResult: PropTypes.arrayOf(ContentTypeResultShape), allResultUrl: PropTypes.string, resourceToLinkProps: PropTypes.func, onFilterRemove: PropTypes.func, small: PropTypes.bool, autofocus: PropTypes.bool, onNavigate: PropTypes.func, t: PropTypes.func.isRequired, }; export default injectT(SearchField);