Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SPFx in Teams tab not rendering until browsed to in spo #9920

Open
3 of 9 tasks
JonoSuave opened this issue Sep 16, 2024 · 6 comments
Open
3 of 9 tasks

SPFx in Teams tab not rendering until browsed to in spo #9920

JonoSuave opened this issue Sep 16, 2024 · 6 comments
Labels
Needs: Triage 🔍 Awaiting categorization and initial review. type:bug-suspected Suspected bug (not working as designed/expected). See “type:bug-confirmed” for confirmed bugs.

Comments

@JonoSuave
Copy link

JonoSuave commented Sep 16, 2024

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

  • 💥 Internet Explorer
  • 💥 Microsoft Edge
  • 💥 Google Chrome
  • 💥 FireFox
  • 💥 Safari
  • mobile (iOS/iPadOS)
  • mobile (Android)
  • not applicable
  • other (enter in the "Additional environment details" area below)

Additional environment details

  • MacOS Teams Desktop Client

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

  • Create 3 webparts: a search webpart, a directory webpart, and a faculty webpart that references the two main components of the search and directory webparts. For example, the FacultyWebpart.tsx will render the Faculty.tsx component, whose function is to render the Search.tsx and Faculty.tsx components.
  • Create your own teams manifest.json file in the teams folder of the SharePoint solution, so that when you deploy the sppkg it knows which webpart to make available in Teams
  • Create a Fluent UI Personal app using the Teams Toolkit
  • In the manifest.json inside the appPackage folder, create two static tabs: one that references the spfx webpart to be served and the other tab for the React app provided by Teams Toolkit
  • Provision, Deploy, and send to Production

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
Screenshot 2024-09-16 at 12 48 05 PM
Screenshot 2024-09-16 at 12 48 25 PM

@JonoSuave JonoSuave added the type:bug-suspected Suspected bug (not working as designed/expected). See “type:bug-confirmed” for confirmed bugs. label Sep 16, 2024
@JonoSuave
Copy link
Author

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": []
    }
  }
}

@JonoSuave
Copy link
Author

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": ""
	}
}

@JonoSuave
Copy link
Author

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>
	);
}

@JonoSuave
Copy link
Author

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

Screenshot 2024-09-18 at 9 09 25 AM

@VesaJuvonen VesaJuvonen added the Needs: Triage 🔍 Awaiting categorization and initial review. label Sep 19, 2024
@JonoSuave
Copy link
Author

@VesaJuvonen any update on your guys end by chance?

@JonoSuave
Copy link
Author

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Needs: Triage 🔍 Awaiting categorization and initial review. type:bug-suspected Suspected bug (not working as designed/expected). See “type:bug-confirmed” for confirmed bugs.
Projects
None yet
Development

No branches or pull requests

2 participants