diff --git a/src/vaadin-password-field.html b/src/vaadin-password-field.html index 0e987f14..a14e5653 100644 --- a/src/vaadin-password-field.html +++ b/src/vaadin-password-field.html @@ -141,6 +141,10 @@ } _onChange(e) { + const slotted = this.querySelector(`${this._slottedTagName}[slot="${this._slottedTagName}"]`); + if (slotted) { + e.stopPropagation(); + } if (this._passwordVisibilityChanging) { this._cachedChangeEvent = e; } else { diff --git a/src/vaadin-text-area.html b/src/vaadin-text-area.html index 185db3be..5ba8b8f8 100644 --- a/src/vaadin-text-area.html +++ b/src/vaadin-text-area.html @@ -56,26 +56,9 @@ - + + +
@@ -173,6 +156,10 @@ } } + get _slottedTagName() { + return 'textarea'; + } + _textAreaValueChanged(value) { this._updateHeight(); } diff --git a/src/vaadin-text-field-mixin.html b/src/vaadin-text-field-mixin.html index e596dba0..81169a22 100644 --- a/src/vaadin-text-field-mixin.html +++ b/src/vaadin-text-field-mixin.html @@ -47,7 +47,9 @@ } /* Reset the native input styles */ - [part="value"] { + [part="value"], + [part="input-field"] ::slotted(input), + [part="input-field"] ::slotted(textarea) { -webkit-appearance: none; -moz-appearance: none; outline: none; @@ -69,8 +71,10 @@ flex: none; } - /* Slotted by vaadin-dropdown-menu-text-field */ [part="value"], + [part="input-field"] ::slotted(input), + [part="input-field"] ::slotted(textarea), + /* Slotted by vaadin-select-text-field */ [part="input-field"] ::slotted([part="value"]) { flex: auto; white-space: nowrap; @@ -79,7 +83,12 @@ height: 100%; } - [part="value"]::-ms-clear { + [part="input-field"] ::slotted(textarea) { + resize: none; + } + + [part="value"]::-ms-clear, + [part="input-field"] ::slotted(input)::-ms-clear { display: none; } @@ -100,6 +109,18 @@ */ window.Vaadin = window.Vaadin || {}; + const HOST_PROPS = { + default: ['list', 'autofocus', 'pattern', 'autocapitalize', 'autocorrect', 'maxlength', + 'minlength', 'name', 'placeholder', 'autocomplete', 'title'], + accessible: ['disabled', 'readonly', 'required', 'invalid'] + }; + + const PROP_TYPE = { + DEFAULT: 'default', + ACCESSIBLE: 'accessible' + }; + + /** * @polymerMixin * @memberof Vaadin @@ -259,11 +280,26 @@ } static get observers() { - return ['_stateChanged(disabled, readonly, clearButtonVisible, hasValue)']; + return ['_stateChanged(disabled, readonly, clearButtonVisible, hasValue)', + '_hostPropsChanged(' + HOST_PROPS.default.join(', ') + ')', + '_hostAccessiblePropsChanged(' + HOST_PROPS.accessible.join(', ') + ')', + '_getActiveErrorId(invalid, errorMessage, _errorId)', + '_getActiveLabelId(label, _labelId)']; } get focusElement() { - return this.root && this.root.querySelector('[part=value]'); + if (!this.shadowRoot) { + return; + } + const slotted = this.querySelector(`${this._slottedTagName}[slot="${this._slottedTagName}"]`); + if (slotted) { + return slotted; + } + return this.shadowRoot.querySelector('[part="value"]'); + } + + get _slottedTagName() { + return 'input'; } _onInput(e) { @@ -271,8 +307,11 @@ const input = this.focusElement; if (input.value.length > 0 && !this.checkValidity()) { input.value = this.value || ''; + return; } } + this.__userInput = true; + this.value = e.target.value; } // NOTE(yuriy): Workaround needed for IE11 and Edge for proper displaying @@ -311,14 +350,25 @@ if (newVal === '' && oldVal === undefined) { return; } - if (this.invalid) { - this.validate(); - } + if (newVal !== '' && newVal != null) { this.hasValue = true; } else { this.hasValue = false; } + + if (this.__userInput) { + this.__userInput = false; + return; + } else if (newVal !== undefined) { + this.focusElement.value = newVal; + } else { + this.value = ''; + } + + if (this.invalid) { + this.validate(); + } } _labelChanged(label) { @@ -329,6 +379,74 @@ } } + _onSlotChange() { + const slotted = this.querySelector(`${this._slottedTagName}[slot="${this._slottedTagName}"]`); + + if (this.value) { + this.focusElement.value = this.value; + this.validate(); + } + + if (slotted && !this._slottedInput) { + this._validateSlottedValue(slotted); + this._addInputListeners(slotted); + this._addIEListeners(slotted); + this._slottedInput = slotted; + } else if (!this._slottedInput) { + this._removeInputListeners(this._slottedInput); + this._removeIEListeners(this._slottedInput); + this._slottedInput = undefined; + } + + Object.keys(PROP_TYPE).map(key => PROP_TYPE[key]).forEach(type => + this._propagateHostAttributes(HOST_PROPS[type].map(attr => this[attr]), type)); + } + + _hostPropsChanged(...attributesValues) { + this._propagateHostAttributes(attributesValues, PROP_TYPE.DEFAULT); + } + + _hostAccessiblePropsChanged(...attributesValues) { + this._propagateHostAttributes(attributesValues, PROP_TYPE.ACCESSIBLE); + } + + _validateSlottedValue(slotted) { + if (slotted.value !== this.value) { + console.warn('Please define value on the vaadin-text-field component!'); + slotted.value = ''; + } + } + + _propagateHostAttributes(attributesValues, type) { + const input = this.focusElement; + const attributeNames = HOST_PROPS[type]; + + if (type === 'accessible') { + attributeNames.forEach((attr, index) => { + this._setOrToggleAttribute(attr, attributesValues[index], input); + this._setOrToggleAttribute(`aria-${attr}`, attributesValues[index], input); + }); + } else { + attributeNames.forEach((attr, index) => { + this._setOrToggleAttribute(attr, attributesValues[index], input); + }); + } + } + + _setOrToggleAttribute(name, value, node) { + if (!name || !node) { + return; + } + + if (node.hasAttribute(name) === !value) { + if (value) { + node.setAttribute(name, (typeof value === 'boolean') ? '' : value); + } else { + node.removeAttribute(name); + } + } + } + /** * Returns true if the current input value satisfies all constraints (if any) * @returns {boolean} @@ -341,9 +459,37 @@ } } + _addInputListeners(node) { + node.addEventListener('input', this._boundOnInput); + node.addEventListener('change', this._boundOnChange); + node.addEventListener('blur', this._boundOnBlur); + } + + _removeInputListeners(node) { + node.removeEventListener('input', this._boundOnInput); + node.removeEventListener('change', this._boundOnChange); + node.removeEventListener('blur', this._boundOnBlur); + } ready() { super.ready(); + + this._boundOnInput = this._onInput.bind(this); + this._boundOnChange = this._onChange.bind(this); + this._boundOnBlur = this.validate.bind(this); + + const defaultInput = this.shadowRoot.querySelector('[part="value"]'); + this._slottedInput = this.querySelector(`${this._slottedTagName}[slot="${this._slottedTagName}"]`); + this._addInputListeners(defaultInput); + this._addIEListeners(defaultInput); + if (this._slottedInput) { + this._addIEListeners(this._slottedInput); + this._addInputListeners(this._slottedInput); + } + + this.shadowRoot.querySelector('[name="input"], [name="textarea"]') + .addEventListener('slotchange', this._onSlotChange.bind(this)); + if (!(window.ShadyCSS && window.ShadyCSS.nativeCss)) { this.updateStyles(); } @@ -355,11 +501,6 @@ var uniqueId = Vaadin.TextFieldMixin._uniqueId = 1 + Vaadin.TextFieldMixin._uniqueId || 0; this._errorId = `${this.constructor.is}-error-${uniqueId}`; this._labelId = `${this.constructor.is}-label-${uniqueId}`; - - /* istanbul ignore if */ - if (navigator.userAgent.match(/Trident/)) { - this._addIEListeners(); - } } /** @@ -381,7 +522,7 @@ this.focusElement.focus(); this.clear(); this._valueClearing = false; - this.focusElement.dispatchEvent(new Event('change', {bubbles: true})); + this.focusElement.dispatchEvent(new Event('change', {bubbles: !this._slottedInput})); } _onKeyDown(e) { @@ -390,31 +531,46 @@ } } - _addIEListeners() { - // IE11 dispatches `input` event in following cases: - // - focus or blur, when placeholder attribute is set - // - placeholder attribute value changed - // https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/101220/ - const prevent = e => { - e.stopImmediatePropagation(); - this.focusElement.removeEventListener('input', prevent); - }; - const shouldPreventInput = () => this.placeholder && this.focusElement.addEventListener('input', prevent); - this.focusElement.addEventListener('focusin', shouldPreventInput); - this.focusElement.addEventListener('focusout', shouldPreventInput); - this._createPropertyObserver('placeholder', shouldPreventInput); + _addIEListeners(node) { + /* istanbul ignore if */ + if (navigator.userAgent.match(/Trident/)) { + // IE11 dispatches `input` event in following cases: + // - focus or blur, when placeholder attribute is set + // - placeholder attribute value changed + // https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/101220/ + this._prevent = e => { + e.stopImmediatePropagation(); + this.focusElement.removeEventListener('input', this._prevent); + }; + this._shouldPreventInput = () => this.placeholder && node.addEventListener('input', this._prevent); + node.addEventListener('focusin', this._shouldPreventInput); + node.addEventListener('focusout', this._shouldPreventInput); + this._createPropertyObserver('placeholder', this._shouldPreventInput); + } + } + + _removeIEListeners(node) { + /* istanbul ignore if */ + if (navigator.userAgent.match(/Trident/)) { + node.removeEventListener('focusin', this._shouldPreventInput); + node.removeEventListener('focusout', this._shouldPreventInput); + } } _getActiveErrorId(invalid, errorMessage, errorId) { - return errorMessage && invalid ? errorId : undefined; + this._setOrToggleAttribute('aria-describedby', + (errorMessage && invalid ? errorId : undefined), + this.focusElement); } _getActiveLabelId(label, labelId) { - return label ? labelId : undefined; + this._setOrToggleAttribute('aria-labelledby', + (label ? labelId : undefined), + this.focusElement); } _getErrorMessageAriaHidden(invalid, errorMessage, errorId) { - return (!this._getActiveErrorId(invalid, errorMessage, errorId)).toString(); + return (!(errorMessage && invalid ? errorId : undefined)).toString(); } /** diff --git a/src/vaadin-text-field.html b/src/vaadin-text-field.html index 7c685283..d3fbee5b 100644 --- a/src/vaadin-text-field.html +++ b/src/vaadin-text-field.html @@ -22,30 +22,9 @@ - + + +
diff --git a/test/accessibility.html b/test/accessibility.html index cad15f07..9f42a984 100644 --- a/test/accessibility.html +++ b/test/accessibility.html @@ -19,18 +19,42 @@ + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/password-field.html b/test/password-field.html index 265b9005..09edd8a7 100644 --- a/test/password-field.html +++ b/test/password-field.html @@ -19,127 +19,141 @@ - diff --git a/test/test-suites.js b/test/test-suites.js index 76a27d8a..7cad4ce1 100644 --- a/test/test-suites.js +++ b/test/test-suites.js @@ -3,5 +3,6 @@ window.VaadinTextFieldSuites = [ 'text-area.html', 'password-field.html', 'validation.html', - 'accessibility.html' + 'accessibility.html', + 'custom-input.html' ]; diff --git a/test/text-area.html b/test/text-area.html index b6b471e4..a1a94b3a 100644 --- a/test/text-area.html +++ b/test/text-area.html @@ -18,338 +18,352 @@ + + + + diff --git a/test/text-field.html b/test/text-field.html index e2cedf60..c3c0d4e6 100644 --- a/test/text-field.html +++ b/test/text-field.html @@ -19,273 +19,287 @@ + + + + diff --git a/test/validation.html b/test/validation.html index d4730b20..b38adbcf 100644 --- a/test/validation.html +++ b/test/validation.html @@ -10,6 +10,7 @@ + @@ -24,6 +25,19 @@ + + + + - diff --git a/test/visual/screens/vaadin-text-field/password-field-custom-input-lumo/password-field-custom-input/chrome.png b/test/visual/screens/vaadin-text-field/password-field-custom-input-lumo/password-field-custom-input/chrome.png new file mode 100644 index 00000000..ff858482 Binary files /dev/null and b/test/visual/screens/vaadin-text-field/password-field-custom-input-lumo/password-field-custom-input/chrome.png differ diff --git a/test/visual/screens/vaadin-text-field/password-field-custom-input-lumo/password-field-custom-input/firefox.png b/test/visual/screens/vaadin-text-field/password-field-custom-input-lumo/password-field-custom-input/firefox.png new file mode 100644 index 00000000..3e351e5e Binary files /dev/null and b/test/visual/screens/vaadin-text-field/password-field-custom-input-lumo/password-field-custom-input/firefox.png differ diff --git a/test/visual/screens/vaadin-text-field/password-field-custom-input-material/password-field-custom-input/chrome.png b/test/visual/screens/vaadin-text-field/password-field-custom-input-material/password-field-custom-input/chrome.png new file mode 100644 index 00000000..685c6736 Binary files /dev/null and b/test/visual/screens/vaadin-text-field/password-field-custom-input-material/password-field-custom-input/chrome.png differ diff --git a/test/visual/screens/vaadin-text-field/password-field-custom-input-material/password-field-custom-input/firefox.png b/test/visual/screens/vaadin-text-field/password-field-custom-input-material/password-field-custom-input/firefox.png new file mode 100644 index 00000000..2d128f26 Binary files /dev/null and b/test/visual/screens/vaadin-text-field/password-field-custom-input-material/password-field-custom-input/firefox.png differ diff --git a/test/visual/screens/vaadin-text-field/text-area-custom-textarea-lumo/text-area-custom-textarea/chrome.png b/test/visual/screens/vaadin-text-field/text-area-custom-textarea-lumo/text-area-custom-textarea/chrome.png new file mode 100644 index 00000000..a459edba Binary files /dev/null and b/test/visual/screens/vaadin-text-field/text-area-custom-textarea-lumo/text-area-custom-textarea/chrome.png differ diff --git a/test/visual/screens/vaadin-text-field/text-area-custom-textarea-lumo/text-area-custom-textarea/firefox.png b/test/visual/screens/vaadin-text-field/text-area-custom-textarea-lumo/text-area-custom-textarea/firefox.png new file mode 100644 index 00000000..c03df701 Binary files /dev/null and b/test/visual/screens/vaadin-text-field/text-area-custom-textarea-lumo/text-area-custom-textarea/firefox.png differ diff --git a/test/visual/screens/vaadin-text-field/text-area-custom-textarea-material/text-area-custom-textarea/chrome.png b/test/visual/screens/vaadin-text-field/text-area-custom-textarea-material/text-area-custom-textarea/chrome.png new file mode 100644 index 00000000..a5a8d525 Binary files /dev/null and b/test/visual/screens/vaadin-text-field/text-area-custom-textarea-material/text-area-custom-textarea/chrome.png differ diff --git a/test/visual/screens/vaadin-text-field/text-area-custom-textarea-material/text-area-custom-textarea/firefox.png b/test/visual/screens/vaadin-text-field/text-area-custom-textarea-material/text-area-custom-textarea/firefox.png new file mode 100644 index 00000000..c22b5ecc Binary files /dev/null and b/test/visual/screens/vaadin-text-field/text-area-custom-textarea-material/text-area-custom-textarea/firefox.png differ diff --git a/test/visual/screens/vaadin-text-field/text-field-custom-input-lumo/text-field-custom-input/chrome.png b/test/visual/screens/vaadin-text-field/text-field-custom-input-lumo/text-field-custom-input/chrome.png new file mode 100644 index 00000000..806207ab Binary files /dev/null and b/test/visual/screens/vaadin-text-field/text-field-custom-input-lumo/text-field-custom-input/chrome.png differ diff --git a/test/visual/screens/vaadin-text-field/text-field-custom-input-lumo/text-field-custom-input/firefox.png b/test/visual/screens/vaadin-text-field/text-field-custom-input-lumo/text-field-custom-input/firefox.png new file mode 100644 index 00000000..13c37edf Binary files /dev/null and b/test/visual/screens/vaadin-text-field/text-field-custom-input-lumo/text-field-custom-input/firefox.png differ diff --git a/test/visual/screens/vaadin-text-field/text-field-custom-input-material/text-field-custom-input/chrome.png b/test/visual/screens/vaadin-text-field/text-field-custom-input-material/text-field-custom-input/chrome.png new file mode 100644 index 00000000..9d37156b Binary files /dev/null and b/test/visual/screens/vaadin-text-field/text-field-custom-input-material/text-field-custom-input/chrome.png differ diff --git a/test/visual/screens/vaadin-text-field/text-field-custom-input-material/text-field-custom-input/firefox.png b/test/visual/screens/vaadin-text-field/text-field-custom-input-material/text-field-custom-input/firefox.png new file mode 100644 index 00000000..f05e319b Binary files /dev/null and b/test/visual/screens/vaadin-text-field/text-field-custom-input-material/text-field-custom-input/firefox.png differ diff --git a/test/visual/vaadin-password-field/password-field-custom-input.html b/test/visual/vaadin-password-field/password-field-custom-input.html new file mode 100644 index 00000000..28f50119 --- /dev/null +++ b/test/visual/vaadin-password-field/password-field-custom-input.html @@ -0,0 +1,29 @@ + + + + + + + + + + + + +
+ + + +
+ + diff --git a/test/visual/vaadin-text-area/styling.html b/test/visual/vaadin-text-area/styling.html index 668f2224..bbe1022b 100644 --- a/test/visual/vaadin-text-area/styling.html +++ b/test/visual/vaadin-text-area/styling.html @@ -26,7 +26,11 @@ font-weight: bold; } - [part~="value"] { + /* NOTE(yuriy): Targeting slotted elements as workaround for + gemini firfox 47 to overcome stronger selector */ + [part="value"], + [part="input-field"] ::slotted(input), + [part="input-field"] ::slotted(textarea) { background-color: red; border: none; --_lumo-text-field-overflow-mask-image: none; diff --git a/test/visual/vaadin-text-area/text-area-custom-textarea.html b/test/visual/vaadin-text-area/text-area-custom-textarea.html new file mode 100644 index 00000000..539c3d1b --- /dev/null +++ b/test/visual/vaadin-text-area/text-area-custom-textarea.html @@ -0,0 +1,29 @@ + + + + + + + + + + + + +
+ + + +
+ + diff --git a/test/visual/vaadin-text-field/styling.html b/test/visual/vaadin-text-field/styling.html index fe1787b2..bd1c6e9b 100644 --- a/test/visual/vaadin-text-field/styling.html +++ b/test/visual/vaadin-text-field/styling.html @@ -26,7 +26,11 @@ font-weight: bold; } - [part~="value"] { + /* NOTE(yuriy): Targeting slotted elements as workaround for + gemini firfox 47 to overcome stronger selector */ + [part="value"], + [part="input-field"] ::slotted(input), + [part="input-field"] ::slotted(textarea) { background-color: red; border: none; --_lumo-text-field-overflow-mask-image: none; diff --git a/test/visual/vaadin-text-field/text-field-custom-input.html b/test/visual/vaadin-text-field/text-field-custom-input.html new file mode 100644 index 00000000..1ae28129 --- /dev/null +++ b/test/visual/vaadin-text-field/text-field-custom-input.html @@ -0,0 +1,29 @@ + + + + + + + + + + + + +
+ + + +
+ + diff --git a/theme/lumo/vaadin-placeholder-styles.html b/theme/lumo/vaadin-placeholder-styles.html new file mode 100644 index 00000000..64f99ede --- /dev/null +++ b/theme/lumo/vaadin-placeholder-styles.html @@ -0,0 +1,61 @@ + + + diff --git a/theme/lumo/vaadin-text-area-styles.html b/theme/lumo/vaadin-text-area-styles.html index 9f49d8e1..38344c11 100644 --- a/theme/lumo/vaadin-text-area-styles.html +++ b/theme/lumo/vaadin-text-area-styles.html @@ -5,7 +5,8 @@ + + diff --git a/theme/material/vaadin-text-field-styles.html b/theme/material/vaadin-text-field-styles.html index 11842aa1..1446f163 100644 --- a/theme/material/vaadin-text-field-styles.html +++ b/theme/material/vaadin-text-field-styles.html @@ -76,13 +76,17 @@ :host([disabled]) [part="label"], :host([disabled]) [part="value"], + :host([disabled]) [part="input-field"] ::slotted(input), + :host([disabled]) [part="input-field"] ::slotted(textarea), :host([disabled]) [part="input-field"] ::slotted([part="value"]) { color: var(--material-disabled-text-color); -webkit-text-fill-color: var(--material-disabled-text-color); } [part="value"], - /* For vaadin-dropdown-menu-text-field */ + :host([disabled]) [part="input-field"] ::slotted(input), + :host([disabled]) [part="input-field"] ::slotted(textarea), + /* Slotted by vaadin-select-text-field */ [part="input-field"] ::slotted([part="value"]) { outline: none; margin: 0; @@ -209,7 +213,7 @@ /* Slotted content */ - [part="input-field"] ::slotted(*:not([part="value"])) { + [part="input-field"] ::slotted(*:not([part="value"]):not(input):not(textarea)) { color: var(--material-secondary-text-color); }