Skip to content

Commit

Permalink
Form: KolLink to KolLinkWc (#6922)
Browse files Browse the repository at this point in the history
  • Loading branch information
deleonio authored Oct 24, 2024
2 parents aa1d128 + c0e0583 commit 2f24cf2
Show file tree
Hide file tree
Showing 57 changed files with 452 additions and 282 deletions.
108 changes: 22 additions & 86 deletions packages/components/src/components/alert/component.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,10 @@
import type { JSX } from '@stencil/core';
import { Log, alertTypeOptions, alertVariantOptions, setState, validateHasCloser, validateLabel, watchBoolean, watchValidator } from '../../schema';
import { alertTypeOptions, alertVariantOptions, setState, validateHasCloser, validateLabel, watchBoolean, watchValidator } from '../../schema';
import { Component, h, Host, Prop, State, Watch } from '@stencil/core';

import { translate } from '../../i18n';
import { watchHeadingLevel } from '../heading/validation';
import { KolIconTag, KolHeadingWcTag, KolButtonWcTag } from '../../core/component-names';

import type { AlertAPI, AlertStates, AlertType, AlertVariant, HasCloserPropType, HeadingLevel, KoliBriAlertEventCallbacks, LabelPropType } from '../../schema';
const Icon = (props: { ariaLabel: string; icon: string; label?: string }) => {
return <KolIconTag class="heading-icon" _label={props.ariaLabel} _icons={props.icon} />;
};

const AlertIcon = (props: { label?: string; type?: AlertType }) => {
switch (props.type) {
case 'error':
return <Icon ariaLabel={translate('kol-error')} icon="codicon codicon-error" label={props.label} />;
case 'info':
return <Icon ariaLabel={translate('kol-info')} icon="codicon codicon-info" label={props.label} />;
case 'warning':
return <Icon ariaLabel={translate('kol-warning')} icon="codicon codicon-warning" label={props.label} />;
case 'success':
return <Icon ariaLabel={translate('kol-success')} icon="codicon codicon-pass" label={props.label} />;
default:
return <Icon ariaLabel={translate('kol-message')} icon="codicon codicon-comment" label={props.label} />;
}
};
import KolAlertFc, { type KolAlertFcProps } from '../../functional-components/Alert';

/**
* @internal
* @slot - Der Inhalt der Meldung.
Expand All @@ -35,76 +15,32 @@ const AlertIcon = (props: { label?: string; type?: AlertType }) => {
})
export class KolAlertWc implements AlertAPI {
private readonly close = () => {
if (this._on?.onClose !== undefined) {
this._on.onClose(new Event('Close'));
}
this._on?.onClose?.(new Event('Close'));
};

private readonly on = {
onClick: this.close,
private readonly handleAlertTimeout = () => {
this.validateAlert(false);
};

public render(): JSX.Element {
if (this.state._alert) {
/**
* - https://developer.mozilla.org/de/docs/Web/API/Navigator/vibrate
* - https://googlechrome.github.io/samples/vibration/
*/
try {
Log.debug(['Navigator should vibrate ...', navigator.vibrate([100, 75, 100, 75, 100])]);
} catch (e) {
Log.debug('Navigator does not support vibration.');
}

setTimeout(() => {
this.validateAlert(false);
}, 10000);
}
const { _alert, _hasCloser, _label, _level, _type, _variant } = this.state;

const props: KolAlertFcProps = {
alert: _alert,
hasCloser: _hasCloser,
label: _label,
level: _level,
type: _type,
variant: _variant,
onCloserClick: this.close,
onAlertTimeout: this.handleAlertTimeout,
};

return (
<Host
class={{
'kol-alert-wc': true,
alert: true,
[this.state._type as string]: true,
[this.state._variant as string]: true,
hasCloser: !!this.state._hasCloser,
}}
role={this.state._alert ? 'alert' : undefined}
>
<div class="heading">
<AlertIcon label={this.state._label} type={this.state._type} />
<div class="heading-content">
{typeof this.state._label === 'string' && this.state._label?.length > 0 && (
<KolHeadingWcTag _label={this.state._label} _level={this.state._level}></KolHeadingWcTag>
)}
{this.state._variant === 'msg' && (
<div class="content">
<slot />
</div>
)}
</div>
{this.state._hasCloser && (
<KolButtonWcTag
class="close"
_ariaDescription={this.state._label?.trim() || ''}
_hideLabel
_icons={{
left: {
icon: 'codicon codicon-close',
},
}}
_label={translate('kol-close-alert')}
_on={this.on}
_tooltipAlign="left"
></KolButtonWcTag>
)}
</div>
{this.state._variant === 'card' && (
<div class="content">
<slot />
</div>
)}
<Host>
<KolAlertFc {...props}>
<slot />
</KolAlertFc>
</Host>
);
}
Expand Down
4 changes: 3 additions & 1 deletion packages/components/src/components/alert/test/html.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ export const getAlertHtml = (props: AlertProps, innerHTML = '', additionalHTML =

return `<kol-alert${additionalHTML} class="kol-alert">
<mock:shadow-root>
<kol-alert-wc class="kol-alert-wc alert ${type} ${props._variant}${props._hasCloser ? ' hasCloser' : ''}"${props._alert === true ? ' role="alert"' : ''}>
<kol-alert-wc>
<div class="kol-alert-wc alert ${type} ${props._variant}${props._hasCloser ? ' hasCloser' : ''}"${props._alert === true ? ' role="alert"' : ''}>
<div class="heading">
<${KolIconTag}
class="heading-icon"
Expand Down Expand Up @@ -80,6 +81,7 @@ export const getAlertHtml = (props: AlertProps, innerHTML = '', additionalHTML =
</div>`
: ''
}
</div>
</kol-alert-wc>
</mock:shadow-root>
${innerHTML}
Expand Down
82 changes: 48 additions & 34 deletions packages/components/src/components/form/shadow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ 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 { KolAlertWcTag, KolLinkTag } from '../../core/component-names';
import { KolLinkWcTag } from '../../core/component-names';
import KolAlertFc from '../../functional-components/Alert';

/**
* @slot - Inhalt der Form.
*/
Expand Down Expand Up @@ -49,42 +51,54 @@ export class KolForm implements FormAPI {
}
};

private renderErrorList(errorList?: ErrorListPropType[]): JSX.Element {
return (
<KolAlertFc type="error" variant="msg">
{translate('kol-error-list-message')}
<nav aria-label={translate('kol-error-list')}>
<ul>
{errorList?.map((error, index) => (
<li key={index}>
<KolLinkWcTag
_href={error.selector}
_label={error.message}
_on={{ onClick: this.handleLinkClick }}
ref={(el) => {
if (index === 0) this.errorListElement = el;
}}
/>
</li>
))}
</ul>
</nav>
</KolAlertFc>
);
}

private renderFormElement(): JSX.Element {
return (
<form method="post" onSubmit={this.onSubmit} onReset={this.onReset} autoComplete="off" noValidate>
{this.state._requiredText === true ? (
<p>
<div class="mandatory-fields-hint">{translate('kol-form-description')}</div>
</p>
) : typeof this.state._requiredText === 'string' && this.state._requiredText.length > 0 ? (
<p>
<div class="mandatory-fields-hint">{this.state._requiredText}</div>
</p>
) : null}
<slot />
</form>
);
}

public render(): JSX.Element {
const hasErrorList = Array.isArray(this._errorList) && this._errorList.length > 0;

return (
<Host class="kol-form">
{this._errorList && this._errorList.length > 0 && (
<KolAlertWcTag _type="error">
{translate('kol-error-list-message')}
<nav aria-label={translate('kol-error-list')}>
<ul>
{this._errorList.map((error, index) => (
<li key={index}>
<KolLinkTag
_href={error.selector}
_label={error.message}
_on={{ onClick: this.handleLinkClick }}
ref={(el?: HTMLKolLinkElement) => {
if (index === 0) this.errorListElement = el;
}}
/>
</li>
))}
</ul>
</nav>
</KolAlertWcTag>
)}
<form method="post" onSubmit={this.onSubmit} onReset={this.onReset} autoComplete="off" noValidate>
{this.state._requiredText === true ? (
<p>
<div class="mandatory-fields-hint">{translate('kol-form-description')}</div>
</p>
) : typeof this.state._requiredText === 'string' && this.state._requiredText.length > 0 ? (
<p>
<div class="mandatory-fields-hint">{this.state._requiredText}</div>
</p>
) : null}
<slot />
</form>
{hasErrorList && this.renderErrorList(this._errorList)}
{this.renderFormElement()}
</Host>
);
}
Expand Down
1 change: 1 addition & 0 deletions packages/components/src/components/form/style.scss
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
@import '../@shared/mixins';
@import '../input-line';
@import '../@shared/kol-alert-mixin.scss';
@import '../link';

@include kol-alert-styles;

Expand Down
4 changes: 2 additions & 2 deletions packages/components/src/components/input-radio/shadow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ import { stopPropagation, tryToDispatchKoliBriEvent } from '../../utils/events';
import { getRenderStates } from '../input/controller';
import { InternalUnderlinedAccessKey } from '../span/InternalUnderlinedAccessKey';
import { InputRadioController } from './controller';
import { FormFieldMsg } from '../@shared/form-field-msg';
import { KolInputWcTag } from '../../core/component-names';
import { propagateSubmitEventToForm } from '../form/controller';
import { KolFormFieldMsgFc } from '../../functional-components';

/**
* @slot - Die Legende/Überschrift der Radiobuttons.
Expand Down Expand Up @@ -167,7 +167,7 @@ export class KolInputRadio implements InputRadioAPI, FocusableElement {
</KolInputWcTag>
);
})}
{hasError && <FormFieldMsg _alert={this.state._alert} _hideError={this.state._hideError} _msg={this.state._msg} _id={this.state._id} />}
{hasError && <KolFormFieldMsgFc _alert={this.state._alert} _hideError={this.state._hideError} _msg={this.state._msg} _id={this.state._id} />}
{hasHint && <span class="hint">{this.state._hint}</span>}
</fieldset>
</Host>
Expand Down
4 changes: 2 additions & 2 deletions packages/components/src/components/input/component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ import type {
TooltipAlignPropType,
W3CInputValue,
} from '../../schema';
import { FormFieldMsg } from '../@shared/form-field-msg';
import type { Props } from './types';
import { KolButtonWcTag, KolIconTag, KolTooltipWcTag } from '../../core/component-names';
import { KolFormFieldMsgFc } from '../../functional-components';

/**
* @internal
Expand Down Expand Up @@ -120,7 +120,7 @@ export class KolInputWc implements Props {
_label={this._label}
></KolTooltipWcTag>
)}
{showFormFieldMsg && <FormFieldMsg _alert={this._alert} _hideError={this._hideError} _msg={this._msg} _id={this._id} />}
{showFormFieldMsg && <KolFormFieldMsgFc _alert={this._alert} _hideError={this._hideError} _msg={this._msg} _id={this._id} />}
{Array.isArray(this._suggestions) && this._suggestions.length > 0 && (
<datalist id={`${this._id}-list`}>
{this._suggestions.map((option: W3CInputValue) => (
Expand Down
70 changes: 70 additions & 0 deletions packages/components/src/functional-components/Alert/Alert.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { h, type FunctionalComponent as FC } from '@stencil/core';
import type { JSXBase } from '@stencil/core/internal';
import clsx from 'clsx';
import type { InternalAlertProps } from '../../schema';
import { Log } from '../../schema';
import { translate } from '../../i18n';
import AlertIcon from '../AlertIcon';
import { KolButtonWcTag, KolHeadingWcTag } from '../../core/component-names';

export type KolAlertFcProps = JSXBase.HTMLAttributes<HTMLDivElement> &
Partial<Omit<InternalAlertProps, 'on'>> & {
onCloserClick?: () => void;
onAlertTimeout?: () => void;
};

const KolAlertFc: FC<KolAlertFcProps> = (props, children) => {
const { class: classNames = {}, type = 'default', variant = 'msg', label, hasCloser, alert, onAlertTimeout, onCloserClick, level, ...other } = props;

if (alert) {
/**
* - https://developer.mozilla.org/de/docs/Web/API/Navigator/vibrate
* - https://googlechrome.github.io/samples/vibration/
*/
try {
Log.debug(['Navigator should vibrate ...', navigator.vibrate([100, 75, 100, 75, 100])]);
} catch (e) {
Log.debug('Navigator does not support vibration.');
}

setTimeout(() => {
onAlertTimeout?.();
}, 10000);
}

const rootProps: Partial<JSXBase.HTMLAttributes<HTMLDivElement>> = {
class: clsx('kol-alert-wc', 'alert', type, variant, { hasCloser: !!hasCloser }, classNames),
role: alert ? 'alert' : undefined,
...other,
};

return (
<div {...rootProps}>
<div class="heading">
<AlertIcon label={label} type={type} />
<div class="heading-content">
{label ? <KolHeadingWcTag _label={label} _level={level} /> : null}
{variant === 'msg' && <div class="content">{children}</div>}
</div>
{hasCloser && (
<KolButtonWcTag
class="close"
_ariaDescription={label?.trim() || ''}
_hideLabel
_icons={{
left: {
icon: 'codicon codicon-close',
},
}}
_label={translate('kol-close-alert')}
_on={{ onClick: onCloserClick }}
_tooltipAlign="left"
></KolButtonWcTag>
)}
</div>
{variant === 'card' && <div class="content">{children}</div>}
</div>
);
};

export default KolAlertFc;
2 changes: 2 additions & 0 deletions packages/components/src/functional-components/Alert/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { default } from './Alert';
export * from './Alert';
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`KolAlertFc should render 1`] = `
<div class="alert default kol-alert-wc msg">
<div class="heading">
<kol-icon _icons="codicon codicon-comment" _label="kol-message" class="heading-icon"></kol-icon>
<div class="heading-content">
<div class="content"></div>
</div>
</div>
</div>
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { h } from '@stencil/core';
import KolAlertFc from './..';
import { renderFunctionalComponentToSpecPage } from '../../../utils/testing';

describe('KolAlertFc', () => {
it('should render', async () => {
const page = await renderFunctionalComponentToSpecPage(() => <KolAlertFc />);

expect(page.root).toMatchSnapshot();
});
});
Loading

0 comments on commit 2f24cf2

Please sign in to comment.