This repository was archived by the owner on Aug 5, 2025. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 108
feat(react-chat): add DashboardCard component #532
Closed
Closed
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
569b624
feat(react-chat): add DashboardCard component
devin-ai-integration[bot] 4351adc
test(react-chat): add comprehensive tests for DashboardCard component
devin-ai-integration[bot] 75dcd2f
refactor(react-chat): rename DashboardCard to AgentCard
devin-ai-integration[bot] File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
34 changes: 34 additions & 0 deletions
34
packages/react-chat/src/components/AgentCard/AgentCard.story.tsx
This file contains hidden or 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,34 @@ | ||
| import type { Meta, StoryObj } from '@storybook/react'; | ||
|
|
||
| import AgentCard from '.'; | ||
|
|
||
| const meta: Meta<typeof AgentCard> = { | ||
| title: 'Components/AgentCard', | ||
| component: AgentCard, | ||
| tags: ['autodocs'], | ||
| } satisfies Meta<typeof AgentCard>; | ||
|
|
||
| export default meta; | ||
| type Story = StoryObj<typeof meta>; | ||
|
|
||
| export const Simple: Story = { | ||
| args: { | ||
| children: <div style={{ padding: '16px' }}>Sample content inside the AgentCard</div>, | ||
| }, | ||
| }; | ||
|
|
||
| export const WithComplexContent: Story = { | ||
| args: { | ||
| children: ( | ||
| <div style={{ padding: '16px' }}> | ||
| <h3 style={{ margin: '0 0 8px 0' }}>Dashboard Title</h3> | ||
| <p style={{ margin: 0 }}>This is a sample dashboard card with multiple elements inside.</p> | ||
| <button style={{ marginTop: '12px' }}>Action Button</button> | ||
| </div> | ||
| ), | ||
| }, | ||
| }; | ||
|
|
||
| /** | ||
| * @see {@link https://voiceflow.github.io/react-chat/?path=/story/components-dashboard-card--simple} | ||
| */ | ||
108 changes: 108 additions & 0 deletions
108
packages/react-chat/src/components/AgentCard/__tests__/index.test.tsx
This file contains hidden or 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,108 @@ | ||
| import * as React from 'react'; | ||
| import { render, screen } from '@testing-library/react'; | ||
| import userEvent from '@testing-library/user-event'; | ||
| import { describe, expect, it } from 'vitest'; | ||
| import '@testing-library/jest-dom'; | ||
|
|
||
| import { ClassName } from '../../../constants'; | ||
| import AgentCard from '..'; | ||
|
|
||
| describe('AgentCard', () => { | ||
| const testContent = 'Test Content'; | ||
|
|
||
| it('renders children correctly', () => { | ||
| render(<AgentCard>{testContent}</AgentCard>); | ||
| expect(screen.getByText(testContent)).toBeInTheDocument(); | ||
| }); | ||
|
|
||
| it('passes through additional props', () => { | ||
| const testId = 'test-agent-card'; | ||
| const ariaLabel = 'Agent Card'; | ||
| render(<AgentCard data-testid={testId} aria-label={ariaLabel}>{testContent}</AgentCard>); | ||
|
|
||
| const card = screen.getByTestId(testId); | ||
| expect(card).toHaveAttribute('aria-label', ariaLabel); | ||
| }); | ||
|
|
||
| it('applies custom className', () => { | ||
| const customClass = 'custom-class'; | ||
| const { container } = render(<AgentCard className={customClass}>{testContent}</AgentCard>); | ||
| expect(container.firstChild).toHaveClass(customClass); | ||
| expect(container.firstChild).toHaveClass(ClassName.AGENT_CARD); | ||
| }); | ||
|
|
||
| it('maintains correct styling', () => { | ||
| const { container } = render(<AgentCard>{testContent}</AgentCard>); | ||
| const card = container.firstChild as HTMLElement; | ||
|
|
||
| expect(card).toHaveStyle({ | ||
| borderRadius: '6px', | ||
| display: 'flex', | ||
| flexDirection: 'column', | ||
| boxSizing: 'border-box', | ||
| overflow: 'hidden', | ||
| backgroundColor: 'rgb(255, 255, 255)', // $white | ||
| }); | ||
| }); | ||
|
|
||
| it('applies box shadow correctly', () => { | ||
| const { container } = render(<AgentCard>{testContent}</AgentCard>); | ||
| const card = container.firstChild as HTMLElement; | ||
|
|
||
| const boxShadow = window.getComputedStyle(card).boxShadow; | ||
| expect(boxShadow).toContain('0px 1px 3px 1px'); | ||
| expect(boxShadow).toContain('0px 4px 8px -6px'); | ||
| expect(boxShadow).toContain('0px 1px 5px -4px'); | ||
| expect(boxShadow).toContain('0px 0px 0px 1px'); | ||
| expect(boxShadow).toContain('0px 1px 0px 0px'); | ||
| }); | ||
|
|
||
| it('handles complex nested content', () => { | ||
| const complexContent = ( | ||
| <div data-testid="complex-content"> | ||
| <h3>Title</h3> | ||
| <p>Description</p> | ||
| <button>Action</button> | ||
| </div> | ||
| ); | ||
|
|
||
| render(<AgentCard>{complexContent}</AgentCard>); | ||
| const content = screen.getByTestId('complex-content'); | ||
| expect(content).toBeInTheDocument(); | ||
| expect(screen.getByText('Title')).toBeInTheDocument(); | ||
| expect(screen.getByText('Description')).toBeInTheDocument(); | ||
| expect(screen.getByRole('button')).toBeInTheDocument(); | ||
| }); | ||
|
|
||
| it('handles empty content gracefully', () => { | ||
| const { container } = render(<AgentCard />); | ||
| expect(container.firstChild).toBeInTheDocument(); | ||
| expect(container.firstChild).toHaveStyle({ | ||
| display: 'flex', | ||
| flexDirection: 'column', | ||
| }); | ||
| }); | ||
|
|
||
| it('maintains layout with different content types', () => { | ||
| const { rerender, container } = render(<AgentCard>Short text</AgentCard>); | ||
| expect(container.firstChild).toHaveStyle({ | ||
| display: 'flex', | ||
| flexDirection: 'column', | ||
| }); | ||
|
|
||
| rerender( | ||
| <AgentCard> | ||
| <div style={{ padding: '20px' }}> | ||
| <h1>Large Content</h1> | ||
| <p>With multiple paragraphs</p> | ||
| <p>And more content</p> | ||
| </div> | ||
| </AgentCard> | ||
| ); | ||
|
|
||
| expect(container.firstChild).toHaveStyle({ | ||
| display: 'flex', | ||
| flexDirection: 'column', | ||
| }); | ||
| }); | ||
| }); |
This file contains hidden or 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,21 @@ | ||
| import React from 'react'; | ||
|
|
||
| import { Container } from './styled'; | ||
|
|
||
| export interface AgentCardProps extends React.HTMLAttributes<HTMLDivElement> { | ||
| /** | ||
| * The content to be rendered inside the card. | ||
| */ | ||
| children?: React.ReactNode; | ||
| } | ||
|
|
||
| const AgentCard: React.FC<AgentCardProps> = ({ children, ...props }) => ( | ||
| <Container {...props}>{children}</Container> | ||
| ); | ||
|
|
||
| /** | ||
| * A flexible card component with a rounded rectangle shape and subtle shadow effects. | ||
| */ | ||
| export default Object.assign(AgentCard, { | ||
| Container, | ||
| }); |
This file contains hidden or 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,21 @@ | ||
| import { ClassName } from '@/constants'; | ||
| import { tagFactory } from '@/hocs'; | ||
| import { styled } from '@/styles'; | ||
|
|
||
| const tag = tagFactory(ClassName.AGENT_CARD); | ||
|
|
||
| export const Container = styled(tag('div'), { | ||
| borderRadius: '6px', | ||
| boxShadow: ` | ||
| 0px 1px 3px 1px #161A1E03, | ||
| 0px 4px 8px -6px #161A1E14, | ||
| 0px 1px 5px -4px #161A1E14, | ||
| 0px 0px 0px 1px #161A1E0A, | ||
| 0px 1px 0px 0px #161A1E05 | ||
| `, | ||
| display: 'flex', | ||
| flexDirection: 'column', | ||
| boxSizing: 'border-box', | ||
| overflow: 'hidden', | ||
| backgroundColor: '$white', | ||
| }); |
This file contains hidden or 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
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Storybook documentation link contains an incorrect path - it points to
components-dashboard-cardbut should point tocomponents-agent-cardto match the component name. The correct link should be:Spotted by Graphite Reviewer
Is this helpful? React 👍 or 👎 to let us know.