Azure DevOps Services | Azure DevOps Server 2022 - Azure DevOps Server 2019
대시보드의 위젯은 확장 프레임워크에서 기여로 구현됩니다. 단일 확장에는 여러 기여가 있을 수 있습니다. 여러 위젯을 기여로 사용하여 확장을 만드는 방법을 알아봅니다.
이 기사는 세 부분으로 나누어져 있으며, 각 부분은 이전 부분을 기반으로 발전합니다. 간단한 위젯으로 시작하고 포괄적인 위젯으로 끝납니다.
팁
Azure DevOps 확장 SDK를 사용한 확장 개발을 위한 최신 문서를 확인해 보세요.
필수 조건
요구 사항 | 설명 |
---|---|
프로그래밍 지식 | 위젯 개발을 위한 JavaScript, HTML 및 CSS 지식 |
Azure DevOps 조직 | 조직 만들기 |
텍스트 편집기 | 자습서에 Visual Studio Code 를 사용합니다. |
Node.js | 최신 버전의 Node.js |
플랫폼 간 CLI | 확장을 패키징하기 위한 tfx-cli 다음을 사용하여 설치합니다. npm i -g tfx-cli |
프로젝트 디렉터리 | 자습서를 완료한 후 이 구조의 홈 디렉터리:|--- README.md |--- sdk |--- node_modules |--- scripts |--- VSS.SDK.min.js |--- img |--- logo.png |--- scripts |--- hello-world.html // html page for your widget |--- vss-extension.json // extension manifest |
자습서 개요
이 자습서에서는 다음 세 가지 점진적 예제를 통해 위젯 개발을 설명합니다.
부분 | 포커스 | 학습 내용 |
---|---|---|
1부: Hello World | 기본 위젯 만들기 | 텍스트를 표시하는 위젯 만들기 |
2부: REST API 통합 | Azure DevOps API 호출 | 데이터를 가져오고 표시하는 REST API 기능 추가 |
3부: 위젯 구성 | 사용자 사용자 지정 | 위젯에 대한 구성 옵션 구현 |
시작하기 전에 제공하는 기본 위젯 스타일 및 구조 지침을 검토합니다.
1부: 헬로 월드
JavaScript를 사용하여 "Hello World"를 표시하는 기본 위젯을 만듭니다. 이 기초는 핵심 위젯 개발 개념을 보여 줍니다.
1단계: 클라이언트 SDK 설치
VSS SDK를 사용하면 위젯이 Azure DevOps와 통신할 수 있습니다. npm을 사용하여 설치합니다.
npm install vss-web-extension-sdk
VSS.SDK.min.js
파일을 vss-web-extension-sdk/lib
에서 home/sdk/scripts
폴더로 복사합니다.
자세한 SDK 설명서는 클라이언트 SDK GitHub 페이지를 참조하세요.
2단계: HTML 구조 만들기
프로젝트 디렉터리에서 hello-world.html
를 만드세요. 이 파일은 위젯의 레이아웃과 필요한 스크립트에 대한 참조를 제공합니다.
<!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>
위젯은 iframe에서 실행되므로 프레임워크를 제외한 <script>
<link>
대부분의 HTML 헤드 요소는 무시됩니다.
3단계: 위젯 JavaScript 추가
위젯 기능을 구현하려면 HTML 파일의 섹션에 다음 스크립트를 <head>
추가합니다.
<script type="text/javascript">
VSS.init({
explicitNotifyLoaded: true,
usePlatformStyles: true
});
VSS.require(["AzureDevOps/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>
주요 JavaScript 구성 요소
기능 | 목적 |
---|---|
VSS.init() |
위젯과 Azure DevOps 간의 통신을 초기화합니다. |
VSS.require() |
필요한 SDK 라이브러리 및 위젯 도우미를 로드합니다. |
VSS.register() |
고유 식별자를 사용하여 위젯을 등록합니다. |
WidgetHelpers.IncludeWidgetStyles() |
기본 Azure DevOps 스타일 적용 |
VSS.notifyLoadSucceeded() |
로드가 성공적으로 완료되었음을 프레임워크에 알 |
중요합니다
위젯 이름은 확장 매니페스트의 위젯 이름과 VSS.register()
일치 id
해야 합니다(5단계).
4단계: 확장 이미지 추가
확장에 필요한 이미지를 만듭니다.
-
확장 로고: 폴더에
img
이름이 지정된logo.png
98x98 픽셀 이미지 -
위젯 카탈로그 아이콘: 폴더에
img
명명된CatalogIcon.png
98x98 픽셀 이미지 -
위젯 미리 보기: 폴더에
img
명명된preview.png
330x160 픽셀 이미지
이러한 이미지는 사용자가 사용 가능한 확장을 찾아볼 때 Marketplace 및 위젯 카탈로그에 표시됩니다.
5단계: 확장 매니페스트 만들기
프로젝트 루트 디렉터리에 vss-extension.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
}
]
}
중요합니다
"publisher": "fabrikam"
을 실제 게시자 이름으로 대체합니다.
게시자를 만드는 방법을 알아봅니다.
필수 매니페스트 속성
섹션 | 목적 |
---|---|
기본 정보 | 확장 이름, 버전, 설명 및 게시자 |
아이콘 | 확장의 시각적 자산에 대한 경로 |
기여 | ID, 형식 및 속성을 포함한 위젯 정의 |
파일 | 확장 패키지에 포함할 모든 파일 |
전체 매니페스트 설명서는 확장 매니페스트 참조를 참조하세요.
6단계: 확장 패키지 및 게시
확장을 패키지하고 Visual Studio Marketplace에 게시합니다.
패키징 도구 설치
npm i -g tfx-cli
확장 패키지 만들기
프로젝트 디렉터리에서 다음을 실행합니다.
tfx extension create --manifest-globs vss-extension.json
이 작업은 패키지된 확장자를 포함하는 파일을 만듭니다 .vsix
.
게시자 설정
- Visual Studio Marketplace 게시 포털로 이동합니다.
- 로그인하고 게시자가 없는 경우 게시자를 만듭니다.
- 매니페스트 파일에 사용되는 고유한 게시자 식별자를 선택합니다.
-
vss-extension.json
"fabrikam" 대신 게시자 이름을 사용하도록 업데이트합니다.
확장 업로드
- 게시 포털에서 새 확장 업로드를 선택합니다.
-
.vsix
파일을 선택하고 업로드합니다. - Azure DevOps 조직과 확장을 공유합니다.
또는 명령줄을 사용합니다.
tfx extension publish --manifest-globs vss-extension.json --share-with yourOrganization
팁
기존 확장을 업데이트할 때 버전 번호를 자동으로 증가시키는 데 사용합니다 --rev-version
.
7단계: 위젯 설치 및 테스트
테스트하려면 대시보드에 위젯을 추가합니다.
- Azure DevOps 프로젝트
https://dev.azure.com/{Your_Organization}/{Your_Project}
로 이동합니다. - Overview>Dashboards로 이동합니다.
- 위젯 추가를 선택합니다.
- 카탈로그에서 위젯을 찾고 추가를 선택합니다.
대시보드에 "Hello World" 위젯이 표시되어 구성한 텍스트가 표시됩니다.
다음 단계: 2부 를 계속 진행하여 Azure DevOps REST API를 위젯에 통합하는 방법을 알아봅니다.
2부: Azure DevOps REST API를 사용하여 헬로 월드
REST API를 사용하여 Azure DevOps 데이터와 상호 작용하도록 위젯을 확장합니다. 이 예제에서는 쿼리 정보를 가져와서 위젯에 동적으로 표시하는 방법을 보여 줍니다.
이 부분에서는 작업 항목 추적 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 // Part 1 widget
|--- hello-world2.html // Part 2 widget (new)
|--- vss-extension.json // Extension manifest
위젯 HTML 구조 업데이트
hello-world2.html
를 변경하십시오.
-
쿼리 데이터에 대한 컨테이너 추가: 쿼리 정보를 표시하는 새
<div>
요소를 포함합니다. -
위젯 식별자 업데이트: 고유 식별을 위해 위젯 이름을 다음으로
HelloWorldWidget
HelloWorldWidget2
변경합니다.
<!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"], 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단계: API 액세스 권한 구성
REST API를 호출하기 전에 확장 매니페스트에서 필요한 권한을 구성합니다.
작업 범위 추가
범위는 vso.work
작업 항목 및 쿼리에 대한 읽기 전용 액세스 권한을 부여합니다.
vss-extension.json
에 이 범위를 추가합니다.
{
"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는 SDK를 통해 JavaScript REST 클라이언트 라이브러리를 제공합니다. 이러한 라이브러리는 AJAX 호출을 래핑하고 API 응답을 사용 가능한 개체에 매핑합니다.
위젯 JavaScript 업데이트
VSS.require
호출을 hello-world2.html
에서 작업 항목 추적 REST 클라이언트를 포함하도록 바꿉니다.
VSS.require(["AzureDevOps/Dashboards/WidgetHelpers", "AzureDevOps/WorkItemTracking/RestClient"],
function (WidgetHelpers, WorkItemTrackingRestClient) {
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 WorkItemTrackingRestClient.getClient().getQuery(projectId, "Shared Queries/Feedback")
.then(function (query) {
// Process query data (implemented in Step 4)
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();
});
주요 구현 세부 정보
구성 요소 | 목적 |
---|---|
WorkItemTrackingRestClient.getClient() |
작업 항목 추적 REST 클라이언트의 인스턴스를 가져옵니다. |
getQuery() |
프라미스 객체로 감싸진 쿼리 정보를 검색합니다. |
WidgetStatusHelper.Failure() |
위젯 오류에 대한 일관된 오류 처리를 제공합니다. |
projectId |
API 호출에 필요한 현재 프로젝트 컨텍스트 |
팁
사용자 지정 쿼리 경로: "공유 쿼리"에 "피드백" 쿼리가 없는 경우 프로젝트에 있는 모든 쿼리의 경로로 바꿉 "Shared Queries/Feedback"
니다.
4단계: API 응답 데이터 표시
REST API 응답을 처리하여 위젯에서 쿼리 정보를 렌더링합니다.
쿼리 데이터 렌더링 추가
주석을 // Process query data
다음 구현으로 바꿉다.
// 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);
메서드는 쿼리 메타데이터에 대한 속성이 있는 Contracts.QueryHierarchyItem
개체를 getQuery()
반환합니다. 다음은 "Hello World" 텍스트 아래에 세 가지 주요 정보를 표시하는 예제입니다.
전체 작업 예제
최종 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, WorkItemTrackingRestClient) {
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 WorkItemTrackingRestClient.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단계: 확장 매니페스트 업데이트
위젯 카탈로그에서 사용할 수 있도록 하려면 확장 매니페스트에 새 위젯을 추가합니다.
두 번째 위젯 기여 추가
vss-extension.json
를 REST API를 활성화한 위젯을 포함하도록 업데이트하세요. 배열에 이 기여를 추가합니다.contributions
{
"contributions": [
// ...existing HelloWorldWidget contribution...,
{
"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"
]
}
팁
미리 보기 이미지: 이미지(330x160픽셀)를 만들고 preview2.png
폴더에 img
배치하여 사용자에게 위젯이 카탈로그의 모양을 표시합니다.
6단계: 패키지, 게시 및 공유
확장을 패키지화하고 게시하며 공유하십시오. 확장을 이미 게시한 경우 Marketplace에서 직접 다시 패키지하고 업데이트할 수 있습니다.
7단계: REST API 위젯 테스트
작동 중인 REST API 통합을 보려면 대시보드에 새 위젯을 추가합니다.
- Azure DevOps 프로젝트
https://dev.azure.com/{Your_Organization}/{Your_Project}
로 이동합니다. - 개요>대시보드를 선택합니다.
- 위젯 추가를 선택합니다.
- "Hello World 위젯 2(API 포함)"를 찾아 추가를 선택합니다.
향상된 위젯은 Azure DevOps 프로젝트의 "Hello World" 텍스트와 라이브 쿼리 정보를 모두 표시합니다.
다음 단계: 3부 를 계속 진행하여 사용자가 표시할 쿼리를 사용자 지정할 수 있는 구성 옵션을 추가합니다.
3부: 헬로 월드 구성
위젯에 사용자 구성 기능을 추가하여 2부 를 빌드합니다. 쿼리 경로를 하드 코딩하는 대신 사용자가 라이브 미리 보기 기능을 사용하여 표시할 쿼리를 선택할 수 있는 구성 인터페이스를 만듭니다.
이 부분에서는 구성 중에 실시간 피드백을 제공하면서 사용자가 특정 요구 사항에 맞게 사용자 지정할 수 있는 구성 가능한 위젯을 만드는 방법을 보여 줍니다.
1단계: 구성 파일 만들기
위젯 구성은 위젯 자체와 많은 유사점을 공유합니다. 둘 다 동일한 SDK, HTML 구조 및 JavaScript 패턴을 사용하지만 확장 프레임워크 내에서는 서로 다른 용도로 사용됩니다.
프로젝트 구조 설정
위젯 구성을 지원하려면 다음 두 개의 새 파일을 만듭니다.
-
hello-world2.html
을 복사하고 구성 가능한 위젯인hello-world3.html
으로 이름을 바꾸십시오. - 구성 인터페이스를 처리하는 새
configuration.html
파일을 만듭니다.
이제 프로젝트 구조에 다음이 포함됩니다.
|--- README.md
|--- sdk/
|--- node_modules
|--- scripts/
|--- VSS.SDK.min.js
|--- img/
|--- logo.png
|--- scripts/
|--- configuration.html // New: Configuration interface
|--- hello-world.html // Part 1: Basic widget
|--- hello-world2.html // Part 2: REST API widget
|--- hello-world3.html // Part 3: Configurable widget (new)
|--- vss-extension.json // Extension manifest
구성 인터페이스 만들기
이 HTML 구조를 configuration.html
에 추가하면 쿼리를 선택하기 위한 드롭다운 선택기가 생성됩니다.
<!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 구성 구현
구성 JavaScript는 위젯과 동일한 초기화 패턴을 따르지만 기본 IWidget
계약 대신 IWidgetConfiguration
계약을 구현합니다.
구성 논리 추가
이 스크립트를 <head>
의 configuration.html
섹션에 삽입합니다.
<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>
구성 계약 세부 정보
계약에는 IWidgetConfiguration
다음과 같은 주요 함수가 필요합니다.
기능 | 목적 | 호출 시 |
---|---|---|
load() |
기존 설정을 사용하여 구성 UI 초기화 | 구성 대화 상자가 열리는 경우 |
onSave() |
사용자 입력 직렬화 및 설정 유효성 검사 | 사용자가 저장을 선택하는 경우 |
팁
데이터 serialization: 이 예제에서는 JSON을 사용하여 설정을 직렬화합니다. 위젯은 widgetSettings.customSettings.data
을(를) 통해 이러한 설정에 액세스 및 그에 따라 역직렬화를 수행해야 합니다.
3단계: 라이브 미리 보기 기능 사용
라이브 미리 보기를 사용하면 사용자가 구성 설정을 수정할 때 위젯 변경 내용을 즉시 볼 수 있으며 저장하기 전에 즉각적인 피드백을 제공할 수 있습니다.
변경 알림 구현
라이브 미리 보기를 사용하도록 설정하려면 함수 내에 다음 이벤트 처리기를 추가합니다 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);
});
구성 파일 완료
마지막 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단계: 위젯을 구성할 수 있도록 설정
하드 코딩된 값 대신 구성 데이터를 사용하도록 2부에서 위젯을 변환합니다. 이 단계를 수행하려면 IConfigurableWidget
계약을 구현해야 합니다.
위젯 등록 업데이트
hello-world3.html
에서 다음과 같이 변경하십시오.
-
위젯 ID 업데이트:
HelloWorldWidget2
에서HelloWorldWidget3
로 변경합니다. -
다시 로드 함수 추가: 계약을 구현합니다
IConfigurableWidget
.
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
하드 코딩된 쿼리 경로 대신 구성 설정을 사용하도록 함수를 업데이트합니다.
var settings = JSON.parse(widgetSettings.customSettings.data);
if (!settings || !settings.queryPath) {
var $container = $('#query-info-container');
$container.empty();
$container.text("Please configure a query path to display data.");
return WidgetHelpers.WidgetStatusHelper.Success();
}
위젯 수명 주기 차이
기능 | 목적 | 사용 지침 |
---|---|---|
load() |
초기 위젯 렌더링 및 일회성 설정 | 많은 작업, 리소스 초기화 |
reload() |
새 구성으로 위젯 업데이트 | 간단한 업데이트, 데이터 새로 고침 |
팁
성능 최적화: 한 번만 실행해야 하는 비용이 많이 드는 작업과 reload()
구성이 변경되면 빠른 업데이트에 사용합니다load()
.
(선택 사항) 자세한 정보를 위한 라이트박스 추가
대시보드 위젯은 공간이 제한되어 포괄적인 정보를 표시하기가 어렵습니다. 라이트박스는 대시보드에서 벗어나지 않고 모달 오버레이에 자세한 데이터를 표시하여 세련된 솔루션을 제공합니다.
위젯에 라이트박스를 사용하는 이유는 무엇인가요?
이익 | 설명 |
---|---|
공간 효율성 | 자세한 보기를 제공하면서 위젯을 간결하게 유지 |
사용자 환경 | 추가 정보를 표시하는 동안 대시보드 컨텍스트 유지 관리 |
점진적 공개 | 위젯에 요약 데이터 표시, 요청 시 세부 정보 |
반응형 디자인 | 다양한 화면 크기 및 위젯 구성에 맞게 조정 |
클릭 가능한 요소 구현
Lightbox를 트리거하는 클릭 가능한 요소를 포함하도록 쿼리 데이터 렌더링을 업데이트합니다.
// Create a list with clickable query details
var $list = $('<ul class="query-summary">');
$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>"));
// Add a clickable element to open detailed view
var $detailsLink = $('<button class="details-link">View Details</button>');
$detailsLink.on('click', function() {
showQueryDetails(query);
});
// Append to the container
var $container = $('#query-info-container');
$container.empty();
$container.append($list);
$container.append($detailsLink);
Lightbox 기능 만들기
위젯 JavaScript에 다음 lightbox 구현을 추가합니다.
function showQueryDetails(query) {
// Create lightbox overlay
var $overlay = $('<div class="lightbox-overlay">');
var $lightbox = $('<div class="lightbox-content">');
// Add close button
var $closeBtn = $('<button class="lightbox-close">×</button>');
$closeBtn.on('click', function() {
$overlay.remove();
});
// Create detailed content
var $content = $('<div class="query-details">');
$content.append($('<h3>').text(query.name || 'Query Details'));
$content.append($('<p>').html('<strong>ID:</strong> ' + query.id));
$content.append($('<p>').html('<strong>Path:</strong> ' + query.path));
$content.append($('<p>').html('<strong>Created:</strong> ' + (query.createdDate ? new Date(query.createdDate).toLocaleDateString() : 'Unknown')));
$content.append($('<p>').html('<strong>Modified:</strong> ' + (query.lastModifiedDate ? new Date(query.lastModifiedDate).toLocaleDateString() : 'Unknown')));
$content.append($('<p>').html('<strong>Created By:</strong> ' + (query.createdBy ? query.createdBy.displayName : 'Unknown')));
$content.append($('<p>').html('<strong>Modified By:</strong> ' + (query.lastModifiedBy ? query.lastModifiedBy.displayName : 'Unknown')));
if (query.queryType) {
$content.append($('<p>').html('<strong>Type:</strong> ' + query.queryType));
}
// Assemble lightbox
$lightbox.append($closeBtn);
$lightbox.append($content);
$overlay.append($lightbox);
// Add to document and show
$('body').append($overlay);
// Close on overlay click
$overlay.on('click', function(e) {
if (e.target === $overlay[0]) {
$overlay.remove();
}
});
// Close on Escape key
$(document).on('keydown.lightbox', function(e) {
if (e.keyCode === 27) { // Escape key
$overlay.remove();
$(document).off('keydown.lightbox');
}
});
}
lightbox 스타일 추가
위젯 HTML <head>
섹션에 라이트박스에 대한 CSS 스타일을 포함합니다.
<style>
.query-summary {
list-style: none;
padding: 0;
margin: 10px 0;
}
.query-summary li {
padding: 2px 0;
font-size: 12px;
}
.details-link {
background: #0078d4;
color: white;
border: none;
padding: 4px 8px;
font-size: 11px;
cursor: pointer;
border-radius: 2px;
margin-top: 8px;
}
.details-link:hover {
background: #106ebe;
}
.lightbox-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.7);
z-index: 10000;
display: flex;
align-items: center;
justify-content: center;
}
.lightbox-content {
background: white;
border-radius: 4px;
padding: 20px;
max-width: 500px;
max-height: 80vh;
overflow-y: auto;
position: relative;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
}
.lightbox-close {
position: absolute;
top: 10px;
right: 15px;
background: none;
border: none;
font-size: 24px;
cursor: pointer;
color: #666;
line-height: 1;
}
.lightbox-close:hover {
color: #000;
}
.query-details h3 {
margin-top: 0;
color: #323130;
}
.query-details p {
margin: 8px 0;
font-size: 14px;
line-height: 1.4;
}
</style>
향상된 위젯 구현
라이트박스 기능이 포함된 완전한 향상된 위젯:
<!DOCTYPE html>
<html>
<head>
<script src="sdk/scripts/VSS.SDK.min.js"></script>
<style>
/* Lightbox styles from above */
.query-summary {
list-style: none;
padding: 0;
margin: 10px 0;
}
.query-summary li {
padding: 2px 0;
font-size: 12px;
}
.details-link {
background: #0078d4;
color: white;
border: none;
padding: 4px 8px;
font-size: 11px;
cursor: pointer;
border-radius: 2px;
margin-top: 8px;
}
.details-link:hover {
background: #106ebe;
}
.lightbox-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.7);
z-index: 10000;
display: flex;
align-items: center;
justify-content: center;
}
.lightbox-content {
background: white;
border-radius: 4px;
padding: 20px;
max-width: 500px;
max-height: 80vh;
overflow-y: auto;
position: relative;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
}
.lightbox-close {
position: absolute;
top: 10px;
right: 15px;
background: none;
border: none;
font-size: 24px;
cursor: pointer;
color: #666;
line-height: 1;
}
.lightbox-close:hover {
color: #000;
}
.query-details h3 {
margin-top: 0;
color: #323130;
}
.query-details p {
margin: 8px 0;
font-size: 14px;
line-height: 1.4;
}
</style>
<script type="text/javascript">
VSS.init({
explicitNotifyLoaded: true,
usePlatformStyles: true
});
VSS.require(["AzureDevOps/Dashboards/WidgetHelpers", "AzureDevOps/WorkItemTracking/RestClient"],
function (WidgetHelpers, WorkItemTrackingRestClient) {
WidgetHelpers.IncludeWidgetStyles();
function showQueryDetails(query) {
// Lightbox implementation from above
}
VSS.register("HelloWorldWidget2", function () {
var projectId = VSS.getWebContext().project.id;
var getQueryInfo = function (widgetSettings) {
return WorkItemTrackingRestClient.getClient().getQuery(projectId, "Shared Queries/Feedback")
.then(function (query) {
// Enhanced display with lightbox trigger
var $list = $('<ul class="query-summary">');
$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>")));
var $detailsLink = $('<button class="details-link">View Details</button>');
$detailsLink.on('click', function() {
showQueryDetails(query);
});
var $container = $('#query-info-container');
$container.empty();
$container.append($list);
$container.append($detailsLink);
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();
});
</script>
</head>
<body>
<div class="widget">
<h2 class="title"></h2>
<div id="query-info-container"></div>
</div>
</body>
</html>
접근성 고려 사항: 라이트박스가 키보드에 액세스할 수 있고 화면 읽기 프로그램을 위한 적절한 레이블이 포함되어 있는지 확인합니다. Azure DevOps의 기본 제공 접근성 기능을 사용하여 테스트합니다.
중요합니다
성능: Lightbox는 빠르게 로드되어야 합니다. 모든 데이터를 처음부터 가져오는 대신 라이트박스가 열릴 때만 자세한 데이터를 지연 로드하는 것을 고려하십시오.
5단계: 확장 매니페스트 구성
구성 가능한 위젯과 해당 구성 인터페이스를 모두 확장 매니페스트에 등록합니다.
위젯 및 구성 기여 추가
다음 두 가지 새 기여를 포함하도록 업데이트 vss-extension.json
합니다.
{
"contributions": [
// ...existing 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
}
]
}
구성 기여 요구 사항
속성 | 목적 | 필수 값 |
---|---|---|
type |
기여를 위젯 구성으로 식별 | ms.vss-dashboards-web.widget-configuration |
targets |
구성이 표시되는 위치 | ms.vss-dashboards-web.widget-configuration |
uri |
구성 HTML 파일 경로 | 구성 파일 경로 |
위젯 대상 지정 패턴
구성 가능한 위젯의 경우 배열에 targets
구성에 대한 참조가 포함되어야 합니다.
<publisher>.<extension-id>.<configuration-id>
경고
구성 단추 표시 유형: 위젯이 구성 기여를 제대로 대상으로 지정하지 않으면 구성 단추가 나타나지 않습니다. 게시자 및 확장 이름이 매니페스트와 정확히 일치하는지 확인합니다.
6단계: 패키지, 게시 및 공유
구성 기능을 사용하여 향상된 확장을 배포합니다.
첫 번째 게시인 경우 6단계: 패키지, 게시 및 공유를 수행합니다. 기존 확장의 경우 Marketplace에서 직접 다시 패키징하고 업데이트합니다.
7단계: 구성 가능한 위젯 테스트
위젯을 추가하고 구성하여 전체 구성 워크플로를 경험하세요.
대시보드에 위젯 추가
-
https://dev.azure.com/{Your_Organization}/{Your_Project}
로 이동합니다. - Overview>Dashboards로 이동합니다.
- 위젯 추가를 선택합니다.
- "Hello World 위젯 3(구성 포함)"을 찾고 추가를 선택합니다.
위젯에 설치가 필요하므로 구성 프롬프트가 표시됩니다.
위젯 구성
다음 방법 중 하나를 통해 구성에 액세스합니다.
- 위젯 메뉴: 위젯을 마우스로 가리키고 줄임표(⋯)를 선택한 다음 구성
- 대시보드 편집 모드: 대시보드에서 편집 을 선택한 다음 위젯의 구성 단추
중앙에 라이브 미리 보기가 있는 구성 패널이 열립니다. 드롭다운에서 쿼리를 선택하여 즉시 업데이트를 확인하고 저장 을 선택하여 변경 내용을 적용합니다.
8단계: 고급 구성 옵션 추가
사용자 지정 이름 및 크기와 같은 더 많은 기본 제공 구성 기능을 사용하여 위젯을 확장합니다.
이름 및 크기 설정 활성화
Azure DevOps는 다음과 같은 두 가지 구성 가능한 기능을 제공합니다.
특징 | 매니페스트 속성 | 목적 |
---|---|---|
사용자 지정 이름 | isNameConfigurable: true |
사용자는 기본 위젯 이름을 재정의할 수 있습니다. |
여러 크기 | 여러 supportedSizes 항목 |
사용자는 위젯의 크기를 조정할 수 있습니다. |
향상된 매니페스트 예제
{
"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"]
}
}
]
}
구성된 이름 표시
사용자 지정 위젯 이름을 표시하려면 다음을 사용하도록 widgetSettings.name
위젯을 업데이트합니다.
return {
load: function (widgetSettings) {
// Display configured name instead of hard-coded text
var $title = $('h2.title');
$title.text(widgetSettings.name);
return getQueryInfo(widgetSettings);
},
reload: function (widgetSettings) {
// Update name during configuration changes
var $title = $('h2.title');
$title.text(widgetSettings.name);
return getQueryInfo(widgetSettings);
}
}
확장을 업데이트한 후 위젯 이름과 크기를 모두 구성할 수 있습니다.
이러한 고급 구성 옵션을 사용하도록 확장을 다시 패키지하고 업데이트합니다.
축하합니다! 라이브 미리 보기 기능 및 사용자 지정 옵션을 사용하여 구성 가능한 완전한 Azure DevOps 대시보드 위젯을 만들었습니다.