Skip to content

Commit

Permalink
feat(ui-library): validation in withoutslot using ElementInternals
Browse files Browse the repository at this point in the history
  • Loading branch information
angsherpa456 committed Mar 6, 2024
1 parent e84fe8d commit e2f4e68
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 75 deletions.
36 changes: 19 additions & 17 deletions packages/ui-library/src/components/checkbox/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export class BlrCheckbox extends LitElement {
@property() label!: string;
@property() checkInputId?: string = '';
@property() arialabel?: string;

@property() required?: boolean;
@property() disabled?: boolean;
@property() checked?: boolean;
@property() indeterminate?: boolean;
Expand All @@ -65,6 +65,22 @@ export class BlrCheckbox extends LitElement {
@state() protected currentCheckedState: boolean | undefined = this.checked;
@state() protected currentIndeterminateState: boolean | undefined = this.indeterminate;

// TESTING BEGIN
// Identify the element as a form-associated custom element
static formAssociated = true;
private _internals: ElementInternals;

constructor() {
super();
// Get access to the internal form control APIs
this._internals = this.attachInternals();
}

public checkValidity() {
return this._internals.checkValidity;
}
// TESTING END

protected updated(changedProperties: Map<string, boolean>) {
if (changedProperties.has('checked')) {
this.currentCheckedState = this.checked || false;
Expand All @@ -78,21 +94,6 @@ export class BlrCheckbox extends LitElement {
}
}

connectedCallback() {
super.connectedCallback();
addEventListener('propChanged', this._onPropChanged);
}

disconnectedCallback() {
super.disconnectedCallback();
removeEventListener('propChanged', this._onPropChanged);
}

_onPropChanged = (event: any) => {
this.hasError = event.detail.hasError;
this.errorMessage = event.detail.errorMessage;
};

protected handleChange(event: Event) {
if (!this.disabled && !this.readonly) {
this.currentIndeterminateState = false;
Expand Down Expand Up @@ -277,10 +278,11 @@ export class BlrCheckbox extends LitElement {
id=${this.checkInputId || nothing}
name=${this.name || nothing}
?disabled=${this.disabled}
?checked=${this.currentCheckedState}
?checked=${this.checked}
?indeterminate=${this.currentIndeterminateState}
?readonly=${this.readonly}
?hasError=${this.hasError}
?required="${this.required}"
@change=${this.handleChange}
aria-hidden="true"
/>
Expand Down
106 changes: 60 additions & 46 deletions packages/ui-library/src/components/form-example-without-slot/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,19 @@ import { property, query } from 'lit/decorators.js';
export class BlrFormExampleWithoutSlot extends LitElement {
@property() theme: ThemeType = 'Light';
@property() firstInputValue: string = '';
@property() firstTextInputHasError: boolean = false;
@property() secondTextInputHasError: boolean = false;
@property() secondInputValue: string = '';
@property() errorMessage: string = '';
@property() firstNameHasError?: boolean = false;
@property() lastNameHasError?: boolean = false;
@property({ reflect: true }) checkBoxChecked: boolean = false;
@property() checkInputHasError?: boolean = false;
@property() selectHasError?: boolean = false;
@property() checkBoxChecked: boolean = false;
@property() textInputErrorMessage: string = 'input field required';
@property() selectErrorMessage: string = 'at least one selection required';

@query('blr-text-input[name="firstInput"]') firstInputElement!: HTMLElement;
@query('blr-text-input[name="secondInput"]') secondInputElement!: HTMLElement;
@query('blr-checkbox[name="checkInput"]') checkboxInputElement!: HTMLElement;
@query('blr-select[name="select"]') selectElement!: HTMLElement;

protected render() {
return html`
Expand All @@ -36,6 +40,8 @@ export class BlrFormExampleWithoutSlot extends LitElement {
showinputicon="true"
@blrTextValueChange="${this.handleFirstInputChange}"
required="true"
.hasError=${this.firstTextInputHasError}
errorMessage=${this.textInputErrorMessage}
></blr-text-input>
<blr-text-input
size="md"
Expand All @@ -53,8 +59,30 @@ export class BlrFormExampleWithoutSlot extends LitElement {
inputicon="blr360"
showinputicon="true"
@blrTextValueChange="${this.handleSecondInputChange}"
.hasError=${this.secondTextInputHasError}
errorMessage=${this.textInputErrorMessage}
required="true"
></blr-text-input>
<blr-select
theme="Light"
size="md"
label="Select one"
labelappendix=""
icon=""
hinticon="blrInfo"
.hasError=${this.selectHasError}
errormessage=${this.selectErrorMessage}
errormessageicon="blrError"
arialabel="Select"
selectid="selectId"
name="select"
haslabel="true"
required="true"
>
<option value="" label="--Please choose an option--"></option>
<option value="option1" label="Option 1"></option>
<option value="option2" label="Option 2"></option>
</blr-select>
<blr-checkbox
theme="Light"
size="md"
Expand All @@ -69,7 +97,10 @@ export class BlrFormExampleWithoutSlot extends LitElement {
checkinputid="checkInputId"
name="checkInput"
@blrCheckedChange=${this.handleCheckInput}
?checked=${this.checkBoxChecked}
.hasError=${this.checkInputHasError}
errorMessage="must be checked"
required="true"
.checked=${this.checkBoxChecked}
></blr-checkbox>
<blr-text-button
theme="Light"
Expand All @@ -88,52 +119,35 @@ export class BlrFormExampleWithoutSlot extends LitElement {

private handleSubmit(event) {
event.preventDefault();
const shadowFirstInputElement = this.firstInputElement.shadowRoot?.querySelector('input[name="firstInput"]');
const shadowSecondInputElement = this.secondInputElement.shadowRoot?.querySelector('input[name="secondInput"]');
const shadowCheckInputElement = this.checkboxInputElement.shadowRoot?.querySelector('input[name="checkInput"]');
const shadowSelectElement = this.selectElement.shadowRoot?.querySelector('select[name="select"]');

if (
(this.secondInputElement.hasAttribute('required') && this.secondInputValue.trim() === '') ||
(this.secondInputElement.hasAttribute('required') && this.secondInputValue.trim() === '') ||
!this.checkboxInputElement.hasAttribute('checked')
) {
if (this.firstInputElement.hasAttribute('required') && this.firstInputValue.trim() === '') {
this.firstInputElement._onPropChanged({
detail: {
hasError: true,
errorMessage: 'This is a required field',
},
});
}
if (!shadowFirstInputElement?.checkValidity()) {
this.firstTextInputHasError = true;
}

if (this.secondInputElement.hasAttribute('required') && this.secondInputValue.trim() === '') {
this.secondInputElement._onPropChanged({
detail: {
hasError: true,
errorMessage: 'This is a required field',
},
});
}
if (!shadowSecondInputElement?.checkValidity()) {
this.secondTextInputHasError = true;
}

if (!this.checkboxInputElement.hasAttribute('checked')) {
this.checkboxInputElement._onPropChanged({
detail: {
hasError: true,
errorMessage: 'This is a required field',
},
});
}
console.log('Please provide a value for both first input and last input fields and check the checkbox.');
// just to simulate the value change. Remove later
setTimeout(() => {
this.dispatchEvent(
new CustomEvent('propChanged', {
detail: { hasError: false, errorMessage: '' },
bubbles: true,
composed: true,
})
);
}, 3000);
return;
if (!shadowCheckInputElement?.checkValidity()) {
this.checkInputHasError = true;
}

if (!shadowSelectElement?.checkValidity()) {
this.selectHasError = true;
}

// just to simulate the value change. Remove later
setTimeout(() => {
this.firstTextInputHasError = false;
this.secondTextInputHasError = false;
this.checkInputHasError = false;
this.selectHasError = false;
}, 2000);

console.log(
`First Input: ${this.firstInputValue}, Second Input: ${this.secondInputValue}, checkbox checked: ${this.checkBoxChecked}`
);
Expand Down
16 changes: 16 additions & 0 deletions packages/ui-library/src/components/select/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,22 @@ export class BlrSelect extends LitElement {

@state() protected isFocused = false;

// TESTING BEGIN
// Identify the element as a form-associated custom element
static formAssociated = true;
private _internals: ElementInternals;

constructor() {
super();
// Get access to the internal form control APIs
this._internals = this.attachInternals();
}

public checkValidity() {
return this._internals.checkValidity;
}
// TESTING END

protected _optionElements: Element[] | undefined;

protected handleFocus = () => {
Expand Down
25 changes: 13 additions & 12 deletions packages/ui-library/src/components/text-input/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,20 +69,21 @@ export class BlrTextInput extends LitElement {
@state() protected currentType: InputTypes = this.type;
@state() protected isFocused = false;

connectedCallback() {
super.connectedCallback();
addEventListener('propChanged', this._onPropChanged);
}
// TESTING BEGIN
// Identify the element as a form-associated custom element
static formAssociated = true;
private _internals: ElementInternals;

disconnectedCallback() {
super.disconnectedCallback();
removeEventListener('propChanged', this._onPropChanged);
constructor() {
super();
// Get access to the internal form control APIs
this._internals = this.attachInternals();
}

_onPropChanged = (event: any) => {
this.hasError = event.detail.hasError;
this.errorMessage = event.detail.errorMessage;
};
public checkValidity() {
return this._internals.checkValidity;
}
// TESTING END

protected togglePassword = () => {
this.currentType = this.currentType === 'password' ? 'text' : 'password';
Expand Down Expand Up @@ -211,7 +212,7 @@ export class BlrTextInput extends LitElement {
@blur=${this.handleBlur}
@focus=${this.handleFocus}
maxlength="${this.maxLength}"
pattern="${this.pattern}"
pattern="${this.pattern || nothing}"
@select=${this.handleSelect}
/>
</div>
Expand Down

0 comments on commit e2f4e68

Please sign in to comment.