Open JonoSuave opened 1 month ago
Here's what my Teams Toolkit manifest.json looks like:
{
"$schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.17/MicrosoftTeams.schema.json",
"version": "1.26.0",
"manifestVersion": "1.17",
"id": "bda4dea9-fa92-4f2c-81a1-8b95a8c70e40",
"name": {
"short": "Directory",
"full": "Full name for Directory"
},
"developer": {
"name": "JourneyTEAM, LLC",
"websiteUrl": "https://www.journeyteam.com",
"privacyUrl": "https://www.journeyteam.com",
"termsOfUseUrl": "https://www.journeyteam.com"
},
"description": {
"short": "Short description of DepartmentDirectory",
"full": "Full description of DepartmentDirectory"
},
"icons": {
"outline": "outline.png",
"color": "color.png"
},
"accentColor": "#FFFFFF",
"staticTabs": [
{
"entityId": "SPFxEmployeeDirectoryTab",
"name": "Faculty & Staff",
"contentUrl": "https://(redacted_domain_name).sharepoint.com/_layouts/15/TeamsLogon.aspx?SPFX=true&dest=/_layouts/15/teamshostedapp.aspx%3Fteams%26personal%26componentId=386dd1b8-71f5-4d5d-a568-b44b211067f0%26forceLocale=en-us",
"scopes": [
"personal"
],
"context": [
"personalTab"
]
},
{
"entityId": "DepartmentDirectoryTab",
"name": "Departments",
"contentUrl": "https://ashy-hill-0f884b110.5.azurestaticapps.net/index.html#/department-directory",
"websiteUrl": "https://ashy-hill-0f884b110.5.azurestaticapps.net/index.html#/department-directory",
"scopes": [
"personal"
]
},
{
"entityId": "about",
"scopes": [
"personal"
]
}
],
"validDomains": [
"(redacted_domain_name).sharepoint.com",
"ashy-hill-0f884b110.5.azurestaticapps.net",
"*.login.microsoftonline.com",
"*.sharepoint.com",
"*.sharepoint-df.com",
"spoppe-a.akamaihd.net",
"spoprod-a.akamaihd.net",
"ashy-hill-0f884b110.5.azurestaticapps.net*"
],
"webApplicationInfo": {
"id": "2db8265f-5a18-4d83-a39c-335603046850",
"resource": "api://ashy-hill-0f884b110.5.azurestaticapps.net/2db8265f-5a18-4d83-a39c-335603046850"
},
"authorization": {
"permissions": {
"resourceSpecific": []
}
}
}
Here's what my spfx teams manifest.json looks like:
{
"$schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.17/MicrosoftTeams.schema.json",
"version": "1.2",
"manifestVersion": "1.17",
"id": "aa1185ab-38c1-4621-b54d-90e94523e602",
"developer": {
"name": "JourneyTEAM, LLC",
"mpnId": "",
"websiteUrl": "https://www.journeyteam.com",
"privacyUrl": "https://www.journeyteam.com",
"termsOfUseUrl": "https://www.journeyteam.com"
},
"name": {
"short": "Teams Faculty Directory",
"full": "Teams Faculty Directory"
},
"description": {
"short": "Teams Employee Directory description",
"full": "Teams Employee Directory description"
},
"icons": {
"outline": "aa1185ab-38c1-4621-b54d-90e94523e602_outline.png",
"color": "aa1185ab-38c1-4621-b54d-90e94523e602_color.png"
},
"accentColor": "#FFFFFF",
"staticTabs": [
{
"entityId": "Belmont Faculty Directory",
"name": "Faculty Directory",
"contentUrl": "https://(redacted_domain_name).sharepoint.com/_layouts/15/TeamsLogon.aspx?SPFX=true&dest=/_layouts/15/teamshostedapp.aspx%3Fteams%26personal%26componentId=386dd1b8-71f5-4d5d-a568-b44b211067f0%26forceLocale=en-us",
"scopes": ["personal"],
"context": ["personalTab"]
},
{
"entityId": "about",
"scopes": ["personal"]
}
],
"validDomains": [
"*.login.microsoftonline.com",
"*.sharepoint.com",
"*.sharepoint-df.com",
"spoppe-a.akamaihd.net",
"spoprod-a.akamaihd.net",
"resourceseng.blob.core.windows.net",
"msft.spoppe.com"
],
"webApplicationInfo": {
"id": "00000003-0000-0ff1-ce00-000000000000",
"resource": ""
}
}
Here's an example of how I call the two components from two separate webparts:
import * as React from "react";
import { useMemo, useState } from "react";
import styles from "./FacultyStudentTeamsDirectory.module.scss";
import type { IFacultyStudentTeamsDirectoryProps } from "./IFacultyStudentTeamsDirectoryProps";
import EmployeeDirectory from "../../employeeDirectory/components/EmployeeDirectory";
import { IReactTemplateProps } from "@journeyteam/directory-extensions";
import SearchBox from "../../searchBox/components/SearchBox";
import { ISelectedRefiner, ISelectedRefinerValues } from "../../../models/refiner";
import * as SPSearch from "@pnp/sp/search/types";
export declare enum TemplateType {
adaptiveCard = "adaptive-card",
react = "react",
handlebars = "handelbars",
}
export default function FacultyStudentTeamsDirectory(props: IFacultyStudentTeamsDirectoryProps) {
const {
isDarkTheme,
hasTeamsContext,
initialSearchText,
employeeCardTemplate,
studentView,
propertyPanelOpen,
focusSections,
directoryRefiners,
refiners,
} = props;
const [selectedRefiners, setSelectedRefiners] =
useState<ISelectedRefinerValues[]>(directoryRefiners);
const [searchBoxRefinersSourceData, setSearchBoxRefinersSourceData] = useState<
SPSearch.IRefiner[]
>([]);
const [searchText, setSearchText] = useState(initialSearchText ? initialSearchText : "");
const [cardWidth, setCardWidth] = useState(275);
useMemo(() => {
const handleResize = () => {
if (window.innerWidth < 500) {
setCardWidth(150);
} else if (window.innerWidth < 800) {
setCardWidth(200);
} else {
setCardWidth(275);
}
};
// Call the handleResize function initially to set the width based on the current viewport size
handleResize();
// Set up the event listener for the resize event
window.addEventListener("resize", handleResize);
// Clean up the event listener when the component is unmounted
return () => {
window.removeEventListener("resize", handleResize);
};
}, []);
const searchBoxRefinersChanged = (searchBoxRefiners: ISelectedRefiner[]) => {
const mappedSelectedRefiners = refiners.map<ISelectedRefinerValues>((refiner) => {
const matchedRefiners = searchBoxRefiners.filter(
(selectedRefiner) => selectedRefiner.Name === refiner.PropertyName
);
return {
PropertyName: refiner.PropertyName,
SelectedRefiners: matchedRefiners,
};
});
setSelectedRefiners(mappedSelectedRefiners);
};
const onSearchChange = (val: string) => {
setSearchText(val);
};
const _refinersChanged = (refiners: SPSearch.IRefiner[]) => {
if (searchBoxRefinersSourceData.length === 0) {
setSearchBoxRefinersSourceData(refiners);
}
if (searchBoxRefinersSourceData[0]?.Entries.length < 20) {
setSearchBoxRefinersSourceData(refiners);
}
};
return (
<section
className={`${styles.facultyStudentTeamsDirectory} ${hasTeamsContext ? styles.teams : ""}`}>
<SearchBox
onSearchChanged={onSearchChange}
initialSearchText={initialSearchText}
refinerSourceData={searchBoxRefinersSourceData}
refiners={props.refiners}
onSelectedRefinersChanged={searchBoxRefinersChanged}
searchByFilters={props.searchByFilters}
searchByLabel={undefined}
searchByPlaceholder="Search By"
refinersLabel=""
refinersPlaceholder="Search Refiners"
searchLabel={""}
searchPlaceholder={""}
/>
<br />
<br />
<EmployeeDirectory
searchText={`${
searchText}*`}
itemsPerPage={20}
preFilter={`-"SPS-HideFromAddressLists":1`}
refiners={selectedRefiners}
onRefinersChanged={_refinersChanged}
useLibraryTemplate={false}
showProfileLink={undefined}
customStyles={undefined}
employeeCardTemplate={employeeCardTemplate}
focusSections={focusSections}
headerSections={props.headerSections}
otherSections={[]}
sourceId={undefined}
sort={[
{
Property: "firstName",
Direction: 0,
},
]}
hiddenRefiner={[]}
serviceScope={props.serviceScope}
context={props.context}
templates={props.templates}
editMode={true}
employeeCardWidth={cardWidth}
propertyPanelOpen={propertyPanelOpen}
paginationLocation={1}
toggleDirectReportsExport={false}
directReportsExportUrl=""
studentView={studentView}
isDarkTheme={isDarkTheme}
/>
</section>
);
}
Further update...just deploying my spfx solution and syncing to Teams works as a standalone personal app. The personal app that has multiple personal tabs (one being the spfx component and the other being a React app provisioned and deployed in Entra) doesn't work for the spfx tab until I first navigate to the standalone spfx personal app
@VesaJuvonen any update on your guys end by chance?
From what I'm seeing, seems the issue may lie in that the teams app manifest only allows for one set of id and resource to be referenced in the webApplicationInfo. In my case, one tab is leveraging an spfx component (resource would be the teamsitedomain and id the multi-tentant Microsoft Graph app id) and the other a teams app that was provisioned in entra using the Teams Toolkit.
Target SharePoint environment
SharePoint Online
What SharePoint development model, framework, SDK or API is this about?
💥 SharePoint Framework
Developer environment
macOS
What browser(s) / client(s) have you tested
Additional environment details
Describe the bug / error
I have a custom Teams personal app that has two tabs: one tab surfaces an spfx webpart, while the other a React SPA provisioned and deployed using the Teams Toolkit. After a couple days of not browsing to the custom Teams app, however, the tab that surfaces the SPFx component is stuck on a spinner -- the other tab has no problems. The console gives me the following error: App resource defined in manifest and iframe origin do not match failureCallback @ TeamsLogon.aspx?SPFX...rceLocale=en-us:240
In order to get the spfx tab to render I have to do the following in Chrome and Edge: browse to a SharePoint page with the webpart and then refresh the static tab in Teams
On the Teams Desktop client I added a reference to the SharePoint page in a Teams Channel and after browsing to that Teams channel tab I can then see the spfx component in the custom app's spfx tab
Steps to reproduce
Expected behavior
I shouldn't have to browse to the SPO page with the spfx webpart to then see it in the custom Teams app