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  }