-
Notifications
You must be signed in to change notification settings - Fork 26
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
a6f2b4b
commit a662e79
Showing
6 changed files
with
273 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
import { render, screen } from '@testing-library/react' | ||
|
||
import { Card } from './Card' | ||
|
||
describe('Card', () => { | ||
it('renders arbitrary child', async () => { | ||
render(<Card>hello</Card>) | ||
const hello = await screen.findByText('hello') | ||
expect(hello).toBeInTheDocument() | ||
}) | ||
|
||
describe('Card.Header', () => { | ||
it('renders', async () => { | ||
render( | ||
<Card> | ||
<Card.Header>Header</Card.Header> | ||
</Card> | ||
) | ||
const header = await screen.findByText('Header') | ||
expect(header).toBeInTheDocument() | ||
}) | ||
}) | ||
|
||
describe('Card.Title', () => { | ||
it('renders', async () => { | ||
render( | ||
<Card> | ||
<Card.Header> | ||
<Card.Title>Title</Card.Title> | ||
</Card.Header> | ||
</Card> | ||
) | ||
const title = await screen.findByText('Title') | ||
expect(title).toBeInTheDocument() | ||
}) | ||
}) | ||
|
||
describe('Card.Description', () => { | ||
it('renders', async () => { | ||
render( | ||
<Card> | ||
<Card.Header> | ||
<Card.Description>Description</Card.Description> | ||
</Card.Header> | ||
</Card> | ||
) | ||
const description = await screen.findByText('Description') | ||
expect(description).toBeInTheDocument() | ||
}) | ||
}) | ||
|
||
describe('Card.Content', () => { | ||
it('renders', async () => { | ||
render( | ||
<Card> | ||
<Card.Content>Content</Card.Content> | ||
</Card> | ||
) | ||
const content = await screen.findByText('Content') | ||
expect(content).toBeInTheDocument() | ||
}) | ||
}) | ||
|
||
describe('Card.Footer', () => { | ||
it('renders', async () => { | ||
render( | ||
<Card> | ||
<Card.Footer>Footer</Card.Footer> | ||
</Card> | ||
) | ||
const footer = await screen.findByText('Footer') | ||
expect(footer).toBeInTheDocument() | ||
}) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
import { Meta, StoryObj } from '@storybook/react' | ||
|
||
import { Card } from './Card' | ||
|
||
// Hack to allow us to specify args that do not exist on Card | ||
type CardStory = React.ComponentProps<typeof Card> & { | ||
titleSize?: 'lg' | 'base' | ||
} | ||
|
||
const meta: Meta<CardStory> = { | ||
title: 'Components/Card', | ||
component: Card, | ||
argTypes: { | ||
titleSize: { | ||
description: 'Controls the font size of Card.Title', | ||
control: 'radio', | ||
options: ['lg', 'base'], | ||
}, | ||
}, | ||
} as Meta | ||
export default meta | ||
|
||
type Story = StoryObj<CardStory> | ||
|
||
export const Default: Story = { | ||
render: () => ( | ||
<Card> | ||
<Card.Content> | ||
Here is the Card component, you are free to render anything you would | ||
like here. | ||
</Card.Content> | ||
</Card> | ||
), | ||
} | ||
|
||
export const CardWithHeader: Story = { | ||
args: { | ||
titleSize: 'lg', | ||
}, | ||
render: (args) => ( | ||
<Card> | ||
<Card.Header> | ||
<Card.Title size={args.titleSize}> | ||
A header can have a title. | ||
</Card.Title> | ||
<Card.Description>And it can have a description.</Card.Description> | ||
</Card.Header> | ||
<Card.Content> | ||
The header will place a border between it and the main Card content. | ||
</Card.Content> | ||
</Card> | ||
), | ||
} | ||
|
||
export const CardWithFooter: Story = { | ||
render: () => ( | ||
<Card> | ||
<Card.Header> | ||
<Card.Title>Card With Footer</Card.Title> | ||
</Card.Header> | ||
<Card.Content> | ||
A footer will similarly have a border between it and the main Card | ||
content. | ||
</Card.Content> | ||
<Card.Footer>Footer!</Card.Footer> | ||
</Card> | ||
), | ||
} | ||
|
||
export const CardWithCustomStyles: Story = { | ||
render: () => ( | ||
<Card className="border-4 border-ds-pink"> | ||
<Card.Header className="border-b-4 border-inherit"> | ||
<Card.Title className="text-ds-blue">Custom Styles</Card.Title> | ||
</Card.Header> | ||
<Card.Content className="flex gap-5"> | ||
<Card className="flex-1"> | ||
<Card.Content className="text-center"> | ||
Using the <code className="bg-ds-gray-secondary">className</code>{' '} | ||
prop, | ||
</Card.Content> | ||
</Card> | ||
<Card className="flex-1"> | ||
<Card.Content className="text-center"> | ||
you can set custom styles! | ||
</Card.Content> | ||
</Card> | ||
</Card.Content> | ||
<Card.Footer className="border-t-4 border-inherit text-center"> | ||
But if you're going to do that, consider adding a{' '} | ||
<a | ||
href="https://cva.style/docs/getting-started/variants" | ||
className="text-ds-blue hover:underline" | ||
> | ||
CVA variant | ||
</a>{' '} | ||
for the component instead. | ||
</Card.Footer> | ||
</Card> | ||
), | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
import { cva, VariantProps } from 'cva' | ||
import React from 'react' | ||
|
||
const card = cva(['border border-ds-gray-secondary']) | ||
interface CardProps | ||
extends React.HTMLAttributes<HTMLDivElement>, | ||
VariantProps<typeof card> {} | ||
|
||
const CardRoot = React.forwardRef<HTMLDivElement, CardProps>( | ||
({ className, ...props }, ref) => ( | ||
<div ref={ref} className={card({ className })} {...props} /> | ||
) | ||
) | ||
CardRoot.displayName = 'Card' | ||
|
||
const header = cva(['border-b', 'border-ds-gray-secondary', 'p-5']) | ||
interface HeaderProps | ||
extends React.HTMLAttributes<HTMLDivElement>, | ||
VariantProps<typeof header> {} | ||
|
||
const Header = React.forwardRef<HTMLDivElement, HeaderProps>( | ||
({ className, ...props }, ref) => ( | ||
<div ref={ref} className={header({ className })} {...props} /> | ||
) | ||
) | ||
Header.displayName = 'Card.Header' | ||
|
||
const title = cva(['font-semibold'], { | ||
variants: { | ||
size: { | ||
base: ['text-base'], | ||
lg: ['text-lg'], | ||
}, | ||
}, | ||
defaultVariants: { | ||
size: 'lg', | ||
}, | ||
}) | ||
interface TitleProps | ||
extends React.HTMLAttributes<HTMLHeadingElement>, | ||
VariantProps<typeof title> {} | ||
|
||
const Title = React.forwardRef<HTMLParagraphElement, TitleProps>( | ||
({ className, size, children, ...props }, ref) => ( | ||
<h3 ref={ref} className={title({ className, size })} {...props}> | ||
{children} | ||
</h3> | ||
) | ||
) | ||
Title.displayName = 'Card.Title' | ||
|
||
const description = cva() | ||
interface DescriptionProps | ||
extends React.HTMLAttributes<HTMLParagraphElement>, | ||
VariantProps<typeof description> {} | ||
|
||
const Description = React.forwardRef<HTMLParagraphElement, DescriptionProps>( | ||
({ className, ...props }, ref) => ( | ||
<p ref={ref} className={description({ className })} {...props} /> | ||
) | ||
) | ||
Description.displayName = 'Card.Description' | ||
|
||
const content = cva(['m-5']) | ||
interface ContentProps | ||
extends React.HTMLAttributes<HTMLDivElement>, | ||
VariantProps<typeof content> {} | ||
|
||
const Content = React.forwardRef<HTMLDivElement, ContentProps>( | ||
({ className, ...props }, ref) => ( | ||
<div ref={ref} className={content({ className })} {...props} /> | ||
) | ||
) | ||
Content.displayName = 'Card.Content' | ||
|
||
const footer = cva(['border-t', 'border-ds-gray-secondary', 'p-5']) | ||
interface FooterProps | ||
extends React.HTMLAttributes<HTMLDivElement>, | ||
VariantProps<typeof footer> {} | ||
|
||
const Footer = React.forwardRef<HTMLDivElement, FooterProps>( | ||
({ className, ...props }, ref) => ( | ||
<div ref={ref} className={footer({ className })} {...props} /> | ||
) | ||
) | ||
Footer.displayName = 'Card.Footer' | ||
|
||
export const Card = Object.assign(CardRoot, { | ||
Header, | ||
Title, | ||
Description, | ||
Content, | ||
Footer, | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { Card } from './Card' |