15067: Creates PropertyChipComponent to be used where tags need listing.
authorLucas Di Pentima <lucas@di-pentima.com.ar>
Thu, 14 Nov 2019 13:06:55 +0000 (10:06 -0300)
committerLucas Di Pentima <lucas@di-pentima.com.ar>
Thu, 14 Nov 2019 13:06:55 +0000 (10:06 -0300)
Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <ldipentima@veritasgenetics.com>

src/views-components/details-panel/project-details.tsx
src/views-components/project-properties-dialog/project-properties-dialog.tsx
src/views-components/resource-properties-form/property-chip.tsx [new file with mode: 0644]
src/views/collection-panel/collection-panel.tsx

index 7db4df7be7d4eb83706fb186928c029ee3a24aae..59035da115574b075a4d58eb0ab5d610302046be 100644 (file)
@@ -4,7 +4,6 @@
 
 import * as React from 'react';
 import { connect } from 'react-redux';
-import { RootState } from '~/store/store';
 import { openProjectPropertiesDialog } from '~/store/details-panel/details-panel-action';
 import { ProjectIcon, RenameIcon } from '~/components/icon/icon';
 import { ProjectResource } from '~/models/project';
@@ -14,13 +13,10 @@ import { resourceLabel } from '~/common/labels';
 import { DetailsData } from "./details-data";
 import { DetailsAttribute } from "~/components/details-attribute/details-attribute";
 import { RichTextEditorLink } from '~/components/rich-text-editor-link/rich-text-editor-link';
-import { withStyles, StyleRulesCallback, Chip, WithStyles } from '@material-ui/core';
+import { withStyles, StyleRulesCallback, WithStyles } from '@material-ui/core';
 import { ArvadosTheme } from '~/common/custom-theme';
-import * as CopyToClipboard from 'react-copy-to-clipboard';
-import { snackbarActions, SnackbarKind } from '~/store/snackbar/snackbar-actions';
-import { getTagValueLabel, getTagKeyLabel, Vocabulary } from '~/models/vocabulary';
-import { getVocabulary } from "~/store/vocabulary/vocabulary-selectors";
 import { Dispatch } from 'redux';
+import { PropertyChipComponent } from '../resource-properties-form/property-chip';
 
 export class ProjectDetails extends DetailsData<ProjectResource> {
     getIcon(className?: string) {
@@ -45,35 +41,23 @@ const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
     }
 });
 
-
 interface ProjectDetailsComponentDataProps {
     project: ProjectResource;
-    vocabulary: Vocabulary;
 }
 
 interface ProjectDetailsComponentActionProps {
     onClick: () => void;
-    onCopy: (message: string) => void;
 }
 
-const mapStateToProps = ({ properties }: RootState) => ({
-    vocabulary: getVocabulary(properties),
-});
-
 const mapDispatchToProps = (dispatch: Dispatch) => ({
     onClick: () => dispatch<any>(openProjectPropertiesDialog()),
-    onCopy: (message: string) => dispatch(snackbarActions.OPEN_SNACKBAR({
-        message,
-        hideDuration: 2000,
-        kind: SnackbarKind.SUCCESS
-    }))
 });
 
 type ProjectDetailsComponentProps = ProjectDetailsComponentDataProps & ProjectDetailsComponentActionProps & WithStyles<CssRules>;
 
-const ProjectDetailsComponent = connect(mapStateToProps, mapDispatchToProps)(
+const ProjectDetailsComponent = connect(null, mapDispatchToProps)(
     withStyles(styles)(
-        ({ classes, project, onClick, vocabulary, onCopy }: ProjectDetailsComponentProps) => <div>
+        ({ classes, project, onClick }: ProjectDetailsComponentProps) => <div>
             <DetailsAttribute label='Type' value={resourceLabel(ResourceKind.PROJECT)} />
             {/* Missing attr */}
             <DetailsAttribute label='Size' value='---' />
@@ -98,14 +82,11 @@ const ProjectDetailsComponent = connect(mapStateToProps, mapDispatchToProps)(
                 </div>
             </DetailsAttribute>
             {
-                Object.keys(project.properties).map(k => {
-                    const label = `${getTagKeyLabel(k, vocabulary)}: ${getTagValueLabel(k, project.properties[k], vocabulary)}`;
-                    return (
-                        <CopyToClipboard key={k} text={label} onCopy={() => onCopy("Copied")}>
-                            <Chip key={k} className={classes.tag} label={label} />
-                        </CopyToClipboard>
-                    );
-                })
+                Object.keys(project.properties).map(k =>
+                    <PropertyChipComponent key={k}
+                        propKey={k} propValue={project.properties[k]}
+                        className={classes.tag} />
+                )
             }
         </div>
     ));
index a071a985417efad9fbc2d5806427808bd9d89e14..7a4cfba6c56e5d133c2a61a94101f33c2d01cd3f 100644 (file)
@@ -9,14 +9,11 @@ import { RootState } from '~/store/store';
 import { withDialog, WithDialogProps } from "~/store/dialog/with-dialog";
 import { ProjectResource } from '~/models/project';
 import { PROJECT_PROPERTIES_DIALOG_NAME, deleteProjectProperty } from '~/store/details-panel/details-panel-action';
-import { Dialog, DialogTitle, DialogContent, DialogActions, Button, Chip, withStyles, StyleRulesCallback, WithStyles } from '@material-ui/core';
+import { Dialog, DialogTitle, DialogContent, DialogActions, Button, withStyles, StyleRulesCallback, WithStyles } from '@material-ui/core';
 import { ArvadosTheme } from '~/common/custom-theme';
 import { ProjectPropertiesForm } from '~/views-components/project-properties-dialog/project-properties-form';
 import { getResource } from '~/store/resources/resources';
-import * as CopyToClipboard from 'react-copy-to-clipboard';
-import { snackbarActions, SnackbarKind } from '~/store/snackbar/snackbar-actions';
-import { getTagValueLabel, getTagKeyLabel, Vocabulary } from '~/models/vocabulary';
-import { getVocabulary } from "~/store/vocabulary/vocabulary-selectors";
+import { PropertyChipComponent } from "../resource-properties-form/property-chip";
 
 type CssRules = 'tag';
 
@@ -29,26 +26,18 @@ const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
 
 interface ProjectPropertiesDialogDataProps {
     project: ProjectResource;
-    vocabulary: Vocabulary;
 }
 
 interface ProjectPropertiesDialogActionProps {
     handleDelete: (key: string) => void;
-    onCopy: (message: string) => void;
 }
 
 const mapStateToProps = ({ detailsPanel, resources, properties }: RootState): ProjectPropertiesDialogDataProps => ({
     project: getResource(detailsPanel.resourceUuid)(resources) as ProjectResource,
-    vocabulary: getVocabulary(properties),
 });
 
 const mapDispatchToProps = (dispatch: Dispatch): ProjectPropertiesDialogActionProps => ({
     handleDelete: (key: string) => dispatch<any>(deleteProjectProperty(key)),
-    onCopy: (message: string) => dispatch(snackbarActions.OPEN_SNACKBAR({
-                message,
-                hideDuration: 2000,
-                kind: SnackbarKind.SUCCESS
-            }))
 });
 
 type ProjectPropertiesDialogProps =  ProjectPropertiesDialogDataProps & ProjectPropertiesDialogActionProps & WithDialogProps<{}> & WithStyles<CssRules>;
@@ -56,7 +45,7 @@ type ProjectPropertiesDialogProps =  ProjectPropertiesDialogDataProps & ProjectP
 export const ProjectPropertiesDialog = connect(mapStateToProps, mapDispatchToProps)(
     withStyles(styles)(
     withDialog(PROJECT_PROPERTIES_DIALOG_NAME)(
-        ({ classes, open, closeDialog, handleDelete, onCopy, project, vocabulary }: ProjectPropertiesDialogProps) =>
+        ({ classes, open, closeDialog, handleDelete, project }: ProjectPropertiesDialogProps) =>
             <Dialog open={open}
                 onClose={closeDialog}
                 fullWidth
@@ -65,16 +54,11 @@ export const ProjectPropertiesDialog = connect(mapStateToProps, mapDispatchToPro
                 <DialogContent>
                     <ProjectPropertiesForm />
                     {project && project.properties &&
-                        Object.keys(project.properties).map(k => {
-                            const label = `${getTagKeyLabel(k, vocabulary)}: ${getTagValueLabel(k, project.properties[k], vocabulary)}`;
-                            return (
-                                <CopyToClipboard key={k} text={label} onCopy={() => onCopy("Copied")}>
-                                    <Chip key={k} className={classes.tag}
-                                        onDelete={() => handleDelete(k)}
-                                        label={label} />
-                                </CopyToClipboard>
-                            );
-                        })
+                        Object.keys(project.properties).map(k =>
+                            <PropertyChipComponent
+                                onDelete={() => handleDelete(k)}
+                                key={k} className={classes.tag}
+                                propKey={k} propValue={project.properties[k]} />)
                     }
                 </DialogContent>
                 <DialogActions>
@@ -86,4 +70,5 @@ export const ProjectPropertiesDialog = connect(mapStateToProps, mapDispatchToPro
                     </Button>
                 </DialogActions>
             </Dialog>
-)));
\ No newline at end of file
+    )
+));
\ No newline at end of file
diff --git a/src/views-components/resource-properties-form/property-chip.tsx b/src/views-components/resource-properties-form/property-chip.tsx
new file mode 100644 (file)
index 0000000..c51a8d8
--- /dev/null
@@ -0,0 +1,52 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as React from 'react';
+import { Chip } from '@material-ui/core';
+import { connect } from 'react-redux';
+import { RootState } from '~/store/store';
+import * as CopyToClipboard from 'react-copy-to-clipboard';
+import { getVocabulary } from '~/store/vocabulary/vocabulary-selectors';
+import { Dispatch } from 'redux';
+import { snackbarActions, SnackbarKind } from '~/store/snackbar/snackbar-actions';
+import { getTagValueLabel, getTagKeyLabel, Vocabulary } from '~/models/vocabulary';
+
+interface PropertyChipComponentDataProps {
+    propKey: string;
+    propValue: string;
+    className: string;
+    vocabulary: Vocabulary;
+}
+
+interface PropertyChipComponentActionProps {
+    onDelete?: () => void;
+    onCopy: (message: string) => void;
+}
+
+type PropertyChipComponentProps = PropertyChipComponentActionProps & PropertyChipComponentDataProps;
+
+const mapStateToProps = ({ properties }: RootState) => ({
+    vocabulary: getVocabulary(properties),
+});
+
+const mapDispatchToProps = (dispatch: Dispatch) => ({
+    onCopy: (message: string) => dispatch(snackbarActions.OPEN_SNACKBAR({
+        message,
+        hideDuration: 2000,
+        kind: SnackbarKind.SUCCESS
+    }))
+});
+
+// Renders a Chip with copyable-on-click tag:value data based on the vocabulary
+export const PropertyChipComponent = connect(mapStateToProps, mapDispatchToProps)(
+    ({ propKey, propValue, vocabulary, className, onCopy, onDelete }: PropertyChipComponentProps) => {
+        const label = `${getTagKeyLabel(propKey, vocabulary)}: ${getTagValueLabel(propKey, propValue, vocabulary)}`;
+        return (
+            <CopyToClipboard key={propKey} text={label} onCopy={() => onCopy("Copied to clipboard")}>
+                <Chip onDelete={onDelete} key={propKey}
+                    className={className} label={label} />
+            </CopyToClipboard>
+        );
+    }
+);
index 87768e640714a7acb306e0e3705b4c9760047a67..28ae2f05eddd1e70c83868ef03b341c16a204e87 100644 (file)
@@ -5,9 +5,8 @@
 import * as React from 'react';
 import {
     StyleRulesCallback, WithStyles, withStyles, Card,
-    CardHeader, IconButton, CardContent, Grid, Chip, Tooltip
+    CardHeader, IconButton, CardContent, Grid, Tooltip
 } from '@material-ui/core';
-import { compose } from "redux";
 import { connect, DispatchProp } from "react-redux";
 import { RouteComponentProps } from 'react-router';
 import { ArvadosTheme } from '~/common/custom-theme';
@@ -25,10 +24,8 @@ import { formatFileSize } from "~/common/formatters";
 import { getResourceData } from "~/store/resources-data/resources-data";
 import { ResourceData } from "~/store/resources-data/resources-data-reducer";
 import { openDetailsPanel } from '~/store/details-panel/details-panel-action';
-import * as CopyToClipboard from 'react-copy-to-clipboard';
 import { snackbarActions, SnackbarKind } from '~/store/snackbar/snackbar-actions';
-import { connectVocabulary, VocabularyProp } from '~/views-components/resource-properties-form/property-field-common';
-import { getTagValueLabel, getTagKeyLabel } from '~/models/vocabulary';
+import { PropertyChipComponent } from '~/views-components/resource-properties-form/property-chip';
 
 type CssRules = 'card' | 'iconHeader' | 'tag' | 'label' | 'value' | 'link';
 
@@ -68,19 +65,16 @@ interface CollectionPanelDataProps {
 type CollectionPanelProps = CollectionPanelDataProps & DispatchProp
     & WithStyles<CssRules> & RouteComponentProps<{ id: string }>;
 
-export const CollectionPanel = compose(
-    connectVocabulary,
-    withStyles(styles))(
-        connect((state: RootState, props: RouteComponentProps<{ id: string }> & VocabularyProp) => {
+export const CollectionPanel = withStyles(styles)(
+        connect((state: RootState, props: RouteComponentProps<{ id: string }>) => {
             const item = getResource(props.match.params.id)(state.resources);
             const data = getResourceData(props.match.params.id)(state.resourcesData);
-            const vocabulary = props.vocabulary;
-            return { item, data, vocabulary };
+            return { item, data };
         })(
-        class extends React.Component<CollectionPanelProps & VocabularyProp> {
+        class extends React.Component<CollectionPanelProps> {
 
             render() {
-                const { classes, item, data, dispatch, vocabulary } = this.props;
+                const { classes, item, data, dispatch } = this.props;
                 return item
                     ? <>
                         <Card className={classes.card}>
@@ -136,14 +130,12 @@ export const CollectionPanel = compose(
                                         <CollectionTagForm />
                                     </Grid>
                                     <Grid item xs={12}>
-                                        {Object.keys(item.properties).map(k => {
-                                            const label = `${getTagKeyLabel(k, vocabulary)}: ${getTagValueLabel(k, item.properties[k], vocabulary)}`;
-                                            return <CopyToClipboard key={k} text={label} onCopy={() => this.onCopy("Copied")}>
-                                                <Chip className={classes.tag}
-                                                    onDelete={this.handleDelete(k)}
-                                                    label={label} />
-                                            </CopyToClipboard>;
-                                        })}
+                                        {Object.keys(item.properties).map(k =>
+                                            <PropertyChipComponent
+                                                key={k} className={classes.tag}
+                                                onDelete={this.handleDelete(k)}
+                                                propKey={k} propValue={item.properties[k]} />
+                                        )}
                                     </Grid>
                                 </Grid>
                             </CardContent>