Skip to content

Commit

Permalink
Refactor error list handling: improve link click behavior and enhance… (
Browse files Browse the repository at this point in the history
  • Loading branch information
deleonio authored Jan 29, 2025
2 parents fdf1a5f + f9b1ca9 commit cfc5f18
Show file tree
Hide file tree
Showing 455 changed files with 74 additions and 51 deletions.
52 changes: 27 additions & 25 deletions packages/components/src/components/form/shadow.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import type { JSX } from '@stencil/core';
import { Component, h, Host, Method, Prop, State, Watch } from '@stencil/core';
import { validateErrorList, watchBoolean, watchString } from '../../schema';
import { Component, h, Host, Prop, State, Watch, Method } from '@stencil/core';

import { translate } from '../../i18n';

import type { ErrorListPropType, FormAPI, FormStates, KoliBriFormCallbacks, Stringified } from '../../schema';
import { KolLinkWcTag } from '../../core/component-names';
import KolAlertFc from '../../functional-components/Alert';
import type { ErrorListPropType, FormAPI, FormStates, KoliBriFormCallbacks, Stringified } from '../../schema';

/**
* @slot - Inhalt der Form.
Expand All @@ -19,7 +19,8 @@ import KolAlertFc from '../../functional-components/Alert';
shadow: true,
})
export class KolForm implements FormAPI {
errorListElement?: HTMLElement;
errorListBlock?: HTMLElement;
errorListFirstLink?: HTMLElement;

/* Hint: This method may not be used at all while events are handled in form/controller#propagateSubmitEventToForm */
private readonly onSubmit = (event: Event) => {
Expand All @@ -38,29 +39,19 @@ export class KolForm implements FormAPI {
}
};

private readonly handleLinkClick = (event: Event) => {
const href = (event.target as HTMLAnchorElement | undefined)?.href;
if (href) {
const hrefUrl = new URL(href);

const targetElement = document.querySelector<HTMLElement>(hrefUrl.hash);
if (targetElement && typeof targetElement.focus === 'function') {
targetElement.scrollIntoView({ behavior: 'smooth' });
targetElement.focus();
}
private readonly handleLinkClick = (selector: string) => {
const targetElement = document.querySelector<HTMLElement>(selector);
if (targetElement && typeof targetElement.focus === 'function') {
targetElement.scrollIntoView({ behavior: 'smooth' });
targetElement.focus();
}
};

private renderErrorList(errorList?: ErrorListPropType[]): JSX.Element {
return (
<KolAlertFc
ref={(el) => {
setTimeout(() => {
if (el && typeof el.focus === 'function') {
el.scrollIntoView({ behavior: 'smooth' });
el.focus();
}
}, 250);
this.errorListBlock = el;
}}
type="error"
variant="card"
Expand All @@ -71,11 +62,14 @@ export class KolForm implements FormAPI {
{errorList?.map((error, index) => (
<li key={index}>
<KolLinkWcTag
_href={error.selector}
_href=""
_label={error.message}
_on={{ onClick: this.handleLinkClick }}
_on={{ onClick: typeof error.selector === 'string' ? () => this.handleLinkClick(String(error.selector)) : error.selector }}
ref={(el) => {
if (index === 0) this.errorListElement = el;
if (index === 0) {
this.errorListFirstLink = el;
this.scrollToErrorList();
}
}}
/>
</li>
Expand Down Expand Up @@ -114,11 +108,19 @@ export class KolForm implements FormAPI {
);
}

private scrollToErrorList(): void {
this.errorListBlock?.scrollIntoView({
behavior: 'smooth',
block: 'start',
});
setTimeout(() => {
this.errorListFirstLink?.querySelector('a')?.focus();
}, 250);
}

@Method()
async focusErrorList(): Promise<void> {
if (this._errorList && this._errorList.length > 0) {
this.errorListElement?.focus();
}
this.scrollToErrorList();
return Promise.resolve();
}

Expand Down
11 changes: 9 additions & 2 deletions packages/components/src/schema/props/error-list.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import type { Generic } from 'adopted-style-sheets';
import { EventCallback } from '../types';
import { watchValidator } from '../utils';

/* types */
export type ErrorListPropType = {
message: string;
selector: string;
selector: string | EventCallback<Event>;
};

export type PropErrorList = {
Expand All @@ -13,5 +14,11 @@ export type PropErrorList = {

/* validator */
export const validateErrorList = (component: Generic.Element.Component, value?: ErrorListPropType[]): void => {
watchValidator(component, 'errorList', (value): boolean => typeof value === 'object', new Set(['Object']), value);
watchValidator(
component,
'errorList',
(value): boolean => Array.isArray(value) && value.find((v) => !(typeof v === 'string' || typeof v === 'function')) === undefined,
new Set(['string', 'function']),
value,
);
};
62 changes: 38 additions & 24 deletions packages/samples/react/src/components/form/error-list.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,42 @@
import { KolButton, KolForm, KolInputText } from '@public-ui/react';
import type { FC } from 'react';
import React from 'react';
import React, { useRef } from 'react';
import { SampleDescription } from '../SampleDescription';
import { KolForm, KolInputText } from '@public-ui/react';

export const FormErrorList: FC = () => (
<>
<SampleDescription>
<p>This sample shows a form with error messages.</p>
</SampleDescription>
export const FormErrorList: FC = () => {
const formRef = useRef<HTMLKolFormElement>();
return (
<>
<SampleDescription>
<p>This sample shows a form with error messages.</p>
</SampleDescription>

<KolForm
className="w-full"
_errorList={[
{
message: 'Error in Input 2',
selector: '#input2',
},
]}
>
<div className="grid gap-2">
<KolInputText id="input1" _label="Input 1" />
<KolInputText id="input2" _label="Input 2" _touched _msg={{ _description: 'Input error', _type: 'error' }} />
<KolInputText id="input3" _label="Input 3" />
</div>
</KolForm>
</>
);
<KolForm
className="w-full"
ref={formRef}
_errorList={[
{
message: 'Error in Input 2',
selector: '#input2',
},
]}
>
<div className="grid gap-2">
<KolInputText id="input1" _label="Input 1" />
<KolInputText id="input2" _label="Input 2" _touched _msg={{ _description: 'Input error', _type: 'error' }} />
<KolInputText id="input3" _label="Input 3" />
<div>
<KolButton
_label="ScrollTo"
_on={{
onClick: () => {
formRef.current?.focusErrorList();
},
}}
/>
</div>
</div>
</KolForm>
</>
);
};
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit cfc5f18

Please sign in to comment.