diff --git a/openbis_ng_ui/src/js/components/common/form/AutocompleterField.jsx b/openbis_ng_ui/src/js/components/common/form/AutocompleterField.jsx index 6cef36c80b088fcef6aae8ad9dc3e77d7933921d..94ab81e8cd9b5f8c395d3fd70e09ae40fd9e2667 100644 --- a/openbis_ng_ui/src/js/components/common/form/AutocompleterField.jsx +++ b/openbis_ng_ui/src/js/components/common/form/AutocompleterField.jsx @@ -218,9 +218,14 @@ class AutocompleterFormField extends React.PureComponent { } renderLabel() { - const { label, mandatory, styles } = this.props + const { label, mandatory, styles, onClick } = this.props return ( - <FormFieldLabel label={label} mandatory={mandatory} styles={styles} /> + <FormFieldLabel + label={label} + mandatory={mandatory} + styles={styles} + onClick={onClick} + /> ) } diff --git a/openbis_ng_ui/src/js/components/common/form/CheckboxField.jsx b/openbis_ng_ui/src/js/components/common/form/CheckboxField.jsx index 70f2f178e73a86115608690778e1f4a05f3fda5b..a88b13132642375200b58be524d43300876473ad 100644 --- a/openbis_ng_ui/src/js/components/common/form/CheckboxField.jsx +++ b/openbis_ng_ui/src/js/components/common/form/CheckboxField.jsx @@ -121,6 +121,7 @@ class CheckboxFormField extends React.PureComponent { label={label} mandatory={mandatory} styles={styles} + onClick={onClick} /> </Typography> </div> diff --git a/openbis_ng_ui/src/js/components/common/form/FormFieldContainer.jsx b/openbis_ng_ui/src/js/components/common/form/FormFieldContainer.jsx index 924ea042878a9c026e93f87f81fbcfed8655634b..0835600515fe9f831b37a7e393f91323f5c01289 100644 --- a/openbis_ng_ui/src/js/components/common/form/FormFieldContainer.jsx +++ b/openbis_ng_ui/src/js/components/common/form/FormFieldContainer.jsx @@ -2,12 +2,22 @@ import React from 'react' import { withStyles } from '@material-ui/core/styles' import FormControl from '@material-ui/core/FormControl' import FormHelperText from '@material-ui/core/FormHelperText' +import InfoIcon from '@material-ui/icons/Info' +import Tooltip from '@src/js/components/common/form/Tooltip.jsx' import logger from '@src/js/common/logger.js' const styles = theme => ({ container: { overflow: 'hidden' }, + subcontainer: { + display: 'flex', + flexDirection: 'row', + alignItems: 'center' + }, + tooltip: { + fontSize: theme.typography.body2.fontSize + }, metadataDefault: { fontSize: theme.typography.label.fontSize, color: theme.typography.label.color, @@ -16,10 +26,15 @@ const styles = theme => ({ marginBottom: theme.spacing(1) / 2 }, controlDefault: { - flex: '0 0' + flex: '1 1 auto' }, descriptionDefault: { - fontSize: theme.typography.label.fontSize + flex: '0 0 auto', + marginLeft: theme.spacing(1) / 2, + lineHeight: '0.7rem', + '& svg': { + color: theme.palette.hint.main + } }, errorDefault: { fontSize: theme.typography.label.fontSize, @@ -30,12 +45,23 @@ const styles = theme => ({ class FormFieldContainer extends React.PureComponent { constructor(props) { super(props) + this.handleClick = this.handleClick.bind(this) + } + + handleClick(event) { + const { onClick } = this.props + if (onClick) { + event.stopPropagation() + event.originalTarget = event.target + event.target = event.currentTarget + onClick(event) + } } render() { logger.log(logger.DEBUG, 'FormField.render') - const { onClick, styles = {}, classes } = this.props + const { styles = {}, classes } = this.props return ( <FormControl @@ -43,11 +69,17 @@ class FormFieldContainer extends React.PureComponent { margin='none' classes={{ root: classes.container }} > - <div onClick={onClick} className={styles.container}> + <div + data-part='container' + onClick={this.handleClick} + className={styles.container} + > {this.renderMetadata()} - {this.renderControl()} + <div className={classes.subcontainer}> + {this.renderControl()} + {this.renderDescription()} + </div> {this.renderError()} - {this.renderDescription()} </div> </FormControl> ) @@ -60,6 +92,7 @@ class FormFieldContainer extends React.PureComponent { return ( <pre data-part='metadata' + onClick={this.handleClick} className={`${classes.metadataDefault} ${styles.metadata}`} > {metadata} @@ -75,6 +108,7 @@ class FormFieldContainer extends React.PureComponent { return ( <div data-part='control' + onClick={this.handleClick} className={`${classes.controlDefault} ${styles.control}`} > {children} @@ -87,14 +121,15 @@ class FormFieldContainer extends React.PureComponent { if (description) { return ( - <FormHelperText> - <span - data-part='description' - className={`${classes.descriptionDefault} ${styles.description}`} - > - {description} - </span> - </FormHelperText> + <span + data-part='description' + onClick={this.handleClick} + className={`${classes.descriptionDefault} ${styles.description}`} + > + <Tooltip title={description}> + <InfoIcon fontSize='small' /> + </Tooltip> + </span> ) } else { return null @@ -109,6 +144,7 @@ class FormFieldContainer extends React.PureComponent { <FormHelperText> <span data-part='error' + onClick={this.handleClick} className={`${classes.errorDefault} ${styles.error}`} > {error} diff --git a/openbis_ng_ui/src/js/components/common/form/FormFieldLabel.jsx b/openbis_ng_ui/src/js/components/common/form/FormFieldLabel.jsx index e789aa3387169afd5f7a8857863acb3a3bc283ca..5034ecb161beaa83d7f35c376e715c8635fbb069 100644 --- a/openbis_ng_ui/src/js/components/common/form/FormFieldLabel.jsx +++ b/openbis_ng_ui/src/js/components/common/form/FormFieldLabel.jsx @@ -14,6 +14,21 @@ const styles = theme => ({ }) class FormFieldLabel extends React.PureComponent { + constructor(props) { + super(props) + this.handleClick = this.handleClick.bind(this) + } + + handleClick(event) { + const { onClick } = this.props + if (onClick) { + event.stopPropagation() + event.originalTarget = event.target + event.target = event.currentTarget + onClick(event) + } + } + render() { logger.log(logger.DEBUG, 'FormFieldLabel.render') @@ -24,6 +39,7 @@ class FormFieldLabel extends React.PureComponent { {label && ( <span data-part='label' + onClick={this.handleClick} className={`${classes.labelDefault} ${styles.label}`} > {label} @@ -32,6 +48,7 @@ class FormFieldLabel extends React.PureComponent { {mandatory && ( <span data-part='mandatory' + onClick={this.handleClick} className={`${classes.mandatoryDefault} ${styles.mandatory}`} > * diff --git a/openbis_ng_ui/src/js/components/common/form/SelectField.jsx b/openbis_ng_ui/src/js/components/common/form/SelectField.jsx index 820f0af6edab060c1416a925357caf8980f1597f..27751d918864bcc4a70fc2550661b5ea8bafa06b 100644 --- a/openbis_ng_ui/src/js/components/common/form/SelectField.jsx +++ b/openbis_ng_ui/src/js/components/common/form/SelectField.jsx @@ -129,6 +129,7 @@ class SelectFormField extends React.PureComponent { label={label} mandatory={mandatory} styles={styles} + onClick={onClick} /> ) : null } diff --git a/openbis_ng_ui/src/js/components/common/form/TextField.jsx b/openbis_ng_ui/src/js/components/common/form/TextField.jsx index 84261d86f308f42578dbb1c6491508f574c64ec2..0063a02b2bea17800d5881a30ebe913dccdb9714 100644 --- a/openbis_ng_ui/src/js/components/common/form/TextField.jsx +++ b/openbis_ng_ui/src/js/components/common/form/TextField.jsx @@ -93,6 +93,7 @@ class TextFormField extends React.PureComponent { label={label} mandatory={mandatory} styles={styles} + onClick={onClick} /> } InputProps={{ diff --git a/openbis_ng_ui/src/js/components/common/form/Tooltip.jsx b/openbis_ng_ui/src/js/components/common/form/Tooltip.jsx new file mode 100644 index 0000000000000000000000000000000000000000..c5ee0c3cd08a0d666d48ca4294062abf24453007 --- /dev/null +++ b/openbis_ng_ui/src/js/components/common/form/Tooltip.jsx @@ -0,0 +1,26 @@ +import React from 'react' +import { withStyles } from '@material-ui/core/styles' +import MaterialTooltip from '@material-ui/core/Tooltip' +import logger from '@src/js/common/logger.js' + +const styles = theme => ({ + title: { + fontSize: theme.typography.body2.fontSize + } +}) + +class Tooltip extends React.PureComponent { + render() { + logger.log(logger.DEBUG, 'Tooltip.render') + + const { children, classes, title } = this.props + + return ( + <MaterialTooltip title={<span className={classes.title}>{title}</span>}> + {children} + </MaterialTooltip> + ) + } +} + +export default withStyles(styles)(Tooltip) diff --git a/openbis_ng_ui/src/js/components/common/theme/ThemeProvider.jsx b/openbis_ng_ui/src/js/components/common/theme/ThemeProvider.jsx index b49b053f9cc18777e640a76382a2e78bb6ec98eb..f67e2f45d72f35a08890e3486b9b5b7c3bc1643f 100644 --- a/openbis_ng_ui/src/js/components/common/theme/ThemeProvider.jsx +++ b/openbis_ng_ui/src/js/components/common/theme/ThemeProvider.jsx @@ -24,6 +24,9 @@ const config = { warning: { main: '#ff9609' }, + hint: { + main: '#bdbdbd' + }, background: { primary: '#ebebeb', secondary: '#f5f5f5' diff --git a/openbis_ng_ui/src/js/components/types/form/TypeFormPreviewProperty.jsx b/openbis_ng_ui/src/js/components/types/form/TypeFormPreviewProperty.jsx index fb9a0d06803a90c2b19aa1bf41794557764c7df9..d533f76455b86b64448f7cd3430f19810dd13f78 100644 --- a/openbis_ng_ui/src/js/components/types/form/TypeFormPreviewProperty.jsx +++ b/openbis_ng_ui/src/js/components/types/form/TypeFormPreviewProperty.jsx @@ -40,8 +40,7 @@ const styles = theme => ({ opacity: 0.4 }, partEmpty: { - fontStyle: 'italic', - opacity: 0.7 + fontStyle: 'italic' }, partSelected: { cursor: 'pointer', @@ -58,11 +57,15 @@ const styles = theme => ({ cursor: 'pointer', pointerEvents: 'initial', paddingBottom: '1px', + borderColor: 'transparent', + borderStyle: 'solid', + borderWidth: '0px 0px 2px 0px', '&:hover': { - borderStyle: 'solid', - borderWidth: '0px 0px 2px 0px', borderColor: theme.palette.border.primary } + }, + property: { + marginTop: '-6px' } }) @@ -319,51 +322,55 @@ class TypeFormPreviewProperty extends React.PureComponent { } renderVarcharProperty() { - const { property, value, mode } = this.props + const { property, value, mode, classes } = this.props return ( - <TextField - name={property.id} - label={this.getLabel()} - description={this.getDescription()} - value={value} - mandatory={this.getMandatory()} - multiline={this.getMultiline()} - metadata={this.getMetadata()} - error={this.getError()} - styles={this.getStyles()} - mode='edit' - disabled={mode !== 'edit'} - onClick={this.handlePropertyClick} - onChange={this.handleChange} - /> + <div className={classes.property}> + <TextField + name={property.id} + label={this.getLabel()} + description={this.getDescription()} + value={value} + mandatory={this.getMandatory()} + multiline={this.getMultiline()} + metadata={this.getMetadata()} + error={this.getError()} + styles={this.getStyles()} + mode='edit' + disabled={mode !== 'edit'} + onClick={this.handlePropertyClick} + onChange={this.handleChange} + /> + </div> ) } renderNumberProperty() { - const { property, value, mode } = this.props + const { property, value, mode, classes } = this.props return ( - <TextField - type='number' - name={property.id} - label={this.getLabel()} - description={this.getDescription()} - value={value} - mandatory={this.getMandatory()} - metadata={this.getMetadata()} - error={this.getError()} - styles={this.getStyles()} - mode='edit' - disabled={mode !== 'edit'} - onClick={this.handlePropertyClick} - onChange={this.handleChange} - /> + <div className={classes.property}> + <TextField + type='number' + name={property.id} + label={this.getLabel()} + description={this.getDescription()} + value={value} + mandatory={this.getMandatory()} + metadata={this.getMetadata()} + error={this.getError()} + styles={this.getStyles()} + mode='edit' + disabled={mode !== 'edit'} + onClick={this.handlePropertyClick} + onChange={this.handleChange} + /> + </div> ) } renderBooleanProperty() { - const { property, value, mode } = this.props + const { property, value, mode, classes } = this.props return ( - <div> + <div className={classes.property}> <CheckboxField name={property.id} label={this.getLabel()} @@ -383,7 +390,7 @@ class TypeFormPreviewProperty extends React.PureComponent { } renderVocabularyProperty() { - const { property, value, mode } = this.props + const { property, value, mode, classes } = this.props const { terms } = this.state let options = [] @@ -396,30 +403,32 @@ class TypeFormPreviewProperty extends React.PureComponent { } return ( - <SelectField - name={property.id} - label={this.getLabel()} - description={this.getDescription()} - value={value} - mandatory={this.getMandatory()} - options={options} - emptyOption={{ - label: '(vocabulary terms preview)', - selectable: false - }} - metadata={this.getMetadata()} - error={this.getError()} - styles={this.getStyles()} - mode='edit' - disabled={mode !== 'edit'} - onClick={this.handlePropertyClick} - onChange={this.handleChange} - /> + <div className={classes.property}> + <SelectField + name={property.id} + label={this.getLabel()} + description={this.getDescription()} + value={value} + mandatory={this.getMandatory()} + options={options} + emptyOption={{ + label: '(vocabulary terms preview)', + selectable: false + }} + metadata={this.getMetadata()} + error={this.getError()} + styles={this.getStyles()} + mode='edit' + disabled={mode !== 'edit'} + onClick={this.handlePropertyClick} + onChange={this.handleChange} + /> + </div> ) } renderMaterialProperty() { - const { property, value, mode } = this.props + const { property, value, mode, classes } = this.props const { materials } = this.state let options = [] @@ -431,30 +440,32 @@ class TypeFormPreviewProperty extends React.PureComponent { } return ( - <SelectField - name={property.id} - label={this.getLabel()} - description={this.getDescription()} - value={value} - mandatory={this.getMandatory()} - options={options} - emptyOption={{ - label: '(materials preview)', - selectable: false - }} - metadata={this.getMetadata()} - error={this.getError()} - styles={this.getStyles()} - mode='edit' - disabled={mode !== 'edit'} - onClick={this.handlePropertyClick} - onChange={this.handleChange} - /> + <div className={classes.property}> + <SelectField + name={property.id} + label={this.getLabel()} + description={this.getDescription()} + value={value} + mandatory={this.getMandatory()} + options={options} + emptyOption={{ + label: '(materials preview)', + selectable: false + }} + metadata={this.getMetadata()} + error={this.getError()} + styles={this.getStyles()} + mode='edit' + disabled={mode !== 'edit'} + onClick={this.handlePropertyClick} + onChange={this.handleChange} + /> + </div> ) } renderSampleProperty() { - const { property, value, mode } = this.props + const { property, value, mode, classes } = this.props const { samples } = this.state let options = [] @@ -466,25 +477,27 @@ class TypeFormPreviewProperty extends React.PureComponent { } return ( - <SelectField - name={property.id} - label={this.getLabel()} - description={this.getDescription()} - value={value} - mandatory={this.getMandatory()} - options={options} - emptyOption={{ - label: '(samples preview)', - selectable: false - }} - metadata={this.getMetadata()} - error={this.getError()} - styles={this.getStyles()} - mode='edit' - disabled={mode !== 'edit'} - onClick={this.handlePropertyClick} - onChange={this.handleChange} - /> + <div className={classes.property}> + <SelectField + name={property.id} + label={this.getLabel()} + description={this.getDescription()} + value={value} + mandatory={this.getMandatory()} + options={options} + emptyOption={{ + label: '(samples preview)', + selectable: false + }} + metadata={this.getMetadata()} + error={this.getError()} + styles={this.getStyles()} + mode='edit' + disabled={mode !== 'edit'} + onClick={this.handlePropertyClick} + onChange={this.handleChange} + /> + </div> ) } diff --git a/openbis_ng_ui/src/js/components/types/form/VocabularyFormParametersTerm.jsx b/openbis_ng_ui/src/js/components/types/form/VocabularyFormParametersTerm.jsx index d957e93b3f16c2788dd24bed1b4d5aa4f595edaf..5b5bc80d19741bf900f6e13f37ddd3ae99397385 100644 --- a/openbis_ng_ui/src/js/components/types/form/VocabularyFormParametersTerm.jsx +++ b/openbis_ng_ui/src/js/components/types/form/VocabularyFormParametersTerm.jsx @@ -203,6 +203,7 @@ class VocabularyFormParametersTerm extends React.PureComponent { reference={this.references.official} label='Official' name='official' + description='Unofficial (aka ad-hoc) terms can be created by regular users from the non-admin UI. Once verified a term can be made official by an admin. WARNING: Official terms cannot be made unofficial again.' error={error} disabled={!enabled} value={value}