Skip to content

Commit

Permalink
feat(ffe-cards-react): component as props pattern on cards
Browse files Browse the repository at this point in the history
  • Loading branch information
pethel committed Jun 18, 2024
1 parent d8576d7 commit de6f127
Show file tree
Hide file tree
Showing 12 changed files with 271 additions and 91 deletions.
20 changes: 20 additions & 0 deletions packages/ffe-cards-react/src/CardBase.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,4 +81,24 @@ describe('<CardBase/>', () => {
card.classList.contains('ffe-card-base--text-center'),
).toBeTruthy();
});

it('should render as wished element', () => {
render(
<CardBase as="li">
<div />
</CardBase>,
);
expect(screen.getByRole('listitem')).toBeInTheDocument();
});

it('should set ref', () => {
const ref = React.createRef<HTMLLIElement>();
render(
<CardBase as="li" ref={ref}>
<div />
</CardBase>,
);
const listitem = screen.getByRole('listitem');
expect(listitem).toBe(ref.current);
});
});
46 changes: 29 additions & 17 deletions packages/ffe-cards-react/src/CardBase.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,41 @@
import React from 'react';
import React, { ElementType, ForwardedRef } from 'react';
import classNames from 'classnames';
import { WithCardActionProps, WithCardAction } from './components';
import { ComponentAsPropParams } from './types';
import { fixedForwardRef } from './fixedForwardRef';

type BgColor = 'sand-30' | 'sand-70' | 'frost-30' | 'syrin-30' | 'syrin-70';
type BgColorDarkmode = 'natt' | 'svart' | 'koksgraa';

export interface CardBaseProps
extends Omit<React.ComponentPropsWithoutRef<'div'>, 'children'> {
export type CardBaseProps<As extends ElementType = 'div'> = Omit<
ComponentAsPropParams<As>,
'children'
> & {
shadow?: boolean;
noMargin?: boolean;
textCenter?: boolean;
bgColor?: BgColor;
bgDarkmodeColor?: BgColorDarkmode;
noPadding?: boolean;
children: WithCardActionProps['children'] | React.ReactNode;
}
};

function CardBaseWithForwardRef<As extends ElementType>(
props: CardBaseProps<As>,
ref: ForwardedRef<any>,
) {
const {
className,
shadow,
noMargin,
textCenter,
bgColor,
bgDarkmodeColor,
noPadding,
children,
...rest
} = props;

export const CardBase: React.FC<CardBaseProps> = ({
className,
shadow,
noMargin,
textCenter,
bgColor,
bgDarkmodeColor,
noPadding,
children,
...rest
}) => {
return (
<WithCardAction
className={classNames('ffe-card-base', className, {
Expand All @@ -37,7 +46,8 @@ export const CardBase: React.FC<CardBaseProps> = ({
'ffe-card-base--text-center': textCenter,
'ffe-card-base--no-padding': noPadding,
})}
{...rest}
{...(rest as React.ComponentPropsWithoutRef<typeof WithCardAction>)}
ref={ref}
>
{({ CardAction }) =>
typeof children === 'function'
Expand All @@ -46,4 +56,6 @@ export const CardBase: React.FC<CardBaseProps> = ({
}
</WithCardAction>
);
};
}

export const CardBase = fixedForwardRef(CardBaseWithForwardRef);
27 changes: 27 additions & 0 deletions packages/ffe-cards-react/src/IconCard/IconCard.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,31 @@ describe('IconCard', () => {
expect(card.classList.contains('ffe-icon-card')).toBeTruthy();
expect(card.classList.contains('my-custom-class')).toBeTruthy();
});

it('should render as wished element', () => {
render(
<IconCard
as="li"
icon={<Icon fileUrl={savingsIconXlarge} size="xl" />}
>
{children}
</IconCard>,
);
expect(screen.getByRole('listitem')).toBeInTheDocument();
});

it('should set ref', () => {
const ref = React.createRef<HTMLLIElement>();
render(
<IconCard
as="li"
ref={ref}
icon={<Icon fileUrl={savingsIconXlarge} size="xl" />}
>
{children}
</IconCard>,
);
const listitem = screen.getByRole('listitem');
expect(listitem).toBe(ref.current);
});
});
33 changes: 19 additions & 14 deletions packages/ffe-cards-react/src/IconCard/IconCard.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,37 @@
import React, { ReactElement } from 'react';
import { CardRenderProps } from '../types';
import React, { ElementType, ForwardedRef, ReactElement } from 'react';
import { CardRenderProps, ComponentAsPropParams } from '../types';
import classNames from 'classnames';
import { WithCardAction, Text, Subtext, Title, CardName } from '../components';
import { fixedForwardRef } from '../fixedForwardRef';

export interface IconCardProps
extends Omit<React.ComponentPropsWithoutRef<'div'>, 'children'> {
export type IconCardProps<As extends ElementType = 'div'> = Omit<
ComponentAsPropParams<As>,
'children'
> & {
/** Element of icon */
icon: ReactElement;
/** Smaller icon and less space */
condensed?: boolean;
children:
| React.ReactNode
| ((cardRenderProps: CardRenderProps) => React.ReactNode);
}
};

function IconCardWithForwardRef<As extends ElementType>(
props: IconCardProps<As>,
ref: ForwardedRef<any>,
) {
const { className, condensed, icon, children, ...rest } = props;

export const IconCard: React.FC<IconCardProps> = ({
className,
condensed,
icon,
children,
...rest
}) => {
return (
<WithCardAction
className={classNames(
'ffe-icon-card',
{ 'ffe-icon-card--condensed': condensed },
className,
)}
{...rest}
{...(rest as React.ComponentPropsWithoutRef<typeof WithCardAction>)}
ref={ref}
>
{({ CardAction }) => (
<>
Expand All @@ -54,4 +57,6 @@ export const IconCard: React.FC<IconCardProps> = ({
)}
</WithCardAction>
);
};
}

export const IconCard = fixedForwardRef(IconCardWithForwardRef);
29 changes: 29 additions & 0 deletions packages/ffe-cards-react/src/ImageCard/ImageCard.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -102,4 +102,33 @@ describe('ImageCard', () => {
expect(card.classList.contains('ffe-image-card')).toBeTruthy();
expect(card.classList.contains('my-custom-class')).toBeTruthy();
});

it('should render as wished element', () => {
render(
<ImageCard
as="li"
imageAltText="Image alt text"
imageSrc="random/path"
>
{children}
</ImageCard>,
);
expect(screen.getByRole('listitem')).toBeInTheDocument();
});

it('should set ref', () => {
const ref = React.createRef<HTMLLIElement>();
render(
<ImageCard
as="li"
ref={ref}
imageAltText="Image alt text"
imageSrc="random/path"
>
{children}
</ImageCard>,
);
const listitem = screen.getByRole('listitem');
expect(listitem).toBe(ref.current);
});
});
32 changes: 18 additions & 14 deletions packages/ffe-cards-react/src/ImageCard/ImageCard.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,33 @@
import React from 'react';
import { CardRenderProps } from '../types';
import React, { ElementType, ForwardedRef } from 'react';
import { CardRenderProps, ComponentAsPropParams } from '../types';
import classNames from 'classnames';
import { CardName, Subtext, Text, Title, WithCardAction } from '../components';
import { fixedForwardRef } from '../fixedForwardRef';

export interface ImageCardProps
extends Omit<React.ComponentPropsWithoutRef<'div'>, 'children'> {
export type ImageCardProps<As extends ElementType = 'div'> = Omit<
ComponentAsPropParams<As>,
'children'
> & {
/** The src for the image */
imageSrc: string;
/** The alt text for the image */
imageAltText: string;
children:
| React.ReactNode
| ((cardRenderProps: CardRenderProps) => React.ReactNode);
}
};

function ImageCardWithForwardRef<As extends ElementType>(
props: ImageCardProps<As>,
ref: ForwardedRef<any>,
) {
const { className, imageSrc, imageAltText, children, ...rest } = props;

export const ImageCard: React.FC<ImageCardProps> = ({
className,
imageSrc,
imageAltText,
children,
...rest
}) => {
return (
<WithCardAction
className={classNames('ffe-image-card', className)}
{...rest}
{...(rest as React.ComponentPropsWithoutRef<typeof WithCardAction>)}
ref={ref}
>
{({ CardAction }) => (
<>
Expand All @@ -51,4 +54,5 @@ export const ImageCard: React.FC<ImageCardProps> = ({
)}
</WithCardAction>
);
};
}
export const ImageCard = fixedForwardRef(ImageCardWithForwardRef);
33 changes: 33 additions & 0 deletions packages/ffe-cards-react/src/StippledCard/StippledCard.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,37 @@ describe('StippledCard', () => {
expect(card.classList.contains('ffe-stippled-card')).toBeTruthy();
expect(card.classList.contains('my-custom-class')).toBeTruthy();
});

it('should render as wished element', () => {
render(
<StippledCard
as="li"
img={{
element: <Icon fileUrl="monitoring" size="md" />,
type: 'icon',
}}
>
{children}
</StippledCard>,
);
expect(screen.getByRole('listitem')).toBeInTheDocument();
});

it('should set ref', () => {
const ref = React.createRef<HTMLLIElement>();
render(
<StippledCard
as="li"
ref={ref}
img={{
element: <Icon fileUrl="monitoring" size="md" />,
type: 'icon',
}}
>
{children}
</StippledCard>,
);
const listitem = screen.getByRole('listitem');
expect(listitem).toBe(ref.current);
});
});
32 changes: 18 additions & 14 deletions packages/ffe-cards-react/src/StippledCard/StippledCard.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import React, { ReactNode } from 'react';
import { CardRenderProps } from '../types';
import React, { ElementType, ForwardedRef, ReactNode } from 'react';
import { CardRenderProps, ComponentAsPropParams } from '../types';
import classNames from 'classnames';
import { CardName, Subtext, Text, Title, WithCardAction } from '../components';
import { fixedForwardRef } from '../fixedForwardRef';

export interface StippledCardProps
extends Omit<React.ComponentPropsWithoutRef<'div'>, 'children'> {
export type StippledCardProps<As extends ElementType = 'div'> = Omit<
ComponentAsPropParams<As>,
'children'
> & {
/** Smaller icon and less space */
condensed?: boolean;
/** Image to be rendered*/
Expand All @@ -15,23 +18,23 @@ export interface StippledCardProps
children:
| React.ReactNode
| ((cardRenderProps: CardRenderProps) => React.ReactNode);
}
};

function StippledCardWithForwardRef<As extends ElementType>(
props: StippledCardProps<As>,
ref: ForwardedRef<any>,
) {
const { className, condensed, img, children, ...rest } = props;

export const StippledCard: React.FC<StippledCardProps> = ({
className,
condensed,
img,
children,
...rest
}) => {
return (
<WithCardAction
className={classNames(
'ffe-stippled-card',
{ 'ffe-stippled-card--condensed': condensed },
className,
)}
{...rest}
{...(rest as React.ComponentPropsWithoutRef<typeof WithCardAction>)}
ref={ref}
>
{({ CardAction }) => (
<>
Expand Down Expand Up @@ -59,4 +62,5 @@ export const StippledCard: React.FC<StippledCardProps> = ({
)}
</WithCardAction>
);
};
}
export const StippledCard = fixedForwardRef(StippledCardWithForwardRef);
16 changes: 16 additions & 0 deletions packages/ffe-cards-react/src/TextCard/TextCard.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,20 @@ describe('TextCard', () => {
expect(card.classList.contains('ffe-text-card')).toBeTruthy();
expect(card.classList.contains('my-custom-class')).toBeTruthy();
});

it('should render as wished element', () => {
render(<TextCard as="li">{children}</TextCard>);
expect(screen.getByRole('listitem')).toBeInTheDocument();
});

it('should set ref', () => {
const ref = React.createRef<HTMLLIElement>();
render(
<TextCard as="li" ref={ref}>
{children}
</TextCard>,
);
const listitem = screen.getByRole('listitem');
expect(listitem).toBe(ref.current);
});
});
Loading

0 comments on commit de6f127

Please sign in to comment.