Use routing to determine view instead of internal state (#60)

* The different tabs now have an associated route (e.g. `/reportees`)
* Use `NavLink` within `Tab` to properly support routes
* All of the above removes the need to keep track what to view via the state of the component
* Remove `MainView` component and include it within the `Main` view
* Create & update more tests
* Add React.lazy and Suspense to lazy load components depending on the view
This commit is contained in:
Armen Zambrano 2019-04-11 11:37:08 -04:00 коммит произвёл GitHub
Родитель 2c1ce29bad
Коммит d1ea227104
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
11 изменённых файлов: 643 добавлений и 241 удалений

Просмотреть файл

@ -1,7 +1,12 @@
import { hot } from 'react-hot-loader';
import React from 'react';
import PropTypes from 'prop-types';
import { BrowserRouter, Switch, Route } from 'react-router-dom';
import {
BrowserRouter,
Switch,
Redirect,
Route,
} from 'react-router-dom';
import { withStyles } from '@material-ui/core/styles';
import ErrorPanel from '@mozilla-frontend-infra/components/ErrorPanel';
import Spinner from '@mozilla-frontend-infra/components/Spinner';
@ -82,12 +87,15 @@ class App extends React.Component {
{authReady ? (
<AuthContext.Provider value={this.authController}>
<Switch>
<PropsRoute path="/" exact component={Main} />
<Route path="/" exact>
<Redirect to="/reportees" />
</Route>
<PropsRoute
path={config.redirectRoute}
component={Auth0Login}
setUserSession={this.authController.setUserSession}
/>
<PropsRoute path="/" component={Main} />
<Route component={NotFound} />
</Switch>
</AuthContext.Provider>

Просмотреть файл

@ -5,6 +5,7 @@ import AppBar from '@material-ui/core/AppBar';
import Toolbar from '@material-ui/core/Toolbar';
import Tabs from '@material-ui/core/Tabs';
import Tab from '@material-ui/core/Tab';
import { NavLink } from 'react-router-dom';
import CredentialsMenu from '../../views/CredentialsMenu';
const styles = theme => ({
@ -19,9 +20,9 @@ const Header = ({ classes, selectedTabIndex, handleTabChange }) => (
<AppBar position="static">
<Toolbar className={classes.styledToolbar}>
<Tabs value={selectedTabIndex} onChange={handleTabChange}>
<Tab label="Reportees" />
<Tab label="Teams" />
<Tab label="Components" />
<Tab label="Reportees" component={NavLink} to="reportees" />
<Tab label="Teams" component={NavLink} to="teams" />
<Tab label="Components" component={NavLink} to="components" />
</Tabs>
<CredentialsMenu />
</Toolbar>

Просмотреть файл

@ -1,64 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import BugzillaComponents from '../BugzillaComponents';
import Reportees from '../Reportees';
class MainView extends React.Component {
renderTabContents() {
const {
ldapEmail, partialOrg, onPersonDetails, teamComponents,
bugzillaComponents, onComponentDetails, selectedTabIndex,
} = this.props;
switch (selectedTabIndex) {
case 0: {
return (
<Reportees
ldapEmail={ldapEmail}
partialOrg={partialOrg}
onPersonDetails={onPersonDetails}
/>
);
}
case 1: {
return (
<BugzillaComponents
bugzillaComponents={teamComponents}
onComponentDetails={onComponentDetails}
/>
);
}
case 2: {
return (
<BugzillaComponents
bugzillaComponents={bugzillaComponents}
onComponentDetails={onComponentDetails}
/>
);
}
default: {
return null;
}
}
}
render() {
return this.renderTabContents();
}
}
MainView.propTypes = {
ldapEmail: PropTypes.string.isRequired,
partialOrg: PropTypes.shape({}).isRequired,
bugzillaComponents: PropTypes.arrayOf(PropTypes.shape({})),
teamComponents: PropTypes.arrayOf(PropTypes.shape({})),
onComponentDetails: PropTypes.func.isRequired,
onPersonDetails: PropTypes.func.isRequired,
selectedTabIndex: PropTypes.number.isRequired,
};
MainView.defaultProps = {
bugzillaComponents: [],
teamComponents: [],
};
export default MainView;

Просмотреть файл

@ -1,22 +1,32 @@
import React, { Component } from 'react';
import React, { Component, Suspense } from 'react';
import { Switch } from 'react-router-dom';
import PropsRoute from '../../components/PropsRoute';
import AuthContext from '../../components/auth/AuthContext';
import Header from '../../components/Header';
import MainView from '../../components/MainView';
import BugzillaComponentDetails from '../../components/BugzillaComponentDetails';
import PersonDetails from '../../components/PersonDetails';
import getAllReportees from '../../utils/getAllReportees';
import getBugzillaOwners from '../../utils/getBugzillaOwners';
import getBugsCountAndLink from '../../utils/bugzilla/getBugsCountAndLink';
import METRICS from '../../utils/bugzilla/metrics';
import TEAMS_CONFIG from '../../teamsConfig';
const BugzillaComponents = React.lazy(() => import('../../components/BugzillaComponents'));
const BugzillaComponentDetails = React.lazy(() => import('../../components/BugzillaComponentDetails'));
const PersonDetails = React.lazy(() => import('../../components/PersonDetails'));
const Reportees = React.lazy(() => import('../../components/Reportees'));
const DEFAULT_STATE = {
bugzillaComponents: {},
partialOrg: undefined,
teamComponents: {},
selectedTabIndex: 0,
showComponent: undefined,
showPerson: undefined,
componentDetails: undefined,
personDetails: undefined,
};
const PATHNAME_TO_TAB_INDEX = {
'/reportees': 0,
'/teams': 1,
'/components': 2,
};
class MainContainer extends Component {
@ -26,6 +36,9 @@ class MainContainer extends Component {
constructor(props) {
super(props);
const { location } = this.props;
// This guarantees that we load the right tab based on the URL's pathname
this.state.selectedTabIndex = PATHNAME_TO_TAB_INDEX[location.pathname] || 0;
this.handleShowComponentDetails = this.handleShowComponentDetails.bind(this);
this.handleShowPersonDetails = this.handleShowPersonDetails.bind(this);
this.handleComponentBackToMenu = this.handleComponentBackToMenu.bind(this);
@ -59,14 +72,18 @@ class MainContainer extends Component {
return partialOrg;
}
handleChangeSelectedTab = (event, selectedTabIndex) => {
this.setState({ selectedTabIndex });
};
handleUserSessionChanged = () => {
this.fetchData();
};
handleNavigateAndClear = (_, selectedTabIndex) => {
this.setState({
componentDetails: undefined,
personDetails: undefined,
selectedTabIndex,
});
};
fetchData() {
const { context } = this;
const userSession = context && context.getUserSession();
@ -155,14 +172,14 @@ class MainContainer extends Component {
// property 'team' to distinguish a component from a set of components
if (teamKey) {
this.setState(prevState => ({
showComponent: {
componentDetails: {
title: prevState.teamComponents[teamKey].label,
...prevState.teamComponents[teamKey],
},
}));
} else {
this.setState(prevState => ({
showComponent: {
componentDetails: {
title: componentKey,
...prevState.bugzillaComponents[componentKey],
},
@ -174,22 +191,22 @@ class MainContainer extends Component {
event.preventDefault();
const { partialOrg } = this.state;
this.setState({
showPerson: partialOrg[properties.ldapEmail],
personDetails: partialOrg[properties.ldapEmail],
});
}
handleComponentBackToMenu(event) {
event.preventDefault();
this.setState({
showComponent: undefined,
showPerson: undefined,
componentDetails: undefined,
personDetails: undefined,
});
}
render() {
const {
showComponent,
showPerson,
componentDetails,
personDetails,
bugzillaComponents,
ldapEmail,
partialOrg,
@ -203,34 +220,53 @@ class MainContainer extends Component {
<div>
<Header
selectedTabIndex={selectedTabIndex}
handleTabChange={this.handleChangeSelectedTab}
handleTabChange={this.handleNavigateAndClear}
/>
{!userSession && <h3>Please sign in</h3>}
{showComponent && (
<BugzillaComponentDetails
{...showComponent}
title={showComponent.title}
onGoBack={this.handleComponentBackToMenu}
/>
{componentDetails && (
<Suspense fallback={<div>Loading...</div>}>
<BugzillaComponentDetails
{...componentDetails}
onGoBack={this.handleComponentBackToMenu}
/>
</Suspense>
)}
{showPerson && (
<PersonDetails
person={showPerson}
bugzillaComponents={Object.values(bugzillaComponents)}
onGoBack={this.handleComponentBackToMenu}
/>
)}
{!showComponent && !showPerson && partialOrg && userSession && (
<MainView
ldapEmail={ldapEmail}
partialOrg={partialOrg}
bugzillaComponents={Object.values(bugzillaComponents)}
teamComponents={Object.values(teamComponents)}
onComponentDetails={this.handleShowComponentDetails}
onPersonDetails={this.handleShowPersonDetails}
selectedTabIndex={selectedTabIndex}
/>
{personDetails && (
<Suspense fallback={<div>Loading...</div>}>
<PersonDetails
person={personDetails}
bugzillaComponents={Object.values(bugzillaComponents)}
onGoBack={this.handleComponentBackToMenu}
/>
</Suspense>
)}
<Suspense fallback={<div>Loading...</div>}>
<Switch>
{partialOrg && (
<PropsRoute
path="/reportees"
component={Reportees}
ldapEmail={ldapEmail}
partialOrg={partialOrg}
onPersonDetails={this.handleShowPersonDetails}
/>
)}
{partialOrg && (
<PropsRoute
path="/components"
component={BugzillaComponents}
bugzillaComponents={Object.values(bugzillaComponents)}
onComponentDetails={this.handleShowComponentDetails}
/>
)}
<PropsRoute
path="/teams"
component={BugzillaComponents}
bugzillaComponents={Object.values(teamComponents)}
onComponentDetails={this.handleShowComponentDetails}
/>
</Switch>
</Suspense>
</div>
);
}

Просмотреть файл

@ -0,0 +1,29 @@
import React from 'react';
import renderer from 'react-test-renderer';
import BugzillaComponents from '../../src/components/BugzillaComponents';
import bugzillaComponents from '../mocks/bugzillaComponents';
import teamsConfig from '../../src/teamsConfig';
it('renders components', () => {
const tree = renderer
.create((
<BugzillaComponents
bugzillaComponents={bugzillaComponents}
onComponentDetails={() => null}
/>
))
.toJSON();
expect(tree).toMatchSnapshot();
});
it('renders components bucketed as teams', () => {
const tree = renderer
.create((
<BugzillaComponents
bugzillaComponents={Object.values(teamsConfig)}
onComponentDetails={() => null}
/>
))
.toJSON();
expect(tree).toMatchSnapshot();
});

Просмотреть файл

@ -0,0 +1,18 @@
import React from 'react';
import renderer from 'react-test-renderer';
import { BrowserRouter as Router } from 'react-router-dom';
import Header from '../../src/components/Header';
it('renders the reportees tab', () => {
const tree = renderer
.create((
<Router>
<Header
selectedTabIndex={0}
handleTabChange={() => null}
/>
</Router>
))
.toJSON();
expect(tree).toMatchSnapshot();
});

Просмотреть файл

@ -1,57 +0,0 @@
import React from 'react';
import renderer from 'react-test-renderer';
import MainView from '../../src/components/MainView';
import partialOrg from '../mocks/partialOrg';
import bugzillaComponents from '../mocks/bugzillaComponents';
import teamsConfig from '../../src/teamsConfig';
it('renders Someone with no reportees', () => {
const tree = renderer
.create((
<MainView
ldapEmail="someone@mozilla.com"
partialOrg={partialOrg}
bugzillaComponents={bugzillaComponents}
teams={{}}
onComponentDetails={() => null}
onPersonDetails={() => null}
selectedTabIndex={0}
/>
))
.toJSON();
expect(tree).toMatchSnapshot();
});
it('renders Manager who has reportees', () => {
const tree = renderer
.create((
<MainView
ldapEmail="manager@mozilla.com"
partialOrg={partialOrg}
bugzillaComponents={bugzillaComponents}
teams={{}}
onComponentDetails={() => null}
onPersonDetails={() => null}
selectedTabIndex={0}
/>
))
.toJSON();
expect(tree).toMatchSnapshot();
});
it('renders Manager who has reportees and teams', () => {
const tree = renderer
.create((
<MainView
ldapEmail="manager@mozilla.com"
partialOrg={partialOrg}
bugzillaComponents={bugzillaComponents}
teams={Object.values(teamsConfig)}
onComponentDetails={() => null}
onPersonDetails={() => null}
selectedTabIndex={0}
/>
))
.toJSON();
expect(tree).toMatchSnapshot();
});

Просмотреть файл

@ -0,0 +1,30 @@
import React from 'react';
import renderer from 'react-test-renderer';
import Reportees from '../../src/components/Reportees';
import partialOrg from '../mocks/partialOrg';
it('renders Someone with no reportees', () => {
const tree = renderer
.create((
<Reportees
ldapEmail="someone@mozilla.com"
partialOrg={partialOrg}
onPersonDetails={() => null}
/>
))
.toJSON();
expect(tree).toMatchSnapshot();
});
it('renders Manager who has reportees', () => {
const tree = renderer
.create((
<Reportees
ldapEmail="manager@mozilla.com"
partialOrg={partialOrg}
onPersonDetails={() => null}
/>
))
.toJSON();
expect(tree).toMatchSnapshot();
});

Просмотреть файл

@ -0,0 +1,293 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders components 1`] = `
<div>
<h3
className="BugzillaComponents-header-1"
/>
<table>
<thead>
<tr>
<th />
<th />
<th
className="BugzillaComponents-metricLabel-3"
>
Untriaged
</th>
<th
className="BugzillaComponents-metricLabel-3"
>
Needinfo
</th>
<th
className="BugzillaComponents-metricLabel-3"
>
P1s
</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<div
className="DrilldownIcon-svgWrapper-4"
name="Core::DOM: IndexedDB"
onClick={[Function]}
onKeyPress={[Function]}
role="button"
tabIndex="0"
>
<svg
aria-hidden="true"
className="MuiSvgIcon-root-6 DrilldownIcon-icon-5"
focusable="false"
role="presentation"
viewBox="0 0 24 24"
>
<path
d="M16.59 8.59L12 13.17 7.41 8.59 6 10l6 6 6-6z"
/>
<path
d="M0 0h24v24H0z"
fill="none"
/>
</svg>
</div>
</td>
<td>
Core::DOM: IndexedDB
</td>
</tr>
<tr>
<td>
<div
className="DrilldownIcon-svgWrapper-4"
name="Core::JavaScript Engine"
onClick={[Function]}
onKeyPress={[Function]}
role="button"
tabIndex="0"
>
<svg
aria-hidden="true"
className="MuiSvgIcon-root-6 DrilldownIcon-icon-5"
focusable="false"
role="presentation"
viewBox="0 0 24 24"
>
<path
d="M16.59 8.59L12 13.17 7.41 8.59 6 10l6 6 6-6z"
/>
<path
d="M0 0h24v24H0z"
fill="none"
/>
</svg>
</div>
</td>
<td>
Core::JavaScript Engine
</td>
</tr>
<tr>
<td>
<div
className="DrilldownIcon-svgWrapper-4"
name="Core::DOM: Core & HTML"
onClick={[Function]}
onKeyPress={[Function]}
role="button"
tabIndex="0"
>
<svg
aria-hidden="true"
className="MuiSvgIcon-root-6 DrilldownIcon-icon-5"
focusable="false"
role="presentation"
viewBox="0 0 24 24"
>
<path
d="M16.59 8.59L12 13.17 7.41 8.59 6 10l6 6 6-6z"
/>
<path
d="M0 0h24v24H0z"
fill="none"
/>
</svg>
</div>
</td>
<td>
Core::DOM: Core & HTML
</td>
<td
className="BugzillaComponents-metric-2"
>
<a
href="https://bugzilla.mozilla.org/buglist.cgi?component=DOM%3A%20Core%20%26%20HTML&f1=bug_severity&f2=keywords&f3=resolution&limit=0&o1=notequals&o2=notsubstring&o3=isempty&product=Core&v1=enhancement&v2=meta"
>
944
</a>
</td>
</tr>
<tr>
<td>
<div
className="DrilldownIcon-svgWrapper-4"
name="Toolkit::Async Tooling"
onClick={[Function]}
onKeyPress={[Function]}
role="button"
tabIndex="0"
>
<svg
aria-hidden="true"
className="MuiSvgIcon-root-6 DrilldownIcon-icon-5"
focusable="false"
role="presentation"
viewBox="0 0 24 24"
>
<path
d="M16.59 8.59L12 13.17 7.41 8.59 6 10l6 6 6-6z"
/>
<path
d="M0 0h24v24H0z"
fill="none"
/>
</svg>
</div>
</td>
<td>
Toolkit::Async Tooling
</td>
</tr>
</tbody>
</table>
</div>
`;
exports[`renders components bucketed as teams 1`] = `
<div>
<h3
className="BugzillaComponents-header-1"
/>
<table>
<thead>
<tr>
<th />
<th />
<th
className="BugzillaComponents-metricLabel-3"
>
Untriaged
</th>
<th
className="BugzillaComponents-metricLabel-3"
>
Needinfo
</th>
<th
className="BugzillaComponents-metricLabel-3"
>
P1s
</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<div
className="DrilldownIcon-svgWrapper-4"
name="DOM Core"
onClick={[Function]}
onKeyPress={[Function]}
role="button"
tabIndex="0"
>
<svg
aria-hidden="true"
className="MuiSvgIcon-root-6 DrilldownIcon-icon-5"
focusable="false"
role="presentation"
viewBox="0 0 24 24"
>
<path
d="M16.59 8.59L12 13.17 7.41 8.59 6 10l6 6 6-6z"
/>
<path
d="M0 0h24v24H0z"
fill="none"
/>
</svg>
</div>
</td>
<td>
DOM Core
</td>
</tr>
<tr>
<td>
<div
className="DrilldownIcon-svgWrapper-4"
name="DOM Fission"
onClick={[Function]}
onKeyPress={[Function]}
role="button"
tabIndex="0"
>
<svg
aria-hidden="true"
className="MuiSvgIcon-root-6 DrilldownIcon-icon-5"
focusable="false"
role="presentation"
viewBox="0 0 24 24"
>
<path
d="M16.59 8.59L12 13.17 7.41 8.59 6 10l6 6 6-6z"
/>
<path
d="M0 0h24v24H0z"
fill="none"
/>
</svg>
</div>
</td>
<td>
DOM Fission
</td>
</tr>
<tr>
<td>
<div
className="DrilldownIcon-svgWrapper-4"
name="Worker and Storage"
onClick={[Function]}
onKeyPress={[Function]}
role="button"
tabIndex="0"
>
<svg
aria-hidden="true"
className="MuiSvgIcon-root-6 DrilldownIcon-icon-5"
focusable="false"
role="presentation"
viewBox="0 0 24 24"
>
<path
d="M16.59 8.59L12 13.17 7.41 8.59 6 10l6 6 6-6z"
/>
<path
d="M0 0h24v24H0z"
fill="none"
/>
</svg>
</div>
</td>
<td>
Worker and Storage
</td>
</tr>
</tbody>
</table>
</div>
`;

Просмотреть файл

@ -0,0 +1,182 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders the reportees tab 1`] = `
<header
className="MuiPaper-root-11 MuiPaper-elevation4-17 MuiAppBar-root-2 MuiAppBar-positionStatic-6 MuiAppBar-colorPrimary-9"
>
<div
className="MuiToolbar-root-38 MuiToolbar-regular-40 MuiToolbar-gutters-39 Header-styledToolbar-1"
>
<div
className="MuiTabs-root-42"
>
<div
className="MuiTabs-flexContainer-43"
>
<div
className="MuiTabs-scroller-45 MuiTabs-fixed-46"
onScroll={[Function]}
role="tablist"
style={
Object {
"marginBottom": 0,
}
}
>
<div
className="MuiTabs-flexContainer-43"
>
<a
aria-current={null}
aria-selected={true}
className="MuiButtonBase-root-63 MuiTab-root-51 MuiTab-textColorInherit-53 MuiTab-selected-56"
href="/reportees"
onBlur={[Function]}
onClick={[Function]}
onContextMenu={[Function]}
onFocus={[Function]}
onKeyDown={[Function]}
onKeyUp={[Function]}
onMouseDown={[Function]}
onMouseLeave={[Function]}
onMouseUp={[Function]}
onTouchEnd={[Function]}
onTouchMove={[Function]}
onTouchStart={[Function]}
role="tab"
tabIndex="0"
>
<span
className="MuiTab-wrapper-59"
>
<span
className="MuiTab-labelContainer-60"
>
<span
className="MuiTab-label-61"
>
Reportees
</span>
</span>
</span>
<span
className="MuiTouchRipple-root-96"
/>
</a>
<a
aria-current={null}
aria-selected={false}
className="MuiButtonBase-root-63 MuiTab-root-51 MuiTab-textColorInherit-53"
href="/teams"
onBlur={[Function]}
onClick={[Function]}
onContextMenu={[Function]}
onFocus={[Function]}
onKeyDown={[Function]}
onKeyUp={[Function]}
onMouseDown={[Function]}
onMouseLeave={[Function]}
onMouseUp={[Function]}
onTouchEnd={[Function]}
onTouchMove={[Function]}
onTouchStart={[Function]}
role="tab"
tabIndex="0"
>
<span
className="MuiTab-wrapper-59"
>
<span
className="MuiTab-labelContainer-60"
>
<span
className="MuiTab-label-61"
>
Teams
</span>
</span>
</span>
<span
className="MuiTouchRipple-root-96"
/>
</a>
<a
aria-current={null}
aria-selected={false}
className="MuiButtonBase-root-63 MuiTab-root-51 MuiTab-textColorInherit-53"
href="/components"
onBlur={[Function]}
onClick={[Function]}
onContextMenu={[Function]}
onFocus={[Function]}
onKeyDown={[Function]}
onKeyUp={[Function]}
onMouseDown={[Function]}
onMouseLeave={[Function]}
onMouseUp={[Function]}
onTouchEnd={[Function]}
onTouchMove={[Function]}
onTouchStart={[Function]}
role="tab"
tabIndex="0"
>
<span
className="MuiTab-wrapper-59"
>
<span
className="MuiTab-labelContainer-60"
>
<span
className="MuiTab-label-61"
>
Components
</span>
</span>
</span>
<span
className="MuiTouchRipple-root-96"
/>
</a>
</div>
<span
className="MuiPrivateTabIndicator-root-66 MuiPrivateTabIndicator-colorSecondary-68 MuiTabs-indicator-50"
style={
Object {
"left": 0,
"width": 0,
}
}
/>
</div>
</div>
</div>
<button
className="MuiButtonBase-root-63 MuiButton-root-70 MuiButton-contained-81 MuiButton-containedSecondary-83 MuiButton-raised-84 MuiButton-raisedSecondary-86 MuiButton-sizeSmall-93 CredentialsMenu-button-69"
disabled={false}
onBlur={[Function]}
onClick={[Function]}
onContextMenu={[Function]}
onFocus={[Function]}
onKeyDown={[Function]}
onKeyUp={[Function]}
onMouseDown={[Function]}
onMouseLeave={[Function]}
onMouseUp={[Function]}
onTouchEnd={[Function]}
onTouchMove={[Function]}
onTouchStart={[Function]}
tabIndex="0"
type="button"
>
<span
className="MuiButton-label-71"
>
Sign in
</span>
<span
className="MuiTouchRipple-root-96"
/>
</button>
</div>
</header>
`;

Просмотреть файл

@ -74,80 +74,6 @@ exports[`renders Manager who has reportees 1`] = `
</div>
`;
exports[`renders Manager who has reportees and teams 1`] = `
<div
className="Reportees-root-1"
>
<div
height="1rem"
>
 
</div>
<div
className="Reportees-person-4"
>
<div
className="DrilldownIcon-svgWrapper-5"
name="manager@mozilla.com"
onClick={[Function]}
onKeyPress={[Function]}
role="button"
tabIndex="0"
>
<svg
aria-hidden="true"
className="MuiSvgIcon-root-7 DrilldownIcon-icon-6"
focusable="false"
role="presentation"
viewBox="0 0 24 24"
>
<path
d="M16.59 8.59L12 13.17 7.41 8.59 6 10l6 6 6-6z"
/>
<path
d="M0 0h24v24H0z"
fill="none"
/>
</svg>
</div>
<span>
Manager
</span>
</div>
<div
className="Reportees-person-4"
>
<div
className="DrilldownIcon-svgWrapper-5"
name="someone@mozilla.com"
onClick={[Function]}
onKeyPress={[Function]}
role="button"
tabIndex="0"
>
<svg
aria-hidden="true"
className="MuiSvgIcon-root-7 DrilldownIcon-icon-6"
focusable="false"
role="presentation"
viewBox="0 0 24 24"
>
<path
d="M16.59 8.59L12 13.17 7.41 8.59 6 10l6 6 6-6z"
/>
<path
d="M0 0h24v24H0z"
fill="none"
/>
</svg>
</div>
<span>
Someone
</span>
</div>
</div>
`;
exports[`renders Someone with no reportees 1`] = `
<div
className="Reportees-root-1"