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 7, 2024
1 parent e84fe8d commit 9b8d23f
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 79 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
134 changes: 84 additions & 50 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,22 @@ 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() radioHasError: 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';
@property() radioErrorMessage: 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;
@query('blr-radio[name="radio"]') radioElement!: HTMLElement;

protected render() {
return html`
Expand All @@ -36,6 +43,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,23 +62,59 @@ 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-radio
theme="Light"
size="md"
value=""
label="Hint and error message"
.hasError=${this.radioHasError}
errormessage=${this.radioErrorMessage}
arialabel=""
erroricon="blrErrorFilled"
optionid="optionId"
name="radio"
required="true"
></blr-radio>
<blr-checkbox
theme="Light"
size="md"
checkedicon="blrCheckmark"
indeterminatedicon="blrMinus"
haslabel="true"
label="I accept the terms and conditions."
hintmessage="This is a small hint message"
hinticon="blrInfo"
erroricon=""
arialabel="check Input"
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 +133,41 @@ 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"]');
const shadowRadioElement = this.radioElement.shadowRoot?.querySelector('input[name="radio"]');

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 (this.secondInputElement.hasAttribute('required') && this.secondInputValue.trim() === '') {
this.secondInputElement._onPropChanged({
detail: {
hasError: true,
errorMessage: 'This is a required field',
},
});
}

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 (!shadowFirstInputElement?.checkValidity()) {
this.firstTextInputHasError = true;
}

if (!shadowSecondInputElement?.checkValidity()) {
this.secondTextInputHasError = true;
}

if (!shadowCheckInputElement?.checkValidity()) {
this.checkInputHasError = true;
}

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

if (!shadowRadioElement?.checkValidity()) {
this.radioHasError = true;
}

// just to simulate the value change. Remove later
setTimeout(() => {
this.firstTextInputHasError = false;
this.secondTextInputHasError = false;
this.checkInputHasError = false;
this.selectHasError = false;
this.radioHasError = 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/radio/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,22 @@ export class BlrRadio extends LitElement {

@property() theme: ThemeType = 'Light';

// 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 render() {
if (this.size) {
const dynamicStyles = this.theme === 'Light' ? [formLight, radioLight] : [formDark, radioDark];
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 9b8d23f

Please sign in to comment.