Merge branch 'master' into 14120-rich-text-editor
authorJanicki Artur <artur.janicki@contractors.roche.com>
Thu, 27 Sep 2018 08:58:03 +0000 (10:58 +0200)
committerJanicki Artur <artur.janicki@contractors.roche.com>
Thu, 27 Sep 2018 08:58:03 +0000 (10:58 +0200)
refs #14120

Arvados-DCO-1.1-Signed-off-by: Janicki Artur <artur.janicki@contractors.roche.com>

12 files changed:
package.json
src/components/rich-text-editor-link/rich-text-editor-link.tsx [new file with mode: 0644]
src/components/text-field/text-field.tsx
src/store/projects/project-update-actions.ts
src/store/rich-text-editor-dialog/rich-text-editor-dialog-actions.tsx [new file with mode: 0644]
src/validators/validators.tsx
src/views-components/details-panel/project-details.tsx
src/views-components/form-fields/project-form-fields.tsx
src/views-components/rich-text-editor-dialog/rich-text-editor-dialog.tsx [new file with mode: 0644]
src/views/workbench/workbench.tsx
typings/global.d.ts
yarn.lock

index 811e4a07ae5dc53eeb56ed4105e03cbae6f05649..64ea1d1a4dcef19aad3f4072b5295c4b4f01b2e0 100644 (file)
     "axios": "0.18.0",
     "classnames": "2.2.6",
     "lodash": "4.17.10",
-    "react": "16.4.2",
+    "react": "16.5.2",
     "react-copy-to-clipboard": "5.0.1",
-    "react-dom": "16.4.2",
+    "react-dom": "16.5.2",
     "react-dropzone": "5.0.1",
     "react-redux": "5.0.7",
     "react-router": "4.3.1",
     "react-router-dom": "4.3.1",
     "react-router-redux": "5.0.0-alpha.9",
+    "react-rte": "0.16.1",
     "react-scripts-ts": "2.17.0",
     "react-splitter-layout": "3.0.1",
     "react-transition-group": "2.4.0",
diff --git a/src/components/rich-text-editor-link/rich-text-editor-link.tsx b/src/components/rich-text-editor-link/rich-text-editor-link.tsx
new file mode 100644 (file)
index 0000000..2d6a5b4
--- /dev/null
@@ -0,0 +1,43 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as React from 'react';
+import { Dispatch } from 'redux';
+import { connect } from 'react-redux';
+import { withStyles, StyleRulesCallback, WithStyles, Typography } from '@material-ui/core';
+import { ArvadosTheme } from '~/common/custom-theme';
+import { openRichTextEditorDialog } from '~/store/rich-text-editor-dialog/rich-text-editor-dialog-actions';
+
+type CssRules = "root";
+
+const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
+    root: {
+        color: theme.palette.primary.main,
+        cursor: 'pointer'
+    }
+});
+
+interface RichTextEditorLinkData {
+    title: string;
+    label: string;
+    content: string;
+}
+
+interface RichTextEditorLinkActions {
+    onClick: (title: string, content: string) => void;
+}
+
+type RichTextEditorLinkProps = RichTextEditorLinkData & RichTextEditorLinkActions & WithStyles<CssRules>;
+
+const mapDispatchToProps = (dispatch: Dispatch) => ({
+    onClick: (title: string, content: string) => dispatch<any>(openRichTextEditorDialog(title, content))
+});
+
+export const RichTextEditorLink = connect(undefined, mapDispatchToProps)(
+    withStyles(styles)(({ classes, title, content, label, onClick }: RichTextEditorLinkProps) =>
+        <Typography component='span' className={classes.root}
+            onClick={() => onClick(title, content) }>
+            {label}
+        </Typography>
+    ));
\ No newline at end of file
index b5671dbb08c962b8bae3458738fdf634b57bc382..076889eabb4b9af6acb630d243e9db9829a47950 100644 (file)
@@ -6,6 +6,7 @@ import * as React from 'react';
 import { WrappedFieldProps } from 'redux-form';
 import { ArvadosTheme } from '~/common/custom-theme';
 import { TextField as MaterialTextField, StyleRulesCallback, WithStyles, withStyles } from '@material-ui/core';
+import RichTextEditor from 'react-rte';
 
 type CssRules = 'textField';
 
@@ -27,3 +28,30 @@ export const TextField = withStyles(styles)((props: WrappedFieldProps & WithStyl
         fullWidth={true}
         {...props.input}
     />);
+
+
+interface RichEditorTextFieldData {
+    label?: string;
+}
+
+type RichEditorTextFieldProps = RichEditorTextFieldData & WrappedFieldProps & WithStyles<CssRules>;
+
+export const RichEditorTextField = withStyles(styles)(
+    class RichEditorTextField extends React.Component<RichEditorTextFieldProps> {
+        state = {
+            value: RichTextEditor.createValueFromString(this.props.input.value, 'html')
+        };
+
+        onChange = (value: any) => {
+            this.setState({ value });
+            this.props.input.onChange(value.toString('html'));
+        }
+
+        render() {
+            return <RichTextEditor 
+                value={this.state.value}
+                onChange={this.onChange}
+                placeholder={this.props.label} />;
+        }
+    }
+);
\ No newline at end of file
index afa2e35e8d7c6555055ba053c0bfaeadb6145d84..34ea42f59bd93796bb355da70d3291cc91133906 100644 (file)
@@ -10,6 +10,7 @@ import { getCommonResourceServiceError, CommonResourceServiceError } from "~/ser
 import { ServiceRepository } from "~/services/services";
 import { ProjectResource } from '~/models/project';
 import { ContextMenuResource } from "~/store/context-menu/context-menu-actions";
+import { getResource } from '~/store/resources/resources';
 
 export interface ProjectUpdateFormDialogData {
     uuid: string;
@@ -20,8 +21,9 @@ export interface ProjectUpdateFormDialogData {
 export const PROJECT_UPDATE_FORM_NAME = 'projectUpdateFormName';
 
 export const openProjectUpdateDialog = (resource: ContextMenuResource) =>
-    (dispatch: Dispatch) => {
-        dispatch(initialize(PROJECT_UPDATE_FORM_NAME, resource));
+    (dispatch: Dispatch, getState: () => RootState) => {
+        const project = getResource(resource.uuid)(getState().resources);
+        dispatch(initialize(PROJECT_UPDATE_FORM_NAME, project));
         dispatch(dialogActions.OPEN_DIALOG({ id: PROJECT_UPDATE_FORM_NAME, data: {} }));
     };
 
diff --git a/src/store/rich-text-editor-dialog/rich-text-editor-dialog-actions.tsx b/src/store/rich-text-editor-dialog/rich-text-editor-dialog-actions.tsx
new file mode 100644 (file)
index 0000000..f2a1c4b
--- /dev/null
@@ -0,0 +1,10 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { Dispatch } from "redux";
+import { dialogActions } from "~/store/dialog/dialog-actions";
+
+export const RICH_TEXT_EDITOR_DIALOG_NAME = 'richTextEditorDialogName';
+export const openRichTextEditorDialog = (title: string, text: string) => 
+    dialogActions.OPEN_DIALOG({ id: RICH_TEXT_EDITOR_DIALOG_NAME, data: { title, text } });
\ No newline at end of file
index 755cd7f7b4c7be4b7a1650653c3b7783406c1179..edc472657eb3178036fb0d8b3917dd0600e39ce5 100644 (file)
@@ -9,7 +9,6 @@ export const TAG_KEY_VALIDATION = [require, maxLength(255)];
 export const TAG_VALUE_VALIDATION = [require, maxLength(255)];
 
 export const PROJECT_NAME_VALIDATION = [require, maxLength(255)];
-export const PROJECT_DESCRIPTION_VALIDATION = [maxLength(255)];
 
 export const COLLECTION_NAME_VALIDATION = [require, maxLength(255)];
 export const COLLECTION_DESCRIPTION_VALIDATION = [maxLength(255)];
index 1e65ec834bf8e48f75230bea8505d843eda82a63..331952f259e7ff357cd48a2e088007064e5a2a8d 100644 (file)
@@ -10,6 +10,7 @@ import { ResourceKind } from '~/models/resource';
 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';
 
 export class ProjectDetails extends DetailsData<ProjectResource> {
 
@@ -27,7 +28,15 @@ export class ProjectDetails extends DetailsData<ProjectResource> {
             <DetailsAttribute label='Created at' value={formatDate(this.item.createdAt)} />
             {/* Missing attr */}
             <DetailsAttribute label='File size' value='1.4 GB' />
-            <DetailsAttribute label='Description' value={this.item.description} />
+            <DetailsAttribute label='Description'>
+                {this.item.description ? 
+                    <RichTextEditorLink
+                        title={`Description of ${this.item.name}`}
+                        content={this.item.description} 
+                        label='Show full description' />
+                    : '---'
+                }
+            </DetailsAttribute>
         </div>;
     }
 }
index 6446c763a81300850c026ed3889ed4d86872837a..8243cfe364d45cee3d05fde0b3637a7b13a3838f 100644 (file)
@@ -4,8 +4,8 @@
 
 import * as React from "react";
 import { Field } from "redux-form";
-import { TextField } from "~/components/text-field/text-field";
-import { PROJECT_NAME_VALIDATION, PROJECT_DESCRIPTION_VALIDATION } from "~/validators/validators";
+import { TextField, RichEditorTextField } from "~/components/text-field/text-field";
+import { PROJECT_NAME_VALIDATION } from "~/validators/validators";
 
 export const ProjectNameField = () =>
     <Field
@@ -18,6 +18,5 @@ export const ProjectNameField = () =>
 export const ProjectDescriptionField = () =>
     <Field
         name='description'
-        component={TextField}
-        validate={PROJECT_DESCRIPTION_VALIDATION}
+        component={RichEditorTextField}
         label="Description - optional" />;
diff --git a/src/views-components/rich-text-editor-dialog/rich-text-editor-dialog.tsx b/src/views-components/rich-text-editor-dialog/rich-text-editor-dialog.tsx
new file mode 100644 (file)
index 0000000..a997355
--- /dev/null
@@ -0,0 +1,38 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as React from "react";
+import { Dialog, DialogTitle, DialogContent, DialogActions, Button, DialogContentText } from "@material-ui/core";
+import { WithDialogProps } from "../../store/dialog/with-dialog";
+import { withDialog } from '~/store/dialog/with-dialog';
+import { RICH_TEXT_EDITOR_DIALOG_NAME } from "~/store/rich-text-editor-dialog/rich-text-editor-dialog-actions";
+import RichTextEditor from 'react-rte';
+
+export interface RichTextEditorDialogDataProps {
+    title: string;
+    text: string;
+}
+
+export const RichTextEditorDialog = withDialog(RICH_TEXT_EDITOR_DIALOG_NAME)(
+    (props: WithDialogProps<RichTextEditorDialogDataProps>) =>
+        <Dialog open={props.open}
+            onClose={props.closeDialog}
+            fullWidth
+            maxWidth='sm'>
+            <DialogTitle>{props.data.title}</DialogTitle>
+            <DialogContent>
+                <RichTextEditor 
+                    value={RichTextEditor.createValueFromString(props.data.text, 'html')}
+                    readOnly={true} />
+            </DialogContent>
+            <DialogActions>
+                <Button
+                    variant='flat'
+                    color='primary'
+                    onClick={props.closeDialog}>
+                    Close
+                </Button>
+            </DialogActions>
+        </Dialog>
+);
\ No newline at end of file
index 78918be0c65c687d5230fa622c215c9e04ca847b..b0d14f09072bf0be2c139bb7eee28525588c9bfd 100644 (file)
@@ -11,6 +11,7 @@ import { ArvadosTheme } from '~/common/custom-theme';
 import { ContextMenu } from "~/views-components/context-menu/context-menu";
 import { FavoritePanel } from "../favorite-panel/favorite-panel";
 import { CurrentTokenDialog } from '~/views-components/current-token-dialog/current-token-dialog';
+import { RichTextEditorDialog } from '~/views-components/rich-text-editor-dialog/rich-text-editor-dialog';
 import { Snackbar } from '~/views-components/snackbar/snackbar';
 import { CollectionPanel } from '../collection-panel/collection-panel';
 import { RenameFileDialog } from '~/views-components/rename-file-dialog/rename-file-dialog';
@@ -117,6 +118,7 @@ export const WorkbenchPanel =
             <PartialCopyCollectionDialog />
             <ProcessCommandDialog />
             <RenameFileDialog />
+            <RichTextEditorDialog />
             <Snackbar />
             <UpdateCollectionDialog />
             <UpdateProcessDialog />
index da8e4415d1f6c2d863eb47cb8586ab9214a5ba32..b9f1cc627770e4061f08cccbf10db29e40fefbac 100644 (file)
@@ -12,4 +12,5 @@ declare interface System {
 }
 declare var System: System;
 
-declare module 'react-splitter-layout';
\ No newline at end of file
+declare module 'react-splitter-layout';
+declare module 'react-rte';
\ No newline at end of file
index c8148433ef7250b47fb7b54896da373b806b40aa..211c82e9294b5996bfc8b06e9f29ae8d972bc9b0 100644 (file)
--- a/yarn.lock
+++ b/yarn.lock
@@ -1123,7 +1123,7 @@ babel-register@^6.26.0:
     mkdirp "^0.5.1"
     source-map-support "^0.4.15"
 
-babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.26.0, babel-runtime@^6.9.2:
+babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.23.0, babel-runtime@^6.26.0, babel-runtime@^6.9.2:
   version "6.26.0"
   resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe"
   dependencies:
@@ -1612,6 +1612,10 @@ clap@^1.0.9:
   dependencies:
     chalk "^1.1.3"
 
+class-autobind@^0.1.4:
+  version "0.1.4"
+  resolved "https://registry.yarnpkg.com/class-autobind/-/class-autobind-0.1.4.tgz#34516c49167cf8d3f639ddc186bcfa2268afff34"
+
 class-utils@^0.3.5:
   version "0.3.6"
   resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463"
@@ -2373,6 +2377,50 @@ dotenv@4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-4.0.0.tgz#864ef1379aced55ce6f95debecdce179f7a0cd1d"
 
+draft-js-export-html@>=0.6.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/draft-js-export-html/-/draft-js-export-html-1.2.0.tgz#1cbe2b78e1fed74fc29c7cdcbfd7540468eca209"
+  dependencies:
+    draft-js-utils "^1.2.0"
+
+draft-js-export-markdown@>=0.3.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/draft-js-export-markdown/-/draft-js-export-markdown-1.2.0.tgz#c423d67389e1c70ddd13e956afe82fd1b72feea5"
+  dependencies:
+    draft-js-utils "^1.2.0"
+
+draft-js-import-element@^1.2.1:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/draft-js-import-element/-/draft-js-import-element-1.2.1.tgz#9a6a56d74690d48d35d8d089564e6d710b4926eb"
+  dependencies:
+    draft-js-utils "^1.2.0"
+    synthetic-dom "^1.2.0"
+
+draft-js-import-html@>=0.4.0:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/draft-js-import-html/-/draft-js-import-html-1.2.1.tgz#88adb8ce5dbe1a5a777663b1893cee6a35239eaa"
+  dependencies:
+    draft-js-import-element "^1.2.1"
+
+draft-js-import-markdown@>=0.3.0:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/draft-js-import-markdown/-/draft-js-import-markdown-1.2.1.tgz#ec18eb15008bab13d9878d65db50e181dd64a5ce"
+  dependencies:
+    draft-js-import-element "^1.2.1"
+    synthetic-dom "^1.2.0"
+
+draft-js-utils@>=0.2.0, draft-js-utils@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/draft-js-utils/-/draft-js-utils-1.2.0.tgz#f5cb23eb167325ffed3d79882fdc317721d2fd12"
+
+draft-js@>=0.10.0:
+  version "0.10.5"
+  resolved "https://registry.yarnpkg.com/draft-js/-/draft-js-0.10.5.tgz#bfa9beb018fe0533dbb08d6675c371a6b08fa742"
+  dependencies:
+    fbjs "^0.8.15"
+    immutable "~3.7.4"
+    object-assign "^4.1.0"
+
 duplexer3@^0.1.4:
   version "0.1.4"
   resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2"
@@ -2862,7 +2910,7 @@ fb-watchman@^2.0.0:
   dependencies:
     bser "^2.0.0"
 
-fbjs@^0.8.1, fbjs@^0.8.16:
+fbjs@^0.8.1, fbjs@^0.8.15, fbjs@^0.8.16:
   version "0.8.17"
   resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.17.tgz#c4d598ead6949112653d6588b01a5cdcd9f90fdd"
   dependencies:
@@ -3549,6 +3597,14 @@ ignore-walk@^3.0.1:
   dependencies:
     minimatch "^3.0.4"
 
+immutable@^3.8.1:
+  version "3.8.2"
+  resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.8.2.tgz#c2439951455bb39913daf281376f1530e104adf3"
+
+immutable@~3.7.4:
+  version "3.7.6"
+  resolved "http://registry.npmjs.org/immutable/-/immutable-3.7.6.tgz#13b4d3cb12befa15482a26fe1b2ebae640071e4b"
+
 import-lazy@^2.1.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43"
@@ -6123,14 +6179,14 @@ react-dev-utils@^5.0.1:
     strip-ansi "3.0.1"
     text-table "0.2.0"
 
-react-dom@16.4.2:
-  version "16.4.2"
-  resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.4.2.tgz#4afed569689f2c561d2b8da0b819669c38a0bda4"
+react-dom@16.5.2:
+  version "16.5.2"
+  resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.5.2.tgz#b69ee47aa20bab5327b2b9d7c1fe2a30f2cfa9d7"
   dependencies:
-    fbjs "^0.8.16"
     loose-envify "^1.1.0"
     object-assign "^4.1.1"
-    prop-types "^15.6.0"
+    prop-types "^15.6.2"
+    schedule "^0.5.0"
 
 react-dropzone@5.0.1:
   version "5.0.1"
@@ -6220,6 +6276,21 @@ react-router@4.3.1, react-router@^4.2.0, react-router@^4.3.1:
     prop-types "^15.6.1"
     warning "^4.0.1"
 
+react-rte@0.16.1:
+  version "0.16.1"
+  resolved "https://registry.yarnpkg.com/react-rte/-/react-rte-0.16.1.tgz#e345664c87e992d15ec053b406f51ffac6e86622"
+  dependencies:
+    babel-runtime "^6.23.0"
+    class-autobind "^0.1.4"
+    classnames "^2.2.5"
+    draft-js ">=0.10.0"
+    draft-js-export-html ">=0.6.0"
+    draft-js-export-markdown ">=0.3.0"
+    draft-js-import-html ">=0.4.0"
+    draft-js-import-markdown ">=0.3.0"
+    draft-js-utils ">=0.2.0"
+    immutable "^3.8.1"
+
 react-scripts-ts@2.17.0:
   version "2.17.0"
   resolved "https://registry.yarnpkg.com/react-scripts-ts/-/react-scripts-ts-2.17.0.tgz#398bae19a30c9b39b3dfe0720ebb40c60c2f6574"
@@ -6286,14 +6357,14 @@ react-transition-group@2.4.0, react-transition-group@^2.2.1:
     prop-types "^15.6.2"
     react-lifecycles-compat "^3.0.4"
 
-react@16.4.2:
-  version "16.4.2"
-  resolved "https://registry.yarnpkg.com/react/-/react-16.4.2.tgz#2cd90154e3a9d9dd8da2991149fdca3c260e129f"
+react@16.5.2:
+  version "16.5.2"
+  resolved "https://registry.yarnpkg.com/react/-/react-16.5.2.tgz#19f6b444ed139baa45609eee6dc3d318b3895d42"
   dependencies:
-    fbjs "^0.8.16"
     loose-envify "^1.1.0"
     object-assign "^4.1.1"
-    prop-types "^15.6.0"
+    prop-types "^15.6.2"
+    schedule "^0.5.0"
 
 read-pkg-up@^1.0.1:
   version "1.0.1"
@@ -6741,6 +6812,12 @@ sax@^1.2.4, sax@~1.2.1:
   version "1.2.4"
   resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
 
+schedule@^0.5.0:
+  version "0.5.0"
+  resolved "https://registry.yarnpkg.com/schedule/-/schedule-0.5.0.tgz#c128fffa0b402488b08b55ae74bb9df55cc29cc8"
+  dependencies:
+    object-assign "^4.1.1"
+
 schema-utils@^0.3.0:
   version "0.3.0"
   resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.3.0.tgz#f5877222ce3e931edae039f17eb3716e7137f8cf"
@@ -7285,6 +7362,10 @@ symbol-tree@^3.2.2:
   version "3.2.2"
   resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.2.tgz#ae27db38f660a7ae2e1c3b7d1bc290819b8519e6"
 
+synthetic-dom@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/synthetic-dom/-/synthetic-dom-1.2.0.tgz#f3589aafe2b5e299f337bb32973a9be42dd5625e"
+
 tapable@^0.2.7:
   version "0.2.8"
   resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.2.8.tgz#99372a5c999bf2df160afc0d74bed4f47948cd22"