Skip to content

Commit 88e4f00

Browse files
authored
Introduction of polymiddleware (microsoft#5515)
* Initial commit of polyMiddleware * Bump react-chain-of-responsibility and handler-chain * Move middleware typing to middleware package * Add useReduceMemo * Update performance snapshot * Fix ESLint * Fix test * Bump to latest * Revert * Apply * Revert * Bump to latest * Fix test * Add expectation * Group activities should invalidate callback on style options change * Add barrel file for Webpack4 * Rename * Better comment * Add comment * Convert to class component * Improve performance * Reduce one use variable * Use internal current config * Hide Init * Add types="jest" * Add comment * Better comment * Fix reducer immutability * Import botframework-webchat-middleware * Export activity poly middleware components * Add priority * Add TODO * Sort * Unify react-chain-of-responsibility version * WIP: Add test * Port useCreateActivityRendererInternal hook * Upgrade test to html2 * Prefix Legacy* and allow extraneous activity props * Fold unnecessary useCreateActivityRendererInternal * Add no import rules * Comments * Pass all legacy props * Add import restriction * Move under folder * Add test * Bump react-chain-of-responsibility * Add snapshot * Better comments * Add tests * Update comment * Rename to LegacyAttachmentMiddleware and added bf-wc-middleware/internal * Check middleware factory marker * Add deprecation * Use valibot for factory function validation * Enhancer must be of type function * Check for no console.warn * Add comments * Add comments * Rename marker to tag * Bump use-reduce-memo * Add tests * Better comments * Prevent wasted rendering on unrelated middleware changes * Fix exports * Fix ESLint * Fix tests for extractEnhancer * Add snapshots * Add React rules * Fix ESLint * Better comments * Use Object.defineProperty * Remove one-use * Offload things from LegacyActivityBridge to LegacyActivityComposer * Update snapshots * Provide deprecation path for api activityMiddleware * Remove unnecessary declaration of activityMiddleware * Simplify import * Fix import * Rename to api-middleware * Fix npm start * Rename/add tests * Fix .eslintrc * Add tests * Add sample * Add sample * Clean up docs * Rename PolyMiddleware to Polymiddleware * Cascade build * Ignore render thru error boundary * Add error box middleware * Fix falsy polymiddleware * Allow middleware of false * Remove unnecessary props * Fix test * Fix grouping * Fix ESLint * Upgrade a test * Add trackException * Print error once by React * No export Provider and extract* * Add priority * Rename * Set TODO priority * Reduce exports * Clean up * Add fallback error box * Clean up * Add snapshot * Add no error box test * Deconfuse render function with function component * Use ErrorBoxPolymiddlewareProxy * Fix homepage * Add new <DebugProvider> * Should not depends on bundle * Remove dependency to bundle * Add debug-provider package * Add debug-theme * Update tests * Remove unnecessary expect * Add new test * Add a red box * Remove node_env * Add date * Rename * Remove localDependencies * Fix typing * Fix test * Fix test * Apply suggestion * Add botframework-webchat-react-hooks package * Use as unknown instead of as any * Add prop type * Add entry * Add entry * Fix ESLint * Typos * Typo * Test bundle content * Reduce boot coed * Add test for exports
1 parent a1d0a3a commit 88e4f00

File tree

209 files changed

+5874
-1111
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

209 files changed

+5874
-1111
lines changed

.dockerignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
!/__tests__
33
/__tests__/__image_snapshots__
44
!/packages/bundle/dist
5+
!/packages/debug-theme/dist
56
!/packages/fluent-theme/dist
67
!/packages/playground/build
78
!/packages/test/fluent-bundle/dist

.github/workflows/pull-request-validation.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ jobs:
5757
./package.json
5858
./__tests__/
5959
./packages/bundle/dist/
60+
./packages/debug-theme/dist/
6061
./packages/fluent-theme/dist/
6162
./packages/test/harness/
6263
./packages/test/page-object/dist/

CHANGELOG.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ Notes: web developers are advised to use [`~` (tilde range)](https://github.com/
4141
- `sendBoxToolbarMiddleware`, related to PR [#5504](https://github.com/microsoft/BotFramework-WebChat/pull/5504)
4242
- `styleOptions.hideUploadButton` is being deprecated in favor of `styleOptions.disableFileUpload`. The option will be removed on or after 2027-07-14
4343
- `botframework-directlinespeech-sdk` no longer ponyfill `AbortController`, it is supported by modern browsers, in PR [#5530](https://github.com/microsoft/BotFramework-WebChat/pull/5530)
44+
- `activityMiddleware` is being deprecated in favor of [`polymiddleware`](./docs/MIDDLEWARE.md). It will be removed on or after 2027-08-16, related to PR [#5515](https://github.com/microsoft/BotFramework-WebChat/pull/5515)
4445

4546
### Added
4647

@@ -108,6 +109,13 @@ Notes: web developers are advised to use [`~` (tilde range)](https://github.com/
108109
- Deprecated `hideUploadButton` in favor of `disableFileUpload`.
109110
- Updated `BasicSendBoxToolbar` to rely solely on `disableFileUpload`.
110111
- Added support for livestreaming via `entities[type="streaminfo"]` in PR [#5517](https://github.com/microsoft/BotFramework-WebChat/pull/5517) by [@kylerohn](https://github.com/kylerohn) and [@compulim](https://github.com/compulim)
112+
- Added `polymiddleware`, a new [universal middleware for every UIs](./docs/MIDDLEWARE.md), by [@compulim](https://github.com/compulim) in PR [#5515](https://github.com/microsoft/BotFramework-WebChat/pull/5515)
113+
- Added `polymiddleware` to `<ThemeProvider>`
114+
- Currently supports activity middleware and the new error box middleware
115+
- New internal packages, by [@compulim](https://github.com/compulim) in PR [#5515](https://github.com/microsoft/BotFramework-WebChat/pull/5515)
116+
- `@msinternal/botframework-webchat-api-middleware` for middleware branch of API package
117+
- `@msinternal/botframework-webchat-debug-theme` package for enabling debugging scenarios
118+
- `@msinternal/botframework-webchat-react-hooks` for helpers for React hooks
111119

112120
### Changed
113121

@@ -151,7 +159,7 @@ Notes: web developers are advised to use [`~` (tilde range)](https://github.com/
151159
- [`micromark@4.0.2`](https://npmjs.com/package/micromark/v/4.0.2)
152160
- [`microsoft-cognitiveservices-speech-sdk@1.45.0`](https://npmjs.com/package/microsoft-cognitiveservices-speech-sdk/v/1.45.0)
153161
- [`mime@4.0.7`](https://npmjs.com/package/mime/v/4.0.7)
154-
- [`react-chain-of-responsibility@0.3.0`](https://npmjs.com/package/react-chain-of-responsibility/v/0.3.0)
162+
- [`react-chain-of-responsibility@0.4.0-main.2a72139`](https://npmjs.com/package/react-chain-of-responsibility/v/0.4.0-main.2a72139)
155163
- [`react-film@4.0.0`](https://npmjs.com/package/react-film/v/4.0.0)
156164
- [`react-say@2.2.0`](https://npmjs.com/package/react-say/v/2.2.0)
157165
- [`react-scroll-to-bottom@4.2.0`](https://npmjs.com/package/react-scroll-to-bottom/v/4.2.0)
@@ -277,11 +285,12 @@ Notes: web developers are advised to use [`~` (tilde range)](https://github.com/
277285
- Fixed [#5518](https://github.com/microsoft/BotFramework-WebChat/issues/5518). Minimal bundled build should work properly, in PR [#5507](https://github.com/microsoft/BotFramework-WebChat/pull/5507), by [@compulim](https://github.com/compulim)
278286
- Fixed [#5520](https://github.com/microsoft/BotFramework-WebChat/issues/5520). Version information should be injected when installed via npm, in PR [#5521](https://github.com/microsoft/BotFramework-WebChat/pull/5521), by [@compulim](https://github.com/compulim)
279287

280-
# Removed
288+
### Removed
281289

282290
- Deprecating `disabled` props and `useDisabled` hook in favor of new `uiState` props and `useUIState` hook, in PR [#5276](https://github.com/microsoft/BotFramework-WebChat/pull/5276), by [@compulim](https://github.com/compulim)
283291
- `useSuggestedActions()` hook is being deprecated in favor of the `useSuggestedActionsHooks().useSuggestedActions()` hook, in PR [#5489](https://github.com/microsoft/BotFramework-WebChat/pull/5489), by [@compulim](https://github.com/compulim)
284292
- Fixed core internal import in legacy CommonJS environments, in [5509](https://github.com/microsoft/BotFramework-WebChat/pull/5509), by [@OEvgeny](https://github.com/OEvgeny)
293+
- `activityMiddleware` is being deprecated in favor of [`polymiddleware`](./docs/MIDDLEWARE.md). It will be removed on or after 2027-08-16, related to PR [#5515](https://github.com/microsoft/BotFramework-WebChat/pull/5515)
285294

286295
### Samples
287296

Loading
13.2 KB
Loading

__tests__/html/focusManagement.disableHeroCard.obsolete.html

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<!DOCTYPE html>
1+
<!doctype html>
22
<html lang="en-US">
33
<head>
44
<link href="/assets/index.css" rel="stylesheet" type="text/css" />
@@ -15,14 +15,29 @@
1515
<script type="text/babel" data-presets="env,stage-3,react">
1616
const {
1717
WebChat: {
18-
Components: { HeroCardContent }
18+
Components: { HeroCardContent },
19+
hooks: { useActivities }
1920
}
2021
} = window;
2122

2223
run(async function () {
2324
const store = testHelpers.createStore();
2425
const directLine = WebChat.createDirectLine({ token: await testHelpers.token.fetchDirectLineToken() });
2526

27+
const AutoDisableHeroCardContent = ({ activity, attachment }) => {
28+
const [activities] = useActivities();
29+
const messageActivities = activities.filter(activity => activity.type === 'message');
30+
const isMostRecentMessageActivity = messageActivities.pop() === activity;
31+
32+
return (
33+
<HeroCardContent
34+
actionPerformedClassName="card__action--performed"
35+
content={attachment.content}
36+
disabled={!isMostRecentMessageActivity}
37+
/>
38+
);
39+
};
40+
2641
WebChat.renderWebChat(
2742
{
2843
attachmentMiddleware:
@@ -35,13 +50,7 @@
3550
const mostRecent = messageActivities.pop() === activity;
3651

3752
if (attachment.contentType === 'application/vnd.microsoft.card.hero') {
38-
return (
39-
<HeroCardContent
40-
actionPerformedClassName="card__action--performed"
41-
content={attachment.content}
42-
disabled={!mostRecent}
43-
/>
44-
);
53+
return <AutoDisableHeroCardContent activity={activity} attachment={attachment} />;
4554
}
4655

4756
return next(...args);

__tests__/html/hooks.useCreateActivityRenderer.js

Lines changed: 0 additions & 5 deletions
This file was deleted.

__tests__/html/renderActivity.performance.html

Lines changed: 78 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,32 @@
1919
<script type="text/babel" data-presets="env,stage-3,react">
2020
const BATCH_SIZE = 5;
2121

22-
const timesActivityRendered = new Map();
22+
const timesEnhancerCalled = new Map();
23+
const timesHandlerCalled = new Map();
2324

2425
function activityRendered() {
25-
return next => (...args) => {
26-
const [{ activity }] = args;
27-
const renderActivity = next(...args)
28-
timesActivityRendered.set(activity.id, (timesActivityRendered.get(activity.id) ?? 0) + 1);
29-
return (...args) => (
30-
<>
31-
{renderActivity.call ? renderActivity(...args) : renderActivity}
32-
<small> Rendered {timesActivityRendered.get(activity.id)} times</small>
33-
</>
34-
)
35-
}
26+
return next =>
27+
(...args) => {
28+
const [{ activity }] = args;
29+
const renderActivity = next(...args);
30+
31+
timesEnhancerCalled.set(activity.id, (timesEnhancerCalled.get(activity.id) ?? 0) + 1);
32+
33+
return (...args) => {
34+
timesHandlerCalled.set(activity.id, (timesHandlerCalled.get(activity.id) ?? 0) + 1);
35+
36+
return (
37+
<>
38+
{renderActivity.call ? renderActivity(...args) : renderActivity}
39+
<small>
40+
{' '}
41+
Enhancer called {timesEnhancerCalled.get(activity.id)} times and handler called{' '}
42+
{timesHandlerCalled.get(activity.id)} times
43+
</small>
44+
</>
45+
);
46+
};
47+
};
3648
}
3749

3850
let shownCount = 0;
@@ -43,7 +55,13 @@
4355
promises.push(
4456
// Plain text message isolate dependencies on Markdown.
4557
directLine.emulateIncomingActivity(
46-
{ id: `activity-${shownCount + index}`, text: `Message ${shownCount + index}.`, textFormat: 'plain', type: 'message', timestamp },
58+
{
59+
id: `activity-${shownCount + index}`,
60+
text: `Message ${shownCount + index}.`,
61+
textFormat: 'plain',
62+
type: 'message',
63+
timestamp
64+
},
4765
{ skipWait: true }
4866
)
4967
);
@@ -54,53 +72,54 @@
5472
await pageConditions.numActivitiesShown(shownCount);
5573
}
5674

57-
run(
58-
async function () {
59-
const {
60-
WebChat: { ReactWebChat }
61-
} = window; // Imports in UMD fashion.
62-
63-
const { directLine, store } = testHelpers.createDirectLineEmulator();
64-
65-
WebChat.renderWebChat({ directLine, store, activityMiddleware: [activityRendered] }, document.querySelector('main'));
66-
67-
await pageConditions.uiConnected();
68-
pageElements.transcript().focus();
69-
70-
// WHEN: Adding 10 activities.
71-
await postMessagesBatch(directLine);
72-
await postMessagesBatch(directLine);
73-
74-
// THEN: Should not re-render activity more than twice.
75-
expect(Math.max(...timesActivityRendered.values())).toEqual(2);
76-
expect(Math.min(...timesActivityRendered.values())).toEqual(1);
77-
78-
// WHEN: Scroll and clicked on the 5th activity.
79-
const previousTimesActivityRendered = structuredClone(timesActivityRendered)
80-
pageElements.activities()[4].scrollIntoView();
81-
await host.clickAt(10, 10, pageElements.activities()[4]);
82-
83-
// THEN: Should focus on the activity.
84-
expect(pageElements.focusedActivity()).toEqual(pageElements.activities()[4]);
85-
86-
// THEN: Should not re-render.
87-
expect(timesActivityRendered).toEqual(previousTimesActivityRendered);
88-
89-
// WHEN: The 9th activity received an update.
90-
const timestamp = new Date().toISOString();
91-
const activity9Renders = timesActivityRendered.get('activity-8');
92-
await directLine.emulateIncomingActivity(
93-
{ id: `activity-8`, text: `Activity 8 got updated`, textFormat: 'plain', type: 'message', timestamp },
94-
{ skipWait: true }
95-
);
75+
run(async function () {
76+
const {
77+
WebChat: { ReactWebChat }
78+
} = window; // Imports in UMD fashion.
9679

97-
// THEN: Should re-render the 9th activity once.
98-
expect(timesActivityRendered.get('activity-8')).toBe(activity9Renders + 1);
99-
// THEN: Should render the updated 9th activity.
100-
pageElements.focusedActivity().scrollIntoView();
101-
await host.snapshot();
102-
}
103-
);
80+
const { directLine, store } = testHelpers.createDirectLineEmulator();
81+
82+
WebChat.renderWebChat(
83+
{ directLine, store, activityMiddleware: [activityRendered] },
84+
document.querySelector('main')
85+
);
86+
87+
await pageConditions.uiConnected();
88+
pageElements.transcript().focus();
89+
90+
// WHEN: Adding 10 activities.
91+
await postMessagesBatch(directLine);
92+
await postMessagesBatch(directLine);
93+
94+
// THEN: Should not re-render activity more than twice.
95+
expect(Math.max(...timesHandlerCalled.values())).toEqual(2);
96+
expect(Math.min(...timesHandlerCalled.values())).toEqual(1);
97+
98+
// WHEN: Scroll and clicked on the 5th activity.
99+
const previousTimesActivityRendered = structuredClone(timesHandlerCalled);
100+
pageElements.activities()[4].scrollIntoView();
101+
await host.clickAt(10, 10, pageElements.activities()[4]);
102+
103+
// THEN: Should focus on the activity.
104+
expect(pageElements.focusedActivity()).toEqual(pageElements.activities()[4]);
105+
106+
// THEN: Should not re-render.
107+
expect(timesHandlerCalled).toEqual(previousTimesActivityRendered);
108+
109+
// WHEN: The 9th activity received an update.
110+
const timestamp = new Date().toISOString();
111+
const activity9Renders = timesHandlerCalled.get('activity-8');
112+
await directLine.emulateIncomingActivity(
113+
{ id: `activity-8`, text: `Activity 8 got updated`, textFormat: 'plain', type: 'message', timestamp },
114+
{ skipWait: true }
115+
);
116+
117+
// THEN: Should re-render the 9th activity once.
118+
expect(timesHandlerCalled.get('activity-8')).toBe(activity9Renders + 1);
119+
// THEN: Should render the updated 9th activity.
120+
pageElements.focusedActivity().scrollIntoView();
121+
await host.snapshot();
122+
});
104123
</script>
105124
</body>
106125
</html>

0 commit comments

Comments
 (0)