зеркало из https://github.com/mozilla/treeherder.git
Bug 1617734 - Push health ui a11y improvements (#6059)
* CSS Cleanup * Use darker-info and darker-secondary for a11y * Put Parent Push metric at end of list and clean it up * Add a `scrollToLine` when clicking/expanding metric names
This commit is contained in:
Родитель
16263fd09e
Коммит
c7d873eda8
|
@ -56,7 +56,7 @@ For example:
|
|||
|
||||
Known reactstrap components that accept the `color` prop and work with custom Treeherder colors: `Badge`, `Button`, `Card`, `DropdownToggle`, `FormText`, `NavBar`, `Progress`, `Spinner`.
|
||||
|
||||
In case you need to add more custom colors, please add on [treeherder-global.css](https://github.com/mozilla/treeherder/blob/master/ui/css/treeherder-global.css#L371) style sheet.
|
||||
In case you need to add more custom colors, please add on [treeherder-custom-styles.css](https://github.com/mozilla/treeherder/blob/master/ui/css/treeherder-custom-styles.css#L348) style sheet.
|
||||
|
||||
### Inserting new colors
|
||||
|
||||
|
|
|
@ -52,4 +52,4 @@ Imports in JS/JSX must be ordered like so (with newlines between each group):
|
|||
|
||||
For CSS, we use [reactstrap](https://reactstrap.github.io/) and Bootstrap's utility classes as
|
||||
much as possible before adding custom CSS to a style sheet. Any custom style that can be made
|
||||
reusable should be named generically and stored in the `ui/css/treeherder-global.css` file.
|
||||
reusable should be named generically and stored in the `ui/css/treeherder-custom-styles.css` file.
|
||||
|
|
|
@ -420,14 +420,14 @@
|
|||
},
|
||||
{
|
||||
"sha": "f7352f24db7b97314617cd6a6c2c2a30210b2db4",
|
||||
"filename": "ui/css/treeherder-resultsets.css",
|
||||
"filename": "ui/css/treeherder-pushes.css",
|
||||
"status": "modified",
|
||||
"additions": 4,
|
||||
"deletions": 0,
|
||||
"changes": 4,
|
||||
"blob_url": "https://github.com/mozilla/treeherder/blob/5219f00e7af7b52e66e362d20bb5d4b0ceb84bfa/ui/css/treeherder-resultsets.css",
|
||||
"raw_url": "https://github.com/mozilla/treeherder/raw/5219f00e7af7b52e66e362d20bb5d4b0ceb84bfa/ui/css/treeherder-resultsets.css",
|
||||
"contents_url": "https://api.github.com/repos/mozilla/treeherder/contents/ui/css/treeherder-resultsets.css?ref=5219f00e7af7b52e66e362d20bb5d4b0ceb84bfa",
|
||||
"blob_url": "https://github.com/mozilla/treeherder/blob/5219f00e7af7b52e66e362d20bb5d4b0ceb84bfa/ui/css/treeherder-pushes.css",
|
||||
"raw_url": "https://github.com/mozilla/treeherder/raw/5219f00e7af7b52e66e362d20bb5d4b0ceb84bfa/ui/css/treeherder-pushes.css",
|
||||
"contents_url": "https://api.github.com/repos/mozilla/treeherder/contents/ui/css/treeherder-pushes.css?ref=5219f00e7af7b52e66e362d20bb5d4b0ceb84bfa",
|
||||
"patch": "@@ -121,6 +121,10 @@ fieldset[disabled] .btn-resultset:hover {\n overflow: auto;\n }\n \n+.revision[data-tags~=\"backout\"] .revision-comment{\n+ color: red;\n+}\n+\n .revision-holder > a {\n padding-top: 2px;\n font: 11px \"Lucida Console\",Monaco,monospace;"
|
||||
},
|
||||
{
|
||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
|||
import fetchMock from 'fetch-mock';
|
||||
import { render, cleanup, waitForElement } from '@testing-library/react';
|
||||
|
||||
import PushParent from '../../../ui/push-health/PushParent';
|
||||
import ParentPush from '../../../ui/push-health/ParentPush';
|
||||
|
||||
beforeEach(() => {
|
||||
fetchMock.get(
|
||||
|
@ -57,7 +57,7 @@ const getParent = (
|
|||
};
|
||||
|
||||
describe('PushParent', () => {
|
||||
const testPushParent = parent => <PushParent parent={parent} />;
|
||||
const testPushParent = parent => <ParentPush parent={parent} />;
|
||||
|
||||
test('should show a parent commit and health icon for that parent', async () => {
|
||||
const parent = getParent();
|
||||
|
|
|
@ -276,7 +276,7 @@ class PushViewSet(viewsets.ViewSet):
|
|||
'result': push_result,
|
||||
'metrics': {
|
||||
'parent': {
|
||||
'name': 'Parent',
|
||||
'name': 'Parent Push',
|
||||
'result': 'none',
|
||||
'details': parent_details,
|
||||
},
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Treeherder Base settings
|
||||
*
|
||||
* This file contains settings which are either not specific to our
|
||||
* UI component css files, or are shared across Treeherder, Logviewer,
|
||||
* UserGuide, and/or Perfherder.
|
||||
*
|
||||
* Including this class will make modifications to elements.
|
||||
*
|
||||
* Please add CSS class styles to the `treeherder-custom-styles.css` file
|
||||
* rather than here.
|
||||
*/
|
||||
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
font-size: 14px;
|
||||
line-height: 1.42;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
body {
|
||||
line-height: inherit;
|
||||
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
a:visited,
|
||||
.link-style:visited {
|
||||
color: purple;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #337ab7;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
label {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
kbd {
|
||||
display: inline-block;
|
||||
padding: 3px 5px;
|
||||
font: 11px/10px Consolas, 'Liberation Mono', Menlo, Courier, monospace;
|
||||
color: #555;
|
||||
vertical-align: middle;
|
||||
background-color: #fcfcfc;
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-color: #ccc #ccc #bbb;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
input[type='checkbox'] {
|
||||
margin-right: 5px;
|
||||
}
|
114
ui/css/treeherder-global.css → ui/css/treeherder-custom-styles.css
Executable file → Normal file
114
ui/css/treeherder-global.css → ui/css/treeherder-custom-styles.css
Executable file → Normal file
|
@ -1,7 +1,8 @@
|
|||
/*
|
||||
* Global settings
|
||||
* Treeherder style settings
|
||||
*
|
||||
* This file contains settings which are either not specific to our
|
||||
* This file contains opt-in styles which can be used across Treeherder
|
||||
* without affecting settings which are either not specific to our
|
||||
* UI component css files, or are shared across Treeherder, Logviewer,
|
||||
* UserGuide, and/or Perfherder.
|
||||
*
|
||||
|
@ -9,29 +10,6 @@
|
|||
* when adding new styles.
|
||||
*/
|
||||
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
font-size: 14px;
|
||||
line-height: 1.42;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
body {
|
||||
line-height: inherit;
|
||||
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
a:visited,
|
||||
.link-style:visited {
|
||||
color: purple;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #337ab7;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Bootstrap 4 will not show <a> as a link style if it has no href.
|
||||
This adds that style back. */
|
||||
.link-style {
|
||||
|
@ -67,10 +45,6 @@ a {
|
|||
justify-content: center;
|
||||
}
|
||||
|
||||
input[type='checkbox'] {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.dropdown-menu > li > a {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
@ -228,71 +202,10 @@ input[type='checkbox'] {
|
|||
background-color: white;
|
||||
}
|
||||
|
||||
/*
|
||||
* Buttons
|
||||
*
|
||||
* Currently used in pushes only but could be
|
||||
* potentially used elsewhere.
|
||||
*/
|
||||
|
||||
.btn {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.th-action-button span.dropdown:hover ul.dropdown-menu {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.th-action-button:after {
|
||||
content: none;
|
||||
}
|
||||
|
||||
.label[href] {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.btn-link {
|
||||
font-weight: normal;
|
||||
color: #337ab7;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.btn-xs {
|
||||
padding: 3px 6px 3px 6px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.btn-sm {
|
||||
padding: 5px 10px;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.btn-light-bordered {
|
||||
border: 1px solid darkgrey;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.btn-primary-soft {
|
||||
color: #fff;
|
||||
background-color: #337ab7;
|
||||
border-color: #2e6da4;
|
||||
}
|
||||
|
||||
/*
|
||||
* Colors
|
||||
*/
|
||||
|
||||
.white,
|
||||
.white a {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.xlightgray,
|
||||
.xlightgray a {
|
||||
color: #e6e6e6;
|
||||
}
|
||||
|
||||
.lightgray,
|
||||
.lightgray a {
|
||||
color: lightgray;
|
||||
|
@ -319,19 +232,6 @@ input[type='checkbox'] {
|
|||
* Text
|
||||
*/
|
||||
|
||||
kbd {
|
||||
display: inline-block;
|
||||
padding: 3px 5px;
|
||||
font: 11px/10px Consolas, 'Liberation Mono', Menlo, Courier, monospace;
|
||||
color: #555;
|
||||
vertical-align: middle;
|
||||
background-color: #fcfcfc;
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-color: #ccc #ccc #bbb;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.queryparam {
|
||||
display: inline-block;
|
||||
font: 12px Consolas, 'Liberation Mono', Menlo, Courier, monospace;
|
||||
|
@ -352,14 +252,6 @@ kbd {
|
|||
color: #737373;
|
||||
}
|
||||
|
||||
label {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
/*
|
||||
* Bootstrap/Reactstrap Custom Colors
|
||||
*/
|
|
@ -14,6 +14,7 @@
|
|||
line-height: 1.32;
|
||||
display: none;
|
||||
transition: transform 0.1s;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.group-btn {
|
||||
|
|
|
@ -15,11 +15,11 @@ export const hashFunction = someString => {
|
|||
return hash >>> 0;
|
||||
};
|
||||
|
||||
export const scrollToLine = (selector, time, iteration = 0) => {
|
||||
export const scrollToLine = (selector, time, iteration = 0, options = true) => {
|
||||
const line = document.querySelector(selector);
|
||||
|
||||
if (line !== null) {
|
||||
line.scrollIntoView(true);
|
||||
line.scrollIntoView(options);
|
||||
return;
|
||||
}
|
||||
if (iteration < 10) {
|
||||
|
|
|
@ -2,7 +2,8 @@ import React from 'react';
|
|||
import { render } from 'react-dom';
|
||||
import 'react-table/react-table.css';
|
||||
|
||||
import '../css/treeherder-global.css';
|
||||
import '../css/treeherder-base.css';
|
||||
import '../css/treeherder-custom-styles.css';
|
||||
import '../css/treeherder-navbar.css';
|
||||
import '../css/intermittent-failures.css';
|
||||
import App from './App';
|
||||
|
|
|
@ -276,8 +276,8 @@ class CustomJobActions extends React.PureComponent {
|
|||
<ModalFooter>
|
||||
{isLoggedIn ? (
|
||||
<Button
|
||||
color="secondary"
|
||||
className={`btn btn-primary-soft ${triggering ? 'disabled' : ''}`}
|
||||
color="darker-info"
|
||||
className={triggering ? 'disabled' : ''}
|
||||
onClick={this.triggerAction}
|
||||
title={isLoggedIn ? 'Trigger this action' : 'Not logged in'}
|
||||
>
|
||||
|
|
|
@ -582,7 +582,7 @@ class PinBoard extends React.Component {
|
|||
>
|
||||
<ButtonGroup className="save-btn-group">
|
||||
<Button
|
||||
className={`btn-light-bordered save-btn ${
|
||||
className={`save-btn ${
|
||||
!isLoggedIn || !this.canSaveClassifications()
|
||||
? 'disabled'
|
||||
: ''
|
||||
|
@ -598,7 +598,7 @@ class PinBoard extends React.Component {
|
|||
<DropdownToggle
|
||||
size="xs"
|
||||
caret
|
||||
className={`btn-light-bordered ${
|
||||
className={`bg-light ${
|
||||
!this.hasPinnedJobs() && !this.pinboardIsDirty()
|
||||
? 'disabled'
|
||||
: ''
|
||||
|
|
|
@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
|
|||
import { connect } from 'react-redux';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faSpinner } from '@fortawesome/free-solid-svg-icons';
|
||||
import { Button } from 'reactstrap';
|
||||
|
||||
import { thMaxPushFetchSize } from '../../../helpers/constants';
|
||||
import { toDateStr, toShortDateStr } from '../../../helpers/display';
|
||||
|
@ -217,13 +218,14 @@ class SimilarJobsTab extends React.Component {
|
|||
</tbody>
|
||||
</table>
|
||||
{hasNextPage && (
|
||||
<button
|
||||
className="btn btn-light-bordered btn-sm link-style"
|
||||
<Button
|
||||
outline
|
||||
className="bg-light"
|
||||
type="button"
|
||||
onClick={this.showNext}
|
||||
>
|
||||
Show previous jobs
|
||||
</button>
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
<div className="similar-job-detail-panel">
|
||||
|
|
|
@ -4,6 +4,7 @@ import Highlighter from 'react-highlight-words';
|
|||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faThumbtack } from '@fortawesome/free-solid-svg-icons';
|
||||
import { connect } from 'react-redux';
|
||||
import { Button } from 'reactstrap';
|
||||
|
||||
import { getSearchWords } from '../../../../helpers/display';
|
||||
import { getBugUrl } from '../../../../helpers/url';
|
||||
|
@ -22,14 +23,16 @@ function BugListItem(props) {
|
|||
|
||||
return (
|
||||
<li>
|
||||
<button
|
||||
className="btn btn-xs btn-light-bordered"
|
||||
<Button
|
||||
className="bg-light px-2 py-1"
|
||||
outline
|
||||
size="xs"
|
||||
type="button"
|
||||
onClick={() => addBug(bug, selectedJobFull)}
|
||||
title="add to list of bugs to associate with all pinned jobs"
|
||||
>
|
||||
<FontAwesomeIcon icon={faThumbtack} title="Select bug" />
|
||||
</button>
|
||||
</Button>
|
||||
<a
|
||||
className={`${bugClassName} ml-1`}
|
||||
href={bugUrl}
|
||||
|
|
|
@ -30,9 +30,9 @@ export default class SuggestionsListItem extends React.Component {
|
|||
<li>
|
||||
<div>
|
||||
<Button
|
||||
color="link"
|
||||
className="bg-light py-1 px-2"
|
||||
outline
|
||||
size="xs"
|
||||
className="btn btn-light-bordered link-style"
|
||||
onClick={() => toggleBugFiler(suggestion)}
|
||||
title="file a bug for this failure"
|
||||
>
|
||||
|
|
|
@ -201,20 +201,23 @@ export default class ActiveFilters extends React.Component {
|
|||
))}
|
||||
</select>
|
||||
)}
|
||||
<button
|
||||
<Button
|
||||
type="submit"
|
||||
className="btn btn-light-bordered btn-sm"
|
||||
size="sm"
|
||||
className="bg-light"
|
||||
onClick={this.addNewFieldFilter}
|
||||
outline
|
||||
>
|
||||
add
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-light-bordered btn-sm"
|
||||
</Button>
|
||||
<Button
|
||||
className="bg-light"
|
||||
outline
|
||||
size="sm"
|
||||
onClick={this.clearNewFieldFilter}
|
||||
>
|
||||
cancel
|
||||
</button>
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
@ -47,7 +47,6 @@ function FiltersMenu(props) {
|
|||
<DropdownToggle
|
||||
title="Set filters"
|
||||
className="btn-view-nav nav-menu-btn"
|
||||
nav
|
||||
caret
|
||||
>
|
||||
Filters
|
||||
|
|
|
@ -30,7 +30,7 @@ class HealthMenu extends PureComponent {
|
|||
<DropdownToggle
|
||||
caret
|
||||
title="Change visibility of the Push Health badge/link"
|
||||
className="btn btn-view-nav btn-sm nav-menu-btn dropdown-toggle"
|
||||
className="btn-view-nav nav-menu-btn"
|
||||
>
|
||||
Health
|
||||
</DropdownToggle>
|
||||
|
|
|
@ -13,7 +13,6 @@ const InfraMenu = () => (
|
|||
<DropdownToggle
|
||||
className="btn-view-nav nav-menu-btn"
|
||||
title="Infrastructure status"
|
||||
nav
|
||||
caret
|
||||
>
|
||||
Infra
|
||||
|
|
|
@ -53,7 +53,6 @@ export default function ReposMenu(props) {
|
|||
<DropdownToggle
|
||||
id="repoLabel"
|
||||
className="btn-view-nav nav-menu-btn"
|
||||
nav
|
||||
caret
|
||||
title="Watch a repo"
|
||||
>
|
||||
|
|
|
@ -3,13 +3,14 @@ import { render } from 'react-dom';
|
|||
|
||||
// Treeherder Styles
|
||||
import '../css/treeherder.css';
|
||||
import '../css/treeherder-global.css';
|
||||
import '../css/treeherder-base.css';
|
||||
import '../css/treeherder-custom-styles.css';
|
||||
import '../css/treeherder-navbar.css';
|
||||
import '../css/treeherder-navbar-panels.css';
|
||||
import '../css/treeherder-notifications.css';
|
||||
import '../css/treeherder-details-panel.css';
|
||||
import '../css/treeherder-job-buttons.css';
|
||||
import '../css/treeherder-resultsets.css';
|
||||
import '../css/treeherder-pushes.css';
|
||||
import '../css/treeherder-pinboard.css';
|
||||
import '../css/treeherder-bugfiler.css';
|
||||
import '../css/treeherder-fuzzyfinder.css';
|
||||
|
|
|
@ -2,7 +2,8 @@ import React from 'react';
|
|||
import { render } from 'react-dom';
|
||||
|
||||
// Treeherder Styles
|
||||
import '../css/treeherder-global.css';
|
||||
import '../css/treeherder-base.css';
|
||||
import '../css/treeherder-custom-styles.css';
|
||||
import '../css/treeherder-navbar.css';
|
||||
import './logviewer.css';
|
||||
|
||||
|
|
|
@ -2,7 +2,8 @@ import React from 'react';
|
|||
import { render } from 'react-dom';
|
||||
import 'react-table/react-table.css';
|
||||
|
||||
import '../css/treeherder-global.css';
|
||||
import '../css/treeherder-base.css';
|
||||
import '../css/treeherder-custom-styles.css';
|
||||
import '../css/treeherder-navbar.css';
|
||||
import '../css/perf.css';
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@ class GroupedTests extends Component {
|
|||
<div key={group.id} data-testid="test-grouping">
|
||||
<Button
|
||||
id={`${group.id}-group`}
|
||||
color="secondary"
|
||||
color="darker-secondary"
|
||||
outline
|
||||
className="p-3 bg-light text-center text-monospace border-bottom-0 border-right-0 border-left-0 border-secondary w-100"
|
||||
title="Click to expand for test detail"
|
||||
|
|
|
@ -6,6 +6,7 @@ import {
|
|||
faCheckCircle,
|
||||
faExclamationTriangle,
|
||||
} from '@fortawesome/free-solid-svg-icons';
|
||||
import camelCase from 'lodash/camelCase';
|
||||
|
||||
import ErrorMessages from '../shared/ErrorMessages';
|
||||
import NotificationList from '../shared/NotificationList';
|
||||
|
@ -16,6 +17,7 @@ import {
|
|||
import PushModel from '../models/push';
|
||||
import StatusProgress from '../shared/StatusProgress';
|
||||
import { getPercentComplete } from '../helpers/display';
|
||||
import { scrollToLine } from '../helpers/utils';
|
||||
import {
|
||||
createQueryParams,
|
||||
parseQueryParams,
|
||||
|
@ -28,7 +30,7 @@ import Metric from './Metric';
|
|||
import Navigation from './Navigation';
|
||||
import TestMetric from './TestMetric';
|
||||
import JobListMetric from './JobListMetric';
|
||||
import PushParent from './PushParent';
|
||||
import ParentPush from './ParentPush';
|
||||
|
||||
export default class Health extends React.PureComponent {
|
||||
constructor(props) {
|
||||
|
@ -45,7 +47,7 @@ export default class Health extends React.PureComponent {
|
|||
failureMessage: null,
|
||||
notifications: [],
|
||||
progressExpanded: true,
|
||||
parentExpanded: false,
|
||||
parentPushExpanded: false,
|
||||
lintingExpanded: false,
|
||||
buildsExpanded: false,
|
||||
testsExpanded: false,
|
||||
|
@ -98,7 +100,7 @@ export default class Health extends React.PureComponent {
|
|||
const notification = {
|
||||
...options,
|
||||
message,
|
||||
severity: severity || 'info',
|
||||
severity: severity || 'darker-info',
|
||||
created: Date.now(),
|
||||
};
|
||||
const newNotifications = [notification, ...notifications];
|
||||
|
@ -114,13 +116,21 @@ export default class Health extends React.PureComponent {
|
|||
this.setState(clearNotificationAtIndex(notifications, index));
|
||||
};
|
||||
|
||||
toggleExpanded = metricName => {
|
||||
const key = `${metricName.toLowerCase()}Expanded`;
|
||||
const { [key]: oldToggle } = this.state;
|
||||
setExpanded = (metricName, expanded) => {
|
||||
const root = camelCase(metricName);
|
||||
const key = `${root}Expanded`;
|
||||
const { [key]: oldExpanded } = this.state;
|
||||
|
||||
this.setState({
|
||||
[key]: !oldToggle,
|
||||
});
|
||||
if (oldExpanded !== expanded) {
|
||||
this.setState({
|
||||
[key]: expanded,
|
||||
});
|
||||
} else if (expanded) {
|
||||
scrollToLine(`#${root}Metric`, 0, 0, {
|
||||
behavior: 'smooth',
|
||||
block: 'center',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
filter = searchStr => {
|
||||
|
@ -149,7 +159,7 @@ export default class Health extends React.PureComponent {
|
|||
notifications,
|
||||
status,
|
||||
progressExpanded,
|
||||
parentExpanded,
|
||||
parentPushExpanded,
|
||||
lintingExpanded,
|
||||
buildsExpanded,
|
||||
testsExpanded,
|
||||
|
@ -180,7 +190,7 @@ export default class Health extends React.PureComponent {
|
|||
{!!tests && (
|
||||
<Nav className="metric-buttons mb-2 pt-2 pl-3 justify-content-between w-100">
|
||||
<span>
|
||||
{[progress, parent, linting, builds, tests, performance].map(
|
||||
{[progress, linting, builds, tests, performance, parent].map(
|
||||
metric => (
|
||||
<span key={metric.name}>
|
||||
{!!metric.details && (
|
||||
|
@ -191,7 +201,7 @@ export default class Health extends React.PureComponent {
|
|||
title={`Click to toggle ${
|
||||
metric.name
|
||||
}: ${metric.result.toUpperCase()}`}
|
||||
onClick={() => this.toggleExpanded(metric.name)}
|
||||
onClick={() => this.setExpanded(metric.name, true)}
|
||||
key={metric.name}
|
||||
>
|
||||
{metric.name}
|
||||
|
@ -225,7 +235,7 @@ export default class Health extends React.PureComponent {
|
|||
)}
|
||||
</Navbar>
|
||||
</Navigation>
|
||||
<Container fluid className="mt-2">
|
||||
<Container fluid className="mt-2 mb-5">
|
||||
<NotificationList
|
||||
notifications={notifications}
|
||||
clearNotification={this.clearNotification}
|
||||
|
@ -237,7 +247,7 @@ export default class Health extends React.PureComponent {
|
|||
name="Progress"
|
||||
result=""
|
||||
expanded={progressExpanded}
|
||||
toggleExpanded={this.toggleExpanded}
|
||||
setExpanded={this.setExpanded}
|
||||
>
|
||||
<div>
|
||||
<div>{percentComplete}% Complete</div>
|
||||
|
@ -245,25 +255,13 @@ export default class Health extends React.PureComponent {
|
|||
</div>
|
||||
</Metric>
|
||||
</Row>
|
||||
{parent.details && (
|
||||
<Row className="w-100">
|
||||
<Metric
|
||||
name="Parent"
|
||||
result=""
|
||||
expanded={parentExpanded}
|
||||
toggleExpanded={this.toggleExpanded}
|
||||
>
|
||||
<PushParent parent={parent.details} />
|
||||
</Metric>
|
||||
</Row>
|
||||
)}
|
||||
<Row>
|
||||
<JobListMetric
|
||||
data={linting}
|
||||
repo={repo}
|
||||
revision={revision}
|
||||
expanded={lintingExpanded}
|
||||
toggleExpanded={this.toggleExpanded}
|
||||
setExpanded={this.setExpanded}
|
||||
/>
|
||||
</Row>
|
||||
<Row>
|
||||
|
@ -272,7 +270,7 @@ export default class Health extends React.PureComponent {
|
|||
repo={repo}
|
||||
revision={revision}
|
||||
expanded={buildsExpanded}
|
||||
toggleExpanded={this.toggleExpanded}
|
||||
setExpanded={this.setExpanded}
|
||||
/>
|
||||
</Row>
|
||||
<Row>
|
||||
|
@ -284,7 +282,7 @@ export default class Health extends React.PureComponent {
|
|||
user={user}
|
||||
notify={this.notify}
|
||||
expanded={testsExpanded}
|
||||
toggleExpanded={this.toggleExpanded}
|
||||
setExpanded={this.setExpanded}
|
||||
searchStr={searchStr}
|
||||
/>
|
||||
</Row>
|
||||
|
@ -294,9 +292,21 @@ export default class Health extends React.PureComponent {
|
|||
repo={repo}
|
||||
revision={revision}
|
||||
expanded={performanceExpanded}
|
||||
toggleExpanded={this.toggleExpanded}
|
||||
setExpanded={this.setExpanded}
|
||||
/>
|
||||
</Row>
|
||||
{parent.details && (
|
||||
<Row className="w-100">
|
||||
<Metric
|
||||
name="Parent Push"
|
||||
result=""
|
||||
expanded={parentPushExpanded}
|
||||
setExpanded={this.setExpanded}
|
||||
>
|
||||
<ParentPush parent={parent.details} />
|
||||
</Metric>
|
||||
</Row>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{failureMessage && <ErrorMessages failureMessage={failureMessage} />}
|
||||
|
|
|
@ -6,7 +6,7 @@ import Job from './Job';
|
|||
|
||||
export default class JobListMetric extends React.PureComponent {
|
||||
render() {
|
||||
const { data, repo, revision, expanded, toggleExpanded } = this.props;
|
||||
const { data, repo, revision, expanded, setExpanded } = this.props;
|
||||
const { name, result, details } = data;
|
||||
|
||||
return (
|
||||
|
@ -14,7 +14,7 @@ export default class JobListMetric extends React.PureComponent {
|
|||
name={name}
|
||||
result={result}
|
||||
expanded={expanded}
|
||||
toggleExpanded={toggleExpanded}
|
||||
setExpanded={setExpanded}
|
||||
>
|
||||
<div>
|
||||
{details.length ? (
|
||||
|
@ -41,6 +41,6 @@ JobListMetric.propTypes = {
|
|||
data: PropTypes.object.isRequired,
|
||||
repo: PropTypes.string.isRequired,
|
||||
revision: PropTypes.string.isRequired,
|
||||
toggleExpanded: PropTypes.func.isRequired,
|
||||
setExpanded: PropTypes.func.isRequired,
|
||||
expanded: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
|
|
@ -3,12 +3,31 @@ import PropTypes from 'prop-types';
|
|||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faMinusSquare } from '@fortawesome/free-regular-svg-icons';
|
||||
import { Button, Badge, Row, Col, Collapse, Card, CardBody } from 'reactstrap';
|
||||
import camelCase from 'lodash/camelCase';
|
||||
|
||||
import { resultColorMap } from './helpers';
|
||||
|
||||
export default class Metric extends React.PureComponent {
|
||||
componentDidUpdate(prevProps) {
|
||||
const { name, expanded } = this.props;
|
||||
const metricName = `${camelCase(name)}Metric`;
|
||||
|
||||
if (expanded && !prevProps.expanded) {
|
||||
const elem = document.getElementById(metricName);
|
||||
|
||||
if (elem) {
|
||||
// componentDidUpdate happens after the props are updated, but prior
|
||||
// to rendering. So we must wait just a bit before the elements will
|
||||
// exist and we try to scroll to them.
|
||||
setTimeout(() => {
|
||||
elem.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
}, 200);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { result, name, expanded, children, toggleExpanded } = this.props;
|
||||
const { result, name, expanded, children, setExpanded } = this.props;
|
||||
const resultColor = resultColorMap[result];
|
||||
|
||||
return (
|
||||
|
@ -18,12 +37,15 @@ export default class Metric extends React.PureComponent {
|
|||
<Col>
|
||||
<Row className="justify-content-between">
|
||||
<Button
|
||||
onClick={() => toggleExpanded(name)}
|
||||
onClick={() => setExpanded(name, false)}
|
||||
outline
|
||||
className="border-0"
|
||||
aria-expanded={expanded}
|
||||
>
|
||||
<span className="metric-name align-top font-weight-bold">
|
||||
<span
|
||||
className="metric-name align-top font-weight-bold"
|
||||
id={`${camelCase(name)}Metric`}
|
||||
>
|
||||
{name}
|
||||
</span>
|
||||
<span>
|
||||
|
@ -56,7 +78,7 @@ Metric.propTypes = {
|
|||
result: PropTypes.string.isRequired,
|
||||
name: PropTypes.string.isRequired,
|
||||
children: PropTypes.object.isRequired,
|
||||
toggleExpanded: PropTypes.func.isRequired,
|
||||
setExpanded: PropTypes.func.isRequired,
|
||||
expanded: PropTypes.bool,
|
||||
};
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import PushHealthStatus from '../shared/PushHealthStatus';
|
|||
import { getJobsUrl } from '../helpers/url';
|
||||
import RepositoryModel from '../models/repository';
|
||||
|
||||
const PushParent = props => {
|
||||
const ParentPush = props => {
|
||||
const {
|
||||
parent: { repository, revision, jobCounts, exactMatch, parentSha, id },
|
||||
} = props;
|
||||
|
@ -53,7 +53,7 @@ const PushParent = props => {
|
|||
);
|
||||
};
|
||||
|
||||
PushParent.propTypes = {
|
||||
ParentPush.propTypes = {
|
||||
parent: PropTypes.shape({
|
||||
repository: PropTypes.object.isRequired,
|
||||
revision: PropTypes.string.isRequired,
|
||||
|
@ -66,4 +66,4 @@ PushParent.propTypes = {
|
|||
}).isRequired,
|
||||
};
|
||||
|
||||
export default PushParent;
|
||||
export default ParentPush;
|
|
@ -98,7 +98,10 @@ class TestFailure extends React.PureComponent {
|
|||
{tier > 1 && (
|
||||
<span className="ml-1 small text-muted">[tier-{tier}]</span>
|
||||
)}
|
||||
<span color="secondary" className="text-uppercase ml-1 mr-1">
|
||||
<span
|
||||
color="text-darker-secondary"
|
||||
className="text-uppercase ml-1 mr-1"
|
||||
>
|
||||
{action} :
|
||||
</span>
|
||||
{failJobs.map(failJob => (
|
||||
|
@ -153,7 +156,7 @@ class TestFailure extends React.PureComponent {
|
|||
<span>
|
||||
<Button
|
||||
id={key}
|
||||
className="border-0 text-info btn-sm p-1"
|
||||
className="border-0 text-darker-info btn-sm p-1"
|
||||
outline
|
||||
onClick={this.toggleDetails}
|
||||
>
|
||||
|
@ -209,7 +212,7 @@ class TestFailure extends React.PureComponent {
|
|||
<span title="Best guess at a classification" className="ml-auto">
|
||||
{classificationMap[suggestedClassification]}
|
||||
<Badge
|
||||
color="secondary"
|
||||
color="darker-secondary"
|
||||
className="ml-2 mr-3"
|
||||
title="Confidence in this classification guess"
|
||||
>
|
||||
|
|
|
@ -15,7 +15,7 @@ export default class TestMetric extends React.PureComponent {
|
|||
notify,
|
||||
currentRepo,
|
||||
expanded,
|
||||
toggleExpanded,
|
||||
setExpanded,
|
||||
searchStr,
|
||||
} = this.props;
|
||||
const { name, result, details } = data;
|
||||
|
@ -27,7 +27,7 @@ export default class TestMetric extends React.PureComponent {
|
|||
name={name}
|
||||
result={result}
|
||||
expanded={expanded}
|
||||
toggleExpanded={toggleExpanded}
|
||||
setExpanded={setExpanded}
|
||||
>
|
||||
<div className="border-bottom border-secondary">
|
||||
<ClassificationGroup
|
||||
|
@ -37,7 +37,9 @@ export default class TestMetric extends React.PureComponent {
|
|||
currentRepo={currentRepo}
|
||||
revision={revision}
|
||||
className="mb-5"
|
||||
headerColor={needInvestigationLength ? 'danger' : 'secondary'}
|
||||
headerColor={
|
||||
needInvestigationLength ? 'danger' : 'darker-secondary'
|
||||
}
|
||||
user={user}
|
||||
hasRetriggerAll
|
||||
notify={notify}
|
||||
|
@ -50,7 +52,7 @@ export default class TestMetric extends React.PureComponent {
|
|||
currentRepo={currentRepo}
|
||||
revision={revision}
|
||||
className="mb-5"
|
||||
headerColor="secondary"
|
||||
headerColor="darker-secondary"
|
||||
expanded={false}
|
||||
user={user}
|
||||
notify={notify}
|
||||
|
@ -77,7 +79,7 @@ TestMetric.propTypes = {
|
|||
currentRepo: PropTypes.object.isRequired,
|
||||
revision: PropTypes.string.isRequired,
|
||||
notify: PropTypes.func.isRequired,
|
||||
toggleExpanded: PropTypes.func.isRequired,
|
||||
setExpanded: PropTypes.func.isRequired,
|
||||
expanded: PropTypes.bool.isRequired,
|
||||
searchStr: PropTypes.string.isRequired,
|
||||
};
|
||||
|
|
|
@ -2,9 +2,9 @@ export const resultColorMap = {
|
|||
pass: 'success',
|
||||
fail: 'danger',
|
||||
indeterminate: 'warning',
|
||||
done: 'info',
|
||||
'in progress': 'secondary',
|
||||
none: 'info',
|
||||
done: 'darker-info',
|
||||
'in progress': 'darker-secondary',
|
||||
none: 'darker-info',
|
||||
};
|
||||
|
||||
export const filterTests = (tests, searchStr) => {
|
||||
|
|
|
@ -2,6 +2,7 @@ import React from 'react';
|
|||
import { render } from 'react-dom';
|
||||
|
||||
// Treeherder Styles
|
||||
import '../css/treeherder-custom-styles.css';
|
||||
import '../css/treeherder-navbar.css';
|
||||
import '../css/treeherder-job-buttons.css';
|
||||
import '../css/treeherder-notifications.css';
|
||||
|
|
|
@ -66,7 +66,7 @@ const menuItems = [
|
|||
|
||||
const HelpMenu = () => (
|
||||
<UncontrolledDropdown>
|
||||
<DropdownToggle className="btn-view-nav nav-menu-btn" nav caret>
|
||||
<DropdownToggle className="btn-view-nav nav-menu-btn" caret>
|
||||
<FontAwesomeIcon
|
||||
icon={faQuestionCircle}
|
||||
className="lightgray mr-1"
|
||||
|
|
|
@ -52,5 +52,5 @@ LogoMenu.propTypes = {
|
|||
|
||||
LogoMenu.defaultProps = {
|
||||
menuImage: null,
|
||||
colorClass: 'white',
|
||||
colorClass: 'text-white',
|
||||
};
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import React from 'react';
|
||||
import { render } from 'react-dom';
|
||||
|
||||
import '../css/treeherder-global.css';
|
||||
import '../css/treeherder-base.css';
|
||||
import '../css/treeherder-custom-styles.css';
|
||||
import '../css/treeherder-userguide.css';
|
||||
import '../css/treeherder-job-buttons.css';
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче