with TypeScript and React

Before you read this page you should read using NotePad with TypeScript
Before you read this page you should read Linking JSX File


Create Project Folder

Open File Explorer and create the (C:\temp\vscode) folder.
Create a new subfolder called "word-typescript" (C:\temp\vscode\word-typescript).
Open Visual Studio Code.
Display the Terminal window (View > Terminal).
Change to that folder.

cd C:\temp\vscode 

Install Yeoman

The Yeoman generator is an open source scaffolding tool for creating web applications.
This generator creates a Node.js project.

npm install -g yo                  // 3.1.1 
npm install -g generator-office // 1.7.8

check the versions and update if necessary.

yo --version 
npm view generator-office version

Generate Template Files

Run the Yeoman generator to create the project.

yo office 

Choose a project type: Select "Office Add-in Task Pane project using React framework".
SS


Choose a script type: Select "TypeScript".
What do you want to name your add-in: Type "Word-TypeScript".
Choose which office client application you want to target: Select "Word".
All the necessary files will then be created for you.
Open File Explorer and browse the following folder (C:\temp\vscode\
To open this folder in Visual Studio Code, type "code ."

code . 

manifest.xml

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
<OfficeApp
  xmlns="http://schemas.microsoft.com/office/appforoffice/1.1"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:bt="http://schemas.microsoft.com/office/officeappbasictypes/1.0"
  xmlns:ov="http://schemas.microsoft.com/office/taskpaneappversionoverrides"
  xsi:type="TaskPaneApp">

<Id> Unique GUID </Id>
<Version>1.0.0.0</Version>
<ProviderName>Better Solutions Limited</ProviderName>
<DefaultLocale>en-US</DefaultLocale>
<DisplayName DefaultValue="Word-TypeScript"/>
<Description DefaultValue="A template to get started."/>
<IconUrl DefaultValue="https://localhost:3000/assets/icon-32.png"/>
<HighResolutionIconUrl DefaultValue="https://localhost:3000/assets/icon-80.png"/>
<SupportUrl DefaultValue="https://www.bettersolutions.com"/>
<AppDomains>
<AppDomain>bettersolutions.com</AppDomain>
</AppDomains>
<Hosts>
<Host Name="Document"/>
</Hosts>
<DefaultSettings>
<SourceLocation DefaultValue="https://localhost:3000/taskpane.html"/>
</DefaultSettings>
<Permissions>ReadWriteDocument</Permissions>
<VersionOverrides xmlns="http://schemas.microsoft.com/office/taskpaneappversionoverrides" xsi:type="VersionOverridesV1_0">
<Hosts>
<Host xsi:type="Document">
<DesktopFormFactor>
<GetStarted>
<Title resid="GetStarted.Title"/>
<Description resid="GetStarted.Description"/>
<LearnMoreUrl resid="GetStarted.LearnMoreUrl"/>
</GetStarted>
<FunctionFile resid="Commands.Url"/>
<ExtensionPoint xsi:type="PrimaryCommandSurface">
<OfficeTab id="TabHome">
<Group id="CommandsGroup">
<Label resid="CommandsGroup.Label"/>
<Icon>
<bt:Image size="16" resid="Icon.16x16"/>
<bt:Image size="32" resid="Icon.32x32"/>
<bt:Image size="80" resid="Icon.80x80"/>
</Icon>
<Control xsi:type="Button" id="TaskpaneButton">
<Label resid="TaskpaneButton.Label"/>
<Supertip>
<Title resid="TaskpaneButton.Label"/>
<Description resid="TaskpaneButton.Tooltip"/>
</Supertip>
<Icon>
<bt:Image size="16" resid="Icon.16x16"/>
<bt:Image size="32" resid="Icon.32x32"/>
<bt:Image size="80" resid="Icon.80x80"/>
</Icon>
<Action xsi:type="ShowTaskpane">
<TaskpaneId>ButtonId1</TaskpaneId>
<SourceLocation resid="Taskpane.Url"/>
</Action>
</Control>
</Group>
</OfficeTab>
</ExtensionPoint>
</DesktopFormFactor>
</Host>
</Hosts>
<Resources>
<bt:Images>
<bt:Image id="Icon.16x16" DefaultValue="https://localhost:3000/assets/icon-16.png"/>
<bt:Image id="Icon.32x32" DefaultValue="https://localhost:3000/assets/icon-32.png"/>
<bt:Image id="Icon.80x80" DefaultValue="https://localhost:3000/assets/icon-80.png"/>
</bt:Images>
<bt:Urls>
<bt:Url id="GetStarted.LearnMoreUrl" DefaultValue="https://go.microsoft.com/fwlink/?LinkId=276812"/>
<bt:Url id="Commands.Url" DefaultValue="https://localhost:3000/commands.html"/>
<bt:Url id="Taskpane.Url" DefaultValue="https://localhost:3000/taskpane.html"/>
</bt:Urls>
<bt:ShortStrings>
<bt:String id="GetStarted.Title" DefaultValue="Get started with your sample add-in!"/>
<bt:String id="CommandsGroup.Label" DefaultValue="Commands Group"/>
<bt:String id="TaskpaneButton.Label" DefaultValue="Show Taskpane"/>
</bt:ShortStrings>
<bt:LongStrings>
<bt:String id="GetStarted.Description" DefaultValue="Your sample add-in loaded succesfully.
             Go to the HOME tab and click the 'Show Taskpane' button to get started."/>
<bt:String id="TaskpaneButton.Tooltip" DefaultValue="Click to Show a Taskpane"/>
</bt:LongStrings>
</Resources>
</VersionOverrides>
</OfficeApp>

package.json file

Expand the Explorer pane on the left hand side.
Expand the project "Word-TypeScript".
Click on the package.json file.
SS


{ 
  "name": "office-addin-taskpane-react",
  "version": "0.0.1",
  "repository": {
    "type": "git",
    "url": "https://github.com/OfficeDev/Office-Addin-TaskPane-React.git"
  },
  "license": "MIT",
  "config": {
    "app-to-debug": "word",
    "app-type-to-debug": "desktop",
    "dev-server-port": 3000
  },
  "scripts": {
    "build": "webpack -p --mode production --https false",
    "build:dev": "webpack --mode development --https false",
    "dev-server": "webpack-dev-server --mode development",
    "lint": "office-addin-lint check",
    "lint:fix": "office-addin-lint fix",
    "prettier": "office-addin-lint prettier",
    "start": "office-addin-debugging start manifest.xml",
    "start:desktop": "office-addin-debugging start manifest.xml desktop",
    "start:web": "office-addin-debugging start manifest.xml web",
    "stop": "office-addin-debugging stop manifest.xml",
    "validate": "office-addin-manifest validate manifest.xml",
    "watch": "webpack --mode development --watch"
  },
  "dependencies": {
    "core-js": "^3.1.4",
    "es6-promise": "^4.2.8",
    "office-ui-fabric-react": "^7.10.0",
    "react": "^16.8.2",
    "react-dom": "^16.8.2"
  },
  "devDependencies": {
    "@babel/core": "^7.11.6",
    "@babel/polyfill": "^7.11.5",
    "@babel/preset-env": "^7.11.5",
    "@types/find-process": "1.2.0",
    "@types/office-js": "^1.0.108",
    "@types/office-runtime": "^1.0.14",
    "@types/react": "^16.8.22",
    "@types/react-dom": "^16.8.4",
    "@types/react-hot-loader": "^4.1.0",
    "@types/webpack": "^4.4.34",
    "@types/webpack-dev-server": "^3.1.6",
    "babel-loader": "^8.1.0",
    "clean-webpack-plugin": "^3.0.0",
    "copy-webpack-plugin": "^6.1.1",
    "eslint-config-office-addins": "^1.0.19",
    "find-process": "^1.4.3",
    "css-loader": "^3.0.0",
    "eslint-plugin-office-addins": "^0.1.1",
    "eslint-plugin-react": "^7.16.0",
    "extract-text-webpack-plugin": "4.0.0-beta.0",
    "file-loader": "^4.2.0",
    "html-loader": "^0.5.5",
    "html-webpack-plugin": "^4.5.0",
    "less": "^3.9.0",
    "less-loader": "^5.0.0",
    "office-addin-cli": "^1.0.13",
    "office-addin-debugging": "^3.0.34",
    "office-addin-dev-certs": "^1.5.5",
    "office-addin-lint": "^1.0.26",
    "office-addin-manifest": "^1.5.12",
    "office-addin-prettier-config": "^1.0.12",
    "postcss-loader": "3.0.0",
    "react-hot-loader": "^4.12.6",
    "source-map-loader": "^0.2.4",
    "style-loader": "^0.23.1",
    "ts-loader": "^6.2.2",
    "typescript": "^4.0.3",
    "webpack": "^4.43.0",
    "webpack-cli": "^3.3.12",
    "webpack-dev-server": "^3.11.0"
  },
  "prettier": "office-addin-prettier-config"
}

webpack.config.js

const devCerts = require("office-addin-dev-certs"); 
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const CopyWebpackPlugin = require("copy-webpack-plugin");
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const HtmlWebpackPlugin = require("html-webpack-plugin");
const webpack = require('webpack');

const urlDev="https://localhost:3000/";
const urlProd="https://www.contoso.com/"; // CHANGE THIS TO YOUR PRODUCTION DEPLOYMENT LOCATION

module.exports = async (env, options) => {
  const dev = options.mode === "development";
  const buildType = dev ? "dev" : "prod";
  const config = {
    devtool: "source-map",
    entry: {
      vendor: [
'react',
'react-dom',
'core-js',
'office-ui-fabric-react'
    ],
    taskpane: [
'react-hot-loader/patch',
'./src/taskpane/index.tsx',
    ],
    commands: './src/commands/commands.ts'
    },
    resolve: {
      extensions: [".ts", ".tsx", ".html", ".js"]
    },
    module: {
      rules: [
        {
          test: /\.tsx?$/,
          use: [
'react-hot-loader/webpack',
'ts-loader'
          ],
          exclude: /node_modules/
        },
        {
          test: /\.css$/,
          use: ['style-loader', 'css-loader']
        },
        {
          test: /\.(png|jpg|jpeg|gif)$/,
          loader: "file-loader",
          options: {
            name: '[path][name].[ext]',
          }
        }
      ]
    },
    plugins: [
      new CleanWebpackPlugin(),
      new CopyWebpackPlugin({
        patterns: [
        {
          to: "taskpane.css",
          from: "./src/taskpane/taskpane.css"
        },
        {
          to: "[name]." + buildType + ".[ext]",
          from: "manifest*.xml",
          transform(content) {
            if (dev) {
              return content;
            } else {
              return content.toString().replace(new RegExp(urlDev, "g"), urlProd);
            }
          }
        }
      ]}),
      new ExtractTextPlugin('[name].[hash].css'),
      new HtmlWebpackPlugin({
        filename: "taskpane.html",
          template: './src/taskpane/taskpane.html',
          chunks: ['taskpane', 'vendor', 'polyfills']
      }),
      new HtmlWebpackPlugin({
          filename: "commands.html",
          template: "./src/commands/commands.html",
          chunks: ["commands"]
      }),
      new webpack.ProvidePlugin({
        Promise: ["es6-promise", "Promise"]
      })
    ],
    devServer: {
      hot: true,
      headers: {
        "Access-Control-Allow-Origin": "*"
      },
      https: (options.https !== undefined) ? options.https : await devCerts.getHttpsServerOptions(),
      port: process.env.npm_package_config_dev_server_port || 3000
    }
  };

  return config;
};

src/taskpane/taskpane.html

This file contains the html code for the task pane.
Expand the "src" folder.
Expand the "taskpane" folder.
Click on the taskpane.html file.

<!DOCTYPE html> 
<html>

<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=Edge" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>React Task Pane Add-in</title>
<script type="text/javascript" src="https://appsforoffice.microsoft.com/lib/1.1/hosted/office.js"></script>
<link rel="stylesheet" href="https://static2.sharepointonline.com/files/fabric/office-ui-fabric-core/9.6.1/css/fabric.min.css"/>
<link href="taskpane.css" rel="stylesheet" type="text/css" />
</head>

<body class="ms-font-m ms-Fabric">
   <div id="container"></div>
</body>
</html>

src/taskpane/index.tsx

This file contains the typescript that sits behind the html page.

import "office-ui-fabric-react/dist/css/fabric.min.css"; 
import App from "./components/App";
import { AppContainer } from "react-hot-loader";
import { initializeIcons } from "office-ui-fabric-react/lib/Icons";
import * as React from "react";
import * as ReactDOM from "react-dom";

initializeIcons();

let isOfficeInitialized = false;

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

const render = Component => {
  ReactDOM.render(
    <AppContainer>
      <Component
         title={title}
         isOfficeInitialized={isOfficeInitialized}
      />
    </AppContainer>,
    document.getElementById("container")
  );
};

/* Render application after Office initializes */
Office.initialize = () => {
  isOfficeInitialized = true;
  render(App);
};

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

Other Project Files

The other project files are displayed on this page: Visual Studio Code > Yeoman Generator for reference.


Load the Add-in - Desktop

Execute the following command:

cd C:\temp\vscode\Word-TypeScript 
npm run start:desktop

This will start the local web server and open Word with the add-in loaded.
A small node window will be displayed.
A security warning dialog box will be displayed, asking you to install the certificate. Press Yes.
Select the Home tab and you will see a new group called "Commands Group" on the right hand side.
SS


?? and press the Run button.
When you have finished testing you need to execute the following command:

npm run stop 

Load the Add-in - Online

Execute the following command:

cd C:\temp\vscode\Word-TypeScript 
npm run start:web

This will start the debugging.
Allow localhost loopback for Microsoft Edge Web Browser: Select "Y".
A small node window will be displayed.
This will start the local web server.
The add-in will not appear automatically and will require side loading.


© 2021 Better Solutions Limited. All Rights Reserved. © 2021 Better Solutions Limited TopPrevNext