From 32db0ff6477ed28a2b21e6c322351e65829142d4 Mon Sep 17 00:00:00 2001
From: Chris Beer <cabeer@stanford.edu>
Date: Wed, 13 Feb 2019 09:25:36 -0800
Subject: [PATCH] Implement card layout for adding windows from manifests; part
 of #1836

---
 src/components/ManifestListItem.js | 82 ++++++++++++++++++++++++------
 src/components/WorkspaceAdd.js     | 18 ++++---
 src/containers/ManifestListItem.js | 10 +++-
 src/state/selectors/index.js       | 11 ++++
 4 files changed, 99 insertions(+), 22 deletions(-)

diff --git a/src/components/ManifestListItem.js b/src/components/ManifestListItem.js
index 74faccb42..0791069ba 100644
--- a/src/components/ManifestListItem.js
+++ b/src/components/ManifestListItem.js
@@ -1,5 +1,13 @@
 import React from 'react';
 import PropTypes from 'prop-types';
+import classNames from 'classnames';
+import { withStyles } from '@material-ui/core/styles';
+import Button from '@material-ui/core/Button';
+import Card from '@material-ui/core/Card';
+import CardHeader from '@material-ui/core/CardHeader';
+import CardMedia from '@material-ui/core/CardMedia';
+import CardContent from '@material-ui/core/CardContent';
+import WindowIcon from './WindowIcon';
 import ns from '../config/css-ns';
 
 
@@ -15,28 +23,72 @@ const handleOpenButtonClick = (event, manifest, addWindow) => {
  * @param {object} [props.manifest = string]
  */
 
-/**
- * Determines which classes should be used for display, based on the state of
- * the manifest
- * @memberof ManifestListItem
- * @private
- */
-const ManifestListItem = ({ manifest, addWindow, handleClose }) => (
-  <li className={ns('manifest-list-item')}>
-    <button type="button" onClick={(event) => { handleOpenButtonClick(event, manifest, addWindow); handleClose(); }}>
-      {manifest}
-    </button>
-  </li>
-);
+/** */
+class ManifestListItem extends React.Component {
+  /** */
+  render() {
+    const {
+      manifestId, title, thumbnail, logo, addWindow, handleClose, classes,
+    } = this.props;
+
+    return (
+      <Card className={classNames(classes.card, ns('manifest-list-item'))}>
+        <CardHeader
+          avatar={
+            <WindowIcon manifestLogo={logo} />
+          }
+          title={title || manifestId}
+        />
+
+        {
+          thumbnail && (
+            <CardMedia
+              className={classes.media}
+              image={thumbnail}
+            />
+          )
+        }
+        <CardContent>
+          <Button
+            color="primary"
+            onClick={
+              (event) => { handleOpenButtonClick(event, manifestId, addWindow); handleClose(); }
+            }
+          >
+            {title || manifestId}
+          </Button>
+        </CardContent>
+      </Card>
+    );
+  }
+}
 
 ManifestListItem.propTypes = {
-  manifest: PropTypes.string.isRequired, // eslint-disable-line react/forbid-prop-types
+  manifestId: PropTypes.string.isRequired,
   addWindow: PropTypes.func.isRequired,
   handleClose: PropTypes.func,
+  title: PropTypes.string,
+  thumbnail: PropTypes.string,
+  logo: PropTypes.string,
+  classes: PropTypes.object, // eslint-disable-line react/forbid-prop-types
 };
 
 ManifestListItem.defaultProps = {
   handleClose: () => {},
+  logo: null,
+  classes: {},
+  thumbnail: null,
+  title: null,
 };
 
-export default ManifestListItem;
+/** */
+const styles = theme => ({
+  card: {
+    maxWidth: 400,
+  },
+  media: {
+    height: 192,
+  },
+});
+
+export default withStyles(styles)(ManifestListItem);
diff --git a/src/components/WorkspaceAdd.js b/src/components/WorkspaceAdd.js
index 25461d64e..b1858624f 100644
--- a/src/components/WorkspaceAdd.js
+++ b/src/components/WorkspaceAdd.js
@@ -1,5 +1,7 @@
 import React from 'react';
 import PropTypes from 'prop-types';
+import GridList from '@material-ui/core/GridList';
+import GridListTile from '@material-ui/core/GridListTile';
 import ns from '../config/css-ns';
 import ManifestForm from '../containers/ManifestForm';
 import ManifestListItem from '../containers/ManifestListItem';
@@ -17,11 +19,13 @@ class WorkspaceAdd extends React.Component {
     const { manifests, setWorkspaceAddVisibility } = this.props;
 
     const manifestList = Object.keys(manifests).map(manifest => (
-      <ManifestListItem
-        key={manifest}
-        manifest={manifest}
-        handleClose={() => setWorkspaceAddVisibility(false)}
-      />
+      <GridListTile key={`tile_${manifest}`}>
+        <ManifestListItem
+          key={manifest}
+          manifestId={manifest}
+          handleClose={() => setWorkspaceAddVisibility(false)}
+        />
+      </GridListTile>
     ));
 
     return (
@@ -29,7 +33,9 @@ class WorkspaceAdd extends React.Component {
         <ManifestForm
           id="add-form"
         />
-        <ul>{manifestList}</ul>
+        <GridList cellHeight={350} cols={3}>
+          {manifestList}
+        </GridList>
       </div>
     );
   }
diff --git a/src/containers/ManifestListItem.js b/src/containers/ManifestListItem.js
index a4614eb24..09f13486f 100644
--- a/src/containers/ManifestListItem.js
+++ b/src/containers/ManifestListItem.js
@@ -1,7 +1,15 @@
 import { connect } from 'react-redux';
+import { getManifestTitle, getManifestLogo, getManifestThumbnail } from '../state/selectors';
 import * as actions from '../state/actions';
 import ManifestListItem from '../components/ManifestListItem';
 
+/** */
+const mapStateToProps = (state, { manifestId }) => ({
+  title: getManifestTitle(state.manifests[manifestId]),
+  logo: getManifestLogo(state.manifests[manifestId]),
+  thumbnail: getManifestThumbnail(state.manifests[manifestId]),
+});
+
 /**
  * mapDispatchToProps - used to hook up connect to action creators
  * @memberof ManifestListItem
@@ -9,4 +17,4 @@ import ManifestListItem from '../components/ManifestListItem';
  */
 const mapDispatchToProps = { addWindow: actions.addWindow };
 
-export default connect(null, mapDispatchToProps)(ManifestListItem);
+export default connect(mapStateToProps, mapDispatchToProps)(ManifestListItem);
diff --git a/src/state/selectors/index.js b/src/state/selectors/index.js
index b7ed6c2d3..bdf5010ef 100644
--- a/src/state/selectors/index.js
+++ b/src/state/selectors/index.js
@@ -21,6 +21,17 @@ export function getManifestLogo(manifest) {
     && manifest.manifestation.getLogo();
 }
 
+/**
+* Return the logo of a manifest or null
+* @param {object} manifest
+* @return {String|null}
+*/
+export function getManifestThumbnail(manifest) {
+  return manifest.manifestation
+    && manifest.manifestation.getThumbnail()
+    && manifest.manifestation.getThumbnail().id;
+}
+
 /**
 * Return the logo of a manifest or null
 * @param {object} manifest
-- 
GitLab