Skip to content
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
17 changes: 11 additions & 6 deletions static/app/utils/performance/trends/trendsDiscoverQuery.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import GenericDiscoverQuery, {
import withApi from 'app/utils/withApi';
import {
TrendChangeType,
TrendFunctionField,
TrendsData,
TrendsDataEvents,
TrendsQuery,
Expand All @@ -20,17 +21,21 @@ import {

export type TrendsRequest = {
trendChangeType?: TrendChangeType;
trendFunctionField?: TrendFunctionField;
eventView: Partial<TrendView>;
};

type RequestProps = DiscoverQueryProps & TrendsRequest;

type ChildrenProps = Omit<GenericChildrenProps<TrendsData>, 'tableData'> & {
export type TrendDiscoveryChildrenProps = Omit<
GenericChildrenProps<TrendsData>,
'tableData'
> & {
trendsData: TrendsData | null;
};

type Props = RequestProps & {
children: (props: ChildrenProps) => React.ReactNode;
children: (props: TrendDiscoveryChildrenProps) => React.ReactNode;
};

type EventChildrenProps = Omit<GenericChildrenProps<TrendsDataEvents>, 'tableData'> & {
Expand All @@ -44,13 +49,13 @@ type EventProps = RequestProps & {
export function getTrendsRequestPayload(props: RequestProps) {
const {eventView} = props;
const apiPayload: TrendsQuery = eventView?.getEventsAPIPayload(props.location);
const trendFunction = getCurrentTrendFunction(props.location);
const trendFunction = getCurrentTrendFunction(props.location, props.trendFunctionField);
const trendParameter = getCurrentTrendParameter(props.location);
apiPayload.trendFunction = generateTrendFunctionAsString(
trendFunction.field,
trendParameter.column
);
apiPayload.trendType = eventView?.trendType;
apiPayload.trendType = eventView?.trendType || props.trendChangeType;
apiPayload.interval = eventView?.interval;
apiPayload.middle = eventView?.middle;
return apiPayload;
Expand All @@ -59,9 +64,9 @@ export function getTrendsRequestPayload(props: RequestProps) {
function TrendsDiscoverQuery(props: Props) {
return (
<GenericDiscoverQuery<TrendsData, TrendsRequest>
{...props}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was there a reason for this change? Seems unlikely that this changed the behaviour of anything but just want to be sure.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general, I'd say spreads should always be first so hardcoded settings are applied with precedence, unless there is a specific thing that props will override. In this specific instance it's been a couple week since I actually wrote that line but I believe there was a bug with props containing extra keys and it causing an issue.

route="events-trends-stats"
getRequestPayload={getTrendsRequestPayload}
{...props}
>
{({tableData, ...rest}) => {
return props.children({trendsData: tableData, ...rest});
Expand All @@ -73,9 +78,9 @@ function TrendsDiscoverQuery(props: Props) {
function EventsDiscoverQuery(props: EventProps) {
return (
<GenericDiscoverQuery<TrendsDataEvents, TrendsRequest>
{...props}
route="events-trends"
getRequestPayload={getTrendsRequestPayload}
{...props}
>
{({tableData, ...rest}) => {
return props.children({trendsData: tableData, ...rest});
Expand Down
52 changes: 3 additions & 49 deletions static/app/views/performance/content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,16 @@ import {GlobalSelection, Organization, Project} from 'app/types';
import {trackAnalyticsEvent} from 'app/utils/analytics';
import EventView from 'app/utils/discover/eventView';
import {PerformanceEventViewProvider} from 'app/utils/performance/contexts/performanceEventViewContext';
import {decodeScalar} from 'app/utils/queryString';
import {MutableSearch} from 'app/utils/tokenizeSearch';
import withApi from 'app/utils/withApi';
import withGlobalSelection from 'app/utils/withGlobalSelection';
import withOrganization from 'app/utils/withOrganization';
import withProjects from 'app/utils/withProjects';

import LandingContent from './landing/content';
import {DEFAULT_MAX_DURATION} from './trends/utils';
import {DEFAULT_STATS_PERIOD, generatePerformanceEventView} from './data';
import {PerformanceLanding} from './landing';
import Onboarding from './onboarding';
import {addRoutePerformanceContext, getPerformanceTrendsUrl} from './utils';
import {addRoutePerformanceContext, handleTrendsClick} from './utils';

type Props = {
api: Client;
Expand Down Expand Up @@ -131,49 +128,6 @@ class PerformanceContent extends Component<Props, State> {
});
};

handleTrendsClick = () => {
const {location, organization} = this.props;

const newQuery = {
...location.query,
};

const query = decodeScalar(location.query.query, '');
const conditions = new MutableSearch(query);

trackAnalyticsEvent({
eventKey: 'performance_views.change_view',
eventName: 'Performance Views: Change View',
organization_id: parseInt(organization.id, 10),
view_name: 'TRENDS',
});

const modifiedConditions = new MutableSearch([]);

if (conditions.hasFilter('tpm()')) {
modifiedConditions.setFilterValues('tpm()', conditions.getFilterValues('tpm()'));
} else {
modifiedConditions.setFilterValues('tpm()', ['>0.01']);
}
if (conditions.hasFilter('transaction.duration')) {
modifiedConditions.setFilterValues(
'transaction.duration',
conditions.getFilterValues('transaction.duration')
);
} else {
modifiedConditions.setFilterValues('transaction.duration', [
'>0',
`<${DEFAULT_MAX_DURATION}`,
]);
}
newQuery.query = modifiedConditions.formatString();

browserHistory.push({
pathname: getPerformanceTrendsUrl(organization),
query: {...newQuery},
});
};

shouldShowOnboarding() {
const {projects, demoMode} = this.props;
const {eventView} = this.state;
Expand Down Expand Up @@ -218,7 +172,7 @@ class PerformanceContent extends Component<Props, State> {
<Button
priority="primary"
data-test-id="landing-header-trends"
onClick={this.handleTrendsClick}
onClick={() => handleTrendsClick(this.props)}
>
{t('View Trends')}
</Button>
Expand Down Expand Up @@ -259,7 +213,7 @@ class PerformanceContent extends Component<Props, State> {
eventView={this.state.eventView}
setError={this.setError}
handleSearch={this.handleSearch}
handleTrendsClick={this.handleTrendsClick}
handleTrendsClick={() => handleTrendsClick(this.props)}
shouldShowOnboarding={this.shouldShowOnboarding()}
{...this.props}
/>
Expand Down
20 changes: 20 additions & 0 deletions static/app/views/performance/landing/utils.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import {ReactText} from 'react';
import {browserHistory} from 'react-router';
import {Location} from 'history';

Expand Down Expand Up @@ -60,6 +61,25 @@ export const LANDING_DISPLAYS = [
},
];

export function excludeTransaction(
transaction: string | ReactText,
props: {eventView: EventView; location: Location}
) {
const {eventView, location} = props;

const searchConditions = new MutableSearch(eventView.query);
searchConditions.addFilterValues('!transaction', [`${transaction}`]);

browserHistory.push({
pathname: location.pathname,
query: {
...location.query,
cursor: undefined,
query: searchConditions.formatString(),
},
});
}

export function getCurrentLandingDisplay(
location: Location,
projects: Project[],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,10 @@ export function AllTransactionsView(props: BasePerformanceViewProps) {
<DoubleChartRow
{...props}
allowedCharts={[
PerformanceWidgetSetting.TPM_AREA,
PerformanceWidgetSetting.MOST_RELATED_ERRORS,
PerformanceWidgetSetting.MOST_RELATED_ISSUES,
PerformanceWidgetSetting.MOST_IMPROVED,
PerformanceWidgetSetting.MOST_REGRESSED,
]}
/>
<Table {...props} setError={usePageError().setPageError} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ function _DataDisplay<T extends WidgetDataConstraint>(
props: GenericPerformanceWidgetProps<T> &
WidgetDataProps<T> & {nextWidgetData: T; totalHeight: number}
) {
const {Visualizations, chartHeight, totalHeight, containerType} = props;
const {Visualizations, chartHeight, totalHeight, containerType, EmptyComponent} = props;

const Container = getPerformanceWidgetContainer({
containerType,
Expand Down Expand Up @@ -111,7 +111,14 @@ function _DataDisplay<T extends WidgetDataConstraint>(
/>
</ContentContainer>
))}
emptyComponent={<Placeholder height={`${totalHeight - paddingOffset}px`} />}
loadingComponent={<Placeholder height={`${totalHeight - paddingOffset}px`} />}
emptyComponent={
EmptyComponent ? (
<EmptyComponent />
) : (
<Placeholder height={`${totalHeight - paddingOffset}px`} />
)
}
/>
</Container>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,8 @@ export const RightAlignedCell = styled('div')`
`;

const ListItemContainer = styled('div')`
display: grid;
grid-template-columns: 24px auto 150px 30px;
grid-template-rows: repeat(2, auto);
grid-column-gap: ${space(1)};
display: flex;

border-top: 1px solid ${p => p.theme.border};
padding: ${space(1)} ${space(2)};
`;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {GenericPerformanceWidgetDataType} from '../types';
import {PerformanceWidgetSetting, WIDGET_DEFINITIONS} from '../widgetDefinitions';
import {LineChartListWidget} from '../widgets/lineChartListWidget';
import {SingleFieldAreaWidget} from '../widgets/singleFieldAreaWidget';
import {TrendsWidget} from '../widgets/trendsWidget';

import {ChartRowProps} from './widgetChartRow';

Expand Down Expand Up @@ -89,7 +90,7 @@ const _WidgetContainer = (props: Props) => {

switch (widgetProps.dataType) {
case GenericPerformanceWidgetDataType.trends:
throw new Error('Trends not currently supported.');
return <TrendsWidget {...props} {...widgetProps} />;
case GenericPerformanceWidgetDataType.area:
return <SingleFieldAreaWidget {...props} {...widgetProps} />;
case GenericPerformanceWidgetDataType.line_list:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import {TrendDiscoveryChildrenProps} from 'app/utils/performance/trends/trendsDiscoverQuery';
import {normalizeTrends} from 'app/views/performance/trends/utils';

export function transformTrendsDiscover(_: any, props: TrendDiscoveryChildrenProps) {
const {trendsData} = props;
const events = trendsData
? normalizeTrends((trendsData && trendsData.events && trendsData.events.data) || [])
: [];
return {
...props,
data: trendsData,
hasData: !!trendsData?.events?.data.length,
loading: props.isLoading,
isLoading: props.isLoading,
isErrored: !!props.error,
errored: props.error,
statsData: trendsData ? trendsData.stats : {},
transactionsList: events && events.slice ? events.slice(0, 3) : [],
events,
};
}
2 changes: 2 additions & 0 deletions static/app/views/performance/landing/widgets/types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ export type GenericPerformanceWidgetProps<T extends WidgetDataConstraint> = {

// Components
HeaderActions?: HeaderActions<T>;
EmptyComponent?: FunctionComponent<{height?: number}>;

Queries: Queries<T>;
Visualizations: Visualizations<T>;
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import {Fragment, FunctionComponent, ReactText, useMemo, useState} from 'react';
import {browserHistory, withRouter} from 'react-router';
import {Fragment, FunctionComponent, useMemo, useState} from 'react';
import {withRouter} from 'react-router';
import styled from '@emotion/styled';
import {Location} from 'history';

import _EventsRequest from 'app/components/charts/eventsRequest';
import {getInterval} from 'app/components/charts/utils';
import Link from 'app/components/links/link';
import Tooltip from 'app/components/tooltip';
import Truncate from 'app/components/truncate';
import {IconClose} from 'app/icons';
import {t} from 'app/locale';
import space from 'app/styles/space';
import {Organization} from 'app/types';
import DiscoverQuery from 'app/utils/discover/discoverQuery';
import EventView from 'app/utils/discover/eventView';
Expand All @@ -17,6 +19,7 @@ import withApi from 'app/utils/withApi';
import _DurationChart from 'app/views/performance/charts/chart';
import {transactionSummaryRouteWithQuery} from 'app/views/performance/transactionSummary/utils';

import {excludeTransaction} from '../../utils';
import {GenericPerformanceWidget} from '../components/performanceWidget';
import SelectableList, {RightAlignedCell} from '../components/selectableList';
import {transformDiscoverToList} from '../transforms/transformDiscoverToList';
Expand All @@ -43,22 +46,6 @@ type DataType = {
list: WidgetDataResult & ReturnType<typeof transformDiscoverToList>;
};

function excludeTransaction(transaction: string | ReactText, props: Props) {
const {eventView, location} = props;

const searchConditions = new MutableSearch(eventView.query);
searchConditions.addFilterValues('!transaction', [`${transaction}`]);

browserHistory.push({
pathname: location.pathname,
query: {
...location.query,
cursor: undefined,
query: searchConditions.formatString(),
},
});
}

export function LineChartListWidget(props: Props) {
const [selectedListIndex, setSelectListIndex] = useState<number>(0);
const {ContainerActions} = props;
Expand Down Expand Up @@ -190,17 +177,20 @@ export function LineChartListWidget(props: Props) {
selectedIndex={selectedListIndex}
setSelectedIndex={setSelectListIndex}
items={provided.widgetData.list.data.map(listItem => () => {
const transaction = listItem.transaction as string;
const transactionTarget = transactionSummaryRouteWithQuery({
orgSlug: props.organization.slug,
projectID: listItem['project.id'] as string,
transaction: listItem.transaction as string,
transaction,
query: props.eventView.getGlobalSelectionQuery(),
});
switch (props.chartSetting) {
case PerformanceWidgetSetting.MOST_RELATED_ISSUES:
return (
<Fragment>
<Link to={transactionTarget}>{listItem.transaction}</Link>
<GrowLink to={transactionTarget}>
<Truncate value={transaction} maxLength={40} />
</GrowLink>
<RightAlignedCell>
<Tooltip title={listItem.title}>
<Link
Expand All @@ -222,7 +212,9 @@ export function LineChartListWidget(props: Props) {
default:
return (
<Fragment>
<Link to={transactionTarget}>{listItem.transaction}</Link>
<GrowLink to={transactionTarget}>
<Truncate value={transaction} maxLength={40} />
</GrowLink>
<RightAlignedCell>{listItem.failure_count}</RightAlignedCell>
<CloseContainer>
<StyledIconClose
Expand Down Expand Up @@ -255,6 +247,10 @@ const CloseContainer = styled('div')`
display: flex;
align-items: center;
justify-content: center;
padding-left: ${space(1)};
`;
const GrowLink = styled(Link)`
flex-grow: 1;
`;

const StyledIconClose = styled(IconClose)`
Expand Down
Loading