Azure DevOps Services | Azure DevOps Server 2022 - Azure DevOps Server 2019
ダッシュボード上のウィジェットは、拡張機能フレームワークのコントリビューションとして実装されます。 1 つの拡張機能に複数のコントリビューションを含めることができます。 複数のウィジェットをコントリビューションとして使用して拡張機能を作成する方法について説明します。
この記事は、前の部分に基づいて構築された 3 つの部分に分かれています。 単純なウィジェットから始め、包括的なウィジェットで終わる。
ヒント
Azure DevOps 拡張 SDK を使用した拡張機能の開発に関する最新のドキュメントをご確認ください。
前提条件
ウィジェットの開発には、JavaScript、HTML、CSS に関する 知識 が必要です。
Azure DevOps の組織。 組織の作成。
テキスト エディター。 多くのチュートリアルでは、「Visual Studio Code」を使用します。
拡張機能をパッケージ化するための Azure DevOps 用クロスプラットフォーム CLI (tfx-cli )。
- tfx-cli は、Node.js のコンポーネントである
npm
を使用してインストールできます。npm i -g tfx-cli
- tfx-cli は、Node.js のコンポーネントである
プロジェクトのホーム ディレクトリ。 このディレクトリはチュートリアル全体で
home
と呼ばれ、この記事の手順を完了した後に次の構造が必要です。|--- README.md |--- sdk |--- node_modules |--- scripts |--- VSS.SDK.min.js |--- img |--- logo.png |--- scripts |--- hello-world.html // html page to be used for your widget |--- vss-extension.json // extension's manifest
このチュートリアルの内容
- パート 1: Hello World: 単純な Hello World メッセージを出力する新しいウィジェットを作成する方法について説明します。
- パート 2: Azure DevOps REST API を使用した Hello World: Azure DevOps REST API への呼び出しを追加することで、最初の部分に基づいて構築されています。
- パート 3: Hello World の構成: ウィジェットに構成を追加する方法について説明します。
注
急いですぐにコードを入手したい場合は、 Azure DevOps のサンプル拡張機能をダウンロードできます。
ダウンロードしたら、widgets
フォルダーに移動し、ステップ 6 とステップ 7 に従って、複雑さの異なる 3 つのサンプル ウィジェットを含むサンプル拡張機能を発行します。
ウィジェットの基本的なスタイルをすぐに使えるように準備し、ウィジェットの構造に関するガイダンスを提供します。
パート 1: Hello World
パート 1 では、JavaScript を使用して Hello World を出力するウィジェットを示します。
手順 1: クライアント SDK を取得する
コア SDK スクリプト VSS.SDK.min.js
により、Web 拡張機能はホスト Azure DevOps フレームと通信できます。 スクリプトは、初期化、拡張機能の読み込み通知、または現在のページに関するコンテキストの取得などの操作を実行します。
SDK を取得するには、 npm install
コマンドを使用します。
npm install vss-web-extension-sdk
VSS.SDK.min.js
フォルダーにあるクライアント SDK vss-web-extension-sdk/lib
ファイルを見つけて、home/sdk/scripts
フォルダーに配置します。
詳細については、クライアント SDK GitHub のページを参照してください。
手順 2: HTML ページを設定する
hello-world.html
という名前でファイルを作成します。 HTML ページは、レイアウトを一緒に保持し、CSS と JavaScript への参照を含む接着です。 このファイルには何でも名前を付けることができます。 別のファイル名を使用する場合は、使用する名前で hello-world
へのすべての参照を更新します。
ウィジェットはHTMLベースで、iframe内でホストされています。
次の HTML を hello-world.html
ファイルにコピーします。 VSS.SDK.min.js
ファイルへの必須の参照を追加し、h2
要素を含めます。この要素は、今後の手順で Hello World という文字列で更新されます。
<!DOCTYPE html>
<html>
<head>
<script src="sdk/scripts/VSS.SDK.min.js"></script>
</head>
<body>
<div class="widget">
<h2 class="title"></h2>
</div>
</body>
</html>
HTML ファイルを使用している場合でも、スクリプトやリンク以外の HTML ヘッド要素のほとんどはフレームワークによって無視されます。
手順 3: JavaScript を更新する
JavaScript を使用して、ウィジェット内のコンテンツをレンダリングします。 この記事では、すべての JavaScript コードを HTML ファイル内の <script>
要素内にラップします。 このコードを別の JavaScript ファイルに含め、HTML ファイルで参照することもできます。
コードはコンテンツをレンダリングします。 この JavaScript コードでは、VSS SDK も初期化され、ウィジェットのコードがウィジェット名にマップされ、ウィジェットの成功または失敗が拡張機能フレームワークに通知されます。
この場合、次のコードは ウィジェットに Hello World を出力します。 この script
要素を HTML の head
に追加します。
<script type="text/javascript">
VSS.init({
explicitNotifyLoaded: true,
usePlatformStyles: true
});
VSS.require("TFS/Dashboards/WidgetHelpers", function (WidgetHelpers) {
WidgetHelpers.IncludeWidgetStyles();
VSS.register("HelloWorldWidget", function () {
return {
load: function (widgetSettings) {
var $title = $('h2.title');
$title.text('Hello World');
return WidgetHelpers.WidgetStatusHelper.Success();
}
};
});
VSS.notifyLoadSucceeded();
});
</script>
は、ウィジェットをホストしているアイフレームとホストフレームの間のハンドシェイクを初期化します。 読み込みが完了したときにウィジェットがホストに明示的に通知できるように、
explicitNotifyLoaded: true
を渡します。 このコントロールを使用すると、依存モジュールが読み込まれたことを確認した後、読み込みの完了を通知できます。 ウィジェットで HTML 要素 (usePlatformStyles: true
やbody
など) に Azure DevOps コア スタイルを使用できるように、div
を使用します。 ウィジェットでこれらのスタイルを使用しない場合は、usePlatformStyles: false
渡します。VSS.require
は、必要な VSS スクリプト ライブラリを読み込むのに使用されます。 このメソッドを呼び出すと、 JQuery や JQueryUI などの一般的なライブラリが自動的に読み込 まれます。 ここでは、ウィジェット フレームワークにウィジェットの状態を伝えるために使用される WidgetHelpers ライブラリに依存しています。 そのため、対応するモジュール名TFS/Dashboards/WidgetHelpers
とコールバックをVSS.require
に渡します。 コールバックは、モジュールが読み込まれた後に呼び出されます。 コールバックには、ウィジェットに必要な残りの JavaScript コードがあります。 コールバックの最後に、VSS.notifyLoadSucceeded
を呼び出して、読み込みの完了を通知します。WidgetHelpers.IncludeWidgetStyles
には、作業を開始するための 基本的な CSS を含むスタイルシートが含まれています。 これらのスタイルを使用するには、クラスwidget
を使用した HTML 要素内でコンテンツを包み込みます。VSS.register
は、JavaScriptで関数をマッピングして、拡張機能内のさまざまな寄与の中でウィジェットを一意に識別するために使用されます。 この関数の名前は、id
で説明したコントリビューションを識別する と一致する必要があります。 ウィジェットの場合、VSS.register
に渡される関数は、IWidget
コントラクトを満たすオブジェクトを返す必要があります。 たとえば、返されるオブジェクトには、ウィジェットをレンダリングするコア ロジックを持つ別の関数である値を持つ load プロパティが必要です。 ここでは、h2
要素のテキストを Hello World に更新します。 これは、ウィジェット フレームワークがウィジェットをインスタンス化するときに呼び出されるこの関数です。 ここでは WidgetHelpers のWidgetStatusHelper
を使用して、WidgetStatus
を success として返します。警告
ウィジェットの登録に使用される名前がマニフェスト内のコントリビューションの ID と一致しない場合、ウィジェットは予期せず機能します。
手順 4: 拡張機能のロゴを更新する
ユーザーが拡張機能をインストールすると、Marketplace とウィジェット カタログにロゴが表示されます。
98 x 98 ピクセルのカタログ アイコンが必要です。 イメージを選択し、logo.png
という名前を付けて、img
フォルダーに配置します。 イメージには、次の手順の拡張機能マニフェストが使用する名前で更新されている限り、必要に応じて名前を付けることができます。
手順 5: 拡張機能マニフェストを作成する
すべての 拡張機能には、拡張マニフェスト ファイルが必要です。 vss-extension.json
ディレクトリに home
という名前の json ファイルを作成し、次の内容を指定します。
{
"manifestVersion": 1,
"id": "azure-devops-extensions-myExtensions",
"version": "1.0.0",
"name": "My First Set of Widgets",
"description": "Samples containing different widgets extending dashboards",
"publisher": "fabrikam",
"categories": ["Azure Boards"],
"targets": [
{
"id": "Microsoft.VisualStudio.Services"
}
],
"icons": {
"default": "img/logo.png"
},
"contributions": [
{
"id": "HelloWorldWidget",
"type": "ms.vss-dashboards-web.widget",
"targets": [
"ms.vss-dashboards-web.widget-catalog"
],
"properties": {
"name": "Hello World Widget",
"description": "My first widget",
"catalogIconUrl": "img/CatalogIcon.png",
"previewImageUrl": "img/preview.png",
"uri": "hello-world.html",
"supportedSizes": [
{
"rowSpan": 1,
"columnSpan": 2
}
],
"supportedScopes": ["project_team"]
}
}
],
"files": [
{
"path": "hello-world.html",
"addressable": true
},
{
"path": "sdk/scripts",
"addressable": true
},
{
"path": "img",
"addressable": true
}
]
}
vss-extension.json
ファイルは、常にホーム ディレクトリのルートにある必要があります。 他のすべてのファイルについては、フォルダー内の任意の構造に配置できますが、HTML ファイルおよび vss-extension.json
マニフェストで参照を適切に更新してください。
必要な属性の詳細については、拡張機能マニフェスト リファレンスを参照してください。
注
publisher
を発行元名に変更します。 パブリッシャーを作成するには、パッケージ/発行/インストールに関する記事を参照してください。
アイコン
icons
スタンザは、マニフェスト内の拡張機能のアイコンへのパスを指定します。
貢献
各コントリビューション エントリは特性を定義します。
投稿を識別するための
id
。 この ID は拡張機能内で一意である必要があります。 このIDは、手順3でウィジェットを登録する際に使用した名前と一致している必要があります。コントリビューションの
type
。 すべてのウィジェットの型は、ms.vss-dashboards-web.widget
である必要があります。コントリビューションの対象となる
targets
の配列。 すべてのウィジェットのターゲットは[ms.vss-dashboards-web.widget-catalog]
である必要があります。properties
は、コントリビューション型のプロパティを含むオブジェクトです。 ウィジェットの場合、次のプロパティが必須です。プロパティ 内容 name
ウィジェット カタログに表示するウィジェットの名前。 description
ウィジェット カタログに表示するウィジェットの説明。 catalogIconUrl
ウィジェットカタログに表示するために手順 4 で追加されたカタログアイコンの相対パス。 イメージは 98 x 98 ピクセルである必要があります。 別のフォルダー構造または別のファイル名を使用した場合は、ここで適切な相対パスを指定します。 previewImageUrl
ウィジェット カタログに表示するために Step 4 で追加したプレビューイメージの相対パス。 イメージは 330 x 160 ピクセルである必要があります。 別のフォルダー構造または別のファイル名を使用した場合は、ここで適切な相対パスを指定します。 uri
手順 1 で追加したHTMLファイルの相対パスです。 別のフォルダー構造または別のファイル名を使用した場合は、ここで適切な相対パスを指定します。 supportedSizes
ウィジェットでサポートされているサイズの配列。 ウィジェットで複数のサイズがサポートされている場合、配列の最初のサイズはウィジェットの既定のサイズです。 widget size
は、ダッシュボード グリッド内でウィジェットが占有する行と列に対して指定されます。 1 つの行/列が 160 px に対応します。 1x1 より大きいディメンションでは、ウィジェット間の余白を表す 10 px が追加されます。 たとえば 3x2 のウィジェットは、幅が160*3+10*2
、高さが160*2+10*1
になります。 サポートされる最大サイズは 4 x 4 です。supportedScopes
現時点では、チーム ダッシュボードのみがサポートされています。 値は project_team
である必要があります。 今後の更新には、ダッシュボード スコープのオプションが追加される可能性があります。
コントリビューション ポイントの詳細については、「 拡張ポイント」を参照してください。
ファイル
files
スタンザは、パッケージに含めるファイル (HTML ページ、スクリプト、SDK スクリプト、ロゴ) を示します。
他のURLアドレス指定が不要なファイルを含まない限り、addressable
を true
に設定します。
注
拡張機能マニフェストファイルのプロパティやその役割などについての詳細は、拡張機能マニフェストリファレンスを参照してください。
手順 6: パッケージ化、発行、共有
作成した拡張機能を Marketplace に移行するための次の手順は、すべてのファイルを一緒にパッケージ化することです。 すべての拡張機能は、VSIX 2.0 と互換性のある .vsix ファイルとしてパッケージ化されています。 Microsoft では、拡張機能をパッケージ化するためのクロスプラットフォーム コマンド ライン インターフェイス (CLI) を提供しています。
パッケージ化ツールを取得する
Node.jsのコンポーネントである npm
を使用して、コマンド ラインから tfx-cli をインストールまたは更新できます。
npm i -g tfx-cli
拡張機能をパッケージ化する
tfx-cli を使用すると、拡張子を .vsix ファイルに簡単にパッケージ化できます。 拡張機能のホーム ディレクトリに移動し、次のコマンドを実行します。
tfx extension create --manifest-globs vss-extension.json
注
拡張機能/統合バージョンは、更新ごとにインクリメントする必要があります。
既存の拡張機能を更新する場合は、マニフェスト内のバージョンを更新するか、コマンドライン スイッチ --rev-version
を渡してください。 これにより、拡張機能の patch バージョン番号がインクリメントされ、新しいバージョンがマニフェストに保存されます。
.vsix ファイルに拡張機能をパッケージ化したら、拡張機能を Marketplace に発行する準備が整います。
拡張機能の発行元を作成する
Microsoft の拡張機能を含むすべての拡張機能は、発行元によって提供されていると識別されます。 既存のパブリッシャーのメンバーでない場合は、作成します。
Visual Studio Marketplace 発行ポータルにサインインします。
既存のパブリッシャーのメンバーでない場合は、パブリッシャーを作成する必要があります。 パブリッシャーが既にある場合は、Related Sites の下にある Publish Extensions をスクロールして選択してください。
- 発行元の識別子を指定します (例: mycompany-myteam)。
- 識別子は、拡張機能のマニフェスト ファイル内の
publisher
属性の値として使用されます。
- 識別子は、拡張機能のマニフェスト ファイル内の
- 発行元の表示名を指定します (例: マイ チーム)。
- 発行元の識別子を指定します (例: mycompany-myteam)。
Marketplace パブリッシャー契約を確認して、「作成」を選択します。
これで、発行元が定義されました。 今後のリリースでは、発行元の拡張機能を表示および管理するためのアクセス許可を付与できます。
共通の発行元の下で拡張機能を公開すると、チームや組織のプロセスが簡略化され、より安全なアプローチが提供されます。 この方法により、1 つの資格情報セットを複数のユーザーに配布する必要がなくなり、セキュリティが強化されます。
サンプルの vss-extension.json
マニフェスト ファイルを更新して、ダミーのパブリッシャー ID fabrikam を発行元 ID に置き換えます。
拡張機能を発行して共有する
これで、拡張機能を Marketplace にアップロードできるようになりました。
新しい拡張機能のアップロードを選択、パッケージ化された .vsix ファイルに移動し、アップロードを選択します。
また、1 つの手順で拡張機能をパッケージ化して発行する代わりに tfx extension create
コマンドをtfx extension publish
使用して、コマンド ラインを使用して拡張機能をアップロードすることもできます。 --share-with
を使用して、公開後に拡張機能を 1 つ以上のアカウントと共有することもできます。 個人用アクセス トークンも必要です。
tfx extension publish --manifest-globs your-manifest.json --share-with yourOrganization
手順 7: カタログからウィジェットを追加する
プロジェクト
http://dev.azure.com/{Your_Organization}/{Your_Project}
にサインインしてください。[概要]>[ダッシュボード] を選択します。
ウィジェットを追加を選択します。
ウィジェットを強調表示し、[ 追加] を選択します。
ウィジェットがダッシュボードに表示されます。
パート 2: Azure DevOps REST API を使用したHello World
ウィジェットは、Azure DevOps の任意の REST API を呼び出して、Azure DevOps リソースを操作できます。
次の例では、WorkItemTracking 用 REST API を使用して、既存のクエリに関する情報を取得し、 Hello World テキストの下のウィジェットにクエリ情報を表示します。
手順 1: HTML ファイルを追加する
前の例のファイル hello-world.html
をコピーし、コピーの名前を に hello-world2.html
変更します。 フォルダーは次の例のようになります。
|--- README.md
|--- node_modules
|--- SDK
|--- scripts
|--- VSS.SDK.min.js
|--- img
|--- logo.png
|--- scripts
|--- hello-world.html // html page to be used for your widget
|--- hello-world2.html // renamed copy of hello-world.html
|--- vss-extension.json // extension's manifest
クエリ情報を保持するには、div
の下に新しい h2
要素を追加します。
HelloWorldWidget
を呼び出す行で、ウィジェットの名前をHelloWorldWidget2
からVSS.register
に更新します。
このアクションにより、フレームワークは拡張機能内のウィジェットを一意に識別できます。
<!DOCTYPE html>
<html>
<head>
<script src="sdk/scripts/VSS.SDK.min.js"></script>
<script type="text/javascript">
VSS.init({
explicitNotifyLoaded: true,
usePlatformStyles: true
});
VSS.require("TFS/Dashboards/WidgetHelpers", function (WidgetHelpers) {
WidgetHelpers.IncludeWidgetStyles();
VSS.register("HelloWorldWidget2", function () {
return {
load: function (widgetSettings) {
var $title = $('h2.title');
$title.text('Hello World');
return WidgetHelpers.WidgetStatusHelper.Success();
}
}
});
VSS.notifyLoadSucceeded();
});
</script>
</head>
<body>
<div class="widget">
<h2 class="title"></h2>
<div id="query-info-container"></div>
</div>
</body>
</html>
手順 2: Azure DevOps リソースにアクセスする
Azure DevOps リソースへのアクセスを有効にするには、拡張機能マニフェストでスコープを指定する必要があります。 マニフェストにvso.work
スコープを追加します。 このスコープは、ウィジェットがクエリと作業項目への読み取り専用アクセス権を必要とすることを示します。 使用可能なすべてのスコープを表示するには、「 スコープ」を参照してください。
拡張機能マニフェストの末尾に次のコードを追加します。
{
"scopes":[
"vso.work"
]
}
その他のプロパティを含めるには、それらを明示的に一覧表示する必要があります。次に例を示します。
{
"name": "example-widget",
"publisher": "example-publisher",
"version": "1.0.0",
"scopes": [
"vso.work"
]
}
警告
拡張機能の発行後にスコープを追加または変更することは現在サポートされていません。 拡張機能を既にアップロードしている場合は、Marketplace から削除します。 Visual Studio Marketplace 発行ポータルに移動し、拡張機能を右選択して、[削除] を選択します。
手順 3: REST API 呼び出しを行う
AZURE DevOps で REST API 呼び出しを行うために SDK を介してアクセスできるクライアント側ライブラリは多数あります。 これらのライブラリは REST クライアントと呼ばれ、使用可能なすべてのサーバー側エンドポイントの Ajax 呼び出しに関する JavaScript ラッパーです。 Ajax 呼び出しを自分で記述する代わりに、これらのクライアントによって提供されるメソッドを使用できます。 これらのメソッドは、API 応答をコードで使用できるオブジェクトにマップします。
この手順では、workItemTracking REST クライアントを提供するVSS.require
を読み込むためのAzureDevOps/WorkItemTracking/RestClient
呼び出しを更新します。 この REST クライアントを使用すると、フォルダー Feedback
の下にある Shared Queries
というクエリに関する情報を取得できます。
VSS.register
に渡す関数内に、現在のプロジェクト ID を保持する変数を作成します。 クエリをフェッチするには、この変数が必要です。 また、REST クライアントを使用する getQueryInfo
新しいメソッドを作成します。 このメソッドは、load メソッドから呼び出されます。
このメソッド getClient
は、必要な REST クライアントのインスタンスを提供します。 メソッド getQuery
は、クエリを promise でラップして返します。
更新されたVSS.require
は次のようになります。
VSS.require(["AzureDevOps/Dashboards/WidgetHelpers", "AzureDevOps/WorkItemTracking/RestClient"],
function (WidgetHelpers, TFS_Wit_WebApi) {
WidgetHelpers.IncludeWidgetStyles();
VSS.register("HelloWorldWidget2", function () {
var projectId = VSS.getWebContext().project.id;
var getQueryInfo = function (widgetSettings) {
// Get a WIT client to make REST calls to Azure DevOps Services
return TFS_Wit_WebApi.getClient().getQuery(projectId, "Shared Queries/Feedback")
.then(function (query) {
// Do something with the query
return WidgetHelpers.WidgetStatusHelper.Success();
}, function (error) {
return WidgetHelpers.WidgetStatusHelper.Failure(error.message);
});
}
return {
load: function (widgetSettings) {
// Set your title
var $title = $('h2.title');
$title.text('Hello World');
return getQueryInfo(widgetSettings);
}
}
});
VSS.notifyLoadSucceeded();
});
WidgetStatusHelper
の Failure メソッドの使用に注意してください。 これにより、エラーが発生したことをウィジェット フレームワークに示し、すべてのウィジェットに提供される標準的なエラー エクスペリエンスを利用できます。
Feedback
フォルダーの下に Shared Queries
クエリがない場合は、コード内の Shared Queries\Feedback
を、プロジェクトに存在するクエリのパスに置き換えます。
手順 4: 応答を表示する
最後の手順では、ウィジェット内にクエリ情報をレンダリングします。 関数 getQuery
は promise 内で型 Contracts.QueryHierarchyItem
のオブジェクトを返します。 この例では、 Hello World テキストの下にクエリ ID、クエリ名、クエリ作成者の名前を表示します。
// Do something with the query
コメントを、次のコードに置き換えます。
// Create a list with query details
var $list = $('<ul>');
$list.append($('<li>').text("Query Id: " + query.id));
$list.append($('<li>').text("Query Name: " + query.name));
$list.append($('<li>').text("Created By: " + (query.createdBy ? query.createdBy.displayName : "<unknown>")));
// Append the list to the query-info-container
var $container = $('#query-info-container');
$container.empty();
$container.append($list);
最終的な hello-world2.html
は、以下のようになります。
<!DOCTYPE html>
<html>
<head>
<script src="sdk/scripts/VSS.SDK.min.js"></script>
<script type="text/javascript">
VSS.init({
explicitNotifyLoaded: true,
usePlatformStyles: true
});
VSS.require(["AzureDevOps/Dashboards/WidgetHelpers", "AzureDevOps/WorkItemTracking/RestClient"],
function (WidgetHelpers, TFS_Wit_WebApi) {
WidgetHelpers.IncludeWidgetStyles();
VSS.register("HelloWorldWidget2", function () {
var projectId = VSS.getWebContext().project.id;
var getQueryInfo = function (widgetSettings) {
// Get a WIT client to make REST calls to Azure DevOps Services
return TFS_Wit_WebApi.getClient().getQuery(projectId, "Shared Queries/Feedback")
.then(function (query) {
// Create a list with query details
var $list = $('<ul>');
$list.append($('<li>').text("Query ID: " + query.id));
$list.append($('<li>').text("Query Name: " + query.name));
$list.append($('<li>').text("Created By: " + (query.createdBy ? query.createdBy.displayName : "<unknown>")));
// Append the list to the query-info-container
var $container = $('#query-info-container');
$container.empty();
$container.append($list);
// Use the widget helper and return success as Widget Status
return WidgetHelpers.WidgetStatusHelper.Success();
}, function (error) {
// Use the widget helper and return failure as Widget Status
return WidgetHelpers.WidgetStatusHelper.Failure(error.message);
});
}
return {
load: function (widgetSettings) {
// Set your title
var $title = $('h2.title');
$title.text('Hello World');
return getQueryInfo(widgetSettings);
}
}
});
VSS.notifyLoadSucceeded();
});
</script>
</head>
<body>
<div class="widget">
<h2 class="title"></h2>
<div id="query-info-container"></div>
</div>
</body>
</html>
手順 5: 拡張機能マニフェストを更新する
この手順では、拡張機能マニフェストを更新して、2 番目のウィジェットのエントリを含めます。
新しいコントリビューションを contributions
プロパティの配列に追加し、新しいファイル hello-world2.html
を files プロパティの配列に追加します。
2 番目のウィジェットには別のプレビュー イメージが必要です。 これにpreview2.png
という名前を付け、img
フォルダーに配置します。
{
...,
"contributions": [
...,
{
"id": "HelloWorldWidget2",
"type": "ms.vss-dashboards-web.widget",
"targets": [
"ms.vss-dashboards-web.widget-catalog"
],
"properties": {
"name": "Hello World Widget 2 (with API)",
"description": "My second widget",
"previewImageUrl": "img/preview2.png",
"uri": "hello-world2.html",
"supportedSizes": [
{
"rowSpan": 1,
"columnSpan": 2
}
],
"supportedScopes": ["project_team"]
}
}
],
"files": [
{
"path": "hello-world.html",
"addressable": true
},
{
"path": "hello-world2.html",
"addressable": true
},
{
"path": "sdk/scripts",
"addressable": true
},
{
"path": "img",
"addressable": true
}
],
"scopes": [
"vso.work"
]
}
手順 6: パッケージ化、発行、共有
拡張をパッケージ化し、公開して共有しましょう。 拡張機能を既に公開している場合は、拡張機能を再パッケージ化し、Marketplace に直接更新できます。
手順 7: カタログからウィジェットを追加する
次に、チーム ダッシュボード (https:\//dev.azure.com/{Your_Organization}/{Your_Project}
) にアクセスします。 このページが既に開いている場合は、更新します。
[編集] にカーソルを合わせ、[追加] を選択します。 ウィジェット カタログが開き、インストールしたウィジェットが見つかります。
ダッシュボードに追加するには、ウィジェットを選択し、 [追加] を選択します。
パート 3: Hello World を構成する
このガイドのパート 2 では、ハードコードされたクエリのクエリ情報を表示するウィジェットの作成方法を学びました。 このパートでは、ハードコーディングされたクエリではなく、使用するクエリを構成する機能を追加します。 構成モードの場合、ユーザーは変更に基づいてウィジェットのライブ プレビューを表示します。 ユーザーが [保存] を選択すると、これらの変更がダッシュボードのウィジェットに 保存されます。
手順 1: HTML ファイルを追加する
ウィジェットとウィジェット構成の実装は、よく似ています。 どちらも、コントリビューションとして拡張フレームワークで実装されます。 どちらも同じ SDK ファイル (VSS.SDK.min.js
) 使用します。 どちらも HTML、JavaScript、CSS に基づいています。
前の例のファイル html-world2.html
をコピーし、そのコピーに hello-world3.html
の名前を付けます。 configuration.html
という名前の別の HTML ファイルを追加します。
フォルダーは次の例のようになります。
|--- README.md
|--- sdk
|--- node_modules
|--- scripts
|--- VSS.SDK.min.js
|--- img
|--- logo.png
|--- scripts
|--- configuration.html
|--- hello-world.html // html page to be used for your widget
|--- hello-world2.html // renamed copy of hello-world.html
|--- hello-world3.html // renamed copy of hello-world2.html
|--- vss-extension.json // extension's manifest
configuration.html
に次の HTML を追加します。 基本的に、 VSS.SDK.min.js
ファイルへの必須参照とドロップダウンの select
要素を追加して、プリセット リストからクエリを選択します。
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<script src="sdk/scripts/VSS.SDK.min.js"></script>
</head>
<body>
<div class="container">
<fieldset>
<label class="label">Query: </label>
<select id="query-path-dropdown" style="margin-top:10px">
<option value="" selected disabled hidden>Please select a query</option>
<option value="Shared Queries/Feedback">Shared Queries/Feedback</option>
<option value="Shared Queries/My Bugs">Shared Queries/My Bugs</option>
<option value="Shared Queries/My Tasks">Shared Queries/My Tasks</option>
</select>
</fieldset>
</div>
</body>
</html>
手順 2: JavaScript を構成する
このガイドのパート 1 のステップ 3 でウィジェットに使用したように、JavaScript を使用してウィジェット構成のコンテンツをレンダリングします。 この JavaScript コードは、コンテンツをレンダリングし、VSS SDK を初期化し、ウィジェット構成のコードを構成名にマップし、構成設定をフレームワークに渡します。 この場合、次のコードはウィジェットの構成を読み込みます。
ファイルconfiguration.html
を開き、次の<script>
要素を<head>
に追加します。
<script type="text/javascript">
VSS.init({
explicitNotifyLoaded: true,
usePlatformStyles: true
});
VSS.require(["AzureDevOps/Dashboards/WidgetHelpers"], function (WidgetHelpers) {
VSS.register("HelloWorldWidget.Configuration", function () {
var $queryDropdown = $("#query-path-dropdown");
return {
load: function (widgetSettings, widgetConfigurationContext) {
var settings = JSON.parse(widgetSettings.customSettings.data);
if (settings && settings.queryPath) {
$queryDropdown.val(settings.queryPath);
}
return WidgetHelpers.WidgetStatusHelper.Success();
},
onSave: function() {
var customSettings = {
data: JSON.stringify({
queryPath: $queryDropdown.val()
})
};
return WidgetHelpers.WidgetConfigurationSave.Valid(customSettings);
}
}
});
VSS.notifyLoadSucceeded();
});
</script>
VSS.init
、VSS.require
、およびVSS.register
は、パート 1 で説明したウィジェットの場合と同じ役割を果たします。 唯一の違いは、ウィジェット構成の場合、VSS.register
に渡される関数は、IWidgetConfiguration
コントラクトを満たすオブジェクトを返す必要があるということです。load
コントラクトのIWidgetConfiguration
プロパティには、値として関数が必要です。 この関数には、ウィジェット構成をレンダリングするための一連の手順があります。 ここでは、ドロップダウン要素の選択した値を既存の設定があれば更新します。 この関数は、フレームワークがwidget configuration
をインスタンス化する際に呼び出されますonSave
コントラクトのIWidgetConfiguration
プロパティには、値として関数が必要です。 この関数は、ユーザーが構成ウィンドウで [保存] を選択すると、フレームワークによって呼び出されます。 ユーザー入力を保存する準備ができたら、文字列にシリアル化し、custom settings
オブジェクトを形成し、WidgetConfigurationSave.Valid()
を使用してユーザー入力を保存します。
このガイドでは、JSON を使用して、ユーザー入力を文字列にシリアル化します。 ユーザー入力を文字列にシリアル化するその他の方法を選択できます。
ウィジェットは WidgetSettings
オブジェクトの customSettings プロパティを通じてアクセス可能です。
ウィジェットはデシリアライズする必要があり、これは手順 4で説明されています。
手順 3: ライブ プレビューを有効にする - JavaScript
ユーザーがドロップダウンからクエリを選択したときにライブ プレビューの更新を有効にするには、変更イベント ハンドラーをボタンにアタッチします。 このハンドラーは、構成が変更されたことをフレームワークに通知します。
また、プレビューの更新に使用するためにcustomSettings
を渡します。 フレームワークに通知するには、notify
の widgetConfigurationContext
メソッドを呼び出す必要があります。 このメソッドは 2 つのパラメーターを受け取ります。1 つはイベント名 (この場合は WidgetHelpers.WidgetEvent.ConfigurationChange
)、もう 1 つは EventArgs
ヘルパーメソッドを使用して customSettings
から作成されたイベントの WidgetEvent.Args
オブジェクトです。
load
プロパティに割り当てられた関数に、次のコードを追加します。
$queryDropdown.on("change", function () {
var customSettings = {
data: JSON.stringify({
queryPath: $queryDropdown.val()
})
};
var eventName = WidgetHelpers.WidgetEvent.ConfigurationChange;
var eventArgs = WidgetHelpers.WidgetEvent.Args(customSettings);
widgetConfigurationContext.notify(eventName, eventArgs);
});
構成の変更が少なくとも 1 回はフレームワークに通知され、[ 保存] ボタンが有効になっていることを確認します。
最後に、configuration.html
はこのようになります。
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<script src="sdk/scripts/VSS.SDK.min.js"></script>
<script type="text/javascript">
VSS.init({
explicitNotifyLoaded: true,
usePlatformStyles: true
});
VSS.require(["AzureDevOps/Dashboards/WidgetHelpers"], function (WidgetHelpers) {
VSS.register("HelloWorldWidget.Configuration", function () {
var $queryDropdown = $("#query-path-dropdown");
return {
load: function (widgetSettings, widgetConfigurationContext) {
var settings = JSON.parse(widgetSettings.customSettings.data);
if (settings && settings.queryPath) {
$queryDropdown.val(settings.queryPath);
}
$queryDropdown.on("change", function () {
var customSettings = {data: JSON.stringify({queryPath: $queryDropdown.val()})};
var eventName = WidgetHelpers.WidgetEvent.ConfigurationChange;
var eventArgs = WidgetHelpers.WidgetEvent.Args(customSettings);
widgetConfigurationContext.notify(eventName, eventArgs);
});
return WidgetHelpers.WidgetStatusHelper.Success();
},
onSave: function() {
var customSettings = {data: JSON.stringify({queryPath: $queryDropdown.val()})};
return WidgetHelpers.WidgetConfigurationSave.Valid(customSettings);
}
}
});
VSS.notifyLoadSucceeded();
});
</script>
</head>
<body>
<div class="container">
<fieldset>
<label class="label">Query: </label>
<select id="query-path-dropdown" style="margin-top:10px">
<option value="" selected disabled hidden>Please select a query</option>
<option value="Shared Queries/Feedback">Shared Queries/Feedback</option>
<option value="Shared Queries/My Bugs">Shared Queries/My Bugs</option>
<option value="Shared Queries/My Tasks">Shared Queries/My Tasks</option>
</select>
</fieldset>
</div>
</body>
</html>
手順 4: ウィジェットに再読み込みを実装する - JavaScript
ユーザーが選択したクエリ パスを格納するようにウィジェット構成を設定します。
ここで、ウィジェットのコードを更新して、前の例のハードコーディングされた Shared Queries/Feedback
ではなく、この格納された構成を使用する必要があります。
hello-world3.html
ファイルを開き、HelloWorldWidget2
を呼び出す行でウィジェットの名前を HelloWorldWidget3
から VSS.register
に更新します。
このアクションにより、フレームワークは拡張機能内のウィジェットを一意に識別できます。
HelloWorldWidget3
を介して VSS.register
にマップされた関数は、現在、IWidget
コントラクトを満たすオブジェクトを返します。
ウィジェットが新たに構成を必要とするため、IConfigurableWidget
契約を満たすオブジェクトを返すようにこの関数を更新する必要があります。
これを行うには、return ステートメントを更新して、次のコードに従って reload というプロパティを含めます。 このプロパティの値は、メソッドをもう一度呼び出す関数です。
この再読み込みメソッドは、ユーザー入力がライブ プレビューを表示するように変更されるたびに、フレームワークによって呼び出されます。 この再読み込みメソッドは、構成を保存するときにも呼び出されます。
return {
load: function (widgetSettings) {
// Set your title
var $title = $('h2.title');
$title.text('Hello World');
return getQueryInfo(widgetSettings);
},
reload: function (widgetSettings) {
return getQueryInfo(widgetSettings);
}
}
のハードコーディングされたクエリ パス getQueryInfo
は、構成されたクエリ パスに置き換える必要があります。これは、 メソッドに渡されるパラメーター widgetSettings
から抽出できます。
getQueryInfo
メソッドの最初に次のコードを追加し、ハードコードされたクエリパスをsettings.queryPath
に置き換えます。
var settings = JSON.parse(widgetSettings.customSettings.data);
if (!settings || !settings.queryPath) {
var $container = $('#query-info-container');
$container.empty();
$container.text("Sorry nothing to show, please configure a query path.");
return WidgetHelpers.WidgetStatusHelper.Success();
}
この時点で、ウィジェットは構成された設定でレンダリングする準備ができました。
load
プロパティとreload
プロパティは似た機能を持っています。 これは、ほとんどの単純なウィジェットの場合です。
複雑なウィジェットの場合、構成の変更回数に関係なく、1 回だけ実行する必要がある特定の操作があります。
または、複数回実行する必要のない重い操作が存在する場合もあります。 このような操作は、load
プロパティではなく、reload
プロパティに対応する関数の一部です。
手順 5: 拡張機能マニフェストを更新する
vss-extension.json
ファイルを開き、contributions
プロパティの配列に 2 つの新しいエントリを含めます。1 つはHelloWorldWidget3
ウィジェット用、もう 1 つは構成用です。
3 番目のウィジェットには、さらに別のプレビュー イメージが必要です。 これにpreview3.png
という名前を付け、img
フォルダーに配置します。
この例で追加した 2 つの新しい HTML ファイルを含むように、 files
プロパティの配列を更新します。
{
...
"contributions": [
... ,
{
"id": "HelloWorldWidget3",
"type": "ms.vss-dashboards-web.widget",
"targets": [
"ms.vss-dashboards-web.widget-catalog",
"fabrikam.azuredevops-extensions-myExtensions.HelloWorldWidget.Configuration"
],
"properties": {
"name": "Hello World Widget 3 (with config)",
"description": "My third widget",
"previewImageUrl": "img/preview3.png",
"uri": "hello-world3.html",
"supportedSizes": [
{
"rowSpan": 1,
"columnSpan": 2
}
],
"supportedScopes": ["project_team"]
}
},
{
"id": "HelloWorldWidget.Configuration",
"type": "ms.vss-dashboards-web.widget-configuration",
"targets": [ "ms.vss-dashboards-web.widget-configuration" ],
"properties": {
"name": "HelloWorldWidget Configuration",
"description": "Configures HelloWorldWidget",
"uri": "configuration.html"
}
}
],
"files": [
{
"path": "hello-world.html", "addressable": true
},
{
"path": "hello-world2.html", "addressable": true
},
{
"path": "hello-world3.html", "addressable": true
},
{
"path": "configuration.html", "addressable": true
},
{
"path": "sdk/scripts", "addressable": true
},
{
"path": "img", "addressable": true
}
],
...
}
ウィジェット構成のコントリビューションは、ウィジェット自体とは少し異なるモデルに従います。 ウィジェット構成のコントリビューション エントリには、次の項目があります。
- 投稿を識別するための
id
。 ID は拡張機能内で一意である必要があります。 - コントリビューションの
type
。 すべてのウィジェット構成では、ms.vss-dashboards-web.widget-configuration
する必要があります。 - コントリビューションの対象となる
targets
の配列。 すべてのウィジェット構成に、ms.vss-dashboards-web.widget-configuration
という単一のエントリがあります。 - 構成に使用される HTML ファイルの名前、説明、および URI を含む一連のプロパティを含む
properties
。
構成をサポートするには、ウィジェットのコントリビューションも変更する必要があります。 ウィジェットの targets
の配列を更新して、構成の ID を次の形式で含める必要があります。
<publisher>.<id for the extension>.<id for the configuration contribution>
この場合、例は次のようになります。
fabrikam.vsts-extensions-myExtensions.HelloWorldWidget.Configuration
警告
構成可能なウィジェットのコントリビューション エントリが、前述のように適切な発行元と拡張機能の名前を使用して構成を対象としていない場合、ウィジェットの [構成] ボタンは表示されません。
このパートの最後には、マニフェスト ファイルに 3 つのウィジェットと 1 つの構成が含まれている必要があります。 完全なサンプル マニフェスト ファイルについては、 vss-extension.jsonを参照してください。
手順 6: パッケージ化、発行、共有
拡張機能が公開されていない場合は、「 手順 6: パッケージ化、発行、共有」を参照してください。 拡張機能を既に公開している場合は、拡張機能を再パッケージ化し、Marketplace に直接更新できます。
手順 7: カタログからウィジェットを追加する
次に、チーム ダッシュボード (https://dev.azure.com/{Your_Organization}/{Your_Project}
) にアクセスします。 このページが既に開いている場合は、更新します。
[編集] にカーソルを合わせ、[追加] を選択します。 このアクションにより、インストールしたウィジェットが見つかるウィジェット カタログが開きます。
ウィジェットをダッシュボードに追加するには、ウィジェットを選択し、 追加を選択します。
次のようなメッセージが表示され、ウィジェットを構成するように求められます。
ウィジェットを構成するには、2 つの方法があります。 1 つは、ウィジェットの上にマウス ポインターを置き、右上隅に表示される省略記号を選択し、[構成] を選択 することです。 もう 1 つは、ダッシュボードの右下にある [編集] ボタンを選択し、ウィジェットの右上隅に表示される構成ボタンを選択することです。 どちらかが右側に構成エクスペリエンスを開き、中央にウィジェットのプレビューを開きます。
先に進み、ドロップダウンからクエリを選択します。 ライブ プレビューには、更新された結果が表示されます。 保存を選択すると、ウィジェットに更新された結果が表示されます。
手順 8: さらに構成する (省略可能)
configuration.html
で必要な数の HTML フォーム要素を追加して、より多くの構成を行うことができます。
すぐに使用できる構成可能な機能は、ウィジェット名とウィジェットサイズの 2 つあります。
既定では、拡張機能マニフェストでウィジェットに指定した名前は、ダッシュボードに追加されるウィジェットのすべてのインスタンスのウィジェット名として格納されます。
ユーザーがウィジェットのインスタンスに任意の名前を追加できるように、構成を許可できます。
このような構成を可能にするには、拡張機能マニフェストのウィジェットに対応するプロパティ セクションに isNameConfigurable:true
を追加します。
拡張機能マニフェストの supportedSizes
配列にウィジェットの複数のエントリを追加すると、ユーザーはウィジェットのサイズを構成することもできます。
このガイドの 3 番目のサンプルの拡張機能マニフェストは、ウィジェットの名前とサイズの構成を有効にした場合の次の例のようになります。
{
...
"contributions": [
... ,
{
"id": "HelloWorldWidget3",
"type": "ms.vss-dashboards-web.widget",
"targets": [
"ms.vss-dashboards-web.widget-catalog",
"fabrikam.azuredevops-extensions-myExtensions.HelloWorldWidget.Configuration"
],
"properties": {
"name": "Hello World Widget 3 (with config)",
"description": "My third widget",
"previewImageUrl": "img/preview3.png",
"uri": "hello-world3.html",
"isNameConfigurable": true,
"supportedSizes": [
{
"rowSpan": 1,
"columnSpan": 2
},
{
"rowSpan": 2,
"columnSpan": 2
}
],
"supportedScopes": ["project_team"]
}
},
...
]
}
拡張機能を再パッケージ化して更新します。 このウィジェットを含むダッシュボードを更新します (Hello Worldウィジェット 3 (構成あり) )。 ウィジェットの構成モードを開くと、ウィジェットの名前とサイズを変更するオプションが表示されます。
ドロップダウンから別のサイズを選択します。 ライブ プレビューのサイズが変更されます。 変更を保存すると、ダッシュボード上のウィジェットのサイズも変更されます。
ウィジェットの名前を変更しても、サンプル ウィジェットにはウィジェット名が表示されないため、ウィジェットに表示される変更はありません。
ハードコーディングされたテキスト Hello World を、要素のテキストを設定した行のwidgetSettings.name
に置き換えて、ハードコーディングされたテキスト h2
ではなくウィジェット名を表示するようにサンプル コードを変更します。
このアクションにより、ページの更新時にウィジェットが読み込まれるたびにウィジェット名が表示されます。
構成が変更されるたびにライブ プレビューを更新する必要があるため、コードの reload
部分にも同じコードを追加する必要があります。
最終的な hello-world3.html
の return ステートメントは次のとおりです。
return {
load: function (widgetSettings) {
// Set your title
var $title = $('h2.title');
$title.text(widgetSettings.name);
return getQueryInfo(widgetSettings);
},
reload: function (widgetSettings) {
// Set your title
var $title = $('h2.title');
$title.text(widgetSettings.name);
return getQueryInfo(widgetSettings);
}
}
あなたの拡張機能を再パッケージして、再度更新します。 このウィジェットを含むダッシュボードを更新します。
ウィジェット名に変更が加えられた場合は、構成モードでウィジェットのタイトルを更新します。