Changeset 249443 in webkit


Ignore:
Timestamp:
Sep 3, 2019 2:52:16 PM (5 years ago)
Author:
Jonathan Bedard
Message:

results.webkit.org: Move legend into sidebar
https://bugs.webkit.org/show_bug.cgi?id=201258

Rubber-stamped by Aakash Jain.

  • resultsdbpy/resultsdbpy/view/static/js/timeline.js:

(Legend): Make the legend vertical instead of horizontal, add ToolTip to dots in the legend.

  • resultsdbpy/resultsdbpy/view/static/js/tooltip.css: Add left and right tooltip arrows.
  • resultsdbpy/resultsdbpy/view/static/js/tooltip.js:

(isPointInElement): Make bound check include borders.
(_ToolTip.toString): Add left/right cases.
(_ToolTip.prototype.setByElement): Set the tooltip location given an element.

  • resultsdbpy/resultsdbpy/view/templates/search.html: Put the legend into the sidebar.
  • resultsdbpy/resultsdbpy/view/templates/suite_results.html: Ditto.
Location:
trunk/Tools
Files:
9 edited

Legend:

Unmodified
Added
Removed
  • trunk/Tools/ChangeLog

    r249432 r249443  
     12019-09-03  Jonathan Bedard  <jbedard@apple.com>
     2
     3        results.webkit.org: Move legend into sidebar
     4        https://bugs.webkit.org/show_bug.cgi?id=201258
     5
     6        Rubber-stamped by Aakash Jain.
     7
     8        * resultsdbpy/resultsdbpy/view/static/js/timeline.js:
     9        (Legend): Make the legend vertical instead of horizontal, add ToolTip to dots in the legend.
     10        * resultsdbpy/resultsdbpy/view/static/js/tooltip.css: Add left and right tooltip arrows.
     11        * resultsdbpy/resultsdbpy/view/static/js/tooltip.js:
     12        (isPointInElement): Make bound check include borders.
     13        (_ToolTip.toString): Add left/right cases.
     14        (_ToolTip.prototype.setByElement): Set the tooltip location given an element.
     15        * resultsdbpy/resultsdbpy/view/templates/search.html: Put the legend into the sidebar.
     16        * resultsdbpy/resultsdbpy/view/templates/suite_results.html: Ditto.
     17
    1182019-09-03  Jonathan Bedard  <jbedard@apple.com>
    219
  • trunk/Tools/resultsdbpy/resultsdbpy/view/static/css/timeline.css

    r247830 r249443  
    2424 */
    2525
    26 .dot.timeout {
     26.dot.timedout {
    2727    background-color: var(--orangeLight);
    2828}
    2929
    30 .dot.crash {
     30.dot.crashed {
    3131    background-color: var(--purpleLight);
    3232}
  • trunk/Tools/resultsdbpy/resultsdbpy/view/static/css/tooltip.css

    r249285 r249443  
    4444}
    4545
     46.tooltip.arrow-left {
     47    border-color: transparent transparent transparent #cccd;
     48}
     49.tooltip.arrow-right {
     50     border-color: transparent #cccd transparent  transparent;
     51}
     52
    4653.tooltip-content {
    47     z-index: 50;
     54    z-index: var(--middleZIndex);
    4855    position: absolute;
    4956    -webkit-backdrop-filter: blur(10px) brightness(88%);
     
    5562    list-style: none;
    5663    max-width: 600px;
     64    min-width: 30px;
     65    min-height: 30px;
    5766}
  • trunk/Tools/resultsdbpy/resultsdbpy/view/static/js/drawer.js

    r249432 r249443  
    7575                if (element.style.display)
    7676                    element.style.display = null;
    77                 else
     77                else {
     78                    for (let node of element.children) {
     79                        if (node.classList.contains("list"))
     80                            setEnableRecursive(node, true);
     81                    }
    7882                    element.style.display = 'block';
     83                }
    7984            }
    8085        }
  • trunk/Tools/resultsdbpy/resultsdbpy/view/static/js/timeline.js

    r249358 r249443  
    245245        },
    246246        onScaleLeave: (event, canvas) => {
    247             if (!ToolTip.isIn({x: event.x, y: event.y}))
     247            const scrollDelta = document.documentElement.scrollTop || document.body.scrollTop;
     248            if (!ToolTip.isIn({x: event.x, y: event.y - scrollDelta}))
    248249                ToolTip.unset();
    249250        },
     
    643644
    644645        function onDotLeave(event, canvas) {
    645             if (!ToolTip.isIn({x: event.pageX, y: event.pageY}))
     646            const scrollDelta = document.documentElement.scrollTop || document.body.scrollTop;
     647            if (!ToolTip.isIn({x: event.pageX, y: event.pageY - scrollDelta}))
    646648                ToolTip.unset();
    647649        }
     
    802804function Legend(callback=null, plural=false) {
    803805    let updateLabelEvents = new EventStream();
    804     let result = `<br>
    805          <div class="lengend timeline">
    806             <div class="item">
    807                 <div class="dot success"><div class="text">${TestResultsSymbolMap.success}</div></div>
    808                 ${LegendLabel(
    809                     updateLabelEvents,
    810                     plural ? 'No unexpected results' : 'Result expected',
    811                     plural ? 'All tests passed' : 'Test passed',
    812                 )}
    813             </div>
    814             <div class="item">
    815                 <div class="dot failed"><div class="text">${TestResultsSymbolMap.failed}</div></div>
    816                 ${LegendLabel(
    817                     updateLabelEvents,
    818                     plural ? 'Some tests unexpectedly failed' : 'Unexpectedly failed',
    819                     plural ? 'Some tests failed' : 'Test failed',
    820                 )}
    821             </div>
    822             <div class="item">
    823                 <div class="dot timeout"><div class="text">${TestResultsSymbolMap.timedout}</div></div>
    824                 ${LegendLabel(
    825                     updateLabelEvents,
    826                     plural ? 'Some tests unexpectedly timed out' : 'Unexpectedly timed out',
    827                     plural ? 'Some tests timed out' : 'Test timed out',
    828                 )}
    829             </div>
    830             <div class="item">
    831                 <div class="dot crash"><div class="text">${TestResultsSymbolMap.crashed}</div></div>
    832                 ${LegendLabel(
    833                     updateLabelEvents,
    834                     plural ? 'Some tests unexpectedly crashed' : 'Unexpectedly crashed',
    835                     plural ? 'Some tests crashed' : 'Test crashed',
    836                 )}
    837             </div>
    838             <br>
     806    const legendDetails = {
     807        success: {
     808            expected: plural ? 'No unexpected results' : 'Result expected',
     809            unexpected: plural ? 'All tests passed' : 'Test passed',
     810        },
     811        failed: {
     812            expected: plural ? 'Some tests unexpectedly failed' : 'Unexpectedly failed',
     813            unexpected: plural ? 'Some tests failed' : 'Test failed',
     814        },
     815        timedout: {
     816            expected: plural ? 'Some tests unexpectedly timed out' : 'Unexpectedly timed out',
     817            unexpected: plural ? 'Some tests timed out' : 'Test timed out',
     818        },
     819        crashed: {
     820            expected: plural ? 'Some tests unexpectedly crashed' : 'Unexpectedly crashed',
     821            unexpected: plural ? 'Some tests crashed' : 'Test crashed',
     822        },
     823    };
     824    let result = `<div class="lengend horizontal">
     825            ${Object.keys(legendDetails).map((key) => {
     826                const dot = REF.createRef({
     827                    onElementMount: (element) => {
     828                        element.addEventListener('mouseleave', (event) => {
     829                            if (!ToolTip.isIn({x: event.x, y: event.y}))
     830                                ToolTip.unset();
     831                        });
     832                        element.onmouseover = (event) => {
     833                            if (!element.classList.contains('disabled'))
     834                                return;
     835                            ToolTip.setByElement(
     836                                `<div class="content">
     837                                    ${willFilterExpected ? legendDetails[key].expected : legendDetails[key].unexpected}
     838                                </div>`,
     839                                element,
     840                                {orientation: ToolTip.HORIZONTAL},
     841                            );
     842                        };
     843                    }
     844                });
     845                return `<div class="item">
     846                        <div class="dot ${key}" ref="${dot}"><div class="text">${TestResultsSymbolMap[key]}</div></div>
     847                        ${LegendLabel(updateLabelEvents, legendDetails[key].expected, legendDetails[key].unexpected)}
     848                    </div>`
     849            }).join('')}
    839850        </div>`;
    840851
     
    853864        });
    854865
    855         result += `<div class="input" style="width:400px">
    856             <label>Filter expected results</label>
     866        result += `<div class="input">
     867            <label style="font-size: var(--tinySize); color: var(--boldInverseColor)">Filter expected results</label>
    857868            <label class="switch">
    858869                <input type="checkbox"${willFilterExpected ? ' checked': ''} ref="${swtch}">
     
    862873    }
    863874
    864     return `<div class="content">${result}</div><br>`;
     875    return `${result}`;
    865876}
    866877
  • trunk/Tools/resultsdbpy/resultsdbpy/view/static/js/tooltip.js

    r249111 r249443  
    2929        return false;
    3030    const bounds = element.getBoundingClientRect();
    31     return point.x >= bounds.left && point.x <= bounds.right && point.y >= bounds.top && point.y <= bounds.bottom;
     31    return point.x >= bounds.left - 1 && point.x <= bounds.right + 1 && point.y >= bounds.top - 1 && point.y <= bounds.bottom + 1;
    3232}
    3333
     
    3737        this.arrow = null;
    3838        this.onArrowClick = null;
     39
     40        this.VERTICAL = 0;
     41        this.HORIZONTAL = 1;
    3942    }
    4043    toString() {
     
    4447            onElementMount: (element) => {
    4548                element.addEventListener('mouseleave', (event) => {
     49                    if (element.style.display === 'none')
     50                        return;
    4651                    if (!isPointInElement(self.arrow.element, event))
    4752                        this.unset()
     
    5257                    DOM.inject(element, stateDiff.content);
    5358                    element.style.display = null;
    54                 }
    55                 if (!state.content && !element.style.display) {
     59                } else {
    5660                    element.style.display = 'none';
    5761                    DOM.inject(element, '');
    5862                }
     63
    5964                if (stateDiff.points) {
    6065                    element.style.left = '0px';
     
    6873                    const viewportHeight = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);
    6974
    70                     // Make an effort to place the tooltip in the center of the viewport.
    7175                    let direction = 'down';
    72                     let tipY = upperPoint.y - 8 - bounds.height;
    7376                    let point = upperPoint;
    74                     if (tipY < scrollDelta || tipY + bounds.height + (lowerPoint.y - upperPoint.y) / 2 < scrollDelta + viewportHeight / 2) {
    75                         direction = 'up';
    76                         tipY = lowerPoint.y + 16;
    77                         point = lowerPoint;
     77
     78                    if (upperPoint.y == lowerPoint.y) {
     79                        // Horizontal tooltip
     80                        const leftPoint = stateDiff.points.length > 1 && stateDiff.points[0].x > stateDiff.points[1].x ? stateDiff.points[1] : stateDiff.points[0];
     81                        const rightPoint = stateDiff.points.length > 1 && stateDiff.points[1].x > stateDiff.points[0].x ? stateDiff.points[1] : stateDiff.points[0];
     82
     83                        direction = 'left';
     84                        let tipX = leftPoint.x - 12 - bounds.width;
     85                        point = rightPoint;
     86                        if (tipX < 0 || tipX + bounds.width + (rightPoint.x - leftPoint.x) / 2 < viewportWitdh / 2) {
     87                            direction = 'right';
     88                            tipX = rightPoint.x + 16;
     89                            point = rightPoint;
     90                        }
     91                        element.style.left = `${tipX}px`;
     92
     93                        let tipY = point.y - bounds.height / 2;
     94                        if (tipY + bounds.height > scrollDelta + viewportHeight)
     95                            tipY = scrollDelta + viewportHeight - bounds.height;
     96                        if (tipY < 0)
     97                            tipY = 0;
     98                        element.style.top = `${tipY}px`;
     99                    } else {
     100                        // Make an effort to place the tooltip in the center of the viewport.
     101                        let tipY = upperPoint.y - 8 - bounds.height;
     102                        point = upperPoint;
     103                        if (tipY < scrollDelta || tipY + bounds.height + (lowerPoint.y - upperPoint.y) / 2 < scrollDelta + viewportHeight / 2) {
     104                            direction = 'up';
     105                            tipY = lowerPoint.y + 16;
     106                            point = lowerPoint;
     107                        }
     108                        element.style.top = `${tipY}px`;
     109
     110                        let tipX = point.x - bounds.width / 2;
     111                        if (tipX + bounds.width > viewportWitdh)
     112                            tipX = viewportWitdh - bounds.width;
     113                        if (tipX < 0)
     114                            tipX = 0;
     115                        element.style.left = `${tipX}px`;
    78116                    }
    79                     element.style.top = `${tipY}px`;
    80 
    81                     let tipX = point.x - bounds.width / 2;
    82                     if (tipX + bounds.width > viewportWitdh)
    83                         tipX = viewportWitdh - bounds.width;
    84                     if (tipX < 0)
    85                         tipX = 0;
    86                     element.style.left = `${tipX}px`;
    87117
    88118                    self.arrow.setState({direction: direction, location: point});
     
    94124            onElementMount: (element) => {
    95125                element.addEventListener('mouseleave', (event) => {
    96                     if (!isPointInElement(self.ref.element, event))
     126                    if (element.style.display === 'none')
     127                        return;
     128                    if (!isPointInElement(self.ref.element, event) && !isPointInElement(element, event))
    97129                        this.unset()
    98130                });
    99131            },
    100             onStateUpdate: (element, stateDiff, state) => {
    101                 if (!state.direction || !state.location) {
     132            onStateUpdate: (element, stateDiff) => {
     133                if (!stateDiff.direction || !stateDiff.location) {
    102134                    element.style.display = 'none';
    103135                    element.onclick = null;
     
    114146                }
    115147
    116                 element.classList = [`tooltip arrow-${state.direction}`];
    117                 element.style.left = `${state.location.x - 15}px`;
    118                 if (state.direction == 'down')
    119                     element.style.top = `${state.location.y - 8}px`;
    120                 else
    121                     element.style.top = `${state.location.y - 13}px`;
     148                element.classList = [`tooltip arrow-${stateDiff.direction}`];
     149               
     150                if (stateDiff.direction == 'down') {
     151                    element.style.left = `${stateDiff.location.x - 15}px`;
     152                    element.style.top = `${stateDiff.location.y - 8}px`;
     153                } else if (stateDiff.direction == 'left') {
     154                    element.style.left = `${stateDiff.location.x - 30}px`;
     155                    element.style.top = `${stateDiff.location.y - 15}px`;
     156                } else if (stateDiff.direction == 'right') {
     157                    element.style.left = `${stateDiff.location.x - 13}px`;
     158                    element.style.top = `${stateDiff.location.y - 15}px`;
     159                } else {
     160                    element.style.left = `${stateDiff.location.x - 15}px`;
     161                    element.style.top = `${stateDiff.location.y - 13}px`;
     162                }
    122163                element.style.display = null;
    123164            },
     
    140181        this.ref.setState({content: content, points: points});
    141182    }
     183    setByElement(content, element, options) {
     184        const bound = element.getBoundingClientRect();
     185        const orientation = options.orientation ? options.orientation : this.VERTICAL;
     186        const onArrowClick = options.onArrowClick ? options.onArrowClick : null;
     187
     188        // Manage the scroll delta
     189        let scrollDelta = 0;
     190        if (window.getComputedStyle(element.offsetParent).getPropertyValue('position') == 'fixed')
     191            scrollDelta = document.documentElement.scrollTop || document.body.scrollTop;
     192
     193        if (options.orientation) {
     194            this.set(content, [
     195                {x: bound.right, y: (bound.top + bound.bottom) / 2 + scrollDelta},
     196                {x: bound.left, y: (bound.top + bound.bottom) / 2 + scrollDelta},
     197            ], onArrowClick);
     198        } else {
     199            this.set(content, [
     200                {x: (bound.right + bound.left) / 2, y: bound.top + scrollDelta},
     201                {x: (bound.right + bound.left) / 2, y: bound.bottom + scrollDelta},
     202            ], onArrowClick);
     203        }
     204    }
    142205    unset() {
    143206        if (this.ref)
  • trunk/Tools/resultsdbpy/resultsdbpy/view/static/library/css/webkit.css

    r249213 r249443  
    9191  --sectionPaddingSize: 12px 24px;
    9292  --formLabelPadding: 14px;
     93  --middleZIndex: 50;
    9394  --topZIndex: 100;
    9495  --boldInverseColor: var(--black);
     
    923924  -webkit-backdrop-filter: blur(5px) brightness(88%);
    924925  backdrop-filter: blur(5px) brightness(88%);
    925   z-index: var(--topZIndex);
     926  z-index: 50;
    926927  right: 0;
    927928}
  • trunk/Tools/resultsdbpy/resultsdbpy/view/templates/search.html

    r249358 r249443  
    129129
    130130                if (diff.children)
    131                     DOM.inject(element, Legend(() => {
    132                         self.ref.state.children.forEach((child) => {
    133                             child.timeline.update();
    134                         });
    135                     }, false) + diff.children.map(renderChild).join(''));
     131                    DOM.inject(element, diff.children.map(renderChild).join(''));
    136132                if (diff.prepending) {
    137133                    diff.prepending.forEach(child => {
     
    228224    `${ToolTip}
    229225    ${Drawer([
     226        Legend(() => {
     227            view.children.forEach((child) => {
     228                child.timeline.update();
     229            });
     230        }, false),
    230231        LimitSlider(() => {view.reload()}),
    231232        BranchSelector(() => {
  • trunk/Tools/resultsdbpy/resultsdbpy/view/templates/suite_results.html

    r249358 r249443  
    4040
    4141const DEFAULT_LIMIT = 100;
    42 const SUITES = JSON.parse('{{ suites|safe }}');   
     42const SUITES = JSON.parse('{{ suites|safe }}');
    4343
    4444class MainView {
     
    144144    render(suites) {
    145145        let children = this.children;
    146         return Legend(() => {
    147             for (let suite in children) {
    148                 children[suite].update();
    149             }
    150         }, true) + suites.map(suite => {
     146        return suites.map(suite => {
    151147            return `<div class="section">
    152148                    <div class="header">
     
    172168DOM.inject(document.getElementById('app'), `${ToolTip}
    173169${Drawer([
     170    Legend(() => {
     171        for (let suite in view.children) {
     172            view.children[suite].update();
     173        }
     174    }, true),
    174175    LimitSlider(() => {view.reload()}),
    175176    BranchSelector(() => {
Note: See TracChangeset for help on using the changeset viewer.