go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/milo/ui/src/common/components/associated_bugs_badge/associated_bugs_badge.ts (about)

     1  // Copyright 2022 The LUCI Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  import '@material/mwc-menu';
    16  import { css, html, render } from 'lit';
    17  import { customElement } from 'lit/decorators.js';
    18  import { computed, makeObservable, observable, reaction } from 'mobx';
    19  
    20  import '@/common/components/associated_bugs_tooltip';
    21  import {
    22    HideTooltipEventDetail,
    23    ShowTooltipEventDetail,
    24  } from '@/common/components/tooltip';
    25  import { AssociatedBug, Cluster } from '@/common/services/luci_analysis';
    26  import { commonStyles } from '@/common/styles/stylesheets';
    27  import { getUniqueBugs } from '@/common/tools/cluster_utils/cluster_utils';
    28  import { MobxExtLitElement } from '@/generic_libs/components/lit_mobx_ext';
    29  
    30  @customElement('milo-associated-bugs-badge')
    31  export class AssociatedBugsBadgeElement extends MobxExtLitElement {
    32    @observable.ref project!: string;
    33    @observable.ref clusters!: readonly Cluster[];
    34  
    35    /**
    36     * Unique bugs in the provided clusters.
    37     */
    38    @computed.struct private get uniqueBugs(): readonly AssociatedBug[] {
    39      return getUniqueBugs(
    40        this.clusters
    41          .map((c) => c.bug)
    42          .filter((b) => !!b) as Array<AssociatedBug>,
    43      );
    44    }
    45  
    46    constructor() {
    47      super();
    48      makeObservable(this);
    49    }
    50  
    51    connectedCallback() {
    52      super.connectedCallback();
    53      this.addDisposer(
    54        reaction(
    55          () => this.uniqueBugs.length > 0,
    56          (shouldDisplay) =>
    57            this.style.setProperty(
    58              'display',
    59              shouldDisplay ? 'inline-block' : 'none',
    60            ),
    61          { fireImmediately: true },
    62        ),
    63      );
    64    }
    65  
    66    private renderTooltip() {
    67      return html`
    68        <milo-associated-bugs-tooltip
    69          .project=${this.project}
    70          .clusters=${this.clusters}
    71        ></milo-associated-bugs-tooltip>
    72      `;
    73    }
    74  
    75    protected render() {
    76      return html`
    77        <div
    78          class="badge"
    79          @mouseover=${() => {
    80            const tooltip = document.createElement('div');
    81            render(this.renderTooltip(), tooltip);
    82  
    83            window.dispatchEvent(
    84              new CustomEvent<ShowTooltipEventDetail>('show-tooltip', {
    85                detail: {
    86                  tooltip,
    87                  targetRect: this.getBoundingClientRect(),
    88                  gapSize: 2,
    89                },
    90              }),
    91            );
    92          }}
    93          @mouseout=${() => {
    94            window.dispatchEvent(
    95              new CustomEvent<HideTooltipEventDetail>('hide-tooltip', {
    96                detail: { delay: 50 },
    97              }),
    98            );
    99          }}
   100        >
   101          ${this.uniqueBugs.map((b) => b.linkText).join(', ')}
   102        </div>
   103      `;
   104    }
   105  
   106    static styles = [
   107      commonStyles,
   108      css`
   109        .badge {
   110          display: inline-block;
   111          margin: 0;
   112          background-color: #b7b7b7;
   113          width: 100%;
   114          box-sizing: border-box;
   115          overflow: hidden;
   116          text-overflow: ellipsis;
   117          vertical-align: sub;
   118          color: white;
   119          padding: 0.25em 0.4em;
   120          font-size: 75%;
   121          font-weight: 700;
   122          line-height: 13px;
   123          text-align: center;
   124          white-space: nowrap;
   125          border-radius: 0.25rem;
   126        }
   127      `,
   128    ];
   129  }