diff --git a/packages/components/src/components/@deprecated/input/controller.ts b/packages/components/src/components/@deprecated/input/controller.ts index 51c0dd73c7..03f9cc7121 100644 --- a/packages/components/src/components/@deprecated/input/controller.ts +++ b/packages/components/src/components/@deprecated/input/controller.ts @@ -1,6 +1,14 @@ import type { Generic } from 'adopted-style-sheets'; -import type { AdjustHeightPropType, ButtonProps, HideErrorPropType, InputTypeOnDefault, LabelWithExpertSlotPropType, MsgPropType } from '@public-ui/schema'; +import type { + AdjustHeightPropType, + ButtonProps, + HideErrorPropType, + InputTypeOnDefault, + LabelWithExpertSlotPropType, + MsgPropType, + TooltipAlignPropType, +} from '@public-ui/schema'; import { a11yHint, a11yHintDisabled, @@ -16,6 +24,7 @@ import { validateTabIndex, watchBoolean, watchString, + validateTooltipAlign, } from '@public-ui/schema'; import { stopPropagation, tryToDispatchKoliBriEvent } from '../../../utils/events'; @@ -50,6 +59,9 @@ export class InputController extends ControlledInputController implements Watche a11yHintDisabled(); } } + public validateTooltipAlign(value?: TooltipAlignPropType): void { + validateTooltipAlign(this.component, value); + } /** * @deprecated diff --git a/packages/components/src/components/input-checkbox/test/html.mock.ts b/packages/components/src/components/input-checkbox/test/html.mock.ts new file mode 100644 index 0000000000..4367aa2882 --- /dev/null +++ b/packages/components/src/components/input-checkbox/test/html.mock.ts @@ -0,0 +1,83 @@ +import type { InputCheckboxProps, InputCheckboxStates } from '@public-ui/schema'; +import { mixMembers } from 'stencil-awesome-test'; +import { nonce } from '../../../utils/dev.utils'; +import { KolIconTag, KolInputTag } from '../../../core/component-names'; +import { showExpertSlot } from '@public-ui/schema'; +import { getRenderStates } from '../../input/controller'; + +export const getInputCheckboxHtml = (props: InputCheckboxProps): string => { + const state = mixMembers( + { + _checked: false, + _hideError: false, + _icons: { + checked: 'codicon codicon-check', + indeterminate: 'codicon codicon-remove', + unchecked: 'codicon codicon-close', + }, + _id: `id-${nonce()}`, + _indeterminate: false, + _label: '', // ⚠ required + _value: true, + _variant: 'default', + }, + props, + ); + const hasExpertSlot = showExpertSlot(state._label); + const { ariaDescribedBy } = getRenderStates(state); + + return ` + + + <${KolInputTag} + ${state._disabled ? `_disabled=""` : ''} + ${state._hideLabel ? `_hideLabel=""` : ''} + ${state._touched ? `_touched=""` : ''} + ${state._required ? `_required=""` : ''} + _hint="" + _id="${state._id}" + _label="${state._label ? `${state._label}` : ''}" + _tooltipalign="top" + class="checkbox ${state._hideLabel ? 'hide-label' : ''} default" + ${state._alert || state._alert === undefined ? `_alert=""` : ''} + > + ${ + hasExpertSlot + ? ` ` + : typeof state._accessKey === 'string' + ? ` + ${state._label} + + ` + : ` ${state._label} ` + } + + + + + `; +}; diff --git a/packages/components/src/components/input-checkbox/test/snapshot.spec.tsx b/packages/components/src/components/input-checkbox/test/snapshot.spec.tsx new file mode 100644 index 0000000000..01649d5120 --- /dev/null +++ b/packages/components/src/components/input-checkbox/test/snapshot.spec.tsx @@ -0,0 +1,34 @@ +import { executeTests } from 'stencil-awesome-test'; + +import { h } from '@stencil/core'; +import { newSpecPage } from '@stencil/core/testing'; + +import { getInputCheckboxHtml } from './html.mock'; + +import type { SpecPage } from '@stencil/core/testing'; +import type { InputCheckboxProps } from '@public-ui/schema'; +import { KolInputCheckbox } from '../component'; + +executeTests( + 'InputCheckbox', + async (props): Promise => { + const page = await newSpecPage({ + components: [KolInputCheckbox], + template: () => , + }); + return page; + }, + { + _label: ['Label'], + _hideLabel: [true, false], + _disabled: [true, false], + _alert: [true, false], + _required: [true, false], + _touched: [true, false], + }, + getInputCheckboxHtml, + { + execMode: 'default', // ready + needTimers: true, + }, +); diff --git a/packages/components/src/components/input-color/test/html.mock.ts b/packages/components/src/components/input-color/test/html.mock.ts new file mode 100644 index 0000000000..259e6c9f2c --- /dev/null +++ b/packages/components/src/components/input-color/test/html.mock.ts @@ -0,0 +1,66 @@ +import type { InputColorProps, InputColorStates } from '@public-ui/schema'; +import { mixMembers } from 'stencil-awesome-test'; +import { nonce } from '../../../utils/dev.utils'; +import { KolInputTag } from '../../../core/component-names'; +import { showExpertSlot } from '@public-ui/schema'; +import { getRenderStates } from '../../input/controller'; + +export const getInputColorHtml = (props: InputColorProps): string => { + const state = mixMembers( + { + _autoComplete: 'off', + _hideError: false, + _id: `id-${nonce()}`, + _label: '', // ⚠ required + _suggestions: [], + }, + props, + ); + const hasExpertSlot = showExpertSlot(state._label); + const { ariaDescribedBy } = getRenderStates(state); + + return ` + + + <${KolInputTag} + ${state._disabled ? `_disabled=""` : ''} + ${state._hideLabel ? `_hideLabel=""` : ''} + ${state._touched ? `_touched=""` : ''} + _hint="" + _id="${state._id}" + _label="${state._label ? `${state._label}` : ''}" + _tooltipalign="top" + class="color ${state._hideLabel ? 'hide-label' : ''} " + role="presentation" + > + ${ + hasExpertSlot + ? ` ` + : typeof state._accessKey === 'string' + ? ` + ${state._label} + + ` + : ` ${state._label} ` + } + +
+ 0 ? `aria-describedby="${ariaDescribedBy.join(' ')}"` : ''} + > +
+ +
+
`; +}; diff --git a/packages/components/src/components/input-color/test/snapshot.spec.tsx b/packages/components/src/components/input-color/test/snapshot.spec.tsx new file mode 100644 index 0000000000..c73c42488b --- /dev/null +++ b/packages/components/src/components/input-color/test/snapshot.spec.tsx @@ -0,0 +1,41 @@ +import { executeTests } from 'stencil-awesome-test'; + +import { h } from '@stencil/core'; +import { newSpecPage } from '@stencil/core/testing'; + +import { getInputColorHtml } from './html.mock'; + +import type { SpecPage } from '@stencil/core/testing'; +import type { InputColorProps } from '@public-ui/schema'; +import { KolInputColor } from '../component'; + +executeTests( + 'InputColor', + async (props): Promise => { + const page = await newSpecPage({ + components: [KolInputColor], + template: () => , + }); + return page; + }, + { + _label: ['Label'], + _hideLabel: [true, false], + _disabled: [true, false], + _alert: [true, false], + _icons: [[{ left: 'codicon codicon-home' }]], + _touched: [true, false], + _smartButton: [ + { + _icons: ['codicon codicon-eye'], + _hideLabel: true, + _label: 'einblenden', + }, + ], + }, + getInputColorHtml, + { + execMode: 'default', // ready + needTimers: true, + }, +); diff --git a/packages/components/src/components/input-date/test/html.mock.ts b/packages/components/src/components/input-date/test/html.mock.ts new file mode 100644 index 0000000000..5f9092eed5 --- /dev/null +++ b/packages/components/src/components/input-date/test/html.mock.ts @@ -0,0 +1,70 @@ +import type { InputDateProps, InputDateStates } from '@public-ui/schema'; +import { mixMembers } from 'stencil-awesome-test'; +import { nonce } from '../../../utils/dev.utils'; +import { KolInputTag } from '../../../core/component-names'; +import { showExpertSlot } from '@public-ui/schema'; +import { getRenderStates } from '../../input/controller'; + +export const getInputDateHtml = (props: InputDateProps): string => { + const state = mixMembers( + { + _autoComplete: 'off', + _hasValue: false, + _hideError: false, + _id: `id-${nonce()}`, + _label: '', // ⚠ required + _suggestions: [], + _type: 'datetime-local', + }, + props, + ); + const hasExpertSlot = showExpertSlot(state._label); + const { ariaDescribedBy } = getRenderStates(state); + return ` + + + <${KolInputTag} + ${state._disabled ? `_disabled=""` : ''} + ${state._hideLabel ? `_hideLabel=""` : ''} + ${state._required ? `_required=""` : ''} + ${state._readOnly ? `_readonly=""` : ''} + ${state._touched ? `_touched=""` : ''} + _hint="" + _id="${state._id}" + _label="${state._label ? `${state._label}` : ''}" + _tooltipalign="top" + class="date ${state._hideLabel ? `hide-label` : ''}" + > + ${ + hasExpertSlot + ? ` ` + : typeof state._accessKey === 'string' + ? ` + ${state._label} + + ` + : ` ${state._label} ` + } + +
+ 0 ? `aria-describedby="${ariaDescribedBy.join(' ')}"` : ''} + > +
+ +
+
`; +}; diff --git a/packages/components/src/components/input-date/test/snapshot.spec.tsx b/packages/components/src/components/input-date/test/snapshot.spec.tsx new file mode 100644 index 0000000000..93c998f216 --- /dev/null +++ b/packages/components/src/components/input-date/test/snapshot.spec.tsx @@ -0,0 +1,43 @@ +import { executeTests } from 'stencil-awesome-test'; + +import { h } from '@stencil/core'; +import { newSpecPage } from '@stencil/core/testing'; + +import { getInputDateHtml } from './html.mock'; + +import type { SpecPage } from '@stencil/core/testing'; +import type { InputDateProps } from '@public-ui/schema'; +import { KolInputDate } from '../component'; + +executeTests( + 'InputDate', + async (props): Promise => { + const page = await newSpecPage({ + components: [KolInputDate], + template: () => , + }); + return page; + }, + { + _label: ['Label'], + _hideLabel: [true, false], + _disabled: [true, false], + _alert: [true, false], + _readOnly: [true, false], + _msg: [{ _type: 'error', _description: 'Error message' }], + _required: [true, false], + _touched: [true, false], + _smartButton: [ + { + _icons: ['codicon codicon-eye'], + _hideLabel: true, + _label: 'einblenden', + }, + ], + }, + getInputDateHtml, + { + execMode: 'default', // ready + needTimers: true, + }, +); diff --git a/packages/components/src/components/input-email/test/html.mock.ts b/packages/components/src/components/input-email/test/html.mock.ts new file mode 100644 index 0000000000..bb1562e76a --- /dev/null +++ b/packages/components/src/components/input-email/test/html.mock.ts @@ -0,0 +1,73 @@ +import type { InputEmailProps, InputEmailStates } from '@public-ui/schema'; +import { mixMembers } from 'stencil-awesome-test'; +import { nonce } from '../../../utils/dev.utils'; +import { KolInputTag } from '../../../core/component-names'; +import { showExpertSlot } from '@public-ui/schema'; +import { getRenderStates } from '../../input/controller'; + +export const getInputEmailHtml = (props: InputEmailProps): string => { + const state = mixMembers( + { + _autoComplete: 'off', + _currentLength: 0, + _hasValue: false, + _hideError: false, + _id: `id-${nonce()}`, + _label: '', // ⚠ required + _suggestions: [], + }, + props, + ); + const hasExpertSlot = showExpertSlot(state._label); + const { ariaDescribedBy } = getRenderStates(state); + + return ` + + + <${KolInputTag} + ${state._disabled ? `_disabled=""` : ''} + ${state._hideLabel ? `_hideLabel=""` : ''} + ${state._required ? `_required=""` : ''} + ${state._readOnly ? `_readonly=""` : ''} + ${state._touched ? `_touched=""` : ''} + _hint="" + _id="${state._id}" + _label="${state._label ? `${state._label}` : ''}" + _tooltipalign="top" + class="email ${state._hideLabel ? `hide-label` : ''}" + role="presentation" + _currentlength="0" + ${state._alert || state._alert === undefined ? `_alert=""` : ''} + > + ${ + hasExpertSlot + ? ` ` + : typeof state._accessKey === 'string' + ? ` + ${state._label} + + ` + : ` ${state._label} ` + } + +
+ 0 ? `aria-describedby="${ariaDescribedBy.join(' ')}"` : ''} + > +
+ +
+
`; +}; diff --git a/packages/components/src/components/input-email/test/snapshot.spec.tsx b/packages/components/src/components/input-email/test/snapshot.spec.tsx new file mode 100644 index 0000000000..15f8b70325 --- /dev/null +++ b/packages/components/src/components/input-email/test/snapshot.spec.tsx @@ -0,0 +1,44 @@ +import { executeTests } from 'stencil-awesome-test'; + +import { h } from '@stencil/core'; +import { newSpecPage } from '@stencil/core/testing'; + +import { getInputEmailHtml } from './html.mock'; + +import type { SpecPage } from '@stencil/core/testing'; +import type { InputEmailProps } from '@public-ui/schema'; +import { KolInputEmail } from '../component'; + +executeTests( + 'InputEmail', + async (props): Promise => { + const page = await newSpecPage({ + components: [KolInputEmail], + template: () => , + }); + return page; + }, + { + _label: ['Label'], + _hideLabel: [true, false], + _disabled: [true, false], + _alert: [true, false], + _icons: [[{ left: 'codicon codicon-home' }]], + _readOnly: [true, false], + _msg: [{ _type: 'error', _description: 'Error message' }], + _required: [true, false], + _touched: [true, false], + _smartButton: [ + { + _icons: ['codicon codicon-eye'], + _hideLabel: true, + _label: 'einblenden', + }, + ], + }, + getInputEmailHtml, + { + execMode: 'default', // ready + needTimers: true, + }, +); diff --git a/packages/components/src/components/input-file/test/html.mock.ts b/packages/components/src/components/input-file/test/html.mock.ts new file mode 100644 index 0000000000..1a09c4cfc9 --- /dev/null +++ b/packages/components/src/components/input-file/test/html.mock.ts @@ -0,0 +1,64 @@ +import type { InputFileProps, InputFileStates } from '@public-ui/schema'; +import { mixMembers } from 'stencil-awesome-test'; +import { nonce } from '../../../utils/dev.utils'; +import { KolInputTag } from '../../../core/component-names'; +import { showExpertSlot } from '@public-ui/schema'; +import { getRenderStates } from '../../input/controller'; + +export const getInputFileHtml = (props: InputFileProps): string => { + const state = mixMembers( + { + _hideError: false, + _id: `id-${nonce()}`, + _label: '', // ⚠ required + }, + props, + ); + const hasExpertSlot = showExpertSlot(state._label); + const { ariaDescribedBy } = getRenderStates(state); + return ` + + + <${KolInputTag} + ${state._disabled ? `_disabled=""` : ''} + ${state._hideLabel ? `_hideLabel=""` : ''} + ${state._required ? `_required=""` : ''} + ${state._touched ? `_touched=""` : ''} + _hint="" + _id="${state._id}" + _label="${state._label ? `${state._label}` : ''}" + _tooltipalign="top" + class="file ${state._hideLabel ? `hide-label` : ''}" + role="presentation" + > + + ${ + hasExpertSlot + ? ` ` + : typeof state._accessKey === 'string' + ? ` + ${state._label} + + ` + : ` ${state._label} ` + } + +
+ 0 ? `aria-describedby="${ariaDescribedBy.join(' ')}"` : ''} + > +
+ +
+
`; +}; diff --git a/packages/components/src/components/input-file/test/snapshot.spec.tsx b/packages/components/src/components/input-file/test/snapshot.spec.tsx new file mode 100644 index 0000000000..f79305cf13 --- /dev/null +++ b/packages/components/src/components/input-file/test/snapshot.spec.tsx @@ -0,0 +1,42 @@ +import { executeTests } from 'stencil-awesome-test'; + +import { h } from '@stencil/core'; +import { newSpecPage } from '@stencil/core/testing'; + +import { getInputFileHtml } from './html.mock'; + +import type { SpecPage } from '@stencil/core/testing'; +import type { InputFileProps } from '@public-ui/schema'; +import { KolInputFile } from '../component'; + +executeTests( + 'InputFile', + async (props): Promise => { + const page = await newSpecPage({ + components: [KolInputFile], + template: () => , + }); + return page; + }, + { + _label: ['Label'], + _hideLabel: [true, false], + _disabled: [true, false], + _alert: [true, false], + _msg: [{ _type: 'error', _description: 'Error message' }], + _required: [true, false], + _touched: [true, false], + _smartButton: [ + { + _icons: ['codicon codicon-eye'], + _hideLabel: true, + _label: 'einblenden', + }, + ], + }, + getInputFileHtml, + { + execMode: 'default', // ready + needTimers: true, + }, +); diff --git a/packages/components/src/components/input-number/test/html.mock.ts b/packages/components/src/components/input-number/test/html.mock.ts new file mode 100644 index 0000000000..7371daf032 --- /dev/null +++ b/packages/components/src/components/input-number/test/html.mock.ts @@ -0,0 +1,73 @@ +import type { InputNumberProps, InputNumberStates } from '@public-ui/schema'; +import { mixMembers } from 'stencil-awesome-test'; +import { nonce } from '../../../utils/dev.utils'; +import { KolInputTag } from '../../../core/component-names'; +import { showExpertSlot } from '@public-ui/schema'; +import { getRenderStates } from '../../input/controller'; + +export const getInputNumberHtml = (props: InputNumberProps): string => { + const state = mixMembers( + { + _autoComplete: 'off', + _hasValue: false, + _hideError: false, + _id: `id-${nonce()}`, + _label: '', // ⚠ required + _suggestions: [], + }, + props, + ); + const hasExpertSlot = showExpertSlot(state._label); + const { ariaDescribedBy } = getRenderStates(state); + + return ` + + + <${KolInputTag} + ${state._disabled ? `_disabled=""` : ''} + ${state._hideLabel ? `_hideLabel=""` : ''} + ${state._touched ? `_touched=""` : ''} + _hint="" + _id="${state._id}" + _label="${state._label ? `${state._label}` : ''}" + _tooltipalign="top" + class="number ${state._hideLabel ? 'hide-label' : ''} " + ${state._readOnly ? `_readonly=""` : ''} + ${state._required ? `_required=""` : ''} + > + ${ + hasExpertSlot + ? ` ` + : typeof state._accessKey === 'string' + ? ` + ${state._label} + + ` + : ` ${state._label} ` + } + +
+ 0 ? `aria-describedby="${ariaDescribedBy.join(' ')}"` : ''} + ${state._required ? `required=""` : ''} + > +
+ +
+
`; +}; diff --git a/packages/components/src/components/input-number/test/snapshot.spec.tsx b/packages/components/src/components/input-number/test/snapshot.spec.tsx new file mode 100644 index 0000000000..b5a23a74e0 --- /dev/null +++ b/packages/components/src/components/input-number/test/snapshot.spec.tsx @@ -0,0 +1,37 @@ +import { executeTests } from 'stencil-awesome-test'; + +import { h } from '@stencil/core'; +import { newSpecPage } from '@stencil/core/testing'; + +import { getInputNumberHtml } from './html.mock'; + +import type { SpecPage } from '@stencil/core/testing'; +import type { InputNumberProps } from '@public-ui/schema'; +import { KolInputNumber } from '../component'; + +executeTests( + 'InputNumber', + async (props): Promise => { + const page = await newSpecPage({ + components: [KolInputNumber], + template: () => , + }); + return page; + }, + { + _label: ['Label'], + _alert: [true, false], + _icons: [[{ left: 'codicon codicon-home' }]], + _readOnly: [true, false], + _required: [true, false], + _touched: [true, false], + _max: [10], + _min: [10], + _step: [1, 2], + }, + getInputNumberHtml, + { + execMode: 'default', // ready + needTimers: true, + }, +); diff --git a/packages/components/src/components/input-password/test/html.mock.ts b/packages/components/src/components/input-password/test/html.mock.ts new file mode 100644 index 0000000000..3b56e68b29 --- /dev/null +++ b/packages/components/src/components/input-password/test/html.mock.ts @@ -0,0 +1,72 @@ +import type { InputPasswordProps, InputPasswordStates } from '@public-ui/schema'; +import { mixMembers } from 'stencil-awesome-test'; +import { nonce } from '../../../utils/dev.utils'; +import { KolInputTag } from '../../../core/component-names'; +import { showExpertSlot } from '@public-ui/schema'; +import { getRenderStates } from '../../input/controller'; + +export const getInputPasswordHtml = (props: InputPasswordProps): string => { + const state = mixMembers( + { + _autoComplete: 'off', + _currentLength: 0, + _hasValue: false, + _hideError: false, + _id: `id-${nonce()}`, + _label: '', // ⚠ required + }, + props, + ); + const hasExpertSlot = showExpertSlot(state._label); + const { ariaDescribedBy } = getRenderStates(state); + + return ` + + + <${KolInputTag} + ${state._disabled ? `_disabled=""` : ''} + ${state._hideLabel ? `_hideLabel=""` : ''} + ${state._touched ? `_touched=""` : ''} + _hint="" + _id="${state._id}" + _label="${state._label ? `${state._label}` : ''}" + _tooltipalign="top" + class="password ${state._hideLabel ? 'hide-label' : ''} " + role="presentation" + _currentlength="0" + ${state._readOnly ? `_readonly=""` : ''} + ${state._required ? `_required=""` : ''} + > + ${ + hasExpertSlot + ? ` ` + : typeof state._accessKey === 'string' + ? ` + ${state._label} + + ` + : ` ${state._label} ` + } + +
+ 0 ? `aria-describedby="${ariaDescribedBy.join(' ')}"` : ''} + ${state._required ? `required=""` : ''} + > +
+ +
+
`; +}; diff --git a/packages/components/src/components/input-password/test/snapshot.spec.tsx b/packages/components/src/components/input-password/test/snapshot.spec.tsx new file mode 100644 index 0000000000..432b4d2550 --- /dev/null +++ b/packages/components/src/components/input-password/test/snapshot.spec.tsx @@ -0,0 +1,36 @@ +import { executeTests } from 'stencil-awesome-test'; + +import { h } from '@stencil/core'; +import { newSpecPage } from '@stencil/core/testing'; + +import { getInputPasswordHtml } from './html.mock'; + +import type { SpecPage } from '@stencil/core/testing'; +import type { InputPasswordProps } from '@public-ui/schema'; +import { KolInputPassword } from '../component'; + +executeTests( + 'InputPassword', + async (props): Promise => { + const page = await newSpecPage({ + components: [KolInputPassword], + template: () => , + }); + return page; + }, + { + _label: ['Label'], + _disabled: [true, false], + _placeholder: ['Mit Icons'], + _alert: [true, false], + _icons: [[{ left: 'codicon codicon-home' }]], + _readOnly: [true, false], + _required: [true, false], + _touched: [true, false], + }, + getInputPasswordHtml, + { + execMode: 'default', // ready + needTimers: true, + }, +); diff --git a/packages/components/src/components/input-radio/component.tsx b/packages/components/src/components/input-radio/component.tsx index 8e542086f8..1959b17ffd 100644 --- a/packages/components/src/components/input-radio/component.tsx +++ b/packages/components/src/components/input-radio/component.tsx @@ -282,7 +282,10 @@ export class KolInputRadio implements InputRadioAPI { public validateAccessKey(value?: string): void { this.controller.validateAccessKey(value); } - + @Watch('_tooltipAlign') + public validateTooltipAlign(value?: TooltipAlignPropType): void { + this.controller.validateTooltipAlign(value); + } @Watch('_alert') public validateAlert(value?: boolean): void { this.controller.validateAlert(value); diff --git a/packages/components/src/components/input-radio/controller.ts b/packages/components/src/components/input-radio/controller.ts index 15281cf863..8067d19de9 100644 --- a/packages/components/src/components/input-radio/controller.ts +++ b/packages/components/src/components/input-radio/controller.ts @@ -30,10 +30,10 @@ export const fillKeyOptionMap = (keyOptionMap: Map>, option }); }; -type RequiredProps = NonNullable; +type RequiredProps = PropLabelWithExpertSlot; type OptionalProps = { required: boolean; -} & PropLabelWithExpertSlot; +}; type InputCheckboxRadioProps = Generic.Element.Members; type InputCheckboxRadioWatches = Generic.Element.Watchers; diff --git a/packages/components/src/components/input-radio/test/html.mock.ts b/packages/components/src/components/input-radio/test/html.mock.ts new file mode 100644 index 0000000000..b3f7d3860e --- /dev/null +++ b/packages/components/src/components/input-radio/test/html.mock.ts @@ -0,0 +1,121 @@ +import type { InputRadioProps, InputRadioStates } from '@public-ui/schema'; +import { mixMembers } from 'stencil-awesome-test'; +import { nonce } from '../../../utils/dev.utils'; +import { KolInputTag } from '../../../core/component-names'; +import { showExpertSlot } from '@public-ui/schema'; +import { getRenderStates } from '../../input/controller'; + +export const getInputRadioHtml = (props: InputRadioProps): string => { + const state = mixMembers( + { + _hideError: false, + _id: `id-${nonce()}`, + _label: '', // ⚠ required + _options: [], + _orientation: 'vertical', + }, + props, + ); + const hasExpertSlot = showExpertSlot(state._label); + const { ariaDescribedBy, hasError } = getRenderStates(state); + const label = state._label ? state._label : ''; + let accessKey = state._accessKey ? state._accessKey : ''; + let [first, ...rest] = label.split(accessKey); + if (rest.length === 0) { + accessKey = accessKey.toUpperCase(); + [first, ...rest] = label.split(accessKey); + } + if (rest.length === 0) { + accessKey = accessKey.toLowerCase(); + [first, ...rest] = label.split(accessKey); + } + + return ` + + +
+ + + + ${ + hasExpertSlot + ? ` ` + : typeof state._accessKey === 'string' + ? ` + <> + ${first} + ${ + rest.length + ? `<> + {accessKey} + {rest.join(accessKey)} + ` + : null + } + + + ` + : ` ${state._label}` + } + + + + + ${state._options + .map((option, index) => { + const customId = `${state._id}-${index}`; + const slotName = `radio-${index}`; + const selected = state._value === option.value; + return `<${KolInputTag} + class=" radio ${state._disabled || option.disabled ? 'disabled' : ''} " + ${state._disabled || option.disabled ? "_disabled=''" : ''} + ${state._hideLabel ? "_hideLabel=''" : ''} + ${state._touched ? "_touched=''" : ''} + ${state._hint ? `_hint="${state._hint}"` : "_hint=''"} + _id="${customId}" + ${(option.label as string) ? `_label="${option.label}"` : ''} + _renderNoLabel="" + ${state._required ? "_required=''" : ''} + _slotName="${slotName}" + _tooltipAlign="top" + > +
+ 0 ? `aria-describedby="${ariaDescribedBy.join(' ')}"` : ''} + ${state._hideLabel && typeof option.label === 'string' ? `aria-label="${option.label}"` : ''} + type="radio" + id="${customId}" + name="${state._name || state._id}" + ${state._disabled || option.disabled ? "disabled=''" : ''} + ${state._required ? "required=''" : ''} + ${state._tabIndex ? `tabIndex="${state._tabIndex}"` : ''} + ${selected ? `checked=""` : ''} + + value="-${index}" + /> + +
+ `; + }) + .join(' ')} + ${hasError ? `` : ''} + + +
+
+
`; +}; diff --git a/packages/components/src/components/input-radio/test/snapshot.spec.tsx b/packages/components/src/components/input-radio/test/snapshot.spec.tsx new file mode 100644 index 0000000000..69b749cf0b --- /dev/null +++ b/packages/components/src/components/input-radio/test/snapshot.spec.tsx @@ -0,0 +1,40 @@ +import { executeTests } from 'stencil-awesome-test'; + +import { h } from '@stencil/core'; +import { newSpecPage } from '@stencil/core/testing'; + +import { getInputRadioHtml } from './html.mock'; + +import type { SpecPage } from '@stencil/core/testing'; +import type { InputRadioProps } from '@public-ui/schema'; +import { KolInputRadio } from '../component'; + +executeTests( + 'InputRadio', + async (props): Promise => { + const page = await newSpecPage({ + components: [KolInputRadio], + template: () => , + }); + return page; + }, + { + _label: ['Label'], + _disabled: [true, false], + _alert: [true, false], + _required: [true, false], + _touched: [true, false], + _options: [ + [ + { label: 'Field 1', value: 1 }, + { label: 'Field 2', value: 2 }, + ], + [{ label: 'Field 1', value: { id: 1, name: 'Option 1' } }], + ], + }, + getInputRadioHtml, + { + execMode: 'default', // ready + needTimers: true, + }, +); diff --git a/packages/components/src/components/input-range/test/html.mock.ts b/packages/components/src/components/input-range/test/html.mock.ts new file mode 100644 index 0000000000..2cbf599fe6 --- /dev/null +++ b/packages/components/src/components/input-range/test/html.mock.ts @@ -0,0 +1,92 @@ +import type { InputRangeStates, InputRangeProps } from '@public-ui/schema'; +import { mixMembers } from 'stencil-awesome-test'; +import { nonce } from '../../../utils/dev.utils'; +import { KolInputTag } from '../../../core/component-names'; +import { showExpertSlot } from '@public-ui/schema'; +import { getRenderStates } from '../../input/controller'; + +export const getInpuRangeHtml = (props: InputRangeProps): string => { + const state = mixMembers( + { + _autoComplete: 'off', + _hideError: false, + _id: `id-${nonce()}`, + _label: '', // ⚠ required + _suggestions: [], + }, + props, + ); + const hasExpertSlot = showExpertSlot(state._label); + const { ariaDescribedBy } = getRenderStates(state); + + return ` + + + <${KolInputTag} + ${state._disabled ? `_disabled=""` : ''} + ${state._hideLabel ? `_hideLabel=""` : ''} + ${state._touched ? `_touched=""` : ''} + ${state._accessKey ? `_accessKey="${state._accessKey}"` : ''} + _hint="" + _id="${state._id}" + _label="${state._label ? `${state._label}` : ''}" + _tooltipalign="top" + class="range ${state._hideLabel ? 'hide-label' : ''} " + + > + ${ + hasExpertSlot + ? ` ` + : typeof state._accessKey === 'string' + ? ` + ${state._label} + + ` + : ` ${state._label} ` + } + +
+
+ 0 ? `aria-describedby="${ariaDescribedBy.join(' ')}"` : ''} + ${state._accessKey ? `accessKey="${state._accessKey}"` : ''} + tabindex="-1" type="range" + > + 0 ? `aria-describedby="${ariaDescribedBy.join(' ')}"` : ''} + ${state._accessKey ? `accessKey="${state._accessKey}"` : ''} + type="number" + id="${state._id}" + > +
+
+ +
+
`; +}; diff --git a/packages/components/src/components/input-range/test/snapshot.spec.tsx b/packages/components/src/components/input-range/test/snapshot.spec.tsx new file mode 100644 index 0000000000..fc6126118d --- /dev/null +++ b/packages/components/src/components/input-range/test/snapshot.spec.tsx @@ -0,0 +1,36 @@ +import { executeTests } from 'stencil-awesome-test'; + +import { h } from '@stencil/core'; +import { newSpecPage } from '@stencil/core/testing'; + +import { getInpuRangeHtml } from './html.mock'; + +import type { SpecPage } from '@stencil/core/testing'; +import type { InputRangeProps } from '@public-ui/schema'; +import { KolInputRange } from '../component'; + +executeTests( + 'InputText', + async (props): Promise => { + const page = await newSpecPage({ + components: [KolInputRange], + template: () => , + }); + return page; + }, + { + _label: ['Label'], + _disabled: [true, false], + _alert: [true, false], + _icons: [[{ left: 'codicon codicon-home' }]], + _touched: [true, false], + _accessKey: ['V'], + _max: [10], + _min: [10], + }, + getInpuRangeHtml, + { + execMode: 'default', // ready + needTimers: true, + }, +); diff --git a/packages/components/src/components/input-text/component.tsx b/packages/components/src/components/input-text/component.tsx index 69b521e336..34949e32b1 100644 --- a/packages/components/src/components/input-text/component.tsx +++ b/packages/components/src/components/input-text/component.tsx @@ -84,11 +84,11 @@ export class KolInputText implements InputTextAPI { ; +type RequiredProps = PropLabelWithExpertSlot; type OptionalProps = { id: string; -} & PropLabelWithExpertSlot & - PropSuggestions; +} & PropSuggestions; type InputTextEmailProps = Generic.Element.Members; type InputTextEmailWatches = Generic.Element.Watchers; diff --git a/packages/components/src/components/input-text/test/html.mock.ts b/packages/components/src/components/input-text/test/html.mock.ts new file mode 100644 index 0000000000..5c8d74a0e6 --- /dev/null +++ b/packages/components/src/components/input-text/test/html.mock.ts @@ -0,0 +1,79 @@ +import type { InputTextProps, InputTextStates } from '@public-ui/schema'; +import { mixMembers } from 'stencil-awesome-test'; +import { nonce } from '../../../utils/dev.utils'; +import { KolInputTag } from '../../../core/component-names'; +import { showExpertSlot } from '@public-ui/schema'; +import { getRenderStates } from '../../input/controller'; + +export const getInpuTextHtml = (props: InputTextProps): string => { + const state = mixMembers( + { + _autoComplete: 'off', + _currentLength: 0, + _hasValue: false, + _hideError: false, + _id: `id-${nonce()}`, + _label: '', // ⚠ required + _suggestions: [], + _type: 'text', + }, + props, + ); + const hasExpertSlot = showExpertSlot(state._label); + const { ariaDescribedBy } = getRenderStates(state); + + return ` + + + <${KolInputTag} + ${state._disabled ? `_disabled=""` : ''} + ${state._hideLabel ? `_hideLabel=""` : ''} + ${state._touched ? `_touched=""` : ''} + ${state._accessKey ? `_accessKey="${state._accessKey}"` : ''} + _hint="" + _id="${state._id}" + _label="${state._label ? `${state._label}` : ''}" + _tooltipalign="top" + class="${state._type} ${state._hideLabel ? 'hide-label' : ''} " + role="presentation" + _currentlength="0" + ${state._readOnly ? `_readonly=""` : ''} + ${state._required ? `_required=""` : ''} + > + ${ + hasExpertSlot + ? ` ` + : typeof state._accessKey === 'string' + ? ` + ${state._label} + + ` + : ` ${state._label} ` + } +
+ 0 ? `aria-describedby="${ariaDescribedBy.join(' ')}"` : ''} + ${state._required ? `required=""` : ''} + ${state._accessKey ? `accessKey="${state._accessKey}"` : ''} + > +
+ +
+
`; +}; diff --git a/packages/components/src/components/input-text/test/snapshot.spec.tsx b/packages/components/src/components/input-text/test/snapshot.spec.tsx new file mode 100644 index 0000000000..1ba2127764 --- /dev/null +++ b/packages/components/src/components/input-text/test/snapshot.spec.tsx @@ -0,0 +1,38 @@ +import { executeTests } from 'stencil-awesome-test'; + +import { h } from '@stencil/core'; +import { newSpecPage } from '@stencil/core/testing'; + +import { getInpuTextHtml } from './html.mock'; + +import type { SpecPage } from '@stencil/core/testing'; +import type { InputTextProps } from '@public-ui/schema'; +import { KolInputText } from '../component'; + +executeTests( + 'InputText', + async (props): Promise => { + const page = await newSpecPage({ + components: [KolInputText], + template: () => , + }); + return page; + }, + { + _label: ['Label'], + _disabled: [true, false], + _placeholder: ['Mit Icons'], + _alert: [true, false], + _icons: [[{ left: 'codicon codicon-home' }]], + _readOnly: [true, false], + _required: [true, false], + _touched: [true, false], + _type: ['search', 'text'], + _accessKey: ['V'], + }, + getInpuTextHtml, + { + execMode: 'default', // ready + needTimers: true, + }, +); diff --git a/packages/components/src/components/input/test/html.mock.ts b/packages/components/src/components/input/test/html.mock.ts new file mode 100644 index 0000000000..834c1de72e --- /dev/null +++ b/packages/components/src/components/input/test/html.mock.ts @@ -0,0 +1,82 @@ +import type { InputProps } from '@public-ui/schema'; +import { showExpertSlot } from '@public-ui/schema'; +import { KolIconTag, KolTooltipWcTag, KolButtonWcTag } from '../../../core/component-names'; +import { translate } from '../../../i18n'; +import type { W3CInputValue, KoliBriCustomIcon } from '@public-ui/schema'; +export const getInputHtml = (props: InputProps): string => { + const isMessageValidError = Boolean(props._msg?._type === 'error' && props._msg._description && props._msg._description?.length > 0); + const hasError = !props._readOnly && isMessageValidError && props._touched === true; + const showFormFieldMsg = Boolean(hasError || (props._msg?._type !== 'error' && props._msg?._description)); + const hasExpertSlot = showExpertSlot(props._label); + const hasHint = typeof props._hint === 'string' && props._hint.length > 0; + const useTooltopInsteadOfLabel = !hasExpertSlot && props._hideLabel; + return ` + + + + ${ + hasHint + ? ` + + ${props._hint} + ` + : '' + } +
+ ${props._icons?.left ? `<${KolIconTag} _label="" _icons=${(props._icons?.left as KoliBriCustomIcon).icon} >` : ''} +
+ ${ + typeof props._smartButton === 'object' && props._smartButton !== null + ? `<${KolButtonWcTag} + ${props._smartButton._customClass ? `_customClass="${props._smartButton._customClass}"` : ''} + ${props._smartButton._disabled ? `_disabled=""` : ''} + ${props._smartButton._hideLabel ? `_hideLabel=""` : ''} + ${props._smartButton._id ? `_id="${props._smartButton._id}"` : ''} + ${props._smartButton._label ? `_label="${props._smartButton._label}"` : ''} + ${props._smartButton._tooltipAlign ? `_tooltipAlign="${props._smartButton._tooltipAlign}"` : ''} + ${props._smartButton._variant ? `_variant="${props._smartButton._variant}"` : ''} + >` + : '' + } +
+ ${ + useTooltopInsteadOfLabel + ? `<${KolTooltipWcTag} + aria-hidden="true" + class="input-tooltip" + _align="top" + _id="${props._hideLabel ? `${props._id}-label` : ''}" + ${props._label ? `_label="${props._label}"` : ''} + >` + : '' + } + ${showFormFieldMsg ? `` : ''} + ${ + Array.isArray(props._suggestions) && props._suggestions.length > 0 + ? ` + ${props._suggestions.map((option: W3CInputValue) => `` + : '' + } + ${ + props._hasCounter + ? ` + ${props._currentLength} + ${ + props._maxLength + ? `<> + + + + {props._maxLength} + ` + : '' + }{' '} + ${translate('kol-characters')} + ` + : '' + } +
`; +}; diff --git a/packages/components/src/components/input/test/snapshot.spec.tsx b/packages/components/src/components/input/test/snapshot.spec.tsx new file mode 100644 index 0000000000..20b58fdbad --- /dev/null +++ b/packages/components/src/components/input/test/snapshot.spec.tsx @@ -0,0 +1,38 @@ +import { executeTests } from 'stencil-awesome-test'; + +import { h } from '@stencil/core'; +import { newSpecPage } from '@stencil/core/testing'; + +import { getInputHtml } from './html.mock'; + +import type { SpecPage } from '@stencil/core/testing'; +import type { InputProps } from '@public-ui/schema'; +import { KolInput } from '../component'; + +executeTests( + 'Input', + async (props): Promise => { + const page = await newSpecPage({ + components: [KolInput], + template: () => , + }); + return page; + }, + { + _id: ['Id'], + _label: ['Label'], + _hideLabel: [true, false], + _disabled: [true, false], + _alert: [true, false], + _icons: [[{ left: 'codicon codicon-home' }]], + _readOnly: [true, false], + _smartButton: [ + { + _icons: ['codicon codicon-eye'], + _hideLabel: true, + _label: 'einblenden', + }, + ], + }, + getInputHtml, +); diff --git a/packages/schema/src/components/input-checkbox.ts b/packages/schema/src/components/input-checkbox.ts index 688ceb8bb5..1a478bf1c5 100644 --- a/packages/schema/src/components/input-checkbox.ts +++ b/packages/schema/src/components/input-checkbox.ts @@ -41,7 +41,7 @@ export type InputCheckboxIconsState = { unchecked: AnyIconFontClass; }; -type RequiredProps = NonNullable; +type RequiredProps = PropLabelWithExpertSlot; type OptionalProps = { accessKey: string; alert: boolean; @@ -60,7 +60,6 @@ type OptionalProps = { PropHideError & PropHideLabel & PropIndeterminate & - PropLabelWithExpertSlot & PropMsg & PropName & PropRequired & diff --git a/packages/schema/src/components/input-color.ts b/packages/schema/src/components/input-color.ts index 5302b550bf..7f3ffda917 100644 --- a/packages/schema/src/components/input-color.ts +++ b/packages/schema/src/components/input-color.ts @@ -14,7 +14,7 @@ import type { import type { InputTypeOnDefault, InputTypeOnOff, KoliBriHorizontalIcons, Stringified, W3CInputValue } from '../types'; import type { ButtonProps } from './button'; -type RequiredProps = NonNullable; +type RequiredProps = PropLabelWithExpertSlot; type OptionalProps = { accessKey: string; alert: boolean; @@ -32,7 +32,6 @@ type OptionalProps = { } & PropDisabled & PropHideError & PropHideLabel & - PropLabelWithExpertSlot & PropMsg & PropName & PropSuggestions & diff --git a/packages/schema/src/components/input-date.ts b/packages/schema/src/components/input-date.ts index f77f4771a6..cc70e544a1 100644 --- a/packages/schema/src/components/input-date.ts +++ b/packages/schema/src/components/input-date.ts @@ -17,7 +17,7 @@ import type { import type { InputDateType, InputTypeOnDefault, InputTypeOnOff, Iso8601, KoliBriHorizontalIcons, OptionalInputProps, W3CInputValue } from '../types'; import type { ButtonProps } from './button'; -type RequiredProps = NonNullable; +type RequiredProps = PropLabelWithExpertSlot; type OptionalProps = { /** * @deprecated Will be removed in v3. Use `msg` instead. @@ -26,7 +26,6 @@ type OptionalProps = { type: InputDateType; } & OptionalInputProps & PropHideError & - PropLabelWithExpertSlot & PropSuggestions & PropMsg; diff --git a/packages/schema/src/components/input-email.ts b/packages/schema/src/components/input-email.ts index cf27a9111d..b978a50e27 100644 --- a/packages/schema/src/components/input-email.ts +++ b/packages/schema/src/components/input-email.ts @@ -19,7 +19,7 @@ import type { import type { InputTypeOnDefault, InputTypeOnOff, KoliBriHorizontalIcons, Stringified, W3CInputValue } from '../types'; import type { ButtonProps } from './button'; -type RequiredProps = NonNullable; +type RequiredProps = PropLabelWithExpertSlot; type OptionalProps = { accessKey: string; alert: boolean; @@ -41,7 +41,6 @@ type OptionalProps = { PropHasCounter & PropHideError & PropHideLabel & - PropLabelWithExpertSlot & PropMsg & PropMultiple & PropName & diff --git a/packages/schema/src/components/input-file.ts b/packages/schema/src/components/input-file.ts index 292910ab29..631c4f0a82 100644 --- a/packages/schema/src/components/input-file.ts +++ b/packages/schema/src/components/input-file.ts @@ -16,7 +16,7 @@ import type { import type { InputTypeOnDefault, KoliBriHorizontalIcons, Stringified } from '../types'; import type { ButtonProps } from './button'; -type RequiredProps = NonNullable; +type RequiredProps = PropLabelWithExpertSlot; type OptionalProps = { accept: string; alert: boolean; @@ -34,7 +34,6 @@ type OptionalProps = { } & PropDisabled & PropHideError & PropHideLabel & - PropLabelWithExpertSlot & PropMsg & PropMultiple & PropName & diff --git a/packages/schema/src/components/input-number.ts b/packages/schema/src/components/input-number.ts index 885cb5ad28..c73ef835fc 100644 --- a/packages/schema/src/components/input-number.ts +++ b/packages/schema/src/components/input-number.ts @@ -17,7 +17,7 @@ import type { import type { InputTypeOnDefault, InputTypeOnOff, Iso8601, KoliBriHorizontalIcons, OptionalInputProps, W3CInputValue } from '../types'; import type { ButtonProps } from './button'; -type RequiredProps = NonNullable; +type RequiredProps = PropLabelWithExpertSlot; type OptionalProps = { /** * @deprecated Will be removed in v3. Use `msg` instead. @@ -26,7 +26,6 @@ type OptionalProps = { placeholder: string; } & OptionalInputProps & PropHideError & - PropLabelWithExpertSlot & PropMsg & PropSuggestions; diff --git a/packages/schema/src/components/input-password.ts b/packages/schema/src/components/input-password.ts index 7d973cf3aa..6ac7f66a01 100644 --- a/packages/schema/src/components/input-password.ts +++ b/packages/schema/src/components/input-password.ts @@ -17,7 +17,7 @@ import type { import type { InputTypeOnDefault, InputTypeOnOff, KoliBriHorizontalIcons, Stringified } from '../types'; import type { ButtonProps } from './button'; -type RequiredProps = NonNullable; +type RequiredProps = PropLabelWithExpertSlot; type OptionalProps = { accessKey: string; alert: boolean; @@ -39,7 +39,6 @@ type OptionalProps = { PropHasCounter & PropHideError & PropHideLabel & - PropLabelWithExpertSlot & PropMsg & PropName & PropReadOnly & diff --git a/packages/schema/src/components/input-radio.ts b/packages/schema/src/components/input-radio.ts index ada2e04d9a..7079d2f6cd 100644 --- a/packages/schema/src/components/input-radio.ts +++ b/packages/schema/src/components/input-radio.ts @@ -10,11 +10,12 @@ import type { PropOptions, PropRequired, PropSyncValueBySelector, + PropTooltipAlign, PropTouched, } from '../props'; import type { InputTypeOnDefault, Option, Orientation, StencilUnknown } from '../types'; -type RequiredProps = NonNullable; +type RequiredProps = PropLabelWithExpertSlot; type OptionalProps = { accessKey: string; alert: boolean; @@ -30,13 +31,13 @@ type OptionalProps = { } & PropDisabled & PropHideError & PropHideLabel & - PropLabelWithExpertSlot & PropMsg & PropName & PropOptions & // PropOptions becomes required with 2.0 PropRequired & PropSyncValueBySelector & - PropTouched; + PropTouched & + PropTooltipAlign; type RequiredStates = { options: Option[]; diff --git a/packages/schema/src/components/input-range.ts b/packages/schema/src/components/input-range.ts index fe8b9c101c..1fa471f84d 100644 --- a/packages/schema/src/components/input-range.ts +++ b/packages/schema/src/components/input-range.ts @@ -14,7 +14,7 @@ import type { } from '../props'; import type { InputTypeOnDefault, InputTypeOnOff, KoliBriHorizontalIcons, Stringified, W3CInputValue } from '../types'; -type RequiredProps = NonNullable; +type RequiredProps = PropLabelWithExpertSlot; type OptionalProps = { accessKey: string; alert: boolean; @@ -34,7 +34,6 @@ type OptionalProps = { } & PropDisabled & PropHideError & PropHideLabel & - PropLabelWithExpertSlot & PropMsg & PropName & PropSuggestions & diff --git a/packages/schema/src/components/input-text.ts b/packages/schema/src/components/input-text.ts index 1066513c42..d851556bcf 100644 --- a/packages/schema/src/components/input-text.ts +++ b/packages/schema/src/components/input-text.ts @@ -18,7 +18,7 @@ import type { import type { InputTextType, InputTypeOnDefault, InputTypeOnOff, KoliBriHorizontalIcons, Stringified, W3CInputValue } from '../types'; import type { ButtonProps } from './button'; -type RequiredProps = NonNullable; +type RequiredProps = PropLabelWithExpertSlot; type OptionalProps = { accessKey: string; alert: boolean; @@ -42,7 +42,6 @@ type OptionalProps = { PropHideError & PropHideLabel & PropId & - PropLabelWithExpertSlot & PropMsg & PropName & PropReadOnly &