Skip to content
Snippets Groups Projects
FacetBar.js 14.4 KiB
Newer Older
import React from 'react'
import PropTypes from 'prop-types'
import intl from 'react-intl-universal'
import { withStyles } from '@material-ui/core/styles'
import HierarchicalFacet from './HierarchicalFacet'
import TextFacet from './TextFacet'
import SliderFacet from './SliderFacet'
import RangeFacet from './RangeFacet'
esikkala's avatar
esikkala committed
import DateFacet from './DateFacet'
import Paper from '@material-ui/core/Paper'
import FacetHeader from './FacetHeader'
import FacetInfo from './FacetInfo'
import DatasetSelector from './DatasetSelector'
import SearchField from './SearchField'
import LeafletMapDialog from './LeafletMapDialog'
esikkala's avatar
esikkala committed
import Accordion from '@material-ui/core/Accordion'
import AccordionSummary from '@material-ui/core/AccordionSummary'
import AccordionDetails from '@material-ui/core/AccordionDetails'
import ExpandMoreIcon from '@material-ui/icons/ExpandMore'
import Typography from '@material-ui/core/Typography'
import clsx from 'clsx'
esikkala's avatar
esikkala committed
const styles = theme => ({
  root: {
    width: '100%',
    height: '100%'
  },
esikkala's avatar
esikkala committed
  facetInfoContainer: {
    padding: theme.spacing(1),
esikkala's avatar
esikkala committed
    borderBottomLeftRadius: 0,
esikkala's avatar
esikkala committed
    borderBottomRightRadius: 0
esikkala's avatar
esikkala committed
  },
esikkala's avatar
esikkala committed
  accordionSummaryRoot: props => ({
esikkala's avatar
esikkala committed
    paddingLeft: theme.spacing(1),
esikkala's avatar
esikkala committed
    cursor: 'default !important',
    minHeight: 38,
    [theme.breakpoints.up(props.layoutConfig.reducedHeightBreakpoint)]: {
      minHeight: 48
    }
  }),
esikkala's avatar
esikkala committed
  accordionSummaryContent: {
esikkala's avatar
esikkala committed
    margin: 0
esikkala's avatar
esikkala committed
  },
esikkala's avatar
esikkala committed
  accordionDetails: {
esikkala's avatar
esikkala committed
    paddingTop: 0,
    paddingLeft: theme.spacing(1),
    flexDirection: 'column'
esikkala's avatar
esikkala committed
  two: {
    height: 60
  },
esikkala's avatar
esikkala committed
  three: {
esikkala's avatar
esikkala committed
  },
esikkala's avatar
esikkala committed
  five: {
esikkala's avatar
esikkala committed
  },
esikkala's avatar
esikkala committed
  six: {
esikkala's avatar
esikkala committed
  },
esikkala's avatar
esikkala committed
  ten: {
esikkala's avatar
esikkala committed

esikkala's avatar
esikkala committed
/**
 * A component for rendering a preconfigured set of facets and related information.
 */
esikkala's avatar
esikkala committed
class FacetBar extends React.Component {
  constructor (props) {
    super(props)
esikkala's avatar
esikkala committed
    this.state = {
      activeFacets: this.props.defaultActiveFacets
    }
esikkala's avatar
esikkala committed
  handleExpandButtonOnClick = facetID => event => {
    const activeFacets = this.state.activeFacets
esikkala's avatar
esikkala committed
    if (activeFacets.has(facetID)) {
      activeFacets.delete(facetID)
esikkala's avatar
esikkala committed
    } else {
      activeFacets.add(facetID)
esikkala's avatar
esikkala committed
    }
    this.setState({ activeFacets })
  renderFacet = (facetID, someFacetIsFetching) => {
    const { classes, facetClass } = this.props
    const { facetUpdateID, updatedFacet, updatedFilter, facets } = this.props.facetData
    const label = intl.get(`perspectives.${facetClass}.properties.${facetID}.label`)
    const description = intl.get(`perspectives.${facetClass}.properties.${facetID}.description`)
    const facet = { ...facets[facetID] }
    const facetConstrainSelf = this.props.facetDataConstrainSelf == null
      ? null
      : this.props.facetDataConstrainSelf.facets[facetID]
    let facetComponent = null
    const isActive = this.state.activeFacets.has(facetID)
    if (this.props.facetedSearchMode === 'clientFS' && facetID !== 'datasetSelector') {
      if (this.props.facetData.results == null) {
        // do not render facets when there are no results
        return null
      } else {
        // integrate the facet values which have been calculated with a Redux selector
        facet.values = this.props.clientFSFacetValues[facetID]
      }
    }
    switch (facet.filterType) {
      case 'uriFilter':
      case 'spatialFilter':
        facetComponent = (
          <HierarchicalFacet
            facetID={facetID}
            facet={facet}
            facetClass={this.props.facetClass}
            resultClass={this.props.resultClass}
            facetUpdateID={facetUpdateID}
            updatedFacet={updatedFacet}
            updatedFilter={updatedFilter}
            fetchFacet={this.props.fetchFacet}
            someFacetIsFetching={someFacetIsFetching}
            updateFacetOption={this.props.updateFacetOption}
          />
      case 'clientFSLiteral':
        // console.log(someFacetIsFetching)
        facetComponent = (
          <HierarchicalFacet
            facetID={facetID}
            facet={facet}
            facetClass={this.props.facetClass}
            resultClass={this.props.resultClass}
            facetUpdateID={facetUpdateID}
            clientFSUpdateFacet={this.props.clientFSUpdateFacet}
            someFacetIsFetching={someFacetIsFetching}
            facetedSearchMode='clientFS'
          />
        )
        break
esikkala's avatar
esikkala committed
      case 'textFilter':
        facetComponent = (
          <TextFacet
            facetID={facetID}
            facet={facet}
            facetClass={this.props.facetClass}
            resultClass={this.props.resultClass}
            facetUpdateID={facetUpdateID}
            fetchFacet={this.props.fetchFacet}
            someFacetIsFetching={someFacetIsFetching}
            updateFacetOption={this.props.updateFacetOption}
          />
      case 'timespanFilter':
          <SliderFacet
            facetID={facetID}
            facet={facet}
            facetFilter={facet.timespanFilter}
esikkala's avatar
esikkala committed
            facetLabel={label}
            facetClass={this.props.facetClass}
            fetchFacet={this.props.fetchFacet}
            someFacetIsFetching={someFacetIsFetching}
            updateFacetOption={this.props.updateFacetOption}
esikkala's avatar
esikkala committed
            showError={this.props.showError}
            dataType='ISOString'
            minLabel={intl.get('facetBar.minYear')}
            maxLabel={intl.get('facetBar.maxYear')}
esikkala's avatar
esikkala committed
      case 'dateFilter':
        facetComponent = (
          <DateFacet
            facetID={facetID}
            facet={facet}
            facetClass={this.props.facetClass}
            resultClass={this.props.resultClass}
            facetUpdateID={facetUpdateID}
            fetchFacet={this.props.fetchFacet}
            someFacetIsFetching={someFacetIsFetching}
            updateFacetOption={this.props.updateFacetOption}
          />
        )
        break
      case 'integerFilter':
        facetComponent = (
          <SliderFacet
            facetID={facetID}
            facet={facet}
            facetFilter={facet.integerFilter}
esikkala's avatar
esikkala committed
            facetLabel={label}
            facetClass={this.props.facetClass}
            fetchFacet={this.props.fetchFacet}
            someFacetIsFetching={someFacetIsFetching}
            updateFacetOption={this.props.updateFacetOption}
esikkala's avatar
esikkala committed
            showError={this.props.showError}
            dataType='integer'
            minLabel={intl.get('facetBar.min')}
            maxLabel={intl.get('facetBar.max')}
esikkala's avatar
esikkala committed
      case 'integerFilterRange':
        facetComponent = (
          <RangeFacet
            facetID={facetID}
            facet={facet}
            facetClass={this.props.facetClass}
            resultClass={this.props.resultClass}
            facetUpdateID={facetUpdateID}
            fetchFacet={this.props.fetchFacet}
            someFacetIsFetching={someFacetIsFetching}
            updateFacetOption={this.props.updateFacetOption}
            dataType='integer'
          />
      case 'datasetSelector':
        facetComponent = (
          <DatasetSelector
            datasets={this.props.facetData.datasets}
            clientFSToggleDataset={this.props.clientFSToggleDataset}
            perspectiveID={this.props.facetClass}
          />
        )
        break
      default:
        facetComponent = (
          <HierarchicalFacet
            facetID={facetID}
            facet={facet}
            facetClass={this.props.facetClass}
            resultClass={this.props.resultClass}
            facetUpdateID={facetUpdateID}
            updatedFacet={updatedFacet}
            updatedFilter={updatedFilter}
            fetchFacet={this.props.fetchFacet}
            updateFacetOption={this.props.updateFacetOption}
          />
esikkala's avatar
esikkala committed
      <Accordion
esikkala's avatar
esikkala committed
        key={facetID}
esikkala's avatar
esikkala committed
        expanded={isActive}
esikkala's avatar
esikkala committed
        // onClick={this.handleExpandButtonOnClick(facetID)}
esikkala's avatar
esikkala committed
        <AccordionSummary
esikkala's avatar
esikkala committed
          classes={{
esikkala's avatar
esikkala committed
            root: classes.accordionSummaryRoot,
            content: classes.accordionSummaryContent
esikkala's avatar
esikkala committed
          }}
esikkala's avatar
esikkala committed
          expandIcon={<ExpandMoreIcon />}
esikkala's avatar
esikkala committed
          IconButtonProps={{ onClick: this.handleExpandButtonOnClick(facetID) }}
esikkala's avatar
esikkala committed
          aria-controls={`${facetID}-content`}
          id={`${facetID}-header`}
esikkala's avatar
esikkala committed
        >
          <FacetHeader
            facetID={facetID}
            facetLabel={label}
esikkala's avatar
esikkala committed
            facet={facet}
            facetConstrainSelf={facetConstrainSelf}
esikkala's avatar
esikkala committed
            facetConstrainSelfUpdateID={this.props.facetDataConstrainSelf
              ? this.props.facetDataConstrainSelf.facetUpdateID
              : null}
esikkala's avatar
esikkala committed
            isActive={isActive}
esikkala's avatar
esikkala committed
            facetClass={this.props.facetClass}
            resultClass={this.props.resultClass}
            fetchFacet={this.props.fetchFacet}
            fetchFacetConstrainSelf={this.props.fetchFacetConstrainSelf}
            fetchResults={this.props.fetchResults}
            facetResults={this.props.facetResults}
esikkala's avatar
esikkala committed
            updateFacetOption={this.props.updateFacetOption}
            facetDescription={description}
esikkala's avatar
esikkala committed
            rootUrl={this.props.rootUrl}
esikkala's avatar
esikkala committed
            layoutConfig={this.props.layoutConfig}
esikkala's avatar
esikkala committed
        </AccordionSummary>
        <AccordionDetails
          className={clsx(classes[facet.containerClass], classes.accordionDetails)}
esikkala's avatar
esikkala committed
          {isActive && facetComponent}
esikkala's avatar
esikkala committed
        </AccordionDetails>
      </Accordion>
esikkala's avatar
esikkala committed
  getTypographyVariant = () => {
    const { screenSize } = this.props
    let variant = 'h6'
    if (screenSize === 'xs' || screenSize === 'sm' || screenSize === 'md') {
      variant = 'subtitle2'
    }
    return variant
  }

  renderFacets = ({ classes, facets, someFacetIsFetching }) => {
    const { screenSize } = this.props
    if (screenSize === 'xs' || screenSize === 'sm') {
      return (
        <Accordion>
          <AccordionSummary
            classes={{
              root: classes.accordionSummaryRoot,
              content: classes.accordionSummaryContent
            }}
            expandIcon={<ExpandMoreIcon />}
            aria-controls='panel1a-content'
            id='panel1a-header'
          >
esikkala's avatar
esikkala committed
            <Typography variant={this.getTypographyVariant()}>{intl.get('facetBar.filters')}</Typography>
          </AccordionSummary>
          <AccordionDetails
            className={classes.accordionDetails}
          />
          {facets && Object.keys(facets).map(facetID => {
            if (facetID === 'datasetSelector') { return null }
            return this.renderFacet(facetID, someFacetIsFetching)
          })}
        </Accordion>
      )
    } else {
      return (
        <>
          {facets && Object.keys(facets).map(facetID => {
            if (facetID === 'datasetSelector') { return null }
            return this.renderFacet(facetID, someFacetIsFetching)
    const { classes, facetClass, resultClass, resultCount, facetData, facetedSearchMode } = this.props
    const { facets } = facetData
    let someFacetIsFetching = false
    const hasClientFSResults = facetData.results !== null
    if (facetedSearchMode === 'serverFS') {
      Object.values(facets).forEach(facet => {
        if (facet.isFetching) {
          someFacetIsFetching = true
        }
      })
    }
esikkala's avatar
esikkala committed
    return (
      <div className={classes.root}>
        {facetedSearchMode === 'clientFS' && this.renderFacet('datasetSelector', false)}
        {facetedSearchMode === 'clientFS' &&
          <SearchField
            search={this.props.facetData}
            fetchResults={this.props.clientFSFetchResults}
            clearResults={this.props.clientFSClearResults}
            updateQuery={this.props.clientFSUpdateQuery}
            datasets={this.props.facetData.datasets}
            perspectiveID={facetClass}
          />}
        {facetedSearchMode === 'clientFS' &&
          <LeafletMapDialog
            clientFSState={this.props.clientFSState}
            clientFSFetchResults={this.props.clientFSFetchResults}
            clientFSClearResults={this.props.clientFSClearResults}
            updateMapBounds={this.props.updateMapBounds}
            showError={this.props.showError}
            perspectiveID={facetClass}
            layoutConfig={this.props.layoutConfig}
        {(facetedSearchMode === 'serverFS' || hasClientFSResults) &&
          <Paper className={classes.facetInfoContainer}>
            <FacetInfo
              facetedSearchMode={facetedSearchMode}
              facetUpdateID={facetData.facetUpdateID}
              facetData={facetData}
              facetClass={facetClass}
              resultClass={resultClass}
              resultCount={resultCount}
              fetchingResultCount={this.props.fetchingResultCount}
              updateFacetOption={this.props.updateFacetOption}
              fetchResultCount={this.props.fetchResultCount}
              someFacetIsFetching={someFacetIsFetching}
              fetchFacet={this.props.fetchFacet}
              perspectiveID={facetClass}
              clearAllFacets={this.props.clearAllFacets}
              screenSize={this.props.screenSize}
        {(facetedSearchMode === 'serverFS' || hasClientFSResults) &&
esikkala's avatar
esikkala committed
          this.renderFacets({ classes, facets, someFacetIsFetching })}
esikkala's avatar
esikkala committed
      </div>
esikkala's avatar
esikkala committed
  }
}

FacetBar.propTypes = {
  classes: PropTypes.object.isRequired,
  facetedSearchMode: PropTypes.string.isRequired,
  facetData: PropTypes.object.isRequired,
  facetDataConstrainSelf: PropTypes.object,
  facetResults: PropTypes.object,
  facetClass: PropTypes.string.isRequired,
esikkala's avatar
esikkala committed
  resultClass: PropTypes.string.isRequired,
esikkala's avatar
esikkala committed
  resultCount: PropTypes.number,
  fetchingResultCount: PropTypes.bool.isRequired,
  fetchFacet: PropTypes.func,
  fetchFacetConstrainSelf: PropTypes.func,
  fetchResults: PropTypes.func,
  clearAllFacets: PropTypes.func,
  fetchResultCount: PropTypes.func,
  updateFacetOption: PropTypes.func,
  updateMapBounds: PropTypes.func,
  clientFS: PropTypes.object,
  clientFSFacetValues: PropTypes.object,
  clientFSToggleDataset: PropTypes.func,
  clientFSFetchResults: PropTypes.func,
  clientFSClearResults: PropTypes.func,
  clientFSUpdateQuery: PropTypes.func,
  clientFSUpdateFacet: PropTypes.func,
  defaultActiveFacets: PropTypes.instanceOf(Set).isRequired,
  leafletMap: PropTypes.object,
esikkala's avatar
esikkala committed
  showError: PropTypes.func.isRequired,
  rootUrl: PropTypes.string.isRequired,
  screenSize: PropTypes.string.isRequired
esikkala's avatar
esikkala committed

esikkala's avatar
esikkala committed
export const FacetBarComponent = FacetBar

export default withStyles(styles)(FacetBar)