@lg-chat/message
v11.0.1
Published
lg-chat Message
Keywords
Readme
Message
Installation
PNPM
pnpm add @lg-chat/messageYarn
yarn add @lg-chat/messageNPM
npm install @lg-chat/messageExample
import { Message } from '@lg-chat/message';
return (
<div>
<Message messageBody="Question" />
<Message isSender={false} messageBody="Answer" />
</div>
);Compound Components
The Message component uses a compound component pattern, allowing you to compose different parts of a message using subcomponents like Message.Actions, Message.Links, Message.Promotion, Message.ActionCard, and Message.VerifiedBanner.
Note: The layout and order of compound components are enforced by the Message component itself. Even if you change the order of subcomponents in your JSX, they will be rendered in the correct, intended order within the message bubble. This ensures consistent UI and accessibility regardless of how you compose your message children.
Message.Actions
import React from 'react';
import { Message } from '@lg-chat/message';
import { MessageRatingValue } from '@lg-chat/message-rating';
const MessageWithActions = () => {
const handleCopy = () => console.log('Message copied');
const handleRetry = () => console.log('Message retried');
const handleRatingChange = (
e: React.ChangeEvent<HTMLInputElement>,
options: { rating: MessageRatingValue },
) => {
console.log('Rating changed:', options.rating);
};
const handleSubmitFeedback = async (
e: React.FormEvent<HTMLFormElement>,
options: { rating: MessageRatingValue; feedback?: string },
) => {
console.log('Feedback submitted:', options);
};
return (
<Message messageBody="Test message">
<Message.Actions
onClickCopy={handleCopy}
onClickRetry={handleRetry}
onRatingChange={handleRatingChange}
onSubmitFeedback={handleSubmitFeedback}
/>
</Message>
);
};Message.Links
import React from 'react';
import { Message } from '@lg-chat/message';
const MessageWithLinks = () => {
const links = [
{
href: 'https://mongodb.design',
children: 'LeafyGreen UI',
variant: 'Website',
},
{
href: 'https://mongodb.github.io/leafygreen-ui/?path=/docs/overview-introduction--docs',
children: 'LeafyGreen UI Docs',
variant: 'Docs',
},
{
href: 'https://learn.mongodb.com/',
children: 'MongoDB University',
variant: 'Learn',
},
];
const handleLinkClick = () => console.log('Link clicked');
return (
<Message isSender={false} messageBody="Test message">
<Message.Links
links={links}
onLinkClick={handleLinkClick}
headingText="Related Resources"
/>
</Message>
);
};Message.Promotion
import React from 'react';
import { Message } from '@lg-chat/message';
const MessageWithPromotion = () => {
const handlePromotionClick = () => console.log('Promotion clicked');
return (
<Message isSender={false} messageBody="Test message">
<Message.Promotion
promotionText="Go learn more about this skill!"
promotionUrl="https://learn.mongodb.com/skills"
onPromotionLinkClick={handlePromotionClick}
/>
</Message>
);
};Message.ActionCard
import React from 'react';
import { Message, ActionCardState } from '@lg-chat/message';
import DatabaseIcon from '@leafygreen-ui/icon/dist/Database';
const MessageWithActionCard = () => {
const handleCancel = () => console.log('Cancel clicked');
const handleRun = () => console.log('Run clicked');
const handleToggleExpanded = (isOpen: boolean) => {
console.log('Expanded state:', isOpen);
};
return (
<Message isSender={false} messageBody="Test message">
<Message.ActionCard
chips={[{ label: 'MongoDB', glyph: <DatabaseIcon /> }]}
initialIsExpanded={false}
onToggleExpanded={handleToggleExpanded}
showExpandButton={true}
state={ActionCardState.Idle}
title="Run list-databases?"
>
<Message.ActionCard.ExpandableContent>
{`# Tool Execution Result
This is a markdown content example showing tool execution results.
\`\`\`javascript
const result = await tool.execute();
console.log(result);
\`\`\``}
</Message.ActionCard.ExpandableContent>
<Message.ActionCard.Button onClick={handleCancel}>
Cancel
</Message.ActionCard.Button>
<Message.ActionCard.Button onClick={handleRun} variant="primary">
Run
</Message.ActionCard.Button>
</Message.ActionCard>
</Message>
);
};Message.VerifiedBanner
import React from 'react';
import { Message } from '@lg-chat/message';
const MessageWithVerifiedBanner = () => {
return (
<Message isSender={false} messageBody="Test message">
<Message.VerifiedBanner
verifier="MongoDB Staff"
verifiedAt={new Date('2024-03-24T16:20:00Z')}
learnMoreUrl="https://mongodb.com/docs"
/>
</Message>
);
};Complete Example with All Subcomponents
import React from 'react';
import { Message, ActionCardState } from '@lg-chat/message';
import { MessageRatingValue } from '@lg-chat/message-rating';
const Example = () => {
const handleCopy = () => console.log('Message copied');
const handleRetry = () => console.log('Message retried');
const handleRatingChange = (
e: React.ChangeEvent<HTMLInputElement>,
options: { rating: MessageRatingValue },
) => {
console.log('Rating changed:', options.rating);
};
const handleSubmitFeedback = async (
e: React.FormEvent<HTMLFormElement>,
options: { rating: MessageRatingValue; feedback?: string },
) => {
console.log('Feedback submitted:', options);
};
const links = [
{
href: 'https://mongodb.design',
children: 'LeafyGreen UI',
variant: 'Website',
},
{
href: 'https://mongodb.com/docs',
children: 'MongoDB Docs',
variant: 'Docs',
},
];
const handleLinkClick = () => console.log('Link clicked');
const handlePromotionClick = () => console.log('Promotion clicked');
const handleCancel = () => console.log('Cancel clicked');
const handleRun = () => console.log('Run clicked');
return (
<Message isSender={false} messageBody="Test message">
<Message.ActionCard
title="Run list-databases?"
state={ActionCardState.Idle}
>
<Message.ActionCard.ExpandableContent>
{`# Tool Execution Result
This is a markdown content example showing tool execution results.`}
</Message.ActionCard.ExpandableContent>
<Message.ActionCard.Button onClick={handleCancel}>
Cancel
</Message.ActionCard.Button>
<Message.ActionCard.Button onClick={handleRun} variant="primary">
Run
</Message.ActionCard.Button>
</Message.ActionCard>
<Message.Promotion
promotionText="Go learn more about this skill!"
promotionUrl="https://learn.mongodb.com/skills"
onPromotionLinkClick={handlePromotionClick}
/>
<Message.Actions
onClickCopy={handleCopy}
onClickRetry={handleRetry}
onRatingChange={handleRatingChange}
onSubmitFeedback={handleSubmitFeedback}
/>
<Message.VerifiedBanner
verifier="MongoDB Staff"
verifiedAt={new Date('2024-03-24T16:20:00Z')}
learnMoreUrl="https://mongodb.com/docs"
/>
<Message.Links links={links} onLinkClick={handleLinkClick} />
</Message>
);
};Properties
Message
| Prop | Type | Description | Default |
| --------------- | --------------------------------------------------------------------------------------------------- | --------------------------------------------------- | ------- |
| isSender | boolean | Indicates if the message is from the current user | true |
| markdownProps | LGMarkdownProps | Props passed to the internal ReactMarkdown instance | |
| messageBody | string | Message body text passed to LGMarkdown | |
| sourceType | 'markdown' \| 'text' | Determines the rendering method of the message | |
| ... | HTMLElementProps<'div'> | Props spread on the root element | |
Message.Actions
| Prop | Type | Description | Default |
| ------------------------------- | ----------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------- |
| errorMessage (optional) | ReactNode | Optional error message to display when feedback submission fails. | 'Oops, please try again.' |
| onClickCopy (optional) | MouseEventHandler<HTMLButtonElement> | Callback fired when the copy button is clicked. | |
| onClickRetry (optional) | MouseEventHandler<HTMLButtonElement> | Callback fired when the retry button is clicked. If not provided, the retry button will not be rendered | |
| onCloseFeedback (optional) | MouseEventHandler<HTMLButtonElement> | Callback fired when the feedback form is closed by clicking the close button | |
| onRatingChange (optional) | (e: ChangeEvent<HTMLInputElement>, options: { rating: MessageRatingValue }) => void | Callback fired when the user clicks the like or dislike button. Receives the original change event and an options object with the rating. If not provided, the rating buttons will not be rendered | |
| onSubmitFeedback (optional) | (e: FormEvent<HTMLFormElement>, options: { rating: MessageRatingValue; feedback?: string }) => void | Callback when the user submits the feedback form. Receives the original form event, plus an options object with rating and feedback. If not provided, the feedback form will not be rendered | |
| submitButtonText (optional) | string | Optional text for the feedback form's submit button | 'Submit' |
| submittedMessage (optional) | ReactNode | Optional success message to display after feedback is submitted. | 'Thanks for your feedback!' |
Message.Links
| Prop | Type | Description | Default |
| -------------------------- | -------------------------------- | ------------------------------------------------------------ | --------------------- |
| headingText (optional) | string | The text to display as the heading of the links section. | 'Related Resources' |
| links | Array<RichLinkProps> | An array of link data to render in the links section. | |
| onLinkClick (optional) | ({ children: string }) => void | A callback function that is called when any link is clicked. | |
| ... | HTMLElementProps<'div'> | Props spread on the root element | |
Message.Promotion
| Prop | Type | Description | Default |
| ----------------------------------- | ------------------------- | ---------------------------------------- | ------- |
| promotionText | string | Promotion text content. | |
| promotionUrl | string | Promotion URL for the "Learn More" link. | |
| onPromotionLinkClick (optional) | () => void | Promotion onClick callback handler. | |
| ... | HTMLElementProps<'div'> | Props spread on the root element | |
Message.ActionCard
| Prop | Type | Description | Default |
| -------------------------------- | ------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
| chips (optional) | Array<{ label: ReactNode; glyph?: ReactNode; formatTooltip?: (label: ReactNode) => ReactNode }> | Metadata chips displayed in the header. Each chip supports label, optional glyph icon, and optional formatTooltip function to customize tooltip content. Tooltips are always shown on hover for chips in ActionCard. | [] |
| darkMode (optional) | boolean | Determines if the component will render in dark mode | false |
| description (optional) | ReactNode | Optional description text displayed below the title row in the Header. | |
| initialIsExpanded (optional) | boolean | Initial state of the expandable section. Ignored when showExpandButton is false. | false |
| onToggleExpanded (optional) | (isOpen: boolean) => void | Callback fired when the expansion toggle is clicked. Receives the new open state as a parameter. | |
| showExpandButton (optional) | boolean | Whether the toggle button is visible. | true |
| state | 'idle' \| 'running' \| 'success' \| 'error' \| 'canceled' | The current lifecycle state of the interaction. Can use string literals or import ActionCardState enum from @lg-chat/message. | |
| title | ReactNode | Primary label displayed in the Header. | |
| ... | HTMLElementProps<'div'> | Props spread on the container div element | |
Message.ActionCard.Button
Message.ActionCard.Button accepts all props from @leafygreen-ui/button, except for the size prop which cannot be configured and is always set to 'small'.
Message.ActionCard.ExpandableContent
| Prop | Type | Description | Default |
| ---------- | ------------------------- | ---------------------------------------------------------------------------- | ------- |
| children | string | Markdown content to render in the expandable content area. Must be a string. | |
| ... | HTMLElementProps<'div'> | Props spread on the container div element | |
Message.VerifiedBanner
| Prop | Type | Description | Default |
| --------------------------- | -------- | ------------------------------------------------- | ------- |
| learnMoreUrl (optional) | string | URL to learn more about the verification. | |
| verifiedAt (optional) | Date | The time the message was last verified. | |
| verifier (optional) | string | The name of the entity that verified the message. | |
Behavior
Message.Actions
The Message.Actions component provides a comprehensive set of actions for chat messages.
Rendering Behavior
- If only some optional props are provided, only those corresponding buttons/functionality are rendered:
- Copy Button: Always renders
- Retry Button: Renders when
onClickRetryis provided - Rating Buttons: Render when
onRatingChangeis provided - Feedback Form: Shows when a rating is selected and
onSubmitFeedbackis provided
Context Integration
The component relies on MessageContext to access the message body for copy functionality.
Rating and Feedback Flow
Rating Only: User clicks thumbs up or thumbs down (only visible when
onRatingChangeis provided)- Calls
onRatingChangewith the selected rating - No feedback form appears
- Calls
Rating + Feedback: User clicks thumbs up or thumbs down, then feedback form appears
- Calls
onRatingChangewith the selected rating - Feedback form appears with a textarea for additional comments
- User can optionally provide feedback text
- User clicks submit to send feedback
- Form shows success message and calls
onSubmitFeedbackwith rating and feedback
- Calls
State Management
The component manages its own internal state for:
- Message rating:
'unselected'|'disliked'|'liked' - Feedback text input
- Form state:
'unset'|'submitting'|'submitted'|'error'
Form state is reset when the feedback form is closed or when a new rating is selected.
Message.Links
The Message.Links component provides an expandable/collapsible section for displaying related links.
Rendering Behavior
- If the
linksarray is empty, the component returnsnulland does not render anything - Links are displayed in an expandable section with a chevron toggle button
- The section starts collapsed by default and can be expanded to show all links
- The link objects provided to the
Message.Linkscomponent'slinksprop can leverage the followingvariantvalues which map to pre-defined badge glyphs, labels, and colors."Blog""Book""Code""Docs""Learn""Video""Website"
State Management
The component manages its own internal state for:
- Expansion state: Controls whether the links section is expanded or collapsed
Message.Promotion
The Message.Promotion component displays promotional content with an award icon and "Learn More" link.
Message.ActionCard
The Message.ActionCard component displays actionable, stateful cards with expandable content and buttons.
Rendering Behavior
- Buttons only render when
state="idle" - Multiple
Message.ActionCard.Buttoncomponents can be used to render buttons
State Management
The component manages its own internal state for:
- Expansion state: Controls whether the expandable content section is expanded or collapsed
- The expansion state can be configured via
initialIsExpandedandonToggleExpandedprops
Message.VerifiedBanner
The Message.VerifiedBanner component displays verification information for messages.
Rendering Behavior
- If no verification props (
verifier,verifiedAt,learnMoreUrl) are provided, basic "Verified" text is displayed - The banner automatically formats the verification date using
toLocaleDateString() - "Learn More" link is displayed when optional
learnMoreUrlis provided
