From 2654fd0f6c1190157b49fe7809a45996ae8161b7 Mon Sep 17 00:00:00 2001 From: Nick Larew Date: Mon, 9 Jun 2025 16:31:18 -0500 Subject: [PATCH 1/3] LG-5307: [Chat] Add callback for rich link clicks --- .changeset/perfect-candles-refuse.md | 6 ++++++ chat/message/src/Message/Message.stories.tsx | 3 +++ chat/message/src/Message/Message.tsx | 2 ++ chat/message/src/Message/Message.types.ts | 5 +++++ chat/message/src/MessageLinks/MessageLinks.tsx | 3 ++- chat/message/src/MessageLinks/MessageLinks.types.ts | 5 +++++ chat/rich-links/src/RichLink/RichLink.stories.tsx | 10 ++++++++++ chat/rich-links/src/RichLink/RichLink.tsx | 3 ++- chat/rich-links/src/RichLink/RichLink.types.ts | 5 +++++ chat/rich-links/src/RichLinksArea/RichLinksArea.tsx | 9 ++++++++- .../src/RichLinksArea/RichLinksArea.types.ts | 5 +++++ packages/emotion/src/version.ts | 2 +- 12 files changed, 54 insertions(+), 4 deletions(-) create mode 100644 .changeset/perfect-candles-refuse.md diff --git a/.changeset/perfect-candles-refuse.md b/.changeset/perfect-candles-refuse.md new file mode 100644 index 0000000000..2b4f02c938 --- /dev/null +++ b/.changeset/perfect-candles-refuse.md @@ -0,0 +1,6 @@ +--- +'@lg-chat/rich-links': minor +'@lg-chat/message': minor +--- + +Add the onLinkClick prop to support callbacks whenever a rich link is clicked diff --git a/chat/message/src/Message/Message.stories.tsx b/chat/message/src/Message/Message.stories.tsx index 0a353da52c..e606e5dcad 100644 --- a/chat/message/src/Message/Message.stories.tsx +++ b/chat/message/src/Message/Message.stories.tsx @@ -199,6 +199,9 @@ WithRichLinks.args = { messageBody: MongoText, avatar: , children: , + onLinkClick: props => { + alert(`Link clicked: ${props.href}`); + }, links: [ { href: 'https://mongodb.design', diff --git a/chat/message/src/Message/Message.tsx b/chat/message/src/Message/Message.tsx index 852a82ba29..babd840121 100644 --- a/chat/message/src/Message/Message.tsx +++ b/chat/message/src/Message/Message.tsx @@ -49,6 +49,7 @@ export const Message = forwardRef( componentOverrides, links, linksHeading, + onLinkClick, markdownProps, verified, darkMode: darkModeProp, @@ -146,6 +147,7 @@ export const Message = forwardRef( as={componentOverrides?.MessageLinks ?? MessageLinks} headingText={linksHeading} links={links} + onLinkClick={onLinkClick} /> ) : null} {children} diff --git a/chat/message/src/Message/Message.types.ts b/chat/message/src/Message/Message.types.ts index 7da1e45b84..0fb220be39 100644 --- a/chat/message/src/Message/Message.types.ts +++ b/chat/message/src/Message/Message.types.ts @@ -70,6 +70,11 @@ export interface MessageProps * The heading text to display for the links section. */ linksHeading?: string; + + /** + * A callback function that is called when any link is clicked. + */ + onLinkClick?: RichLinkProps['onLinkClick']; } export interface VerificationInfo { diff --git a/chat/message/src/MessageLinks/MessageLinks.tsx b/chat/message/src/MessageLinks/MessageLinks.tsx index c79fe96718..983a23f2ed 100644 --- a/chat/message/src/MessageLinks/MessageLinks.tsx +++ b/chat/message/src/MessageLinks/MessageLinks.tsx @@ -16,6 +16,7 @@ export function MessageLinks({ darkMode: darkModeProp, headingText = 'Related Resources', links, + onLinkClick, ...divProps }: MessageLinksProps) { const { theme } = useDarkMode(darkModeProp); @@ -23,7 +24,7 @@ export function MessageLinks({

{headingText} - +
); } diff --git a/chat/message/src/MessageLinks/MessageLinks.types.ts b/chat/message/src/MessageLinks/MessageLinks.types.ts index 6e0712227b..03cc811923 100644 --- a/chat/message/src/MessageLinks/MessageLinks.types.ts +++ b/chat/message/src/MessageLinks/MessageLinks.types.ts @@ -10,6 +10,11 @@ export interface MessageLinksProps */ headingText?: string; + /** + * A callback function that is called when any link is clicked. + */ + onLinkClick?: RichLinkProps['onLinkClick']; + /** * An list of link data to render in the links section. */ diff --git a/chat/rich-links/src/RichLink/RichLink.stories.tsx b/chat/rich-links/src/RichLink/RichLink.stories.tsx index 303aacf1a8..5e78fc0f68 100644 --- a/chat/rich-links/src/RichLink/RichLink.stories.tsx +++ b/chat/rich-links/src/RichLink/RichLink.stories.tsx @@ -94,3 +94,13 @@ ExplicitlyUndefinedVariant.args = { children: 'Example Link (variant undefined)', variant: undefined, }; + +export const ClickHandler = Template.bind({}); +ClickHandler.args = { + href: 'https://example.com', + children: 'Example Link (variant undefined)', + variant: undefined, + onLinkClick: props => { + alert(`Link clicked: ${props.href}`); + }, +}; diff --git a/chat/rich-links/src/RichLink/RichLink.tsx b/chat/rich-links/src/RichLink/RichLink.tsx index 3caa71fe1a..a6d52b25fc 100644 --- a/chat/rich-links/src/RichLink/RichLink.tsx +++ b/chat/rich-links/src/RichLink/RichLink.tsx @@ -18,7 +18,7 @@ import { RichLinkBadge } from './RichLinkBadge'; import { richLinkVariants } from './richLinkVariants'; export const RichLink = forwardRef( - ({ darkMode: darkModeProp, ...props }, ref) => { + ({ darkMode: darkModeProp, onLinkClick, ...props }, ref) => { const { darkMode, theme } = useDarkMode(darkModeProp); const richLinkVariantProps = @@ -64,6 +64,7 @@ export const RichLink = forwardRef( [imageBackgroundStyles(imageUrl ?? '')]: showImageBackground, })} {...conditionalProps} + onClick={() => onLinkClick?.(props)} > {children} diff --git a/chat/rich-links/src/RichLink/RichLink.types.ts b/chat/rich-links/src/RichLink/RichLink.types.ts index 9ad7a1c183..37682f951d 100644 --- a/chat/rich-links/src/RichLink/RichLink.types.ts +++ b/chat/rich-links/src/RichLink/RichLink.types.ts @@ -15,6 +15,11 @@ export interface BaseRichLinkProps * A URL for the background image of the rich link */ imageUrl?: string; + + /** + * A callback function that is called when the link is clicked. + */ + onLinkClick?: (props: Omit) => void; } export interface RichLinkVariantControlProps { diff --git a/chat/rich-links/src/RichLinksArea/RichLinksArea.tsx b/chat/rich-links/src/RichLinksArea/RichLinksArea.tsx index cbd5e71712..c530cb7f30 100644 --- a/chat/rich-links/src/RichLinksArea/RichLinksArea.tsx +++ b/chat/rich-links/src/RichLinksArea/RichLinksArea.tsx @@ -13,6 +13,7 @@ import { type RichLinksAreaProps } from './RichLinksArea.types'; export function RichLinksArea({ links, darkMode: darkModeProp, + onLinkClick, ...props }: RichLinksAreaProps) { const { darkMode } = useDarkMode(darkModeProp); @@ -20,7 +21,13 @@ export function RichLinksArea({
{links.map(richLinkProps => { - return ; + return ( + onLinkClick?.(richLinkProps)} + {...richLinkProps} + /> + ); })}
diff --git a/chat/rich-links/src/RichLinksArea/RichLinksArea.types.ts b/chat/rich-links/src/RichLinksArea/RichLinksArea.types.ts index cb8ef2e600..249329a402 100644 --- a/chat/rich-links/src/RichLinksArea/RichLinksArea.types.ts +++ b/chat/rich-links/src/RichLinksArea/RichLinksArea.types.ts @@ -7,4 +7,9 @@ export interface RichLinksAreaProps extends HTMLElementProps<'div', never>, DarkModeProps { links: Array; + + /** + * A callback function that is called when any link is clicked. + */ + onLinkClick?: RichLinkProps['onLinkClick']; } diff --git a/packages/emotion/src/version.ts b/packages/emotion/src/version.ts index fa2f2d32cf..0026e871f3 100644 --- a/packages/emotion/src/version.ts +++ b/packages/emotion/src/version.ts @@ -1 +1 @@ -export const VERSION = '4.1.1'; +export const VERSION = '5.0.0'; From c03ba86ef8c8520f1e5ae33d8cad873cd7f7e567 Mon Sep 17 00:00:00 2001 From: Nick Larew Date: Mon, 30 Jun 2025 14:07:20 -0500 Subject: [PATCH 2/3] remove onLinkClick story --- chat/message/src/Message/Message.stories.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/chat/message/src/Message/Message.stories.tsx b/chat/message/src/Message/Message.stories.tsx index e606e5dcad..0a353da52c 100644 --- a/chat/message/src/Message/Message.stories.tsx +++ b/chat/message/src/Message/Message.stories.tsx @@ -199,9 +199,6 @@ WithRichLinks.args = { messageBody: MongoText, avatar: , children: , - onLinkClick: props => { - alert(`Link clicked: ${props.href}`); - }, links: [ { href: 'https://mongodb.design', From 643d5f006e644ccc0cdff762e03dba9b83a4068a Mon Sep 17 00:00:00 2001 From: Nick Larew Date: Mon, 30 Jun 2025 14:35:47 -0500 Subject: [PATCH 3/3] add RichLink onClick test --- .../rich-links/src/RichLink/RichLink.spec.tsx | 46 +++++++++++++++++-- .../src/RichLink/RichLink.stories.tsx | 10 ---- 2 files changed, 43 insertions(+), 13 deletions(-) diff --git a/chat/rich-links/src/RichLink/RichLink.spec.tsx b/chat/rich-links/src/RichLink/RichLink.spec.tsx index 47f12ad5aa..ffe9a8d3ca 100644 --- a/chat/rich-links/src/RichLink/RichLink.spec.tsx +++ b/chat/rich-links/src/RichLink/RichLink.spec.tsx @@ -1,9 +1,8 @@ import React from 'react'; import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; -import { RichLinkVariantName } from '../../dist'; - -import { RichLink } from '.'; +import { RichLink, type RichLinkVariantName } from '.'; describe('@lg-chat/rich-links', () => { describe('RichLink', () => { @@ -95,5 +94,46 @@ describe('@lg-chat/rich-links', () => { expect(screen.queryByText('Undefined Variant Link')).toBeInTheDocument(); }); + + it('calls the onLinkClick prop when the link is clicked', () => { + const onLinkClick = jest.fn(); + render( + <> + + Built-in Variant Link + + + Custom Link + + , + ); + const link = screen.getByText('Built-in Variant Link'); + userEvent.click(link); + expect(onLinkClick).toHaveBeenCalledWith({ + href: 'https://mongodb.design/built-in', + children: 'Built-in Variant Link', + variant: 'Website', + }); + + const customLink = screen.getByText('Custom Link'); + userEvent.click(customLink); + expect(onLinkClick).toHaveBeenCalledWith({ + href: 'https://mongodb.design/custom', + children: 'Custom Link', + badgeGlyph: 'ArrowRight', + badgeLabel: 'Custom Label', + badgeColor: 'blue', + }); + }); }); }); diff --git a/chat/rich-links/src/RichLink/RichLink.stories.tsx b/chat/rich-links/src/RichLink/RichLink.stories.tsx index 5e78fc0f68..303aacf1a8 100644 --- a/chat/rich-links/src/RichLink/RichLink.stories.tsx +++ b/chat/rich-links/src/RichLink/RichLink.stories.tsx @@ -94,13 +94,3 @@ ExplicitlyUndefinedVariant.args = { children: 'Example Link (variant undefined)', variant: undefined, }; - -export const ClickHandler = Template.bind({}); -ClickHandler.args = { - href: 'https://example.com', - children: 'Example Link (variant undefined)', - variant: undefined, - onLinkClick: props => { - alert(`Link clicked: ${props.href}`); - }, -};