diff --git a/CHANGELOG.md b/CHANGELOG.md index 9205393585..fe6e929f59 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,12 +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 ### Fixed - Fixed an error when a failed activity is present when Web Chat mounts, resolving [#5812](https://github.com/microsoft/BotFramework-WebChat/issues/5812), in PR [#5848](https://github.com/microsoft/BotFramework-WebChat/pull/5848), by [@OEvgeny](https://github.com/OEvgeny) - ## [4.19.1] - 2026-06-09 ### Changed diff --git a/__tests__/html2/adaptiveCard/msteams/signInSubAction.disallowedScheme.html b/__tests__/html2/adaptiveCard/msteams/signInSubAction.disallowedScheme.html new file mode 100644 index 0000000000..f685f79214 --- /dev/null +++ b/__tests__/html2/adaptiveCard/msteams/signInSubAction.disallowedScheme.html @@ -0,0 +1,117 @@ + + + + + + +
+ + + + diff --git a/__tests__/html2/adaptiveCard/msteams/signInSubAction.html b/__tests__/html2/adaptiveCard/msteams/signInSubAction.html new file mode 100644 index 0000000000..2d64094d2a --- /dev/null +++ b/__tests__/html2/adaptiveCard/msteams/signInSubAction.html @@ -0,0 +1,126 @@ + + + + + + +
+ + + + diff --git a/__tests__/html2/adaptiveCard/msteams/signInSubAction.html.snap-1.png b/__tests__/html2/adaptiveCard/msteams/signInSubAction.html.snap-1.png new file mode 100644 index 0000000000..7862e7e63a Binary files /dev/null and b/__tests__/html2/adaptiveCard/msteams/signInSubAction.html.snap-1.png differ diff --git a/__tests__/html2/adaptiveCard/msteams/signInSubAction.relativeURL.html b/__tests__/html2/adaptiveCard/msteams/signInSubAction.relativeURL.html new file mode 100644 index 0000000000..3884b40105 --- /dev/null +++ b/__tests__/html2/adaptiveCard/msteams/signInSubAction.relativeURL.html @@ -0,0 +1,118 @@ + + + + + + +
+ + + + diff --git a/__tests__/html2/adaptiveCard/msteams/signInSubAction.size.html b/__tests__/html2/adaptiveCard/msteams/signInSubAction.size.html new file mode 100644 index 0000000000..6941a90178 --- /dev/null +++ b/__tests__/html2/adaptiveCard/msteams/signInSubAction.size.html @@ -0,0 +1,129 @@ + + + + + + +
+ + + + diff --git a/__tests__/html2/adaptiveCard/msteams/signInSubAction.skip.html b/__tests__/html2/adaptiveCard/msteams/signInSubAction.skip.html new file mode 100644 index 0000000000..acb2c2ee99 --- /dev/null +++ b/__tests__/html2/adaptiveCard/msteams/signInSubAction.skip.html @@ -0,0 +1,10 @@ + + + + + + + diff --git a/__tests__/html2/adaptiveCard/msteams/unknownSubAction.html b/__tests__/html2/adaptiveCard/msteams/unknownSubAction.html new file mode 100644 index 0000000000..1ab4e2dcce --- /dev/null +++ b/__tests__/html2/adaptiveCard/msteams/unknownSubAction.html @@ -0,0 +1,106 @@ + + + + + + +
+ + + + diff --git a/packages/bundle/src/adaptiveCards/AdaptiveCardsStyleOptions.ts b/packages/bundle/src/adaptiveCards/AdaptiveCardsStyleOptions.ts index 6e83467a61..49f8e0ffe8 100644 --- a/packages/bundle/src/adaptiveCards/AdaptiveCardsStyleOptions.ts +++ b/packages/bundle/src/adaptiveCards/AdaptiveCardsStyleOptions.ts @@ -33,6 +33,20 @@ 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 866c954677..bf8b3bd0e8 100644 --- a/packages/bundle/src/adaptiveCards/Attachment/AdaptiveCardRenderer.tsx +++ b/packages/bundle/src/adaptiveCards/Attachment/AdaptiveCardRenderer.tsx @@ -17,7 +17,21 @@ import React, { type MouseEventHandler } from 'react'; import { useRefFrom } from 'use-ref-from'; -import { any, boolean, object, optional, pipe, readonly, string, type InferInput } from 'valibot'; +import { + any, + boolean, + check, + literal, + object, + optional, + pipe, + readonly, + safeParse, + string, + transform, + url, + type InferInput +} from 'valibot'; import useAdaptiveCardsHostConfig from '../hooks/useAdaptiveCardsHostConfig'; import useAdaptiveCardsPackage from '../hooks/useAdaptiveCardsPackage'; @@ -32,6 +46,30 @@ 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; @@ -55,6 +93,9 @@ function AdaptiveCardRenderer(props: AdaptiveCardRendererProps) { tapAction } = validateProps(adaptiveCardRendererPropsSchema, props); + const { adaptiveCardSignInActionPopupWindowHeight, adaptiveCardSignInActionPopupWindowWidth } = normalizeStyleOptions( + useStyleOptions()[0] + ); const [{ GlobalSettings, HostConfig }] = useAdaptiveCardsPackage(); const [adaptiveCardsHostConfig] = useAdaptiveCardsHostConfig(); const [uiState] = useUIState(); @@ -155,12 +196,39 @@ function AdaptiveCardRenderer(props: AdaptiveCardRendererProps) { } else if (data.__isBotFrameworkCardAction) { performCardAction(data.cardAction); } else { - performCardAction({ - image, - title, - type: 'postBack', - value: data - }); + 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 + }); + } } } @@ -170,7 +238,13 @@ function AdaptiveCardRenderer(props: AdaptiveCardRendererProps) { console.error(action); } }, - [disabledRef, performCardAction, scrollToEnd] + [ + adaptiveCardSignInActionPopupWindowHeight, + adaptiveCardSignInActionPopupWindowWidth, + 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 d3ff62377b..f5002a8a44 100644 --- a/packages/bundle/src/adaptiveCards/defaultStyleOptions.ts +++ b/packages/bundle/src/adaptiveCards/defaultStyleOptions.ts @@ -1,6 +1,8 @@ import { type AdaptiveCardsStyleOptions } from './AdaptiveCardsStyleOptions'; const ADAPTIVE_CARDS_DEFAULT_STYLE_OPTIONS: Required = { + adaptiveCardSignInActionPopupWindowHeight: 640, + adaptiveCardSignInActionPopupWindowWidth: 480, adaptiveCardsParserMaxVersion: undefined, cardEmphasisBackgroundColor: '#F9F9F9', cardPushButtonBackgroundColor: '#0063B1',