From becfc89b40a69d87586f831f162c5b1afc0ba463 Mon Sep 17 00:00:00 2001 From: William Wong Date: Tue, 15 Sep 2026 00:24:54 +0000 Subject: [PATCH 01/21] Add `webchat:callURL` action --- .../openUrlDialog/dialog.skip.html | 10 ++ .../adaptiveCard/openUrlDialog/simple.html | 129 ++++++++++++++++++ packages/api/src/StyleOptions.ts | 14 ++ packages/api/src/defaultStyleOptions.ts | 5 +- packages/api/src/hooks/Composer.tsx | 20 ++- .../api/src/types/CardActionMiddleware.ts | 8 +- .../AdaptiveCardsStyleOptions.ts | 14 -- .../Attachment/AdaptiveCardRenderer.tsx | 99 +++----------- .../src/adaptiveCards/defaultStyleOptions.ts | 2 - .../internal/useParseAdaptiveCardJSON.ts | 15 ++ .../CardAction/createCoreMiddleware.js | 92 ------------- .../CardAction/createCoreMiddleware.ts | 102 ++++++++++++++ .../CardAction/private/getScheme.ts | 26 ++++ .../types/external/DirectLineCardAction.ts | 5 +- .../internal/WebChatCallURLCardAction.ts | 20 +++ 15 files changed, 356 insertions(+), 205 deletions(-) create mode 100644 __tests__/html2/adaptiveCard/openUrlDialog/dialog.skip.html create mode 100644 __tests__/html2/adaptiveCard/openUrlDialog/simple.html delete mode 100644 packages/component/src/Middleware/CardAction/createCoreMiddleware.js create mode 100644 packages/component/src/Middleware/CardAction/createCoreMiddleware.ts create mode 100644 packages/component/src/Middleware/CardAction/private/getScheme.ts create mode 100644 packages/core/src/types/internal/WebChatCallURLCardAction.ts diff --git a/__tests__/html2/adaptiveCard/openUrlDialog/dialog.skip.html b/__tests__/html2/adaptiveCard/openUrlDialog/dialog.skip.html new file mode 100644 index 0000000000..acb2c2ee99 --- /dev/null +++ b/__tests__/html2/adaptiveCard/openUrlDialog/dialog.skip.html @@ -0,0 +1,10 @@ + + + + + + + diff --git a/__tests__/html2/adaptiveCard/openUrlDialog/simple.html b/__tests__/html2/adaptiveCard/openUrlDialog/simple.html new file mode 100644 index 0000000000..7b8518f41a --- /dev/null +++ b/__tests__/html2/adaptiveCard/openUrlDialog/simple.html @@ -0,0 +1,129 @@ + + + + + + +
+ + + + diff --git a/packages/api/src/StyleOptions.ts b/packages/api/src/StyleOptions.ts index 029ad8db68..280e6ae231 100644 --- a/packages/api/src/StyleOptions.ts +++ b/packages/api/src/StyleOptions.ts @@ -921,6 +921,20 @@ type StrictStyleOptions = { * @default 'auto' */ showMicrophoneButton: 'auto' | 'hide' | undefined; + + /** + * Adaptive Cards: sign-in action popup window height (in pixel) + * + * @default 640 + */ + callURLActionPopupWindowHeight: number | undefined; + + /** + * Adaptive Cards: sign-in action popup window width (in pixel) + * + * @default 480 + */ + callURLActionPopupWindowWidth: number | undefined; }; // StrictStyleOptions is only used internally in Web Chat and for simplifying our code: diff --git a/packages/api/src/defaultStyleOptions.ts b/packages/api/src/defaultStyleOptions.ts index d0239b5ed5..e17c03fcb8 100644 --- a/packages/api/src/defaultStyleOptions.ts +++ b/packages/api/src/defaultStyleOptions.ts @@ -307,7 +307,10 @@ const DEFAULT_OPTIONS: StrictStyleOptions = { sendBoxAttachmentBarMaxThumbnail: 3, // Speech-to-speech options - showMicrophoneButton: 'auto' + showMicrophoneButton: 'auto', + + callURLActionPopupWindowHeight: 640, + callURLActionPopupWindowWidth: 480 }; export default DEFAULT_OPTIONS; diff --git a/packages/api/src/hooks/Composer.tsx b/packages/api/src/hooks/Composer.tsx index 4b46fd4fd0..b928abc65d 100644 --- a/packages/api/src/hooks/Composer.tsx +++ b/packages/api/src/hooks/Composer.tsx @@ -45,7 +45,7 @@ import React, { useCallback, useEffect, useMemo, useRef, useState, type ReactNod import { Provider } from 'react-redux'; import updateIn from 'simple-update-in'; -import { type StyleOptions } from '../StyleOptions'; +import { type StrictStyleOptions, type StyleOptions } from '../StyleOptions'; import errorBoxTelemetryPolymiddleware from '../errorBox/errorBoxTelemetryPolymiddleware'; import PrecompiledGlobalize from '../external/PrecompiledGlobalize'; import usePonyfill from '../hooks/usePonyfill'; @@ -133,7 +133,8 @@ function createCardActionContext({ directLine, dispatch, markAllAsAcknowledged, - ponyfill + ponyfill, + styleOptions }: { cardActionMiddleware: readonly CardActionMiddleware[]; continuous: boolean; @@ -141,12 +142,13 @@ function createCardActionContext({ dispatch: (...args: unknown[]) => unknown; markAllAsAcknowledged: () => void; ponyfill: GlobalScopePonyfill; + styleOptions: StrictStyleOptions; }) { const runMiddleware = applyMiddleware( 'card action', ...cardActionMiddleware, createDefaultCardActionMiddleware() - )({ dispatch }); + )({ dispatch, styleOptions }); return { onCardAction: (cardAction, { target }: { target?: any } = {}) => { @@ -348,16 +350,10 @@ const ComposerCore = ({ directLine, dispatch, markAllAsAcknowledged, - ponyfill + ponyfill, + styleOptions }), - [ - cardActionMiddleware, - directLine, - dispatch, - markAllAsAcknowledged, - ponyfill, - styleOptions.speechRecognitionContinuous - ] + [cardActionMiddleware, directLine, dispatch, markAllAsAcknowledged, ponyfill, styleOptions] ); const patchedSelectVoice = useMemo( diff --git a/packages/api/src/types/CardActionMiddleware.ts b/packages/api/src/types/CardActionMiddleware.ts index a3d3610395..c740651752 100644 --- a/packages/api/src/types/CardActionMiddleware.ts +++ b/packages/api/src/types/CardActionMiddleware.ts @@ -1,11 +1,17 @@ import type { DirectLineCardAction } from 'botframework-webchat-core'; import FunctionMiddleware from './FunctionMiddleware'; +import type { StrictStyleOptions } from '../StyleOptions.js'; type PerformCardAction = (cardAction: DirectLineCardAction, event?: { target: EventTarget }) => void; type CardActionMiddleware = FunctionMiddleware< - [{ dispatch: (action: any) => void }], + [ + { + dispatch: (action: any) => void; + styleOptions: StrictStyleOptions; + } + ], [ { cardAction: DirectLineCardAction; diff --git a/packages/bundle/src/adaptiveCards/AdaptiveCardsStyleOptions.ts b/packages/bundle/src/adaptiveCards/AdaptiveCardsStyleOptions.ts index 49f8e0ffe8..6e83467a61 100644 --- a/packages/bundle/src/adaptiveCards/AdaptiveCardsStyleOptions.ts +++ b/packages/bundle/src/adaptiveCards/AdaptiveCardsStyleOptions.ts @@ -33,20 +33,6 @@ type StrictAdaptiveCardsStyleOptions = { * style; see issue #4327). */ richCardTitleOmitHeadingRole: boolean | undefined; - - /** - * Adaptive Cards: sign-in action popup window height (in pixel) - * - * @default 640 - */ - adaptiveCardSignInActionPopupWindowHeight: number | undefined; - - /** - * Adaptive Cards: sign-in action popup window width (in pixel) - * - * @default 480 - */ - adaptiveCardSignInActionPopupWindowWidth: number | undefined; }; type AdaptiveCardsStyleOptions = Partial; diff --git a/packages/bundle/src/adaptiveCards/Attachment/AdaptiveCardRenderer.tsx b/packages/bundle/src/adaptiveCards/Attachment/AdaptiveCardRenderer.tsx index bf8b3bd0e8..4ea4181c34 100644 --- a/packages/bundle/src/adaptiveCards/Attachment/AdaptiveCardRenderer.tsx +++ b/packages/bundle/src/adaptiveCards/Attachment/AdaptiveCardRenderer.tsx @@ -17,21 +17,7 @@ import React, { type MouseEventHandler } from 'react'; import { useRefFrom } from 'use-ref-from'; -import { - any, - boolean, - check, - literal, - object, - optional, - pipe, - readonly, - safeParse, - string, - transform, - url, - type InferInput -} from 'valibot'; +import { any, boolean, object, optional, pipe, readonly, string, type InferInput } from 'valibot'; import useAdaptiveCardsHostConfig from '../hooks/useAdaptiveCardsHostConfig'; import useAdaptiveCardsPackage from '../hooks/useAdaptiveCardsPackage'; @@ -46,30 +32,6 @@ import { directLineCardActionSchema } from './private/directLineSchema'; import renderAdaptiveCard from './private/renderAdaptiveCard'; import styles from './AdaptiveCardRenderer.module.css'; -import useStyleOptions from '../../hooks/useStyleOptions'; -import normalizeStyleOptions from '../normalizeStyleOptions'; - -const microsoftTeamsSubActionSchema = object({ - msteams: object({}) -}); - -const microsoftTeamsSignInSubActionSchema = object({ - msteams: object({ - type: literal('signin', 'Sub-action type must be "signin"'), - value: pipe( - string('"value" must be a string'), - url('"value" must be an absolute URL'), - check(value => { - try { - return ['http:', 'https:'].includes(new URL(value).protocol); - } catch { - return false; - } - }, '"value" must have protocol of either "http:" or "https:"'), - transform(value => value as any) - ) - }) -}); const { useLocalizer, usePerformCardAction, useRenderMarkdownAsHTML, useScrollToEnd, useUIState } = hooks; @@ -93,9 +55,6 @@ function AdaptiveCardRenderer(props: AdaptiveCardRendererProps) { tapAction } = validateProps(adaptiveCardRendererPropsSchema, props); - const { adaptiveCardSignInActionPopupWindowHeight, adaptiveCardSignInActionPopupWindowWidth } = normalizeStyleOptions( - useStyleOptions()[0] - ); const [{ GlobalSettings, HostConfig }] = useAdaptiveCardsPackage(); const [adaptiveCardsHostConfig] = useAdaptiveCardsHostConfig(); const [uiState] = useUIState(); @@ -180,6 +139,15 @@ function AdaptiveCardRenderer(props: AdaptiveCardRendererProps) { type: 'openUrl', value }); + } else if (actionTypeName === 'Action.OpenUrlDialog') { + const { url: value } = action as OpenUrlAction; + + performCardAction({ + image, + title, + type: 'webchat:callURL', + value + }); } else if (actionTypeName === 'Action.Submit') { const { data } = action as SubmitAction as { data: string | BotFrameworkCardAction; @@ -196,39 +164,12 @@ function AdaptiveCardRenderer(props: AdaptiveCardRendererProps) { } else if (data.__isBotFrameworkCardAction) { performCardAction(data.cardAction); } else { - const parseMSTeamsSubActionResult = safeParse(microsoftTeamsSubActionSchema, data); - - if (parseMSTeamsSubActionResult.success) { - const parseMSTeamsSignInSubActionResult = safeParse(microsoftTeamsSignInSubActionSchema, data); - - if (parseMSTeamsSignInSubActionResult.success) { - const { value } = parseMSTeamsSignInSubActionResult.output.msteams; - - window.open( - value, - '_blank', - [ - ['height', adaptiveCardSignInActionPopupWindowHeight], - ['popup', ''], - ['width', adaptiveCardSignInActionPopupWindowWidth] - ] - .map(([key, value]) => (value ? [key, encodeURIComponent(value)].join('=') : key)) - .join(',') - ); - } else { - console.warn( - 'botframework-webchat: "Action.Submit/msteams" sub-action validation error.', - ...parseMSTeamsSignInSubActionResult.issues.map(({ message }) => message) - ); - } - } else { - performCardAction({ - image, - title, - type: 'postBack', - value: data - }); - } + performCardAction({ + image, + title, + type: 'postBack', + value: data + }); } } @@ -238,13 +179,7 @@ function AdaptiveCardRenderer(props: AdaptiveCardRendererProps) { console.error(action); } }, - [ - adaptiveCardSignInActionPopupWindowHeight, - adaptiveCardSignInActionPopupWindowWidth, - disabledRef, - performCardAction, - scrollToEnd - ] + [disabledRef, performCardAction, scrollToEnd] ); // For accessibility issue #1340, `tabindex="0"` must not be set for the root container if it is not interactive. diff --git a/packages/bundle/src/adaptiveCards/defaultStyleOptions.ts b/packages/bundle/src/adaptiveCards/defaultStyleOptions.ts index f5002a8a44..d3ff62377b 100644 --- a/packages/bundle/src/adaptiveCards/defaultStyleOptions.ts +++ b/packages/bundle/src/adaptiveCards/defaultStyleOptions.ts @@ -1,8 +1,6 @@ import { type AdaptiveCardsStyleOptions } from './AdaptiveCardsStyleOptions'; const ADAPTIVE_CARDS_DEFAULT_STYLE_OPTIONS: Required = { - adaptiveCardSignInActionPopupWindowHeight: 640, - adaptiveCardSignInActionPopupWindowWidth: 480, adaptiveCardsParserMaxVersion: undefined, cardEmphasisBackgroundColor: '#F9F9F9', cardPushButtonBackgroundColor: '#0063B1', diff --git a/packages/bundle/src/adaptiveCards/hooks/internal/useParseAdaptiveCardJSON.ts b/packages/bundle/src/adaptiveCards/hooks/internal/useParseAdaptiveCardJSON.ts index cb69a37f62..b269469d55 100644 --- a/packages/bundle/src/adaptiveCards/hooks/internal/useParseAdaptiveCardJSON.ts +++ b/packages/bundle/src/adaptiveCards/hooks/internal/useParseAdaptiveCardJSON.ts @@ -3,6 +3,7 @@ import { useCallback, useMemo } from 'react'; import useAdaptiveCardsPackage from '../useAdaptiveCardsPackage'; import useStyleOptions from '../../../hooks/useStyleOptions'; +import { OpenUrlAction } from 'adaptivecards'; const { useDirection } = hooks; @@ -52,6 +53,20 @@ export default function useParseAdaptiveCardJSON() { const errors = []; const serializationContext = new SerializationContext(maxVersion); + serializationContext.actionRegistry.register( + 'Action.OpenUrlDialog', + // TODO: Fix this + // @ts-expect-error + class OpenUrlDialogAction extends OpenUrlAction { + static readonly JsonTypeName: string = 'Action.OpenUrlDialog'; + + // eslint-disable-next-line class-methods-use-this + getJsonTypeName(): string { + return OpenUrlDialogAction.JsonTypeName; + } + } + ); + card.parse(content, serializationContext); const { eventCount } = serializationContext; diff --git a/packages/component/src/Middleware/CardAction/createCoreMiddleware.js b/packages/component/src/Middleware/CardAction/createCoreMiddleware.js deleted file mode 100644 index efc9315463..0000000000 --- a/packages/component/src/Middleware/CardAction/createCoreMiddleware.js +++ /dev/null @@ -1,92 +0,0 @@ -import { ie11 } from '../../Utils/detectBrowser'; - -// This code is adopted from sanitize-html/naughtyScheme. -// sanitize-html is a dependency of Web Chat but the naughtScheme function is neither exposed nor reusable. -// https://github.com/apostrophecms/sanitize-html/ -function getScheme(href) { - // Browsers ignore character codes of 32 (space) and below in a surprising - // number of situations. Start reading here: - // https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#Embedded_tab - - /* eslint-disable-next-line no-control-regex */ - href = href.replace(/[\x00-\x20]+/gu, ''); - - // Clobber any comments in URLs, which the browser might - // interpret inside an XML data island, allowing - // a javascript: URL to be snuck through - href = href.replace(//gu, ''); - - // Case insensitive so we don't get faked out by JAVASCRIPT #1 - const matches = href.match(/^([a-zA-Z]+):/u); - - if (!matches) { - // Protocol-relative URL or no scheme - return; - } - - return matches[1].toLowerCase(); -} - -const ALLOWED_SCHEMES = ['data', 'http', 'https', 'ftp', 'mailto', 'sip', 'tel']; - -export default function createDefaultCardActionMiddleware() { - return [ - () => - next => - (...args) => { - const [ - { - cardAction: { type, value }, - getSignInUrl - } - ] = args; - - switch (type) { - case 'call': - case 'downloadFile': - case 'openUrl': - case 'playAudio': - case 'playVideo': - case 'showImage': - if (ALLOWED_SCHEMES.includes(getScheme(value))) { - if (ie11) { - const newWindow = window.open(); - newWindow.opener = null; - newWindow.location = value; - } else { - window.open(value, '_blank', 'noopener noreferrer'); - } - } else { - console.warn('botframework-webchat: Cannot open URL with disallowed schemes.', value); - } - - break; - - case 'signin': { - /** - * @todo TODO: [P3] We should prime the URL into the OAuthCard directly, instead of calling getSessionId on-demand - * This is to eliminate the delay between window.open() and location.href call - */ - - (async function () { - const popup = window.open(); - const url = await getSignInUrl(); - - if (['http', 'https'].includes(getScheme(url))) { - popup.location.href = url; - } else { - console.warn('botframework-webchat: Cannot open URL with disallowed schemes.', url); - - popup.close(); - } - })(); - - break; - } - - default: - return next(...args); - } - } - ]; -} diff --git a/packages/component/src/Middleware/CardAction/createCoreMiddleware.ts b/packages/component/src/Middleware/CardAction/createCoreMiddleware.ts new file mode 100644 index 0000000000..23755f5454 --- /dev/null +++ b/packages/component/src/Middleware/CardAction/createCoreMiddleware.ts @@ -0,0 +1,102 @@ +import type { CardActionMiddleware } from 'botframework-webchat-api'; + +import getScheme from './private/getScheme.js'; +import { check, pipe, safeParse, string, transform, url } from 'valibot'; + +const ALLOWED_SCHEMES = ['data', 'http', 'https', 'ftp', 'mailto', 'sip', 'tel']; + +const callURLValueSchema = pipe( + string('"value" must be a string'), + url('"value" must be an absolute URL'), + check(value => { + try { + return ['http:', 'https:'].includes(new URL(value).protocol); + } catch { + return false; + } + }, '"value" must have protocol of either "http:" or "https:"'), + transform(value => value as any) +); + +// TODO: Pass styleOptions. +export default function createDefaultCardActionMiddleware(): readonly CardActionMiddleware[] { + return [ + ({ styleOptions }) => + next => + (...args) => { + const [ + { + cardAction: { type, value }, + getSignInUrl + } + ] = args; + + switch (type) { + case 'call': + case 'downloadFile': + case 'openUrl': + case 'playAudio': + case 'playVideo': + case 'showImage': + if (ALLOWED_SCHEMES.includes(getScheme(value))) { + window.open(value, '_blank', 'noopener noreferrer'); + } else { + console.warn('botframework-webchat: Cannot open URL with disallowed schemes.', value); + } + + break; + + // Currently, this is exposed as Adaptive Cards `Action.OpenUrlDialog` action. + case 'webchat:callURL': { + const callURLValueParseResult = safeParse(callURLValueSchema, value); + + if (callURLValueParseResult.success) { + window.open( + callURLValueParseResult.output, + '_blank', + [ + // Implicit allow opener/referer because we are calling into a dialog that can return result. + `height=${styleOptions.callURLActionPopupWindowHeight}`, + 'popup', + `width=${styleOptions.callURLActionPopupWindowWidth}` + ].join(' ') + ); + } else { + console.warn( + 'botframework-webchat: Cannot call invalid URL.', + value, + callURLValueParseResult.issues.map(({ message }) => message) + ); + } + + break; + } + + case 'signin': { + /** + * @todo TODO: [P3] We should prime the URL into the OAuthCard directly, instead of calling getSessionId on-demand + * This is to eliminate the delay between window.open() and location.href call + */ + + (async function () { + const popup = window.open(); + const url = await getSignInUrl(); + + if (['http', 'https'].includes(getScheme(url))) { + popup.location.href = url; + } else { + console.warn('botframework-webchat: Cannot open URL with disallowed schemes.', url); + + popup.close(); + } + })(); + + break; + } + + default: + return next(...args); + } + } + ]; +} diff --git a/packages/component/src/Middleware/CardAction/private/getScheme.ts b/packages/component/src/Middleware/CardAction/private/getScheme.ts new file mode 100644 index 0000000000..98d896e9d2 --- /dev/null +++ b/packages/component/src/Middleware/CardAction/private/getScheme.ts @@ -0,0 +1,26 @@ +// This code is adopted from sanitize-html/naughtyScheme. +// sanitize-html is a dependency of Web Chat but the naughtScheme function is neither exposed nor reusable. +// https://github.com/apostrophecms/sanitize-html/ +export default function getScheme(href: string): string | undefined { + // Browsers ignore character codes of 32 (space) and below in a surprising + // number of situations. Start reading here: + // https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#Embedded_tab + + /* eslint-disable-next-line no-control-regex */ + href = href.replace(/[\x00-\x20]+/gu, ''); + + // Clobber any comments in URLs, which the browser might + // interpret inside an XML data island, allowing + // a javascript: URL to be snuck through + href = href.replace(//gu, ''); + + // Case insensitive so we don't get faked out by JAVASCRIPT #1 + const matches = href.match(/^([a-zA-Z]+):/u); + + if (!matches) { + // Protocol-relative URL or no scheme + return; + } + + return matches[1].toLowerCase(); +} diff --git a/packages/core/src/types/external/DirectLineCardAction.ts b/packages/core/src/types/external/DirectLineCardAction.ts index 6d2e14a31c..39a6edbdc2 100644 --- a/packages/core/src/types/external/DirectLineCardAction.ts +++ b/packages/core/src/types/external/DirectLineCardAction.ts @@ -1,3 +1,5 @@ +import type { WebChatCallURLCardAction } from '../internal/WebChatCallURLCardAction.ts'; + type CardActionWithImageAndTitle = | { image: string } | { title: string } @@ -123,6 +125,7 @@ type DirectLineCardAction = | PlayVideoCardAction | PostBackCardAction | ShowImageCardAction - | SignInCardAction; + | SignInCardAction + | WebChatCallURLCardAction; export type { DirectLineCardAction }; diff --git a/packages/core/src/types/internal/WebChatCallURLCardAction.ts b/packages/core/src/types/internal/WebChatCallURLCardAction.ts new file mode 100644 index 0000000000..649c5b5996 --- /dev/null +++ b/packages/core/src/types/internal/WebChatCallURLCardAction.ts @@ -0,0 +1,20 @@ +type CardActionWithImageAndTitle = + | { image: string; title?: undefined } + | { image?: undefined; title: string } + | { + image: string; + title: string; + }; + +/** + * Web Chat-only `webchat:callUrl` action represents a hyperlink to be handled by the client. + * + * The hyperlink will be opened in a popup window to indicate its modality. + * The popup window can return a single value and it will be postback to the bot. + */ +type WebChatCallURLCardAction = CardActionWithImageAndTitle & { + type: 'webchat:callURL'; + value: string; +}; + +export { WebChatCallURLCardAction }; From 1a418308052f5c72fe3d21dc15e74b63440d22af Mon Sep 17 00:00:00 2001 From: William Wong Date: Tue, 15 Sep 2026 06:56:42 +0000 Subject: [PATCH 02/21] Add tests --- .../adaptiveCard/msteams/signInSubAction.html | 126 ------------------ .../msteams/signInSubAction.html.snap-1.png | Bin 7912 -> 0 bytes .../msteams/signInSubAction.skip.html | 10 -- .../msteams/unknownSubAction.html | 106 --------------- .../disallowedScheme.html} | 24 ++-- .../relativeURL.html} | 22 +-- .../adaptiveCard/openUrlDialog/simple.html | 4 +- .../openUrlDialog/simple.html.snap-1.png | Bin 0 -> 10075 bytes .../size.html} | 27 ++-- .../CardAction/createCoreMiddleware.ts | 4 +- 10 files changed, 46 insertions(+), 277 deletions(-) delete mode 100644 __tests__/html2/adaptiveCard/msteams/signInSubAction.html delete mode 100644 __tests__/html2/adaptiveCard/msteams/signInSubAction.html.snap-1.png delete mode 100644 __tests__/html2/adaptiveCard/msteams/signInSubAction.skip.html delete mode 100644 __tests__/html2/adaptiveCard/msteams/unknownSubAction.html rename __tests__/html2/adaptiveCard/{msteams/signInSubAction.disallowedScheme.html => openUrlDialog/disallowedScheme.html} (83%) rename __tests__/html2/adaptiveCard/{msteams/signInSubAction.relativeURL.html => openUrlDialog/relativeURL.html} (85%) create mode 100644 __tests__/html2/adaptiveCard/openUrlDialog/simple.html.snap-1.png rename __tests__/html2/adaptiveCard/{msteams/signInSubAction.size.html => openUrlDialog/size.html} (82%) diff --git a/__tests__/html2/adaptiveCard/msteams/signInSubAction.html b/__tests__/html2/adaptiveCard/msteams/signInSubAction.html deleted file mode 100644 index 2d64094d2a..0000000000 --- a/__tests__/html2/adaptiveCard/msteams/signInSubAction.html +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - -
- - - - diff --git a/__tests__/html2/adaptiveCard/msteams/signInSubAction.html.snap-1.png b/__tests__/html2/adaptiveCard/msteams/signInSubAction.html.snap-1.png deleted file mode 100644 index 7862e7e63a4512fb2cb8ac452e60318c870b0a2a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7912 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 diff --git a/__tests__/html2/adaptiveCard/msteams/signInSubAction.skip.html b/__tests__/html2/adaptiveCard/msteams/signInSubAction.skip.html deleted file mode 100644 index acb2c2ee99..0000000000 --- a/__tests__/html2/adaptiveCard/msteams/signInSubAction.skip.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - diff --git a/__tests__/html2/adaptiveCard/msteams/unknownSubAction.html b/__tests__/html2/adaptiveCard/msteams/unknownSubAction.html deleted file mode 100644 index 1ab4e2dcce..0000000000 --- a/__tests__/html2/adaptiveCard/msteams/unknownSubAction.html +++ /dev/null @@ -1,106 +0,0 @@ - - - - - - -
- - - - diff --git a/__tests__/html2/adaptiveCard/msteams/signInSubAction.disallowedScheme.html b/__tests__/html2/adaptiveCard/openUrlDialog/disallowedScheme.html similarity index 83% rename from __tests__/html2/adaptiveCard/msteams/signInSubAction.disallowedScheme.html rename to __tests__/html2/adaptiveCard/openUrlDialog/disallowedScheme.html index f685f79214..36604109fb 100644 --- a/__tests__/html2/adaptiveCard/msteams/signInSubAction.disallowedScheme.html +++ b/__tests__/html2/adaptiveCard/openUrlDialog/disallowedScheme.html @@ -52,15 +52,18 @@ { actions: [ { - title: 'Sign in', - type: 'Action.Submit', - data: { - msteams: { - type: 'signin', - value: 'mailto:johndoe@microsoft.com' - } + type: 'Action.OpenUrlDialog', + dialogHeight: 'medium', + dialogWidth: 'medium', + title: 'Sign in (Action.OpenUrlDialog)', + url: 'mailto:johndoe@microsoft.com', + + fallback: { + type: 'Action.OpenUrl', + title: 'Sign in (Action.OpenUrl)', + url: 'mailto:johndoe@microsoft.com' } - } + }, ], type: 'ActionSet' } @@ -101,14 +104,15 @@ }; // WHEN: The "Sign in" button is clicked. - await host.click(document.querySelector('[aria-label="Sign in"]')); + await host.click(document.querySelector('[aria-label="Sign in (Action.OpenUrlDialog)"]')); // THEN: Should not call `window.open`. expect(windowOpenCalled).toBe(false); // THEN: Should warn. expect(consoleWarnArgs).toEqual([ - 'botframework-webchat: "Action.Submit/msteams" sub-action validation error.', + 'botframework-webchat: Cannot call invalid URL.', + 'mailto:johndoe@microsoft.com', '"value" must have protocol of either "http:" or "https:"' ]); }); diff --git a/__tests__/html2/adaptiveCard/msteams/signInSubAction.relativeURL.html b/__tests__/html2/adaptiveCard/openUrlDialog/relativeURL.html similarity index 85% rename from __tests__/html2/adaptiveCard/msteams/signInSubAction.relativeURL.html rename to __tests__/html2/adaptiveCard/openUrlDialog/relativeURL.html index 3884b40105..819c287006 100644 --- a/__tests__/html2/adaptiveCard/msteams/signInSubAction.relativeURL.html +++ b/__tests__/html2/adaptiveCard/openUrlDialog/relativeURL.html @@ -52,13 +52,16 @@ { actions: [ { - title: 'Sign in', - type: 'Action.Submit', - data: { - msteams: { - type: 'signin', - value: '/index.html' - } + type: 'Action.OpenUrlDialog', + dialogHeight: 'medium', + dialogWidth: 'medium', + title: 'Sign in (Action.OpenUrlDialog)', + url: '/index.html', + + fallback: { + type: 'Action.OpenUrl', + title: 'Sign in (Action.OpenUrl)', + url: '/index.html' } } ], @@ -101,14 +104,15 @@ }; // WHEN: The "Sign in" button is clicked. - await host.click(document.querySelector('[aria-label="Sign in"]')); + await host.click(document.querySelector('[aria-label="Sign in (Action.OpenUrlDialog)"]')); // THEN: Should not call `window.open`. expect(windowOpenCalled).toBe(false); // THEN: Should warn. expect(consoleWarnArgs).toEqual([ - 'botframework-webchat: "Action.Submit/msteams" sub-action validation error.', + 'botframework-webchat: Cannot call invalid URL.', + '/index.html', '"value" must be an absolute URL', '"value" must have protocol of either "http:" or "https:"' ]); diff --git a/__tests__/html2/adaptiveCard/openUrlDialog/simple.html b/__tests__/html2/adaptiveCard/openUrlDialog/simple.html index 7b8518f41a..4c11ce81c0 100644 --- a/__tests__/html2/adaptiveCard/openUrlDialog/simple.html +++ b/__tests__/html2/adaptiveCard/openUrlDialog/simple.html @@ -56,12 +56,12 @@ dialogHeight: 'medium', dialogWidth: 'medium', title: 'Sign in (Action.OpenUrlDialog)', - url: new URL('dialog.skip.html', location).toString(), + url: new URL('dialog.skip.html', location).toString(), fallback: { type: 'Action.OpenUrl', title: 'Sign in (Action.OpenUrl)', - url: new URL('dialog.skip.html', location).toString() + url: new URL('dialog.skip.html', location).toString() } }, ], diff --git a/__tests__/html2/adaptiveCard/openUrlDialog/simple.html.snap-1.png b/__tests__/html2/adaptiveCard/openUrlDialog/simple.html.snap-1.png new file mode 100644 index 0000000000000000000000000000000000000000..c3f8d61b47c29566f7ccba62ffdad8275af0ad07 GIT binary patch literal 10075 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 U>X_sLdqA3Mx=*SfzkL5c0Av;4G5`Po literal 0 HcmV?d00001 diff --git a/__tests__/html2/adaptiveCard/msteams/signInSubAction.size.html b/__tests__/html2/adaptiveCard/openUrlDialog/size.html similarity index 82% rename from __tests__/html2/adaptiveCard/msteams/signInSubAction.size.html rename to __tests__/html2/adaptiveCard/openUrlDialog/size.html index 6941a90178..48476a5ce3 100644 --- a/__tests__/html2/adaptiveCard/msteams/signInSubAction.size.html +++ b/__tests__/html2/adaptiveCard/openUrlDialog/size.html @@ -32,8 +32,8 @@ const { directLine, store } = createDirectLineEmulator(); const styleOptions = { - adaptiveCardSignInActionPopupWindowHeight: 480, - adaptiveCardSignInActionPopupWindowWidth: 360 + callURLActionPopupWindowHeight: 480, + callURLActionPopupWindowWidth: 360 }; renderWebChat({ directLine, store, styleOptions }, document.getElementById('webchat')); @@ -56,13 +56,16 @@ { actions: [ { - title: 'Sign in', - type: 'Action.Submit', - data: { - msteams: { - type: 'signin', - value: new URL('signInSubAction.skip.html', location) - } + type: 'Action.OpenUrlDialog', + dialogHeight: 'medium', + dialogWidth: 'medium', + title: 'Sign in (Action.OpenUrlDialog)', + url: new URL('dialog.skip.html', location).toString(), + + fallback: { + type: 'Action.OpenUrl', + title: 'Sign in (Action.OpenUrl)', + url: new URL('dialog.skip.html', location).toString() } } ], @@ -96,7 +99,7 @@ // Note: popup.addEventListener('close') does not dispatch. const interval = setInterval(() => { - if (popup.closed) { + if (popup?.closed) { clearInterval(interval); popupClosedDeferred.resolve(); @@ -107,7 +110,7 @@ }; // WHEN: The "Sign in" button is clicked. - await host.click(document.querySelector('[aria-label="Sign in"]')); + await host.click(document.querySelector('[aria-label="Sign in (Action.OpenUrlDialog)"]')); // THEN: Popup window should close itself. await Promise.race([ @@ -119,7 +122,7 @@ ]); expect(windowOpenArgs).toEqual([ - expect.stringContaining('signInSubAction.skip.html'), + expect.stringContaining('dialog.skip.html'), '_blank', 'height=480,popup,width=360' ]); diff --git a/packages/component/src/Middleware/CardAction/createCoreMiddleware.ts b/packages/component/src/Middleware/CardAction/createCoreMiddleware.ts index 23755f5454..de3e6cdb49 100644 --- a/packages/component/src/Middleware/CardAction/createCoreMiddleware.ts +++ b/packages/component/src/Middleware/CardAction/createCoreMiddleware.ts @@ -59,13 +59,13 @@ export default function createDefaultCardActionMiddleware(): readonly CardAction `height=${styleOptions.callURLActionPopupWindowHeight}`, 'popup', `width=${styleOptions.callURLActionPopupWindowWidth}` - ].join(' ') + ].join(',') ); } else { console.warn( 'botframework-webchat: Cannot call invalid URL.', value, - callURLValueParseResult.issues.map(({ message }) => message) + ...callURLValueParseResult.issues.map(({ message }) => message) ); } From 3da480a4c0d068606a5d723d69bd8075680a0d1f Mon Sep 17 00:00:00 2001 From: William Wong Date: Tue, 15 Sep 2026 07:14:36 +0000 Subject: [PATCH 03/21] Upgrade to naughtyHref --- .../CardAction/private/cleanHref.ts | 34 +++++++++++++++++++ .../CardAction/private/getScheme.ts | 19 ++--------- 2 files changed, 37 insertions(+), 16 deletions(-) create mode 100644 packages/component/src/Middleware/CardAction/private/cleanHref.ts diff --git a/packages/component/src/Middleware/CardAction/private/cleanHref.ts b/packages/component/src/Middleware/CardAction/private/cleanHref.ts new file mode 100644 index 0000000000..6c20cd363c --- /dev/null +++ b/packages/component/src/Middleware/CardAction/private/cleanHref.ts @@ -0,0 +1,34 @@ +// This code is adopted from sanitize-html/naughtyScheme. +// sanitize-html is a dependency of Web Chat but the naughtScheme function is neither exposed nor reusable. +// https://github.com/apostrophecms/sanitize-html/ + +// Strip characters browsers ignore inside URLs (control chars and +// embedded HTML comments) that are commonly used to sneak XSS +// payloads past simple scheme checks. +export default function cleanHref(href: string): string { + // Browsers ignore character codes of 32 (space) and below in a surprising + // number of situations. Start reading here: + // https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#Embedded_tab + // eslint-disable-next-line no-control-regex, require-unicode-regexp + href = href.replace(/[\x00-\x20]+/g, ''); + // Clobber any comments in URLs, which the browser might + // interpret inside an XML data island, allowing + // a javascript: URL to be snuck through + // eslint-disable-next-line no-constant-condition + while (true) { + const firstIndex = href.indexOf('', firstIndex + 4); + // eslint-disable-next-line no-magic-numbers + if (lastIndex === -1) { + break; + } + // eslint-disable-next-line no-magic-numbers + href = href.substring(0, firstIndex) + href.substring(lastIndex + 3); + } + return href; +} diff --git a/packages/component/src/Middleware/CardAction/private/getScheme.ts b/packages/component/src/Middleware/CardAction/private/getScheme.ts index 98d896e9d2..6f6072462b 100644 --- a/packages/component/src/Middleware/CardAction/private/getScheme.ts +++ b/packages/component/src/Middleware/CardAction/private/getScheme.ts @@ -1,21 +1,8 @@ -// This code is adopted from sanitize-html/naughtyScheme. -// sanitize-html is a dependency of Web Chat but the naughtScheme function is neither exposed nor reusable. -// https://github.com/apostrophecms/sanitize-html/ -export default function getScheme(href: string): string | undefined { - // Browsers ignore character codes of 32 (space) and below in a surprising - // number of situations. Start reading here: - // https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#Embedded_tab - - /* eslint-disable-next-line no-control-regex */ - href = href.replace(/[\x00-\x20]+/gu, ''); - - // Clobber any comments in URLs, which the browser might - // interpret inside an XML data island, allowing - // a javascript: URL to be snuck through - href = href.replace(//gu, ''); +import cleanHref from './cleanHref.js'; +export default function getScheme(href: string): string | undefined { // Case insensitive so we don't get faked out by JAVASCRIPT #1 - const matches = href.match(/^([a-zA-Z]+):/u); + const matches = cleanHref(href).match(/^([a-zA-Z]+):/u); if (!matches) { // Protocol-relative URL or no scheme From a391e2af5f6e1d9baba2ba03211c3e78c9161380 Mon Sep 17 00:00:00 2001 From: William Wong Date: Tue, 15 Sep 2026 07:14:39 +0000 Subject: [PATCH 04/21] Add entry --- CHANGELOG.md | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fe6e929f59..f7b34bd37f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,11 +22,16 @@ Legends: ### Added - Added `styleOptions.richCardTitleOmitHeadingRole` (default `false`) to opt out of `style: 'heading'` on rich card titles, in PR [#5839](https://github.com/microsoft/BotFramework-WebChat/pull/5839), by [@cjennison](https://github.com/cjennison) -- Added support of Adaptive Cards `Action.Submit` action with `msteams/signin` sub-action to open sign-in link in a popup window, in PR [#5860](https://github.com/microsoft/BotFramework-WebChat/pull/5860), by [@compulim](https://github.com/compulim) - - Added `styleOptions.adaptiveCardSignInActionPopupWindowHeight/Width` for sizing the sign-in popup window - - Link to [Adaptive Cards spec](https://adaptivecards.microsoft.com/?topic=SigninSubmitActionData) - - Refer to [this test](./__tests__/html2/adaptiveCard/signInAction.html) for the reference payload - - Note: this implementation is based on observation of how Microsoft Teams behave and could deviate from their official implementation +- ~Added support of Adaptive Cards `Action.Submit` action with `msteams/signin` sub-action to open sign-in link in a popup window, in PR [#5860](https://github.com/microsoft/BotFramework-WebChat/pull/5860), by [@compulim](https://github.com/compulim)~ + - ~Added `styleOptions.adaptiveCardSignInActionPopupWindowHeight/Width` for sizing the sign-in popup window~ + - ~Link to [Adaptive Cards spec](https://adaptivecards.microsoft.com/?topic=SigninSubmitActionData)~ + - ~Refer to [this test](./__tests__/html2/adaptiveCard/signInAction.html) for the reference payload~ + - ~Note: this implementation is based on observation of how Microsoft Teams behave and could deviate from their official implementation~ + - Obsoleted in favor of PR [#5862](https://github.com/microsoft/BotFramework-WebChat/pull/5862) +- Added card action `webchat:callURL` and [Adaptive Card action `Action.OpenUrlDialog`](https://adaptivecards.microsoft.com/?topic=Action.OpenUrlDialog), in PR [#5862](https://github.com/microsoft/BotFramework-WebChat/pull/5862), by [@compulim](https://github.com/compulim) + - Added `styleOptions.callURLActionPopupWindowHeight/Width` for sizing the popup window + - Refer to [this test](./__tests__/html2/adaptiveCard/openUrlDialog/simple.html) for the reference payload + - Note: the Adaptive Card implementation is based on observation of how other apps behave and could deviate from their official implementation ### Fixed From 66c151b697880789404edf74f3b531344a30c005 Mon Sep 17 00:00:00 2001 From: William Wong Date: Tue, 15 Sep 2026 07:28:49 +0000 Subject: [PATCH 05/21] Add TODO --- .../component/src/Middleware/CardAction/private/cleanHref.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/component/src/Middleware/CardAction/private/cleanHref.ts b/packages/component/src/Middleware/CardAction/private/cleanHref.ts index 6c20cd363c..e09b4c1940 100644 --- a/packages/component/src/Middleware/CardAction/private/cleanHref.ts +++ b/packages/component/src/Middleware/CardAction/private/cleanHref.ts @@ -1,3 +1,5 @@ +// TODO: [P1] Should move to [launder](https://www.npmjs.com/package/launder) package. + // This code is adopted from sanitize-html/naughtyScheme. // sanitize-html is a dependency of Web Chat but the naughtScheme function is neither exposed nor reusable. // https://github.com/apostrophecms/sanitize-html/ From 8148b139b4a39f1962132d7fee3d19e389e2c72e Mon Sep 17 00:00:00 2001 From: William Wong Date: Tue, 15 Sep 2026 07:30:23 +0000 Subject: [PATCH 06/21] Remove dialogHeight/dialogWidth --- .../html2/adaptiveCard/openUrlDialog/disallowedScheme.html | 6 ++---- __tests__/html2/adaptiveCard/openUrlDialog/relativeURL.html | 6 ++---- __tests__/html2/adaptiveCard/openUrlDialog/simple.html | 6 ++---- __tests__/html2/adaptiveCard/openUrlDialog/size.html | 6 ++---- 4 files changed, 8 insertions(+), 16 deletions(-) diff --git a/__tests__/html2/adaptiveCard/openUrlDialog/disallowedScheme.html b/__tests__/html2/adaptiveCard/openUrlDialog/disallowedScheme.html index 36604109fb..c159e60278 100644 --- a/__tests__/html2/adaptiveCard/openUrlDialog/disallowedScheme.html +++ b/__tests__/html2/adaptiveCard/openUrlDialog/disallowedScheme.html @@ -52,15 +52,13 @@ { actions: [ { - type: 'Action.OpenUrlDialog', - dialogHeight: 'medium', - dialogWidth: 'medium', title: 'Sign in (Action.OpenUrlDialog)', + type: 'Action.OpenUrlDialog', url: 'mailto:johndoe@microsoft.com', fallback: { - type: 'Action.OpenUrl', title: 'Sign in (Action.OpenUrl)', + type: 'Action.OpenUrl', url: 'mailto:johndoe@microsoft.com' } }, diff --git a/__tests__/html2/adaptiveCard/openUrlDialog/relativeURL.html b/__tests__/html2/adaptiveCard/openUrlDialog/relativeURL.html index 819c287006..c90d8e07e2 100644 --- a/__tests__/html2/adaptiveCard/openUrlDialog/relativeURL.html +++ b/__tests__/html2/adaptiveCard/openUrlDialog/relativeURL.html @@ -52,15 +52,13 @@ { actions: [ { - type: 'Action.OpenUrlDialog', - dialogHeight: 'medium', - dialogWidth: 'medium', title: 'Sign in (Action.OpenUrlDialog)', + type: 'Action.OpenUrlDialog', url: '/index.html', fallback: { - type: 'Action.OpenUrl', title: 'Sign in (Action.OpenUrl)', + type: 'Action.OpenUrl', url: '/index.html' } } diff --git a/__tests__/html2/adaptiveCard/openUrlDialog/simple.html b/__tests__/html2/adaptiveCard/openUrlDialog/simple.html index 4c11ce81c0..82151781b8 100644 --- a/__tests__/html2/adaptiveCard/openUrlDialog/simple.html +++ b/__tests__/html2/adaptiveCard/openUrlDialog/simple.html @@ -52,15 +52,13 @@ { actions: [ { - type: 'Action.OpenUrlDialog', - dialogHeight: 'medium', - dialogWidth: 'medium', title: 'Sign in (Action.OpenUrlDialog)', + type: 'Action.OpenUrlDialog', url: new URL('dialog.skip.html', location).toString(), fallback: { - type: 'Action.OpenUrl', title: 'Sign in (Action.OpenUrl)', + type: 'Action.OpenUrl', url: new URL('dialog.skip.html', location).toString() } }, diff --git a/__tests__/html2/adaptiveCard/openUrlDialog/size.html b/__tests__/html2/adaptiveCard/openUrlDialog/size.html index 48476a5ce3..ace527e222 100644 --- a/__tests__/html2/adaptiveCard/openUrlDialog/size.html +++ b/__tests__/html2/adaptiveCard/openUrlDialog/size.html @@ -56,15 +56,13 @@ { actions: [ { - type: 'Action.OpenUrlDialog', - dialogHeight: 'medium', - dialogWidth: 'medium', title: 'Sign in (Action.OpenUrlDialog)', + type: 'Action.OpenUrlDialog', url: new URL('dialog.skip.html', location).toString(), fallback: { - type: 'Action.OpenUrl', title: 'Sign in (Action.OpenUrl)', + type: 'Action.OpenUrl', url: new URL('dialog.skip.html', location).toString() } } From a1b4f40d631f800d3d3c473dca9e83951e299a51 Mon Sep 17 00:00:00 2001 From: William Wong Date: Tue, 15 Sep 2026 07:38:15 +0000 Subject: [PATCH 07/21] Add license --- .../src/Middleware/CardAction/private/cleanHref.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/packages/component/src/Middleware/CardAction/private/cleanHref.ts b/packages/component/src/Middleware/CardAction/private/cleanHref.ts index e09b4c1940..8d5bfbdad7 100644 --- a/packages/component/src/Middleware/CardAction/private/cleanHref.ts +++ b/packages/component/src/Middleware/CardAction/private/cleanHref.ts @@ -1,6 +1,18 @@ // TODO: [P1] Should move to [launder](https://www.npmjs.com/package/launder) package. -// This code is adopted from sanitize-html/naughtyScheme. +/*! + * (launder@1.7.1 is MIT but does not have license file, we are using the license file from sanitize-html instead.) + * + * Copyright (c) 2013, 2014, 2015 P'unk Avenue LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +// This code is adopted from sanitize-html/launder. // sanitize-html is a dependency of Web Chat but the naughtScheme function is neither exposed nor reusable. // https://github.com/apostrophecms/sanitize-html/ From 61fdcfa06ccb366b806b638dc096669f8a0fa128 Mon Sep 17 00:00:00 2001 From: William Wong Date: Tue, 15 Sep 2026 07:38:23 +0000 Subject: [PATCH 08/21] Add comment --- .../adaptiveCards/hooks/internal/useParseAdaptiveCardJSON.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/bundle/src/adaptiveCards/hooks/internal/useParseAdaptiveCardJSON.ts b/packages/bundle/src/adaptiveCards/hooks/internal/useParseAdaptiveCardJSON.ts index b269469d55..b2b8a6bc29 100644 --- a/packages/bundle/src/adaptiveCards/hooks/internal/useParseAdaptiveCardJSON.ts +++ b/packages/bundle/src/adaptiveCards/hooks/internal/useParseAdaptiveCardJSON.ts @@ -55,7 +55,7 @@ export default function useParseAdaptiveCardJSON() { serializationContext.actionRegistry.register( 'Action.OpenUrlDialog', - // TODO: Fix this + // Adaptive Cards own `OpenUrlAction.JsonTypeName` was not made extensible. // @ts-expect-error class OpenUrlDialogAction extends OpenUrlAction { static readonly JsonTypeName: string = 'Action.OpenUrlDialog'; From ddd6fb153a405590a5a870cc392a0328e627279f Mon Sep 17 00:00:00 2001 From: William Wong Date: Tue, 15 Sep 2026 07:41:01 +0000 Subject: [PATCH 09/21] Sort --- .../adaptiveCards/hooks/internal/useParseAdaptiveCardJSON.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/bundle/src/adaptiveCards/hooks/internal/useParseAdaptiveCardJSON.ts b/packages/bundle/src/adaptiveCards/hooks/internal/useParseAdaptiveCardJSON.ts index b2b8a6bc29..b5659443de 100644 --- a/packages/bundle/src/adaptiveCards/hooks/internal/useParseAdaptiveCardJSON.ts +++ b/packages/bundle/src/adaptiveCards/hooks/internal/useParseAdaptiveCardJSON.ts @@ -1,9 +1,9 @@ +import { OpenUrlAction } from 'adaptivecards'; import { hooks } from 'botframework-webchat-component'; import { useCallback, useMemo } from 'react'; import useAdaptiveCardsPackage from '../useAdaptiveCardsPackage'; import useStyleOptions from '../../../hooks/useStyleOptions'; -import { OpenUrlAction } from 'adaptivecards'; const { useDirection } = hooks; From 8d8b54dc88f4d1ac7d1023924ed8c01fcaf63f13 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 15 Sep 2026 07:55:01 +0000 Subject: [PATCH 10/21] Derive OpenUrlAction from configured Adaptive Cards package Co-authored-by: compulim <1622400+compulim@users.noreply.github.com> --- .../adaptiveCards/hooks/internal/useParseAdaptiveCardJSON.ts | 5 ++--- packages/bundle/src/types/AdaptiveCardsPackage.ts | 2 ++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/bundle/src/adaptiveCards/hooks/internal/useParseAdaptiveCardJSON.ts b/packages/bundle/src/adaptiveCards/hooks/internal/useParseAdaptiveCardJSON.ts index b5659443de..7dabc731d1 100644 --- a/packages/bundle/src/adaptiveCards/hooks/internal/useParseAdaptiveCardJSON.ts +++ b/packages/bundle/src/adaptiveCards/hooks/internal/useParseAdaptiveCardJSON.ts @@ -1,4 +1,3 @@ -import { OpenUrlAction } from 'adaptivecards'; import { hooks } from 'botframework-webchat-component'; import { useCallback, useMemo } from 'react'; @@ -29,7 +28,7 @@ export default function useParseAdaptiveCardJSON() { const [direction] = useDirection(); const [{ adaptiveCardsParserMaxVersion }] = useStyleOptions(); - const { AdaptiveCard, SerializationContext, Version } = adaptiveCardsPackage; + const { AdaptiveCard, OpenUrlAction, SerializationContext, Version } = adaptiveCardsPackage; const maxVersion = useMemo(() => { const maxVersion = Version.parse(adaptiveCardsParserMaxVersion, new SerializationContext()); @@ -85,6 +84,6 @@ export default function useParseAdaptiveCardJSON() { return card; }, - [AdaptiveCard, adaptiveCardsPackage, direction, maxVersion, SerializationContext] + [AdaptiveCard, adaptiveCardsPackage, direction, maxVersion, OpenUrlAction, SerializationContext] ); } diff --git a/packages/bundle/src/types/AdaptiveCardsPackage.ts b/packages/bundle/src/types/AdaptiveCardsPackage.ts index 6a19938960..a6913f870e 100644 --- a/packages/bundle/src/types/AdaptiveCardsPackage.ts +++ b/packages/bundle/src/types/AdaptiveCardsPackage.ts @@ -3,6 +3,7 @@ import { GlobalSettings, HorizontalAlignment, HostConfig, + OpenUrlAction, SerializationContext, TextSize, TextWeight, @@ -14,6 +15,7 @@ type AdaptiveCardsPackage = { GlobalSettings: typeof GlobalSettings; HorizontalAlignment: typeof HorizontalAlignment; HostConfig: typeof HostConfig; + OpenUrlAction: typeof OpenUrlAction; TextSize: typeof TextSize; TextWeight: typeof TextWeight; SerializationContext: typeof SerializationContext; From cc5466c89f75aa3dfb19372b050e67d3fd4e035d Mon Sep 17 00:00:00 2001 From: William Wong Date: Tue, 15 Sep 2026 00:55:16 -0700 Subject: [PATCH 11/21] Update CHANGELOG with recent feature changes Removed deprecated Adaptive Cards sign-in action support and added new card action for opening URLs. Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- CHANGELOG.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f7b34bd37f..f36fe162e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,11 +22,11 @@ Legends: ### Added - Added `styleOptions.richCardTitleOmitHeadingRole` (default `false`) to opt out of `style: 'heading'` on rich card titles, in PR [#5839](https://github.com/microsoft/BotFramework-WebChat/pull/5839), by [@cjennison](https://github.com/cjennison) -- ~Added support of Adaptive Cards `Action.Submit` action with `msteams/signin` sub-action to open sign-in link in a popup window, in PR [#5860](https://github.com/microsoft/BotFramework-WebChat/pull/5860), by [@compulim](https://github.com/compulim)~ - - ~Added `styleOptions.adaptiveCardSignInActionPopupWindowHeight/Width` for sizing the sign-in popup window~ - - ~Link to [Adaptive Cards spec](https://adaptivecards.microsoft.com/?topic=SigninSubmitActionData)~ - - ~Refer to [this test](./__tests__/html2/adaptiveCard/signInAction.html) for the reference payload~ - - ~Note: this implementation is based on observation of how Microsoft Teams behave and could deviate from their official implementation~ +- ~~Added support of Adaptive Cards `Action.Submit` action with `msteams/signin` sub-action to open sign-in link in a popup window, in PR [#5860](https://github.com/microsoft/BotFramework-WebChat/pull/5860), by [@compulim](https://github.com/compulim)~~ + - ~~Added `styleOptions.adaptiveCardSignInActionPopupWindowHeight/Width` for sizing the sign-in popup window~~ + - ~~Link to [Adaptive Cards spec](https://adaptivecards.microsoft.com/?topic=SigninSubmitActionData)~~ + - ~~Refer to [this test](./__tests__/html2/adaptiveCard/signInAction.html) for the reference payload~~ + - ~~Note: this implementation is based on observation of how Microsoft Teams behave and could deviate from their official implementation~~ - Obsoleted in favor of PR [#5862](https://github.com/microsoft/BotFramework-WebChat/pull/5862) - Added card action `webchat:callURL` and [Adaptive Card action `Action.OpenUrlDialog`](https://adaptivecards.microsoft.com/?topic=Action.OpenUrlDialog), in PR [#5862](https://github.com/microsoft/BotFramework-WebChat/pull/5862), by [@compulim](https://github.com/compulim) - Added `styleOptions.callURLActionPopupWindowHeight/Width` for sizing the popup window From f69dc54ccdc46e5f0b0d17b8aa163e2ed7978567 Mon Sep 17 00:00:00 2001 From: William Wong Date: Tue, 15 Sep 2026 00:55:41 -0700 Subject: [PATCH 12/21] Clarify callURL action popup window height and width Updated documentation for callURL action popup window dimensions. Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- packages/api/src/StyleOptions.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/api/src/StyleOptions.ts b/packages/api/src/StyleOptions.ts index 280e6ae231..e44a4f3a0d 100644 --- a/packages/api/src/StyleOptions.ts +++ b/packages/api/src/StyleOptions.ts @@ -923,14 +923,14 @@ type StrictStyleOptions = { showMicrophoneButton: 'auto' | 'hide' | undefined; /** - * Adaptive Cards: sign-in action popup window height (in pixel) + * `webchat:callURL` action popup window height (in pixels) * * @default 640 */ callURLActionPopupWindowHeight: number | undefined; /** - * Adaptive Cards: sign-in action popup window width (in pixel) + * `webchat:callURL` action popup window width (in pixels) * * @default 480 */ From 8187de45ad52979f51e192cacec278b20fbcf91c Mon Sep 17 00:00:00 2001 From: William Wong Date: Tue, 15 Sep 2026 00:58:15 -0700 Subject: [PATCH 13/21] Update documentation for WebChatCallURLCardAction Clarified handling of return values from popup in WebChatCallURLCardAction. Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- packages/core/src/types/internal/WebChatCallURLCardAction.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/types/internal/WebChatCallURLCardAction.ts b/packages/core/src/types/internal/WebChatCallURLCardAction.ts index 649c5b5996..20a86829ec 100644 --- a/packages/core/src/types/internal/WebChatCallURLCardAction.ts +++ b/packages/core/src/types/internal/WebChatCallURLCardAction.ts @@ -10,7 +10,7 @@ type CardActionWithImageAndTitle = * Web Chat-only `webchat:callUrl` action represents a hyperlink to be handled by the client. * * The hyperlink will be opened in a popup window to indicate its modality. - * The popup window can return a single value and it will be postback to the bot. + * Return values from the popup are not currently handled by Web Chat. */ type WebChatCallURLCardAction = CardActionWithImageAndTitle & { type: 'webchat:callURL'; From 53f3f35e7899b97458d83ddc223165f0f756c068 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 15 Sep 2026 08:05:03 +0000 Subject: [PATCH 14/21] Preserve webchat:callURL card action through standard card renderers Co-authored-by: compulim <1622400+compulim@users.noreply.github.com> --- .../cardAction/heroCard.webchatCallURL.html | 85 +++++++++++++++++++ .../Attachment/AdaptiveCardBuilder.ts | 1 + .../component/src/SendBox/SuggestedAction.tsx | 3 +- .../src/SendBox/SuggestedActions.tsx | 3 +- .../suggestedActions/SuggestedAction.tsx | 3 +- .../suggestedActions/SuggestedActions.tsx | 3 +- 6 files changed, 94 insertions(+), 4 deletions(-) create mode 100644 __tests__/html2/cardAction/heroCard.webchatCallURL.html diff --git a/__tests__/html2/cardAction/heroCard.webchatCallURL.html b/__tests__/html2/cardAction/heroCard.webchatCallURL.html new file mode 100644 index 0000000000..af67ea79ca --- /dev/null +++ b/__tests__/html2/cardAction/heroCard.webchatCallURL.html @@ -0,0 +1,85 @@ + + + + + + +
+ + + + diff --git a/packages/bundle/src/adaptiveCards/Attachment/AdaptiveCardBuilder.ts b/packages/bundle/src/adaptiveCards/Attachment/AdaptiveCardBuilder.ts index b48cadffed..62a62be3db 100644 --- a/packages/bundle/src/adaptiveCards/Attachment/AdaptiveCardBuilder.ts +++ b/packages/bundle/src/adaptiveCards/Attachment/AdaptiveCardBuilder.ts @@ -34,6 +34,7 @@ function addCardAction(cardAction: DirectLineCardAction, includesOAuthButtons?: type === 'imBack' || type === 'messageBack' || type === 'postBack' || + type === 'webchat:callURL' || (type === 'signin' && includesOAuthButtons) ) { action = new SubmitAction(); diff --git a/packages/component/src/SendBox/SuggestedAction.tsx b/packages/component/src/SendBox/SuggestedAction.tsx index cbb7410e21..b6c74fa8d9 100644 --- a/packages/component/src/SendBox/SuggestedAction.tsx +++ b/packages/component/src/SendBox/SuggestedAction.tsx @@ -47,7 +47,8 @@ const suggestedActionPropsSchema = pipe( literal('playVideo'), literal('postBack'), literal('showImage'), - literal('signin') + literal('signin'), + literal('webchat:callURL') ]) ), value: any() diff --git a/packages/component/src/SendBox/SuggestedActions.tsx b/packages/component/src/SendBox/SuggestedActions.tsx index 2af00d1007..5891246802 100644 --- a/packages/component/src/SendBox/SuggestedActions.tsx +++ b/packages/component/src/SendBox/SuggestedActions.tsx @@ -227,7 +227,8 @@ const SuggestedActions = ({ className }: SuggestedActionsProps) => { | 'playVideo' | 'postBack' | 'showImage' - | 'signin'; + | 'signin' + | 'webchat:callURL'; value?: { [key: string]: any } | string; }; diff --git a/packages/fluent-theme/src/components/suggestedActions/SuggestedAction.tsx b/packages/fluent-theme/src/components/suggestedActions/SuggestedAction.tsx index 038ed5506d..45935febd8 100644 --- a/packages/fluent-theme/src/components/suggestedActions/SuggestedAction.tsx +++ b/packages/fluent-theme/src/components/suggestedActions/SuggestedAction.tsx @@ -29,7 +29,8 @@ type SuggestedActionProps = Readonly<{ | 'playVideo' | 'postBack' | 'showImage' - | 'signin'; + | 'signin' + | 'webchat:callURL'; value?: any; }>; diff --git a/packages/fluent-theme/src/components/suggestedActions/SuggestedActions.tsx b/packages/fluent-theme/src/components/suggestedActions/SuggestedActions.tsx index fbec94e2e7..bd70604504 100644 --- a/packages/fluent-theme/src/components/suggestedActions/SuggestedActions.tsx +++ b/packages/fluent-theme/src/components/suggestedActions/SuggestedActions.tsx @@ -71,7 +71,8 @@ function SuggestedActions() { | 'playVideo' | 'postBack' | 'showImage' - | 'signin'; + | 'signin' + | 'webchat:callURL'; value?: { [key: string]: any } | string; }; From 1292388f5e4a89a09d4e2d6919dedd18972ea8ab Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 15 Sep 2026 08:09:04 +0000 Subject: [PATCH 15/21] Preserve webchat:callURL card action in Hero/Thumbnail cards and suggested actions Co-authored-by: compulim <1622400+compulim@users.noreply.github.com> --- .../webchatCallURL.heroCard.button.html | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 __tests__/html2/cardAction/webchatCallURL.heroCard.button.html diff --git a/__tests__/html2/cardAction/webchatCallURL.heroCard.button.html b/__tests__/html2/cardAction/webchatCallURL.heroCard.button.html new file mode 100644 index 0000000000..71332fbfdd --- /dev/null +++ b/__tests__/html2/cardAction/webchatCallURL.heroCard.button.html @@ -0,0 +1,88 @@ + + + + + + +
+ + + + From 87ac1c385953b851ea27f1bbfebe04ee33d07cfd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 15 Sep 2026 08:16:10 +0000 Subject: [PATCH 16/21] Retain and dispatch webchat:callURL in Hero/Thumbnail cards and suggested actions Co-authored-by: compulim <1622400+compulim@users.noreply.github.com> --- CHANGELOG.md | 1 + .../adaptiveCard/openUrlDialog/heroCard.html | 114 ++++++++++++++++++ .../Attachment/AdaptiveCardBuilder.ts | 16 +++ .../component/src/SendBox/SuggestedAction.tsx | 4 +- 4 files changed, 133 insertions(+), 2 deletions(-) create mode 100644 __tests__/html2/adaptiveCard/openUrlDialog/heroCard.html diff --git a/CHANGELOG.md b/CHANGELOG.md index f36fe162e3..4d00efb66f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,7 @@ Legends: - Added `styleOptions.callURLActionPopupWindowHeight/Width` for sizing the popup window - Refer to [this test](./__tests__/html2/adaptiveCard/openUrlDialog/simple.html) for the reference payload - Note: the Adaptive Card implementation is based on observation of how other apps behave and could deviate from their official implementation + - `webchat:callURL` card action is now retained and dispatched by Hero/Thumbnail cards and suggested actions, refer to [this test](./__tests__/html2/adaptiveCard/openUrlDialog/heroCard.html) for the reference payload ### Fixed diff --git a/__tests__/html2/adaptiveCard/openUrlDialog/heroCard.html b/__tests__/html2/adaptiveCard/openUrlDialog/heroCard.html new file mode 100644 index 0000000000..7bccc68427 --- /dev/null +++ b/__tests__/html2/adaptiveCard/openUrlDialog/heroCard.html @@ -0,0 +1,114 @@ + + + + + + +
+ + + + diff --git a/packages/bundle/src/adaptiveCards/Attachment/AdaptiveCardBuilder.ts b/packages/bundle/src/adaptiveCards/Attachment/AdaptiveCardBuilder.ts index 62a62be3db..2ec46e845a 100644 --- a/packages/bundle/src/adaptiveCards/Attachment/AdaptiveCardBuilder.ts +++ b/packages/bundle/src/adaptiveCards/Attachment/AdaptiveCardBuilder.ts @@ -26,6 +26,17 @@ export interface BotFrameworkCardAction { cardAction: DirectLineCardAction; } +// Adaptive Cards own `OpenUrlAction.JsonTypeName` was not made extensible. +// @ts-expect-error +class OpenUrlDialogAction extends OpenUrlAction { + static readonly JsonTypeName: string = 'Action.OpenUrlDialog'; + + // eslint-disable-next-line class-methods-use-this + getJsonTypeName(): string { + return OpenUrlDialogAction.JsonTypeName; + } +} + function addCardAction(cardAction: DirectLineCardAction, includesOAuthButtons?: boolean) { const { type } = cardAction; let action; @@ -45,6 +56,11 @@ function addCardAction(cardAction: DirectLineCardAction, includesOAuthButtons?: }; action.title = (cardAction as { title: string }).title; + } else if (type === 'webchat:callURL') { + action = new OpenUrlDialogAction(); + + action.title = (cardAction as { title: string }).title; + action.url = cardAction.value; } else { action = new OpenUrlAction(); diff --git a/packages/component/src/SendBox/SuggestedAction.tsx b/packages/component/src/SendBox/SuggestedAction.tsx index b6c74fa8d9..2e3fd6c6a8 100644 --- a/packages/component/src/SendBox/SuggestedAction.tsx +++ b/packages/component/src/SendBox/SuggestedAction.tsx @@ -99,8 +99,8 @@ function SuggestedAction(props: SuggestedActionProps) { // Instead, we should pass a "cardAction" props. performCardAction({ displayText, text, type, value } as DirectLineCardAction, { target }); - // Since "openUrl" action do not submit, the suggested action buttons do not hide after click. - type === 'openUrl' && setSuggestedActions([]); + // Since "openUrl" and "webchat:callURL" actions do not submit, the suggested action buttons do not hide after click. + (type === 'openUrl' || type === 'webchat:callURL') && setSuggestedActions([]); scrollToEnd(); })(); From 2d3f7863abb513e42eca37cbc7db540883c34146 Mon Sep 17 00:00:00 2001 From: William Wong Date: Tue, 15 Sep 2026 08:38:22 +0000 Subject: [PATCH 17/21] Add test for suggested actions --- .../adaptiveCard/openUrlDialog/heroCard.html | 8 +- .../openUrlDialog/suggestedActions.html | 111 ++++++++++++++++++ .../component/src/SendBox/SuggestedAction.tsx | 3 +- 3 files changed, 115 insertions(+), 7 deletions(-) create mode 100644 __tests__/html2/adaptiveCard/openUrlDialog/suggestedActions.html diff --git a/__tests__/html2/adaptiveCard/openUrlDialog/heroCard.html b/__tests__/html2/adaptiveCard/openUrlDialog/heroCard.html index 7bccc68427..7534010167 100644 --- a/__tests__/html2/adaptiveCard/openUrlDialog/heroCard.html +++ b/__tests__/html2/adaptiveCard/openUrlDialog/heroCard.html @@ -31,12 +31,8 @@ window.WebChat = { createStoreWithOptions }; const { directLine, store } = createDirectLineEmulator(); - const styleOptions = { - callURLActionPopupWindowHeight: 480, - callURLActionPopupWindowWidth: 360 - }; - renderWebChat({ directLine, store, styleOptions }, document.getElementById('webchat')); + renderWebChat({ directLine, store }, document.getElementById('webchat')); await pageConditions.uiConnected(); @@ -106,7 +102,7 @@ expect(windowOpenArgs).toEqual([ expect.stringContaining('dialog.skip.html'), '_blank', - 'height=480,popup,width=360' + 'height=640,popup,width=480' ]); }); diff --git a/__tests__/html2/adaptiveCard/openUrlDialog/suggestedActions.html b/__tests__/html2/adaptiveCard/openUrlDialog/suggestedActions.html new file mode 100644 index 0000000000..1c97971992 --- /dev/null +++ b/__tests__/html2/adaptiveCard/openUrlDialog/suggestedActions.html @@ -0,0 +1,111 @@ + + + + + + +
+ + + + diff --git a/packages/component/src/SendBox/SuggestedAction.tsx b/packages/component/src/SendBox/SuggestedAction.tsx index 2e3fd6c6a8..80f033b949 100644 --- a/packages/component/src/SendBox/SuggestedAction.tsx +++ b/packages/component/src/SendBox/SuggestedAction.tsx @@ -99,7 +99,8 @@ function SuggestedAction(props: SuggestedActionProps) { // Instead, we should pass a "cardAction" props. performCardAction({ displayText, text, type, value } as DirectLineCardAction, { target }); - // Since "openUrl" and "webchat:callURL" actions do not submit, the suggested action buttons do not hide after click. + // Since "openUrl" and "webchat:callURL" actions do not submit, the suggested action buttons do not automatically hide after click. + // We need to hide it manually. (type === 'openUrl' || type === 'webchat:callURL') && setSuggestedActions([]); scrollToEnd(); From 66ccaa907e0a7f997a4875bec584d39bee0937b0 Mon Sep 17 00:00:00 2001 From: William Wong Date: Tue, 15 Sep 2026 08:38:29 +0000 Subject: [PATCH 18/21] Comment --- packages/component/src/SendBox/SuggestedAction.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/component/src/SendBox/SuggestedAction.tsx b/packages/component/src/SendBox/SuggestedAction.tsx index 80f033b949..7fe0b2e48f 100644 --- a/packages/component/src/SendBox/SuggestedAction.tsx +++ b/packages/component/src/SendBox/SuggestedAction.tsx @@ -99,8 +99,8 @@ function SuggestedAction(props: SuggestedActionProps) { // Instead, we should pass a "cardAction" props. performCardAction({ displayText, text, type, value } as DirectLineCardAction, { target }); - // Since "openUrl" and "webchat:callURL" actions do not submit, the suggested action buttons do not automatically hide after click. - // We need to hide it manually. + // Since "openUrl" and "webchat:callURL" actions do not actually submit or send any activity, + // suggested action buttons will not hide automatically after click. We need to hide it manually. (type === 'openUrl' || type === 'webchat:callURL') && setSuggestedActions([]); scrollToEnd(); From eed1d7748dbffcd7b9babe49a4ce08a0e669cc28 Mon Sep 17 00:00:00 2001 From: William Wong Date: Tue, 15 Sep 2026 08:39:40 +0000 Subject: [PATCH 19/21] Update entry --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d00efb66f..dd69393c5a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,9 +30,9 @@ Legends: - Obsoleted in favor of PR [#5862](https://github.com/microsoft/BotFramework-WebChat/pull/5862) - Added card action `webchat:callURL` and [Adaptive Card action `Action.OpenUrlDialog`](https://adaptivecards.microsoft.com/?topic=Action.OpenUrlDialog), in PR [#5862](https://github.com/microsoft/BotFramework-WebChat/pull/5862), by [@compulim](https://github.com/compulim) - Added `styleOptions.callURLActionPopupWindowHeight/Width` for sizing the popup window - - Refer to [this test](./__tests__/html2/adaptiveCard/openUrlDialog/simple.html) for the reference payload + - Reference payload for Direct Line `webchat:callURL` card action can be found in [this test](./__tests__/html2/adaptiveCard/openUrlDialog/heroCard.html) + - Reference payload for Adaptive Card `Action.OpenUrlDialog` can be found in [this test](./__tests__/html2/adaptiveCard/openUrlDialog/simple.html) - Note: the Adaptive Card implementation is based on observation of how other apps behave and could deviate from their official implementation - - `webchat:callURL` card action is now retained and dispatched by Hero/Thumbnail cards and suggested actions, refer to [this test](./__tests__/html2/adaptiveCard/openUrlDialog/heroCard.html) for the reference payload ### Fixed From 3e51d60dab49fd8100e3a872c7bc976fef5ef8f1 Mon Sep 17 00:00:00 2001 From: William Wong Date: Tue, 15 Sep 2026 08:40:18 +0000 Subject: [PATCH 20/21] Fix comment --- .../html2/adaptiveCard/openUrlDialog/suggestedActions.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__tests__/html2/adaptiveCard/openUrlDialog/suggestedActions.html b/__tests__/html2/adaptiveCard/openUrlDialog/suggestedActions.html index 1c97971992..892b5cd01e 100644 --- a/__tests__/html2/adaptiveCard/openUrlDialog/suggestedActions.html +++ b/__tests__/html2/adaptiveCard/openUrlDialog/suggestedActions.html @@ -103,7 +103,7 @@ 'height=640,popup,width=480' ]); - // THEN: The suggested action button should not be hidden. + // THEN: The suggested action button should be hidden after click. expect(document.querySelector('[data-testid="suggested action button"]')).toBeNull(); }); From f3f8467cb2ca8b19fb51149c34548a495035490b Mon Sep 17 00:00:00 2001 From: William Wong Date: Tue, 15 Sep 2026 19:21:52 +0000 Subject: [PATCH 21/21] Fix if branch --- .../bundle/src/adaptiveCards/Attachment/AdaptiveCardBuilder.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/bundle/src/adaptiveCards/Attachment/AdaptiveCardBuilder.ts b/packages/bundle/src/adaptiveCards/Attachment/AdaptiveCardBuilder.ts index 2ec46e845a..50fb392e38 100644 --- a/packages/bundle/src/adaptiveCards/Attachment/AdaptiveCardBuilder.ts +++ b/packages/bundle/src/adaptiveCards/Attachment/AdaptiveCardBuilder.ts @@ -45,7 +45,6 @@ function addCardAction(cardAction: DirectLineCardAction, includesOAuthButtons?: type === 'imBack' || type === 'messageBack' || type === 'postBack' || - type === 'webchat:callURL' || (type === 'signin' && includesOAuthButtons) ) { action = new SubmitAction();