diff --git a/src/components/import-error/import-error.css b/src/components/import-error/import-error.css deleted file mode 100644 index 4ffac015f190e63252f9b6a242a949173de43bef..0000000000000000000000000000000000000000 --- a/src/components/import-error/import-error.css +++ /dev/null @@ -1,61 +0,0 @@ -/* - * NOTE: the copious use of `important` is needed to overwrite - * the default tooltip styling, and is required by the 3rd party - * library being used, `react-tooltip` - */ - -@import "../../css/colors.css"; - -.import-error { - background-color: $data-primary !important; - border: 1px solid hsla(0, 0%, 0%, .1) !important; - border-radius: .25rem !important; - box-shadow: 0 0 .5rem hsla(0, 0%, 0%, .25) !important; - padding: .75rem 1rem !important; - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif !important; - font-size: 1rem !important; - line-height: 1.25rem !important; - z-index: 100 !important; -} - -.import-error:after { - content: ""; - border-top: 1px solid hsla(0, 0%, 0%, .1) !important; - border-left: 0 !important; - border-bottom: 0 !important; - border-right: 1px solid hsla(0, 0%, 0%, .1) !important; - border-radius: .25rem; - background-color: $data-primary !important; - height: 1rem !important; - width: 1rem !important; -} - -.show, -.show:before, -.show:after { - opacity: 1 !important; -} - -.left:after { - margin-top: -.5rem !important; - right: -.5rem !important; - transform: rotate(45deg) !important; -} - -.right:after { - margin-top: -.5rem !important; - left: -.5rem !important; - transform: rotate(-135deg) !important; -} - -.top:after { - margin-right: -.5rem !important; - bottom: -.5rem !important; - transform: rotate(135deg) !important; -} - -.bottom:after { - margin-left: -.5rem !important; - top: -.5rem !important; - transform: rotate(-45deg) !important; -} diff --git a/src/components/import-error/import-error.jsx b/src/components/import-error/import-error.jsx deleted file mode 100644 index af677e4a26a3b59c4fc6f1179ee379ac2c3850df..0000000000000000000000000000000000000000 --- a/src/components/import-error/import-error.jsx +++ /dev/null @@ -1,131 +0,0 @@ -import bindAll from 'lodash.bindall'; -import classNames from 'classnames'; -import {defineMessages, injectIntl, FormattedMessage} from 'react-intl'; -import PropTypes from 'prop-types'; -import React from 'react'; -import ReactTooltip from 'react-tooltip'; - -import styles from './import-error.css'; - -// TODO store different error messages depending on the situation (?) and -// needs to use intl lib for localization support - -// TODO error tooltip should be always visible in the error state, -// instead of popping up hen hovering over the input - -const messages = defineMessages({ - invalidLink: { - id: 'gui.importError.invalidLink', - defaultMessage: 'Uh oh, that link doesn\'t look quite right.', - description: 'Invalid link error message' - } -}); - -class ImportErrorContent extends React.Component { - constructor (props) { - super(props); - bindAll(this, [ - 'setHide', - 'setShow', - 'getContent' - ]); - this.state = { - isShowing: true - }; - } - setShow () { - // needed to set the opacity to 1, since the default is .9 on show - this.setState({isShowing: true}); - } - setHide () { - this.setState({isShowing: false}); - } - getContent () { - const messageId = this.props.errorMessage; - return ( - <p> - <FormattedMessage - {...messages[`${messageId}`]} - /> - </p> - ); - } - render () { - return ( - <ReactTooltip - afterHide={this.setHide} - afterShow={this.setShow} - className={classNames( - styles.importError, - this.props.className, - { - [styles.show]: (this.state.isShowing), - [styles.left]: (this.props.place === 'left'), - [styles.right]: (this.props.place === 'right'), - [styles.top]: (this.props.place === 'top'), - [styles.bottom]: (this.props.place === 'bottom') - } - )} - getContent={this.getContent} - id={this.props.tooltipId} - /> - ); - } -} - -ImportErrorContent.propTypes = { - className: PropTypes.string, - errorMessage: PropTypes.string.isRequired, - place: PropTypes.oneOf(['top', 'right', 'bottom', 'left']), - tooltipId: PropTypes.string.isRequired - -}; - -ImportErrorContent.defaultProps = { - place: 'bottom' -}; - -const ImportError = injectIntl(ImportErrorContent); - -const ImportErrorTooltip = props => ( - <div className={props.className}> - <div - data-delay-hide={props.delayHide} - data-delay-show={props.delayShow} - data-effect="solid" - data-for={props.tooltipId} - data-place={props.place} - data-tip="tooltip" - > - {props.children} - </div> - <ImportError - className={props.tooltipClassName} - errorMessage={props.errorMessage} - place={props.place} - tooltipId={props.tooltipId} - /> - </div> -); - -ImportErrorTooltip.propTypes = { - children: PropTypes.node.isRequired, - className: PropTypes.string, - delayHide: PropTypes.number, - delayShow: PropTypes.number, - errorMessage: PropTypes.string.isRequired, - place: PropTypes.oneOf(['top', 'right', 'bottom', 'left']), - tooltipClassName: PropTypes.string, - tooltipId: PropTypes.string.isRequired - -}; - -ImportErrorTooltip.defaultProps = { - delayHide: 0, - delayShow: 0 -}; - -export { - ImportError as ImportErrorComponent, - ImportErrorTooltip -}; diff --git a/src/components/import-input/import-input.jsx b/src/components/import-input/import-input.jsx deleted file mode 100644 index 21ea902a0e38c5582026ff637f2d0698b2770e9c..0000000000000000000000000000000000000000 --- a/src/components/import-input/import-input.jsx +++ /dev/null @@ -1,58 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; - -import {ImportErrorTooltip} from '../import-error/import-error.jsx'; - - -// TODO error tooltip needs to go around both the input and the button -// error tooltip should also be always visible -class ImportInput extends React.Component { - render () { - let input = null; - if (this.props.hasValidationError) { - input = ( - <ImportErrorTooltip - className={this.props.errorDivClassName} - errorMessage={this.props.errorMessage} - place="bottom" - tooltipId="import-input-error" - > - <input - autoFocus - className={this.props.badClassName} - placeholder={this.props.placeholder} - value={this.props.inputValue} - onChange={this.props.onChange} - onKeyPress={this.props.onKeyPress} - /> - </ImportErrorTooltip> - ); - } else { - input = ( - <input - autoFocus - className={this.props.okClassName} - placeholder={this.props.placeholder} - value={this.props.inputValue} - onChange={this.props.onChange} - onKeyPress={this.props.onKeyPress} - /> - ); - } - return input; - } -} - -ImportInput.propTypes = { - badClassName: PropTypes.string, - errorDivClassName: PropTypes.string, - errorMessage: PropTypes.string.isRequired, - hasValidationError: PropTypes.bool.isRequired, - inputValue: PropTypes.string.isRequired, - okClassName: PropTypes.string, - onChange: PropTypes.func.isRequired, - onKeyPress: PropTypes.func.isRequired, - placeholder: PropTypes.string -}; - -export default ImportInput; diff --git a/src/components/import-modal/import-modal.css b/src/components/import-modal/import-modal.css index 8dd6f80ba44286c4995083115c41700fe0768eff..8ffbcb3c6909a864de9f9dd0778f15be8aff98fe 100644 --- a/src/components/import-modal/import-modal.css +++ b/src/components/import-modal/import-modal.css @@ -98,12 +98,6 @@ $sides: 20rem; justify-content: center; } -.input-row div.error-div { - margin: 0; - width: 100%; - padding: 0; -} - .input-row input { width: 100%; padding: 0 1rem; @@ -117,13 +111,13 @@ $sides: 20rem; color: rgba(87,94,117,0.5); } -.input-row input.ok-input:focus { +.input-row input.ok-input { outline: none; border: 1px solid $motion-primary; border-radius: 0.25rem } -.input-row input.bad-input:focus { +.input-row input.bad-input { outline: none; border: 1px solid $data-primary; border-radius: 0.25rem @@ -136,6 +130,7 @@ $sides: 20rem; cursor: pointer; border: 1px solid $import-primary; border-radius: 0.25rem; + outline: none; } .input-row button.ok-button { @@ -143,6 +138,26 @@ $sides: 20rem; color: white; } +.empty-row { + margin: 0; +} + +.error-row { + margin: 1.5rem 0; + text-align: center; + display: flex; + justify-content: center; +} +.error-row div { + background: $data-primary; + opacity: 0.8; + color: white; + padding: 0.5rem 1rem; + font-size: .5rem; + border: 1px solid $data-primary; + border-radius: 0.25rem; +} + /* Confirmation buttons at the bottom of the modal */ .button-row { margin: 1.5rem 0; diff --git a/src/components/import-modal/import-modal.jsx b/src/components/import-modal/import-modal.jsx index 03a1aef8f0764b4f42b640e914483e3931962450..87c396109ed4f671cfd4eba252ebaf63cb6bef69 100644 --- a/src/components/import-modal/import-modal.jsx +++ b/src/components/import-modal/import-modal.jsx @@ -6,7 +6,6 @@ import {defineMessages, injectIntl, intlShape, FormattedMessage} from 'react-int import classNames from 'classnames'; import CloseButton from '../close-button/close-button.jsx'; -import ImportInput from '../import-input/import-input.jsx'; import styles from './import-modal.css'; @@ -21,6 +20,11 @@ const messages = defineMessages({ 'Enter a link to one of your shared Scratch projects. Changes made in this 3.0 Preview will not be saved.', description: 'Import project message', id: 'gui.importInfo.message' + }, + invalidFormatError: { + id: 'gui.importInfo.invalidFormatError', + defaultMessage: 'Uh oh, that project link or id doesn\'t look quite right.', + description: 'Invalid project link or id message' } }); @@ -65,16 +69,12 @@ const ImportModal = ({intl, ...props}) => ( <p> {intl.formatMessage({...messages.formDescription})} </p> - <Box className={styles.inputRow}> - <ImportInput - badClassName={styles.badInput} - errorDivClassName={styles.errorDiv} - errorMessage={props.errorMessage} - hasValidationError={props.hasValidationError} - inputValue={props.inputValue} - okClassName={styles.okInput} + <input + autoFocus + className={props.hasValidationError ? styles.badInput : styles.okInput} placeholder={props.placeholder} + value={props.inputValue} onChange={props.onChange} onKeyPress={props.onKeyPress} /> @@ -90,6 +90,17 @@ const ImportModal = ({intl, ...props}) => ( /> </button> </Box> + <Box className={props.hasValidationError ? styles.errorRow : styles.emptyRow}> + {props.hasValidationError ? + <div className={styles.importErrorDiv}> + <p> + {/* intl.formatMessage({...messages.invalidLink})*/} + <FormattedMessage + {...messages[`${props.errorMessage}`]} + /> + </p> + </div> : null} + </Box> <Box className={styles.buttonRow}> <button className={styles.noButton} diff --git a/src/containers/import-modal.jsx b/src/containers/import-modal.jsx index e3112da072c1808dd06d011d29216fbab2bb1ef4..6bc667440a05c139e91cefff098e682b3e705087 100644 --- a/src/containers/import-modal.jsx +++ b/src/containers/import-modal.jsx @@ -43,11 +43,11 @@ class ImportModal extends React.Component { // TODO handle error messages and error states this.setState({ hasValidationError: true, - errorMessage: `invalidLink`}); + errorMessage: `invalidFormatError`}); } } handleChange (e) { - this.setState({inputValue: e.target.value}); + this.setState({inputValue: e.target.value, hasValidationError: false}); } validate (input) { const urlMatches = input.match(/^(https:\/\/)?scratch\.mit\.edu\/projects\/(\d+)(\/?|(\/#editor)?)$/);