Menu déroulant configurable

1. DESCRIPTION GÉNÉRALE

L'administrateur peut créer des éléments d'interface utilisateur de type « Dropdown » (menu déroulant) avec un comportement personnalisé, et les ajouter aux vues. La personnalisation consiste en ce que l'administrateur peut définir, sous la forme d'une requête v2 API, la règle selon laquelle le menu déroulant génère la liste des valeurs qu'il affiche lorsque l'utilisateur clique dessus.

Le cas d'utilisation typique du menu déroulant configurable est celui d'un « sélecteur dépendant ». Cela signifie disposer d'un menu déroulant qui affiche une liste limitée des valeurs du champ concerné, en fonction des valeurs définies pour d'autres champs de la même entité. Cela évite à l'utilisateur d'avoir à faire défiler une longue liste de toutes les valeurs possibles.

Exemple 1 : lorsque l'utilisateur associe une fonctionnalité à un incrément de programme (PI), il souhaite que le menu déroulant « Incrément de programme » ne répertorie que les PI du train de livraison agile (ART) particulier attribué à cette fonctionnalité. L'utilisateur ne souhaite pas faire défiler et deviner parmi tous les PI des ART.

Exemple 2 : lorsque l'utilisateur associe un élément de travail à un résultat clé, il souhaite que le menu déroulant « Résultat clé » n'affiche que les résultats clés de l'objectif auquel cet élément de travail est associé. L'utilisateur ne souhaite pas faire défiler tous les résultats clés dans le système.

2. COMMENT CONFIGURER ET AJOUTER DES MENUS DÉROULANTS

2.1. Qu'est-ce qu'une configuration?

Un cas particulier de personnalisation du comportement du menu déroulant est appelé « configuration » du menu déroulant. Le menu déroulant configurable prend actuellement en charge, et toutes ses configurations peuvent être utilisées dans :

  1. Vues détaillées, en tant qu'élément de contrôle déroulant,
  2. Vues de liste et listes internes, en tant qu'unité.

Chaque configuration peut être associée à un seul champ d'un type d'entité (types). Ce champ peut être un lien vers une entité par défaut (domaine natif), un lien vers une entité de domaine extensible ou un champ personnalisé de type « Entité Targetprocess ».

Une configuration est en fait un morceau de code, une requête adressée à l'API Configurable Controls qui contient un ensemble d'attributs. Les attributs sont expliqués dans la section Attributs de configuration.

2.2. Création d'une configuration déroulante

Une configuration peut être créée à l'aide d'un mashup.

Créons un exemple de configuration pour résoudre le problème de l'exemple 1 ci-dessus.

Avec le modèle de données SAFe, notre compte dispose, entre autres, des types d'entités suivants :

  1. Agile Release Train, ou ART. Il s'agit essentiellement d'une « équipe d'équipes ». Il est implémenté en tant que type d'entité de domaine extensible et est le parent du type d'entité Équipe.
  2. Incrément de programme, ou PI. Il s'agit essentiellement d'une itération du travail d'un ART, c'est-à-dire d'une période liée à un ART. Il s'agit d'une version renommée, qui est un type d'entité par défaut.

Par défaut, le menu déroulant « Incrément de programme » affiche une liste des PI de tous les ART présents dans le compte, même si un ART particulier a été attribué à l'entité en question. Il peut être difficile pour l'utilisateur de parcourir toutes les informations personnelles, dont la plupart ne sont pas pertinentes. Cette capture d'écran illustre le problème :

Le menu déroulant affiche une liste des PI de tous les ART présents dans le compte Les PI de tous les ART sont disponibles dans le menu déroulant Program Increment (vue Liste)

Ajoutons maintenant un mashup avec une configuration qui atténue le problème – voir le code ci-dessous. Cette configuration fait en sorte que le menu déroulant du champ « Incrément de programme » n'affiche que les incréments de programme de l'ART attribué à cette entité particulière (par exemple, une fonctionnalité). En d'autres termes, cela évite à l'utilisateur d'avoir à sélectionner parmi les PI de toutes les ART du compte.

Cette configuration trie également la liste des PI dans le menu déroulant par nom, dans l'ordre décroissant. Compte tenu des conventions de dénomination de ce compte de démonstration particulier, cela signifie que les PI les plus récents apparaîtront en haut de la liste déroulante.

Exemple de code :

tau.mashups
    .addDependency('tau/api/configurable-controls/controls/v1')
    .addMashup(( controlsApiV1 ) => {
        controlsApiV1.addConfiguration('dropdown', {
          id: 'dd_conf_01',
		  name: 'Program Increments of the assigned ART',
          label: 'Program Increment',
          supportedEntityTypes: ['feature'],
          requiredEntityFields: ['id', 'name', 'release'],
          sampleData: {
            release: { name: 'Program Increment' }
          },
          entityClickBehavior: 'openEntity',
          field: 'release',
          dropdownItemsSource: {
            type: 'interpolatedQuery',
            query: "release?where=(agileReleaseTrain.id==${{agileReleaseTrain.id}}$)&orderBy=name desc"
          }
        })
    })
Lors de la configuration du mashup, dans le champ « Placeholder(s) », vous pouvez utiliser n'importe quel espace réservé global, par exemple

footerPlaceholder

Une fois le mashup ajouté, vous pouvez utiliser cette configuration du menu déroulant dans les vues. Notez que vous pouvez ajouter autant de configurations que vous le souhaitez dans un seul mashup. (Pour en savoir plus sur le fonctionnement de ce mashup, consultez la section Attributs de configuration.)

2.3. Ajout d'une configuration déroulante aux vues détaillées

Sur la page Éditeur de mise en page détaillée, vous trouverez un extrait de la configuration nécessaire à utiliser dans votre mise en page : Sur la page Éditeur de mise en page détaillée, vous trouverez un extrait de la configuration nécessaire à utiliser dans votre mise en page. Cet extrait doit être ajouté en tant que nouveau composant de propriété ou placé à la place d'un composant de propriété existant dans la mise en page détaillée du type d'entité cible : Remplacement d'un composant de propriété par défaut par une configuration déroulante personnalisée dans les vues détaillées. Désormais, dans les vues détaillées, notre exemple de liste déroulante Program Increment n'affiche que les PI de l'ART attribués à l'entité : Le menu déroulant « Program Increment » (Incrément de programme) affiche uniquement les PI de l'ART attribué à l'entité

2.4. Ajouter une configuration déroulante à une vue de liste

Dans les vues Liste, la configuration du menu déroulant est reflétée dans une unité personnalisée pour le champ cible. Ajoutez cette unité personnalisée à la configuration de l'affichage dans l'onglet « Personnaliser les cartes » (vous souhaiterez probablement remplacer l'unité par défaut) : Modification de l'unité par défaut « Incrément de programme » en une unité configurée dans une vue Liste. Désormais, dans cette vue Liste, le menu déroulant Exemple d'incrément de programme n'affiche que les PI de l'ART attribué à cette entité particulière : Seuls les PI de l'ART sélectionné sont disponibles dans le menu déroulant (vue Liste)

2.5. Ajouter une configuration déroulante à l'ajout rapide

Pour ajouter des champs déroulants configurables dans Quick Add, la première étape consiste à créer une configuration. Assurez-vous que "allowedLocations" contient "quickAdd" (voir les attributs de configuration ci-dessous) ou n'est pas défini du tout.
tau.mashups
    .addDependency('tau/api/configurable-controls/controls/v1')
    .addMashup(( controlsApiV1 ) => {
        controlsApiV1.addConfiguration('dropdown', {
          id: 'dd_conf_01',
          name: 'Program Increments of the assigned ART',
          label: 'Program Increment',
          supportedEntityTypes: ['feature'],
          requiredEntityFields: ['id', 'name', 'release'],
          allowReset:false,
          sampleData: {
            release: { name: 'Program Increment' }
          },
          entityClickBehavior: 'openEntity',
          field: 'release',
          dropdownItemsSource: {
            type: 'custom',
            getItems: async ({entity}) => {
                const artId = entity.agilereleasetrain || (entity.agileReleaseTrain && entity.agileReleaseTrain.id) || null
                const response = await fetch(___PROTECTED_5___);
                const {items} = await response.json();
                return items;
              }
            //type: 'interpolatedQuery',
            //query: "release?where=(agileReleaseTrain.id==${{agileReleaseTrain.id}}$)&orderBy=name desc"
            }
        })
    })
Allez maintenant dans « Paramètres », sélectionnez « Ajout rapide » sur le côté gauche et choisissez l'entité souhaitée. Cette image montre les paramètres, puis le choix de « Quick Add » (Ajout rapide) sur le côté gauche et la sélection de l'entité souhaitée. Choisissez « Ajouter un champ » et sélectionnez le champ précédemment configuré. Tous les champs déroulants configurables seront visibles en bas. Cette image montre la recherche rapide de fonctionnalités Après avoir sélectionné un champ, celui-ci sera ajouté à la liste des champs configurés. Le drapeau « Requis » est contrôlé par "allowReset" le drapeau dans la configuration. Si \"allowReset\" n'est pas défini ou est défini sur « true », le champ ne sera pas obligatoire. Ouvrez maintenant la fenêtre contextuelle Ajout rapide en cliquant sur le bouton dans le coin supérieur droit et sélectionnez l'entité que vous avez configurée. Vous devriez voir un champ déroulant configurable dans la liste des champs d'entité. Cette image montre l'ouverture de la fenêtre contextuelle Ajout rapide en cliquant sur le bouton dans le coin supérieur droit et en sélectionnant l'entité que vous avez configurée.

3. ATTRIBUTS DE CONFIGURATION

Tous les attributs possibles d'une configuration d'un menu déroulant sont répertoriés et expliqués ci-dessous.

3.1. <b>id</b>

Obligatoire ? Oui Identifiant du texte spécifié par le créateur du mashup. Doit être unique parmi les contrôles du même type (c'est-à-dire « liste déroulante »). Il est recommandé d'utiliser des lettres minuscules pour le paramètre ID.

3.2. <b>nom</b>

Obligatoire ? Non. Nom convivial du texte. Utilisez ceci pour aider les gens à comprendre ce que fait cette configuration.

3.3. <b>label</b>

Obligatoire ? Oui Texte affiché comme libellé du menu déroulant dans les vues détaillées et comme en-tête de colonne dans les listes.

3.4. <b>supportedEntityTypes</b>

Obligatoire ? Non. Noms des types d'entités Targetprocess (en minuscules) qui prennent en charge le rendu de ce contrôle particulier. Si aucune n'est fournie, le contrôle est considéré comme universel. Exemple d'extrait : supportedEntityTypes: ['userstory', 'bug']

3.5. <b>allowedLocations</b>

Obligatoire ? Non. Permet d'utiliser cette configuration déroulante uniquement dans certains types d'affichage, par exemple uniquement dans les vues détaillées, mais pas dans les listes. Exemple d'extrait :
allowedLocations: {
  listUnit: false,
  detailedView: true,
  quickAdd: true,
}
Par défaut, une configuration peut être utilisée dans tous les types d'affichage actuellement pris en charge par le menu déroulant configurable.

3.6. <b>requiredEntityFields</b>

Obligatoire ? Non (Oui - pour le champ personnalisé Entité). Liste des champs qui doivent être disponibles dans “ConfigurableControlInfo” (voir ci-dessous) et qui doivent être demandés pour que ce contrôle puisse s'afficher. Les champs peuvent appartenir à l'entité même avec laquelle cette configuration déroulante sera utilisée (l'entité « cible »), ainsi qu'aux entités qui y sont liées. Les champs sont regroupés dans un seul sélecteur v2 API, il est donc possible d'utiliser des noms de champs (par exemple « effort ») ou des sélecteurs v2 API alias (par exemple "userStoryId:userStory «.id » – un identifiant d'une User Story liée à l'entité cible). Les noms de champs doivent être écrits en camel case. E.g. userStory. Exemple d'extrait :
requiredEntityFields: [
    'id', 
    'name', 
    'feature:{id:feature.id,name:feature.name,projectId:feature.project.id}', 
    'tasks:tasks.select({id,name})',
    'customFieldName: {id:CustomValues.Entity("Custom Field Name").id, name:CustomValues.Entity("Custom Field Name").name, resourceType:"entityTypeName"}'
]

3.7. <b>sampleData</b>

Obligatoire ? Non. Liste des valeurs fictives pour tous les champs requis par l'attribut « Champs d'entité obligatoires ». Ces valeurs fictives seront utilisées pour afficher le contrôle dans tous les éditeurs (tels que Personnaliser les cartes) où les données d'entité ne sont pas disponibles, afin d'afficher un aperçu significatif. Exemple d'extrait :
sampleData: {
  id: 5,
  name: 'Some name',
  feature: {id: 10, name: 'Some feature name', projectId: 50},
  tasks: [
    {id: 18, name: 'Task 1'},
    {id: 19, name: 'Task 2'}
  ]
}

3.8. <b>isEnabled</b>

Obligatoire ? Non. Une fonction de rappel qui consiste en une vérification particulière visant à déterminer si le contrôle doit être activé ou désactivé (c'est-à-dire en lecture seule). Modèle :
isEnabled?: {
    callback: (info: ConfigurableControlInfo) => boolean | PromiseLike<boolean>;
    rerunOnRequiredFieldsChanged: string[];
  }
Exemple d'extrait 1 – le contrôle n'est activé que si le nom de l'entité cible commence par un « S » ou un « s » :
isEnabled: {
    callback: info => info.entity.name.toLowerCase().startsWith('s'),
    rerunOnRequiredFieldsChanged: ['name']
}
Exemple d'extrait 2 – le contrôle est toujours désactivé (lecture seule) :
isEnabled: { 
    callback: () => false, 
    rerunOnRequiredFieldsChanged: [] 
}
Le rappel est exécuté a) avant l'initialisation du contrôle, et b) lorsqu'un des champs de l'entité répertoriés dans "rerunOnRequiredFieldsChanged" est mis à jour. Lors du rendu initial, si la fonction de rappel renvoie une valeur de type promesse, le contrôle sera désactivé et restera désactivé jusqu'à ce que la valeur de type promesse soit résolue avec une valeur vraie; si elle est rejetée ou résolue avec une valeur fausse, le contrôle restera désactivé. Lors d'un nouveau rendu du menu déroulant, la "rerunOnRequiredFieldsChanged" collection sera vérifiée par rapport aux champs présents dans requiredEntityFields et qui ont été mis à jour. Si l'un d'entre eux a été modifié, le rappel est exécuté. Si le rappel renvoie une promesse, pour des raisons d'expérience utilisateur, le contrôle conserve visuellement (mais pas fonctionnellement) son état « désactivé » précédent jusqu'à ce que la promesse soit résolue, afin d'éviter tout clignotement à chaque modification. REMARQUE : lorsque vous enregistrez le contrôle en tant qu'unité (c'est-à-dire dans les listes), ce rappel sera exécuté pour chaque carte visible. Ainsi, si le rappel effectue un travail lourd et/ou asynchrone pour vérifier la visibilité (comme une requête réseau), cela peut avoir un impact négatif sur les performances lorsqu'il est utilisé comme une unité. Si "rerunOnRequiredFieldsChanged" n'est pas fourni ou est vide, la fonction de rappel ne sera exécutée que pour le rendu initial. Si isEnabled n'est pas fourni, le contrôle est toujours activé.

3.9. <b>entityClickBehavior</b>

Obligatoire ? Oui Détermine ce qui se passe lorsque le menu déroulant n'est pas ouvert et que l'utilisateur clique sur le nom de l'entité sélectionnée pour ce champ. "openDropdown" ignore le lien vers l'entité et ouvre le menu déroulant; "openEntity" ouvre la vue détaillée de l'entité. Exemple d'extrait : entityClickBehavior: 'openEntity'

3.10. <b>champ</b>

Obligatoire ? Oui Nom du champ de l'entité pour lequel le menu déroulant doit être utilisé. Pour les champs personnalisés, le nom du champ personnalisé est spécifié. Pour les champs non personnalisés, l'un des champs présents dans requiredEntityFields est spécifié. Le comportement sur le terrain doit satisfaire aux conditions suivantes :
  1. C'est null lorsqu'aucune valeur n'est définie.
  2. Il comporte les paramètres idname et (tous deux obligatoires), ainsi qu'un paramètre resourceType facultatif qui sert à produire la couleur correcte de l'étiquette de l'entité.

Le champ sera utilisé à la fois pour afficher la valeur et pour la mettre à jour lorsqu'une nouvelle option du menu déroulant sera sélectionnée.

Le nom du champ doit être écrit en camel case. E.g. userStory.

3.11. <b>allowReset</b>

Obligatoire ? Non. Attribut booléen qui détermine si la liste déroulante prend en charge une valeur vide. Est « vrai » par défaut.

3.12. <b>dropdownItemsSource</b>

Obligatoire ? Oui Sous-ensemble d'attributs qui détermine la provenance des éléments de la liste déroulante. Les éléments peuvent être fournis soit par une requête interpolée, soit par une fonction personnalisée. Modèle :
dropdownItemsSource:
    | {
        type: 'interpolatedQuery';
        query: string;
      }
    | {
        type: 'custom';
        getItems: (info: ConfigurableControlInfo) => PromiseLike<{id: any; name: string}[]>;
      };
Une requête interpolée est une requête modélisée adressée à l'API v2. Il peut référencer les champs de l'entité cible à l'aide d'une syntaxe de modèle : ${{}}$ (signe dollar, double accolade ouverte, expression, double accolade fermée, signe dollar), par exemple : "feature?where=(epic.id=='${{epic.id}}$)" Ce qui signifie « afficher les fonctionnalités liées à la même épopée que celle à laquelle cette entité cible est liée ». Dans le cas du type: 'custom' option, les éléments de la liste sont fournis par une fonction personnalisée qui prend en entrée les informations sur l'état du menu déroulant (y compris les requiredEntityFields valeurs) et renvoie de manière asynchrone un tableau d'objets avec les champs « id » et « name ». En principe, cette fonction personnalisée peut envoyer des requêtes vers n'importe quelle destination ou même renvoyer certaines données codées en dur. Exemple :
getItems: async info => {
  const response = await fetch(___PROTECTED_17___);
  const items = await response.json();
  return items;
}
Cette fonction personnalisée envoie une requête à un service particulier pour obtenir un tableau d'entités qui satisfont une condition particulière, puis renvoie le tableau.

3.13. <b>showSearch</b>

Obligatoire ? Non. Attribut booléen qui détermine si le menu déroulant effectue une recherche en permanence. Est « faux » par défaut. Si cette option n'est pas activée, le menu déroulant permet de rechercher plus de 10 éléments.

4. BIBLIOTHÈQUE DE CONFIGURATIONS

Cette section regroupe des exemples utiles de configuration de menus déroulants provenant de comptes clients réels. Vous pouvez utiliser ces exemples et les modifier pour créer vos nouvelles configurations. De nouveaux exemples utiles seront ajoutés ici dès qu'ils seront disponibles.

4.1. Afficher les flux de valeur pertinents pour les ART attribués

Avec cette configuration, le menu déroulant « Flux de valeur » n'affichera que les flux de valeur pertinents pour les ART attribués à l'entité cible. Si aucun ART n'est attribué, le menu déroulant affiche tous les flux de valeur du compte.
tau.mashups
    .addDependency('tau/api/configurable-controls/controls/v1')
    .addMashup(controlsApiV1 => {
        controlsApiV1.addConfiguration('dropdown', {
            id: 'feature_valuestream_filtered_by_assigned_art',
            name: 'Value Stream filtered by ART',
            label: 'Value Stream',
            supportedEntityTypes: ['feature'],
            requiredEntityFields: ['valueStream'],
            sampleData: {
                valueStream: { resourceType: 'valuestream', name: 'Value Stream' }
            },
            entityClickBehavior: 'openDropdown',
            field: 'valueStream',
            dropdownItemsSource: {
                type: 'interpolatedQuery',
                query: "valuestream?where=(valueStreamAgileReleaseTrains.count(${{agileReleaseTrain.id}}$ == null or agileReleaseTrain.id == ${{agileReleaseTrain.id}}$) > 0)&take=999"
            }
        })
    })

4.2. Afficher les incréments du programme avec leurs projets correspondants

Avec cette configuration, le menu déroulant Program Increment (ou Release) affichera sa liste de valeurs possibles, chacune d'entre elles étant associée au nom de l'entité Project correspondante. Ceci est mis en œuvre à l'aide d'une source d'éléments personnalisée, car une requête interpolée ne fonctionnerait pas. Notez que la liste ne contiendra que les incréments de programme actuellement en cours d'exécution (via la &where=(IsCurrent == True) condition). Cette image montre les listes déroulantes PI avec les noms des projets correspondants.
tau.mashups
    .addDependency('tau/api/configurable-controls/controls/v1')
    .addDependency('tau/configurator')
    .addMashup(( controlsApiV1, configurator ) => {
        const store2 = configurator.getStore2();
        controlsApiV1.addConfiguration('dropdown', {
          id: 'dd_conf_03',
          name: 'PI with Project',
          label: 'PI w/ Project',
          supportedEntityTypes: ['teamiteration'],
          requiredEntityFields: ['release'],
          sampleData: {
            release: {name: 'Program Increment', resourceType: 'release'}
          },
          entityClickBehavior: 'openEntity',
          field: 'release',
          dropdownItemsSource: {
            type: 'custom',
            getItems: async () => {
                const releasesWithProjects = await store2.findAll('release?select={id,name,projectName:project.name}&where=(IsCurrent == True)&orderBy=name desc');
                return releasesWithProjects.map(release => ({
                    id: release.id,
                    name: ___PROTECTED_19___
                }));
            }
          }
        });
    })

4.3. Afficher uniquement les utilisateurs appropriés et disponibles pour une répartition du travail

Cette configuration limite la liste déroulante des utilisateurs connectés dans une entité d'attribution de travail uniquement aux utilisateurs qui :
  1. avoir au moins une compétence requise dans cette répartition du travail;
  2. disposer de disponibilités dans toutes les périodes où cette répartition du travail comporte des demandes, et ces disponibilités doivent avoir une capacité égale ou supérieure à l'ETP requis par cette répartition du travail.

En d'autres termes, le menu déroulant n'affichera que les utilisateurs qui possèdent les compétences requises et disposent d'une capacité suffisante non allouée pendant les périodes nécessaires.

tau.mashups
    .addDependency('tau/api/configurable-controls/controls/v1')
    .addMashup(( controlsApiV1 ) => {
        controlsApiV1.addConfiguration('dropdown', {
          id: 'dd_conf_04',
          name: 'Connected User',
          label: 'Connected User',
          supportedEntityTypes: ['WorkAllocation'],
          requiredEntityFields: ['connectedUser:connectedUser==null?null:{id:connectedUser.id,name:connectedUser.fullName,resourceType:connectedUser.resourceType}'],
          sampleData: {
            ConnectedUser: { name: 'John Smith' }
          },
          entityClickBehavior: 'openEntity',
          field: 'connectedUser',
          dropdownItemsSource: {
            type: 'interpolatedQuery',
            query: "user?select={id, fullname as name}&where=(Skills.Where(name==\"${{Skill.Name}}$\").Count()>0 and Availabilities.Where(Timeframe.Id in [${{Demands.Select(Timeframe.Id)}}$]).Count()==${{Demands.Count()}}$ and Availabilities.Where(Timeframe.Id in [${{Demands.Select(Timeframe.Id)}}$]).Where((AvailableCapacity/100)<${{CustomValues.Get(\"FTE\").Value}}$).Count()==0)"
          }
        })
    })
Pour requiredEntityFields l'attribut dans cet exemple particulier, il n'est pas possible d'utiliser une connectedUser référence de manière standard (connectedUser: {id: connectedUser.id, name: connectedUser.name, resourceType: connectedUser.resourceType}). Le sélecteur doit être modifié car il fait référence à l'entité User qui ne possède pas de name champ. Pour cette raison, nous utilisons l'alias fullName comme name: {id: connectedUser.id, name: connectedUser.fullName, resourceType: connectedUser.resourceType}. Cependant, l'utilisation de ce sélecteur seul n'est désormais plus suffisante, car connectedUser being null produira un objet vide (connectedUser: {}) au lieu de null. Pour éviter cela, nous ajoutons une vérification supplémentaire à l'aide de l'opérateur ternaire afin d'évaluer null chaque fois que l'entité a connectedUser également une null valeur. Pour plus d'informations sur la syntaxe des sélecteurs v2 API, consultez la v2 présentation de l'API REST et les documents associés. VEUILLEZ NOTER que les attributions de tâches, les compétences, les disponibilités, les délais et les demandes sont des entités de domaine extensibles qui peuvent ne pas être présentes dans votre compte Targetprocess particulier.