diff --git a/api/js/etemplate/Et2Email/Et2Email.ts b/api/js/etemplate/Et2Email/Et2Email.ts index 1a0d50bc58..eea6fe699e 100644 --- a/api/js/etemplate/Et2Email/Et2Email.ts +++ b/api/js/etemplate/Et2Email/Et2Email.ts @@ -1351,7 +1351,7 @@ export class Et2Email extends Et2InputWidget(LitElement) implements SearchMixinI ?active=${this.open} >
const element = await before(); element.noLang = true; return element -}, "", "input"); \ No newline at end of file +}, "fake@example.com", "input"); \ No newline at end of file diff --git a/api/js/etemplate/Et2InputWidget/Et2InputWidget.ts b/api/js/etemplate/Et2InputWidget/Et2InputWidget.ts index 4440b57c6a..79c81189d5 100644 --- a/api/js/etemplate/Et2InputWidget/Et2InputWidget.ts +++ b/api/js/etemplate/Et2InputWidget/Et2InputWidget.ts @@ -189,6 +189,7 @@ const Et2InputWidgetMixin = >(superclass : T) this._messagesHeldWhileFocused = []; this.readonly = false; + this.required = false; this._oldValue = this.getValue(); this.isSlComponent = typeof (this).handleChange === 'function'; @@ -224,7 +225,7 @@ const Et2InputWidgetMixin = >(superclass : T) * A property has changed, and we want to make adjustments to other things * based on that * - * @param {import('@lion/core').PropertyValues } changedProperties + * @param changedProperties */ updated(changedProperties : PropertyValues) { diff --git a/api/js/etemplate/Et2InputWidget/test/InputBasicTests.ts b/api/js/etemplate/Et2InputWidget/test/InputBasicTests.ts index 75c34a5174..314cabd7a5 100644 --- a/api/js/etemplate/Et2InputWidget/test/InputBasicTests.ts +++ b/api/js/etemplate/Et2InputWidget/test/InputBasicTests.ts @@ -118,11 +118,16 @@ export function inputBasicTests(before : Function, test_value : string, value_se { beforeEach(async() => { + assert.isNotEmpty(test_value, "test_value needs to be a value"); + element = await before(); + await elementUpdated(element); + element.required = true; + await elementUpdated(element); }); // This is just visually comparing for a difference, no deep inspection - it("looks different when required") + //it("looks different when required") /* Not yet working attempt to have playwright compare visually @@ -142,5 +147,35 @@ export function inputBasicTests(before : Function, test_value : string, value_se */ + it("is invalid without a value", async() => + { + element.set_value(""); + + // wait for asychronous changes to the DOM + await elementUpdated(element); + + // widget returns what we gave it + assert.equal(element.get_value(), ""); + assert.equal(element.required, true, "required not set"); + + // widget fails validation + let messages = []; + assert.isFalse(element.isValid(messages), `Required has no value (${element.getValue()}), but is considered valid`); + }); + it("is valid with a value", async() => + { + element.set_value(test_value); + + // wait for asychronous changes to the DOM + await elementUpdated(element); + + // widget returns what we gave it + assert.equal(element.get_value(), test_value); + assert.equal(element.required, true, "required not set"); + + // widget fails validation + let messages = []; + assert.isTrue(element.isValid(messages), `Required has a value (${element.getValue()}), but is not considered valid. ` + messages.join("\n")); + }); }); } \ No newline at end of file diff --git a/api/js/etemplate/Et2Select/Et2Select.ts b/api/js/etemplate/Et2Select/Et2Select.ts index ad827b7bee..3629052458 100644 --- a/api/js/etemplate/Et2Select/Et2Select.ts +++ b/api/js/etemplate/Et2Select/Et2Select.ts @@ -293,9 +293,6 @@ export class Et2Select extends Et2WithSearchMixin(Et2WidgetWithSelect) /** The select's help text. If you need to display HTML, use the `help-text` slot instead. */ @property({attribute: 'help-text'}) helpText = ''; - /** The select's required attribute. */ - @property({type: Boolean, reflect: true}) required = false; - /** If the select is limited to 1 row, we show the number of tags not visible */ @state() protected _tagsHidden = 0; @@ -817,9 +814,6 @@ export class Et2Select extends Et2WithSearchMixin(Et2WidgetWithSelect) return this.select?.open ?? false; } - protected _renderOptions() - {return Promise.resolve();} - protected get select() : SlSelect { return this.shadowRoot?.querySelector("sl-select"); @@ -1034,7 +1028,7 @@ export class Et2Select extends Et2WithSearchMixin(Et2WidgetWithSelect) return html` ${this._styleTemplate()} >(supe } - willUpdate(changedProperties : PropertyValues) - { - // Add in actual option tags to the DOM based on the new select_options - if(changedProperties.has('select_options') || changedProperties.has("emptyLabel")) - { - // Add in options as children to the target node - const optionPromise = this._renderOptions(); - - // This is needed to display initial load value in some cases, like infolog nm header filters - if(typeof this.selectionChanged !== "undefined") - { - optionPromise.then(async() => - { - await this.updateComplete; - this.selectionChanged(); - }); - } - } - } - public getValueAsArray() { if(Array.isArray(this.value)) @@ -200,48 +180,6 @@ export const Et2WidgetWithSelectMixin = >(supe return result; } - /** - * Render select_options as child DOM Nodes - * @protected - */ - protected _renderOptions() - { - return Promise.resolve(); - // Add in options as children to the target node - if(!this._optionTargetNode) - { - return Promise.resolve(); - } - /** - * Doing all this garbage to get the options to always show up. - * If we just do `render(options, target)`, they only show up in the DOM the first time. If the - * same option comes back in a subsequent search, map() does not put it into the DOM. - * If we render into a new target, the options get rendered, but we have to wait for them to be - * rendered before we can do anything else with them. - */ - let temp_target = document.createElement("div"); - - let options = html`${this._emptyLabelTemplate()}${this.select_options - // Filter out empty values if we have empty label to avoid duplicates - .filter(o => this.emptyLabel ? o.value !== '' : o) - .map(this._groupTemplate.bind(this))}`; - - render(options, temp_target); - this._optionRenderPromise = Promise.all(([...temp_target.querySelectorAll(":scope > *")].map(item => item.render))) - .then(() => - { - this._optionTargetNode.replaceChildren( - ...Array.from(temp_target.querySelectorAll(":scope > *")), - ...Array.from(this._optionTargetNode.querySelectorAll(":scope > [slot]")) - ); - if(typeof this.handleMenuSlotChange == "function") - { - this.handleMenuSlotChange(); - } - }); - return this._optionRenderPromise; - } - /** * Set the select options * diff --git a/api/js/etemplate/Et2Select/test/Et2SelectBasic.test.ts b/api/js/etemplate/Et2Select/test/Et2SelectBasic.test.ts index c9bd9c4987..956e20729b 100644 --- a/api/js/etemplate/Et2Select/test/Et2SelectBasic.test.ts +++ b/api/js/etemplate/Et2Select/test/Et2SelectBasic.test.ts @@ -157,6 +157,6 @@ inputBasicTests(async() => { const element = await before(); element.noLang = true; - element.select_options = [{value: "", label: ""}]; + element.select_options = [{value: "", label: ""}, {value: "one", label: "one"}]; return element -}, "", "sl-select"); \ No newline at end of file +}, "one", "sl-select"); \ No newline at end of file diff --git a/api/js/etemplate/Et2Textbox/test/Et2Textbox.test.ts b/api/js/etemplate/Et2Textbox/test/Et2Textbox.test.ts index 9091883da4..4445a758de 100644 --- a/api/js/etemplate/Et2Textbox/test/Et2Textbox.test.ts +++ b/api/js/etemplate/Et2Textbox/test/Et2Textbox.test.ts @@ -1,10 +1,17 @@ /** * Test file for Etemplate webComponent Textbox */ -import {assert, fixture, html} from '@open-wc/testing'; +import {assert, elementUpdated, fixture, html} from '@open-wc/testing'; import {Et2Textbox} from "../Et2Textbox"; import {inputBasicTests} from "../../Et2InputWidget/test/InputBasicTests"; +import * as sinon from "sinon"; +// Stub global egw for cssImage to find +// @ts-ignore +window.egw = { + lang: i => i + "*", + tooltipUnbind: () => {} +}; // Reference to component under test let element : Et2Textbox; @@ -14,6 +21,11 @@ async function before() element = await fixture(html` `); + + // Stub egw() + sinon.stub(element, "egw").returns(window.egw); + await elementUpdated(element); + return element; }