Skip to content
Snippets Groups Projects
ResultTable.js 9.03 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 clsx from 'clsx';
import Table from '@material-ui/core/Table';
import TableBody from '@material-ui/core/TableBody';
import ResultTableCell from './ResultTableCell';
import TableRow from '@material-ui/core/TableRow';
import TableCell from '@material-ui/core/TableCell';
import IconButton from '@material-ui/core/IconButton';
import CircularProgress from '@material-ui/core/CircularProgress';
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
import purple from '@material-ui/core/colors/purple';
esikkala's avatar
esikkala committed
import querystring from 'querystring';
import ResultTableHead from './ResultTableHead';
import TablePagination from '@material-ui/core/TablePagination';
import ResultTablePaginationActions from './ResultTablePaginationActions';
esikkala's avatar
esikkala committed
import history from '../../History';
import has from 'lodash';
const styles = theme => ({
esikkala's avatar
esikkala committed
  tableContainer: {
    overflow: 'auto',
esikkala's avatar
esikkala committed
    width: '100%',
    height: 'auto',
    [theme.breakpoints.up('md')]: {
      height: 'calc(100% - 130px)'
    },
    backgroundColor: theme.palette.background.paper,
esikkala's avatar
esikkala committed
    borderTop: '1px solid rgba(224, 224, 224, 1);',
  paginationRoot: {
    display: 'flex',
esikkala's avatar
esikkala committed
    backgroundColor: '#fff',
    borderTop: '1px solid rgba(224, 224, 224, 1);',
  },
  paginationCaption: {
esikkala's avatar
esikkala committed
    minWidth: 94
  },
  paginationToolbar: {
    [theme.breakpoints.down('xs')]: {
      display: 'flex',
      flexWrap: 'wrap',
      height: 100
    },
esikkala's avatar
esikkala committed
    width: '100%',
esikkala's avatar
esikkala committed
    height: 'calc(100% - 72px)',
    display: 'flex',
    alignItems: 'center',
    justifyContent: 'center',
  },
  expandCell: {
    paddingRight: 0,
    paddingTop: 0,
    paddingBottom: 0
  },
  expand: {
    transform: 'rotate(0deg)',
    marginLeft: 'auto',
    transition: theme.transitions.create('transform', {
      duration: theme.transitions.duration.shortest,
    }),
  },
  expandOpen: {
    transform: 'rotate(180deg)',
  },
});

class ResultTable extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      expandedRows: new Set(),
    };
  }
  componentDidMount = () => {

    // first check if page was given as url parameter
    if (this.props.routeProps.location.search === '') {
      page = this.props.data.page === -1 ? 0 : this.props.data.page;
esikkala's avatar
esikkala committed
      const qs = this.props.routeProps.location.search.replace('?', '');
      page = parseInt(querystring.parse(qs).page);

    // then update app state and url accordingly
esikkala's avatar
esikkala committed
    this.props.updatePage(this.props.resultClass, page);
    history.push({
esikkala's avatar
esikkala committed
      pathname: `/${this.props.resultClass}/faceted-search/table`,
      search: `?page=${page}`,
    });
    // check if facet updates have been made before
    if (this.props.facetUpdateID > 0) {
      this.fetchResults();
    }

  componentDidUpdate = prevProps => {
    // always fetch new results when page has updated
    if (prevProps.data.page != this.props.data.page) {
      this.fetchResults();
esikkala's avatar
esikkala committed
      history.push({
esikkala's avatar
esikkala committed
        pathname: `/${this.props.resultClass}/faceted-search/table`,
esikkala's avatar
esikkala committed
        search: `?page=${this.props.data.page}`,
    // when sort property or direction changes, return to first page
    if (this.needNewResults(prevProps)) {
      if (this.props.data.page == 0) {
        this.fetchResults();
      } else {
        this.props.updatePage(this.props.resultClass, 0);
    // handle browser's back button
    window.onpopstate  = () => {
      const qs = this.props.routeProps.location.search.replace('?', '');
      const newPage = parseInt(querystring.parse(qs).page);
      if (newPage != this.props.data.page) {
        this.props.updatePage(this.props.resultClass, newPage);
      }
    };
  }

  fetchResults = () => {
    this.props.fetchPaginatedResults(this.props.resultClass, this.props.facetClass, this.props.data.sortBy, this.props.variant);
  }

  needNewResults = prevProps => {
    return (
      prevProps.data.sortBy != this.props.data.sortBy
      || prevProps.data.sortDirection != this.props.data.sortDirection
      || prevProps.facetUpdateID != this.props.facetUpdateID
      || prevProps.data.pagesize != this.props.data.pagesize
  }

  handleChangePage = (event, page) => {
    if (event != null && !this.props.data.fetching) {
esikkala's avatar
esikkala committed
      this.props.updatePage(this.props.resultClass, page);
esikkala's avatar
esikkala committed

  handleOnChangeRowsPerPage = event => {
    const rowsPerPage = event.target.value;
    if (rowsPerPage != this.props.data.pagesize) {
      this.props.updateRowsPerPage(this.props.resultClass, rowsPerPage);
    }
  }

  handleSortBy = sortBy => event => {
    if (event != null) {
esikkala's avatar
esikkala committed
      this.props.sortResults(this.props.resultClass, sortBy);
  handleExpandRow = rowId => () => {
    let expandedRows = this.state.expandedRows;
    if (expandedRows.has(rowId)) {
      expandedRows.delete(rowId);
    } else {
      expandedRows.add(rowId);
    }
    this.setState({ expandedRows});
    const { classes } = this.props;
    const expanded = this.state.expandedRows.has(row.id);
    let hasExpandableContent = false;
    const dataCells = this.props.data.properties.map(column => {
      if (column.onlyOnInstancePage) { return null; }
esikkala's avatar
esikkala committed
      const columnData = row[column.id] == null ? '-' : row[column.id];
      const isArray = Array.isArray(columnData);
      if (isArray) {
        hasExpandableContent = true;
      }
      if (!isArray
esikkala's avatar
esikkala committed
        && columnData !== '-'
        && column.valueType === 'string'
        && column.collapsedMaxWords
esikkala's avatar
esikkala committed
        && columnData.split(' ').length > column.collapsedMaxWords
      ) {
        hasExpandableContent = true;
      }
      return (
        <ResultTableCell
          key={column.id}
          columnId={column.id}
esikkala's avatar
esikkala committed
          data={columnData}
          valueType={column.valueType}
          makeLink={column.makeLink}
          externalLink={column.externalLink}
          sortValues={column.sortValues}
          numberedList={column.numberedList}
          minWidth={column.minWidth}
          container='cell'
          expanded={expanded}
          linkAsButton={has(column, 'linkAsButton')
            ? column.linkAsButton
            : null
          }
          collapsedMaxWords={has(column, 'collapsedMaxWords')
            ? column.collapsedMaxWords
            : null
          }
        <TableCell className={classes.expandCell}>
          {hasExpandableContent &&
            <IconButton
              className={clsx(classes.expand, {
                [classes.expandOpen]: expanded,
              })}
              onClick={this.handleExpandRow(row.id)}
              aria-expanded={expanded}
              aria-label="Show more"
            >
              <ExpandMoreIcon />
            </IconButton>
          }
        </TableCell>
    const { classes } = this.props;
    const { resultCount, paginatedResults, page, pagesize, sortBy, sortDirection, fetching } = this.props.data;
      <React.Fragment>
        <TablePagination
          component='div'
          classes={{
            root: classes.paginationRoot,
            caption: classes.paginationCaption,
esikkala's avatar
esikkala committed
            toolbar: classes.paginationToolbar
          }}
          count={resultCount}
          rowsPerPage={pagesize}
          labelRowsPerPage={intl.get('table.rowsPerPage')}
          rowsPerPageOptions={[5, 10, 15, 25, 30, 50, 100]}
          page={page == -1 || resultCount == 0 ? 0 : page}
          onChangePage={this.handleChangePage}
          onChangeRowsPerPage={this.handleOnChangeRowsPerPage}
          ActionsComponent={ResultTablePaginationActions}
        />
        <div className={classes.tableContainer}>
          {fetching ?
            <div className={classes.progressContainer}>
              <CircularProgress style={{ color: purple[500] }} thickness={5} />
            </div>
            :
esikkala's avatar
esikkala committed
            <Table size='small'>
              <ResultTableHead
                resultClass={this.props.resultClass}
                columns={this.props.data.properties}
                onSortBy={this.handleSortBy}
                sortBy={sortBy}
                sortDirection={sortDirection}
                routeProps={this.props.routeProps}
              />
              <TableBody>
                {paginatedResults.map(row => this.rowRenderer(row))}
              </TableBody>
            </Table>
          }
        </div>
      </React.Fragment>
  }
}

ResultTable.propTypes = {
  classes: PropTypes.object.isRequired,
  data: PropTypes.object.isRequired,
  resultClass: PropTypes.string.isRequired,
  facetClass: PropTypes.string.isRequired,
  variant: PropTypes.string,
  facetUpdateID: PropTypes.number.isRequired,
  fetchPaginatedResults: PropTypes.func.isRequired,
  sortResults: PropTypes.func.isRequired,
  updatePage: PropTypes.func.isRequired,
  updateRowsPerPage: PropTypes.func.isRequired,
  routeProps: PropTypes.object.isRequired
};

export default withStyles(styles)(ResultTable);