Switch Et2Template to extend SlInput instead of LionInput

Also includes changes to how validation messages are handled to use our own stuff
This commit is contained in:
nathan 2022-07-21 12:39:00 -06:00
parent e4f8c88e1e
commit 379ceeb3e5
5 changed files with 112 additions and 88 deletions

View File

@ -1,8 +1,9 @@
import {et2_IInput, et2_IInputNode, et2_ISubmitListener} from "../et2_core_interfaces";
import {Et2Widget} from "../Et2Widget/Et2Widget";
import {css, dedupeMixin, LitElement, PropertyValues} from "@lion/core";
import {ManualMessage} from "../Validators/ManualMessage";
import {Required} from "../Validators/Required";
import {ManualMessage} from "../Validators/ManualMessage";
import {LionValidationFeedback} from "@lion/form-core";
/**
* This mixin will allow any LitElement to become an Et2InputWidget
@ -320,9 +321,100 @@ const Et2InputWidgetMixin = <T extends Constructor<LitElement>>(superclass : T)
}
}
/**
* Massively simplified validate, as compared to what ValidatorMixin gives us, since ValidatorMixin extends
* FormControlMixin which breaks SlSelect's render()
*/
async validate()
{
let validators = [...(this.validators || []), ...(this.defaultValidators || [])];
let fieldName = this.id;
let feedbackData = [];
let resultPromises = [];
this.querySelector("lion-validation-feedback")?.remove();
const doValidate = async function(validator, value)
{
if(validator.config.fieldName)
{
fieldName = await validator.config.fieldName;
}
// @ts-ignore [allow-protected]
return validator._getMessage({
modelValue: value,
formControl: this,
fieldName,
}).then((message) =>
{
feedbackData.push({message, type: validator.type, validator});
});
}.bind(this);
const doCheck = async(value, validator) =>
{
const result = validator.execute(value, validator.param, {node: this});
if(result === true)
{
resultPromises.push(doValidate(validator, value));
}
else if(result !== false && typeof result.then === 'function')
{
result.then(doValidate(validator, value));
resultPromises.push(result);
}
};
validators.map(async validator =>
{
let values = this.value;
if(!Array.isArray(values))
{
values = [values];
}
if(!values.length)
{
values = [''];
} // so required validation works
// Run manual validation messages just once, doesn't usually matter what the value is
if(validator instanceof ManualMessage)
{
doCheck(values, validator);
}
else
{
// Validate each individual item
values.forEach((value) => doCheck(value, validator));
}
});
await Promise.all(resultPromises);
if(feedbackData.length > 0)
{
let feedback = <LionValidationFeedback>document.createElement("lion-validation-feedback");
feedback.feedbackData = feedbackData;
feedback.slot = "help-text";
this.append(feedback);
// Not always visible?
(<HTMLElement>this.shadowRoot.querySelector("#help-text")).style.display = "initial";
}
}
set_validation_error(err : string | false)
{
// ToDo - implement Lion validators properly, most likely by adding to this.validators
/* Shoelace uses constraint validation API
https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#the-constraint-validation-api
if(err === false && this.setCustomValidity)
{
// Remove custom validity
this.setCustomValidity('');
return;
}
this.setCustomValidity(err);
// must call reportValidity() or nothing will happen
this.reportValidity();
*/
if(err === false)
{

View File

@ -18,7 +18,6 @@ import {Et2WithSearchMixin} from "./SearchMixin";
import {Et2Tag} from "./Tag/Et2Tag";
import {LionValidationFeedback} from "@lion/form-core";
import {RowLimitedMixin} from "../Layout/RowLimitedMixin";
import {ManualMessage} from "../Validators/ManualMessage";
// export Et2WidgetWithSelect which is used as type in other modules
export class Et2WidgetWithSelect extends RowLimitedMixin(Et2widgetWithSelectMixin(SlSelect))
@ -437,83 +436,6 @@ export class Et2Select extends Et2WithSearchMixin(Et2WidgetWithSelect)
return [...this.querySelectorAll<SlMenuItem>(this.optionTag)];
}
/**
* Massively simplified validate, as compared to what ValidatorMixin gives us, since ValidatorMixin extends
* FormControlMixin which breaks SlSelect's render()
*/
async validate()
{
let validators = [...(this.validators || []), ...(this.defaultValidators || [])];
let fieldName = this.id;
let feedbackData = [];
let resultPromises = [];
this.querySelector("lion-validation-feedback")?.remove();
const doValidate = async function(validator, value)
{
if(validator.config.fieldName)
{
fieldName = await validator.config.fieldName;
}
// @ts-ignore [allow-protected]
return validator._getMessage({
modelValue: value,
formControl: this,
fieldName,
}).then((message) =>
{
feedbackData.push({message, type: validator.type, validator});
});
}.bind(this);
const doCheck = async(value, validator) =>
{
const result = validator.execute(value, validator.param, {node: this});
if(result === true)
{
resultPromises.push(doValidate(validator, value));
}
else if(result !== false && typeof result.then === 'function')
{
result.then(doValidate(validator, value));
resultPromises.push(result);
}
};
validators.map(async validator =>
{
let values = this.value;
if(!Array.isArray(values))
{
values = [values];
}
if(!values.length)
{
values = [''];
} // so required validation works
// Run manual validation messages just once, doesn't usually matter what the value is
if(validator instanceof ManualMessage)
{
doCheck(values, validator);
}
else
{
// Validate each individual item
values.forEach((value) => doCheck(value, validator));
}
});
await Promise.all(resultPromises);
if(feedbackData.length > 0)
{
let feedback = <LionValidationFeedback>document.createElement("lion-validation-feedback");
feedback.feedbackData = feedbackData;
feedback.slot = "help-text";
this.append(feedback);
// Not always visible?
(<HTMLElement>this.shadowRoot.querySelector("#help-text")).style.display = "initial";
}
}
/**
* Override parent to always call validate(), as our simple implementation needs to validate on clear as well.
*

View File

@ -10,22 +10,30 @@
import {css, PropertyValues} from "@lion/core";
import {LionInput} from "@lion/input";
import {Et2InputWidget} from "../Et2InputWidget/Et2InputWidget";
import {Regex} from "../Validators/Regex";
import {SlInput} from "@shoelace-style/shoelace";
import shoelace from "../Styles/shoelace";
import {Et2InputWidget} from "../Et2InputWidget/Et2InputWidget";
export class Et2Textbox extends Et2InputWidget(LionInput)
export class Et2Textbox extends Et2InputWidget(SlInput)
{
static get styles()
{
return [
...shoelace,
...super.styles,
css`
:host([type="hidden"]) {
display: none;
}
`,
:host([type="hidden"]) {
display: none;
}
.input__control {
border: none;
}
.input:hover:not(.input--disabled) .input__control {
color: var(--input-text-color, inherit);
}
`,
];
}

View File

@ -16,7 +16,7 @@ registerIconLibrary('default', {
/**
* Customise shoelace styles to match our stuff
* External CSS will override this
* External CSS & widget styles will override this
*/
export default [sl_css, css`
:root,

View File

@ -2102,6 +2102,8 @@ div.message.floating, lion-validation-feedback[type] {
position: absolute;
margin: 0px;
z-index: 1;
top: 100%;
left: 0px;
}
.message.validation_error, lion-validation-feedback[type="error"] {