diff --git a/msteams-platform/concepts/build-and-test/deep-link-application.md b/msteams-platform/concepts/build-and-test/deep-link-application.md index cc9f5d3ebc7..4e2e71af62e 100644 --- a/msteams-platform/concepts/build-and-test/deep-link-application.md +++ b/msteams-platform/concepts/build-and-test/deep-link-application.md @@ -4,7 +4,7 @@ description: Learn how to create deep links to an application and navigate using ms.topic: conceptual ms.author: vikasalmal ms.localizationpriority: high -ms.date: 03/05/2026 +ms.date: 04/17/2026 --- # Deep link to an application @@ -376,7 +376,7 @@ A dialog deep link is a serialization of the `TaskInfo` object with two other de --- -For the data types and allowable values for ``, ``, ``, ``, and ``, see [TaskInfo object](~/task-modules-and-cards/task-modules/invoking-task-modules.md#dialoginfo-object). +For the data types and allowable values for ``, ``, ``, ``, and ``, see [TaskInfo object](~/task-modules-and-cards/task-modules/invoking-task-modules.md#dialog-metadata). > [!TIP] > Encode the deep link URL when using the `card` parameter, for example, JavaScript [`encodeURI()` function](https://www.w3schools.com/jsref/jsref_encodeURI.asp). diff --git a/msteams-platform/get-started/glossary.md b/msteams-platform/get-started/glossary.md index 872bd4ac4cb..a8be368eed8 100644 --- a/msteams-platform/get-started/glossary.md +++ b/msteams-platform/get-started/glossary.md @@ -3,7 +3,7 @@ title: Teams Developer Documentation Glossary description: Learn about the glossary such as common terms, meanings, and definitions used in Microsoft Teams developer documentation. ms.localizationpriority: high ms.topic: reference -ms.date: 01/23/2025 +ms.date: 04/17/2026 --- # Glossary @@ -260,7 +260,7 @@ Common terms and definitions used in Microsoft Teams developer documentation. | [Tab](../tabs/what-are-tabs.md) | Tabs are client-aware webpages embedded in Microsoft Teams, Outlook, and Microsoft 365 that point to domains declared in app manifest. You can add it as part of a channel inside a team, group chat, or personal app for an individual user. | | [Tab chat](../tabs/how-to/conversational-tabs.md) | A type of tab that lets a user have a focused conversation experience in dynamic tabs. | | [Task modules](../task-modules-and-cards/what-are-task-modules.md) (referred as dialogs in TeamsJS v2.x)| A feature of Teams app to create modal pop-up for completing tasks, displaying videos, or dashboard.
**See also**: [Adaptive Card](#a), [Dialogs](#d) | -| [Task info](../task-modules-and-cards/task-modules/invoking-task-modules.md#dialoginfo-object) | The `TaskInfo` object contains the metadata for a dialogs (referred as task modules in TeamsJS v.1.0).| +| [Task info](../task-modules-and-cards/task-modules/invoking-task-modules.md#dialog-metadata) | The `TaskInfo` object contains the metadata for a dialog (referred as task modules in TeamsJS v.1.0).| | [Thread discussion](../tabs/design/tabs.md#thread-discussion) | A conversation posted on a channel or chat between users.
**See also** [Conversation](#c); [Channel](#c) | | [Teams](../overview.md) | Microsoft Teams is the ultimate message app for your organization. It's a workspace for real-time collaboration and communication, meetings, file and app sharing. | | [Teams SDK](/microsoftteams/platform/teams-ai-library/welcome) (formerly known as Teams AI library) | It provides a simplified SDK, support for Model Context Protocol (MCP), Agent-to-Agent communication (A2A), and streamlined tools to enable developers to build intelligent agents for Teams.
**See also**: [Custom engine agent](#c) | diff --git a/msteams-platform/resources/messaging-extension-v3/create-extensions.md b/msteams-platform/resources/messaging-extension-v3/create-extensions.md index c7377f0dfcb..17fc59b8758 100644 --- a/msteams-platform/resources/messaging-extension-v3/create-extensions.md +++ b/msteams-platform/resources/messaging-extension-v3/create-extensions.md @@ -3,7 +3,7 @@ title: Action-based Message Extensions description: Learn how to create and configure action-based message extensions for Microsoft Teams using Bot Framework SDK to allow users to trigger external services. ms.localizationpriority: medium ms.topic: how-to -ms.date: 04/02/2023 +ms.date: 04/17/2026 ms.owner: slamba --- # Initiate actions with message extensions @@ -241,7 +241,7 @@ When a user chooses a command with static parameters, Teams generates a form in In this method, your service can define a custom Adaptive Card to collect the user input. For this approach, set the `fetchTask` parameter to `true` in the manifest. If you set `fetchTask` to `true`, any static parameters defined for the command are ignored. -In this method, your service receives a `composeExtensions/fetchTask` event and responds with an Adaptive Card based [task module response](~/task-modules-and-cards/task-modules/invoking-task-modules.md#dialoginfo-object). Following is a sample response with an Adaptive Card: +In this method, your service receives a `composeExtensions/fetchTask` event and responds with an Adaptive Card based [task module response](~/task-modules-and-cards/task-modules/invoking-task-modules.md#dialog-metadata). Following is a sample response with an Adaptive Card: ```json { @@ -292,7 +292,7 @@ The bot can also respond with an auth/config response if the user needs to authe In this method, your service can show an ` - - - +app.message(/.*/i, async (context) => { + await context.send({ + type: 'message', + attachments: [createTaskModuleAdaptiveCard()], + }); +}); ``` -The following code provides an example of the CSS: - -```css -#embed-container iframe { - position: absolute; - top: 0; - left: 0; - width: 95%; - height: 95%; - padding-left: 20px; - padding-right: 20px; - padding-top: 10px; - padding-bottom: 10px; - border-style: none; -} +# [Python](#tab/python) + +```python +from microsoft_teams.api import MessageActivity, MessageActivityInput +from microsoft_teams.apps import ActivityContext +from microsoft_teams.cards import AdaptiveCard, TextBlock, TaskFetchAction + +@app.on_message +async def handle_message(context: ActivityContext[MessageActivity]) -> None: + adaptive_card = AdaptiveCard(version="1.4").with_body( + [TextBlock(text="Task Module Invocation from Adaptive Card", weight="Bolder", size="Large")] + ).with_actions( + [ + TaskFetchAction(value={"data": "AdaptiveCard"}).with_title("Adaptive Card"), + TaskFetchAction(value={"data": "CustomForm"}).with_title("Custom Form"), + TaskFetchAction(value={"data": "MultiStep"}).with_title("Multi-step Form"), + ] + ) + + message = MessageActivityInput().add_card(adaptive_card) + await context.send(message) ``` -### Example 2: PowerApp +--- + +## Handle the dialog open event + +When Teams sends a task fetch invoke, your app returns the dialog content. The content can be an Adaptive Card or a webpage URL. In C#, wrap the dialog metadata in a `ContinueTask` response. In TypeScript, return a `TaskModuleResponse` with `type: 'continue'`. In Python, return an `InvokeResponse` containing a `TaskModuleContinueResponse`. + +# [C#](#tab/csharp) + +```csharp +using System.Text.Json; +using Microsoft.Teams.Api.TaskModules; +using Microsoft.Teams.Cards; +using Microsoft.Teams.Common; -You can use the same approach to embed a PowerApp. As the height or width of any individual PowerApp is customizable, you can adjust the height, and width to achieve the desired presentation. +teamsApp.OnTaskFetch(async (context) => +{ + var activity = context.Activity; + var json = JsonSerializer.Deserialize(JsonSerializer.Serialize(activity)); + var data = json.GetProperty("value").GetProperty("data").GetProperty("data").GetString(); -:::image type="content" source="../../assets/images/task-module/powerapp-example.png" alt-text="powerapp"::: + TaskInfo taskInfo; -The following code provides an example of the HTML for PowerApp: + if (data == "CustomForm") + { + taskInfo = new TaskInfo + { + Title = "Custom Form", + Width = new Union(510), + Height = new Union(450), + Url = $"{botEndpoint}/customform", + FallbackUrl = $"{botEndpoint}/customform" + }; + } + else if (data == "MultiStep") + { + var step1Card = new AdaptiveCard + { + Body = new List + { + new TextBlock("Step 1 of 2 - Your Name") { Size = TextSize.Large, Weight = TextWeight.Bolder }, + new TextInput { Id = "name", Label = "Name", Placeholder = "Enter your name", IsRequired = true } + }, + Actions = new List + { + new SubmitAction().WithTitle("Next").WithData( + new Union(new SubmitActionData + { + NonSchemaProperties = new Dictionary { { "submissiontype", "multi_step_1" } } + })) + } + }; -```html - + taskInfo = new TaskInfo + { + Title = "Multi-step Form", + Width = new Union(400), + Height = new Union(300), + Card = new Attachment + { + ContentType = new ContentType("application/vnd.microsoft.card.adaptive"), + Content = step1Card + } + }; + } + else + { + var dialogCard = new AdaptiveCard + { + Body = new List + { + new TextBlock("Enter Text Here") { Weight = TextWeight.Bolder }, + new TextInput { Id = "usertext", Placeholder = "add some text and submit", IsMultiline = true } + }, + Actions = new List { new SubmitAction { Title = "Submit" } } + }; + + taskInfo = new TaskInfo + { + Title = "Adaptive Card: Inputs", + Width = new Union(400), + Height = new Union(200), + Card = new Attachment + { + ContentType = new ContentType("application/vnd.microsoft.card.adaptive"), + Content = dialogCard + } + }; + } + + return new Response(new ContinueTask(taskInfo)); +}); ``` -The following code provides an example of the CSS: - -```css -#embed-container iframe { - position: absolute; - top: 0; - left: 0; - width: 94%; - height: 95%; - padding-left: 20px; - padding-right: 20px; - padding-top: 10px; - padding-bottom: 10px; - border-style: none; +# [TypeScript](#tab/typescript) + +```typescript +import { + Attachment, + TaskModuleRequest, + TaskModuleResponse, + UrlTaskModuleTaskInfo, + CardTaskModuleTaskInfo, + cardAttachment, +} from '@microsoft/teams.api'; +import { AdaptiveCard, TextBlock, TextInput, SubmitAction } from '@microsoft/teams.cards'; + +function createTextInputCard(): Attachment { + const card = new AdaptiveCard( + new TextBlock('Enter Text Here', { weight: 'Bolder' }), + new TextInput({ id: 'usertext', placeholder: 'add some text and submit', isMultiline: true }), + ).withVersion('1.0').withActions( + new SubmitAction({ title: 'Submit' }), + ); + return cardAttachment('adaptive', card); +} + +function createMultiStepStep1Card(): Attachment { + const card = new AdaptiveCard( + new TextBlock('Step 1 of 2 - Your Name', { size: 'Large', weight: 'Bolder' }), + new TextInput({ id: 'name', label: 'Name', placeholder: 'Enter your name', isRequired: true }), + ).withVersion('1.4').withActions( + new SubmitAction({ title: 'Next', data: { submissiontype: 'multi_step_1' } }), + ); + return cardAttachment('adaptive', card); } + +app.on('dialog.open', async (context) => { + const taskModuleRequest = context.activity.value as TaskModuleRequest; + const cardData = taskModuleRequest.data?.data ?? 'AdaptiveCard'; + + if (cardData === 'CustomForm') { + const taskInfo: UrlTaskModuleTaskInfo = { + title: 'Custom Form', + width: 510, + height: 450, + url: `${BOT_ENDPOINT}/CustomForm/`, + fallbackUrl: `${BOT_ENDPOINT}/CustomForm/`, + }; + return { task: { type: 'continue', value: taskInfo } } as TaskModuleResponse; + } + + if (cardData === 'MultiStep') { + const taskInfo: CardTaskModuleTaskInfo = { + title: 'Multi-step Form', + width: 400, + height: 300, + card: createMultiStepStep1Card(), + }; + return { task: { type: 'continue', value: taskInfo } } as TaskModuleResponse; + } + + // Default: AdaptiveCard + const taskInfo: CardTaskModuleTaskInfo = { + title: 'Adaptive Card: Inputs', + width: 400, + height: 200, + card: createTextInputCard(), + }; + return { task: { type: 'continue', value: taskInfo } } as TaskModuleResponse; +}); ``` -The next section provides details on invoking your card using Adaptive Card or Adaptive Card bot card attachment. +# [Python](#tab/python) + +```python +from microsoft_teams.api import ( + AdaptiveCardAttachment, + CardTaskModuleTaskInfo, + InvokeResponse, + TaskFetchInvokeActivity, + TaskModuleContinueResponse, + TaskModuleResponse, + UrlTaskModuleTaskInfo, + card_attachment, +) +from microsoft_teams.apps import ActivityContext +from microsoft_teams.cards import AdaptiveCard, SubmitAction, SubmitActionData, TextBlock, TextInput + +@app.on_dialog_open +async def handle_dialog_open(context: ActivityContext[TaskFetchInvokeActivity]): + data = context.activity.value.data + card_data = data.get("data") if isinstance(data, dict) else data + + if card_data == "CustomForm": + return InvokeResponse( + body=TaskModuleResponse( + task=TaskModuleContinueResponse( + value=UrlTaskModuleTaskInfo( + title="Custom Form", + width=510, + height=450, + url=f"{os.getenv('BOT_ENDPOINT', 'http://localhost:3978')}/customform", + fallback_url=f"{os.getenv('BOT_ENDPOINT', 'http://localhost:3978')}/customform", + ) + ) + ) + ) + + elif card_data == "MultiStep": + dialog_card = AdaptiveCard(version="1.4").with_body([ + TextBlock(text="Step 1 of 2 - Your Name", size="Large", weight="Bolder"), + TextInput().with_id("name").with_label("Name").with_placeholder("Enter your name").with_is_required(True), + ]).with_actions([ + SubmitAction().with_title("Next").with_data( + SubmitActionData().with_data({"submissiontype": "multi_step_1"}) + ), + ]) + + return InvokeResponse( + body=TaskModuleResponse( + task=TaskModuleContinueResponse( + value=CardTaskModuleTaskInfo( + title="Multi-step Form", + width=400, + height=300, + card=card_attachment(AdaptiveCardAttachment(content=dialog_card)), + ) + ) + ) + ) + + dialog_card = AdaptiveCard(version="1.0").with_body([ + TextBlock(text="Enter Text Here", weight="Bolder"), + TextInput().with_id("usertext").with_placeholder("add some text and submit").with_is_multiline(True), + ]).with_actions([ + SubmitAction().with_title("Submit"), + ]) + + return InvokeResponse( + body=TaskModuleResponse( + task=TaskModuleContinueResponse( + value=CardTaskModuleTaskInfo( + title="Adaptive Card: Inputs", + width=400, + height=200, + card=card_attachment(AdaptiveCardAttachment(content=dialog_card)), + ) + ) + ) + ) +``` -## Adaptive Card or Adaptive Card bot card attachment +--- -Depending on how you're invoking your card, you'll need to use either an Adaptive Card or an Adaptive Card bot card attachment (an Adaptive Card wrapped in an `attachment` object). +## Handle the dialog submission -If you're invoking from a tab, use an Adaptive Card: +When a user presses `Action.Submit` in a dialog, Teams sends a task submit invoke to your app. You can respond by completing the task, showing a message, or opening another dialog (for example, to chain multi-step forms). -```json -{ - "type": "AdaptiveCard", - "body": [ - { - "type": "TextBlock", - "text": "Here is a guitar:" - }, - { - "type": "Image", - "url": "https://adaptivecards.microsoft.com/images/guitar1.jpeg", - "size": "Medium" - } - ], - "version": "1.0" -} -``` +# [C#](#tab/csharp) -If you're invoking from a bot, use an Adaptive Card bot card attachment: +```csharp +using System.Text.Json; +using Microsoft.Teams.Api.TaskModules; +using Microsoft.Teams.Cards; +using Microsoft.Teams.Common; -```json +teamsApp.OnTaskSubmit(async (context) => { - "contentType": "application/vnd.microsoft.card.adaptive", - "content": { - "type": "AdaptiveCard", - "body": [ + var activity = context.Activity; + var json = JsonSerializer.Deserialize(JsonSerializer.Serialize(activity)); + var submitData = JsonSerializer.Deserialize>( + json.GetProperty("value").GetProperty("data").GetRawText()); + var submissionType = submitData?.GetValueOrDefault("submissiontype")?.ToString(); + + if (submissionType == "multi_step_1") + { + var name = submitData["name"]?.ToString(); + var step2Card = new AdaptiveCard + { + Body = new List { - "type": "TextBlock", - "text": "Here is a guitar:" + new TextBlock("Step 2 of 2 - Your Email") { Size = TextSize.Large, Weight = TextWeight.Bolder }, + new TextInput { Id = "email", Label = "Email", Placeholder = "Enter your email", IsRequired = true } }, + Actions = new List + { + new SubmitAction().WithTitle("Submit").WithData( + new Union(new SubmitActionData + { + NonSchemaProperties = new Dictionary + { + { "submissiontype", "multi_step_2" }, + { "name", name! } + } + })) + } + }; + + var taskInfo = new TaskInfo + { + Title = "Multi-step Form: Step 2", + Width = new Union(400), + Height = new Union(300), + Card = new Attachment { - "type": "Image", - "url": "https://adaptivecards.microsoft.com/images/guitar1.jpeg", - "size": "Medium" + ContentType = new ContentType("application/vnd.microsoft.card.adaptive"), + Content = step2Card } - ], - "version": "1.0" + }; + + return new Response(new ContinueTask(taskInfo)); } -} + + if (submissionType == "multi_step_2") + { + await context.Send($"Hi {submitData["name"]}, thanks for submitting! Your email is {submitData["email"]}"); + return new Response(new MessageTask("Multi-step form completed!")); + } + + var usertext = submitData?.GetValueOrDefault("usertext")?.ToString(); + await context.Send($"You submitted: {usertext}"); + return new Response(new MessageTask("Thanks for submitting!")); +}); ``` -The next section provides details on dialog accessibility. +# [TypeScript](#tab/typescript) + +```typescript +import { + TaskModuleRequest, + TaskModuleResponse, + CardTaskModuleTaskInfo, +} from '@microsoft/teams.api'; + +app.on('dialog.submit', async (context) => { + const taskModuleRequest = context.activity.value as TaskModuleRequest; + const data = taskModuleRequest.data || {}; + const submissionType = typeof data === 'object' ? data.submissiontype : undefined; + + if (submissionType === 'multi_step_1') { + const taskInfo: CardTaskModuleTaskInfo = { + title: 'Multi-step Form: Step 2', + width: 400, + height: 300, + card: createMultiStepStep2Card(data.name), + }; + return { task: { type: 'continue', value: taskInfo } } as TaskModuleResponse; + } + + if (submissionType === 'multi_step_2') { + const { name, email } = data; + await context.send(`Hi ${name}, thanks for submitting! Your email is ${email}`); + return { task: { type: 'message', value: 'Multi-step form completed!' } } as TaskModuleResponse; + } + + // Default: adaptive card text input + const usertext = data?.usertext; + await context.send(`You submitted: ${usertext}`); + return { task: { type: 'message', value: 'Thanks for submitting!' } } as TaskModuleResponse; +}); +``` -## Keyboard and accessibility guidelines +# [Python](#tab/python) + +```python +from microsoft_teams.api import ( + AdaptiveCardAttachment, + CardTaskModuleTaskInfo, + InvokeResponse, + TaskModuleContinueResponse, + TaskModuleMessageResponse, + TaskModuleResponse, + TaskSubmitInvokeActivity, + card_attachment, +) +from microsoft_teams.apps import ActivityContext +from microsoft_teams.cards import AdaptiveCard, SubmitAction, SubmitActionData, TextBlock, TextInput + +@app.on_dialog_submit +async def handle_dialog_submit(context: ActivityContext[TaskSubmitInvokeActivity]): + data = context.activity.value.data + submission_type = data.get("submissiontype") if isinstance(data, dict) else None + + if submission_type == "multi_step_1": + name = data.get("name") + next_card = AdaptiveCard(version="1.4").with_body([ + TextBlock(text="Step 2 of 2 - Your Email", size="Large", weight="Bolder"), + TextInput().with_id("email").with_label("Email").with_placeholder("Enter your email").with_is_required(True), + ]).with_actions([ + SubmitAction().with_title("Submit").with_data( + SubmitActionData().with_data({"submissiontype": "multi_step_2", "name": name}) + ), + ]) + + return InvokeResponse( + body=TaskModuleResponse( + task=TaskModuleContinueResponse( + value=CardTaskModuleTaskInfo( + title="Multi-step Form: Step 2", + width=400, + height=300, + card=card_attachment(AdaptiveCardAttachment(content=next_card)), + ) + ) + ) + ) + + if submission_type == "multi_step_2": + name = data.get("name") + email = data.get("email") + await context.send(f"Hi {name}, thanks for submitting! Your email is {email}") + return InvokeResponse( + body=TaskModuleResponse(task=TaskModuleMessageResponse(value="Multi-step form completed!")) + ) + + usertext = data.get("usertext") if data else None + await context.send(f"You submitted: {usertext}") + return InvokeResponse( + body=TaskModuleResponse(task=TaskModuleMessageResponse(value="Thanks for submitting!")) + ) +``` -With HTML or JavaScript-based dialogs, you must ensure that users can interact with your dialog with a keyboard. Screen reader programs also depend on the ability to navigate using the keyboard. Following are the important considerations: +--- -* Using the [tabindex attribute](https://developer.mozilla.org/docs/Web/HTML/Global_attributes/tabindex) in your HTML tags to control which elements can be focused. Also, use tabindex attribute to identify where it participates in sequential keyboard navigation usually with the Tab and Shift-Tab keys. -* Handling the Esc key in the JavaScript for your dialog. The following code provides an example of how to handle the Esc key: +## Keyboard and accessibility guidelines - ```javascript - // Handle the Esc key - document.onkeyup = function(event) { - if ((event.key === 27) || (event.key === "Escape")) { - microsoftTeams.submitTask(null); // this will return an err object to the completionHandler() - } - } - ``` +For URL-based dialogs that load HTML content, ensure keyboard accessibility: + +* Use the [tabindex attribute](https://developer.mozilla.org/docs/Web/HTML/Global_attributes/tabindex) in your HTML tags to control which elements can be focused and to define sequential keyboard navigation with the Tab and Shift-Tab keys. +* Handle the Esc key appropriately in the JavaScript for your dialog page. Microsoft Teams ensures that keyboard navigation works properly from the dialog header into your HTML and vice-versa. ## Code sample -|Sample name | Description | .NET | Node.js | Manifest| +|Sample name | Description | .NET | Node.js | Python | |----------------|-----------------|--------------|----------------|----------------| -|Dialog sample bots-V4 | This sample app demonstrate how to use Dialogs (referred as task modules in TeamsJS v1.x) using Bot Framework v4. |[View](https://github.com/OfficeDev/Microsoft-Teams-Samples/tree/main/samples/bot-task-module/csharp)|[View](https://github.com/OfficeDev/Microsoft-Teams-Samples/tree/main/samples/bot-task-module/nodejs)|[View](https://github.com/OfficeDev/Microsoft-Teams-Samples/tree/main/samples/bot-task-module/csharp/demo-manifest/bot-task-module.zip) +|Bot task modules | This sample app demonstrates how to use dialogs (referred as task modules in TeamsJS v1.x) using the Teams AI SDK. |[View](https://github.com/OfficeDev/Microsoft-Teams-Samples/tree/main/samples/TeamsSDK/bot-task-modules/dotnet/bot-task-modules)|[View](https://github.com/OfficeDev/Microsoft-Teams-Samples/tree/main/samples/TeamsSDK/bot-task-modules)|[View](https://github.com/OfficeDev/Microsoft-Teams-Samples/tree/main/samples/TeamsSDK/bot-task-modules/python/bot-task-modules)| ## Next step @@ -231,7 +606,7 @@ Microsoft Teams ensures that keyboard navigation works properly from the dialog ## See also * [Cards and dialogs](../cards-and-task-modules.md) -* [Request device permissions](~/concepts/device-capabilities/native-device-permissions.md) -* [Integrate media capabilities](~/concepts/device-capabilities/media-capabilities.md) -* [Integrate QR or barcode scanner capability in Teams](~/concepts/device-capabilities/qr-barcode-scanner-capability.md) -* [Integrate location capabilities in Teams](~/concepts/device-capabilities/location-capability.md) +* [Teams SDK overview](/microsoftteams/platform/teams-sdk/) +* [Dialogs in the Teams SDK](/microsoftteams/platform/teams-sdk/in-depth-guides/dialogs/overview) +* [Adaptive Cards in the Teams SDK](/microsoftteams/platform/teams-sdk/in-depth-guides/adaptive-cards/overview) +* [Migrate from BotBuilder](/microsoftteams/platform/teams-sdk/migrations/botbuilder/overview) diff --git a/msteams-platform/task-modules-and-cards/task-modules/task-modules-bots.md b/msteams-platform/task-modules-and-cards/task-modules/task-modules-bots.md index ff6161e724e..a199b7b01b0 100644 --- a/msteams-platform/task-modules-and-cards/task-modules/task-modules-bots.md +++ b/msteams-platform/task-modules-and-cards/task-modules/task-modules-bots.md @@ -1,26 +1,26 @@ --- title: Use dialogs in Microsoft Teams bots -description: Learn how to use dialogs with Microsoft Teams bots and invoke dialogs, about Bot Framework card and Adaptive Card actions, deep links, and respond to messages. +description: Learn how to use dialogs with Microsoft Teams bots using the Teams SDK, invoke and submit dialogs with Adaptive Cards, and respond to dialog events. ms.localizationpriority: medium ms.topic: how-to -ms.date: 01/31/2023 +ms.date: 04/29/2026 --- # Use dialogs with bots -Invoke dialogs (referred as task modules in TeamsJS v1.x) from Microsoft Teams bots using buttons on Adaptive Cards and Bot Framework cards that are hero, thumbnail, and connector for Microsoft 365 Groups. Dialogs are often a better user experience than multiple conversation steps. Keep track of bot state and allow the user to interrupt or cancel the sequence. +Invoke dialogs (referred as task modules in TeamsJS v1.x) from Microsoft Teams bots using `TaskFetchAction` buttons on Adaptive Cards. Dialogs provide a focused interaction by opening a pop-up window for the user, making them ideal for complex forms or multi-step workflows. -There are two ways of invoking dialogs: - -* A new invoke message `task/fetch`: Using the `invoke` [card action](~/task-modules-and-cards/cards/cards-actions.md#action-type-invoke) for Bot Framework cards, or the `Action.Submit` [card action](~/task-modules-and-cards/cards/cards-actions.md#adaptive-cards-actions) for Adaptive Cards, with `task/fetch`, either an HTML or Adaptive Card-based dialog is fetched dynamically from your bot. -* Deep link URLs: Using the [deep link syntax for dialogs](../../concepts/build-and-test/deep-link-application.md#deep-link-to-open-a-dialog), you can use the `openUrl` [card action](~/task-modules-and-cards/cards/cards-actions.md#action-type-openurl) for Bot Framework cards or the `Action.OpenUrl` [card action](~/task-modules-and-cards/cards/cards-actions.md#adaptive-cards-actions) for Adaptive Cards, respectively. With deep link URLs, the dialog URL or Adaptive Card body is already known to avoid a server round-trip relative to `task/fetch`. +When a user selects a `TaskFetchAction` button on an Adaptive Card, Teams sends a dialog open event to your app. Your app handles this event and returns dialog content, either an Adaptive Card or a URL to a webpage, which Teams displays in a pop-up dialog window. For more information, see [Creating Dialogs](/microsoftteams/platform/teams-sdk/in-depth-guides/dialogs/creating-dialogs?pivots=csharp). > [!IMPORTANT] > Each `url` and `fallbackUrl` must implement the HTTPS encryption protocol. -## Invoke a dialog using `task/fetch` +> [!NOTE] +> In Teams client v1, dialogs were called task modules. They may occasionally be used synonymously. + +## Create a dialog launcher -When the `value` object of the `invoke` card action or `Action.Submit` is initialized and when a user selects the button, an `invoke` message is sent to the bot. In the HTTP response to the `invoke` message, there's a [TaskInfo object](~/task-modules-and-cards/task-modules/invoking-task-modules.md#dialoginfo-object) embedded in a wrapper object, which Teams uses to display the dialog (referred as task module in TeamsJS v1.x). +To invoke a dialog from a bot, send an Adaptive Card with `TaskFetchAction` buttons. Each button includes data that your bot uses to determine which dialog content to return. [!INCLUDE [ocdi-warning](../../includes/tabs/ocdi-warning.md)] @@ -30,7 +30,7 @@ The following steps provide instructions on how to invoke a dialog (referred as 1. This image shows a Bot Framework hero card with a **Buy** `invoke` [card action](~/task-modules-and-cards/cards/cards-actions.md#action-type-invoke). The value of the `type` property is `task/fetch` and the rest of the `value` object can be of your choice. 1. The bot receives the `invoke` HTTP POST message. -1. The bot creates a response object and returns it in the body of the POST response with an HTTP 200 response code. For more information on schema for responses, see the [discussion on task/submit](#responds-to-the-tasksubmit-messages). The following code provides an example of body of the HTTP response that contains a [TaskInfo object](~/task-modules-and-cards/task-modules/invoking-task-modules.md#dialoginfo-object) embedded in a wrapper object: +1. The bot creates a response object and returns it in the body of the POST response with an HTTP 200 response code. For more information on schema for responses, see [handle dialog submit events](#handle-dialog-submit-events). The following code provides an example of body of the HTTP response that contains a [TaskInfo object](~/task-modules-and-cards/task-modules/invoking-task-modules.md#dialog-metadata) embedded in a wrapper object: ```json { @@ -55,200 +55,257 @@ The next section provides details on submitting the result of a dialog. ## Submit the result of a dialog -When the user is finished with the dialog, submitting the result back to the bot is similar to the way it works with tabs. For more information, see [example of submitting the result of a dialog](~/task-modules-and-cards/task-modules/task-modules-tabs.md#example-of-submitting-the-result-of-a-dialog). There are a few differences as follows: - -* HTML or JavaScript that is `TaskInfo.url`: Once you've validated what the user has entered, you call the `microsoftTeams.tasks.submitTask()` function referred to hereafter as `submitTask()` for readability purposes. You can call `submitTask()` without any parameters if you want Teams to close the dialog (referred as task module in TeamsJS v1.x), but you must pass an object or a string to your `submitHandler`. Pass it as the first parameter, `result`. Teams invokes `submitHandler`, `err` is `null`, and `result` is the object or string you passed to `submitTask()`. If you call `submitTask()` with a `result` parameter, you must pass an `appId` or an array of `appId` strings. This action allows Teams to validate that the app sending the result is the same one, which invoked the dialog. Your bot receives a `task/submit` message including `result`. For more information, see [payload of `task/fetch` and `task/submit` messages](#payload-of-taskfetch-and-tasksubmit-messages). -* Adaptive Card that is `TaskInfo.card`: The Adaptive Card body as filled in by the user is sent to the bot through a `task/submit` message when the user selects any `Action.Submit` button. +When the user finishes with the dialog, the result is submitted back to your app. How submission works depends on the dialog content type: -The next section provides details on how to respond to the `task/submit` messages. +* **Adaptive Card (TaskInfo.card)**: When the user selects an `Action.Submit` button, Teams sends a dialog submit event to your app. Your dialog submit handler receives the form data from the card. In C#, use the `[TaskSubmit]` attribute. In TypeScript, use `app.on('dialog.submit', ...)`. In Python, use `@app.on_dialog_submit`. +* **Webpage (TaskInfo.url)**: The webpage calls `microsoftTeams.tasks.submitTask(formData)` from the TeamsJS client library, which triggers the same dialog submit event in your app. -## Responds to the `task/submit` messages - -When the user finishes with a dialog (referred as task module in TeamsJS v1.x) invoked from a bot, the bot always receives a `task/submit invoke` message. You have several options when responding to the `task/submit` message as follows: - -| HTTP body response | Scenario | -| --------------------------------------- | --------------------------------------- | -| None ignore the `task/submit` message | The simplest response is no response at all. Your bot isn't required to respond when the user is finished with the dialog. | -|
{
"task": {
"type": "message",
"value": "Message text"
}
}
| Teams displays the value of `value` in a pop-up message box. | -|
{
"task": {
"type": "continue",
"value": <TaskInfo object>
}
}
| Allows you to chain sequences of Adaptive Cards together in a wizard or multi-step experience. | - -> [!NOTE] -> Chaining Adaptive Cards into a sequence is an advanced scenario. The Node.js sample app supports it. For more information, see [Microsoft Teams dialog Node.js](https://github.com/OfficeDev/microsoft-teams-sample-task-module-nodejs#implementation-notes). +## Handle dialog submit events -The next section provides details on payload of `task/fetch` and `task/submit` messages. +When the user submits a dialog, the bot receives a `task/submit` invoke message. You have several options when responding: -## Payload of `task/fetch` and `task/submit` messages +| Response type | Scenario | +|---|---| +| No response | The simplest response is no response at all. Your bot isn't required to respond when the user finishes with the dialog. | +| `MessageTask` | Teams displays a message in a pop-up message box in the dialog. | +| `ContinueTask` | Allows you to chain sequences of Adaptive Cards together in a wizard or multi-step experience. | -This section defines the schema of what your bot receives when it receives a `task/fetch` or `task/submit` Bot Framework `Activity` object. The following table provides the properties of payload of `task/fetch` and `task/submit` messages: - -| Property | Description | -| -------- | ------------------------------------ | -| `type` | Is always `invoke`. | -| `name` | Is either `task/fetch` or `task/submit`. | -| `value` | Is the developer-defined payload. The structure of the `value` object is the same as what is sent from Teams. In this case, however, it's different. It requires support for dynamic fetch that is `task/fetch` from both Bot Framework, which is `value` and Adaptive Card `Action.Submit` actions, which is `data`. A way to communicate Teams `context` to the bot is required in addition to what is included in `value` or `data`.

Combine 'value' and 'data' into a parent object:

{
"context": {
"theme": "default" | "dark" | "contrast",
},
"data": [value field from Bot Framework card] | [data field from Adaptive Card]
}
| - -The next section provides an example of receiving and responding to `task/fetch` and `task/submit` invoke messages in Node.js. - - The following tabs provide `task/fetch` and `task/submit` invoke messages in .NET, Node.js, and python: +The following tabs show how to handle dialog submit events in .NET, TypeScript, and Python: # [.NET](#tab/csharp) ```csharp -protected override Task OnTeamsTaskModuleFetchAsync(ITurnContext turnContext, TaskModuleRequest taskModuleRequest, CancellationToken cancellationToken) +using System.Text.Json; +using Microsoft.Teams.Api.TaskModules; +using Microsoft.Teams.Apps; +using Microsoft.Teams.Apps.Activities.Invokes; +using Microsoft.Teams.Apps.Annotations; +using Microsoft.Teams.Common.Logging; + +[TaskSubmit] +public async Task OnTaskSubmit([Context] Tasks.SubmitActivity activity, [Context] IContext.Client client, [Context] ILogger log) { - var asJobject = JObject.FromObject(taskModuleRequest.Data); - var value = asJobject.ToObject>()?.Data; + var data = activity.Value?.Data as JsonElement?; + if (data == null) + { + log.Info("[TASK_SUBMIT] No data found in the activity value"); + return new Microsoft.Teams.Api.TaskModules.Response( + new Microsoft.Teams.Api.TaskModules.MessageTask("No data found in the activity value")); + } + + var submissionType = data.Value.TryGetProperty("submissiondialogtype", out var submissionTypeObj) && submissionTypeObj.ValueKind == JsonValueKind.String + ? submissionTypeObj.ToString() + : null; - var taskInfo = new TaskModuleTaskInfo(); - switch (value) + string? GetFormValue(string key) { - case TaskModuleIds.YouTube: - taskInfo.Url = taskInfo.FallbackUrl = _baseUrl + "/" + TaskModuleIds.YouTube; - SetTaskInfo(taskInfo, TaskModuleUIConstants.YouTube); - break; - case TaskModuleIds.CustomForm: - taskInfo.Url = taskInfo.FallbackUrl = _baseUrl + "/" + TaskModuleIds.CustomForm; - SetTaskInfo(taskInfo, TaskModuleUIConstants.CustomForm); - break; - case TaskModuleIds.AdaptiveCard: - taskInfo.Card = CreateAdaptiveCardAttachment(); - SetTaskInfo(taskInfo, TaskModuleUIConstants.AdaptiveCard); - break; - default: - break; + if (data.Value.TryGetProperty(key, out var val)) + { + if (val is JsonElement element) + return element.GetString(); + return val.ToString(); + } + return null; } - return Task.FromResult(taskInfo.ToTaskModuleResponse()); + switch (submissionType) + { + case "simple_form": + var name = GetFormValue("name") ?? "Unknown"; + await client.Send($"Hi {name}, thanks for submitting the form!"); + return new Microsoft.Teams.Api.TaskModules.Response( + new Microsoft.Teams.Api.TaskModules.MessageTask("Form was submitted")); + default: + return new Microsoft.Teams.Api.TaskModules.Response( + new Microsoft.Teams.Api.TaskModules.MessageTask("Unknown submission type")); + } } +``` -protected override async Task OnTeamsTaskModuleSubmitAsync(ITurnContext turnContext, TaskModuleRequest taskModuleRequest, CancellationToken cancellationToken) -{ - var reply = MessageFactory.Text("OnTeamsTaskModuleSubmitAsync Value: " + JsonConvert.SerializeObject(taskModuleRequest)); - await turnContext.SendActivityAsync(reply, cancellationToken); +# [TypeScript](#tab/nodejs) - return TaskModuleResponseFactory.CreateResponse("Thanks!"); -} +```typescript +import { App } from '@microsoft/teams.apps'; +// ... -private static void SetTaskInfo(TaskModuleTaskInfo taskInfo, UISettings uIConstants) -{ - taskInfo.Height = uIConstants.Height; - taskInfo.Width = uIConstants.Width; - taskInfo.Title = uIConstants.Title.ToString(); -} +app.on('dialog.submit', async ({ activity, send, next }) => { + const dialogType = activity.value.data?.submissiondialogtype; + + if (dialogType === 'simple_form') { + const name = activity.value.data.name; + await send(`Hi ${name}, thanks for submitting the form!`); + return { + task: { + type: 'message', + value: 'Form was submitted', + }, + }; + } + + if (dialogType === 'webpage_dialog') { + const name = activity.value.data.name; + const email = activity.value.data.email; + await send(`Hi ${name}, thanks for submitting the form! We got that your email is ${email}`); + return { + status: 200, + }; + } +}); ``` -# [Node.js](#tab/nodejs) +# [Python](#tab/python) -```typescript -handleTeamsTaskModuleFetch(context, taskModuleRequest) { - // Called when the user selects an options from the displayed HeroCard or - // AdaptiveCard. The result is the action to perform. - - const cardTaskFetchValue = taskModuleRequest.data.data; - var taskInfo = {}; // TaskModuleTaskInfo - - if (cardTaskFetchValue === TaskModuleIds.YouTube) { - // Display the YouTube.html page - taskInfo.url = taskInfo.fallbackUrl = this.baseUrl + '/' + TaskModuleIds.YouTube + '.html'; - this.setTaskInfo(taskInfo, TaskModuleUIConstants.YouTube); - } else if (cardTaskFetchValue === TaskModuleIds.CustomForm) { - // Display the CustomForm.html page, and post the form data back via - // handleTeamsTaskModuleSubmit. - taskInfo.url = taskInfo.fallbackUrl = this.baseUrl + '/' + TaskModuleIds.CustomForm + '.html'; - this.setTaskInfo(taskInfo, TaskModuleUIConstants.CustomForm); - } else if (cardTaskFetchValue === TaskModuleIds.AdaptiveCard) { - // Display an AdaptiveCard to prompt user for text, and post it back via - // handleTeamsTaskModuleSubmit. - taskInfo.card = this.createAdaptiveCardAttachment(); - this.setTaskInfo(taskInfo, TaskModuleUIConstants.AdaptiveCard); - } +```python +from typing import Optional, Any +from microsoft_teams.api import TaskSubmitInvokeActivity, InvokeResponse, TaskModuleResponse, TaskModuleMessageResponse +from microsoft_teams.apps import ActivityContext + +@app.on_dialog_submit +async def handle_dialog_submit(ctx: ActivityContext[TaskSubmitInvokeActivity]): + data: Optional[Any] = ctx.activity.value.data + dialog_type = data.get("submissiondialogtype") if data else None + + if dialog_type == "webpage_dialog": + name = data.get("name") if data else None + email = data.get("email") if data else None + await ctx.send(f"Hi {name}, thanks for submitting the form! We got that your email is {email}") + return InvokeResponse( + body=TaskModuleResponse(task=TaskModuleMessageResponse(value="Form submitted successfully")) + ) +``` - return TaskModuleResponseFactory.toTaskModuleResponse(taskInfo); -} +--- -async handleTeamsTaskModuleSubmit(context, taskModuleRequest) { - // Called when data is being returned from the selected option (see `handleTeamsTaskModuleFetch'). +## Multi-step dialog chaining - // Echo the users input back. In a production bot, this is where you'd add behavior in - // response to the input. - await context.sendActivity(MessageFactory.text('handleTeamsTaskModuleSubmit: ' + JSON.stringify(taskModuleRequest.data))); +You can chain Adaptive Cards into a multi-step wizard by returning a `ContinueTask` response from the submit handler. Each step returns a new card, and the final step returns a `MessageTask` to close the dialog. - // Return TaskModuleResponse - return { - // TaskModuleMessageResponse - task: { - type: 'message', - value: 'Thanks!' +# [.NET](#tab/csharp) + +```csharp +using System.Text.Json; +using Microsoft.Teams.Api; +using Microsoft.Teams.Api.TaskModules; +using Microsoft.Teams.Cards; + +// Add these cases to your OnTaskSubmit method +case "webpage_dialog_step_1": + var nameStep1 = GetFormValue("name") ?? "Unknown"; + var nextStepCardJson = $$""" + { + "type": "AdaptiveCard", + "version": "1.4", + "body": [ + { + "type": "TextBlock", + "text": "Email", + "size": "Large", + "weight": "Bolder" + }, + { + "type": "Input.Text", + "id": "email", + "label": "Email", + "placeholder": "Enter your email", + "isRequired": true + } + ], + "actions": [ + { + "type": "Action.Submit", + "title": "Submit", + "data": {"submissiondialogtype": "webpage_dialog_step_2", "name": "{{nameStep1}}"} + } + ] + } + """; + + var nextStepCard = JsonSerializer.Deserialize(nextStepCardJson) + ?? throw new InvalidOperationException("Failed to deserialize next step card"); + + var nextStepTaskInfo = new TaskInfo + { + Title = $"Thanks {nameStep1} - Get Email", + Card = new Attachment + { + ContentType = new ContentType("application/vnd.microsoft.card.adaptive"), + Content = nextStepCard } }; -} -setTaskInfo(taskInfo, uiSettings) { - taskInfo.height = uiSettings.height; - taskInfo.width = uiSettings.width; - taskInfo.title = uiSettings.title; -} + return new Response(new ContinueTask(nextStepTaskInfo)); + +case "webpage_dialog_step_2": + var nameStep2 = GetFormValue("name") ?? "Unknown"; + var emailStep2 = GetFormValue("email") ?? "No email"; + await client.Send($"Hi {nameStep2}, thanks for submitting the form! We got that your email is {emailStep2}"); + return new Response(new MessageTask("Multi-step form completed successfully")); +``` + +# [TypeScript](#tab/nodejs) + +```typescript +import { cardAttachment } from '@microsoft/teams.api'; +import { AdaptiveCard, TextInput, SubmitAction } from '@microsoft/teams.cards'; + +// Return from dialog.submit handler to chain to the next step +const dialogCard = new AdaptiveCard( + { + type: 'TextBlock', + text: 'This is a multi-step form', + size: 'Large', + weight: 'Bolder', + }, + new TextInput() + .withLabel('Name') + .withIsRequired() + .withId('name') + .withPlaceholder('Enter your name') +) + .withActions( + new SubmitAction() + .withTitle('Submit') + .withData({ submissiondialogtype: 'webpage_dialog_step_1' }) + ); + +return { + task: { + type: 'continue', + value: { + title: 'Multi-step Form Dialog', + card: cardAttachment('adaptive', dialogCard), + }, + }, +}; ``` # [Python](#tab/python) ```python -async def on_teams_task_module_fetch( - self, turn_context: TurnContext, task_module_request: TaskModuleRequest -) -> TaskModuleResponse: - """ - Called when the user selects an options from the displayed HeroCard or - AdaptiveCard. The result is the action to perform. - """ - - card_task_fetch_value = task_module_request.data["data"] - - task_info = TaskModuleTaskInfo() - if card_task_fetch_value == TaskModuleIds.YOUTUBE: - # Display the YouTube.html page - task_info.url = task_info.fallback_url = ( - self.__base_url + "/" + TaskModuleIds.YOUTUBE + ".html" - ) - TeamsTaskModuleBot.__set_task_info(task_info, TaskModuleUIConstants.YOUTUBE) - elif card_task_fetch_value == TaskModuleIds.CUSTOM_FORM: - # Display the CustomForm.html page, and post the form data back via - # on_teams_task_module_submit. - task_info.url = task_info.fallback_url = ( - self.__base_url + "/" + TaskModuleIds.CUSTOM_FORM + ".html" - ) - TeamsTaskModuleBot.__set_task_info(task_info, TaskModuleUIConstants.CUSTOM_FORM) - elif card_task_fetch_value == TaskModuleIds.ADAPTIVE_CARD: - # Display an AdaptiveCard to prompt user for text, and post it back via - # on_teams_task_module_submit. - task_info.card = TeamsTaskModuleBot.__create_adaptive_card_attachment() - TeamsTaskModuleBot.__set_task_info(task_info, TaskModuleUIConstants.ADAPTIVE_CARD) - - return TaskModuleResponseFactory.to_task_module_response(task_info) - -async def on_teams_task_module_submit( - self, turn_context: TurnContext, task_module_request: TaskModuleRequest -) -> TaskModuleResponse: - """ - Called when data is being returned from the selected option (see `on_teams_task_module_fetch'). - """ - - # Echo the users input back. In a production bot, this is where you'd add behavior in - # response to the input. - await turn_context.send_activity( - MessageFactory.text( - f"on_teams_task_module_submit: {json.dumps(task_module_request.data)}" +# In your on_dialog_submit handler, return a ContinueTask to show the next step +from microsoft_teams.api import TaskModuleResponse, TaskModuleContinueResponse, CardTaskModuleTaskInfo +from microsoft_teams.cards import AdaptiveCard, TextInput, SubmitAction, Attachment + +next_card = AdaptiveCard( + body=[ + {"type": "TextBlock", "text": "Step 2: Enter Email", "size": "Large", "weight": "Bolder"}, + {"type": "Input.Text", "id": "email", "label": "Email", "placeholder": "Enter your email", "isRequired": True} + ], + actions=[ + {"type": "Action.Submit", "title": "Submit", "data": {"submissiondialogtype": "webpage_dialog_step_2", "name": name}} + ] +) + +return InvokeResponse( + body=TaskModuleResponse( + task=TaskModuleContinueResponse( + value=CardTaskModuleTaskInfo( + title=f"Thanks {name} - Get Email", + card=Attachment(content_type="application/vnd.microsoft.card.adaptive", content=next_card) + ) ) ) - - message_response = TaskModuleMessageResponse(value="Thanks!") - return TaskModuleResponse(task=message_response) - -@staticmethod -def __set_task_info(task_info: TaskModuleTaskInfo, ui_constants: UISettings): - task_info.height = ui_constants.height - task_info.width = ui_constants.width - task_info.title = ui_constants.title - +) ``` --- @@ -263,12 +320,13 @@ The schema for Bot Framework card actions is different from Adaptive Card `Actio ## Code sample -|Sample name | Description | .NET | Node.js | Manifest| +|Sample name | Description | .NET | Node.js | Manifest| Python | |----------------|-----------------|--------------|----------------|----------------| -|Dialog sample bots-V4 | This sample app demonstrate how to use Dialogs (referred as task modules in TeamsJS v1.x) using Bot Framework v4. |[View](https://github.com/OfficeDev/Microsoft-Teams-Samples/tree/main/samples/bot-task-module/csharp)|[View](https://github.com/OfficeDev/Microsoft-Teams-Samples/tree/main/samples/bot-task-module/nodejs)|[View](https://github.com/OfficeDev/Microsoft-Teams-Samples/tree/main/samples/bot-task-module/csharp/demo-manifest/bot-task-module.zip) +|Dialog sample bots-V4 | This sample app demonstrate how to use Dialogs (referred as task modules in TeamsJS v1.x) using Bot Framework v4. |[View](https://github.com/OfficeDev/Microsoft-Teams-Samples/tree/main/samples/TeamsSDK/bot-task-modules/dotnet/bot-task-modules)|[View](https://github.com/OfficeDev/Microsoft-Teams-Samples/tree/main/samples/TeamsSDK/bot-task-modules/nodejs/bot-task-modules)| NA | [View](https://github.com/OfficeDev/Microsoft-Teams-Samples/tree/main/samples/TeamsSDK/bot-task-modules/python/bot-task-modules) | ## See also * [Cards and dialogs](../cards-and-task-modules.md) -* [Microsoft Teams dialog sample code in Node.js](https://github.com/OfficeDev/microsoft-teams-sample-task-module-nodejs/blob/master/src/TeamsBot.ts) -* [Bot Framework samples](https://github.com/OfficeDev/Microsoft-Teams-Samples/blob/main/README.md) +* [Teams SDK Dialogs overview](/microsoftteams/platform/teams-sdk/in-depth-guides/dialogs/overview) +* [Teams SDK Adaptive Cards - Executing Actions](/microsoftteams/platform/teams-sdk/in-depth-guides/adaptive-cards/executing-actions) +* [Migrate from BotBuilder to Teams SDK](/microsoftteams/platform/teams-sdk/migrations/botbuilder/overview) diff --git a/msteams-platform/task-modules-and-cards/task-modules/task-modules-tabs.md b/msteams-platform/task-modules-and-cards/task-modules/task-modules-tabs.md index ed7678fe36e..1295c527a4d 100644 --- a/msteams-platform/task-modules-and-cards/task-modules/task-modules-tabs.md +++ b/msteams-platform/task-modules-and-cards/task-modules/task-modules-tabs.md @@ -3,7 +3,7 @@ title: Use dialogs in Microsoft Teams tabs description: Learn how to invoke dialogs (task modules) from Teams tabs and submitting its result using the Teams JavaScript client library (TeamsJS). It includes code samples. ms.localizationpriority: medium ms.topic: how-to -ms.date: 02/22/2023 +ms.date: 04/15/2026 --- # Use dialogs in tabs @@ -82,7 +82,7 @@ The value of `UrlDialogInfo.url` is set to the location of the content of your d microsoftTeams.dialog.adaptiveCard.open(adaptiveCardDialogInfo, submitHandler); ``` -The value of `adaptiveCardDialogInfo.card` is the [JSON for an Adaptive Card](../../task-modules-and-cards/task-modules/invoking-task-modules.md#adaptive-card-or-adaptive-card-bot-card-attachment). You can specify a `submitHandler` to be called with an *err* string, if there was an error when invoking `open()` or if the user closes the dialog using the **X** (Exit) button. +The value of `adaptiveCardDialogInfo.card` is the JSON for an [Adaptive Card](https://adaptivecards.io/explorer/). You can specify a `submitHandler` to be called with an *err* string, if there was an error when invoking `open()` or if the user closes the dialog using the **X** (Exit) button. The next section gives an example of invoking a dialog. @@ -161,8 +161,8 @@ Teams then invokes your `submitHandler` where `err` is *null* and `result` is th When you invoke the dialog with a `submitHandler` and the user selects an `Action.Submit` button, the values in the card are returned as its `data` object. If the user presses the **Esc** key or selects **X** to exit the dialog, your `submitHandler` is called with the `err` string. If your app contains a bot in addition to a tab, you can include the `appId` of the bot as the value of `completionBotId` in the `TaskInfo` ([BotAdaptiveCardDialogInfo](/javascript/api/@microsoft/teams-js/botadaptivecarddialoginfo)) object. -The Adaptive Card body as filled in by the user is sent to the bot using a `task/submit invoke` message when the user selects an `Action.Submit` button. The schema for the object you receive is similar to [the schema you receive for task/fetch and task/submit messages](../../task-modules-and-cards/task-modules/task-modules-bots.md#payload-of-taskfetch-and-tasksubmit-messages). -The only difference is that the schema of the JSON object is an Adaptive Card object as opposed to an object containing an Adaptive Card object as [when Adaptive Cards are used with bots](../../task-modules-and-cards/task-modules/task-modules-bots.md#payload-of-taskfetch-and-tasksubmit-messages). +The Adaptive Card body as filled in by the user is sent to the bot using a `task/submit invoke` message when the user selects an `Action.Submit` button. The schema for the object you receive is similar to [the schema you receive for dialog submit messages](../../task-modules-and-cards/task-modules/task-modules-bots.md#handle-dialog-submit-events). +The only difference is that the schema of the JSON object is an Adaptive Card object as opposed to an object containing an Adaptive Card object as [when Adaptive Cards are used with bots](../../task-modules-and-cards/task-modules/task-modules-bots.md#handle-dialog-submit-events). The following code is the example of payload: diff --git a/msteams-platform/task-modules-and-cards/what-are-task-modules.md b/msteams-platform/task-modules-and-cards/what-are-task-modules.md index 085e6aebdd6..9803d98d945 100644 --- a/msteams-platform/task-modules-and-cards/what-are-task-modules.md +++ b/msteams-platform/task-modules-and-cards/what-are-task-modules.md @@ -4,7 +4,7 @@ description: Learn how to create a modal pop-up experience and embed a webpage t ms.localizationpriority: medium ms.topic: overview ms.author: anclear -ms.date: 11/12/2024 +ms.date: 04/17/2026 --- # Dialogs @@ -35,14 +35,14 @@ A dialog includes the following as shown in the previous image: 1. Your app's [`color` icon](/microsoft-365/extensibility/schema/root-icons#color). 2. Your app's [`short` name](/microsoft-365/extensibility/schema/root-name#short). -3. The dialog's title specified in the `title` property of the [DialogInfo object](~/task-modules-and-cards/task-modules/invoking-task-modules.md#dialoginfo-object). +3. The dialog's title specified in the `title` property of the [TaskInfo object](~/task-modules-and-cards/task-modules/invoking-task-modules.md#dialog-metadata). 4. The dialog's close or cancel button. If the user selects this button, your app receives an `err` event. For more information, see [example for submitting the result of a dialog](~/task-modules-and-cards/task-modules/task-modules-tabs.md#example-of-submitting-the-result-of-a-dialog). > [!NOTE] > It isn't possible to detect the `err` event when a dialog is invoked from a bot. -5. The blue rectangle is where your web page appears if you're loading your own web page using the `url` property of the [TaskInfo object](~/task-modules-and-cards/task-modules/invoking-task-modules.md#dialoginfo-object). For more information, see [Invoke and dismiss dialogs](~/task-modules-and-cards/task-modules/invoking-task-modules.md). -6. If you're displaying an Adaptive Card using the `card` property of the [TaskInfo object](~/task-modules-and-cards/task-modules/invoking-task-modules.md#dialoginfo-object), the padding is added for you. For more information, see [CSS for HTML or JavaScript dialogs](~/task-modules-and-cards/task-modules/invoking-task-modules.md#css-for-html-or-javascript-dialogs). +5. The blue rectangle is where your web page appears if you're loading your own web page using the `url` property of the [TaskInfo object](~/task-modules-and-cards/task-modules/invoking-task-modules.md#dialog-metadata). For more information, see [Invoke and dismiss dialogs](~/task-modules-and-cards/task-modules/invoking-task-modules.md). +6. If you're displaying an Adaptive Card using the `card` property of the [TaskInfo object](~/task-modules-and-cards/task-modules/invoking-task-modules.md#dialog-metadata), the padding is added for you. 7. Adaptive Card buttons render after you select **Sign up**. When using your own page, create your own buttons. By design, the primary button style (solid) is applied to the last root action in an Adaptive Card. For all other actions, the default button style is applied. ## Using dialogs in Bot Framework