Skip to content

Commit

Permalink
Merge pull request #377 from rebeccaalpert/quickstartstile
Browse files Browse the repository at this point in the history
feat(QuickStartsTile): Add quick starts tile for messages
  • Loading branch information
nicolethoen authored Jan 7, 2025
2 parents 6accb95 + 93660e4 commit 9653fd2
Show file tree
Hide file tree
Showing 15 changed files with 849 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React from 'react';
import Message from '@patternfly/chatbot/dist/dynamic/Message';
import patternflyAvatar from './patternfly_avatar.jpg';
import { explorePipelinesQuickStart } from './explore-pipeline-quickstart.ts';
import { monitorSampleAppQuickStart } from '@patternfly/chatbot/src/Message/QuickStarts/monitor-sampleapp-quickstart.ts';
import { QuickStart } from '@patternfly/chatbot/dist/esm/Message/QuickStarts/types';

export const MessageWithQuickStartExample: React.FunctionComponent = () => (
<>
<Message
name="Bot"
role="bot"
avatar={patternflyAvatar}
content="Text-based bot message with a quick start tile."
quickStarts={{
quickStart: explorePipelinesQuickStart as QuickStart,
onSelectQuickStart: (id) => alert(id)
}}
/>
<Message
name="Bot"
role="bot"
avatar={patternflyAvatar}
content="Text-based bot message with a quick start tile that includes prerequisites and a default icon."
quickStarts={{
quickStart: monitorSampleAppQuickStart,
onSelectQuickStart: (id) => alert(id)
}}
/>
</>
);
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ import FileDetailsLabel from '@patternfly/chatbot/dist/dynamic/FileDetailsLabel'
import FileDropZone from '@patternfly/chatbot/dist/dynamic/FileDropZone';
import { PreviewAttachment } from '@patternfly/chatbot/dist/dynamic/PreviewAttachment';
import ChatbotAlert from '@patternfly/chatbot/dist/dynamic/ChatbotAlert';

import { explorePipelinesQuickStart } from './explore-pipeline-quickstart.ts';
import { monitorSampleAppQuickStart } from '@patternfly/chatbot/src/Message/QuickStarts/monitor-sampleapp-quickstart.ts';
import userAvatar from './user_avatar.svg';
import squareImg from './PF-social-color-square.svg';

Expand Down Expand Up @@ -116,6 +117,16 @@ The API for a source requires a link at minimum, but we strongly recommend provi

```

### Messages with quick start tiles

[Quick start](/extensions/quick-starts/) tiles can be added to messages via the `quickStarts` prop. Users can initiate the quick start from a link within the message tile.

The quick start tile displayed below the message is based on the tile included in the [PatternFly quick starts extension](https://github.com/patternfly/patternfly-quickstarts), but with slightly more limited functionality. For example, it does not track the state of the extension. However, it supports an additional `onSelectQuickStart` prop, so that the name of the quick start can be captured on click. This can be used to trigger other behavior in your application, such as launching that quick start in your main UI.

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

```

### User messages

Messages from users have a different background color to differentiate them from bot messages. You can also display a custom avatar that is uploaded by the user. You can further customize the avatar by applying an additional class or passing [PatternFly avatar props](/components/avatar) to the `<Message>` component via `avatarProps`.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import userAvatar from './user_avatar.svg';

export const explorePipelinesQuickStart = {
apiVersion: 'console.openshift.io/v1',
kind: 'QuickStarts',
metadata: {
name: 'explore-pipelines'
},
spec: {
version: 4.7,
displayName: `Installing the Pipelines Operator`,
durationMinutes: 10,
icon: userAvatar,
description: `Install the OpenShift® Pipelines Operator to build Pipelines using Tekton.`,
prerequisites: [''],
introduction: `OpenShift® Pipelines is a cloud-native, continuous integration and continuous delivery (CI/CD) solution based on Kubernetes resources. It uses Tekton building blocks to automate deployments across multiple Kubernetes distributions by abstracting away the underlying implementation details.
* OpenShift Pipelines is a serverless CI/CD system that runs pipelines with all the required dependencies in isolated containers.
* They are designed for decentralized teams that work on a microservice-based architecture.
* They are defined using standard Custom Resource Definitions making them extensible and easy to integrate with the existing Kubernetes tools. This enables you to scale on-demand.
* You can use OpenShift Pipelines to build images with Kubernetes tools such as Source-to-Image (S2I), Buildah, Buildpacks, and Kaniko that are portable across any Kubernetes platform.
* You can use the Developer perspective to create and manage pipelines and view logs in your namespaces.
To start using Pipelines, install the OpenShift® Pipelines Operator on your cluster.`,
tasks: [
{
title: `Installing the OpenShift Pipelines Operator`,
description: `### To install the OpenShift Pipelines Operator:
1. From the **Administrator** perspective in the console navigation panel, click **Operators > OperatorHub**.
2. In the **Filter by keyword** field, type \`OpenShift Pipelines Operator\`.
3. If the tile has an Installed label, the Operator is already installed. Proceed to the next quick start to create a Pipeline.
4. Click the **tile** to open the Operator details.
5. At the top of the OpenShift Pipelines Operator panel that opens, click **Install**.
6. Fill out the Operator subscription form by selecting the channel that matches your OpenShift cluster, and then click **Install**.
7. On the **Installed Operators** page, wait for the OpenShift Pipelines Operator's status to change from **Installing** to **Succeeded**. `,
review: {
instructions: `#### To verify that the OpenShift Pipelines Operator is installed:
1. From the **Operators** section of the navigation, go to the **Installed Operators** page.
2. Verify that the **OpenShift Pipelines Operator** appears in the list of Operators.
In the status column, is the status of the OpenShift Pipelines Operator **Succeeded**?`,
failedTaskHelp: `This task isn’t verified yet. Try the task again, or [read more](https://docs.openshift.com/container-platform/4.6/pipelines/installing-pipelines.html#op-installing-pipelines-operator-in-web-console_installing-pipelines) about this topic.`
},
summary: {
success: `You have installed the Pipelines Operator!`,
failed: `Try the steps again.`
}
}
],
conclusion: `You successfully installed the OpenShift Pipelines Operator! If you want to learn how to deploy an application and associate a Pipeline with it, take the Creating a Pipeline quick start.`,
nextQuickStart: [`install-app-and-associate-pipeline`],
accessReviewResources: [
{
group: 'operators.coreos.com',
resource: 'operatorgroups',
verb: 'list'
},
{
group: 'packages.operators.coreos.com',
resource: 'packagemanifests',
verb: 'list'
}
]
}
};
54 changes: 54 additions & 0 deletions packages/module/src/Message/Message.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom';
import Message from './Message';
import userEvent from '@testing-library/user-event';
import { monitorSampleAppQuickStart } from './QuickStarts/monitor-sampleapp-quickstart';
import { monitorSampleAppQuickStartWithImage } from './QuickStarts/monitor-sampleapp-quickstart-with-image';

const ALL_ACTIONS = [
{ label: /Good response/i },
Expand Down Expand Up @@ -437,4 +439,56 @@ describe('Message', () => {
expect(screen.getByRole('img')).toHaveClass('test');
expect(screen.getByRole('img')).toHaveClass('pf-chatbot__message-avatar');
});
it('should handle QuickStart tile correctly', () => {
render(
<Message
avatar="./img"
role="user"
name="User"
content="Hi"
quickStarts={{
quickStart: monitorSampleAppQuickStart,
onSelectQuickStart: (id) => alert(id)
}}
/>
);
expect(screen.getByRole('button', { name: 'Monitoring your sample application' })).toBeTruthy();
expect(screen.getByRole('heading', { name: '1 Prerequisite' })).toBeTruthy();
expect(screen.getByRole('button', { name: 'Show prerequisites' })).toBeTruthy();
expect(screen.getByRole('button', { name: 'Start' })).toBeTruthy();
});
it('should handle click on QuickStart tile correctly', async () => {
const spy = jest.fn();
render(
<Message
avatar="./img"
role="user"
name="User"
content="Hi"
quickStarts={{
quickStart: monitorSampleAppQuickStart,
onSelectQuickStart: (id) => spy(id)
}}
/>
);
await userEvent.click(screen.getByRole('button', { name: 'Monitoring your sample application' }));
expect(spy).toHaveBeenCalledTimes(1);
expect(spy).toHaveBeenCalledWith(monitorSampleAppQuickStart.metadata.name);
});
it('should handle QuickStart tile with image correctly', async () => {
const spy = jest.fn();
render(
<Message
avatar="./img"
role="user"
name="User"
content="Hi"
quickStarts={{
quickStart: monitorSampleAppQuickStartWithImage,
onSelectQuickStart: (id) => spy(id)
}}
/>
);
expect(screen.getAllByRole('img')[1]).toHaveAttribute('src', 'test.png');
});
});
27 changes: 27 additions & 0 deletions packages/module/src/Message/Message.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import SourcesCard, { SourcesCardProps } from '../SourcesCard';
import ListItemMessage from './ListMessage/ListItemMessage';
import UnorderedListMessage from './ListMessage/UnorderedListMessage';
import OrderedListMessage from './ListMessage/OrderedListMessage';
import QuickStartTile from './QuickStarts/QuickStartTile';
import { QuickStart, QuickstartAction } from './QuickStarts/types';

export interface QuickResponse extends Omit<LabelProps, 'children'> {
content: string;
Expand Down Expand Up @@ -89,6 +91,19 @@ export interface MessageProps extends Omit<React.HTMLProps<HTMLDivElement>, 'rol
hasRoundAvatar?: boolean;
/** Any additional props applied to the avatar, for additional customization */
avatarProps?: Omit<AvatarProps, 'alt'>;
/** Props for QuickStart card */
quickStarts?: {
quickStart: QuickStart;
onSelectQuickStart: (id?: string) => void;
minuteWord?: string;
minuteWordPlural?: string;
prerequisiteWord?: string;
prerequisiteWordPlural?: string;
quickStartButtonAriaLabel?: string;
className?: string;
onClick?: () => void;
action?: QuickstartAction;
};
}

export const Message: React.FunctionComponent<MessageProps> = ({
Expand All @@ -108,6 +123,7 @@ export const Message: React.FunctionComponent<MessageProps> = ({
attachments,
hasRoundAvatar = true,
avatarProps,
quickStarts,
...props
}: MessageProps) => {
let avatarClassName;
Expand Down Expand Up @@ -165,6 +181,17 @@ export const Message: React.FunctionComponent<MessageProps> = ({
</Markdown>
)}
{!isLoading && sources && <SourcesCard {...sources} />}
{quickStarts && quickStarts.quickStart && (
<QuickStartTile
quickStart={quickStarts.quickStart}
onSelectQuickStart={quickStarts.onSelectQuickStart}
minuteWord={quickStarts.minuteWord}
minuteWordPlural={quickStarts.minuteWordPlural}
prerequisiteWord={quickStarts.prerequisiteWord}
prerequisiteWordPlural={quickStarts.prerequisiteWordPlural}
quickStartButtonAriaLabel={quickStarts.quickStartButtonAriaLabel}
/>
)}
{!isLoading && actions && <ResponseActions actions={actions} />}
{!isLoading && quickResponses && (
<LabelGroup
Expand Down
24 changes: 24 additions & 0 deletions packages/module/src/Message/QuickStarts/FallbackImg.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import * as React from 'react';

interface FallbackImgProps {
/** Image source */
src: string;
/** Alt text for image */
alt?: string;
/** ClassName applied to image */
className?: string;
/** Fallback */
fallback?: React.ReactNode;
}

const FallbackImg: React.FC<FallbackImgProps> = ({ src, alt, className, fallback }) => {
const [isSrcValid, setIsSrcValid] = React.useState<boolean>(true);

if (src && isSrcValid) {
return <img className={className} src={src} alt={alt} onError={() => setIsSrcValid(false)} />;
}

return <>{fallback}</>;
};

export default FallbackImg;
25 changes: 25 additions & 0 deletions packages/module/src/Message/QuickStarts/QuickStartTile.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
.pf-chatbot__quickstarts-tile {
min-width: 360px;
max-width: 650px;
width: 100%;

@media screen and (max-width: 700px) {
max-width: 100%;
min-width: initial;
}
// some icons provided to catalog tiles might have no defined height/width. Without this style, in those cases
// the icons would have a height and width of 0.
.pf-v6-c-card__header-main {
.pf-v6-c-icon__content {
display: contents;
}
}
}

.pf-v6-theme-dark {
.pf-chatbot__quickstarts-tile {
.pfext-catalog-item-icon__img {
filter: brightness(1.5) invert(1) hue-rotate(180deg) saturate(4);
}
}
}
Loading

0 comments on commit 9653fd2

Please sign in to comment.