Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ The samples are located in `packages/samples/react` and demonstrate how to use t

- Formatting is enforced via **Prettier** with settings defined in `prettier.config.js` (print width 160, single quotes, tabs).
- `.editorconfig` sets `indent_style = tab` and `max_line_length = 160` for code files. Markdown and YAML files use spaces.
- ESLint and Stylelint are run using `pnpm lint`. Pre‑commit hooks run `lint-staged` which formats and lints changed files.
- ESLint and Stylelint are run using `pnpm lint`. Pre‑commit hooks run `lint-staged` which formats and lints changed files. Lint rules should **not** be disabled via inline comments. Instead, describe the problem and work towards a clean solution.
- Lists and enumerations in code should be kept in alphanumeric order. This also applies to import specifiers and union type literals.
- Commit messages follow the **Conventional Commits** specification.
- See also the [Contributing Guide](CONTRIBUTING.md) for more details on coding conventions and best practices.
Expand Down
8 changes: 8 additions & 0 deletions packages/components/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,14 @@ To make the components easier to learn, property names and their descriptions sh
- Whenever possible, keep the types of identical property names the same.
- Minimize the number of different properties, descriptions and types.

#### Props Handling

Every property lives in a dedicated file under `src/schema/props`. The file
contains the prop type, the prop schema and a validator function. Components and
their controllers must import these validators instead of implementing custom
logic. Always use the validator exported from the prop schema to keep behaviour
consistent across components.

#### Open vs Show

Use `_open` when the component renders an element on demand, for example a drawer or popover that appears from nothing. Use `_show` when the element already exists in the DOM and you only toggle its visibility.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,23 +18,23 @@ import type {
} from '../../../schema';
import {
a11yHint,
a11yHintDisabled,
devHint,
objectObjectHandler,
parseJson,
setState,
validateAccessKey,
validateAdjustHeight,
validateDisabled,
validateHideMsg,
validateHideLabel,
validateLabelWithExpertSlot,
validateMsg,
validateHint,
validateShortKey,
validateTabIndex,
validateTooltipAlign,
watchBoolean,
watchString,
} from '../../../schema';
import { validateTabIndex } from '../../../schema/props/tab-index';

import { dispatchDomEvent, KolEvent } from '../../../utils/events';
import { ControlledInputController } from '../../input-adapter-leanup/controller';
Expand Down Expand Up @@ -66,10 +66,7 @@ export class InputController extends ControlledInputController implements Watche
}

public validateDisabled(value?: DisabledPropType): void {
watchBoolean(this.component, '_disabled', value);
if (value === true) {
a11yHintDisabled();
}
validateDisabled(this.component, value);
}
public validateTooltipAlign(value?: TooltipAlignPropType): void {
validateTooltipAlign(this.component, value);
Expand Down Expand Up @@ -100,7 +97,7 @@ export class InputController extends ControlledInputController implements Watche
}

public validateHint(value?: HintPropType): void {
watchString(this.component, '_hint', value);
validateHint(this.component, value);
}

public validateId(value?: string): void {
Expand Down
37 changes: 17 additions & 20 deletions packages/components/src/components/alert/component.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
import type { JSX } from '@stencil/core';
import { alertTypeOptions, alertVariantOptions, setState, validateHasCloser, validateLabel, watchBoolean, watchValidator } from '../../schema';
import { setState, validateAlertType, validateAlertVariant, validateHasCloser, validateLabel, watchBoolean } from '../../schema';
import { Component, Element, h, Prop, State, Watch } from '@stencil/core';
import { watchHeadingLevel } from '../heading/validation';
import type { AlertAPI, AlertStates, AlertType, AlertVariant, HasCloserPropType, HeadingLevel, KoliBriAlertEventCallbacks, LabelPropType } from '../../schema';
import type {
AlertAPI,
AlertStates,
AlertTypePropType,
AlertVariantPropType,
HasCloserPropType,
HeadingLevel,
KoliBriAlertEventCallbacks,
LabelPropType,
} from '../../schema';
import KolAlertFc, { type KolAlertFcProps } from '../../functional-components/Alert';
import { dispatchDomEvent, KolEvent } from '../../utils/events';

Expand Down Expand Up @@ -78,12 +87,12 @@ export class KolAlertWc implements AlertAPI {
/**
* Defines either the type of the component or of the components interactive element.
*/
@Prop() public _type?: AlertType = 'default';
@Prop() public _type?: AlertTypePropType = 'default';

/**
* Defines which variant should be used for presentation.
*/
@Prop() public _variant?: AlertVariant = 'msg';
@Prop() public _variant?: AlertVariantPropType = 'msg';

@State() public state: AlertStates = {
_level: 0,
Expand Down Expand Up @@ -137,25 +146,13 @@ export class KolAlertWc implements AlertAPI {
}

@Watch('_type')
public validateType(value?: AlertType): void {
watchValidator(
this,
'_type',
(value?) => typeof value === 'string' && alertTypeOptions.includes(value),
new Set(`String {${alertTypeOptions.join(', ')}`),
value,
);
public validateType(value?: AlertTypePropType): void {
validateAlertType(this, value);
}

@Watch('_variant')
public validateVariant(value?: AlertVariant): void {
watchValidator(
this,
'_variant',
(value?) => typeof value === 'string' && alertVariantOptions.includes(value),
new Set(`AlertVariant {${alertVariantOptions.join(', ')}`),
value,
);
public validateVariant(value?: AlertVariantPropType): void {
validateAlertVariant(this, value);
}

public componentWillLoad(): void {
Expand Down
6 changes: 3 additions & 3 deletions packages/components/src/components/alert/shadow.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { AlertProps, AlertStates, AlertType, AlertVariant, HeadingLevel, KoliBriAlertEventCallbacks, LabelPropType } from '../../schema';
import type { AlertProps, AlertStates, AlertTypePropType, AlertVariantPropType, HeadingLevel, KoliBriAlertEventCallbacks, LabelPropType } from '../../schema';
import type { JSX } from '@stencil/core';
import { Component, h, Prop, State } from '@stencil/core';
import { KolAlertWcTag } from '../../core/component-names';
Expand Down Expand Up @@ -59,12 +59,12 @@ export class KolAlert implements AlertProps {
/**
* Defines either the type of the component or of the components interactive element.
*/
@Prop() public _type?: AlertType = 'default';
@Prop() public _type?: AlertTypePropType = 'default';

/**
* Defines which variant should be used for presentation.
*/
@Prop() public _variant?: AlertVariant = 'msg';
@Prop() public _variant?: AlertVariantPropType = 'msg';

@State() public state: AlertStates = {
_level: 0,
Expand Down
2 changes: 1 addition & 1 deletion packages/components/src/components/button/component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,10 @@ import {
validateIcons,
validateLabelWithExpertSlot,
validateShortKey,
validateTabIndex,
validateTooltipAlign,
watchString,
} from '../../schema';
import { validateTabIndex } from '../../schema/props/tab-index';
import type { JSX } from '@stencil/core';
import { Component, Element, h, Host, Method, Prop, State, Watch } from '@stencil/core';

Expand Down
13 changes: 7 additions & 6 deletions packages/components/src/components/combobox/controller.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { ComboboxWatches, ComboboxProps, SuggestionsPropType } from '../../schema';
import { watchBoolean, validateSuggestions, watchString } from '../../schema';
import type { ComboboxProps, ComboboxWatches, PlaceholderPropType, RequiredPropType, SuggestionsPropType } from '../../schema';
import { validatePlaceholder, validateRequired, validateSuggestions, watchString } from '../../schema';

import { InputIconController } from '../@deprecated/input/controller-icon';

Expand All @@ -13,11 +13,12 @@ export class ComboboxController extends InputIconController implements ComboboxW
this.component = component;
}

public validatePlaceholder(value?: string): void {
watchString(this.component, '_placeholder', value);
public validatePlaceholder(value?: PlaceholderPropType): void {
validatePlaceholder(this.component, value);
}
public validateRequired(value?: boolean): void {
watchBoolean(this.component, '_required', value);

public validateRequired(value?: RequiredPropType): void {
validateRequired(this.component, value);
}

public validateSuggestions(value?: SuggestionsPropType): void {
Expand Down
32 changes: 17 additions & 15 deletions packages/components/src/components/combobox/shadow.tsx
Original file line number Diff line number Diff line change
@@ -1,37 +1,39 @@
import type { JSX } from '@stencil/core';
import { Component, Element, h, Listen, Method, Prop, State, Watch } from '@stencil/core';
import clsx from 'clsx';
import { getRenderStates } from '../../functional-component-wrappers/_helpers/getRenderStates';
import KolFormFieldStateWrapperFc, { type FormFieldStateWrapperProps } from '../../functional-component-wrappers/FormFieldStateWrapper/FormFieldStateWrapper';
import KolInputContainerFc from '../../functional-component-wrappers/InputContainerStateWrapper/InputContainerStateWrapper';
import type { InputStateWrapperProps } from '../../functional-component-wrappers/InputStateWrapper/InputStateWrapper';
import KolInputStateWrapperFc from '../../functional-component-wrappers/InputStateWrapper/InputStateWrapper';
import CustomSuggestionsOptionFc from '../../functional-components/CustomSuggestionsOption/CustomSuggestionsOption';
import CustomSuggestionsOptionsGroupFc from '../../functional-components/CustomSuggestionsOptionsGroup';
import CustomSuggestionsToggleFc from '../../functional-components/CustomSuggestionsToggle';
import type {
ComboboxAPI,
ComboboxStates,
DisabledPropType,
HideLabelPropType,
HideMsgPropType,
HintPropType,
IconsHorizontalPropType,
IdPropType,
InputTypeOnDefault,
LabelWithExpertSlotPropType,
MsgPropType,
NamePropType,
PlaceholderPropType,
RequiredPropType,
ShortKeyPropType,
Stringified,
SuggestionsPropType,
SyncValueBySelectorPropType,
TooltipAlignPropType,
DisabledPropType,
HideLabelPropType,
HintPropType,
W3CInputValue,
} from '../../schema';
import clsx from 'clsx';
import type { EventDetail } from '../../schema/interfaces/EventDetail';
import { nonce } from '../../utils/dev.utils';
import KolFormFieldStateWrapperFc, { type FormFieldStateWrapperProps } from '../../functional-component-wrappers/FormFieldStateWrapper/FormFieldStateWrapper';
import { ComboboxController } from './controller';
import { getRenderStates } from '../../functional-component-wrappers/_helpers/getRenderStates';
import type { InputStateWrapperProps } from '../../functional-component-wrappers/InputStateWrapper/InputStateWrapper';
import KolInputStateWrapperFc from '../../functional-component-wrappers/InputStateWrapper/InputStateWrapper';
import KolInputContainerFc from '../../functional-component-wrappers/InputContainerStateWrapper/InputContainerStateWrapper';
import CustomSuggestionsToggleFc from '../../functional-components/CustomSuggestionsToggle';
import CustomSuggestionsOptionFc from '../../functional-components/CustomSuggestionsOption/CustomSuggestionsOption';
import CustomSuggestionsOptionsGroupFc from '../../functional-components/CustomSuggestionsOptionsGroup';
import type { EventDetail } from '../../schema/interfaces/EventDetail';

/**
* @slot - Die Beschriftung des Eingabefeldes.
Expand Down Expand Up @@ -490,7 +492,7 @@ export class KolCombobox implements ComboboxAPI {
}

@Watch('_placeholder')
public validatePlaceholder(value?: string): void {
public validatePlaceholder(value?: PlaceholderPropType): void {
this.controller.validatePlaceholder(value);
}

Expand Down Expand Up @@ -561,7 +563,7 @@ export class KolCombobox implements ComboboxAPI {
}

@Watch('_required')
public validateRequired(value?: boolean): void {
public validateRequired(value?: RequiredPropType): void {
this.controller.validateRequired(value);
}

Expand Down
17 changes: 2 additions & 15 deletions packages/components/src/components/heading/validation.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,8 @@
import type { HeadingLevel } from '../../schema';
import { headingLevelOptions, watchValidator } from '../../schema';
import { validateLevel } from '../../schema';

import type { Generic } from 'adopted-style-sheets';

export const watchHeadingLevel = (component: Generic.Element.Component, value?: HeadingLevel): void => {
watchValidator(
component,
'_level',
(value?: HeadingLevel): boolean => {
return typeof value === 'number' && headingLevelOptions.includes(value);
},
new Set([`Number {${headingLevelOptions.join(', ')}`]),
value,
{
// TODO: options not in the validator
defaultValue: 1,
required: true,
},
);
validateLevel(component, value);
};
21 changes: 8 additions & 13 deletions packages/components/src/components/input-checkbox/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ import type {
CheckedPropType,
IndeterminatePropType,
InputCheckboxIconsProp,
InputCheckboxIconsPropType,
InputCheckboxIconsState,
InputCheckboxProps,
InputCheckboxVariant,
InputCheckboxVariantPropType,
InputCheckboxWatches,
LabelAlignPropType,
StencilUnknown,
Stringified,
} from '../../schema';
import { inputCheckboxVariantOptions, isString, setState, validateChecked, validateIndeterminate, validateLabelAlign, watchValidator } from '../../schema';
import { isString, setState, validateChecked, validateIndeterminate, validateLabelAlign, validateVariantInputCheckbox, watchValidator } from '../../schema';

import { InputCheckboxRadioController } from '../input-radio/controller';

Expand All @@ -36,12 +36,13 @@ export class InputCheckboxController extends InputCheckboxRadioController implem
this.setFormAssociatedCheckboxValue(this.component.state._value as StencilUnknown);
}

public validateIcons(value?: Stringified<InputCheckboxIconsProp>): void {
public validateIcons(value?: InputCheckboxIconsPropType): void {
watchValidator(
this.component,
'_icons',
(value): boolean => {
return typeof value === 'object' && value !== null && (isString(value.checked, 1) || isString(value.indeterminate, 1) || isString(value.unchecked, 1));
const v = value as Record<string, unknown>;
return typeof v === 'object' && v !== null && (isString(v.checked, 1) || isString(v.indeterminate, 1) || isString(v.unchecked, 1));
},
new Set(['InputCheckboxIcons']),
value,
Expand Down Expand Up @@ -71,14 +72,8 @@ export class InputCheckboxController extends InputCheckboxRadioController implem
this.setFormAssociatedCheckboxValue(this.component.state._value as StencilUnknown);
}

public validateVariant(value?: InputCheckboxVariant): void {
watchValidator(
this.component,
'_variant',
(value): boolean => typeof value === 'string' && inputCheckboxVariantOptions.includes(value),
new Set([`String {${inputCheckboxVariantOptions.join(', ')}`]),
value,
);
public validateVariant(value?: InputCheckboxVariantPropType): void {
validateVariantInputCheckbox(this.component, value);
}

public componentWillLoad(): void {
Expand Down
Loading
Loading