Changeset 251028 in webkit


Ignore:
Timestamp:
Oct 11, 2019 4:17:52 PM (4 years ago)
Author:
Dewei Zhu
Message:

Improve test freshness page interaction experience.
https://bugs.webkit.org/show_bug.cgi?id=202684

Reviewed by Ryosuke Niwa.

Change test freshness page show tooltip on click instead of popuping on mouse hover.
And clicking anywhere in 'page-with-heading' section except the tooltip can dismiss tooltip.
Add keyboard support to move focus around including 'Tab' key support.
Add support to use 'Enter' key to show or dismiss tooltip.
Add support to use 'Escape' key to dismiss tooltip.

  • public/shared/common-component-base.js: Added support for link to specify 'tabindex'.

(CommonComponentBase.prototype.createLink):
(CommonComponentBase.createLink):
(CommonComponentBase):

  • public/v3/components/base.js: Added support for customizing whether or not prevent default and stop propagation

while creating event handler.
(ComponentBase.prototype.createEventHandler):
(ComponentBase.createEventHandler):
(ComponentBase):

  • public/v3/components/freshness-indicator.js:

(FreshnessIndicator): Removed 'url' property and removed customization for mouse event.
(FreshnessIndicator.prototype.update):
(FreshnessIndicator.prototype.didConstructShadowTree): Deleted.

  • public/v3/pages/test-freshness-page.js:

(TestFreshnessPage): Changed to show tooltip on click and added key board event.
(TestFreshnessPage.prototype.didConstructShadowTree): Added key event support.
(TestFreshnessPage.prototype._findClosestIndicatorAnchorForCoordinate):
(TestFreshnessPage.prototype.render):
(TestFreshnessPage.prototype._renderTooltip):
(TestFreshnessPage.prototype._constructTableCell): Added tabIndex for each cell that contains freshness indicator.
(TestFreshnessPage.prototype._configureAnchorForIndicator):
(TestFreshnessPage.prototype._clearIndicatorState): Changed the color of links in tooltip to a more readable color.
Added styles when anchor for status cell and links on tooltip are focused.
(TestFreshnessPage.cssTemplate):

Location:
trunk/Websites/perf.webkit.org
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/Websites/perf.webkit.org/ChangeLog

    r250760 r251028  
     12019-10-11  Dewei Zhu  <dewei_zhu@apple.com>
     2
     3        Improve test freshness page interaction experience.
     4        https://bugs.webkit.org/show_bug.cgi?id=202684
     5
     6        Reviewed by Ryosuke Niwa.
     7
     8        Change test freshness page show tooltip on click instead of popuping on mouse hover.
     9        And clicking anywhere in 'page-with-heading' section except the tooltip can dismiss tooltip.
     10        Add keyboard support to move focus around including 'Tab' key support.
     11        Add support to use 'Enter' key to show or dismiss tooltip.
     12        Add support to use 'Escape' key to dismiss tooltip.
     13
     14        * public/shared/common-component-base.js: Added support for link to specify 'tabindex'.
     15        (CommonComponentBase.prototype.createLink):
     16        (CommonComponentBase.createLink):
     17        (CommonComponentBase):
     18        * public/v3/components/base.js: Added support for customizing whether or not prevent default and stop propagation
     19        while creating event handler.
     20        (ComponentBase.prototype.createEventHandler):
     21        (ComponentBase.createEventHandler):
     22        (ComponentBase):
     23        * public/v3/components/freshness-indicator.js:
     24        (FreshnessIndicator): Removed 'url' property and removed customization for mouse event.
     25        (FreshnessIndicator.prototype.update):
     26        (FreshnessIndicator.prototype.didConstructShadowTree): Deleted.
     27        * public/v3/pages/test-freshness-page.js:
     28        (TestFreshnessPage): Changed to show tooltip on click and added key board event.
     29        (TestFreshnessPage.prototype.didConstructShadowTree): Added key event support.
     30        (TestFreshnessPage.prototype._findClosestIndicatorAnchorForCoordinate):
     31        (TestFreshnessPage.prototype.render):
     32        (TestFreshnessPage.prototype._renderTooltip):
     33        (TestFreshnessPage.prototype._constructTableCell): Added tabIndex for each cell that contains freshness indicator.
     34        (TestFreshnessPage.prototype._configureAnchorForIndicator):
     35        (TestFreshnessPage.prototype._clearIndicatorState): Changed the color of links in tooltip to a more readable color.
     36        Added styles when anchor for status cell and links on tooltip are focused.
     37        (TestFreshnessPage.cssTemplate):
     38
    1392019-10-04  Zhifei Fang  <zhifei_fang@apple.com>
    240
  • trunk/Websites/perf.webkit.org/public/shared/common-component-base.js

    r232614 r251028  
    136136    }
    137137
    138     createLink(content, titleOrCallback, callback, isExternal)
     138    createLink(content, titleOrCallback, callback, isExternal, tabIndex=null)
    139139    {
    140         return CommonComponentBase.createLink(content, titleOrCallback, callback, isExternal);
     140        return CommonComponentBase.createLink(content, titleOrCallback, callback, isExternal, tabIndex);
    141141    }
    142142
    143     static createLink(content, titleOrCallback, callback, isExternal)
     143    static createLink(content, titleOrCallback, callback, isExternal, tabIndex=null)
    144144    {
    145145        var title = titleOrCallback;
     
    153153            title: title,
    154154        };
     155
     156        if (tabIndex)
     157            attributes['tabindex'] = tabIndex;
    155158
    156159        if (typeof(callback) === 'string')
  • trunk/Websites/perf.webkit.org/public/v3/components/base.js

    r233333 r251028  
    250250    }
    251251
    252     createEventHandler(callback) { return ComponentBase.createEventHandler(callback); }
    253     static createEventHandler(callback)
     252    createEventHandler(callback, options={}) { return ComponentBase.createEventHandler(callback, options); }
     253    static createEventHandler(callback, options={})
    254254    {
    255255        return function (event) {
    256             event.preventDefault();
    257             event.stopPropagation();
     256            if (!('preventDefault' in options) || options['preventDefault'])
     257                event.preventDefault();
     258            if (!('stopPropagation' in options) || options['stopPropagation'])
     259                event.stopPropagation();
    258260            callback.call(this, event);
    259261        };
  • trunk/Websites/perf.webkit.org/public/v3/components/freshness-indicator.js

    r250749 r251028  
    11class FreshnessIndicator extends ComponentBase {
    2     constructor(lastDataPointDuration, testAgeTolerance, summary, url)
     2    constructor(lastDataPointDuration, testAgeTolerance, summary)
    33    {
    44        super('freshness-indicator');
    55        this._lastDataPointDuration = lastDataPointDuration;
    66        this._testAgeTolerance = testAgeTolerance;
    7         this._url = url;
    87        this._highlighted = false;
    98
     
    1110    }
    1211
    13     update(lastDataPointDuration, testAgeTolerance, url, highlighted)
     12    update(lastDataPointDuration, testAgeTolerance, highlighted)
    1413    {
    1514        this._lastDataPointDuration = lastDataPointDuration;
    1615        this._testAgeTolerance = testAgeTolerance;
    17         this._url = url;
    1816        this._highlighted = highlighted;
    1917        this.enqueueToRender();
    20     }
    21 
    22     didConstructShadowTree()
    23     {
    24         const container = this.content('container');
    25         container.addEventListener('mouseenter', () => this.dispatchAction('select', this));
    26         container.addEventListener('mouseleave', () => this.dispatchAction('unselect'));
    2718    }
    2819
     
    4738        const hue = Math.round(120 * rating);
    4839        const brightness = Math.round(30 + 50 * rating);
    49         const indicator = element('a', {id: 'cell', href: url, class: highlighted ? 'highlight' : ''});
     40        const indicator = element('a', {id: 'cell', class: `${highlighted ? 'highlight' : ''}`});
    5041
    5142        indicator.style.backgroundColor = `hsl(${hue}, 100%, ${brightness}%)`;
  • trunk/Websites/perf.webkit.org/public/v3/pages/test-freshness-page.js

    r250760 r251028  
    1010        this._indicatorByConfiguration = null;
    1111        this._renderTableLazily = new LazilyEvaluatedFunction(this._renderTable.bind(this));
    12         this._currentlyHighlightedIndicator = null;
    13         this._hoveringTooltip = false;
     12        this._hoveringIndicator = null;
     13        this._indicatorForTooltip = null;
     14        this._firstIndicatorAnchor = null;
     15        this._showTooltip = false;
    1416        this._builderByIndicator = null;
     17        this._tabIndexForIndicator = null;
     18        this._coordinateForIndicator = null;
     19        this._indicatorAnchorGrid = null;
     20        this._skipNextClick = false;
     21        this._skipNextStateCleanOnScroll = false;
     22        this._lastFocusedCell = null;
    1523        this._renderTooltipLazily = new LazilyEvaluatedFunction(this._renderTooltip.bind(this));
    1624
    1725        this._loadConfig(summaryPageConfiguration);
    18     }
    19 
    20     didConstructShadowTree()
    21     {
    22         const tooltipTable = this.content('tooltip-table');
    23         tooltipTable.addEventListener('mouseenter', () => {
    24             this._hoveringTooltip = true;
    25             this.enqueueToRender();
    26         });
    27         tooltipTable.addEventListener('mouseleave', () => {
    28             this._hoveringTooltip = false;
    29             this.enqueueToRender();
    30         });
    3126    }
    3227
     
    6762        this._fetchTestResults();
    6863        super.open(state);
     64    }
     65
     66    didConstructShadowTree()
     67    {
     68        super.didConstructShadowTree();
     69
     70        const tooltipTable = this.content('tooltip-table');
     71        this.content().addEventListener('click', (event) => {
     72            if (!tooltipTable.contains(event.target))
     73                this._clearIndicatorState(false);
     74        });
     75
     76        tooltipTable.onkeydown = this.createEventHandler((event) => {
     77            if (event.code == 'Escape') {
     78                event.preventDefault();
     79                event.stopPropagation();
     80                this._lastFocusedCell.focus({preventScroll: true});
     81            }
     82        }, {preventDefault: false, stopPropagation: false});
     83
     84        window.addEventListener('keydown', (event) => {
     85            if (!['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(event.code))
     86                return;
     87
     88            event.preventDefault();
     89            if (!this._indicatorForTooltip && !this._hoveringIndicator) {
     90                if (this._firstIndicatorAnchor)
     91                    this._firstIndicatorAnchor.focus({preventScroll: true});
     92                return;
     93            }
     94
     95            let [row, column] = this._coordinateForIndicator.get(this._indicatorForTooltip || this._hoveringIndicator);
     96            let direction = null;
     97
     98            switch (event.code) {
     99                case 'ArrowUp':
     100                    row -= 1;
     101                    break;
     102                case 'ArrowDown':
     103                    row += 1;
     104                    break;
     105                case 'ArrowLeft':
     106                    column -= 1;
     107                    direction = 'leftOnly'
     108                    break;
     109                case 'ArrowRight':
     110                    column += 1;
     111                    direction = 'rightOnly'
     112            }
     113
     114            const closestIndicatorAnchor = this._findClosestIndicatorAnchorForCoordinate(column, row, this._indicatorAnchorGrid, direction);
     115            if (closestIndicatorAnchor)
     116                closestIndicatorAnchor.focus({preventScroll: true});
     117        });
     118    }
     119
     120    _findClosestIndicatorAnchorForCoordinate(columnIndex, rowIndex, grid, direction)
     121    {
     122        rowIndex = Math.min(Math.max(rowIndex, 0), grid.length - 1);
     123        const row = grid[rowIndex];
     124        if (!row.length)
     125            return null;
     126
     127        const start = Math.min(Math.max(columnIndex, 0), row.length - 1);
     128        if (row[start])
     129            return row[start];
     130
     131        let offset = 1;
     132        while (true) {
     133            const leftIndex = start - offset;
     134            if (leftIndex >= 0 && row[leftIndex] && direction != 'rightOnly')
     135                return row[leftIndex];
     136            const rightIndex = start + offset;
     137            if (rightIndex < row.length && row[rightIndex] && direction != 'leftOnly')
     138                return row[rightIndex];
     139            if (leftIndex < 0 && rightIndex >= row.length)
     140                break;
     141            offset += 1;
     142        }
     143        return null;
    69144    }
    70145
     
    124199        this._renderTableLazily.evaluate(this._platforms, this._metrics);
    125200
    126         let buildSummaryForCurrentlyHighlightedIndicator = null;
    127         let buildForCurrentlyHighlightedIndicator = null;
    128         let commitSetForCurrentHighlightedIndicator = null;
    129         const builderForCurrentlyHighlightedIndicator = this._currentlyHighlightedIndicator ? this._builderByIndicator.get(this._currentlyHighlightedIndicator) : null;
     201        let buildSummaryForFocusingIndicator = null;
     202        let buildForFocusingIndicator = null;
     203        let commitSetForFocusingdIndicator = null;
     204        let chartURLForFocusingIndicator = null;
     205        let platformForFocusingIndicator = null;
     206        let metricForFocusingIndicator = null;
     207        const builderForFocusingIndicator = this._indicatorForTooltip ? this._builderByIndicator.get(this._indicatorForTooltip) : null;
     208        const builderForHoveringIndicator = this._hoveringIndicator ? this._builderByIndicator.get(this._hoveringIndicator) : null;
    130209        for (const [platform, lastDataPointByMetric] of this._lastDataPointByConfiguration.entries()) {
    131210            for (const [metric, lastDataPoint] of lastDataPointByMetric.entries()) {
     
    134213                const timeDurationSummary = BuildRequest.formatTimeInterval(timeDuration);
    135214                const summary = `${timeDurationSummaryPrefix}${timeDurationSummary} since latest data point.`;
    136                 const url = this._router.url('charts', ChartsPage.createStateForDashboardItem(platform.id(), metric.id(),
    137                     this._measurementSetFetchTime - this._timeDuration));
    138215
    139216                const indicator = this._indicatorByConfiguration.get(platform).get(metric);
    140                 if (this._currentlyHighlightedIndicator && this._currentlyHighlightedIndicator === indicator) {
    141                     buildSummaryForCurrentlyHighlightedIndicator = summary;
    142                     buildForCurrentlyHighlightedIndicator = lastDataPoint.lastBuild;
    143                     commitSetForCurrentHighlightedIndicator = lastDataPoint.commitSetOfLastPoint;
     217                if (this._indicatorForTooltip && this._indicatorForTooltip === indicator) {
     218                    buildSummaryForFocusingIndicator = summary;
     219                    buildForFocusingIndicator = lastDataPoint.lastBuild;
     220                    commitSetForFocusingdIndicator = lastDataPoint.commitSetOfLastPoint;
     221                    chartURLForFocusingIndicator =  this._router.url('charts', ChartsPage.createStateForDashboardItem(platform.id(), metric.id(),
     222                        this._measurementSetFetchTime - this._timeDuration));
     223                    platformForFocusingIndicator = platform;
     224                    metricForFocusingIndicator = metric;
    144225                }
    145226                this._builderByIndicator.set(indicator, lastDataPoint.builder);
    146                 indicator.update(timeDuration, this._testAgeTolerance, url, builderForCurrentlyHighlightedIndicator && builderForCurrentlyHighlightedIndicator === lastDataPoint.builder);
    147             }
    148         }
    149         this._renderTooltipLazily.evaluate(this._currentlyHighlightedIndicator, this._hoveringTooltip, buildSummaryForCurrentlyHighlightedIndicator, buildForCurrentlyHighlightedIndicator, commitSetForCurrentHighlightedIndicator);
    150     }
    151 
    152     _renderTooltip(indicator, hoveringTooltip, buildSummary, build, commitSet)
    153     {
    154         if (!indicator || !buildSummary) {
    155             this.content('tooltip-anchor').style.display = hoveringTooltip ? null : 'none';
     227                const highlighted = builderForFocusingIndicator && builderForFocusingIndicator == lastDataPoint.builder
     228                    || builderForHoveringIndicator && builderForHoveringIndicator === lastDataPoint.builder;
     229                indicator.update(timeDuration, this._testAgeTolerance, highlighted);
     230            }
     231        }
     232        this._renderTooltipLazily.evaluate(this._indicatorForTooltip, this._showTooltip, buildSummaryForFocusingIndicator, buildForFocusingIndicator,
     233            commitSetForFocusingdIndicator, chartURLForFocusingIndicator, platformForFocusingIndicator, metricForFocusingIndicator, this._tabIndexForIndicator.get(this._indicatorForTooltip));
     234    }
     235
     236    _renderTooltip(indicator, showTooltip, buildSummary, build, commitSet, chartURL, platform, metric, tabIndex)
     237    {
     238        if (!indicator || !buildSummary || !showTooltip) {
     239            this.content('tooltip-anchor').style.display =  showTooltip ? null : 'none';
    156240            return;
    157241        }
     
    167251        let tableContent = [element('tr', element('td', {colspan: 2}, buildSummary))];
    168252
     253        if (chartURL) {
     254            const linkDescription = `${metric.test().name()} on ${platform.name()}`;
     255            tableContent.push(element('tr', [
     256                element('td', 'Chart'),
     257                element('td', {colspan: 2}, link(linkDescription, linkDescription, chartURL, true, tabIndex))
     258            ]));
     259        }
     260
    169261        if (commitSet) {
    170262            if (commitSet.repositories().length)
     
    175267                return element('tr', [
    176268                    element('td', repository.name()),
    177                     element('td', commit.url() ? link(commit.label(), commit.label(), commit.url(), true) : commit.label())
     269                    element('td', commit.url() ? link(commit.label(), commit.label(), commit.url(), true, tabIndex) : commit.label())
    178270                ]);
    179271            }));
     
    186278                element('td', 'Build'),
    187279                element('td', {colspan: 2}, [
    188                     url ? link(buildNumber, build.label(), url, true) : buildNumber
     280                    url ? link(buildNumber, build.label(), url, true, tabIndex) : buildNumber
    189281                ]),
    190282            ]));
     
    197289    {
    198290        const element = ComponentBase.createElement;
    199         const tableBodyElement = [];
    200291        const tableHeadElements = [element('th',  {class: 'table-corner row-head'}, 'Platform \\ Test')];
    201292
     
    204295
    205296        this._indicatorByConfiguration = new Map;
    206         for (const platform of platforms) {
     297        this._coordinateForIndicator = new Map;
     298        this._tabIndexForIndicator = new Map;
     299        this._indicatorAnchorGrid = [];
     300        this._firstIndicatorAnchor = null;
     301        let currentTabIndex = 1;
     302
     303        const tableBodyElement = platforms.map((platform, rowIndex) =>  {
    207304            const indicatorByMetric = new Map;
    208305            this._indicatorByConfiguration.set(platform, indicatorByMetric);
    209             tableBodyElement.push(element('tr',
    210                 [element('th', {class: 'row-head'}, platform.label()), ...metrics.map((metric) => this._constructTableCell(platform, metric, indicatorByMetric))]));
    211         }
    212 
    213         this.renderReplace(this.content('test-health'), [element('thead', tableHeadElements), element('tbody', tableBodyElement)]);
     306
     307            let indicatorAnchorsInCurrentRow = [];
     308
     309            const cells = metrics.map((metric, columnIndex) => {
     310                const [cell, anchor, indicator] = this._constructTableCell(platform, metric, currentTabIndex);
     311
     312                indicatorAnchorsInCurrentRow.push(anchor);
     313                if (!indicator)
     314                    return cell;
     315
     316                indicatorByMetric.set(metric, indicator);
     317                this._tabIndexForIndicator.set(indicator, currentTabIndex);
     318                this._coordinateForIndicator.set(indicator, [rowIndex, columnIndex]);
     319
     320                ++currentTabIndex;
     321                if (!this._firstIndicatorAnchor)
     322                    this._firstIndicatorAnchor = anchor;
     323                return cell;
     324            });
     325            this._indicatorAnchorGrid.push(indicatorAnchorsInCurrentRow);
     326
     327            const row = element('tr', [element('th', {class: 'row-head'}, platform.label()), ...cells]);
     328            return row;
     329        });
     330
     331        const tableBody = element('tbody', tableBodyElement);
     332        tableBody.onscroll = this.createEventHandler(() => this._clearIndicatorState(true));
     333
     334        this.renderReplace(this.content('test-health'), [element('thead', tableHeadElements), tableBody]);
    214335    }
    215336
     
    221342    }
    222343
    223     _constructTableCell(platform, metric, indicatorByMetric)
     344    _constructTableCell(platform, metric, tabIndex)
    224345    {
    225346        const element = ComponentBase.createElement;
    226 
     347        const link = ComponentBase.createLink;
    227348        if (!this._isValidPlatformMetricCombination(platform, metric))
    228             return element('td', {class: 'blank-cell'}, element('div'));
     349            return [element('td', {class: 'blank-cell'}, element('div')), null, null];
    229350
    230351        const indicator = new FreshnessIndicator;
    231         indicator.listenToAction('select', (originator) => {
    232             this._currentlyHighlightedIndicator = originator;
     352        const anchor = link(indicator, '', () => {
     353            if (this._skipNextClick) {
     354                this._skipNextClick = false;
     355                return;
     356            }
     357            this._showTooltip = !this._showTooltip;
     358            this.enqueueToRender();
     359        }, false, tabIndex);
     360
     361        const cell = element('td', {class: 'status-cell'}, anchor);
     362        this._configureAnchorForIndicator(anchor, indicator);
     363        return [cell, anchor, indicator];
     364    }
     365
     366    _configureAnchorForIndicator(anchor, indicator)
     367    {
     368        anchor.onmouseover = this.createEventHandler(() => {
     369            this._hoveringIndicator = indicator;
    233370            this.enqueueToRender();
    234371        });
    235         indicator.listenToAction('unselect', () => {
    236             this._currentlyHighlightedIndicator = null;
     372        anchor.onmousedown = this.createEventHandler(() =>
     373            this._skipNextClick = this._indicatorForTooltip != indicator, {preventDefault: false, stopPropagation: false});
     374        anchor.onfocus = this.createEventHandler(() => {
     375            this._showTooltip = this._indicatorForTooltip != indicator;
     376            this._hoveringIndicator = indicator;
     377            this._indicatorForTooltip = indicator;
     378            this._lastFocusedCell = anchor;
     379            this._skipNextStateCleanOnScroll = true;
    237380            this.enqueueToRender();
    238381        });
    239         indicatorByMetric.set(metric, indicator);
    240         return element('td', {class: 'status-cell'}, indicator);
     382        anchor.onkeydown = this.createEventHandler((event) => {
     383            if (event.code == 'Escape') {
     384                event.preventDefault();
     385                event.stopPropagation();
     386                this._showTooltip = event.code == 'Enter' ? !this._showTooltip : false;
     387                this.enqueueToRender();
     388            }
     389        }, {preventDefault: false, stopPropagation: false});
     390    }
     391
     392    _clearIndicatorState(dueToScroll)
     393    {
     394        if (this._skipNextStateCleanOnScroll) {
     395            this._skipNextStateCleanOnScroll = false;
     396            if (dueToScroll)
     397                return;
     398        }
     399        this._showTooltip = false;
     400        this._indicatorForTooltip = null;
     401        this._hoveringIndicator = null;
     402        this.enqueueToRender();
    241403    }
    242404
     
    244406    {
    245407        return `<section class="page-with-heading">
     408            <table id="test-health"></table>
    246409            <div id="tooltip-anchor">
    247410                <table id="tooltip-table"></table>
    248411            </div>
    249             <table id="test-health"></table>
    250412        </section>`;
    251413    }
     
    296458            }
    297459            #test-health td.status-cell {
     460                position: relative;
    298461                margin: 0;
    299462                padding: 0;
     
    302465                min-width: 2.2rem;
    303466                min-height: 2.2rem;
     467            }
     468            #test-health td.status-cell>a {
     469                display: block;
     470            }
     471            #test-health td.status-cell>a:focus {
     472                outline: none;
     473            }
     474            #test-health td.status-cell>a:focus::after {
     475                position: absolute;
     476                content: "";
     477                bottom: -0.1rem;
     478                left: 50%;
     479                margin-left: -0.2rem;
     480                height: 0rem;
     481                border-width: 0.2rem;
     482                border-style: solid;
     483                border-color: transparent transparent red transparent;
     484                outline: none;
    304485            }
    305486            #test-health td.blank-cell {
     
    350531                position: absolute;
    351532                width: 22rem;
    352                 background-color: #34495E;
    353                 opacity: 0.9;
     533                background-color: #696969;
     534                opacity: 0.96;
    354535                margin: 0.3rem;
    355536                padding: 0.3rem;
     
    375556                border-width: 0.3rem;
    376557                border-style: solid;
    377                 border-color: #34495E transparent transparent transparent;
     558                border-color: #696969 transparent transparent transparent;
    378559            }
    379560            #tooltip-table a {
    380                 color: #B03A2E;
     561                color: white;
    381562                font-weight: bold;
     563            }
     564            #tooltip-table a:focus {
     565                background-color: #AAB7B8;
     566                outline: none;
    382567            }
    383568        `;
Note: See TracChangeset for help on using the changeset viewer.