diff --git a/opentrons-ai-client/src/molecules/PromptPreview/PromptPreview.stories.tsx b/opentrons-ai-client/src/molecules/PromptPreview/PromptPreview.stories.tsx index 434016b8a27..a900455c4ca 100644 --- a/opentrons-ai-client/src/molecules/PromptPreview/PromptPreview.stories.tsx +++ b/opentrons-ai-client/src/molecules/PromptPreview/PromptPreview.stories.tsx @@ -10,7 +10,11 @@ const meta: Meta = { decorators: [ Story => ( - + @@ -23,6 +27,9 @@ type Story = StoryObj export const PromptPreviewExample: Story = { args: { isSubmitButtonEnabled: false, + handleSubmit: () => { + alert('Submit button clicked') + }, promptPreviewData: [ { title: 'Application', @@ -63,3 +70,12 @@ export const PromptPreviewExample: Story = { ], }, } + +export const PromptPreviewPlaceholderMessage: Story = { + args: { + isSubmitButtonEnabled: false, + handleSubmit: () => { + alert('Submit button clicked') + }, + }, +} diff --git a/opentrons-ai-client/src/molecules/PromptPreview/__tests__/PromptPreview.test.tsx b/opentrons-ai-client/src/molecules/PromptPreview/__tests__/PromptPreview.test.tsx new file mode 100644 index 00000000000..8adb5bd86f8 --- /dev/null +++ b/opentrons-ai-client/src/molecules/PromptPreview/__tests__/PromptPreview.test.tsx @@ -0,0 +1,105 @@ +import { screen } from '@testing-library/react' +import { describe, it, vi, beforeEach, expect } from 'vitest' +import { renderWithProviders } from '../../../__testing-utils__' +import { i18n } from '../../../i18n' +import { PROMPT_PREVIEW_PLACEHOLDER_MESSAGE, PromptPreview } from '..' + +const mockHandleClick = vi.fn() +const render = (props: React.ComponentProps) => { + return renderWithProviders(, { + i18nInstance: i18n, + }) +} + +describe('PromptPreview', () => { + let props: React.ComponentProps + + beforeEach(() => { + props = { + isSubmitButtonEnabled: false, + handleSubmit: () => { + mockHandleClick() + }, + promptPreviewData: [ + { + title: 'Test Section 1', + items: ['item1', 'item2'], + }, + { + title: 'Test Section 2', + items: ['item3', 'item4'], + }, + ], + } + }) + + it('should render the PromptPreview component', () => { + render(props) + + expect(screen.getByText('Prompt')).toBeInTheDocument() + }) + + it('should render the submit button', () => { + render(props) + + expect(screen.getByText('Submit prompt')).toBeInTheDocument() + }) + + it('should render the placeholder message when all sections are empty', () => { + props.promptPreviewData = [ + { + title: 'Test Section 1', + items: [], + }, + { + title: 'Test Section 2', + items: [], + }, + ] + render(props) + + expect( + screen.getByText(PROMPT_PREVIEW_PLACEHOLDER_MESSAGE) + ).toBeInTheDocument() + }) + + it('should not render the placeholder message when at least one section has items', () => { + render(props) + + expect( + screen.queryByText(PROMPT_PREVIEW_PLACEHOLDER_MESSAGE) + ).not.toBeInTheDocument() + }) + + it('should render the sections with items', () => { + render(props) + + expect(screen.getByText('Test Section 1')).toBeInTheDocument() + expect(screen.getByText('Test Section 2')).toBeInTheDocument() + }) + + it('should display submit button disabled when isSubmitButtonEnabled is false', () => { + render(props) + + expect(screen.getByRole('button', { name: 'Submit prompt' })).toBeDisabled() + }) + + it('should display submit button enabled when isSubmitButtonEnabled is true', () => { + props.isSubmitButtonEnabled = true + render(props) + + expect( + screen.getByRole('button', { name: 'Submit prompt' }) + ).not.toBeDisabled() + }) + + it('should call handleSubmit when the submit button is clicked', () => { + props.isSubmitButtonEnabled = true + render(props) + + const submitButton = screen.getByRole('button', { name: 'Submit prompt' }) + submitButton.click() + + expect(mockHandleClick).toHaveBeenCalled() + }) +}) diff --git a/opentrons-ai-client/src/molecules/PromptPreview/index.tsx b/opentrons-ai-client/src/molecules/PromptPreview/index.tsx index c4ca71858d2..94a7a30929d 100644 --- a/opentrons-ai-client/src/molecules/PromptPreview/index.tsx +++ b/opentrons-ai-client/src/molecules/PromptPreview/index.tsx @@ -1,8 +1,20 @@ import styled from 'styled-components' -import { Flex, StyledText, LargeButton, COLORS } from '@opentrons/components' -import { PromptPreviewSection, type PromptPreviewSectionProps } from '../PromptPreviewSection' +import { + Flex, + StyledText, + LargeButton, + COLORS, + JUSTIFY_SPACE_BETWEEN, + DIRECTION_COLUMN, + SIZE_AUTO, + DIRECTION_ROW, + ALIGN_CENTER, + SPACING, +} from '@opentrons/components' +import { PromptPreviewSection } from '../PromptPreviewSection' +import type { PromptPreviewSectionProps } from '../PromptPreviewSection' -const PROMPT_PREVIEW_PLACEHOLDER_MESSAGE = +export const PROMPT_PREVIEW_PLACEHOLDER_MESSAGE = 'As you complete the sections on the left, your prompt will be built here. When all requirements are met you will be able to generate the protocol.' interface PromptPreviewProps { @@ -12,29 +24,30 @@ interface PromptPreviewProps { } const PromptPreviewContainer = styled(Flex)` - flex-direction: column; + flex-direction: ${DIRECTION_COLUMN}; width: 100%; - height: auto; - padding-top: 8px; - background-color: transparent; + height: ${SIZE_AUTO}; + padding-top: ${SPACING.spacing8}; + background-color: ${COLORS.transparent}; ` const PromptPreviewHeading = styled(Flex)` - flex-direction: row; - justify-content: space-between; - align-items: center; - margin-bottom: 16px; + flex-direction: ${DIRECTION_ROW}; + justify-content: ${JUSTIFY_SPACE_BETWEEN}; + align-items: ${ALIGN_CENTER}; + margin-bottom: ${SPACING.spacing16}; ` const PromptPreviewPlaceholderMessage = styled(StyledText)` padding: 82px 73px; color: ${COLORS.grey60}; + text-align: ${ALIGN_CENTER}; ` export function PromptPreview({ isSubmitButtonEnabled = false, handleSubmit, - promptPreviewData, + promptPreviewData = [], }: PromptPreviewProps): JSX.Element { const areAllSectionsEmpty = (): boolean => { return promptPreviewData.every(section => section.items.length === 0) @@ -50,7 +63,7 @@ export function PromptPreview({ onClick={handleSubmit} /> - + {areAllSectionsEmpty() && ( {PROMPT_PREVIEW_PLACEHOLDER_MESSAGE} @@ -60,7 +73,11 @@ export function PromptPreview({ {Object.values(promptPreviewData).map( (section, index) => section.items.length > 0 && ( - + ) )} diff --git a/opentrons-ai-client/src/molecules/PromptPreviewSection/__tests__/PromptPreviewSection.test.tsx b/opentrons-ai-client/src/molecules/PromptPreviewSection/__tests__/PromptPreviewSection.test.tsx new file mode 100644 index 00000000000..96baaae6528 --- /dev/null +++ b/opentrons-ai-client/src/molecules/PromptPreviewSection/__tests__/PromptPreviewSection.test.tsx @@ -0,0 +1,51 @@ +import type * as React from 'react' +import { screen } from '@testing-library/react' +import { describe, it, beforeEach, expect } from 'vitest' +import { renderWithProviders } from '../../../__testing-utils__' +import { i18n } from '../../../i18n' + +import { PromptPreviewSection } from '../index' + +const render = (props: React.ComponentProps) => { + return renderWithProviders(, { + i18nInstance: i18n, + }) +} + +describe('PromptPreviewSection', () => { + let props: React.ComponentProps + + beforeEach(() => { + props = { + title: 'Test Section', + items: ['test item 1', 'test item 2'], + } + }) + + it('should render the PromptPreviewSection component', () => { + render(props) + + expect(screen.getByText('Test Section')).toBeInTheDocument() + }) + + it('should render the section title', () => { + render(props) + + expect(screen.getByText('Test Section')).toBeInTheDocument() + }) + + it('should render the items', () => { + render(props) + + expect(screen.getByText('test item 1')).toBeInTheDocument() + expect(screen.getByText('test item 2')).toBeInTheDocument() + }) + + it("should not render the item tag if it's an empty string", () => { + props.items = ['test item 1', ''] + render(props) + + const items = screen.getAllByTestId('Tag_default') + expect(items).toHaveLength(1) + }) +}) diff --git a/opentrons-ai-client/src/molecules/PromptPreviewSection/index.tsx b/opentrons-ai-client/src/molecules/PromptPreviewSection/index.tsx index ef0152b535f..e42d66d5da9 100644 --- a/opentrons-ai-client/src/molecules/PromptPreviewSection/index.tsx +++ b/opentrons-ai-client/src/molecules/PromptPreviewSection/index.tsx @@ -1,5 +1,13 @@ import styled from 'styled-components' -import { Flex, StyledText, COLORS, Tag } from '@opentrons/components' +import { + Flex, + StyledText, + COLORS, + Tag, + DIRECTION_COLUMN, + WRAP, + SPACING, +} from '@opentrons/components' export interface PromptPreviewSectionProps { title: string @@ -7,17 +15,17 @@ export interface PromptPreviewSectionProps { } const PromptPreviewSectionContainer = styled(Flex)` - flex-direction: column; - margin-top: 32px; + flex-direction: ${DIRECTION_COLUMN}; + margin-top: ${SPACING.spacing32}; ` const SectionHeading = styled(StyledText)` - margin-bottom: 8px; + margin-bottom: ${SPACING.spacing8}; ` const TagGrid = styled(Flex)` - grid-gap: 4px; - flex-wrap: wrap; + grid-gap: ${SPACING.spacing4}; + flex-wrap: ${WRAP}; color: ${COLORS.grey60}; ` @@ -29,9 +37,11 @@ export function PromptPreviewSection({ {title} - {items.map((item: string, index: number) => ( - - ))} + {items.map((item: string, index: number) => + item.trim() === '' ? null : ( + + ) + )} )