Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
54b5ebb
feat: Init Tela drawer
georgylobko Jul 14, 2025
5a98ffb
feat: Introduce ai drawer component on the left
georgylobko Jul 16, 2025
bcc0fc4
chore: Amazon q icon
georgylobko Jul 16, 2025
1daeea9
chore: Focus control for the ai drawer
georgylobko Jul 16, 2025
4629c8e
fix: Focus control
georgylobko Jul 16, 2025
8bf24d4
chore: Resizing for the ai drawer
georgylobko Jul 16, 2025
0dac664
chore: Adjust closing button for the ai drawer
georgylobko Jul 16, 2025
18d8e70
chore: AI drawer's header and styles
georgylobko Jul 17, 2025
4c02e31
chore: Adjust app layout horizontal layout calculation to include the…
georgylobko Jul 17, 2025
ac24da4
chore: Update snapshot tests
georgylobko Jul 17, 2025
c4c209a
feat: AI drawer mobile styles
georgylobko Jul 17, 2025
6a1d56a
fix: Split panel inline size
georgylobko Jul 17, 2025
ffca2c7
chore: Small refactoring
georgylobko Jul 17, 2025
17aad31
Merge branch 'main' into feat/tela-drawer
georgylobko Jul 17, 2025
77cd03d
chore: Stick ai drawer to the left position
georgylobko Jul 18, 2025
2e0b236
Merge branch 'main' into feat/tela-drawer
georgylobko Jul 18, 2025
c866866
feat: Optional white header for ai drawer
georgylobko Jul 18, 2025
b4c6dc6
feat: Focus mode for ai drawer
georgylobko Jul 18, 2025
5769d23
chore: Chat demo content
georgylobko Jul 18, 2025
b173a07
chore: Small refactoring
georgylobko Jul 18, 2025
67edaf4
chore: Adjust ai drawer mobile styles
georgylobko Jul 21, 2025
762994a
Merge branch 'main' into feat/tela-drawer
georgylobko Jul 21, 2025
f765869
feat: AI drawer demo with an AI chat content
georgylobko Jul 21, 2025
c4f11b5
Merge branch 'main' into feat/tela-drawer
georgylobko Jul 22, 2025
6c90c46
feat: Divider for the ai drawer
georgylobko Jul 22, 2025
311e3b8
chore: Adjust drawer's gap styles
georgylobko Jul 22, 2025
26e5645
Merge branch 'feat/tela-drawer' into feat/tela-drawer-demo
georgylobko Jul 22, 2025
09d2ecc
chore: Ai drawer logo
georgylobko Jul 22, 2025
a191f7e
chore: Remove amazon q from sidecar-demo.page.tsx
georgylobko Jul 22, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@
"@cloudscape-design/component-toolkit": "^1.0.0-beta",
"@cloudscape-design/test-utils-core": "^1.0.0",
"@cloudscape-design/theming-runtime": "^1.0.0",
"@cloudscape-design/chat-components": "^1.0.0",
"@cloudscape-design/code-view": "^3.0.0",
"@cloudscape-design/design-tokens": "^3.0.0",
"@dnd-kit/core": "^6.0.8",
"@dnd-kit/sortable": "^7.0.2",
"@dnd-kit/utilities": "^3.2.1",
Expand Down Expand Up @@ -167,7 +170,7 @@
{
"path": "lib/components/internal/widget-exports.js",
"brotli": false,
"limit": "800 kB",
"limit": "801 kB",
"ignore": "react-dom"
}
],
Expand Down
55 changes: 55 additions & 0 deletions pages/app-layout/chat/additional-info/dialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import React from 'react';

import Button from '~components/button';

import '../dialog.scss';

const Dialog = React.forwardRef(
(
{
children,
footer,
onDismiss,
ariaLabel,
}: {
children: React.ReactNode;
footer: React.ReactNode;
onDismiss: () => void;
ariaLabel: string;
},
ref: React.Ref<HTMLDivElement>
) => {
return (
<div
ref={ref}
className="dialog"
role="dialog"
aria-label={ariaLabel}
aria-modal="false" // Maintains natural focus flow since it's an inline dialog
tabIndex={-1} // This allows the dialog to receive focus
>
<div className="content">
<div className="dismiss">
<Button
iconName="close"
variant="icon"
onClick={onDismiss}
ariaLabel="Close dialog"
data-testid="dialog-dismiss-button"
/>
</div>

<div className="inner-content">{children}</div>
</div>

<div className="footer">{footer}</div>
</div>
);
}
);

Dialog.displayName = 'Dialog';

export default Dialog;
125 changes: 125 additions & 0 deletions pages/app-layout/chat/additional-info/feedback-dialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import React, { useEffect, useRef } from 'react';

import Box from '~components/box';
import Button from '~components/button';
import Checkbox from '~components/checkbox';
import Form from '~components/form';
import FormField from '~components/form-field';
import SpaceBetween from '~components/space-between';
import Textarea from '~components/textarea';

import Dialog from './dialog';

export default function FeedbackDialog({ onDismiss, onSubmit }: { onDismiss: () => void; onSubmit: () => void }) {
const [feedbackOptions, setFeedbackOptions] = React.useState({
harmful: false,
incomplete: false,
inaccurate: false,
other: false,
});
const [feedbackText, setFeedbackText] = React.useState('');
const dialogRef = useRef<HTMLDivElement>(null);
const triggerRef = useRef<HTMLElement | null>(null);

useEffect(() => {
// Store the element that had focus before dialog opened
triggerRef.current = document.activeElement as HTMLElement;

// Focus the dialog container when it opens
dialogRef.current?.focus();

// Cleanup: Return focus to the trigger element when dialog closes
return () => {
triggerRef.current?.focus();
};
}, [dialogRef]);

const selectOption = (option: string, checked: boolean) =>
setFeedbackOptions({ ...feedbackOptions, [option]: checked });

const isFeedbackOptionSelected = Object.values(feedbackOptions).some(val => !!val);
const isSubmittable = isFeedbackOptionSelected || feedbackText.length > 0;

const submitFeedback = () => {
if (!isSubmittable) {
return;
}

onSubmit();
};

return (
<Dialog
ariaLabel="Feedback dialog"
ref={dialogRef}
onDismiss={onDismiss}
footer={
<div style={{ display: 'flex', flexDirection: 'row-reverse', gap: '4px' }}>
<Button
data-testid="feedback-submit-button"
onClick={submitFeedback}
ariaLabel="Submit form"
disabled={!isSubmittable}
>
Submit
</Button>

<Button variant="link" onClick={onDismiss} ariaLabel="Close dialog" data-testid="feedback-close-button">
Close
</Button>
</div>
}
>
<Form
header={
<Box variant="h4">
Tell us more - <i>optional</i>
</Box>
}
>
<SpaceBetween direction="vertical" size="l">
<FormField label="What did you dislike about the response?">
<SpaceBetween size="l" direction="horizontal">
<Checkbox
data-testid="feedback-checkbox-harmful"
checked={feedbackOptions.harmful}
onChange={({ detail }) => selectOption('harmful', detail.checked)}
>
Harmful
</Checkbox>
<Checkbox
checked={feedbackOptions.incomplete}
onChange={({ detail }) => selectOption('incomplete', detail.checked)}
>
Incomplete
</Checkbox>
<Checkbox
checked={feedbackOptions.inaccurate}
onChange={({ detail }) => selectOption('inaccurate', detail.checked)}
>
Inaccurate
</Checkbox>
<Checkbox
checked={feedbackOptions.other}
onChange={({ detail }) => selectOption('other', detail.checked)}
>
Other
</Checkbox>
</SpaceBetween>
</FormField>

<FormField label="Additional notes" stretch={true}>
<Textarea
rows={3}
onChange={({ detail }) => setFeedbackText(detail.value)}
value={feedbackText}
placeholder={'Additional feedback'}
/>
</FormField>
</SpaceBetween>
</Form>
</Dialog>
);
}
58 changes: 58 additions & 0 deletions pages/app-layout/chat/chat.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: MIT-0

@use '~design-tokens' as cs;

.chat-container {
display: flex;
flex-direction: column;
gap: cs.$space-static-l;

min-block-size: 100%;

&:not(.classic) {
@media only screen and (min-width: 688px) and (max-width: 1280px) {
padding-inline: 60px;
}
}

&.classic {
inline-size: 100%;
block-size: 1000px;
}
}

.chat-internal-container {
border-radius: 0 !important;
border: none !important;
margin-inline-end: 16px;
}

.messages {
display: flex;
flex-direction: column;
gap: cs.$space-scaled-xs;

padding-block-start: cs.$space-scaled-xxs;
padding-block-end: cs.$space-container-horizontal;
padding-inline: cs.$space-container-horizontal;
}

.access-denied-alert-wrapper {
display: flex;
gap: cs.$space-scaled-s;

&__box {
flex: 1;
overflow-x: auto;
padding-inline: cs.$space-scaled-m;
padding-block: cs.$space-scaled-s;
border: 2px solid cs.$color-border-input-default;
border-radius: cs.$border-radius-input;
background: cs.$color-background-input-default;
}
}

.other-content-vertically-align {
margin-inline-start: 36px;
}
Loading
Loading