diff --git a/packages/module/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithSources.tsx b/packages/module/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithSources.tsx index 060252ee6..b29962c00 100644 --- a/packages/module/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithSources.tsx +++ b/packages/module/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithSources.tsx @@ -1,6 +1,8 @@ import { FunctionComponent, MouseEvent as ReactMouseEvent, KeyboardEvent as ReactKeyboardEvent } from 'react'; import Message from '@patternfly/chatbot/dist/dynamic/Message'; import patternflyAvatar from './patternfly_avatar.jpg'; +import { Button, Flex, FlexItem, Label, Popover } from '@patternfly/react-core'; +import { OutlinedQuestionCircleIcon } from '@patternfly/react-icons'; export const MessageWithSourcesExample: FunctionComponent = () => { const onSetPage = (_event: ReactMouseEvent | ReactKeyboardEvent | MouseEvent, newPage: number) => { @@ -8,8 +10,76 @@ export const MessageWithSourcesExample: FunctionComponent = () => { console.log(`Page changed to ${newPage}`); }; + const date = new Date(); + + const datePart = date.toLocaleDateString('en', { + year: 'numeric', + month: 'short', + day: 'numeric' + }); + + const timePart = date.toLocaleTimeString('en', { + hour: '2-digit', + minute: '2-digit', + hour12: true + }); + + const formattedDate = `${datePart}, ${timePart}`; + return ( <> + + + + + + + + + + + + Why this confidence score? + + } + bodyContent={ + <> + A high confidence score indicates a strong match. The system found significant overlap in + key data points, including text content, names, dates, and organizational details, with a + high degree of certainty. This match is highly reliable. + + } + > + + + + + + {`Last updated: ${formattedDate}`} + + ) + } + ] + }} + /> { ); expect(screen.getByRole('link', { name: /How to make an apple pie/i })).toHaveClass('test'); }); + + it('should apply cardTitleProps appropriately', () => { + render( + + ); + expect(screen.getByTestId('card-title')).toHaveClass('test'); + }); + + it('should apply cardBodyProps appropriately', () => { + render( + + ); + expect(screen.getByTestId('card-body')).toHaveClass('test'); + }); + + it('should apply cardFooterProps appropriately', () => { + render( + + ); + expect(screen.getByTestId('card-footer')).toHaveClass('test'); + }); + + it('should apply truncateProps appropriately', () => { + render( + + ); + expect(screen.getByTestId('card-truncate')).toHaveClass('test'); + }); + + it('should apply custom footer appropriately when there is one source', () => { + render( + I am a custom footer }]} /> + ); + expect(screen.getByText('I am a custom footer')); + expect(screen.queryByText('1/1')).toBeFalsy(); + }); + + it('should apply custom footer appropriately when are multiple sources', () => { + render( + I am a custom footer }, + { title: 'How to bake bread', link: '' } + ]} + /> + ); + expect(screen.getByText('I am a custom footer')); + // does not show navigation bar + expect(screen.queryByText('1/2')).toBeFalsy(); + }); + + it('should apply footer props to custom footer appropriately', () => { + render( + I am a custom footer }]} + /> + ); + expect(screen.getByText('I am a custom footer')); + expect(screen.getByTestId('card-footer')).toHaveClass('test'); + }); + + it('should apply subtitle appropriately', () => { + render( + + ); + expect(screen.getByText('How to make an apple pie')); + expect(screen.getByText('You must first create the universe')); + }); }); diff --git a/packages/module/src/SourcesCard/SourcesCard.tsx b/packages/module/src/SourcesCard/SourcesCard.tsx index c589ef5ba..446db1d28 100644 --- a/packages/module/src/SourcesCard/SourcesCard.tsx +++ b/packages/module/src/SourcesCard/SourcesCard.tsx @@ -10,14 +10,18 @@ import { ButtonVariant, Card, CardBody, + CardBodyProps, CardFooter, + CardFooterProps, CardProps, CardTitle, + CardTitleProps, ExpandableSection, ExpandableSectionVariant, Icon, pluralize, - Truncate + Truncate, + TruncateProps } from '@patternfly/react-core'; import { ExternalLinkSquareAltIcon } from '@patternfly/react-icons'; @@ -34,6 +38,8 @@ export interface SourcesCardProps extends CardProps { sources: { /** Title of sources card */ title?: string; + /** Subtitle of sources card */ + subtitle?: string; /** Link to source */ link: string; /** Body of sources card */ @@ -46,6 +52,10 @@ export interface SourcesCardProps extends CardProps { onClick?: React.MouseEventHandler; /** Any additional props applied to the title of the Sources card */ titleProps?: ButtonProps; + /** Custom footer applied to the Sources card */ + footer?: React.ReactNode; + /** Additional props passed to Truncate component */ + truncateProps?: TruncateProps; }[]; /** Label for the English word "source" */ sourceWord?: string; @@ -65,6 +75,12 @@ export interface SourcesCardProps extends CardProps { showMoreWords?: string; /** Label for English words "show less" */ showLessWords?: string; + /** Additional props passed to card title */ + cardTitleProps?: CardTitleProps; + /** Additional props passed to card body */ + cardBodyProps?: CardBodyProps; + /** Additional props passed to card footer */ + cardFooterProps?: CardFooterProps; } const SourcesCard: FunctionComponent = ({ @@ -82,6 +98,9 @@ const SourcesCard: FunctionComponent = ({ showMoreWords = 'show more', showLessWords = 'show less', isCompact, + cardTitleProps, + cardBodyProps, + cardFooterProps, ...props }: SourcesCardProps) => { const [page, setPage] = useState(1); @@ -96,9 +115,9 @@ const SourcesCard: FunctionComponent = ({ onSetPage && onSetPage(_evt, newPage); }; - const renderTitle = (title?: string) => { + const renderTitle = (title?: string, truncateProps?: TruncateProps) => { if (title) { - return ; + return ; } return `Source ${page}`; }; @@ -107,24 +126,32 @@ const SourcesCard: FunctionComponent = ({
{pluralize(sources.length, sourceWord, sourceWordPlural)} - - + +
+ + {sources[page - 1].subtitle && ( + {sources[page - 1].subtitle} + )} +
{sources[page - 1].body && ( - + {sources[page - 1].hasShowMore ? ( // prevents extra VO announcements of button text - parent Message has aria-live
@@ -143,68 +170,77 @@ const SourcesCard: FunctionComponent = ({ )} )} - {sources.length > 1 && ( - -
- -
+ {sources[page - 1].footer ? ( + + {sources[page - 1].footer} + ) : ( + sources.length > 1 && ( + +
+ +
+
+ ) )}