Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -1,15 +1,85 @@
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) => {
// eslint-disable-next-line no-console
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 (
<>
<Message
name="Bot"
role="bot"
avatar={patternflyAvatar}
content="This example has a custom subtitle and footer with no pagination"
sources={{
sources: [
{
title: 'Getting started with Red Hat OpenShift',
subtitle: 'Red Hat knowledge base',
link: '#',
body: 'Red Hat OpenShift on IBM Cloud is a managed offering to create your own cluster of compute hosts where you can deploy and manage containerized apps on IBM Cloud ...',
isExternal: true,
footer: (
<Flex className="pf-chatbot__sources-card-subtle" gap={{ default: 'gapXs' }}>
<FlexItem alignSelf={{ default: 'alignSelfStretch' }}>
<Flex justifyContent={{ default: 'justifyContentSpaceBetween' }}>
<FlexItem>
<Label color="green">Confidence 93%</Label>
</FlexItem>
<FlexItem>
<Popover
headerContent={
<Flex gap={{ default: 'gapXs' }}>
<FlexItem>
<OutlinedQuestionCircleIcon />
</FlexItem>
<FlexItem>Why this confidence score?</FlexItem>
</Flex>
}
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.
</>
}
>
<Button variant="link" icon={<OutlinedQuestionCircleIcon />}>
Learn about this score
</Button>
</Popover>
</FlexItem>
</Flex>
</FlexItem>
<FlexItem>{`Last updated: ${formattedDate}`}</FlexItem>
</Flex>
)
}
]
}}
/>
<Message
name="Bot"
role="bot"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import Message from '@patternfly/chatbot/dist/dynamic/Message';
import MessageDivider from '@patternfly/chatbot/dist/dynamic/MessageDivider';
import { rehypeCodeBlockToggle } from '@patternfly/chatbot/dist/esm/Message/Plugins/rehypeCodeBlockToggle';
import SourcesCard from '@patternfly/chatbot/dist/dynamic/SourcesCard';
import { ArrowCircleDownIcon, ArrowRightIcon, CheckCircleIcon, CubeIcon, CubesIcon, DownloadIcon, InfoCircleIcon, RedoIcon, RobotIcon } from '@patternfly/react-icons';
import { ArrowCircleDownIcon, ArrowRightIcon, CheckCircleIcon, CubeIcon, CubesIcon, DownloadIcon, InfoCircleIcon, OutlinedQuestionCircleIcon, RedoIcon, RobotIcon } from '@patternfly/react-icons';
import patternflyAvatar from './patternfly_avatar.jpg';
import AttachmentEdit from '@patternfly/chatbot/dist/dynamic/AttachmentEdit';
import FileDetails from '@patternfly/chatbot/dist/dynamic/FileDetails';
Expand Down
17 changes: 17 additions & 0 deletions packages/module/src/SourcesCard/SourcesCard.scss
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,17 @@
box-shadow: var(--pf-t--global--box-shadow--sm);
}

.pf-chatbot__compact-sources-card-body {
--pf-v6-c-card--child--PaddingBlockEnd: var(--pf-t--global--spacer--xs);
}

.pf-chatbot__sources-card-subtitle,
.pf-chatbot__sources-card-subtle {
color: var(--pf-t--global--text--color--subtle);
font-size: var(--pf-t--global--font--size--body--sm);
font-weight: var(--pf-t--global--font--weight--body--default);
}

.pf-chatbot__sources-card-body-text {
display: block;
display: -webkit-box;
Expand All @@ -27,6 +38,12 @@
text-overflow: ellipsis;
}

.pf-chatbot__sources-card-title-container {
display: flex;
flex-direction: column;
gap: var(--pf-t--global--spacer--xs);
}

.pf-chatbot__sources-card-footer-container {
border-top: var(--pf-t--global--border--width--regular) solid var(--pf-t--global--border--color--default);
padding: var(--pf-t--global--spacer--sm) var(--pf-t--global--spacer--md) var(--pf-t--global--spacer--sm)
Expand Down
93 changes: 93 additions & 0 deletions packages/module/src/SourcesCard/SourcesCard.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -256,4 +256,97 @@ describe('SourcesCard', () => {
);
expect(screen.getByRole('link', { name: /How to make an apple pie/i })).toHaveClass('test');
});

it('should apply cardTitleProps appropriately', () => {
render(
<SourcesCard
cardTitleProps={{ 'data-testid': 'card-title', className: 'test' } as any}
sources={[{ title: 'How to make an apple pie', link: '' }]}
/>
);
expect(screen.getByTestId('card-title')).toHaveClass('test');
});

it('should apply cardBodyProps appropriately', () => {
render(
<SourcesCard
cardBodyProps={
{ 'data-testid': 'card-body', body: 'To make an apple pie, you must first...', className: 'test' } as any
}
sources={[{ title: 'How to make an apple pie', link: '', body: 'To make an apple pie, you must first...' }]}
/>
);
expect(screen.getByTestId('card-body')).toHaveClass('test');
});

it('should apply cardFooterProps appropriately', () => {
render(
<SourcesCard
cardFooterProps={{ 'data-testid': 'card-footer', className: 'test' } as any}
sources={[
{ title: 'How to make an apple pie', link: '' },
{ title: 'How to make cookies', link: '' }
]}
/>
);
expect(screen.getByTestId('card-footer')).toHaveClass('test');
});

it('should apply truncateProps appropriately', () => {
render(
<SourcesCard
sources={[
{
title: 'How to make an apple pie',
link: '',
truncateProps: { 'data-testid': 'card-truncate', className: 'test' } as any
}
]}
/>
);
expect(screen.getByTestId('card-truncate')).toHaveClass('test');
});

it('should apply custom footer appropriately when there is one source', () => {
render(
<SourcesCard sources={[{ title: 'How to make an apple pie', link: '', footer: <>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(
<SourcesCard
sources={[
{ title: 'How to make an apple pie', link: '', footer: <>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(
<SourcesCard
cardFooterProps={{ 'data-testid': 'card-footer', className: 'test' } as any}
sources={[{ title: 'How to make an apple pie', link: '', footer: <>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(
<SourcesCard
sources={[{ title: 'How to make an apple pie', link: '', subtitle: 'You must first create the universe' }]}
/>
);
expect(screen.getByText('How to make an apple pie'));
expect(screen.getByText('You must first create the universe'));
});
});
Loading
Loading