From 7ae9d2d92f632ccdd5255648d5c5ca6eddd0891f Mon Sep 17 00:00:00 2001 From: Emilien Date: Mon, 7 Nov 2016 16:30:05 +0100 Subject: [PATCH 01/50] Update memory-scroll.js --- src/list/mixin/memory-scroll.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/list/mixin/memory-scroll.js b/src/list/mixin/memory-scroll.js index 98b397d69..39ea6ec79 100644 --- a/src/list/mixin/memory-scroll.js +++ b/src/list/mixin/memory-scroll.js @@ -15,6 +15,14 @@ let memoryMixin = { maxElements: this.props.perPage }; }, + + componentWillReceiveProps(nextProps) { + if(nextProps.perPage && nextProps.perPage !== this.props.perPage) { + this.setState({ + maxElements: nextProps.perPage + }); + } + }, /** * Calculate the number of element to display in the memory list. From 55f8f6b63c4517339524202f32dbb02aad566929 Mon Sep 17 00:00:00 2001 From: GuenoleK Date: Wed, 28 Dec 2016 11:59:16 +0100 Subject: [PATCH 02/50] Starting to list all react supported html attributes --- src/common/react-html-attributes/index.js | 130 ++++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 src/common/react-html-attributes/index.js diff --git a/src/common/react-html-attributes/index.js b/src/common/react-html-attributes/index.js new file mode 100644 index 000000000..bbf6ffe9d --- /dev/null +++ b/src/common/react-html-attributes/index.js @@ -0,0 +1,130 @@ +export const inputHtmlAttributes { + accept, + alt, + autoComplete, + autoFocus, + checked, + dir, + disabled, + form, + formAction, + formEncType, + formMethod, + formNoValidate, + formTarget, + height, + list, + max, + maxLength, + min, + multiple, + name, + pattern, + placeholder, + readOnly, + required, + size, + src, + step, + type, + value, + width, +} + +export const otherHtmlAttributes { + acceptCharset, + accessKey, + action, + allowFullScreen, + allowTransparency, + async, + autoPlay, + capture, + cellPadding, + cellSpacing, + challenge + charSet, + cite, + classID, + className, + colSpan, + cols, + content, + contentEditable, + contextMenu, + controls, + coords, + crossOrigin, + data, + dateTime, + default, + defer, + download, + draggable, + encType, + frameBorder, + headers, + hidden, + high, + href, + hrefLang, + htmlFor, + httpEquiv, + icon, + id, + inputMode, + integrity, + is, + keyParams, + keyType, + kind, + label, + lang, + loop, + low, + manifest, + marginHeight, + marginWidth, + media, + mediaGroup, + method, + minLength, + muted, + noValidate, + nonce, + open, + optimum, + poster, + preload, + profile, + radioGroup, + rel, + reversed, + role, + rowSpan, + rows, + sandbox, + scope, + scoped, + scrolling, + seamless, + selected, + shape, + sizes, + span, + spellCheck, + srcDoc, + srcLang, + srcSet, + start, + step, + style, + summary, + tabIndex, + target, + title, + type, + useMap, + wmode, + wrap +} From 4cba25b4a88fb8a22175ca76d86a09eb3eb92648 Mon Sep 17 00:00:00 2001 From: GuenoleK Date: Wed, 28 Dec 2016 12:00:33 +0100 Subject: [PATCH 03/50] Adding missing coma --- src/common/react-html-attributes/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/react-html-attributes/index.js b/src/common/react-html-attributes/index.js index bbf6ffe9d..187c406bb 100644 --- a/src/common/react-html-attributes/index.js +++ b/src/common/react-html-attributes/index.js @@ -42,7 +42,7 @@ export const otherHtmlAttributes { capture, cellPadding, cellSpacing, - challenge + challenge, charSet, cite, classID, From fdb786caab5dea44b80834c7e985dc2bb46a873e Mon Sep 17 00:00:00 2001 From: GuenoleK Date: Wed, 28 Dec 2016 16:43:30 +0100 Subject: [PATCH 04/50] preparing props comparisons --- src/common/react-html-attributes/index.js | 258 +++++++++++----------- src/components/input/text/index.js | 10 +- 2 files changed, 138 insertions(+), 130 deletions(-) diff --git a/src/common/react-html-attributes/index.js b/src/common/react-html-attributes/index.js index 187c406bb..fc92fbec4 100644 --- a/src/common/react-html-attributes/index.js +++ b/src/common/react-html-attributes/index.js @@ -1,130 +1,130 @@ -export const inputHtmlAttributes { - accept, - alt, - autoComplete, - autoFocus, - checked, - dir, - disabled, - form, - formAction, - formEncType, - formMethod, - formNoValidate, - formTarget, - height, - list, - max, - maxLength, - min, - multiple, - name, - pattern, - placeholder, - readOnly, - required, - size, - src, - step, - type, - value, - width, -} +export const inputHtmlAttributes = [ + "accept", + "alt", + "autoComplete", + "autoFocus", + "checked", + "dir", + "disabled", + "form", + "formAction", + "formEncType", + "formMethod", + "formNoValidate", + "formTarget", + "height", + "list", + "max", + "maxLength", + "min", + "multiple", + "name", + "pattern", + "placeholder", + "readOnly", + "required", + "size", + "src", + "step", + "type", + "value", + "width" +] -export const otherHtmlAttributes { - acceptCharset, - accessKey, - action, - allowFullScreen, - allowTransparency, - async, - autoPlay, - capture, - cellPadding, - cellSpacing, - challenge, - charSet, - cite, - classID, - className, - colSpan, - cols, - content, - contentEditable, - contextMenu, - controls, - coords, - crossOrigin, - data, - dateTime, - default, - defer, - download, - draggable, - encType, - frameBorder, - headers, - hidden, - high, - href, - hrefLang, - htmlFor, - httpEquiv, - icon, - id, - inputMode, - integrity, - is, - keyParams, - keyType, - kind, - label, - lang, - loop, - low, - manifest, - marginHeight, - marginWidth, - media, - mediaGroup, - method, - minLength, - muted, - noValidate, - nonce, - open, - optimum, - poster, - preload, - profile, - radioGroup, - rel, - reversed, - role, - rowSpan, - rows, - sandbox, - scope, - scoped, - scrolling, - seamless, - selected, - shape, - sizes, - span, - spellCheck, - srcDoc, - srcLang, - srcSet, - start, - step, - style, - summary, - tabIndex, - target, - title, - type, - useMap, - wmode, - wrap -} +export const otherHtmlAttributes = [ + "acceptCharset", + "accessKey", + "action", + "allowFullScreen", + "allowTransparency", + "async", + "autoPlay", + "capture", + "cellPadding", + "cellSpacing", + "challenge", + "charSet", + "cite", + "classID", + "className", + "colSpan", + "cols", + "content", + "contentEditable", + "contextMenu", + "controls", + "coords", + "crossOrigin", + "data", + "dateTime", + "default", + "defer", + "download", + "draggable", + "encType", + "frameBorder", + "headers", + "hidden", + "high", + "href", + "hrefLang", + "htmlFor", + "httpEquiv", + "icon", + "id", + "inputMode", + "integrity", + "is", + "keyParams", + "keyType", + "kind", + "label", + "lang", + "loop", + "low", + "manifest", + "marginHeight", + "marginWidth", + "media", + "mediaGroup", + "method", + "minLength", + "muted", + "noValidate", + "nonce", + "open", + "optimum", + "poster", + "preload", + "profile", + "radioGroup", + "rel", + "reversed", + "role", + "rowSpan", + "rows", + "sandbox", + "scope", + "scoped", + "scrolling", + "seamless", + "selected", + "shape", + "sizes", + "span", + "spellCheck", + "srcDoc", + "srcLang", + "srcSet", + "start", + "step", + "style", + "summary", + "tabIndex", + "target", + "title", + "type", + "useMap", + "wmode", + "wrap" +] diff --git a/src/components/input/text/index.js b/src/components/input/text/index.js index 16b2499f1..2d1dfd1ad 100644 --- a/src/components/input/text/index.js +++ b/src/components/input/text/index.js @@ -5,6 +5,7 @@ import {identity} from 'lodash/utility'; import ComponentBaseBehaviour from '../../../behaviours/component-base'; import MDBehaviour from '../../../behaviours/material'; const MODE = {isEdit: true}; +import {inputHtmlAttributes} from '../../../common/react-html-attributes'; const propTypes = { disabled: PropTypes.bool, @@ -68,7 +69,14 @@ class InputText extends Component { * @override */ render() { - const { autoFocus, onBlur, disabled, formatter, unformatter, maxLength, onFocus, onClick, onKeyDown, onKeyPress, error, name, placeholder, style, value: rawValue, size, type} = this.props; + const a = [100, 200, 300] + // console.log(a.indexOf(200)); + inputHtmlAttributes.filter( (e, index) => { + if(this.props[e]) { + console.log(this.props[e]); + } + }); + const { autoFocus, disabled, formatter, unformatter, maxLength, onFocus, onClick, onKeyDown, onKeyPress, error, name, placeholder, style, value: rawValue, size, type} = this.props; const value = formatter(rawValue, MODE); const pattern = error ? 'hasError' : null; //add pattern to overide mdl error style when displaying an focus error. const inputProps = { autoFocus, disabled, onKeyDown, onKeyPress, onBlur, maxLength, onFocus, onClick, id: name, onChange: this._handleInputChange, pattern, size, type, value: value === undefined || value === null ? '' : value}; From 52a496d8ce891def3ae877afe8a75adc90ce5e10 Mon Sep 17 00:00:00 2001 From: GuenoleK Date: Wed, 25 Jan 2017 17:49:31 +0100 Subject: [PATCH 05/50] Checking the props and seperate recognized input props from unrecognized props on InputText --- src/common/mixin/field-component-behaviour.js | 12 +- src/common/react-html-attributes/index.js | 160 ++++-------------- src/components/input/text/index.js | 52 ++++-- 3 files changed, 81 insertions(+), 143 deletions(-) diff --git a/src/common/mixin/field-component-behaviour.js b/src/common/mixin/field-component-behaviour.js index 1b3a55f5e..ec29b72a0 100644 --- a/src/common/mixin/field-component-behaviour.js +++ b/src/common/mixin/field-component-behaviour.js @@ -1,10 +1,10 @@ import assign from 'object-assign'; import {isUndefined, isObject} from 'lodash/lang'; /** - * Identity function - * @param {object} d - data to treat. - * @return {object} - The same object. - */ +* Identity function +* @param {object} d - data to treat. +* @return {object} - The same object. +*/ function identity(d) { return d; } @@ -50,8 +50,8 @@ const fieldBehaviourMixin = { isRequired: (!isUndefined(options.isRequired) && options.isRequired) || def.isRequired || def.required, //legacy on required on model generation. //Style style: options.style, - // Type - type: def.type, + // Type + type: def.type, //Methods validator: def.validator, formatter: def.formatter || identity, diff --git a/src/common/react-html-attributes/index.js b/src/common/react-html-attributes/index.js index fc92fbec4..b3b905dd1 100644 --- a/src/common/react-html-attributes/index.js +++ b/src/common/react-html-attributes/index.js @@ -1,130 +1,36 @@ export const inputHtmlAttributes = [ - "accept", - "alt", - "autoComplete", - "autoFocus", - "checked", - "dir", - "disabled", - "form", - "formAction", - "formEncType", - "formMethod", - "formNoValidate", - "formTarget", - "height", - "list", - "max", - "maxLength", - "min", - "multiple", - "name", - "pattern", - "placeholder", - "readOnly", - "required", - "size", - "src", - "step", - "type", - "value", - "width" -] - -export const otherHtmlAttributes = [ - "acceptCharset", - "accessKey", - "action", - "allowFullScreen", - "allowTransparency", - "async", - "autoPlay", - "capture", - "cellPadding", - "cellSpacing", - "challenge", - "charSet", - "cite", - "classID", - "className", - "colSpan", - "cols", - "content", - "contentEditable", - "contextMenu", - "controls", - "coords", - "crossOrigin", - "data", - "dateTime", - "default", - "defer", - "download", - "draggable", - "encType", - "frameBorder", - "headers", - "hidden", - "high", - "href", - "hrefLang", - "htmlFor", - "httpEquiv", - "icon", - "id", - "inputMode", - "integrity", - "is", - "keyParams", - "keyType", - "kind", - "label", - "lang", - "loop", - "low", - "manifest", - "marginHeight", - "marginWidth", - "media", - "mediaGroup", - "method", - "minLength", - "muted", - "noValidate", - "nonce", - "open", - "optimum", - "poster", - "preload", - "profile", - "radioGroup", - "rel", - "reversed", - "role", - "rowSpan", - "rows", - "sandbox", - "scope", - "scoped", - "scrolling", - "seamless", - "selected", - "shape", - "sizes", - "span", - "spellCheck", - "srcDoc", - "srcLang", - "srcSet", - "start", - "step", - "style", - "summary", - "tabIndex", - "target", - "title", - "type", - "useMap", - "wmode", + "accept", "alt", "autoComplete", "autoFocus", "checked", "dir", + "disabled", "form", "formAction", "formEncType", "formMethod", + "formNoValidate", "formTarget", "height", "label", "list", "max", + "maxLength", "min", "multiple", "name", "onCopy", "onCut", + "onPast", "onKeyDown", "onKeyPress", "onKeyUp", "onCompositionEnd", + "onCompositionStart", "onCompositionUpdate", "onFocus", "onBlur", + "onChange", "onInput", "onSubmit", "onInput", "onClick", "onContextMenu", + "onDoubleClick", "onDrag", "onDragEnd", "onDragEnter", "onDragExit", + "onDragLeave", "onDragOver", "onDragStart", "onDrop", "onMouseDown", + "onMouseEnter", "onMouseLeave", "onMouseMove", "onMouseOut", + "onMouseOver", "onMouseUp", "onSelect", "onAbort", "onCanPlay", + "onCanPlayThrough", "onDurationChange", "onEmptied", "onEncrypted", + "onEnded", "onError", "onLoadedData", "onLoadedMetadata", + "onLoadStart", "onPause", "onPlay", "onPlaying", "onProgress", + "onRateChange", "onSeeked", "onSeeking", "onStalled", "onSuspend", + "onTimeUpdate", "onVolumeChange", "onWaiting", "onLoad", "onError", + "onAnimationStart", "onAnimationEnd", "onAnimationIteration", + "onTransitionEnd", "pattern", "placeholder", "readOnly", "required", + "size", "src", "step", "type", "value", "width", "acceptCharset", + "accessKey", "action", "allowFullScreen", + "allowTransparency", "async", "autoPlay", "capture", "cellPadding", + "cellSpacing", "challenge", "charSet", "cite", "classID", "className", + "colSpan", "cols", "content", "contentEditable", "contextMenu", "controls", + "coords", "crossOrigin", "data", "dateTime", "default", "defer", "download", + "draggable", "encType", "frameBorder", "headers", "hidden", "high", "href", + "hrefLang", "htmlFor", "httpEquiv", "icon", "id", "inputMode", "integrity", + "is", "keyParams", "keyType", "kind", "lang", "loop", "low", "manifest", + "marginHeight", "marginWidth", "media", "mediaGroup", "method", "minLength", + "muted", "noValidate", "nonce", "open", "optimum", "poster", "preload", + "profile", "radioGroup", "rel", "reversed", "role", "rowSpan", "rows", + "sandbox", "scope", "scoped", "scrolling", "seamless", "selected", "shape", + "sizes", "span", "spellCheck", "srcDoc", "srcLang", "srcSet", "start", "step", + "style", "summary", "tabIndex", "target", "title", "type", "useMap", "wmode", "wrap" ] diff --git a/src/components/input/text/index.js b/src/components/input/text/index.js index 2d1dfd1ad..e6bdf44c6 100644 --- a/src/components/input/text/index.js +++ b/src/components/input/text/index.js @@ -64,23 +64,55 @@ class InputText extends Component { const {value} = evt.target; return onChange(unformatter(value, MODE)); }; + + /** + * comments will be right there + */ + _checkProps = (props) => { + const values = Object.values(props); + let validInputProps = {}; + let invalidInputProps = {}; + for(let key in props) { + const returnedValue = inputHtmlAttributes.map((e, index) => { + if(e === key) { + let value; + switch (key) { + case 'value': + value = props.formatter(props[key], MODE); + break; + case 'onChange': + value = this._handleInputChange; + break; + default: + value = props[key] + } + validInputProps[`${key}`] = value; + } else { + invalidInputProps[`${key}`] = props[key]; + } + }) + } + const managedProps = [validInputProps, invalidInputProps]; + return managedProps; + }; /** * @inheritdoc * @override */ render() { - const a = [100, 200, 300] - // console.log(a.indexOf(200)); - inputHtmlAttributes.filter( (e, index) => { - if(this.props[e]) { - console.log(this.props[e]); - } - }); - const { autoFocus, disabled, formatter, unformatter, maxLength, onFocus, onClick, onKeyDown, onKeyPress, error, name, placeholder, style, value: rawValue, size, type} = this.props; - const value = formatter(rawValue, MODE); + const managedProps = this._checkProps(this.props); + + const validInputProps = managedProps[0]; + const invalidInputProps = managedProps[1]; + + const { name, placeholder } = validInputProps; + const { error, style } = invalidInputProps; + const pattern = error ? 'hasError' : null; //add pattern to overide mdl error style when displaying an focus error. - const inputProps = { autoFocus, disabled, onKeyDown, onKeyPress, onBlur, maxLength, onFocus, onClick, id: name, onChange: this._handleInputChange, pattern, size, type, value: value === undefined || value === null ? '' : value}; + + const inputProps = validInputProps; const cssClass = `mdl-textfield mdl-js-textfield${error ? ' is-invalid' : ''}`; + return (
From 75b4dd671b72176b818a568563052151cba5d761 Mon Sep 17 00:00:00 2001 From: GuenoleK Date: Wed, 25 Jan 2017 18:09:59 +0100 Subject: [PATCH 06/50] Updating the input text to make tests work again --- src/components/input/text/__tests__/input-text-test.js | 2 +- src/components/input/text/index.js | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/components/input/text/__tests__/input-text-test.js b/src/components/input/text/__tests__/input-text-test.js index c20ff1e3b..4baa16a51 100644 --- a/src/components/input/text/__tests__/input-text-test.js +++ b/src/components/input/text/__tests__/input-text-test.js @@ -133,7 +133,7 @@ describe('The input text', () => { expect(htmlError).to.exist; expect(htmlError.innerHTML).to.have.string(error); }); - it('input should have a pattern attribute', () => { + it.only('input should have a pattern attribute', () => { expect(htmlInput).to.exist; expect(htmlInput.getAttribute('pattern')).to.equal('hasError'); }); diff --git a/src/components/input/text/index.js b/src/components/input/text/index.js index e6bdf44c6..75bc6d06e 100644 --- a/src/components/input/text/index.js +++ b/src/components/input/text/index.js @@ -69,7 +69,9 @@ class InputText extends Component { * comments will be right there */ _checkProps = (props) => { - const values = Object.values(props); + const values = Object.keys(props).map( value => { + return props[value] + }); let validInputProps = {}; let invalidInputProps = {}; for(let key in props) { @@ -109,8 +111,8 @@ class InputText extends Component { const { error, style } = invalidInputProps; const pattern = error ? 'hasError' : null; //add pattern to overide mdl error style when displaying an focus error. - - const inputProps = validInputProps; + + const inputProps = {...validInputProps, pattern}; const cssClass = `mdl-textfield mdl-js-textfield${error ? ' is-invalid' : ''}`; return ( From 9b09170235131f4f726fc6f3af180683c0c56f7d Mon Sep 17 00:00:00 2001 From: GuenoleK Date: Wed, 25 Jan 2017 18:11:30 +0100 Subject: [PATCH 07/50] Removing .only --- src/components/input/text/__tests__/input-text-test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/input/text/__tests__/input-text-test.js b/src/components/input/text/__tests__/input-text-test.js index 4baa16a51..c20ff1e3b 100644 --- a/src/components/input/text/__tests__/input-text-test.js +++ b/src/components/input/text/__tests__/input-text-test.js @@ -133,7 +133,7 @@ describe('The input text', () => { expect(htmlError).to.exist; expect(htmlError.innerHTML).to.have.string(error); }); - it.only('input should have a pattern attribute', () => { + it('input should have a pattern attribute', () => { expect(htmlInput).to.exist; expect(htmlInput.getAttribute('pattern')).to.equal('hasError'); }); From a500b11f7966dbf6e6f9b4de8290d38706eee7ee Mon Sep 17 00:00:00 2001 From: GuenoleK Date: Sat, 28 Jan 2017 23:24:57 +0100 Subject: [PATCH 08/50] Refactoring the code and fixing the fact the valid props were also in the invalid props --- src/components/input/text/index.js | 58 ++++++++++++------------------ 1 file changed, 22 insertions(+), 36 deletions(-) diff --git a/src/components/input/text/index.js b/src/components/input/text/index.js index 75bc6d06e..9f42736ab 100644 --- a/src/components/input/text/index.js +++ b/src/components/input/text/index.js @@ -31,16 +31,16 @@ const defaultProps = { }; /** - * Component standing for an HTML input. - */ +* Component standing for an HTML input. +*/ @MDBehaviour('inputText') @ComponentBaseBehaviour class InputText extends Component { /** - * Get the dom value of the component. - * @return {object} - The unformated dom value. - */ + * Get the dom value of the component. + * @return {object} - The unformated dom value. + */ getValue = () => { const {unformatter} = this.props; const domEl = ReactDOM.findDOMNode(this.refs.htmlInput); @@ -55,10 +55,10 @@ class InputText extends Component { } } /** - * Handle the change on the input text, it only propagate the value. - * @param {object} evt - The react DOM event. - * @return {object} - The function onChannge from the props, called. - */ + * Handle the change on the input text, it only propagate the value. + * @param {object} evt - The react DOM event. + * @return {object} - The function onChannge from the props, called. + */ _handleInputChange = (evt) => { const {unformatter, onChange} = this.props; const {value} = evt.target; @@ -66,40 +66,26 @@ class InputText extends Component { }; /** - * comments will be right there - */ + * comments will be right there + */ _checkProps = (props) => { - const values = Object.keys(props).map( value => { - return props[value] - }); let validInputProps = {}; let invalidInputProps = {}; - for(let key in props) { - const returnedValue = inputHtmlAttributes.map((e, index) => { - if(e === key) { - let value; - switch (key) { - case 'value': - value = props.formatter(props[key], MODE); - break; - case 'onChange': - value = this._handleInputChange; - break; - default: - value = props[key] - } - validInputProps[`${key}`] = value; - } else { - invalidInputProps[`${key}`] = props[key]; - } - }) - } + + Object.keys(props).map(key => { + if(key === inputHtmlAttributes[inputHtmlAttributes.indexOf(key)]) { + let value = key === 'value' ? props.formatter(props[key], MODE) : key === 'onChange' ? this._handleInputChange : props[key]; + validInputProps[key] = value; + } else { + invalidInputProps[key] = props[key]; + } + }); const managedProps = [validInputProps, invalidInputProps]; return managedProps; }; /** - * @inheritdoc - * @override + * @inheritdoc + * @override */ render() { const managedProps = this._checkProps(this.props); From a14d1af4da09da1389fd9b9f4d4df67c8abd3789 Mon Sep 17 00:00:00 2001 From: GuenoleK Date: Wed, 8 Feb 2017 19:53:04 +0100 Subject: [PATCH 09/50] Adding decorator to input components --- src/behaviours/input-component/index.js | 22 +++++++ .../input/autocomplete-select/edit.js | 23 +++++-- .../input/autocomplete-text/edit.js | 66 +++++++------------ src/components/input/checkbox/index.js | 16 ++++- src/components/input/radio/index.js | 19 ++++-- src/components/input/select/index.js | 16 ++++- src/components/input/text/index.js | 47 +++++++------ src/components/input/textarea/index.js | 18 ++++- src/components/input/toggle/index.js | 13 +++- 9 files changed, 153 insertions(+), 87 deletions(-) create mode 100644 src/behaviours/input-component/index.js diff --git a/src/behaviours/input-component/index.js b/src/behaviours/input-component/index.js new file mode 100644 index 000000000..058a0c343 --- /dev/null +++ b/src/behaviours/input-component/index.js @@ -0,0 +1,22 @@ +import {inputHtmlAttributes} from '../../common/react-html-attributes'; + +export const InputBehaviour = Component => class InputComponent extends Component { + + /** + * comments will be right there + */ + _checkProps(props) { + let validInputProps = {}; + let invalidInputProps = {}; + + Object.keys(props).map(key => { + if(key === inputHtmlAttributes[inputHtmlAttributes.indexOf(key)]) { + validInputProps[key] = (key === 'value' && (props[key] === null || props[key] === undefined)) ? '' : props[key]; + } else { + invalidInputProps[key] = props[key]; + } + }); + const managedProps = [validInputProps, invalidInputProps]; + return managedProps; + }; +}; diff --git a/src/components/input/autocomplete-select/edit.js b/src/components/input/autocomplete-select/edit.js index e28458eb1..e57cf10c1 100644 --- a/src/components/input/autocomplete-select/edit.js +++ b/src/components/input/autocomplete-select/edit.js @@ -2,6 +2,7 @@ import React, {Component, PropTypes} from 'react'; import ReactDOM from 'react-dom'; import ComponentBaseBehaviour from '../../../behaviours/component-base'; import MDBehaviour from '../../../behaviours/material'; +import {InputBehaviour} from '../../../behaviours/input-component'; import closest from 'closest'; import debounce from 'lodash/function/debounce'; @@ -35,6 +36,7 @@ const defaultProps = { @MDBehaviour('loader') @MDBehaviour('inputText') @ComponentBaseBehaviour +@InputBehaviour class Autocomplete extends Component { constructor(props) { super(props); @@ -213,15 +215,22 @@ class Autocomplete extends Component { }; render () { - const { autoFocus, onBlur, disabled, onKeyPress, maxLength, onFocus, onClick, customError, placeholder, renderOptions, ...otherProps } = this.props; const {inputValue, isLoading} = this.state; + const {customError, renderOptions} = this.props; const {_handleQueryFocus, _handleQueryKeyDown, _handleQueryChange} = this; - const inputProps = { - autoFocus, disabled, onKeyPress, maxLength, onFocus, onClick, - onChange: _handleQueryChange, onFocus: _handleQueryFocus, - onKeyDown: _handleQueryKeyDown, onBlur, - value: inputValue === undefined || inputValue === null ? '' : inputValue - }; + + const managedProps = this._checkProps(this.props); + + const validInputProps = managedProps[0]; + const invalidInputProps = managedProps[1] + + const { name, placeholder, value: valueToFormat } = validInputProps; + + validInputProps.value = inputValue; + validInputProps.onFocus = this._handleQueryFocus; + validInputProps.onChange = this._handleQueryChange; + + const inputProps = {...validInputProps}; return (
diff --git a/src/components/input/autocomplete-text/edit.js b/src/components/input/autocomplete-text/edit.js index 1639b36c0..78bf36b50 100644 --- a/src/components/input/autocomplete-text/edit.js +++ b/src/components/input/autocomplete-text/edit.js @@ -2,12 +2,14 @@ import React, {Component, PropTypes} from 'react'; import ReactDOM from 'react-dom'; import ComponentBaseBehaviour from '../../../behaviours/component-base'; import MDBehaviour from '../../../behaviours/material'; +import {InputBehaviour} from '../../../behaviours/input-component'; import debounce from 'lodash/function/debounce'; @MDBehaviour('materialInput') @MDBehaviour('loader') @ComponentBaseBehaviour +@InputBehaviour class AutocompleteTextEdit extends Component { static defaultProps = { placeholder: 'Search here...', @@ -60,9 +62,9 @@ class AutocompleteTextEdit extends Component { emptyShowAll: PropTypes.bool, /** - * [inputTimeout description] - * @type {number} - */ + * [inputTimeout description] + * @type {number} + */ inputTimeout : PropTypes.number.isRequired }; @@ -82,8 +84,8 @@ class AutocompleteTextEdit extends Component { } componentDidMount() { - const {inputTimeout} = this.props; - this._debouncedQuerySearcher = debounce(this._querySearcher, inputTimeout); + const {inputTimeout} = this.props; + this._debouncedQuerySearcher = debounce(this._querySearcher, inputTimeout); } // Returns the state's inputValue @@ -165,13 +167,26 @@ class AutocompleteTextEdit extends Component { // Maybe give the option for the floating label render() { - const {inputValue, hasSuggestions, hasFocus, isLoading, ...otherProps} = this.state; - const {placeholder, inputTimeout, showAtFocus, emptyShowAll, error} = this.props + const {inputValue, hasSuggestions, hasFocus, isLoading} = this.state; + + const managedProps = this._checkProps(this.props); + const validInputProps = managedProps[0]; + const invalidInputProps = managedProps[1]; + + const {inputTimeout, error} = invalidInputProps; + const {placeholder} = validInputProps; + + validInputProps.value = inputValue === undefined || inputValue === null ? '' : inputValue; + validInputProps.onFocus = this.toggleHasFocus; + validInputProps.onChange = this.onQueryChange; + validInputProps.onBlur = this.toggleHasFocus; + const inputProps = {...validInputProps}; + return(
- + {this.i18n(error)}
@@ -184,38 +199,3 @@ class AutocompleteTextEdit extends Component { } export default AutocompleteTextEdit; - -/* -EXAMPLE -const _querySearcher = query => { - let data = [ - { - key: 'JL', - label: 'Joh Lickeur' - }, - { - key: 'GK', - label: 'Guénolé Kikabou' - }, - { - key: 'YL', - label: 'Yannick Lounivis' - } - ]; - return new Promise((resolve, reject) => { - setTimeout(() => { - resolve({ - data, - totalCount: data.length - }); - }, 500); - }); -}; - - -*/ diff --git a/src/components/input/checkbox/index.js b/src/components/input/checkbox/index.js index 3a20fff27..c7d4d79b1 100644 --- a/src/components/input/checkbox/index.js +++ b/src/components/input/checkbox/index.js @@ -2,6 +2,7 @@ import React, {Component, PropTypes} from 'react'; import ReactDOM from 'react-dom'; import Translation from '../../../behaviours/translation'; import Material from '../../../behaviours/material'; +import {InputBehaviour} from '../../../behaviours/input-component'; const propTypes = { label: PropTypes.string, @@ -19,6 +20,7 @@ const displayName = 'InputCheckBox'; @Translation @Material('mdlHolder') +@InputBehaviour class InputCheckBox extends Component { getValue = () => { const domElement = ReactDOM.findDOMNode(this.refs.checkbox); @@ -34,17 +36,25 @@ class InputCheckBox extends Component { } } - handleOnChange({target: {checked}}) { + handleOnChange = ({target: {checked}}) => { const {onChange} = this.props; onChange(checked); } render() { - const {label, value, disabled} = this.props; + const managedProps = this._checkProps(this.props); + const validInputProps = managedProps[0]; + const invalidInputProps = managedProps[1]; + + const {label, value, disabled} = validInputProps; + + validInputProps.onChange = this.handleOnChange; + const inputProps = {...validInputProps}; + return (
diff --git a/src/components/input/radio/index.js b/src/components/input/radio/index.js index 90d93aeca..8161878d3 100644 --- a/src/components/input/radio/index.js +++ b/src/components/input/radio/index.js @@ -2,11 +2,13 @@ import React, {Component, PropTypes} from 'react'; import Translation from '../../../behaviours/translation'; import GridBehaviour from '../../../behaviours/grid'; import MaterialBehaviour from '../../../behaviours/material'; +import {InputBehaviour} from '../../../behaviours/input-component'; import {isUndefined} from 'lodash/lang'; @Translation @MaterialBehaviour('inputMdl') @GridBehaviour +@InputBehaviour class Radio extends Component { static defaultProps = { value: false @@ -41,7 +43,7 @@ class Radio extends Component { * Executed actions on change event. * @param {event} event */ - _onChange() { + _onChange = () => { this.setState({isChecked: !this.state.isChecked}, () => { if(this.props.onChange) { this.props.onChange(this.state.isChecked); @@ -63,13 +65,20 @@ class Radio extends Component { */ render() { const {isChecked} = this.state; - const {label, name, ...otherProps} = this.props; - // we use inputProps to be able to display 'checked' property. it is required to be able to use MDL. - const checkedProps = isChecked ? {checked: 'checked'} : {}; + + const managedProps = this._checkProps(this.props); + const validInputProps = managedProps[0]; + const invalidInputProps = managedProps[1]; + + const {label, onChange} = validInputProps; + + validInputProps.onChange = this._onChange; + validInputProps.checked = isChecked ? 'checked' : undefined; + const inputProps = {...validInputProps}; return ( ); diff --git a/src/components/input/select/index.js b/src/components/input/select/index.js index e910766ff..a594516c5 100644 --- a/src/components/input/select/index.js +++ b/src/components/input/select/index.js @@ -2,6 +2,7 @@ import React, {Component, PropTypes} from 'react'; import ReactDOM from 'react-dom'; import ComponentBaseBehaviour from '../../../behaviours/component-base'; +import {InputBehaviour} from '../../../behaviours/input-component'; import {isUndefined, isNull, isNumber} from 'lodash/lang'; import {union} from 'lodash/array'; const UNSELECTED_KEY = 'UNSELECTED_KEY'; @@ -55,6 +56,7 @@ const defaultProps = { * Component standing for an HTML input. */ @ComponentBaseBehaviour +@InputBehaviour class Select extends Component { /** @@ -102,11 +104,19 @@ class Select extends Component { * @override */ render() { - const { autoFocus, error, multiple, name, placeholder, style, value, values, disabled, onChange, size } = this.props; - const selectProps = { autoFocus, disabled, multiple, size }; + const managedProps = this._checkProps(this.props); + const validInputProps = managedProps[0]; + const invalidInputProps = managedProps[1]; + + const {error} = invalidInputProps; + const {style} = validInputProps; + + validInputProps.onChange = this._handleSelectChange; + const inputProps = {...validInputProps}; + return (
- {this._renderOptions(this.props)} {error &&
{error}
} diff --git a/src/components/input/text/index.js b/src/components/input/text/index.js index 9f42736ab..c94af43c8 100644 --- a/src/components/input/text/index.js +++ b/src/components/input/text/index.js @@ -4,8 +4,8 @@ import ReactDOM from 'react-dom'; import {identity} from 'lodash/utility'; import ComponentBaseBehaviour from '../../../behaviours/component-base'; import MDBehaviour from '../../../behaviours/material'; +import {InputBehaviour} from '../../../behaviours/input-component'; const MODE = {isEdit: true}; -import {inputHtmlAttributes} from '../../../common/react-html-attributes'; const propTypes = { disabled: PropTypes.bool, @@ -35,6 +35,7 @@ const defaultProps = { */ @MDBehaviour('inputText') @ComponentBaseBehaviour +@InputBehaviour class InputText extends Component { /** @@ -65,24 +66,24 @@ class InputText extends Component { return onChange(unformatter(value, MODE)); }; - /** - * comments will be right there - */ - _checkProps = (props) => { - let validInputProps = {}; - let invalidInputProps = {}; - - Object.keys(props).map(key => { - if(key === inputHtmlAttributes[inputHtmlAttributes.indexOf(key)]) { - let value = key === 'value' ? props.formatter(props[key], MODE) : key === 'onChange' ? this._handleInputChange : props[key]; - validInputProps[key] = value; - } else { - invalidInputProps[key] = props[key]; - } - }); - const managedProps = [validInputProps, invalidInputProps]; - return managedProps; - }; + // /** + // * comments will be right there + // */ + // _checkProps = (props) => { + // let validInputProps = {}; + // let invalidInputProps = {}; + // + // Object.keys(props).map(key => { + // if(key === inputHtmlAttributes[inputHtmlAttributes.indexOf(key)]) { + // let value = key === 'value' ? props.formatter(props[key], MODE) : key === 'onChange' ? this._handleInputChange : props[key]; + // validInputProps[key] = value; + // } else { + // invalidInputProps[key] = props[key]; + // } + // }); + // const managedProps = [validInputProps, invalidInputProps]; + // return managedProps; + // }; /** * @inheritdoc * @override @@ -91,9 +92,13 @@ class InputText extends Component { const managedProps = this._checkProps(this.props); const validInputProps = managedProps[0]; - const invalidInputProps = managedProps[1]; + const invalidInputProps = managedProps[1] + + const { name, placeholder, value: valueToFormat } = validInputProps; + + validInputProps.value = this.props.formatter(valueToFormat, MODE); + validInputProps.onChange = this._handleInputChange; - const { name, placeholder } = validInputProps; const { error, style } = invalidInputProps; const pattern = error ? 'hasError' : null; //add pattern to overide mdl error style when displaying an focus error. diff --git a/src/components/input/textarea/index.js b/src/components/input/textarea/index.js index a1f58a55d..1b24ad11a 100644 --- a/src/components/input/textarea/index.js +++ b/src/components/input/textarea/index.js @@ -3,6 +3,7 @@ import ReactDOM from 'react-dom'; import {identity} from 'lodash/utility'; import ComponentBaseBehaviour from '../../../behaviours/component-base'; import MDBehaviour from '../../../behaviours/material'; +import {InputBehaviour} from '../../../behaviours/input-component'; const propTypes = { cols: PropTypes.number, @@ -42,6 +43,7 @@ const defaultProps = { */ @MDBehaviour('inputTextarea') @ComponentBaseBehaviour +@InputBehaviour class InputTextarea extends Component { /** @@ -68,11 +70,21 @@ class InputTextarea extends Component { * @override */ render() { - const { autoFocus, disabled, formatter, maxLength, onFocus, onClick, onKeyPress, error, name, placeholder, style, value: rawValue, size, type} = this.props; - const value = formatter(rawValue); + const managedProps = this._checkProps(this.props); + const validInputProps = managedProps[0]; + const invalidInputProps = managedProps[1]; + + const {error} = invalidInputProps; + const {formatter} = this.props; + const {name, style, placeholder, value} = validInputProps; + const pattern = error ? 'hasError' : null; //add pattern to overide mdl error style when displaying an focus error. - const inputProps = { autoFocus, disabled, onKeyPress, maxLength, onFocus, onClick, id: name, onChange: this._handleInputChange, pattern, size, type, value: value === undefined || value === null ? '' : value }; const mdlClasses = `mdl-textfield mdl-js-textfield${error ? ' is-invalid' : ''}`; + + validInputProps.value = formatter(value) === undefined || formatter(value) === null ? '' : formatter(value); + validInputProps.onChange = this._handleInputChange + const inputProps = {...validInputProps}; + return (
diff --git a/src/components/input/toggle/index.js b/src/components/input/toggle/index.js index 9f33e9125..6708cb2c9 100644 --- a/src/components/input/toggle/index.js +++ b/src/components/input/toggle/index.js @@ -29,10 +29,19 @@ class InputToggle extends Component { }; render() { - const {label, value} = this.props; + const managedProps = this._checkProps(this.props); + const validInputProps = managedProps[0]; + const invalidInputProps = managedProps[1]; + + const {label, value} = validInputProps; + + validInputProps.onChange = this.handleOnChange; + validInputProps.checked = value; + const inputProps = {...validInputProps}; + return ( ); From 6219a58bf3b6509e96130a4018530ac1457d25ae Mon Sep 17 00:00:00 2001 From: GuenoleK Date: Thu, 9 Feb 2017 17:40:24 +0100 Subject: [PATCH 10/50] Fixing the tests --- .../input/autocomplete-select/edit.js | 1 + .../autocomplete-select/example/index.jsx | 90 +++++++++++-------- src/components/input/textarea/index.js | 2 +- src/components/input/toggle/index.js | 2 + 4 files changed, 56 insertions(+), 39 deletions(-) diff --git a/src/components/input/autocomplete-select/edit.js b/src/components/input/autocomplete-select/edit.js index e57cf10c1..cb7ccb8b1 100644 --- a/src/components/input/autocomplete-select/edit.js +++ b/src/components/input/autocomplete-select/edit.js @@ -228,6 +228,7 @@ class Autocomplete extends Component { validInputProps.value = inputValue; validInputProps.onFocus = this._handleQueryFocus; + validInputProps.onKeyDown = this._handleQueryKeyDown; validInputProps.onChange = this._handleQueryChange; const inputProps = {...validInputProps}; diff --git a/src/components/input/autocomplete-select/example/index.jsx b/src/components/input/autocomplete-select/example/index.jsx index 2855db7ce..a5fcbcd61 100644 --- a/src/components/input/autocomplete-select/example/index.jsx +++ b/src/components/input/autocomplete-select/example/index.jsx @@ -1,10 +1,61 @@ const {AutocompleteSelect} = FocusComponents.components.input; +const data = [ + { + key: 'NY', + label: 'New York' + }, + { + key: 'PAR', + label: 'Paris' + }, + { + key: 'TOY', + label: 'Tokyo' + }, + { + key: 'BEI', + label: 'Pékin' + }, + { + key: 'LON', + label: 'Londres' + }, + { + key: 'BER', + label: 'Berlin' + } +]; + +const querySearcher = query => { + const value = data.filter(({key, label}) => label.toLowerCase().indexOf(query.toLowerCase()) !== -1); + return new Promise((resolve, reject) => { + setTimeout(() => { + resolve({ + data: value, + totalCount: value.length + }); + }, 200); + }); +}; + const keyResolver = key => { return new Promise((resolve, reject) => { - setTimeout(resolve.bind(this, 'Resolved value'), 300); + data.forEach(element => { + if(element.key === key) { + setTimeout(resolve.bind(this, element.label), 10); + } + }); }); } + +const AutoComplete = (props) => { + return( + + ) +} const resources = { dev: { translation: { @@ -19,43 +70,6 @@ const resources = { i18n.init({resStore: resources}); -const querySearcher = query => { - const data = [ - { - key: 'NY', - label: 'New York' - }, - { - key: 'PAR', - label: 'Paris' - }, - { - key: 'TOY', - label: 'Tokyo' - }, - { - key: 'BEI', - label: 'Pékin' - }, - { - key: 'LON', - label: 'Londres' - }, - { - key: 'BER', - label: 'Berlin' - } - ].filter(({key, label}) => label.toLowerCase().indexOf(query.toLowerCase()) !== -1); - return new Promise((resolve, reject) => { - setTimeout(() => { - resolve({ - data, - totalCount: 40 - }); - }, 200); - }); -} - class Sample extends React.Component { state = {isEdit: true}; diff --git a/src/components/input/textarea/index.js b/src/components/input/textarea/index.js index 1b24ad11a..18915e732 100644 --- a/src/components/input/textarea/index.js +++ b/src/components/input/textarea/index.js @@ -83,7 +83,7 @@ class InputTextarea extends Component { validInputProps.value = formatter(value) === undefined || formatter(value) === null ? '' : formatter(value); validInputProps.onChange = this._handleInputChange - const inputProps = {...validInputProps}; + const inputProps = {...validInputProps, pattern}; return (
diff --git a/src/components/input/toggle/index.js b/src/components/input/toggle/index.js index 6708cb2c9..d5ebbca80 100644 --- a/src/components/input/toggle/index.js +++ b/src/components/input/toggle/index.js @@ -2,6 +2,7 @@ import React, {Component, PropTypes} from 'react'; import ReactDOM from 'react-dom'; import Translation from '../../../behaviours/translation'; import Material from '../../../behaviours/material'; +import {InputBehaviour} from '../../../behaviours/input-component'; const propTypes = { label: PropTypes.string, @@ -17,6 +18,7 @@ const displayName = 'InputToggle'; @Translation @Material('mdlHolder') +@InputBehaviour class InputToggle extends Component { getValue = () => { const domElement = ReactDOM.findDOMNode(this.refs.toggle); From fb4fb69ad02984f0ccda7df91ced67123d061b42 Mon Sep 17 00:00:00 2001 From: clementdelebecque Date: Tue, 28 Feb 2017 15:36:23 +0100 Subject: [PATCH 11/50] Update built-in-components.js Fix for InputLabelComponent prop use --- src/common/field/mixin/built-in-components.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/field/mixin/built-in-components.js b/src/common/field/mixin/built-in-components.js index d3defda36..15fd375ac 100644 --- a/src/common/field/mixin/built-in-components.js +++ b/src/common/field/mixin/built-in-components.js @@ -252,7 +252,7 @@ const fieldBuiltInComponentsMixin = { error: error, onChange: this.onInputChange }; - const finalBuildedProps = addRefToPropsIfNotPure(this.props.FieldComponent, buildedProps, INPUT); + const finalBuildedProps = addRefToPropsIfNotPure(FieldComponent, buildedProps, INPUT); return ; } }; From 8bde2a13f716fe1432942a283e4e8cee255d2151 Mon Sep 17 00:00:00 2001 From: GuenoleK Date: Thu, 2 Mar 2017 14:48:09 +0100 Subject: [PATCH 12/50] Adding spinner when the form is saving and disabling the button --- src/common/mixin/built-in-components.js | 3 + src/components/button/index.js | 102 +++++++++++++----------- src/components/button/style/button.scss | 14 ++++ 3 files changed, 74 insertions(+), 45 deletions(-) diff --git a/src/common/mixin/built-in-components.js b/src/common/mixin/built-in-components.js index 463011776..8c1de271f 100644 --- a/src/common/mixin/built-in-components.js +++ b/src/common/mixin/built-in-components.js @@ -206,6 +206,7 @@ module.exports = { * @returns {object} - A React save button. */ buttonSave() { + const {isLoading} = this.state; const handleOnClick = () => { if (this._validate()) { this.action.save.call(this, this._getEntity()); @@ -218,6 +219,8 @@ module.exports = { label='button.save' shape={null} type='button' + isLoading={isLoading} + processLabel='button.saving' /> ); }, diff --git a/src/components/button/index.js b/src/components/button/index.js index b0386b485..a0bd248db 100644 --- a/src/components/button/index.js +++ b/src/components/button/index.js @@ -11,12 +11,13 @@ const RIPPLE_EFFECT = 'mdl-js-ripple-effect'; const propTypes = { color: PropTypes.oneOf([undefined,'colored', 'primary', 'accent']), - id: PropTypes.string, handleOnClick: PropTypes.func, //to remove in V2 hasRipple: PropTypes.bool, - isJs: PropTypes.bool, + id: PropTypes.string, icon: PropTypes.string, iconLibrary: PropTypes.oneOf(['material', 'font-awesome', 'font-custom']), + isJs: PropTypes.bool, + isLoading: PropTypes.bool, label: PropTypes.string, onClick: PropTypes.func, shape: PropTypes.oneOf([undefined, 'raised', 'fab', 'icon', 'mini-fab']), @@ -24,14 +25,14 @@ const propTypes = { } const defaultProps = { - type: 'submit', - shape: 'raised', - label: '', + hasRipple: false, icon: null, + iconLibrary: 'material', id: '', - hasRipple: false, isJs: false, - iconLibrary: 'material' + label: '', + shape: 'raised', + type: 'submit' } @MDBehaviour('materialButton', 'MaterialButton') @@ -49,6 +50,13 @@ class Button extends Component { } } + componentDidUpdate() { + const spinnerNode = ReactDOM.findDOMNode(this.refs['double-action-button-spinner']); + if(spinnerNode) { + componentHandler.upgradeElement(spinnerNode, 'MaterialSpinner'); + } + } + /** * Date de composant. * @return {string} Classe. @@ -98,47 +106,51 @@ class Button extends Component { const {icon, iconLibrary} = this.props; switch (iconLibrary) { case 'material': - return {icon}; - case 'font-awesome': + return {icon}; + case 'font-awesome': const faCss = `fa fa-${icon}`; return ; - case 'font-custom': - return ; - default: - return null; - } - }; + case 'font-custom': + return ; + default: + return null; + } + }; - /** - * Render the label. - * @return {Component} - Tle button label. - */ - _renderLabel() { - const {label, shape} = this.props; - if (label && 'fab' !== shape && 'icon' !== shape && 'mini-fab' !== shape ) { - return this.i18n(label); - } - return null; - }; + /** + * Render the label. + * @return {Component} - Tle button label. + */ + _renderLabel() { + const {isLoading, label, processLabel, shape} = this.props; - /** inheritedDoc */ - render() { - // attribute doc : https://developer.mozilla.org/fr/docs/Web/HTML/Element/Button - // be careful the way you declare your attribute names : https://developer.mozilla.org/fr/docs/Web/HTML/Element/Button - const {className, disabled, formNoValidate, handleOnClick, icon, id, onClick, type, label, style, hasRipple, isJs, iconLibrary, ...rest} = this.props; - const otherInputProps = { disabled, formNoValidate, onClick: handleOnClick ? handleOnClick : onClick, style, type, ...rest }; //on click for legacy. Remove handleOnClick in v2 - const renderedClassName = `${className ? className : ''} ${::this._getComponentClassName()}`.trim(); - return ( - - ); - } -} + if (label && 'fab' !== shape && 'icon' !== shape && 'mini-fab' !== shape && (!isLoading || !processLabel)) { + return {this.i18n(label)}; + } else if (processLabel && 'fab' !== shape && 'icon' !== shape && 'mini-fab' !== shape && isLoading) { + return {this.i18n(processLabel)} + } + return null; + }; + + /** inheritedDoc */ + render() { + // attribute doc : https://developer.mozilla.org/fr/docs/Web/HTML/Element/Button + // be careful the way you declare your attribute names : https://developer.mozilla.org/fr/docs/Web/HTML/Element/Button + const {className, formNoValidate, handleOnClick, icon, id, onClick, type, label, style, hasRipple, isJs, iconLibrary, isLoading, ...rest} = this.props; + const otherInputProps = { formNoValidate, onClick: handleOnClick ? handleOnClick : onClick, style, type, ...rest }; //on click for legacy. Remove handleOnClick in v2 + const renderedClassName = `${className ? className : ''} ${::this._getComponentClassName()}`.trim(); + return ( + + ); + } + } -Button.displayName = 'Button' -Button.defaultProps = defaultProps; -Button.propTypes = propTypes; + Button.displayName = 'Button' + Button.defaultProps = defaultProps; + Button.propTypes = propTypes; -export default Button; + export default Button; diff --git a/src/components/button/style/button.scss b/src/components/button/style/button.scss index f2a62b33a..b08fd465c 100644 --- a/src/components/button/style/button.scss +++ b/src/components/button/style/button.scss @@ -1,6 +1,20 @@ [data-focus="button-action"] { + user-select: none; + transition: width 1s; i { margin-right: 5px; } + + [data-focus="button-label"], [data-focus="double-action-button-spinner"] { + vertical-align: middle; + } + + [data-focus="double-action-button-spinner"] { + margin-left: 13px; + } +} + +[data-saving="true"] { + cursor: not-allowed; } From d2f1530b3e42fa117c66c8643092f850972b4cae Mon Sep 17 00:00:00 2001 From: GuenoleK Date: Thu, 2 Mar 2017 14:54:24 +0100 Subject: [PATCH 13/50] Adding unit tests --- src/components/button/__tests__/index.js | 34 ++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/components/button/__tests__/index.js b/src/components/button/__tests__/index.js index 93dd211fe..fe43304a8 100644 --- a/src/components/button/__tests__/index.js +++ b/src/components/button/__tests__/index.js @@ -44,5 +44,39 @@ describe('Button Component', () => { }); }); }); + describe.only('When we set a processingLabel', () => { + let renderedButton; + describe('When isLoading props is false', () => { + before(() => { + renderedButton = renderIntoDocument( - ); - } - } + /** inheritedDoc */ + render() { + // attribute doc : https://developer.mozilla.org/fr/docs/Web/HTML/Element/Button + // be careful the way you declare your attribute names : https://developer.mozilla.org/fr/docs/Web/HTML/Element/Button + const { className, formNoValidate, handleOnClick, icon, id, onClick, type, label, style, hasRipple, isJs, iconLibrary, isLoading, ...rest } = this.props; + const otherInputProps = { formNoValidate, onClick: handleOnClick ? handleOnClick : onClick, style, type, ...rest }; //on click for legacy. Remove handleOnClick in v2 + const renderedClassName = `${className ? className : ''} ${::this._getComponentClassName()}`.trim(); + return ( + + ); + } +} - Button.displayName = 'Button' - Button.defaultProps = defaultProps; - Button.propTypes = propTypes; +Button.displayName = 'Button' +Button.defaultProps = defaultProps; +Button.propTypes = propTypes; - export default Button; +export default Button; \ No newline at end of file From 7654cc78b4d62eb1d562fd13cc515772e75f817c Mon Sep 17 00:00:00 2001 From: Hartorn Date: Tue, 18 Apr 2017 10:43:25 +0200 Subject: [PATCH 26/50] triggerOnChangeIfEmpty should be true by default --- src/components/input/date/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/input/date/index.js b/src/components/input/date/index.js index 0d336e46e..039209fc9 100644 --- a/src/components/input/date/index.js +++ b/src/components/input/date/index.js @@ -37,7 +37,7 @@ const propTypes = { const defaultProps = { beforeValueGetter: value => value, checkOnlyOnBlur: false, - triggerOnChangeIfEmpty: false, + triggerOnChangeIfEmpty: true, drops: 'down', format: 'MM/DD/YYYY', locale: 'en', From 935489c61cb6bcdef556a6ff810c6c947b4feba0 Mon Sep 17 00:00:00 2001 From: Hartorn Date: Fri, 21 Apr 2017 14:07:06 +0200 Subject: [PATCH 27/50] Updating eslint config Correcting html attributes, based on focus-components in v3 --- package.json | 11 ++--- src/behaviours/input-component/index.js | 30 ++++++------- .../input-component/react-html-attributes.js | 39 ++++++++++++++++ src/common/react-html-attributes/index.js | 36 --------------- .../input/autocomplete-select/edit.js | 45 +++++++++---------- .../input/autocomplete-text/edit.js | 44 ++++++++---------- src/components/input/checkbox/index.js | 20 ++++----- src/components/input/date/index.js | 8 +--- src/components/input/radio/index.js | 12 ++--- src/components/input/select-checkbox/index.js | 8 ++-- src/components/input/select-radio/index.js | 2 +- src/components/input/select/index.js | 15 +++---- src/components/input/text/index.js | 27 +---------- src/components/input/textarea/index.js | 11 ++--- src/components/input/toggle/index.js | 5 +-- 15 files changed, 129 insertions(+), 184 deletions(-) create mode 100644 src/behaviours/input-component/react-html-attributes.js delete mode 100644 src/common/react-html-attributes/index.js diff --git a/package.json b/package.json index 47f256d3d..2511ae042 100644 --- a/package.json +++ b/package.json @@ -94,7 +94,7 @@ "babel": "^6.3.26", "babel-cli": "^6.4.0", "babel-core": "^6.4.0", - "babel-eslint": "4.1.3", + "babel-eslint": "7.1.1", "babel-loader": "^6.2.1", "babel-preset-focus": "^0.5.2", "babel-runtime": "^6.3.19", @@ -102,10 +102,11 @@ "chai": "^3.2.0", "chai-subset": "^1.0.1", "css-loader": "^0.19.0", - "eslint": "^2.3.0", - "eslint-config-focus": "0.3.0", - "eslint-plugin-filenames": "0.1.1", - "eslint-plugin-react": "3.5.0", + "eslint-config-focus": "0.5.0", + "eslint": "3.17.1", + "eslint-plugin-filenames": "1.1.0", + "eslint-plugin-import": "2.2.0", + "eslint-plugin-react": "6.10.0", "exports-loader": "^0.6.2", "extract-text-webpack-plugin": "^0.8.2", "file-loader": "^0.8.4", diff --git a/src/behaviours/input-component/index.js b/src/behaviours/input-component/index.js index 058a0c343..bd8e6efed 100644 --- a/src/behaviours/input-component/index.js +++ b/src/behaviours/input-component/index.js @@ -1,22 +1,22 @@ -import {inputHtmlAttributes} from '../../common/react-html-attributes'; +import {inputHtmlAttributes, eventHtmlAttributes} from './react-html-attributes'; +import {pairs} from 'lodash'; export const InputBehaviour = Component => class InputComponent extends Component { /** - * comments will be right there - */ + * Filter the incoming props, so only valid props for native HTML elements are passed through. + * Value prop is also defaulted to empty string, instead of null or undefined. + * + * @param {object} props the incoming props + * @returns {object} an object with valid + */ _checkProps(props) { - let validInputProps = {}; - let invalidInputProps = {}; - - Object.keys(props).map(key => { - if(key === inputHtmlAttributes[inputHtmlAttributes.indexOf(key)]) { - validInputProps[key] = (key === 'value' && (props[key] === null || props[key] === undefined)) ? '' : props[key]; - } else { - invalidInputProps[key] = props[key]; + return pairs(props).reduce((acc, [key, value]) => { + if (inputHtmlAttributes.indexOf(key) !== -1 || eventHtmlAttributes.indexOf(key) !== -1) { + acc[key] = (key === 'value' && (value === null || value === undefined)) ? '' : value; } - }); - const managedProps = [validInputProps, invalidInputProps]; - return managedProps; - }; + return acc; + }, {}); + } }; + \ No newline at end of file diff --git a/src/behaviours/input-component/react-html-attributes.js b/src/behaviours/input-component/react-html-attributes.js new file mode 100644 index 000000000..97f306454 --- /dev/null +++ b/src/behaviours/input-component/react-html-attributes.js @@ -0,0 +1,39 @@ +export const inputHtmlAttributes = [ + 'accept', 'alt', 'autoComplete', 'autoFocus', 'checked', 'dir', + 'disabled', 'form', 'formAction', 'formEncType', 'formMethod', + 'formNoValidate', 'formTarget', 'height', 'label', 'list', 'max', + 'maxLength', 'min', 'multiple', 'name', 'pattern', 'placeholder', 'readOnly', 'required', + 'size', 'src', 'step', 'type', 'value', 'width', 'acceptCharset', + 'accessKey', 'action', 'allowFullScreen', + 'allowTransparency', 'async', 'autoPlay', 'capture', 'cellPadding', + 'cellSpacing', 'challenge', 'charSet', 'cite', 'classID', 'className', + 'colSpan', 'cols', 'content', 'contentEditable', 'contextMenu', 'controls', + 'coords', 'crossOrigin', 'data', 'dateTime', 'default', 'defer', 'download', + 'draggable', 'encType', 'frameBorder', 'headers', 'hidden', 'high', 'href', + 'hrefLang', 'htmlFor', 'httpEquiv', 'icon', 'id', 'inputMode', 'integrity', + 'is', 'keyParams', 'keyType', 'kind', 'lang', 'loop', 'low', 'manifest', + 'marginHeight', 'marginWidth', 'media', 'mediaGroup', 'method', 'minLength', + 'muted', 'noValidate', 'nonce', 'open', 'optimum', 'poster', 'preload', + 'profile', 'radioGroup', 'rel', 'reversed', 'role', 'rowSpan', 'rows', + 'sandbox', 'scope', 'scoped', 'scrolling', 'seamless', 'selected', 'shape', + 'sizes', 'span', 'spellCheck', 'srcDoc', 'srcLang', 'srcSet', 'start', 'step', + 'style', 'summary', 'tabIndex', 'target', 'title', 'type', 'useMap', 'wmode', + 'wrap' +] +export const eventHtmlAttributes = [ + 'onCopy', 'onCut', + 'onPast', 'onKeyDown', 'onKeyPress', 'onKeyUp', 'onCompositionEnd', + 'onCompositionStart', 'onCompositionUpdate', 'onFocus', 'onBlur', + 'onChange', 'onInput', 'onSubmit', 'onInput', 'onClick', 'onContextMenu', + 'onDoubleClick', 'onDrag', 'onDragEnd', 'onDragEnter', 'onDragExit', + 'onDragLeave', 'onDragOver', 'onDragStart', 'onDrop', 'onMouseDown', + 'onMouseEnter', 'onMouseLeave', 'onMouseMove', 'onMouseOut', + 'onMouseOver', 'onMouseUp', 'onSelect', 'onAbort', 'onCanPlay', + 'onCanPlayThrough', 'onDurationChange', 'onEmptied', 'onEncrypted', + 'onEnded', 'onError', 'onLoadedData', 'onLoadedMetadata', + 'onLoadStart', 'onPause', 'onPlay', 'onPlaying', 'onProgress', + 'onRateChange', 'onSeeked', 'onSeeking', 'onStalled', 'onSuspend', + 'onTimeUpdate', 'onVolumeChange', 'onWaiting', 'onLoad', 'onError', + 'onAnimationStart', 'onAnimationEnd', 'onAnimationIteration', + 'onTransitionEnd' +] \ No newline at end of file diff --git a/src/common/react-html-attributes/index.js b/src/common/react-html-attributes/index.js deleted file mode 100644 index b3b905dd1..000000000 --- a/src/common/react-html-attributes/index.js +++ /dev/null @@ -1,36 +0,0 @@ -export const inputHtmlAttributes = [ - "accept", "alt", "autoComplete", "autoFocus", "checked", "dir", - "disabled", "form", "formAction", "formEncType", "formMethod", - "formNoValidate", "formTarget", "height", "label", "list", "max", - "maxLength", "min", "multiple", "name", "onCopy", "onCut", - "onPast", "onKeyDown", "onKeyPress", "onKeyUp", "onCompositionEnd", - "onCompositionStart", "onCompositionUpdate", "onFocus", "onBlur", - "onChange", "onInput", "onSubmit", "onInput", "onClick", "onContextMenu", - "onDoubleClick", "onDrag", "onDragEnd", "onDragEnter", "onDragExit", - "onDragLeave", "onDragOver", "onDragStart", "onDrop", "onMouseDown", - "onMouseEnter", "onMouseLeave", "onMouseMove", "onMouseOut", - "onMouseOver", "onMouseUp", "onSelect", "onAbort", "onCanPlay", - "onCanPlayThrough", "onDurationChange", "onEmptied", "onEncrypted", - "onEnded", "onError", "onLoadedData", "onLoadedMetadata", - "onLoadStart", "onPause", "onPlay", "onPlaying", "onProgress", - "onRateChange", "onSeeked", "onSeeking", "onStalled", "onSuspend", - "onTimeUpdate", "onVolumeChange", "onWaiting", "onLoad", "onError", - "onAnimationStart", "onAnimationEnd", "onAnimationIteration", - "onTransitionEnd", "pattern", "placeholder", "readOnly", "required", - "size", "src", "step", "type", "value", "width", "acceptCharset", - "accessKey", "action", "allowFullScreen", - "allowTransparency", "async", "autoPlay", "capture", "cellPadding", - "cellSpacing", "challenge", "charSet", "cite", "classID", "className", - "colSpan", "cols", "content", "contentEditable", "contextMenu", "controls", - "coords", "crossOrigin", "data", "dateTime", "default", "defer", "download", - "draggable", "encType", "frameBorder", "headers", "hidden", "high", "href", - "hrefLang", "htmlFor", "httpEquiv", "icon", "id", "inputMode", "integrity", - "is", "keyParams", "keyType", "kind", "lang", "loop", "low", "manifest", - "marginHeight", "marginWidth", "media", "mediaGroup", "method", "minLength", - "muted", "noValidate", "nonce", "open", "optimum", "poster", "preload", - "profile", "radioGroup", "rel", "reversed", "role", "rowSpan", "rows", - "sandbox", "scope", "scoped", "scrolling", "seamless", "selected", "shape", - "sizes", "span", "spellCheck", "srcDoc", "srcLang", "srcSet", "start", "step", - "style", "summary", "tabIndex", "target", "title", "type", "useMap", "wmode", - "wrap" -] diff --git a/src/components/input/autocomplete-select/edit.js b/src/components/input/autocomplete-select/edit.js index cb7ccb8b1..dacfa603c 100644 --- a/src/components/input/autocomplete-select/edit.js +++ b/src/components/input/autocomplete-select/edit.js @@ -1,5 +1,4 @@ import React, {Component, PropTypes} from 'react'; -import ReactDOM from 'react-dom'; import ComponentBaseBehaviour from '../../../behaviours/component-base'; import MDBehaviour from '../../../behaviours/material'; import {InputBehaviour} from '../../../behaviours/input-component'; @@ -53,7 +52,7 @@ class Autocomplete extends Component { }; this.state = state; this.autocompleteId = uniqueId('autocomplete-text-'); - }; + } componentDidMount() { const {value, keyResolver, inputTimeout} = this.props; @@ -64,7 +63,7 @@ class Autocomplete extends Component { } document.addEventListener('click', this._handleDocumentClick); this._debouncedQuerySearcher = debounce(this._querySearcher, inputTimeout); - }; + } componentWillReceiveProps({value, customError, error}) { const {keyResolver} = this.props; @@ -75,10 +74,10 @@ class Autocomplete extends Component { } else if (customError !== this.props.customError) { this.setState({customError}); } - if(error) { + if (error) { this.setState({customError: error}); } - }; + } componentDidUpdate() { if (this.props.customError) { @@ -86,11 +85,11 @@ class Autocomplete extends Component { } else { this.refs.inputText.classList.remove('is-invalid'); } - }; + } componentWillUnmount() { document.removeEventListener('click', this._handleDocumentClick); - }; + } getValue() { const {labelName, keyName, value} = this.props; @@ -105,14 +104,14 @@ class Autocomplete extends Component { } else { // The user selected an option (or no value was provided), return it return selected || null; } - }; + } _handleDocumentClick = ({target}) => { const {focus, inputValue} = this.state; const {onBadInput} = this.props; if (focus) { const closestACParent = closest(target, `[data-id='${this.autocompleteId}']`, true); - if(closestACParent === undefined) { + if (closestACParent === undefined) { this.setState({focus: false}, () => { if (onBadInput && this.getValue() === null && inputValue !== '') { onBadInput(inputValue); @@ -188,7 +187,7 @@ class Autocomplete extends Component { this.setState({inputValue: this.i18n(resolvedLabel), selected: key, focus: false}, () => { if (onChange) onChange(key); }); - }; + } _renderOptions = () => { const {active, options, focus} = this.state; @@ -197,19 +196,19 @@ class Autocomplete extends Component { const isActive = active === key; renderedOptions.push(
  • - {this.i18n(value)} + {this.i18n(value)}
  • ); } return (
      - {renderedOptions} + {renderedOptions}
    ); }; @@ -217,14 +216,10 @@ class Autocomplete extends Component { render () { const {inputValue, isLoading} = this.state; const {customError, renderOptions} = this.props; - const {_handleQueryFocus, _handleQueryKeyDown, _handleQueryChange} = this; - const managedProps = this._checkProps(this.props); + const validInputProps = this._checkProps(this.props); - const validInputProps = managedProps[0]; - const invalidInputProps = managedProps[1] - - const { name, placeholder, value: valueToFormat } = validInputProps; + const { placeholder } = validInputProps; validInputProps.value = inputValue; validInputProps.onFocus = this._handleQueryFocus; @@ -236,7 +231,7 @@ class Autocomplete extends Component { return (
    -
    +
    ); - }; + } } Autocomplete.displayName = 'Autocomplete'; diff --git a/src/components/input/autocomplete-text/edit.js b/src/components/input/autocomplete-text/edit.js index 78bf36b50..0aecf1dd2 100644 --- a/src/components/input/autocomplete-text/edit.js +++ b/src/components/input/autocomplete-text/edit.js @@ -1,5 +1,4 @@ import React, {Component, PropTypes} from 'react'; -import ReactDOM from 'react-dom'; import ComponentBaseBehaviour from '../../../behaviours/component-base'; import MDBehaviour from '../../../behaviours/material'; import {InputBehaviour} from '../../../behaviours/input-component'; @@ -65,7 +64,7 @@ class AutocompleteTextEdit extends Component { * [inputTimeout description] * @type {number} */ - inputTimeout : PropTypes.number.isRequired + inputTimeout: PropTypes.number.isRequired }; state = { @@ -78,7 +77,7 @@ class AutocompleteTextEdit extends Component { }; componentWillReceiveProps({error}) { - if(error) { + if (error) { this.setState({error: error}); } } @@ -89,12 +88,11 @@ class AutocompleteTextEdit extends Component { } // Returns the state's inputValue - getValue = () => { + getValue = () => { const {inputValue} = this.state; - if(inputValue !== undefined) { + if (inputValue !== undefined) { return inputValue; - } - else { + } else { return null; } }; @@ -103,10 +101,9 @@ class AutocompleteTextEdit extends Component { // Sets the hasSuggestions' state if the given object has a none empty array _querySearcher = value => { const {querySearcher} = this.props; - const {hasSuggestions} = this.state; querySearcher(value).then(({data, totalCount}) => { - if(totalCount > 0) { + if (totalCount > 0) { this.setState({hasSuggestions: true, suggestions: data, error: ''}); } this.refs.loader.classList.remove('mdl-progress__indeterminate'); @@ -121,14 +118,13 @@ class AutocompleteTextEdit extends Component { // Sets the state's inputValue when the user is typing onQueryChange = ({target: {value}}) => { this.setState({inputValue: value}); - if(value.trim() == '') { + if (value.trim() === '') { this.setState({hasSuggestions: false}); - } - else { + } else { this.refs.loader.classList.add('mdl-progress__indeterminate'); this.setState({isLoading: true}); this._debouncedQuerySearcher(value); - // this._querySearcher(value); + // this._querySearcher(value); } }; @@ -137,13 +133,13 @@ class AutocompleteTextEdit extends Component { this.refs.inputText.value = value; this.setState({inputValue: value, hasSuggestions: false, suggestions: []}); return value; - }; + } // Returns an html list whith the Suggestions renderSuggestions = () => { const {suggestions} = this.state; const allSuggestions = suggestions.map(({key, label}) =>
  • {this.onResultClick(label); e.preventDefault();}} data-focus='option' >{label}
  • ); - return( + return (
      {allSuggestions}
    @@ -155,10 +151,10 @@ class AutocompleteTextEdit extends Component { const {hasSuggestions, hasFocus} = this.state; const {showAtFocus, emptyShowAll} = this.props; this.setState({hasFocus: !this.state.hasFocus}); - if(hasSuggestions && !showAtFocus && hasFocus === false) { + if (hasSuggestions && !showAtFocus && hasFocus === false) { this.setState({hasSuggestions: false}); } - if(!hasSuggestions && e.target.value.trim() === '' && emptyShowAll && hasFocus === false) { + if (!hasSuggestions && e.target.value.trim() === '' && emptyShowAll && hasFocus === false) { // Doing a global search here this._querySearcher(''); } @@ -169,12 +165,8 @@ class AutocompleteTextEdit extends Component { render() { const {inputValue, hasSuggestions, hasFocus, isLoading} = this.state; - const managedProps = this._checkProps(this.props); - const validInputProps = managedProps[0]; - const invalidInputProps = managedProps[1]; - - const {inputTimeout, error} = invalidInputProps; - const {placeholder} = validInputProps; + const validInputProps = this._checkProps(this.props); + const {placeholder, error} = this.props; validInputProps.value = inputValue === undefined || inputValue === null ? '' : inputValue; validInputProps.onFocus = this.toggleHasFocus; @@ -182,13 +174,13 @@ class AutocompleteTextEdit extends Component { validInputProps.onBlur = this.toggleHasFocus; const inputProps = {...validInputProps}; - return( + return (
    - - {this.i18n(error)} + + {this.i18n(error)}
    {hasSuggestions && hasFocus && this.renderSuggestions() diff --git a/src/components/input/checkbox/index.js b/src/components/input/checkbox/index.js index c7d4d79b1..263199435 100644 --- a/src/components/input/checkbox/index.js +++ b/src/components/input/checkbox/index.js @@ -42,22 +42,20 @@ class InputCheckBox extends Component { } render() { - const managedProps = this._checkProps(this.props); - const validInputProps = managedProps[0]; - const invalidInputProps = managedProps[1]; + const validInputProps = this._checkProps(this.props); - const {label, value, disabled} = validInputProps; + const {label, value, disabled} = this.props; validInputProps.onChange = this.handleOnChange; const inputProps = {...validInputProps}; - + return ( -
    - -
    +
    + +
    ); } } diff --git a/src/components/input/date/index.js b/src/components/input/date/index.js index 219ed3ea0..35ed0091a 100644 --- a/src/components/input/date/index.js +++ b/src/components/input/date/index.js @@ -66,12 +66,6 @@ class InputDate extends Component { document.addEventListener('click', this._onDocumentClick); } - - componentDidMount() { - const {drops, showDropdowns} = this.props; - const {inputDate: startDate} = this.state; - } - componentWillReceiveProps({value}) { this.setState({ dropDownDate: isISOString(value) ? moment.utc(value, moment.ISO_8601) : moment.utc(), @@ -191,7 +185,7 @@ class InputDate extends Component { ref='picker' minDate={minDate} maxDate={maxDate} - /> + />
    }
    diff --git a/src/components/input/radio/index.js b/src/components/input/radio/index.js index 8161878d3..20c9fcfda 100644 --- a/src/components/input/radio/index.js +++ b/src/components/input/radio/index.js @@ -45,7 +45,7 @@ class Radio extends Component { */ _onChange = () => { this.setState({isChecked: !this.state.isChecked}, () => { - if(this.props.onChange) { + if (this.props.onChange) { this.props.onChange(this.state.isChecked); } }); @@ -65,19 +65,15 @@ class Radio extends Component { */ render() { const {isChecked} = this.state; - - const managedProps = this._checkProps(this.props); - const validInputProps = managedProps[0]; - const invalidInputProps = managedProps[1]; - - const {label, onChange} = validInputProps; + const {label} = this.props; + const validInputProps = this._checkProps(this.props); validInputProps.onChange = this._onChange; validInputProps.checked = isChecked ? 'checked' : undefined; const inputProps = {...validInputProps}; return ( -