Sdílet prostřednictvím


Přidání widgetu řídicího panelu

Azure DevOps Services | Azure DevOps Server | Azure DevOps Server 2022

Widgety se implementují jako příspěvky v rámci rozšíření. Jedno rozšíření může obsahovat více příspěvků widgetů. Tento článek ukazuje, jak vytvořit rozšíření, které poskytuje jeden nebo více widgetů.

Návod

Projděte si nejnovější dokumentaci k vývoji rozšíření pomocí sady SDK rozšíření Azure DevOps.

Návod

Pokud spouštíte nové rozšíření Azure DevOps, vyzkoušejte nejdřív tyto spravované ukázkové kolekce – pracují s aktuálními buildy produktů a pokrývají moderní scénáře (například přidávání karet na stránkách žádostí o přijetí změn).

Pokud ukázka ve vaší organizaci nefunguje, nainstalujte ji do osobní nebo testovací organizace a porovnejte cílové ID manifestu rozšíření a verze rozhraní API s aktuálními dokumenty. Referenční informace a rozhraní API najdete tady:

Požadavky

Požadavek Popis
Znalosti programování Znalosti JavaScriptu, HTML a CSS pro vývoj widgetů
Organizace Azure DevOps Vytvoření organizace
Textový editor Visual Studio Code používáme pro kurzy
Node.js Nejnovější verze Node.js
Multiplatformní CLI tfx-cli pro balení rozšíření
Instalace pomocí: npm i -g tfx-cli
Adresář projektu Domovský adresář s touto strukturou po dokončení kurzu:

|--- 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

Přehled kurzů

V tomto kurzu se naučíte vývoj widgetů prostřednictvím tří progresivních příkladů:

Část Soustředění Co se naučíte
Část 1: Hello World Vytvoření základního widgetu Vytvoření widgetu, který zobrazuje text
Část 2: Integrace rozhraní REST API Volání rozhraní API Azure DevOps Přidání funkcí rozhraní REST API pro načtení a zobrazení dat
Část 3: Konfigurace widgetu Přizpůsobení uživatele Implementace možností konfigurace pro widget

Návod

Pokud chcete přejít přímo na pracovní příklady, zahrnuté ukázky (viz předchozí poznámka) ukazují sadu widgetů, které můžete zabalit a publikovat.

Než začnete, projděte si základní styly widgetů a strukturální pokyny, které poskytujeme.

Část 1: Hello World

Vytvořte základní widget, který zobrazí "Hello World" pomocí JavaScriptu. Tento základ ukazuje základní koncepty vývoje widgetů.

Snímek obrazovky řídicího panelu Přehled s ukázkovým widgetem

Krok 1: Instalace klientské sady SDK

Sada VSS SDK umožňuje vaší widgetu komunikovat s Azure DevOps. Nainstalujte ho pomocí npm:

npm install vss-web-extension-sdk

Zkopírujte soubor VSS.SDK.min.js z vss-web-extension-sdk/lib do své složky home/sdk/scripts.

Další dokumentaci k sadě SDK najdete na stránce GitHub klientské sady SDK.

Krok 2: Vytvoření struktury HTML

Vytvořte hello-world.html v adresáři projektu. Tento soubor poskytuje rozložení widgetu a odkazy na požadované skripty.

<!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>

Widgety běží v elementech iframe, takže většina prvků v hlavičce HTML s výjimkou <script> a <link> je ignorována frameworkem.

Krok 3: Přidání widgetu JavaScriptu

Pokud chcete implementovat funkci widgetu, přidejte tento skript do <head> části souboru 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>

Klíčové komponenty JavaScriptu

Funkce Účel
VSS.init() Inicializuje komunikaci mezi widgetem a Azure DevOps.
VSS.require() Načítá požadované knihovny SDK a pomocné moduly widgetů
VSS.register() Zaregistruje widget s jedinečným identifikátorem.
WidgetHelpers.IncludeWidgetStyles() Použije výchozí styly Azure DevOps.
VSS.notifyLoadSucceeded() Upozorní rámec, že se načítání úspěšně dokončilo.

Důležité

Název VSS.register() widgetu se musí shodovat s manifestem id rozšíření (krok 5).

Krok 4: Přidání obrázků rozšíření

Vytvořte požadované image pro vaše rozšíření:

  • Logo rozšíření: 98x98 pixelový obrázek s názvem logo.png ve složce img
  • Ikona katalogu widgetů: obrázek 98x98 pixelů pojmenovaný CatalogIcon.png ve img složce
  • Náhled widgetu: obrázek 330 × 160 pixelů pojmenovaný preview.png ve img složce

Tyto obrázky se zobrazují v katalogu marketplace a widgetů, když uživatelé procházejí dostupná rozšíření.

Krok 5: Vytvoření manifestu rozšíření

Vytvořte vss-extension.json v kořenovém adresáři projektu. Tento soubor definuje metadata a příspěvky rozšíření:

{
    "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
        }
    ]
}

Důležité

Nahraďte "publisher": "fabrikam" skutečným názvem vydavatele. Zjistěte, jak vytvořit vydavatele.

Základní vlastnosti manifestu

Oddíl Účel
Základní informace Název rozšíření, verze, popis a vydavatel
ikony Cesty k vizuálním prvkům rozšíření
Příspěvky Definice widgetů včetně ID, typu a vlastností
Soubory Všechny soubory, které se mají zahrnout do balíčku rozšíření

Kompletní dokumentaci k manifestu najdete v referenčních informacích k manifestu rozšíření.

Krok 6: Zabalení a publikování rozšíření

Zabalte rozšíření a publikujte ho na Visual Studio Marketplace.

Instalace nástroje pro balení

npm i -g tfx-cli

Vytvoření balíčku rozšíření

V adresáři projektu spusťte:

tfx extension create --manifest-globs vss-extension.json

Tato akce vytvoří .vsix soubor, který obsahuje zabalenou příponu.

Nastavení vydavatele

  1. Přejděte na portál publikování na Webu Visual Studio Marketplace.
  2. Pokud ho nemáte, přihlaste se a vytvořte vydavatele.
  3. Zvolte jedinečný identifikátor vydavatele (použitý v souboru manifestu).
  4. Aktualizujte vss-extension.json tak, aby používalo jméno vašeho vydavatele místo "fabrikam".

Nahrajte rozšíření

  1. Na portálu publikování vyberte Nahrát nové rozšíření.
  2. Zvolte soubor .vsix a nahrajte ho.
  3. Sdílejte rozšíření s vaší organizací Azure DevOps.

Případně použijte příkazový řádek:

tfx extension publish --manifest-globs vss-extension.json --share-with yourOrganization

Návod

Slouží --rev-version k automatickému zvýšení čísla verze při aktualizaci existujícího rozšíření.

Krok 7: Instalace a otestování widgetu

Pokud chcete widget otestovat, přidejte ho na řídicí panel:

  1. Přejděte do projektu Azure DevOps: https://dev.azure.com/{Your_Organization}/{Your_Project}.
  2. Přejděte na Přehled>Řídicí panely.
  3. Vyberte Přidejte pomůcku.
  4. Vyhledejte widget v katalogu a vyberte Přidat.

Na řídicím panelu se zobrazí widget "Hello World", který zobrazuje text, který jste nakonfigurovali.

Další krok: Pokračujte 2. částí a zjistěte, jak integrovat rozhraní REST API Azure DevOps do widgetu.

Část 2: Hello World s využitím azure DevOps REST API

Rozšíření widgetu pro interakci s daty Azure DevOps pomocí rozhraní REST API Tento příklad ukazuje, jak načíst informace o dotazu a dynamicky je zobrazit ve widgetu.

V této části použijte rozhraní REST API pro sledování pracovních položek k načtení informací o existujícím dotazu a zobrazení podrobností dotazu pod textem Hello World.

Snímek obrazovky řídicího panelu Přehled s ukázkovým widgetem pomocí rozhraní REST API pro WorkItemTracking

Krok 1: Vytvoření rozšířeného souboru HTML

Vytvořte nový soubor widgetu, který vychází z předchozího příkladu. Zkopírujte a přejmenujte hello-world.html ho na hello-world2.html. Struktura projektu teď zahrnuje:

|--- 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

Aktualizace struktury HTML widgetu

Proveďte tyto změny na hello-world2.html:

  1. Přidání kontejneru pro data dotazu: Přidejte nový <div> prvek pro zobrazení informací o dotazu.
  2. Aktualizujte identifikátor widgetu: Změňte název widgetu z HelloWorldWidget na HelloWorldWidget2 pro jedinečnou identifikaci.
<!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>

Krok 2: Konfigurace přístupových oprávnění rozhraní API

Před voláním rozhraní REST API nakonfigurujte požadovaná oprávnění v manifestu rozšíření.

Přidání oboru práce

Rozsah vso.work uděluje přístup k pracovním položkám a dotazům pouze pro čtení. Přidejte tento obor do svého vss-extension.json:

{
    "scopes": [
        "vso.work"
    ]
}

Kompletní příklad manifestu

Úplný manifest s dalšími vlastnostmi strukturujte takto:

{
    "name": "example-widget",
    "publisher": "example-publisher", 
    "version": "1.0.0",
    "scopes": [
        "vso.work"
    ]
}

Důležité

Omezení oboru: Přidání nebo změna oborů po publikování se nepodporuje. Pokud jste rozšíření už publikovali, musíte ho nejprve odebrat z Marketplace. Přejděte na portál publikování na Webu Visual Studio Marketplace, vyhledejte rozšíření a vyberte Odebrat.

Krok 3: Implementace integrace rozhraní REST API

Azure DevOps poskytuje klientské knihovny REST jazyka JavaScript prostřednictvím sady SDK. Tyto knihovny zabalí volání AJAX a mapují odpovědi rozhraní API na použitelné objekty.

Aktualizace widgetu v JavaScriptu

Nahraďte v VSS.require volání hello-world2.html, abyste zahrnuli REST klienta pro sledování pracovních položek.

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();
    });

Klíčové podrobnosti implementace

Součást Účel
WorkItemTrackingRestClient.getClient() Získá instanci klienta rozhraní REST pro sledování položek práce.
getQuery() Načte informace o dotazu zabalené do příslibu.
WidgetStatusHelper.Failure() Poskytuje konzistentní zpracování chyb při selhání widgetu.
projectId Aktuální kontext projektu vyžadovaný pro volání rozhraní API

Návod

Vlastní cesty dotazů: Pokud nemáte v části Sdílené dotazy dotaz Feedback, nahraďte "Shared Queries/Feedback" cestu k jakémukoli dotazu, který v projektu existuje.

Krok 4: Zobrazení dat odpovědí rozhraní API

Vykreslete informace z dotazu ve vašem widgetu zpracováním odpovědi z REST API.

Přidání vykreslování dat dotazů

// Process query data Nahraďte komentář touto implementací:

// 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);

Metoda getQuery() vrátí Contracts.QueryHierarchyItem objekt s vlastnostmi pro metadata dotazu. Tento příklad zobrazuje tři klíčové informace pod textem "Hello World".

Příklad dokončení práce

Konečný hello-world2.html soubor by měl vypadat takto:

<!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>

Krok 5: Aktualizace manifestu rozšíření

Pokud ho chcete zpřístupnit v katalogu widgetů, přidejte nový widget do manifestu rozšíření.

Přidání druhého příspěvku widgetu

Aktualizujte vss-extension.json tak, aby zahrnoval widget s podporou rozhraní REST API. Přidejte tento příspěvek do contributions pole:

{
    "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"
    ]
}

Návod

Obrázek náhledu: Vytvořte preview2.png obrázek (330 × 160 pixelů) a umístěte ho do img složky, abyste uživatelům ukázali, jak váš widget vypadá v katalogu.

Krok 6: Balení, publikování a sdílení

Zabalte, publikujte a sdílejte své rozšíření. Pokud jste rozšíření už publikovali, můžete ho znovu zabalit a aktualizovat přímo na Marketplace.

Krok 7: Testování widgetu rozhraní REST API

Pokud chcete zobrazit integraci rozhraní REST API v akci, přidejte do řídicího panelu nový widget:

  1. Přejděte do projektu Azure DevOps: https://dev.azure.com/{Your_Organization}/{Your_Project}.
  2. Vyberte Přehled>Řídicí panely.
  3. Vyberte Přidejte pomůcku.
  4. Najděte "Hello World Widget 2 (s rozhraním API)" a vyberte Přidat.

Ve vylepšeném widgetu se zobrazí text "Hello World" i informace o živém dotazu z projektu Azure DevOps.

Další kroky: Pokračujte na část 3 a přidejte možnosti konfigurace, které uživatelům umožňují přizpůsobit, který dotaz se má zobrazit.

Část 3: Konfigurace Hello World

Navázat na část 2 přidáním možností konfigurace uživatele do widgetu. Místo pevného kódování cesty dotazu vytvořte konfigurační rozhraní, které uživatelům umožňuje vybrat dotaz, který se má zobrazit, s funkcemi živého náhledu.

Tato část ukazuje, jak vytvořit konfigurovatelné widgety, které si uživatelé můžou přizpůsobit podle svých konkrétních potřeb a současně poskytnout zpětnou vazbu v reálném čase během konfigurace.

Snímek obrazovky řídicího panelu Přehled s živým náhledem widgetu založeným na změnách

Krok 1: Vytvoření konfiguračních souborů

Konfigurace widgetů sdílejí mnoho podobností s vlastními widgety – používají stejnou sadu SDK, strukturu HTML i vzory JavaScriptu, ale v rámci architektury rozšíření slouží různým účelům.

Nastavení struktury projektu

Pokud chcete podporovat konfiguraci widgetu, vytvořte dva nové soubory:

  1. Zkopírujte ho a přejmenujte hello-world2.html na hello-world3.htmlkonfigurovatelný widget.
  2. Vytvořte nový soubor s názvem configuration.html, který zpracovává konfigurační rozhraní.

Struktura projektu teď zahrnuje:

|--- 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

Vytvoření konfiguračního rozhraní

Přidejte tuto strukturu HTML do configuration.html, která vytvoří selektor rozevíracího seznamu pro výběr dotazů:

<!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>

Krok 2: Implementace konfigurace JavaScriptu

Konfigurace JavaScriptu se řídí stejným vzorem inicializace jako widgety, ale implementuje IWidgetConfiguration kontrakt místo základního IWidget kontraktu.

Přidejte logiku konfigurace

Vložte tento skript do <head> části 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>

Podrobnosti o konfiguraci smlouvy

Kontrakt IWidgetConfiguration vyžaduje tyto klíčové funkce:

Funkce Účel Při zavolání
load() Inicializujte konfigurační uživatelské rozhraní pomocí existujících nastavení Po otevření dialogového okna konfigurace
onSave() Serializace uživatelského vstupu a ověření nastavení Když uživatel vybere Možnost Uložit

Návod

Serializace dat: V tomto příkladu se k serializaci nastavení používá JSON. Widget k těmto nastavením přistupuje prostřednictvím widgetSettings.customSettings.data a musí je odpovídajícím způsobem deserializovat.

Krok 3: Povolení funkce živého náhledu

Dynamický náhled umožňuje uživatelům zobrazit změny widgetu okamžitě při úpravě nastavení konfigurace a poskytnout okamžitou zpětnou vazbu před uložením.

Implementace oznámení o změnách

Pokud chcete povolit dynamický náhled, přidejte tuto obslužnou rutinu události do funkce 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);
});

Dokončení konfiguračního souboru

Váš configuration.html by měl vypadat takto:

<!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>

Důležité

Povolit tlačítko Uložit: Rozhraní vyžaduje alespoň jedno oznámení o změně konfigurace, aby bylo možné povolit tlačítko Uložit . Obslužná rutina události změny zajistí, že k této akci dojde, když uživatelé vyberou možnost.

Krok 4: Nastavení konfigurovatelného widgetu

Transformujte widget z části 2 tak, aby místo pevně zakódovaných hodnot používal konfigurační data. Tento krok vyžaduje implementaci kontraktu IConfigurableWidget .

Aktualizace registrace widgetu

V hello-world3.htmlaplikaci proveďte tyto změny:

  1. Aktualizovat ID widgetu: Změna z HelloWorldWidget2 na HelloWorldWidget3.
  2. Přidání funkce pro opětovné načtení: Implementujte IConfigurableWidget kontrakt.
return {
    load: function (widgetSettings) {
        // Set your title
        var $title = $('h2.title');
        $title.text('Hello World');

        return getQueryInfo(widgetSettings);
    },
    reload: function (widgetSettings) {
        return getQueryInfo(widgetSettings);
    }
}

Zpracování konfiguračních dat

Aktualizujte funkci tak getQueryInfo , aby místo pevně zakódovaných cest dotazů používala nastavení konfigurace:

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();
}

Rozdíly životního cyklu widgetu

Funkce Účel Pokyny k používání
load() Počáteční vykreslování widgetů a jednorázové nastavení Náročné operace, inicializace prostředků
reload() Aktualizace widgetu s novou konfigurací Zjednodušené aktualizace, aktualizace dat

Návod

Optimalizace výkonu: Používejte load() pro nákladné operace, které stačí spustit jen jednou, a reload() rychlé aktualizace při změnách konfigurace.

(Volitelné) Přidání lightboxu pro podrobné informace

Widgety řídicího panelu mají omezený prostor, což ztěžuje zobrazení komplexních informací. Lightbox poskytuje elegantní řešení zobrazením podrobných dat v modální překryvné oblasti, aniž byste museli přecházet z řídicího panelu.

Proč používat lightbox ve widgetech?

Prospěch Popis
Prostorová efektivita Zachování kompaktního widgetu při nabízení podrobných zobrazení
Uživatelské prostředí Zachování kontextu řídicího panelu při zobrazení dalších informací
Postupné odhalení Zobrazení souhrnných dat ve widgetu, podrobnosti na vyžádání
Responzivní návrh Přizpůsobení různých velikostí obrazovek a konfigurací widgetů

Implementace klikatelných elementů

Aktualizujte vykreslování dat dotazů tak, aby zahrnovalo kliknutelné prvky, které aktivují 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);

Vytvoření funkce lightboxu

Přidejte tuto implementaci lightboxu do JavaScriptu widgetu:

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">&times;</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');
        }
    });
}

Přidání stylu lightboxu

Do oddílu HTML <head> widgetu zahrňte styly CSS pro lightbox:

<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>

Vylepšená implementace widgetu

Kompletní vylepšený widget s funkcemi lightboxu:

<!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>

Aspekty přístupnosti: Ujistěte se, že lightbox je přístupný z klávesnice a obsahuje správné popisky pro čtečky obrazovky. Testování pomocí integrovaných funkcí přístupnosti v Azure DevOps

Důležité

Výkon: Lightboxes by se měly načíst rychle. Zvažte načítání podrobných dat metodou lazy-loading pouze tehdy, když se lightbox otevře, místo načítání všeho předem.

Krok 5: Konfigurace manifestu rozšíření

Zaregistrujte v manifestu rozšíření konfigurovatelný widget i jeho konfigurační rozhraní.

Přidání widgetu a příspěvků ke konfiguraci

Aktualizace vss-extension.json pro zahrnutí dvou nových příspěvků:

{
    "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
                    },
                    {
                        "rowSpan": 2,
                        "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
        }
    ]
}

Požadavky na přispění k nastavení konfigurace

Vlastnost Účel Požadovaná hodnota
type Identifikuje příspěvek jako konfiguraci widgetu. ms.vss-dashboards-web.widget-configuration
targets Kde se zobrazí konfigurace ms.vss-dashboards-web.widget-configuration
uri Cesta ke konfiguračnímu souboru HTML Cesta ke konfiguračnímu souboru

Model cílení widgetů

Pro konfigurovatelné widgety targets musí pole obsahovat odkaz na konfiguraci:

<publisher>.<extension-id>.<configuration-id>

Upozorňující

Viditelnost tlačítka konfigurace: Pokud widget nemá správný cíl na svůj příspěvek konfigurace, tlačítko Konfigurovat se nezobrazí. Ověřte, že názvy vydavatelů a přípon přesně odpovídají vašemu manifestu.

Krok 6: Balení, publikování a sdílení

Nasaďte rozšířené rozšíření s možnostmi konfigurace.

Pokud se jedná o vaši první publikaci, postupujte podle kroku 6: Zabalení, publikování a sdílení. U existujících rozšíření znovu zabalte a aktualizujte je přímo na Marketplace.

Krok 7: Otestování konfigurovatelného widgetu

Vyzkoušejte si úplný pracovní postup konfigurace přidáním a konfigurací widgetu.

Přidání widgetu na řídicí panel

  1. Přejděte na https://dev.azure.com/{Your_Organization}/{Your_Project} .
  2. Přejděte na Přehled>Řídicí panely.
  3. Vyberte Přidejte pomůcku.
  4. Najděte "Hello World Widget 3 (s konfigurací)" a vyberte Přidat.

Zobrazí se výzva k konfiguraci, protože widget vyžaduje nastavení:

Snímek obrazovky panelu Přehled s ukázkovým widgetem z katalogu

Konfigurace widgetu

Konfigurace přístupu prostřednictvím jedné z metod:

  • Nabídka widgetu: Najeďte myší na widget, vyberte tři tečky (⋯) a pak nakonfigurujte
  • Režim úprav řídicího panelu: Vyberte Upravit na řídicím panelu a pak na widgetu tlačítko Konfigurovat.

Otevře se konfigurační panel s živým náhledem uprostřed. Výběrem dotazu z rozevíracího seznamu zobrazíte okamžité aktualizace a pak výběrem možnosti Uložit změny použijete.

Krok 8: Přidání rozšířených možností konfigurace

Rozšiřte widget o více integrovaných funkcí konfigurace, jako jsou vlastní názvy a velikosti.

Povolení konfigurace názvu a velikosti

Azure DevOps nabízí dvě konfigurovatelné funkce:

Vlastnost Vlastnost manifestu Účel
Vlastní názvy isNameConfigurable: true Uživatelé můžou přepsat výchozí název widgetu.
Více velikostí Více supportedSizes položek Uživatelé můžou měnit velikost widgetů

Příklad rozšířeného manifestu

{
    "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"]
             }
         }
    ]
}

Zobrazení nakonfigurovaných názvů

Pokud chcete zobrazit vlastní názvy widgetů, aktualizujte widget tak, aby používal 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);
    }
}

Po aktualizaci rozšíření můžete nakonfigurovat název i velikost widgetu:

Snímek obrazovky znázorňující, kde je možné nakonfigurovat název a velikost widgetu

Znovu zabalte a aktualizujte rozšíření, abyste povolili tyto pokročilé možnosti konfigurace.

Gratulujeme! Vytvořili jste kompletní konfigurovatelný widget řídicího panelu Azure DevOps s možnostmi živého náhledu a možnostmi přizpůsobení uživatelů.