Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Azure DevOps Services | Azure DevOps Server 2022 - Azure DevOps Server 2019
I widget in un dashboard vengono implementati come contributi nel framework di estensione. Una singola estensione può avere più contributi. Informazioni su come creare un'estensione con più widget come contributi.
Questo articolo è suddiviso in tre parti, ognuna delle quali si basa sulla precedente. Iniziare con un widget semplice e terminare con un widget completo.
Suggerimento
Vedere la documentazione più recente sullo sviluppo di estensioni con Azure DevOps Extension SDK.
Prerequisiti
Requisito | Descrizione |
---|---|
Conoscenza della programmazione | Conoscenza javaScript, HTML e CSS per lo sviluppo di widget |
Organizzazione DevOps di Azure | Creare un'organizzazione |
Editor di testo | Per le esercitazioni viene usato Visual Studio Code |
Node.js | Versione più recente di Node.js |
Interfaccia della riga di comando multipiattaforma |
tfx-cli per impacchettare estensioni Installare con: npm i -g tfx-cli |
Directory del progetto | Home directory con questa struttura dopo aver completato l'esercitazione:|--- 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 |
Panoramica delle esercitazioni
Questa esercitazione illustra lo sviluppo di widget tramite tre esempi progressivi:
Parte | Concentrazione (or Messa a fuoco) | Cosa imparerai |
---|---|---|
Parte 1: Hello World | Creazione di widget di base | Creare un widget che visualizza il testo |
Parte 2: Integrazione dell'API REST | Chiamate API di Azure DevOps | Aggiungere la funzionalità dell'API REST per recuperare e visualizzare i dati |
Parte 3: Configurazione del widget | Personalizzazione utente | Implementare le opzioni di configurazione per il widget |
Suggerimento
Ignorare l'esercitazione: Scaricare l'estensione di esempio completa, passare alla widgets
cartella e passare al passaggio 6 per pubblicare tre widget di esempio pronti per l'uso.
Prima di iniziare, esaminare gli stili dei widget di base e le linee guida strutturali fornite.
Parte 1: Hello World
Creare un widget di base che visualizza "Hello World" usando JavaScript. Questa base illustra i concetti di base relativi allo sviluppo di widget.
Passaggio 1: Installare l'SDK client
VSS SDK consente al widget di comunicare con Azure DevOps. Installarlo con npm:
npm install vss-web-extension-sdk
Copiare il VSS.SDK.min.js
file da vss-web-extension-sdk/lib
nella home/sdk/scripts
cartella.
Per altre informazioni sull'SDK, vedere la pagina GitHub dell'SDK client.
Passaggio 2: Creare la struttura HTML
Crea hello-world.html
nella directory del progetto. Questo file fornisce il layout e i riferimenti del widget agli script necessari.
<!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>
I widget vengono eseguiti in iframe, quindi la maggior parte degli elementi head HTML tranne <script>
e <link>
vengono ignorati dal framework.
Passaggio 3: Aggiungere il widget JavaScript
Per implementare la funzionalità del widget, aggiungere questo script alla <head>
sezione del file HTML:
<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>
Componenti JavaScript principali
Funzione | Scopo |
---|---|
VSS.init() |
Inizializza la comunicazione tra widget e Azure DevOps |
VSS.require() |
Carica le librerie SDK necessarie e gli helper widget |
VSS.register() |
Registra il widget con un identificatore univoco |
WidgetHelpers.IncludeWidgetStyles() |
Applica lo stile predefinito di Azure DevOps |
VSS.notifyLoadSucceeded() |
Notifica al framework che il caricamento è stato completato correttamente |
Importante
Il nome del widget in VSS.register()
deve corrispondere a quello nel manifesto dell'estensione id
(Passaggio 5).
Passaggio 4: Aggiungere immagini di estensione
Creare le immagini necessarie per l'estensione:
-
Logo dell'estensione: immagine pixel 98x98 denominata
logo.png
nellaimg
cartella -
Icona del catalogo widget: immagine 98×98 pixel nella cartella denominata
CatalogIcon.png
img
-
Anteprima widget: immagine di 330x160 pixel denominata
preview.png
nellaimg
cartella
Queste immagini vengono visualizzate nel catalogo marketplace e widget quando gli utenti esplorano le estensioni disponibili.
Passaggio 5: Creare il manifesto dell'estensione
Crea vss-extension.json
nella directory radice del progetto. Questo file definisce i metadati e i contributi dell'estensione:
{
"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
}
]
}
Importante
Sostituire "publisher": "fabrikam"
con il nome effettivo dell'editore. Informazioni su come creare un editore.
Proprietà essenziali del manifesto
Sezione | Scopo |
---|---|
Informazioni di base | Nome dell'estensione, versione, descrizione e autore |
icone | Percorsi delle risorse visive dell'estensione |
Contributi | Definizioni di widget, tra cui ID, tipo e proprietà |
File | Tutti i file da includere nel pacchetto di estensione |
Per la documentazione completa del manifesto, vedere Informazioni di riferimento sul manifesto dell'estensione.
Passaggio 6: Creare un pacchetto e pubblicare l'estensione
Creare il pacchetto dell'estensione e pubblicarlo in Visual Studio Marketplace.
Installare lo strumento di creazione pacchetti
npm i -g tfx-cli
Creare il pacchetto di estensione
Dalla directory del progetto, esegui:
tfx extension create --manifest-globs vss-extension.json
Questa azione crea un .vsix
file che contiene l'estensione in pacchetto.
Configurare un server di pubblicazione
- Passare al portale di pubblicazione di Visual Studio Marketplace.
- Accedi e crea un publisher se non ne hai uno.
- Scegliere un identificatore di autore univoco (usato nel file manifesto).
- Aggiorna
vss-extension.json
per usare il tuo nome del publisher anziché "fabrikam".
Caricare l'estensione
- Nel portale di pubblicazione selezionare Carica nuova estensione.
- Scegliere il
.vsix
file e caricarlo. - Condividere l'estensione con l'organizzazione di Azure DevOps.
In alternativa, usare la riga di comando:
tfx extension publish --manifest-globs vss-extension.json --share-with yourOrganization
Suggerimento
Usare --rev-version
per incrementare automaticamente il numero di versione durante l'aggiornamento di un'estensione esistente.
Passaggio 7: Installare e testare il widget
Per testare, aggiungere il widget a un dashboard:
- Passare al progetto Azure DevOps:
https://dev.azure.com/{Your_Organization}/{Your_Project}
. - Passare a Panoramica>Dashboard.
- Seleziona Aggiungi un widget.
- Trovare il widget nel catalogo e selezionare Aggiungi.
Il widget "Hello World" viene visualizzato nel dashboard, visualizzando il testo configurato.
Passaggio successivo: Passare alla parte 2 per informazioni su come integrare le API REST di Azure DevOps nel widget.
Parte 2: Hello World con l'API REST di Azure DevOps
Estendere il widget per interagire con i dati di Azure DevOps usando le API REST. In questo esempio viene illustrato come recuperare le informazioni sulle query e visualizzarle in modo dinamico nel widget.
In questa parte, usare l'API REST per il monitoraggio degli elementi di lavoro per recuperare informazioni su una query esistente e visualizzare i dettagli della query sotto il testo "Hello World".
Passaggio 1: Creare il file HTML avanzato
Creare un nuovo file widget basato sull'esempio precedente. Copiare hello-world.html
e rinominarlo in hello-world2.html
. La struttura del progetto include ora:
|--- 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
Aggiornare la struttura HTML del widget
Apportare queste modifiche a hello-world2.html
:
-
Aggiungere un contenitore per i dati di query: includere un nuovo
<div>
elemento per visualizzare le informazioni sulle query. -
Aggiornare l'identificatore del widget: modificare il nome del widget da
HelloWorldWidget
a perHelloWorldWidget2
l'identificazione univoca.
<!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>
Passaggio 2: Configurare le autorizzazioni di accesso all'API
Prima di effettuare chiamate API REST, configurare le autorizzazioni necessarie nel manifesto dell'estensione.
Aggiungere l'ambito di lavoro
L'ambito vso.work
concede l'accesso in sola lettura agli elementi di lavoro e alle query. Aggiungi questo ambito a vss-extension.json
:
{
"scopes": [
"vso.work"
]
}
Esempio di manifesto completo
Per un manifesto completo con altre proprietà, strutturarlo come segue:
{
"name": "example-widget",
"publisher": "example-publisher",
"version": "1.0.0",
"scopes": [
"vso.work"
]
}
Importante
Limitazioni dell'ambito: l'aggiunta o la modifica degli ambiti dopo la pubblicazione non è supportata. Se l'estensione è già stata pubblicata, è necessario rimuoverla prima dal Marketplace. Passare al portale di pubblicazione di Visual Studio Marketplace, trovare l'estensione e selezionare Rimuovi.
Passaggio 3: Implementare l'integrazione dell'API REST
Azure DevOps fornisce librerie client REST JavaScript tramite l'SDK. Queste librerie eseguono il wrapping delle chiamate AJAX e mappano le risposte api agli oggetti utilizzabili.
Aggiornare il widget JavaScript
Sostituire la chiamata VSS.require
nel tuo hello-world2.html
per includere il client REST di tracciamento degli elementi di lavoro.
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();
});
Dettagli chiave dell'implementazione
Componente | Scopo |
---|---|
WorkItemTrackingRestClient.getClient() |
Ottiene un'istanza del client REST di rilevamento degli elementi di lavoro |
getQuery() |
Recupera le informazioni sulla query incapsulata in una promise |
WidgetStatusHelper.Failure() |
Fornisce una gestione coerente degli errori per i guasti del widget |
projectId |
Contesto di progetto corrente necessario per le chiamate API |
Suggerimento
Percorsi di query personalizzati: se non si ha una query "Feedback" in "Query condivise", sostituire "Shared Queries/Feedback"
con il percorso di qualsiasi query presente nel progetto.
Passaggio 4: Visualizzare i dati di risposta dell'API
Esegui il rendering delle informazioni della query nel tuo widget elaborando la risposta dell'API REST.
Aggiungere il rendering dei dati delle query
Sostituire il // Process query data
commento con questa implementazione:
// 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);
Il getQuery()
metodo restituisce un Contracts.QueryHierarchyItem
oggetto con proprietà per i metadati della query. In questo esempio vengono visualizzate tre informazioni chiave sotto il testo "Hello World".
Esempio di lavoro completo
Il file finale hello-world2.html
sarà simile al seguente:
<!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>
Passaggio 5: Aggiornare il manifesto dell'estensione
Per renderlo disponibile nel catalogo dei widget, aggiungere il nuovo widget al manifesto dell'estensione.
Aggiungere il secondo contributo del widget
Aggiornare vss-extension.json
per includere il widget abilitato per l'API REST. Aggiungere questo contributo alla contributions
matrice:
{
"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"
]
}
Suggerimento
Immagine di anteprima: creare un'immagine preview2.png
(330x160 pixel) e inserirla nella img
cartella per mostrare agli utenti l'aspetto del widget nel catalogo.
Passaggio 6: Creare un pacchetto, pubblicare e condividere
Creare un pacchetto, pubblicare e condividere l'estensione. Se l'estensione è già stata pubblicata, è possibile riassemblarla e aggiornarla direttamente nel Marketplace.
Passaggio 7: Testare il widget dell'API REST
Per visualizzare l'integrazione dell'API REST in azione, aggiungere il nuovo widget al dashboard:
- Passare al progetto Azure DevOps:
https://dev.azure.com/{Your_Organization}/{Your_Project}
. - Selezionare Panoramica>Dashboard.
- Seleziona Aggiungi un widget.
- Trovare "Hello World Widget 2 (con API)" e selezionare Aggiungi.
Il widget avanzato visualizza sia il testo "Hello World" che le informazioni sulle query in tempo reale del progetto Azure DevOps.
Passaggi successivi: passare alla parte 3 per aggiungere opzioni di configurazione che consentono agli utenti di personalizzare la query da visualizzare.
Parte 3: Configurare Hello World
Compilare la parte 2 aggiungendo funzionalità di configurazione utente al widget. Invece di impostare come hardcoded il percorso della query, creare un'interfaccia di configurazione che consente agli utenti di selezionare la query da visualizzare, con funzionalità di anteprima in tempo reale.
Questa parte illustra come creare widget configurabili che gli utenti possono personalizzare in base alle proprie esigenze specifiche fornendo feedback in tempo reale durante la configurazione.
Passaggio 1: Creare file di configurazione
Le configurazioni dei widget condividono molte analogie con i widget stessi, entrambi usano lo stesso SDK, la struttura HTML e i modelli JavaScript, ma servono scopi diversi all'interno del framework di estensione.
Configurare la struttura del progetto
Per supportare la configurazione del widget, creare due nuovi file:
- Copiare
hello-world2.html
e rinominarlo inhello-world3.html
, il widget configurabile. - Creare un nuovo file denominato
configuration.html
, che gestisce l'interfaccia di configurazione.
La struttura del progetto include ora:
|--- 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
Creare l'interfaccia di configurazione
Aggiungere questa struttura HTML a configuration.html
, che crea un selettore a discesa per la scelta delle query:
<!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>
Passaggio 2: Implementare la configurazione javaScript
Configuration JavaScript segue lo stesso modello di inizializzazione dei widget, ma implementa il IWidgetConfiguration
contratto anziché il contratto di base IWidget
.
Aggiungere la logica di configurazione
Inserire questo script nella <head>
sezione di 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>
Dettagli del contratto di configurazione
Il IWidgetConfiguration
contratto richiede queste funzioni chiave:
Funzione | Scopo | Quando viene chiamato |
---|---|---|
load() |
Inizializzare l'interfaccia utente di configurazione con le impostazioni esistenti | Quando si apre la finestra di dialogo di configurazione |
onSave() |
Serializzare l'input dell'utente e convalidare le impostazioni | Quando l'utente seleziona Salva |
Suggerimento
Serializzazione dei dati: questo esempio usa JSON per serializzare le impostazioni. Il widget accede a queste impostazioni tramite widgetSettings.customSettings.data
e deve deserializzarli di conseguenza.
Passaggio 3: Abilitare la funzionalità di anteprima in tempo reale
L'anteprima in tempo reale consente agli utenti di visualizzare immediatamente le modifiche dei widget quando modificano le impostazioni di configurazione, fornendo feedback istantaneo prima del salvataggio.
Implementare le notifiche delle modifiche
Per abilitare l'anteprima live, aggiungere questo gestore eventi all'interno della load
funzione :
$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);
});
File di configurazione completo
Il tuo configuration.html
finale dovrebbe apparire così:
<!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>
Importante
Abilita pulsante Salva: il framework richiede almeno una notifica di una modifica nella configurazione per abilitare il pulsante Salva. Il gestore eventi di modifica garantisce che questa azione venga eseguita quando gli utenti selezionano un'opzione.
Passaggio 4: Rendere il widget configurabile
Trasforma il tuo widget della Parte 2 per usare i dati di configurazione anziché i valori predefiniti. Questo passaggio richiede l'implementazione del IConfigurableWidget
contratto.
Aggiornare la registrazione del widget
In hello-world3.html
apportare queste modifiche:
-
Aggiornare l'ID del widget: passare da
HelloWorldWidget2
aHelloWorldWidget3
. -
Aggiungere la funzione di ricaricamento: implementare il
IConfigurableWidget
contratto.
return {
load: function (widgetSettings) {
// Set your title
var $title = $('h2.title');
$title.text('Hello World');
return getQueryInfo(widgetSettings);
},
reload: function (widgetSettings) {
return getQueryInfo(widgetSettings);
}
}
Gestire i dati di configurazione
Aggiornare la funzione getQueryInfo
per usare le impostazioni di configurazione anziché i percorsi di query codificati fissi:
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();
}
Differenze del ciclo di vita dei widget
Funzione | Scopo | Linee guida per l'utilizzo |
---|---|---|
load() |
Rendering iniziale del widget e configurazione monouso | Operazioni pesanti, inizializzazione delle risorse |
reload() |
Aggiornare il widget con la nuova configurazione | Aggiornamenti leggeri, aggiornamento dei dati |
Suggerimento
Ottimizzazione delle prestazioni: usare load()
per operazioni costose che devono essere eseguite una sola volta e reload()
per aggiornamenti rapidi in caso di modifiche alla configurazione.
(Facoltativo) Aggiungere un lightbox per informazioni dettagliate
I widget del dashboard hanno spazio limitato, rendendo difficile visualizzare informazioni complete. Un lightbox offre una soluzione elegante mostrando dati dettagliati in una sovrimpressione modale senza uscire dal dashboard.
Perché usare un lightbox nei widget?
Beneficio | Descrizione |
---|---|
Efficienza dello spazio | Mantenere il widget compatto offrendo visualizzazioni dettagliate |
Esperienza utente | Gestire il contesto del dashboard durante la visualizzazione di altre informazioni |
Divulgazione progressiva | Visualizzare i dati di riepilogo nel widget, i dettagli su richiesta |
progettazione reattiva | Adattarsi alle diverse dimensioni dello schermo e alle configurazioni dei widget |
Implementare elementi selezionabili
Aggiornare il rendering dei dati della query in modo da includere elementi selezionabili che attivano la 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);
Creare la funzionalità lightbox
Aggiungi questa implementazione lightbox al tuo widget JavaScript.
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');
}
});
}
Aggiungi lo stile del lightbox
Includi stili CSS per lightbox nella sezione HTML <head>
del widget:
<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>
Implementazione avanzata dei widget
Il widget avanzato completo con la funzionalità lightbox:
<!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>
Considerazioni sull'accessibilità: assicurarsi che la lightbox sia accessibile dalla tastiera e includa etichette appropriate per i lettori di schermo. Testare con le funzionalità di accessibilità predefinite di Azure DevOps.
Importante
Prestazioni: i lightbox devono essere caricati rapidamente. Considerare il caricamento differito dei dati dettagliati solo quando si apre la lightbox, invece di recuperare tutto in precedenza.
Passaggio 5: Configurare il manifesto dell'estensione
Registrare sia il widget configurabile che l'interfaccia di configurazione nel manifesto dell'estensione.
Aggiungere widget e contributi di configurazione
Aggiornamento vss-extension.json
per includere due nuovi contributi:
{
"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
}
]
}
Requisiti per i contributi di configurazione
Proprietà | Scopo | Valore obbligatorio |
---|---|---|
type |
Identifica il contributo come configurazione del widget | ms.vss-dashboards-web.widget-configuration |
targets |
Posizione in cui viene visualizzata la configurazione | ms.vss-dashboards-web.widget-configuration |
uri |
Percorso del file HTML di configurazione | Percorso del file di configurazione |
Modello di destinazione del widget
Per i widget configurabili, la targets
matrice deve includere un riferimento alla configurazione:
<publisher>.<extension-id>.<configuration-id>
Avviso
Visibilità del pulsante di configurazione: se il widget non ha come destinazione correttamente il contributo alla configurazione, il pulsante Configura non viene visualizzato. Verificare che i nomi del distributore e dell'estensione corrispondano esattamente al vostro manifesto.
Passaggio 6: Creare un pacchetto, pubblicare e condividere
Distribuire l'estensione avanzata con funzionalità di configurazione.
Se si tratta della prima pubblicazione, seguire il passaggio 6: Creare un pacchetto, pubblicare e condividere. Per le estensioni esistenti, creare un nuovo pacchetto e aggiornare direttamente nel Marketplace.
Passaggio 7: Testare il widget configurabile
Provare il flusso di lavoro di configurazione completo aggiungendo e configurando il widget.
Aggiungere il widget al dashboard
- Passare a
https://dev.azure.com/{Your_Organization}/{Your_Project}
. - Passare a Panoramica>Dashboard.
- Seleziona Aggiungi un widget.
- Trovare "Hello World Widget 3 (con config)" e selezionare Aggiungi.
Viene visualizzato un prompt di configurazione perché il widget richiede l'installazione:
Configurare il widget
Accedere alla configurazione tramite uno dei metodi seguenti:
- Menu widget: passare il puntatore del mouse sul widget, selezionare i puntini di sospensione (⋯), quindi Configura
- Modalità di modifica dashboard: selezionare Modifica nel dashboard, quindi il pulsante Configura nel widget
Il pannello di configurazione viene aperto con un'anteprima in tempo reale al centro. Selezionare una query nell'elenco a discesa per visualizzare gli aggiornamenti immediati, quindi selezionare Salva per applicare le modifiche.
Passaggio 8: Aggiungere opzioni di configurazione avanzate
Estendere il widget con funzionalità di configurazione predefinite, ad esempio nomi e dimensioni personalizzati.
Abilitare la configurazione del nome e delle dimensioni
Azure DevOps offre due funzionalità configurabili predefinite:
Caratteristica / Funzionalità | Proprietà del manifesto | Scopo |
---|---|---|
Nomi personalizzati | isNameConfigurable: true |
Gli utenti possono eseguire l'override del nome del widget predefinito |
Dimensioni multiple | Molteplici supportedSizes voci |
Gli utenti possono ridimensionare i widget |
Esempio di manifesto migliorato
{
"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"]
}
}
]
}
Visualizzare i nomi configurati
Per visualizzare i nomi dei widget personalizzati, aggiornare il widget per usare 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);
}
}
Dopo aver aggiornato l'estensione, è possibile configurare sia il nome del widget che le dimensioni:
Ripacchetto e aggiornare l'estensione per abilitare queste opzioni di configurazione avanzate.
Congratulazioni! È stato creato un widget completo e configurabile del dashboard di Azure DevOps con funzionalità di anteprima in tempo reale e opzioni di personalizzazione degli utenti.