From 50a6695ef62d60bc213b62192d2bb2c960944923 Mon Sep 17 00:00:00 2001
From: esikkala <esko.ikkala@aalto.fi>
Date: Mon, 6 May 2019 18:28:05 +0300
Subject: [PATCH] Simplify facet updates

---
 .../components/facet_bar/ActiveFilters.js     |  7 +-
 src/client/components/facet_bar/ChipsArray.js | 12 ++-
 src/client/components/facet_bar/FacetBar.js   | 11 ++-
 .../{Tree.js => HierarchicalFacet.js}         | 85 +++++++++++--------
 src/client/reducers/helpers.js                | 10 ++-
 src/client/reducers/manuscriptsFacets.js      | 36 ++++----
 6 files changed, 94 insertions(+), 67 deletions(-)
 rename src/client/components/facet_bar/{Tree.js => HierarchicalFacet.js} (85%)

diff --git a/src/client/components/facet_bar/ActiveFilters.js b/src/client/components/facet_bar/ActiveFilters.js
index 3ba78f44..d8bec99b 100644
--- a/src/client/components/facet_bar/ActiveFilters.js
+++ b/src/client/components/facet_bar/ActiveFilters.js
@@ -8,15 +8,12 @@ const ActiveFilters = props => {
     <React.Fragment>
       {Object.keys(uriFilters).map(facetID => {
         const facetValues = [];
-        Object.entries(uriFilters[facetID]).forEach(([ key, value]) => {
+        Object.values(uriFilters[facetID]).forEach(value => {
           facetValues.push({
             facetID: facetID,
             facetLabel: facets[facetID].label,
             filterType: 'uriFilter',
-            value: {
-              id: key,
-              label: value.length > 18 ? `${value.substring(0, 18)}...` : value,
-            }
+            value: value // a react sortable tree object
           });
         });
         return (
diff --git a/src/client/components/facet_bar/ChipsArray.js b/src/client/components/facet_bar/ChipsArray.js
index 1512deb9..332b7876 100644
--- a/src/client/components/facet_bar/ChipsArray.js
+++ b/src/client/components/facet_bar/ChipsArray.js
@@ -21,10 +21,16 @@ class ChipsArray extends React.Component {
       facetClass: this.props.facetClass,
       facetID: data.facetID,
       option: data.filterType,
-      value: data.value
+      value: data.value  // a react sortable tree object
     });
   };
 
+  generateLabel = (facetLabel, valueLabel) => {
+    return  valueLabel.length > 18
+      ? `${facetLabel}: ${valueLabel.substring(0, 18)}...`
+      : `${facetLabel}: ${valueLabel}`;
+  }
+
   render() {
     const { classes, data } = this.props;
     return (
@@ -33,9 +39,9 @@ class ChipsArray extends React.Component {
           let icon = null;
           return (
             <Chip
-              key={item.value.id}
+              key={item.value.node.id}
               icon={icon}
-              label={`${item.facetLabel.toLowerCase()}: ${item.value.label}`}
+              label={this.generateLabel(item.facetLabel, item.value.node.prefLabel)}
               onDelete={this.handleDelete(item)}
               className={classes.chip}
             />
diff --git a/src/client/components/facet_bar/FacetBar.js b/src/client/components/facet_bar/FacetBar.js
index def6e3ae..cdbf8f67 100644
--- a/src/client/components/facet_bar/FacetBar.js
+++ b/src/client/components/facet_bar/FacetBar.js
@@ -2,7 +2,7 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import { has } from 'lodash';
 import { withStyles } from '@material-ui/core/styles';
-import Tree from './Tree';
+import HierarchicalFacet from './HierarchicalFacet';
 import DateSlider from './slider/DateSlider';
 import Paper from '@material-ui/core/Paper';
 import FacetHeader from './FacetHeader';
@@ -60,12 +60,13 @@ class FacetBar extends React.Component {
 
   render() {
     const { classes, facetClass } = this.props;
-    const { facetUpdateID, updatedFacet, facets } = this.props.facetData;
+    const { facetUpdateID, updatedFacet, updatedFilter, facets } = this.props.facetData;
     let uriFilters = {};
     let spatialFilters = {};
     let activeUriFilters = false;
     let activeSpatialFilters = false;
     for (const [key, value] of Object.entries(facets)) {
+      //
       if (value.uriFilter !== null) {
         activeUriFilters = true;
         uriFilters[key] = value.uriFilter;
@@ -113,14 +114,16 @@ class FacetBar extends React.Component {
                 updateFacetOption={this.props.updateFacetOption}
               />
               <div className={classes[facets[id].containerClass]}>
-                {facets[id].filterType === 'uriFilter' || facets[id].filterType === 'spatialFilter' ?
-                  <Tree
+                {facets[id].filterType === 'uriFilter'
+                  || facets[id].filterType === 'spatialFilter' ?
+                  <HierarchicalFacet
                     facetID={id}
                     facet={facets[id]}
                     facetClass={this.props.facetClass}
                     resultClass={this.props.resultClass}
                     facetUpdateID={facetUpdateID}
                     updatedFacet={updatedFacet}
+                    updatedFilter={updatedFilter}
                     fetchFacet={this.props.fetchFacet}
                     updateFacetOption={this.props.updateFacetOption}
                   /> :
diff --git a/src/client/components/facet_bar/Tree.js b/src/client/components/facet_bar/HierarchicalFacet.js
similarity index 85%
rename from src/client/components/facet_bar/Tree.js
rename to src/client/components/facet_bar/HierarchicalFacet.js
index b72fdc45..b440cb62 100644
--- a/src/client/components/facet_bar/Tree.js
+++ b/src/client/components/facet_bar/HierarchicalFacet.js
@@ -12,7 +12,6 @@ import IconButton from '@material-ui/core/IconButton';
 import NavigateNextIcon from '@material-ui/icons/NavigateNext';
 import NavigateBeforeIcon from '@material-ui/icons/NavigateBefore';
 import Typography from '@material-ui/core/Typography';
-import ChipsArray from './ChipsArray';
 
 const styles = () => ({
   facetSearchContainer: {
@@ -64,7 +63,7 @@ const styles = () => ({
 This component is based on the React Sortable Tree example at:
 https://frontend-collective.github.io/react-sortable-tree/storybook/?selectedKind=Basics&selectedStory=Search&full=0&addons=0&stories=1&panelRight=0
 */
-class Tree extends Component {
+class HierarchicalFacet extends Component {
   constructor(props) {
     super(props);
     this.state = {
@@ -85,23 +84,32 @@ class Tree extends Component {
   }
 
   componentDidUpdate = prevProps => {
-    // if (this.props.facetID === 'productionPlace') {
-    //   console.log(this.props.facet.values)
-    // }
 
-    if (prevProps.facet.values != this.props.facet.values) {
-      this.setState({
-        treeData: this.props.facet.values
-      });
-    }
-    if (this.props.updatedFacet !== null
-      && this.props.updatedFacet !== this.props.facetID
-      && prevProps.facetUpdateID !== this.props.facetUpdateID) {
-      this.props.fetchFacet({
-        facetClass: this.props.facetClass,
-        facetID: this.props.facetID,
-      });
+    if (prevProps.facetUpdateID !== this.props.facetUpdateID) {
+      // update component state if the user modified this facet
+      if (this.props.updatedFacet === this.props.facetID ) {
+        const treeObj = this.props.updatedFilter;
+        const newTreeData = changeNodeAtPath({
+          treeData: this.state.treeData,
+          getNodeKey: ({ treeIndex }) =>  treeIndex,
+          path: treeObj.path,
+          newNode: {
+            ...treeObj.node,
+            selected: treeObj.added ? 'true' : 'false'
+          },
+        });
+        this.setState({ treeData: newTreeData });
+      }
+      // else fetch new values, because some other facet was updated
+      else {
+        this.props.fetchFacet({
+          facetClass: this.props.facetClass,
+          facetID: this.props.facetID,
+        });
+      }
     }
+
+    // fetch new values if the user changes the filter type or sort order
     if (prevProps.facet.filterType !== this.props.facet.filterType
       && this.props.facet.filterType === 'uriFilter') {
       this.props.fetchFacet({
@@ -115,27 +123,21 @@ class Tree extends Component {
         facetID: this.props.facetID,
       });
     }
+
+    // when values have been fetched, update component's state
+    if (prevProps.facet.values != this.props.facet.values) {
+      this.setState({
+        treeData: this.props.facet.values
+      });
+    }
   }
 
-  handleCheckboxChange = treeObj => event => {
-    const newTreeData = changeNodeAtPath({
-      treeData: this.state.treeData,
-      getNodeKey: ({ treeIndex }) =>  treeIndex,
-      path: treeObj.path,
-      newNode: {
-        ...treeObj.node,
-        selected: event.target.checked ? 'true' : 'false'
-      },
-    });
-    this.setState({ treeData: newTreeData });
+  handleCheckboxChange = treeObj => () => {
     this.props.updateFacetOption({
       facetClass: this.props.facetClass,
       facetID: this.props.facetID,
       option: this.props.facet.filterType,
-      value: {
-        id: treeObj.node.id,
-        label: treeObj.node.prefLabel
-      }
+      value: treeObj
     });
   };
 
@@ -168,6 +170,15 @@ class Tree extends Component {
 
   generateLabel = node => {
     let count = node.totalInstanceCount == null || node.totalInstanceCount == 0 ? node.instanceCount : node.totalInstanceCount;
+
+    if (node.noHits === 'true' || Array.isArray(node.noHits)) {
+      if (Array.isArray(node.instanceCount) ) {
+        count = Math.min(...node.instanceCount);
+      } else {
+        count = 0;
+      }
+    }
+
     return (
       <React.Fragment>
         <a
@@ -203,6 +214,10 @@ class Tree extends Component {
     const { classes, facet } = this.props;
     const { isFetching, searchField } = facet;
 
+    // if (this.props.facetID == 'owner') {
+    //   console.log(this.state.treeData)
+    // }
+
     // Case insensitive search of `node.title`
     const customSearchMethod = ({ node, searchQuery }) => {
       let prefLabel = Array.isArray(node.prefLabel) ? node.prefLabel[0] : node.prefLabel;
@@ -226,7 +241,6 @@ class Tree extends Component {
             : 0,
       });
 
-    //<ChipsArray data={this.props.facet.uriFilter} />}
     return (
       <React.Fragment>
         {isFetching ?
@@ -302,7 +316,7 @@ class Tree extends Component {
   }
 }
 
-Tree.propTypes = {
+HierarchicalFacet.propTypes = {
   classes: PropTypes.object.isRequired,
   facetID: PropTypes.string.isRequired,
   facet: PropTypes.object.isRequired,
@@ -311,7 +325,8 @@ Tree.propTypes = {
   fetchFacet: PropTypes.func,
   updateFacetOption: PropTypes.func,
   facetUpdateID: PropTypes.number,
+  updatedFilter: PropTypes.object,
   updatedFacet: PropTypes.string,
 };
 
-export default withStyles(styles)(Tree);
+export default withStyles(styles)(HierarchicalFacet);
diff --git a/src/client/reducers/helpers.js b/src/client/reducers/helpers.js
index 2d4a68eb..9f6b9d08 100644
--- a/src/client/reducers/helpers.js
+++ b/src/client/reducers/helpers.js
@@ -70,13 +70,16 @@ const updateFacetFilter = (state, action) => {
   let newFacet = {};
   if (oldFacet.filterType === 'uriFilter') {
     let newUriFilter = oldFacet.uriFilter == null ? {} : oldFacet.uriFilter;
-    if (has(newUriFilter, value.id)) {
-      delete newUriFilter[value.id];
+    // 'value' is a react sortable tree object
+    if (has(newUriFilter, value.node.id)) {
+      value.added = false;
+      delete newUriFilter[value.node.id];
       if (isEmpty(newUriFilter)) {
         newUriFilter = null;
       }
     } else {
-      newUriFilter[value.id] = value.label;
+      value.added = true;
+      newUriFilter[value.node.id] = value;
     }
     newFacet = {
       ...state.facets[facetID],
@@ -92,6 +95,7 @@ const updateFacetFilter = (state, action) => {
     ...state,
     updatedFacet: facetID,
     facetUpdateID: ++state.facetUpdateID,
+    updatedFilter: value, // a react sortable tree object
     facets: {
       ...state.facets,
       [ facetID ]: newFacet
diff --git a/src/client/reducers/manuscriptsFacets.js b/src/client/reducers/manuscriptsFacets.js
index bcca7273..2d80f22d 100644
--- a/src/client/reducers/manuscriptsFacets.js
+++ b/src/client/reducers/manuscriptsFacets.js
@@ -14,6 +14,7 @@ import {
 export const INITIAL_STATE = {
   updatedFacet: null,
   facetUpdateID: 0,
+  updatedFilter: null,
   facets: {
     source: {
       id: 'source',
@@ -68,23 +69,23 @@ export const INITIAL_STATE = {
     //   startValue: null,
     //   endValue: null
     // },
-    author: {
-      id: 'author',
-      label: 'Author',
-      // predicate: defined in backend
-      distinctValueCount: 0,
-      values: [],
-      flatValues: [],
-      sortBy: 'prefLabel',
-      sortDirection: 'asc',
-      sortButton: true,
-      spatialFilterButton: false,
-      isFetching: false,
-      searchField: true,
-      containerClass: 'ten',
-      filterType: 'uriFilter',
-      uriFilter: null
-    },
+    // author: {
+    //   id: 'author',
+    //   label: 'Author',
+    //   // predicate: defined in backend
+    //   distinctValueCount: 0,
+    //   values: [],
+    //   flatValues: [],
+    //   sortBy: 'prefLabel',
+    //   sortDirection: 'asc',
+    //   sortButton: true,
+    //   spatialFilterButton: false,
+    //   isFetching: false,
+    //   searchField: true,
+    //   containerClass: 'ten',
+    //   filterType: 'uriFilter',
+    //   uriFilter: null
+    // },
     owner: {
       id: 'owner',
       label: 'Owner',
@@ -125,6 +126,7 @@ const manuscriptsFacets = (state = INITIAL_STATE, action) => {
       case UPDATE_FACET_VALUES:
         return updateFacetValues(state, action);
       case UPDATE_FACET_OPTION:
+        // console.log(action)
         return updateFacetOption(state, action);
       default:
         return state;
-- 
GitLab