go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/milo/ui/src/common/components/changelists_badge/changelists_tooltip.tsx (about) 1 // Copyright 2023 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 { MobxLitElement } from '@adobe/lit-mobx'; 16 import createCache from '@emotion/cache'; 17 import { CacheProvider, EmotionCache } from '@emotion/react'; 18 import { customElement } from 'lit/decorators.js'; 19 import { makeObservable, observable } from 'mobx'; 20 import { createRoot, Root } from 'react-dom/client'; 21 22 import { Changelist } from '@/common/services/luci_analysis'; 23 import { commonStyles } from '@/common/styles/stylesheets'; 24 25 export interface ChangelistBadgeProps { 26 readonly changelists: readonly Changelist[]; 27 } 28 29 export function getClLabel(cl: Changelist): string { 30 return `c/${cl.change}/${cl.patchset}`; 31 } 32 33 export function getClLink(cl: Changelist): string { 34 return `https://${cl.host}/c/${cl.change}/${cl.patchset}`; 35 } 36 37 export function ChangelistsTooltip({ changelists }: ChangelistBadgeProps) { 38 return ( 39 <div css={{ padding: 5 }}> 40 {changelists.map((cl, i) => ( 41 <a key={i} href={getClLink(cl)} target="_blank" rel="noreferrer"> 42 {getClLabel(cl)} 43 </a> 44 ))} 45 </div> 46 ); 47 } 48 49 @customElement('milo-changelists-tooltip') 50 export class ChangelistsTooltipElement extends MobxLitElement { 51 @observable.ref changelists!: readonly Changelist[]; 52 53 private readonly cache: EmotionCache; 54 private readonly parent: HTMLSpanElement; 55 private readonly root: Root; 56 57 constructor() { 58 super(); 59 makeObservable(this); 60 this.parent = document.createElement('span'); 61 const child = document.createElement('span'); 62 this.root = createRoot(child); 63 this.parent.appendChild(child); 64 this.cache = createCache({ 65 key: 'milo-changelists-tooltip', 66 container: this.parent, 67 }); 68 } 69 70 protected render() { 71 this.root.render( 72 <CacheProvider value={this.cache}> 73 <ChangelistsTooltip changelists={this.changelists} /> 74 </CacheProvider>, 75 ); 76 return this.parent; 77 } 78 79 static styles = [commonStyles]; 80 }