Skip to content

Commit

Permalink
fix(opentrons-ai-client): fix example buttons and clear text after su…
Browse files Browse the repository at this point in the history
…bmitting prompt (#15299)

closes AUTH-434 and AUTH-437
  • Loading branch information
shlokamin authored May 31, 2024
1 parent fc7b662 commit 1a4c0c7
Show file tree
Hide file tree
Showing 7 changed files with 69 additions and 43 deletions.
17 changes: 14 additions & 3 deletions opentrons-ai-client/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import React from 'react'
import { useAuth0 } from '@auth0/auth0-react'
import { useTranslation } from 'react-i18next'
import { useForm, FormProvider } from 'react-hook-form'
import { useAtom } from 'jotai'

import {
COLORS,
Flex,
Expand All @@ -18,6 +18,10 @@ import { SidePanel } from './molecules/SidePanel'
import { Loading } from './molecules/Loading'
import { ChatContainer } from './organisms/ChatContainer'

export interface InputType {
userPrompt: string
}

export function App(): JSX.Element | null {
const { t } = useTranslation('protocol_generator')
const { isAuthenticated, logout, isLoading, loginWithRedirect } = useAuth0()
Expand All @@ -32,6 +36,11 @@ export function App(): JSX.Element | null {
console.error('Error fetching access token:', error)
}
}
const methods = useForm<InputType>({
defaultValues: {
userPrompt: '',
},
})

React.useEffect(() => {
if (!isAuthenticated && !isLoading) {
Expand Down Expand Up @@ -64,8 +73,10 @@ export function App(): JSX.Element | null {
{t('logout')}
</LinkButton>
</Flex>
<SidePanel />
<ChatContainer />
<FormProvider {...methods}>
<SidePanel />
<ChatContainer />
</FormProvider>
</Flex>
)
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,30 @@
import React from 'react'
import { describe, it, expect } from 'vitest'
import { FormProvider, useForm } from 'react-hook-form'
import { fireEvent, screen } from '@testing-library/react'
import { renderWithProviders } from '../../../__testing-utils__'
import { i18n } from '../../../i18n'
import { InputPrompt } from '../index'

const WrappingForm = (wrappedComponent: {
children: React.ReactNode
}): JSX.Element => {
const methods = useForm({
defaultValues: {
userPrompt: '',
},
})
// eslint-disable-next-line testing-library/no-node-access
return <FormProvider {...methods}>{wrappedComponent.children}</FormProvider>
}

const render = () => {
return renderWithProviders(<InputPrompt />, { i18nInstance: i18n })
return renderWithProviders(
<WrappingForm>
<InputPrompt />
</WrappingForm>,
{ i18nInstance: i18n }
)
}

describe('InputPrompt', () => {
Expand Down
27 changes: 3 additions & 24 deletions opentrons-ai-client/src/molecules/InputPrompt/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react'
import { useTranslation } from 'react-i18next'
import styled, { css } from 'styled-components'
import { useForm } from 'react-hook-form'
import { useFormContext } from 'react-hook-form'
import { useAtom } from 'jotai'

import {
Expand All @@ -15,31 +15,17 @@ import {
TYPOGRAPHY,
} from '@opentrons/components'
import { SendButton } from '../../atoms/SendButton'
import {
chatDataAtom,
chatHistoryAtom,
preparedPromptAtom,
tokenAtom,
} from '../../resources/atoms'
import { chatDataAtom, chatHistoryAtom, tokenAtom } from '../../resources/atoms'
import { useApiCall } from '../../resources/hooks'
import { calcTextAreaHeight } from '../../resources/utils/utils'
import { END_POINT } from '../../resources/constants'

import type { AxiosRequestConfig } from 'axios'
import type { ChatData } from '../../resources/types'

interface InputType {
userPrompt: string
}

export function InputPrompt(): JSX.Element {
const { t } = useTranslation('protocol_generator')
const { register, watch, setValue, reset } = useForm<InputType>({
defaultValues: {
userPrompt: '',
},
})
const [preparedPrompt, setPreparedPrompt] = useAtom(preparedPromptAtom)
const { register, watch, reset } = useFormContext()
const [, setChatData] = useAtom(chatDataAtom)
const [chatHistory, setChatHistory] = useAtom(chatHistoryAtom)
const [token] = useAtom(tokenAtom)
Expand All @@ -53,7 +39,6 @@ export function InputPrompt(): JSX.Element {
reply: userPrompt,
}
reset()
setPreparedPrompt('')
setChatData(chatData => [...chatData, userInput])

try {
Expand Down Expand Up @@ -84,12 +69,6 @@ export function InputPrompt(): JSX.Element {
}
}

React.useEffect(() => {
if (preparedPrompt !== '') {
setValue('userPrompt', preparedPrompt)
}
}, [preparedPrompt, setPreparedPrompt, setValue])

React.useEffect(() => {
if (submitted && data != null && !isLoading) {
const { role, reply } = data
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import React from 'react'
import { screen } from '@testing-library/react'
import { describe, it, expect } from 'vitest'
import { describe, it, expect, vi, beforeEach } from 'vitest'
import { useFormContext } from 'react-hook-form'

import { renderWithProviders } from '../../../__testing-utils__'
import { i18n } from '../../../i18n'

import { SidePanel } from '../index'

vi.mock('react-hook-form')

const LOGO_FILE_NAME =
'/opentrons-ai-client/src/assets/images/opentrons_logo.svg'

Expand All @@ -19,6 +22,12 @@ const render = (): ReturnType<typeof renderWithProviders> => {
}

describe('SidePanel', () => {
beforeEach(() => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
vi.mocked(useFormContext).mockReturnValue({
setValue: vi.fn(),
} as any)
})
it('should render logo and text', () => {
render()
const image = screen.getByRole('img')
Expand Down
Original file line number Diff line number Diff line change
@@ -1,36 +1,49 @@
import React from 'react'
import { useAtom } from 'jotai'
import { fireEvent, screen, renderHook } from '@testing-library/react'
import { describe, it, beforeEach, expect } from 'vitest'
import { fireEvent, screen } from '@testing-library/react'
import { describe, it, beforeEach, expect, vi } from 'vitest'

import { renderWithProviders } from '../../../__testing-utils__'
import { reagentTransfer } from '../../../assets/prompts'
import { preparedPromptAtom } from '../../../resources/atoms'
import { PromptButton } from '../index'

import type * as ReactHookForm from 'react-hook-form'

vi.mock('react-hook-form', async importOriginal => {
const actual = await importOriginal<typeof ReactHookForm>()
return {
...actual,
useFormContext: vi.fn(() => ({
setValue: mockSetValue,
})),
}
})

const render = (props: React.ComponentProps<typeof PromptButton>) => {
return renderWithProviders(<PromptButton {...props} />)
}

let mockSetValue = vi.fn()

describe('PromptButton', () => {
let props: React.ComponentProps<typeof PromptButton>

beforeEach(() => {
props = {
buttonText: 'Reagent Transfer',
}
mockSetValue = vi.fn()
})

it('should render text', () => {
render(props)
screen.getByRole('button', { name: 'Reagent Transfer' })
})

it('should call a mock function when clicking a button', () => {
it('should render reagent transfer text into the form', () => {
render(props)
const button = screen.getByRole('button', { name: 'Reagent Transfer' })
fireEvent.click(button)
const { result } = renderHook(() => useAtom(preparedPromptAtom))
expect(mockSetValue).toHaveBeenCalledWith('userPrompt', reagentTransfer)
fireEvent.click(button)
expect(result.current[0]).toBe(reagentTransfer)
})
})
7 changes: 3 additions & 4 deletions opentrons-ai-client/src/organisms/PromptButton/index.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import React from 'react'
import styled from 'styled-components'
import { useAtom } from 'jotai'
import { BORDERS, PrimaryButton } from '@opentrons/components'
import {
reagentTransfer,
flexReagentTransfer,
pcr,
flexPcr,
} from '../../assets/prompts'
import { preparedPromptAtom } from '../../resources/atoms'
import { useFormContext } from 'react-hook-form'

interface PromptButtonProps {
buttonText: string
Expand All @@ -31,10 +30,10 @@ const PROMPT_BY_NAME: Record<string, { prompt: string }> = {
}

export function PromptButton({ buttonText }: PromptButtonProps): JSX.Element {
const [, setPreparedPrompt] = useAtom(preparedPromptAtom)
const { setValue } = useFormContext()
const handleClick = (): void => {
const { prompt } = PROMPT_BY_NAME[buttonText]
setPreparedPrompt(prompt)
setValue('userPrompt', prompt)
}

return <PromptBtn onClick={handleClick}>{buttonText}</PromptBtn>
Expand Down
3 changes: 0 additions & 3 deletions opentrons-ai-client/src/resources/atoms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@
import { atom } from 'jotai'
import type { Chat, ChatData } from './types'

/** preparedPromptAtom is for PromptButton */
export const preparedPromptAtom = atom<string>('')

/** ChatDataAtom is for chat data (user prompt and response from OpenAI API) */
export const chatDataAtom = atom<ChatData[]>([])

Expand Down

0 comments on commit 1a4c0c7

Please sign in to comment.