Skip to content
This repository has been archived by the owner on Feb 5, 2025. It is now read-only.

Commit

Permalink
fix: header actions (COR-3811) (#278)
Browse files Browse the repository at this point in the history
  • Loading branch information
gillyb authored Nov 7, 2024
1 parent eed772e commit 95cc945
Show file tree
Hide file tree
Showing 8 changed files with 159 additions and 122 deletions.
208 changes: 104 additions & 104 deletions apps/documentation/public/bundle/bundle.mjs

Large diffs are not rendered by default.

9 changes: 6 additions & 3 deletions apps/documentation/src/pages/molecules/header.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,18 @@ import { StoryEmbed } from '../../components/StoryEmbed';

## Base

<StoryEmbed for="Header" name="Base" clientOnly={true} />
<StoryEmbed for="Header" name="Actionable" clientOnly={true} />

## With actions
## Muted

<StoryEmbed for="Header" name="Actionable" clientOnly={true} />
<StoryEmbed for="Header" name="Muted" clientOnly={true} />

## Without an image

<StoryEmbed for="Header" name="NoImage" clientOnly={true} />

## Mobile

<StoryEmbed for="Header" name="Mobile" clientOnly={true} />

</div>
1 change: 1 addition & 0 deletions packages/chat/src/assets/svg/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export { default as documentUrl } from './document-url.svg?react';
export { default as largeArrowLeft } from './large-arrow-left.svg?react';
export { default as microphone } from './microphone.svg?react';
export { default as minus } from './minus.svg?react';
export { default as mute } from './mute.svg?react';
export { default as reset } from './reset.svg?react';
export { default as smallArrowUp } from './small-arrow-up.svg?react';
export { default as sound } from './sound.svg?react';
Expand Down
5 changes: 5 additions & 0 deletions packages/chat/src/assets/svg/mute.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
23 changes: 18 additions & 5 deletions packages/chat/src/components/Header/Header.story.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { Meta, StoryObj } from '@storybook/react';

import { VF_ICON } from '@/fixtures';
import EMPTY_IMAGE from '@/__fixtures__/empty-image.png';
import { WithDefaultPalette } from '@/storybook/decorators';

import { Header } from '.';
Expand All @@ -10,7 +10,7 @@ const meta: Meta<typeof Header> = {
component: Header,
args: {
title: 'Agent name',
image: VF_ICON,
image: EMPTY_IMAGE,
actions: [],
},
render: (args) => <Header {...args} />,
Expand All @@ -25,21 +25,34 @@ export const Base: Story = {};

export const Actionable: Story = {
args: {
actions: [{ svg: 'volume' }, { svg: 'reset' }, { svg: 'close' }],
actions: [{ svg: 'volume' }, { svg: 'reset' }],
},
};

export const Muted: Story = {
args: {
actions: [{ svg: 'mute' }, { svg: 'reset' }],
},
};

export const Themed: Story = {
args: {
actions: [{ svg: 'volume' }, { svg: 'reset' }, { svg: 'close' }],
actions: [{ svg: 'volume' }, { svg: 'reset' }],
},
decorators: [WithDefaultPalette],
};

export const NoImage: Story = {
args: {
actions: [{ svg: 'volume' }, { svg: 'reset' }, { svg: 'close' }],
actions: [{ svg: 'volume' }, { svg: 'reset' }],
image: undefined,
},
decorators: [WithDefaultPalette],
};

export const Mobile: Story = {
args: {
actions: [{ svg: 'volume' }, { svg: 'reset' }, { svg: 'close' }],
},
decorators: [WithDefaultPalette],
};
21 changes: 12 additions & 9 deletions packages/chat/src/components/NewChat/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,9 @@ import { useContext, useMemo, useRef, useState } from 'react';

import { ClassName } from '@/constants';
import { AutoScrollProvider, RuntimeStateAPIContext, RuntimeStateContext } from '@/contexts';
import { RenderMode } from '@/dtos/RenderOptions.dto';
import type { Nullish } from '@/types';
import { chain } from '@/utils/functional';

import mockAvatar from '../../assets/blank-image.png';
import { Header, type HeaderActionProps, type HeaderProps } from '../Header';
import { type INewFooter, NewFooter } from '../NewFooter';
import { Prompt } from '../Prompt';
Expand All @@ -26,6 +24,11 @@ export interface INewChat extends HeaderProps, IWelcomeMessage, INewFooter, Reac
*/
audioInterface?: boolean;

/**
* If true, the user is using a mobile device.
*/
isMobile?: boolean;

/**
* A unix timestamp indicating the start of the conversation.
*/
Expand Down Expand Up @@ -58,6 +61,7 @@ export const NewChat: React.FC<INewChat> = ({
extraLinkUrl,
children,
audioInterface,
isMobile,
}) => {
const [hasAlert, setAlert] = useState(false);

Expand All @@ -75,15 +79,14 @@ export const NewChat: React.FC<INewChat> = ({
const handleResume = (): void => setAlert(false);

const headerActions = useMemo<HeaderActionProps[]>(() => {
const items: HeaderActionProps[] = [{ svg: 'close', onClick: handleClose }];

if (config.render?.mode === RenderMode.OVERLAY) {
items.unshift({ svg: 'minus', onClick: onMinimize });
const items: HeaderActionProps[] = [{ svg: 'reset', onClick: handleClose }];
if (isMobile) {
items.push({ svg: 'close', onClick: onMinimize });
}

if (audioInterface) {
items.unshift({
svg: state.audioOutput ? 'sound' : 'soundOff',
svg: state.audioOutput ? 'volume' : 'mute',
onClick: toggleAudioOutput,
});
}
Expand All @@ -96,7 +99,7 @@ export const NewChat: React.FC<INewChat> = ({

return (
<div className={clsx(ClassName.CHAT, chatContainer)}>
<Header title={title} image={mockAvatar} actions={headerActions} />
<Header title={title} image={avatar} actions={headerActions} />
<div ref={scrollableAreaRef} className={dialogContainer}>
<AutoScrollProvider target={scrollableAreaRef}>
<WelcomeMessage title={title} description={description} avatar={avatar} />
Expand All @@ -117,7 +120,7 @@ export const NewChat: React.FC<INewChat> = ({
/>
<Prompt
visible={hasAlert}
accept={{ label: 'End Chat', /* type: 'warn', */ onClick: chain(onEnd, handleResume) }}
accept={{ label: 'Start new chat', onClick: chain(onEnd, handleResume) }}
cancel={{ label: 'Cancel', onClick: handleResume }}
/>
</div>
Expand Down
10 changes: 10 additions & 0 deletions packages/chat/src/dtos/RenderOptions.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,17 @@ import { z } from 'zod';
export const EMBEDDED_TARGET = 'voiceflow-chat-frame';

export enum RenderMode {
/**
* Embed the chat window into a specific container in the screen.
* This won't show the Launcher button because the chat will be
* opened by default.
*/
EMBEDDED = 'embedded',

/**
* Shows the launcher button in the bottom corder of the screen,
* and the user needs to push it to open/minimize the chat window.
*/
OVERLAY = 'overlay',
}

Expand Down
4 changes: 3 additions & 1 deletion packages/chat/src/views/ChatWindow/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ import { SessionStatus, TurnType } from '@/types';

export interface ChatWindowProps {
className?: string;
isMobile?: boolean;
}

export const ChatWindow: React.FC<ChatWindowProps> = ({ className }) => {
export const ChatWindow: React.FC<ChatWindowProps> = ({ isMobile, className }) => {
const runtime = useContext(RuntimeStateAPIContext);
const state = useContext(RuntimeStateContext);
const { assistant, config } = runtime;
Expand Down Expand Up @@ -59,6 +60,7 @@ export const ChatWindow: React.FC<ChatWindowProps> = ({ className }) => {
messageInputProps={{
onSubmit: runtime.reply,
}}
isMobile={isMobile}
>
{state.session.turns.map((turn, turnIndex) =>
match(turn)
Expand Down

0 comments on commit 95cc945

Please sign in to comment.