Updates from: 02/19/2023 02:55:23
Service Microsoft Docs article Related commit history on GitHub Change details
platform Update Apple Store Team Connect Id https://github.com/MicrosoftDocs/msteams-docs/commits/main/msteams-platform/concepts/deploy-and-publish/appsource/prepare/update-apple-store-team-connect-id.md
Title: Update Apple Store Connect Team ID on Partner center
-description: Update your Apple App Store Connect Team ID in the Microsoft Partner Center to enable end-users to search and acquire your app on the Teams iOS platform.
+ Title: Update Apple Developer Program Team ID Team ID on Partner center
+description: Update your Apple Developer Program Team ID in the Microsoft Partner Center to enable end-users to search and acquire your app on the Teams iOS platform.
ms.localizationpriority: medium
-# Update Apple App Store Connect Team ID
+# Update Apple Developer Program Team ID
-To enable end-users to install your app on the Teams iOS platform, update your Apple App Store Connect Team ID in the Microsoft Partner Center. The Apple App Store Connect Team ID is shared with Apple. To update your Apple App Store Connect Team ID, follow these steps:
+To enable end-users to install your app on the Teams iOS platform, update your Apple Developer Program Team ID in the Microsoft Partner Center. The Apple Developer Program Team ID is shared with Apple. To update your Apple App Store Connect Team ID, follow these steps:
1. Sign in to [Microsoft Partner Center](https://partner.microsoft.com/dashboard/home) using the global admin credentials. 1. Select the settings icon from the upper right corner of the page. 1. Go to the **Legal info** section under **Organization profile** from the left pane. 1. Select the **Developer** tab.
-1. Provide your Apple App Store Connect Team ID.
+1. Provide your Apple Developer Program Team ID.
1. Go to the offer page and republish your Teams app.
-Your Apple App Store Connect Team ID is now updated and users can install your app on the Teams iOS platform.
+Your Apple Developer Program Team ID is now updated and users can install your app on the Teams iOS platform.
-To get Apple App Store Connect Team ID from Apple Developer portal, follow these steps:
+To get Apple Developer Program Team ID from Apple Developer portal, follow these steps:
1. Sign in to [Apple Developer Center](https://developer.apple.com/). 1. Select **Account** and go to **Membership**.
-1. Under **Membership**, access **Apple App Store Connect Team ID**.
+1. Under **Membership**, access **Apple Developer Program Team ID**.
+
+You can also refer to Apple's official documentation to locate Apple Developer Program Team ID from here - https://developer.apple.com/help/account/manage-your-team/locate-your-team-id
## Next step
platform Link Unfurling https://github.com/MicrosoftDocs/msteams-docs/commits/main/msteams-platform/messaging-extensions/how-to/link-unfurling.md
The document guides you on how to add link unfurling to your app manifest using
> [!NOTE] >
-> * Currently, link unfurling is not supported on Mobile clients.
+> * Link unfurling isn't supported on Mobile clients.
> * The link unfurling result is cached for 30 minutes. > * Messaging extension commands are not required for Link unfurling. However, there must be at least one command in manifest as it is a mandatory property in messaging extensions. For more information, see [compose extensions](/microsoftteams/platform/resources/schema/manifest-schema). The following image is an example of link unfurling using the Azure DevOps message extension. When the Azure DevOps link is pasted into the Teams compose message area, the link unfurls into a card with the work item details: See the following video to learn more about link unfurling: <br>
See the following video to learn more about link unfurling:
To add link unfurling to your app manifest, add a new `messageHandlers` array to the `composeExtensions` section of your app manifest JSON. You can add the array with the help of Developer Portal or manually. Domain listings can include wildcards, for example `*.example.com`. This matches exactly one segment of the domain; if you need to match `a.b.example.com` then use `*.*.example.com`. > [!NOTE]
-> Ensure not to add domains that are not in your control, either directly or through wildcards. For example, `yourapp.onmicrosoft.com` is valid, but `*.onmicrosoft.com` is not valid. The top-level domains are prohibited, for example, `*.com`, `*.org`.
+> Ensure not to add domains that are not in your control, either directly or through wildcards. For example, `yourapp.onmicrosoft.com` is valid, but `*.onmicrosoft.com` isn't valid. The top-level domains are prohibited, for example, `*.com`, `*.org`.
### Add link unfurling using Developer Portal 1. Open **Developer Portal** from the Microsoft Teams client and then select the **Apps** tab.
- :::image type="content" source="../../assets/images/tdp/create-new-app.png" alt-text="create new app in developer portal" lightbox="../../assets/images/tdp/create-new-app.png":::
+ :::image type="content" source="../../assets/images/tdp/create-new-app.png" alt-text="Screenshot shows Developer portal with Apps highlighted in red." lightbox="../../assets/images/tdp/create-new-app.png":::
> [!NOTE] > You need to add Developer Portal app, if you don't have it added in your Teams client.
- :::image type="content" source="../../assets/images/tdp/dev-portal-app.png" alt-text="Add developer portal app" lightbox="../../assets/images/tdp/dev-portal-app.png":::
+ :::image type="content" source="../../assets/images/tdp/dev-portal-app.png" alt-text="Screenshot shows Teams with search and Developer portal highlighted in red." lightbox="../../assets/images/tdp/dev-portal-app.png":::
1. Load your app manifest.
- :::image type="content" source="../../assets/images/tdp/load-app-manifest.png" alt-text="load your app manifest" lightbox="../../assets/images/tdp/load-app-manifest.png":::
+ :::image type="content" source="../../assets/images/tdp/load-app-manifest.png" alt-text="Screenshot of Developer portal showing the apps information." lightbox="../../assets/images/tdp/load-app-manifest.png":::
1. Select **Messaging Extension** under **App features** and then either choose **Select an existing bot** or **Create a new bot**.
- :::image type="content" source="../../assets/images/tdp/select-messaging-extension.png" alt-text="Select messaging extension option" lightbox="../../assets/images/tdp/select-messaging-extension.png":::
+ :::image type="content" source="../../assets/images/tdp/select-messaging-extension.png" alt-text="Screenshot of App features with App features and Messaging extension highlighted in red." lightbox="../../assets/images/tdp/select-messaging-extension.png":::
- :::image type="content" source="../../assets/images/tdp/select-create-bot.png" alt-text="select existing bot or create a new bot" lightbox="../../assets/images/tdp/select-create-bot.png":::
+ :::image type="content" source="../../assets/images/tdp/select-create-bot.png" alt-text="Screenshot of Messaging extension with Select an existing bot highlighted in red." lightbox="../../assets/images/tdp/select-create-bot.png":::
1. Select **Save**. 1. Select **Add a domain** under **Preview links** section and then enter valid domain.
To get your app ready for zero install link unfurling, follow these steps:
1. **Advantages and limitations**:
- # [Advantages](#tab/advantages)
+# [Advantages](#tab/advantages)
- Zero install link unfurling helps you provide enhanced experience to the users, such as:
+Zero install link unfurling helps you provide enhanced experience to the users, such as:
- * Unfurl previews for your links that users share in Teams even before they've installed your app.
+* Unfurl previews for your links that users share in Teams even before they've installed your app.
+* Create a welcome card for your app to show a preview with the placeholder fields.
- * Create a welcome card for your app to show a preview with the placeholder fields.
+# [Limitations](#tab/limitations)
- # [Limitations](#tab/limitations)
+The following are the limitations:
- The following are the limitations:
+* The bot can only send back a response as `result` or `auth` as the value for the `type` property in response to the `composeExtension/anonymousQueryLink` invoke request. The user can log an error for all other response types, such as, *silentAuth* and *config*.
- * The bot can only send back a response as `result` or `auth` as the value for the `type` property in response to the `composeExtension/anonymousQueryLink` invoke request. The user can log an error for all other response types, such as, *silentAuth* and *config*.
+* The bot can't send back an acv2 card in response to the `composeExtension/anonymousQueryLink` invoke request, either as a result or as a pre-auth card in auth.
- * The bot can't send back an acv2 card in response to the `composeExtension/anonymousQueryLink` invoke request, either as a result or as a pre-auth card in auth.
+* If the bot selects to send back the `"type": "auth"` with a pre-auth card, the Teams client strips away any action buttons from the card, and adds a sign in action button to get users to authenticate into your app.
- * If the bot selects to send back the `"type": "auth"` with a pre-auth card, the Teams client strips away any action buttons from the card, and adds a sign in action button to get users to authenticate into your app.
-
+ * The bot can't send back an acv2 card in response to the `composeExtension/anonymousQueryLink` invoke request, either as a result or as a pre-auth card in auth.
+
+ * If the bot selects to send back the `"type": "auth"` with a pre-auth card, the Teams client strips away any action buttons from the card, and adds a sign in action button to get users to authenticate into your app.
++ ## Remove link unfurling cache
The following JSON payload example for `suggestedActions` property:
Follow the [step-by-step guide](../../sbs-botbuilder-linkunfurling.yml) to unfurl links in Teams using bot.
+## Code sample
+
+|**Sample name** | **Description** | **.NET** | **Node.js**|
+|-|--|--|-|
+| Zero install link unfurling. | Demonstrates how to use Search-based Messaging Extension with a configuration page. | [View](https://github.com/OfficeDev/Microsoft-Teams-Samples/tree/main/samples/msgext-search-auth-config/csharp) | [View](https://github.com/OfficeDev/Microsoft-Teams-Samples/tree/main/samples/msgext-search-sso-config/nodejs) |
+ ## See also * [Message extensions](../what-are-messaging-extensions.md)
platform Manifest Schema https://github.com/MicrosoftDocs/msteams-docs/commits/main/msteams-platform/resources/schema/manifest-schema.md
The following schema sample shows all extensibility options:
"configurationUrl": "https://contoso.com/teamstab/configure", "scopes": [ "team",
- "groupchat"
+ "groupChat"
], "canUpdateConfiguration": true, "context": [
The following schema sample shows all extensibility options:
"scopes": [ "team", "personal",
- "groupchat"
+ "groupChat"
], "needsChannelSelector": false, "isNotificationOnly": false,
The following schema sample shows all extensibility options:
{ "scopes": [ "team",
- "groupchat"
+ "groupChat"
], "commands": [ {
The following schema sample shows all extensibility options:
{ "scopes": [ "personal",
- "groupchat"
+ "groupChat"
], "commands": [ {
The following schema sample shows all extensibility options:
"defaultGroupCapability": { "meetings": "tab", "team": "bot",
- "groupchat": "bot"
+ "groupChat": "bot"
}, "configurableProperties": [ "name",
The value must be a valid HTML color code starting with '#', for example `#4464e
**Optional**ΓÇöarray
-Used when your app experience has a team channel tab experience that requires extra configuration before it's added. Configurable tabs are supported only in the `team` and `groupchat` scopes and you can configure the same tabs multiple times. However, you can define it in the manifest only once.
+Used when your app experience has a team channel tab experience that requires extra configuration before it's added. Configurable tabs are supported only in the `team` and `groupChat` scopes and you can configure the same tabs multiple times. However, you can define it in the manifest only once.
|Name| Type| Maximum size | Required | Description| |||||| |`configurationUrl`|string|2048 characters|✔️|The https:// URL to use when configuring the tab.|
-|`scopes`|array of enums|1|✔️|Currently, configurable tabs support only the `team` and `groupchat` scopes. |
+|`scopes`|array of enums|1|✔️|Currently, configurable tabs support only the `team` and `groupChat` scopes. |
|`canUpdateConfiguration`|Boolean|||A value indicating whether an instance of the tab's configuration can be updated by the user after creation. Default: **true**.| |`context` |array of enums|6||The set of `contextItem` scopes where a [tab is supported](../../tabs/how-to/access-teams-context.md). Default: **[channelTab, privateChatTab, meetingChatTab, meetingDetailsTab]**.| |`sharePointPreviewImage`|string|2048||A relative file path to a tab preview image for use in SharePoint. Size 1024x768. |
The item is an array (maximum of only one element&mdash;currently only one bot i
|Name| Type| Maximum size | Required | Description| |||||| |`botId`|string|64 characters|✔️|The unique Microsoft app ID for the bot as registered with the Bot Framework. The ID can be the same as the overall [app ID](#id).|
-|`scopes`|array of enums|3|✔️|Specifies whether the bot offers an experience in the context of a channel in a `team`, in a group chat (`groupchat`), or an experience scoped to an individual user alone (`personal`). These options are non-exclusive.|
+|`scopes`|array of enums|3|✔️|Specifies whether the bot offers an experience in the context of a channel in a `team`, in a group chat (`groupChat`), or an experience scoped to an individual user alone (`personal`). These options are non-exclusive.|
|`needsChannelSelector`|Boolean|||Describes whether or not the bot uses a user hint to add the bot to a specific channel. Default: **`false`**| |`isNotificationOnly`|Boolean|||Indicates whether a bot is a one-way, notification-only bot, as opposed to a conversational bot. Default: **`false`**| |`supportsFiles`|Boolean|||Indicates whether the bot supports the ability to upload/download files in personal chat. Default: **`false`**|
A list of commands that your bot can recommend to users. The object is an array
|Name| Type| Maximum size | Required | Description| ||||||
-|`items.scopes`|array of enums|3|✔️|Specifies the scope for which the command list is valid. Options are `team`, `personal`, and `groupchat`.|
+|`items.scopes`|array of enums|3|✔️|Specifies the scope for which the command list is valid. Options are `team`, `personal`, and `groupChat`.|
|`items.commands`|array of objects|10|✔️|An array of commands the bot supports:<br>`title`: the bot command name (string, 32)<br>`description`: a simple description or example of the command syntax and its argument (string, 128).| ### bots.commandLists.commands
Specifies the install scope defined for this app by default. The defined scope w
* `personal` * `team`
-* `groupchat`
+* `groupChat`
* `meetings` ## defaultGroupCapability
Specifies the install scope defined for this app by default. The defined scope w
When a group install scope is selected, it will define the default capability when the user installs the app. Options are: * `team`
-* `groupchat`
+* `groupChat`
* `meetings` |Name| Type| Maximum size | Required | Description| |||||| |`team`|string|||When the install scope selected is `team`, this field specifies the default capability available. Options: `tab`, `bot`, or `connector`.|
-|`groupchat`|string|||When the install scope selected is `groupchat`, this field specifies the default capability available. Options: `tab`, `bot`, or `connector`.|
+|`groupChat`|string|||When the install scope selected is `groupChat`, this field specifies the default capability available. Options: `tab`, `bot`, or `connector`.|
|`meetings`|string|||When the install scope selected is `meetings`, this field specifies the default capability available. Options: `tab`, `bot`, or `connector`.| ## configurableProperties
platform Create Channel Group Tab https://github.com/MicrosoftDocs/msteams-docs/commits/main/msteams-platform/tabs/how-to/create-channel-group-tab.md
Title: Create a channel tab or group tab
-description: Create custom channel, group tab with Node.js, ASP.NET Core, ASP.NET Core MVC. Generate app, create package, build and run app, secret tunnel, upload to Teams
+description: Create custom channel, group tab with Node.js, ASP.NET Core, ASP.NET Core MVC. Generate app, create package, build and run app, secret tunnel, upload to Teams and build your first app using Blazor.
ms.localizationpriority: high
-zone_pivot_groups: teams-app-environment
+zone_pivot_groups: teams-app-environment-blazor
# Create a channel tab or group tab
Ensure that you have all the [prerequisites](~/tabs/how-to/tab-requirements.md)
npm install generator-teams --global ```
-Following are the steps to create a channel or group tab:
-
-* [Generate your application with a channel or group tab](#generate-your-application-with-a-channel-or-group-tab)
-* [Create your app package](#create-your-app-package)
-* [Build and run your application](#build-and-run-your-application)
-* [Establish a secure tunnel to your tab](#establish-a-secure-tunnel-to-your-tab)
-* [Upload your application to Teams](#upload-your-application-to-teams)
-
-### Generate your application with a channel or group tab
+## Generate your application with a channel or group tab
1. At the command prompt, create a new directory for your channel or group tab.
Following are the steps to create a channel or group tab:
1. Provide your values to a series of questions prompted by Microsoft Teams app generator to update your `manifest.json` file:
- ![generator opening screenshot](/microsoftteams/platform/assets/images/tab-images/teamsTabScreenshot.PNG)
+ :::image type="content" source="../../assets/images/tab-images/teamsTabScreenshot.PNG" alt-text="Screenshot that shows a generator to update manifest.json files.":::
- <details>
- <summary><b>Series of questions to update your manifest.json file</b></summary>
+<details>
+<summary><b>Series of questions to update your manifest.json file</b></summary>
- * **What is your solution name?**
+* **What is your solution name?**
- The solution name is your project name. You can accept the suggested name by selecting **Enter**.
+ The solution name is your project name. You can accept the suggested name by selecting **Enter**.
- * **Where do you want to place the files?**
+* **Where do you want to place the files?**
- You're currently in your project directory. Select **Enter**.
+ You're currently in your project directory. Select **Enter**.
- * **Title of your Microsoft Teams app project?**
+* **Title of your Microsoft Teams app project?**
- The title is your app package name and is used in the app manifest and description. Enter a title or select **Enter** to accept the default name.
+ The title is your app package name and is used in the app manifest and description. Enter a title or select **Enter** to accept the default name.
- * **Your (company) name? (max 32 characters)**
+* **Your (company) name? (max 32 characters)**
- Your company name can be used in the app manifest. Enter a company name or select **Enter** to accept the default name.
+ Your company name can be used in the app manifest. Enter a company name or select **Enter** to accept the default name.
- * **Which manifest version would you like to use?**
+* **Which manifest version would you like to use?**
- Select the default schema.
+ Select the default schema.
- * **Quick scaffolding? (Y/n)**
+* **Quick scaffolding? (Y/n)**
- The default is yes; enter **n** to enter your Microsoft Partner ID.
+ The default is yes; enter **n** to enter your Microsoft Partner ID.
- * **Enter your Microsoft Partner Id, if you have one? (Leave blank to skip)**
+* **Enter your Microsoft Partner Id, if you have one? (Leave blank to skip)**
- This field isn't required and must be used only if you're already part of the [Microsoft Partner Network](https://partner.microsoft.com).
+ This field isn't required and must be used only if you're already part of the [Microsoft Partner Network](https://partner.microsoft.com).
- * **What do you want to add to your project?**
+* **What do you want to add to your project?**
- Select **( &ast; ) A Tab**.
+ Select **( &ast; ) A Tab**.
- * **The URL to host this solution?**
+* **The URL to host this solution?**
- By default, the generator suggests an Azure website URL. You're only testing your app locally, so a valid URL isn't necessary.
+ By default, the generator suggests an Azure website URL. You're only testing your app locally, so a valid URL isn't necessary.
- * **Would you like show a loading indicator when your app/tab loads?**
+* **Would you like show a loading indicator when your app/tab loads?**
- Choose **not** to include a loading indicator when your app or tab loads. The default is no, enter **n**.
+ Choose **not** to include a loading indicator when your app or tab loads. The default is no, enter **n**.
- * **Would you like personal apps to be rendered without a tab header-bar?**
+* **Would you like personal apps to be rendered without a tab header-bar?**
- Choose **not** to include personal apps to be rendered without a tab header-bar. Default is no, enter **n**.
+ Choose **not** to include personal apps to be rendered without a tab header-bar. Default is no, enter **n**.
- * **Would you like to include Test framework and initial tests? (y/N)**
+* **Would you like to include Test framework and initial tests? (y/N)**
- Choose **not** to include a test framework for this project. The default is no, enter **n**.
+ Choose **not** to include a test framework for this project. The default is no, enter **n**.
- * **Would you like to include ESLint support? (y/N)**
+* **Would you like to include ESLint support? (y/N)**
- Choose not to include ESLint support. The default is no, enter **n**.
+ Choose not to include ESLint support. The default is no, enter **n**.
- * **Would you like to use Azure Applications Insights for telemetry? (y/N)**
+* **Would you like to use Azure Applications Insights for telemetry? (y/N)**
- Choose **not** to include [Azure Application Insights](/azure/azure-monitor/app/app-insights-overview). The default is no; enter **n**.
+ Choose **not** to include [Azure Application Insights](/azure/azure-monitor/app/app-insights-overview). The default is no; enter **n**.
- * **Default Tab Name (max 16 characters)?**
+* **Default Tab Name (max 16 characters)?**
- Name your tab. This tab name is used throughout your project as a file or URL path component.
+ Name your tab. This tab name is used throughout your project as a file or URL path component.
- * **What kind of Tab would you like to create?**
+* **What kind of Tab would you like to create?**
- Use the arrow keys to select **Configurable** tab.
+ Use the arrow keys to select **Configurable** tab.
- * **What scopes do you intend to use for your Tab?**
+* **What scopes do you intend to use for your Tab?**
- You can select a team or a group chat.
+ You can select a team or a group chat.
- * **Do you require Microsoft Azure Active Directory (Azure AD) Single-Sign-On support for the tab?**
+* **Do you require Microsoft Azure Active Directory (Azure AD) Single-Sign-On support for the tab?**
- Choose **not** to include Microsoft Azure Active Directory (Azure AD) Single-Sign-On support for the tab. The default is yes, enter **n**.
+ Choose **not** to include Microsoft Azure Active Directory (Azure AD) Single-Sign-On support for the tab. The default is yes, enter **n**.
- * **Do you want this tab to be available in SharePoint Online? (Y/n)**
+* **Do you want this tab to be available in SharePoint Online? (Y/n)**
- Enter **n**.
+ Enter **n**.
- </details>
+</details>
> [!IMPORTANT] > The path component **yourDefaultTabNameTab** is the value that you entered in the generator for **Default Tab Name** plus the word **Tab**. For example, `DefaultTabName` is **MyTab** then **/MyTabTab/**.
Following are the steps to create a channel or group tab:
* A `manifest.json` file that specifies the attributes of your app. >
-### Create your app package
+## Create your app package
You must have an app package to build and run your application in Teams. The app package is created through a gulp task that validates the `manifest.json` file and generates the zip folder in the `./package` directory. At the command prompt, enter the following command:
You must have an app package to build and run your application in Teams. The app
gulp manifest ```
-### Build and run your application
+## Build and run your application
-#### Build your application
+### Build your application
Enter the following command at the command prompt to transpile your solution into the `./dist` folder:
Enter the following command at the command prompt to transpile your solution int
gulp build ```
-#### Run your application
+### Run your application
1. At the command prompt, enter the following command to start a local web server:
gulp build
:::image type="content" source="~/assets/images/tab-images/configurationPage.png" alt-text="Tab configuration page":::
-### Establish a secure tunnel to your tab
+## Establish a secure tunnel to your tab
To establish a secure tunnel to your tab, exit the localhost and enter the following command:
gulp ngrok-serve
> [!IMPORTANT] > After your tab is uploaded to Microsoft Teams through **ngrok**, and successfully saved, you can view it in Teams until your tunnel session ends. If you restart your ngrok session, you must update your app with the new URL.
-### Upload your application to Teams
+## Upload your application to Teams
1. Go to Teams and select **Apps**&nbsp;:::image type="content" source="~/assets/images/tab-images/store.png" alt-text="Teams Store":::. 1. Select **Manage your apps** > **Upload an app** > **Upload a custom app**.
gulp ngrok-serve
git clone https://github.com/OfficeDev/Microsoft-Teams-Samples.git ```
-Following are the steps to create a channel or group tab:
-
-* [Generate your application with a channel or group tab](#generate-your-application-with-a-channel-or-group-tab-1)
-* [Establish a secure tunnel to your tab](#establish-a-secure-tunnel-to-your-tab-1)
-* [Update your application](#update-your-application)
-* [Build and run your application](#build-and-run-your-application-1)
-* [Update your app package with Developer Portal](#update-your-app-package-with-developer-portal)
-* [Preview your app in Teams](#preview-your-app-in-teams)
-
-### Generate your application with a channel or group tab
+## Generate your application with a channel or group tab
1. Open Visual Studio and select **Open a project or solution**.
Following are the steps to create a channel or group tab:
<details> <summary><b>Review the source code</b></summary>
-#### Startup.cs
+### Startup.cs
This project was created from an ASP.NET Core 3.1 web application empty template with the **Advanced * Configure for HTTPS** check box selected at setup. The MVC services are registered by the dependency injection framework's `ConfigureServices()` method. Additionally, the empty template doesn't enable serving static content by default, so the static files middleware is added to the `Configure()` method using the following code:
public void Configure(IApplicationBuilder app)
} ```
-#### wwwroot folder
+### wwwroot folder
In ASP.NET Core, the web root folder is where the application looks for static files.
-#### Index.cshtml
+### Index.cshtml
ASP.NET Core treats files called **Index** as the default or home page for the site. When your browser URL points to the root of the site, **Index.cshtml** is displayed as the home page for your application.
-#### Tab.cs
+### Tab.cs
This C# file contains a method that is called from **Tab.cshtml** during configuration.
-#### AppManifest folder
+### AppManifest folder
This folder contains the following required app package files:
This folder contains the following required app package files:
These files need to be zipped in an app package for use in uploading your tab to Teams. When a user chooses to add or update your tab, Teams loads the `configurationUrl` specified in your manifest, embeds it in an IFrame, and renders it in your tab.
-#### .csproj
+### .csproj
In the Visual Studio Solution Explorer window, right-click on the project and select **Edit Project File**. At the end of the file, you see the following code that creates and updates your zip folder when the application builds:
In the Visual Studio Solution Explorer window, right-click on the project and se
</details>
-### Establish a secure tunnel to your tab
+## Establish a secure tunnel to your tab
At the command prompt in the root of your project directory, run the following command to establish a secure tunnel to your tab:
ngrok http 3978 --host-header=localhost
Ensure that you keep the command prompt with ngrok running and make a note of the URL.
-### Update your application
+## Update your application
1. Open Visual Studio Solution Explorer and go to the **Pages** > **Shared** folder and open **_Layout.cshtml**, and add the following to the <head> tags section:
Ensure that you keep the command prompt with ngrok running and make a note of th
1. Save the updated **Tab.cshtml**.
-### Build and run your application
+## Build and run your application
1. In Visual Studio, select **F5** or choose **Start Debugging** from the **Debug** menu.
Ensure that you keep the command prompt with ngrok running and make a note of th
> App Studio can be used to edit your `manifest.json` file and upload the completed package to Teams. You can also manually edit the `manifest.json` file. If you do, ensure that you build the solution again to create the `tab.zip` file to upload. >
-### Update your app package with Developer Portal
+## Update your app package with Developer Portal
1. Go to Teams. If you use the [web-based version](https://teams.microsoft.com), you can inspect your front-end code using your browser's [developer tools](~/tabs/how-to/developer-tools.md).
Ensure that you keep the command prompt with ngrok running and make a note of th
1. Open **Apps** and select **Import app**.
- <! TBD: This steps seems to be removed from main now so commenting it for now.
-
- Select **Import an existing app** in the **Manifest editor** to begin updating the app package for your tab. The source code comes with its own partially complete manifest. The name of your app package is `tab.zip`. It is available from the following path:
- >
+ <! TBD: This steps seems to be removed from main now so commenting it for now.
+ Select **Import an existing app** in the **Manifest editor** to begin updating the app package for your tab. The source code comes with its own partially complete manifest. The name of your app package is `tab.zip`. It is available from the following path:
+ >
1. The name of your app package is `tab.zip`. It's available in the following path:
Ensure that you keep the command prompt with ngrok running and make a note of th
1. In the Domains section, domains from your tabs must contain your ngrok URL without the HTTPS prefix `<yourngrokurl>.ngrok.io`.
-### Preview your app in Teams
+## Preview your app in Teams
1. Select **Preview in Teams** from the Developer Portal toolbar, Developer Portal informs you that your app is sideloaded successfully. The **Add** page appears for your app in Teams.
Ensure that you keep the command prompt with ngrok running and make a note of th
git clone https://github.com/OfficeDev/Microsoft-Teams-Samples.git ```
-Following are the steps to create a channel or group tab:
-
-* [Generate your application with a channel or group tab](#generate-your-application-with-a-channel-or-group-tab-2)
-* [Establish a secure tunnel to your tab](#establish-a-secure-tunnel-to-your-tab-2)
-* [Update your application](#update-your-application-1)
-* [Build and run your application](#build-and-run-your-application-2)
-* [Update your app package with Developer Portal](#update-your-app-package-with-developer-portal-1)
-* [Preview your app in Teams](#preview-your-app-in-teams-1)
-
-### Generate your application with a channel or group tab
+## Generate your application with a channel or group tab
1. Open Visual Studio and select **Open a project or solution**.
Following are the steps to create a channel or group tab:
<details> <summary><b>Review the source code</b></summary>
-#### Startup.cs
+### Startup.cs
This project was created from an ASP.NET Core 3.1 web application empty template with the **Advanced - Configure for HTTPS** check box selected at setup. The MVC services are registered by the dependency injection framework's `ConfigureServices()` method. Additionally, the empty template doesn't enable serving static content by default, so the static files middleware is added to the `Configure()` method using the following code:
public void Configure(IApplicationBuilder app)
} ```
-#### wwwroot folder
+### wwwroot folder
In ASP.NET Core, the web root folder is where the application looks for static files.
-#### AppManifest folder
+### AppManifest folder
This folder contains the following required app package files:
This folder contains the following required app package files:
These files need to be zipped in an app package for use in uploading your tab to Teams.
-#### .csproj
+### .csproj
In the Visual Studio Solution Explorer window, right-click on the project and select **Edit Project File**. At the end of the file you, see the following code that creates and updates your zip folder when the application builds:
In the Visual Studio Solution Explorer window, right-click on the project and se
</ItemGroup> ```
-#### Models
+### Models
**ChannelGroup.cs** presents a message object and methods that can be called from the controllers during configuration.
-#### Views
+### Views
These are the different views in ASP.NET Core MVC:
These are the different views in ASP.NET Core MVC:
* Shared: The partial view markup **_Layout.cshtml** contains the application's overall page structure and shared visual elements that also reference the Teams Library.
-#### Controllers
+### Controllers
The controllers use the `ViewBag` property to transfer values dynamically to the views. </details>
-### Establish a secure tunnel to your tab
+## Establish a secure tunnel to your tab
At the command prompt in the root of your project directory, run the following command to establish a secure tunnel to your tab:
ngrok http 3978 --host-header=localhost
Ensure that you keep the command prompt with ngrok running and make a note of the URL.
-### Update your application
+## Update your application
1. Open Visual Studio Solution Explorer and go to the **Views** > **Shared** folder and open **_Layout.cshtml**, and add the following to the <head> tags section:
Ensure that you keep the command prompt with ngrok running and make a note of th
1. Make sure to save the updated **Tab.cshtml**.
-### Build and run your application
+## Build and run your application
1. In Visual Studio, select **F5** or choose **Start Debugging** from the **Debug** menu.
Ensure that you keep the command prompt with ngrok running and make a note of th
> [!TIP] > You need to have both your application in Visual Studio and ngrok running to complete the steps provided in this article. If you need to stop running your application in Visual Studio to work on it, **keep ngrok running**. It listens and resumes routing your application's request when it restarts in Visual Studio. If you have to restart the ngrok service it returns a new URL and you have to update your application with the new URL.
-### Update your app package with Developer Portal
+## Update your app package with Developer Portal
1. Go to Teams. If you use the [web-based version](https://teams.microsoft.com), you can inspect your front-end code using your browser's [developer tools](~/tabs/how-to/developer-tools.md).
Ensure that you keep the command prompt with ngrok running and make a note of th
1. In the Domains section, domains from your tabs must contain your ngrok URL without the HTTPS prefix `<yourngrokurl>.ngrok.io`.
-### Preview your app in Teams
+## Preview your app in Teams
1. Select **Preview in Teams** from the Developer Portal toolbar, Developer Portal informs you that your app is sideloaded successfully. The **Add** page appears for your app in Teams.
Ensure that you keep the command prompt with ngrok running and make a note of th
::: zone-end +
+Blazor lets you build interactive web UIs using C#, instead of JavaScript. You can create a tab app and a bot app with Blazor and the latest version of Visual Studio.
++
+> [!NOTE]
+> Teams Toolkit doesn't support the message extension capability.
+
+Here's a list of tools you require for building and deploying your app.
+
+| &nbsp; | Install | For using... |
+| | | |
+| **Required** | &nbsp; | &nbsp; |
+| &nbsp; | [Visual Studio version 17.2.0 preview 2.1](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=enterprise&ch=pre&rel=17)| Select Visual Studio Enterprise 2022 Preview (version 17.2.0 preview 2.1). |
+| &nbsp; | [Microsoft Teams](https://www.microsoft.com/en-us/microsoft-teams/download-app) | Microsoft Teams to collaborate with everyone you work with through apps for chat, meetings and calls - all in one place. |
+| &nbsp; | [Microsoft Edge](https://www.microsoft.com/edge) (recommended) or [Google Chrome](https://www.google.com/chrome/) | A browser with developer tools. |
+
+## Prepare development environment
+
+After you've installed the required tools, set up the development environment.
+
+### Install Microsoft Teams Toolkit
+
+The Teams Toolkit helps simplify the development process with tools to provision and deploy cloud resources for your app, publish to the Teams store, and more. You can use the toolkit with Visual Studio, or as a Command Line Interface (called `teamsfx`).
+
+# [Latest version of the Visual Studio](#tab/vs)
+
+You can use the latest version of the Visual Studio to develop Teams apps with Blazor Server in .NET.
+
+To install the Microsoft Teams Toolkit extension:
+
+1. Download the latest version of the Visual Studio.
+1. Open `vs_enterprise__3bed52501a604464b1eff2ce580fd4eb.exe` from your download folder.
+1. Select **Continue** in the **Visual Studio Installer** page to configure your installation.
+
+ :::image type="content" source="../../assets/images/teams-toolkit-v2/blazor/visual-studio-installer.PNG" alt-text="Screenshot of Visual Studio Installer with continue options highlighted in red.":::
+
+1. Select **ASP.NET and web development** under **Workloads**.
+
+1. Select **Microsoft Teams development tools** under **Installation details**.
+
+1. Select **Install**.
+
+ :::image type="content" source="../../assets/images/teams-toolkit-v2/blazor-vs-preview2/vs.install.PNG" alt-text="Screenshot of Visual Studio Enterprise Preview with the option Asp.NET, web development, and Microsoft Teams development tools under installation details and install highlighted in red.":::
+
+Your Visual Studio is installed in a few minutes.
+
+# [Command line](#tab/cli)
+
+To install the TeamsFx CLI, use the `npm` package
+
+``` bash
+npm install -g @microsoft/teamsfx-cli
+```
+
+Depending on your configuration, you may need to use `sudo` to install the CLI:
+
+``` bash
+sudo npm install -g --unsafe-perm @microsoft/teamsfx-cli
+ ```
+
+This condition is more common on Linux and macOS systems.
+
+Ensure you add the npm global cache to your PATH. This step is normally done as part of the Node.js installer.
+
+You can use the CLI with the `teamsfx` command. Verify that the command is working by running `teamsfx -h`.
+
+> [!CAUTION]
+> Before you can run TeamsFx in PowerShell terminals, you must enable the remote signed execution policy for PowerShell.
+++
+## Set up your Teams development tenant
+
+A tenant is like a space or a container for your organization in Teams, where you chat, share files, and run meetings. This space is also where you sideload and test your app. Let's verify if you're ready to develop with the tenant.
+
+### Enable sideloading option
+
+After creating the app, you must load your app in Teams without distributing it. This process is known as sideloading. Sign in to your Microsoft 365 account to view this option.
+
+Do you already have a tenant, and do you have the admin access? Let's check if you really do!
+
+Verify if you can sideload apps in Teams:
+
+1. In the Teams client, select **Store** icon.
+1. Select **Manage your apps**.
+1. Select **Upload a custom app**. If you see Upload a customized app option, sideloading apps is enabled.
+
+ :::image type="content" source="../../assets/images/teams-toolkit-v2/blazor/upload-custom-app.PNG" alt-text="Screenshot of Manage your apps in Teams with the Upload an app dialog open. Apps icon, Manage your apps, Upload an app, and Upload a customised app options highlighted in red.":::
+
+ > [!NOTE]
+ > If Teams doesn't show the option to upload a custom app, connect to your Teams administrator.
+
+### Create a free Teams developer tenant (optional)
+
+If you don't have a Teams developer account, you can get it for free. Join the Microsoft 365 developer program!
+
+1. Go to the [Microsoft 365 developer program](https://developer.microsoft.com/microsoft-365/dev-program).
+1. Select **Join Now** and follow the onscreen instructions.
+1. In the welcome screen, select **Set up E5 subscription**.
+1. Set up your administrator account. After you finish, the following screen appears.
+
+ :::image type="content" source="../../assets/images/build-your-first-app/dev-program-subscription.PNG" alt-text="Screenshot of Microsoft 365 Developer Program displaying your Microsoft 365 developer subscriptions for the blazor app.":::
+
+1. Sign in to Teams using the administrator account you just set up. Verify that you have the **Upload a custom app** option in Teams.
+
+## Get a free Azure account
+
+If you wish to host your app or access resources in Azure, you must have an Azure subscription. [Create a free account](https://azure.microsoft.com/free/) before you begin.
+
+Now you've got all tools and set up your accounts. Next, let's set up your development environment and start building!
+
+## Create project workspace for your tab app
+
+Start Teams app development by creating your first app. This app uses the tab capability.
++
+This tutorial walks you through the steps to create, run, and deploy your first Teams app using .NET/Blazor.
+
+In this section, you can learn:
+
+1. [How to set up a new tab project with Teams Toolkit](#create-your-tab-project)
+1. [About the directory structure of your app](#take-a-tour-of-the-source-code-for-teams-tab-app)
+
+## Create your tab project
+
+Use Teams Toolkit to create your first tab project. The toolkit takes you through a series of pages to create and configure your Teams app project:
+
+1. **Create a new project** page: You select the project type.
+1. **Configure your new project** page: You enter the project details.
+1. **Create a new Teams application** page: You select the Teams app capabilities.
+
+**To create your tab project workspace**
+
+1. Open the latest version of Visual Studio.
+
+1. Select **Create a new project**.
+
+ :::image type="content" source="../../assets/images/teams-toolkit-v2/blazor/vs-select-project.png" alt-text="Screenshot of Visual Studio with Create a new project option highlighted in red for blazor app.":::
+
+ The **Create a new project** page appears.
+
+1. Select the project details.
+
+ Select the project type:
+
+ 1. Search for **Microsoft Teams** from templates dropdown list.
+
+ 1. Select **Microsoft Teams App** as the template.
+
+ 1. Select **Next**.
+
+ :::image type="content" source="../../assets/images/teams-toolkit-v2/blazor/vs-select-teams-app.png" alt-text="Screenshot of Create a new project with Next option highlighted in red for blazor app creation.":::
+
+ The **Configure your new project** page appears.
+
+1. Configure the new project details.
+
+ Select the following project configuration:
+
+ 1. Enter a suitable name for your project.
+
+ > [!NOTE]
+ > You can note that the project name you enter is automatically filled in as the **Solution name** also. If you want, you can change the solution name with no affect on project name.
+
+ 1. Select the folder path where you want to create the project workspace.
+
+ 1. Enter a different solution name, if you want.
+
+ 1. Check the option to save the project and solution in the same folder, if you want. For this tutorial, you don't need this option.
+
+ 1. Select **Create**.
+
+ :::image type="content" source="../../assets/images/teams-toolkit-v2/blazor-vs-preview2/configure-new-project.PNG" alt-text="Screenshot1 of Configure your new project with Create option highlighted in red.":::
+
+ The **Create a new Teams application** page appears.
+
+1. Select Teams app feature.
+
+ Select app feature:
+
+ 1. Select the **Tab** as the capability for your app.
+
+ 1. Select **Create**.
++
+Your Teams tab app is created in a few seconds.
++
+<details>
+<summary>A quick recap of creating a Teams tab app.</summary>
+Watch this short recap for creating a Teams tab app.
++
+### Take a tour of the source code for Teams tab app
+
+After project creation, you have the components to build a basic personal app. You can view the project directory structure in the **Solution Explorer** pane of the Visual Studio.
++
+Teams Toolkit creates a scaffolding for your project based on the capabilities you selected. Among other files, Teams Toolkit maintains:
+
+| Folder name | Contents |
+| | |
+| App icons | The app icons are stored as PNG files in `color.png` and `outline.png`. |
+| `manifest.json` | The app manifest for publishing through the Developer Portal for Teams is stored in `Properties/manifest.json`. |
+| `BackendController.cs` | A backend controller is provided in `Controllers/BackendController.cs` for assisting with authentication. |
+| `Pages/Tab.razor` | The app manifest for publishing through the Developer Portal for Teams is stored in `Properties/manifest.json`. |
+| `TeamsFx.cs` and `JS/src/index.js` | The content is used for initializing communications with the Teams host. |
+
+You can add backend functionality by adding other ASP.NET Core controllers to your application.
+</details>
+
+## Build and run your first Teams tab app
+
+After you set up your project workspace with Teams Toolkit, build your tab project.
+
+To build and run your app:
+
+1. Select **Project** > **Teams Toolkit** > **Prepare Teams App Dependencies**.
+
+ :::image type="content" source="../../assets/images/teams-toolkit-v2/blazor-vs-preview2/configure-msteamsapp_1.png" alt-text="Screenshot5 of Visual Studio with Project, Teams Toolkit, and Prepare Teams App Dependencies options are highlighted in red.":::
+
+1. Select your Microsoft 365 account or **Add an account** to sign in.
+
+ :::image type="content" source="../../assets/images/teams-toolkit-v2/m365-account_1.PNG" alt-text="Screenshot6 of Microsoft 365 Account with Continue option highlighted in red.":::
+
+1. Select **Debug** > **Start Debugging** or select **F5** to run your app in debug mode.
+ <!-- markdownlint-disable MD033 -->
+ <details>
+ <summary>Learn what happens when you run your app locally in the debugger.</summary>
+
+ When you select **F5**, Teams Toolkit:
+
+ 1. Registers your application with Azure Active Directory.
+ 1. Registers your application for sideloading in Teams.
+ 1. Starts your application backend running locally.
+ 1. Starts your application front-end hosted locally.
+ 1. Starts Teams in a web browser with a command to instruct Teams to side load the application (the URL is registered inside the application manifest).
+
+ </details>
+
+1. Install the self-signed SSL certificate for local debugging, if requested.
+
+ :::image type="content" source="../../assets/images/teams-toolkit-v2/ssl-prompt.png" alt-text="Screenshot7 of Security Warning with Yes option highlighted in red.":::
+
+ Teams is loaded in a web browser.
+
+1. Select **Add** when prompted to install the app to Teams.
+
+ :::image type="content" source="../../assets/images/teams-toolkit-v2/blazor-vs-preview2/blazor-add-app_1.png" alt-text="Screenshot8 of BlazorApp local debug with Add option highlighted in red.":::
+
+ Congratulations, your first tab app is running in your local environment!
+
+ :::image type="content" source="../../assets/images/teams-toolkit-v2/blazor-vs-preview2/app-local.png" alt-text="Screenshot9 of Microsoft Teams with Your app is running in your local environment highlighted in red.":::
+
+1. Move through the page to view the user details.
+
+ :::image type="content" source="../../assets/images/teams-toolkit-v2/blazor-vs-preview2/authorize-user-info.png" alt-text="Screenshot10 of Microsoft Teams with Authorize option highlighted in red.":::
+
+1. Select **Authorize** to let your app retrieve user details using Microsoft Graph.
+
+ The app requests permission to grant access for displaying user details.
+
+ :::image type="content" source="../../assets/images/teams-toolkit-v2/blazor-vs-preview2/ms-graph-permission_1.png" alt-text="Screenshot11 of Permissions requested displaying the App info.":::
+
+1. Select **Accept** to let your app access user details.
+
+ Your photograph and details appear in your **Personal Tab**.
+
+ :::image type="content" source="../../assets/images/teams-toolkit-v2/blazor/tab-user-info.png" alt-text="Screenshot12 of your Personal Tab displaying basic information.":::
+
+ You can do normal debugging activities, such as setting breakpoints, as if it were any other web application. The app supports hot reloading. If you change any file within the project, the page will be reloaded.
+
+ <!-- markdownlint-disable MD033 -->
+ <details>
+ <summary>Learn how to troubleshoot if your app doesn't run locally.</summary>
+
+ To run your app in Teams, you need a Microsoft 365 development account that allows app sideloading. You can learn more about it in the Prerequisites section.
+
+ </details>
+
+1. Stop debugging in Visual Studio.
+
+## Preview your first Teams tab app
+
+You've learned to create, build, and run Teams app with tab capability. The following final steps are to deploy your app on Azure and Preview in Teams:
+
+1. [Provision your tab app in the cloud](#to-provision-your-tab-app-in-the-cloud): You can provision your tab app in the cloud.
+1. [Deploy your tab app to cloud](#to-deploy-your-tab-app-to-cloud): You can deploy your tab app to cloud.
+1. [Preview your tab app in Teams](#to-preview-your-tab-app-in-teams): Your tab app opens in Teams.
+
+ Let's deploy the first app with tab capability on Azure using Teams Toolkit.
+
+### **To provision your tab app in the cloud**
+
+1. Select **Project** > **Teams Toolkit** > **Provision in the Cloud**.
+
+ :::image type="content" source="../../assets/images/teams-toolkit-v2/blazor-vs-preview2/vs-build-provision_1.png" alt-text="Screenshot13 of Visual Studio with Project, Teams Toolkit, and Provision in the Cloud options are highlighted in red.":::
+
+1. Enter the subscription and resource group details in the **Provision** dialog:
+ 1. Select the subscription name from **Subscription name** dropdown list.
+ 1. Select the resource group from **Resource group** dropdown list or select **New** to add the resource group generated for your app.
+ 1. Select your **Region**, if new resource group is created.
+ 1. Select **Provision**.
+
+ :::image type="content" source="../../assets/images/teams-toolkit-v2/blazor-vs-preview2/select-subscription.PNG" alt-text="Screenshot14 of Provision with New and Provision highlighted in red.":::
+
+ Provision warning displays.
+
+1. Select **Provision**.
+
+ :::image type="content" source="../../assets/images/teams-toolkit-v2/blazor-vs-preview2/provision-warning.PNG" alt-text="Screenshot15 of Teams Toolkit with Provision highlighted in red.":::
+
+ It takes a few minutes for your resource group to provision in the cloud.
+
+1. After the provision is complete, select **OK**.
+
+ :::image type="content" source="../../assets/images/teams-toolkit-v2/provision-complete.png" alt-text="Screenshot16 of Teams Toolkit with OK option highlighted in red.":::
+
+1. Select **View Provisioned Resources** to view on Azure portal.
+
+ :::image type="content" source="../../assets/images/teams-toolkit-v2/blazor-vs-preview2/view-resource.PNG" alt-text="Screenshot17 of Teams Toolkit with View Provisioned Resources highlighted in red.":::
+
+1. Sign in to your Azure portal account on sign-in prompt.
+
+ Your app-dev-rg appears.
+
+ :::image type="content" source="../../assets/images/teams-toolkit-v2/blazor-vs-preview2/app-dev-rg-azure.PNG" alt-text="Screenshot18 of Blazorapp-dev-rg displaying the Resources provisioned in the Azure portal.":::
+
+ Your resources are provisioned in the Azure portal!
+
+#### **To deploy your tab app to cloud**
+
+1. Select **Project** > **Teams Toolkit** > **Deploy to the Cloud**.
+
+ :::image type="content" source="../../assets/images/teams-toolkit-v2/blazor-vs-preview2/vs-build-deploytocloud_1.png" alt-text="Screenshot19 of Visual Studio with Project, Teams Toolkit, and Deploy to the Cloud options highlighted in red.":::
+
+1. Select **OK**.
+
+ :::image type="content" source="../../assets/images/teams-toolkit-v2/blazor-vs-preview2/deploy-success.PNG" alt-text="Screenshot20 of Teams Toolkit with OK option highlighted in red.":::
+
+ Your tab app is successfully deployed to the cloud!
+
+#### **To preview your tab app in Teams**
+
+1. Select **Project** > **Teams Toolkit** > **Preview in Teams**.
+
+ :::image type="content" source="../../assets/images/teams-toolkit-v2/blazor-vs-preview2/vs-build-preview_1.png" alt-text="Screenshot21of Visual Studio with Project, Teams Toolkit, and Preview in Teams options are highlighted in red.":::
+
+1. Select **Add** when prompted to install the app to Teams.
+
+ :::image type="content" source="../../assets/images/teams-toolkit-v2/blazor-vs-preview2/blazor-add-app.PNG" alt-text="Screenshot22 of Teams displaying the Add option for adding Blazor app. The Add option highlighted in red.":::
+
+ Congratulations, your first tab app is running in your Azure environment!
+
+ :::image type="content" source="../../assets/images/teams-toolkit-v2/blazor-vs-preview2/blazor-tab-app-azure.PNG" alt-text="Screenshot23 of Microsoft Teams with Your app is running in your Azure environment highlighted in red.":::
+
+1. Move through the page to view the user details.
+
+ :::image type="content" source="../../assets/images/teams-toolkit-v2/blazor-vs-preview2/authorize-user-info.png" alt-text="Screenshot24 of Microsoft Teams with Authorize option under Personal Tab highlighted in red.":::
+
+1. Select **Authorize** to let your app retrieve user details using Microsoft Graph.
+
+ The app requests permission to grant access for displaying user details.
+
+ :::image type="content" source="../../assets/images/teams-toolkit-v2/blazor-vs-preview2/ms-graph-permission_1.png" alt-text="Screenshot25 of Permissions requested displaying the App info.":::
+
+1. Select **Accept** to let your app access user details.
+
+ Your photograph and details appear in your **Personal Tab**.
+
+ :::image type="content" source="../../assets/images/teams-toolkit-v2/blazor-vs-preview2/azure-user-info.png" alt-text="Screenshot26 of your Personal Tab displaying basic information.":::
+
+## Congratulations
+
+You've done it!
+
+You've completed the tutorial to build a tab app with Blazor.
++ ## Next step > [!div class="nextstepaction"]
platform Dashboard Teams Toolkit https://github.com/MicrosoftDocs/msteams-docs/commits/main/msteams-platform/tabs/how-to/dashboard-teams-toolkit.md
+
+ Title: Create a dashboard in Teams
+
+description: Describes Dashboard, widget and Graph API call in Teams toolkit
+
+ms.localizationpriority: medium
+ Last updated : 01/17/2023++
+# Build a dashboard tab app
+
+A dashboard is a tool to track, analyze, and display data to gain insight of an organization or a specific process. Dashboards in Teams allow you to monitor and view important metrics.
+
+The dashboard tab template from Teams Toolkit allows you to get started with integrating a canvas with multiple cards that provide an overview of content in Teams. You can:
+
+* Use widgets to display content from apps and services within your dashboard tab.
+* Integrate your app with Graph API to visualize details about the implementation of the selected data.
+* Create customizable dashboards that allow your business to set specific goals that help you track the information you need to view in multiple areas and across departments.
++
+Your team can get the latest updates from different sources in Teams using the Teams dashboard tab app. Use dashboard tab apps to connect numerous metrics, data sources, APIs, and services to help your business extract relevant information from the sources and present it to the users. For more information about creating a dashboard tab app and the source code directory structure, see [step-by-step guide](#step-by-step-guide).
+
+## Add a new dashboard
+
+After you've created a dashboard tab app, you can add a new dashboard.
+
+To add a new dashboard, follow these steps:
+
+1. [Create a dashboard class](#create-a-dashboard-class)
+1. [Override methods to customize dashboard tab app](#override-methods-to-customize-dashboard-tab-app)
+1. [Add a route for the new dashboard tab app](#add-a-route-for-the-new-dashboard-tab-app)
+1. [Modify manifest to add a new dashboard tab app](#modify-manifest-to-add-a-new-dashboard-tab-app)
+
+### Create a dashboard class
+
+Create a file with the .tsx extension for your dashboard in the tabs/src/views/dashboards directory, for example, YourDashboard.tsx. Then, create a class that extends the [Dashboard](https://github.com/OfficeDev/TeamsFx/wiki/) class:
+
+```typescript
+
+export default class YourDashboard extends Dashboard {}
+
+```
+
+> [!NOTE]
+> All methods are optional. If you don't override any method, the default dashboard layout is used.
+
+### Override methods to customize dashboard tab app
+
+The `dashboard` class provides few methods that you can override to customize the dashboard layout. The following table lists the methods that you can override:
+
+| **Methods** | **Function** |
+|||
+| `rowHeights()` | Customize the height of each row of the dashboard. |
+| `columnWidths()` | Customize how many columns the dashboard has at most and the width of each column. |
+| `dashboardLayout()` | Define widgets layout. |
+
+The following code is an example to customize the dashboard layout:
+
+```typescript
+export default class YourDashboard extends Dashboard {
+ protected rowHeights(): string | undefined {
+ return "500px";
+ }
+
+ protected columnWidths(): string | undefined {
+ return "4fr 6fr";
+ }
+
+ protected dashboardLayout(): JSX.Element | undefined {
+ return (
+ <>
+ <SampleWidget />
+ <div style={oneColumn("6fr 4fr")}>
+ <SampleWidget />
+ <SampleWidget />
+ </div>
+ </>
+ );
+ }
+}
+```
+
+### Add a route for the new dashboard tab app
+
+You must link your widget to a data source file. The widget picks up the data that's presented in the dashboard from the source file.
+
+Open tabs/src/App.tsx file and add a route for the new dashboard. Here's an example:
+
+```typescript
+import YourDashboard from "./views/dashboards/YourDashboard";
+
+export default function App() {
+ ...
+ <Route exact path="/yourdashboard" component={YourDashboard} />
+ ...
+}
+```
+
+### Modify manifest to add a new dashboard tab app
+
+Open templates/appPackage/manifest.template.json file and add a new dashboard tab under the `staticTabs`. Here's an example:
+
+```json
+{
+ "name": "Your Dashboard",
+ "entityId": "yourdashboard",
+ "contentUrl": "{{state.fx-resource-frontend-hosting.endpoint}}{{state.fx-resource-frontend-hosting.indexPath}}/yourdashboard",
+ "websiteUrl": "{{state.fx-resource-frontend-hosting.endpoint}}{{state.fx-resource-frontend-hosting.indexPath}}/yourdashboard",
+ "scopes": ["personal"]
+}
+```
+
+## Customize the dashboard layout
+
+Teamsfx provides convenient methods to define and modify the layout of the dashboard. The following are the methods:
+
+* Three widgets in a row with the height of 350 px occupying 20 percent, 60 percent, and 20 percent of the width, respectively.
+
+ ```typescript
+ export default class SampleDashboard extends Dashboard {
+ protected rowHeights(): string | undefined {
+ return "350px";
+ }
+
+ protected columnWidths(): string | undefined {
+ return "2fr 6fr 2fr";
+ }
+
+ protected dashboardLayout(): undefined | JSX.Element {
+ return (
+ <>
+ <ListWidget />
+ <ChartWidget />
+ <NewsWidget />
+ </>
+ );
+ }
+ }
+ ```
+
+ :::image type="content" source="../../assets/images/sbs-create-a-new-dashboard/customize-dashboard-layout.png" alt-text="Screenshot shows the customized dashboard layout.":::
+
+* Two widgets in a row with a width of 600 px and 1100 px. The height of the first line is the maximum height of its content, and the height of the second line is 400 px.
+
+ ```typescript
+ export default class SampleDashboard extends Dashboard {
+ protected rowHeights(): string | undefined {
+ return "max-content 400px";
+ }
+
+ protected columnWidths(): string | undefined {
+ return "600px 1100px";
+ }
+
+ protected dashboardLayout(): undefined | JSX.Element {
+ return (
+ <>
+ <ListWidget />
+ <ChartWidget />
+ <NewsWidget />
+ </>
+ );
+ }
+ }
+ ```
+
+ :::image type="content" source="../../assets/images/sbs-create-a-new-dashboard/customize-dashboard-layout2.png" alt-text="Screenshot shows the customization of height and width of the dashboard layout.":::
+
+* Arrange two widgets in a column.
+
+ ```typescript
+ import { oneColumn } from '../lib/Dashboard.styles';
+ export default class SampleDashboard extends Dashboard {
+ protected rowHeights(): string | undefined {
+ return "max-content";
+ }
+
+ protected columnWidths(): string | undefined {
+ return "4fr 6fr";
+ }
+
+ protected dashboardLayout(): undefined | JSX.Element {
+ return (
+ <>
+ <NewsWidget />
+ <div style={oneColumn()}>
+ <ListWidget />
+ <ChartWidget />
+
+ </div>
+ </>
+ );
+ }
+ }
+ ```
+
+ :::image type="content" source="../../assets/images/sbs-create-a-new-dashboard/widget-customize.png" alt-text="Screenshot shows the two-widget customization.":::
+
+* Customize the height of widgets in a row.
+
+ The following code can achieve a height of 400 px for the `ListWidget` and a height of 350 px for the `ChartWidget`:
+
+ ```typescript
+ import { oneColumn } from '../lib/Dashboard.styles';
+ export default class SampleDashboard extends Dashboard {
+ protected rowHeights(): string | undefined {
+ return "max-content";
+ }
+
+ protected columnWidths(): string | undefined {
+ return "4fr 6fr";
+ }
+
+ protected dashboardLayout(): undefined | JSX.Element {
+ return (
+ <>
+ <NewsWidget />
+ <div style={oneColumn("400px 350px")}>
+ <ListWidget />
+ <ChartWidget />
+ </div>
+ </>
+ );
+ }
+ }
+ ```
+
+ :::image type="content" source="../../assets/images/sbs-create-a-new-dashboard/chart-widget.png" alt-text="Screenshot shows the customization of a chart widget.":::
+
+### Dashboard tab app abstraction
+
+To adjust the layout of the dashboard, Teamsfx provides a `dashboard` class for the developers to implement a dashboard.
+
+The following code is an example of a dashboard class:
+
+```typescript
+interface IDashboardState {
+ isMobile?: boolean;
+ observer?: ResizeObserver;
+}
+
+/**
+ * The dashboard class which is the base class for all dashboard components.
+ */
+export class Dashboard extends Component<any, IDashboardState> {
+ private ref: React.RefObject<HTMLDivElement>;
+
+ /**
+ * Constructor for the dashboard class. Initializes the dashboard state.
+ * @param props The properties for the dashboard.
+ */
+ constructor(props: any) {
+ super(props);
+ this.state = {
+ isMobile: undefined,
+ observer: undefined,
+ };
+ this.ref = React.createRef<HTMLDivElement>();
+ }
+
+ /**
+ * This method is invoked immediately after a component is mounted. It's a good place to fetch data from server.
+ */
+ componentDidMount(): void {
+ // Observe the dashboard div for resize events
+ const observer = new ResizeObserver((entries) => {
+ for (let entry of entries) {
+ if (entry.target === this.ref.current) {
+ const { width } = entry.contentRect;
+ this.setState({ isMobile: width < 600 });
+ }
+ }
+ });
+ observer.observe(this.ref.current!);
+ }
+
+ /**
+ * This method is invoked immediately when a component is unmounted. It's a good place to clean up the resources.
+ */
+ componentWillUnmount(): void {
+ // Unobserve the dashboard div for resize events
+ if (this.state.observer && this.ref.current) {
+ this.state.observer.unobserve(this.ref.current);
+ }
+ }
+
+ /**
+ * Define the dashboard default layout, you can edit the code here to customize your dashboard layout.
+ */
+ render() {
+ return (
+ <>
+ <div
+ ref={this.ref}
+ style={dashboardStyles(
+ this.state.isMobile,
+ this.rowHeights(),
+ this.columnWidths()
+ )}
+ >
+ {this.dashboardLayout()}
+ </div>
+ </>
+ );
+ }
+
+ /**
+ * Implement this method to define the row heights of the dashboard.
+ * For example, if you want to have 3 rows, and the height of the first row is 100px, the height of the second row is 200px, and the height of the third row is 300px, you can return "100px 200px 300px".
+ * @returns The row heights of the dashboard.
+ */
+ protected rowHeights(): string | undefined {
+ return undefined;
+ }
+
+ /**
+ * Implement this method to define the column widths of the dashboard.
+ * For example, if you want to have 3 columns, and each column occupies 1/3 of the full width, you can return "1fr 1fr 1fr".
+ * @returns The column widths of the dashboard.
+ */
+ protected columnWidths(): string | undefined {
+ return undefined;
+ }
+
+ /**
+ * Implement this method to define the dashboard layout.
+ */
+ protected dashboardLayout(): JSX.Element | undefined {
+ return undefined;
+ }
+}
+```
+
+In the `dashboard` class, Teamsfx provides basic layouts with customizable methods. The dashboard is still a react component, and Teamsfx provides basic implementations of functions based on the lifecycle of react components, such as:
+
+* Implementing a basic render logic based on the grid layout.
+* Adding an observer to automatically adapt to mobile devices.
+
+The following are the customizable methods to override:
+
+| File | Content | Recommend to override |
+||||
+| **constructor()** | Initializes the dashboard state and variables. | No |
+| **componentDidMount()** | Invokes after a component is mounted. | No |
+| **componentWillUnmount()** | Invokes when a component is unmounted. | No |
+| **render()** | Invokes when there's an update. The dashboard default layout is defined in this method. | No |
+| **rowHeights()** | Customizes the height of each row of the dashboard. | Yes |
+| **columnWidths()** | Customizes the number of columns the dashboard has at most and the width of each column. | Yes |
+| **dashboardLayout()** | Defines the widget layout in dashboard. | Yes |
+
+## Use a widget in your dashboard
+
+Widgets display configurable information and charts on dashboards. They appear on the widget board where you can pin, unpin, arrange, resize, and customize widgets to reflect your interests. Your widget board is optimized to show relevant widgets and personalized content based on your usage.
+
+### Customize the widget
+
+You can customize the widget by overriding the following methods in the `widget` class:
+
+* Override `headerContent()`, `bodyContent()`, and `footerContent()` to customize the widget.
+
+ ```typescript
+ export class NewsWidget extends Widget<void> {
+
+ headerContent(): JSX.Element | undefined {
+ return (
+ <div style={headerContentStyle()}>
+ <News28Regular />
+ <Text style={headerTextStyle()}>Your News</Text>
+ <Button icon={<MoreHorizontal32Regular />} appearance="transparent" />
+ </div>
+ );
+ }
+
+ bodyContent(): JSX.Element | undefined {
+ return (
+ <div style={contentLayoutStyle()}>
+ <Image src="image.svg" style={imageStyle()} />
+ <Text style={titleStyle()}>Lorem Ipsum Dolor</Text>
+ <Text style={descStyle()}>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Enim, elementum sed</Text>
+ </div>
+ );
+ }
+
+ footerContent(): JSX.Element | undefined {
+ return (
+ <Button
+ appearance="transparent"
+ icon={<ArrowRight16Filled />}
+ iconPosition="after"
+ size="small"
+ style={footerButtonStyle()}
+ onClick={() => { }} // navigate to detailed page
+ >
+ View details
+ </Button>
+ );
+ }
+ }
+ ```
+
+ :::image type="content" source="../../assets/images/sbs-create-a-new-dashboard/override-header-count.png" alt-text="Screenshot shows the example of header, body, and footer content in a widget.":::
+
+* Override `bodyContent()` and `footerContent()` to customize the widget.
+
+ ```typescript
+ export class NewsWidget extends Widget<void> {
+
+ bodyContent(): JSX.Element | undefined {
+ return (
+ <div style={contentLayoutStyle()}>
+ <Image src="image.svg" style={imageStyle()} />
+ <Text style={titleStyle()}>Lorem Ipsum Dolor</Text>
+ <Text style={descStyle()}>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Enim, elementum sed</Text>
+ </div>
+ );
+ }
+
+ footerContent(): JSX.Element | undefined {
+ return (
+ <Button
+ appearance="transparent"
+ icon={<ArrowRight16Filled />}
+ iconPosition="after"
+ size="small"
+ style={footerButtonStyle()}
+ onClick={() => { }} // navigate to detailed page
+ >
+ View details
+ </Button>
+ );
+ }
+ }
+ ```
+
+ :::image type="content" source="../../assets/images/sbs-create-a-new-dashboard/override-body-content-footer-content.png" alt-text="Screenshot shows the body and footer content in a widget.":::
+
+* Override `bodyContent()` to customize the widget.
+
+ ```typescript
+ export class NewsWidget extends Widget<void> {
+
+ bodyContent(): JSX.Element | undefined {
+ return (
+ <div style={contentLayoutStyle()}>
+ <Image src="image.svg" style={imageStyle()} />
+ <Text style={titleStyle()}>Lorem Ipsum Dolor</Text>
+ <Text style={descStyle()}>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Enim, elementum sed</Text>
+ </div>
+ );
+ }
+
+ }
+ ```
+
+ :::image type="content" source="../../assets/images/sbs-create-a-new-dashboard/override-body-content.png" alt-text="Screenshot shows body content in a widget.":::
+
+### Include a data loader
+
+If you want to include a data loader to your widget before the widget is loaded, you can add a property to the state of the widget to indicate that the data loader is loading. You can use this property to show a loading indicator to the user.
+
+The following steps show how to add a property to the state of `ListWidget` and how to use it to show a loading spinner while the data is loading.
+
+1. Define a state type: Define a state type including a property named `loading` that indicates whether the data is loading.
+
+ ```typescript
+ interface ListWidgetState {
+ data: ListModel[];
+ loading?: boolean;
+ }
+ ```
+
+1. Add a data loader: Modify the `bodyContent` method to show a loading spinner if data is loading.
+
+ ```tsx
+ bodyContent(): JSX.Element | undefined {
+ return (
+ <>
+ {this.state.loading !== false ? (
+ <div style={{ display: "grid", justifyContent: "center", height: "100%" }}>
+ <Spinner label="Loading..." labelPosition="below" />
+ </div>
+ ) : (
+ <div style={bodyContentStyle()}>
+ ...
+ </div>
+ )}
+ </>
+ );
+ }
+ ```
+
+1. Hide the footer button if the data is loading.
+
+ The following code is an example of footer button:
+
+ ```tsx
+ footerContent(): JSX.Element | undefined {
+ if (this.state.loading === false) {
+ return (
+ <Button
+ ...
+ </Button>
+ );
+ }
+ }
+ ```
+
+1. Update the state reference: Update the state reference in the widget file to use the new state type and update the state in the `getData` method to set the `loading` property to `false` after the data is loaded.
+
+ Now, the loading spinner is shown while the data is loading. When the data is loaded, the loading spinner is hidden and the list data, and footer button are shown.
+
+ :::image type="content" source="../../assets/images/sbs-create-a-new-dashboard/spinner.gif" alt-text="Graphical representation shows the loading spinner while the data is loading.":::
+
+### Handle empty state
+
+You can display a specific content in your widget when the data is empty. To do so, you need to modify the `bodyContent` method in your widget file to adopt different states of the data.
+
+The following example shows how to display an empty image when the data of `ListWidget` is empty.
+
+```tsx
+bodyContent(): JSX.Element | undefined {
+ let hasData = this.state.data && this.state.data.length > 0;
+ return (
+ <div style={bodyContentStyle()}>
+ {hasData ? (
+ <>
+ {this.state.data?.map((t: ListModel) => {
+ ...
+ })}
+ </>
+ ) : (
+ <div
+ style={{
+ display: "grid",
+ gap: "1rem",
+ justifyContent: "center",
+ alignContent: "center",
+ }}
+ >
+ <Image src="empty-default.svg" height="150px" />
+ <Text align="center">No data</Text>
+ </div>
+ )}
+ </div>
+ );
+ }
+```
+
+You can use a similar approach to remove the footer content of your widget when the data is empty.
+
+```tsx
+footerContent(): JSX.Element | undefined {
+ let hasData = this.state.data && this.state.data.length > 0;
+ if (hasData) {
+ return (
+ <Button
+ ...
+ </Button>
+ );
+ }
+ }
+```
+
+ :::image type="content" source="../../assets/images/sbs-create-a-new-dashboard/no-data.png" alt-text="Screenshot showing no data in the list.":::
+
+### Refresh data as scheduled
+
+The following example shows how to display real-time data in a widget. The widget displays the current time and updates.
+
+```tsx
+import { Widget } from "../lib/Widget";
+
+interface IRefreshWidgetState {
+ data: string;
+}
+
+export class RefreshWidget extends Widget<IRefreshWidgetState> {
+ bodyContent(): JSX.Element | undefined {
+ return <>{this.state.data}</>;
+ }
+
+ async componentDidMount() {
+ setInterval(() => {
+ this.setState({ data: new Date().toLocaleTimeString() });
+ }, 1000);
+ }
+}
+```
+
+You can modify `setInterval` method to call your own function to refresh data like this `setInterval(() => yourGetDataFunction(), 1000)`.
+
+### Widget abstraction
+
+To simplify the development of a widget, Teamsfx provides a `widget` class for developers to inherit to implement a widget that meets their needs without paying much attention to implement the widget layout.
+
+The following code is an example of widget class:
+
+```typescript
+export abstract class Widget<T> extends Component<any, { data?: T | void }> {
+ constructor(props: any) {
+ super(props);
+ this.state = {
+ data: undefined,
+ };
+ }
+
+ /**
+ * This method is invoked immediately after a component is mounted.
+ * It's a good place to fetch data from server.
+ */
+ async componentDidMount() {
+ this.setState({ data: await this.getData() });
+ }
+
+ /**
+ * Define your widget layout, you can edit the code here to customize your widget.
+ */
+ render() {
+ return (
+ <div style={widgetStyles()}>
+ {this.headerContent() && (
+ <div style={headerStyles()}>{this.headerContent()}</div>
+ )}
+ {this.bodyContent() && <div>{this.bodyContent()}</div>}
+ {this.footerContent() && <div>{this.footerContent()}</div>}
+ </div>
+ );
+ }
+
+ /**
+ * Get data required by the widget, you can get data from a api call or static data stored in a file. Override this method according to your needs.
+ * @returns data for the widget
+ */
+ protected async getData(): Promise<T> {
+ return new Promise<T>(() => {});
+ }
+
+ /**
+ * Override this method to customize the widget header.
+ * @returns JSX component for the widget body
+ */
+ protected headerContent(): JSX.Element | undefined {
+ return undefined;
+ }
+
+ /**
+ * Override this method to customize the widget body.
+ * @returns JSX component for the widget body
+ */
+ protected bodyContent(): JSX.Element | undefined {
+ return undefined;
+ }
+
+ /**
+ * Override this method to customize the widget footer.
+ * @returns react node for the widget footer
+ */
+ protected footerContent(): JSX.Element | undefined {
+ return undefined;
+ }
+}
+```
+
+The following are the recommended methods to override:
+
+| Methods | Function | Recommend to override |
+||||
+| **constructor()** | Invokes the initial `this.state` and calls the constructor of the super class `React` component. | No |
+| **componentDidMount()** | Invokes after a component is mounted and assigns a value to the `data` property of the state by calling the `getData()` method. | No |
+| **render()** | Invokes whenever there's an update. The dashboard default layout is defined in this method. | No |
+| **getData()** | Invokes the data needed by the widget. The value returned by this method is set to `this.state.data`. |
+| **headerContent()** | Invokes what the widget header looks like. You can choose to override this method to customize a widget or not, if not, the widget won't have a header. | Yes |
+| **bodyContent()** | Invokes what the widget body looks like. You can choose to override this method to customize a widget or not, if not, the widget won't have a body. | Yes |
+| **footerContent()** | Invokes what the widget footer looks like. You can choose to override this method to customize a widget or not, if not, the widget won't have a footer. | Yes |
+
+### Microsoft Graph Toolkit as widget content
+
+Microsoft Graph Toolkit is a set of renewable, framework-agnostic web components, which helps accessing and working with Microsoft Graph. You can use the Microsoft Graph Toolkit with any web framework or without a framework.
+
+To use Microsoft Graph Toolkit as your widget content, follow these steps:
+
+1. Add SSO feature to your Teams app: Microsoft Teams provides single sign-on (SSO) function for an app to obtain signed in Teams user token to access Microsoft Graph. For more information, refer [SSO feature to your Teams app](../../toolkit/add-single-sign-on.md).
+
+1. Install required `npm` packages.
+
+ Run the following command in your project `tabs` folder to install the required `npm` packages:
+
+ ```bash
+ npm install @microsoft/mgt-react @microsoft/mgt-teamsfx-provider
+
+ ```
+
+1. Add a new Graph Toolkit widget: Create a new widget file in your project tabs/src/views/widgets folder, for example, GraphyWidget.tsx. In this widget, we'll guide users to consent our app to access Microsoft Graph and then show the user's `Todo` list by using Microsoft Graph Toolkit.
+
+ The following code is an example of using `Todo` component from Microsoft Graph Toolkit in widget:
+
+ ```tsx
+ import { Providers, ProviderState, Todo } from "@microsoft/mgt-react";
+ import { TeamsFxProvider } from "@microsoft/mgt-teamsfx-provider";
+
+ import { loginAction } from "../../internal/login";
+ import { TeamsUserCredentialContext } from "../../internal/singletonContext";
+ import { Widget } from "../lib/Widget";
+
+ interface IGraphWidgetState {
+ needLogin: boolean;
+ }
+
+ export class GraphWidget extends Widget<IGraphWidgetState> {
+ protected bodyContent(): JSX.Element | undefined {
+ return <div>{this.state.needLogin === false && <Todo />}</div>;
+ }
+
+ async componentDidMount() {
+ super.componentDidMount();
+
+ // Initialize TeamsFx provider
+ const provider = new TeamsFxProvider(TeamsUserCredentialContext.getInstance().getCredential(), [
+ "Tasks.ReadWrite",
+ ]);
+ Providers.globalProvider = provider;
+
+ // Check if user is signed in
+ if (await this.checkIsConsentNeeded()) {
+ await loginAction(["Tasks.ReadWrite"]);
+ }
+
+ // Update signed in state
+ Providers.globalProvider.setState(ProviderState.SignedIn);
+ this.setState({ needLogin: false });
+ }
+
+ /**
+ * Check if user needs to consent
+ * @returns true if user needs to consent
+ */
+ async checkIsConsentNeeded() {
+ let needConsent = false;
+ try {
+ await TeamsUserCredentialContext.getInstance().getCredential().getToken(["Tasks.ReadWrite"]);
+ } catch (error) {
+ needConsent = true;
+ }
+ return needConsent;
+ }
+ }
+
+ ```
+
+ For more information, refer [Microsoft Graph Toolkit](/graph/toolkit/overview).
+
+1. Add the widget to dashboard layout. Include the new widget in your dashboard file.
+
+ ```tsx
+ ...
+ export default class YourDashboard extends Dashboard {
+ ...
+ protected dashboardLayout(): undefined | JSX.Element {
+ return (
+ <>
+ <GraphWiget />
+ </>
+ );
+ }
+ ...
+ }
+ ```
+
+Now, launch or refresh your Teams app, you'll see the new widget using Microsoft Graph Toolkit.
+
+## Graph API call
+
+Microsoft Graph API is a web API that you can use to communicate with Microsoft cloud and other services. Custom applications can use the Microsoft Graph API to connect to data and use it in custom applications to enhance organizational productivity.
+
+To add a Graph API call:
+
+* [Call Graph API from the front-end (use delegated permissions)](#call-graph-api-from-the-front-end-use-delegated-permissions)
+* [Call Graph API from the back-end (use application permissions)](#call-graph-api-from-the-back-end-use-application-permissions)
+
+### Call Graph API from the front-end (use delegated permissions)
+
+If you want to call a Graph API from the front-end tab, follow these steps:
+
+1. Consent delegated permissions first: You can call `addNewPermissionScope(scopes: string[])` to consent the scopes of permissions you want to add. The consented status is preserved in a global context `FxContext`.
+
+1. Create a Graph client by adding the scope related to the Graph API you want to call.
+
+ ```typescript
+ let teamsfx;
+ teamsfx = FxContextInstance.getTeamsFx();
+ const graphClient = createMicrosoftGraphClient(teamsfx, scope);
+ ```
+
+1. Call the Graph API and parse the response into a certain model.
+
+ ```typescript
+ try {
+ const graphApiResult = await graphClient.api("<GRAPH_API_PATH>").get();
+ // Parse the graphApiResult into a Model you defined, used by the front-end.
+ } catch (e) {}
+ ```
+
+### Call Graph API from the back-end (use application permissions)
+
+If you want to call a Graph API from the back-end, follow these steps:
+
+1. [Consent application permissions](#consent-application-permissions)
+1. [Add an Azure function](#add-an-azure-function)
+1. [Add your logic in Azure function](#add-your-logic-in-azure-function)
+1. [Call the Azure function from the front-end](#call-the-azure-function-from-the-front-end)
+
+#### Consent application permissions
+
+To consent application permissions, follow these steps:
+
+1. Go to [Azure portal](https://ms.portal.azure.com/#home).
+1. Select **Azure Active Directory**.
+1. Select **App registrations** in the left pane.
+1. Select your dashboard app.
+1. Select **API permissions** in the left pane.
+1. Select **Add permission**.
+1. Select **Microsoft Graph**.
+1. Select **Application permissions**.
+1. Find the permissions you need.
+1. Select the **Add permissions** button at the bottom.
+1. Select **Γ£öGrant admin consent**.
+1. Select the **Yes** button to finish the admin consent.
+
+#### Add an Azure function
+
+In the left pane of the Visual Studio Code, select **Teams Toolkit** > **Adding features** > **Azure Functions** > and Enter the function name.
++
+#### Add your logic in Azure function
+
+In the `index.ts`/`index.ts` under the folder named Azure Function, you can add your logic that contains back-end Graph API calling with application permissions. Refer to the following code snippet:
+
+```typescript
+/**
+ * This function handles requests from teamsfx client.
+ * The HTTP request should contain an SSO token queried from Teams in the header.
+ * Before triggering this function, teamsfx binding would process the SSO token and generate teamsfx configuration.
+ *
+ * You should initializes the teamsfx SDK with the configuration and calls these APIs.
+ *
+ * The response contains multiple message blocks constructed into a JSON object, including:
+ * - An echo of the request body.
+ * - The display name encoded in the SSO token.
+ * - Current user's Microsoft 365 profile if the user has consented.
+ *
+ * @param {Context} context - The Azure Functions context object.
+ * @param {HttpRequest} req - The HTTP request.
+ * @param {teamsfxContext} TeamsfxContext - The context generated by teamsfx binding.
+ */
+export default async function run(
+ context: Context,
+ req: HttpRequest,
+ teamsfxContext: TeamsfxContext
+): Promise<Response> {
+ context.log("HTTP trigger function processed a request.");
+
+ // Initialize response.
+ const res: Response = {
+ status: 200,
+ body: {},
+ };
+
+ // Your logic here.
+
+ return res;
+}
+```
+
+#### Call the Azure function from the front-end
+
+Call the Azure function by function name. Refer to the following code snippet to call the Azure function:
+
+```typescript
+const functionName = process.env.REACT_APP_FUNC_NAME || "myFunc";
+async function callFunction(teamsfx) {
+ if (!teamsfx) {
+ throw new Error("TeamsFx SDK is not initialized.");
+ }
+ try {
+ const credential = teamsfx.getCredential();
+ const apiBaseUrl = teamsfx.getConfig("apiEndpoint") + "/api/";
+ // createApiClient(...) creates an Axios instance which uses BearerTokenAuthProvider to inject token to request header
+ const apiClient = createApiClient(
+ apiBaseUrl,
+ new BearerTokenAuthProvider(
+ async () => (await credential.getToken(""))!.token
+ )
+ );
+ const response = await apiClient.get(functionName);
+ return response.data;
+ } catch (e) {}
+}
+```
+
+For more information, see:
+
+* [sample](https://github.com/OfficeDev/TeamsFx-Samples/blob/dev/hello-world-tab-with-backend/tabs/src/components/sample/AzureFunctions.tsx)
+* [Developer guide](/azure/azure-functions/functions-reference?tabs=blob)
+
+## Embed Power BI to dashboard
+
+To embed Power BI to the dashboard, see [Power BI client react](/javascript/api/overview/powerbi/powerbi-client-react).
+
+## Step-by-step guide
+
+Follow the [step-by-step](~/sbs-create-dashboard-widget-graph-api-call-in-Teams-toolkit.yml) guide to build a dashboard, and learn to add a widget and Graph API call to the dashboard.
+
+## See also
+
+* [What are Teams tabs](../what-are-tabs.md)
+* [App design guidelines for tab](../design/tabs.md)
+* [Fluent UI library](https://react.fluentui.dev/?path=/docs/concepts-introduction--page)
+* [Fluent UI React charting examples](https://fluentuipr.z22.web.core.windows.net/heads/master/react-charting/demo/https://docsupdatetracker.net/index.html#/)
platform Whats New https://github.com/MicrosoftDocs/msteams-docs/commits/main/msteams-platform/whats-new.md
Teams platform features that are available to all app developers.
**2023 February**
-***February 09, 2023***: [Apps for Teams meetings support anonymous users.](apps-in-teams-meetings/build-apps-for-anonymous-user.md)
+* ***February 17, 2023***: [Create a dashboard in Teams.](tabs/how-to/dashboard-teams-toolkit.md)
+
+* ***February 09, 2023***: [Apps for Teams meetings support anonymous users.](apps-in-teams-meetings/build-apps-for-anonymous-user.md)
:::column-end::: :::row-end:::
Teams platform features that are available to all app developers.
| **Date** | **Update** | **Find here** | | -- | | -|
-|31/01/2023| Introducing update and soft delete event notifications in bot | Build bots > Bot conversations > [Messages in bot conversations](bots/how-to/conversations/conversation-messages.md#update-message) |
| 08/12/2022 | Introducing Teams developer documentation FAQs. | [Teams developer documentation FAQs](teams-faq.md) | | 07/12/2022 | Introducing notification bot in Teams. | Build bots > Bot conversations > [Notification bot in Teams](bots/how-to/conversations/notification-bot-in-teams.md) | | 07/12/2022 | Introducing command bot in Teams. | Build bots > Bot conversations > [Command bot in Teams](bots/how-to/conversations/command-bot-in-teams.md) |
Explore updates from the previous GA releases listed here.
|03/05/2021|Default install scope and group capability | Distribute your app > [Default install scope and group capability](concepts/deploy-and-publish/add-default-install-scope.md) | |03/05/2021|Reorder personal app tabs | Build tabs > [Reorder the chat tab in personal apps](tabs/how-to/create-personal-tab.md#reorder-static-personal-tabs) | |03/04/2021|Information masking in Adaptive cards | Build cards and task modules > Build cards > [Information masking in Adaptive cards](task-modules-and-cards/cards/cards-format.md#information-masking-in-adaptive-cards) |
-|02/19/2021|Added location capabilities. <br/> Location capabilities information is added in the device capabilities overview, native device permissions, integrate media capabilities, and QR or barcode scanner capability files | ΓÇó App fundamentals > Device capabilities > [Overview](concepts/device-capabilities/device-capabilities-overview.md) </br> ΓÇó App fundamentals > Device capabilities > [Request device permissions](concepts/device-capabilities/native-device-permissions.md) </br> ΓÇó App fundamentals > Device capabilities > [Integrate media capabilities](concepts/device-capabilities/media-capabilities.md) </br> ΓÇó App fundamentals > Device capabilities > [Integrate QR or barcode scanner capability](concepts/device-capabilities/qr-barcode-scanner-capability.md) </br> ΓÇó App fundamentals > Device capabilities > [Integrate location capabilities](concepts/device-capabilities/location-capability.md) |
-|02/18/2021|Added QR or barcode scanner capability. <br/> QR or barcode scanner capability information is added in the device capabilities overview, native device permissions, and integrate media capabilities files | ΓÇó App fundamentals > Device capabilities > [Overview](concepts/device-capabilities/device-capabilities-overview.md) </br> ΓÇó App fundamentals > Device capabilities > [Request device permissions](concepts/device-capabilities/native-device-permissions.md) </br> ΓÇó App fundamentals > Device capabilities > [Integrate media capabilities](concepts/device-capabilities/media-capabilities.md) </br> ΓÇó App fundamentals > Device capabilities > [Integrate QR or barcode scanner capability](concepts/device-capabilities/qr-barcode-scanner-capability.md) |
-|02/09/2021|Added device capabilities overview. <br/> Microphone capability information is added in the native device permissions and integrate media capabilities files |ΓÇó App fundamentals > Device capabilities > [Overview](concepts/device-capabilities/device-capabilities-overview.md) </br> App fundamentals > ΓÇó Device capabilities > [Request device permissions](concepts/device-capabilities/native-device-permissions.md) </br> ΓÇó App fundamentals > Device capabilities > [Integrate media capabilities](concepts/device-capabilities/media-capabilities.md)|
+|02/19/2021|Added location capabilities. | ΓÇó App fundamentals > Device capabilities > [Overview](concepts/device-capabilities/device-capabilities-overview.md) </br> ΓÇó App fundamentals > Device capabilities > [Request device permissions](concepts/device-capabilities/native-device-permissions.md) </br> ΓÇó App fundamentals > Device capabilities > [Integrate media capabilities](concepts/device-capabilities/media-capabilities.md) </br> ΓÇó App fundamentals > Device capabilities > [Integrate QR or barcode scanner capability](concepts/device-capabilities/qr-barcode-scanner-capability.md) </br> ΓÇó App fundamentals > Device capabilities > [Integrate location capabilities](concepts/device-capabilities/location-capability.md) |
+|02/18/2021|Added QR or barcode scanner capability.| ΓÇó App fundamentals > Device capabilities > [Overview](concepts/device-capabilities/device-capabilities-overview.md) </br> ΓÇó App fundamentals > Device capabilities > [Request device permissions](concepts/device-capabilities/native-device-permissions.md) </br> ΓÇó App fundamentals > Device capabilities > [Integrate media capabilities](concepts/device-capabilities/media-capabilities.md) </br> ΓÇó App fundamentals > Device capabilities > [Integrate QR or barcode scanner capability](concepts/device-capabilities/qr-barcode-scanner-capability.md) |
+|02/09/2021|Added microphone capability information in device capabilities overview. |ΓÇó App fundamentals > Device capabilities > [Overview](concepts/device-capabilities/device-capabilities-overview.md) </br> App fundamentals > ΓÇó Device capabilities > [Request device permissions](concepts/device-capabilities/native-device-permissions.md) </br> ΓÇó App fundamentals > Device capabilities > [Integrate media capabilities](concepts/device-capabilities/media-capabilities.md)|
<br>
Explore updates from the previous GA releases listed here.
| 12/26/2019 | The `replyToId` parameter in payloads sent to a bot is no longer encrypted, allowing you to use this value to construct deep links to these messages. Message payloads include the encrypted values in the parameter `legacy.replyToId`. | | 11/05/2019 | Single sign-on using the Teams JavaScript SDK. | [Single sign-on](tabs/how-to/authentication/tab-sso-overview.md) | | 10/31/2019 | Conversational bots and message extension documentation updated to reflect the 4.6 Bot Framework SDK. Documentation for the v3 SDK is available in the Resources section. | All bot and message extension documentation |
-| 10/31/2019 | New documentation structure, and major article refactoring. Please report any dead links or 404's by creating a GitHub Issue. | All of them! |
+| 10/31/2019 | New documentation structure, and major article refactoring. Report any dead links or 404's by creating a GitHub Issue. | All of them! |
| 09/13/2019 | Request bot is installed from action-based message extension. | [Initiate actions with message extensions](resources/messaging-extension-v3/create-extensions.md#request-to-install-your-conversational-bot) | 08/28/2019 | Support for private channels in tabs and Connectors. | [Get context for your tab](tabs/how-to/access-teams-context.md#retrieve-context-in-private-channels) | | 06/20/2019 | Share an external website, from an external website, into a Teams channel. | [Share to Teams](concepts/build-and-test/share-to-teams-overview.md). |
Explore updates from the previous GA releases listed here.
| **Date** | **Update** | **Find here** | | -- | | |
-| 11/12/2018 | Tabs in group chat is now available in the released version of Teams. As part of this work, the tabs section has been reworked for clarity.| [Configurable tabs](~/concepts/tabs/tabs-configurable.md) |
+| 11/12/2018 | Tabs in group chat is now available in the released version of Teams. The tabs section is updated for clarity.| [Configurable tabs](~/concepts/tabs/tabs-configurable.md) |
| 11/09/2018 | You can now create deep links to private chats between users. | [Deep linking to a chat](concepts/build-and-test/deep-link-teams.md#deep-links-to-navigate-to-chat-messages) |
-| 11/08/2018 | SharePoint Framework 1.7 has shipped and with it a new feature to use Microsoft Teams tab as a SharePoint Framework web part. | [Tabs in SharePoint](~/concepts/tabs/tabs-in-sharepoint.md) |
-| 11/05/2018 | The **task module** feature was released. A task module allows you to create modal pop-up experiences in your Teams application, from both bots and tabs. Inside the pop-up, you can run your own custom HTML/JavaScript code, show an `<iframe>`-based widget such as a YouTube or Microsoft Stream video, or display an [Adaptive card](/adaptive-cards/). | [Task module Overview](~/concepts/task-modules/task-modules-overview.md), [task module in tabs](~/concepts/task-modules/task-modules-tabs.md), [task module in bots](~/concepts/task-modules/task-modules-bots.md) |
-| 10/05/2018 | Formatting information for cards has been updated and tested in the desktop, iOS, and Android clients for Teams. | [Cards](~/concepts/cards/cards.md), [Card formatting](~/concepts/cards/cards-format.md) |
-| 09/24/2018 | Calls and online meetings APIs for Microsoft Graph were released to beta, and Teams apps can now interact with users in rich ways using voice and video. | [Calls and online meetings bots](~/concepts/calls-and-meetings/registering-calling-bot.md), [Real-time media concepts](~/concepts/calls-and-meetings/real-time-media-concepts.md), [Registering a calling bot](~/concepts/calls-and-meetings/registering-calling-bot.md), [Debugging and local testing](~/concepts/calls-and-meetings/debugging-local-testing-calling-meeting-bots.md), [Application-hosted media](~/concepts/calls-and-meetings/requirements-considerations-application-hosted-media-bots.md), [Handling incoming call notifications](~/concepts/calls-and-meetings/call-notifications.md) |
+| 11/08/2018 | SharePoint Framework 1.7 and a new feature to use Microsoft Teams tab as a SharePoint Framework web part is shipped. | [Tabs in SharePoint](~/concepts/tabs/tabs-in-sharepoint.md) |
+| 11/05/2018 | The **task module** feature is released. A task module allows you to create modal pop-up experiences in your Teams application, from both bots and tabs. Inside the pop-up, you can run your own custom HTML/JavaScript code, show an `<iframe>`-based widget such as a YouTube or Microsoft Stream video, or display an [Adaptive card](/adaptive-cards/). | [Task module Overview](~/concepts/task-modules/task-modules-overview.md), [task module in tabs](~/concepts/task-modules/task-modules-tabs.md), [task module in bots](~/concepts/task-modules/task-modules-bots.md) |
+| 10/05/2018 | Formatting information for cards is updated and tested in the desktop, iOS, and Android clients for Teams. | [Cards](~/concepts/cards/cards.md), [Card formatting](~/concepts/cards/cards-format.md) |
+| 09/24/2018 | Calls and online meetings APIs for Microsoft Graph is released to beta, and Teams apps can now interact with users in rich ways using voice and video. | [Calls and online meetings bots](~/concepts/calls-and-meetings/registering-calling-bot.md), [Real-time media concepts](~/concepts/calls-and-meetings/real-time-media-concepts.md), [Registering a calling bot](~/concepts/calls-and-meetings/registering-calling-bot.md), [Debugging and local testing](~/concepts/calls-and-meetings/debugging-local-testing-calling-meeting-bots.md), [Application-hosted media](~/concepts/calls-and-meetings/requirements-considerations-application-hosted-media-bots.md), [Handling incoming call notifications](~/concepts/calls-and-meetings/call-notifications.md) |
| 09/11/2018 | Tab configuration pages are now significantly taller. | [Tab Design](tabs/design/tabs.md) | | 08/15/2018 | Adaptive cards are now supported in Teams.|[Adaptive card actions in Teams](task-modules-and-cards/cards/cards-reference.md#adaptive-card) | | 08/10/2018 | Client support for DevTools.| [DevTools for the Microsoft Teams Desktop Client](~/resources/dev-preview/developer-preview-tools.md)| | 08/08/2018 | Message extensions now supports multiple commands. | [composeExtensions.commands](~/resources/schem#composeextensionscommands)|
-| 08/07/2018 | Inline configuration is now supported in Connectors. The Connectors documentation has also been revised and expanded for clarity.| [Connectors](~/concepts/connectors/connectors.md)|
+| 08/07/2018 | Inline configuration is now supported in Connectors. The Connectors documentation is revised and expanded for clarity.| [Connectors](~/concepts/connectors/connectors.md)|
| 08/06/2018 | Your bot can now send and receive files. | [Send and receive files through your bot](~/bots/how-to/bots-filesv4.md)|
-| 07/23/2018 | Information about app re-certification has been added to the Publishing section. |[Manifest permissions](resources/schem#permissions)|
-| 07/16/2018 | More space has been allocated to the tab configuration page. | [The tab configuration page is significantly taller](tabs/design/tabs.md)|
+| 07/23/2018 | Information about app recertification is added to the publishing section. |[Manifest permissions](resources/schem#permissions)|
+| 07/16/2018 | Additional space is allocated to the tab configuration page. | [The tab configuration page is significantly taller](tabs/design/tabs.md)|
| 07/12/2018 | Information on guest access. | [Guest access in Microsoft Teams](/microsoftteams/guest-access#guest-access-overview)|
-| 06/07/2018 | Information for the Microsoft Teams Tenant App Catalog has been added. | [Publish your Microsoft Teams app](~/publishing/apps-publish.md)|
+| 06/07/2018 | Information for the Microsoft Teams Tenant App Catalog is added. | [Publish your Microsoft Teams app](~/publishing/apps-publish.md)|
| 05/29/2018 | Adaptive cards are supported in Teams. | [Adaptive card actions in Teams](task-modules-and-cards/cards/cards-reference.md) |
-| 04/17/2018 | replyToID has been added to the payload for the `Invoke` and `MessageBack` card actions. This is especially useful if you need to update the message that the card action came from. | [Card actions](~/concepts/cards/cards-actions.md)|
+| 04/17/2018 | replyToID is added to the payload for the `Invoke` and `MessageBack` card actions. This is especially useful if you need to update the message that the card action came from. | [Card actions](~/concepts/cards/cards-actions.md)|
| 04/12/2018 | Added this topic to track changes to the Teams programming interface and this documentation set. | [What's new](~/whats-new.md)| | 04/10/2018 | Changed authentication URLs to consistently use the tenant ID in the path. | [Authentication flow for Tabs](~/concepts/authentication/auth-flow-tab.md), [Azure AD Tab authentication](~/concepts/authentication/auth-tab-AAD.md)| | 04/06/2018 | Added design guidelines for using the Command Box. |[Command box](~/resources/design/framework/command-box.md)|
Discover Microsoft Teams platform features that are deprecated. You can now get
:::column-end::: :::column span="2":::
-Teams platform features that are not available.
+Teams platform features that aren't available.
**2022 August**