diff --git a/packages/ffe-cards-react/src/CardBase.spec.tsx b/packages/ffe-cards-react/src/CardBase.spec.tsx index 37444e3dcb..081c1e132a 100644 --- a/packages/ffe-cards-react/src/CardBase.spec.tsx +++ b/packages/ffe-cards-react/src/CardBase.spec.tsx @@ -81,4 +81,24 @@ describe('', () => { card.classList.contains('ffe-card-base--text-center'), ).toBeTruthy(); }); + + it('should render as wished element', () => { + render( + +
+ , + ); + expect(screen.getByRole('listitem')).toBeInTheDocument(); + }); + + it('should set ref', () => { + const ref = React.createRef(); + render( + +
+ , + ); + const listitem = screen.getByRole('listitem'); + expect(listitem).toBe(ref.current); + }); }); diff --git a/packages/ffe-cards-react/src/CardBase.tsx b/packages/ffe-cards-react/src/CardBase.tsx index 5ca18b7184..f6a5e22652 100644 --- a/packages/ffe-cards-react/src/CardBase.tsx +++ b/packages/ffe-cards-react/src/CardBase.tsx @@ -1,12 +1,16 @@ -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, 'children'> { +export type CardBaseProps = Omit< + ComponentAsPropParams, + 'children' +> & { shadow?: boolean; noMargin?: boolean; textCenter?: boolean; @@ -14,19 +18,24 @@ export interface CardBaseProps bgDarkmodeColor?: BgColorDarkmode; noPadding?: boolean; children: WithCardActionProps['children'] | React.ReactNode; -} +}; + +function CardBaseWithForwardRef( + props: CardBaseProps, + ref: ForwardedRef, +) { + const { + className, + shadow, + noMargin, + textCenter, + bgColor, + bgDarkmodeColor, + noPadding, + children, + ...rest + } = props; -export const CardBase: React.FC = ({ - className, - shadow, - noMargin, - textCenter, - bgColor, - bgDarkmodeColor, - noPadding, - children, - ...rest -}) => { return ( = ({ 'ffe-card-base--text-center': textCenter, 'ffe-card-base--no-padding': noPadding, })} - {...rest} + {...(rest as React.ComponentPropsWithoutRef)} + ref={ref} > {({ CardAction }) => typeof children === 'function' @@ -46,4 +56,6 @@ export const CardBase: React.FC = ({ } ); -}; +} + +export const CardBase = fixedForwardRef(CardBaseWithForwardRef); diff --git a/packages/ffe-cards-react/src/IconCard/IconCard.spec.tsx b/packages/ffe-cards-react/src/IconCard/IconCard.spec.tsx index 0497040e79..e81da3f5cd 100644 --- a/packages/ffe-cards-react/src/IconCard/IconCard.spec.tsx +++ b/packages/ffe-cards-react/src/IconCard/IconCard.spec.tsx @@ -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( + } + > + {children} + , + ); + expect(screen.getByRole('listitem')).toBeInTheDocument(); + }); + + it('should set ref', () => { + const ref = React.createRef(); + render( + } + > + {children} + , + ); + const listitem = screen.getByRole('listitem'); + expect(listitem).toBe(ref.current); + }); }); diff --git a/packages/ffe-cards-react/src/IconCard/IconCard.tsx b/packages/ffe-cards-react/src/IconCard/IconCard.tsx index 419e3f3af4..7b173fade4 100644 --- a/packages/ffe-cards-react/src/IconCard/IconCard.tsx +++ b/packages/ffe-cards-react/src/IconCard/IconCard.tsx @@ -1,10 +1,13 @@ -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, 'children'> { +export type IconCardProps = Omit< + ComponentAsPropParams, + 'children' +> & { /** Element of icon */ icon: ReactElement; /** Smaller icon and less space */ @@ -12,15 +15,14 @@ export interface IconCardProps children: | React.ReactNode | ((cardRenderProps: CardRenderProps) => React.ReactNode); -} +}; + +function IconCardWithForwardRef( + props: IconCardProps, + ref: ForwardedRef, +) { + const { className, condensed, icon, children, ...rest } = props; -export const IconCard: React.FC = ({ - className, - condensed, - icon, - children, - ...rest -}) => { return ( = ({ { 'ffe-icon-card--condensed': condensed }, className, )} - {...rest} + {...(rest as React.ComponentPropsWithoutRef)} + ref={ref} > {({ CardAction }) => ( <> @@ -54,4 +57,6 @@ export const IconCard: React.FC = ({ )} ); -}; +} + +export const IconCard = fixedForwardRef(IconCardWithForwardRef); diff --git a/packages/ffe-cards-react/src/ImageCard/ImageCard.spec.tsx b/packages/ffe-cards-react/src/ImageCard/ImageCard.spec.tsx index ef624fef3d..6248b5af3e 100644 --- a/packages/ffe-cards-react/src/ImageCard/ImageCard.spec.tsx +++ b/packages/ffe-cards-react/src/ImageCard/ImageCard.spec.tsx @@ -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( + + {children} + , + ); + expect(screen.getByRole('listitem')).toBeInTheDocument(); + }); + + it('should set ref', () => { + const ref = React.createRef(); + render( + + {children} + , + ); + const listitem = screen.getByRole('listitem'); + expect(listitem).toBe(ref.current); + }); }); diff --git a/packages/ffe-cards-react/src/ImageCard/ImageCard.tsx b/packages/ffe-cards-react/src/ImageCard/ImageCard.tsx index 559ac7159d..5ddacfd0e1 100644 --- a/packages/ffe-cards-react/src/ImageCard/ImageCard.tsx +++ b/packages/ffe-cards-react/src/ImageCard/ImageCard.tsx @@ -1,10 +1,13 @@ -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, 'children'> { +export type ImageCardProps = Omit< + ComponentAsPropParams, + 'children' +> & { /** The src for the image */ imageSrc: string; /** The alt text for the image */ @@ -12,19 +15,19 @@ export interface ImageCardProps children: | React.ReactNode | ((cardRenderProps: CardRenderProps) => React.ReactNode); -} +}; + +function ImageCardWithForwardRef( + props: ImageCardProps, + ref: ForwardedRef, +) { + const { className, imageSrc, imageAltText, children, ...rest } = props; -export const ImageCard: React.FC = ({ - className, - imageSrc, - imageAltText, - children, - ...rest -}) => { return ( )} + ref={ref} > {({ CardAction }) => ( <> @@ -51,4 +54,5 @@ export const ImageCard: React.FC = ({ )} ); -}; +} +export const ImageCard = fixedForwardRef(ImageCardWithForwardRef); diff --git a/packages/ffe-cards-react/src/StippledCard/StippledCard.spec.tsx b/packages/ffe-cards-react/src/StippledCard/StippledCard.spec.tsx index ca747c6da6..55f3b509fb 100644 --- a/packages/ffe-cards-react/src/StippledCard/StippledCard.spec.tsx +++ b/packages/ffe-cards-react/src/StippledCard/StippledCard.spec.tsx @@ -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( + , + type: 'icon', + }} + > + {children} + , + ); + expect(screen.getByRole('listitem')).toBeInTheDocument(); + }); + + it('should set ref', () => { + const ref = React.createRef(); + render( + , + type: 'icon', + }} + > + {children} + , + ); + const listitem = screen.getByRole('listitem'); + expect(listitem).toBe(ref.current); + }); }); diff --git a/packages/ffe-cards-react/src/StippledCard/StippledCard.tsx b/packages/ffe-cards-react/src/StippledCard/StippledCard.tsx index d19a576504..e426ac49bc 100644 --- a/packages/ffe-cards-react/src/StippledCard/StippledCard.tsx +++ b/packages/ffe-cards-react/src/StippledCard/StippledCard.tsx @@ -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, 'children'> { +export type StippledCardProps = Omit< + ComponentAsPropParams, + 'children' +> & { /** Smaller icon and less space */ condensed?: boolean; /** Image to be rendered*/ @@ -15,15 +18,14 @@ export interface StippledCardProps children: | React.ReactNode | ((cardRenderProps: CardRenderProps) => React.ReactNode); -} +}; + +function StippledCardWithForwardRef( + props: StippledCardProps, + ref: ForwardedRef, +) { + const { className, condensed, img, children, ...rest } = props; -export const StippledCard: React.FC = ({ - className, - condensed, - img, - children, - ...rest -}) => { return ( = ({ { 'ffe-stippled-card--condensed': condensed }, className, )} - {...rest} + {...(rest as React.ComponentPropsWithoutRef)} + ref={ref} > {({ CardAction }) => ( <> @@ -59,4 +62,5 @@ export const StippledCard: React.FC = ({ )} ); -}; +} +export const StippledCard = fixedForwardRef(StippledCardWithForwardRef); diff --git a/packages/ffe-cards-react/src/TextCard/TextCard.spec.tsx b/packages/ffe-cards-react/src/TextCard/TextCard.spec.tsx index 077e46a7b0..ede030bea9 100644 --- a/packages/ffe-cards-react/src/TextCard/TextCard.spec.tsx +++ b/packages/ffe-cards-react/src/TextCard/TextCard.spec.tsx @@ -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({children}); + expect(screen.getByRole('listitem')).toBeInTheDocument(); + }); + + it('should set ref', () => { + const ref = React.createRef(); + render( + + {children} + , + ); + const listitem = screen.getByRole('listitem'); + expect(listitem).toBe(ref.current); + }); }); diff --git a/packages/ffe-cards-react/src/TextCard/TextCard.tsx b/packages/ffe-cards-react/src/TextCard/TextCard.tsx index f98d2865a2..02bc2ed733 100644 --- a/packages/ffe-cards-react/src/TextCard/TextCard.tsx +++ b/packages/ffe-cards-react/src/TextCard/TextCard.tsx @@ -1,24 +1,27 @@ -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 TextCardProps - extends Omit, 'children'> { +export type TextCardProps = Omit< + ComponentAsPropParams, + 'children' +> & { /** Left-aligned text on the card */ leftAlign?: boolean; /** Function that's passed available sub-components as arguments, or regular children */ children: | React.ReactNode | ((cardRenderProps: CardRenderProps) => React.ReactNode); -} +}; + +function TextCardWithForwardRef( + props: TextCardProps, + ref: ForwardedRef, +) { + const { className, leftAlign, children, ...rest } = props; -export const TextCard: React.FC = ({ - className, - leftAlign, - children, - ...rest -}) => { return ( = ({ { 'ffe-text-card--left-align': leftAlign }, className, )} - {...rest} + {...(rest as React.ComponentPropsWithoutRef)} + ref={ref} > {({ CardAction }) => typeof children === 'function' @@ -35,4 +39,6 @@ export const TextCard: React.FC = ({ } ); -}; +} + +export const TextCard = fixedForwardRef(TextCardWithForwardRef); diff --git a/packages/ffe-cards-react/src/components/WithCardAction.spec.tsx b/packages/ffe-cards-react/src/components/WithCardAction.spec.tsx index b4ecb30a54..110e5216d1 100644 --- a/packages/ffe-cards-react/src/components/WithCardAction.spec.tsx +++ b/packages/ffe-cards-react/src/components/WithCardAction.spec.tsx @@ -81,4 +81,19 @@ describe('', () => { const button = screen.getByRole('button'); expect(button).toBe(ref.current); }); + + it('should set refs on ', () => { + const ref = React.createRef(); + render( + + {({ CardAction }) => ( + + en knapp + + )} + , + ); + const button = screen.getByRole('button'); + expect(button).toBe(ref.current); + }); }); diff --git a/packages/ffe-cards-react/src/components/WithCardAction.tsx b/packages/ffe-cards-react/src/components/WithCardAction.tsx index e9e32663ef..9552f2c0d6 100644 --- a/packages/ffe-cards-react/src/components/WithCardAction.tsx +++ b/packages/ffe-cards-react/src/components/WithCardAction.tsx @@ -3,30 +3,37 @@ import classNames from 'classnames'; import { mergeRefs } from '../mergeRefs'; import { CardAction, CardActionProps } from './CardAction'; import { fixedForwardRef } from '../fixedForwardRef'; +import { ComponentAsPropParams } from '../types'; -export interface WithCardActionProps - extends Omit, 'children'> { +export type WithCardActionProps = Omit< + ComponentAsPropParams, + 'children' +> & { children: (props: { CardAction: typeof CardAction }) => React.ReactNode; -} +}; -export const WithCardAction: React.FC = ({ - children, - onClick, - ...rest -}) => { - const actionRef = useRef(null); +function WithCardActionForwardRef( + props: WithCardActionProps, + ref: ForwardedRef, +) { + const { children, as: Comp = 'div', onClick, ...rest } = props; + const actionInnerRef = useRef(null); const PartialAppliedCardAction = useCallback( - ( - { className, ...restCardAction }: CardActionProps, - ref?: ForwardedRef, + ( + { className, ...restCardAction }: CardActionProps, + actionRef?: ForwardedRef, ) => { return ( ); @@ -35,19 +42,21 @@ export const WithCardAction: React.FC = ({ ); return ( - // eslint-disable-next-line jsx-a11y/no-static-element-interactions -
) => { - if (!actionRef.current?.contains(e.target as Node)) { - actionRef.current?.click(); + if (!actionInnerRef.current?.contains(e.target as Node)) { + actionInnerRef.current?.click(); } onClick?.(e); }} + ref={ref} > {children({ CardAction: fixedForwardRef(PartialAppliedCardAction), })} -
+ ); -}; +} + +export const WithCardAction = fixedForwardRef(WithCardActionForwardRef);