├── .azure-devops ├── build.yml ├── cleanup.yml ├── devcerts.yml ├── edgewebview.yml ├── full-pipeline.yml ├── install.yml ├── lint.yml └── test.yml ├── .eslintrc.json ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ └── bug_report.md └── pull_request_template.md ├── .gitignore ├── .npmrc ├── .vscode ├── extensions.json ├── launch.json ├── settings.json └── tasks.json ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── SECURITY.md ├── assets ├── icon-128.png ├── icon-16.png ├── icon-32.png ├── icon-64.png ├── icon-80.png └── logo-filled.png ├── babel.config.json ├── convertToSingleHost.js ├── manifest.excel.xml ├── manifest.onenote.xml ├── manifest.outlook.xml ├── manifest.powerpoint.xml ├── manifest.project.xml ├── manifest.word.xml ├── manifest.xml ├── package-lock.json ├── package.json ├── src ├── commands │ ├── commands.html │ └── commands.ts └── taskpane │ ├── app │ ├── app.component.html │ ├── app.component.ts │ ├── app.module.ts │ ├── excel.app.component.ts │ ├── onenote.app.component.ts │ ├── outlook.app.component.ts │ ├── powerpoint.app.component.ts │ ├── project.app.component.ts │ └── word.app.component.ts │ ├── taskpane.css │ ├── taskpane.html │ └── taskpane.ts ├── test ├── end-to-end │ ├── src │ │ ├── test-helpers.ts │ │ ├── test-taskpane.html │ │ ├── test-taskpane.ts │ │ ├── test.app.component.ts │ │ ├── test.app.module.ts │ │ ├── test.excel.app.component.ts │ │ ├── test.powerpoint.app.compontent.ts │ │ └── test.word.app.component.ts │ ├── test-manifest.xml │ ├── ui-test.ts │ └── webpack.config.js └── unit │ ├── excel.test.ts │ ├── powerpoint.test.ts │ └── word.test.ts ├── tsconfig.json └── webpack.config.js /.azure-devops/build.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - task: Npm@1 3 | displayName: 'Build' 4 | inputs: 5 | command: custom 6 | customCommand: 'run build' 7 | -------------------------------------------------------------------------------- /.azure-devops/cleanup.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - task: mspremier.PostBuildCleanup.PostBuildCleanup-task.PostBuildCleanup@3 3 | displayName: "Cleanup" 4 | -------------------------------------------------------------------------------- /.azure-devops/devcerts.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - script: | 3 | echo Install Office-AddinDev-Certs at machine level 4 | call npx office-addin-dev-certs install --machine 5 | 6 | displayName: 'Install add-in dev cert' 7 | -------------------------------------------------------------------------------- /.azure-devops/edgewebview.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - script: | 3 | echo Enable EdgeWebView Loopback 4 | call npx office-addin-dev-settings appcontainer EdgeWebView --loopback --yes 5 | 6 | echo Set Edge WebView Registry Settings 7 | 8 | set PATH1="HKEY_CURRENT_USER\SOFTWARE\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppContainer\Mappings\S-1-15-2-1310292540-1029022339-4008023048-2190398717-53961996-4257829345-603366646" 9 | 10 | reg add %PATH1% /f /v DisplayName /t REG_SZ /d "@{Microsoft.Win32WebViewHost_10.0.19041.423_neutral_neutral_cw5n1h2txyewy?ms-resource://Windows.Win32WebViewHost/resources/DisplayName}" 11 | reg add %PATH1% /f /v Moniker /t REG_SZ /d "microsoft.win32webviewhost_cw5n1h2txyewy" 12 | 13 | set PATH2="HKEY_CURRENT_USER\SOFTWARE\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppContainer\Mappings\S-1-15-2-1310292540-1029022339-4008023048-2190398717-53961996-4257829345-603366646\Children\S-1-15-2-1310292540-1029022339-4008023048-2190398717-53961996-4257829345-603366646-3829197285-1050560373-949424154-522343454" 14 | 15 | reg add %PATH2% /f /v DisplayName /t REG_SZ /d "microsoft.win32webviewhost_cw5n1h2txyewy/123" 16 | reg add %PATH2% /f /v Moniker /t REG_SZ /d "123" 17 | reg add %PATH2% /f /v ParentMoniker /t REG_SZ /d "microsoft.win32webviewhost_cw5n1h2txyewy" 18 | 19 | displayName: 'Enable Edge WebView' 20 | -------------------------------------------------------------------------------- /.azure-devops/full-pipeline.yml: -------------------------------------------------------------------------------- 1 | # Build a general Node.js project with npm. 2 | # Add steps that analyze code, save build artifacts, deploy, and more: 3 | # https://docs.microsoft.com/azure/devops/pipelines/languages/javascript 4 | 5 | jobs: 6 | - job: Windows_10_Latest 7 | pool: 8 | name: OE-OfficeClientApps 9 | steps: 10 | - template: ./install.yml 11 | - template: ./lint.yml 12 | - template: ./build.yml 13 | - template: ./devcerts.yml 14 | - template: ./edgewebview.yml 15 | - template: ./test.yml 16 | parameters: 17 | webView: "edge-chromium" 18 | 19 | - job: WebView_EdgeLegacy 20 | pool: 21 | name: OE-OfficeClientApps 22 | steps: 23 | - template: ./install.yml 24 | - template: ./lint.yml 25 | - template: ./build.yml 26 | - template: ./devcerts.yml 27 | - template: ./edgewebview.yml 28 | - template: ./test.yml 29 | parameters: 30 | webView: "edge-legacy" 31 | 32 | - job: Mac 33 | pool: 34 | name: OPX Mac Dogfood Pool 35 | steps: 36 | - template: ./install.yml 37 | - template: ./lint.yml 38 | - template: ./build.yml 39 | - template: ./test.yml 40 | - template: ./cleanup.yml 41 | -------------------------------------------------------------------------------- /.azure-devops/install.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - task: Npm@1 3 | displayName: 'Install' 4 | -------------------------------------------------------------------------------- /.azure-devops/lint.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - task: Npm@1 3 | displayName: 'Lint' 4 | inputs: 5 | command: custom 6 | customCommand: 'run lint' 7 | -------------------------------------------------------------------------------- /.azure-devops/test.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | - name: webView 3 | type: string 4 | default: "edge-chromium" 5 | 6 | steps: 7 | - task: CmdLine@2 8 | inputs: 9 | script: | 10 | echo Setting WebView Type: ${{ parameters.webView }} 11 | call npx office-addin-dev-settings webview manifest.xml ${{ parameters.webView }} 12 | call npx office-addin-dev-settings webview test/end-to-end/test-manifest.xml ${{ parameters.webView }} 13 | 14 | echo Running Tests 15 | npm run test 16 | 17 | echo Done running tests 18 | displayName: 'Run Tests' 19 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "office-addins" 4 | ], 5 | "extends": [ 6 | "plugin:office-addins/recommended" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @OfficeDev/office-platform-devx -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: needs triage 6 | assignees: '' 7 | 8 | --- 9 | 10 | # Prerequisites 11 | 12 | Please answer the following questions before submitting an issue. 13 | **YOU MAY DELETE THE PREREQUISITES SECTION.** 14 | - [ ] I am running the latest version of Node and the tools 15 | - [ ] I checked the documentation and found no answer 16 | - [ ] I checked to make sure that this issue has not already been filed 17 | 18 | 19 | # Expected behavior 20 | 21 | Please describe the behavior you were expecting 22 | 23 | 24 | # Current behavior 25 | 26 | Please provide information about the failure. What is the current behavior? If it is not a bug, please submit your idea to the [Microsoft Tech Community Ideas forum](https://techcommunity.microsoft.com/t5/microsoft-365-developer-platform/idb-p/Microsoft365DeveloperPlatform), so that it gets added to our feature roadmap. 27 | 28 | 29 | ## Steps to Reproduce 30 | 31 | Please provide detailed steps for reproducing the issue. 32 | 33 | 1. step 1 34 | 2. step 2 35 | 3. you get it... 36 | 37 | 38 | ## Context 39 | 40 | Please provide any relevant information about your setup. This is important in case the issue is not reproducible except for under certain conditions. 41 | 42 | * Operating System: 43 | * Node version: 44 | * Office version: 45 | * Tool version: 46 | 47 | ## Failure Logs 48 | 49 | Please include any relevant log snippets, screenshots or code samples here. 50 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | Thank you for your pull request! Please provide the following information. 2 | 3 | --- 4 | 5 | **Change Description**: 6 | 7 | Describe what the PR is about. 8 | 9 | 1. **Do these changes impact any *npm scripts* commands (in package.json)?** (e.g., running 'npm run start') 10 | If Yes, briefly describe what is impacted. 11 | 12 | 13 | 2. **Do these changes impact *VS Code debugging* options (launch.json)?** 14 | If Yes, briefly describe what is impacted. 15 | 16 | 17 | 3. **Do these changes impact *template output*?** (e.g., add/remove file, update file location, update file contents) 18 | If Yes, briefly describe what is impacted. 19 | 20 | 21 | 4. **Do these changes impact *documentation*?** (e.g., a tutorial on https://docs.microsoft.com/en-us/office/dev/add-ins/overview/office-add-ins) 22 | If Yes, briefly describe what is impacted. 23 | 24 | 25 | If you answered yes to any of these please do the following: 26 | > Include 'Rick-Kirkham' in the review 27 | > Make sure the README file is correct 28 | 29 | **Validation/testing performed**: 30 | 31 | Describe manual testing done. 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build output 2 | dist/ 3 | 4 | # Node modules folder 5 | node_modules/ 6 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true 2 | registry=https://registry.npmjs.org/ -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. 3 | // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp 4 | 5 | // List of extensions which should be recommended for users of this workspace. 6 | "recommendations": [ 7 | "ms-edgedevtools.vscode-edge-devtools", 8 | "msoffice.microsoft-office-add-in-debugger", 9 | "dbaeumer.vscode-eslint", 10 | "esbenp.prettier-vscode" 11 | ], 12 | // List of extensions recommended by VS Code that should not be recommended for users of this workspace. 13 | "unwantedRecommendations": [] 14 | } 15 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Debug UI Tests", 9 | "type": "node", 10 | "request": "launch", 11 | "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha", 12 | "args": [ 13 | "-u", 14 | "bdd", 15 | "--timeout", 16 | "999999", 17 | "--colors", 18 | "${workspaceFolder}/test/end-to-end", 19 | "-r", 20 | "ts-node/register", 21 | "${workspaceFolder}/test/end-to-end/*.ts" 22 | ], 23 | "internalConsoleOptions": "openOnSessionStart", 24 | "runtimeArgs": ["--preserve-symlinks"] 25 | }, 26 | { 27 | "name": "Debug Unit Tests", 28 | "type": "node", 29 | "request": "launch", 30 | "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha", 31 | "args": [ 32 | "-u", 33 | "bdd", 34 | "--timeout", 35 | "999999", 36 | "--colors", 37 | "${workspaceFolder}/test/unit", 38 | "-r", 39 | "ts-node/register", 40 | "${workspaceFolder}/test/unit/*.test.ts" 41 | ], 42 | "internalConsoleOptions": "openOnSessionStart", 43 | "runtimeArgs": ["--preserve-symlinks"] 44 | }, 45 | { 46 | "name": "Excel Desktop (Edge Chromium)", 47 | "type": "msedge", 48 | "request": "attach", 49 | "port": 9229, 50 | "timeout": 600000, 51 | "webRoot": "${workspaceRoot}", 52 | "preLaunchTask": "Debug: Excel Desktop", 53 | "postDebugTask": "Stop Debug" 54 | }, 55 | { 56 | "name": "Excel Desktop (Edge Legacy)", 57 | "type": "office-addin", 58 | "request": "attach", 59 | "url": "https://localhost:3000/taskpane.html?_host_Info=Excel$Win32$16.01$en-US$$$$0", 60 | "port": 9222, 61 | "timeout": 600000, 62 | "webRoot": "${workspaceRoot}", 63 | "preLaunchTask": "Debug: Excel Desktop", 64 | "postDebugTask": "Stop Debug" 65 | }, 66 | { 67 | "name": "Outlook Desktop (Edge Chromium)", 68 | "type": "msedge", 69 | "request": "attach", 70 | "port": 9229, 71 | "timeout": 600000, 72 | "webRoot": "${workspaceRoot}", 73 | "preLaunchTask": "Debug: Outlook Desktop", 74 | "postDebugTask": "Stop Debug" 75 | }, 76 | { 77 | "name": "Outlook Desktop (Edge Legacy)", 78 | "type": "office-addin", 79 | "request": "attach", 80 | "url": "https://localhost:3000/taskpane.html?_host_Info=Outlook$Win32$16.01$en-US$$$$0", 81 | "port": 9222, 82 | "timeout": 600000, 83 | "webRoot": "${workspaceRoot}", 84 | "preLaunchTask": "Debug: Outlook Desktop", 85 | "postDebugTask": "Stop Debug" 86 | }, 87 | { 88 | "name": "PowerPoint Desktop (Edge Chromium)", 89 | "type": "msedge", 90 | "request": "attach", 91 | "port": 9229, 92 | "timeout": 600000, 93 | "webRoot": "${workspaceRoot}", 94 | "preLaunchTask": "Debug: PowerPoint Desktop", 95 | "postDebugTask": "Stop Debug" 96 | }, 97 | { 98 | "name": "PowerPoint Desktop (Edge Legacy)", 99 | "type": "office-addin", 100 | "request": "attach", 101 | "url": "https://localhost:3000/taskpane.html?_host_Info=PowerPoint$Win32$16.01$en-US$$$$0", 102 | "port": 9222, 103 | "timeout": 600000, 104 | "webRoot": "${workspaceRoot}", 105 | "preLaunchTask": "Debug: PowerPoint Desktop", 106 | "postDebugTask": "Stop Debug" 107 | }, 108 | { 109 | "name": "Word Desktop (Edge Chromium)", 110 | "type": "msedge", 111 | "request": "attach", 112 | "port": 9229, 113 | "timeout": 600000, 114 | "webRoot": "${workspaceRoot}", 115 | "preLaunchTask": "Debug: Word Desktop", 116 | "postDebugTask": "Stop Debug" 117 | }, 118 | { 119 | "name": "Word Desktop (Edge Legacy)", 120 | "type": "office-addin", 121 | "request": "attach", 122 | "url": "https://localhost:3000/taskpane.html?_host_Info=Word$Win32$16.01$en-US$$$$0", 123 | "port": 9222, 124 | "timeout": 600000, 125 | "webRoot": "${workspaceRoot}", 126 | "preLaunchTask": "Debug: Word Desktop", 127 | "postDebugTask": "Stop Debug" 128 | } 129 | ] 130 | } 131 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "eslint.validate": [ 3 | "javascript", 4 | "javascriptreact", 5 | "typescript" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "Build (Development)", 8 | "type": "npm", 9 | "script": "build:dev", 10 | "group": { 11 | "kind": "build", 12 | "isDefault": true 13 | }, 14 | "presentation": { 15 | "clear": true, 16 | "panel": "shared", 17 | "showReuseMessage": false 18 | } 19 | }, 20 | { 21 | "label": "Build (Production)", 22 | "type": "npm", 23 | "script": "build", 24 | "group": "build", 25 | "presentation": { 26 | "clear": true, 27 | "panel": "shared", 28 | "showReuseMessage": false 29 | } 30 | }, 31 | { 32 | "label": "Debug: Excel Desktop", 33 | "type": "npm", 34 | "script": "start:desktop -- --app excel", 35 | "presentation": { 36 | "clear": true, 37 | "panel": "dedicated", 38 | }, 39 | "problemMatcher": [] 40 | }, 41 | { 42 | "label": "Debug: Outlook Desktop", 43 | "type": "npm", 44 | "script": "start:desktop -- --app outlook", 45 | "presentation": { 46 | "clear": true, 47 | "panel": "dedicated", 48 | }, 49 | "problemMatcher": [] 50 | }, 51 | { 52 | "label": "Debug: PowerPoint Desktop", 53 | "type": "npm", 54 | "script": "start:desktop -- --app powerpoint", 55 | "presentation": { 56 | "clear": true, 57 | "panel": "dedicated", 58 | }, 59 | "problemMatcher": [] 60 | }, 61 | { 62 | "label": "Debug: Word Desktop", 63 | "type": "npm", 64 | "script": "start:desktop -- --app word", 65 | "presentation": { 66 | "clear": true, 67 | "panel": "dedicated", 68 | }, 69 | "problemMatcher": [] 70 | }, 71 | { 72 | "label": "Dev Server", 73 | "type": "npm", 74 | "script": "dev-server", 75 | "presentation": { 76 | "clear": true, 77 | "panel": "dedicated" 78 | }, 79 | "problemMatcher": [] 80 | }, 81 | { 82 | "label": "Install", 83 | "type": "npm", 84 | "script": "install", 85 | "presentation": { 86 | "clear": true, 87 | "panel": "shared", 88 | "showReuseMessage": false 89 | }, 90 | "problemMatcher": [] 91 | }, 92 | { 93 | "label": "Lint: Check for problems", 94 | "type": "npm", 95 | "script": "lint", 96 | "problemMatcher": [ 97 | "$eslint-stylish" 98 | ] 99 | }, 100 | { 101 | "label": "Lint: Fix all auto-fixable problems", 102 | "type": "npm", 103 | "script": "lint:fix", 104 | "problemMatcher": [ 105 | "$eslint-stylish" 106 | ] 107 | }, 108 | { 109 | "label": "Stop Debug", 110 | "type": "npm", 111 | "script": "stop", 112 | "presentation": { 113 | "clear": true, 114 | "panel": "shared", 115 | "showReuseMessage": false 116 | }, 117 | "problemMatcher": [] 118 | }, 119 | { 120 | "label": "Watch", 121 | "type": "npm", 122 | "script": "watch", 123 | "presentation": { 124 | "clear": true, 125 | "panel": "dedicated" 126 | }, 127 | "problemMatcher": [] 128 | }, 129 | ] 130 | } 131 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribute to this code sample 2 | 3 | Thank you for your interest in this sample! Your contributions and improvements will help the developer community. 4 | 5 | ## Ways to contribute 6 | 7 | There are several ways you can contribute to this sample: providing better code comments, fixing open issues, and adding new features. 8 | 9 | ### Provide better code comments 10 | 11 | Code comments make code samples even better by helping developers learn to use the code correctly in their own applications. If you spot a class, method, or section of code that you think could use better descriptions, then create a pull request with your code comments. 12 | 13 | 14 | In general, we want our code comments to follow these guidelines: 15 | 16 | - Any code that has associated documentation displayed in an IDE (such as IntelliSense, or JavaDocs) has code comments. 17 | - Classes, methods, parameters, and return values have clear descriptions. 18 | - Exceptions and errors are documented. 19 | - Remarks exist for anything special or notable about the code. 20 | - Sections of code that have complex algorithms have appropriate comments describing what they do. 21 | - Code added from Stack Overflow, or any other source, is clearly attributed. 22 | 23 | ### Fix open issues 24 | 25 | Sometimes we get a lot of issues, and it can be hard to keep up. If you have a solution to an open issue that hasn't been addressed, fix the issue and then submit a pull request. 26 | 27 | ### Add a new feature 28 | 29 | New features are great! Be sure to check with the repository admin first to be sure the feature fits the intent of the sample. Start by opening an issue in the repository that proposes and describes the feature. The repository admin will respond and may ask for more information. If the admin agrees to the new feature, create the feature and submit a pull request. 30 | 31 | ## Contribution guidelines 32 | 33 | We have some guidelines to help maintain a healthy repo and code for everyone. 34 | 35 | ### The Contribution License Agreement 36 | 37 | For most contributions, we ask you to sign a Contribution License Agreement (CLA). This will happen when you submit a pull request. Microsoft will send a link to the CLA to sign via email. Once you sign the CLA, your pull request can proceed. Read the CLA carefully, because you may need to have your employer sign it. 38 | 39 | ### Code contribution checklist 40 | 41 | Be sure to satisfy all of the requirements in the following list before submitting a pull request: 42 | 43 | - Follow the code style that is appropriate for the platform and language in this repo. For example, Android code follows the style conventions found in the [Code Style for Contributors guide](https://source.android.com/source/code-style.html). 44 | - Test your code. 45 | - Test the UI thoroughly to be sure your change hasn't broken anything. 46 | - Keep the size of your code change reasonable. If the repository owner cannot review your code change in 4 hours or less, your pull request may not be reviewed and approved quickly. 47 | - Avoid unnecessary changes. The reviewer will check differences between your code and the original code. Whitespace changes are called out along with your code. Be sure your changes will help improve the content. 48 | 49 | ### Submit a pull request to the master branch 50 | 51 | When you're finished with your work and are ready to have it merged into the master repository, follow these steps. Note: pull requests are typically reviewed within 10 business days. If your pull request is accepted you will be credited for your submission. 52 | 53 | 1. Submit your pull request against the master branch. 54 | 2. Sign the CLA, if you haven't already done so. 55 | 3. One of the repo admins will process your pull request, including performing a code review. If there are questions, discussions, or change requests in the pull request, be sure to respond. 56 | 4. When the repo admins are satisfied, they will accept and merge the pull request. 57 | 58 | Congratulations, you have successfully contributed to the sample! 59 | 60 | ## FAQ 61 | 62 | ### Where do I get a Contributor's License Agreement? 63 | 64 | If your pull request requires one, you'll automatically be sent a notice that you need to sign the Contributor's License Agreement (CLA). 65 | 66 | As a community member, you must sign the CLA before you can contribute large submissions to this project. You only need complete and submit the CLA document once. Carefully review the document. You may be required to have your employer sign the document. 67 | 68 | ### What happens with my contributions? 69 | 70 | When you submit your changes via a pull request, our team will be notified and will review your pull request. You'll receive notifications about your pull request from GitHub; you may also be notified by someone from our team if we need more information. We reserve the right to edit your submission for legal, style, clarity, or other issues. 71 | 72 | ### Who approves pull requests? 73 | 74 | The admin of the repository approves pull requests. 75 | 76 | ### How soon will I get a response about my change request or issue? 77 | 78 | We typically review pull requests and respond to issues within 10 business days. 79 | 80 | ## More resources 81 | 82 | - To learn more about Markdown, see [Daring Fireball](http://daringfireball.net/). 83 | - To learn more about using Git and GitHub, check out the [GitHub Help section](http://help.github.com/). 84 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Office Add-in TaskPane 2 | 3 | MIT License 4 | 5 | Copyright (c) Microsoft Corporation. All rights reserved. 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | We don't have the resources to support an Angular option in Yo Office, so this repo is being archived. 2 | 3 | # Office-Addin-TaskPane-Angular 4 | 5 | This repository contains the source code used by the [Yo Office generator](https://github.com/OfficeDev/generator-office) when you create a new Office Add-in that appears in the task pane. You can also use this repository as a sample to base your own project from if you choose not to use the generator. 6 | 7 | *Note that current versions of Angular do not support IE (Internet Explorer). This means that versions of office that use the IE version of the web view will not work with and Angular base Office Add-in 8 | 9 | ## TypeScript 10 | 11 | This template is written using [TypeScript](http://www.typescriptlang.org/). For the JavaScript version of this template, go to [Office-Addin-TaskPane-Angular-JS](https://github.com/OfficeDev/Office-Addin-TaskPane-Angular-JS). 12 | 13 | ## Debugging 14 | 15 | This template supports debugging using any of the following techniques: 16 | 17 | - [Use a browser's developer tools](https://docs.microsoft.com/office/dev/add-ins/testing/debug-add-ins-in-office-online) 18 | - [Attach a debugger from the task pane](https://docs.microsoft.com/office/dev/add-ins/testing/attach-debugger-from-task-pane) 19 | 20 | ## Questions and comments 21 | 22 | We'd love to get your feedback about this sample. You can send your feedback to us in the *Issues* section of this repository. 23 | 24 | Questions about Office Add-ins development in general should be posted to [Microsoft Q&A](https://docs.microsoft.com/answers/questions/185087/questions-about-office-add-ins.html). If your question is about the Office JavaScript APIs, make sure it's tagged with [office-js-dev]. 25 | 26 | ## Join the Microsoft 365 Developer Program 27 | Get a free sandbox, tools, and other resources you need to build solutions for the Microsoft 365 platform. 28 | - [Free developer sandbox](https://developer.microsoft.com/microsoft-365/dev-program#Subscription) Get a free, renewable 90-day Microsoft 365 E5 developer subscription. 29 | - [Sample data packs](https://developer.microsoft.com/microsoft-365/dev-program#Sample) Automatically configure your sandbox by installing user data and content to help you build your solutions. 30 | - [Access to experts](https://developer.microsoft.com/microsoft-365/dev-program#Experts) Access community events to learn from Microsoft 365 experts. 31 | - [Personalized recommendations](https://developer.microsoft.com/microsoft-365/dev-program#Recommendations) Find developer resources quickly from your personalized dashboard. 32 | 33 | ## Additional resources 34 | 35 | * [Office Add-ins documentation](https://docs.microsoft.com/office/dev/add-ins/overview/office-add-ins) 36 | * More Office Add-ins samples at [OfficeDev on Github](https://github.com/officedev) 37 | 38 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information, see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 39 | 40 | ## Copyright 41 | 42 | Copyright (c) 2021 Microsoft Corporation. All rights reserved. 43 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). 40 | 41 | 42 | -------------------------------------------------------------------------------- /assets/icon-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/Office-Addin-TaskPane-Angular/752e903c1a063560a18b1b17473c834c8fdb3867/assets/icon-128.png -------------------------------------------------------------------------------- /assets/icon-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/Office-Addin-TaskPane-Angular/752e903c1a063560a18b1b17473c834c8fdb3867/assets/icon-16.png -------------------------------------------------------------------------------- /assets/icon-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/Office-Addin-TaskPane-Angular/752e903c1a063560a18b1b17473c834c8fdb3867/assets/icon-32.png -------------------------------------------------------------------------------- /assets/icon-64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/Office-Addin-TaskPane-Angular/752e903c1a063560a18b1b17473c834c8fdb3867/assets/icon-64.png -------------------------------------------------------------------------------- /assets/icon-80.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/Office-Addin-TaskPane-Angular/752e903c1a063560a18b1b17473c834c8fdb3867/assets/icon-80.png -------------------------------------------------------------------------------- /assets/logo-filled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/Office-Addin-TaskPane-Angular/752e903c1a063560a18b1b17473c834c8fdb3867/assets/logo-filled.png -------------------------------------------------------------------------------- /babel.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-typescript"] 3 | } -------------------------------------------------------------------------------- /convertToSingleHost.js: -------------------------------------------------------------------------------- 1 | /* global require, process, console */ 2 | 3 | const convertTest = process.argv[3] === "convert-test"; 4 | const fs = require("fs"); 5 | const host = process.argv[2]; 6 | const hosts = ["excel", "onenote", "outlook", "powerpoint", "project", "word"]; 7 | const path = require("path"); 8 | const util = require("util"); 9 | const testPackages = [ 10 | "@types/mocha", 11 | "@types/node", 12 | "current-processes", 13 | "mocha", 14 | "office-addin-mock", 15 | "office-addin-test-helpers", 16 | "office-addin-test-server", 17 | "ts-node", 18 | ]; 19 | const readFileAsync = util.promisify(fs.readFile); 20 | const unlinkFileAsync = util.promisify(fs.unlink); 21 | const writeFileAsync = util.promisify(fs.writeFile); 22 | 23 | async function modifyProjectForSingleHost(host) { 24 | if (!host) { 25 | throw new Error("The host was not provided."); 26 | } 27 | if (!hosts.includes(host)) { 28 | throw new Error(`'${host}' is not a supported host.`); 29 | } 30 | await convertProjectToSingleHost(host); 31 | await updatePackageJsonForSingleHost(host); 32 | if (!convertTest) { 33 | await updateLaunchJsonFile(); 34 | } 35 | } 36 | 37 | async function convertProjectToSingleHost(host) { 38 | // copy host-specific manifest over manifest.xml 39 | const manifestContent = await readFileAsync(`./manifest.${host}.xml`, "utf8"); 40 | await writeFileAsync(`./manifest.xml`, manifestContent); 41 | 42 | // copy over host-specific taskpane code to taskpane.ts 43 | const srcContent = await readFileAsync(`./src/taskpane/app/${host}.app.component.ts`, "utf8"); 44 | await writeFileAsync(`./src/taskpane/app/app.component.ts`, srcContent); 45 | 46 | // delete all test files by default for now - eventually we want to convert the tests by default 47 | if (convertTest && (host === "excel" || host === "word")) { 48 | // copy over host-specific taskpane test code to test-taskpane.ts 49 | const testTaskpaneContent = await readFileAsync(`./test/src/test.${host}.app.component.ts`, "utf8"); 50 | const updatedTestTaskpaneContent = testTaskpaneContent.replace( 51 | `../../src/taskpane/app/${host}.app.component`, 52 | `../../src/taskpane/app/app.component` 53 | ); 54 | await writeFileAsync(`./test/src/test.app.component.ts`, updatedTestTaskpaneContent); 55 | 56 | // update ui-test.ts to only run against specified host 57 | const testContent = await readFileAsync(`./test/ui-test.ts`, "utf8"); 58 | const updatedTestContent = testContent.replace(`const hosts = ["Excel", "Word"]`, `const hosts = ["${host == "excel" ? "Excel" : "Word"}"]`); 59 | await writeFileAsync(`./test/ui-test.ts`, updatedTestContent); 60 | 61 | // delete all host-specific test files after converting to single host 62 | hosts.forEach(async function (host) { 63 | if (host == "excel" || host == "word") { 64 | await unlinkFileAsync(`./test/src/test.${host}.app.component.ts`); 65 | } 66 | }); 67 | } else { 68 | deleteFolder(path.resolve(`./test`)); 69 | } 70 | 71 | // delete all host-specific files 72 | hosts.forEach(async function (host) { 73 | await unlinkFileAsync(`./manifest.${host}.xml`); 74 | await unlinkFileAsync(`./src/taskpane/app/${host}.app.component.ts`); 75 | }); 76 | 77 | // delete the .github folder 78 | deleteFolder(path.resolve(`./.github`)); 79 | 80 | // delete CI/CD pipeline files 81 | deleteFolder(path.resolve(`./.azure-devops`)); 82 | 83 | // delete repo support files 84 | await deleteSupportFiles(); 85 | } 86 | 87 | async function updatePackageJsonForSingleHost(host) { 88 | // update package.json to reflect selected host 89 | const packageJson = `./package.json`; 90 | const data = await readFileAsync(packageJson, "utf8"); 91 | let content = JSON.parse(data); 92 | 93 | // update 'config' section in package.json to use selected host 94 | content.config["app_to_debug"] = host; 95 | 96 | // remove 'engines' section 97 | delete content.engines; 98 | 99 | // update sideload and unload scripts to use selected host. 100 | ["sideload", "unload"].forEach((key) => { 101 | content.scripts[key] = content.scripts[`${key}:${host}`]; 102 | }); 103 | 104 | // remove scripts that are unrelated to the selected host 105 | Object.keys(content.scripts).forEach(function (key) { 106 | if ( 107 | key.startsWith("sideload:") || 108 | key.startsWith("unload:") || 109 | key === "convert-to-single-host" || 110 | key === "start:desktop:outlook" 111 | ) { 112 | delete content.scripts[key]; 113 | } 114 | }); 115 | 116 | if (!convertTest) { 117 | // remove test-related scripts 118 | Object.keys(content.scripts).forEach(function (key) { 119 | if (key.includes("test")) { 120 | delete content.scripts[key]; 121 | } 122 | }); 123 | 124 | // remove test-related packages 125 | Object.keys(content.devDependencies).forEach(function (key) { 126 | if (testPackages.includes(key)) { 127 | delete content.devDependencies[key]; 128 | } 129 | }); 130 | } 131 | 132 | // write updated json to file 133 | await writeFileAsync(packageJson, JSON.stringify(content, null, 2)); 134 | } 135 | 136 | async function updateLaunchJsonFile() { 137 | // remove 'Debug Tests' configuration from launch.json 138 | const launchJson = `.vscode/launch.json`; 139 | const launchJsonContent = await readFileAsync(launchJson, "utf8"); 140 | const regex = /(.+{\r?\n.*"name": "Debug (?:UI|Unit) Tests",\r?\n(?:.*\r?\n)*?.*},.*\r?\n)/gm; 141 | const updatedContent = launchJsonContent.replace(regex, ""); 142 | await writeFileAsync(launchJson, updatedContent); 143 | } 144 | 145 | function deleteFolder(folder) { 146 | try { 147 | if (fs.existsSync(folder)) { 148 | fs.readdirSync(folder).forEach(function (file) { 149 | const curPath = `${folder}/${file}`; 150 | 151 | if (fs.lstatSync(curPath).isDirectory()) { 152 | deleteFolder(curPath); 153 | } else { 154 | fs.unlinkSync(curPath); 155 | } 156 | }); 157 | fs.rmdirSync(folder); 158 | } 159 | } catch (err) { 160 | throw new Error(`Unable to delete folder "${folder}".\n${err}`); 161 | } 162 | } 163 | 164 | async function deleteSupportFiles() { 165 | await unlinkFileAsync("CONTRIBUTING.md"); 166 | await unlinkFileAsync("LICENSE"); 167 | await unlinkFileAsync("README.md"); 168 | await unlinkFileAsync("SECURITY.md"); 169 | await unlinkFileAsync("./convertToSingleHost.js"); 170 | await unlinkFileAsync(".npmrc"); 171 | await unlinkFileAsync("package-lock.json"); 172 | } 173 | 174 | /** 175 | * Modify the project so that it only supports a single host. 176 | * @param host The host to support. 177 | */ 178 | modifyProjectForSingleHost(host).catch((err) => { 179 | console.error(`Error: ${err instanceof Error ? err.message : err}`); 180 | process.exitCode = 1; 181 | }); 182 | -------------------------------------------------------------------------------- /manifest.excel.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 05c2e1c9-3e1d-406e-9a91-e9ac64854143 7 | 1.0.0.0 8 | Contoso 9 | en-US 10 | 11 | 12 | 13 | 14 | 15 | 16 | https://www.contoso.com 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | ReadWriteDocument 25 | 26 | 27 | 28 | 29 | 30 | 31 | <Description resid="GetStarted.Description"/> 32 | <LearnMoreUrl resid="GetStarted.LearnMoreUrl"/> 33 | </GetStarted> 34 | <FunctionFile resid="Commands.Url" /> 35 | <ExtensionPoint xsi:type="PrimaryCommandSurface"> 36 | <OfficeTab id="TabHome"> 37 | <Group id="CommandsGroup"> 38 | <Label resid="CommandsGroup.Label" /> 39 | <Icon> 40 | <bt:Image size="16" resid="Icon.16x16" /> 41 | <bt:Image size="32" resid="Icon.32x32" /> 42 | <bt:Image size="80" resid="Icon.80x80" /> 43 | </Icon> 44 | <Control xsi:type="Button" id="TaskpaneButton"> 45 | <Label resid="TaskpaneButton.Label" /> 46 | <Supertip> 47 | <Title resid="TaskpaneButton.Label" /> 48 | <Description resid="TaskpaneButton.Tooltip" /> 49 | </Supertip> 50 | <Icon> 51 | <bt:Image size="16" resid="Icon.16x16" /> 52 | <bt:Image size="32" resid="Icon.32x32" /> 53 | <bt:Image size="80" resid="Icon.80x80" /> 54 | </Icon> 55 | <Action xsi:type="ShowTaskpane"> 56 | <TaskpaneId>ButtonId1</TaskpaneId> 57 | <SourceLocation resid="Taskpane.Url" /> 58 | </Action> 59 | </Control> 60 | </Group> 61 | </OfficeTab> 62 | </ExtensionPoint> 63 | </DesktopFormFactor> 64 | </Host> 65 | </Hosts> 66 | <Resources> 67 | <bt:Images> 68 | <bt:Image id="Icon.16x16" DefaultValue="https://localhost:3000/assets/icon-16.png"/> 69 | <bt:Image id="Icon.32x32" DefaultValue="https://localhost:3000/assets/icon-32.png"/> 70 | <bt:Image id="Icon.80x80" DefaultValue="https://localhost:3000/assets/icon-80.png"/> 71 | </bt:Images> 72 | <bt:Urls> 73 | <bt:Url id="GetStarted.LearnMoreUrl" DefaultValue="https://go.microsoft.com/fwlink/?LinkId=276812" /> 74 | <bt:Url id="Commands.Url" DefaultValue="https://localhost:3000/commands.html" /> 75 | <bt:Url id="Taskpane.Url" DefaultValue="https://localhost:3000/taskpane.html" /> 76 | </bt:Urls> 77 | <bt:ShortStrings> 78 | <bt:String id="GetStarted.Title" DefaultValue="Get started with your sample add-in!" /> 79 | <bt:String id="CommandsGroup.Label" DefaultValue="Commands Group" /> 80 | <bt:String id="TaskpaneButton.Label" DefaultValue="Show Taskpane" /> 81 | </bt:ShortStrings> 82 | <bt:LongStrings> 83 | <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." /> 84 | <bt:String id="TaskpaneButton.Tooltip" DefaultValue="Click to Show a Taskpane" /> 85 | </bt:LongStrings> 86 | </Resources> 87 | </VersionOverrides> 88 | </OfficeApp> -------------------------------------------------------------------------------- /manifest.onenote.xml: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="UTF-8" standalone="yes"?> 2 | <OfficeApp xmlns="http://schemas.microsoft.com/office/appforoffice/1.1" 3 | xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 | xmlns:bt="http://schemas.microsoft.com/office/officeappbasictypes/1.0" 5 | xmlns:ov="http://schemas.microsoft.com/office/taskpaneappversionoverrides" xsi:type="TaskPaneApp"> 6 | <Id>05c2e1c9-3e1d-406e-9a91-e9ac64854143</Id> 7 | <Version>1.0.0.0</Version> 8 | <ProviderName>Contoso</ProviderName> 9 | <DefaultLocale>en-US</DefaultLocale> 10 | <DisplayName DefaultValue="Contoso Task Pane Add-in"/> 11 | <Description DefaultValue="A template to get started."/> 12 | <IconUrl DefaultValue="https://localhost:3000/assets/icon-32.png"/> 13 | <HighResolutionIconUrl DefaultValue="https://localhost:3000/assets/icon-64.png"/> 14 | <SupportUrl DefaultValue="https://www.contoso.com/help"/> 15 | <AppDomains> 16 | <AppDomain>https://www.contoso.com</AppDomain> 17 | </AppDomains> 18 | <Hosts> 19 | <Host Name="Notebook"/> 20 | </Hosts> 21 | <DefaultSettings> 22 | <SourceLocation DefaultValue="https://localhost:3000/taskpane.html"/> 23 | </DefaultSettings> 24 | <Permissions>ReadWriteDocument</Permissions> 25 | <VersionOverrides xmlns="http://schemas.microsoft.com/office/taskpaneappversionoverrides" xsi:type="VersionOverridesV1_0"> 26 | <Hosts> 27 | <Host xsi:type="Notebook"> 28 | <DesktopFormFactor> 29 | <GetStarted> 30 | <Title resid="GetStarted.Title"/> 31 | <Description resid="GetStarted.Description"/> 32 | <LearnMoreUrl resid="GetStarted.LearnMoreUrl"/> 33 | </GetStarted> 34 | <FunctionFile resid="Commands.Url" /> 35 | <ExtensionPoint xsi:type="PrimaryCommandSurface"> 36 | <OfficeTab id="TabHome"> 37 | <Group id="CommandsGroup"> 38 | <Label resid="CommandsGroup.Label" /> 39 | <Icon> 40 | <bt:Image size="16" resid="Icon.16x16" /> 41 | <bt:Image size="32" resid="Icon.32x32" /> 42 | <bt:Image size="80" resid="Icon.80x80" /> 43 | </Icon> 44 | <Control xsi:type="Button" id="TaskpaneButton"> 45 | <Label resid="TaskpaneButton.Label" /> 46 | <Supertip> 47 | <Title resid="TaskpaneButton.Label" /> 48 | <Description resid="TaskpaneButton.Tooltip" /> 49 | </Supertip> 50 | <Icon> 51 | <bt:Image size="16" resid="Icon.16x16" /> 52 | <bt:Image size="32" resid="Icon.32x32" /> 53 | <bt:Image size="80" resid="Icon.80x80" /> 54 | </Icon> 55 | <Action xsi:type="ShowTaskpane"> 56 | <TaskpaneId>ButtonId1</TaskpaneId> 57 | <SourceLocation resid="Taskpane.Url" /> 58 | </Action> 59 | </Control> 60 | </Group> 61 | </OfficeTab> 62 | </ExtensionPoint> 63 | </DesktopFormFactor> 64 | </Host> 65 | </Hosts> 66 | <Resources> 67 | <bt:Images> 68 | <bt:Image id="Icon.16x16" DefaultValue="https://localhost:3000/assets/icon-16.png"/> 69 | <bt:Image id="Icon.32x32" DefaultValue="https://localhost:3000/assets/icon-32.png"/> 70 | <bt:Image id="Icon.80x80" DefaultValue="https://localhost:3000/assets/icon-80.png"/> 71 | </bt:Images> 72 | <bt:Urls> 73 | <bt:Url id="GetStarted.LearnMoreUrl" DefaultValue="https://go.microsoft.com/fwlink/?LinkId=276812" /> 74 | <bt:Url id="Commands.Url" DefaultValue="https://localhost:3000/commands.html" /> 75 | <bt:Url id="Taskpane.Url" DefaultValue="https://localhost:3000/taskpane.html" /> 76 | </bt:Urls> 77 | <bt:ShortStrings> 78 | <bt:String id="GetStarted.Title" DefaultValue="Get started with your sample add-in!" /> 79 | <bt:String id="CommandsGroup.Label" DefaultValue="Commands Group" /> 80 | <bt:String id="TaskpaneButton.Label" DefaultValue="Show Taskpane" /> 81 | </bt:ShortStrings> 82 | <bt:LongStrings> 83 | <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." /> 84 | <bt:String id="TaskpaneButton.Tooltip" DefaultValue="Click to Show a Taskpane" /> 85 | </bt:LongStrings> 86 | </Resources> 87 | </VersionOverrides> 88 | </OfficeApp> -------------------------------------------------------------------------------- /manifest.outlook.xml: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="UTF-8" standalone="yes"?> 2 | <OfficeApp xmlns="http://schemas.microsoft.com/office/appforoffice/1.1" 3 | xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 | xmlns:bt="http://schemas.microsoft.com/office/officeappbasictypes/1.0" 5 | xmlns:mailappor="http://schemas.microsoft.com/office/mailappversionoverrides/1.0" xsi:type="MailApp"> 6 | <Id>05c2e1c9-3e1d-406e-9a91-e9ac64854143</Id> 7 | <Version>1.0.0.0</Version> 8 | <ProviderName>Contoso</ProviderName> 9 | <DefaultLocale>en-US</DefaultLocale> 10 | <DisplayName DefaultValue="Contoso Task Pane Add-in"/> 11 | <Description DefaultValue="A template to get started."/> 12 | <IconUrl DefaultValue="https://localhost:3000/assets/icon-64.png"/> 13 | <HighResolutionIconUrl DefaultValue="https://localhost:3000/assets/icon-128.png"/> 14 | <SupportUrl DefaultValue="https://www.contoso.com/help"/> 15 | <AppDomains> 16 | <AppDomain>https://www.contoso.com</AppDomain> 17 | </AppDomains> 18 | <Hosts> 19 | <Host Name="Mailbox" /> 20 | </Hosts> 21 | <Requirements> 22 | <Sets> 23 | <Set Name="Mailbox" MinVersion="1.1" /> 24 | </Sets> 25 | </Requirements> 26 | <FormSettings> 27 | <Form xsi:type="ItemRead"> 28 | <DesktopSettings> 29 | <SourceLocation DefaultValue="https://localhost:3000/taskpane.html"/> 30 | <RequestedHeight>250</RequestedHeight> 31 | </DesktopSettings> 32 | </Form> 33 | </FormSettings> 34 | <Permissions>ReadWriteItem</Permissions> 35 | <Rule xsi:type="RuleCollection" Mode="Or"> 36 | <Rule xsi:type="ItemIs" ItemType="Message" FormType="Read" /> 37 | </Rule> 38 | <DisableEntityHighlighting>false</DisableEntityHighlighting> 39 | <VersionOverrides xmlns="http://schemas.microsoft.com/office/mailappversionoverrides" xsi:type="VersionOverridesV1_0"> 40 | <Requirements> 41 | <bt:Sets DefaultMinVersion="1.3"> 42 | <bt:Set Name="Mailbox" /> 43 | </bt:Sets> 44 | </Requirements> 45 | <Hosts> 46 | <Host xsi:type="MailHost"> 47 | <DesktopFormFactor> 48 | <FunctionFile resid="Commands.Url" /> 49 | <ExtensionPoint xsi:type="MessageReadCommandSurface"> 50 | <OfficeTab id="TabDefault"> 51 | <Group id="msgReadGroup"> 52 | <Label resid="GroupLabel" /> 53 | <Control xsi:type="Button" id="msgReadOpenPaneButton"> 54 | <Label resid="TaskpaneButton.Label" /> 55 | <Supertip> 56 | <Title resid="TaskpaneButton.Label" /> 57 | <Description resid="TaskpaneButton.Tooltip" /> 58 | </Supertip> 59 | <Icon> 60 | <bt:Image size="16" resid="Icon.16x16" /> 61 | <bt:Image size="32" resid="Icon.32x32" /> 62 | <bt:Image size="80" resid="Icon.80x80" /> 63 | </Icon> 64 | <Action xsi:type="ShowTaskpane"> 65 | <SourceLocation resid="Taskpane.Url" /> 66 | </Action> 67 | </Control> 68 | <Control xsi:type="Button" id="ActionButton"> 69 | <Label resid="ActionButton.Label"/> 70 | <Supertip> 71 | <Title resid="ActionButton.Label"/> 72 | <Description resid="ActionButton.Tooltip"/> 73 | </Supertip> 74 | <Icon> 75 | <bt:Image size="16" resid="Icon.16x16"/> 76 | <bt:Image size="32" resid="Icon.32x32"/> 77 | <bt:Image size="80" resid="Icon.80x80"/> 78 | </Icon> 79 | <Action xsi:type="ExecuteFunction"> 80 | <FunctionName>action</FunctionName> 81 | </Action> 82 | </Control> 83 | </Group> 84 | </OfficeTab> 85 | </ExtensionPoint> 86 | </DesktopFormFactor> 87 | </Host> 88 | </Hosts> 89 | <Resources> 90 | <bt:Images> 91 | <bt:Image id="Icon.16x16" DefaultValue="https://localhost:3000/assets/icon-16.png"/> 92 | <bt:Image id="Icon.32x32" DefaultValue="https://localhost:3000/assets/icon-32.png"/> 93 | <bt:Image id="Icon.80x80" DefaultValue="https://localhost:3000/assets/icon-80.png"/> 94 | </bt:Images> 95 | <bt:Urls> 96 | <bt:Url id="Commands.Url" DefaultValue="https://localhost:3000/commands.html" /> 97 | <bt:Url id="Taskpane.Url" DefaultValue="https://localhost:3000/taskpane.html" /> 98 | </bt:Urls> 99 | <bt:ShortStrings> 100 | <bt:String id="GroupLabel" DefaultValue="Contoso Add-in"/> 101 | <bt:String id="TaskpaneButton.Label" DefaultValue="Show Taskpane"/> 102 | <bt:String id="ActionButton.Label" DefaultValue="Perform an action"/> 103 | </bt:ShortStrings> 104 | <bt:LongStrings> 105 | <bt:String id="TaskpaneButton.Tooltip" DefaultValue="Opens a pane displaying all available properties."/> 106 | <bt:String id="ActionButton.Tooltip" DefaultValue="Perform an action when clicked."/> 107 | </bt:LongStrings> 108 | </Resources> 109 | </VersionOverrides> 110 | </OfficeApp> -------------------------------------------------------------------------------- /manifest.powerpoint.xml: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="UTF-8" standalone="yes"?> 2 | <OfficeApp xmlns="http://schemas.microsoft.com/office/appforoffice/1.1" 3 | xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 | xmlns:bt="http://schemas.microsoft.com/office/officeappbasictypes/1.0" 5 | xmlns:ov="http://schemas.microsoft.com/office/taskpaneappversionoverrides" xsi:type="TaskPaneApp"> 6 | <Id>05c2e1c9-3e1d-406e-9a91-e9ac64854143</Id> 7 | <Version>1.0.0.0</Version> 8 | <ProviderName>Contoso</ProviderName> 9 | <DefaultLocale>en-US</DefaultLocale> 10 | <DisplayName DefaultValue="Contoso Task Pane Add-in"/> 11 | <Description DefaultValue="A template to get started."/> 12 | <IconUrl DefaultValue="https://localhost:3000/assets/icon-32.png"/> 13 | <HighResolutionIconUrl DefaultValue="https://localhost:3000/assets/icon-64.png"/> 14 | <SupportUrl DefaultValue="https://www.contoso.com/help"/> 15 | <AppDomains> 16 | <AppDomain>https://www.contoso.com</AppDomain> 17 | </AppDomains> 18 | <Hosts> 19 | <Host Name="Presentation"/> 20 | </Hosts> 21 | <DefaultSettings> 22 | <SourceLocation DefaultValue="https://localhost:3000/taskpane.html"/> 23 | </DefaultSettings> 24 | <Permissions>ReadWriteDocument</Permissions> 25 | <VersionOverrides xmlns="http://schemas.microsoft.com/office/taskpaneappversionoverrides" xsi:type="VersionOverridesV1_0"> 26 | <Hosts> 27 | <Host xsi:type="Presentation"> 28 | <DesktopFormFactor> 29 | <GetStarted> 30 | <Title resid="GetStarted.Title"/> 31 | <Description resid="GetStarted.Description"/> 32 | <LearnMoreUrl resid="GetStarted.LearnMoreUrl"/> 33 | </GetStarted> 34 | <FunctionFile resid="Commands.Url" /> 35 | <ExtensionPoint xsi:type="PrimaryCommandSurface"> 36 | <OfficeTab id="TabHome"> 37 | <Group id="CommandsGroup"> 38 | <Label resid="CommandsGroup.Label" /> 39 | <Icon> 40 | <bt:Image size="16" resid="Icon.16x16" /> 41 | <bt:Image size="32" resid="Icon.32x32" /> 42 | <bt:Image size="80" resid="Icon.80x80" /> 43 | </Icon> 44 | <Control xsi:type="Button" id="TaskpaneButton"> 45 | <Label resid="TaskpaneButton.Label" /> 46 | <Supertip> 47 | <Title resid="TaskpaneButton.Label" /> 48 | <Description resid="TaskpaneButton.Tooltip" /> 49 | </Supertip> 50 | <Icon> 51 | <bt:Image size="16" resid="Icon.16x16" /> 52 | <bt:Image size="32" resid="Icon.32x32" /> 53 | <bt:Image size="80" resid="Icon.80x80" /> 54 | </Icon> 55 | <Action xsi:type="ShowTaskpane"> 56 | <TaskpaneId>ButtonId1</TaskpaneId> 57 | <SourceLocation resid="Taskpane.Url" /> 58 | </Action> 59 | </Control> 60 | </Group> 61 | </OfficeTab> 62 | </ExtensionPoint> 63 | </DesktopFormFactor> 64 | </Host> 65 | </Hosts> 66 | <Resources> 67 | <bt:Images> 68 | <bt:Image id="Icon.16x16" DefaultValue="https://localhost:3000/assets/icon-16.png"/> 69 | <bt:Image id="Icon.32x32" DefaultValue="https://localhost:3000/assets/icon-32.png"/> 70 | <bt:Image id="Icon.80x80" DefaultValue="https://localhost:3000/assets/icon-80.png"/> 71 | </bt:Images> 72 | <bt:Urls> 73 | <bt:Url id="GetStarted.LearnMoreUrl" DefaultValue="https://go.microsoft.com/fwlink/?LinkId=276812" /> 74 | <bt:Url id="Commands.Url" DefaultValue="https://localhost:3000/commands.html" /> 75 | <bt:Url id="Taskpane.Url" DefaultValue="https://localhost:3000/taskpane.html" /> 76 | </bt:Urls> 77 | <bt:ShortStrings> 78 | <bt:String id="GetStarted.Title" DefaultValue="Get started with your sample add-in!" /> 79 | <bt:String id="CommandsGroup.Label" DefaultValue="Commands Group" /> 80 | <bt:String id="TaskpaneButton.Label" DefaultValue="Show Taskpane" /> 81 | </bt:ShortStrings> 82 | <bt:LongStrings> 83 | <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." /> 84 | <bt:String id="TaskpaneButton.Tooltip" DefaultValue="Click to Show a Taskpane" /> 85 | </bt:LongStrings> 86 | </Resources> 87 | </VersionOverrides> 88 | </OfficeApp> -------------------------------------------------------------------------------- /manifest.project.xml: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="UTF-8" standalone="yes"?> 2 | <OfficeApp xmlns="http://schemas.microsoft.com/office/appforoffice/1.1" 3 | xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 | xmlns:bt="http://schemas.microsoft.com/office/officeappbasictypes/1.0" 5 | xmlns:ov="http://schemas.microsoft.com/office/taskpaneappversionoverrides" xsi:type="TaskPaneApp"> 6 | <Id>05c2e1c9-3e1d-406e-9a91-e9ac64854143</Id> 7 | <Version>1.0.0.0</Version> 8 | <ProviderName>Contoso</ProviderName> 9 | <DefaultLocale>en-US</DefaultLocale> 10 | <DisplayName DefaultValue="Contoso Task Pane Add-in"/> 11 | <Description DefaultValue="A template to get started."/> 12 | <IconUrl DefaultValue="https://localhost:3000/assets/icon-32.png"/> 13 | <HighResolutionIconUrl DefaultValue="https://localhost:3000/assets/icon-64.png"/> 14 | <SupportUrl DefaultValue="https://www.contoso.com/help"/> 15 | <AppDomains> 16 | <AppDomain>https://www.contoso.com</AppDomain> 17 | </AppDomains> 18 | <Hosts> 19 | <Host Name="Project"/> 20 | </Hosts> 21 | <DefaultSettings> 22 | <SourceLocation DefaultValue="https://localhost:3000/taskpane.html"/> 23 | </DefaultSettings> 24 | <Permissions>ReadWriteDocument</Permissions> 25 | </OfficeApp> -------------------------------------------------------------------------------- /manifest.word.xml: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="UTF-8" standalone="yes"?> 2 | <OfficeApp xmlns="http://schemas.microsoft.com/office/appforoffice/1.1" 3 | xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 | xmlns:bt="http://schemas.microsoft.com/office/officeappbasictypes/1.0" 5 | xmlns:ov="http://schemas.microsoft.com/office/taskpaneappversionoverrides" xsi:type="TaskPaneApp"> 6 | <Id>05c2e1c9-3e1d-406e-9a91-e9ac64854143</Id> 7 | <Version>1.0.0.0</Version> 8 | <ProviderName>Contoso</ProviderName> 9 | <DefaultLocale>en-US</DefaultLocale> 10 | <DisplayName DefaultValue="Contoso Task Pane Add-in"/> 11 | <Description DefaultValue="A template to get started."/> 12 | <IconUrl DefaultValue="https://localhost:3000/assets/icon-32.png"/> 13 | <HighResolutionIconUrl DefaultValue="https://localhost:3000/assets/icon-64.png"/> 14 | <SupportUrl DefaultValue="https://www.contoso.com/help"/> 15 | <AppDomains> 16 | <AppDomain>https://www.contoso.com</AppDomain> 17 | </AppDomains> 18 | <Hosts> 19 | <Host Name="Document"/> 20 | </Hosts> 21 | <DefaultSettings> 22 | <SourceLocation DefaultValue="https://localhost:3000/taskpane.html"/> 23 | </DefaultSettings> 24 | <Permissions>ReadWriteDocument</Permissions> 25 | <VersionOverrides xmlns="http://schemas.microsoft.com/office/taskpaneappversionoverrides" xsi:type="VersionOverridesV1_0"> 26 | <Hosts> 27 | <Host xsi:type="Document"> 28 | <DesktopFormFactor> 29 | <GetStarted> 30 | <Title resid="GetStarted.Title"/> 31 | <Description resid="GetStarted.Description"/> 32 | <LearnMoreUrl resid="GetStarted.LearnMoreUrl"/> 33 | </GetStarted> 34 | <FunctionFile resid="Commands.Url" /> 35 | <ExtensionPoint xsi:type="PrimaryCommandSurface"> 36 | <OfficeTab id="TabHome"> 37 | <Group id="CommandsGroup"> 38 | <Label resid="CommandsGroup.Label" /> 39 | <Icon> 40 | <bt:Image size="16" resid="Icon.16x16" /> 41 | <bt:Image size="32" resid="Icon.32x32" /> 42 | <bt:Image size="80" resid="Icon.80x80" /> 43 | </Icon> 44 | <Control xsi:type="Button" id="TaskpaneButton"> 45 | <Label resid="TaskpaneButton.Label" /> 46 | <Supertip> 47 | <Title resid="TaskpaneButton.Label" /> 48 | <Description resid="TaskpaneButton.Tooltip" /> 49 | </Supertip> 50 | <Icon> 51 | <bt:Image size="16" resid="Icon.16x16" /> 52 | <bt:Image size="32" resid="Icon.32x32" /> 53 | <bt:Image size="80" resid="Icon.80x80" /> 54 | </Icon> 55 | <Action xsi:type="ShowTaskpane"> 56 | <TaskpaneId>ButtonId1</TaskpaneId> 57 | <SourceLocation resid="Taskpane.Url" /> 58 | </Action> 59 | </Control> 60 | </Group> 61 | </OfficeTab> 62 | </ExtensionPoint> 63 | </DesktopFormFactor> 64 | </Host> 65 | </Hosts> 66 | <Resources> 67 | <bt:Images> 68 | <bt:Image id="Icon.16x16" DefaultValue="https://localhost:3000/assets/icon-16.png"/> 69 | <bt:Image id="Icon.32x32" DefaultValue="https://localhost:3000/assets/icon-32.png"/> 70 | <bt:Image id="Icon.80x80" DefaultValue="https://localhost:3000/assets/icon-80.png"/> 71 | </bt:Images> 72 | <bt:Urls> 73 | <bt:Url id="GetStarted.LearnMoreUrl" DefaultValue="https://go.microsoft.com/fwlink/?LinkId=276812" /> 74 | <bt:Url id="Commands.Url" DefaultValue="https://localhost:3000/commands.html" /> 75 | <bt:Url id="Taskpane.Url" DefaultValue="https://localhost:3000/taskpane.html" /> 76 | </bt:Urls> 77 | <bt:ShortStrings> 78 | <bt:String id="GetStarted.Title" DefaultValue="Get started with your sample add-in!" /> 79 | <bt:String id="CommandsGroup.Label" DefaultValue="Commands Group" /> 80 | <bt:String id="TaskpaneButton.Label" DefaultValue="Show Taskpane" /> 81 | </bt:ShortStrings> 82 | <bt:LongStrings> 83 | <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." /> 84 | <bt:String id="TaskpaneButton.Tooltip" DefaultValue="Click to Show a Taskpane" /> 85 | </bt:LongStrings> 86 | </Resources> 87 | </VersionOverrides> 88 | </OfficeApp> -------------------------------------------------------------------------------- /manifest.xml: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="UTF-8" standalone="yes"?> 2 | <OfficeApp xmlns="http://schemas.microsoft.com/office/appforoffice/1.1" 3 | xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 | xmlns:bt="http://schemas.microsoft.com/office/officeappbasictypes/1.0" 5 | xmlns:ov="http://schemas.microsoft.com/office/taskpaneappversionoverrides" xsi:type="TaskPaneApp"> 6 | <Id>05c2e1c9-3e1d-406e-9a91-e9ac64854143</Id> 7 | <Version>1.0.0.0</Version> 8 | <ProviderName>Contoso</ProviderName> 9 | <DefaultLocale>en-US</DefaultLocale> 10 | <DisplayName DefaultValue="Contoso Task Pane Add-in"/> 11 | <Description DefaultValue="A template to get started."/> 12 | <IconUrl DefaultValue="https://localhost:3000/assets/icon-32.png"/> 13 | <HighResolutionIconUrl DefaultValue="https://localhost:3000/assets/icon-64.png"/> 14 | <SupportUrl DefaultValue="https://www.contoso.com/help"/> 15 | <AppDomains> 16 | <AppDomain>https://www.contoso.com</AppDomain> 17 | </AppDomains> 18 | <Hosts> 19 | <Host Name="Document"/> 20 | <Host Name="Notebook"/> 21 | <Host Name="Presentation"/> 22 | <Host Name="Workbook"/> 23 | </Hosts> 24 | <DefaultSettings> 25 | <SourceLocation DefaultValue="https://localhost:3000/taskpane.html"/> 26 | </DefaultSettings> 27 | <Permissions>ReadWriteDocument</Permissions> 28 | <VersionOverrides xmlns="http://schemas.microsoft.com/office/taskpaneappversionoverrides" xsi:type="VersionOverridesV1_0"> 29 | <Hosts> 30 | <Host xsi:type="Document"> 31 | <DesktopFormFactor> 32 | <GetStarted> 33 | <Title resid="GetStarted.Title"/> 34 | <Description resid="GetStarted.Description"/> 35 | <LearnMoreUrl resid="GetStarted.LearnMoreUrl"/> 36 | </GetStarted> 37 | <FunctionFile resid="Commands.Url" /> 38 | <ExtensionPoint xsi:type="PrimaryCommandSurface"> 39 | <OfficeTab id="TabHome"> 40 | <Group id="CommandsGroup"> 41 | <Label resid="CommandsGroup.Label" /> 42 | <Icon> 43 | <bt:Image size="16" resid="Icon.16x16" /> 44 | <bt:Image size="32" resid="Icon.32x32" /> 45 | <bt:Image size="80" resid="Icon.80x80" /> 46 | </Icon> 47 | <Control xsi:type="Button" id="TaskpaneButton"> 48 | <Label resid="TaskpaneButton.Label" /> 49 | <Supertip> 50 | <Title resid="TaskpaneButton.Label" /> 51 | <Description resid="TaskpaneButton.Tooltip" /> 52 | </Supertip> 53 | <Icon> 54 | <bt:Image size="16" resid="Icon.16x16" /> 55 | <bt:Image size="32" resid="Icon.32x32" /> 56 | <bt:Image size="80" resid="Icon.80x80" /> 57 | </Icon> 58 | <Action xsi:type="ShowTaskpane"> 59 | <TaskpaneId>ButtonId1</TaskpaneId> 60 | <SourceLocation resid="Taskpane.Url" /> 61 | </Action> 62 | </Control> 63 | </Group> 64 | </OfficeTab> 65 | </ExtensionPoint> 66 | </DesktopFormFactor> 67 | </Host> 68 | <Host xsi:type="Notebook"> 69 | <DesktopFormFactor> 70 | <GetStarted> 71 | <Title resid="GetStarted.Title"/> 72 | <Description resid="GetStarted.Description"/> 73 | <LearnMoreUrl resid="GetStarted.LearnMoreUrl"/> 74 | </GetStarted> 75 | <FunctionFile resid="Commands.Url" /> 76 | <ExtensionPoint xsi:type="PrimaryCommandSurface"> 77 | <OfficeTab id="TabHome"> 78 | <Group id="CommandsGroup"> 79 | <Label resid="CommandsGroup.Label" /> 80 | <Icon> 81 | <bt:Image size="16" resid="Icon.16x16" /> 82 | <bt:Image size="32" resid="Icon.32x32" /> 83 | <bt:Image size="80" resid="Icon.80x80" /> 84 | </Icon> 85 | <Control xsi:type="Button" id="TaskpaneButton"> 86 | <Label resid="TaskpaneButton.Label" /> 87 | <Supertip> 88 | <Title resid="TaskpaneButton.Label" /> 89 | <Description resid="TaskpaneButton.Tooltip" /> 90 | </Supertip> 91 | <Icon> 92 | <bt:Image size="16" resid="Icon.16x16" /> 93 | <bt:Image size="32" resid="Icon.32x32" /> 94 | <bt:Image size="80" resid="Icon.80x80" /> 95 | </Icon> 96 | <Action xsi:type="ShowTaskpane"> 97 | <TaskpaneId>ButtonId1</TaskpaneId> 98 | <SourceLocation resid="Taskpane.Url" /> 99 | </Action> 100 | </Control> 101 | </Group> 102 | </OfficeTab> 103 | </ExtensionPoint> 104 | </DesktopFormFactor> 105 | </Host> 106 | <Host xsi:type="Presentation"> 107 | <DesktopFormFactor> 108 | <GetStarted> 109 | <Title resid="GetStarted.Title"/> 110 | <Description resid="GetStarted.Description"/> 111 | <LearnMoreUrl resid="GetStarted.LearnMoreUrl"/> 112 | </GetStarted> 113 | <FunctionFile resid="Commands.Url" /> 114 | <ExtensionPoint xsi:type="PrimaryCommandSurface"> 115 | <OfficeTab id="TabHome"> 116 | <Group id="CommandsGroup"> 117 | <Label resid="CommandsGroup.Label" /> 118 | <Icon> 119 | <bt:Image size="16" resid="Icon.16x16" /> 120 | <bt:Image size="32" resid="Icon.32x32" /> 121 | <bt:Image size="80" resid="Icon.80x80" /> 122 | </Icon> 123 | <Control xsi:type="Button" id="TaskpaneButton"> 124 | <Label resid="TaskpaneButton.Label" /> 125 | <Supertip> 126 | <Title resid="TaskpaneButton.Label" /> 127 | <Description resid="TaskpaneButton.Tooltip" /> 128 | </Supertip> 129 | <Icon> 130 | <bt:Image size="16" resid="Icon.16x16" /> 131 | <bt:Image size="32" resid="Icon.32x32" /> 132 | <bt:Image size="80" resid="Icon.80x80" /> 133 | </Icon> 134 | <Action xsi:type="ShowTaskpane"> 135 | <TaskpaneId>ButtonId1</TaskpaneId> 136 | <SourceLocation resid="Taskpane.Url" /> 137 | </Action> 138 | </Control> 139 | </Group> 140 | </OfficeTab> 141 | </ExtensionPoint> 142 | </DesktopFormFactor> 143 | </Host> 144 | <Host xsi:type="Workbook"> 145 | <DesktopFormFactor> 146 | <GetStarted> 147 | <Title resid="GetStarted.Title"/> 148 | <Description resid="GetStarted.Description"/> 149 | <LearnMoreUrl resid="GetStarted.LearnMoreUrl"/> 150 | </GetStarted> 151 | <FunctionFile resid="Commands.Url" /> 152 | <ExtensionPoint xsi:type="PrimaryCommandSurface"> 153 | <OfficeTab id="TabHome"> 154 | <Group id="CommandsGroup"> 155 | <Label resid="CommandsGroup.Label" /> 156 | <Icon> 157 | <bt:Image size="16" resid="Icon.16x16" /> 158 | <bt:Image size="32" resid="Icon.32x32" /> 159 | <bt:Image size="80" resid="Icon.80x80" /> 160 | </Icon> 161 | <Control xsi:type="Button" id="TaskpaneButton"> 162 | <Label resid="TaskpaneButton.Label" /> 163 | <Supertip> 164 | <Title resid="TaskpaneButton.Label" /> 165 | <Description resid="TaskpaneButton.Tooltip" /> 166 | </Supertip> 167 | <Icon> 168 | <bt:Image size="16" resid="Icon.16x16" /> 169 | <bt:Image size="32" resid="Icon.32x32" /> 170 | <bt:Image size="80" resid="Icon.80x80" /> 171 | </Icon> 172 | <Action xsi:type="ShowTaskpane"> 173 | <TaskpaneId>ButtonId1</TaskpaneId> 174 | <SourceLocation resid="Taskpane.Url" /> 175 | </Action> 176 | </Control> 177 | </Group> 178 | </OfficeTab> 179 | </ExtensionPoint> 180 | </DesktopFormFactor> 181 | </Host> 182 | </Hosts> 183 | <Resources> 184 | <bt:Images> 185 | <bt:Image id="Icon.16x16" DefaultValue="https://localhost:3000/assets/icon-16.png"/> 186 | <bt:Image id="Icon.32x32" DefaultValue="https://localhost:3000/assets/icon-32.png"/> 187 | <bt:Image id="Icon.80x80" DefaultValue="https://localhost:3000/assets/icon-80.png"/> 188 | </bt:Images> 189 | <bt:Urls> 190 | <bt:Url id="GetStarted.LearnMoreUrl" DefaultValue="https://go.microsoft.com/fwlink/?LinkId=276812" /> 191 | <bt:Url id="Commands.Url" DefaultValue="https://localhost:3000/commands.html" /> 192 | <bt:Url id="Taskpane.Url" DefaultValue="https://localhost:3000/taskpane.html" /> 193 | </bt:Urls> 194 | <bt:ShortStrings> 195 | <bt:String id="GetStarted.Title" DefaultValue="Get started with your sample add-in!" /> 196 | <bt:String id="CommandsGroup.Label" DefaultValue="Commands Group" /> 197 | <bt:String id="TaskpaneButton.Label" DefaultValue="Show Taskpane" /> 198 | </bt:ShortStrings> 199 | <bt:LongStrings> 200 | <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." /> 201 | <bt:String id="TaskpaneButton.Tooltip" DefaultValue="Click to Show a Taskpane" /> 202 | </bt:LongStrings> 203 | </Resources> 204 | </VersionOverrides> 205 | </OfficeApp> -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "office-addin-taskpane-angular", 3 | "version": "0.0.1", 4 | "repository": { 5 | "type": "git", 6 | "url": "https://github.com/OfficeDev/Office-Addin-TaskPane-Angular.git" 7 | }, 8 | "license": "MIT", 9 | "config": { 10 | "app_to_debug": "excel", 11 | "app_type_to_debug": "desktop", 12 | "dev_server_port": 3000 13 | }, 14 | "engines": { 15 | "node": ">=16 <19", 16 | "npm": ">=7 <10" 17 | }, 18 | "scripts": { 19 | "build": "webpack --mode production", 20 | "build:dev": "webpack --mode development", 21 | "convert-to-single-host": "node convertToSingleHost.js", 22 | "dev-server": "webpack serve --mode development", 23 | "lint": "office-addin-lint check", 24 | "lint:fix": "office-addin-lint fix", 25 | "prettier": "office-addin-lint prettier", 26 | "start": "office-addin-debugging start manifest.xml", 27 | "start:desktop": "office-addin-debugging start manifest.xml desktop", 28 | "start:desktop:outlook": "office-addin-debugging start manifest.outlook.xml desktop --app outlook", 29 | "start:web": "office-addin-debugging start manifest.xml web", 30 | "stop": "office-addin-debugging stop manifest.xml", 31 | "test": "npm run test:unit && npm run test:e2e", 32 | "test:e2e": "mocha -r ts-node/register test/end-to-end/*.ts", 33 | "test:unit": "mocha -r ts-node/register test/unit/*.test.ts", 34 | "validate": "office-addin-manifest validate manifest.xml", 35 | "watch": "webpack --mode development --watch" 36 | }, 37 | "dependencies": { 38 | "@angular/common": "^12.2.16", 39 | "@angular/compiler": "^12.2.16", 40 | "@angular/core": "^12.2.16", 41 | "@angular/platform-browser": "^12.2.16", 42 | "@angular/platform-browser-dynamic": "^12.2.16", 43 | "@microsoft/office-js-helpers": "^1.0.1", 44 | "core-js": "^3.9.1", 45 | "office-ui-fabric-js": "^1.3.0", 46 | "regenerator-runtime": "^0.13.7", 47 | "rxjs": "~7.5.0", 48 | "zone.js": "^0.11.4" 49 | }, 50 | "devDependencies": { 51 | "@babel/core": "^7.13.10", 52 | "@babel/preset-typescript": "^7.13.0", 53 | "@types/mocha": "^8.2.0", 54 | "@types/node": "^14.14.22", 55 | "@types/office-js": "^1.0.256", 56 | "@types/office-runtime": "^1.0.23", 57 | "acorn": "^8.5.0", 58 | "babel-loader": "^8.2.2", 59 | "copy-webpack-plugin": "^9.0.1", 60 | "eslint-plugin-office-addins": "^2.1.5", 61 | "file-loader": "^6.2.0", 62 | "html-loader": "^4.1.0", 63 | "html-webpack-plugin": "^5.5.0", 64 | "mocha": "^10.1.0", 65 | "office-addin-cli": "^1.5.5", 66 | "office-addin-debugging": "^5.0.5", 67 | "office-addin-dev-certs": "^1.11.3", 68 | "office-addin-lint": "^2.2.5", 69 | "office-addin-manifest": "^1.12.3", 70 | "office-addin-mock": "^2.3.9", 71 | "office-addin-prettier-config": "^1.2.0", 72 | "office-addin-test-helpers": "^1.4.5", 73 | "office-addin-test-server": "^1.4.8", 74 | "os-browserify": "^0.3.0", 75 | "process": "^0.11.10", 76 | "source-map-loader": "^3.0.0", 77 | "ts-loader": "^9.4.1", 78 | "ts-node": "^10.9.1", 79 | "typescript": "^4.3.5", 80 | "webpack": "^5.76.3", 81 | "webpack-cli": "^5.0.1", 82 | "webpack-dev-server": "4.13.1" 83 | }, 84 | "prettier": "office-addin-prettier-config", 85 | "browserslist": [ 86 | "ie 11" 87 | ] 88 | } 89 | -------------------------------------------------------------------------------- /src/commands/commands.html: -------------------------------------------------------------------------------- 1 | <!-- Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT License. --> 2 | 3 | <!DOCTYPE html> 4 | <html> 5 | 6 | <head> 7 | <meta charset="UTF-8" /> 8 | <meta http-equiv="X-UA-Compatible" content="IE=Edge" /> 9 | 10 | <!-- Office JavaScript API --> 11 | <script type="text/javascript" src="https://appsforoffice.microsoft.com/lib/1.1/hosted/office.js"></script> 12 | </head> 13 | 14 | <body> 15 | 16 | </body> 17 | 18 | </html> -------------------------------------------------------------------------------- /src/commands/commands.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. 3 | * See LICENSE in the project root for license information. 4 | */ 5 | 6 | /* global global, Office, self, window */ 7 | 8 | Office.onReady(() => { 9 | // If needed, Office.js is ready to be called 10 | }); 11 | 12 | /** 13 | * Shows a notification when the add-in command is executed. 14 | * @param event 15 | */ 16 | function action(event: Office.AddinCommands.Event) { 17 | const message: Office.NotificationMessageDetails = { 18 | type: Office.MailboxEnums.ItemNotificationMessageType.InformationalMessage, 19 | message: "Performed action.", 20 | icon: "Icon.80x80", 21 | persistent: true, 22 | }; 23 | 24 | // Show a notification message 25 | Office.context.mailbox.item.notificationMessages.replaceAsync("action", message); 26 | 27 | // Be sure to indicate when the add-in command function is complete 28 | event.completed(); 29 | } 30 | 31 | function getGlobal() { 32 | return typeof self !== "undefined" 33 | ? self 34 | : typeof window !== "undefined" 35 | ? window 36 | : typeof global !== "undefined" 37 | ? global 38 | : undefined; 39 | } 40 | 41 | const g = getGlobal() as any; 42 | 43 | // The add-in command functions need to be available in global scope 44 | g.action = action; 45 | -------------------------------------------------------------------------------- /src/taskpane/app/app.component.html: -------------------------------------------------------------------------------- 1 | <header class="ms-welcome__header ms-bgColor-neutralLighter ms-u-fadeIn500"> 2 | <img width="90" height="90" src="../../../assets/logo-filled.png" alt="Contoso Task Pane Add-in" title="Contoso Task Pane Add-in" /> 3 | <h1 class="ms-fontSize-su ms-fontWeight-light ms-fontColor-neutralPrimary">{{welcomeMessage}}</h1> 4 | </header> 5 | <main class="ms-welcome__main"> 6 | <h2 class="ms-font-xl ms-fontWeight-semilight ms-fontColor-neutralPrimary ms-u-slideUpIn20"> Discover what Office Add-ins can do for you today! </h2> 7 | <ul class="ms-List ms-welcome__features ms-u-slideUpIn10"> 8 | <li class="ms-ListItem"> 9 | <i class="ms-Icon ms-Icon--Ribbon"></i> 10 | <span class="ms-font-m ms-fontColor-neutralPrimary">Achieve more with Office integration</span> 11 | </li> 12 | <li class="ms-ListItem"> 13 | <i class="ms-Icon ms-Icon--Unlock"></i> 14 | <span class="ms-font-m ms-fontColor-neutralPrimary">Unlock features and functionality</span> 15 | </li> 16 | <li class="ms-ListItem"> 17 | <i class="ms-Icon ms-Icon--Design"></i> 18 | <span class="ms-font-m ms-fontColor-neutralPrimary">Create and visualize like a pro</span> 19 | </li> 20 | </ul> 21 | <br /> 22 | <br /> 23 | <p class="ms-font-l">Modify the source files, then click <b>Run</b>.</p> 24 | <div role="button" class="ms-welcome__action ms-Button ms-Button--hero ms-u-slideUpIn20" (click)="run()"> 25 | <span class="ms-Button-label">Run</span> 26 | <span class="ms-Button-icon"><i class="ms-Icon ms-Icon--ChevronRight"></i></span> 27 | </div> 28 | </main> -------------------------------------------------------------------------------- /src/taskpane/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from "@angular/core"; 2 | import * as excel from "./excel.app.component"; 3 | import * as onenote from "./onenote.app.component"; 4 | import * as outlook from "./outlook.app.component"; 5 | import * as powerpoint from "./powerpoint.app.component"; 6 | import * as project from "./project.app.component"; 7 | import * as word from "./word.app.component"; 8 | /* global Office */ 9 | 10 | @Component({ 11 | selector: "app-home", 12 | templateUrl: "./app.component.html", 13 | }) 14 | export default class AppComponent { 15 | welcomeMessage = "Welcome"; 16 | 17 | async run() { 18 | switch (Office.context.host) { 19 | case Office.HostType.Excel: { 20 | const excelComponent = new excel.default(); 21 | return excelComponent.run(); 22 | } 23 | case Office.HostType.OneNote: { 24 | const onenoteComponent = new onenote.default(); 25 | return onenoteComponent.run(); 26 | } 27 | case Office.HostType.Outlook: { 28 | const outlookComponent = new outlook.default(); 29 | return outlookComponent.run(); 30 | } 31 | case Office.HostType.PowerPoint: { 32 | const powerpointComponent = new powerpoint.default(); 33 | return powerpointComponent.run(); 34 | } 35 | case Office.HostType.Project: { 36 | const projectComponent = new project.default(); 37 | return projectComponent.run(); 38 | } 39 | case Office.HostType.Word: { 40 | const wordComponent = new word.default(); 41 | return wordComponent.run(); 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/taskpane/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from "@angular/core"; 2 | import { BrowserModule } from "@angular/platform-browser"; 3 | import AppComponent from "./app.component"; 4 | 5 | @NgModule({ 6 | declarations: [AppComponent], 7 | imports: [BrowserModule], 8 | bootstrap: [AppComponent], 9 | }) 10 | export default class AppModule {} 11 | -------------------------------------------------------------------------------- /src/taskpane/app/excel.app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from "@angular/core"; 2 | 3 | /* global console, Excel */ 4 | 5 | @Component({ 6 | selector: "app-home", 7 | templateUrl: "./app.component.html", 8 | }) 9 | export default class AppComponent { 10 | welcomeMessage = "Welcome"; 11 | 12 | async run() { 13 | try { 14 | await Excel.run(async (context) => { 15 | /** 16 | * Insert your Excel code here 17 | */ 18 | const range = context.workbook.getSelectedRange(); 19 | 20 | // Read the range address 21 | range.load("address"); 22 | 23 | // Update the fill color 24 | range.format.fill.color = "yellow"; 25 | 26 | await context.sync(); 27 | console.log(`The range address was ${range.address}.`); 28 | }); 29 | } catch (error) { 30 | console.error(error); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/taskpane/app/onenote.app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from "@angular/core"; 2 | 3 | @Component({ 4 | selector: "app-home", 5 | templateUrl: "./app.component.html", 6 | }) 7 | export default class AppComponent { 8 | welcomeMessage = "Welcome"; 9 | 10 | async run() { 11 | /** 12 | * Insert your OneNote code here 13 | */ 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/taskpane/app/outlook.app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from "@angular/core"; 2 | 3 | @Component({ 4 | selector: "app-home", 5 | templateUrl: "./app.component.html", 6 | }) 7 | export default class AppComponent { 8 | welcomeMessage = "Welcome"; 9 | 10 | async run() { 11 | /** 12 | * Insert your Outlook code here 13 | */ 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/taskpane/app/powerpoint.app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from "@angular/core"; 2 | 3 | /* global Office, console */ 4 | 5 | @Component({ 6 | selector: "app-home", 7 | templateUrl: "./app.component.html", 8 | }) 9 | export default class AppComponent { 10 | welcomeMessage = "Welcome"; 11 | 12 | async run() { 13 | /** 14 | * Insert your PowerPoint code here 15 | */ 16 | Office.context.document.setSelectedDataAsync( 17 | "Hello World!", 18 | { 19 | coercionType: Office.CoercionType.Text, 20 | }, 21 | (result) => { 22 | if (result.status === Office.AsyncResultStatus.Failed) { 23 | console.error(result.error.message); 24 | } 25 | } 26 | ); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/taskpane/app/project.app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from "@angular/core"; 2 | 3 | /* global Office, console*/ 4 | 5 | @Component({ 6 | selector: "app-home", 7 | templateUrl: "./app.component.html", 8 | }) 9 | export default class AppComponent { 10 | welcomeMessage = "Welcome"; 11 | 12 | async run() { 13 | try { 14 | // Get the GUID of the selected task 15 | Office.context.document.getSelectedTaskAsync((result) => { 16 | let taskGuid; 17 | if (result.status === Office.AsyncResultStatus.Succeeded) { 18 | taskGuid = result.value; 19 | 20 | // Set the specified fields for the selected task. 21 | const targetFields = [Office.ProjectTaskFields.Name, Office.ProjectTaskFields.Notes]; 22 | const fieldValues = ["New task name", "Notes for the task."]; 23 | 24 | // Set the field value. If the call is successful, set the next field. 25 | for (let index = 0; index < targetFields.length; index++) { 26 | Office.context.document.setTaskFieldAsync(taskGuid, targetFields[index], fieldValues[index], (result) => { 27 | if (result.status === Office.AsyncResultStatus.Succeeded) { 28 | index++; 29 | } else { 30 | console.log(result.error); 31 | } 32 | }); 33 | } 34 | } else { 35 | console.log(result.error); 36 | } 37 | }); 38 | } catch (error) { 39 | console.error(error); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/taskpane/app/word.app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from "@angular/core"; 2 | 3 | /* global Word */ 4 | 5 | @Component({ 6 | selector: "app-home", 7 | templateUrl: "./app.component.html", 8 | }) 9 | export default class AppComponent { 10 | welcomeMessage = "Welcome"; 11 | 12 | async run() { 13 | return Word.run(async (context) => { 14 | /** 15 | * Insert your Word code here 16 | */ 17 | 18 | // insert a paragraph at the end of the document. 19 | const paragraph = context.document.body.insertParagraph("Hello World", Word.InsertLocation.end); 20 | 21 | // change the paragraph color to blue. 22 | paragraph.font.color = "blue"; 23 | 24 | await context.sync(); 25 | }); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/taskpane/taskpane.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. 3 | * See LICENSE in the project root for license information. 4 | */ 5 | 6 | html, 7 | body { 8 | width: 100%; 9 | height: 100%; 10 | margin: 0; 11 | padding: 0; 12 | } 13 | 14 | ul { 15 | margin: 0; 16 | padding: 0; 17 | } 18 | 19 | .ms-welcome__header { 20 | padding: 20px; 21 | padding-bottom: 30px; 22 | padding-top: 100px; 23 | display: -webkit-flex; 24 | display: flex; 25 | -webkit-flex-direction: column; 26 | flex-direction: column; 27 | align-items: center; 28 | } 29 | 30 | .ms-welcome__main { 31 | display: -webkit-flex; 32 | display: flex; 33 | -webkit-flex-direction: column; 34 | flex-direction: column; 35 | -webkit-flex-wrap: nowrap; 36 | flex-wrap: nowrap; 37 | -webkit-align-items: center; 38 | align-items: center; 39 | -webkit-flex: 1 0 0; 40 | flex: 1 0 0; 41 | padding: 10px 20px; 42 | } 43 | 44 | .ms-welcome__main > h2 { 45 | width: 100%; 46 | text-align: center; 47 | } 48 | 49 | .ms-welcome__features { 50 | list-style-type: none; 51 | margin-top: 20px; 52 | } 53 | 54 | .ms-welcome__features.ms-List .ms-ListItem { 55 | padding-bottom: 20px; 56 | display: -webkit-flex; 57 | display: flex; 58 | } 59 | 60 | .ms-welcome__features.ms-List .ms-ListItem > .ms-Icon { 61 | margin-right: 10px; 62 | } 63 | 64 | .ms-welcome__action.ms-Button--hero { 65 | margin-top: 30px; 66 | } 67 | 68 | .ms-Button.ms-Button--hero .ms-Button-label { 69 | color: #0078d7; 70 | } 71 | 72 | .ms-Button.ms-Button--hero:hover .ms-Button-label, 73 | .ms-Button.ms-Button--hero:focus .ms-Button-label{ 74 | color: #005a9e; 75 | cursor: pointer; 76 | } 77 | 78 | b { 79 | font-weight: bold; 80 | } -------------------------------------------------------------------------------- /src/taskpane/taskpane.html: -------------------------------------------------------------------------------- 1 | <!-- Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT License. --> 2 | <!-- This file shows how to design a first-run page that provides a welcome screen to the user about the features of the add-in. --> 3 | 4 | <!DOCTYPE html> 5 | <html> 6 | 7 | <head> 8 | <meta charset="UTF-8" /> 9 | <meta http-equiv="X-UA-Compatible" content="IE=Edge" /> 10 | <meta name="viewport" content="width=device-width, initial-scale=1"> 11 | <title>Contoso Task Pane Add-in 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |

Please sideload your add-in to see app body.

25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/taskpane/taskpane.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. 3 | * See LICENSE in the project root for license information. 4 | */ 5 | import "zone.js"; // Required for Angular 6 | import { platformBrowserDynamic } from "@angular/platform-browser-dynamic"; 7 | import AppModule from "./app/app.module"; 8 | 9 | /* global console, document, Office */ 10 | 11 | Office.onReady(() => { 12 | document.getElementById("sideload-msg").style.display = "none"; 13 | 14 | // Bootstrap the app 15 | platformBrowserDynamic() 16 | .bootstrapModule(AppModule) 17 | .catch((error) => console.error(error)); 18 | }); 19 | -------------------------------------------------------------------------------- /test/end-to-end/src/test-helpers.ts: -------------------------------------------------------------------------------- 1 | import * as childProcess from "child_process"; 2 | 3 | /* global Excel, process, Promise, setTimeout */ 4 | 5 | export async function closeDesktopApplication(application: string): Promise { 6 | let processName: string = ""; 7 | switch (application.toLowerCase()) { 8 | case "excel": 9 | processName = "Excel"; 10 | break; 11 | case "powerpoint": 12 | processName = process.platform === "win32" ? "Powerpnt" : "PowerPoint"; 13 | break; 14 | case "onenote": 15 | processName = "Onenote"; 16 | break; 17 | case "outlook": 18 | processName = "Outlook"; 19 | break; 20 | case "project": 21 | processName = "Project"; 22 | break; 23 | case "word": 24 | processName = process.platform === "win32" ? "Winword" : "Word"; 25 | break; 26 | default: 27 | throw new Error(`${application} is not a valid Office desktop application.`); 28 | } 29 | 30 | await sleep(3000); // wait for host to settle 31 | try { 32 | let cmdLine: string; 33 | if (process.platform == "win32") { 34 | cmdLine = `tskill ${processName}`; 35 | } else { 36 | cmdLine = `pkill ${processName}`; 37 | } 38 | 39 | return await executeCommandLine(cmdLine); 40 | } catch (err) { 41 | throw new Error(`Unable to kill ${application} process. ${err}`); 42 | } 43 | } 44 | 45 | export async function closeWorkbook(): Promise { 46 | await sleep(1000); 47 | await Excel.run(async (context) => context.workbook.close(Excel.CloseBehavior.skipSave)); 48 | } 49 | 50 | export function addTestResult(testValues: any[], resultName: string, resultValue: any, expectedValue: any) { 51 | var data = {}; 52 | data["expectedValue"] = expectedValue; 53 | data["resultName"] = resultName; 54 | data["resultValue"] = resultValue; 55 | testValues.push(data); 56 | } 57 | 58 | export async function sleep(ms: number): Promise { 59 | return new Promise((resolve) => setTimeout(resolve, ms)); 60 | } 61 | 62 | async function executeCommandLine(cmdLine: string): Promise { 63 | return new Promise((resolve, reject) => { 64 | childProcess.exec(cmdLine, (error) => { 65 | if (error) { 66 | reject(false); 67 | } else { 68 | resolve(true); 69 | } 70 | }); 71 | }); 72 | } 73 | -------------------------------------------------------------------------------- /test/end-to-end/src/test-taskpane.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Contoso Task Pane Add-in 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |

Please sideload your addin to see app 25 | body.

26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /test/end-to-end/src/test-taskpane.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. 3 | * See LICENSE in the project root for license information. 4 | */ 5 | import "zone.js"; // Required for Angular 6 | import { platformBrowserDynamic } from "@angular/platform-browser-dynamic"; 7 | import AppModule from "./test.app.module"; 8 | /* global Office, document, console*/ 9 | 10 | Office.initialize = (reason) => { 11 | document.getElementById("sideload-msg").style.display = "none"; 12 | 13 | // Bootstrap the app 14 | platformBrowserDynamic() 15 | .bootstrapModule(AppModule) 16 | .catch((error) => console.error(error)); 17 | }; 18 | -------------------------------------------------------------------------------- /test/end-to-end/src/test.app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from "@angular/core"; 2 | import { pingTestServer } from "office-addin-test-helpers"; 3 | import * as excelComponent from "./test.excel.app.component"; 4 | import * as powerpointComponent from "./test.powerpoint.app.compontent"; 5 | import * as wordComponent from "./test.word.app.component"; 6 | 7 | /* global Office */ 8 | const port: number = 4201; 9 | 10 | @Component({ 11 | selector: "app-home", 12 | templateUrl: "./app.component.html", 13 | }) 14 | export default class AppComponent { 15 | welcomeMessage = "Welcome"; 16 | constructor() { 17 | Office.onReady(async (info) => { 18 | if ( 19 | info.host === Office.HostType.Excel || 20 | info.host === Office.HostType.PowerPoint || 21 | info.host === Office.HostType.Word 22 | ) { 23 | const testServerResponse: object = await pingTestServer(port); 24 | if (testServerResponse["status"] == 200) { 25 | switch (info.host) { 26 | case Office.HostType.Excel: { 27 | const excel = new excelComponent.default(); 28 | return excel.runTest(); 29 | } 30 | case Office.HostType.PowerPoint: { 31 | const powerpoint = new powerpointComponent.default(); 32 | return powerpoint.runTest(); 33 | } 34 | case Office.HostType.Word: { 35 | const word = new wordComponent.default(); 36 | return word.runTest(); 37 | } 38 | } 39 | } 40 | } 41 | }); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /test/end-to-end/src/test.app.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from "@angular/core"; 2 | import { BrowserModule } from "@angular/platform-browser"; 3 | import AppComponent from "./test.app.component"; 4 | 5 | @NgModule({ 6 | declarations: [AppComponent], 7 | imports: [BrowserModule], 8 | bootstrap: [AppComponent], 9 | }) 10 | export default class AppModule {} 11 | -------------------------------------------------------------------------------- /test/end-to-end/src/test.excel.app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from "@angular/core"; 2 | import { pingTestServer, sendTestResults } from "office-addin-test-helpers"; 3 | import * as testHelpers from "./test-helpers"; 4 | import * as excel from "../../../src/taskpane/app/excel.app.component"; 5 | 6 | /* global Office, Excel*/ 7 | const port: number = 4201; 8 | let testValues: any = []; 9 | 10 | @Component({ 11 | selector: "app-home", 12 | templateUrl: "./app.component.html", 13 | }) 14 | export default class AppComponent { 15 | welcomeMessage = "Welcome"; 16 | constructor() { 17 | Office.onReady(async () => { 18 | const testServerResponse: object = await pingTestServer(port); 19 | if (testServerResponse["status"] == 200) { 20 | this.runTest(); 21 | } 22 | }); 23 | } 24 | 25 | async runTest(): Promise { 26 | try { 27 | // Execute taskpane code 28 | const excelComponent = new excel.default(); 29 | await excelComponent.run(); 30 | await testHelpers.sleep(2000); 31 | 32 | // Get output of executed taskpane code 33 | await Excel.run(async (context) => { 34 | const range = context.workbook.getSelectedRange(); 35 | const cellFill = range.format.fill; 36 | cellFill.load("color"); 37 | await context.sync(); 38 | await testHelpers.sleep(2000); 39 | 40 | testHelpers.addTestResult(testValues, "fill-color", cellFill.color, "#FFFF00"); 41 | await sendTestResults(testValues, port); 42 | testValues.pop(); 43 | await testHelpers.closeWorkbook(); 44 | Promise.resolve(); 45 | }); 46 | } catch { 47 | Promise.reject(); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /test/end-to-end/src/test.powerpoint.app.compontent.ts: -------------------------------------------------------------------------------- 1 | import { Component } from "@angular/core"; 2 | import { pingTestServer, sendTestResults } from "office-addin-test-helpers"; 3 | import * as testHelpers from "./test-helpers"; 4 | import * as powerpoint from "../../../src/taskpane/app/powerpoint.app.component"; 5 | 6 | /* global Office, PowerPoint*/ 7 | const port: number = 4201; 8 | let testValues: any = []; 9 | 10 | @Component({ 11 | selector: "app-home", 12 | templateUrl: "./app.component.html", 13 | }) 14 | export default class AppComponent { 15 | welcomeMessage = "Welcome"; 16 | constructor() { 17 | Office.onReady(async () => { 18 | const testServerResponse: object = await pingTestServer(port); 19 | if (testServerResponse["status"] == 200) { 20 | this.runTest(); 21 | } 22 | }); 23 | } 24 | 25 | async runTest(): Promise { 26 | try { 27 | // Execute taskpane code 28 | const powerpointComponent = new powerpoint.default(); 29 | await powerpointComponent.run(); 30 | await testHelpers.sleep(2000); 31 | 32 | // Get output of executed taskpane code 33 | // eslint-disable-next-line no-unused-vars 34 | PowerPoint.run(async (context) => { 35 | // get selected text 36 | const selectedText = await this.getSelectedText(); 37 | // send test results 38 | testHelpers.addTestResult(testValues, "output-message", selectedText, " Hello World!"); 39 | await sendTestResults(testValues, port); 40 | testValues.pop(); 41 | Promise.resolve(); 42 | }); 43 | } catch { 44 | Promise.reject(); 45 | } 46 | } 47 | 48 | async getSelectedText(): Promise { 49 | return new Promise((resolve, reject) => { 50 | Office.context.document.getSelectedDataAsync(Office.CoercionType.Text, (result: Office.AsyncResult) => { 51 | if (result.status === Office.AsyncResultStatus.Failed) { 52 | reject(result.error); 53 | } else { 54 | resolve(result.value); 55 | } 56 | }); 57 | }); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /test/end-to-end/src/test.word.app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from "@angular/core"; 2 | import { pingTestServer, sendTestResults } from "office-addin-test-helpers"; 3 | import * as testHelpers from "./test-helpers"; 4 | import * as word from "../../../src/taskpane/app/word.app.component"; 5 | 6 | /* global Office, Word */ 7 | const port: number = 4201; 8 | let testValues: any = []; 9 | 10 | @Component({ 11 | selector: "app-home", 12 | templateUrl: "./app.component.html", 13 | }) 14 | export default class AppComponent { 15 | welcomeMessage = "Welcome"; 16 | constructor() { 17 | Office.onReady(async () => { 18 | const testServerResponse: object = await pingTestServer(port); 19 | if (testServerResponse["status"] == 200) { 20 | this.runTest(); 21 | } 22 | }); 23 | } 24 | 25 | async runTest(): Promise { 26 | try { 27 | // Execute taskpane code 28 | const wordComponent = new word.default(); 29 | await wordComponent.run(); 30 | await testHelpers.sleep(2000); 31 | 32 | // Get output of executed taskpane code 33 | Word.run(async (context) => { 34 | var firstParagraph = context.document.body.paragraphs.getFirst(); 35 | firstParagraph.load("text"); 36 | await context.sync(); 37 | await testHelpers.sleep(2000); 38 | 39 | testHelpers.addTestResult(testValues, "output-message", firstParagraph.text, "Hello World"); 40 | await sendTestResults(testValues, port); 41 | testValues.pop(); 42 | Promise.resolve(); 43 | }); 44 | } catch { 45 | Promise.reject(); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /test/end-to-end/test-manifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | ca968be6-628b-4f14-ba3c-3e614effa9bd 7 | 1.0.0.0 8 | Contoso 9 | en-US 10 | 11 | 12 | 13 | 14 | 15 | 16 | https://www.contoso.com 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | ReadWriteDocument 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /test/end-to-end/ui-test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "assert"; 2 | import { parseNumber } from "office-addin-cli"; 3 | import { AppType, startDebugging, stopDebugging } from "office-addin-debugging"; 4 | import { toOfficeApp } from "office-addin-manifest"; 5 | import * as officeAddinTestHelpers from "office-addin-test-helpers"; 6 | import * as officeAddinTestServer from "office-addin-test-server"; 7 | import * as path from "path"; 8 | import * as testHelpers from "./src/test-helpers"; 9 | 10 | /* global process, describe, before, it, after */ 11 | 12 | const hosts = ["Excel", "PowerPoint", "Word"]; 13 | const manifestPath = path.resolve(`${process.cwd()}/test/end-to-end/test-manifest.xml`); 14 | const testServerPort: number = 4201; 15 | 16 | hosts.forEach(function (host) { 17 | const testServer = new officeAddinTestServer.TestServer(testServerPort); 18 | let testValues: any = []; 19 | 20 | describe(`Test ${host} Task Pane Project`, function () { 21 | before(`Setup test environment and sideload ${host}`, async function () { 22 | this.timeout(0); 23 | // Start test server and ping to ensure it's started 24 | const testServerStarted = await testServer.startTestServer(true /* mochaTest */); 25 | const serverResponse = await officeAddinTestHelpers.pingTestServer(testServerPort); 26 | assert.strictEqual(testServerStarted, true); 27 | assert.strictEqual(serverResponse["status"], 200); 28 | 29 | // Call startDebugging to start dev-server and sideload 30 | const devServerCmd = `npm run dev-server -- --config ./test/end-to-end/webpack.config.js `; 31 | const devServerPort = parseNumber(process.env.npm_package_config_dev_server_port || 3000); 32 | await startDebugging(manifestPath, { 33 | app: toOfficeApp(host), 34 | appType: AppType.Desktop, 35 | devServerCommandLine: devServerCmd, 36 | devServerPort: devServerPort, 37 | enableDebugging: false, 38 | }); 39 | }), 40 | describe(`Get test results for ${host} taskpane project`, function () { 41 | it("Validate expected result count", async function () { 42 | this.timeout(0); 43 | testValues = await testServer.getTestResults(); 44 | assert.strictEqual(testValues.length > 0, true); 45 | }); 46 | it("Validate expected result name", async function () { 47 | assert.strictEqual( 48 | testValues[0].resultName, 49 | host.toLowerCase() === "excel" ? "fill-color" : "output-message" 50 | ); 51 | }); 52 | it("Validate expected result", async function () { 53 | assert.strictEqual(testValues[0].resultValue, testValues[0].expectedValue); 54 | }); 55 | }); 56 | after(`Teardown test environment and shutdown ${host}`, async function () { 57 | this.timeout(0); 58 | // Stop the test server 59 | const stopTestServer = await testServer.stopTestServer(); 60 | assert.strictEqual(stopTestServer, true); 61 | 62 | const applicationClosed = await testHelpers.closeDesktopApplication(host); 63 | assert.strictEqual(applicationClosed, true); 64 | }); 65 | }); 66 | }); 67 | 68 | after(`Unregister the add-in`, async function () { 69 | return stopDebugging(manifestPath); 70 | }); 71 | -------------------------------------------------------------------------------- /test/end-to-end/webpack.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-undef */ 2 | 3 | const devCerts = require("office-addin-dev-certs"); 4 | const HtmlWebpackPlugin = require("html-webpack-plugin"); 5 | const CopyWebpackPlugin = require("copy-webpack-plugin"); 6 | const path = require("path"); 7 | const webpack = require("webpack"); 8 | 9 | async function getHttpsOptions() { 10 | const httpsOptions = await devCerts.getHttpsServerOptions(); 11 | return { ca: httpsOptions.ca, key: httpsOptions.key, cert: httpsOptions.cert }; 12 | } 13 | 14 | module.exports = async (env, options) => { 15 | // const dev = options.mode === "development"; 16 | const config = { 17 | devtool: "source-map", 18 | entry: { 19 | polyfill: ["core-js/stable", "regenerator-runtime/runtime"], 20 | test: ["./test/end-to-end/src/test-taskpane.ts", "./test/end-to-end/src/test-taskpane.html"], 21 | }, 22 | output: { 23 | path: path.resolve(__dirname, "testBuild"), 24 | clean: true, 25 | }, 26 | resolve: { 27 | extensions: [".ts", ".tsx", ".html", ".js"], 28 | fallback: { 29 | child_process: false, 30 | fs: false, 31 | os: require.resolve("os-browserify/browser"), 32 | }, 33 | }, 34 | module: { 35 | rules: [ 36 | { 37 | test: /\.ts$/, 38 | exclude: /node_modules/, 39 | use: { 40 | loader: "babel-loader", 41 | options: { 42 | presets: ["@babel/preset-typescript"], 43 | }, 44 | }, 45 | }, 46 | { 47 | test: /\.tsx?$/, 48 | exclude: /node_modules/, 49 | use: "ts-loader", 50 | }, 51 | { 52 | test: /\.html$/, 53 | exclude: /node_modules/, 54 | use: "html-loader", 55 | }, 56 | { 57 | test: /\.(png|jpg|jpeg|gif|ico)$/, 58 | type: "asset/resource", 59 | generator: { 60 | filename: "assets/[name][ext][query]", 61 | }, 62 | }, 63 | ], 64 | }, 65 | plugins: [ 66 | new webpack.ProvidePlugin({ 67 | process: "process/browser", 68 | }), 69 | new HtmlWebpackPlugin({ 70 | filename: "taskpane.html", 71 | template: "./test/end-to-end/src/test-taskpane.html", 72 | chunks: ["polyfill", "test"], 73 | }), 74 | new HtmlWebpackPlugin({ 75 | filename: "app.component.html", 76 | template: "./src/taskpane/app/app.component.html", 77 | chunks: ["polyfill", "app.component"], 78 | }), 79 | new CopyWebpackPlugin({ 80 | patterns: [ 81 | { 82 | from: "assets/*", 83 | to: "assets/[name][ext][query]", 84 | }, 85 | ], 86 | }), 87 | ], 88 | devServer: { 89 | static: { 90 | directory: "testBuild", 91 | }, 92 | headers: { 93 | "Access-Control-Allow-Origin": "*", 94 | }, 95 | server: { 96 | type: "https", 97 | options: env.WEBPACK_BUILD || options.https !== undefined ? options.https : await getHttpsOptions(), 98 | }, 99 | port: process.env.npm_package_config_dev_server_port || 3000, 100 | }, 101 | }; 102 | 103 | return config; 104 | }; 105 | -------------------------------------------------------------------------------- /test/unit/excel.test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "assert"; 2 | import "mocha"; 3 | import { OfficeMockObject } from "office-addin-mock"; 4 | import * as excelComponent from "../../src/taskpane/app/excel.app.component"; 5 | 6 | /* global describe, global, it */ 7 | 8 | const ExcelMockData = { 9 | context: { 10 | workbook: { 11 | range: { 12 | address: "G4", 13 | format: { 14 | fill: {}, 15 | }, 16 | }, 17 | getSelectedRange: function () { 18 | return this.range; 19 | }, 20 | }, 21 | }, 22 | run: async function (callback) { 23 | await callback(this.context); 24 | }, 25 | }; 26 | 27 | const OfficeMockData = { 28 | onReady: async function () {}, 29 | }; 30 | 31 | describe("Excel", function () { 32 | it("Run", async function () { 33 | const excelMock: OfficeMockObject = new OfficeMockObject(ExcelMockData); // Mocking the host specific namespace 34 | global.Excel = excelMock as any; 35 | global.Office = new OfficeMockObject(OfficeMockData) as any; // Mocking the common office-js namespace 36 | 37 | const excel = new excelComponent.default(); 38 | await excel.run(); 39 | 40 | assert.strictEqual(excelMock.context.workbook.range.format.fill.color, "yellow"); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /test/unit/powerpoint.test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "assert"; 2 | import "mocha"; 3 | import { OfficeMockObject } from "office-addin-mock"; 4 | import * as powerpointComponent from "../../src/taskpane/app/powerpoint.app.component"; 5 | 6 | /* global describe, global, it */ 7 | 8 | const PowerPointMockData = { 9 | context: { 10 | document: { 11 | setSelectedDataAsync: function (data: string, options?) { 12 | this.data = data; 13 | this.options = options; 14 | }, 15 | }, 16 | }, 17 | CoercionType: { 18 | Text: {}, 19 | }, 20 | onReady: async function () {}, 21 | }; 22 | 23 | describe("PowerPoint", function () { 24 | it("Run", async function () { 25 | const officeMock: OfficeMockObject = new OfficeMockObject(PowerPointMockData); // Mocking the common office-js namespace 26 | global.Office = officeMock as any; 27 | 28 | const powerpoint = new powerpointComponent.default(); 29 | await powerpoint.run(); 30 | 31 | assert.strictEqual(officeMock.context.document.data, "Hello World!"); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /test/unit/word.test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "assert"; 2 | import "mocha"; 3 | import { OfficeMockObject } from "office-addin-mock"; 4 | import * as wordComponent from "../../src/taskpane/app/word.app.component"; 5 | 6 | /* global describe, global, it, Word */ 7 | 8 | const WordMockData = { 9 | context: { 10 | document: { 11 | body: { 12 | paragraph: { 13 | font: {}, 14 | text: "", 15 | }, 16 | insertParagraph: function (paragraphText: string, insertLocation: Word.InsertLocation): Word.Paragraph { 17 | this.paragraph.text = paragraphText; 18 | this.paragraph.insertLocation = insertLocation; 19 | return this.paragraph; 20 | }, 21 | }, 22 | }, 23 | }, 24 | InsertLocation: { 25 | end: "End", 26 | }, 27 | run: async function (callback) { 28 | await callback(this.context); 29 | }, 30 | }; 31 | 32 | const OfficeMockData = { 33 | onReady: async function () {}, 34 | }; 35 | 36 | describe("Word", function () { 37 | it("Run", async function () { 38 | const wordMock: OfficeMockObject = new OfficeMockObject(WordMockData); // Mocking the host specific namespace 39 | global.Word = wordMock as any; 40 | global.Office = new OfficeMockObject(OfficeMockData) as any; // Mocking the common office-js namespace 41 | 42 | const word = new wordComponent.default(); 43 | await word.run(); 44 | 45 | assert.strictEqual(wordMock.context.document.body.paragraph.font.color, "blue"); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "baseUrl": ".", 5 | "esModuleInterop": true, 6 | "experimentalDecorators": true, 7 | "jsx": "react", 8 | "noEmitOnError": true, 9 | "outDir": "lib", 10 | "sourceMap": true, 11 | "target": "es5", 12 | "lib": [ 13 | "es2015", 14 | "dom" 15 | ] 16 | }, 17 | "exclude": [ 18 | "node_modules", 19 | "dist", 20 | "lib", 21 | "lib-amd" 22 | ], 23 | "ts-node": { 24 | "files": true 25 | } 26 | } -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-undef */ 2 | 3 | const devCerts = require("office-addin-dev-certs"); 4 | const CopyWebpackPlugin = require("copy-webpack-plugin"); 5 | const HtmlWebpackPlugin = require("html-webpack-plugin"); 6 | 7 | const urlDev = "https://localhost:3000/"; 8 | const urlProd = "https://www.contoso.com/"; // CHANGE THIS TO YOUR PRODUCTION DEPLOYMENT LOCATION 9 | 10 | async function getHttpsOptions() { 11 | const httpsOptions = await devCerts.getHttpsServerOptions(); 12 | return { ca: httpsOptions.ca, key: httpsOptions.key, cert: httpsOptions.cert }; 13 | } 14 | 15 | module.exports = async (env, options) => { 16 | const dev = options.mode === "development"; 17 | const config = { 18 | devtool: "source-map", 19 | entry: { 20 | polyfill: ["core-js/stable", "regenerator-runtime/runtime"], 21 | taskpane: ["./src/taskpane/taskpane.ts", "./src/taskpane/taskpane.html"], 22 | commands: "./src/commands/commands.ts", 23 | }, 24 | output: { 25 | clean: true, 26 | }, 27 | resolve: { 28 | extensions: [".ts", ".tsx", ".html", ".js"], 29 | }, 30 | module: { 31 | rules: [ 32 | { 33 | test: /\.ts$/, 34 | exclude: /node_modules/, 35 | use: { 36 | loader: "babel-loader", 37 | options: { 38 | presets: ["@babel/preset-typescript"], 39 | }, 40 | }, 41 | }, 42 | { 43 | test: /\.tsx?$/, 44 | exclude: /node_modules/, 45 | use: "ts-loader", 46 | }, 47 | { 48 | test: /\.html$/, 49 | exclude: /node_modules/, 50 | use: "html-loader", 51 | }, 52 | { 53 | test: /\.(png|jpg|jpeg|gif|ico)$/, 54 | type: "asset/resource", 55 | generator: { 56 | filename: "assets/[name][ext][query]", 57 | }, 58 | }, 59 | ], 60 | }, 61 | plugins: [ 62 | new HtmlWebpackPlugin({ 63 | filename: "taskpane.html", 64 | template: "./src/taskpane/taskpane.html", 65 | chunks: ["polyfill", "taskpane"], 66 | }), 67 | new HtmlWebpackPlugin({ 68 | filename: "app.component.html", 69 | template: "./src/taskpane/app/app.component.html", 70 | chunks: ["polyfill", "app.component"], 71 | }), 72 | new CopyWebpackPlugin({ 73 | patterns: [ 74 | { 75 | from: "assets/*", 76 | to: "assets/[name][ext][query]", 77 | }, 78 | { 79 | from: "manifest*.xml", 80 | to: "[name]" + "[ext]", 81 | transform(content) { 82 | if (dev) { 83 | return content; 84 | } else { 85 | return content.toString().replace(new RegExp(urlDev, "g"), urlProd); 86 | } 87 | }, 88 | }, 89 | ], 90 | }), 91 | new HtmlWebpackPlugin({ 92 | filename: "commands.html", 93 | template: "./src/commands/commands.html", 94 | chunks: ["polyfill", "commands"], 95 | }), 96 | ], 97 | devServer: { 98 | headers: { 99 | "Access-Control-Allow-Origin": "*", 100 | }, 101 | server: { 102 | type: "https", 103 | options: env.WEBPACK_BUILD || options.https !== undefined ? options.https : await getHttpsOptions(), 104 | }, 105 | port: process.env.npm_package_config_dev_server_port || 3000, 106 | }, 107 | }; 108 | 109 | return config; 110 | }; 111 | --------------------------------------------------------------------------------