Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 78 additions & 16 deletions src/components/elementRenderer/elementRenderer.cy.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import React from 'react'
import { v4 as getUUID } from 'uuid'

import {
supportedViewports,
testUser,
} from '../../../cypress/support/variables'
import { YoutubeVideo } from '..'
import { StreamingText, YoutubeVideo } from '..'
import Text from '../text/text'
import ElementRenderer from './elementRenderer'

const supportedElements = {
text: Text,
video: YoutubeVideo,
streamingText: StreamingText,
}

const sampleMessage = {
Expand Down Expand Up @@ -38,23 +40,27 @@ describe('ElementRenderer', () => {
cy.viewport(viewport)
cy.mount(
<ElementRenderer
message={{
...sampleMessage,
data: { text: 'Test Text' },
format: 'text',
}}
messages={[
{
...sampleMessage,
data: { text: 'Test Text' },
format: 'text',
},
]}
{...commonProps}
ws={mockWsClient}
/>
)
cy.get('p').should('contain.text', 'Test Text')
cy.mount(
<ElementRenderer
message={{
...sampleMessage,
data: { youtubeVideoId: 'MtN1YnoL46Q' },
format: 'video',
}}
messages={[
{
...sampleMessage,
data: { youtubeVideoId: 'MtN1YnoL46Q' },
format: 'video',
},
]}
{...commonProps}
ws={mockWsClient}
/>
Expand All @@ -71,6 +77,60 @@ describe('ElementRenderer', () => {
})
})

it(`renders the original message and its update messages correctly on ${viewport} screen`, () => {
const mockWsClient = {
send: cy.stub(),
close: cy.stub(),
reconnect: cy.stub(),
}

cy.viewport(viewport)
cy.mount(
<ElementRenderer
messages={[
{
...sampleMessage,
data: { text: 'This' },
format: 'streamingText',
},
{
id: getUUID(),
timestamp: '2020-01-02T00:00:00.000Z',
sender: testUser,
conversationId: 'lkd9vc',
topic: 'default',
threadId: '1',
data: { text: ' is' },
format: 'streamingText',
},
{
id: getUUID(),
timestamp: '2020-01-02T00:00:00.000Z',
sender: testUser,
conversationId: 'lkd9vc',
topic: 'default',
threadId: '1',
data: { text: ' streaming' },
format: 'streamingText',
},
{
id: getUUID(),
timestamp: '2020-01-02T00:00:00.000Z',
sender: testUser,
conversationId: 'lkd9vc',
topic: 'default',
threadId: '1',
data: { text: ' text.' },
format: 'streamingText',
},
]}
{...commonProps}
ws={mockWsClient}
/>
)
cy.get('p').should('contain.text', 'This is streaming text.')
})

it(`renders a message for an unsupported format on ${viewport} screen`, () => {
const mockWsClient = {
send: cy.stub(),
Expand All @@ -81,11 +141,13 @@ describe('ElementRenderer', () => {
cy.viewport(viewport)
cy.mount(
<ElementRenderer
message={{
...sampleMessage,
data: { text: 'Test Text' },
format: 'unsupported',
}}
messages={[
{
...sampleMessage,
data: { text: 'Test Text' },
format: 'unsupported',
},
]}
{...commonProps}
ws={mockWsClient}
/>
Expand Down
25 changes: 11 additions & 14 deletions src/components/elementRenderer/elementRenderer.tsx
Original file line number Diff line number Diff line change
@@ -1,39 +1,36 @@
import Typography from '@mui/material/Typography'
import React from 'react'

import type {
ComponentMap,
Sender,
ThreadableMessage,
WebSocketClient,
} from '../types'
import type { ComponentMap, Message, Sender, WebSocketClient } from '../types'

interface ElementRendererProps {
sender: Sender
ws: WebSocketClient
message: ThreadableMessage
messages: Message[]
supportedElements: ComponentMap
}

const ElementRenderer = (props: ElementRendererProps) => {
const MaybeElement = props.supportedElements[props.message.format]
const rootMessage = props.messages[0]
const updateMessages = props.messages.slice(1)

const MaybeElement = props.supportedElements[rootMessage.format]
return (
<>
{MaybeElement ? (
React.createElement(MaybeElement, {
sender: props.sender,
ws: props.ws,
messageId: props.message.id,
conversationId: props.message.conversationId,
...props.message.data,
...(props.message.threadMessagesData && {
updatedData: props.message.threadMessagesData,
messageId: rootMessage.id,
conversationId: rootMessage.conversationId,
...rootMessage.data,
...(updateMessages.length > 0 && {
updatedData: updateMessages.map((message) => message.data),
}),
})
) : (
<Typography variant="body2">
Unsupported element format: {props.message.format}
Unsupported element format: {rootMessage.format}
</Typography>
)}
</>
Expand Down
3 changes: 2 additions & 1 deletion src/components/input/textInput/textInput.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ meta.argTypes = {
'A websocket client with supports the following methods:\n' +
'send: (msg: Message) => void\n' +
'close: () => void\n' +
'reconnect: () => void',
'reconnect: () => void\n' +
'onReceive?: (handler: (message: Message) => void) => void',
},
},
},
Expand Down
6 changes: 3 additions & 3 deletions src/components/messageCanvas/actions/action.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ import Tooltip from '@mui/material/Tooltip'
import type { ReactNode } from 'react'
import React from 'react'

import type { ThreadableMessage } from '../../types'
import type { Message } from '../../types'

interface ActionProps {
label: string
message: ThreadableMessage
message: Message
icon: ReactNode
onClick: (message: ThreadableMessage) => void
onClick: (message: Message) => void
}

export default function Action(props: ActionProps) {
Expand Down
6 changes: 3 additions & 3 deletions src/components/messageCanvas/actions/copy/copyText.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import React, { useState } from 'react'

import Icon from '../../../icon/icon'
import type { ThreadableMessage } from '../../../types'
import type { Message } from '../../../types'
import Action from '../index'

export interface CopyTextProps {
message: ThreadableMessage
message: Message
}

export default function CopyText(props: CopyTextProps) {
const [tooltipContent, setTooltipContent] = useState('Copy text')
const twoSeconds = 2000

function handleOnClick(message: ThreadableMessage) {
function handleOnClick(message: Message) {
if (message.data.text) {
navigator.clipboard
.writeText(message.data.text)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import type { ThreadableMessage } from '../../../types' // Adjust if needed
import type { Message } from '../../../types' // Adjust if needed
import TextToSpeech from './textToSpeech'

describe('TextToSpeech Component', () => {
const mockMessage: ThreadableMessage = {
const mockMessage: Message = {
id: '1',
timestamp: '2020-01-02T00:00:00.000Z',
conversationId: 'lkd9vc',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import React, { useState } from 'react'

import Icon from '../../../icon/icon'
import type { ThreadableMessage } from '../../../types'
import type { Message } from '../../../types'
import Action from '../index'

export interface TextToSpeechProps {
message: ThreadableMessage
message: Message
}

export default function TextToSpeech(props: TextToSpeechProps) {
Expand Down
4 changes: 2 additions & 2 deletions src/components/messageCanvas/messageCanvas.cy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
testUser,
} from '../../../cypress/support/variables'
import Icon from '../icon/icon'
import type { ThreadableMessage } from '../types'
import type { Message } from '../types'
import CopyText from './actions/copy/copyText'
import MessageCanvas from './messageCanvas'

Expand Down Expand Up @@ -43,7 +43,7 @@ describe('MessageCanvas', () => {
cy.mount(
<MessageCanvas
message={testMessage}
getActionsComponent={(message: ThreadableMessage) => {
getActionsComponent={(message: Message) => {
const copyButton = message.format === 'text' && (
<CopyText message={message} />
)
Expand Down
28 changes: 15 additions & 13 deletions src/components/messageCanvas/messageCanvas.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import Typography from '@mui/material/Typography'
import React from 'react'

import { ElementRenderer, MarkedMarkdown, type ThreadableMessage } from '..'
import ElementRenderer from '../elementRenderer'
import Icon from '../icon/icon'
import MarkedMarkdown from '../markdown'
import Text from '../text/text'
import type { Message } from '../types'
import CopyText from './actions/copy/copyText'
import TextToSpeech from './actions/textToSpeech/textToSpeech'
import MessageCanvas from './messageCanvas'
Expand Down Expand Up @@ -111,7 +113,7 @@ const elementRendererString = `<ElementRenderer
supportedElements={{ text: Text }}
/>`

const profileString = `(message: ThreadableMessage) => {
const profileString = `(message: Message) => {
<>
{getProfileIcon(message)}
<Typography variant="body1" color="text.secondary">
Expand All @@ -120,23 +122,23 @@ const profileString = `(message: ThreadableMessage) => {
</>
}`

function getProfileIcon(message: ThreadableMessage) {
function getProfileIcon(message: Message) {
if (message.sender.name?.includes('agent')) {
return <Icon name="smart_toy" />
} else {
return <Icon name="account_circle" />
}
}

function getProfileName(message: ThreadableMessage) {
function getProfileName(message: Message) {
return (
<Typography variant="body1" color="text.secondary">
{message.sender.name}
</Typography>
)
}

function getProfileIconAndName(message: ThreadableMessage) {
function getProfileIconAndName(message: Message) {
return (
<>
{getProfileIcon(message)}
Expand All @@ -149,7 +151,7 @@ export const WithProfileIcon = {
args: {
children: (
<ElementRenderer
message={messageFromHuman}
messages={[messageFromHuman]}
supportedElements={{ text: Text }}
{...commonElementRendererProps}
/>
Expand All @@ -175,7 +177,7 @@ export const NoIcon = {
args: {
children: (
<ElementRenderer
message={messageFromAgent}
messages={[messageFromAgent]}
supportedElements={{ text: Text }}
{...commonElementRendererProps}
/>
Expand All @@ -201,14 +203,14 @@ export const WithCopyIcon = {
args: {
children: (
<ElementRenderer
message={messageFromHuman}
messages={[messageFromHuman]}
supportedElements={{ text: Text }}
{...commonElementRendererProps}
/>
),
message: messageFromHuman,
getProfileComponent: getProfileIconAndName,
getActionsComponent: (message: ThreadableMessage) => {
getActionsComponent: (message: Message) => {
const copyButton = message.format === 'text' && (
<CopyText message={message} />
)
Expand All @@ -222,7 +224,7 @@ export const WithCopyIcon = {
source: {
code: `<MessageCanvas
getProfileComponent={${profileString}}
getActionsComponent={(message: ThreadableMessage) => {
getActionsComponent={(message: Message) => {
const copyButton = message.format === 'text' && <CopyText message={message} />
if (copyButton) {
return <>{copyButton}</>
Expand All @@ -241,14 +243,14 @@ export const WithTextToSpeech = {
args: {
children: (
<ElementRenderer
message={markdownMessage}
messages={[markdownMessage]}
supportedElements={{ markdown: MarkedMarkdown }}
{...commonElementRendererProps}
/>
),
message: markdownMessage,
getProfileComponent: getProfileIconAndName,
getActionsComponent: (message: ThreadableMessage) => {
getActionsComponent: (message: Message) => {
return (
<>
<TextToSpeech message={message} />
Expand All @@ -265,7 +267,7 @@ export const WithTextToSpeech = {
source: {
code: `<MessageCanvas
getProfileComponent={${profileString}}
getActionsComponent={(message: ThreadableMessage) => {
getActionsComponent={(message: Message) => {
const copyButton = message.format === 'text' && <CopyText message={message} />
if (copyButton) {
return <>{copyButton}</>
Expand Down
Loading