зеркало из https://github.com/mozilla/treeherder.git
Bug 1574628 - Improve graph colors and add symbols (#5752)
change color palette with accessibility in mind; add symbols
This commit is contained in:
Родитель
d9bf17542c
Коммит
364d7d98db
|
@ -12,6 +12,7 @@ import {
|
|||
endpoints,
|
||||
filterText,
|
||||
graphColors,
|
||||
graphSymbols,
|
||||
} from '../../../ui/perfherder/constants';
|
||||
import GraphsViewControls from '../../../ui/perfherder/graphs/GraphsViewControls';
|
||||
import repos from '../mock/repositories';
|
||||
|
@ -26,7 +27,12 @@ import {
|
|||
getApiUrl,
|
||||
} from '../../../ui/helpers/url';
|
||||
|
||||
const graphData = createGraphData(testData, [], [...graphColors]);
|
||||
const graphData = createGraphData(
|
||||
testData,
|
||||
[],
|
||||
[...graphColors],
|
||||
[...graphSymbols],
|
||||
);
|
||||
|
||||
const frameworks = [
|
||||
{ id: 1, name: 'talos' },
|
||||
|
|
|
@ -450,32 +450,28 @@ li.pagination-active.active > button {
|
|||
}
|
||||
|
||||
/* graph colors */
|
||||
.blue {
|
||||
border-left-color: #1752b8;
|
||||
.orange {
|
||||
border-left-color: #ffb851;
|
||||
}
|
||||
|
||||
.green {
|
||||
border-left-color: #19a572;
|
||||
.fire-red {
|
||||
border-left-color: #c92d2f;
|
||||
}
|
||||
|
||||
.darkorchid {
|
||||
border-left-color: #9932cc;
|
||||
.cerulean {
|
||||
border-left-color: #16bcde;
|
||||
}
|
||||
|
||||
.scarlet {
|
||||
border-left-color: #b81752;
|
||||
.blue-bell {
|
||||
border-left-color: #464876;
|
||||
}
|
||||
|
||||
.turquoise {
|
||||
border-left-color: #17a2b8;
|
||||
.purple {
|
||||
border-left-color: #921181;
|
||||
}
|
||||
|
||||
.magenta {
|
||||
border-left-color: #e252cf;
|
||||
}
|
||||
|
||||
.brown {
|
||||
border-left-color: #b87e17;
|
||||
.dark-puce {
|
||||
border-left-color: #4c3146;
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 1700px) {
|
||||
|
|
|
@ -63,12 +63,21 @@ export const alertStatusMap = {
|
|||
};
|
||||
|
||||
export const graphColors = [
|
||||
['scarlet', '#b81752'],
|
||||
['turquoise', '#17a2b8'],
|
||||
['green', '#19a572'],
|
||||
['brown', '#b87e17'],
|
||||
['darkorchid', '#9932cc'],
|
||||
['blue', '#1752b8'],
|
||||
['orange', '#FFB851'],
|
||||
['fire-red', '#C92D2F'],
|
||||
['cerulean', '#16BCDE'],
|
||||
['blue-bell', '#464876'],
|
||||
['purple', '#921181'],
|
||||
['dark-puce', '#4C3146'],
|
||||
];
|
||||
|
||||
export const graphSymbols = [
|
||||
['circle', 'outline'],
|
||||
['square', 'outline'],
|
||||
['triangleUp', 'outline'],
|
||||
['circle', 'fill'],
|
||||
['square', 'fill'],
|
||||
['triangleUp', 'fill'],
|
||||
];
|
||||
|
||||
export const phFrameworksWithRelatedBranches = [
|
||||
|
|
|
@ -416,19 +416,26 @@ class GraphsContainer extends React.Component {
|
|||
|
||||
<VictoryScatter
|
||||
name="scatter-plot"
|
||||
symbol={({ datum }) => (datum._z ? datum._z[0] : 'circle')}
|
||||
style={{
|
||||
data: {
|
||||
fill: ({ datum }) =>
|
||||
(datum.alertSummary || hasHighlightedRevision(datum)) &&
|
||||
highlightPoints
|
||||
fill: ({ datum }) => {
|
||||
const symbolType = datum._z || '';
|
||||
return ((datum.alertSummary ||
|
||||
hasHighlightedRevision(datum)) &&
|
||||
highlightPoints) ||
|
||||
symbolType[1] === 'fill'
|
||||
? datum.z
|
||||
: '#fff',
|
||||
: '#fff';
|
||||
},
|
||||
strokeOpacity: ({ datum }) =>
|
||||
(datum.alertSummary || hasHighlightedRevision(datum)) &&
|
||||
highlightPoints
|
||||
? 0.3
|
||||
: 100,
|
||||
stroke: ({ datum }) => datum.z,
|
||||
stroke: ({ datum }) => {
|
||||
return datum.z;
|
||||
},
|
||||
strokeWidth: ({ datum }) =>
|
||||
(datum.alertSummary || hasHighlightedRevision(datum)) &&
|
||||
highlightPoints
|
||||
|
|
|
@ -18,6 +18,7 @@ import { processSelectedParam, createGraphData } from '../helpers';
|
|||
import {
|
||||
endpoints,
|
||||
graphColors,
|
||||
graphSymbols,
|
||||
phTimeRanges,
|
||||
phDefaultTimeRangeValue,
|
||||
} from '../constants';
|
||||
|
@ -42,6 +43,7 @@ class GraphsView extends React.Component {
|
|||
options: {},
|
||||
loading: false,
|
||||
colors: [...graphColors],
|
||||
symbols: [...graphSymbols],
|
||||
showModal: false,
|
||||
visibilityChanged: false,
|
||||
};
|
||||
|
@ -165,19 +167,23 @@ class GraphsView extends React.Component {
|
|||
};
|
||||
|
||||
createGraphObject = async seriesData => {
|
||||
const { colors } = this.state;
|
||||
const { colors, symbols } = this.state;
|
||||
const alertSummaries = await Promise.all(
|
||||
seriesData.map(series =>
|
||||
this.getAlertSummaries(series.signature_id, series.repository_id),
|
||||
),
|
||||
);
|
||||
const newColors = [...colors];
|
||||
const newSymbols = [...symbols];
|
||||
|
||||
const graphData = createGraphData(
|
||||
seriesData,
|
||||
alertSummaries.flat(),
|
||||
newColors,
|
||||
newSymbols,
|
||||
);
|
||||
this.setState({ colors: newColors });
|
||||
|
||||
this.setState({ colors: newColors, symbols: newSymbols });
|
||||
return graphData;
|
||||
};
|
||||
|
||||
|
@ -314,6 +320,7 @@ class GraphsView extends React.Component {
|
|||
zoom,
|
||||
options,
|
||||
colors,
|
||||
symbols,
|
||||
showModal,
|
||||
visibilityChanged,
|
||||
} = this.state;
|
||||
|
@ -352,6 +359,7 @@ class GraphsView extends React.Component {
|
|||
this.setState(state, this.changeParams)
|
||||
}
|
||||
colors={colors}
|
||||
symbols={symbols}
|
||||
selectedDataPoint={selectedDataPoint}
|
||||
/>
|
||||
</div>
|
||||
|
@ -389,6 +397,7 @@ class GraphsView extends React.Component {
|
|||
zoom: {},
|
||||
selectedDataPoint: null,
|
||||
colors: [...graphColors],
|
||||
symbols: [...graphSymbols],
|
||||
},
|
||||
this.getTestData,
|
||||
)
|
||||
|
|
|
@ -8,6 +8,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
|||
import { faTimes } from '@fortawesome/free-solid-svg-icons';
|
||||
|
||||
import { graphColors, legendCardText } from '../constants';
|
||||
import GraphIcon from '../../shared/GraphIcon';
|
||||
|
||||
const LegendCard = ({
|
||||
series,
|
||||
|
@ -17,29 +18,36 @@ const LegendCard = ({
|
|||
selectedDataPoint,
|
||||
frameworks,
|
||||
colors,
|
||||
symbols,
|
||||
}) => {
|
||||
const updateSelectedTest = () => {
|
||||
const newColors = [...colors];
|
||||
const newSymbols = [...symbols];
|
||||
const errorMessages = [];
|
||||
let updates;
|
||||
const newTestData = [...testData].map(item => {
|
||||
if (item.signature_id === series.signature_id) {
|
||||
const isVisible = !item.visible;
|
||||
|
||||
if (isVisible && newColors.length) {
|
||||
if (isVisible && newColors.length && newSymbols.length) {
|
||||
item.color = newColors.pop();
|
||||
item.symbol = newSymbols.pop();
|
||||
item.visible = isVisible;
|
||||
item.data = item.data.map(test => ({
|
||||
...test,
|
||||
z: item.color[1],
|
||||
_z: item.symbol,
|
||||
}));
|
||||
} else if (!isVisible) {
|
||||
newColors.push(item.color);
|
||||
newSymbols.push(item.symbol);
|
||||
item.color = ['border-secondary', ''];
|
||||
item.symbol = ['circle', 'outline'];
|
||||
item.visible = isVisible;
|
||||
item.data = item.data.map(test => ({
|
||||
...test,
|
||||
z: item.color[1],
|
||||
_z: item.symbol,
|
||||
}));
|
||||
} else {
|
||||
errorMessages.push(
|
||||
|
@ -56,6 +64,7 @@ const LegendCard = ({
|
|||
updates = {
|
||||
testData: newTestData,
|
||||
colors: newColors,
|
||||
symbols: newSymbols,
|
||||
errorMessages,
|
||||
visibilityChanged: true,
|
||||
};
|
||||
|
@ -68,9 +77,10 @@ const LegendCard = ({
|
|||
updateState({ options, showModal: true });
|
||||
};
|
||||
|
||||
const resetParams = (testData, newColors = null) => {
|
||||
const resetParams = (testData, newColors = null, newSymbols = null) => {
|
||||
const updates = { testData };
|
||||
if (newColors) updates.colors = newColors;
|
||||
if (newSymbols) updates.symbols = newSymbols;
|
||||
|
||||
if (
|
||||
selectedDataPoint &&
|
||||
|
@ -125,6 +135,7 @@ const LegendCard = ({
|
|||
return framework ? framework.name : legendCardText.unknownFrameworkMessage;
|
||||
};
|
||||
const subtitleStyle = 'p-0 mb-0 border-0 text-secondary text-left';
|
||||
const symbolType = series.symbol || ['circle', 'outline'];
|
||||
|
||||
return (
|
||||
<FormGroup check className="pl-0 border">
|
||||
|
@ -145,6 +156,12 @@ const LegendCard = ({
|
|||
title="Add related configurations"
|
||||
type="button"
|
||||
>
|
||||
<GraphIcon
|
||||
iconType={symbolType[0]}
|
||||
fill={symbolType[1] === 'fill' ? series.color[1] : '#ffffff'}
|
||||
stroke={series.color[1]}
|
||||
/>
|
||||
|
||||
{series.name}
|
||||
</p>
|
||||
<p
|
||||
|
|
|
@ -696,15 +696,17 @@ export const retriggerMultipleJobs = async (
|
|||
);
|
||||
};
|
||||
|
||||
export const createGraphData = (seriesData, alertSummaries, colors) =>
|
||||
export const createGraphData = (seriesData, alertSummaries, colors, symbols) =>
|
||||
seriesData.map(series => {
|
||||
const color = colors.pop();
|
||||
const symbol = symbols.pop();
|
||||
// signature_id, framework_id and repository_name are
|
||||
// not renamed in camel case in order to match the fields
|
||||
// returned by the performance/summary API (since we only fetch
|
||||
// new data if a user adds additional tests to the graph)
|
||||
return {
|
||||
color: color || ['border-secondary', ''],
|
||||
symbol: symbol || ['circle', 'outline'],
|
||||
visible: Boolean(color),
|
||||
name: series.name,
|
||||
signature_id: series.signature_id,
|
||||
|
@ -718,6 +720,7 @@ export const createGraphData = (seriesData, alertSummaries, colors) =>
|
|||
x: new Date(dataPoint.push_timestamp),
|
||||
y: dataPoint.value,
|
||||
z: color ? color[1] : '',
|
||||
_z: symbol || ['circle', 'outline'],
|
||||
revision: dataPoint.revision,
|
||||
alertSummary: alertSummaries.find(
|
||||
item => item.push_id === dataPoint.push_id,
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
const GraphIcon = ({ iconType, fill, stroke }) => {
|
||||
let iconPath;
|
||||
|
||||
switch (iconType) {
|
||||
case 'circle':
|
||||
iconPath = <circle cx="9" cy="9" r="7" />;
|
||||
break;
|
||||
case 'square':
|
||||
iconPath = <rect x="1" y="1" width="15" height="15" />;
|
||||
break;
|
||||
case 'triangleUp':
|
||||
iconPath = <polygon points="1,13.5 7.1,2.3 13.5,13.5" />;
|
||||
break;
|
||||
default:
|
||||
iconPath = <circle cx="10" cy="10" r="5" />;
|
||||
}
|
||||
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="20px"
|
||||
height="20px"
|
||||
fill={fill || '#6c757d'}
|
||||
stroke={stroke || '#6c757d'}
|
||||
strokeWidth="2"
|
||||
>
|
||||
{iconPath}
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
GraphIcon.propTypes = {
|
||||
iconType: PropTypes.string,
|
||||
fill: PropTypes.string,
|
||||
stroke: PropTypes.string,
|
||||
};
|
||||
|
||||
GraphIcon.defaultProps = {
|
||||
iconType: 'circle',
|
||||
fill: '#ffffff',
|
||||
stroke: '#ccc',
|
||||
};
|
||||
|
||||
export default GraphIcon;
|
Загрузка…
Ссылка в новой задаче