From c9a6600071b410335c7be94f37d2ed6a2156c5cb Mon Sep 17 00:00:00 2001 From: wlee221 Date: Mon, 3 Aug 2020 18:39:19 -0700 Subject: [PATCH 01/71] amplify-chatbot initial import --- .../amplify-ui-components/src/components.d.ts | 61 +++++++++++++ .../amplify-chatbot/amplify-chatbot.scss | 78 ++++++++++++++++ .../amplify-chatbot/amplify-chatbot.spec.tsx | 18 ++++ .../amplify-chatbot/amplify-chatbot.tsx | 91 +++++++++++++++++++ .../src/components/amplify-chatbot/readme.md | 35 +++++++ .../amplify-container.spec.ts | 9 +- .../components/amplify-icon-button/readme.md | 10 +- .../src/components/amplify-icon/icons.tsx | 16 ++++ .../src/components/amplify-icon/readme.md | 8 +- 9 files changed, 313 insertions(+), 13 deletions(-) create mode 100644 packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.scss create mode 100644 packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.spec.tsx create mode 100644 packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx create mode 100644 packages/amplify-ui-components/src/components/amplify-chatbot/readme.md diff --git a/packages/amplify-ui-components/src/components.d.ts b/packages/amplify-ui-components/src/components.d.ts index 60a8b78e6a6..b4e319ef899 100644 --- a/packages/amplify-ui-components/src/components.d.ts +++ b/packages/amplify-ui-components/src/components.d.ts @@ -76,6 +76,32 @@ export namespace Components { */ "variant": ButtonVariant; } + interface AmplifyChatbot { + /** + * Name of the bot + */ + "botName": string; + /** + * Text placed in the top header + */ + "botTitle": string; + /** + * Clear messages when conversation finishes + */ + "clearOnComplete": boolean; + /** + * Continue listening to users after they send the message + */ + "conversationModeOn": boolean; + /** + * Callback to be called after conversation finishes + */ + "onComplete": (err: string, data: object) => void; + /** + * Greeting message displayed to users + */ + "welcomeMessage": string; + } interface AmplifyCheckbox { /** * If `true`, the checkbox is selected. @@ -1116,6 +1142,12 @@ declare global { prototype: HTMLAmplifyButtonElement; new (): HTMLAmplifyButtonElement; }; + interface HTMLAmplifyChatbotElement extends Components.AmplifyChatbot, HTMLStencilElement { + } + var HTMLAmplifyChatbotElement: { + prototype: HTMLAmplifyChatbotElement; + new (): HTMLAmplifyChatbotElement; + }; interface HTMLAmplifyCheckboxElement extends Components.AmplifyCheckbox, HTMLStencilElement { } var HTMLAmplifyCheckboxElement: { @@ -1410,6 +1442,7 @@ declare global { "amplify-auth0-button": HTMLAmplifyAuth0ButtonElement; "amplify-authenticator": HTMLAmplifyAuthenticatorElement; "amplify-button": HTMLAmplifyButtonElement; + "amplify-chatbot": HTMLAmplifyChatbotElement; "amplify-checkbox": HTMLAmplifyCheckboxElement; "amplify-code-field": HTMLAmplifyCodeFieldElement; "amplify-confirm-sign-in": HTMLAmplifyConfirmSignInElement; @@ -1523,6 +1556,32 @@ declare namespace LocalJSX { */ "variant"?: ButtonVariant; } + interface AmplifyChatbot { + /** + * Name of the bot + */ + "botName"?: string; + /** + * Text placed in the top header + */ + "botTitle"?: string; + /** + * Clear messages when conversation finishes + */ + "clearOnComplete"?: boolean; + /** + * Continue listening to users after they send the message + */ + "conversationModeOn"?: boolean; + /** + * Callback to be called after conversation finishes + */ + "onComplete"?: (err: string, data: object) => void; + /** + * Greeting message displayed to users + */ + "welcomeMessage"?: string; + } interface AmplifyCheckbox { /** * If `true`, the checkbox is selected. @@ -2541,6 +2600,7 @@ declare namespace LocalJSX { "amplify-auth0-button": AmplifyAuth0Button; "amplify-authenticator": AmplifyAuthenticator; "amplify-button": AmplifyButton; + "amplify-chatbot": AmplifyChatbot; "amplify-checkbox": AmplifyCheckbox; "amplify-code-field": AmplifyCodeField; "amplify-confirm-sign-in": AmplifyConfirmSignIn; @@ -2600,6 +2660,7 @@ declare module "@stencil/core" { "amplify-auth0-button": LocalJSX.AmplifyAuth0Button & JSXBase.HTMLAttributes; "amplify-authenticator": LocalJSX.AmplifyAuthenticator & JSXBase.HTMLAttributes; "amplify-button": LocalJSX.AmplifyButton & JSXBase.HTMLAttributes; + "amplify-chatbot": LocalJSX.AmplifyChatbot & JSXBase.HTMLAttributes; "amplify-checkbox": LocalJSX.AmplifyCheckbox & JSXBase.HTMLAttributes; "amplify-code-field": LocalJSX.AmplifyCodeField & JSXBase.HTMLAttributes; "amplify-confirm-sign-in": LocalJSX.AmplifyConfirmSignIn & JSXBase.HTMLAttributes; diff --git a/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.scss b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.scss new file mode 100644 index 00000000000..d18ee229616 --- /dev/null +++ b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.scss @@ -0,0 +1,78 @@ +:host { + --header-color: var(--amplify-secondary-color); + --header-size: var(--amplify-text-lg); +} + +.amplify-chatbot { + position: relative; + background-color: var(--background-color); + display: inline-block; + border-radius: 6px; + box-shadow: 1px 0px 4px 0 rgba(0, 0, 0, 0.15); + box-sizing: border-box; + font-family: var(--amplify-font-family); + width: 460px; + max-width: 460px; + margin-bottom: 1rem; +} +.header { + margin-top: 20px; + color: var(--header-color); + font-size: var(--header-size); + font-weight: bold; + text-align: center; + margin-bottom: 24px; +} + +.body { + display: flex; + flex-direction: column; + height: 500px; + overflow: auto; +} + +.bubble { + max-width: 80%; + padding: 0.8em 1.4em; + clear: both; + word-wrap: break-word; + margin-bottom: 0.625rem; +} +.bot { + margin-left: 1rem; + margin-right: auto; + background-color: #dbdbdb; + color: black; + border-radius: 1.5rem 1.5rem 1.5rem 0; +} +.user { + margin-right: 1rem; + margin-left: auto; + background-color: #099ac8; // TODO: expose amplify blue color + color: var(--amplify-white); + border-radius: 1.5rem 1.5rem 0 1.5rem; +} + +.chatbot-control { + display: flex; + align-items: center; + border-top: 1px solid rgba(0, 0, 0, 0.05); + padding: 1rem; + input { + flex-grow: 1; + border: none; + &:focus { + outline: none; + } + } +} +.icon-button { + margin-left: 0.625rem; + cursor: pointer; +} + +.chatbot-footer { + position: absolute; + bottom: 0px; + height: 2rem; +} diff --git a/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.spec.tsx b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.spec.tsx new file mode 100644 index 00000000000..6534bcb2d46 --- /dev/null +++ b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.spec.tsx @@ -0,0 +1,18 @@ +import { newSpecPage } from '@stencil/core/testing'; +import { AmplifyChatbot } from './amplify-chatbot'; + +describe('amplify-chatbot', () => { + it('renders', async () => { + const page = await newSpecPage({ + components: [AmplifyChatbot], + html: ``, + }); + expect(page.root).toEqualHtml(` + + + + + + `); + }); +}); diff --git a/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx new file mode 100644 index 00000000000..4606aa4b97d --- /dev/null +++ b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx @@ -0,0 +1,91 @@ +import { Component, Host, h, Prop, State } from '@stencil/core'; + +type Message = { + content: string; + from: 'user' | 'bot'; +}; + +const messageJSX = (messages: Message[]) => { + return messages.map(message =>
{message.content}
); +}; + +@Component({ + tag: 'amplify-chatbot', + styleUrl: 'amplify-chatbot.scss', + shadow: true, +}) +export class AmplifyChatbot { + /** Name of the bot */ + @Prop() botName: string; + /** Clear messages when conversation finishes */ + @Prop() clearOnComplete: boolean = false; + /** Continue listening to users after they send the message */ + @Prop() conversationModeOn: boolean = false; + /** Greeting message displayed to users */ + @Prop() welcomeMessage: string; + /** Callback to be called after conversation finishes */ + @Prop() onComplete: (err: string, data: object) => void; + /** Text placed in the top header */ + @Prop() botTitle: string; + @State() messages: Message[] = []; + @State() text: string = ''; + + handleChange(event: Event) { + const target = event.target as HTMLInputElement; + this.text = target.value; + } + + send() { + if (this.text === '') return; + this.messages = [ + ...this.messages, + { + content: this.text, + from: 'user', + }, + { + content: 'echo: ' + this.text, + from: 'bot', + }, + ]; + this.text = ''; + } + + render() { + const testMessages: Message[] = [ + { content: 'Bot: hi', from: 'bot' }, + { content: 'User: hi', from: 'user' }, + { + content: + 'long mesageeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee', + from: 'bot', + }, + ]; + if (this.messages.length === 0) this.messages.push(...testMessages); + return ( + +
+
+
{this.botTitle}
+
{messageJSX(this.messages)}
+
+
+ this.handleChange(e)} + /> + + + + this.send()}> + + +
+
+
+ ); + } +} diff --git a/packages/amplify-ui-components/src/components/amplify-chatbot/readme.md b/packages/amplify-ui-components/src/components/amplify-chatbot/readme.md new file mode 100644 index 00000000000..0a153c665ba --- /dev/null +++ b/packages/amplify-ui-components/src/components/amplify-chatbot/readme.md @@ -0,0 +1,35 @@ +# amplify-chatbot + + + + + + +## Properties + +| Property | Attribute | Description | Type | Default | +| -------------------- | ---------------------- | ------------------------------------------------------- | ------------------------------------- | ----------- | +| `botName` | `bot-name` | Name of the bot | `string` | `undefined` | +| `botTitle` | `bot-title` | Text placed in the top header | `string` | `undefined` | +| `clearOnComplete` | `clear-on-complete` | Clear messages when conversation finishes | `boolean` | `false` | +| `conversationModeOn` | `conversation-mode-on` | Continue listening to users after they send the message | `boolean` | `false` | +| `onComplete` | -- | Callback to be called after conversation finishes | `(err: string, data: object) => void` | `undefined` | +| `welcomeMessage` | `welcome-message` | Greeting message displayed to users | `string` | `undefined` | + + +## Dependencies + +### Depends on + +- [amplify-icon](../amplify-icon) + +### Graph +```mermaid +graph TD; + amplify-chatbot --> amplify-icon + style amplify-chatbot fill:#f9f,stroke:#333,stroke-width:4px +``` + +---------------------------------------------- + +*Built with [StencilJS](https://stenciljs.com/)* diff --git a/packages/amplify-ui-components/src/components/amplify-container/amplify-container.spec.ts b/packages/amplify-ui-components/src/components/amplify-container/amplify-container.spec.ts index 1633b42c140..60d8018ae1f 100644 --- a/packages/amplify-ui-components/src/components/amplify-container/amplify-container.spec.ts +++ b/packages/amplify-ui-components/src/components/amplify-container/amplify-container.spec.ts @@ -3,11 +3,10 @@ import { AmplifyContainer } from './amplify-container'; describe('amplify-container spec:', () => { describe('Component logic ->', () => { - let container; - - beforeEach(() => { - container = new AmplifyContainer(); - }); + // let container; + // beforeEach(() => { + // container = new AmplifyContainer(); + // }); }); describe('Render logic ->', () => { it('should render with an empty slot', async () => { diff --git a/packages/amplify-ui-components/src/components/amplify-icon-button/readme.md b/packages/amplify-ui-components/src/components/amplify-icon-button/readme.md index bfdb7363d23..a9159d1d3a5 100644 --- a/packages/amplify-ui-components/src/components/amplify-icon-button/readme.md +++ b/packages/amplify-ui-components/src/components/amplify-icon-button/readme.md @@ -5,11 +5,11 @@ ## Properties -| Property | Attribute | Description | Type | Default | -| ----------------- | ------------------- | ------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | -| `autoShowTooltip` | `auto-show-tooltip` | (Optional) Whether or not to show the tooltip automatically | `boolean` | `false` | -| `name` | `name` | The name of the icon used inside of the button | `"amazon" \| "auth0" \| "enter-vr" \| "exit-vr" \| "facebook" \| "google" \| "loading" \| "maximize" \| "minimize" \| "photoPlaceholder" \| "sound" \| "sound-mute" \| "warning"` | `undefined` | -| `tooltip` | `tooltip` | (Optional) The tooltip that will show on hover of the button | `string` | `null` | +| Property | Attribute | Description | Type | Default | +| ----------------- | ------------------- | ------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | +| `autoShowTooltip` | `auto-show-tooltip` | (Optional) Whether or not to show the tooltip automatically | `boolean` | `false` | +| `name` | `name` | The name of the icon used inside of the button | `"amazon" \| "auth0" \| "enter-vr" \| "exit-vr" \| "facebook" \| "google" \| "loading" \| "maximize" \| "microphone" \| "minimize" \| "photoPlaceholder" \| "send" \| "sound" \| "sound-mute" \| "warning"` | `undefined` | +| `tooltip` | `tooltip` | (Optional) The tooltip that will show on hover of the button | `string` | `null` | ## Dependencies diff --git a/packages/amplify-ui-components/src/components/amplify-icon/icons.tsx b/packages/amplify-ui-components/src/components/amplify-icon/icons.tsx index 5d8752521b4..9df229c5929 100644 --- a/packages/amplify-ui-components/src/components/amplify-icon/icons.tsx +++ b/packages/amplify-ui-components/src/components/amplify-icon/icons.tsx @@ -189,6 +189,22 @@ export const icons = { ); }, + + microphone() { + return ( + + + + ); + }, + + send() { + return ( + + + + ); + }, }; export type IconNameType = keyof typeof icons; diff --git a/packages/amplify-ui-components/src/components/amplify-icon/readme.md b/packages/amplify-ui-components/src/components/amplify-icon/readme.md index fde691c4ede..964e3bb28a2 100644 --- a/packages/amplify-ui-components/src/components/amplify-icon/readme.md +++ b/packages/amplify-ui-components/src/components/amplify-icon/readme.md @@ -5,15 +5,16 @@ ## Properties -| Property | Attribute | Description | Type | Default | -| -------- | --------- | ----------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | -| `name` | `name` | (Required) Name of icon used to determine the icon rendered | `"amazon" \| "auth0" \| "enter-vr" \| "exit-vr" \| "facebook" \| "google" \| "loading" \| "maximize" \| "minimize" \| "photoPlaceholder" \| "sound" \| "sound-mute" \| "warning"` | `undefined` | +| Property | Attribute | Description | Type | Default | +| -------- | --------- | ----------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | +| `name` | `name` | (Required) Name of icon used to determine the icon rendered | `"amazon" \| "auth0" \| "enter-vr" \| "exit-vr" \| "facebook" \| "google" \| "loading" \| "maximize" \| "microphone" \| "minimize" \| "photoPlaceholder" \| "send" \| "sound" \| "sound-mute" \| "warning"` | `undefined` | ## Dependencies ### Used by + - [amplify-chatbot](../amplify-chatbot) - [amplify-icon-button](../amplify-icon-button) - [amplify-loading-spinner](../amplify-loading-spinner) - [amplify-photo-picker](../amplify-photo-picker) @@ -23,6 +24,7 @@ ### Graph ```mermaid graph TD; + amplify-chatbot --> amplify-icon amplify-icon-button --> amplify-icon amplify-loading-spinner --> amplify-icon amplify-photo-picker --> amplify-icon From cc5d16b95afb14d004d4287455d1cb90f7410d11 Mon Sep 17 00:00:00 2001 From: wlee221 Date: Mon, 3 Aug 2020 19:00:08 -0700 Subject: [PATCH 02/71] Use interface and comment out test --- .../amplify-chatbot/amplify-chatbot.spec.tsx | 31 ++++++++++--------- .../amplify-chatbot/amplify-chatbot.tsx | 4 +-- .../amplify-icon-button.spec.ts.snap | 28 +++++++++++++++++ .../__snapshots__/amplify-icon.spec.ts.snap | 20 ++++++++++++ 4 files changed, 67 insertions(+), 16 deletions(-) diff --git a/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.spec.tsx b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.spec.tsx index 6534bcb2d46..b26b5c150a6 100644 --- a/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.spec.tsx +++ b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.spec.tsx @@ -1,18 +1,21 @@ -import { newSpecPage } from '@stencil/core/testing'; -import { AmplifyChatbot } from './amplify-chatbot'; +// import { newSpecPage } from '@stencil/core/testing'; +// import { AmplifyChatbot } from './amplify-chatbot'; describe('amplify-chatbot', () => { - it('renders', async () => { - const page = await newSpecPage({ - components: [AmplifyChatbot], - html: ``, - }); - expect(page.root).toEqualHtml(` - - - - - - `); + // it('renders', async () => { + // const page = await newSpecPage({ + // components: [AmplifyChatbot], + // html: ``, + // }); + // expect(page.root).toEqualHtml(` + // + // + // + // + // + // `); + // }); + it('Test not yet implemented', () => { + return expect(true).toBe(true); }); }); diff --git a/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx index 4606aa4b97d..02f572b2ea0 100644 --- a/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx +++ b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx @@ -1,9 +1,9 @@ import { Component, Host, h, Prop, State } from '@stencil/core'; -type Message = { +interface Message { content: string; from: 'user' | 'bot'; -}; +} const messageJSX = (messages: Message[]) => { return messages.map(message =>
{message.content}
); diff --git a/packages/amplify-ui-components/src/components/amplify-icon-button/__snapshots__/amplify-icon-button.spec.ts.snap b/packages/amplify-ui-components/src/components/amplify-icon-button/__snapshots__/amplify-icon-button.spec.ts.snap index f3918328753..7aaf30195aa 100644 --- a/packages/amplify-ui-components/src/components/amplify-icon-button/__snapshots__/amplify-icon-button.spec.ts.snap +++ b/packages/amplify-ui-components/src/components/amplify-icon-button/__snapshots__/amplify-icon-button.spec.ts.snap @@ -112,6 +112,20 @@ exports[`amplify-icon-button spec: Render logic -> renders maximize icon button `; +exports[`amplify-icon-button spec: Render logic -> renders microphone icon button correctly 1`] = ` + + + + + + + + + +`; + exports[`amplify-icon-button spec: Render logic -> renders minimize icon button correctly 1`] = ` @@ -140,6 +154,20 @@ exports[`amplify-icon-button spec: Render logic -> renders photoPlaceholder icon `; +exports[`amplify-icon-button spec: Render logic -> renders send icon button correctly 1`] = ` + + + + + + + + + +`; + exports[`amplify-icon-button spec: Render logic -> renders sound icon button correctly 1`] = ` diff --git a/packages/amplify-ui-components/src/components/amplify-icon/__snapshots__/amplify-icon.spec.ts.snap b/packages/amplify-ui-components/src/components/amplify-icon/__snapshots__/amplify-icon.spec.ts.snap index f8f2f730998..6e03f4edddd 100644 --- a/packages/amplify-ui-components/src/components/amplify-icon/__snapshots__/amplify-icon.spec.ts.snap +++ b/packages/amplify-ui-components/src/components/amplify-icon/__snapshots__/amplify-icon.spec.ts.snap @@ -106,6 +106,16 @@ exports[`amplify-icon spec: Render logic -> renders maximize correctly 1`] = ` `; +exports[`amplify-icon spec: Render logic -> renders microphone correctly 1`] = ` + + + + + + + +`; + exports[`amplify-icon spec: Render logic -> renders minimize correctly 1`] = ` @@ -130,6 +140,16 @@ exports[`amplify-icon spec: Render logic -> renders photoPlaceholder correctly 1 `; +exports[`amplify-icon spec: Render logic -> renders send correctly 1`] = ` + + + + + + + +`; + exports[`amplify-icon spec: Render logic -> renders sound correctly 1`] = ` From 143441405e9b1e87ac903f9202ad21853562c74e Mon Sep 17 00:00:00 2001 From: wlee221 Date: Tue, 4 Aug 2020 13:39:22 -0700 Subject: [PATCH 03/71] Expose additional css variables and add icon variant to button --- .../src/common/types/ui-types.ts | 2 +- .../amplify-ui-components/src/components.d.ts | 10 +++- .../amplify-authenticator/readme.md | 1 + .../amplify-button/amplify-button.scss | 17 ++++++- .../amplify-button/amplify-button.tsx | 4 ++ .../src/components/amplify-button/readme.md | 20 +++++--- .../amplify-chatbot/amplify-chatbot.scss | 46 ++++++++++++------- .../amplify-chatbot/amplify-chatbot.tsx | 44 ++++++++++-------- .../src/components/amplify-chatbot/readme.md | 7 ++- .../amplify-confirm-sign-in/readme.md | 1 + .../amplify-confirm-sign-up/readme.md | 1 + .../amplify-federated-sign-in/readme.md | 1 + .../amplify-forgot-password/readme.md | 1 + .../components/amplify-form-section/readme.md | 1 + .../components/amplify-greetings/readme.md | 1 + .../components/amplify-icon/amplify-icon.scss | 7 ++- .../components/amplify-icon/amplify-icon.tsx | 4 +- .../src/components/amplify-icon/icons.tsx | 4 +- .../src/components/amplify-icon/readme.md | 4 +- .../amplify-input/amplify-input.scss | 6 ++- .../src/components/amplify-input/readme.md | 2 + .../components/amplify-photo-picker/readme.md | 1 + .../src/components/amplify-picker/readme.md | 1 + .../amplify-require-new-password/readme.md | 1 + .../src/components/amplify-s3-album/readme.md | 1 + .../amplify-s3-image-picker/readme.md | 1 + .../amplify-s3-text-picker/readme.md | 1 + .../amplify-select-mfa-type/readme.md | 1 + .../src/components/amplify-sign-in/readme.md | 1 + .../src/components/amplify-sign-out/readme.md | 1 + .../src/components/amplify-sign-up/readme.md | 1 + .../components/amplify-totp-setup/readme.md | 1 + .../amplify-verify-contact/readme.md | 1 + .../src/Interactions/ChatBot.tsx | 1 - 34 files changed, 141 insertions(+), 56 deletions(-) diff --git a/packages/amplify-ui-components/src/common/types/ui-types.ts b/packages/amplify-ui-components/src/common/types/ui-types.ts index 46702a06a09..d1bd541a86d 100644 --- a/packages/amplify-ui-components/src/common/types/ui-types.ts +++ b/packages/amplify-ui-components/src/common/types/ui-types.ts @@ -1,4 +1,4 @@ export interface InputEvent extends Event {} export type TextFieldTypes = 'date' | 'email' | 'number' | 'password' | 'search' | 'tel' | 'text' | 'url' | 'time'; export type ButtonTypes = 'button' | 'submit' | 'reset'; -export type ButtonVariant = 'button' | 'anchor'; +export type ButtonVariant = 'button' | 'anchor' | 'icon'; diff --git a/packages/amplify-ui-components/src/components.d.ts b/packages/amplify-ui-components/src/components.d.ts index 3aa899bfb61..962e9f41b62 100644 --- a/packages/amplify-ui-components/src/components.d.ts +++ b/packages/amplify-ui-components/src/components.d.ts @@ -8,9 +8,9 @@ import { HTMLStencilElement, JSXBase } from "@stencil/core/internal"; import { AuthState, AuthStateHandler, CognitoUserInterface, FederatedConfig, MFATypesInterface, UsernameAliasStrings } from "./common/types/auth-types"; import { FormFieldTypes } from "./components/amplify-auth-fields/amplify-auth-fields-interface"; import { ButtonTypes, ButtonVariant, InputEvent, TextFieldTypes } from "./common/types/ui-types"; +import { IconNameType } from "./components/amplify-icon/icons"; import { FunctionalComponent } from "@stencil/core"; import { CountryCodeDialOptions } from "./components/amplify-country-dial-code/amplify-country-dial-code-interface"; -import { IconNameType } from "./components/amplify-icon/icons"; import { AccessLevel, StorageObject } from "./common/types/storage-types"; import { SelectOptionsNumber, SelectOptionsString } from "./components/amplify-select/amplify-select-interface"; export namespace Components { @@ -67,6 +67,10 @@ export namespace Components { * (Optional) Callback called when a user clicks on the button */ "handleButtonClick": (evt: Event) => void; + /** + * Name of icon to be placed inside the button + */ + "icon"?: IconNameType; /** * Type of the button: 'button', 'submit' or 'reset' */ @@ -1551,6 +1555,10 @@ declare namespace LocalJSX { * (Optional) Callback called when a user clicks on the button */ "handleButtonClick"?: (evt: Event) => void; + /** + * Name of icon to be placed inside the button + */ + "icon"?: IconNameType; /** * Type of the button: 'button', 'submit' or 'reset' */ diff --git a/packages/amplify-ui-components/src/components/amplify-authenticator/readme.md b/packages/amplify-ui-components/src/components/amplify-authenticator/readme.md index 2d3d39849cc..34b4f27a621 100644 --- a/packages/amplify-ui-components/src/components/amplify-authenticator/readme.md +++ b/packages/amplify-ui-components/src/components/amplify-authenticator/readme.md @@ -45,6 +45,7 @@ graph TD; amplify-sign-in --> amplify-strike amplify-sign-in --> amplify-auth-fields amplify-sign-in --> amplify-loading-spinner + amplify-button --> amplify-icon amplify-form-section --> amplify-section amplify-form-section --> amplify-button amplify-form-section --> amplify-loading-spinner diff --git a/packages/amplify-ui-components/src/components/amplify-button/amplify-button.scss b/packages/amplify-ui-components/src/components/amplify-button/amplify-button.scss index 29a344182e7..5714b87b3d1 100644 --- a/packages/amplify-ui-components/src/components/amplify-button/amplify-button.scss +++ b/packages/amplify-ui-components/src/components/amplify-button/amplify-button.scss @@ -10,6 +10,9 @@ --link-hover: var(--amplify-primary-tint); --link-active: var(--amplify-primary-shade); --text-transform: uppercase; + --icon-color: var(--amplify-primary-color); + --icon-height: 1.25rem; + --padding: 1rem; width: 100%; text-align: center; @@ -35,7 +38,7 @@ user-select: none; background-image: none; color: var(--color); - padding: 1rem; + padding: var(--padding); letter-spacing: 0.75px; text-transform: var(--text-transform); background-color: var(--background-color); @@ -59,6 +62,18 @@ } } +.icon { + background-color: inherit; + border: none; + font: inherit; + cursor: pointer; + padding: var(--padding); + amplify-icon { + --icon-fill-color: var(--icon-color); + --height: var(--icon-height); + } +} + .anchor { color: var(--link-color); background-color: inherit; diff --git a/packages/amplify-ui-components/src/components/amplify-button/amplify-button.tsx b/packages/amplify-ui-components/src/components/amplify-button/amplify-button.tsx index 2cf30064cb4..bd77d663e07 100644 --- a/packages/amplify-ui-components/src/components/amplify-button/amplify-button.tsx +++ b/packages/amplify-ui-components/src/components/amplify-button/amplify-button.tsx @@ -1,6 +1,7 @@ import { Element, Component, Prop, h } from '@stencil/core'; import { ButtonTypes, ButtonVariant } from '../../common/types/ui-types'; import { hasShadowDom } from '../../common/helpers'; +import { IconNameType } from '../amplify-icon/icons'; @Component({ tag: 'amplify-button', @@ -17,6 +18,8 @@ export class AmplifyButton { @Prop() handleButtonClick: (evt: Event) => void; /** Disabled state of the button */ @Prop() disabled?: boolean = false; + /** Name of icon to be placed inside the button */ + @Prop() icon?: IconNameType; private handleClick = (ev: Event) => { if (this.handleButtonClick) { @@ -55,6 +58,7 @@ export class AmplifyButton { disabled={this.disabled} onClick={this.handleClick} > + {this.variant === 'icon' && } ); diff --git a/packages/amplify-ui-components/src/components/amplify-button/readme.md b/packages/amplify-ui-components/src/components/amplify-button/readme.md index b22405cb80c..a09e725d372 100644 --- a/packages/amplify-ui-components/src/components/amplify-button/readme.md +++ b/packages/amplify-ui-components/src/components/amplify-button/readme.md @@ -5,18 +5,20 @@ ## Properties -| Property | Attribute | Description | Type | Default | -| ------------------- | ---------- | ----------------------------------------------------------- | --------------------------------- | ----------- | -| `disabled` | `disabled` | Disabled state of the button | `boolean` | `false` | -| `handleButtonClick` | -- | (Optional) Callback called when a user clicks on the button | `(evt: Event) => void` | `undefined` | -| `type` | `type` | Type of the button: 'button', 'submit' or 'reset' | `"button" \| "reset" \| "submit"` | `'button'` | -| `variant` | `variant` | Variant of a button: 'button' \| 'anchor' | `"anchor" \| "button"` | `'button'` | +| Property | Attribute | Description | Type | Default | +| ------------------- | ---------- | ----------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | +| `disabled` | `disabled` | Disabled state of the button | `boolean` | `false` | +| `handleButtonClick` | -- | (Optional) Callback called when a user clicks on the button | `(evt: Event) => void` | `undefined` | +| `icon` | `icon` | Name of icon to be placed inside the button | `"amazon" \| "auth0" \| "enter-vr" \| "exit-vr" \| "facebook" \| "google" \| "loading" \| "maximize" \| "microphone" \| "minimize" \| "photoPlaceholder" \| "send" \| "sound" \| "sound-mute" \| "warning"` | `undefined` | +| `type` | `type` | Type of the button: 'button', 'submit' or 'reset' | `"button" \| "reset" \| "submit"` | `'button'` | +| `variant` | `variant` | Variant of a button: 'button' \| 'anchor' | `"anchor" \| "button" \| "icon"` | `'button'` | ## Dependencies ### Used by + - [amplify-chatbot](../amplify-chatbot) - [amplify-confirm-sign-in](../amplify-confirm-sign-in) - [amplify-confirm-sign-up](../amplify-confirm-sign-up) - [amplify-forgot-password](../amplify-forgot-password) @@ -29,9 +31,15 @@ - [amplify-sign-up](../amplify-sign-up) - [amplify-verify-contact](../amplify-verify-contact) +### Depends on + +- [amplify-icon](../amplify-icon) + ### Graph ```mermaid graph TD; + amplify-button --> amplify-icon + amplify-chatbot --> amplify-button amplify-confirm-sign-in --> amplify-button amplify-confirm-sign-up --> amplify-button amplify-forgot-password --> amplify-button diff --git a/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.scss b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.scss index d18ee229616..6fce906b36e 100644 --- a/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.scss +++ b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.scss @@ -2,17 +2,14 @@ --header-color: var(--amplify-secondary-color); --header-size: var(--amplify-text-lg); } - .amplify-chatbot { - position: relative; - background-color: var(--background-color); display: inline-block; + background-color: var(--background-color); border-radius: 6px; box-shadow: 1px 0px 4px 0 rgba(0, 0, 0, 0.15); box-sizing: border-box; font-family: var(--amplify-font-family); width: 460px; - max-width: 460px; margin-bottom: 1rem; } .header { @@ -21,16 +18,16 @@ font-size: var(--header-size); font-weight: bold; text-align: center; - margin-bottom: 24px; + padding-bottom: 24px; + border-bottom: 1px solid rgba(0, 0, 0, 0.05); } - .body { + padding-top: 2rem; display: flex; flex-direction: column; height: 500px; overflow: auto; } - .bubble { max-width: 80%; padding: 0.8em 1.4em; @@ -52,25 +49,40 @@ color: var(--amplify-white); border-radius: 1.5rem 1.5rem 0 1.5rem; } - .chatbot-control { display: flex; align-items: center; border-top: 1px solid rgba(0, 0, 0, 0.05); - padding: 1rem; - input { - flex-grow: 1; - border: none; - &:focus { - outline: none; - } + padding-right: 0.625rem; + amplify-input { + --border: none; + --margin: 0; } + // input { + // padding: 1.25rem; + // flex-grow: 1; + // border: none; + // &:focus { + // outline: none; + // } + // } } + .icon-button { - margin-left: 0.625rem; - cursor: pointer; + --icon-height: 1.25rem; + --icon-color: var(--amplify-primary-color); + --padding: 0.625rem; } +// .amplify-icon-button { +// margin-right: 0.625rem; +// cursor: pointer; + +// amplify-icon { +// --height: 1.25rem; +// --icon-fill-color: var(--amplify-primary-color); +// } +// } .chatbot-footer { position: absolute; bottom: 0px; diff --git a/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx index 02f572b2ea0..6a39004b3de 100644 --- a/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx +++ b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx @@ -1,14 +1,10 @@ -import { Component, Host, h, Prop, State } from '@stencil/core'; +import { Component, Host, h, Prop, State, Listen } from '@stencil/core'; interface Message { content: string; from: 'user' | 'bot'; } -const messageJSX = (messages: Message[]) => { - return messages.map(message =>
{message.content}
); -}; - @Component({ tag: 'amplify-chatbot', styleUrl: 'amplify-chatbot.scss', @@ -27,9 +23,19 @@ export class AmplifyChatbot { @Prop() onComplete: (err: string, data: object) => void; /** Text placed in the top header */ @Prop() botTitle: string; + @State() messages: Message[] = []; @State() text: string = ''; + @Listen('formSubmit') + submitHandler(_event: CustomEvent) { + this.send(); + } + + messageJSX = (messages: Message[]) => { + return messages.map(message =>
{message.content}
); + }; + handleChange(event: Event) { const target = event.target as HTMLInputElement; this.text = target.value; @@ -53,7 +59,7 @@ export class AmplifyChatbot { render() { const testMessages: Message[] = [ - { content: 'Bot: hi', from: 'bot' }, + { content: 'hi', from: 'bot' }, { content: 'User: hi', from: 'user' }, { content: @@ -65,24 +71,26 @@ export class AmplifyChatbot { return (
-
-
{this.botTitle}
-
{messageJSX(this.messages)}
-
+
{this.botTitle}
+
{this.messageJSX(this.messages)}
- this.handleChange(e)} - /> - - - - this.send()}> - - + /> */} + + this.handleChange(evt)} + value={this.text} + > + + this.send()} />
diff --git a/packages/amplify-ui-components/src/components/amplify-chatbot/readme.md b/packages/amplify-ui-components/src/components/amplify-chatbot/readme.md index 0a153c665ba..67dae60faf3 100644 --- a/packages/amplify-ui-components/src/components/amplify-chatbot/readme.md +++ b/packages/amplify-ui-components/src/components/amplify-chatbot/readme.md @@ -21,12 +21,15 @@ ### Depends on -- [amplify-icon](../amplify-icon) +- [amplify-input](../amplify-input) +- [amplify-button](../amplify-button) ### Graph ```mermaid graph TD; - amplify-chatbot --> amplify-icon + amplify-chatbot --> amplify-input + amplify-chatbot --> amplify-button + amplify-button --> amplify-icon style amplify-chatbot fill:#f9f,stroke:#333,stroke-width:4px ``` diff --git a/packages/amplify-ui-components/src/components/amplify-confirm-sign-in/readme.md b/packages/amplify-ui-components/src/components/amplify-confirm-sign-in/readme.md index 2532deafb6b..704aca3d27e 100644 --- a/packages/amplify-ui-components/src/components/amplify-confirm-sign-in/readme.md +++ b/packages/amplify-ui-components/src/components/amplify-confirm-sign-in/readme.md @@ -36,6 +36,7 @@ graph TD; amplify-form-section --> amplify-section amplify-form-section --> amplify-button amplify-form-section --> amplify-loading-spinner + amplify-button --> amplify-icon amplify-loading-spinner --> amplify-icon amplify-auth-fields --> amplify-username-field amplify-auth-fields --> amplify-password-field diff --git a/packages/amplify-ui-components/src/components/amplify-confirm-sign-up/readme.md b/packages/amplify-ui-components/src/components/amplify-confirm-sign-up/readme.md index f0aeea45dad..c4bb9494f30 100644 --- a/packages/amplify-ui-components/src/components/amplify-confirm-sign-up/readme.md +++ b/packages/amplify-ui-components/src/components/amplify-confirm-sign-up/readme.md @@ -34,6 +34,7 @@ graph TD; amplify-confirm-sign-up --> amplify-button amplify-confirm-sign-up --> amplify-form-section amplify-confirm-sign-up --> amplify-auth-fields + amplify-button --> amplify-icon amplify-form-section --> amplify-section amplify-form-section --> amplify-button amplify-form-section --> amplify-loading-spinner diff --git a/packages/amplify-ui-components/src/components/amplify-federated-sign-in/readme.md b/packages/amplify-ui-components/src/components/amplify-federated-sign-in/readme.md index 5a533fdfbeb..327164f9c40 100644 --- a/packages/amplify-ui-components/src/components/amplify-federated-sign-in/readme.md +++ b/packages/amplify-ui-components/src/components/amplify-federated-sign-in/readme.md @@ -28,6 +28,7 @@ graph TD; amplify-form-section --> amplify-section amplify-form-section --> amplify-button amplify-form-section --> amplify-loading-spinner + amplify-button --> amplify-icon amplify-loading-spinner --> amplify-icon amplify-federated-buttons --> amplify-google-button amplify-federated-buttons --> amplify-facebook-button diff --git a/packages/amplify-ui-components/src/components/amplify-forgot-password/readme.md b/packages/amplify-ui-components/src/components/amplify-forgot-password/readme.md index 2c91f148281..ec2cab7812a 100644 --- a/packages/amplify-ui-components/src/components/amplify-forgot-password/readme.md +++ b/packages/amplify-ui-components/src/components/amplify-forgot-password/readme.md @@ -38,6 +38,7 @@ graph TD; amplify-form-section --> amplify-section amplify-form-section --> amplify-button amplify-form-section --> amplify-loading-spinner + amplify-button --> amplify-icon amplify-loading-spinner --> amplify-icon amplify-auth-fields --> amplify-username-field amplify-auth-fields --> amplify-password-field diff --git a/packages/amplify-ui-components/src/components/amplify-form-section/readme.md b/packages/amplify-ui-components/src/components/amplify-form-section/readme.md index c374954fcca..678b18142f6 100644 --- a/packages/amplify-ui-components/src/components/amplify-form-section/readme.md +++ b/packages/amplify-ui-components/src/components/amplify-form-section/readme.md @@ -42,6 +42,7 @@ graph TD; amplify-form-section --> amplify-section amplify-form-section --> amplify-button amplify-form-section --> amplify-loading-spinner + amplify-button --> amplify-icon amplify-loading-spinner --> amplify-icon amplify-confirm-sign-in --> amplify-form-section amplify-confirm-sign-up --> amplify-form-section diff --git a/packages/amplify-ui-components/src/components/amplify-greetings/readme.md b/packages/amplify-ui-components/src/components/amplify-greetings/readme.md index 8334c78b76f..fd238fe5f56 100644 --- a/packages/amplify-ui-components/src/components/amplify-greetings/readme.md +++ b/packages/amplify-ui-components/src/components/amplify-greetings/readme.md @@ -27,6 +27,7 @@ graph TD; amplify-greetings --> amplify-nav amplify-greetings --> amplify-sign-out amplify-sign-out --> amplify-button + amplify-button --> amplify-icon style amplify-greetings fill:#f9f,stroke:#333,stroke-width:4px ``` diff --git a/packages/amplify-ui-components/src/components/amplify-icon/amplify-icon.scss b/packages/amplify-ui-components/src/components/amplify-icon/amplify-icon.scss index 1e3390f5434..69106f5c362 100644 --- a/packages/amplify-ui-components/src/components/amplify-icon/amplify-icon.scss +++ b/packages/amplify-ui-components/src/components/amplify-icon/amplify-icon.scss @@ -1,7 +1,12 @@ :host { + display: inline-block; --icon-fill-color: var(--amplify-white); + --width: auto; + height: var(--height); } -.icon { +svg { fill: var(--icon-fill-color); + width: var(--width); + height: 100%; } diff --git a/packages/amplify-ui-components/src/components/amplify-icon/amplify-icon.tsx b/packages/amplify-ui-components/src/components/amplify-icon/amplify-icon.tsx index 31859a8a2c7..1b3e11ae5d8 100644 --- a/packages/amplify-ui-components/src/components/amplify-icon/amplify-icon.tsx +++ b/packages/amplify-ui-components/src/components/amplify-icon/amplify-icon.tsx @@ -1,4 +1,4 @@ -import { Component, Prop, Watch, h } from '@stencil/core'; +import { Component, Prop, Watch } from '@stencil/core'; import { icons, IconNameType } from './icons'; @Component({ @@ -20,6 +20,6 @@ export class AmplifyIcon { // https://stenciljs.com/docs/templating-jsx#avoid-shared-jsx-nodes render() { - return {icons[this.name]()}; + return icons[this.name](); } } diff --git a/packages/amplify-ui-components/src/components/amplify-icon/icons.tsx b/packages/amplify-ui-components/src/components/amplify-icon/icons.tsx index 9df229c5929..4542c124ede 100644 --- a/packages/amplify-ui-components/src/components/amplify-icon/icons.tsx +++ b/packages/amplify-ui-components/src/components/amplify-icon/icons.tsx @@ -192,7 +192,7 @@ export const icons = { microphone() { return ( - + ); @@ -200,7 +200,7 @@ export const icons = { send() { return ( - + ); diff --git a/packages/amplify-ui-components/src/components/amplify-icon/readme.md b/packages/amplify-ui-components/src/components/amplify-icon/readme.md index 964e3bb28a2..03fc37511ed 100644 --- a/packages/amplify-ui-components/src/components/amplify-icon/readme.md +++ b/packages/amplify-ui-components/src/components/amplify-icon/readme.md @@ -14,7 +14,7 @@ ### Used by - - [amplify-chatbot](../amplify-chatbot) + - [amplify-button](../amplify-button) - [amplify-icon-button](../amplify-icon-button) - [amplify-loading-spinner](../amplify-loading-spinner) - [amplify-photo-picker](../amplify-photo-picker) @@ -24,7 +24,7 @@ ### Graph ```mermaid graph TD; - amplify-chatbot --> amplify-icon + amplify-button --> amplify-icon amplify-icon-button --> amplify-icon amplify-loading-spinner --> amplify-icon amplify-photo-picker --> amplify-icon diff --git a/packages/amplify-ui-components/src/components/amplify-input/amplify-input.scss b/packages/amplify-ui-components/src/components/amplify-input/amplify-input.scss index af47aec08ba..5f460f7559f 100644 --- a/packages/amplify-ui-components/src/components/amplify-input/amplify-input.scss +++ b/packages/amplify-ui-components/src/components/amplify-input/amplify-input.scss @@ -3,6 +3,8 @@ --background-color: var(--amplify-secondary-contrast); --border-color: var(--amplify-light-grey); --border-color-focus: var(--amplify-primary-color); + --border: 1px solid var(--border-color); + --margin: 0 0 0.625rem 0; } .input-host { @@ -17,10 +19,10 @@ color: var(--color); background-color: var(--background-color); background-image: none; - border: 1px solid var(--border-color); + border: var(--border); border-radius: 3px; box-sizing: border-box; - margin: 0 0 0.625rem 0; + margin: var(--margin); height: 3.125rem; line-height: 1.1; diff --git a/packages/amplify-ui-components/src/components/amplify-input/readme.md b/packages/amplify-ui-components/src/components/amplify-input/readme.md index ec9c74c03ba..167b7efa99e 100644 --- a/packages/amplify-ui-components/src/components/amplify-input/readme.md +++ b/packages/amplify-ui-components/src/components/amplify-input/readme.md @@ -29,6 +29,7 @@ ### Used by + - [amplify-chatbot](../amplify-chatbot) - [amplify-form-field](../amplify-form-field) - [amplify-phone-field](../amplify-phone-field) - [amplify-verify-contact](../amplify-verify-contact) @@ -36,6 +37,7 @@ ### Graph ```mermaid graph TD; + amplify-chatbot --> amplify-input amplify-form-field --> amplify-input amplify-phone-field --> amplify-input amplify-verify-contact --> amplify-input diff --git a/packages/amplify-ui-components/src/components/amplify-photo-picker/readme.md b/packages/amplify-ui-components/src/components/amplify-photo-picker/readme.md index 1bbfd5be7a9..973a8e794d7 100644 --- a/packages/amplify-ui-components/src/components/amplify-photo-picker/readme.md +++ b/packages/amplify-ui-components/src/components/amplify-photo-picker/readme.md @@ -36,6 +36,7 @@ graph TD; amplify-photo-picker --> amplify-icon amplify-photo-picker --> amplify-button amplify-picker --> amplify-button + amplify-button --> amplify-icon amplify-s3-image-picker --> amplify-photo-picker style amplify-photo-picker fill:#f9f,stroke:#333,stroke-width:4px ``` diff --git a/packages/amplify-ui-components/src/components/amplify-picker/readme.md b/packages/amplify-ui-components/src/components/amplify-picker/readme.md index 1c30bc85bfb..9b3c57e8181 100644 --- a/packages/amplify-ui-components/src/components/amplify-picker/readme.md +++ b/packages/amplify-ui-components/src/components/amplify-picker/readme.md @@ -28,6 +28,7 @@ ```mermaid graph TD; amplify-picker --> amplify-button + amplify-button --> amplify-icon amplify-photo-picker --> amplify-picker amplify-s3-album --> amplify-picker amplify-s3-text-picker --> amplify-picker diff --git a/packages/amplify-ui-components/src/components/amplify-require-new-password/readme.md b/packages/amplify-ui-components/src/components/amplify-require-new-password/readme.md index 6eec979c3c7..4bcbaf41897 100644 --- a/packages/amplify-ui-components/src/components/amplify-require-new-password/readme.md +++ b/packages/amplify-ui-components/src/components/amplify-require-new-password/readme.md @@ -36,6 +36,7 @@ graph TD; amplify-form-section --> amplify-section amplify-form-section --> amplify-button amplify-form-section --> amplify-loading-spinner + amplify-button --> amplify-icon amplify-loading-spinner --> amplify-icon amplify-auth-fields --> amplify-username-field amplify-auth-fields --> amplify-password-field diff --git a/packages/amplify-ui-components/src/components/amplify-s3-album/readme.md b/packages/amplify-ui-components/src/components/amplify-s3-album/readme.md index f19d6c07d4f..675314ffc95 100644 --- a/packages/amplify-ui-components/src/components/amplify-s3-album/readme.md +++ b/packages/amplify-ui-components/src/components/amplify-s3-album/readme.md @@ -36,6 +36,7 @@ graph TD; amplify-s3-album --> amplify-s3-image amplify-s3-album --> amplify-picker amplify-picker --> amplify-button + amplify-button --> amplify-icon style amplify-s3-album fill:#f9f,stroke:#333,stroke-width:4px ``` diff --git a/packages/amplify-ui-components/src/components/amplify-s3-image-picker/readme.md b/packages/amplify-ui-components/src/components/amplify-s3-image-picker/readme.md index 81943228183..c5d8ef319fa 100644 --- a/packages/amplify-ui-components/src/components/amplify-s3-image-picker/readme.md +++ b/packages/amplify-ui-components/src/components/amplify-s3-image-picker/readme.md @@ -36,6 +36,7 @@ graph TD; amplify-photo-picker --> amplify-icon amplify-photo-picker --> amplify-button amplify-picker --> amplify-button + amplify-button --> amplify-icon style amplify-s3-image-picker fill:#f9f,stroke:#333,stroke-width:4px ``` diff --git a/packages/amplify-ui-components/src/components/amplify-s3-text-picker/readme.md b/packages/amplify-ui-components/src/components/amplify-s3-text-picker/readme.md index 2c7bc987edb..d62c09969ad 100644 --- a/packages/amplify-ui-components/src/components/amplify-s3-text-picker/readme.md +++ b/packages/amplify-ui-components/src/components/amplify-s3-text-picker/readme.md @@ -31,6 +31,7 @@ graph TD; amplify-s3-text-picker --> amplify-s3-text amplify-s3-text-picker --> amplify-picker amplify-picker --> amplify-button + amplify-button --> amplify-icon style amplify-s3-text-picker fill:#f9f,stroke:#333,stroke-width:4px ``` diff --git a/packages/amplify-ui-components/src/components/amplify-select-mfa-type/readme.md b/packages/amplify-ui-components/src/components/amplify-select-mfa-type/readme.md index 2f399a8600c..1126d910ec4 100644 --- a/packages/amplify-ui-components/src/components/amplify-select-mfa-type/readme.md +++ b/packages/amplify-ui-components/src/components/amplify-select-mfa-type/readme.md @@ -29,6 +29,7 @@ graph TD; amplify-form-section --> amplify-section amplify-form-section --> amplify-button amplify-form-section --> amplify-loading-spinner + amplify-button --> amplify-icon amplify-loading-spinner --> amplify-icon amplify-radio-button --> amplify-label amplify-totp-setup --> amplify-form-section diff --git a/packages/amplify-ui-components/src/components/amplify-sign-in/readme.md b/packages/amplify-ui-components/src/components/amplify-sign-in/readme.md index 71516df407f..e267528a813 100644 --- a/packages/amplify-ui-components/src/components/amplify-sign-in/readme.md +++ b/packages/amplify-ui-components/src/components/amplify-sign-in/readme.md @@ -51,6 +51,7 @@ graph TD; amplify-sign-in --> amplify-strike amplify-sign-in --> amplify-auth-fields amplify-sign-in --> amplify-loading-spinner + amplify-button --> amplify-icon amplify-form-section --> amplify-section amplify-form-section --> amplify-button amplify-form-section --> amplify-loading-spinner diff --git a/packages/amplify-ui-components/src/components/amplify-sign-out/readme.md b/packages/amplify-ui-components/src/components/amplify-sign-out/readme.md index 699179dced8..bf48b2f601b 100644 --- a/packages/amplify-ui-components/src/components/amplify-sign-out/readme.md +++ b/packages/amplify-ui-components/src/components/amplify-sign-out/readme.md @@ -25,6 +25,7 @@ ```mermaid graph TD; amplify-sign-out --> amplify-button + amplify-button --> amplify-icon amplify-greetings --> amplify-sign-out style amplify-sign-out fill:#f9f,stroke:#333,stroke-width:4px ``` diff --git a/packages/amplify-ui-components/src/components/amplify-sign-up/readme.md b/packages/amplify-ui-components/src/components/amplify-sign-up/readme.md index c50d5986663..10d274d1734 100644 --- a/packages/amplify-ui-components/src/components/amplify-sign-up/readme.md +++ b/packages/amplify-ui-components/src/components/amplify-sign-up/readme.md @@ -51,6 +51,7 @@ graph TD; amplify-form-section --> amplify-section amplify-form-section --> amplify-button amplify-form-section --> amplify-loading-spinner + amplify-button --> amplify-icon amplify-loading-spinner --> amplify-icon amplify-auth-fields --> amplify-username-field amplify-auth-fields --> amplify-password-field diff --git a/packages/amplify-ui-components/src/components/amplify-totp-setup/readme.md b/packages/amplify-ui-components/src/components/amplify-totp-setup/readme.md index f5d828d179c..3ef1ec2c38f 100644 --- a/packages/amplify-ui-components/src/components/amplify-totp-setup/readme.md +++ b/packages/amplify-ui-components/src/components/amplify-totp-setup/readme.md @@ -33,6 +33,7 @@ graph TD; amplify-form-section --> amplify-section amplify-form-section --> amplify-button amplify-form-section --> amplify-loading-spinner + amplify-button --> amplify-icon amplify-loading-spinner --> amplify-icon amplify-form-field --> amplify-label amplify-form-field --> amplify-input diff --git a/packages/amplify-ui-components/src/components/amplify-verify-contact/readme.md b/packages/amplify-ui-components/src/components/amplify-verify-contact/readme.md index 64e4800ace0..a75b06031f4 100644 --- a/packages/amplify-ui-components/src/components/amplify-verify-contact/readme.md +++ b/packages/amplify-ui-components/src/components/amplify-verify-contact/readme.md @@ -35,6 +35,7 @@ graph TD; amplify-form-section --> amplify-section amplify-form-section --> amplify-button amplify-form-section --> amplify-loading-spinner + amplify-button --> amplify-icon amplify-loading-spinner --> amplify-icon amplify-authenticator --> amplify-verify-contact style amplify-verify-contact fill:#f9f,stroke:#333,stroke-width:4px diff --git a/packages/aws-amplify-react/src/Interactions/ChatBot.tsx b/packages/aws-amplify-react/src/Interactions/ChatBot.tsx index aa156b4c8d7..b2a6f812816 100644 --- a/packages/aws-amplify-react/src/Interactions/ChatBot.tsx +++ b/packages/aws-amplify-react/src/Interactions/ChatBot.tsx @@ -97,7 +97,6 @@ export class ChatBot extends React.Component { constructor(props) { super(props); - if (this.props.voiceEnabled) { require('./aws-lex-audio'); // @ts-ignore From 49f7e36a2749e8a6696c2f46759c058a1bd02843 Mon Sep 17 00:00:00 2001 From: wlee221 Date: Tue, 4 Aug 2020 13:58:15 -0700 Subject: [PATCH 04/71] Update snapshot --- .../__snapshots__/amplify-icon.spec.ts.snap | 184 ++++++++---------- 1 file changed, 77 insertions(+), 107 deletions(-) diff --git a/packages/amplify-ui-components/src/components/amplify-icon/__snapshots__/amplify-icon.spec.ts.snap b/packages/amplify-ui-components/src/components/amplify-icon/__snapshots__/amplify-icon.spec.ts.snap index 6e03f4edddd..88e775a427f 100644 --- a/packages/amplify-ui-components/src/components/amplify-icon/__snapshots__/amplify-icon.spec.ts.snap +++ b/packages/amplify-ui-components/src/components/amplify-icon/__snapshots__/amplify-icon.spec.ts.snap @@ -2,188 +2,158 @@ exports[`amplify-icon spec: Render logic -> renders amazon correctly 1`] = ` - - - - - - - + + + + +
`; exports[`amplify-icon spec: Render logic -> renders auth0 correctly 1`] = ` - - - - - + + + `; exports[`amplify-icon spec: Render logic -> renders enter-vr correctly 1`] = ` - - - - - - + + + + - - + + `; exports[`amplify-icon spec: Render logic -> renders exit-vr correctly 1`] = ` - - - - - - + + + + - - + + `; exports[`amplify-icon spec: Render logic -> renders facebook correctly 1`] = ` - - - - - - + + + + - - + + `; exports[`amplify-icon spec: Render logic -> renders google correctly 1`] = ` - - - - - - - - + + + + + + `; exports[`amplify-icon spec: Render logic -> renders loading correctly 1`] = ` - - - - - - - - + + + + + + - - + + `; exports[`amplify-icon spec: Render logic -> renders maximize correctly 1`] = ` - - - - - - - + + + + + `; exports[`amplify-icon spec: Render logic -> renders microphone correctly 1`] = ` - - - - - + + + `; exports[`amplify-icon spec: Render logic -> renders minimize correctly 1`] = ` - - - - - - - + + + + + `; exports[`amplify-icon spec: Render logic -> renders photoPlaceholder correctly 1`] = ` - - - - - - - + + + + + `; exports[`amplify-icon spec: Render logic -> renders send correctly 1`] = ` - - - - - + + + `; exports[`amplify-icon spec: Render logic -> renders sound correctly 1`] = ` - - - - - - - + + + + + `; exports[`amplify-icon spec: Render logic -> renders sound-mute correctly 1`] = ` - - - - - - - + + + + + `; exports[`amplify-icon spec: Render logic -> renders warning correctly 1`] = ` - - - - - - + + + + - - + + `; From 6c357285ff095351769eebfb99459f890aa6e044 Mon Sep 17 00:00:00 2001 From: wlee221 Date: Tue, 4 Aug 2020 15:12:05 -0700 Subject: [PATCH 05/71] Clean up code --- .../amplify-chatbot/amplify-chatbot.scss | 19 ------------------- .../amplify-chatbot/amplify-chatbot.tsx | 8 -------- 2 files changed, 27 deletions(-) diff --git a/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.scss b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.scss index 6fce906b36e..dc75538fbc7 100644 --- a/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.scss +++ b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.scss @@ -58,31 +58,12 @@ --border: none; --margin: 0; } - // input { - // padding: 1.25rem; - // flex-grow: 1; - // border: none; - // &:focus { - // outline: none; - // } - // } } - .icon-button { --icon-height: 1.25rem; --icon-color: var(--amplify-primary-color); --padding: 0.625rem; } - -// .amplify-icon-button { -// margin-right: 0.625rem; -// cursor: pointer; - -// amplify-icon { -// --height: 1.25rem; -// --icon-fill-color: var(--amplify-primary-color); -// } -// } .chatbot-footer { position: absolute; bottom: 0px; diff --git a/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx index 6a39004b3de..d0e65b2aa6b 100644 --- a/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx +++ b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx @@ -74,14 +74,6 @@ export class AmplifyChatbot {
{this.botTitle}
{this.messageJSX(this.messages)}
- {/* this.handleChange(e)} - /> */} - Date: Tue, 4 Aug 2020 15:52:16 -0700 Subject: [PATCH 06/71] Remove unused test case --- .../components/amplify-container/amplify-container.spec.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/packages/amplify-ui-components/src/components/amplify-container/amplify-container.spec.ts b/packages/amplify-ui-components/src/components/amplify-container/amplify-container.spec.ts index 60d8018ae1f..21a0a71352a 100644 --- a/packages/amplify-ui-components/src/components/amplify-container/amplify-container.spec.ts +++ b/packages/amplify-ui-components/src/components/amplify-container/amplify-container.spec.ts @@ -2,12 +2,6 @@ import { newSpecPage } from '@stencil/core/testing'; import { AmplifyContainer } from './amplify-container'; describe('amplify-container spec:', () => { - describe('Component logic ->', () => { - // let container; - // beforeEach(() => { - // container = new AmplifyContainer(); - // }); - }); describe('Render logic ->', () => { it('should render with an empty slot', async () => { const page = await newSpecPage({ From 5f404e8e904c9efbf313e621b7262a5129466a17 Mon Sep 17 00:00:00 2001 From: wlee221 Date: Tue, 11 Aug 2020 11:42:55 -0700 Subject: [PATCH 07/71] Add snapshot testing --- .../amplify-chatbot.spec.tsx.snap | 17 +++++++++++++ .../amplify-chatbot/amplify-chatbot.spec.tsx | 25 ++++++------------- .../amplify-chatbot/amplify-chatbot.tsx | 16 +++--------- 3 files changed, 28 insertions(+), 30 deletions(-) create mode 100644 packages/amplify-ui-components/src/components/amplify-chatbot/__snapshots__/amplify-chatbot.spec.tsx.snap diff --git a/packages/amplify-ui-components/src/components/amplify-chatbot/__snapshots__/amplify-chatbot.spec.tsx.snap b/packages/amplify-ui-components/src/components/amplify-chatbot/__snapshots__/amplify-chatbot.spec.tsx.snap new file mode 100644 index 00000000000..6e5c67a069a --- /dev/null +++ b/packages/amplify-ui-components/src/components/amplify-chatbot/__snapshots__/amplify-chatbot.spec.tsx.snap @@ -0,0 +1,17 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`amplify-chatbot renders chatbot 1`] = ` + + +
+
+
+
+ + + +
+
+
+
+`; diff --git a/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.spec.tsx b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.spec.tsx index b26b5c150a6..9f9840ae35d 100644 --- a/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.spec.tsx +++ b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.spec.tsx @@ -1,21 +1,12 @@ -// import { newSpecPage } from '@stencil/core/testing'; -// import { AmplifyChatbot } from './amplify-chatbot'; +import { newSpecPage } from '@stencil/core/testing'; +import { AmplifyChatbot } from './amplify-chatbot'; describe('amplify-chatbot', () => { - // it('renders', async () => { - // const page = await newSpecPage({ - // components: [AmplifyChatbot], - // html: ``, - // }); - // expect(page.root).toEqualHtml(` - // - // - // - // - // - // `); - // }); - it('Test not yet implemented', () => { - return expect(true).toBe(true); + it('renders chatbot', async () => { + const page = await newSpecPage({ + components: [AmplifyChatbot], + html: ``, + }); + expect(page.root).toMatchSnapshot(); }); }); diff --git a/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx index d0e65b2aa6b..aa8fe5532a3 100644 --- a/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx +++ b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx @@ -29,7 +29,7 @@ export class AmplifyChatbot { @Listen('formSubmit') submitHandler(_event: CustomEvent) { - this.send(); + this.sendText(); } messageJSX = (messages: Message[]) => { @@ -41,7 +41,7 @@ export class AmplifyChatbot { this.text = target.value; } - send() { + sendText() { if (this.text === '') return; this.messages = [ ...this.messages, @@ -58,16 +58,6 @@ export class AmplifyChatbot { } render() { - const testMessages: Message[] = [ - { content: 'hi', from: 'bot' }, - { content: 'User: hi', from: 'user' }, - { - content: - 'long mesageeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee', - from: 'bot', - }, - ]; - if (this.messages.length === 0) this.messages.push(...testMessages); return (
@@ -82,7 +72,7 @@ export class AmplifyChatbot { value={this.text} > - this.send()} /> + this.sendText()} />
From b09348f9f1b4ba77acfbdaafcd0fb0802426a1fc Mon Sep 17 00:00:00 2001 From: wlee221 Date: Tue, 11 Aug 2020 17:56:05 -0700 Subject: [PATCH 08/71] Apply comments from @ashika01 --- .../amplify-button/amplify-button.scss | 2 +- .../amplify-chatbot/amplify-chatbot.scss | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/amplify-ui-components/src/components/amplify-button/amplify-button.scss b/packages/amplify-ui-components/src/components/amplify-button/amplify-button.scss index 5714b87b3d1..2d047f461e1 100644 --- a/packages/amplify-ui-components/src/components/amplify-button/amplify-button.scss +++ b/packages/amplify-ui-components/src/components/amplify-button/amplify-button.scss @@ -10,7 +10,7 @@ --link-hover: var(--amplify-primary-tint); --link-active: var(--amplify-primary-shade); --text-transform: uppercase; - --icon-color: var(--amplify-primary-color); + --icon-color: var(--amplify-white); --icon-height: 1.25rem; --padding: 1rem; diff --git a/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.scss b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.scss index dc75538fbc7..7abfd1c524a 100644 --- a/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.scss +++ b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.scss @@ -5,27 +5,27 @@ .amplify-chatbot { display: inline-block; background-color: var(--background-color); - border-radius: 6px; - box-shadow: 1px 0px 4px 0 rgba(0, 0, 0, 0.15); + border-radius: 0.375rem; + box-shadow: 0.0625rem 0rem 0.25rem 0 rgba(0, 0, 0, 0.15); box-sizing: border-box; font-family: var(--amplify-font-family); - width: 460px; + width: 28.75rem; margin-bottom: 1rem; } .header { - margin-top: 20px; + margin-top: 1.25rem; color: var(--header-color); font-size: var(--header-size); font-weight: bold; text-align: center; - padding-bottom: 24px; - border-bottom: 1px solid rgba(0, 0, 0, 0.05); + padding-bottom: 1.5rem; + border-bottom: 0.0625rem solid rgba(0, 0, 0, 0.05); } .body { padding-top: 2rem; display: flex; flex-direction: column; - height: 500px; + height: 31.25rem; overflow: auto; } .bubble { @@ -52,7 +52,7 @@ .chatbot-control { display: flex; align-items: center; - border-top: 1px solid rgba(0, 0, 0, 0.05); + border-top: 0.062rem solid rgba(0, 0, 0, 0.05); padding-right: 0.625rem; amplify-input { --border: none; @@ -66,6 +66,6 @@ } .chatbot-footer { position: absolute; - bottom: 0px; + bottom: 0rem; height: 2rem; } From a865263fdea8554b40e8ac3bc6f28c4b329c99fb Mon Sep 17 00:00:00 2001 From: wlee221 Date: Wed, 12 Aug 2020 11:09:22 -0700 Subject: [PATCH 09/71] Rename --icon-color to --icon-fill --- .../src/components/amplify-button/amplify-button.scss | 4 ++-- .../src/components/amplify-icon/amplify-icon.scss | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/amplify-ui-components/src/components/amplify-button/amplify-button.scss b/packages/amplify-ui-components/src/components/amplify-button/amplify-button.scss index 2d047f461e1..46b5593b4ce 100644 --- a/packages/amplify-ui-components/src/components/amplify-button/amplify-button.scss +++ b/packages/amplify-ui-components/src/components/amplify-button/amplify-button.scss @@ -10,7 +10,7 @@ --link-hover: var(--amplify-primary-tint); --link-active: var(--amplify-primary-shade); --text-transform: uppercase; - --icon-color: var(--amplify-white); + --icon-fill: var(--amplify-white); --icon-height: 1.25rem; --padding: 1rem; @@ -69,7 +69,7 @@ cursor: pointer; padding: var(--padding); amplify-icon { - --icon-fill-color: var(--icon-color); + --icon-fill-color: var(--icon-fill); --height: var(--icon-height); } } diff --git a/packages/amplify-ui-components/src/components/amplify-icon/amplify-icon.scss b/packages/amplify-ui-components/src/components/amplify-icon/amplify-icon.scss index 69106f5c362..fd274ccb3c9 100644 --- a/packages/amplify-ui-components/src/components/amplify-icon/amplify-icon.scss +++ b/packages/amplify-ui-components/src/components/amplify-icon/amplify-icon.scss @@ -1,7 +1,8 @@ :host { display: inline-block; - --icon-fill-color: var(--amplify-white); --width: auto; + --height: 1.25rem; + --icon-fill-color: var(--amplify-white); height: var(--height); } From 82155ff262aa4a12d0dbfdf5c6ac8c908b5feb0c Mon Sep 17 00:00:00 2001 From: wlee221 Date: Wed, 12 Aug 2020 11:09:33 -0700 Subject: [PATCH 10/71] Remove unused class css --- .../src/components/amplify-chatbot/amplify-chatbot.scss | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.scss b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.scss index 7abfd1c524a..fd21344cd49 100644 --- a/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.scss +++ b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.scss @@ -61,11 +61,6 @@ } .icon-button { --icon-height: 1.25rem; - --icon-color: var(--amplify-primary-color); + --icon-fill: var(--amplify-primary-color); --padding: 0.625rem; } -.chatbot-footer { - position: absolute; - bottom: 0rem; - height: 2rem; -} From d504ff8399a0df4369c08cd66c00982a358224cb Mon Sep 17 00:00:00 2001 From: wlee221 Date: Wed, 12 Aug 2020 14:08:00 -0700 Subject: [PATCH 11/71] Update css for compatibility with existing components --- .../src/components/amplify-icon/amplify-icon.scss | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/amplify-ui-components/src/components/amplify-icon/amplify-icon.scss b/packages/amplify-ui-components/src/components/amplify-icon/amplify-icon.scss index fd274ccb3c9..b61ee492ed2 100644 --- a/packages/amplify-ui-components/src/components/amplify-icon/amplify-icon.scss +++ b/packages/amplify-ui-components/src/components/amplify-icon/amplify-icon.scss @@ -1,13 +1,11 @@ :host { - display: inline-block; --width: auto; - --height: 1.25rem; + --height: auto; --icon-fill-color: var(--amplify-white); - height: var(--height); } svg { fill: var(--icon-fill-color); width: var(--width); - height: 100%; + height: var(--height); } From e686aa3244ca850c80e68bc60ee0312716d49aa9 Mon Sep 17 00:00:00 2001 From: wlee221 Date: Wed, 12 Aug 2020 14:08:25 -0700 Subject: [PATCH 12/71] Set default height --- .../src/components/amplify-icon/icons.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/amplify-ui-components/src/components/amplify-icon/icons.tsx b/packages/amplify-ui-components/src/components/amplify-icon/icons.tsx index 4542c124ede..50d6567f26c 100644 --- a/packages/amplify-ui-components/src/components/amplify-icon/icons.tsx +++ b/packages/amplify-ui-components/src/components/amplify-icon/icons.tsx @@ -192,7 +192,7 @@ export const icons = { microphone() { return ( - + ); @@ -200,7 +200,7 @@ export const icons = { send() { return ( - + ); From dc4dbb10258467026445bb22d6004c6bb1214942 Mon Sep 17 00:00:00 2001 From: wlee221 Date: Wed, 12 Aug 2020 14:09:12 -0700 Subject: [PATCH 13/71] Integrate Interactions text message --- .../src/common/constants.ts | 2 + .../amplify-ui-components/src/components.d.ts | 16 ++++ .../amplify-chatbot/amplify-chatbot.tsx | 96 +++++++++++++++---- .../src/components/amplify-chatbot/readme.md | 2 + .../amplify-ui-components/stencil.config.ts | 8 +- 5 files changed, 103 insertions(+), 21 deletions(-) diff --git a/packages/amplify-ui-components/src/common/constants.ts b/packages/amplify-ui-components/src/common/constants.ts index ee2718ad19e..3830d9da945 100644 --- a/packages/amplify-ui-components/src/common/constants.ts +++ b/packages/amplify-ui-components/src/common/constants.ts @@ -20,6 +20,8 @@ export const AUTHENTICATOR_AUTHSTATE = 'amplify-authenticator-authState'; export const PHONE_EMPTY_ERROR_MESSAGE = 'Phone number can not be empty'; export const NO_AUTH_MODULE_FOUND = 'No Auth module found, please ensure @aws-amplify/auth is imported'; export const NO_STORAGE_MODULE_FOUND = 'No Storage module found, please ensure @aws-amplify/storage is imported'; +export const NO_Interactions_MODULE_FOUND = + 'No Interactions module found, please ensure @aws-amplify/interactions is imported'; // TOTP Messages export const SETUP_TOTP = 'SETUP_TOTP'; diff --git a/packages/amplify-ui-components/src/components.d.ts b/packages/amplify-ui-components/src/components.d.ts index 962e9f41b62..c4ac45201b3 100644 --- a/packages/amplify-ui-components/src/components.d.ts +++ b/packages/amplify-ui-components/src/components.d.ts @@ -101,6 +101,14 @@ export namespace Components { * Callback to be called after conversation finishes */ "onComplete": (err: string, data: object) => void; + /** + * Whether text chat is enabled + */ + "textEnabled": boolean; + /** + * Whether voice chat is enabled + */ + "voiceEnabled": boolean; /** * Greeting message displayed to users */ @@ -1589,6 +1597,14 @@ declare namespace LocalJSX { * Callback to be called after conversation finishes */ "onComplete"?: (err: string, data: object) => void; + /** + * Whether text chat is enabled + */ + "textEnabled"?: boolean; + /** + * Whether voice chat is enabled + */ + "voiceEnabled"?: boolean; /** * Greeting message displayed to users */ diff --git a/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx index aa8fe5532a3..c675f716f1c 100644 --- a/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx +++ b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx @@ -1,10 +1,15 @@ import { Component, Host, h, Prop, State, Listen } from '@stencil/core'; +import { Interactions } from '@aws-amplify/interactions'; +import { JSXBase } from '@stencil/core/internal'; +type Agent = 'user' | 'bot'; interface Message { content: string; - from: 'user' | 'bot'; + from: Agent; } +type AppState = 'initial' | 'listening' | 'sending' | 'speaking'; + @Component({ tag: 'amplify-chatbot', styleUrl: 'amplify-chatbot.scss', @@ -23,9 +28,16 @@ export class AmplifyChatbot { @Prop() onComplete: (err: string, data: object) => void; /** Text placed in the top header */ @Prop() botTitle: string; + /** Whether voice chat is enabled */ + @Prop() voiceEnabled: boolean = false; + /** Whether text chat is enabled */ + @Prop() textEnabled: boolean = true; + /** Messages in current session */ @State() messages: Message[] = []; + /** Value of the text */ @State() text: string = ''; + @State() state: AppState = 'initial'; @Listen('formSubmit') submitHandler(_event: CustomEvent) { @@ -41,39 +53,83 @@ export class AmplifyChatbot { this.text = target.value; } - sendText() { - if (this.text === '') return; + appendMessage(content: string, from: Agent) { this.messages = [ ...this.messages, { - content: this.text, - from: 'user', - }, - { - content: 'echo: ' + this.text, - from: 'bot', + content, + from, }, ]; + } + + async sendText() { + if (this.text.length === 0 || this.state !== 'initial') return; + + const text = this.text; this.text = ''; + this.appendMessage(text, 'user'); + this.state = 'sending'; + + const response = await Interactions.send(this.botName, text); + this.appendMessage(response.message, 'bot'); + this.state = 'initial'; + } + + async componentWillLoad() { + // TODO: check if interactions is avaialable + if (this.messages.length === 0 && this.welcomeMessage && this.welcomeMessage.length > 0) { + this.messages.push({ + content: this.welcomeMessage, + from: 'bot', + }); + } + } + + footerJSX(): JSXBase.IntrinsicElements[] { + const textInput = ( + this.handleChange(evt)} + value={this.text} + /> + ); + const micButton = ( + + ); + const sendButton = ( + this.sendText()} + disabled={this.state !== 'initial'} + /> + ); + // TODO: clever way to handle this logic? + console.log(this.voiceEnabled && this.textEnabled); + if (this.voiceEnabled && this.textEnabled) { + return [textInput, micButton, sendButton]; + } else if (this.voiceEnabled) { + return [textInput, micButton]; + } else if (this.textEnabled) { + return [textInput, sendButton]; + } else { + // TODO: throw an warning that at least one should be enabled. + return [textInput, sendButton]; + } } render() { + console.log(this.voiceEnabled); return (
{this.botTitle}
{this.messageJSX(this.messages)}
-
- this.handleChange(evt)} - value={this.text} - > - - this.sendText()} /> -
+
{this.footerJSX()}
); diff --git a/packages/amplify-ui-components/src/components/amplify-chatbot/readme.md b/packages/amplify-ui-components/src/components/amplify-chatbot/readme.md index 67dae60faf3..926101d3858 100644 --- a/packages/amplify-ui-components/src/components/amplify-chatbot/readme.md +++ b/packages/amplify-ui-components/src/components/amplify-chatbot/readme.md @@ -14,6 +14,8 @@ | `clearOnComplete` | `clear-on-complete` | Clear messages when conversation finishes | `boolean` | `false` | | `conversationModeOn` | `conversation-mode-on` | Continue listening to users after they send the message | `boolean` | `false` | | `onComplete` | -- | Callback to be called after conversation finishes | `(err: string, data: object) => void` | `undefined` | +| `textEnabled` | `text-enabled` | Whether text chat is enabled | `boolean` | `true` | +| `voiceEnabled` | `voice-enabled` | Whether voice chat is enabled | `boolean` | `false` | | `welcomeMessage` | `welcome-message` | Greeting message displayed to users | `string` | `undefined` | diff --git a/packages/amplify-ui-components/stencil.config.ts b/packages/amplify-ui-components/stencil.config.ts index 6e449aaf5c8..6e17326c604 100644 --- a/packages/amplify-ui-components/stencil.config.ts +++ b/packages/amplify-ui-components/stencil.config.ts @@ -14,7 +14,13 @@ export const config: Config = { plugins: [ externals({ // deps to include in externals (default: []) - include: ['@aws-amplify/auth', '@aws-amplify/core', '@aws-amplify/storage', '@aws-amplify/xr'], + include: [ + '@aws-amplify/auth', + '@aws-amplify/core', + '@aws-amplify/storage', + '@aws-amplify/xr', + '@aws-amplify/interactions', + ], }), nodePolyfills(), sass({ From 2a8d0525d0962f1984e031dd7482d519374aa9ed Mon Sep 17 00:00:00 2001 From: wlee221 Date: Wed, 12 Aug 2020 14:14:04 -0700 Subject: [PATCH 14/71] Update snapshots --- .../__snapshots__/amplify-chatbot.spec.tsx.snap | 1 - .../amplify-icon/__snapshots__/amplify-icon.spec.ts.snap | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/amplify-ui-components/src/components/amplify-chatbot/__snapshots__/amplify-chatbot.spec.tsx.snap b/packages/amplify-ui-components/src/components/amplify-chatbot/__snapshots__/amplify-chatbot.spec.tsx.snap index 6e5c67a069a..06c5f559ba3 100644 --- a/packages/amplify-ui-components/src/components/amplify-chatbot/__snapshots__/amplify-chatbot.spec.tsx.snap +++ b/packages/amplify-ui-components/src/components/amplify-chatbot/__snapshots__/amplify-chatbot.spec.tsx.snap @@ -8,7 +8,6 @@ exports[`amplify-chatbot renders chatbot 1`] = `
-
diff --git a/packages/amplify-ui-components/src/components/amplify-icon/__snapshots__/amplify-icon.spec.ts.snap b/packages/amplify-ui-components/src/components/amplify-icon/__snapshots__/amplify-icon.spec.ts.snap index 88e775a427f..c4b2e31a5f1 100644 --- a/packages/amplify-ui-components/src/components/amplify-icon/__snapshots__/amplify-icon.spec.ts.snap +++ b/packages/amplify-ui-components/src/components/amplify-icon/__snapshots__/amplify-icon.spec.ts.snap @@ -92,7 +92,7 @@ exports[`amplify-icon spec: Render logic -> renders maximize correctly 1`] = ` exports[`amplify-icon spec: Render logic -> renders microphone correctly 1`] = ` - + @@ -120,7 +120,7 @@ exports[`amplify-icon spec: Render logic -> renders photoPlaceholder correctly 1 exports[`amplify-icon spec: Render logic -> renders send correctly 1`] = ` - + From 0189195aa2fe496604222d82ebfc3ba8c1cdbc02 Mon Sep 17 00:00:00 2001 From: wlee221 Date: Wed, 12 Aug 2020 14:26:43 -0700 Subject: [PATCH 15/71] Simplify code --- .../amplify-chatbot/amplify-chatbot.tsx | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx index c675f716f1c..91adad8af13 100644 --- a/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx +++ b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx @@ -96,10 +96,10 @@ export class AmplifyChatbot { value={this.text} /> ); - const micButton = ( + const micButton = this.voiceEnabled && ( ); - const sendButton = ( + const sendButton = this.textEnabled && ( ); - // TODO: clever way to handle this logic? - console.log(this.voiceEnabled && this.textEnabled); - if (this.voiceEnabled && this.textEnabled) { - return [textInput, micButton, sendButton]; - } else if (this.voiceEnabled) { - return [textInput, micButton]; - } else if (this.textEnabled) { - return [textInput, sendButton]; - } else { - // TODO: throw an warning that at least one should be enabled. - return [textInput, sendButton]; - } + return [textInput, micButton, sendButton]; } render() { From 5d8e16cdf85f03d8953c723131468ea4e4037a33 Mon Sep 17 00:00:00 2001 From: wlee221 Date: Thu, 13 Aug 2020 12:04:19 -0700 Subject: [PATCH 16/71] Add audiorecorder and integrate voice chat --- .../src/common/audio-control/helper.ts | 86 ++++++++++ .../src/common/audio-control/recorder.ts | 150 ++++++++++++++++++ .../src/common/constants.ts | 2 +- .../src/common/types/interactions-types.ts | 4 + .../amplify-ui-components/src/components.d.ts | 9 +- .../amplify-chatbot/amplify-chatbot.tsx | 113 +++++++++++-- .../src/components/amplify-chatbot/readme.md | 26 +-- 7 files changed, 359 insertions(+), 31 deletions(-) create mode 100644 packages/amplify-ui-components/src/common/audio-control/helper.ts create mode 100644 packages/amplify-ui-components/src/common/audio-control/recorder.ts create mode 100644 packages/amplify-ui-components/src/common/types/interactions-types.ts diff --git a/packages/amplify-ui-components/src/common/audio-control/helper.ts b/packages/amplify-ui-components/src/common/audio-control/helper.ts new file mode 100644 index 00000000000..5658086eab1 --- /dev/null +++ b/packages/amplify-ui-components/src/common/audio-control/helper.ts @@ -0,0 +1,86 @@ +export const exportBuffer = ( + recBuffer: Float32Array[], + recLength: number, + recordSampleRate: number, + exportSampleRate: number, +) => { + const mergedBuffers = mergeBuffers(recBuffer, recLength); + const downsampledBuffer = downsampleBuffer(mergedBuffers, recordSampleRate, exportSampleRate); + const encodedWav = encodeWAV(downsampledBuffer, exportSampleRate); + const audioBlob = new Blob([encodedWav], { + type: 'application/octet-stream', + }); + return audioBlob; +}; + +const mergeBuffers = (bufferArray: Float32Array[], recLength: number) => { + const result = new Float32Array(recLength); + let offset = 0; + for (let i = 0; i < bufferArray.length; i++) { + result.set(bufferArray[i], offset); + offset += bufferArray[i].length; + } + return result; +}; + +const downsampleBuffer = (buffer: Float32Array, recordSampleRate: number, exportSampleRate: number) => { + if (exportSampleRate === recordSampleRate) { + return buffer; + } + const sampleRateRatio = recordSampleRate / exportSampleRate; + const newLength = Math.round(buffer.length / sampleRateRatio); + const result = new Float32Array(newLength); + let offsetResult = 0; + let offsetBuffer = 0; + while (offsetResult < result.length) { + const nextOffsetBuffer = Math.round((offsetResult + 1) * sampleRateRatio); + let accum = 0, + count = 0; + for (let i = offsetBuffer; i < nextOffsetBuffer && i < buffer.length; i++) { + accum += buffer[i]; + count++; + } + result[offsetResult] = accum / count; + offsetResult++; + offsetBuffer = nextOffsetBuffer; + } + return result; +}; + +const floatTo16BitPCM = (output: DataView, offset: number, input: Float32Array) => { + let byteOffset = offset; + for (let i = 0; i < input.length; i++, byteOffset += 2) { + const s = Math.max(-1, Math.min(1, input[i])); + output.setInt16(byteOffset, s < 0 ? s * 0x8000 : s * 0x7fff, true); + } +}; + +const writeString = (view: DataView, offset: number, string: string) => { + for (let i = 0; i < string.length; i++) { + view.setUint8(offset + i, string.charCodeAt(i)); + } +}; + +const encodeWAV = (samples: Float32Array, exportSampleRate: number) => { + const buffer = new ArrayBuffer(44 + samples.length * 2); + const view = new DataView(buffer); + writeString(view, 0, 'RIFF'); + view.setUint32(4, 32 + samples.length * 2, true); + writeString(view, 8, 'WAVE'); + + // fmt subchunk + writeString(view, 12, 'fmt '); + view.setUint32(16, 16, true); + view.setUint16(20, 1, true); + view.setUint16(22, 1, true); + view.setUint32(24, exportSampleRate, true); + view.setUint32(28, exportSampleRate * 2, true); + view.setUint16(32, 2, true); + view.setUint16(34, 16, true); + + // data subchunk + writeString(view, 36, 'data'); + view.setUint32(40, samples.length * 2, true); + floatTo16BitPCM(view, 44, samples); + return view; +}; diff --git a/packages/amplify-ui-components/src/common/audio-control/recorder.ts b/packages/amplify-ui-components/src/common/audio-control/recorder.ts new file mode 100644 index 00000000000..737ffa97723 --- /dev/null +++ b/packages/amplify-ui-components/src/common/audio-control/recorder.ts @@ -0,0 +1,150 @@ +import { exportBuffer } from './helper'; + +type SilenceDetectionConfig = { + time: number; + amplitude: number; +}; + +export class AudioRecorder { + private options: SilenceDetectionConfig; + private audioContext: AudioContext; + private audioSupported: boolean; + + private analyserNode: AnalyserNode; + private onSilence: Function; + + // input mic stream is stored in a buffer + private streamBuffer: Float32Array[] = []; + private streamBufferLength = 0; + + // recording props + private start: number; + private recording = false; + + constructor(options: SilenceDetectionConfig) { + this.options = options; + this.init(); + } + + private init() { + window.AudioContext = window.AudioContext || (window as any).webkitAudioContext; + this.audioContext = new AudioContext(); + navigator.mediaDevices + .getUserMedia({ audio: true }) + .then(stream => { + this.audioSupported = true; + this.setupAudioNodes(stream); + }) + .catch(() => { + this.audioSupported = false; + }); + } + + private async setupAudioNodes(stream: MediaStream) { + console.log(this.audioContext); + + await this.audioContext + .resume() + .then(() => console.log('resumed')) + .catch(() => console.error('what?')); + const sourceNode = this.audioContext.createMediaStreamSource(stream); + const processorNode = this.audioContext.createScriptProcessor(4096, 1, 1); + + processorNode.onaudioprocess = audioProcessingEvent => { + if (!this.recording) return; + const stream = audioProcessingEvent.inputBuffer.getChannelData(0); + console.log(stream); + this.streamBuffer.push(new Float32Array(stream)); // set to a copy of the stream + this.streamBufferLength += stream.length; + this.analyse(); + }; + + const analyserNode = this.audioContext.createAnalyser(); + analyserNode.minDecibels = -90; + analyserNode.maxDecibels = -10; + analyserNode.smoothingTimeConstant = 0.85; + + sourceNode.connect(analyserNode); + analyserNode.connect(processorNode); + processorNode.connect(sourceNode.context.destination); + + this.analyserNode = analyserNode; + } + + public startRecording(onSilence?: Function) { + const silenceHandler = onSilence || function() {}; + this.onSilence = silenceHandler; + if (this.recording || !this.audioSupported) return; + + const context = this.audioContext; + context.resume().then(() => { + this.start = Date.now(); + this.recording = true; + }); + } + + public stopRecording() { + if (!this.audioSupported) return; + this.recording = false; + } + + public clear() { + this.stopRecording(); + this.streamBufferLength = 0; + this.streamBuffer = []; + } + + public play(buffer: Uint8Array, callback: Function) { + if (!buffer) return; + const myBlob = new Blob([buffer]); + + const fileReader = new FileReader(); + fileReader.onload = () => { + const playbackSource = this.audioContext.createBufferSource(); + this.audioContext.decodeAudioData(fileReader.result as ArrayBuffer, buf => { + playbackSource.buffer = buf; + playbackSource.connect(this.audioContext.destination); + playbackSource.onended = function(_event) { + if (typeof callback === 'function') { + callback(); + } + }; + playbackSource.start(0); + }); + }; + fileReader.readAsArrayBuffer(myBlob); + } + + private analyse() { + const analyser = this.analyserNode; + analyser.fftSize = 2048; + + const bufferLength = analyser.fftSize; + const dataArray = new Uint8Array(bufferLength); + const amplitude = this.options.amplitude; + const time = this.options.time; + + analyser.getByteTimeDomainData(dataArray); + + for (let i = 0; i < bufferLength; i++) { + // Normalize between -1 and 1. + const curr_value_time = dataArray[i] / 128 - 1.0; + if (curr_value_time > amplitude || curr_value_time < -1 * amplitude) { + this.start = Date.now(); + } + } + const newtime = Date.now(); + const elapsedTime = newtime - this.start; + if (elapsedTime > time) { + this.onSilence(); + } + } + + public exportWAV(callback: Function, exportSampleRate: number = 16000) { + if (!this.audioSupported) return; + const recordSampleRate = this.audioContext.sampleRate; + const blob = exportBuffer(this.streamBuffer, this.streamBufferLength, recordSampleRate, exportSampleRate); + callback(blob); + this.clear(); + } +} diff --git a/packages/amplify-ui-components/src/common/constants.ts b/packages/amplify-ui-components/src/common/constants.ts index 3830d9da945..23416affef4 100644 --- a/packages/amplify-ui-components/src/common/constants.ts +++ b/packages/amplify-ui-components/src/common/constants.ts @@ -20,7 +20,7 @@ export const AUTHENTICATOR_AUTHSTATE = 'amplify-authenticator-authState'; export const PHONE_EMPTY_ERROR_MESSAGE = 'Phone number can not be empty'; export const NO_AUTH_MODULE_FOUND = 'No Auth module found, please ensure @aws-amplify/auth is imported'; export const NO_STORAGE_MODULE_FOUND = 'No Storage module found, please ensure @aws-amplify/storage is imported'; -export const NO_Interactions_MODULE_FOUND = +export const NO_INTERACTIONS_MODULE_FOUND = 'No Interactions module found, please ensure @aws-amplify/interactions is imported'; // TOTP Messages diff --git a/packages/amplify-ui-components/src/common/types/interactions-types.ts b/packages/amplify-ui-components/src/common/types/interactions-types.ts new file mode 100644 index 00000000000..ce8eefc0b7c --- /dev/null +++ b/packages/amplify-ui-components/src/common/types/interactions-types.ts @@ -0,0 +1,4 @@ +export interface ChatResult { + data?: object; + err?: string; +} diff --git a/packages/amplify-ui-components/src/components.d.ts b/packages/amplify-ui-components/src/components.d.ts index c4ac45201b3..f81584c28d0 100644 --- a/packages/amplify-ui-components/src/components.d.ts +++ b/packages/amplify-ui-components/src/components.d.ts @@ -9,6 +9,7 @@ import { AuthState, AuthStateHandler, CognitoUserInterface, FederatedConfig, MFA import { FormFieldTypes } from "./components/amplify-auth-fields/amplify-auth-fields-interface"; import { ButtonTypes, ButtonVariant, InputEvent, TextFieldTypes } from "./common/types/ui-types"; import { IconNameType } from "./components/amplify-icon/icons"; +import { ChatResult } from "./common/types/interactions-types"; import { FunctionalComponent } from "@stencil/core"; import { CountryCodeDialOptions } from "./components/amplify-country-dial-code/amplify-country-dial-code-interface"; import { AccessLevel, StorageObject } from "./common/types/storage-types"; @@ -97,10 +98,6 @@ export namespace Components { * Continue listening to users after they send the message */ "conversationModeOn": boolean; - /** - * Callback to be called after conversation finishes - */ - "onComplete": (err: string, data: object) => void; /** * Whether text chat is enabled */ @@ -1594,9 +1591,9 @@ declare namespace LocalJSX { */ "conversationModeOn"?: boolean; /** - * Callback to be called after conversation finishes + * Event emitted when conversation is completed */ - "onComplete"?: (err: string, data: object) => void; + "onChatCompleted"?: (event: CustomEvent) => void; /** * Whether text chat is enabled */ diff --git a/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx index 91adad8af13..a7f7565693c 100644 --- a/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx +++ b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx @@ -1,6 +1,9 @@ -import { Component, Host, h, Prop, State, Listen } from '@stencil/core'; +import { Component, Host, h, Prop, State, Listen, Event, EventEmitter } from '@stencil/core'; import { Interactions } from '@aws-amplify/interactions'; import { JSXBase } from '@stencil/core/internal'; +import { AudioRecorder } from '../../common/audio-control/recorder'; +import { ChatResult } from '../../common/types/interactions-types'; +import { NO_INTERACTIONS_MODULE_FOUND } from '../../common/constants'; type Agent = 'user' | 'bot'; interface Message { @@ -24,8 +27,6 @@ export class AmplifyChatbot { @Prop() conversationModeOn: boolean = false; /** Greeting message displayed to users */ @Prop() welcomeMessage: string; - /** Callback to be called after conversation finishes */ - @Prop() onComplete: (err: string, data: object) => void; /** Text placed in the top header */ @Prop() botTitle: string; /** Whether voice chat is enabled */ @@ -35,15 +36,22 @@ export class AmplifyChatbot { /** Messages in current session */ @State() messages: Message[] = []; - /** Value of the text */ + /** Text input box value */ @State() text: string = ''; + /** Current app state */ @State() state: AppState = 'initial'; + private audioRecorder: AudioRecorder; + private audioInput: Blob; + @Listen('formSubmit') submitHandler(_event: CustomEvent) { this.sendText(); } + /** Event emitted when conversation is completed */ + @Event() chatCompleted: EventEmitter; + messageJSX = (messages: Message[]) => { return messages.map(message =>
{message.content}
); }; @@ -63,26 +71,97 @@ export class AmplifyChatbot { ]; } + reset() { + this.state = 'initial'; + this.text = ''; + this.audioRecorder && this.audioRecorder.clear(); + } + + micButtonHandler() { + if (this.state !== 'initial') return; + this.state = 'listening'; + this.audioRecorder.startRecording(() => this.onSilenceHandler()); + } + + onSilenceHandler() { + this.state = 'sending'; + this.audioRecorder.stopRecording(); + this.audioRecorder.exportWAV(blob => { + this.audioInput = blob; + this.lexResponseHandler(); + }); + } + + async lexResponseHandler() { + this.state = 'speaking'; + if (!Interactions || typeof Interactions.send !== 'function') { + throw new Error(NO_INTERACTIONS_MODULE_FOUND); + } + const interactionsMessage = { + content: this.audioInput, + options: { + messageType: 'voice', + }, + }; + const response = await Interactions.send(this.botName, interactionsMessage); + if ((response as any).inputTranscript === '') { + this.appendMessage('⠀', 'user'); + } else { + this.appendMessage((response as any).inputTranscript, 'user'); + } + this.appendMessage((response as any).message, 'bot'); + this.playTranscript((response as any).audioStream); + } + + playTranscript(audioStream: Uint8Array) { + this.audioRecorder.play(audioStream, () => { + this.state = 'initial'; + }); + } + async sendText() { if (this.text.length === 0 || this.state !== 'initial') return; - const text = this.text; this.text = ''; this.appendMessage(text, 'user'); this.state = 'sending'; const response = await Interactions.send(this.botName, text); - this.appendMessage(response.message, 'bot'); + if (response.message) { + this.appendMessage(response.message, 'bot'); + } this.state = 'initial'; } - async componentWillLoad() { - // TODO: check if interactions is avaialable - if (this.messages.length === 0 && this.welcomeMessage && this.welcomeMessage.length > 0) { - this.messages.push({ - content: this.welcomeMessage, - from: 'bot', + connectedCallback() { + if (this.voiceEnabled) { + this.audioRecorder = new AudioRecorder({ + time: 1500, + amplitude: 0.2, }); + console.log(this.audioRecorder); + } + } + + componentWillLoad() { + if (this.messages.length === 0 && this.welcomeMessage && this.welcomeMessage.length > 0) { + this.appendMessage(this.welcomeMessage, 'bot'); + } + if (this.botName) { + if (!Interactions || typeof Interactions.onComplete !== 'function') { + throw new Error(NO_INTERACTIONS_MODULE_FOUND); + } + const onComplete = (err: string, data: object) => { + console.log({ err, data }); + this.chatCompleted.emit({ + data, + err, + }); + if (this.clearOnComplete) { + this.messages = []; + } + }; + Interactions.onComplete(this.botName, onComplete); } } @@ -97,7 +176,13 @@ export class AmplifyChatbot { /> ); const micButton = this.voiceEnabled && ( - + this.micButtonHandler()} + class="icon-button" + variant="icon" + icon="microphone" + disabled={this.state !== 'initial'} + /> ); const sendButton = this.textEnabled && (
diff --git a/packages/amplify-ui-components/src/components/amplify-chatbot/readme.md b/packages/amplify-ui-components/src/components/amplify-chatbot/readme.md index 926101d3858..e9003a0c482 100644 --- a/packages/amplify-ui-components/src/components/amplify-chatbot/readme.md +++ b/packages/amplify-ui-components/src/components/amplify-chatbot/readme.md @@ -7,16 +7,22 @@ ## Properties -| Property | Attribute | Description | Type | Default | -| -------------------- | ---------------------- | ------------------------------------------------------- | ------------------------------------- | ----------- | -| `botName` | `bot-name` | Name of the bot | `string` | `undefined` | -| `botTitle` | `bot-title` | Text placed in the top header | `string` | `undefined` | -| `clearOnComplete` | `clear-on-complete` | Clear messages when conversation finishes | `boolean` | `false` | -| `conversationModeOn` | `conversation-mode-on` | Continue listening to users after they send the message | `boolean` | `false` | -| `onComplete` | -- | Callback to be called after conversation finishes | `(err: string, data: object) => void` | `undefined` | -| `textEnabled` | `text-enabled` | Whether text chat is enabled | `boolean` | `true` | -| `voiceEnabled` | `voice-enabled` | Whether voice chat is enabled | `boolean` | `false` | -| `welcomeMessage` | `welcome-message` | Greeting message displayed to users | `string` | `undefined` | +| Property | Attribute | Description | Type | Default | +| -------------------- | ---------------------- | ------------------------------------------------------- | --------- | ----------- | +| `botName` | `bot-name` | Name of the bot | `string` | `undefined` | +| `botTitle` | `bot-title` | Text placed in the top header | `string` | `undefined` | +| `clearOnComplete` | `clear-on-complete` | Clear messages when conversation finishes | `boolean` | `false` | +| `conversationModeOn` | `conversation-mode-on` | Continue listening to users after they send the message | `boolean` | `false` | +| `textEnabled` | `text-enabled` | Whether text chat is enabled | `boolean` | `true` | +| `voiceEnabled` | `voice-enabled` | Whether voice chat is enabled | `boolean` | `false` | +| `welcomeMessage` | `welcome-message` | Greeting message displayed to users | `string` | `undefined` | + + +## Events + +| Event | Description | Type | +| --------------- | -------------------------------------------- | ------------------------- | +| `chatCompleted` | Event emitted when conversation is completed | `CustomEvent` | ## Dependencies From 7aadabc5b34e76a99cb528b87de12fd3b1416a78 Mon Sep 17 00:00:00 2001 From: wlee221 Date: Thu, 13 Aug 2020 12:09:22 -0700 Subject: [PATCH 17/71] Use interface over type --- .../src/common/audio-control/recorder.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/amplify-ui-components/src/common/audio-control/recorder.ts b/packages/amplify-ui-components/src/common/audio-control/recorder.ts index 737ffa97723..557df76dcfc 100644 --- a/packages/amplify-ui-components/src/common/audio-control/recorder.ts +++ b/packages/amplify-ui-components/src/common/audio-control/recorder.ts @@ -1,9 +1,9 @@ import { exportBuffer } from './helper'; -type SilenceDetectionConfig = { +interface SilenceDetectionConfig { time: number; amplitude: number; -}; +} export class AudioRecorder { private options: SilenceDetectionConfig; From 1a07f47c631c66e410d4b3c9534e253af58d820e Mon Sep 17 00:00:00 2001 From: wlee221 Date: Thu, 13 Aug 2020 12:23:22 -0700 Subject: [PATCH 18/71] Reorder functions and add byte descriptions --- .../src/common/audio-control/helper.ts | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/packages/amplify-ui-components/src/common/audio-control/helper.ts b/packages/amplify-ui-components/src/common/audio-control/helper.ts index 5658086eab1..7fee46f7f19 100644 --- a/packages/amplify-ui-components/src/common/audio-control/helper.ts +++ b/packages/amplify-ui-components/src/common/audio-control/helper.ts @@ -1,18 +1,3 @@ -export const exportBuffer = ( - recBuffer: Float32Array[], - recLength: number, - recordSampleRate: number, - exportSampleRate: number, -) => { - const mergedBuffers = mergeBuffers(recBuffer, recLength); - const downsampledBuffer = downsampleBuffer(mergedBuffers, recordSampleRate, exportSampleRate); - const encodedWav = encodeWAV(downsampledBuffer, exportSampleRate); - const audioBlob = new Blob([encodedWav], { - type: 'application/octet-stream', - }); - return audioBlob; -}; - const mergeBuffers = (bufferArray: Float32Array[], recLength: number) => { const result = new Float32Array(recLength); let offset = 0; @@ -65,22 +50,37 @@ const encodeWAV = (samples: Float32Array, exportSampleRate: number) => { const buffer = new ArrayBuffer(44 + samples.length * 2); const view = new DataView(buffer); writeString(view, 0, 'RIFF'); - view.setUint32(4, 32 + samples.length * 2, true); + view.setUint32(4, 32 + samples.length * 2, true); // chunk size writeString(view, 8, 'WAVE'); // fmt subchunk writeString(view, 12, 'fmt '); - view.setUint32(16, 16, true); - view.setUint16(20, 1, true); - view.setUint16(22, 1, true); - view.setUint32(24, exportSampleRate, true); - view.setUint32(28, exportSampleRate * 2, true); - view.setUint16(32, 2, true); - view.setUint16(34, 16, true); + view.setUint32(16, 16, true); // subchunk size + view.setUint16(20, 1, true); // audio format, pcm = 1 + view.setUint16(22, 1, true); // number of channels + view.setUint32(24, exportSampleRate, true); // sample rate + view.setUint32(28, exportSampleRate * 2, true); // byte rate + view.setUint16(32, 2, true); // block align, # of bytes per sample + view.setUint16(34, 16, true); // bits per sample // data subchunk writeString(view, 36, 'data'); - view.setUint32(40, samples.length * 2, true); - floatTo16BitPCM(view, 44, samples); + view.setUint32(40, samples.length * 2, true); // subchunk size + floatTo16BitPCM(view, 44, samples); // pcm values here return view; }; + +export const exportBuffer = ( + recBuffer: Float32Array[], + recLength: number, + recordSampleRate: number, + exportSampleRate: number, +) => { + const mergedBuffers = mergeBuffers(recBuffer, recLength); + const downsampledBuffer = downsampleBuffer(mergedBuffers, recordSampleRate, exportSampleRate); + const encodedWav = encodeWAV(downsampledBuffer, exportSampleRate); + const audioBlob = new Blob([encodedWav], { + type: 'application/octet-stream', + }); + return audioBlob; +}; From 346a4ccdcf2f73750b765d04025007c445c49331 Mon Sep 17 00:00:00 2001 From: wlee221 Date: Mon, 17 Aug 2020 13:54:32 -0700 Subject: [PATCH 19/71] Add loading animation --- .../src/common/audio-control/recorder.ts | 1 - .../amplify-chatbot/amplify-chatbot.scss | 68 ++++++++++++++++++- .../amplify-chatbot/amplify-chatbot.tsx | 30 ++++---- 3 files changed, 83 insertions(+), 16 deletions(-) diff --git a/packages/amplify-ui-components/src/common/audio-control/recorder.ts b/packages/amplify-ui-components/src/common/audio-control/recorder.ts index 557df76dcfc..660a0ef81c7 100644 --- a/packages/amplify-ui-components/src/common/audio-control/recorder.ts +++ b/packages/amplify-ui-components/src/common/audio-control/recorder.ts @@ -53,7 +53,6 @@ export class AudioRecorder { processorNode.onaudioprocess = audioProcessingEvent => { if (!this.recording) return; const stream = audioProcessingEvent.inputBuffer.getChannelData(0); - console.log(stream); this.streamBuffer.push(new Float32Array(stream)); // set to a copy of the stream this.streamBufferLength += stream.length; this.analyse(); diff --git a/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.scss b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.scss index fd21344cd49..16d2b34865c 100644 --- a/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.scss +++ b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.scss @@ -38,7 +38,7 @@ .bot { margin-left: 1rem; margin-right: auto; - background-color: #dbdbdb; + background-color: rgb(230, 230, 230); color: black; border-radius: 1.5rem 1.5rem 1.5rem 0; } @@ -64,3 +64,69 @@ --icon-fill: var(--amplify-primary-color); --padding: 0.625rem; } +/** + * Loading animation adapted from: https://github.com/nzbin/three-dots. + */ +.dot-flashing { + position: relative; + margin-left: 15px; + margin-right: 15px; + margin-top: 0.3rem; + margin-bottom: 0.3rem; + width: 10px; + height: 10px; + border-radius: 5px; + background-color: rgba(0, 0, 0, 0.65); + // background-color: var(--amplify-primary-color); + // color: var(--amplify-primary-color); + -webkit-animation: dot-flashing 1s infinite linear alternate; + animation: dot-flashing 1s infinite linear alternate; + -webkit-animation-delay: 0.5s; + animation-delay: 0.5s; +} +.dot-flashing::before, +.dot-flashing::after { + content: ''; + position: absolute; + top: 0; + background-color: rgba(0, 0, 0, 0.65); + // background-color: var(--amplify-primary-color); +} +.dot-flashing::before { + left: -15px; + width: 10px; + height: 10px; + border-radius: 5px; + -webkit-animation: dot-flashing 1s infinite alternate; + animation: dot-flashing 1s infinite alternate; + -webkit-animation-delay: 0s; + animation-delay: 0s; +} +.dot-flashing::after { + left: 15px; + width: 10px; + height: 10px; + border-radius: 5px; + -webkit-animation: dot-flashing 1s infinite alternate; + animation: dot-flashing 1s infinite alternate; + -webkit-animation-delay: 1s; + animation-delay: 1s; +} +@-webkit-keyframes dot-flashing { + 0% { + background-color: #9880ff; + } + 50%, + 100% { + background-color: #ebe6ff; + } +} +@keyframes dot-flashing { + 0% { + background-color: rgba(0, 0, 0, 0.65); + } + 50%, + 100% { + background-color: rgba(0, 0, 0, 0.1); + } +} diff --git a/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx index a7f7565693c..f0f2479b421 100644 --- a/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx +++ b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx @@ -52,10 +52,6 @@ export class AmplifyChatbot { /** Event emitted when conversation is completed */ @Event() chatCompleted: EventEmitter; - messageJSX = (messages: Message[]) => { - return messages.map(message =>
{message.content}
); - }; - handleChange(event: Event) { const target = event.target as HTMLInputElement; this.text = target.value; @@ -93,7 +89,6 @@ export class AmplifyChatbot { } async lexResponseHandler() { - this.state = 'speaking'; if (!Interactions || typeof Interactions.send !== 'function') { throw new Error(NO_INTERACTIONS_MODULE_FOUND); } @@ -104,13 +99,10 @@ export class AmplifyChatbot { }, }; const response = await Interactions.send(this.botName, interactionsMessage); - if ((response as any).inputTranscript === '') { - this.appendMessage('⠀', 'user'); - } else { - this.appendMessage((response as any).inputTranscript, 'user'); - } - this.appendMessage((response as any).message, 'bot'); - this.playTranscript((response as any).audioStream); + this.state = 'speaking'; + if (response.inputTranscript) this.appendMessage(response.inputTranscript, 'user'); + this.appendMessage(response.message, 'bot'); + this.playTranscript(response.audioStream); } playTranscript(audioStream: Uint8Array) { @@ -139,7 +131,6 @@ export class AmplifyChatbot { time: 1500, amplitude: 0.2, }); - console.log(this.audioRecorder); } } @@ -152,7 +143,6 @@ export class AmplifyChatbot { throw new Error(NO_INTERACTIONS_MODULE_FOUND); } const onComplete = (err: string, data: object) => { - console.log({ err, data }); this.chatCompleted.emit({ data, err, @@ -196,6 +186,18 @@ export class AmplifyChatbot { return [textInput, micButton, sendButton]; } + private messageJSX = (messages: Message[]) => { + const messageList = messages.map(message =>
{message.content}
); + if (this.state === 'sending') { + messageList.push( +
+
+
, + ); + } + return messageList; + }; + render() { console.log(this.state); return ( From 7b0e4cb40662d25bb89c476a95737c9b01f497a8 Mon Sep 17 00:00:00 2001 From: wlee221 Date: Mon, 17 Aug 2020 13:54:40 -0700 Subject: [PATCH 20/71] Update interaction types --- packages/interactions/src/Interactions.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/interactions/src/Interactions.ts b/packages/interactions/src/Interactions.ts index 01a402118d6..b5c6e269d56 100644 --- a/packages/interactions/src/Interactions.ts +++ b/packages/interactions/src/Interactions.ts @@ -104,11 +104,14 @@ export class InteractionsClass { botname: string, message: InteractionsMessage ): Promise; - public async send(botname: string, message: object): Promise; + public async send( + botname: string, + message: object + ): Promise; public async send( botname: string, message: string | object - ): Promise { + ): Promise { if (!this._options.bots || !this._options.bots[botname]) { throw new Error('Bot ' + botname + ' does not exist'); } From 9e9163f05f1ce6f156bf65da03531e625f15559e Mon Sep 17 00:00:00 2001 From: wlee221 Date: Mon, 17 Aug 2020 15:16:22 -0700 Subject: [PATCH 21/71] Scroll to bottom --- .../src/components/amplify-chatbot/amplify-chatbot.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx index f0f2479b421..507ee48e714 100644 --- a/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx +++ b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx @@ -1,4 +1,4 @@ -import { Component, Host, h, Prop, State, Listen, Event, EventEmitter } from '@stencil/core'; +import { Component, Host, h, Prop, State, Listen, Event, EventEmitter, Element } from '@stencil/core'; import { Interactions } from '@aws-amplify/interactions'; import { JSXBase } from '@stencil/core/internal'; import { AudioRecorder } from '../../common/audio-control/recorder'; @@ -41,6 +41,8 @@ export class AmplifyChatbot { /** Current app state */ @State() state: AppState = 'initial'; + @Element() private element: HTMLElement; + private audioRecorder: AudioRecorder; private audioInput: Blob; @@ -155,6 +157,11 @@ export class AmplifyChatbot { } } + componentDidRender() { + const body = this.element.shadowRoot.querySelector('.body'); + body.scrollTop = body.scrollHeight; + } + footerJSX(): JSXBase.IntrinsicElements[] { const textInput = ( Date: Mon, 17 Aug 2020 15:53:53 -0700 Subject: [PATCH 22/71] set methods private --- .../amplify-chatbot/amplify-chatbot.tsx | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx index 507ee48e714..1920c5b79f2 100644 --- a/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx +++ b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx @@ -41,7 +41,7 @@ export class AmplifyChatbot { /** Current app state */ @State() state: AppState = 'initial'; - @Element() private element: HTMLElement; + @Element() element: HTMLAmplifyChatbotElement; private audioRecorder: AudioRecorder; private audioInput: Blob; @@ -54,12 +54,12 @@ export class AmplifyChatbot { /** Event emitted when conversation is completed */ @Event() chatCompleted: EventEmitter; - handleChange(event: Event) { + private handleChange(event: Event) { const target = event.target as HTMLInputElement; this.text = target.value; } - appendMessage(content: string, from: Agent) { + private appendMessage(content: string, from: Agent) { this.messages = [ ...this.messages, { @@ -69,19 +69,19 @@ export class AmplifyChatbot { ]; } - reset() { + private reset() { this.state = 'initial'; this.text = ''; this.audioRecorder && this.audioRecorder.clear(); } - micButtonHandler() { + private micButtonHandler() { if (this.state !== 'initial') return; this.state = 'listening'; this.audioRecorder.startRecording(() => this.onSilenceHandler()); } - onSilenceHandler() { + private onSilenceHandler() { this.state = 'sending'; this.audioRecorder.stopRecording(); this.audioRecorder.exportWAV(blob => { @@ -90,7 +90,7 @@ export class AmplifyChatbot { }); } - async lexResponseHandler() { + private async lexResponseHandler() { if (!Interactions || typeof Interactions.send !== 'function') { throw new Error(NO_INTERACTIONS_MODULE_FOUND); } @@ -107,13 +107,13 @@ export class AmplifyChatbot { this.playTranscript(response.audioStream); } - playTranscript(audioStream: Uint8Array) { + private playTranscript(audioStream: Uint8Array) { this.audioRecorder.play(audioStream, () => { this.state = 'initial'; }); } - async sendText() { + private async sendText() { if (this.text.length === 0 || this.state !== 'initial') return; const text = this.text; this.text = ''; @@ -151,6 +151,7 @@ export class AmplifyChatbot { }); if (this.clearOnComplete) { this.messages = []; + this.reset(); } }; Interactions.onComplete(this.botName, onComplete); @@ -162,7 +163,7 @@ export class AmplifyChatbot { body.scrollTop = body.scrollHeight; } - footerJSX(): JSXBase.IntrinsicElements[] { + private footerJSX(): JSXBase.IntrinsicElements[] { const textInput = ( Date: Mon, 17 Aug 2020 15:55:54 -0700 Subject: [PATCH 23/71] Rename css class --- .../src/components/amplify-chatbot/amplify-chatbot.scss | 2 +- .../src/components/amplify-chatbot/amplify-chatbot.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.scss b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.scss index 16d2b34865c..6981c240633 100644 --- a/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.scss +++ b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.scss @@ -49,7 +49,7 @@ color: var(--amplify-white); border-radius: 1.5rem 1.5rem 0 1.5rem; } -.chatbot-control { +.footer { display: flex; align-items: center; border-top: 0.062rem solid rgba(0, 0, 0, 0.05); diff --git a/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx index 1920c5b79f2..5ad7a2e533b 100644 --- a/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx +++ b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx @@ -213,7 +213,7 @@ export class AmplifyChatbot {
{this.botTitle}
{this.messageJSX(this.messages)}
-
{this.footerJSX()}
+
); From f59e7eae69e199051f5660033bf01c3de9ecca7b Mon Sep 17 00:00:00 2001 From: wlee221 Date: Mon, 17 Aug 2020 15:56:57 -0700 Subject: [PATCH 24/71] Update snapshot --- .../amplify-chatbot/__snapshots__/amplify-chatbot.spec.tsx.snap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/amplify-ui-components/src/components/amplify-chatbot/__snapshots__/amplify-chatbot.spec.tsx.snap b/packages/amplify-ui-components/src/components/amplify-chatbot/__snapshots__/amplify-chatbot.spec.tsx.snap index 06c5f559ba3..a2e247a447d 100644 --- a/packages/amplify-ui-components/src/components/amplify-chatbot/__snapshots__/amplify-chatbot.spec.tsx.snap +++ b/packages/amplify-ui-components/src/components/amplify-chatbot/__snapshots__/amplify-chatbot.spec.tsx.snap @@ -6,7 +6,7 @@ exports[`amplify-chatbot renders chatbot 1`] = `
-
+ From 52e73ee0e2ab9943ae1e471cc5fd4e3136dd1493 Mon Sep 17 00:00:00 2001 From: wlee221 Date: Tue, 18 Aug 2020 15:10:29 -0700 Subject: [PATCH 25/71] Add error handling and reorder functions --- .../amplify-chatbot/amplify-chatbot.tsx | 130 ++++++++++-------- .../src/components/amplify-chatbot/readme.md | 21 +-- .../src/components/amplify-toast/readme.md | 2 + 3 files changed, 90 insertions(+), 63 deletions(-) diff --git a/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx index 5ad7a2e533b..6ba5300c7da 100644 --- a/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx +++ b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx @@ -11,7 +11,7 @@ interface Message { from: Agent; } -type AppState = 'initial' | 'listening' | 'sending' | 'speaking'; +type AppState = 'initial' | 'listening' | 'sending' | 'speaking' | 'error'; @Component({ tag: 'amplify-chatbot', @@ -28,7 +28,7 @@ export class AmplifyChatbot { /** Greeting message displayed to users */ @Prop() welcomeMessage: string; /** Text placed in the top header */ - @Prop() botTitle: string; + @Prop() botTitle: string = 'ChatBot Lex'; /** Whether voice chat is enabled */ @Prop() voiceEnabled: boolean = false; /** Whether text chat is enabled */ @@ -40,6 +40,8 @@ export class AmplifyChatbot { @State() text: string = ''; /** Current app state */ @State() state: AppState = 'initial'; + /** Toast error message */ + @State() error: string; @Element() element: HTMLAmplifyChatbotElement; @@ -48,18 +50,58 @@ export class AmplifyChatbot { @Listen('formSubmit') submitHandler(_event: CustomEvent) { - this.sendText(); + this.sendTextMessage(); } /** Event emitted when conversation is completed */ @Event() chatCompleted: EventEmitter; + connectedCallback() { + if (this.voiceEnabled) { + this.audioRecorder = new AudioRecorder({ + time: 1500, + amplitude: 0.2, + }); + } + } + + componentWillLoad() { + if (this.messages.length === 0 && this.welcomeMessage && this.welcomeMessage.length > 0) { + this.appendToChat(this.welcomeMessage, 'bot'); + } + if (this.botName) { + if (!Interactions || typeof Interactions.onComplete !== 'function') { + throw new Error(NO_INTERACTIONS_MODULE_FOUND); + } + const onComplete = (err: string, data: object) => { + this.chatCompleted.emit({ + data, + err, + }); + if (this.clearOnComplete) { + this.messages = []; + this.reset(); + } + }; + Interactions.onComplete(this.botName, onComplete); + } else { + this.state = 'error'; + this.error = 'Error: Bot Name must be provided to ChatBot'; + } + } + + componentDidRender() { + // scroll to the bottom of messages if necessary + const body = this.element.shadowRoot.querySelector('.body'); + body.scrollTop = body.scrollHeight; + } + private handleChange(event: Event) { const target = event.target as HTMLInputElement; this.text = target.value; } - private appendMessage(content: string, from: Agent) { + private appendToChat(content: string, from: Agent) { this.messages = [ ...this.messages, { @@ -72,6 +114,7 @@ export class AmplifyChatbot { private reset() { this.state = 'initial'; this.text = ''; + this.error = undefined; this.audioRecorder && this.audioRecorder.clear(); } @@ -86,11 +129,11 @@ export class AmplifyChatbot { this.audioRecorder.stopRecording(); this.audioRecorder.exportWAV(blob => { this.audioInput = blob; - this.lexResponseHandler(); + this.sendVoiceMessage(); }); } - private async lexResponseHandler() { + private async sendVoiceMessage() { if (!Interactions || typeof Interactions.send !== 'function') { throw new Error(NO_INTERACTIONS_MODULE_FOUND); } @@ -102,8 +145,8 @@ export class AmplifyChatbot { }; const response = await Interactions.send(this.botName, interactionsMessage); this.state = 'speaking'; - if (response.inputTranscript) this.appendMessage(response.inputTranscript, 'user'); - this.appendMessage(response.message, 'bot'); + if (response.inputTranscript) this.appendToChat(response.inputTranscript, 'user'); + this.appendToChat(response.message, 'bot'); this.playTranscript(response.audioStream); } @@ -113,56 +156,20 @@ export class AmplifyChatbot { }); } - private async sendText() { - if (this.text.length === 0 || this.state !== 'initial') return; + private async sendTextMessage() { + if (this.text.length === 0 || this.state !== 'initial' || typeof this.error !== undefined) return; const text = this.text; this.text = ''; - this.appendMessage(text, 'user'); + this.appendToChat(text, 'user'); this.state = 'sending'; const response = await Interactions.send(this.botName, text); if (response.message) { - this.appendMessage(response.message, 'bot'); + this.appendToChat(response.message, 'bot'); } this.state = 'initial'; } - connectedCallback() { - if (this.voiceEnabled) { - this.audioRecorder = new AudioRecorder({ - time: 1500, - amplitude: 0.2, - }); - } - } - - componentWillLoad() { - if (this.messages.length === 0 && this.welcomeMessage && this.welcomeMessage.length > 0) { - this.appendMessage(this.welcomeMessage, 'bot'); - } - if (this.botName) { - if (!Interactions || typeof Interactions.onComplete !== 'function') { - throw new Error(NO_INTERACTIONS_MODULE_FOUND); - } - const onComplete = (err: string, data: object) => { - this.chatCompleted.emit({ - data, - err, - }); - if (this.clearOnComplete) { - this.messages = []; - this.reset(); - } - }; - Interactions.onComplete(this.botName, onComplete); - } - } - - componentDidRender() { - const body = this.element.shadowRoot.querySelector('.body'); - body.scrollTop = body.scrollHeight; - } - private footerJSX(): JSXBase.IntrinsicElements[] { const textInput = ( this.handleChange(evt)} value={this.text} + disabled={this.state === 'error'} /> ); const micButton = this.voiceEnabled && ( @@ -179,7 +187,7 @@ export class AmplifyChatbot { class="icon-button" variant="icon" icon="microphone" - disabled={this.state !== 'initial'} + disabled={this.state === 'error' || this.state !== 'initial'} /> ); const sendButton = this.textEnabled && ( @@ -187,8 +195,8 @@ export class AmplifyChatbot { class="icon-button" variant="icon" icon="send" - handleButtonClick={() => this.sendText()} - disabled={this.state !== 'initial'} + handleButtonClick={() => this.sendTextMessage()} + disabled={this.state === 'error' || this.state !== 'initial'} /> ); return [textInput, micButton, sendButton]; @@ -211,9 +219,23 @@ export class AmplifyChatbot { return (
-
{this.botTitle}
-
{this.messageJSX(this.messages)}
- +
+ {this.botTitle} +
+
+ {this.messageJSX(this.messages)} +
+ + {this.error ? ( + { + this.error = undefined; + }} + /> + ) : null}
); diff --git a/packages/amplify-ui-components/src/components/amplify-chatbot/readme.md b/packages/amplify-ui-components/src/components/amplify-chatbot/readme.md index e9003a0c482..181a6f1ea78 100644 --- a/packages/amplify-ui-components/src/components/amplify-chatbot/readme.md +++ b/packages/amplify-ui-components/src/components/amplify-chatbot/readme.md @@ -7,15 +7,15 @@ ## Properties -| Property | Attribute | Description | Type | Default | -| -------------------- | ---------------------- | ------------------------------------------------------- | --------- | ----------- | -| `botName` | `bot-name` | Name of the bot | `string` | `undefined` | -| `botTitle` | `bot-title` | Text placed in the top header | `string` | `undefined` | -| `clearOnComplete` | `clear-on-complete` | Clear messages when conversation finishes | `boolean` | `false` | -| `conversationModeOn` | `conversation-mode-on` | Continue listening to users after they send the message | `boolean` | `false` | -| `textEnabled` | `text-enabled` | Whether text chat is enabled | `boolean` | `true` | -| `voiceEnabled` | `voice-enabled` | Whether voice chat is enabled | `boolean` | `false` | -| `welcomeMessage` | `welcome-message` | Greeting message displayed to users | `string` | `undefined` | +| Property | Attribute | Description | Type | Default | +| -------------------- | ---------------------- | ------------------------------------------------------- | --------- | --------------- | +| `botName` | `bot-name` | Name of the bot | `string` | `undefined` | +| `botTitle` | `bot-title` | Text placed in the top header | `string` | `'ChatBot Lex'` | +| `clearOnComplete` | `clear-on-complete` | Clear messages when conversation finishes | `boolean` | `false` | +| `conversationModeOn` | `conversation-mode-on` | Continue listening to users after they send the message | `boolean` | `false` | +| `textEnabled` | `text-enabled` | Whether text chat is enabled | `boolean` | `true` | +| `voiceEnabled` | `voice-enabled` | Whether voice chat is enabled | `boolean` | `false` | +| `welcomeMessage` | `welcome-message` | Greeting message displayed to users | `string` | `undefined` | ## Events @@ -31,13 +31,16 @@ - [amplify-input](../amplify-input) - [amplify-button](../amplify-button) +- [amplify-toast](../amplify-toast) ### Graph ```mermaid graph TD; amplify-chatbot --> amplify-input amplify-chatbot --> amplify-button + amplify-chatbot --> amplify-toast amplify-button --> amplify-icon + amplify-toast --> amplify-icon style amplify-chatbot fill:#f9f,stroke:#333,stroke-width:4px ``` diff --git a/packages/amplify-ui-components/src/components/amplify-toast/readme.md b/packages/amplify-ui-components/src/components/amplify-toast/readme.md index 06f011d9227..eb7b50adffb 100644 --- a/packages/amplify-ui-components/src/components/amplify-toast/readme.md +++ b/packages/amplify-ui-components/src/components/amplify-toast/readme.md @@ -16,6 +16,7 @@ ### Used by - [amplify-authenticator](../amplify-authenticator) + - [amplify-chatbot](../amplify-chatbot) ### Depends on @@ -26,6 +27,7 @@ graph TD; amplify-toast --> amplify-icon amplify-authenticator --> amplify-toast + amplify-chatbot --> amplify-toast style amplify-toast fill:#f9f,stroke:#333,stroke-width:4px ``` From ce066a1d7be857cbf56493f75f346cc1f10cf106 Mon Sep 17 00:00:00 2001 From: wlee221 Date: Tue, 18 Aug 2020 15:34:53 -0700 Subject: [PATCH 26/71] Refactor error handling --- .../amplify-chatbot/amplify-chatbot.tsx | 77 +++++++++++-------- 1 file changed, 44 insertions(+), 33 deletions(-) diff --git a/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx index 6ba5300c7da..c16e7d41093 100644 --- a/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx +++ b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx @@ -56,38 +56,41 @@ export class AmplifyChatbot { /** Event emitted when conversation is completed */ @Event() chatCompleted: EventEmitter; - connectedCallback() { + componentWillLoad() { + // Check props and Interactions + if (!this.voiceEnabled && !this.textEnabled) { + this.setError('Error: you must enable voice or text for the chatbot'); + } else if (!this.botName) { + this.setError('Error: Bot Name must be provided to ChatBot'); + } else if (!Interactions || typeof Interactions.onComplete !== 'function') { + this.setError(NO_INTERACTIONS_MODULE_FOUND); + } + + // Initialize AudioRecorder if voice is enabled if (this.voiceEnabled) { this.audioRecorder = new AudioRecorder({ time: 1500, amplitude: 0.2, }); } - } - componentWillLoad() { + // Set welcome message if (this.messages.length === 0 && this.welcomeMessage && this.welcomeMessage.length > 0) { this.appendToChat(this.welcomeMessage, 'bot'); } - if (this.botName) { - if (!Interactions || typeof Interactions.onComplete !== 'function') { - throw new Error(NO_INTERACTIONS_MODULE_FOUND); + + // Callback function to be called after chat is completed + const onComplete = (err: string, data: object) => { + this.chatCompleted.emit({ + data, + err, + }); + if (this.clearOnComplete) { + this.reset(); } - const onComplete = (err: string, data: object) => { - this.chatCompleted.emit({ - data, - err, - }); - if (this.clearOnComplete) { - this.messages = []; - this.reset(); - } - }; + Interactions.onComplete(this.botName, onComplete); - } else { - this.state = 'error'; - this.error = 'Error: Bot Name must be provided to ChatBot'; - } + }; } componentDidRender() { @@ -115,6 +118,7 @@ export class AmplifyChatbot { this.state = 'initial'; this.text = ''; this.error = undefined; + this.messages = this.welcomeMessage ? this.messages.slice(0, 1) : []; this.audioRecorder && this.audioRecorder.clear(); } @@ -147,11 +151,7 @@ export class AmplifyChatbot { this.state = 'speaking'; if (response.inputTranscript) this.appendToChat(response.inputTranscript, 'user'); this.appendToChat(response.message, 'bot'); - this.playTranscript(response.audioStream); - } - - private playTranscript(audioStream: Uint8Array) { - this.audioRecorder.play(audioStream, () => { + this.audioRecorder.play(response.audioStream, () => { this.state = 'initial'; }); } @@ -170,6 +170,11 @@ export class AmplifyChatbot { this.state = 'initial'; } + private setError(message: string) { + this.state = 'error'; + this.error = message; + } + private footerJSX(): JSXBase.IntrinsicElements[] { const textInput = ( { + this.error = undefined; + }} + /> + ) + ); + } + render() { console.log(this.state); return ( @@ -228,14 +246,7 @@ export class AmplifyChatbot { - {this.error ? ( - { - this.error = undefined; - }} - /> - ) : null} + {this.errorToast()}
); From 5a9a0f8e739961eeb27fe70f39401d913cbc5670 Mon Sep 17 00:00:00 2001 From: wlee221 Date: Tue, 18 Aug 2020 19:24:13 -0700 Subject: [PATCH 27/71] Refactor chatbot functions --- .../amplify-chatbot.spec.tsx.snap | 13 +- .../amplify-chatbot/amplify-chatbot.tsx | 169 ++++++++++-------- 2 files changed, 102 insertions(+), 80 deletions(-) diff --git a/packages/amplify-ui-components/src/components/amplify-chatbot/__snapshots__/amplify-chatbot.spec.tsx.snap b/packages/amplify-ui-components/src/components/amplify-chatbot/__snapshots__/amplify-chatbot.spec.tsx.snap index a2e247a447d..2de385fa782 100644 --- a/packages/amplify-ui-components/src/components/amplify-chatbot/__snapshots__/amplify-chatbot.spec.tsx.snap +++ b/packages/amplify-ui-components/src/components/amplify-chatbot/__snapshots__/amplify-chatbot.spec.tsx.snap @@ -4,12 +4,15 @@ exports[`amplify-chatbot renders chatbot 1`] = `
-
-
- diff --git a/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx index c16e7d41093..b443215c8be 100644 --- a/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx +++ b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx @@ -46,8 +46,8 @@ export class AmplifyChatbot { @Element() element: HTMLAmplifyChatbotElement; private audioRecorder: AudioRecorder; - private audioInput: Blob; + // Occurs when user presses enter in input box @Listen('formSubmit') submitHandler(_event: CustomEvent) { this.sendTextMessage(); @@ -56,15 +56,30 @@ export class AmplifyChatbot { /** Event emitted when conversation is completed */ @Event() chatCompleted: EventEmitter; + /** + * Lifecycle functions + */ + componentWillLoad() { - // Check props and Interactions + if (!Interactions || typeof Interactions.onComplete !== 'function') { + throw new Error(NO_INTERACTIONS_MODULE_FOUND); + } + this.updateProps(); + } + + componentDidRender() { + // scroll to the bottom of messages if necessary + const body = this.element.shadowRoot.querySelector('.body'); + body.scrollTop = body.scrollHeight; + } + + private updateProps() { if (!this.voiceEnabled && !this.textEnabled) { this.setError('Error: you must enable voice or text for the chatbot'); } else if (!this.botName) { this.setError('Error: Bot Name must be provided to ChatBot'); - } else if (!Interactions || typeof Interactions.onComplete !== 'function') { - this.setError(NO_INTERACTIONS_MODULE_FOUND); } + this.reset(); // Initialize AudioRecorder if voice is enabled if (this.voiceEnabled) { @@ -74,11 +89,6 @@ export class AmplifyChatbot { }); } - // Set welcome message - if (this.messages.length === 0 && this.welcomeMessage && this.welcomeMessage.length > 0) { - this.appendToChat(this.welcomeMessage, 'bot'); - } - // Callback function to be called after chat is completed const onComplete = (err: string, data: object) => { this.chatCompleted.emit({ @@ -88,61 +98,58 @@ export class AmplifyChatbot { if (this.clearOnComplete) { this.reset(); } - - Interactions.onComplete(this.botName, onComplete); }; + try { + Interactions.onComplete(this.botName, onComplete); + } catch (err) { + this.setError(err); + } } - componentDidRender() { - // scroll to the bottom of messages if necessary - const body = this.element.shadowRoot.querySelector('.body'); - body.scrollTop = body.scrollHeight; - } - - private handleChange(event: Event) { - const target = event.target as HTMLInputElement; - this.text = target.value; - } - - private appendToChat(content: string, from: Agent) { - this.messages = [ - ...this.messages, - { - content, - from, - }, - ]; - } - - private reset() { - this.state = 'initial'; - this.text = ''; - this.error = undefined; - this.messages = this.welcomeMessage ? this.messages.slice(0, 1) : []; - this.audioRecorder && this.audioRecorder.clear(); - } + /** + * Handlers + */ - private micButtonHandler() { + private handleMicButton() { if (this.state !== 'initial') return; this.state = 'listening'; - this.audioRecorder.startRecording(() => this.onSilenceHandler()); + this.audioRecorder.startRecording(() => this.handleSilence()); } - private onSilenceHandler() { + private handleSilence() { this.state = 'sending'; this.audioRecorder.stopRecording(); this.audioRecorder.exportWAV(blob => { - this.audioInput = blob; - this.sendVoiceMessage(); + this.sendVoiceMessage(blob); }); } - private async sendVoiceMessage() { - if (!Interactions || typeof Interactions.send !== 'function') { - throw new Error(NO_INTERACTIONS_MODULE_FOUND); + private handleTextChange(event: Event) { + const target = event.target as HTMLInputElement; + this.text = target.value; + } + + /** + * Interactions helpers + */ + + private async sendTextMessage() { + if (this.text.length === 0 || this.state !== 'initial') return; + const text = this.text; + this.text = ''; + this.appendToChat(text, 'user'); + this.state = 'sending'; + + const response = await Interactions.send(this.botName, text); + if (response.message) { + this.appendToChat(response.message, 'bot'); } + this.state = 'initial'; + } + + private async sendVoiceMessage(audioInput: Blob) { const interactionsMessage = { - content: this.audioInput, + content: audioInput, options: { messageType: 'voice', }, @@ -156,39 +163,64 @@ export class AmplifyChatbot { }); } - private async sendTextMessage() { - if (this.text.length === 0 || this.state !== 'initial' || typeof this.error !== undefined) return; - const text = this.text; - this.text = ''; - this.appendToChat(text, 'user'); - this.state = 'sending'; - - const response = await Interactions.send(this.botName, text); - if (response.message) { - this.appendToChat(response.message, 'bot'); - } - this.state = 'initial'; + private appendToChat(content: string, from: Agent) { + this.messages = [ + ...this.messages, + { + content, + from, + }, + ]; } + /** + * State control functions + */ + private setError(message: string) { this.state = 'error'; this.error = message; } + private reset() { + this.state = 'initial'; + this.text = ''; + this.error = undefined; + this.messages = []; + if (this.welcomeMessage) this.appendToChat(this.welcomeMessage, 'bot'); + this.audioRecorder && this.audioRecorder.clear(); + } + + /** + * Rendering related methods + */ + + private messageJSX = (messages: Message[]) => { + const messageList = messages.map(message =>
{message.content}
); + if (this.state === 'sending') { + messageList.push( +
+
+
, + ); + } + return messageList; + }; + private footerJSX(): JSXBase.IntrinsicElements[] { const textInput = ( this.handleChange(evt)} + handleInputChange={evt => this.handleTextChange(evt)} value={this.text} disabled={this.state === 'error'} /> ); const micButton = this.voiceEnabled && ( this.micButtonHandler()} + handleButtonClick={() => this.handleMicButton()} class="icon-button" variant="icon" icon="microphone" @@ -207,18 +239,6 @@ export class AmplifyChatbot { return [textInput, micButton, sendButton]; } - private messageJSX = (messages: Message[]) => { - const messageList = messages.map(message =>
{message.content}
); - if (this.state === 'sending') { - messageList.push( -
-
-
, - ); - } - return messageList; - }; - private errorToast() { return ( this.error && ( @@ -233,7 +253,6 @@ export class AmplifyChatbot { } render() { - console.log(this.state); return (
From 399dc3b92a98f5e90ce4185bccbc5263e5e3861f Mon Sep 17 00:00:00 2001 From: wlee221 Date: Tue, 18 Aug 2020 23:41:51 -0700 Subject: [PATCH 28/71] Cleanup --- .../amplify-chatbot/amplify-chatbot.scss | 6 +++--- .../components/amplify-chatbot/amplify-chatbot.tsx | 14 ++++++-------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.scss b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.scss index 6981c240633..98e1b69d298 100644 --- a/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.scss +++ b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.scss @@ -31,7 +31,7 @@ .bubble { max-width: 80%; padding: 0.8em 1.4em; - clear: both; + text-align: left; word-wrap: break-word; margin-bottom: 0.625rem; } @@ -114,11 +114,11 @@ } @-webkit-keyframes dot-flashing { 0% { - background-color: #9880ff; + background-color: rgba(0, 0, 0, 0.65); } 50%, 100% { - background-color: #ebe6ff; + background-color: rgba(0, 0, 0, 0.1); } } @keyframes dot-flashing { diff --git a/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx index b443215c8be..7ec2e21ff11 100644 --- a/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx +++ b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx @@ -59,7 +59,6 @@ export class AmplifyChatbot { /** * Lifecycle functions */ - componentWillLoad() { if (!Interactions || typeof Interactions.onComplete !== 'function') { throw new Error(NO_INTERACTIONS_MODULE_FOUND); @@ -75,12 +74,12 @@ export class AmplifyChatbot { private updateProps() { if (!this.voiceEnabled && !this.textEnabled) { - this.setError('Error: you must enable voice or text for the chatbot'); + this.setError('Error: Either voice or text must be enabled for the chatbot'); } else if (!this.botName) { this.setError('Error: Bot Name must be provided to ChatBot'); } - this.reset(); + if (this.welcomeMessage) this.appendToChat(this.welcomeMessage, 'bot'); // Initialize AudioRecorder if voice is enabled if (this.voiceEnabled) { this.audioRecorder = new AudioRecorder({ @@ -99,6 +98,7 @@ export class AmplifyChatbot { this.reset(); } }; + try { Interactions.onComplete(this.botName, onComplete); } catch (err) { @@ -109,7 +109,6 @@ export class AmplifyChatbot { /** * Handlers */ - private handleMicButton() { if (this.state !== 'initial') return; this.state = 'listening'; @@ -119,7 +118,7 @@ export class AmplifyChatbot { private handleSilence() { this.state = 'sending'; this.audioRecorder.stopRecording(); - this.audioRecorder.exportWAV(blob => { + this.audioRecorder.exportWAV((blob: Blob) => { this.sendVoiceMessage(blob); }); } @@ -132,7 +131,6 @@ export class AmplifyChatbot { /** * Interactions helpers */ - private async sendTextMessage() { if (this.text.length === 0 || this.state !== 'initial') return; const text = this.text; @@ -176,7 +174,6 @@ export class AmplifyChatbot { /** * State control functions */ - private setError(message: string) { this.state = 'error'; this.error = message; @@ -194,7 +191,6 @@ export class AmplifyChatbot { /** * Rendering related methods */ - private messageJSX = (messages: Message[]) => { const messageList = messages.map(message =>
{message.content}
); if (this.state === 'sending') { @@ -220,6 +216,7 @@ export class AmplifyChatbot { ); const micButton = this.voiceEnabled && ( this.handleMicButton()} class="icon-button" variant="icon" @@ -229,6 +226,7 @@ export class AmplifyChatbot { ); const sendButton = this.textEnabled && ( Date: Tue, 18 Aug 2020 23:43:37 -0700 Subject: [PATCH 29/71] Update snapshot --- .../amplify-chatbot/__snapshots__/amplify-chatbot.spec.tsx.snap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/amplify-ui-components/src/components/amplify-chatbot/__snapshots__/amplify-chatbot.spec.tsx.snap b/packages/amplify-ui-components/src/components/amplify-chatbot/__snapshots__/amplify-chatbot.spec.tsx.snap index 2de385fa782..336f9d6e9a7 100644 --- a/packages/amplify-ui-components/src/components/amplify-chatbot/__snapshots__/amplify-chatbot.spec.tsx.snap +++ b/packages/amplify-ui-components/src/components/amplify-chatbot/__snapshots__/amplify-chatbot.spec.tsx.snap @@ -10,7 +10,7 @@ exports[`amplify-chatbot renders chatbot 1`] = `
From 41aca35bb50af90800aece13b59814306e317409 Mon Sep 17 00:00:00 2001 From: wlee221 Date: Wed, 19 Aug 2020 00:24:44 -0700 Subject: [PATCH 30/71] Expose width css variable from amplify-button --- .../src/components/amplify-button/amplify-button.scss | 3 ++- .../src/components/amplify-chatbot/amplify-chatbot.scss | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/amplify-ui-components/src/components/amplify-button/amplify-button.scss b/packages/amplify-ui-components/src/components/amplify-button/amplify-button.scss index 46b5593b4ce..5df4cd71ddc 100644 --- a/packages/amplify-ui-components/src/components/amplify-button/amplify-button.scss +++ b/packages/amplify-ui-components/src/components/amplify-button/amplify-button.scss @@ -13,8 +13,9 @@ --icon-fill: var(--amplify-white); --icon-height: 1.25rem; --padding: 1rem; + --width: 100%; - width: 100%; + width: var(--width); text-align: center; @include md { diff --git a/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.scss b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.scss index 98e1b69d298..25b85eeed1b 100644 --- a/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.scss +++ b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.scss @@ -63,6 +63,7 @@ --icon-height: 1.25rem; --icon-fill: var(--amplify-primary-color); --padding: 0.625rem; + --width: inherit; } /** * Loading animation adapted from: https://github.com/nzbin/three-dots. From 8eb2a4446a234e56f138f9cf17afa1010a0cde08 Mon Sep 17 00:00:00 2001 From: wlee221 Date: Wed, 19 Aug 2020 00:27:35 -0700 Subject: [PATCH 31/71] px to rem --- .../amplify-chatbot/amplify-chatbot.scss | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.scss b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.scss index 25b85eeed1b..0434519fd0d 100644 --- a/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.scss +++ b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.scss @@ -70,13 +70,13 @@ */ .dot-flashing { position: relative; - margin-left: 15px; - margin-right: 15px; + margin-left: 1rem; + margin-right: 1rem; margin-top: 0.3rem; margin-bottom: 0.3rem; - width: 10px; - height: 10px; - border-radius: 5px; + width: 0.625rem; + height: 0.625rem; + border-radius: 10rem; background-color: rgba(0, 0, 0, 0.65); // background-color: var(--amplify-primary-color); // color: var(--amplify-primary-color); @@ -94,20 +94,20 @@ // background-color: var(--amplify-primary-color); } .dot-flashing::before { - left: -15px; - width: 10px; - height: 10px; - border-radius: 5px; + left: -1rem; + width: 0.625rem; + height: 0.625rem; + border-radius: 10rem; -webkit-animation: dot-flashing 1s infinite alternate; animation: dot-flashing 1s infinite alternate; -webkit-animation-delay: 0s; animation-delay: 0s; } .dot-flashing::after { - left: 15px; - width: 10px; - height: 10px; - border-radius: 5px; + left: 1rem; + width: 0.625rem; + height: 0.625rem; + border-radius: 10rem; -webkit-animation: dot-flashing 1s infinite alternate; animation: dot-flashing 1s infinite alternate; -webkit-animation-delay: 1s; From 9271bb1704ded58bc89211333b325d39aa85e6c7 Mon Sep 17 00:00:00 2001 From: wlee221 Date: Wed, 19 Aug 2020 00:55:31 -0700 Subject: [PATCH 32/71] Expose width and height variable; Control height at top level --- .../components/amplify-chatbot/amplify-chatbot.scss | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.scss b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.scss index 0434519fd0d..6a76d675e31 100644 --- a/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.scss +++ b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.scss @@ -1,16 +1,23 @@ :host { --header-color: var(--amplify-secondary-color); --header-size: var(--amplify-text-lg); + --width: 28.75rem; + --height: 37.5rem; } .amplify-chatbot { - display: inline-block; + display: inline-flex; + flex-direction: column; background-color: var(--background-color); border-radius: 0.375rem; box-shadow: 0.0625rem 0rem 0.25rem 0 rgba(0, 0, 0, 0.15); box-sizing: border-box; font-family: var(--amplify-font-family); - width: 28.75rem; margin-bottom: 1rem; + width: 95%; + height: var(--height); + @include md { + width: var(--width); + } } .header { margin-top: 1.25rem; @@ -24,8 +31,8 @@ .body { padding-top: 2rem; display: flex; + flex-grow: 1; flex-direction: column; - height: 31.25rem; overflow: auto; } .bubble { From 9389a64c105cf271d663b035eec446df0e2fad6e Mon Sep 17 00:00:00 2001 From: wlee221 Date: Wed, 19 Aug 2020 01:04:07 -0700 Subject: [PATCH 33/71] Add header slot --- .../src/components/amplify-chatbot/amplify-chatbot.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx index 7ec2e21ff11..c7133dd6952 100644 --- a/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx +++ b/packages/amplify-ui-components/src/components/amplify-chatbot/amplify-chatbot.tsx @@ -254,9 +254,11 @@ export class AmplifyChatbot { return (
-
- {this.botTitle} -
+ +
+ {this.botTitle} +
+
{this.messageJSX(this.messages)}
From 2dfb93e0bbd78acfe43643996076011648857355 Mon Sep 17 00:00:00 2001 From: wlee221 Date: Wed, 19 Aug 2020 17:01:31 -0700 Subject: [PATCH 34/71] Add listening animation --- .../src/common/audio-control/recorder.ts | 10 ++- .../src/components/amplify-button/readme.md | 14 +-- .../amplify-chatbot.spec.tsx.snap | 8 +- .../amplify-chatbot/amplify-chatbot.scss | 12 ++- .../amplify-chatbot/amplify-chatbot.tsx | 88 +++++++++++++++++-- .../src/components/amplify-chatbot/readme.md | 4 +- .../amplify-icon-button.spec.ts.snap | 14 +++ .../components/amplify-icon-button/readme.md | 10 +-- .../__snapshots__/amplify-icon.spec.ts.snap | 8 ++ .../src/components/amplify-icon/icons.tsx | 8 ++ .../src/components/amplify-icon/readme.md | 6 +- 11 files changed, 150 insertions(+), 32 deletions(-) diff --git a/packages/amplify-ui-components/src/common/audio-control/recorder.ts b/packages/amplify-ui-components/src/common/audio-control/recorder.ts index 660a0ef81c7..601985250a1 100644 --- a/packages/amplify-ui-components/src/common/audio-control/recorder.ts +++ b/packages/amplify-ui-components/src/common/audio-control/recorder.ts @@ -5,6 +5,8 @@ interface SilenceDetectionConfig { amplitude: number; } +type Visualizer = (dataArray: Uint8Array, bufferLength: number) => void; + export class AudioRecorder { private options: SilenceDetectionConfig; private audioContext: AudioContext; @@ -12,6 +14,7 @@ export class AudioRecorder { private analyserNode: AnalyserNode; private onSilence: Function; + private visualizer: Visualizer; // input mic stream is stored in a buffer private streamBuffer: Float32Array[] = []; @@ -70,9 +73,10 @@ export class AudioRecorder { this.analyserNode = analyserNode; } - public startRecording(onSilence?: Function) { + public startRecording(onSilence?: Function, visualizer?: Visualizer) { const silenceHandler = onSilence || function() {}; this.onSilence = silenceHandler; + this.visualizer = visualizer; if (this.recording || !this.audioSupported) return; const context = this.audioContext; @@ -125,6 +129,10 @@ export class AudioRecorder { analyser.getByteTimeDomainData(dataArray); + if (this.visualizer) { + this.visualizer(dataArray, bufferLength); + } + for (let i = 0; i < bufferLength; i++) { // Normalize between -1 and 1. const curr_value_time = dataArray[i] / 128 - 1.0; diff --git a/packages/amplify-ui-components/src/components/amplify-button/readme.md b/packages/amplify-ui-components/src/components/amplify-button/readme.md index a09e725d372..966c26956bd 100644 --- a/packages/amplify-ui-components/src/components/amplify-button/readme.md +++ b/packages/amplify-ui-components/src/components/amplify-button/readme.md @@ -5,13 +5,13 @@ ## Properties -| Property | Attribute | Description | Type | Default | -| ------------------- | ---------- | ----------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | -| `disabled` | `disabled` | Disabled state of the button | `boolean` | `false` | -| `handleButtonClick` | -- | (Optional) Callback called when a user clicks on the button | `(evt: Event) => void` | `undefined` | -| `icon` | `icon` | Name of icon to be placed inside the button | `"amazon" \| "auth0" \| "enter-vr" \| "exit-vr" \| "facebook" \| "google" \| "loading" \| "maximize" \| "microphone" \| "minimize" \| "photoPlaceholder" \| "send" \| "sound" \| "sound-mute" \| "warning"` | `undefined` | -| `type` | `type` | Type of the button: 'button', 'submit' or 'reset' | `"button" \| "reset" \| "submit"` | `'button'` | -| `variant` | `variant` | Variant of a button: 'button' \| 'anchor' | `"anchor" \| "button" \| "icon"` | `'button'` | +| Property | Attribute | Description | Type | Default | +| ------------------- | ---------- | ----------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | +| `disabled` | `disabled` | Disabled state of the button | `boolean` | `false` | +| `handleButtonClick` | -- | (Optional) Callback called when a user clicks on the button | `(evt: Event) => void` | `undefined` | +| `icon` | `icon` | Name of icon to be placed inside the button | `"amazon" \| "auth0" \| "ban" \| "enter-vr" \| "exit-vr" \| "facebook" \| "google" \| "loading" \| "maximize" \| "microphone" \| "minimize" \| "photoPlaceholder" \| "send" \| "sound" \| "sound-mute" \| "warning"` | `undefined` | +| `type` | `type` | Type of the button: 'button', 'submit' or 'reset' | `"button" \| "reset" \| "submit"` | `'button'` | +| `variant` | `variant` | Variant of a button: 'button' \| 'anchor' | `"anchor" \| "button" \| "icon"` | `'button'` | ## Dependencies diff --git a/packages/amplify-ui-components/src/components/amplify-chatbot/__snapshots__/amplify-chatbot.spec.tsx.snap b/packages/amplify-ui-components/src/components/amplify-chatbot/__snapshots__/amplify-chatbot.spec.tsx.snap index 336f9d6e9a7..376ee73beaa 100644 --- a/packages/amplify-ui-components/src/components/amplify-chatbot/__snapshots__/amplify-chatbot.spec.tsx.snap +++ b/packages/amplify-ui-components/src/components/amplify-chatbot/__snapshots__/amplify-chatbot.spec.tsx.snap @@ -4,9 +4,11 @@ exports[`amplify-chatbot renders chatbot 1`] = `
-
- ChatBot Lex -
+ +
+ ChatBot Lex +
+