| // Copyright 2019 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import {LitElement, html, css} from 'lit-element'; |
| import page from 'page'; |
| import {connectStore} from 'reducers/base.js'; |
| import * as issueV0 from 'reducers/issueV0.js'; |
| import 'elements/chops/chops-choice-buttons/chops-choice-buttons.js'; |
| import '../mr-mode-selector/mr-mode-selector.js'; |
| import './mr-grid-dropdown.js'; |
| import {SERVER_LIST_ISSUES_LIMIT} from 'shared/consts/index.js'; |
| import {urlWithNewParams} from 'shared/helpers.js'; |
| import {fieldsForIssue} from 'shared/issue-fields.js'; |
| |
| // A list of the valid default field names available in an issue grid. |
| // High cardinality fields must be excluded, so the grid only includes a subset |
| // of AVAILABLE FIELDS. |
| export const DEFAULT_GRID_FIELDS = Object.freeze([ |
| 'Project', |
| 'Attachments', |
| 'Blocked', |
| 'BlockedOn', |
| 'Blocking', |
| 'Component', |
| 'MergedInto', |
| 'Reporter', |
| 'Stars', |
| 'Status', |
| 'Type', |
| 'Owner', |
| ]); |
| |
| /** |
| * Component for displaying the controls shown on the Monorail issue grid page. |
| * @extends {LitElement} |
| */ |
| export class MrGridControls extends connectStore(LitElement) { |
| /** @override */ |
| static get styles() { |
| return css` |
| :host { |
| display: flex; |
| justify-content: space-between; |
| align-items: center; |
| box-sizing: border-box; |
| margin: 0.5em 0; |
| height: 32px; |
| } |
| mr-grid-dropdown { |
| padding-right: 20px; |
| } |
| .left-controls { |
| display: flex; |
| align-items: center; |
| justify-content: flex-start; |
| flex-grow: 0; |
| } |
| .right-controls { |
| display: flex; |
| align-items: center; |
| flex-grow: 0; |
| } |
| .issue-count { |
| display: inline-block; |
| padding-right: 20px; |
| } |
| `; |
| }; |
| |
| /** @override */ |
| render() { |
| const hideCounts = this.totalIssues === 0; |
| return html` |
| <div class="left-controls"> |
| <mr-grid-dropdown |
| class="row-selector" |
| .text=${'Rows'} |
| .items=${this.gridOptions} |
| .selection=${this.queryParams.y} |
| @change=${this._rowChanged}> |
| </mr-grid-dropdown> |
| <mr-grid-dropdown |
| class="col-selector" |
| .text=${'Cols'} |
| .items=${this.gridOptions} |
| .selection=${this.queryParams.x} |
| @change=${this._colChanged}> |
| </mr-grid-dropdown> |
| <chops-choice-buttons |
| class="cell-selector" |
| .options=${this.cellOptions} |
| .value=${this.cellType}> |
| </chops-choice-buttons> |
| </div> |
| <div class="right-controls"> |
| ${hideCounts ? '' : html` |
| <div class="issue-count"> |
| ${this.issueCount} |
| of |
| ${this.totalIssuesDisplay} |
| </div> |
| `} |
| <mr-mode-selector |
| .projectName=${this.projectName} |
| .queryParams=${this.queryParams} |
| value="grid" |
| ></mr-mode-selector> |
| </div> |
| `; |
| } |
| |
| /** @override */ |
| constructor() { |
| super(); |
| this.gridOptions = this._computeGridOptions([]); |
| this.queryParams = {}; |
| |
| this.totalIssues = 0; |
| |
| this._page = page; |
| }; |
| |
| /** @override */ |
| static get properties() { |
| return { |
| gridOptions: {type: Array}, |
| projectName: {tupe: String}, |
| queryParams: {type: Object}, |
| issueCount: {type: Number}, |
| totalIssues: {type: Number}, |
| _issues: {type: Array}, |
| }; |
| }; |
| |
| /** @override */ |
| stateChanged(state) { |
| this.totalIssues = issueV0.totalIssues(state) || 0; |
| this._issues = issueV0.issueList(state) || []; |
| } |
| |
| /** @override */ |
| update(changedProperties) { |
| if (changedProperties.has('_issues')) { |
| this.gridOptions = this._computeGridOptions(this._issues); |
| } |
| super.update(changedProperties); |
| } |
| |
| /** |
| * Gets what issue filtering options exist on the grid view. |
| * @param {Array<Issue>} issues The issues to find values on. |
| * @param {Array<string>=} defaultFields Available built in fields. |
| * @return {Array<string>} Array of names of fields you can filter by. |
| */ |
| _computeGridOptions(issues, defaultFields = DEFAULT_GRID_FIELDS) { |
| const availableFields = new Set(defaultFields); |
| issues.forEach((issue) => { |
| fieldsForIssue(issue, true).forEach((field) => { |
| availableFields.add(field); |
| }); |
| }); |
| const options = [...availableFields].sort(); |
| options.unshift('None'); |
| return options; |
| } |
| |
| /** |
| * @return {string} Display text of total issue number. |
| */ |
| get totalIssuesDisplay() { |
| if (this.issueCount === 1) { |
| return `${this.issueCount} issue shown`; |
| } else if (this.issueCount === SERVER_LIST_ISSUES_LIMIT) { |
| // Server has hard limit up to 100,000 list results |
| return `100,000+ issues shown`; |
| } |
| return `${this.issueCount} issues shown`; |
| } |
| |
| /** |
| * @return {string} What cell mode the user has selected. |
| * ie: Tiles, IDs, Counts |
| */ |
| get cellType() { |
| const cells = this.queryParams.cells; |
| return cells || 'tiles'; |
| } |
| |
| /** |
| * @return {Array<Object>} Cell options available to the user, formatted for |
| * <mr-mode-selector> |
| */ |
| get cellOptions() { |
| return [ |
| {text: 'Tile', value: 'tiles', |
| url: this._updatedGridViewUrl({}, ['cells'])}, |
| {text: 'IDs', value: 'ids', |
| url: this._updatedGridViewUrl({cells: 'ids'})}, |
| {text: 'Counts', value: 'counts', |
| url: this._updatedGridViewUrl({cells: 'counts'})}, |
| ]; |
| } |
| |
| /** |
| * Changes the URL parameters on the page in response to a user changing |
| * their row setting. |
| * @param {Event} e 'change' event fired by <mr-grid-dropdown> |
| */ |
| _rowChanged(e) { |
| const y = e.target.selection; |
| let deletedParams; |
| if (y === 'None') { |
| deletedParams = ['y']; |
| } |
| this._changeUrlParams({y}, deletedParams); |
| } |
| |
| /** |
| * Changes the URL parameters on the page in response to a user changing |
| * their col setting. |
| * @param {Event} e 'change' event fired by <mr-grid-dropdown> |
| */ |
| _colChanged(e) { |
| const x = e.target.selection; |
| let deletedParams; |
| if (x === 'None') { |
| deletedParams = ['x']; |
| } |
| this._changeUrlParams({x}, deletedParams); |
| } |
| |
| /** |
| * Helper method to update URL params with a new grid view URL. |
| * @param {Array<Object>} newParams |
| * @param {Array<string>} deletedParams |
| */ |
| _changeUrlParams(newParams, deletedParams) { |
| const newUrl = this._updatedGridViewUrl(newParams, deletedParams); |
| this._page(newUrl); |
| } |
| |
| /** |
| * Helper to generate a new grid view URL given a set of params. |
| * @param {Array<Object>} newParams |
| * @param {Array<string>} deletedParams |
| * @return {string} The generated URL. |
| */ |
| _updatedGridViewUrl(newParams, deletedParams) { |
| return urlWithNewParams(`/p/${this.projectName}/issues/list`, |
| this.queryParams, newParams, deletedParams); |
| } |
| }; |
| |
| customElements.define('mr-grid-controls', MrGridControls); |