From 84f82374407fe6ce5c2b63fb371bf7561d1205d2 Mon Sep 17 00:00:00 2001 From: FELIPE BELGINE Date: Wed, 16 Oct 2024 16:41:13 -0400 Subject: [PATCH 1/8] feat: initial commit --- .../src/molecules/PromptPreview/index.tsx | 122 ++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 opentrons-ai-client/src/molecules/PromptPreview/index.tsx diff --git a/opentrons-ai-client/src/molecules/PromptPreview/index.tsx b/opentrons-ai-client/src/molecules/PromptPreview/index.tsx new file mode 100644 index 00000000000..c8919dc108b --- /dev/null +++ b/opentrons-ai-client/src/molecules/PromptPreview/index.tsx @@ -0,0 +1,122 @@ +import styled from 'styled-components' +import { + Flex, + StyledText, + LargeButton, + COLORS, + Tag, +} from '@opentrons/components' + +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.' +const SECTION_TITLES = [ + 'Application', + 'Instruments', + 'Modules', + 'Labware and Liquids', + 'Steps', +] + +interface AccordionProps { + id?: string + isSubmitButtonEnabled?: boolean + handleSubmit: () => void + promptPreviewData: PromptPreviewData +} + +interface PromptPreviewData { + application: string[] + instruments: string[] + modules: string[] + labwareAndLiquids: string[] + steps: string[] +} + +const PromptPreviewContainer = styled(Flex)` + flex-direction: column; + width: 516px; + height: auto; + padding-top: 8px; + background-color: transparent; +` + +const PromptPreviewHeading = styled(Flex)` + flex-direction: row; + justify-content: space-between; + align-items: center; + margin-bottom: 16px; +` + +const PromptPreviewPlaceholderMessage = styled(StyledText)` + padding: 82px 73px; + color: ${COLORS.grey60}; +` + +const PromptPreviewSection = styled(Flex)` + flex-direction: column; + margin-top: 32px; +` + +const SectionHeading = styled(StyledText)` + margin-bottom: 8px; +` + +const TagGrid = styled(Flex)` + grid-gap: 4px; + flex-wrap: wrap; + color: ${COLORS.grey60}; +` + +export function PromptPreview({ + id, + isSubmitButtonEnabled = false, + handleSubmit, + promptPreviewData, +}: AccordionProps): JSX.Element { + const areAllSectionsEmpty = (): boolean => { + const { + application, + instruments, + modules, + labwareAndLiquids, + steps, + } = promptPreviewData + return [application, instruments, modules, labwareAndLiquids, steps].every( + arr => arr.length === 0 + ) + } + + return ( + + + Prompt + + + {areAllSectionsEmpty() && ( + + {PROMPT_PREVIEW_PLACEHOLDER_MESSAGE} + + )} + + {Object.values(promptPreviewData).map( + (section, index) => + section.length > 0 && ( + + + {SECTION_TITLES[index]} + + + {section.map((item: string, index: number) => ( + + ))} + + + ) + )} + + ) +} From d8941e12b42dae759ebeca81bf0c9db969996739 Mon Sep 17 00:00:00 2001 From: FELIPE BELGINE Date: Wed, 16 Oct 2024 16:50:08 -0400 Subject: [PATCH 2/8] refactor: simplify data structure --- .../src/molecules/PromptPreview/index.tsx | 39 +++++-------------- 1 file changed, 10 insertions(+), 29 deletions(-) diff --git a/opentrons-ai-client/src/molecules/PromptPreview/index.tsx b/opentrons-ai-client/src/molecules/PromptPreview/index.tsx index c8919dc108b..09cb25a8089 100644 --- a/opentrons-ai-client/src/molecules/PromptPreview/index.tsx +++ b/opentrons-ai-client/src/molecules/PromptPreview/index.tsx @@ -9,27 +9,17 @@ import { 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.' -const SECTION_TITLES = [ - 'Application', - 'Instruments', - 'Modules', - 'Labware and Liquids', - 'Steps', -] + +interface SectionData { + title: string + items: string[] +} interface AccordionProps { id?: string isSubmitButtonEnabled?: boolean handleSubmit: () => void - promptPreviewData: PromptPreviewData -} - -interface PromptPreviewData { - application: string[] - instruments: string[] - modules: string[] - labwareAndLiquids: string[] - steps: string[] + promptPreviewData: SectionData[] } const PromptPreviewContainer = styled(Flex)` @@ -74,16 +64,7 @@ export function PromptPreview({ promptPreviewData, }: AccordionProps): JSX.Element { const areAllSectionsEmpty = (): boolean => { - const { - application, - instruments, - modules, - labwareAndLiquids, - steps, - } = promptPreviewData - return [application, instruments, modules, labwareAndLiquids, steps].every( - arr => arr.length === 0 - ) + return promptPreviewData.every(section => section.items.length === 0) } return ( @@ -104,13 +85,13 @@ export function PromptPreview({ {Object.values(promptPreviewData).map( (section, index) => - section.length > 0 && ( + section.items.length > 0 && ( - {SECTION_TITLES[index]} + {section.title} - {section.map((item: string, index: number) => ( + {section.items.map((item: string, index: number) => ( ))} From 955c629bcee41fbd3a2468d14e2943fdce135e72 Mon Sep 17 00:00:00 2001 From: FELIPE BELGINE Date: Thu, 17 Oct 2024 08:19:26 -0400 Subject: [PATCH 3/8] refactor: split PromptPreviewSection --- .../src/molecules/PromptPreview/index.tsx | 48 +++---------------- .../molecules/PromptPreviewSection/index.tsx | 38 +++++++++++++++ 2 files changed, 45 insertions(+), 41 deletions(-) create mode 100644 opentrons-ai-client/src/molecules/PromptPreviewSection/index.tsx diff --git a/opentrons-ai-client/src/molecules/PromptPreview/index.tsx b/opentrons-ai-client/src/molecules/PromptPreview/index.tsx index 09cb25a8089..5e4a9ec1b36 100644 --- a/opentrons-ai-client/src/molecules/PromptPreview/index.tsx +++ b/opentrons-ai-client/src/molecules/PromptPreview/index.tsx @@ -1,30 +1,20 @@ import styled from 'styled-components' -import { - Flex, - StyledText, - LargeButton, - COLORS, - Tag, -} from '@opentrons/components' +import { Flex, StyledText, LargeButton, COLORS } from '@opentrons/components' +import { PromptPreviewSection, type PromptPreviewSectionProps } from '../PromptPreviewSection' 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 SectionData { - title: string - items: string[] -} - -interface AccordionProps { +interface PromptPreviewProps { id?: string isSubmitButtonEnabled?: boolean handleSubmit: () => void - promptPreviewData: SectionData[] + promptPreviewData: PromptPreviewSectionProps[] } const PromptPreviewContainer = styled(Flex)` flex-direction: column; - width: 516px; + width: 100%; height: auto; padding-top: 8px; background-color: transparent; @@ -42,27 +32,12 @@ const PromptPreviewPlaceholderMessage = styled(StyledText)` color: ${COLORS.grey60}; ` -const PromptPreviewSection = styled(Flex)` - flex-direction: column; - margin-top: 32px; -` - -const SectionHeading = styled(StyledText)` - margin-bottom: 8px; -` - -const TagGrid = styled(Flex)` - grid-gap: 4px; - flex-wrap: wrap; - color: ${COLORS.grey60}; -` - export function PromptPreview({ id, isSubmitButtonEnabled = false, handleSubmit, promptPreviewData, -}: AccordionProps): JSX.Element { +}: PromptPreviewProps): JSX.Element { const areAllSectionsEmpty = (): boolean => { return promptPreviewData.every(section => section.items.length === 0) } @@ -86,16 +61,7 @@ export function PromptPreview({ {Object.values(promptPreviewData).map( (section, index) => section.items.length > 0 && ( - - - {section.title} - - - {section.items.map((item: string, index: number) => ( - - ))} - - + ) )} diff --git a/opentrons-ai-client/src/molecules/PromptPreviewSection/index.tsx b/opentrons-ai-client/src/molecules/PromptPreviewSection/index.tsx new file mode 100644 index 00000000000..ef0152b535f --- /dev/null +++ b/opentrons-ai-client/src/molecules/PromptPreviewSection/index.tsx @@ -0,0 +1,38 @@ +import styled from 'styled-components' +import { Flex, StyledText, COLORS, Tag } from '@opentrons/components' + +export interface PromptPreviewSectionProps { + title: string + items: string[] +} + +const PromptPreviewSectionContainer = styled(Flex)` + flex-direction: column; + margin-top: 32px; +` + +const SectionHeading = styled(StyledText)` + margin-bottom: 8px; +` + +const TagGrid = styled(Flex)` + grid-gap: 4px; + flex-wrap: wrap; + color: ${COLORS.grey60}; +` + +export function PromptPreviewSection({ + title, + items, +}: PromptPreviewSectionProps): JSX.Element { + return ( + + {title} + + {items.map((item: string, index: number) => ( + + ))} + + + ) +} From 21941e775a2633fa5352f1242f0d4337e1b7c7c5 Mon Sep 17 00:00:00 2001 From: FELIPE BELGINE Date: Thu, 17 Oct 2024 09:09:52 -0400 Subject: [PATCH 4/8] feat: add storybook and remove id --- .../PromptPreview/PromptPreview.stories.tsx | 65 +++++++++++++++++++ .../src/molecules/PromptPreview/index.tsx | 5 +- 2 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 opentrons-ai-client/src/molecules/PromptPreview/PromptPreview.stories.tsx diff --git a/opentrons-ai-client/src/molecules/PromptPreview/PromptPreview.stories.tsx b/opentrons-ai-client/src/molecules/PromptPreview/PromptPreview.stories.tsx new file mode 100644 index 00000000000..434016b8a27 --- /dev/null +++ b/opentrons-ai-client/src/molecules/PromptPreview/PromptPreview.stories.tsx @@ -0,0 +1,65 @@ +import { I18nextProvider } from 'react-i18next' +import { COLORS, Flex, SPACING } from '@opentrons/components' +import { i18n } from '../../i18n' +import type { Meta, StoryObj } from '@storybook/react' +import { PromptPreview } from '.' + +const meta: Meta = { + title: 'AI/molecules/PromptPreview', + component: PromptPreview, + decorators: [ + Story => ( + + + + + + ), + ], +} +export default meta +type Story = StoryObj + +export const PromptPreviewExample: Story = { + args: { + isSubmitButtonEnabled: false, + promptPreviewData: [ + { + title: 'Application', + items: [ + 'Cherrypicking', + 'I have a Chlorine Reagent Set (Total), Ultra Low Range...', + ], + }, + { + title: 'Instruments', + items: [ + 'Opentrons Flex', + 'Flex 1-Channel 50 uL', + 'Flex 8-Channel 1000 uL', + ], + }, + { + title: 'Modules', + items: [ + 'Thermocycler GEN2', + 'Heater-Shaker with Universal Flat Adaptor', + ], + }, + { + title: 'Labware and Liquids', + items: [ + 'Opentrons 96 Well Plate', + 'Thermocycler GEN2', + 'Opentrons 96 Deep Well Plate', + 'Liquid 1: These are my samples, the...', + 'Liquid 2: This is the reagent in my ...', + ], + }, + { + title: 'Steps', + items: ['Fill the first column of a Elisa...'], + }, + ], + }, +} diff --git a/opentrons-ai-client/src/molecules/PromptPreview/index.tsx b/opentrons-ai-client/src/molecules/PromptPreview/index.tsx index 5e4a9ec1b36..c4ca71858d2 100644 --- a/opentrons-ai-client/src/molecules/PromptPreview/index.tsx +++ b/opentrons-ai-client/src/molecules/PromptPreview/index.tsx @@ -6,7 +6,6 @@ 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 { - id?: string isSubmitButtonEnabled?: boolean handleSubmit: () => void promptPreviewData: PromptPreviewSectionProps[] @@ -33,7 +32,6 @@ const PromptPreviewPlaceholderMessage = styled(StyledText)` ` export function PromptPreview({ - id, isSubmitButtonEnabled = false, handleSubmit, promptPreviewData, @@ -43,7 +41,7 @@ export function PromptPreview({ } return ( - + Prompt + {areAllSectionsEmpty() && ( {PROMPT_PREVIEW_PLACEHOLDER_MESSAGE} From b6a0fa3a54544442b695147453b8bf6a9fda3a32 Mon Sep 17 00:00:00 2001 From: FELIPE BELGINE Date: Thu, 17 Oct 2024 11:01:34 -0400 Subject: [PATCH 5/8] refactor: add tests and update constants --- .../PromptPreview/PromptPreview.stories.tsx | 18 ++- .../__tests__/PromptPreview.test.tsx | 105 ++++++++++++++++++ .../src/molecules/PromptPreview/index.tsx | 45 +++++--- .../__tests__/PromptPreviewSection.test.tsx | 51 +++++++++ .../molecules/PromptPreviewSection/index.tsx | 28 +++-- 5 files changed, 223 insertions(+), 24 deletions(-) create mode 100644 opentrons-ai-client/src/molecules/PromptPreview/__tests__/PromptPreview.test.tsx create mode 100644 opentrons-ai-client/src/molecules/PromptPreviewSection/__tests__/PromptPreviewSection.test.tsx 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 : ( + + ) + )} ) From 54390cf86196ae41c5b59f8b465adf9cef14df2c Mon Sep 17 00:00:00 2001 From: FELIPE BELGINE Date: Fri, 18 Oct 2024 10:27:47 -0400 Subject: [PATCH 6/8] refactor: truncate texts and add elipses, and adjust sb examples --- .../PromptPreview/PromptPreview.stories.tsx | 11 ++++-- .../molecules/PromptPreviewSection/index.tsx | 37 ++++++++++++++----- 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/opentrons-ai-client/src/molecules/PromptPreview/PromptPreview.stories.tsx b/opentrons-ai-client/src/molecules/PromptPreview/PromptPreview.stories.tsx index a900455c4ca..79e7b822dcc 100644 --- a/opentrons-ai-client/src/molecules/PromptPreview/PromptPreview.stories.tsx +++ b/opentrons-ai-client/src/molecules/PromptPreview/PromptPreview.stories.tsx @@ -35,7 +35,7 @@ export const PromptPreviewExample: Story = { title: 'Application', items: [ 'Cherrypicking', - 'I have a Chlorine Reagent Set (Total), Ultra Low Range...', + 'I have a Chlorine Reagent Set (Total), Ultra Low Range', ], }, { @@ -59,13 +59,16 @@ export const PromptPreviewExample: Story = { 'Opentrons 96 Well Plate', 'Thermocycler GEN2', 'Opentrons 96 Deep Well Plate', - 'Liquid 1: These are my samples, the...', - 'Liquid 2: This is the reagent in my ...', + 'Liquid 1: In commodo lectus nec erat commodo blandit. Etiam leo dui, porttitor vel imperdiet sed, tristique nec nisl. Maecenas pulvinar sapien quis sodales imperdiet.', + 'Liquid 2: Lorem ipsum dolor sit amet, consectetur adipiscing elit.', ], }, { title: 'Steps', - items: ['Fill the first column of a Elisa...'], + items: [ + 'Fill the first column of a Elisa plate with 100 uL of Liquid 1', + 'Fill the second column of a Elisa plate with 100 uL of Liquid 2', + ], }, ], }, diff --git a/opentrons-ai-client/src/molecules/PromptPreviewSection/index.tsx b/opentrons-ai-client/src/molecules/PromptPreviewSection/index.tsx index e42d66d5da9..91dfabb75ec 100644 --- a/opentrons-ai-client/src/molecules/PromptPreviewSection/index.tsx +++ b/opentrons-ai-client/src/molecules/PromptPreviewSection/index.tsx @@ -2,7 +2,6 @@ import styled from 'styled-components' import { Flex, StyledText, - COLORS, Tag, DIRECTION_COLUMN, WRAP, @@ -23,10 +22,27 @@ const SectionHeading = styled(StyledText)` margin-bottom: ${SPACING.spacing8}; ` -const TagGrid = styled(Flex)` +const TagsContainer = styled(Flex)` grid-gap: ${SPACING.spacing4}; flex-wrap: ${WRAP}; - color: ${COLORS.grey60}; + justify-content: flex-start; + width: 100%; +` + +const TagItemWrapper = styled(Flex)` + width: auto; + white-space: nowrap; + overflow: hidden; + max-width: 35%; + + & > div { + overflow: hidden; + + > p { + overflow: hidden; + text-overflow: ellipsis; + } + } ` export function PromptPreviewSection({ @@ -36,13 +52,16 @@ export function PromptPreviewSection({ return ( {title} - - {items.map((item: string, index: number) => - item.trim() === '' ? null : ( - - ) + + {items.map( + (item: string, index: number) => + item.trim() !== '' && ( + + + + ) )} - + ) } From 7066170a54e485e7ea5ca2c904d3aa8a604545fc Mon Sep 17 00:00:00 2001 From: Felipe Belgine Date: Mon, 21 Oct 2024 14:07:49 -0400 Subject: [PATCH 7/8] refactor: add item max size for different sections --- .../src/molecules/PromptPreview/index.tsx | 1 + .../__tests__/PromptPreviewSection.test.tsx | 9 +++++++++ .../src/molecules/PromptPreviewSection/index.tsx | 13 ++++++++++--- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/opentrons-ai-client/src/molecules/PromptPreview/index.tsx b/opentrons-ai-client/src/molecules/PromptPreview/index.tsx index 94a7a30929d..6cc8e37320e 100644 --- a/opentrons-ai-client/src/molecules/PromptPreview/index.tsx +++ b/opentrons-ai-client/src/molecules/PromptPreview/index.tsx @@ -77,6 +77,7 @@ export function PromptPreview({ key={`section-${index}`} title={section.title} items={section.items} + itemMaxWidth={index <= 2 ? '33.33%' : '100%'} /> ) )} diff --git a/opentrons-ai-client/src/molecules/PromptPreviewSection/__tests__/PromptPreviewSection.test.tsx b/opentrons-ai-client/src/molecules/PromptPreviewSection/__tests__/PromptPreviewSection.test.tsx index 96baaae6528..e194bae5a8e 100644 --- a/opentrons-ai-client/src/molecules/PromptPreviewSection/__tests__/PromptPreviewSection.test.tsx +++ b/opentrons-ai-client/src/molecules/PromptPreviewSection/__tests__/PromptPreviewSection.test.tsx @@ -48,4 +48,13 @@ describe('PromptPreviewSection', () => { const items = screen.getAllByTestId('Tag_default') expect(items).toHaveLength(1) }) + + it('should render the item with the correct max item width', () => { + props.items = ['test item 1 long text long text long text long text'] + props.itemMaxWidth = '23%' + render(props) + + const item = screen.getByTestId('item-tag-wrapper-0') + expect(item).toHaveStyle({ maxWidth: '23%' }) + }) }) diff --git a/opentrons-ai-client/src/molecules/PromptPreviewSection/index.tsx b/opentrons-ai-client/src/molecules/PromptPreviewSection/index.tsx index 91dfabb75ec..c781e0308d7 100644 --- a/opentrons-ai-client/src/molecules/PromptPreviewSection/index.tsx +++ b/opentrons-ai-client/src/molecules/PromptPreviewSection/index.tsx @@ -11,6 +11,7 @@ import { export interface PromptPreviewSectionProps { title: string items: string[] + itemMaxWidth?: string } const PromptPreviewSectionContainer = styled(Flex)` @@ -29,11 +30,12 @@ const TagsContainer = styled(Flex)` width: 100%; ` -const TagItemWrapper = styled(Flex)` +const TagItemWrapper = styled.div<{ itemMaxWidth: string }>` + display: flex; width: auto; white-space: nowrap; overflow: hidden; - max-width: 35%; + max-width: ${props => props.itemMaxWidth}; & > div { overflow: hidden; @@ -48,6 +50,7 @@ const TagItemWrapper = styled(Flex)` export function PromptPreviewSection({ title, items, + itemMaxWidth = '35%', }: PromptPreviewSectionProps): JSX.Element { return ( @@ -56,7 +59,11 @@ export function PromptPreviewSection({ {items.map( (item: string, index: number) => item.trim() !== '' && ( - + ) From c13515ceeeaf1cc100ad6b8f564f08168295b5a6 Mon Sep 17 00:00:00 2001 From: Felipe Belgine Date: Mon, 21 Oct 2024 14:14:11 -0400 Subject: [PATCH 8/8] refactor: remove hardcoded test --- .../src/assets/localization/en/protocol_generator.json | 4 +++- .../PromptPreview/__tests__/PromptPreview.test.tsx | 6 +++++- .../src/molecules/PromptPreview/index.tsx | 10 +++++----- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/opentrons-ai-client/src/assets/localization/en/protocol_generator.json b/opentrons-ai-client/src/assets/localization/en/protocol_generator.json index f44eff34e73..cc5c727c1e8 100644 --- a/opentrons-ai-client/src/assets/localization/en/protocol_generator.json +++ b/opentrons-ai-client/src/assets/localization/en/protocol_generator.json @@ -34,5 +34,7 @@ "well_allocations": "Well allocations: Describe where liquids should go in labware.", "what_if_you": "What if you don’t provide all of those pieces of information? OpentronsAI asks you to provide it!", "what_typeof_protocol": "What type of protocol do you need?", - "you": "You" + "you": "You", + "prompt_preview_submit_button": "Submit prompt", + "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." } diff --git a/opentrons-ai-client/src/molecules/PromptPreview/__tests__/PromptPreview.test.tsx b/opentrons-ai-client/src/molecules/PromptPreview/__tests__/PromptPreview.test.tsx index 8adb5bd86f8..ab7d69543ba 100644 --- a/opentrons-ai-client/src/molecules/PromptPreview/__tests__/PromptPreview.test.tsx +++ b/opentrons-ai-client/src/molecules/PromptPreview/__tests__/PromptPreview.test.tsx @@ -2,9 +2,13 @@ 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 '..' +import { PromptPreview } from '..' + +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.' const mockHandleClick = vi.fn() + const render = (props: React.ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, diff --git a/opentrons-ai-client/src/molecules/PromptPreview/index.tsx b/opentrons-ai-client/src/molecules/PromptPreview/index.tsx index 6cc8e37320e..b789cfbb4c7 100644 --- a/opentrons-ai-client/src/molecules/PromptPreview/index.tsx +++ b/opentrons-ai-client/src/molecules/PromptPreview/index.tsx @@ -13,9 +13,7 @@ import { } from '@opentrons/components' import { PromptPreviewSection } from '../PromptPreviewSection' import type { PromptPreviewSectionProps } from '../PromptPreviewSection' - -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.' +import { useTranslation } from 'react-i18next' interface PromptPreviewProps { isSubmitButtonEnabled?: boolean @@ -49,6 +47,8 @@ export function PromptPreview({ handleSubmit, promptPreviewData = [], }: PromptPreviewProps): JSX.Element { + const { t } = useTranslation('protocol_generator') + const areAllSectionsEmpty = (): boolean => { return promptPreviewData.every(section => section.items.length === 0) } @@ -58,7 +58,7 @@ export function PromptPreview({ Prompt @@ -66,7 +66,7 @@ export function PromptPreview({ {areAllSectionsEmpty() && ( - {PROMPT_PREVIEW_PLACEHOLDER_MESSAGE} + {t('prompt_preview_placeholder_message')} )}