Skip to content
32 changes: 31 additions & 1 deletion static/app/views/issueList/overview.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ import {TagStore} from 'sentry/stores/tagStore';
import {localStorageWrapper} from 'sentry/utils/localStorage';
import * as parseLinkHeaderModule from 'sentry/utils/parseLinkHeader';
import IssueListOverview from 'sentry/views/issueList/overview';
import {DEFAULT_QUERY} from 'sentry/views/issueList/utils';
import {
DEFAULT_QUERY,
getStoredIssueSort,
IssueSortOptions,
} from 'sentry/views/issueList/utils';

const DEFAULT_LINKS_HEADER =
'<http://127.0.0.1:8000/api/0/organizations/org-slug/issues/?cursor=1443575731:0:1>; rel="previous"; results="false"; cursor="1443575731:0:1", ' +
Expand Down Expand Up @@ -350,6 +354,32 @@ describe('IssueList', () => {
});
});

describe('sort persistence', () => {
it('does not persist sort to localStorage without the recommended-sort feature', async () => {
render(<IssueListOverview />, {organization, initialRouterConfig});

await userEvent.click(await screen.findByRole('button', {name: 'Last Seen'}));
await userEvent.click(screen.getByRole('option', {name: 'Events'}));

// Writing while the feature is off would leave a stale value that overrides
// the Recommended default once the flag is enabled.
expect(getStoredIssueSort(organization.slug)).toBeNull();
});

it('persists sort to localStorage with the recommended-sort feature', async () => {
const featureOrg = OrganizationFixture({
...organization,
features: ['issue-stream-recommended-sort'],
});
render(<IssueListOverview />, {organization: featureOrg, initialRouterConfig});

await userEvent.click(await screen.findByRole('button', {name: 'Recommended'}));
await userEvent.click(screen.getByRole('option', {name: 'Events'}));

expect(getStoredIssueSort(featureOrg.slug)).toBe(IssueSortOptions.FREQ);
});
});

describe('transitionTo', () => {
it('pushes to history when query is updated', async () => {
MockApiClient.addMockResponse({
Expand Down
18 changes: 13 additions & 5 deletions static/app/views/issueList/overview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ import {
DEFAULT_ISSUE_STREAM_SORT,
DEFAULT_QUERY,
FOR_REVIEW_QUERIES,
getStoredIssueSort,
setStoredIssueSort,
isForReviewQuery,
IssueSortOptions,
Query,
Expand Down Expand Up @@ -213,10 +215,13 @@ function IssueListOverviewInner({
const query = defined(location.query.query)
? (decodeScalar(location.query.query) ?? '')
: initialQuery;
const sort = decodeScalar(
location.query.sort,
DEFAULT_ISSUE_STREAM_SORT
) as IssueSortOptions;
const hasRecommendedSort = organization.features.includes(
'issue-stream-recommended-sort'
);
const defaultSort = hasRecommendedSort
? (getStoredIssueSort(organization.slug) ?? IssueSortOptions.RECOMMENDED)
: DEFAULT_ISSUE_STREAM_SORT;
const sort = decodeScalar(location.query.sort, defaultSort) as IssueSortOptions;

const getGroupStatsPeriod = useCallback((): string => {
const currentPeriod = decodeScalar(
Expand Down Expand Up @@ -248,7 +253,7 @@ function IssueListOverviewInner({
params.start = getUtcDateString(params.start);
}

if (sort !== DEFAULT_ISSUE_STREAM_SORT) {
if (sort !== IssueSortOptions.DATE) {
params.sort = sort;
}

Expand Down Expand Up @@ -690,6 +695,9 @@ function IssueListOverviewInner({
organization,
sort: newSort,
});
if (hasRecommendedSort) {
setStoredIssueSort(organization.slug, newSort as IssueSortOptions);
}
transitionTo({sort: newSort});
};

Expand Down
19 changes: 19 additions & 0 deletions static/app/views/issueList/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {t} from 'sentry/locale';
import type {Event} from 'sentry/types/event';
import type {Group} from 'sentry/types/group';
import type {Organization} from 'sentry/types/organization';
import {localStorageWrapper} from 'sentry/utils/localStorage';

export const DEFAULT_QUERY = 'is:unresolved issue.priority:[high, medium]';

Expand Down Expand Up @@ -85,6 +86,24 @@ export const FOR_REVIEW_QUERIES: string[] = [Query.FOR_REVIEW];
export const SAVED_SEARCHES_SIDEBAR_OPEN_LOCALSTORAGE_KEY =
'issue-stream-saved-searches-sidebar-open';

const ISSUE_STREAM_SORT_LOCALSTORAGE_KEY = 'issue-stream-sort';

function makeSortStorageKey(orgSlug: string): string {
return `${ISSUE_STREAM_SORT_LOCALSTORAGE_KEY}:${orgSlug}`;
}

export function getStoredIssueSort(orgSlug: string): IssueSortOptions | null {
const value = localStorageWrapper.getItem(makeSortStorageKey(orgSlug));
if (value && Object.values(IssueSortOptions).includes(value as IssueSortOptions)) {
return value as IssueSortOptions;
}
return null;
}

export function setStoredIssueSort(orgSlug: string, sort: IssueSortOptions): void {
localStorageWrapper.setItem(makeSortStorageKey(orgSlug), sort);
}

export function createIssueLink({
organization,
data,
Expand Down
Loading