Skip to content

Commit 1af9221

Browse files
committed
DismissButton based on Button
1 parent 85eb33f commit 1af9221

16 files changed

Lines changed: 307 additions & 264 deletions

packages/api-middleware/src/PolymiddlewareComposer.tsx

Lines changed: 22 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,12 @@ import {
1515
} from 'valibot';
1616

1717
import { ActivityPolymiddlewareProvider, extractActivityEnhancer } from './activityPolymiddleware';
18+
import { ButtonPolymiddlewareProvider, extractButtonEnhancer } from './buttonPolymiddleware';
1819
import {
1920
ChatLauncherButtonPolymiddlewareProvider,
2021
extractChatLauncherButtonEnhancer
2122
} from './chatLauncherButtonPolymiddleware';
2223
import { ErrorBoxPolymiddlewareProvider, extractErrorBoxEnhancer } from './errorBoxPolymiddleware';
23-
import { IconButtonPolymiddlewareProvider, extractIconButtonEnhancer } from './iconButtonPolymiddleware';
2424
import { PopoverPolymiddlewareProvider, extractPopoverEnhancer } from './popoverPolymiddleware';
2525
import { Polymiddleware } from './types/Polymiddleware';
2626

@@ -55,6 +55,21 @@ function PolymiddlewareComposer(props: PolymiddlewareComposerProps) {
5555

5656
const activityPolymiddleware = useMemo(() => activityEnhancers.map(enhancer => () => enhancer), [activityEnhancers]);
5757

58+
const buttonEnhancers = useMemoWithPrevious<ReturnType<typeof extractButtonEnhancer>>(
59+
(prevButtonEnhancers = []) => {
60+
const buttonEnhancers = extractButtonEnhancer(polymiddleware);
61+
62+
// Checks for array equality, return previous version if nothing has changed.
63+
return prevButtonEnhancers.length === buttonEnhancers.length &&
64+
buttonEnhancers.every((middleware, index) => Object.is(middleware, prevButtonEnhancers.at(index)))
65+
? prevButtonEnhancers
66+
: buttonEnhancers;
67+
},
68+
[polymiddleware]
69+
);
70+
71+
const buttonPolymiddleware = useMemo(() => buttonEnhancers.map(enhancer => () => enhancer), [buttonEnhancers]);
72+
5873
const chatLauncherButtonEnhancers = useMemoWithPrevious<ReturnType<typeof extractChatLauncherButtonEnhancer>>(
5974
(prevChatLauncherButtonEnhancers = []) => {
6075
const chatLauncherButtonEnhancers = extractChatLauncherButtonEnhancer(polymiddleware);
@@ -90,24 +105,6 @@ function PolymiddlewareComposer(props: PolymiddlewareComposerProps) {
90105

91106
const errorBoxPolymiddleware = useMemo(() => errorBoxEnhancers.map(enhancer => () => enhancer), [errorBoxEnhancers]);
92107

93-
const iconButtonEnhancers = useMemoWithPrevious<ReturnType<typeof extractIconButtonEnhancer>>(
94-
(prevIconButtonEnhancers = []) => {
95-
const iconButtonEnhancers = extractIconButtonEnhancer(polymiddleware);
96-
97-
// Checks for array equality, return previous version if nothing has changed.
98-
return prevIconButtonEnhancers.length === iconButtonEnhancers.length &&
99-
iconButtonEnhancers.every((middleware, index) => Object.is(middleware, prevIconButtonEnhancers.at(index)))
100-
? prevIconButtonEnhancers
101-
: iconButtonEnhancers;
102-
},
103-
[polymiddleware]
104-
);
105-
106-
const iconButtonPolymiddleware = useMemo(
107-
() => iconButtonEnhancers.map(enhancer => () => enhancer),
108-
[iconButtonEnhancers]
109-
);
110-
111108
const popoverEnhancers = useMemoWithPrevious<ReturnType<typeof extractPopoverEnhancer>>(
112109
(prevPopoverEnhancers = []) => {
113110
const popoverEnhancers = extractPopoverEnhancer(polymiddleware);
@@ -134,13 +131,13 @@ function PolymiddlewareComposer(props: PolymiddlewareComposerProps) {
134131

135132
return (
136133
<ActivityPolymiddlewareProvider middleware={activityPolymiddleware}>
137-
<ChatLauncherButtonPolymiddlewareProvider middleware={chatLauncherButtonPolymiddleware}>
138-
<ErrorBoxPolymiddlewareProvider middleware={errorBoxPolymiddleware}>
139-
<IconButtonPolymiddlewareProvider middleware={iconButtonPolymiddleware}>
134+
<ButtonPolymiddlewareProvider middleware={buttonPolymiddleware}>
135+
<ChatLauncherButtonPolymiddlewareProvider middleware={chatLauncherButtonPolymiddleware}>
136+
<ErrorBoxPolymiddlewareProvider middleware={errorBoxPolymiddleware}>
140137
<PopoverPolymiddlewareProvider middleware={popoverPolymiddleware}>{children}</PopoverPolymiddlewareProvider>
141-
</IconButtonPolymiddlewareProvider>
142-
</ErrorBoxPolymiddlewareProvider>
143-
</ChatLauncherButtonPolymiddlewareProvider>
138+
</ErrorBoxPolymiddlewareProvider>
139+
</ChatLauncherButtonPolymiddlewareProvider>
140+
</ButtonPolymiddlewareProvider>
144141
</ActivityPolymiddlewareProvider>
145142
);
146143
}
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
import { forwardedRef, reactNode, refObject, validateProps } from '@msinternal/botframework-webchat-react-valibot';
2+
import React, { forwardRef, memo, useMemo, type ForwardedRef, type ReactNode, type RefObject } from 'react';
3+
import { function_, object, optional, picklist, pipe, readonly, string, type InferInput } from 'valibot';
4+
5+
import createErrorBoundaryMiddleware from './private/createErrorBoundaryMiddleware';
6+
import templatePolymiddleware, {
7+
type InferHandler,
8+
type InferHandlerResult,
9+
type InferMiddleware,
10+
type InferProps,
11+
type InferProviderProps,
12+
type InferRenderer,
13+
type InferRequest
14+
} from './private/templatePolymiddleware';
15+
16+
const {
17+
createMiddleware: createButtonPolymiddleware,
18+
extractEnhancer: extractButtonEnhancer,
19+
Provider,
20+
Proxy,
21+
reactComponent: buttonComponent,
22+
useBuildRenderCallback: useBuildRenderButtonCallback
23+
} = templatePolymiddleware<
24+
{
25+
readonly appearance?: 'elevated' | 'flat' | undefined;
26+
readonly size?: 'hero' | 'normal' | undefined;
27+
},
28+
{
29+
readonly children?: ReactNode | undefined;
30+
readonly className?: string | undefined;
31+
readonly forwardedRef?: ForwardedRef<HTMLElement> | undefined;
32+
readonly onClick?: (() => void) | undefined;
33+
readonly popoverTargetAction?: 'hide' | 'show' | 'toggle' | undefined;
34+
readonly popoverTargetRef?: RefObject<Element> | undefined;
35+
}
36+
>('Button');
37+
38+
type ButtonPolymiddleware = InferMiddleware<typeof Provider>;
39+
type ButtonPolymiddlewareHandler = InferHandler<typeof Provider>;
40+
type ButtonPolymiddlewareHandlerResult = InferHandlerResult<typeof Provider>;
41+
type ButtonPolymiddlewareProps = InferProps<typeof Provider>;
42+
type ButtonPolymiddlewareRenderer = InferRenderer<typeof Provider>;
43+
type ButtonPolymiddlewareRequest = InferRequest<typeof Provider>;
44+
type ButtonPolymiddlewareProviderProps = InferProviderProps<typeof Provider>;
45+
46+
const buttonPolymiddlewareProxyPropsSchema = pipe(
47+
object({
48+
appearance: optional(picklist(['elevated', 'flat'])),
49+
children: optional(reactNode()),
50+
className: optional(string()),
51+
forwardedRef: optional(forwardedRef<HTMLElement>()),
52+
onClick: optional(function_()),
53+
popoverTargetAction: optional(picklist(['hide', 'show', 'toggle'])),
54+
popoverTargetRef: optional(refObject<Element>()),
55+
size: optional(picklist(['hero', 'normal']))
56+
}),
57+
readonly()
58+
);
59+
60+
type ButtonPolymiddlewareProxyProps = Readonly<InferInput<typeof buttonPolymiddlewareProxyPropsSchema>>;
61+
62+
// A friendlier version than the organic <Proxy>.
63+
const ButtonPolymiddlewareProxy = memo(
64+
forwardRef(function ButtonPolymiddlewareProxy(props: ButtonPolymiddlewareProxyProps, ref: ForwardedRef<HTMLElement>) {
65+
const { appearance, children, className, onClick, popoverTargetAction, popoverTargetRef, size } = validateProps(
66+
buttonPolymiddlewareProxyPropsSchema,
67+
props
68+
);
69+
70+
const request = useMemo(() => Object.freeze({ appearance, size }), [appearance, size]);
71+
72+
return (
73+
<Proxy
74+
className={className}
75+
forwardedRef={ref}
76+
onClick={onClick}
77+
popoverTargetAction={popoverTargetAction}
78+
popoverTargetRef={popoverTargetRef}
79+
request={request}
80+
>
81+
{children}
82+
</Proxy>
83+
);
84+
})
85+
);
86+
87+
const ButtonPolymiddlewareProvider = memo(function ButtonPolymiddlewareProvider({
88+
children,
89+
middleware
90+
}: ButtonPolymiddlewareProviderProps) {
91+
// Decorates middleware with <ErrorBoundary>.
92+
const middlewareWithErrorBoundary = useMemo(
93+
() =>
94+
Object.freeze([
95+
// TODO: [P1] Should we simplify this middleware signature and allow error boundary middleware to run on every type of middleware?
96+
// (init: any) => next => (request: undefined) => reactComponentForAll()
97+
// We can't do it today because we have sanity check that prevent `reactComponent()` from different middleware cross-polluting each other.
98+
createErrorBoundaryMiddleware({
99+
createMiddleware: createButtonPolymiddleware,
100+
reactComponent: buttonComponent,
101+
where: 'Button polymiddleware'
102+
}),
103+
...middleware
104+
]),
105+
[middleware]
106+
);
107+
108+
return <Provider middleware={middlewareWithErrorBoundary}>{children}</Provider>;
109+
});
110+
111+
export {
112+
buttonComponent,
113+
ButtonPolymiddlewareProvider,
114+
ButtonPolymiddlewareProxy,
115+
createButtonPolymiddleware,
116+
extractButtonEnhancer,
117+
useBuildRenderButtonCallback,
118+
type ButtonPolymiddleware,
119+
type ButtonPolymiddlewareHandler,
120+
type ButtonPolymiddlewareHandlerResult,
121+
type ButtonPolymiddlewareProps,
122+
type ButtonPolymiddlewareProviderProps,
123+
type ButtonPolymiddlewareProxyProps,
124+
type ButtonPolymiddlewareRenderer,
125+
type ButtonPolymiddlewareRequest
126+
};

packages/api-middleware/src/iconButtonPolymiddleware.tsx

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

packages/api-middleware/src/index.ts

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,20 @@ export {
1212
type ActivityPolymiddlewareRequest
1313
} from './activityPolymiddleware';
1414

15+
export {
16+
buttonComponent,
17+
ButtonPolymiddlewareProxy,
18+
createButtonPolymiddleware,
19+
useBuildRenderButtonCallback,
20+
type ButtonPolymiddleware,
21+
type ButtonPolymiddlewareHandler,
22+
type ButtonPolymiddlewareHandlerResult,
23+
type ButtonPolymiddlewareProps,
24+
type ButtonPolymiddlewareProxyProps,
25+
type ButtonPolymiddlewareRenderer,
26+
type ButtonPolymiddlewareRequest
27+
} from './buttonPolymiddleware';
28+
1529
export {
1630
chatLauncherButtonComponent,
1731
ChatLauncherButtonPolymiddlewareProxy,
@@ -40,20 +54,6 @@ export {
4054
type ErrorBoxPolymiddlewareRequest
4155
} from './errorBoxPolymiddleware';
4256

43-
export {
44-
createIconButtonPolymiddleware,
45-
iconButtonComponent,
46-
IconButtonPolymiddlewareProxy,
47-
useBuildRenderIconButtonCallback,
48-
type IconButtonPolymiddleware,
49-
type IconButtonPolymiddlewareHandler,
50-
type IconButtonPolymiddlewareHandlerResult,
51-
type IconButtonPolymiddlewareProps,
52-
type IconButtonPolymiddlewareProxyProps,
53-
type IconButtonPolymiddlewareRenderer,
54-
type IconButtonPolymiddlewareRequest
55-
} from './iconButtonPolymiddleware';
56-
5757
export {
5858
createPopoverPolymiddleware,
5959
popoverComponent,
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import { type ActivityPolymiddleware } from '../activityPolymiddleware';
2+
import { type ButtonPolymiddleware } from '../buttonPolymiddleware';
23
import { type ChatLauncherButtonPolymiddleware } from '../chatLauncherButtonPolymiddleware';
34
import { type ErrorBoxPolymiddleware } from '../errorBoxPolymiddleware';
4-
import { type IconButtonPolymiddleware } from '../iconButtonPolymiddleware';
55
import { type PopoverPolymiddleware } from '../popoverPolymiddleware';
66

77
export type Polymiddleware =
88
| ActivityPolymiddleware
9+
| ButtonPolymiddleware
910
| ChatLauncherButtonPolymiddleware
1011
| ErrorBoxPolymiddleware
11-
| IconButtonPolymiddleware
1212
| PopoverPolymiddleware;

0 commit comments

Comments
 (0)