Skip to content
Open
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
Expand Up @@ -78,6 +78,37 @@ export const MessageWithSourcesExample: FunctionComponent = () => {
]
}}
/>
<Message
name="Bot"
role="bot"
content="This example has a PatternFly label above the title:"
sources={{
sources: [
{
title: 'Getting started with Red Hat OpenShift',
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,
headerContent: <Label>Documentation</Label>
},
{
title: 'Azure Red Hat OpenShift documentation',
link: '#',
body: 'Microsoft Azure Red Hat OpenShift allows you to deploy a production ready Red Hat OpenShift cluster in Azure ...',
isExternal: true,
headerContent: <Label>Knowledge base</Label>
},
{
title: 'OKD Documentation: Home',
link: '#',
body: 'OKD is a distribution of Kubernetes optimized for continuous application development and multi-tenant deployment. OKD also serves as the upstream code base upon ...',
isExternal: true,
headerContent: <Label>Tutorial</Label>
}
],
onSetPage
}}
/>
<Message
name="Bot"
role="bot"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,8 @@ If a source will open outside of the ChatBot window, add an external link icon v

The API for a source requires a link at minimum, but we strongly recommend providing a more descriptive title and body description so users have enough context. For the best clarity and readability, we strongly recommend limiting the title to 1 line and the body to 2 lines. If the body description is more than 2 lines, use the "long sources" or "very long sources" variant.

To display extra content above a source title, such as a [PatternFly label](/components/label), pass `headerContent`.

```js file="./MessageWithSources.tsx"

```
Expand Down
6 changes: 6 additions & 0 deletions packages/module/src/SourcesCard/SourcesCard.scss
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@
text-overflow: ellipsis;
}

.pf-chatbot__sources-header-content-container {
display: inline-flex;
width: fit-content;
margin-block-end: var(--pf-t--global--spacer--sm);
}

.pf-chatbot__sources-card-title-container {
display: flex;
flex-direction: column;
Expand Down
16 changes: 16 additions & 0 deletions packages/module/src/SourcesCard/SourcesCard.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,22 @@ describe('SourcesCard', () => {
screen.getByRole('button', { name: /Go to next page/i });
});

it('should render headerContent above the title', () => {
render(
<SourcesCard
sources={[
{
title: 'How to make an apple pie',
link: '',
headerContent: <span>Dessert</span>
}
]}
/>
);
expect(screen.getByText('Dessert')).toBeTruthy();
expect(screen.getByText('How to make an apple pie')).toBeTruthy();
});

it('should render with wrap layout when layout is set to wrap', () => {
render(
<SourcesCard
Expand Down
2 changes: 2 additions & 0 deletions packages/module/src/SourcesCard/SourcesCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ export interface SourcesCardProps extends CardProps {
footer?: React.ReactNode;
/** Additional props passed to Truncate component */
truncateProps?: TruncateProps;
/** Additional content applied to the beginning of the sources card header, such as a label */
headerContent?: React.ReactNode;
}[];
/** Label for the English word "source" */
sourceWord?: string;
Expand Down
62 changes: 62 additions & 0 deletions packages/module/src/SourcesCardBase/SourcesCardBase.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,68 @@ describe('SourcesCardBase', () => {
expect(screen.getByRole('link', { name: /How to make an apple pie/i })).toHaveClass('test');
});

it('should render headerContent above the title', () => {
render(
<SourcesCardBase
sources={[
{
title: 'How to make an apple pie',
link: '',
headerContent: <span>Dessert</span>
}
]}
/>
);
expect(screen.getByText('Dessert')).toBeTruthy();
expect(screen.getByText('How to make an apple pie')).toBeTruthy();
});

it('should update headerContent when navigating between sources', async () => {
render(
<SourcesCardBase
sources={[
{
title: 'How to make an apple pie',
link: '',
headerContent: <span>Dessert</span>
},
{
title: 'How to make cookies',
link: '',
headerContent: <span>Snack</span>
}
]}
/>
);
expect(screen.getByText('Dessert')).toBeTruthy();
expect(screen.queryByText('Snack')).toBeFalsy();
await userEvent.click(screen.getByRole('button', { name: /Go to next page/i }));
expect(screen.queryByText('Dessert')).toBeFalsy();
expect(screen.getByText('Snack')).toBeTruthy();
});

it('should render headerContent when using wrap layout', () => {
render(
<SourcesCardBase
layout="wrap"
sources={[
{
title: 'How to make an apple pie',
link: '',
headerContent: <span>Dessert</span>
},
{
title: 'How to make cookies',
link: '',
headerContent: <span>Snack</span>
}
]}
/>
);
expect(screen.getByText('Dessert')).toBeTruthy();
expect(screen.getByText('Snack')).toBeTruthy();
});

it('should render with wrap layout when layout prop is set to wrap', () => {
render(
<SourcesCardBase
Expand Down
8 changes: 8 additions & 0 deletions packages/module/src/SourcesCardBase/SourcesCardBase.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ export interface SourcesCardBaseProps extends CardProps {
footer?: React.ReactNode;
/** Additional props passed to Truncate component */
truncateProps?: TruncateProps;
/** Additional content applied to the beginning of the sources card header, such as a label */
headerContent?: React.ReactNode;
}[];
/** Accessible label for the button which moves to the next page. */
toNextPageAriaLabel?: string;
Expand Down Expand Up @@ -133,6 +135,9 @@ const SourcesCardBase: FunctionComponent<SourcesCardBaseProps> = ({
>
<Card isFullHeight isCompact={isCompact} className="pf-chatbot__sources-card" {...props}>
<CardTitle className="pf-chatbot__sources-card-title" {...cardTitleProps}>
{source.headerContent && (
<div className="pf-chatbot__sources-header-content-container">{source.headerContent}</div>
)}
<div className="pf-chatbot__sources-card-title-container">
<Button
component="a"
Expand Down Expand Up @@ -196,6 +201,9 @@ const SourcesCardBase: FunctionComponent<SourcesCardBaseProps> = ({
<div className="pf-chatbot__sources-card-base">
<Card isCompact={isCompact} className="pf-chatbot__sources-card" {...props}>
<CardTitle className="pf-chatbot__sources-card-title" {...cardTitleProps}>
{sources[page - 1].headerContent && (
<div className="pf-chatbot__sources-header-content-container">{sources[page - 1].headerContent}</div>
)}
<div className="pf-chatbot__sources-card-title-container">
<Button
component="a"
Expand Down
Loading