Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
1eeb7bc
Upgrade tests
compulim Apr 19, 2025
9aaa078
Use test ID
compulim Apr 19, 2025
1383a1f
Adding attachment bar
compulim Apr 19, 2025
3c00687
Upload multiple files
compulim Apr 19, 2025
82f34c4
Rename
compulim Apr 19, 2025
b78c97f
Add more text-only test case
compulim Apr 19, 2025
dd3970a
Add text only style
compulim Apr 20, 2025
02982a0
Scroll into view
compulim Apr 20, 2025
707be00
Allow container/media query and custom variables
compulim Apr 20, 2025
66b2914
Fit and finish
compulim Apr 20, 2025
eca24ab
Trim bar padding
compulim Apr 20, 2025
0873c19
Support reduced motion
compulim Apr 20, 2025
c519fb4
Add escape key focus
compulim Apr 20, 2025
9f86357
Add tests
compulim Apr 20, 2025
ffb47db
Add file preview thumbnail
compulim Apr 20, 2025
63e783d
Refactor styleSet
compulim Apr 20, 2025
2da8bf4
Bigger delete icon
compulim Apr 20, 2025
5ba5d97
Add test for ESCAPE key
compulim Apr 20, 2025
6823212
Add test for delete button
compulim Apr 20, 2025
8324e08
Fix extra bottom padding
compulim Apr 20, 2025
07c9f37
Add test for delete in text mode
compulim Apr 20, 2025
194f664
Focus on text box after attachment is deleted
compulim Apr 21, 2025
eedc126
Add clear after send test
compulim Apr 21, 2025
aa52c2b
No line break
compulim Apr 21, 2025
dbbf890
16x16 icon
compulim Apr 21, 2025
cc1396f
Add accessibility labels
compulim Apr 21, 2025
1a4cab6
Add styleOptions
compulim Apr 21, 2025
cdb318a
Rename to as-list-item and as-thumbnail
compulim Apr 21, 2025
bbd0451
Fix padding
compulim Apr 21, 2025
f419a05
Use test ID
compulim Apr 21, 2025
5e155f8
Clean up
compulim Apr 21, 2025
50d2018
Add entry
compulim Apr 21, 2025
ffca949
Add issue number
compulim Apr 21, 2025
58e1dfb
Fix class naem
compulim Apr 21, 2025
8b4ce2f
Add styleOptions.sendBoxAttachmentBarMaxHeight
compulim Apr 21, 2025
de5000b
Update PR number
compulim Apr 21, 2025
dba5a4b
Make attachment bar full width
compulim May 19, 2025
f285c79
Fix test
compulim May 20, 2025
2cb6356
Fix empty attachment bar
compulim May 20, 2025
797cb8c
Add preview
compulim May 20, 2025
ec797b0
Fix flaky test
compulim May 20, 2025
1d7a330
Fix screenshot for full-width attachment bar
compulim May 20, 2025
3bf6551
Fix screenshot for full-width attachment bar
compulim May 20, 2025
9ef18cb
Remove propTypes
compulim May 20, 2025
0a4ba5c
Update prop checking
compulim May 20, 2025
6aebcf9
Merge branch 'main' into feat-file-attachment
compulim May 20, 2025
d389fe7
Upgrade to valibot for props validation
compulim May 20, 2025
837bf4c
Fix flaky
compulim May 20, 2025
66fbd82
Add dark mode test for delete button
compulim May 20, 2025
3f0b501
Incorporate suggestions
compulim May 20, 2025
d227f5b
Use #region/#endregion
compulim May 20, 2025
1a9fb75
Remove center after move to masker
compulim May 20, 2025
456b165
Remove display: content
compulim May 20, 2025
64643a3
Adds <ModdableIcon>
compulim May 21, 2025
0b66f20
Use moddable icon
compulim May 22, 2025
4050a27
Use icon size
compulim May 23, 2025
c4efc05
Move attachment bar to top
compulim May 27, 2025
4bddb50
Fix test for moving attachment bar above send box
compulim May 27, 2025
3bcf233
Fix test
compulim May 27, 2025
5df4ccf
Merge branch 'main' into feat-file-attachment
compulim May 27, 2025
00f1bb9
Fix button size
compulim May 28, 2025
13b4e63
Fix delete icon size
compulim May 28, 2025
f4eaade
Merge branch 'main' into feat-file-attachment
compulim May 28, 2025
ad19e83
Add achievement
OEvgeny May 28, 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
9 changes: 9 additions & 0 deletions ACHIEVEMENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,15 @@ A curated list of major achievements by the Web Chat team. This document celebra

## 🎨 UI & Theming

### 📎 Attachment Preview for `sendAttachmentOn: "send"`

**Goal:** Improve multi-file upload UX by introducing persistent attachment previews.
**By:** [@compulim](https://github.com/compulim) in [PR #5464](https://github.com/microsoft/BotFramework-WebChat/pull/5464)

- Added `SendBoxAttachmentBar` to allow users to preview and remove attachments before sending.
- Previews switch between thumbnails and list mode based on count and accessibility settings.
- Enhances multi-folder upload workflows and aligns with modern messaging UX.

### 🧾 Code Block Rendering & Highlighting System

**Goal:** Unify and polish code block rendering across Markdown and UI components.
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ Notes: web developers are advised to use [`~` (tilde range)](https://github.com/
- Existing behavior will be kept and activities will be grouped by `sender` followed by `status`
- `useGroupActivitiesByName` is favored over the existing `useGroupActivities` hook for performance reason
- Middleware which support the new grouping name init argument should only compute the grouping if they match the grouping name, or the grouping name is not specified, otherwise, should do nothing and call the downstream middleware
- Resolved [#5463](https://github.com/microsoft/BotFramework-WebChat/issues/5463). Added attachment preview for `sendAttachmentOn: "send"`, in PR [#5464](https://github.com/microsoft/BotFramework-WebChat/pull/5464), by [@compulim](https://github.com/compulim)
- Attaching files will no longer remove previously attached files

### Changed

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions __tests__/html/sendAttachmentOn/useSendBoxAttachments.html
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
const fileReader = new FileReader();

fileReader.onerror = reject;
fileReader.onload = () => resolve(fileReader.result);
fileReader.onload = () => resolve(new URL(fileReader.result));
fileReader.readAsDataURL(thumbnailBlob);
});

Expand All @@ -64,7 +64,7 @@
useSendBoxAttachments()[1](attachmentsRef.current);
});

// THEN: It should show checkmark on the button.
// THEN: It should show checkmark on the button and file preview.
await host.snapshot();

// WHEN: `useSendBoxAttachments` hook is called to get the attachments.
Expand Down
5 changes: 0 additions & 5 deletions __tests__/html/sendAttachmentOn/withMessage.js

This file was deleted.

2 changes: 2 additions & 0 deletions __tests__/html2/avatar/layout.default.html
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@
await pageObjects.sendMessageViaSendBox('no avatar');
await pageConditions.numActivitiesShown(6);

await pageConditions.allOutgoingActivitiesSent();

await host.snapshot('local', { skipCheckAccessibility: true });

await renderWebChat({
Expand Down
2 changes: 2 additions & 0 deletions __tests__/html2/bubble/fixedWidth.html
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@

await pageConditions.numActivitiesShown(5);

await pageConditions.allImagesLoaded();

await host.snapshot('local');
});
</script>
Expand Down
70 changes: 70 additions & 0 deletions __tests__/html2/sendBox/previewBeforeSend/clearAfterSend.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<!doctype html>
<html lang="en-US">
<head>
<link href="/assets/index.css" rel="stylesheet" type="text/css" />
<script crossorigin="anonymous" src="/test-harness.js"></script>
<script crossorigin="anonymous" src="/test-page-object.js"></script>
<script crossorigin="anonymous" src="/__dist__/webchat-es5.js"></script>
</head>
<body>
<main id="webchat"></main>
<script type="importmap">
{
"imports": {
"@testduet/wait-for": "https://esm.sh/@testduet/wait-for",
"@testing-library/dom": "https://esm.sh/@testing-library/dom"
}
}
</script>
<script type="module">
import { waitFor } from '@testduet/wait-for';
import { queryAllByTestId } from '@testing-library/dom';

const {
testHelpers: { createDirectLineEmulator },
WebChat: { createDirectLine, testIds }
} = window;

run(async function () {
// We enable reduced motion by default, disable it first.
await host.sendDevToolsCommand('Emulation.setEmulatedMedia', {
features: [{ name: 'prefers-reduced-motion', value: '' }]
});

const { directLine, store } = createDirectLineEmulator();

WebChat.renderWebChat({ directLine, store }, document.getElementById('webchat'));

await pageConditions.uiConnected();

// WHEN: Upload button is clicked and a ZIP file is selected.
await host.upload(pageElements.uploadButton(), 'minecraftdungeons.jpg');

await waitFor(() => expect(queryAllByTestId(document, testIds.sendBoxAttachmentBarItem)).toHaveLength(1));

// THEN: Should match screenshot.
await host.snapshot('local');

// WHEN: Press ENTER key.
const { resolveAll } = await directLine.actPostActivity(async () => {
await host.sendKeys('ENTER');
});

// THEN: Should clear attachments.
await waitFor(() => expect(queryAllByTestId(document, testIds.sendBoxAttachmentBarItem)).toHaveLength(0));

// THEN: Should match screenshot.
await host.snapshot('local');

// WHEN: After attachment is sent.
await resolveAll();

// THEN: Should show the activity.
await pageConditions.numActivitiesShown(1);

// THEN: Should match screenshot.
await host.snapshot('local');
});
</script>
</body>
</html>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
65 changes: 65 additions & 0 deletions __tests__/html2/sendBox/previewBeforeSend/deleteButton.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<!doctype html>
<html lang="en-US">
<head>
<link href="/assets/index.css" rel="stylesheet" type="text/css" />
<script crossorigin="anonymous" src="/test-harness.js"></script>
<script crossorigin="anonymous" src="/test-page-object.js"></script>
<script crossorigin="anonymous" src="/__dist__/webchat-es5.js"></script>
</head>
<body>
<main id="webchat"></main>
<script type="importmap">
{
"imports": {
"@testduet/wait-for": "https://esm.sh/@testduet/wait-for",
"@testing-library/dom": "https://esm.sh/@testing-library/dom"
}
}
</script>
<script type="module">
import { waitFor } from '@testduet/wait-for';
import { queryAllByTestId } from '@testing-library/dom';

const {
testHelpers: { createDirectLineEmulator },
WebChat: { createDirectLine, testIds }
} = window;

run(async function () {
// We enable reduced motion by default, disable it first.
await host.sendDevToolsCommand('Emulation.setEmulatedMedia', {
features: [{ name: 'prefers-reduced-motion', value: '' }]
});

const { directLine, store } = createDirectLineEmulator();

WebChat.renderWebChat({ directLine, store }, document.getElementById('webchat'));

await pageConditions.uiConnected();

// WHEN: Upload button is clicked and a ZIP file is selected.
await host.upload(pageElements.uploadButton(), 'minecraftdungeons.jpg');

await waitFor(() => expect(queryAllByTestId(document, testIds.sendBoxAttachmentBarItem)).toHaveLength(1));

// WHEN: Press SHIFT-TAB key twice to move the focus to the delete button.
await host.sendShiftTab(2);

// THEN: Should focus on the attachment delete button.
expect(document.activeElement.getAttribute('data-testid')).toBe(testIds.sendBoxAttachmentBarItemDeleteButton);

// WHEN: Press SPACEBAR key.
await host.sendKeys(' ');

// THEN: The attachment should be removed.
await waitFor(() => expect(queryAllByTestId(document, testIds.sendBoxAttachmentBarItem)).toHaveLength(0));

// THEN: Should focus on the send box text box.
expect(document.activeElement).toBe(pageElements.sendBoxTextBox());

// THEN: Should match screenshot.
await host.snapshot('local');
});
</script>
</body>
</html>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<!doctype html>
<html lang="en-US">
<head>
<link href="/assets/index.css" rel="stylesheet" type="text/css" />
<script crossorigin="anonymous" src="/test-harness.js"></script>
<script crossorigin="anonymous" src="/test-page-object.js"></script>
<script crossorigin="anonymous" src="/__dist__/webchat-es5.js"></script>
</head>
<body>
<main id="webchat"></main>
<script type="importmap">
{
"imports": {
"@testduet/wait-for": "https://esm.sh/@testduet/wait-for",
"@testing-library/dom": "https://esm.sh/@testing-library/dom"
}
}
</script>
<script type="module">
import { waitFor } from '@testduet/wait-for';
import { queryAllByTestId } from '@testing-library/dom';

const {
testHelpers: { createDirectLineEmulator },
WebChat: { createDirectLine, testIds }
} = window;

run(async function () {
// We enable reduced motion by default, disable it first.
await host.sendDevToolsCommand('Emulation.setEmulatedMedia', {
features: [{ name: 'prefers-reduced-motion', value: '' }]
});

const { directLine, store } = createDirectLineEmulator();

WebChat.renderWebChat({ directLine, store }, document.getElementById('webchat'));

await pageConditions.uiConnected();

// WHEN: 4 files are uploaded.
for (const filename of ['seaofthieves.jpg', 'forzahorizon5.jpg', 'haloinfinite.jpg', 'minecraftdungeons.jpg']) {
const { length: numAttachment } = queryAllByTestId(document, testIds.sendBoxAttachmentBarItem);

await host.upload(pageElements.uploadButton(), filename);

await waitFor(() =>
expect(queryAllByTestId(document, testIds.sendBoxAttachmentBarItem)).toHaveLength(numAttachment + 1)
);
}

// WHEN: Press SHIFT-TAB key 5 times to move the focus to the delete button of the first attachment.
await host.sendShiftTab(5);

// THEN: Should focus on the attachment delete button of the first attachment.
expect(document.activeElement).toBe(pageElements.allByTestId(testIds.sendBoxAttachmentBarItemDeleteButton)[0]);

// WHEN: Press SPACEBAR key.
await host.sendKeys(' ');

// THEN: The attachment should be removed.
await waitFor(() => expect(queryAllByTestId(document, testIds.sendBoxAttachmentBarItem)).toHaveLength(3));

// THEN: Should focus on the send box text box.
expect(document.activeElement).toBe(pageElements.sendBoxTextBox());

// THEN: Should match screenshot.
await host.snapshot('local');
});
</script>
</body>
</html>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
65 changes: 65 additions & 0 deletions __tests__/html2/sendBox/previewBeforeSend/escapeKey.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<!doctype html>
<html lang="en-US">
<head>
<link href="/assets/index.css" rel="stylesheet" type="text/css" />
<script crossorigin="anonymous" src="/test-harness.js"></script>
<script crossorigin="anonymous" src="/test-page-object.js"></script>
<script crossorigin="anonymous" src="/__dist__/webchat-es5.js"></script>
</head>
<body>
<main id="webchat"></main>
<script type="importmap">
{
"imports": {
"@testduet/wait-for": "https://esm.sh/@testduet/wait-for",
"@testing-library/dom": "https://esm.sh/@testing-library/dom"
}
}
</script>
<script type="module">
import { waitFor } from '@testduet/wait-for';
import { queryAllByTestId } from '@testing-library/dom';

const {
testHelpers: { createDirectLineEmulator },
WebChat: { createDirectLine, testIds }
} = window;

run(async function () {
// We enable reduced motion by default, disable it first.
await host.sendDevToolsCommand('Emulation.setEmulatedMedia', {
features: [{ name: 'prefers-reduced-motion', value: '' }]
});

const { directLine, store } = createDirectLineEmulator();

WebChat.renderWebChat({ directLine, store }, document.getElementById('webchat'));

await pageConditions.uiConnected();

// WHEN: Upload button is clicked and a ZIP file is selected.
await host.upload(pageElements.uploadButton(), 'minecraftdungeons.jpg');

await waitFor(() => expect(queryAllByTestId(document, testIds.sendBoxAttachmentBarItem)).toHaveLength(1));

// WHEN: Press TAB key twice to move the focus to the delete button.
await host.sendShiftTab(2);

// THEN: Should focus on the attachment delete button.
expect(document.activeElement).toBe(pageElements.allByTestId(testIds.sendBoxAttachmentBarItemDeleteButton)[0]);

// THEN: Should match screenshot.
await host.snapshot('local');

// WHEN: Press ESCAPE key.
await host.sendKeys('ESCAPE');

// THEN: Should focus on the send box text box.
expect(document.activeElement).toBe(pageElements.sendBoxTextBox());

// THEN: Should match screenshot.
await host.snapshot('local');
});
</script>
</body>
</html>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
50 changes: 50 additions & 0 deletions __tests__/html2/sendBox/previewBeforeSend/filePreview.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<!doctype html>
<html lang="en-US">
<head>
<link href="/assets/index.css" rel="stylesheet" type="text/css" />
<script crossorigin="anonymous" src="/test-harness.js"></script>
<script crossorigin="anonymous" src="/test-page-object.js"></script>
<script crossorigin="anonymous" src="/__dist__/webchat-es5.js"></script>
</head>
<body>
<main id="webchat"></main>
<script type="importmap">
{
"imports": {
"@testduet/wait-for": "https://esm.sh/@testduet/wait-for",
"@testing-library/dom": "https://esm.sh/@testing-library/dom"
}
}
</script>
<script type="module">
import { waitFor } from '@testduet/wait-for';
import { queryAllByTestId } from '@testing-library/dom';

const {
testHelpers: { createDirectLineEmulator },
WebChat: { createDirectLine, testIds }
} = window;

run(async function () {
// We enable reduced motion by default, disable it first.
await host.sendDevToolsCommand('Emulation.setEmulatedMedia', {
features: [{ name: 'prefers-reduced-motion', value: '' }]
});

const { directLine, store } = createDirectLineEmulator();

WebChat.renderWebChat({ directLine, store }, document.getElementById('webchat'));

await pageConditions.uiConnected();

// WHEN: Upload button is clicked and a ZIP file is selected.
await host.upload(pageElements.uploadButton(), 'empty.zip');

await waitFor(() => expect(queryAllByTestId(document, testIds.sendBoxAttachmentBarItem)).toHaveLength(1));

// THEN: With 3 attachments, they should appear as thumbnails.
await host.snapshot('local');
});
</script>
</body>
</html>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading