From b116329e537f76dce9cff6058315e5551f0c696b Mon Sep 17 00:00:00 2001 From: web-padawan Date: Fri, 23 Jul 2021 16:20:57 +0300 Subject: [PATCH 1/3] feat: add TextArea base class --- .../src/vaadin-input-container.js | 5 + packages/text-area/LICENSE | 190 +++++++++ packages/text-area/README.md | 5 + packages/text-area/package.json | 44 +++ packages/text-area/src/vaadin-text-area.d.ts | 12 + packages/text-area/src/vaadin-text-area.js | 206 ++++++++++ packages/text-area/test/.eslintrc.json | 5 + packages/text-area/test/text-area.test.js | 372 ++++++++++++++++++ packages/text-area/vaadin-text-area.d.ts | 1 + packages/text-area/vaadin-text-area.js | 1 + 10 files changed, 841 insertions(+) create mode 100644 packages/text-area/LICENSE create mode 100644 packages/text-area/README.md create mode 100644 packages/text-area/package.json create mode 100644 packages/text-area/src/vaadin-text-area.d.ts create mode 100644 packages/text-area/src/vaadin-text-area.js create mode 100644 packages/text-area/test/.eslintrc.json create mode 100644 packages/text-area/test/text-area.test.js create mode 100644 packages/text-area/vaadin-text-area.d.ts create mode 100644 packages/text-area/vaadin-text-area.js diff --git a/packages/input-container/src/vaadin-input-container.js b/packages/input-container/src/vaadin-input-container.js index 4bed9dbc0ad..61326f91f3e 100644 --- a/packages/input-container/src/vaadin-input-container.js +++ b/packages/input-container/src/vaadin-input-container.js @@ -29,6 +29,11 @@ export class InputContainer extends ThemableMixin(DirMixin(PolymerElement)) { ::slotted(input) { -webkit-appearance: none; -moz-appearance: none; + flex: auto; + white-space: nowrap; + overflow: hidden; + width: 100%; + height: 100%; outline: none; flex: auto; white-space: nowrap; diff --git a/packages/text-area/LICENSE b/packages/text-area/LICENSE new file mode 100644 index 00000000000..b577ea38081 --- /dev/null +++ b/packages/text-area/LICENSE @@ -0,0 +1,190 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright 2021 Vaadin Ltd. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/text-area/README.md b/packages/text-area/README.md new file mode 100644 index 00000000000..4dd6bf2bc31 --- /dev/null +++ b/packages/text-area/README.md @@ -0,0 +1,5 @@ +# @vaadin/text-area + +> ⚠️ Work in progress, please do not use this component yet. + +The new version of `vaadin-text-area` component using slotted ``. diff --git a/packages/text-area/package.json b/packages/text-area/package.json new file mode 100644 index 00000000000..d49575b6826 --- /dev/null +++ b/packages/text-area/package.json @@ -0,0 +1,44 @@ +{ + "name": "@vaadin/text-area", + "version": "22.0.0-alpha1", + "description": "vaadin-text-area", + "main": "vaadin-text-area.js", + "module": "vaadin-text-area.js", + "repository": "vaadin/web-components", + "keywords": [ + "Vaadin", + "input", + "web-components", + "web-component" + ], + "author": "Vaadin Ltd", + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/vaadin/web-components/issues" + }, + "homepage": "https://vaadin.com/components", + "files": [ + "vaadin-*.d.ts", + "vaadin-*.js", + "src", + "theme" + ], + "dependencies": { + "@polymer/polymer": "^3.0.0", + "@vaadin/field-base": "^22.0.0-alpha1", + "@vaadin/input-container": "^22.0.0-alpha1", + "@vaadin/text-field": "^22.0.0-alpha1", + "@vaadin/vaadin-element-mixin": "^22.0.0-alpha1", + "@vaadin/vaadin-lumo-styles": "^22.0.0-alpha1", + "@vaadin/vaadin-material-styles": "^22.0.0-alpha1", + "@vaadin/vaadin-themable-mixin": "^22.0.0-alpha1" + }, + "devDependencies": { + "@esm-bundle/chai": "^4.1.5", + "@vaadin/testing-helpers": "^0.2.1", + "sinon": "^9.2.1" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/packages/text-area/src/vaadin-text-area.d.ts b/packages/text-area/src/vaadin-text-area.d.ts new file mode 100644 index 00000000000..fb425b95d93 --- /dev/null +++ b/packages/text-area/src/vaadin-text-area.d.ts @@ -0,0 +1,12 @@ +/** + * @license + * Copyright (c) 2021 Vaadin Ltd. + * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/ + */ +import { ElementMixin } from '@vaadin/vaadin-element-mixin/vaadin-element-mixin.js'; +import { TextFieldMixin } from '@vaadin/field-base/src/text-field-mixin.js'; +import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js'; + +declare class TextArea extends TextFieldMixin(ThemableMixin(ElementMixin(HTMLElement))) {} + +export { TextArea }; diff --git a/packages/text-area/src/vaadin-text-area.js b/packages/text-area/src/vaadin-text-area.js new file mode 100644 index 00000000000..917a99ea196 --- /dev/null +++ b/packages/text-area/src/vaadin-text-area.js @@ -0,0 +1,206 @@ +/** + * @license + * Copyright (c) 2021 Vaadin Ltd. + * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/ + */ +import { PolymerElement, html } from '@polymer/polymer'; +import { ElementMixin } from '@vaadin/vaadin-element-mixin/vaadin-element-mixin.js'; +import { TextFieldMixin } from '@vaadin/field-base/src/text-field-mixin.js'; +import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js'; +import '@vaadin/input-container/src/vaadin-input-container.js'; +import '@vaadin/text-field/src/vaadin-input-field-shared-styles.js'; + +export class TextArea extends TextFieldMixin(ThemableMixin(ElementMixin(PolymerElement))) { + static get is() { + return 'vaadin-text-area'; + } + + static get version() { + return '22.0.0-alpha1'; + } + + static get template() { + return html` + + +
+
+ +
+ + + +
+ +
+ +
+
+ +
+ +
+ +
+ +
+
+ `; + } + + get slots() { + const slots = super.slots; + + delete slots.input; + + return { + ...slots, + textarea: () => { + const native = document.createElement('textarea'); + const value = this.getAttribute('value'); + if (value) { + native.setAttribute('value', value); + } + const name = this.getAttribute('name'); + if (name) { + native.setAttribute('name', name); + } + return native; + } + }; + } + + /** @protected */ + get clearElement() { + return this.$.clearButton; + } + + /** @protected */ + get _inputNode() { + return this._getDirectSlotChild('textarea'); + } + + /** @protected */ + connectedCallback() { + super.connectedCallback(); + + this._updateHeight(); + } + + /** @protected */ + ready() { + super.ready(); + this.addEventListener('animationend', this._onAnimationEnd); + } + + /** @private */ + _onAnimationEnd(e) { + if (e.animationName.indexOf('vaadin-text-area-appear') === 0) { + this._updateHeight(); + } + } + + /** + * @param {unknown} newVal + * @param {unknown} oldVal + * @protected + */ + _valueChanged(newVal, oldVal) { + super._valueChanged(newVal, oldVal); + + this._updateHeight(); + } + + /** @private */ + _updateHeight() { + if (this._inputNode) { + /* https://css-tricks.com/the-cleanest-trick-for-autogrowing-textareas/ */ + this.__textAreaWrapper = this.__textAreaWrapper || this.shadowRoot.querySelector('.textarea-wrapper'); + this.__textAreaWrapper.dataset.replicatedValue = this._inputNode.value; + // getComputedStyle is expensive, maybe we can use ResizeObserver in the future + this._dispatchIronResizeEventIfNeeded('InputHeight', getComputedStyle(this.__textAreaWrapper).height); + } + } +} diff --git a/packages/text-area/test/.eslintrc.json b/packages/text-area/test/.eslintrc.json new file mode 100644 index 00000000000..7eeefc33b66 --- /dev/null +++ b/packages/text-area/test/.eslintrc.json @@ -0,0 +1,5 @@ +{ + "env": { + "mocha": true + } +} diff --git a/packages/text-area/test/text-area.test.js b/packages/text-area/test/text-area.test.js new file mode 100644 index 00000000000..c10693fca80 --- /dev/null +++ b/packages/text-area/test/text-area.test.js @@ -0,0 +1,372 @@ +import { expect } from '@esm-bundle/chai'; +import sinon from 'sinon'; +import { fixtureSync, oneEvent } from '@vaadin/testing-helpers'; +import { TextArea } from '../src/vaadin-text-area.js'; + +customElements.define('vaadin-text-area', TextArea); + +describe('text-area', () => { + let textArea; + + beforeEach(() => { + textArea = fixtureSync(''); + }); + + describe('properties', () => { + let native; + + beforeEach(() => { + native = textArea._inputNode; + }); + + describe('native', () => { + function assertAttrCanBeSet(prop, value) { + textArea[prop] = value; + const attrValue = native.getAttribute(prop); + + if (value === true) { + expect(attrValue).not.to.be.null; + } else if (value === false) { + expect(attrValue).to.be.null; + } else if (value) { + expect(attrValue).to.be.equal(String(value)); + } + } + + function assertPropCanBeSet(prop, value) { + for (let i = 0; i < 3; i++) { + // Check different values + const newValue = typeof value === 'boolean' ? i % 2 === 0 : value + i; + textArea[prop] = newValue; + expect(native[prop]).to.be.equal(newValue); + } + } + + ['placeholder', 'value'].forEach((prop) => { + it('should set string property ' + prop, () => { + assertPropCanBeSet(prop, 'foo'); + }); + }); + + ['disabled'].forEach((prop) => { + it('should set boolean property ' + prop, () => { + assertPropCanBeSet(prop, true); + }); + }); + + ['maxlength', 'minlength'].forEach((prop) => { + it('should set numeric attribute ' + prop, () => { + assertAttrCanBeSet(prop, 2); + }); + }); + + ['autocomplete'].forEach((prop) => { + it('should set boolean attribute ' + prop, () => { + assertAttrCanBeSet(prop, 'on'); + }); + }); + + ['autocapitalize'].forEach((prop) => { + it('should set boolean attribute ' + prop, () => { + assertAttrCanBeSet(prop, 'none'); + }); + }); + + ['autocomplete', 'autocorrect', 'readonly', 'required'].forEach((prop) => { + it('should set boolean attribute ' + prop, () => { + assertAttrCanBeSet(prop, true); + assertAttrCanBeSet(prop, false); + }); + }); + + describe('input field', () => { + let inputField; + + beforeEach(() => { + inputField = textArea.shadowRoot.querySelector('[part="input-field"]'); + }); + + it('should propagate invalid property to the input container', () => { + textArea.invalid = true; + expect(inputField.invalid).to.be.true; + + textArea.invalid = false; + expect(inputField.invalid).to.be.false; + }); + + it('should propagate readonly property to the input container', () => { + textArea.readonly = true; + expect(inputField.readonly).to.be.true; + + textArea.readonly = false; + expect(inputField.readonly).to.be.false; + }); + + it('should propagate disabled property to the input container', () => { + textArea.disabled = true; + expect(inputField.disabled).to.be.true; + + textArea.disabled = false; + expect(inputField.disabled).to.be.false; + }); + + it('should propagate theme attribute to the input container', () => { + textArea.setAttribute('theme', 'align-center'); + expect(inputField.getAttribute('theme')).to.equal('align-center'); + }); + }); + }); + + describe('binding', () => { + it('default value should be empty string', () => { + expect(textArea.value).to.be.equal(''); + }); + + it('setting textarea value updates value', () => { + native.value = 'foo'; + native.dispatchEvent(new Event('input', { bubbles: true, cancelable: true, composed: true })); + expect(textArea.value).to.be.equal('foo'); + }); + + it('setting native value updates has-value attribute', () => { + textArea.value = 'foo'; + expect(textArea.hasAttribute('has-value')).to.be.true; + }); + + it('setting value to undefined should not update has-value attribute', () => { + textArea.value = undefined; + expect(textArea.hasAttribute('has-value')).to.be.false; + }); + + it('setting empty value does not update has-value attribute', () => { + textArea.value = ''; + expect(textArea.hasAttribute('has-value')).to.be.false; + }); + + // User could accidentally set a 0 or false value + it('setting number value updates has-value attribute', () => { + textArea.value = 0; + expect(textArea.hasAttribute('has-value')).to.be.true; + }); + + it('setting boolean value updates has-value attribute', () => { + textArea.value = false; + expect(textArea.hasAttribute('has-value')).to.be.true; + }); + }); + + describe('label', () => { + it('should not update focused property on click if disabled', () => { + textArea.disabled = true; + const label = textArea.shadowRoot.querySelector('[part="label"]'); + label.click(); + expect(textArea.getAttribute('focused')).to.be.null; + }); + }); + }); + + describe('vaadin-text-area-appear', () => { + it('should update height on show after hidden', async () => { + const savedHeight = textArea.clientHeight; + textArea.hidden = true; + // Three new lines will expand initial height + textArea.value = '\n\n\n'; + textArea.hidden = false; + await oneEvent(textArea, 'animationend'); + expect(textArea.clientHeight).to.be.above(savedHeight); + }); + + it('should not update height on custom animation name', () => { + const spy = sinon.spy(textArea, '_updateHeight'); + const ev = new Event('animationend'); + ev.animationName = 'foo'; + textArea.dispatchEvent(ev); + expect(spy.called).to.be.false; + }); + }); + + describe('multi-line', () => { + let native, container, inputField; + + beforeEach(() => { + native = textArea._inputNode; + inputField = textArea.shadowRoot.querySelector('[part=input-field]'); + container = textArea.shadowRoot.querySelector('[part=container]'); + }); + + it('should grow height with unwrapped text', () => { + const originalHeight = parseInt(window.getComputedStyle(inputField).height); + + // Make sure there are enough characters to grow the textarea + textArea.value = Array(400).join('400'); + + const newHeight = parseInt(window.getComputedStyle(inputField).height); + expect(newHeight).to.be.at.least(originalHeight + 10); + }); + + it('should not grow over max-height', () => { + inputField.style.padding = '0'; + inputField.style.border = 'none'; + textArea.style.maxHeight = '100px'; + + textArea.value = ` + there + should + be + a + lot + of + rows`; + + expect(parseFloat(window.getComputedStyle(textArea).height)).to.be.lte(100); + expect(parseFloat(window.getComputedStyle(container).height)).to.be.lte(100); + expect(parseFloat(window.getComputedStyle(inputField).height)).to.be.lte(100); + }); + + it('should not shrink less than min-height', () => { + textArea.style.minHeight = '125px'; + + expect(window.getComputedStyle(textArea).height).to.be.equal('125px'); + expect(window.getComputedStyle(container).height).to.be.equal('125px'); + expect(parseFloat(window.getComputedStyle(inputField).height)).to.be.above(100); + + // Check that value modification doesn't break min-height rule + textArea.value = '1 row'; + + expect(window.getComputedStyle(textArea).height).to.be.equal('125px'); + expect(window.getComputedStyle(container).height).to.be.equal('125px'); + expect(parseFloat(window.getComputedStyle(inputField).height)).to.be.above(100); + }); + + it('should stay between min and max height', () => { + textArea.style.minHeight = '100px'; + textArea.style.maxHeight = '175px'; + + expect(window.getComputedStyle(textArea).height).to.be.equal('100px'); + expect(window.getComputedStyle(container).height).to.be.equal('100px'); + + // Check that value modification doesn't break min-height rule + textArea.value = ` + there + should + be + a + lot + of + rows + and + more + and + even + more`; + + expect(window.getComputedStyle(textArea).height).to.be.equal('175px'); + expect(window.getComputedStyle(container).height).to.be.equal('175px'); + expect(parseFloat(window.getComputedStyle(inputField).height)).to.be.above(150); + }); + + it('should increase inputField height', () => { + textArea.style.height = '200px'; + textArea.value = 'foo'; + expect(inputField.clientHeight).to.be.closeTo(200, 10); + }); + + it('should maintain scroll top', () => { + textArea.style.maxHeight = '100px'; + textArea.value = Array(400).join('400'); + inputField.scrollTop = 200; + textArea.value += 'foo'; + expect(inputField.scrollTop).to.equal(200); + }); + + it('should decrease height automatically', () => { + textArea.value = Array(400).join('400'); + const height = textArea.clientHeight; + textArea.value = ''; + expect(textArea.clientHeight).to.be.below(height); + }); + + it('should not change height', () => { + textArea.style.maxHeight = '100px'; + textArea.value = Array(400).join('400'); + const height = textArea.clientHeight; + + textArea.value = textArea.value.slice(0, -1); + expect(textArea.clientHeight).to.equal(height); + }); + + it('should have the correct width', () => { + textArea.style.width = '300px'; + expect(native.clientWidth).to.equal( + Math.round( + textArea.clientWidth - + parseFloat(getComputedStyle(inputField).marginLeft) - + parseFloat(getComputedStyle(inputField).marginRight) - + parseFloat(getComputedStyle(inputField).paddingLeft) - + parseFloat(getComputedStyle(inputField).paddingRight) - + parseFloat(getComputedStyle(inputField).borderLeftWidth) - + parseFloat(getComputedStyle(inputField).borderRightWidth) + ) + ); + }); + + it('should have matching height', () => { + inputField.style.padding = '0'; + textArea.style.maxHeight = '100px'; + + textArea.value = Array(400).join('400'); + textArea.value = textArea.value.slice(0, -1); + expect(native.clientHeight).to.equal(inputField.scrollHeight); + }); + + it('should cover native field', () => { + inputField.style.padding = '0'; + inputField.style.border = 'none'; + textArea.style.minHeight = '300px'; + textArea.style.padding = '0'; + textArea.value = 'foo'; + + expect(native.clientHeight).to.equal( + Math.round( + textArea.clientHeight - + parseFloat(getComputedStyle(inputField).marginTop) - + parseFloat(getComputedStyle(inputField).marginBottom) - + parseFloat(getComputedStyle(inputField).paddingTop) - + parseFloat(getComputedStyle(inputField).paddingBottom) - + parseFloat(getComputedStyle(inputField).borderTopWidth) - + parseFloat(getComputedStyle(inputField).borderBottomWidth) + ) + ); + }); + }); + + describe('resize', () => { + let spy; + + beforeEach(() => { + spy = sinon.spy(); + textArea.addEventListener('iron-resize', spy); + }); + + it('should not dispatch `iron-resize` event on init', () => { + expect(spy.callCount).to.equal(0); + }); + + it('should dispatch `iron-resize` event on height change', () => { + textArea.value = ` + there + should + be + a + lot + of + rows`; + expect(spy.callCount).to.equal(1); + }); + + it('should not dispatch `iron-resize` event on value change if height did not change', () => { + textArea.value = 'just 1 row'; + expect(spy.callCount).to.equal(0); + }); + }); +}); diff --git a/packages/text-area/vaadin-text-area.d.ts b/packages/text-area/vaadin-text-area.d.ts new file mode 100644 index 00000000000..ab500a556f7 --- /dev/null +++ b/packages/text-area/vaadin-text-area.d.ts @@ -0,0 +1 @@ +export { TextArea } from './src/vaadin-text-area.js'; diff --git a/packages/text-area/vaadin-text-area.js b/packages/text-area/vaadin-text-area.js new file mode 100644 index 00000000000..ab500a556f7 --- /dev/null +++ b/packages/text-area/vaadin-text-area.js @@ -0,0 +1 @@ +export { TextArea } from './src/vaadin-text-area.js'; From bc7b4d1b84beaf9d2ae14f0b5c4a1a94e1dd793a Mon Sep 17 00:00:00 2001 From: web-padawan Date: Mon, 26 Jul 2021 16:44:43 +0300 Subject: [PATCH 2/3] feat: add Lumo and Material for text-area --- .../screenshots/text-area/baseline/basic.png | Bin 0 -> 500 bytes .../text-area/baseline/clear-button.png | Bin 0 -> 1505 bytes .../text-area/baseline/disabled.png | Bin 0 -> 462 bytes .../text-area/baseline/error-message.png | Bin 0 -> 2252 bytes .../text-area/baseline/helper-text.png | Bin 0 -> 1280 bytes .../screenshots/text-area/baseline/label.png | Bin 0 -> 1212 bytes .../text-area/baseline/placeholder.png | Bin 0 -> 1826 bytes .../screenshots/text-area/baseline/prefix.png | Bin 0 -> 790 bytes .../text-area/baseline/readonly.png | Bin 0 -> 772 bytes .../text-area/baseline/required.png | Bin 0 -> 1276 bytes .../screenshots/text-area/baseline/suffix.png | Bin 0 -> 794 bytes .../screenshots/text-area/baseline/value.png | Bin 0 -> 1289 bytes .../test/visual/lumo/text-area.test.js | 87 ++++++++++++++++++ .../screenshots/text-area/baseline/basic.png | Bin 0 -> 389 bytes .../text-area/baseline/clear-button.png | Bin 0 -> 1064 bytes .../text-area/baseline/disabled.png | Bin 0 -> 387 bytes .../text-area/baseline/error-message.png | Bin 0 -> 1944 bytes .../text-area/baseline/helper-text.png | Bin 0 -> 1072 bytes .../screenshots/text-area/baseline/label.png | Bin 0 -> 909 bytes .../text-area/baseline/placeholder.png | Bin 0 -> 1206 bytes .../screenshots/text-area/baseline/prefix.png | Bin 0 -> 647 bytes .../text-area/baseline/readonly.png | Bin 0 -> 389 bytes .../text-area/baseline/required.png | Bin 0 -> 999 bytes .../screenshots/text-area/baseline/suffix.png | Bin 0 -> 641 bytes .../screenshots/text-area/baseline/value.png | Bin 0 -> 914 bytes .../test/visual/material/text-area.test.js | 87 ++++++++++++++++++ .../theme/lumo/vaadin-text-area-styles.js | 68 ++++++++++++++ .../text-area/theme/lumo/vaadin-text-area.js | 8 ++ .../theme/material/vaadin-text-area-styles.js | 33 +++++++ .../theme/material/vaadin-text-area.js | 8 ++ packages/text-area/vaadin-text-area.js | 2 + 31 files changed, 293 insertions(+) create mode 100644 packages/text-area/test/visual/lumo/screenshots/text-area/baseline/basic.png create mode 100644 packages/text-area/test/visual/lumo/screenshots/text-area/baseline/clear-button.png create mode 100644 packages/text-area/test/visual/lumo/screenshots/text-area/baseline/disabled.png create mode 100644 packages/text-area/test/visual/lumo/screenshots/text-area/baseline/error-message.png create mode 100644 packages/text-area/test/visual/lumo/screenshots/text-area/baseline/helper-text.png create mode 100644 packages/text-area/test/visual/lumo/screenshots/text-area/baseline/label.png create mode 100644 packages/text-area/test/visual/lumo/screenshots/text-area/baseline/placeholder.png create mode 100644 packages/text-area/test/visual/lumo/screenshots/text-area/baseline/prefix.png create mode 100644 packages/text-area/test/visual/lumo/screenshots/text-area/baseline/readonly.png create mode 100644 packages/text-area/test/visual/lumo/screenshots/text-area/baseline/required.png create mode 100644 packages/text-area/test/visual/lumo/screenshots/text-area/baseline/suffix.png create mode 100644 packages/text-area/test/visual/lumo/screenshots/text-area/baseline/value.png create mode 100644 packages/text-area/test/visual/lumo/text-area.test.js create mode 100644 packages/text-area/test/visual/material/screenshots/text-area/baseline/basic.png create mode 100644 packages/text-area/test/visual/material/screenshots/text-area/baseline/clear-button.png create mode 100644 packages/text-area/test/visual/material/screenshots/text-area/baseline/disabled.png create mode 100644 packages/text-area/test/visual/material/screenshots/text-area/baseline/error-message.png create mode 100644 packages/text-area/test/visual/material/screenshots/text-area/baseline/helper-text.png create mode 100644 packages/text-area/test/visual/material/screenshots/text-area/baseline/label.png create mode 100644 packages/text-area/test/visual/material/screenshots/text-area/baseline/placeholder.png create mode 100644 packages/text-area/test/visual/material/screenshots/text-area/baseline/prefix.png create mode 100644 packages/text-area/test/visual/material/screenshots/text-area/baseline/readonly.png create mode 100644 packages/text-area/test/visual/material/screenshots/text-area/baseline/required.png create mode 100644 packages/text-area/test/visual/material/screenshots/text-area/baseline/suffix.png create mode 100644 packages/text-area/test/visual/material/screenshots/text-area/baseline/value.png create mode 100644 packages/text-area/test/visual/material/text-area.test.js create mode 100644 packages/text-area/theme/lumo/vaadin-text-area-styles.js create mode 100644 packages/text-area/theme/lumo/vaadin-text-area.js create mode 100644 packages/text-area/theme/material/vaadin-text-area-styles.js create mode 100644 packages/text-area/theme/material/vaadin-text-area.js diff --git a/packages/text-area/test/visual/lumo/screenshots/text-area/baseline/basic.png b/packages/text-area/test/visual/lumo/screenshots/text-area/baseline/basic.png new file mode 100644 index 0000000000000000000000000000000000000000..3dbc78f57e5642d68d441c9e567d498d4abf9649 GIT binary patch literal 500 zcmeAS@N?(olHy`uVBq!ia0vp^SAaN-gAGV-U$)~kkYX$ja(7}_cTVOdkiE{+#WAE} z&fD8}{SG;Zv|f~4p|qg${r@RyO7&u|q-RKO>J7WLYo6NkM>Pv`z2+{<)N(lTXghnu zf0qCO6%7s{5f)CaCKgu5rX~eP#|aA*JdlLj<{e)CIil>hv!wp@N6MxDHb-SkPV4@( zCvy7LV>0h7S z*0}Ta&A-nU*ccbCuPVDAZHM1l9Il~_a2DUHYriHJADDl2^Y62cyx#U{cb3n)n{T%> z*8Z*u&zacs@2+RgT6PT)5SI!i&-ks~%5&Pa^4^-$uL?8vuT$4}P%YPk5_C8myN}_9 Xg2U|T?3_%vqA|M)uV!}L35J5I!1;R42njgJhd)n)d?|bjN_wIY&Ur!vy`pO}o0uTTI95OdU z*$Q+}Fjz4W!LBcE=oZLye^ri|s&+*3= zSx4h-%O zHW;XghPpKA2qhK?w4kd9!*D4?u$CDzPOc~)C1Lw-JHN^Z3t5rukK8{%N`@3lm07CO zZfHg`axT+}d{*V&E!^tXf})h@-ZXV~lp?CG?fAnZiCei#M_Y$Bwo|#Iq=(laDP2Ct zx~jP$7{Ld$*EuN7Nu{%849UqgcgEgV+RmJo`OkEFX-dhL)BQ9azOH4C9DCIlD+JSa zRR60!Qu1xW_JTn_Egn9g_ht1$Hax6IHm6WluEB=1W^`QGKCI{#n_pE*1l6mJ&Npn? zz8I&WSr9J{iijhH_{Zr zx(5iZAr{!;RIi}%%G>m`>Rhw@--b-imc5`20gWa)AF?;r#m(uPdj4fKExQMgN5CX6xWe#nGPfbV)?SFxoyyW;6PyfqTC|dq5EwRdVBPEhf)2Q)DR3!~3-p z2R2Xg>GwHN5jxy}Z^@l`aB^>Pp@R_kIjhZ?JGeF9IMfA;A_bRACfTz#q#!B*^I5X; z?#C*V=i6HPD?uU zxx>kiH{IST0va5f@-DH05}17ps;XqMhG#XdH2cn-3_=(b{jN(Mcr1rbZr}%f)-s&#H-*p4v{*_J(Na2A02z=|SOciX_#l zu>*02F@)ecx3g;fAFM2c^sDuiia6nDqD#l}Jg{JEPT&m&mqdhtaLED1D}%u|!3c55{e4IGFC;urdCK)Vj;JW9&pRm7XDiP&+!2!rLPd}gUm-@ub&RUQNLQLeyf zVrq)Cd@GsZTF8sj_%(5L9H~q?!&jXO-TR!uMUQ>D@S1x*T!LO?`D2)uNr#_X;0zuB zaq*hDi!{`^RFJQ_KGOKMoz_DwMKGP$n@fODVOT-?Y_3Lfk6LD_7+7#kHj2l_Orq_C z$i&pceV0E9(ZX76oQm~g3wt&5q9uA5KXa<6C*i}0W!`;i#m=&o3ZL;(O?@Y4`Wglr z(Oc8l1QUJ+|++!`%7#^Ok?ymzx}U?tb~Q&HMB3yzl?`v$nd* zq9Rm2|8~Unr&n%Wep!3}y+1$8`ZuTU*~?BjZu2iZ{M$ZfP8{ZAx0R-%7hW!SR{3jk z@d5kSASYhAz01y&XU{I}>Ut|+$n5^{@y7A%Z_U2_{i6C|)3Zvc_qWZaJ^eHL;?e~T s_lq?c6&)Qt0tAFaT3A@IGZ)6+{bw?}c;DLlzzAaSboFyt=akR{0JS2tAOHXW literal 0 HcmV?d00001 diff --git a/packages/text-area/test/visual/lumo/screenshots/text-area/baseline/error-message.png b/packages/text-area/test/visual/lumo/screenshots/text-area/baseline/error-message.png new file mode 100644 index 0000000000000000000000000000000000000000..bddd03bcf44ce847eb6b18d9fc5930713644ffcc GIT binary patch literal 2252 zcmb_eX;c!37RDOONVCjNi=nSYr^Kxsa!kP#_Xw9nM+LQMG{lTdOEC$nu_l|D1jQZP zcf=(f6h$jD)I?J>%>{A`1(k5kP#Mp>AMd<#-upH4<1XjibM8IgIrsbSed3CAPy-$T z0ssItCr4ZNt=P9^WR)FTzTsjrcPl7`yF1tbm|aI_005OoPPWz_QJHh2By8U*h$|Zy zB}ATC-uaD&Gvk5Pt5nb-mCc;c4%}hyp5lblD&&MxO~j$ACS6a4*_Se}n8d%n5T>7t z@QF;o?S53vOu*$FJ9eLU`?XHMca9p{*h`cK$ll((N{EO`}YL=3Yk>^&fk5dscS{)mo zT;5x#gHMX^f}iltNE#@5$-~K(wELr%C3Ocn;i%Fu0oM`TFigph-isi__?y?q$wwH z8_+1@YqSTPNQrQhHy9y@121>B*ORjC3>yxp6HgKqtc49M;fjk=>WSys+V=?VR#rN$ z#Oc^Oy{mFY>rGB8N0q3U>qw!FR>Dt+5L zw^0vM3qS%h7NG1hUlv~csjr!Q271H^v-C3HLOkr9u;LE1URVo0ub9F>W0o#Jb^JWI zju(bm3@>srb}ppMW2ispEk<3Fnm@gL6sz!QiL)~Mm-q|*W0)5W)W+Q+*t_hJh3S%b7HjlR(TsLA8Mb^3+ajMEcd_kk-T@T0U zyA%~$8DrreVa7jBwf}CIxpPX^Ibjm3xbhc}wV3|^@j;{8Tw~+9TWh6GdWlY4U+$0Y zlfI|P3Fz4!2H$JDh-oEXbWiWS*_2V14e0y@1^eG(5M~4TxqBUXqem)E4MbHm zT(y6e40d;8w)M>S4Y)w>b-~7R4!`Ls#nK(d$L#F{!CoVyd$V>GbrbKv)1!*q_P6YK zY~%Md6F*cgi}#t5YYv77-%Q?)3Jz0bH;~m>;IR41m<=A#nS!2|Qkv%(>$ex9;~ywL z>y(B+1|75xwCfozZF}N1Y4!Z_=2D)__tVwHQvWN+c;(jhXdZ%JcuB^F_>m#~hi-IV z-`{SgED1&Zv}j$jm_0r-F^QZSAA7^)rd~OpM6z`0pj8J+QzC)pBTPrTGQHl3)blF{ z`6Zo;@0Y^spx4;uL|pR5$Yp<4NMr803})I$=ld0Xe!=$#q*khl(dBw9wbDA0>*fT+ zncf!Z(pxU$>AbxQt;K`|Ij0mS)ofXLho~>4+$4DgcAz1$>&h*5X zy>5gqk#0?}YPSlcg*K0~tBbA^5Q#Xqu;^d;s}kxezXO4d`(3vJzB)Ol;~`6EDMT=vgrO zjU6md@+MWzXZZ?2lt!s%VAFofTXx7zkIv>loY~Ni0r$r%W6}n1b$jUv-yhM%tMP&j z2p}PC?U<%IOaH?=MsDwtnZ8*&pWu>6@cWj8dD_??N7qEw)jhsd5>a5uP7&Qddw^C? zNv}Rtn=N~7k+8iTQ?g-NI;`o1>heS_Bz15)g4S5)^!>tIW#T`ASL013?nFb=C0z^D7mE`? z%b5pzfN7i5b`~mKmA8`SMr~9BrLX@hBxmg5L#D%Y%vUd6e9IZ#1*3${v&p^Gg@}6$ z+vt7AwfC4e4al0S;O8|v7C*R0cck6c(Nbp(+5Or`hr5=j6E1^YsOcj^lIRXmg#}oc z-Us}M07tz%!C4S6dDl(rc^`;xYp7KiO7~cj@oEg2LFJ`!-3#ov)N~`yBrr2bw0ge4 z0u@uD{~W(kdTm}ZDXu%ELaLIFM1(Ysg9(eseO0cq`qO`1Ys4A=-U$C2inQe4fcW}o UuKmKtt@i}rWQVk6+5|lK6C!mo{{R30 literal 0 HcmV?d00001 diff --git a/packages/text-area/test/visual/lumo/screenshots/text-area/baseline/helper-text.png b/packages/text-area/test/visual/lumo/screenshots/text-area/baseline/helper-text.png new file mode 100644 index 0000000000000000000000000000000000000000..bcf284ffb27eb695530f303a552a85301e6c85f7 GIT binary patch literal 1280 zcmeAS@N?(olHy`uVBq!ia0vp^SAaO1gAGXXJFVFPq!^2X+?^QKos)UVz`(N0)5S5Q zV$Rz;h90vsWsZN0zp{Gk*GC6q`}L+D6ywsH=EPE1Y-xSv=)z`w8TLyDQn@qK91A8j zOQn zUuV57Z@k9#@5J}I-^n+b8K>%;*(qH)o2cy8x{?sB`g zS+5UoEBpRU^pU}WM=nZ&S3|;A{)?KMwf+i=y`E11!?|Ovf>;B5VK=XqwYL4o7;o`k z%MKjnl)N%at($e?+^P0ft9@6m+&9H@NAxUt-|V8J8EP?HHBXp%SpTao4Y+19^TX7M zDo3YO&AGBSW2345mtw1RGZz+~^$04S{>MJ*O2GydYwi8!|7YDY`H;Rm&->P#&r@SM zwZE3d9PV~M?>#AO-f8Js(^Sr?EW3K^^7@oL`=0oC*Dp6+u*8Tz`P5sl=2@Np*F`7S zKfR{6Y^tTDct96->Q!Hxs%LTYYpOmkmHrsNi}(4Q;`ev7_n-IG5#Li*;Pdc$9#F-r z;=Ap~Ex-TDod3FRSJa0%<#$)p{yqG(`S4ch&C2U`f7qJzS!VXefi3*<&NI` z$-4}{JI(yYeoE#|PH*{BUA-6bp5`hm|2j^LUEi`ft?Ss8iT_;h9a$5Ue_!Ki=_cWY zIjJnAkCdlP`JFLMvtp0Mx|F0pHv;3@m&~{Oymqm9srPI5{^d25!L8g$Ua6lQTSK#! ztyprbY^fT%sqp)7ql_gEg3pV2uhtd#7CBvvUG?E>sjm6`knDdCFMd5=n0nV&$8_?P z+Xw4%CYyhA`CQ%=`=0UOjr&it_h{#z;;-0U>3di>`{(-YCi5kyKl06*aHT+9uh;hC zpXW+{9?iUFV)?~wuxQgOmOkAYvwe0%Sugm(U&EI-jq-)!Y>)*Dp@xA{vb=FGH(~I{_tNwLWaH`0E-<3NZ ze-`hY`!UXa_8!G|pA>hj{~^Bn=j~^%`74>HcFMnD0u=;D>Mt=#B(>u$3Q%&|h5F|+ WeQVTy9;pKsX$+pOelF{r5}E+6&_G`R literal 0 HcmV?d00001 diff --git a/packages/text-area/test/visual/lumo/screenshots/text-area/baseline/label.png b/packages/text-area/test/visual/lumo/screenshots/text-area/baseline/label.png new file mode 100644 index 0000000000000000000000000000000000000000..681f74019f984dcae584cd8b2162f385fe82f043 GIT binary patch literal 1212 zcmeAS@N?(olHy`uVBq!ia0vp^SAe*jgAGVlhvh{8DaPU;cPEB*=VV?oFtC()x;TbZ z%z1m)(c^Zy%(0K^S5-Imma#;gVmTNlv7>o+#e{DgZZ*GljF!;lm?#@kb|Tl{5TA6a zHIL*QgA3lz6E!C133u&&kr>NA)seYTMol(?wcFXG<>jR;BU9@geQTtZIsI?ltg~0( zsEq&1dQbmn2(nu&-T`?yb^YD(){IL&whH{RXgLtR_{RFy4u*sC+{3g_BQ^N zqO!8)`vy0A|BM$`*Bo1l?O_9*st#z@vQ|o;6GJ-9aradp+|5=xJUiQV00U_&S--Y_uIc|Udv__f9G;PmQ z?F`#m@8XkN(4(^RHG#Os3nol7#(k?6*X-XitY!wP3_TH)Rd3eD)b*=92Y40C?^SjE-*dD2xzW>dF zvhUwkOR-tJ(X#xV%6>FkZO;lI>(5kvTsBiLrBje^w5(S+Cs;MU$E(*1 z4G(v(oU}Rq+o@#UhpXBc`E~;(7DVWXosONO|3>S3M)dCL->VX&3tq4=Fm4d<5ng%C%@p2Rp(Nhe*1?husCAyboFyt=akR{0HR+J6#xJL literal 0 HcmV?d00001 diff --git a/packages/text-area/test/visual/lumo/screenshots/text-area/baseline/placeholder.png b/packages/text-area/test/visual/lumo/screenshots/text-area/baseline/placeholder.png new file mode 100644 index 0000000000000000000000000000000000000000..673baf61a7f23ab0c78ccae17327fd3468a64880 GIT binary patch literal 1826 zcmb7_X*e4O7{}x6+;xq*qSh*Ngz=3Q?Oi3r4?94`UL!)nU_fG7iQ5B0#3IMaOB@p!?WjZg!x*Oak(r?>J7 zndNWFyc@*nadv%!!_Cu%d!1i`_TC>5ZIsvWoWUWQ3qe;^*d68WE+ilT6>rzmM2k}9 z%J=Ntss+R7sxQ}eV7XOo9Cl5|vt1s}owmD5ZwSrKmgVjy2x?*EMa~EOm@a1wgWglF zD7BYFA+@P3M}eFA$2P@Z{o=l@eH&Lyj{twY%8gQwLM`i9 z%U^xNPnv`8dTD@;>p5cg+o~+rGFEGk?A!u#e%s{LZw5`t%i)Lx^}9LJ;S&ihHGSD_ z%TC_h%cm+6wGZ0A4(6;pbp6NEzcTe}zg+Eg^=V*Y$k6SzPNk8wj$tL<)7Sf@}zLZWK#hxH)JW6u;_( z#2Km-6E+Ycb>=<14-jVF$gSf6uwW}hBEO_D$NAv&+gYQX8?~^qz-Tfhi*G^WqR8d+P)Fp{Ip5*2>ON zsg|82k+te8MV$tj1k8@o9U23fd%SPg9V30TW6*dp!YE?EY*CqWc$eI#sUB*eu`Oww z(p-=lOR}|C*ldi4F6h^%jrcQzsFw0Vde}?A^(O;}xNo4Yo6<}cixmb0(DCg)oTt=k zYs3IvHfdOKOvv?n5x+-W@fVvh7|jH5ZZWPqfak-r?qMFwb#qzfx`!#Ga!OT!3?VNH z1pY9>q-`8or33wNxl{a>&bn#WMvi0(TYP!mH(**<&X_uafqk~*52d=BQ= zD>E*{*x#6#BLuJ^woIfRI%!9dP1$QTXbr-oUnvQN#1$&wuO~Kz6QL8CpvD@T*J0-` z-u_{VcxDm7U4JlpZA3WdJ7`Dq>Fy@*V5%vfw>jz?u=SSIs$5AHq~dK_Y&RDHGS7du zCs?PlUXZR^*!2p{u&GY>U-ik3naCdfVQWqwsaq)LZJC(1JVSTp@QPQqcch}L{du2P z#w@cz)Y-j*RR&xdptds?&%^5_ued4NID$Sd-8Mkm_nRD1yt zR%2Ck-YVilzNlap_uJ}q;Hd`^`XRVv2{U4d@=J_(o5F`X;lMUX1t>T##d7V}yRb8# zGu%5enwdMS8gh3#ZK6RYm9?^F+A+!{-3QN->xpX}!<^-}0-kx2NDLD@a9v=u>mbKJ z{2I?MOMQ|)B4Qv^M~?$zYS}uA9M*-ntrtmUz3|_|DS2YIDjZPy<6VjQ^1|WO(Oy^d z`L%wq0>Wt{^M_Ziwq>bd!+od`_2XOr_t*Y)KRrc(!)sPouxBB zImb5xTWOglw(~?!1g$pz8k&H$V;_taXyz-=ve)o45c}k0NGcQM?aW21V*{c^1KTAZ zytk>A&;Ui2B@{vU2PtwJ=Q6CCvi*A!_mzRkDrN6N8VpKGsE(=6JEQTFek4^QLZuV9 z9(|6sv?p>Q`T3$waQPD7A;NqM`+}{7qU7yUAXa)vFeXnCr>gVsc>RlX|3csYZzDfK z=@FC?5*|iBfvb|bN0u)rWRvUB{~44p4`m-mx;V_cc%58_5@dm(m{WcKOYwY(oxbzFl}5IjTIlqh`P2C^ZRcX&8b8~A`|Hwg%PvT-J^$(N$Lp)t z$8XpkxVnG4-QU{p$A6_(Zp@pPUSgoO@AtQ`)vvAQn*EQjmfrenM^J$5Gc3Vep{qmuX*Ssb=jrleb-&9=i2Q!P;r!&(7IfNeJhft2Q}?*4{DuzqPFX{`%>q>#tp_ z|B)2ST6@)O1^17n@{Mw9)?aD9wd$;_{mk@DF&8{LtxfmT8ZHEN(U|?3^tm@9*^^X!`0WLguOoMXmD# z3QQ}V4ypIw0ro z`hSFa<+GNpJoqK#?EM*QXJ$8ld;eNUWu$PmFz3;#XNFf Q3QXt>p00i_>zopr0G=yh*Z=?k literal 0 HcmV?d00001 diff --git a/packages/text-area/test/visual/lumo/screenshots/text-area/baseline/readonly.png b/packages/text-area/test/visual/lumo/screenshots/text-area/baseline/readonly.png new file mode 100644 index 0000000000000000000000000000000000000000..11ad6af1a25232e0d35a8fc912f69022d70f7fce GIT binary patch literal 772 zcmeAS@N?(olHy`uVBq!ia0vp^SAaNzgAGXbuDQ7zNHG=%xjQkeJ16s!fq`kCr;B4q z#hkadPx}i8GPHgS&OTzn*DuihaB76`+a|rpkKcC}C0As$MhI%0XWep`VcUTn22!zS zbuCjfe%76y&6vlbzEpnUl+;Ky1~LDxeGCR(5+Vsl7BDEYu}TMd)Vz&}PouOIw)|MOH)?4&ObAK%(Lf9vFs*S*VBGHv=o)`z

=JvP=JqRR?ZpsvPgPIvt*g`?y@)$!X12Ywx_b4`>+4NSCAC8| zzuY&k+MB)BY*tl#Y`~XCJH3_$eGq4C^PQIJrN3@Vb-|ijw~s!U|NVg@;|cLUa<9&N z8-BP^bMKenpPUa1%A(xMckvj!@jGvI@O;ouhQ$xQ2fS6e>LSDY%kDscvKNEf!Tq1s z*8jCTQO&@AZGZjmGr~42%AWu6o6aaFyoe>iWeP)2hfsrHAkzuQDGXdraIS%p7lUPo zkb@&c7Q}UP;Z$G`WYXXWWP)-JbZ9U>QSf45RRqg|xDtY@4KgmA3lv;9pIET6ERz-EvoK z8`xjIN)LZ7KK%FcUn zcXnlOukG*kb!&94t7bk(2L@HuVogt}e&ws#S0~!t&Xmn8{~s}Fi$V0oM|aLFwvNs= zx#}6@c`0b^s!oet(>M2?%{?2o%oG@HBK7S|Ju7N3lM1d>^p`2<{1%nWP@_Y@WX<5| L>gTe~DWM4fWvn`y literal 0 HcmV?d00001 diff --git a/packages/text-area/test/visual/lumo/screenshots/text-area/baseline/required.png b/packages/text-area/test/visual/lumo/screenshots/text-area/baseline/required.png new file mode 100644 index 0000000000000000000000000000000000000000..9dfb50f15fd80817d15a51a61542c3b223eb045e GIT binary patch literal 1276 zcmeAS@N?(olHy`uVBq!ia0vp^SAe*jgAGVlhvh{8DaPU;cPEB*=VV?oFtBX%ba4!+ znDh3op+`(P!|{*zjlcOl&g_~Xpv9r)bdXOhiruqq7LS^9Pk@lX6doG5Tt}FU(%*?#M`_H5+Pku8x zR{Y=p{{MsjPxpTI<0;x1rMR|AmJF-^~ICxzb-((dLxOUk$?yMCZKc{in zBp`A@>BpR%Y5VrB*%$Kb z;>O*Jf9C)IiRX8UIP_W8c>yZbxK zK5ixR$D+MgYutKgX2$NH%UAerjoY!JSrbj>pL=;~ZTr5{OiBiJpHjD)l!iWDsd;vN z%2u1-{EAbi{r_2^epE!i`p@50)0U}Z2FR{FxZh{x`8LaPpR>!inVL)sRug=2$a?+L za?S(6n^?D&Eq0gxcKph$H&=FcwRDLcyK>9+2)(ub;Z*TDV;9uL>KrRI$=r zov+_3h3!}TKQ`yuHE~l*lNBGQ&Y0zQWrYW)tZmrJ=9PuL%fzNmTykd7GWWbJ%m3NT z^LGDv7qD5g_U2yg8N#tr7IzmNyWRZi_nS#>t6yCy-4!;gN@-PcddPgqYe)OVw#HON z^vcFs)jzqoka_FeUppRG^Y+Aqh1Z8^>0YlcO)t?q&&?dy((s>&|Hp@8dbpCN>#rp) zYP*+y`RkjT`@Z9Q^G74@CWqGFTvKkuuc^?fQCn!Q`BC9>!2Y|pPgFM^i2GWV+1d5z z;lC!uFUt3iJ!{@0&hjQT@LS!B1>3fje~RMSaNYIh?}g2eOoih@1Hakrf6Cm$!DXNX zPOi#LOoNT%#4gHmaB~d%hy9|d@7wuS708<#@0qpe&EE^k-d)`=|2XPRKiw)j=VYE{ zZGCX`ZR;)r-W$bD3IPxIF{yqN*Ot@fvDfzsVE8Ai-@`zpCl}vmWOh{!E1Dec04&ZJ NJYD@<);T3K0RTCOCFlSE literal 0 HcmV?d00001 diff --git a/packages/text-area/test/visual/lumo/screenshots/text-area/baseline/suffix.png b/packages/text-area/test/visual/lumo/screenshots/text-area/baseline/suffix.png new file mode 100644 index 0000000000000000000000000000000000000000..efa0c648682dbee81e108d18bc7cb9eecea95985 GIT binary patch literal 794 zcmeAS@N?(olHy`uVBq!ia0vp^SAaN-gAGV-U$)~kkYX$ja(7}_cTVOd0|V0)PZ!6K ziaBrZ>Sx5J%N&1r-}Bw9Wu6O57W+&IG*ship3*U^(;zrJINWs6^G9lGYFcc;#iG+! z9y)U6&x#p)zc?u-t?J=XJEd`F_Oi_UNu6Eq_`T0_-k)@L-t&pmi|_4u^ta$#WpUmm z8Ch9bQGtyGw}czEcP>!y2oO-w;1CjF;pA#!VRdY3QgC#ffF#VjOJ>jiny|~ut4*yf zbo5t#Hp`y1jIEn{YgOLPY3H__Uy}LzYj65*{Z}Vbz8?{8sVsf?$7^nE)$*$w*XI8$ z`#x`Og_FVgU2Fbcb3Dt(=Wil8qdfLn-RlK5#uvk5ukDMq!)EoDYbiEse(z5^p7U4l z{`>yN6)_ZgLKEF@(S-f@auFctbhh?-yXd`P^76%{ubcwDT*x<_xo_q~R@eP& z>Ox+Jm%n-!epss2TxC^`*M;E8A2;mVu{}8SdiLwr(xp#Lk8i&_ML=Y)sU-jF_QIf*5A?w$?^RkIAub6n&*E>bJb0xRfulEi(@X#6Hc(Q zDX%&@jd}Zl{a@DqUjEBj=1x(oYo4OclCmk~+P>EdfChMF8aXWUPJ6qXuj{%` zMX>7TZ#z%1W3^jDxykEG&0+_GB`5!qN6MGI+V(|DS&r+kG|-T@>!#tjKD0Y`)e1xAXi= z-}7n<@T^RXUwy9k%&dFP2`f$5pN3ZY_I@gf4PUPtc_R6*)q9VvyUVrwFZFsG-qpLa z*m-&X%QF+DM0<~xeChV%-TUCCyEWhHg>QG=%eGl#ygXV&^S5)ZuWrm8hg&LNd+$z} z61w7+bnuq^fX-EOPfazsC4PB})|ZoI>s3~M`5bKZg^J<2)6;*M+88XkpSDco|KE@1&9drWH>cwV|rBUf+c5fuMW8-c=aly`_xvTXP;!}_x}HQ?#Oy8xxCOlDgAsVZXvN7 zJa=A?x_ZIvPm+3Uk713`suS#|%d4J7@8dMsU={weOFJZ7sg$2b>z?@K633~h#OI$< zWvePZIPulD_N}j#_j_>{PnjVT`>|kW>wB+BkCKD`=g!%)^tSi$DL+EBH0yGm?B zod5PN^S`ZH{Q*JyPT7C;&W-JwX0d9`{e@?{tM^#moV4WKX2YrPZZ6t6QSa}S|J#gblbihSHE|c+x>Y@1wOrQ|Kj(r+#)ust9Py}((fFVdQ&MBb@01cy7n*aa+ literal 0 HcmV?d00001 diff --git a/packages/text-area/test/visual/lumo/text-area.test.js b/packages/text-area/test/visual/lumo/text-area.test.js new file mode 100644 index 00000000000..849edf09387 --- /dev/null +++ b/packages/text-area/test/visual/lumo/text-area.test.js @@ -0,0 +1,87 @@ +import { fixtureSync } from '@vaadin/testing-helpers/dist/fixture.js'; +import { visualDiff } from '@web/test-runner-visual-regression'; +import '../../../theme/lumo/vaadin-text-area.js'; +import { TextArea } from '../../../src/vaadin-text-area.js'; + +customElements.define('vaadin-text-area', TextArea); + +describe('text-area', () => { + let div, element; + + beforeEach(() => { + div = document.createElement('div'); + div.style.display = 'inline-block'; + div.style.padding = '10px'; + element = fixtureSync('', div); + }); + + it('basic', async () => { + await visualDiff(div, `${import.meta.url}_basic`); + }); + + it('disabled', async () => { + element.disabled = true; + await visualDiff(div, `${import.meta.url}_disabled`); + }); + + it('readonly', async () => { + element.readonly = true; + await visualDiff(div, `${import.meta.url}_readonly`); + }); + + it('label', async () => { + element.label = 'Label'; + await visualDiff(div, `${import.meta.url}_label`); + }); + + it('placeholder', async () => { + element.placeholder = 'Placeholder'; + await visualDiff(div, `${import.meta.url}_placeholder`); + }); + + it('value', async () => { + element.value = 'value'; + await visualDiff(div, `${import.meta.url}_value`); + }); + + it('required', async () => { + element.label = 'Label'; + element.required = true; + await visualDiff(div, `${import.meta.url}_required`); + }); + + it('error message', async () => { + element.label = 'Label'; + element.errorMessage = 'This field is required'; + element.required = true; + element.validate(); + await visualDiff(div, `${import.meta.url}_error-message`); + }); + + it('helper text', async () => { + element.helperText = 'Helper text'; + await visualDiff(div, `${import.meta.url}_helper-text`); + }); + + it('clear button', async () => { + element.value = 'value'; + element.clearButtonVisible = true; + await visualDiff(div, `${import.meta.url}_clear-button`); + }); + + it('prefix slot', async () => { + const span = document.createElement('span'); + span.setAttribute('slot', 'prefix'); + span.textContent = '$'; + element.appendChild(span); + await visualDiff(div, `${import.meta.url}_prefix`); + }); + + it('suffix slot', async () => { + const span = document.createElement('span'); + span.setAttribute('slot', 'suffix'); + span.textContent = '$'; + element.appendChild(span); + await visualDiff(div, `${import.meta.url}_suffix`); + }); +}); diff --git a/packages/text-area/test/visual/material/screenshots/text-area/baseline/basic.png b/packages/text-area/test/visual/material/screenshots/text-area/baseline/basic.png new file mode 100644 index 0000000000000000000000000000000000000000..f3cfa0b7c8dec6385e0859b8a348d2be85f15b53 GIT binary patch literal 389 zcmeAS@N?(olHy`uVBq!ia0vp^SAaNygAGWgw#HNgDaPU;cPEB*=VV?2+4i0;jv*Cu z-d@|td)R@8#qo5&_W1MHy|*q%V3q#nbLPP$)`~M{_B0&%eVO^ebPg`3rltuC6jU@g zIEOy7oboFyt=akR{ E0K4yas{jB1 literal 0 HcmV?d00001 diff --git a/packages/text-area/test/visual/material/screenshots/text-area/baseline/clear-button.png b/packages/text-area/test/visual/material/screenshots/text-area/baseline/clear-button.png new file mode 100644 index 0000000000000000000000000000000000000000..cbf2d49ae954bde990970b7d8ec6ffc61a2f7f45 GIT binary patch literal 1064 zcmeAS@N?(olHy`uVBq!ia0vp^SAaNygAGWgw#HNgDaPU;cPEB*=VV?oFfc#zba4!+ znDh3|!lG9`673)V|9lq1u*t|sNK#TVa;EnQpR;jnMZW1_a-A-2>w18|Md|eqmPX+y zzSd8djAlMJzdV!4+u_U1oVXePZsl!{W;FPC-+w=2f)@vuflEMxViOaOph!Z;0tROm zRtY7I0|JhWZDdP*IqR4k625X()#ttX+%>}IAm+!)gh&T14PEM_f@JbUvYKEift38UtYIdaows5 zZ{AOyV(Izz*XjB{Z!c*+i|bCRzcRC{HC%4)v^gt2B&^%fcI&8M_*sGDZlxb4oi>q| zTffWiszA8Jw)Ca@pT2#obawvcz$KH?X3i^K8C%?UDEVjY*+-SnRQ3mL)(ly3QOheY ze8Q5%)7$wqzGpctv|FN;Z!_6tx~6Sm{lA*qg^bzZtXpq?t6V#)*J!QC(~=jN!P*rk zb*+1)j3@qfTR5Y3pTLjA@V>L(Z|_?=ZMDU%cb>1Rj~@Pd%!I$VNh2}o-j6{Jth}z6V_|Jx;FbADofcYdf050>HTlyN+tnUbSO3Weu3g-;{;GQG zyv^#Lud8eLoSFRT)ZD%-k;$uzZazP9LuT^UFrK*5`6~*h7TvX1>s(-v`Flm%?D9U( zq|@!5)j@G@n5F*L*|W61jB;-k*y{IemU{Nl4=2BzXK+lM)yF2V^i(~sR*#^_vgc1s z-kRjKk_wu4Zr`=oPzpV223cl68hGy(Mg>6(z=5_JAK79T9b>4gCiVS^y oeet!A|9@g^y8*=HWt)HONzopr0A452v;Y7A literal 0 HcmV?d00001 diff --git a/packages/text-area/test/visual/material/screenshots/text-area/baseline/disabled.png b/packages/text-area/test/visual/material/screenshots/text-area/baseline/disabled.png new file mode 100644 index 0000000000000000000000000000000000000000..93593dc9d330c00b4eb68071a56cbf6537580d92 GIT binary patch literal 387 zcmeAS@N?(olHy`uVBq!ia0vp^SAaNygAGWgw#HNgDaPU;cPEB*=VV?2*|wf8jv*Cu z-d;20I_x08;&}4E|M$JG`7I_aa8w9S6_&}lRPg-WJ4O{de|d&{B@GTvE~loZ2@4cd zhCcJc(*HYto-%q~Y?s_`M zbpQZ>9_kVTv!}y*L(&HB?F$E%w)R8=k8!^UXy`Xy0RXfxD1@tD;zRM2%cW~cW_*c{ z=^1T(gN5=(bogshDAe^NvS+B#({Xn0V6{L{|0|pvd+VlczWzWwn;#F!C^QX%T!8)y zc%T(|a3U;`l%5GR*6|kVJC+i=PYx*M)y3N%(=CP58--jY1>Y3eA$9fvcq!&-S^x#* zASg8F5dgmbqoWp99S2=Cvb1&1A$-%!Nt(K!{0+-edlfWXZW zq&P%l1EvEn;32d6>AR94xIyTl8znhh>&l3tA_L>)T6QHl+Z6QQuC)SYiRUXlgyJsU zL)JhZNm*2ee+a6OrnXDDpS~F#&-u@1bQ`Qb5zW{M>U){#-eXxa*P=gG7BR@^kyv8| z;F|X|{MAwHSy64^FL-Lv=(N0%Qge=1yHdf3jE{q@L72N=j_5hAJk@Vo>pT&L?>#bA zFeEd8sf~X|jr5CZ+o9s|u&B#E317%$JUwvoqx%?aUd!0k9R~(;{g^i`>ElV zp;aZtEX=!R(t6b^)d|^q_?^AjiNH%#C0zko+_B-AD&TXYp!ipxc}`J|FdBHf3KFS zZw1Eg`-NkywOQjB5?&{JKm))v2BgokP4{mmywLq0slH<}eAUtddUJ^1(m(7AHbv7C zJF{ddi~A;FzlQpb@o%f$vpe5U*0+8RaoYlqBr8}LM*&;=Qb|NwfRGiP8XLckoBvkXsnPi zTZk4MIKi2wjzjT~SY@&^c$?5II$um3uWX(A(Pb=7m0}lRSN!hom;+VL-Abo#NgD9a z#m|{{i~QPP3$eGNy)tl%^l+wy(5kXe#2(D82uDXg|Bw;28Z&$p2(qU%^hjEoZchFwb_Jl6`k9{QVvw|@>D+pMt`-hl>FDy5e4SMq(hwVOTOMfqVarL{xzxqoZUm(iYJv@A# z^ex{HFhrREzc_`ulEfzzwSI7x4=`mG7HpBsuiTt`%5gv@-48$d&P%=alYB`U6~Mbh zP!<2Y|5}ynor>*QH%z5;*6ugJX~xKaYHg^VAnP@}?DTs30N?)Jj@4=N;Hgfd-{v#l zDre}P=mM7v-8|L+8|V7Eds#!@X=9T$I5`(GCEBL-LzD&7&C`NI1krvYmhVqA&Q zmF473(;qA)AvA%n8kep2NLFrf>4191@G$_&2jVC)qruE|)8wU`>pCIVp5( zzPBoXBiT)@hJ!1~2!~40L~gCW(ni=$e}ndDAg&w}^x>?bgtb;DMM@24j}ocey6St* rX)g?N&CYA;+QK!m{w(u90~1lR$(oc#Z9fXzLoEP>^hPvX3?=;mdAOdr literal 0 HcmV?d00001 diff --git a/packages/text-area/test/visual/material/screenshots/text-area/baseline/helper-text.png b/packages/text-area/test/visual/material/screenshots/text-area/baseline/helper-text.png new file mode 100644 index 0000000000000000000000000000000000000000..ebbe2aa45ff0cbd7c36d362b698ebf09683ba06d GIT binary patch literal 1072 zcmeAS@N?(olHy`uVBq!ia0vp^SAe*PgAGV(&;I`!NHG=%xjQkeJ16s!fr0szr;B4q z#hkad6+<3pNVGn*t`2iiQl2<*;)xSpDXF&0^nfg>npkD`Yl1tC)=k{ucFpu%{KS$4 zt*KGpHf>I?kPof%Q6IA5Oc zl(w_6sd-k&@ISk}ygWVkyKaks-Oq>y{pS7}%uo<#xw=x1>+0Q)S@*-V{KVs5UJYBH zp8G2-H1PG|-T$|LKG0z-l2Gz3-fPa<)uB4q4nHngRy0ea>gV5mp`m47>2s%@b_-e+ zX&EZC*ZltKwO*!PS511=K3AuH>{kzo)w}AE#g$n%U-Mmb=;K)?lGl#amfTF-794w} z>$K0^mwW!UH|Z$C+z2sy$?TrC=d>- z{Fj`zKeDxEE9YHB5(WsHS^+6U$ps9C_aDT zwQi@^mydd?dA^!2WtzF_w#OtT)tfG>%3qb18coP5GoHh~Nab=$nexcv6E*+NfGul|{~ za+Bq(3wc$wo2EyEPrUc-T+N?;=jGR;MD#cNPyH4o%8B3r>eJo``&G_-gEHxT&tHqcRgM6bAH9^TW>ck|F(Np$+C*T)sL$<)2{8VxmdcY zBzE=N_Vv4W1q5z=6`y-^MWs|2zr67J#oo{E3#!jD@&0Q%&-C}Upu!!h_qPAKw`7;E zP-=1J$IR~^KZaJG4+vcJa!+ue*yBSkpMO8pySuUT(Ylb0H9OC$eqMgQdTE5W?y=n^ zk(R-sVp88GWrd}60n>r>(x*QH_0O5)bBE@x%q+4wAIUBM_5OeRD;2kG9`|zopr0BWbVp8x;= literal 0 HcmV?d00001 diff --git a/packages/text-area/test/visual/material/screenshots/text-area/baseline/label.png b/packages/text-area/test/visual/material/screenshots/text-area/baseline/label.png new file mode 100644 index 0000000000000000000000000000000000000000..a2c4a9d02293ddff9c660ffd39f9bdf8fc166f0a GIT binary patch literal 909 zcmeAS@N?(olHy`uVBq!ia0vp^SAe*HgAGWg1;;%AQjEnx?oJHr&dI!FU|@Flba4!+ znDh3QZn0LnO#8!nD|Z(+CGCkdEKg3I@~Gh{dg`U3CMOx$d8dDaP~=Xb&c#Y9?3a9= zr+hjsQ&knK^~UDrniIC?PZt&2$n~?j{Q0(7o}o^)sY$`nal!%xj{pG`4Gtj@7EZ1v z7FI{v3NPSiHT{;m)ql5D%`<(uvaeURUG1O0(=)WUc580@{Jp#?GZ%s!^yRH~p4GS9 zwcFFh)4E^1TV7gim$85K@~_LHaV&f6d)( zt7gtW?>2dsS*YndeLmx~Zu@=b%_djs&R&&UZ}q|A&8quhrl*5_-uE_Yi_BZ7W*WcN z&))vLPrCLtDcK7dH+jVze@!p?zm=B4cV*9!)ph#h`LFOF_muAezc;pJWmN?p z{JihgrGUr(-md2h-RyZc^y=HYOI97bwg0A$Y?=T3`i(sC8q!zpE=k+Ew(k4i__{a# zzgHx#`P{bc_3|oNvu|7N?9a?NlYNtC>&tWdI16uFoj?2G*+%x5>HYdY+k8t8+v}ef zi@oc=ymWugs@U@N@29Vd4BP%<^=+H;7Uw5lUn6!pk~cPb_Vn|2UpQ?dJ z8t%_xcF%tNvEt87e#ZS=@wRkJIKRFvW|o(i-~Y+-!75uDn?DzJfSvIuz41SgTe~DWM4fTp)gM literal 0 HcmV?d00001 diff --git a/packages/text-area/test/visual/material/screenshots/text-area/baseline/placeholder.png b/packages/text-area/test/visual/material/screenshots/text-area/baseline/placeholder.png new file mode 100644 index 0000000000000000000000000000000000000000..d96d2ce37213668db65994cf580338c0c2af6745 GIT binary patch literal 1206 zcmeAS@N?(olHy`uVBq!ia0vp^SAaNygAGWgw#HNgDaPU;cPEB*=VV?oFt8MOx;TbZ z%z1ljec5drnf8zG?-?g#NN7kzWUTP!;4yjsb&9mqGa(jUZe~_q-lKn*)cfXI)ZhAh zZSBp^j@I32tDpbqR9$QTYu|g;14ruXtC$WLD{34NaAa)b;4*LtXi#io;t>={=vctu z%)%<6M6T3@^Zh*UzD+GV`zifg{?phpnU%F$Cz~z3_EZ1dheS`qD_wb3{pTwZ-f3!G zaa^tc_vZ)M8VwE4NuO_3Ex)Nedy!MD_K*158IAAHrk$K_DSA)ySlG!YO$WYz`DlJc z*HF_ls>Z#vI&|~bMU(GWosG+AUm7CQK4&lQd3${`E&XdThcD?L(Y`)mN>YF=pGEvS zJ&WD>5C7gZd|vErxWdnGAMfEiV$b{DpVL_}Wzmy6y)&YI=325{d8_^Q)n2owQ9mt~ z1#Z-Q`uXwn+>0$r|MlcoU>{!yYOMZ)xIk)W!IdTUBQ)hgMHWKn0LD2`tPjY zY+SVD>Q%$j#lgZNf5VohNB&upGp*)s+-|p`ysZ);*Uf@H8dXa_UF{UKTF>`*PTK50 zHLPj_0rG^493e@T*YrLo-f)rwhFGYj8| ze?PnRo=Ewzn3=P=_HH;{lO{pyGXMVZ z^X7!jQ|C-hntrT$_nT{%bTv=C{4?`hn4h0x?)EUd-;$Gdl|AfMI(=-Wz1j9R7CWDs zTg+=at9jpc^WV#f@29k-KDeT_zxJv`R;YJyk^IavUgdel6Q?XPS(!fJ>GG%ZUf%g| z_okeej#t-VtL%N8@fLhJ%kMwq&Xbq3soHmAmG|q`6?63B^JW@tFInxk)};H@yMt{1 zIO9Le7COAWX5*~&?7PdV9+}(hFpTYfbnE%tZ=p;7UV4`P(P-wJMd$P<-&(Qb$FoyY z)|dRe&!{=q@AH~xZOcEfI`ZHT>tuf4Jq$>FZkCXFA2c{IWxAdGgns8`Z5ywnH*BVbkeRU;{%4F6-;msL-+lqGAY$-z^>bP0l+XkK&od*- literal 0 HcmV?d00001 diff --git a/packages/text-area/test/visual/material/screenshots/text-area/baseline/prefix.png b/packages/text-area/test/visual/material/screenshots/text-area/baseline/prefix.png new file mode 100644 index 0000000000000000000000000000000000000000..4bb3797cf6c53bbd9ab7c88428b65c5e1b3d6b26 GIT binary patch literal 647 zcmeAS@N?(olHy`uVBq!ia0vp^SAaNygAGWgw#HNgDaPU;cPEB*=VV?oFfciKx;TbZ z%z1l9F<6OF;`qbs(##Wg$Tw!F2z840mUK__P>FiuvS{Ltiy2Rpb5t%}D|r6GqTF2X zd-=O7&pWNTKTo^A;rFJ3Yu{_*9gb|Tw`HhNYid$(beyn2!6QIGMT0{~goTrK!lL zZ+qoS_0_vs!M!UMtZIw-B>Vm9yH(FNd-l%j>s2$I9sc&)-Q7Iw@@IeE-{)u?ecj5C zvxX`Ee~VP_>XoZvvn3h#8|P>i-nm}3b>;H8M~)_iPM>^HKAtK5M3$v2N|tqUhh@4`ujcLrqP~ zv{{*(veZo1x8J(6_s*x%(*2byx{q$T{jl`sM%t literal 0 HcmV?d00001 diff --git a/packages/text-area/test/visual/material/screenshots/text-area/baseline/readonly.png b/packages/text-area/test/visual/material/screenshots/text-area/baseline/readonly.png new file mode 100644 index 0000000000000000000000000000000000000000..f3cfa0b7c8dec6385e0859b8a348d2be85f15b53 GIT binary patch literal 389 zcmeAS@N?(olHy`uVBq!ia0vp^SAaNygAGWgw#HNgDaPU;cPEB*=VV?2+4i0;jv*Cu z-d@|td)R@8#qo5&_W1MHy|*q%V3q#nbLPP$)`~M{_B0&%eVO^ebPg`3rltuC6jU@g zIEOy7oboFyt=akR{ E0K4yas{jB1 literal 0 HcmV?d00001 diff --git a/packages/text-area/test/visual/material/screenshots/text-area/baseline/required.png b/packages/text-area/test/visual/material/screenshots/text-area/baseline/required.png new file mode 100644 index 0000000000000000000000000000000000000000..48ed60d0508e17a73f326358fe11aed7a4e42ec8 GIT binary patch literal 999 zcmeAS@N?(olHy`uVBq!ia0vp^SAe*HgAGWg1;;%AQjEnx?oJHr&dI!FU|?S8>Eakt zG3V_q#fsTc3~dkhSF0&0Pn_)WWa`8dUMl<8DpiC!SGv3TDNPJlnmE=j66L+M z+e&_J{+4!swUivw@fzFb4)2~-&SO3B=yEkb!wyvzRtZ6o1ebsYK}SZnf#U4dTw%e| z`gX}*)$%+3x7rpc#uW*A76x!M*VWD3yKL>lxC1luO4_ViM7H?{P13x{%ayqL<)Z&A z2iD)s2+&^Yy7*a~)bGGppQ|R{e|@@l_|VzkHND-(6j!_zXWtZ5Ix%ajO{RI~?J}u; z`Om8^s{OoKYBaTUVaZbCx2Cmkmuw2T$2;p^<>~Uq`^o%Te2%#~q2}iqU3nLV&JD>D zJNo7BicNVTYf5hLZ*`1vDecewG6>#rCcG->d$eeci!5 zPr7GSY2MdKU+j{pr}@rLE-mHh-+oEun=4K&t+TNEaKm4!N^^am;oh=y>(>N{9+wEu zboBY5e8xCrk?&=h|8{45x6W?uu6I}4{!M-SGnJWT-3jR{-nzJHbTtDjZg0i0mAlJK>etk4u;J0V|?vF>A`5z=#{+^WoCZzwr?VPDwyG%j@ zpK|5?{rh*)C$Cb=74P_r&n1336%sb{nAM8ve=+9j?>i^?F7|quBLCIe+w+0{uG!Bk z>)tyuY=7idU&XSAzlo`bgUdik<3Ps(24y;N7N37U@5hfH6=w?>SAGaZE8|-beyx(>%{#I$4PHC$LepY`cs-(xA)A?k~_gHte$OWee>E+>*)D?O5S_==JzLc z8&8#_gv9+!*njq!;>`KyjVgDU&3sxj&nBlmu-|H3KHt8Mmp_e8Pt5+~bnuDFWflI} z{rt-FES!OcZ;#XutDNOICr+=(aQ2_VlhG`!=l61Dzgco#Ti(Xp^5mBij*scmF<9d%*tft5>gLzst(mQV9!jt4U6)YX1xWw7UTWOQR;l@Jt3a0zG-qy{Hm=8E5K zt>CS9Yu~PC-N#X`aiC)X!{W`&uY#7Xx%KMnhHFcGB47F0r*EEL-Z%5@8XfOf&u*7R zhej^DviEM8&GVDf54^RoUJ)v{ByI7xC4GLmQ=InQs`r|CN^jNM>aVry_pKM5wcyVz z2B4P82}#y-H;2AC)$h!@@8nPA`&!`vi_h*^u`V>^!oKC2>$27!J?(g^dC9x)_WwVY zmj(p8*skSIkInci5^CaTYOZx>2KT&$-wI7;mvtpC&+Q62v31tI^}ph-iiFQPw%I7I zxbnA0cYBT4$|bJr+MQn2755*?-MK&IInZf40(VDeuML~+#`U{N#aH>h;sT{rzS+E* z!Ie+vF5%p{seGF_|Mj`z1-ms?{JkN*HM9KG*1*LJlKpqD4VvO~H~ZQQSHb9|Umv}< zX1f38Q0nKyH^NunGud3|f5~8lrJcX*8~1JfrB*ZVv^k#rcsS^^Q&!u;1F8QnyuK%S?ZD^`s?FgIX6|dZXK+H??ch6&|U3);Eh*K1x}*IM}N4#Vj#}q{>fa zuG77aDy=fP&)JwAYxc?W@{Q#;U)@XD7`Cc;Q_;6Q!dY7lHTf^czW93O+p`%?8`p|{ z`cyLCY}Kjs=X2+smtZQ)es`=;X8&)chWSn5{S3-YOg$W221?*Kr59)M@8a^By1IX7 zUNhv~di?ltt+W^T_n5Y1%Johhgvtbs?GssG6qjqKbLh*2~7Yt CqJrW8 literal 0 HcmV?d00001 diff --git a/packages/text-area/test/visual/material/text-area.test.js b/packages/text-area/test/visual/material/text-area.test.js new file mode 100644 index 00000000000..6fbb2783d0a --- /dev/null +++ b/packages/text-area/test/visual/material/text-area.test.js @@ -0,0 +1,87 @@ +import { fixtureSync } from '@vaadin/testing-helpers/dist/fixture.js'; +import { visualDiff } from '@web/test-runner-visual-regression'; +import '../../../theme/material/vaadin-text-area.js'; +import { TextArea } from '../../../src/vaadin-text-area.js'; + +customElements.define('vaadin-text-area', TextArea); + +describe('text-area', () => { + let div, element; + + beforeEach(() => { + div = document.createElement('div'); + div.style.display = 'inline-block'; + div.style.padding = '10px'; + element = fixtureSync('', div); + }); + + it('basic', async () => { + await visualDiff(div, `${import.meta.url}_basic`); + }); + + it('disabled', async () => { + element.disabled = true; + await visualDiff(div, `${import.meta.url}_disabled`); + }); + + it('readonly', async () => { + element.readonly = true; + await visualDiff(div, `${import.meta.url}_readonly`); + }); + + it('label', async () => { + element.label = 'Label'; + await visualDiff(div, `${import.meta.url}_label`); + }); + + it('placeholder', async () => { + element.placeholder = 'Placeholder'; + await visualDiff(div, `${import.meta.url}_placeholder`); + }); + + it('value', async () => { + element.value = 'value'; + await visualDiff(div, `${import.meta.url}_value`); + }); + + it('required', async () => { + element.label = 'Label'; + element.required = true; + await visualDiff(div, `${import.meta.url}_required`); + }); + + it('error message', async () => { + element.label = 'Label'; + element.errorMessage = 'This field is required'; + element.required = true; + element.validate(); + await visualDiff(div, `${import.meta.url}_error-message`); + }); + + it('helper text', async () => { + element.helperText = 'Helper text'; + await visualDiff(div, `${import.meta.url}_helper-text`); + }); + + it('clear button', async () => { + element.value = 'value'; + element.clearButtonVisible = true; + await visualDiff(div, `${import.meta.url}_clear-button`); + }); + + it('prefix slot', async () => { + const span = document.createElement('span'); + span.setAttribute('slot', 'prefix'); + span.textContent = '$'; + element.appendChild(span); + await visualDiff(div, `${import.meta.url}_prefix`); + }); + + it('suffix slot', async () => { + const span = document.createElement('span'); + span.setAttribute('slot', 'suffix'); + span.textContent = '$'; + element.appendChild(span); + await visualDiff(div, `${import.meta.url}_suffix`); + }); +}); diff --git a/packages/text-area/theme/lumo/vaadin-text-area-styles.js b/packages/text-area/theme/lumo/vaadin-text-area-styles.js new file mode 100644 index 00000000000..89199b077b2 --- /dev/null +++ b/packages/text-area/theme/lumo/vaadin-text-area-styles.js @@ -0,0 +1,68 @@ +/** + * @license + * Copyright (c) 2021 Vaadin Ltd. + * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/ + */ +import { registerStyles, css } from '@vaadin/vaadin-themable-mixin/register-styles.js'; +import '@vaadin/vaadin-lumo-styles/color.js'; +import '@vaadin/vaadin-lumo-styles/sizing.js'; +import '@vaadin/vaadin-lumo-styles/typography.js'; +import '@vaadin/text-field/theme/lumo/vaadin-input-field-shared-styles.js'; + +registerStyles( + 'vaadin-text-area', + css` + [part='input-field'], + [part='input-field'] ::slotted(textarea) { + height: auto; + box-sizing: border-box; + } + + [part='input-field'] { + /* Equal to the implicit padding in vaadin-text-field */ + padding-top: calc((var(--lumo-text-field-size) - 1em * var(--lumo-line-height-s)) / 2); + padding-bottom: calc((var(--lumo-text-field-size) - 1em * var(--lumo-line-height-s)) / 2); + transition: background-color 0.1s; + line-height: var(--lumo-line-height-s); + } + + :host(:not([readonly])) [part='input-field']::after { + display: none; + } + + :host([readonly]) [part='input-field'] { + border: 1px dashed var(--lumo-contrast-30pct); + } + + :host([readonly]) [part='input-field']::after { + border: none; + } + + :host(:hover:not([readonly]):not([focused]):not([invalid])) [part='input-field'] { + background-color: var(--lumo-contrast-20pct); + } + + @media (pointer: coarse) { + :host(:hover:not([readonly]):not([focused]):not([invalid])) [part='input-field'] { + background-color: var(--lumo-contrast-10pct); + } + + :host(:active:not([readonly]):not([focused])) [part='input-field'] { + background-color: var(--lumo-contrast-20pct); + } + } + + [part='input-field'] ::slotted(textarea) { + white-space: pre-wrap; /* override "nowrap" from */ + align-self: stretch; /* override "baseline" from */ + line-height: inherit; + --_lumo-text-field-overflow-mask-image: none; + } + + /* Vertically align icon prefix/suffix with the first line of text */ + [part='input-field'] ::slotted(vaadin-icon) { + margin-top: calc((var(--lumo-icon-size-m) - 1em * var(--lumo-line-height-s)) / -2); + } + `, + { moduleId: 'lumo-text-area', include: ['lumo-input-field-shared-styles'] } +); diff --git a/packages/text-area/theme/lumo/vaadin-text-area.js b/packages/text-area/theme/lumo/vaadin-text-area.js new file mode 100644 index 00000000000..5f3e8862940 --- /dev/null +++ b/packages/text-area/theme/lumo/vaadin-text-area.js @@ -0,0 +1,8 @@ +/** + * @license + * Copyright (c) 2021 Vaadin Ltd. + * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/ + */ +import '@vaadin/input-container/theme/lumo/vaadin-input-container.js'; +import './vaadin-text-area-styles.js'; +import '../../src/vaadin-text-area.js'; diff --git a/packages/text-area/theme/material/vaadin-text-area-styles.js b/packages/text-area/theme/material/vaadin-text-area-styles.js new file mode 100644 index 00000000000..da0ba1833b2 --- /dev/null +++ b/packages/text-area/theme/material/vaadin-text-area-styles.js @@ -0,0 +1,33 @@ +/** + * @license + * Copyright (c) 2021 Vaadin Ltd. + * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/ + */ +import { registerStyles, css } from '@vaadin/vaadin-themable-mixin/register-styles.js'; +import '@vaadin/text-field/theme/material/vaadin-input-field-shared-styles.js'; + +registerStyles( + 'vaadin-text-area', + css` + [part='input-field'] { + height: auto; + box-sizing: border-box; + } + + .textarea-wrapper { + margin-top: 4px; + padding: 0; + } + + [part='input-field'] ::slotted(textarea), + .textarea-wrapper::after { + padding: 0 0 8px; + } + + [part='input-field'] ::slotted(textarea) { + white-space: pre-wrap; /* override "nowrap" from */ + align-self: stretch; /* override "baseline" from */ + } + `, + { moduleId: 'material-text-area', include: ['material-input-field-shared-styles'] } +); diff --git a/packages/text-area/theme/material/vaadin-text-area.js b/packages/text-area/theme/material/vaadin-text-area.js new file mode 100644 index 00000000000..c794c3481a5 --- /dev/null +++ b/packages/text-area/theme/material/vaadin-text-area.js @@ -0,0 +1,8 @@ +/** + * @license + * Copyright (c) 2021 Vaadin Ltd. + * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/ + */ +import '@vaadin/input-container/theme/material/vaadin-input-container.js'; +import './vaadin-text-area-styles.js'; +import '../../src/vaadin-text-area.js'; diff --git a/packages/text-area/vaadin-text-area.js b/packages/text-area/vaadin-text-area.js index ab500a556f7..b25b51a8b4e 100644 --- a/packages/text-area/vaadin-text-area.js +++ b/packages/text-area/vaadin-text-area.js @@ -1 +1,3 @@ +import './theme/lumo/vaadin-text-area.js'; + export { TextArea } from './src/vaadin-text-area.js'; From 225c3d5b24283d3d8a78e99c837e1b427ef4b5e0 Mon Sep 17 00:00:00 2001 From: web-padawan Date: Fri, 6 Aug 2021 11:16:53 +0300 Subject: [PATCH 3/3] refactor: apply code review suggestions --- packages/text-area/src/vaadin-text-area.js | 10 ++++++++-- packages/text-area/test/text-area.test.js | 23 +++++++--------------- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/packages/text-area/src/vaadin-text-area.js b/packages/text-area/src/vaadin-text-area.js index 917a99ea196..b4ff5cb3fbe 100644 --- a/packages/text-area/src/vaadin-text-area.js +++ b/packages/text-area/src/vaadin-text-area.js @@ -152,12 +152,18 @@ export class TextArea extends TextFieldMixin(ThemableMixin(ElementMixin(PolymerE }; } - /** @protected */ + /** + * Used by `ClearButtonMixin` as a reference to the clear button element. + * @protected + */ get clearElement() { return this.$.clearButton; } - /** @protected */ + /** + * Override getter provided by `InputMixin` to use a different slot. + * @protected + */ get _inputNode() { return this._getDirectSlotChild('textarea'); } diff --git a/packages/text-area/test/text-area.test.js b/packages/text-area/test/text-area.test.js index c10693fca80..1f82bb20b7e 100644 --- a/packages/text-area/test/text-area.test.js +++ b/packages/text-area/test/text-area.test.js @@ -118,51 +118,42 @@ describe('text-area', () => { }); describe('binding', () => { - it('default value should be empty string', () => { + it('should set default value to empty string', () => { expect(textArea.value).to.be.equal(''); }); - it('setting textarea value updates value', () => { + it('should update value on native textarea input', () => { native.value = 'foo'; native.dispatchEvent(new Event('input', { bubbles: true, cancelable: true, composed: true })); expect(textArea.value).to.be.equal('foo'); }); - it('setting native value updates has-value attribute', () => { + it('should update has-value attribute when value is set', () => { textArea.value = 'foo'; expect(textArea.hasAttribute('has-value')).to.be.true; }); - it('setting value to undefined should not update has-value attribute', () => { + it('should not update has-value attribute when value is set to undefined', () => { textArea.value = undefined; expect(textArea.hasAttribute('has-value')).to.be.false; }); - it('setting empty value does not update has-value attribute', () => { + it('should not update has-value attribute when value is set to empty string', () => { textArea.value = ''; expect(textArea.hasAttribute('has-value')).to.be.false; }); // User could accidentally set a 0 or false value - it('setting number value updates has-value attribute', () => { + it('should update has-value attribute when numeric value is set', () => { textArea.value = 0; expect(textArea.hasAttribute('has-value')).to.be.true; }); - it('setting boolean value updates has-value attribute', () => { + it('should update has-value attribute when boolean value is set', () => { textArea.value = false; expect(textArea.hasAttribute('has-value')).to.be.true; }); }); - - describe('label', () => { - it('should not update focused property on click if disabled', () => { - textArea.disabled = true; - const label = textArea.shadowRoot.querySelector('[part="label"]'); - label.click(); - expect(textArea.getAttribute('focused')).to.be.null; - }); - }); }); describe('vaadin-text-area-appear', () => {