Skip to content

Commit 3f32831

Browse files
authored
[LG-5362] Add chat/suggestions (#2963)
* add suggestion card * address pr comments * add dark mode for text * story updates * update lock file * update types and tests * param redesign and testing * cleanup object * fix lock file * fix and add documentatin + changeset * fix errors * lock file again * fix dependency * dependency again * rename and address comments * reorganize param * pnpm fix * fix stories * fix lint * fix merge conflict * alter naming to fit conventions
1 parent 053d32f commit 3f32831

21 files changed

+2427
-1189
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ import Button from '@leafygreen-ui/button';
158158
| [@lg-chat/message-prompts](./chat/message-prompts) | [![version](https://img.shields.io/npm/v/@lg-chat/message-prompts)](https://www.npmjs.com/package/@lg-chat/message-prompts) | ![downloads](https://img.shields.io/npm/dm/@lg-chat/message-prompts?color=white) | [Live Example](http://mongodb.design/component/message-prompts/live-example) |
159159
| [@lg-chat/message-rating](./chat/message-rating) | [![version](https://img.shields.io/npm/v/@lg-chat/message-rating)](https://www.npmjs.com/package/@lg-chat/message-rating) | ![downloads](https://img.shields.io/npm/dm/@lg-chat/message-rating?color=white) | [Live Example](http://mongodb.design/component/message-rating/live-example) |
160160
| [@lg-chat/rich-links](./chat/rich-links) | [![version](https://img.shields.io/npm/v/@lg-chat/rich-links)](https://www.npmjs.com/package/@lg-chat/rich-links) | ![downloads](https://img.shields.io/npm/dm/@lg-chat/rich-links?color=white) | [Live Example](http://mongodb.design/component/rich-links/live-example) |
161+
| [@lg-chat/suggestions](./chat/suggestions) | [![version](https://img.shields.io/npm/v/@lg-chat/suggestions)](https://www.npmjs.com/package/@lg-chat/suggestions) | ![downloads](https://img.shields.io/npm/dm/@lg-chat/suggestions?color=white) | [Live Example](http://mongodb.design/component/suggestions/live-example) |
161162
| [@lg-chat/title-bar](./chat/title-bar) | [![version](https://img.shields.io/npm/v/@lg-chat/title-bar)](https://www.npmjs.com/package/@lg-chat/title-bar) | ![downloads](https://img.shields.io/npm/dm/@lg-chat/title-bar?color=white) | [Live Example](http://mongodb.design/component/title-bar/live-example) |
162163
| [@lg-tools/build](./tools/build) | [![version](https://img.shields.io/npm/v/@lg-tools/build)](https://www.npmjs.com/package/@lg-tools/build) | ![downloads](https://img.shields.io/npm/dm/@lg-tools/build?color=white) | |
163164
| [@lg-tools/cli](./tools/cli) | [![version](https://img.shields.io/npm/v/@lg-tools/cli)](https://www.npmjs.com/package/@lg-tools/cli) | ![downloads](https://img.shields.io/npm/dm/@lg-tools/cli?color=white) | |

chat/suggestions/README.md

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
# Suggestion Card
2+
3+
![npm (scoped)](https://img.shields.io/npm/v/@lg-chat/suggestions.svg)
4+
5+
#### [View on MongoDB.design](https://www.mongodb.design/component/suggestions/live-example/)
6+
7+
## Installation
8+
9+
### PNPM
10+
11+
```shell
12+
pnpm add @lg-chat/suggestions
13+
```
14+
15+
### Yarn
16+
17+
```shell
18+
yarn add @lg-chat/suggestions
19+
```
20+
21+
### NPM
22+
23+
```shell
24+
npm install @lg-chat/suggestions
25+
```
26+
27+
## Example
28+
29+
```tsx
30+
import { SuggestedActions, State } from '@lg-chat/suggestions';
31+
32+
const configurationParameters = [
33+
{ key: 'Cluster Tier', value: 'M10 ($9.00/month)' },
34+
{ key: 'Provider', value: 'AWS / N. Virginia (us-east-1)' },
35+
{ key: 'Storage', value: '10 GB' },
36+
{ key: 'RAM', value: '2 GB' },
37+
{ key: 'vCPUs', value: '2 vCPUs' },
38+
];
39+
40+
// Basic suggestion card with apply button
41+
<SuggestedActions
42+
state={State.Unset}
43+
configurationParameters={configurationParameters}
44+
onClickApply={() => console.log('Apply clicked')}
45+
/>
46+
47+
// Success state with applied parameters
48+
<SuggestedActions
49+
state={State.Success}
50+
configurationParameters={[
51+
{ key: 'Cluster Tier', value: 'M10 ($9.00/month)' },
52+
{ key: 'Provider', value: 'AWS / N. Virginia (us-east-1)' },
53+
{ key: 'Cloud Provider & Region', value: 'AWS / N. Virginia (us-east-1)', state: State.Success },
54+
{ key: 'Cluster Tier', value: 'M10 ($9.00/month)', state: State.Success },
55+
]}
56+
onClickApply={() => console.log('Apply clicked')}
57+
/>
58+
59+
// Error state with failed parameters
60+
<SuggestedActions
61+
state={State.Error}
62+
configurationParameters={[
63+
{ key: 'Cluster Tier', value: 'M30 ($31.00/month)' },
64+
{ key: 'Provider', value: 'GCP / Iowa (us-central1)' },
65+
{ key: 'Cloud Provider & Region', value: 'GCP / Iowa (us-central1)', state: State.Error },
66+
{ key: 'Cluster Tier', value: 'M30 ($31.00/month)', state: State.Error },
67+
]}
68+
onClickApply={() => console.log('Apply clicked')}
69+
/>
70+
```
71+
72+
## State Types
73+
74+
The `State` enum provides the following options:
75+
76+
| State | Value | Description |
77+
| --------- | ----------- | ------------------------------------------------------ |
78+
| `Unset` | `'unset'` | Shows configuration suggestions with an "Apply" button |
79+
| `Success` | `'success'` | Shows success banner with applied parameters |
80+
| `Error` | `'error'` | Shows error banner with failed parameters |
81+
82+
## Configuration Parameters
83+
84+
Each configuration parameter is an object with the following structure:
85+
86+
```tsx
87+
interface ConfigurationParameter {
88+
key: string; // The parameter name
89+
value: string; // The parameter value
90+
state?: State; // The parameter state (defaults to 'unset')
91+
}
92+
```
93+
94+
The component automatically filters and displays parameters based on their state:
95+
96+
- **Table**: Shows parameters with `unset` state (or no state)
97+
- **Success Banner**: Shows parameters with `success` state
98+
- **Error Banner**: Shows parameters with `error` state
99+
100+
## Properties
101+
102+
| Prop | Type | Description | Default |
103+
| ------------------------- | ------------------------- | -------------------------------------------------------------------------------------------- | ------- |
104+
| `state` | `State` | Determines the current state and rendering behavior of the suggestion card | |
105+
| `configurationParameters` | `ConfigurationParameters` | Array of configuration parameters, each with key, value, and optional state | |
106+
| `onClickApply` | `() => void` | Callback fired when the user clicks the "Apply" button (shown when `state` is `State.Unset`) | |
107+
| `darkMode` | `boolean` | Determines if the component is rendered in dark mode | `false` |
108+
| `...` | `HTMLElementProps<'div'>` | Props spread on root element | |

chat/suggestions/package.json

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
2+
{
3+
"name": "@lg-chat/suggestions",
4+
"version": "0.1.0",
5+
"description": "LeafyGreen UI Kit Suggestion Card",
6+
"main": "./dist/umd/index.js",
7+
"module": "./dist/esm/index.js",
8+
"types": "./dist/types/index.d.ts",
9+
"license": "Apache-2.0",
10+
"exports": {
11+
".": {
12+
"require": "./dist/umd/index.js",
13+
"import": "./dist/esm/index.js",
14+
"types": "./dist/types/index.d.ts"
15+
},
16+
"./testing": {
17+
"require": "./dist/umd/testing/index.js",
18+
"import": "./dist/esm/testing/index.js",
19+
"types": "./dist/types/testing/index.d.ts"
20+
}
21+
},
22+
"scripts": {
23+
"build": "lg-build bundle",
24+
"tsc": "lg-build tsc",
25+
"docs": "lg-build docs"
26+
},
27+
"publishConfig": {
28+
"access": "public"
29+
},
30+
"dependencies": {
31+
"@leafygreen-ui/banner": "workspace:^",
32+
"@leafygreen-ui/button": "workspace:^",
33+
"@leafygreen-ui/emotion": "workspace:^",
34+
"@leafygreen-ui/icon": "workspace:^",
35+
"@leafygreen-ui/lib": "workspace:^",
36+
"@leafygreen-ui/tokens": "workspace:^"
37+
},
38+
"devDependencies": {
39+
"@lg-tools/test-harnesses": "workspace:^"
40+
},
41+
"peerDependencies": {
42+
"@leafygreen-ui/leafygreen-provider": "workspace:^"
43+
},
44+
"homepage": "https://github.com/mongodb/leafygreen-ui/tree/main/chat/suggestions",
45+
"repository": {
46+
"type": "git",
47+
"url": "https://github.com/mongodb/leafygreen-ui"
48+
},
49+
"bugs": {
50+
"url": "https://jira.mongodb.org/projects/LG/summary"
51+
}
52+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { css } from '@leafygreen-ui/emotion';
2+
import { fontWeights, spacing, typeScales } from '@leafygreen-ui/tokens';
3+
4+
export const bannerWrapperStyles = css`
5+
width: 100%;
6+
gap: ${spacing[200]}px;
7+
font-size: ${typeScales.body1.fontSize}px;
8+
line-height: ${typeScales.body1.lineHeight}px;
9+
justify-content: space-between;
10+
`;
11+
12+
export const boldedTextStyle = css`
13+
font-weight: ${fontWeights.bold};
14+
`;
15+
16+
export const listStyles = css`
17+
padding-left: ${spacing[500]}px;
18+
line-height: ${typeScales.body1.lineHeight}px;
19+
margin: ${spacing[100]}px;
20+
`;
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import React from 'react';
2+
3+
import Banner, { Variant as BannerVariant } from '@leafygreen-ui/banner';
4+
5+
import { ConfigurationParameter, State } from '../shared.types';
6+
7+
import {
8+
bannerWrapperStyles,
9+
boldedTextStyle,
10+
listStyles,
11+
} from './StatusBanner.styles';
12+
13+
export const StatusBanner = ({
14+
state,
15+
appliedParameters,
16+
failedParameters,
17+
}: {
18+
state: State;
19+
appliedParameters?: Array<ConfigurationParameter>;
20+
failedParameters?: Array<ConfigurationParameter>;
21+
}) => {
22+
const clusterConfigurationBannerContent = (
23+
parameters?: Array<ConfigurationParameter>,
24+
) => {
25+
return (
26+
<ul className={listStyles}>
27+
{(parameters ?? []).map(param => (
28+
<li key={param.key}>
29+
<u>{param.key}</u>: {param.value}
30+
</li>
31+
))}
32+
</ul>
33+
);
34+
};
35+
36+
return (
37+
<Banner
38+
className={bannerWrapperStyles}
39+
variant={
40+
state === State.Success ? BannerVariant.Success : BannerVariant.Danger
41+
}
42+
>
43+
<div>
44+
{state === State.Success && (
45+
<>
46+
<div className={boldedTextStyle}>
47+
The suggestions have been applied.
48+
</div>
49+
{clusterConfigurationBannerContent(appliedParameters)}
50+
</>
51+
)}
52+
{state === State.Error && (
53+
<>
54+
<div className={boldedTextStyle}>
55+
We ran into an error when applying the suggestion. Please manually
56+
try it:
57+
</div>
58+
{clusterConfigurationBannerContent(failedParameters)}
59+
</>
60+
)}
61+
</div>
62+
</Banner>
63+
);
64+
};
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import React from 'react';
2+
import {
3+
storybookArgTypes,
4+
storybookExcludedControlParams,
5+
StoryMetaType,
6+
} from '@lg-tools/storybook-utils';
7+
import { StoryFn } from '@storybook/react';
8+
9+
import { State, SuggestedActions, SuggestedActionsProps } from '.';
10+
11+
const meta: StoryMetaType<typeof SuggestedActions> = {
12+
title: 'Chat/SuggestedActions',
13+
component: SuggestedActions,
14+
parameters: {
15+
default: 'LiveExample',
16+
generate: {
17+
combineArgs: {
18+
darkMode: [false, true],
19+
state: Object.values(State),
20+
},
21+
},
22+
controls: {
23+
exclude: [...storybookExcludedControlParams],
24+
},
25+
docs: {
26+
description: {
27+
component:
28+
'A card that displays a suggestion for a MongoDB cluster configuration and allows the user to apply the suggestion.',
29+
},
30+
},
31+
},
32+
args: {
33+
state: State.Unset,
34+
configurationParameters: [
35+
{
36+
key: 'Cluster Tier',
37+
value: 'M200 ($14.59/hour)',
38+
state: State.Success,
39+
},
40+
{
41+
key: 'Provider',
42+
value: 'AWS / N. Virginia (us-east-1)',
43+
state: State.Success,
44+
},
45+
{ key: 'Storage', value: '1500 GB' },
46+
{ key: 'RAM', value: '256 GB', state: State.Error },
47+
{ key: 'vCPUs', value: '64 vCPUs', state: State.Error },
48+
],
49+
onClickApply: () => {},
50+
darkMode: false,
51+
},
52+
argTypes: {
53+
darkMode: storybookArgTypes.darkMode,
54+
state: {
55+
options: Object.values(State),
56+
control: { type: 'select' },
57+
},
58+
configurationParameters: {
59+
control: 'object',
60+
},
61+
onClickApply: {
62+
action: 'onClickApply',
63+
},
64+
},
65+
};
66+
67+
export default meta;
68+
69+
export const LiveExample: StoryFn<SuggestedActionsProps> = (
70+
props: SuggestedActionsProps,
71+
) => <SuggestedActions {...props} />;
72+
LiveExample.parameters = {
73+
chromatic: { disableSnapshot: true },
74+
};
75+
76+
export const Generated = () => {};

0 commit comments

Comments
 (0)