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 }