mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-11-07 16:44:20 +01:00
Api: Fix missing required validation & styling
This commit is contained in:
parent
dbf77cb004
commit
50352cf36a
@ -1351,7 +1351,7 @@ export class Et2Email extends Et2InputWidget(LitElement) implements SearchMixinI
|
||||
?active=${this.open}
|
||||
>
|
||||
<div
|
||||
part="combobox"
|
||||
part="combobox base"
|
||||
class="email__combobox"
|
||||
slot="anchor"
|
||||
@keydown=${this.handleComboboxKeyDown}
|
||||
|
@ -233,4 +233,4 @@ inputBasicTests(async() =>
|
||||
const element = await before();
|
||||
element.noLang = true;
|
||||
return element
|
||||
}, "", "input");
|
||||
}, "fake@example.com", "input");
|
@ -189,6 +189,7 @@ const Et2InputWidgetMixin = <T extends Constructor<LitElement>>(superclass : T)
|
||||
this._messagesHeldWhileFocused = [];
|
||||
|
||||
this.readonly = false;
|
||||
this.required = false;
|
||||
this._oldValue = this.getValue();
|
||||
|
||||
this.isSlComponent = typeof (<any>this).handleChange === 'function';
|
||||
@ -224,7 +225,7 @@ const Et2InputWidgetMixin = <T extends Constructor<LitElement>>(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)
|
||||
{
|
||||
|
@ -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><unknown>element);
|
||||
element.required = true;
|
||||
await elementUpdated(<Element><unknown>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><unknown>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><unknown>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"));
|
||||
});
|
||||
});
|
||||
}
|
@ -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()}
|
||||
<sl-select
|
||||
exportparts="prefix, tags, display-input, expand-icon, combobox, listbox, option"
|
||||
exportparts="prefix, tags, display-input, expand-icon, combobox, combobox:base, listbox, option"
|
||||
label=${this.label}
|
||||
placeholder=${this.placeholder || (this.multiple && this.emptyLabel ? this.emptyLabel : "")}
|
||||
?multiple=${this.multiple}
|
||||
|
@ -8,7 +8,7 @@
|
||||
*/
|
||||
|
||||
import {Et2InputWidget, Et2InputWidgetInterface} from "../Et2InputWidget/Et2InputWidget";
|
||||
import {html, LitElement, PropertyValues, render, TemplateResult} from "lit";
|
||||
import {html, LitElement, PropertyValues, TemplateResult} from "lit";
|
||||
import {property} from "lit/decorators/property.js";
|
||||
import {et2_readAttrWithDefault} from "../et2_core_xml";
|
||||
import {cleanSelectOptions, find_select_options, SelectOption} from "./FindSelectOptions";
|
||||
@ -140,26 +140,6 @@ export const Et2WidgetWithSelectMixin = <T extends Constructor<LitElement>>(supe
|
||||
|
||||
}
|
||||
|
||||
willUpdate(changedProperties : PropertyValues<this>)
|
||||
{
|
||||
// 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 = <T extends Constructor<LitElement>>(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
|
||||
*
|
||||
|
@ -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");
|
||||
}, "one", "sl-select");
|
@ -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<Et2Textbox>(html`
|
||||
<et2-textbox></et2-textbox>
|
||||
`);
|
||||
|
||||
// Stub egw()
|
||||
sinon.stub(element, "egw").returns(window.egw);
|
||||
await elementUpdated(element);
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user