blob: 9ef3adafeccc5b42da4de06c406c9aff8393ed9b [file] [log] [blame]
// Copyright 2021 The LUCI Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import '@material/mwc-button';
import { css, customElement, html } from 'lit-element';
import { observable, reaction } from 'mobx';
import '../components/dot_spinner';
import '../components/hotkey';
import '../components/test_search_filter';
import '../components/test_variants_table';
import '../components/test_variants_table/tvt_config_widget';
import { MiloBaseElement } from '../components/milo_base';
import { TestVariantsTableElement } from '../components/test_variants_table';
import { AppState, consumeAppState } from '../context/app_state';
import { consumeInvocationState, InvocationState } from '../context/invocation_state';
import { consumeConfigsStore, UserConfigsStore } from '../context/user_configs';
import { GA_ACTIONS, GA_CATEGORIES, trackEvent } from '../libs/analytics_utils';
import { consumer } from '../libs/context';
import { errorHandler, forwardWithoutMsg, reportRenderError } from '../libs/error_handler';
import commonStyle from '../styles/common_style.css';
/**
* Display test results.
*/
@customElement('milo-test-results-tab')
@errorHandler(forwardWithoutMsg)
@consumer
export class TestResultsTabElement extends MiloBaseElement {
@observable.ref
@consumeAppState()
appState!: AppState;
@observable.ref
@consumeConfigsStore()
configsStore!: UserConfigsStore;
@observable.ref
@consumeInvocationState()
invocationState!: InvocationState;
private allVariantsWereExpanded = false;
private toggleAllVariants(expand: boolean) {
this.allVariantsWereExpanded = expand;
this.shadowRoot!.querySelector<TestVariantsTableElement>('milo-test-variants-table')!.toggleAllVariants(expand);
}
private readonly toggleAllVariantsByHotkey = () => this.toggleAllVariants(!this.allVariantsWereExpanded);
connectedCallback() {
super.connectedCallback();
this.appState.selectedTabId = 'test-results';
trackEvent(GA_CATEGORIES.TEST_RESULTS_TAB, GA_ACTIONS.TAB_VISITED, window.location.href);
// Update filters to match the querystring without saving them.
const searchParams = new URLSearchParams(window.location.search);
if (searchParams.has('q')) {
this.invocationState.searchText = searchParams.get('q')!;
}
if (searchParams.has('cols')) {
const cols = searchParams.get('cols')!;
this.invocationState.columnsParam = cols.split(',').filter((col) => col !== '');
}
if (searchParams.has('sortby')) {
const sortingKeys = searchParams.get('sortby')!;
this.invocationState.sortingKeysParam = sortingKeys.split(',').filter((col) => col !== '');
}
if (searchParams.has('groupby')) {
const groupingKeys = searchParams.get('groupby')!;
this.invocationState.groupingKeysParam = groupingKeys.split(',').filter((key) => key !== '');
}
// Update the querystring when filters are updated.
this.addDisposer(
reaction(
() => {
const displayedCols = this.invocationState.displayedColumns.join(',');
const defaultCols = this.invocationState.defaultColumns.join(',');
const sortingKeys = this.invocationState.sortingKeys.join(',');
const defaultSortingKeys = this.invocationState.defaultSortingKeys.join(',');
const groupingKeys = this.invocationState.groupingKeys.join(',');
const defaultGroupingKeys = this.invocationState.defaultGroupingKeys.join(',');
const newSearchParams = new URLSearchParams({
...(!this.invocationState.searchText ? {} : { q: this.invocationState.searchText }),
...(displayedCols === defaultCols ? {} : { cols: displayedCols }),
...(sortingKeys === defaultSortingKeys ? {} : { sortby: sortingKeys }),
...(groupingKeys === defaultGroupingKeys ? {} : { groupby: groupingKeys }),
});
const newSearchParamsStr = newSearchParams.toString();
return newSearchParamsStr ? '?' + newSearchParamsStr : '';
},
(newQueryStr) => {
const location = window.location;
const newUrl = `${location.protocol}//${location.host}${location.pathname}${newQueryStr}`;
window.history.replaceState({ path: newUrl }, '', newUrl);
},
{ fireImmediately: true }
)
);
this.addDisposer(
reaction(
() => this.configsStore.userConfigs.testResults.columnWidths,
(columnWidths) => (this.invocationState.customColumnWidths = columnWidths),
{ fireImmediately: true }
)
);
}
private renderBody() {
const state = this.invocationState;
if (state.invocationId === '') {
return html`
<div id="no-invocation">
No associated invocation.<br />
You need to integrate with ResultDB to see the test results.<br />
See <a href="http://go/resultdb" target="_blank">go/resultdb</a> or ask
<a href="mailto: luci-eng@google.com" target="_blank">luci-eng@</a> for help.
</div>
`;
}
return html`
${this.invocationState.warning
? html`<div id="test-results-tab-warning">${this.invocationState.warning}</div>`
: ''}
<milo-test-variants-table
style="--columns: ${this.invocationState.columnWidths.map((width) => width + 'px').join(' ')}"
></milo-test-variants-table>
`;
}
protected render = reportRenderError.bind(this)(() => {
return html`
<div id="header">
<milo-tvt-config-widget class="filters-container"></milo-tvt-config-widget>
<div class="filters-container-delimiter"></div>
<milo-test-search-filter></milo-test-search-filter>
<milo-hotkey key="x" .handler=${this.toggleAllVariantsByHotkey} title="press x to expand/collapse all entries">
<mwc-button dense unelevated @click=${() => this.toggleAllVariants(true)}>Expand All</mwc-button>
<mwc-button dense unelevated @click=${() => this.toggleAllVariants(false)}>Collapse All</mwc-button>
</milo-hotkey>
</div>
${this.renderBody()}
`;
});
static styles = [
commonStyle,
css`
:host {
/* Required to make 'position: sticky' work in Safari. */
display: block;
}
#header {
display: grid;
grid-template-columns: auto auto 1fr auto;
grid-gap: 5px;
height: 30px;
padding: 5px 10px 3px 10px;
position: sticky;
top: 0px;
background: white;
border-bottom: 1px solid var(--divider-color);
z-index: 2;
}
milo-test-search-filter {
max-width: 1000px;
}
mwc-button {
margin-top: 1px;
width: var(--expand-button-width);
}
.filters-container {
display: inline-block;
padding: 4px 5px 0;
}
.filters-container-delimiter {
border-left: 1px solid var(--divider-color);
width: 0px;
height: 100%;
}
#test-results-tab-warning {
background-color: var(--warning-color);
border-bottom: 1px solid var(--divider-color);
height: 24px;
line-height: 24px;
text-align: center;
}
`,
];
}