diff --git a/packages/atomic/src/components/common/button.ts b/packages/atomic/src/components/common/button.ts index 82a6ae184a8..f385e432aeb 100644 --- a/packages/atomic/src/components/common/button.ts +++ b/packages/atomic/src/components/common/button.ts @@ -1,3 +1,4 @@ +import {FunctionalComponentWithChildren} from '@/src/utils/functional-component-utils'; import {html} from 'lit'; import {ifDefined} from 'lit/directives/if-defined.js'; import {when} from 'lit/directives/when.js'; @@ -40,12 +41,9 @@ export interface ButtonProps { title?: string; } -export const button = ({ +export const button: FunctionalComponentWithChildren = ({ props, children, -}: { - props: ButtonProps; - children: T; }) => { const rippleColor = getRippleColorForButtonStyle(props.style); const className = getClassNameForButtonStyle(props.style); diff --git a/packages/atomic/src/components/common/checkbox.spec.ts b/packages/atomic/src/components/common/checkbox.spec.ts index 929ee5e0cba..f8fc230c14c 100644 --- a/packages/atomic/src/components/common/checkbox.spec.ts +++ b/packages/atomic/src/components/common/checkbox.spec.ts @@ -18,9 +18,11 @@ describe('checkbox', () => { const renderCheckbox = (props: Partial): HTMLButtonElement => { render( html`${checkbox({ - ...props, - checked: props.checked ?? false, - onToggle: props.onToggle ?? vi.fn(), + props: { + ...props, + checked: props.checked ?? false, + onToggle: props.onToggle ?? vi.fn(), + }, })}`, container ); @@ -111,7 +113,6 @@ describe('checkbox', () => { expect(button).toHaveClass( 'w-4', // TODO: KIT-3907 - // @ts-expect-error the typing is incorrect. matchers should be a string[] 'h-4', 'grid', 'place-items-center', diff --git a/packages/atomic/src/components/common/checkbox.ts b/packages/atomic/src/components/common/checkbox.ts index 07a0e9bcd0d..99e774b16df 100644 --- a/packages/atomic/src/components/common/checkbox.ts +++ b/packages/atomic/src/components/common/checkbox.ts @@ -1,4 +1,5 @@ -import {html, TemplateResult} from 'lit'; +import {FunctionalComponent} from '@/src/utils/functional-component-utils'; +import {html} from 'lit'; import {classMap} from 'lit/directives/class-map.js'; import {ifDefined} from 'lit/directives/if-defined.js'; import {ref, RefOrCallback} from 'lit/directives/ref.js'; @@ -26,7 +27,7 @@ export interface CheckboxProps { onMouseDown?(evt: MouseEvent): void; } -export const checkbox = (props: CheckboxProps): TemplateResult => { +export const checkbox: FunctionalComponent = ({props}) => { const partName = props.part ?? 'checkbox'; const baseClassNames = 'w-4 h-4 grid place-items-center rounded no-outline hover:border-primary-light focus-visible:border-primary-light'; diff --git a/packages/atomic/src/components/common/heading.spec.ts b/packages/atomic/src/components/common/heading.spec.ts index d0636b06541..29c02b5eebb 100644 --- a/packages/atomic/src/components/common/heading.spec.ts +++ b/packages/atomic/src/components/common/heading.spec.ts @@ -19,13 +19,10 @@ describe('heading', () => { children?: string ): HTMLElement => { render( - html`${heading( - { - ...props, - level: props.level ?? 1, - }, - html`${children}` - )}`, + html` ${heading({ + props: {...props, level: props.level ?? 1}, + children: html`${children}`, + })}`, container ); return within(container).getByRole( diff --git a/packages/atomic/src/components/common/heading.ts b/packages/atomic/src/components/common/heading.ts index 3ba1023ae34..08eedc1cd47 100644 --- a/packages/atomic/src/components/common/heading.ts +++ b/packages/atomic/src/components/common/heading.ts @@ -1,3 +1,4 @@ +import {FunctionalComponentWithChildren} from '@/src/utils/functional-component-utils'; import {ifDefined} from 'lit/directives/if-defined.js'; import {html, literal, unsafeStatic} from 'lit/static-html.js'; @@ -18,10 +19,12 @@ export interface HeadingProps { part?: string; } -export const heading = ( - {level, class: classname, part}: HeadingProps, - children?: T -) => { +export const heading: FunctionalComponentWithChildren = ({ + props, + children, +}) => { + const {level, class: classname, part} = props; + const headingTag = level > 0 && level <= 6 ? unsafeStatic(`h${level}`) : literal`div`; diff --git a/packages/atomic/src/components/common/load-more/button.ts b/packages/atomic/src/components/common/load-more/button.ts index 98b88a3eaa9..670c5466637 100644 --- a/packages/atomic/src/components/common/load-more/button.ts +++ b/packages/atomic/src/components/common/load-more/button.ts @@ -1,4 +1,6 @@ +import {FunctionalComponent} from '@/src/utils/functional-component-utils'; import {i18n} from 'i18next'; +import {html} from 'lit'; import {button, ButtonProps} from '../button'; interface LoadMoreButtonProps { @@ -8,20 +10,21 @@ interface LoadMoreButtonProps { label?: 'load-more-results' | 'load-more-products'; } -export function loadMoreButton({ - i18n, - onClick, - moreAvailable, - label, -}: LoadMoreButtonProps) { +export const loadMoreButton: FunctionalComponent = ({ + props, +}) => { + const {i18n, onClick, moreAvailable, label} = props; if (!moreAvailable) { return; } - const props: ButtonProps = { + const buttonProps: ButtonProps = { style: 'primary', part: 'load-more-results-button', class: 'my-2 p-3 font-bold', onClick: () => onClick(), }; - return button({props, children: i18n.t(label || 'load-more-results')}); -} + return button({ + props: buttonProps, + children: html`${i18n.t(label || 'load-more-results')}`, + }); +}; diff --git a/packages/atomic/src/components/common/radio-button.spec.ts b/packages/atomic/src/components/common/radio-button.spec.ts index cd29eb3f922..a0e7554092f 100644 --- a/packages/atomic/src/components/common/radio-button.spec.ts +++ b/packages/atomic/src/components/common/radio-button.spec.ts @@ -24,9 +24,10 @@ describe('radioButton', () => { props: Partial ): HTMLInputElement => { render( - html`${radioButton({...props, groupName: 'test-group'})}`, + html`${radioButton({props: {...props, groupName: 'test-group'}})}`, container ); + return within(container).getByRole('radio'); }; @@ -69,9 +70,9 @@ describe('radioButton', () => { }; render( - html`${radioButton({...props, text: 'radio-1'})} - ${radioButton({...props, text: 'radio-2'})} - ${radioButton({...props, text: 'radio-3'})}`, + html`${radioButton({props: {...props, text: 'radio-1'}})} + ${radioButton({props: {...props, text: 'radio-2'}})} + ${radioButton({props: {...props, text: 'radio-3'}})}`, container ); @@ -131,7 +132,7 @@ describe('radioButton', () => { ref, }; - render(html`${radioButton(props)}`, container); + render(html`${radioButton({props})}`, container); expect(ref).toHaveBeenCalled(); }); diff --git a/packages/atomic/src/components/common/radio-button.ts b/packages/atomic/src/components/common/radio-button.ts index 04b336c51b4..8afc4c9d95f 100644 --- a/packages/atomic/src/components/common/radio-button.ts +++ b/packages/atomic/src/components/common/radio-button.ts @@ -1,4 +1,5 @@ -import {html, TemplateResult} from 'lit'; +import {FunctionalComponent} from '@/src/utils/functional-component-utils'; +import {html} from 'lit'; import {classMap} from 'lit/directives/class-map.js'; import {ifDefined} from 'lit/directives/if-defined.js'; import {ref, RefOrCallback} from 'lit/directives/ref.js'; @@ -31,7 +32,7 @@ export interface RadioButtonProps { ref?: RefOrCallback; } -export const radioButton = (props: RadioButtonProps): TemplateResult => { +export const radioButton: FunctionalComponent = ({props}) => { const classNames = { 'btn-radio': true, selected: Boolean(props.checked), diff --git a/packages/atomic/src/utils/functional-component-utils.ts b/packages/atomic/src/utils/functional-component-utils.ts new file mode 100644 index 00000000000..99f678504ea --- /dev/null +++ b/packages/atomic/src/utils/functional-component-utils.ts @@ -0,0 +1,18 @@ +import {TemplateResult} from 'lit'; + +export interface FunctionalComponent { + ({props}: {props: T}): TemplateResult | undefined; +} + +export interface FunctionalComponentWithChildren { + ({ + props, + children, + }: { + props: T; + children: + | TemplateResult + | TemplateResult[] + | (TemplateResult | undefined)[]; + }): TemplateResult; +}