Skip to content

fix(ui) Fix graph flicker on tab change #13696

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 17, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,6 @@ class EventDetails extends AsyncComponent {
];
}

onRequestSuccess({data}) {
// Select the first interface as the active sub-tab
this.setState({activeTab: data.entries[0].type});
}

handleClose = event => {
event.preventDefault();
const {location} = this.props;
Expand All @@ -90,8 +85,6 @@ class EventDetails extends AsyncComponent {
});
};

handleTabChange = tab => this.setState({activeTab: tab});

get projectId() {
if (this.props.eventSlug) {
const [projectId] = this.props.eventSlug.split(':');
Expand All @@ -116,15 +109,14 @@ class EventDetails extends AsyncComponent {

renderBody() {
const {organization, view, location} = this.props;
const {event, activeTab} = this.state;
const {event} = this.state;

return (
<ModalContainer>
<CloseButton onClick={this.handleClose} size={30} />
<EventModalContent
onTabChange={this.handleTabChange}
event={event}
activeTab={activeTab}
projectId={this.projectId}
organization={organization}
view={view}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import React from 'react';
import PropTypes from 'prop-types';

import {t} from 'app/locale';
import SentryTypes from 'app/sentryTypes';
import {INTERFACES} from 'app/components/events/eventEntries';
import ErrorBoundary from 'app/components/errorBoundary';
import EventDataSection from 'app/components/events/eventDataSection';
import EventDevice from 'app/components/events/device';
import EventExtraData from 'app/components/events/extraData';
import EventPackageData from 'app/components/events/packageData';
import NavTabs from 'app/components/navTabs';
import {objectIsEmpty, toTitleCase} from 'app/utils';

const OTHER_SECTIONS = {
context: EventExtraData,
packages: EventPackageData,
device: EventDevice,
};

/**
* Render the currently active event interface tab.
* Some but not all interface elements require a projectId.
*/
const ActiveTab = props => {
const {projectId, event, activeTab} = props;
if (!activeTab) {
return null;
}
const entry = event.entries.find(item => item.type === activeTab);
if (INTERFACES[activeTab]) {
const Component = INTERFACES[activeTab];
return (
<Component
projectId={projectId}
event={event}
type={entry.type}
data={entry.data}
isShare={false}
/>
);
} else if (OTHER_SECTIONS[activeTab]) {
const Component = OTHER_SECTIONS[activeTab];
return <Component event={event} isShare={false} />;
} else {
/*eslint no-console:0*/
window.console &&
console.error &&
console.error('Unregistered interface: ' + activeTab);

return (
<EventDataSection event={event} type={entry.type} title={entry.type}>
<p>{t('There was an error rendering this data.')}</p>
</EventDataSection>
);
}
};
ActiveTab.propTypes = {
event: SentryTypes.Event.isRequired,
activeTab: PropTypes.string,
projectId: PropTypes.string.isRequired,
};

class EventInterfaces extends React.Component {
static propTypes = {
event: SentryTypes.Event.isRequired,
projectId: PropTypes.string.isRequired,
};

constructor(props) {
super(props);
this.state = {
activeTab: props.event.entries[0].type,
};
}

handleTabChange = tab => this.setState({activeTab: tab});

render() {
const {event, projectId} = this.props;
const {activeTab} = this.state;

return (
<React.Fragment>
<NavTabs underlined={true}>
{event.entries.map(entry => {
if (!INTERFACES.hasOwnProperty(entry.type)) {
return null;
}
const type = entry.type;
const classname = type === activeTab ? 'active' : null;
return (
<li key={type} className={classname}>
<a
href="#"
onClick={evt => {
evt.preventDefault();
this.handleTabChange(type);
}}
>
{toTitleCase(type)}
</a>
</li>
);
})}
{Object.keys(OTHER_SECTIONS).map(section => {
if (objectIsEmpty(event[section])) {
return null;
}
const classname = section === activeTab ? 'active' : null;
return (
<li key={section} className={classname}>
<a
href="#"
onClick={evt => {
evt.preventDefault();
this.handleTabChange(section);
}}
>
{toTitleCase(section)}
</a>
</li>
);
})}
</NavTabs>
<ErrorBoundary message={t('Could not render event details')}>
<ActiveTab event={event} activeTab={activeTab} projectId={projectId} />
</ErrorBoundary>
</React.Fragment>
);
}
}

export default EventInterfaces;
Original file line number Diff line number Diff line change
Expand Up @@ -2,84 +2,27 @@ import PropTypes from 'prop-types';
import React from 'react';
import styled from 'react-emotion';

import {INTERFACES} from 'app/components/events/eventEntries';
import {t} from 'app/locale';
import SentryTypes from 'app/sentryTypes';
import DateTime from 'app/components/dateTime';
import ErrorBoundary from 'app/components/errorBoundary';
import EventDataSection from 'app/components/events/eventDataSection';
import EventDevice from 'app/components/events/device';
import EventExtraData from 'app/components/events/extraData';
import EventPackageData from 'app/components/events/packageData';
import ExternalLink from 'app/components/links/externalLink';
import FileSize from 'app/components/fileSize';
import NavTabs from 'app/components/navTabs';
import SentryTypes from 'app/sentryTypes';
import overflowEllipsis from 'app/styles/overflowEllipsis';
import space from 'app/styles/space';
import {objectIsEmpty, toTitleCase} from 'app/utils';
import {getMessage, getTitle} from 'app/utils/events';
import getDynamicText from 'app/utils/getDynamicText';
import {getMessage, getTitle} from 'app/utils/events';

import EventInterfaces from './eventInterfaces';
import LinkedIssuePreview from './linkedIssuePreview';
import ModalPagination from './modalPagination';
import ModalLineGraph from './modalLineGraph';
import TagsTable from './tagsTable';

const OTHER_SECTIONS = {
context: EventExtraData,
packages: EventPackageData,
device: EventDevice,
};

/**
* Render the currently active event interface tab.
* Some but not all interface elements require a projectId.
*/
const ActiveTab = props => {
const {projectId, event, activeTab} = props;
if (!activeTab) {
return null;
}
const entry = event.entries.find(item => item.type === activeTab);
if (INTERFACES[activeTab]) {
const Component = INTERFACES[activeTab];
return (
<Component
projectId={projectId}
event={event}
type={entry.type}
data={entry.data}
isShare={false}
/>
);
} else if (OTHER_SECTIONS[activeTab]) {
const Component = OTHER_SECTIONS[activeTab];
return <Component event={event} isShare={false} />;
} else {
/*eslint no-console:0*/
window.console &&
console.error &&
console.error('Unregistered interface: ' + activeTab);

return (
<EventDataSection event={event} type={entry.type} title={entry.type}>
<p>{t('There was an error rendering this data.')}</p>
</EventDataSection>
);
}
};
ActiveTab.propTypes = {
event: SentryTypes.Event.isRequired,
activeTab: PropTypes.string,
projectId: PropTypes.string.isRequired,
};

/**
* Render the columns and navigation elements inside the event modal view.
* Controlled by the EventDetails View.
*/
const EventModalContent = props => {
const {event, activeTab, projectId, organization, onTabChange, location, view} = props;
const {event, projectId, organization, location, view} = props;
const isGroupedView = !!view.data.groupby;
const eventJsonUrl = `/api/0/projects/${organization.slug}/${projectId}/events/${
event.eventID
Expand All @@ -103,50 +46,7 @@ const EventModalContent = props => {
})}
</HeaderBox>
<ContentColumn>
<NavTabs underlined={true}>
{event.entries.map(entry => {
if (!INTERFACES.hasOwnProperty(entry.type)) {
return null;
}
const type = entry.type;
const classname = type === activeTab ? 'active' : null;
return (
<li key={type} className={classname}>
<a
href="#"
onClick={evt => {
evt.preventDefault();
onTabChange(type);
}}
>
{toTitleCase(type)}
</a>
</li>
);
})}
{Object.keys(OTHER_SECTIONS).map(section => {
if (objectIsEmpty(event[section])) {
return null;
}
const classname = section === activeTab ? 'active' : null;
return (
<li key={section} className={classname}>
<a
href="#"
onClick={evt => {
evt.preventDefault();
onTabChange(section);
}}
>
{toTitleCase(section)}
</a>
</li>
);
})}
</NavTabs>
<ErrorBoundary message={t('Could not render event details')}>
<ActiveTab event={event} activeTab={activeTab} projectId={projectId} />
</ErrorBoundary>
<EventInterfaces event={event} projectId={projectId} />
</ContentColumn>
<SidebarColumn>
{event.groupID && <LinkedIssuePreview groupId={event.groupID} />}
Expand All @@ -159,8 +59,8 @@ const EventModalContent = props => {
);
};
EventModalContent.propTypes = {
...ActiveTab.propTypes,
onTabChange: PropTypes.func.isRequired,
event: SentryTypes.Event.isRequired,
projectId: PropTypes.string.isRequired,
organization: SentryTypes.Organization.isRequired,
view: PropTypes.object.isRequired,
location: PropTypes.object.isRequired,
Expand Down