Add team drilldown
This commit is contained in:
Родитель
facff1c1e5
Коммит
349e90b2c2
|
@ -41,11 +41,11 @@ const constructQuery = (metrics, product, component) => Object.values(metrics).m
|
|||
});
|
||||
|
||||
const BugzillaComponentDetails = ({
|
||||
classes, bugzillaEmail, product, component, metrics = {}, onGoBack,
|
||||
classes, bugzillaEmail, product, component, title, metrics = {}, onGoBack,
|
||||
}) => (
|
||||
<DetailView title={`${product}::${component}`} onGoBack={onGoBack}>
|
||||
<DetailView title={title} onGoBack={onGoBack}>
|
||||
<div>
|
||||
<h4 className={classes.subtitle}>{bugzillaEmail}</h4>
|
||||
{bugzillaEmail && <h4 className={classes.subtitle}>{bugzillaEmail}</h4>}
|
||||
{Object.keys(metrics).sort().map(metric => (
|
||||
metrics[metric] && (
|
||||
<div key={metric} className={classes.metric}>
|
||||
|
@ -57,7 +57,7 @@ const BugzillaComponentDetails = ({
|
|||
)
|
||||
))}
|
||||
<BugzillaGraph
|
||||
label={`${product}::${component}`}
|
||||
label={title}
|
||||
queries={constructQuery(METRICS, product, component)}
|
||||
/>
|
||||
</div>
|
||||
|
@ -67,14 +67,22 @@ const BugzillaComponentDetails = ({
|
|||
|
||||
BugzillaComponentDetails.propTypes = {
|
||||
classes: PropTypes.shape({}).isRequired,
|
||||
bugzillaEmail: PropTypes.string.isRequired,
|
||||
product: PropTypes.string.isRequired,
|
||||
component: PropTypes.string.isRequired,
|
||||
bugzillaEmail: PropTypes.string,
|
||||
product: PropTypes.oneOfType([
|
||||
PropTypes.arrayOf(PropTypes.string),
|
||||
PropTypes.string,
|
||||
]).isRequired,
|
||||
component: PropTypes.oneOfType([
|
||||
PropTypes.arrayOf(PropTypes.string),
|
||||
PropTypes.string,
|
||||
]).isRequired,
|
||||
metrics: PropTypes.shape({}),
|
||||
title: PropTypes.string.isRequired,
|
||||
onGoBack: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
BugzillaComponentDetails.defaultProps = {
|
||||
bugzillaEmail: '',
|
||||
metrics: {},
|
||||
};
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ const BugzillaComponents = ({
|
|||
{bugzillaComponents
|
||||
.sort(sortByComponentName)
|
||||
.map(({
|
||||
label, component, product, metrics = {},
|
||||
label, component, product, metrics = {}, teamKey = null,
|
||||
}) => (
|
||||
<tr key={label}>
|
||||
{onComponentDetails && (
|
||||
|
@ -51,12 +51,16 @@ const BugzillaComponents = ({
|
|||
name={label}
|
||||
onKeyPress={onComponentDetails}
|
||||
onClick={onComponentDetails}
|
||||
product={product}
|
||||
component={component}
|
||||
bzcomponentkey={teamKey || `${product}::${component}`}
|
||||
teamkey={teamKey}
|
||||
role="button"
|
||||
tabIndex="0"
|
||||
>
|
||||
<ExpandMore classes={{ root: classes.icon }} />
|
||||
<ExpandMore
|
||||
classes={{ root: classes.icon }}
|
||||
bzcomponentkey={teamKey || `${product}::${component}`}
|
||||
teamkey={teamKey}
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
)}
|
||||
|
|
|
@ -29,6 +29,7 @@ const MainView = ({
|
|||
<BugzillaComponents
|
||||
title="Teams"
|
||||
bugzillaComponents={teamComponents}
|
||||
onComponentDetails={onComponentDetails}
|
||||
/>
|
||||
<BugzillaComponents
|
||||
title="Components"
|
||||
|
|
|
@ -45,7 +45,10 @@ BugzillaGraph.propTypes = {
|
|||
label: PropTypes.string.isRequired,
|
||||
parameters: PropTypes.shape({
|
||||
include_fields: PropTypes.string,
|
||||
component: PropTypes.string,
|
||||
component: PropTypes.oneOfType([
|
||||
PropTypes.arrayOf(PropTypes.string),
|
||||
PropTypes.string,
|
||||
]),
|
||||
resolution: PropTypes.string,
|
||||
priority: PropTypes.string,
|
||||
}),
|
||||
|
|
|
@ -104,8 +104,11 @@ class MainContainer extends Component {
|
|||
// This will cause the teams to be displayed before having any metrics
|
||||
this.setState({ teamComponents });
|
||||
Object.entries(teamComponents).map(async ([teamKey, teamInfo]) => {
|
||||
const team = Object.assign({}, teamInfo);
|
||||
team.metrics = {};
|
||||
const team = {
|
||||
teamKey,
|
||||
...teamInfo,
|
||||
metrics: {},
|
||||
};
|
||||
const { product, component } = teamInfo;
|
||||
await Promise.all(Object.keys(METRICS).map(async (metric) => {
|
||||
team.metrics[metric] = await getBugsCountAndLink(product, component, metric);
|
||||
|
@ -132,12 +135,26 @@ class MainContainer extends Component {
|
|||
handleShowComponentDetails(event) {
|
||||
event.preventDefault();
|
||||
const element = event.target.tagName === 'DIV' ? event.target : event.target.parentElement;
|
||||
const product = element.getAttribute('product');
|
||||
const component = element.getAttribute('component');
|
||||
this.setState(prevState => ({
|
||||
showComponent: prevState.bugzillaComponents[`${product}::${component}`],
|
||||
showPerson: undefined,
|
||||
}));
|
||||
// IDEA: In the future we could unify bugzilla components and teams into
|
||||
// the same data structure and make this logic simpler. We could use a
|
||||
// property 'team' to distinguish a component from a set of components
|
||||
const bzComponentKey = element.getAttribute('bzcomponentkey');
|
||||
const teamKey = element.getAttribute('teamkey');
|
||||
if (teamKey) {
|
||||
this.setState(prevState => ({
|
||||
showComponent: {
|
||||
title: prevState.teamComponents[teamKey].label,
|
||||
...prevState.teamComponents[teamKey],
|
||||
},
|
||||
}));
|
||||
} else {
|
||||
this.setState(prevState => ({
|
||||
showComponent: {
|
||||
title: bzComponentKey,
|
||||
...prevState.bugzillaComponents[bzComponentKey],
|
||||
},
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
handleShowPersonDetails(event) {
|
||||
|
@ -146,7 +163,6 @@ class MainContainer extends Component {
|
|||
const ldapEmail = element.getAttribute('value');
|
||||
const { partialOrg } = this.state;
|
||||
this.setState({
|
||||
showComponent: undefined,
|
||||
showPerson: partialOrg[ldapEmail],
|
||||
});
|
||||
}
|
||||
|
@ -169,6 +185,7 @@ class MainContainer extends Component {
|
|||
{showComponent && (
|
||||
<BugzillaComponentDetails
|
||||
{...showComponent}
|
||||
title={showComponent.title}
|
||||
onGoBack={this.handleComponentBackToMenu}
|
||||
/>
|
||||
)}
|
||||
|
|
|
@ -8,6 +8,7 @@ it('renders the details for a Bugzilla component', () => {
|
|||
.create((
|
||||
<BugzillaComponentDetails
|
||||
{...bugzillaComponents[0]}
|
||||
title="Hello world!"
|
||||
onGoBack={() => null}
|
||||
/>
|
||||
))
|
||||
|
|
|
@ -31,7 +31,7 @@ exports[`renders the details for a Bugzilla component 1`] = `
|
|||
<h2
|
||||
className="DetailView-title-7"
|
||||
>
|
||||
Core::DOM: IndexedDB
|
||||
Hello world!
|
||||
</h2>
|
||||
<div>
|
||||
<h4
|
||||
|
|
|
@ -119,19 +119,21 @@ exports[`renders Manager who has reportees 1`] = `
|
|||
<tr>
|
||||
<td>
|
||||
<div
|
||||
component="DOM: IndexedDB"
|
||||
bzcomponentkey="Core::DOM: IndexedDB"
|
||||
name="Core::DOM: IndexedDB"
|
||||
onClick={[Function]}
|
||||
onKeyPress={[Function]}
|
||||
product="Core"
|
||||
role="button"
|
||||
tabIndex="0"
|
||||
teamkey={null}
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
bzcomponentkey="Core::DOM: IndexedDB"
|
||||
className="MuiSvgIcon-root-7 BugzillaComponents-icon-19"
|
||||
focusable="false"
|
||||
role="presentation"
|
||||
teamkey={null}
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
|
@ -151,19 +153,21 @@ exports[`renders Manager who has reportees 1`] = `
|
|||
<tr>
|
||||
<td>
|
||||
<div
|
||||
component="JavaScript Engine"
|
||||
bzcomponentkey="Core::JavaScript Engine"
|
||||
name="Core::JavaScript Engine"
|
||||
onClick={[Function]}
|
||||
onKeyPress={[Function]}
|
||||
product="Core"
|
||||
role="button"
|
||||
tabIndex="0"
|
||||
teamkey={null}
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
bzcomponentkey="Core::JavaScript Engine"
|
||||
className="MuiSvgIcon-root-7 BugzillaComponents-icon-19"
|
||||
focusable="false"
|
||||
role="presentation"
|
||||
teamkey={null}
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
|
@ -183,19 +187,21 @@ exports[`renders Manager who has reportees 1`] = `
|
|||
<tr>
|
||||
<td>
|
||||
<div
|
||||
component="DOM: Core & HTML"
|
||||
bzcomponentkey="Core::DOM: Core & HTML"
|
||||
name="Core::DOM: Core & HTML"
|
||||
onClick={[Function]}
|
||||
onKeyPress={[Function]}
|
||||
product="Core"
|
||||
role="button"
|
||||
tabIndex="0"
|
||||
teamkey={null}
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
bzcomponentkey="Core::DOM: Core & HTML"
|
||||
className="MuiSvgIcon-root-7 BugzillaComponents-icon-19"
|
||||
focusable="false"
|
||||
role="presentation"
|
||||
teamkey={null}
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
|
@ -224,19 +230,21 @@ exports[`renders Manager who has reportees 1`] = `
|
|||
<tr>
|
||||
<td>
|
||||
<div
|
||||
component="Async Tooling"
|
||||
bzcomponentkey="Toolkit::Async Tooling"
|
||||
name="Toolkit::Async Tooling"
|
||||
onClick={[Function]}
|
||||
onKeyPress={[Function]}
|
||||
product="Toolkit"
|
||||
role="button"
|
||||
tabIndex="0"
|
||||
teamkey={null}
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
bzcomponentkey="Toolkit::Async Tooling"
|
||||
className="MuiSvgIcon-root-7 BugzillaComponents-icon-19"
|
||||
focusable="false"
|
||||
role="presentation"
|
||||
teamkey={null}
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
|
@ -380,19 +388,21 @@ exports[`renders Manager who has reportees and teams 1`] = `
|
|||
<tr>
|
||||
<td>
|
||||
<div
|
||||
component="DOM: IndexedDB"
|
||||
bzcomponentkey="Core::DOM: IndexedDB"
|
||||
name="Core::DOM: IndexedDB"
|
||||
onClick={[Function]}
|
||||
onKeyPress={[Function]}
|
||||
product="Core"
|
||||
role="button"
|
||||
tabIndex="0"
|
||||
teamkey={null}
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
bzcomponentkey="Core::DOM: IndexedDB"
|
||||
className="MuiSvgIcon-root-7 BugzillaComponents-icon-19"
|
||||
focusable="false"
|
||||
role="presentation"
|
||||
teamkey={null}
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
|
@ -412,19 +422,21 @@ exports[`renders Manager who has reportees and teams 1`] = `
|
|||
<tr>
|
||||
<td>
|
||||
<div
|
||||
component="JavaScript Engine"
|
||||
bzcomponentkey="Core::JavaScript Engine"
|
||||
name="Core::JavaScript Engine"
|
||||
onClick={[Function]}
|
||||
onKeyPress={[Function]}
|
||||
product="Core"
|
||||
role="button"
|
||||
tabIndex="0"
|
||||
teamkey={null}
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
bzcomponentkey="Core::JavaScript Engine"
|
||||
className="MuiSvgIcon-root-7 BugzillaComponents-icon-19"
|
||||
focusable="false"
|
||||
role="presentation"
|
||||
teamkey={null}
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
|
@ -444,19 +456,21 @@ exports[`renders Manager who has reportees and teams 1`] = `
|
|||
<tr>
|
||||
<td>
|
||||
<div
|
||||
component="DOM: Core & HTML"
|
||||
bzcomponentkey="Core::DOM: Core & HTML"
|
||||
name="Core::DOM: Core & HTML"
|
||||
onClick={[Function]}
|
||||
onKeyPress={[Function]}
|
||||
product="Core"
|
||||
role="button"
|
||||
tabIndex="0"
|
||||
teamkey={null}
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
bzcomponentkey="Core::DOM: Core & HTML"
|
||||
className="MuiSvgIcon-root-7 BugzillaComponents-icon-19"
|
||||
focusable="false"
|
||||
role="presentation"
|
||||
teamkey={null}
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
|
@ -485,19 +499,21 @@ exports[`renders Manager who has reportees and teams 1`] = `
|
|||
<tr>
|
||||
<td>
|
||||
<div
|
||||
component="Async Tooling"
|
||||
bzcomponentkey="Toolkit::Async Tooling"
|
||||
name="Toolkit::Async Tooling"
|
||||
onClick={[Function]}
|
||||
onKeyPress={[Function]}
|
||||
product="Toolkit"
|
||||
role="button"
|
||||
tabIndex="0"
|
||||
teamkey={null}
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
bzcomponentkey="Toolkit::Async Tooling"
|
||||
className="MuiSvgIcon-root-7 BugzillaComponents-icon-19"
|
||||
focusable="false"
|
||||
role="presentation"
|
||||
teamkey={null}
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
|
@ -641,19 +657,21 @@ exports[`renders Someone with no reportees 1`] = `
|
|||
<tr>
|
||||
<td>
|
||||
<div
|
||||
component="DOM: IndexedDB"
|
||||
bzcomponentkey="Core::DOM: IndexedDB"
|
||||
name="Core::DOM: IndexedDB"
|
||||
onClick={[Function]}
|
||||
onKeyPress={[Function]}
|
||||
product="Core"
|
||||
role="button"
|
||||
tabIndex="0"
|
||||
teamkey={null}
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
bzcomponentkey="Core::DOM: IndexedDB"
|
||||
className="MuiSvgIcon-root-7 BugzillaComponents-icon-19"
|
||||
focusable="false"
|
||||
role="presentation"
|
||||
teamkey={null}
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
|
@ -673,19 +691,21 @@ exports[`renders Someone with no reportees 1`] = `
|
|||
<tr>
|
||||
<td>
|
||||
<div
|
||||
component="JavaScript Engine"
|
||||
bzcomponentkey="Core::JavaScript Engine"
|
||||
name="Core::JavaScript Engine"
|
||||
onClick={[Function]}
|
||||
onKeyPress={[Function]}
|
||||
product="Core"
|
||||
role="button"
|
||||
tabIndex="0"
|
||||
teamkey={null}
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
bzcomponentkey="Core::JavaScript Engine"
|
||||
className="MuiSvgIcon-root-7 BugzillaComponents-icon-19"
|
||||
focusable="false"
|
||||
role="presentation"
|
||||
teamkey={null}
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
|
@ -705,19 +725,21 @@ exports[`renders Someone with no reportees 1`] = `
|
|||
<tr>
|
||||
<td>
|
||||
<div
|
||||
component="DOM: Core & HTML"
|
||||
bzcomponentkey="Core::DOM: Core & HTML"
|
||||
name="Core::DOM: Core & HTML"
|
||||
onClick={[Function]}
|
||||
onKeyPress={[Function]}
|
||||
product="Core"
|
||||
role="button"
|
||||
tabIndex="0"
|
||||
teamkey={null}
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
bzcomponentkey="Core::DOM: Core & HTML"
|
||||
className="MuiSvgIcon-root-7 BugzillaComponents-icon-19"
|
||||
focusable="false"
|
||||
role="presentation"
|
||||
teamkey={null}
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
|
@ -746,19 +768,21 @@ exports[`renders Someone with no reportees 1`] = `
|
|||
<tr>
|
||||
<td>
|
||||
<div
|
||||
component="Async Tooling"
|
||||
bzcomponentkey="Toolkit::Async Tooling"
|
||||
name="Toolkit::Async Tooling"
|
||||
onClick={[Function]}
|
||||
onKeyPress={[Function]}
|
||||
product="Toolkit"
|
||||
role="button"
|
||||
tabIndex="0"
|
||||
teamkey={null}
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
bzcomponentkey="Toolkit::Async Tooling"
|
||||
className="MuiSvgIcon-root-7 BugzillaComponents-icon-19"
|
||||
focusable="false"
|
||||
role="presentation"
|
||||
teamkey={null}
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
|
|
Загрузка…
Ссылка в новой задаче