OfficeDev / office-js

A repo and NPM package for Office.js, corresponding to a copy of what gets published to the official "evergreen" Office.js CDN, at https://appsforoffice.microsoft.com/lib/1/hosted/office.js.
https://learn.microsoft.com/javascript/api/overview
Other
670 stars 96 forks source link

Open dialog with localhost throws Error 12004. #3926

Open joaquinlefebvre opened 9 months ago

joaquinlefebvre commented 9 months ago

Open dialog with localhost throws Error 12004.

Your Environment

Platform [PC desktop, Mac, iOS, Office on the web]: Office on the web Host [Excel, Word, PowerPoint, etc.]: Outlook Office version number: modern Outlook UI Operating System: Windows Browser: Chrome

Expected behavior

Open a dialog on an IFrame when domain is https://localhost

It was working fine until today. (Or at least the last two days).

When call this function:

export function showDialog(url: string, options?: OfficeShowDialogOptions, callback?: OfficeShowDialogCallback): void {
  _callback = callback
  Office.context.ui.displayDialogAsync(url, options, (asyncResult) => {
    _dialog = asyncResult.value
    _dialog.addEventHandler(Office.EventType.DialogMessageReceived, processMessage)
    _dialog.addEventHandler(Office.EventType.DialogEventReceived, processMessage)
  })
}

With this parameters:

const url = new URI(`login.html`).absoluteTo(window.location.toString()).toString();
const height = 46;
const width = 22;

showDialog(url, {
  width,
  height,
  displayInIframe: true,
} as OfficeShowDialogOptions);

A dialog with the content of this url: 'https://localhost:3000/login.html' is shown in an IFrame as expected.

Current behavior

The dialog is not showing and a javascript error is thrown:

12004 | The domain of the URL passed to displayDialogAsync is not trusted. The domain must be the same domain as the host page (including protocol and port number).

We also try to add https://localhost and https://localhost:3000 on AppDomain key on manifest.xml but the result is the same error.

(https://learn.microsoft.com/en-gb/javascript/api/manifest/appdomain?view=common-js-preview)

Aditional comment

In this url: https://learn.microsoft.com/en-gb/javascript/api/manifest/appdomain?view=common-js-preview we can read on point 4:

Listing the same domain as the one specified in the [SourceLocation element](https://learn.microsoft.com/en-gb/javascript/api/manifest/sourcelocation?view=common-js-preview) has no effect and may be misleading. In particular, when you are developing on localhost, you don't need to create an <AppDomain> element for localhost.

Steps to reproduce

  1. Create Outlook React project add-in via Yeoman with TypeScript option.
  2. Add a directory called login on /src path
  3. Create a file src/login/index.tsx with this code:
    
    import * as React from "react";
    import { createRoot } from "react-dom/client";

import { FluentProvider, webLightTheme } from "@fluentui/react-components"; import App from "./components/App";

/ global document, Office, module, require /

const title = "Contoso Task Pane Add-in";

const rootElement: HTMLElement = document.getElementById("container"); const root = createRoot(rootElement);

/ Render application after Office initializes / Office.onReady(() => { root.render(

); });

if ((module as any).hot) { (module as any).hot.accept("./components/App", () => { const NextApp = require("./components/App").default; root.render(NextApp); }); }

4. Add a directory called **_components_** on **_/src/login_** path
5. Create a file **_src/login/components/App.tsx_** with this code:
```typescript
import * as React from "react";
import { makeStyles } from "@fluentui/react-components";

const useStyles = makeStyles({
  root: {
    minHeight: "100vh",
  },
});

const App = () => {
  const styles = useStyles();
  return <div className={styles.root}>LOGIN VIEW</div>;
};

export default App;
  1. Replace the content of file src/taskpane/components/App.tsx with this code:
    
    import * as React from "react";
    import Header from "./Header";
    import HeroList, { HeroListItem } from "./HeroList";
    import TextInsertion from "./TextInsertion";
    import { makeStyles } from "@fluentui/react-components";
    import { Ribbon24Regular, LockOpen24Regular, DesignIdeas24Regular } from "@fluentui/react-icons";
    import { OfficeShowDialogOptions, showDialog } from "./Dialog";
    import URI from "urijs";

interface AppProps { title: string; }

const useStyles = makeStyles({ root: { minHeight: "100vh", }, });

const App = (props: AppProps) => { const styles = useStyles(); // The list items are static and won't change at runtime, // so this should be an ordinary const, not a part of state. const listItems: HeroListItem[] = [ { icon: , primaryText: "Achieve more with Office integration", }, { icon: , primaryText: "Unlock features and functionality", }, { icon: , primaryText: "Create and visualize like a pro", }, ];

function showLogin() { const url = new URI(login.html).absoluteTo(window.location.toString()).toString();

let height = 46;
let width = 22;
showDialog(url, {
  width,
  height,
  displayInIframe: true,
} as OfficeShowDialogOptions);

}

return (

); };

export default App;


7. Create a file **_src/taskpane/components/Dialog.ts_** with this code:

```typescript
export type OfficeShowDialogCallback = (result: Office.AsyncResult<Office.Dialog>) => void | undefined
export type OfficeShowDialogOptions = Office.DialogOptions | undefined

let _callback: any
let _dialog: any

function processMessage(_arg: any) {
  _dialog.close()
  if (typeof _callback === 'function') {
    _callback()
  }
}

export function showDialog(url: string, options?: OfficeShowDialogOptions, callback?: OfficeShowDialogCallback): void {
  _callback = callback
  Office.context.ui.displayDialogAsync(url, options, (asyncResult) => {
    _dialog = asyncResult.value
    _dialog.addEventHandler(Office.EventType.DialogMessageReceived, processMessage)
    _dialog.addEventHandler(Office.EventType.DialogEventReceived, processMessage)
  })
}

export function closeDialog() {
  if (!Office.context || !Office.context.ui || typeof Office.context.ui.messageParent !== 'function') {
    return
  }
  var messageObject = { messageType: 'dialogClosed' }
  var jsonMessage = JSON.stringify(messageObject)
  Office.context.ui.messageParent(jsonMessage)
}
  1. Execute commands to add urijs from npm:
    npm i urijs @types/urijs
  2. Add this line to webpack.config.js:

Inside entry key:

login: ["./src/login/index.tsx", "./src/taskpane/taskpane.html"],

And this code inside plugins key:

new HtmlWebpackPlugin({
        filename: "login.html",
        template: "./src/taskpane/taskpane.html",
        chunks: ["polyfill", "vendor", "login"],
      }),
  1. Finally execute add-in whith:
npm start

Context

With a localhost domain is not working, but when we execute same code with a valid domain, is working fine.

Useful logs

{
    "status": "failed",
    "error": {
        "name": "Error de presentación del diálogo",
        "message": "El dominio de la dirección URL no está incluido en el elemento AppDomains del manifiesto y no es un subdominio de la ubicación de origen.",
        "code": 12004
    }
}
neville-jones commented 9 months ago

We recently encountered this problem as well; we are using Vue2 and typescript.

A colleague did include an AppDomain entry in the manifest like <AppDomain>https://localhost:3000</AppDomain> and after 'forcing' the reload of the sideloaded add-in (clearing of cache, F5 refresh, etc) the problem did disappear.

joaquinlefebvre commented 9 months ago

We recently encountered this problem as well; we are using Vue2 and typescript.

A colleague did include an AppDomain entry in the manifest like <AppDomain>https://localhost:3000</AppDomain> and after 'forcing' the reload of the sideloaded add-in (clearing of cache, F5 refresh, etc) the problem did disappear.

Thank you very much @neville-jones. We did the same and it seems to have worked. It's more about updating the documentation or so I don't know if this is really an issue or a new feature. Maybe you should ask Microsoft so that other people don't run into the same problem.

Again, thank you very much. By adding https://localhost:3000/ to the manifest in the AppDomains key and clearing all LocalStorage data I was able to display the dialog as expected.

exextoc commented 9 months ago

@joaquinlefebvre We are checking this internally to confirm if it's bug or documentation gap.