Skip to content
Merged
Show file tree
Hide file tree
Changes from 54 commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
45120ed
refactor(MessageFeed): convert to CompoundComponent and temp remove a…
shaneeza Jan 27, 2026
1277ed5
Merge branch 's/initial-message-integration' of github.com:mongodb/le…
shaneeza Jan 27, 2026
defdad3
refactor(MessageFeed): enhance component structure
shaneeza Jan 27, 2026
958d58a
feat(MessageFeed): add MessageFeedContext
shaneeza Jan 27, 2026
c701f84
docs(MessageFeed): add changeset
shaneeza Jan 27, 2026
a6d7616
fix(MessageFeed): update error message for context provider
shaneeza Jan 27, 2026
e427185
refactor(Message): undo message changes
shaneeza Jan 27, 2026
0d9295d
chore(MessageFeed): add compound-component dependency
shaneeza Jan 27, 2026
eb31dd4
chore(pnpm-lock): add compound-component to dependencies
shaneeza Jan 27, 2026
fbb16db
chore(MessageFeed): update dependencies for compound-component integr…
shaneeza Jan 27, 2026
478ee3c
refactor(MessageFeed): move shared types
shaneeza Jan 27, 2026
43fc610
Merge branch 'LG-5932-message-feed-compound' of github.com:mongodb/le…
shaneeza Jan 27, 2026
8b67331
refactor(InitialMessage): update import path for shared types and use…
shaneeza Jan 27, 2026
8926ff5
feat(InitialMessage): integrate MessageFeedContext to manage initial …
shaneeza Jan 28, 2026
bd83d5f
chore(changeset): update dependency version for @lg-chat/message-feed
shaneeza Jan 28, 2026
6441471
fix(tsconfig): add missing newline at end of file
shaneeza Jan 28, 2026
9e0a42e
fix(MessageFeedContext): handle error boundary for React 17 in contex…
shaneeza Jan 28, 2026
bebc9bf
Merge branch 'LG-5932-message-feed-compound' of github.com:mongodb/le…
shaneeza Jan 28, 2026
7dcc812
feat(InitialMessage): implement styles and update component structure…
shaneeza Jan 28, 2026
3516dd6
feat(InitialMessage): enhance initial message component with structur…
shaneeza Jan 28, 2026
87414ee
refactor(InitialMessage): replace hardcoded title and description wit…
shaneeza Jan 28, 2026
7caaef2
feat(ChatWindow): add initial message prompts and enhance message han…
shaneeza Jan 28, 2026
e41b22b
refactor(ChatWindow): remove enableHideOnSelect prop from Suggested P…
shaneeza Jan 28, 2026
5f6e619
test(InitialMessage): add unit tests for accessibility, rendering, an…
shaneeza Jan 28, 2026
1d87c3b
merge conflict
shaneeza Jan 29, 2026
1ef0ae9
test(MessageFeed): enhance tests with scrollTo mock and update query …
shaneeza Jan 29, 2026
d4adca5
refactor(InitialMessage): simplify getWrapperStyles function and remo…
shaneeza Jan 29, 2026
52846e0
chore(MessageFeed): update dependencies and tsconfig to include new L…
shaneeza Jan 29, 2026
eb70b3f
refactor(ChatWindow): rename initial message component and update pro…
shaneeza Jan 29, 2026
a33deba
chore(MessageFeed): remove unused @leafygreen-ui/hooks dependency fro…
shaneeza Jan 29, 2026
d7f10d4
refactor(MessageFeed): remove commented-out MyMessage component from …
shaneeza Jan 29, 2026
298ffc1
chore(MessageFeed): update changeset
shaneeza Jan 29, 2026
4d14756
refactor(MessageFeed): integrate MessagePrompts and MessagePrompt com…
shaneeza Jan 29, 2026
770c81f
refactor(InitialMessage): update styles for title and description com…
shaneeza Jan 29, 2026
5e005d2
refactor(InitialMessage): adjust inner wrapper styles with focus ring…
shaneeza Jan 29, 2026
010b86f
Merge branch 'LG-5935-initial-message' of github.com:mongodb/leafygre…
shaneeza Jan 29, 2026
16b306b
test(InitialMessage, MessagePrompt, MessagePrompts): enhance tests fo…
shaneeza Jan 29, 2026
c61fd68
refactor(InitialMessage): rename component and add message prompts fo…
shaneeza Jan 29, 2026
853e636
chore(pnpm-lock): add '@lg-chat/message-prompts' dependency and clean…
shaneeza Jan 29, 2026
20fd21d
test(InitialMessage, MessagePrompts): improve error handling tests fo…
shaneeza Jan 29, 2026
294f879
chore(changeset): update to include new components
shaneeza Jan 29, 2026
9a80426
test(InitialMessage): fix JSX syntax in type validation tests for pro…
shaneeza Jan 29, 2026
4250267
test(MessageFeed): add tests for rendering MessagePrompts and its chi…
shaneeza Jan 29, 2026
bce10f3
refactor(InitialMessage): restructure inner wrapper and remove descri…
shaneeza Jan 30, 2026
41ce6d9
Merge branch 'LG-5935-initial-message' of github.com:mongodb/leafygre…
shaneeza Jan 30, 2026
c075973
refactor(InitialMessage): integrate LeafyGreenProvider for dark mode …
shaneeza Jan 30, 2026
9d657aa
merge conflict
shaneeza Jan 30, 2026
a8f8029
test(InitialMessage, MessageFeed): update tests to ensure proper rend…
shaneeza Jan 30, 2026
4744bdc
refactor(InitialMessage): remove LeafyGreenProvider and simplify comp…
shaneeza Jan 30, 2026
9a145bc
refactor(InitialMessage): remove LeafyGreenProvider and streamline co…
shaneeza Jan 30, 2026
499fc40
feat(InitialMessage): add generated story for initialMessage
shaneeza Jan 30, 2026
75a2d91
merge conflict
shaneeza Jan 30, 2026
ae55764
feat(InitialMessage): enhance story with interactive MessagePrompts a…
shaneeza Jan 30, 2026
98a80a0
chore(ALL_PACKAGES): revert ALL_PACKAGES
shaneeza Jan 30, 2026
338cbec
refactor(InitialMessage): update AssistantAvatar size and adjust prop…
shaneeza Jan 30, 2026
9db8c4d
fix(InitialMessage): export InitialMessageProps type for better type …
shaneeza Jan 30, 2026
748ef93
refactor(InitialMessage): Components to components
shaneeza Jan 30, 2026
da60688
refactor(InitialMessage): update import paths from Components to comp…
shaneeza Jan 30, 2026
730e077
feat(InitialMessage): add interactive story for message addition with…
shaneeza Feb 2, 2026
308b6b5
fix(InitialMessage): add visibility property to transition styles for…
shaneeza Feb 2, 2026
9572937
feat(MessageFeed): enhance InitialMessage stories with new message ha…
shaneeza Feb 2, 2026
e8c475c
feat(InitialMessage): rename Components to components
shaneeza Feb 2, 2026
983cd0c
fix(InitialMessage): update import paths from Components to component…
shaneeza Feb 2, 2026
cfaee9f
merge conflict
shaneeza Feb 2, 2026
3ee02ba
refactor(InitialMessage): replace MessageFeedSubcomponentProperty wit…
shaneeza Feb 2, 2026
b43ec06
feat(MessagePromptsItem): add MessagePromptsItem component with assoc…
shaneeza Feb 2, 2026
b7fecc6
refactor(MessagePrompts): rename MessagePrompt to MessagePromptsItem …
shaneeza Feb 2, 2026
72f8c20
refactor(MessageFeed): simplify initial message handling by removing …
shaneeza Feb 2, 2026
abf16ff
refactor(MessageFeed): export InitialMessageProps type for better acc…
shaneeza Feb 2, 2026
f521fba
Merge branch 'LG-5935-initial-message' of github.com:mongodb/leafygre…
shaneeza Feb 2, 2026
f8159af
refactor(MessageFeed): export additional message prompts types for im…
shaneeza Feb 2, 2026
c743937
merge conflict
shaneeza Feb 3, 2026
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
4 changes: 3 additions & 1 deletion .changeset/dark-pots-tell.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@
---

- [LG-5932](https://jira.mongodb.org/browse/LG-5932): Refactor to use `CompoundComponent` pattern
- [LG-5934](https://jira.mongodb.org/browse/LG-5934): add `MessageFeedProvider` and `useMessageFeedContext`
- [LG-5934](https://jira.mongodb.org/browse/LG-5934): add `MessageFeedProvider` and `useMessageFeedContext`
- [LG-5935](https://jira.mongodb.org/browse/LG-5935): add `MessageFeed.InitialMessage` component
- [LG-5933](https://jira.mongodb.org/browse/LG-5933): add `MessageFeed.InitialMessage.MessagePrompts` and `MessageFeed.InitialMessage.MessagePrompt` components.
96 changes: 95 additions & 1 deletion chat/chat-window/src/ChatWindow.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,6 @@ const WithMessagePromptsComponent = ({
<LeafyGreenChatProvider assistantName={assistantName}>
<ChatWindow {...props}>
<MessageFeed>
<div style={{ flex: 1 }} aria-hidden="true" />
{messages.map((messageFields, index) => (
<Message
key={messageFields.id}
Expand Down Expand Up @@ -263,6 +262,101 @@ export const WithMessagePrompts: StoryObj<ChatWindowStoryProps> = {
},
};

const WithInitialMessageWithMessagePromptsComponent = ({
assistantName,
...props
}: ChatWindowStoryProps) => {
const [messages, setMessages] = useState<Array<any>>([]);
const [selectedPromptIndex, setSelectedPromptIndex] = useState<
number | undefined
>();

const prompts = [
'What is MongoDB?',
'How do I create a database?',
'Can you explain indexes?',
];

const handlePromptSelect = (index: number) => {
setSelectedPromptIndex(index);
const selectedPrompt = prompts[index];

// Add user message with selected prompt
const userMessage = {
id: messages.length,
messageBody: selectedPrompt,
isSender: true,
};

// Add assistant response
const assistantMessage = {
id: messages.length + 1,
messageBody: `Great question! Let me explain about "${selectedPrompt}"...`,
isSender: false,
};

setMessages(prev => [...prev, userMessage, assistantMessage]);
};

const handleMessageSend = (messageBody: string) => {
const newMessage = {
id: messages.length,
messageBody,
isSender: true,
};
setMessages(prev => [...prev, newMessage]);
};

return (
<LeafyGreenChatProvider assistantName={assistantName}>
<ChatWindow {...props}>
<MessageFeed>
<MessageFeed.InitialMessage>
<MessageFeed.InitialMessage.MessagePrompts
label="Suggested Prompts"
onClickRefresh={() => {
// eslint-disable-next-line no-console
console.log('Refresh prompts');
setSelectedPromptIndex(undefined);
}}
>
{prompts.map((prompt, promptIndex) => (
<MessageFeed.InitialMessage.MessagePrompt
key={prompt}
selected={selectedPromptIndex === promptIndex}
onClick={() => handlePromptSelect(promptIndex)}
data-testid={`prompt-${promptIndex}`}
>
{prompt}
</MessageFeed.InitialMessage.MessagePrompt>
))}
</MessageFeed.InitialMessage.MessagePrompts>
</MessageFeed.InitialMessage>
{messages.map(messageFields => (
<Message
key={messageFields.id}
sourceType="markdown"
isSender={messageFields.isSender}
messageBody={messageFields.messageBody}
/>
))}
</MessageFeed>
<InputBar onMessageSend={handleMessageSend} />
</ChatWindow>
</LeafyGreenChatProvider>
);
};

export const WithInitialMessageWithMessagePrompts: StoryObj<ChatWindowStoryProps> =
{
render: WithInitialMessageWithMessagePromptsComponent,
parameters: {
chromatic: {
disableSnapshot: true,
},
},
};

const ChatDrawerContent = ({ assistantName }: { assistantName?: string }) => {
const [messages, setMessages] = useState<Array<any>>(baseMessages);

Expand Down
4 changes: 3 additions & 1 deletion chat/message-feed/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,22 @@
"access": "public"
},
"dependencies": {
"@leafygreen-ui/avatar": "workspace:^",
"@leafygreen-ui/button": "workspace:^",
"@leafygreen-ui/compound-component": "workspace:^",
"@leafygreen-ui/emotion": "workspace:^",
"@leafygreen-ui/icon": "workspace:^",
"@leafygreen-ui/lib": "workspace:^",
"@leafygreen-ui/palette": "workspace:^",
"@leafygreen-ui/tokens": "workspace:^",
"@leafygreen-ui/typography": "workspace:^",
"@lg-chat/message": "workspace:^",
"@lg-chat/message-prompts": "workspace:^",
"@lg-chat/message-rating": "workspace:^",
"react-intersection-observer": "^8.25.1",
"react-keyed-flatten-children": "^2.2.1"
},
"devDependencies": {
"@lg-chat/message-prompts": "workspace:^",
"@lg-tools/build": "workspace:^"
},
"peerDependencies": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import { axe } from 'jest-axe';

import { MessageFeedProvider } from '../../MessageFeedContext';

import {
INITIAL_MESSAGE_DESCRIPTION,
INITIAL_MESSAGE_TITLE,
} from './constants';
import { InitialMessage } from './InitialMessage';
import { InitialMessageProps } from './InitialMessage.types';

jest.mock('@lg-chat/lg-markdown', () => ({
LGMarkdown: jest.fn(({ children }) => <div>{children}</div>),
}));

const renderInitialMessage = ({
shouldHideInitialMessage = false,
...rest
}: InitialMessageProps & { shouldHideInitialMessage?: boolean }) => {
return render(
<MessageFeedProvider shouldHideInitialMessage={shouldHideInitialMessage}>
<InitialMessage {...rest} />
</MessageFeedProvider>,
);
};

describe('InitialMessage', () => {
describe('a11y', () => {
test('does not have basic accessibility issues', async () => {
const { container } = renderInitialMessage({});
const results = await axe(container);
expect(results).toHaveNoViolations();
});
});

test('renders the title and description', () => {
renderInitialMessage({});
expect(screen.getByText(INITIAL_MESSAGE_TITLE)).toBeInTheDocument();
expect(screen.getByText(INITIAL_MESSAGE_DESCRIPTION)).toBeInTheDocument();
});

test('does not render children if its not a subcomponent', () => {
renderInitialMessage({
children: <div>I heard you like MongoDB</div>,
});
expect(
screen.queryByText('I heard you like MongoDB'),
).not.toBeInTheDocument();
});

test('is hidden when the shouldHideInitialMessage is true', () => {
renderInitialMessage({
shouldHideInitialMessage: true,
children: (
<InitialMessage.MessagePrompts>
<InitialMessage.MessagePrompt>
I heard you like MongoDB
</InitialMessage.MessagePrompt>
</InitialMessage.MessagePrompts>
),
});
expect(screen.getByText(INITIAL_MESSAGE_TITLE)).not.toBeVisible();
expect(screen.getByText(INITIAL_MESSAGE_DESCRIPTION)).not.toBeVisible();
expect(screen.getByText('I heard you like MongoDB')).not.toBeVisible();
});

// renders the MessagePrompts subcomponent
test('renders the MessagePrompts subcomponent', () => {
renderInitialMessage({
children: (
<InitialMessage.MessagePrompts>
<InitialMessage.MessagePrompt>
What is MongoDB?
</InitialMessage.MessagePrompt>
<InitialMessage.MessagePrompt>
How do I query MongoDB?
</InitialMessage.MessagePrompt>
<InitialMessage.MessagePrompt>
What is blue but not heavy?
</InitialMessage.MessagePrompt>
</InitialMessage.MessagePrompts>
),
});
expect(screen.getByText('What is MongoDB?')).toBeInTheDocument();
expect(screen.getByText('How do I query MongoDB?')).toBeInTheDocument();
expect(screen.getByText('What is blue but not heavy?')).toBeInTheDocument();
});

/* eslint-disable jest/no-disabled-tests */
describe.skip('types behave as expected', () => {
test('does not throw errors', () => {
<InitialMessage>
<InitialMessage.MessagePrompts label="hello">
<InitialMessage.MessagePrompt selected>
What is MongoDB?
</InitialMessage.MessagePrompt>
<InitialMessage.MessagePrompt disabled>
How do I query MongoDB?
</InitialMessage.MessagePrompt>
<InitialMessage.MessagePrompt onClick={() => {}}>
What is blue but not heavy?
</InitialMessage.MessagePrompt>
</InitialMessage.MessagePrompts>
</InitialMessage>;
});

test('throws errors when enableHideOnSelect is used', () => {
<InitialMessage>
{/* @ts-expect-error - enableHideOnSelect is not a prop */}
<InitialMessage.MessagePrompts enableHideOnSelect={false}>
<InitialMessage.MessagePrompt>
What is MongoDB?
</InitialMessage.MessagePrompt>
<InitialMessage.MessagePrompt>
How do I query MongoDB?
</InitialMessage.MessagePrompt>
<InitialMessage.MessagePrompt>
What is blue but not heavy?
</InitialMessage.MessagePrompt>
</InitialMessage.MessagePrompts>
</InitialMessage>;
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import React from 'react';
import { storybookArgTypes, StoryMetaType } from '@lg-tools/storybook-utils';
import { StoryFn } from '@storybook/react';

import { MessageFeedProvider } from '../../MessageFeedContext';

import { InitialMessage, InitialMessageProps } from '.';

const messagePrompts = [
<InitialMessage.MessagePrompts
key="message-prompts"
onClickRefresh={() => {}}
label="Suggested Prompts"
>
<InitialMessage.MessagePrompt>
What is MongoDB?
</InitialMessage.MessagePrompt>
<InitialMessage.MessagePrompt>
How do I query MongoDB?
</InitialMessage.MessagePrompt>
</InitialMessage.MessagePrompts>,
];

const meta: StoryMetaType<typeof InitialMessage> = {
title: 'Composition/Chat/MessageFeed/InitialMessage',
component: InitialMessage,
parameters: {
default: 'LiveExample',
generate: {
combineArgs: {
darkMode: [false, true],
children: [messagePrompts, undefined],
},
decorator: Instance => {
return (
<MessageFeedProvider shouldHideInitialMessage={false}>
<Instance />
</MessageFeedProvider>
);
},
},
},
argTypes: {
darkMode: storybookArgTypes.darkMode,
},
decorators: [
Instance => (
<MessageFeedProvider shouldHideInitialMessage={false}>
<Instance />
</MessageFeedProvider>
),
],
};

export default meta;

const Template: StoryFn<InitialMessageProps> = props => (
<InitialMessage {...props} />
);

export const LiveExample = {
render: Template,
args: {},
};

export const Generated = () => {};
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { css, cx } from '@leafygreen-ui/emotion';
import { spacing, transitionDuration, typeScales } from '@leafygreen-ui/tokens';

const FOCUS_RING_WIDTH = 4;

const baseOuterWrapperStyles = css`
display: grid;
grid-template-rows: 1fr;
gap: ${spacing[200]}px;
`;

const transitionStyles = css`
transform-origin: top left;
transition-property: grid-template-rows, opacity, transform;
transition-duration: ${transitionDuration.slower}ms;
transition-timing-function: ease-out;
`;

const hiddenWrapperStyles = css`
grid-template-rows: 0fr;
opacity: 0;
transform: scale(0.8);
`;

export const getWrapperStyles = ({ shouldHide }: { shouldHide: boolean }) =>
cx(baseOuterWrapperStyles, transitionStyles, {
[hiddenWrapperStyles]: shouldHide,
});

export const titleStyles = css`
font-size: ${typeScales.body2.fontSize}px;
line-height: ${typeScales.body2.lineHeight}px;
`;

export const innerWrapperStyles = css`
overflow: hidden;
margin: -${FOCUS_RING_WIDTH}px;
padding: ${FOCUS_RING_WIDTH}px;
display: flex;
flex-direction: column;
gap: ${spacing[400]}px;
`;
Loading
Loading