From 6383d79ecf8bbf1ea42853a0f402f00f01ff1b10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Thu, 8 Apr 2021 12:40:55 +0200 Subject: [PATCH] Adding property validators --- .../src/components/propertyValueElement.tsx | 19 ++++++++++++ webapp/src/styles/variables.scss | 3 +- webapp/src/widgets/editable.scss | 5 ++++ webapp/src/widgets/editable.tsx | 29 ++++++++++++++++--- 4 files changed, 51 insertions(+), 5 deletions(-) diff --git a/webapp/src/components/propertyValueElement.tsx b/webapp/src/components/propertyValueElement.tsx index 5267fe73e..2b00c652a 100644 --- a/webapp/src/components/propertyValueElement.tsx +++ b/webapp/src/components/propertyValueElement.tsx @@ -29,6 +29,24 @@ const PropertyValueElement = (props:Props): JSX.Element => { const displayValue = OctoUtils.propertyDisplayValue(card, propertyValue, propertyTemplate) const finalDisplayValue = displayValue || emptyDisplayValue + const validateProp = (propType: string, val: string): boolean => { + if (val === '') { + return true + } + switch (propType) { + case 'number': + return !isNaN(parseInt(val, 10)) + case 'email': { + const emailRegexp = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ + return emailRegexp.test(val.toLowerCase()) + } + case 'text': + return true + default: + return false + } + } + if (propertyTemplate.type === 'select') { let propertyColorCssClassName = '' const cardPropertyValue = propertyTemplate.options.find((o) => o.id === propertyValue) @@ -85,6 +103,7 @@ const PropertyValueElement = (props:Props): JSX.Element => { onChange={setValue} onSave={() => mutator.changePropertyValue(card, propertyTemplate.id, value)} onCancel={() => setValue(propertyValue)} + validator={(value) => validateProp(propertyTemplate.type, value)} /> ) } diff --git a/webapp/src/styles/variables.scss b/webapp/src/styles/variables.scss index e26ff6bce..1a84ff7a0 100644 --- a/webapp/src/styles/variables.scss +++ b/webapp/src/styles/variables.scss @@ -8,6 +8,7 @@ --button-bg: 22, 109, 204; --link-color: 35, 137, 215; --link-visited-color: #551a8b; + --error-color: #ffa9a9; // Label Colors --prop-default: #fff; @@ -32,4 +33,4 @@ // Radius --default-rad: 4px; --modal-rad: 8px; -} \ No newline at end of file +} diff --git a/webapp/src/widgets/editable.scss b/webapp/src/widgets/editable.scss index d0bec1007..57444f25b 100644 --- a/webapp/src/widgets/editable.scss +++ b/webapp/src/widgets/editable.scss @@ -3,6 +3,7 @@ border: 0; overflow: hidden; text-overflow: ellipsis; + border: 1px solid transparent; &.active { min-width: 100px; } @@ -10,4 +11,8 @@ color: rgba(var(--body-color), 0.4); opacity: 1; } + &.error { + border: 1px solid var(--error-color); + border-radius: var(--default-rad); + } } diff --git a/webapp/src/widgets/editable.tsx b/webapp/src/widgets/editable.tsx index ba12d090e..ed28ddfc6 100644 --- a/webapp/src/widgets/editable.tsx +++ b/webapp/src/widgets/editable.tsx @@ -12,6 +12,7 @@ type Props = { saveOnEsc?: boolean readonly?: boolean + validator?: (value: string) => boolean onCancel?: () => void onSave?: (saveType: 'onEnter'|'onEsc'|'onBlur') => void } @@ -24,6 +25,22 @@ export default class Editable extends React.Component { return true } + save = (saveType: 'onEnter'|'onEsc'|'onBlur'): void => { + if (this.props.validator && !this.props.validator(this.props.value || '')) { + return + } + if (!this.props.onSave) { + return + } + if (saveType === 'onBlur' && !this.saveOnBlur) { + return + } + if (saveType === 'onEsc' && !this.props.saveOnEsc) { + return + } + this.props.onSave(saveType) + } + public focus(selectAll = false): void { if (this.elementRef.current) { const valueLength = this.elementRef.current.value.length @@ -44,30 +61,34 @@ export default class Editable extends React.Component { public render(): JSX.Element { const {value, onChange, className, placeholderText} = this.props + let error = false + if (this.props.validator) { + error = !this.props.validator(value || '') + } return ( ) => { onChange(e.target.value) }} value={value} title={value} - onBlur={() => this.saveOnBlur && this.props.onSave && this.props.onSave('onBlur')} + onBlur={() => this.save('onBlur')} onKeyDown={(e: React.KeyboardEvent): void => { if (e.keyCode === 27 && !(e.metaKey || e.ctrlKey) && !e.shiftKey && !e.altKey) { // ESC e.stopPropagation() if (this.props.saveOnEsc) { - this.props.onSave?.('onEsc') + this.save('onEsc') } else { this.props.onCancel?.() } this.blur() } else if (e.keyCode === 13 && !(e.metaKey || e.ctrlKey) && !e.shiftKey && !e.altKey) { // Return e.stopPropagation() - this.props.onSave?.('onEnter') + this.save('onEnter') this.blur() } }}