Skip to content

Commit

Permalink
Merge pull request #42 from xsolla/PAYMENTS-16979-errors
Browse files Browse the repository at this point in the history
fix(text): show touched input error only
  • Loading branch information
simbirromanmakarov authored Nov 2, 2023
2 parents 0bf0d7b + 43fbb50 commit f356639
Show file tree
Hide file tree
Showing 6 changed files with 47 additions and 22 deletions.
2 changes: 2 additions & 0 deletions src/core/actions/next-action.interface.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { CheckStatusAction } from './check-status.action.type';
import { RedirectAction } from './redirect/redirect.action.type';
import { ShowErrorsAction } from './show-errors.action.type';
import { ShowFieldsAction } from './show-fields.action.type';
import { StatusUpdatedAction } from './status-updated.action.type';
Expand All @@ -9,4 +10,5 @@ export type NextAction =
| ShowFieldsAction
| StatusUpdatedAction
| ShowErrorsAction
| RedirectAction
| ThreeDsAction;
20 changes: 20 additions & 0 deletions src/core/actions/redirect/redirect.action.type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Field } from '../../form/field.interface';
import { FormError } from '../../form/form-error.interface';
import { FormMessage } from '../../form/form-message.interface';
import { Action } from '../action.interface';

export type RedirectActionType = 'redirect';

export interface RedirectActionData {
fields: Field[];
errors: FormError[] | null;
messages: FormMessage[] | undefined;
invoiceId?: string;
order?: object;
redirect: {
redirectUrl: string;
data: object;
};
}

export type RedirectAction = Action<RedirectActionType, RedirectActionData>;
1 change: 1 addition & 0 deletions src/core/form/field-status.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ export interface FieldStatus {
validationStatus: 'VALID' | 'INVALID' | 'PENDING' | 'DISABLED';
errors: ValidationErrors | null;
isFocused?: boolean;
isTouched?: boolean;
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
import { ElementEventName } from './element-events.enum';

export abstract class BaseControl<
Config extends ControlComponentConfig
Config extends ControlComponentConfig,
> extends WebComponentAbstract {
protected postMessagesClient: PostMessagesClient;
protected headlessCheckout: HeadlessCheckout;
Expand All @@ -33,7 +33,7 @@ export abstract class BaseControl<
}

protected async getComponentConfig(
inputName: string
inputName: string,
): Promise<ControlComponentConfig> {
const msg: Message<{ inputName: string }> = {
name: EventName.getControlComponentConfig,
Expand All @@ -48,7 +48,7 @@ export abstract class BaseControl<
return getControlComponentConfigHandler(message, (controlName) => {
return msg.data?.inputName === controlName;
});
}
},
) as Promise<ControlComponentConfig>;
}

Expand All @@ -61,7 +61,7 @@ export abstract class BaseControl<
value,
},
},
publicControlOnValueChanges
publicControlOnValueChanges,
);
}

Expand All @@ -74,7 +74,7 @@ export abstract class BaseControl<
event: ElementEventName.focus,
},
},
publicControlChangeState
publicControlChangeState,
);
}

Expand All @@ -87,7 +87,7 @@ export abstract class BaseControl<
event: ElementEventName.blur,
},
},
publicControlChangeState
publicControlChangeState,
);
}

Expand All @@ -110,7 +110,7 @@ export abstract class BaseControl<
}

protected getErrorFromFieldStatus(
errors: ValidationErrors | null
errors: ValidationErrors | null,
): string | null {
if (!errors) {
return null;
Expand All @@ -128,10 +128,10 @@ export abstract class BaseControl<
if (!errorElement) {
const newErrorElement = this.window.document.createElement('div');
newErrorElement.classList.add('field-error');
newErrorElement.textContent = this.config.error;
newErrorElement.innerHTML = this.config.error;
rootElement.appendChild(newErrorElement);
} else {
errorElement.textContent = this.config.error;
errorElement.innerHTML = this.config.error;
}
} else {
if (errorElement) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const invalidFieldsStatus: FormFieldsStatus = {
[fieldName]: {
name: fieldName,
validationStatus: 'INVALID',
isTouched: true,
errors: {
errorName: {
message: 'message',
Expand Down Expand Up @@ -59,7 +60,7 @@ describe('TextComponent', () => {

window.customElements.define(
WebComponentTagName.TextComponent,
TextComponent
TextComponent,
);

beforeEach(() => {
Expand Down Expand Up @@ -124,7 +125,7 @@ describe('TextComponent', () => {
spyOn(headlessCheckout.form, 'onFieldsStatusChange').and.callFake(
(callbackFn) => {
setTimeout(() => (callback = callbackFn));
}
},
);

element = createComponent();
Expand All @@ -146,7 +147,7 @@ describe('TextComponent', () => {
spyOn(headlessCheckout.form, 'onFieldsStatusChange').and.callFake(
(callbackFn) => {
setTimeout(() => (callback = callbackFn));
}
},
);

element = createComponent();
Expand All @@ -168,7 +169,7 @@ describe('TextComponent', () => {
spyOn(headlessCheckout.form, 'onFieldsStatusChange').and.callFake(
(callbackFn) => {
setTimeout(() => (callback = callbackFn));
}
},
);

element = createComponent();
Expand All @@ -190,7 +191,7 @@ describe('TextComponent', () => {
spyOn(headlessCheckout.form, 'onFieldsStatusChange').and.callFake(
(callbackFn) => {
setTimeout(() => (callback = callbackFn));
}
},
);

element = createComponent();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { getControlComponentConfigHandler } from '../get-control-component-confi
import { HeadlessCheckout } from '../../headless-checkout';
import { ValidationErrors } from '../../../../core/form/validation-errors.interface';
import { TextComponentConfig } from './text-component.config.interface';
import { FieldStatus } from '../../../../core/form/field-status.interface';

export class TextComponent extends SecureComponentAbstract {
protected config?: TextComponentConfig;
Expand Down Expand Up @@ -43,7 +44,7 @@ export class TextComponent extends SecureComponentAbstract {
}

protected async getControlComponentConfig(
inputName: string
inputName: string,
): Promise<TextComponentConfig> {
const msg: Message<{ inputName: string }> = {
name: EventName.getControlComponentConfig,
Expand All @@ -58,13 +59,13 @@ export class TextComponent extends SecureComponentAbstract {
return getControlComponentConfigHandler(message, (controlName) => {
return msg.data?.inputName === controlName;
});
}
},
) as Promise<TextComponentConfig>;
}

protected readonly configLoadedHandler = (
config: TextComponentConfig,
componentName: string
componentName: string,
): void => {
this.config = config;
this.componentName = componentName;
Expand Down Expand Up @@ -113,22 +114,22 @@ export class TextComponent extends SecureComponentAbstract {
}

this.config.error = this.getFirstError(fieldStatus.errors);
this.updateError(fieldStatus.isFocused);
this.updateError(fieldStatus);
});
}

private updateError(isFieldInFocus: boolean | undefined): void {
private updateError(fieldStatus: FieldStatus): void {
const rootElement = this.shadowRoot ?? this;
const errorElement = rootElement.querySelector('.field-error');

if (this.config?.error && !isFieldInFocus) {
if (this.config?.error && fieldStatus.isTouched && !fieldStatus.isFocused) {
if (!errorElement) {
const newErrorElement = this.window.document.createElement('div');
newErrorElement.classList.add('field-error');
newErrorElement.textContent = this.config.error;
newErrorElement.innerHTML = this.config.error;
rootElement.appendChild(newErrorElement);
} else {
errorElement.textContent = this.config.error;
errorElement.innerHTML = this.config.error;
}
} else {
if (errorElement) {
Expand Down

0 comments on commit f356639

Please sign in to comment.