diff --git a/packages/web-react/src/components/Card/Card.tsx b/packages/web-react/src/components/Card/Card.tsx index 58fa30c203..d45e3018ff 100644 --- a/packages/web-react/src/components/Card/Card.tsx +++ b/packages/web-react/src/components/Card/Card.tsx @@ -8,8 +8,8 @@ import { SpiritCardProps } from '../../types'; import { useCardStyleProps } from './useCardStyleProps'; const defaultProps: Partial = { - elementType: 'article', direction: Direction.VERTICAL, + elementType: 'article', isBoxed: false, }; diff --git a/packages/web-react/src/components/Card/README.md b/packages/web-react/src/components/Card/README.md index d78b30de93..636396a166 100644 --- a/packages/web-react/src/components/Card/README.md +++ b/packages/web-react/src/components/Card/README.md @@ -24,11 +24,9 @@ Card composition example: - {/* User content */} - … - {/* End user content */} + {/* User content */}…{/* End user content */} - … + ``` @@ -96,7 +94,7 @@ Advanced example: ``` -### API +### API - Card Title | Name | Type | Default | Required | Description | | ------------- | ------------- | ------- | -------- | ------------------------------------------ | @@ -107,6 +105,10 @@ On top of the API options, the components accept [additional attributes][readme- If you need more control over the styling of a component, you can use [style props][readme-style-props] and [escape hatches][readme-escape-hatches]. +### API - Card Link + +`CardLink` utilizes the `Link` component internally and shares its API. For more details, please refer to the [Link component documentation][link-readme]. + ## Card Body Basic usage: @@ -115,25 +117,25 @@ Basic usage: ``` -## Card Actions +## Card Footer Basic usage: ```jsx - + ``` Advanced example: ```jsx - + ``` ### API -| Name | Type | Default | Required | Description | -| ------------ | --------------------------------------------- | ------- | -------- | --------------------------- | -| `alignmentX` | [AlignmentX dictionary][dictionary-alignment] | `left` | ✕ | Alignment of Footer Actions | +| Name | Type | Default | Required | Description | +| ------------ | --------------------------------------------- | ------- | -------- | ------------------------- | +| `alignmentX` | [AlignmentX dictionary][dictionary-alignment] | `left` | ✕ | Alignment of Footer Items | On top of the API options, the components accept [additional attributes][readme-additional-attributes]. If you need more control over the styling of a component, you can use [style props][readme-style-props] @@ -142,6 +144,7 @@ and [escape hatches][readme-escape-hatches]. [dictionary-alignment]: https://github.com/lmc-eu/spirit-design-system/blob/main/docs/DICTIONARIES.md#alignment [dictionary-direction]: https://github.com/lmc-eu/spirit-design-system/blob/main/docs/DICTIONARIES.md#direction [dictionary-size]: https://github.com/lmc-eu/spirit-design-system/tree/main/docs/DICTIONARIES.md#size +[link-readme]: https://github.com/lmc-eu/spirit-design-system/blob/main/packages/web-react/src/components/Link/README.md#api [readme-additional-attributes]: https://github.com/lmc-eu/spirit-design-system/blob/main/packages/web-react/README.md#additional-attributes [readme-escape-hatches]: https://github.com/lmc-eu/spirit-design-system/blob/main/packages/web-react/README.md#escape-hatches [readme-style-props]: https://github.com/lmc-eu/spirit-design-system/blob/main/packages/web-react/README.md#style-props diff --git a/packages/web-react/src/components/Card/__tests__/CardFooter.test.tsx b/packages/web-react/src/components/Card/__tests__/CardFooter.test.tsx index 2e01d1ec0b..a6d28f1459 100644 --- a/packages/web-react/src/components/Card/__tests__/CardFooter.test.tsx +++ b/packages/web-react/src/components/Card/__tests__/CardFooter.test.tsx @@ -2,6 +2,7 @@ import '@testing-library/jest-dom'; import { render, screen } from '@testing-library/react'; import React from 'react'; import { classNamePrefixProviderTest } from '../../../../tests/providerTests/classNamePrefixProviderTest'; +import { alignmentXPropsTest } from '../../../../tests/providerTests/dictionaryPropsTest'; import { restPropsTest } from '../../../../tests/providerTests/restPropsTest'; import { stylePropsTest } from '../../../../tests/providerTests/stylePropsTest'; import CardFooter from '../CardFooter'; @@ -13,18 +14,14 @@ describe('CardFooter', () => { restPropsTest(CardFooter, '.CardFooter'); + alignmentXPropsTest(CardFooter, 'CardFooter'); + it('should render card component', () => { render(); expect(screen.getByRole('contentinfo')).toBeInTheDocument(); }); - it('should have alignment class', () => { - render(); - - expect(screen.getByRole('contentinfo')).toHaveClass('CardFooter--alignmentXCenter'); - }); - it('should render text children', () => { render(Hello World); diff --git a/packages/web-react/src/components/Card/__tests__/CardMedia.test.tsx b/packages/web-react/src/components/Card/__tests__/CardMedia.test.tsx index 3949ac973f..c3aed48161 100644 --- a/packages/web-react/src/components/Card/__tests__/CardMedia.test.tsx +++ b/packages/web-react/src/components/Card/__tests__/CardMedia.test.tsx @@ -2,6 +2,7 @@ import '@testing-library/jest-dom'; import { render, screen } from '@testing-library/react'; import React from 'react'; import { classNamePrefixProviderTest } from '../../../../tests/providerTests/classNamePrefixProviderTest'; +import { sizePropsTest } from '../../../../tests/providerTests/dictionaryPropsTest'; import { restPropsTest } from '../../../../tests/providerTests/restPropsTest'; import { stylePropsTest } from '../../../../tests/providerTests/stylePropsTest'; import CardMedia from '../CardMedia'; @@ -13,16 +14,18 @@ describe('CardMedia', () => { restPropsTest(CardMedia, '.CardMedia'); + sizePropsTest(CardMedia); + it('should render card media component', () => { render(); expect(screen.getByTestId('test')).toBeInTheDocument(); }); - it('should render small size', () => { - render(); + it('should render auto size', () => { + render(); - expect(screen.getByTestId('test')).toHaveClass('CardMedia--small'); + expect(screen.getByTestId('test')).toHaveClass('CardMedia--auto'); }); it('should fill the height', () => { diff --git a/packages/web-react/src/components/Card/demo/CardGeneralOptions.tsx b/packages/web-react/src/components/Card/demo/CardGeneralOptions.tsx index 05c5588c0a..c361ad26ce 100644 --- a/packages/web-react/src/components/Card/demo/CardGeneralOptions.tsx +++ b/packages/web-react/src/components/Card/demo/CardGeneralOptions.tsx @@ -1,7 +1,6 @@ import React from 'react'; import { Button } from '../../Button'; import { Grid } from '../../Grid'; -import { Link } from '../../Link'; import Card from '../Card'; import CardBody from '../CardBody'; import CardEyebrow from '../CardEyebrow'; @@ -27,9 +26,7 @@ const CardGeneralOptions = () => ( Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean fermentum risus id tortor. Integer lacinia. Sed vel lectus.

- - Read more - +
Read more
{/* End user content */} diff --git a/packages/web-react/src/components/Card/demo/CardMediaOptions.tsx b/packages/web-react/src/components/Card/demo/CardMediaOptions.tsx index 895109c9db..26019c0e14 100644 --- a/packages/web-react/src/components/Card/demo/CardMediaOptions.tsx +++ b/packages/web-react/src/components/Card/demo/CardMediaOptions.tsx @@ -14,7 +14,7 @@ import { MEDIA_IMAGE } from './constants'; export const CardMediaOptions = () => ( - + @@ -37,7 +37,7 @@ export const CardMediaOptions = () => (
- + @@ -60,7 +60,7 @@ export const CardMediaOptions = () => (
- + @@ -83,7 +83,7 @@ export const CardMediaOptions = () => ( - + @@ -106,13 +106,7 @@ export const CardMediaOptions = () => ( - + diff --git a/packages/web-react/src/components/Card/stories/CardLink.stories.tsx b/packages/web-react/src/components/Card/stories/CardLink.stories.tsx index 5892891532..63a7cb9de0 100644 --- a/packages/web-react/src/components/Card/stories/CardLink.stories.tsx +++ b/packages/web-react/src/components/Card/stories/CardLink.stories.tsx @@ -26,7 +26,7 @@ const meta: Meta = { control: 'text', description: 'Text to display in the CardLink.', table: { - defaultValue: { summary: 'Card Eyebrow text' }, + defaultValue: { summary: 'Card CardLink text' }, }, }, elementType: { @@ -36,10 +36,18 @@ const meta: Meta = { defaultValue: { summary: 'a' }, }, }, + href: { + control: 'text', + description: 'URL to link to.', + table: { + defaultValue: { summary: '' }, + }, + }, }, args: { children: 'Card Link Title', elementType: 'a', + href: '#', }, }; @@ -60,9 +68,7 @@ export const CardLinkComponent: Story = { Card Title - - {children} - + {children}

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla accumsan, metus ultrices eleifend gravida, diff --git a/packages/web-react/src/components/Card/stories/CardMedia.stories.tsx b/packages/web-react/src/components/Card/stories/CardMedia.stories.tsx index be9ed1dfdf..102f431ef8 100644 --- a/packages/web-react/src/components/Card/stories/CardMedia.stories.tsx +++ b/packages/web-react/src/components/Card/stories/CardMedia.stories.tsx @@ -24,7 +24,7 @@ const meta: Meta = { argTypes: { hasFilledHeight: { control: 'boolean', - description: 'Fill the height of the media.', + description: 'Fill the height of the media. Only works when the card direction is not vertical.', table: { defaultValue: { summary: 'false' }, }, diff --git a/packages/web-react/src/components/Card/stories/CardTitle.stories.tsx b/packages/web-react/src/components/Card/stories/CardTitle.stories.tsx index 6efb530527..679428149b 100644 --- a/packages/web-react/src/components/Card/stories/CardTitle.stories.tsx +++ b/packages/web-react/src/components/Card/stories/CardTitle.stories.tsx @@ -25,7 +25,7 @@ const meta: Meta = { control: 'text', description: 'Text to display in the CardTitle.', table: { - defaultValue: { summary: 'Card Eyebrow text' }, + defaultValue: { summary: 'Card CardTitle text' }, }, }, elementType: { @@ -65,7 +65,7 @@ export const CardTitleComponent: Story = { - Card Title + Card Eyebrow {children}

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla accumsan, metus ultrices eleifend gravida, diff --git a/packages/web-react/src/components/Card/useCardStyleProps.ts b/packages/web-react/src/components/Card/useCardStyleProps.ts index 0ff0fcb5be..30f2a7e6f7 100644 --- a/packages/web-react/src/components/Card/useCardStyleProps.ts +++ b/packages/web-react/src/components/Card/useCardStyleProps.ts @@ -28,26 +28,29 @@ export interface UseCardStylePropsReturn { export function useCardStyleProps(props?: UseCardStyleProps): UseCardStylePropsReturn { const { direction, isBoxed, isExpanded, alignmentX, size, isHeading, hasFilledHeight } = props || {}; const cardClass = useClassNamePrefix('Card'); - const isBoxedClass = `${cardClass}--boxed`; + const bodyClass = `${cardClass}Body`; const directionClass = direction ? `${cardClass}--${kebabCaseToCamelCase(direction)}` : ''; - const mediaSizeClass = size ? `${cardClass}Media--${size}` : ''; - const mediaIsExpandedClass = `${cardClass}Media--expanded`; - const mediaHasFilledHeightClass = `${cardClass}Media--filledHeight`; + const eyebrowClass = `${cardClass}Eyebrow`; + const footerClass = `${cardClass}Footer`; + const isBoxedClass = `${cardClass}--boxed`; + const mediaClass = `${cardClass}Media`; + const mediaHasFilledHeightClass = `${mediaClass}--filledHeight`; + const mediaIsExpandedClass = `${mediaClass}--expanded`; + const mediaSizeClass = size ? `${mediaClass}--${size}` : ''; + const titleClass = `${cardClass}Title`; - const rootClasses = classNames(cardClass, directionClass, { - [isBoxedClass]: isBoxed, + const footerClasses = classNames(footerClass, { + [useAlignmentClass(footerClass, alignmentX!, 'alignmentX')]: alignmentX, }); - const mediaClasses = classNames(`${cardClass}Media`, mediaSizeClass, { + const mediaClasses = classNames(mediaClass, mediaSizeClass, { [mediaIsExpandedClass]: isExpanded, [mediaHasFilledHeightClass]: hasFilledHeight, }); - const bodyClass = `${cardClass}Body`; - const eyebrowClass = `${cardClass}Eyebrow`; - const titleClasses = classNames(`${cardClass}Title`, { - [`${cardClass}Title--heading`]: isHeading, + const rootClasses = classNames(cardClass, directionClass, { + [isBoxedClass]: isBoxed, }); - const footerClasses = classNames(`${cardClass}Footer`, { - [useAlignmentClass(`${cardClass}Footer`, alignmentX!, 'alignmentX')]: alignmentX, + const titleClasses = classNames(titleClass, { + [`${titleClass}--heading`]: isHeading, }); return { diff --git a/packages/web-react/tests/providerTests/dictionaryPropsTest.tsx b/packages/web-react/tests/providerTests/dictionaryPropsTest.tsx index b2c795714e..c8be2f458a 100644 --- a/packages/web-react/tests/providerTests/dictionaryPropsTest.tsx +++ b/packages/web-react/tests/providerTests/dictionaryPropsTest.tsx @@ -17,6 +17,12 @@ import { TextColors, ValidationStatesDictionaryType, ValidationStates, + AlignmentX, + AlignmentXExtended, + AlignmentXDictionaryType, + AlignmentXExtendedDictionaryType, + AlignmentYDictionaryType, + AlignmentYExtendedDictionaryType, } from '../../src'; import getElement from '../testUtils/getElement'; @@ -115,3 +121,51 @@ export const validationStatePropsTest = (Component: ComponentType, prefix: }); }); }; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export const alignmentXPropsTest = (Component: ComponentType, prefix?: string, testId?: string) => { + it.each([Object.values(AlignmentX)])('should render alignmentX %s', async (alignment) => { + const dom = render(} />); + + await waitFor(() => { + const element = getElement(dom, testId); + expect(element).toHaveClass(`${prefix}--alignmentX${alignment.charAt(0).toUpperCase() + alignment.slice(1)}`); + }); + }); +}; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export const alignmentXExtendedPropsTest = (Component: ComponentType, prefix?: string, testId?: string) => { + it.each([Object.values(AlignmentXExtended)])('should render extended alignmentX %s', async (alignment) => { + const dom = render(} />); + + await waitFor(() => { + const element = getElement(dom, testId); + expect(element).toHaveClass(`${prefix}--alignmentX${alignment.charAt(0).toUpperCase() + alignment.slice(1)}`); + }); + }); +}; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export const alignmentYPropsTest = (Component: ComponentType, prefix?: string, testId?: string) => { + it.each([Object.values(AlignmentX)])('should render alignmentY %s', async (alignment) => { + const dom = render(} />); + + await waitFor(() => { + const element = getElement(dom, testId); + expect(element).toHaveClass(`${prefix}--alignmentY${alignment.charAt(0).toUpperCase() + alignment.slice(1)}`); + }); + }); +}; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export const alignmentYExtendedPropsTest = (Component: ComponentType, prefix?: string, testId?: string) => { + it.each([Object.values(AlignmentXExtended)])('should render extended alignmentY %s', async (alignment) => { + const dom = render(} />); + + await waitFor(() => { + const element = getElement(dom, testId); + expect(element).toHaveClass(`${prefix}--alignmentY${alignment.charAt(0).toUpperCase() + alignment.slice(1)}`); + }); + }); +};