From 526e6947aaf3a6271682507ca0575ad1b1a8e537 Mon Sep 17 00:00:00 2001 From: Dan Fuller Date: Fri, 17 Apr 2026 18:49:54 -0700 Subject: [PATCH] ref(flags): Remove organizations:dashboards-starred-reordering (frontend) The flag scanner classified this as flagpole-disabled. The frontend consumers switched between the legacy dashboard list endpoint (with filter=onlyFavorites) and a new per-user starred endpoint; with the flag always False only the legacy path was reachable. Removes the new-path branches and the now-orphan modules: - DashboardsNavigationItems + spec - OwnedDashboardsTable + useOwnedDashboards - The shared manage/tableView/table helper - Sort-option branches for mostFavorited and table-view defaults - starredDashboardsApiOptions - Tests that exercised the flag-enabled paths Backend changes ship in a separate PR. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../utils/dashboards/dashboardsApiOptions.tsx | 10 - .../hooks/useGetStarredDashboards.tsx | 20 +- .../dashboards/hooks/useOwnedDashboards.tsx | 33 --- .../views/dashboards/manage/index.spec.tsx | 84 +------ static/app/views/dashboards/manage/index.tsx | 94 +------- .../manage/tableView/ownedDashboardsTable.tsx | 22 -- .../manage/tableView/table.spec.tsx | 221 ----------------- .../dashboards/manage/tableView/table.tsx | 227 ------------------ .../dashboardsNavigationItems.spec.tsx | 30 --- .../dashboards/dashboardsNavigationItems.tsx | 94 -------- .../dashboardsSecondaryNavigation.spec.tsx | 7 +- .../dashboardsSecondaryNavigation.tsx | 19 +- 12 files changed, 25 insertions(+), 836 deletions(-) delete mode 100644 static/app/views/dashboards/hooks/useOwnedDashboards.tsx delete mode 100644 static/app/views/dashboards/manage/tableView/ownedDashboardsTable.tsx delete mode 100644 static/app/views/dashboards/manage/tableView/table.spec.tsx delete mode 100644 static/app/views/dashboards/manage/tableView/table.tsx delete mode 100644 static/app/views/navigation/secondary/sections/dashboards/dashboardsNavigationItems.spec.tsx delete mode 100644 static/app/views/navigation/secondary/sections/dashboards/dashboardsNavigationItems.tsx diff --git a/static/app/utils/dashboards/dashboardsApiOptions.tsx b/static/app/utils/dashboards/dashboardsApiOptions.tsx index 9cab13f4c24180..91001d29da14a4 100644 --- a/static/app/utils/dashboards/dashboardsApiOptions.tsx +++ b/static/app/utils/dashboards/dashboardsApiOptions.tsx @@ -3,16 +3,6 @@ import {apiOptions} from 'sentry/utils/api/apiOptions'; import type {QueryParamValue} from 'sentry/utils/useLocation'; import type {DashboardListItem} from 'sentry/views/dashboards/types'; -export function starredDashboardsApiOptions(organization: Organization) { - return apiOptions.as()( - '/organizations/$organizationIdOrSlug/dashboards/starred/', - { - path: {organizationIdOrSlug: organization.slug}, - staleTime: Infinity, - } - ); -} - export function dashboardsApiOptions( organization: Organization, options?: { diff --git a/static/app/views/dashboards/hooks/useGetStarredDashboards.tsx b/static/app/views/dashboards/hooks/useGetStarredDashboards.tsx index e02efadf0daa03..6ab916783009d2 100644 --- a/static/app/views/dashboards/hooks/useGetStarredDashboards.tsx +++ b/static/app/views/dashboards/hooks/useGetStarredDashboards.tsx @@ -1,17 +1,11 @@ import {useQuery} from '@tanstack/react-query'; import type {Organization} from 'sentry/types/organization'; -import { - dashboardsApiOptions, - starredDashboardsApiOptions, -} from 'sentry/utils/dashboards/dashboardsApiOptions'; +import {dashboardsApiOptions} from 'sentry/utils/dashboards/dashboardsApiOptions'; import {useHasProjectAccess} from 'sentry/utils/useHasProjectAccess'; import {useOrganization} from 'sentry/utils/useOrganization'; export function getStarredDashboardsQueryKey(organization: Organization) { - if (organization.features.includes('dashboards-starred-reordering')) { - return starredDashboardsApiOptions(organization).queryKey; - } return dashboardsApiOptions(organization, { query: {filter: 'onlyFavorites'}, }).queryKey; @@ -21,16 +15,10 @@ export function useGetStarredDashboards() { const organization = useOrganization(); const {hasProjectAccess, projectsLoaded} = useHasProjectAccess(); - const usesStarredEndpoint = organization.features.includes( - 'dashboards-starred-reordering' - ); - return useQuery({ - ...(usesStarredEndpoint - ? starredDashboardsApiOptions(organization) - : dashboardsApiOptions(organization, { - query: {filter: 'onlyFavorites'}, - })), + ...dashboardsApiOptions(organization, { + query: {filter: 'onlyFavorites'}, + }), staleTime: Infinity, enabled: hasProjectAccess || !projectsLoaded, }); diff --git a/static/app/views/dashboards/hooks/useOwnedDashboards.tsx b/static/app/views/dashboards/hooks/useOwnedDashboards.tsx deleted file mode 100644 index fd4bddc3d1a12a..00000000000000 --- a/static/app/views/dashboards/hooks/useOwnedDashboards.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import {useQuery} from '@tanstack/react-query'; - -import {selectJsonWithHeaders} from 'sentry/utils/api/apiOptions'; -import {dashboardsApiOptions} from 'sentry/utils/dashboards/dashboardsApiOptions'; -import {useOrganization} from 'sentry/utils/useOrganization'; - -export function useOwnedDashboards({ - query, - cursor, - sort, - enabled, -}: { - cursor: string; - enabled: boolean; - query: string; - sort: string; -}) { - const organization = useOrganization(); - return useQuery({ - ...dashboardsApiOptions(organization, { - query: { - query, - cursor, - sort, - filter: 'owned', - pin: 'favorites', - per_page: 20, - }, - }), - select: selectJsonWithHeaders, - enabled, - }); -} diff --git a/static/app/views/dashboards/manage/index.spec.tsx b/static/app/views/dashboards/manage/index.spec.tsx index dc78f5ee896e2e..fb80f6bb44e322 100644 --- a/static/app/views/dashboards/manage/index.spec.tsx +++ b/static/app/views/dashboards/manage/index.spec.tsx @@ -3,7 +3,7 @@ import {LocationFixture} from 'sentry-fixture/locationFixture'; import {OrganizationFixture} from 'sentry-fixture/organization'; import {ProjectFixture} from 'sentry-fixture/project'; -import {act, render, screen, userEvent, waitFor} from 'sentry-test/reactTestingLibrary'; +import {act, render, screen, userEvent} from 'sentry-test/reactTestingLibrary'; import {selectEvent} from 'sentry-test/selectEvent'; import {ProjectsStore} from 'sentry/stores/projectsStore'; @@ -285,86 +285,4 @@ describe('Dashboards > Detail', () => { expect(await screen.findByTestId('dashboard-grid')).toBeInTheDocument(); }); - - it('uses recently viewed sort by default on table view', async () => { - const org = OrganizationFixture({ - features: [...FEATURES, 'dashboards-starred-reordering'], - }); - const mockNavigate = jest.fn(); - mockUseNavigate.mockReturnValue(mockNavigate); - - // mock the view type to table - localStorageWrapper.setItem(LAYOUT_KEY, '"table"'); - - render(, { - organization: org, - }); - - const sortBy = await screen.findByTestId('sort-by-select'); - expect(sortBy).toBeInTheDocument(); - // The prefix and the selection are concatenated when using text content - expect(sortBy).toHaveTextContent('Sort ByRecently Viewed'); - }); - - it('does not show My Dashboards as a sort option in table view', async () => { - const org = OrganizationFixture({ - features: [...FEATURES, 'dashboards-starred-reordering'], - }); - const mockNavigate = jest.fn(); - mockUseNavigate.mockReturnValue(mockNavigate); - - // mock the view type to table - localStorageWrapper.setItem(LAYOUT_KEY, '"table"'); - - render(, { - organization: org, - }); - - const sortBy = await screen.findByTestId('sort-by-select'); - expect(sortBy).toBeInTheDocument(); - // The prefix and the selection are concatenated when using text content - expect(sortBy).toHaveTextContent('Sort ByRecently Viewed'); - expect(screen.queryByText('My Dashboards')).not.toBeInTheDocument(); - }); - - it('redirects the URL to the default sort option when the sort option is not valid', async () => { - const org = OrganizationFixture({ - features: [...FEATURES, 'dashboards-starred-reordering'], - }); - const mockNavigate = jest.fn(); - mockUseNavigate.mockReturnValue(mockNavigate); - mockUseLocation.mockReturnValue(LocationFixture({query: {sort: 'invalid'}})); - - localStorageWrapper.setItem(LAYOUT_KEY, '"grid"'); - - render(, { - organization: org, - }); - - expect(await screen.findByText('My Dashboards')).toBeInTheDocument(); - await waitFor(() => { - expect(mockNavigate).toHaveBeenCalledWith( - expect.objectContaining({query: {sort: 'mydashboards'}}) - ); - }); - }); - - it('shows the most favorited sort option for the %s view type', async () => { - const org = OrganizationFixture({ - features: [...FEATURES, 'dashboards-starred-reordering'], - }); - const mockNavigate = jest.fn(); - mockUseNavigate.mockReturnValue(mockNavigate); - - render(, { - organization: org, - }); - - await selectEvent.openMenu(await screen.findByRole('button', {name: /sort by/i})); - await userEvent.click(await screen.findByRole('option', {name: 'Most Starred'})); - - expect(mockNavigate).toHaveBeenCalledWith( - expect.objectContaining({query: {sort: 'mostFavorited'}}) - ); - }); }); diff --git a/static/app/views/dashboards/manage/index.tsx b/static/app/views/dashboards/manage/index.tsx index 81ce292515f7b4..faca56243c4192 100644 --- a/static/app/views/dashboards/manage/index.tsx +++ b/static/app/views/dashboards/manage/index.tsx @@ -27,7 +27,6 @@ import {SearchBar} from 'sentry/components/searchBar'; import {SentryDocumentTitle} from 'sentry/components/sentryDocumentTitle'; import {IconAdd, IconGrid, IconList} from 'sentry/icons'; import {t} from 'sentry/locale'; -import type {Organization} from 'sentry/types/organization'; import {trackAnalytics} from 'sentry/utils/analytics'; import {selectJsonWithHeaders} from 'sentry/utils/api/apiOptions'; import {dashboardsApiOptions} from 'sentry/utils/dashboards/dashboardsApiOptions'; @@ -43,12 +42,7 @@ import {useLocation} from 'sentry/utils/useLocation'; import {useNavigate} from 'sentry/utils/useNavigate'; import {useOrganization} from 'sentry/utils/useOrganization'; import {DashboardCreateLimitWrapper} from 'sentry/views/dashboards/createLimitWrapper'; -import {useOwnedDashboards} from 'sentry/views/dashboards/hooks/useOwnedDashboards'; import DashboardTable from 'sentry/views/dashboards/manage/dashboardTable'; -import { - OWNED_CURSOR_KEY, - OwnedDashboardsTable, -} from 'sentry/views/dashboards/manage/tableView/ownedDashboardsTable'; import type {DashboardsLayout} from 'sentry/views/dashboards/manage/types'; import {DashboardFilter, PREBUILT_DASHBOARD_LABEL} from 'sentry/views/dashboards/types'; import {PREBUILT_DASHBOARDS} from 'sentry/views/dashboards/utils/prebuiltConfigs'; @@ -86,22 +80,10 @@ function getDashboardsOverviewLayout(): DashboardsLayout { : TABLE; } -function getSortOptions({ - organization, - dashboardsLayout, - isOnlyPrebuilt, -}: { - dashboardsLayout: DashboardsLayout; - isOnlyPrebuilt: boolean; - organization: Organization; -}) { +function getSortOptions({isOnlyPrebuilt}: {isOnlyPrebuilt: boolean}) { const options = []; - if ( - !isOnlyPrebuilt && - (!organization.features.includes('dashboards-starred-reordering') || - dashboardsLayout === GRID) - ) { + if (!isOnlyPrebuilt) { options.push({label: t('My Dashboards'), value: 'mydashboards'}); } @@ -110,38 +92,18 @@ function getSortOptions({ {label: t('Dashboard Name (Z-A)'), value: '-title'}, {label: t('Date Created (Newest)'), value: '-dateCreated'}, {label: t('Date Created (Oldest)'), value: 'dateCreated'}, - {label: t('Most Popular'), value: 'mostPopular'} + {label: t('Most Popular'), value: 'mostPopular'}, + {label: t('Recently Viewed'), value: 'recentlyViewed'} ); - if (organization.features.includes('dashboards-starred-reordering')) { - options.push({label: t('Most Starred'), value: 'mostFavorited'}); - } - - options.push({label: t('Recently Viewed'), value: 'recentlyViewed'}); - return options; } -function getDefaultSort({ - organization, - dashboardsLayout, - isOnlyPrebuilt, -}: { - dashboardsLayout: DashboardsLayout; - isOnlyPrebuilt: boolean; - organization: Organization; -}) { +function getDefaultSort({isOnlyPrebuilt}: {isOnlyPrebuilt: boolean}) { if (isOnlyPrebuilt) { return DEFAULT_PREBUILT_SORT; } - if ( - organization.features.includes('dashboards-starred-reordering') && - dashboardsLayout === TABLE - ) { - return 'recentlyViewed'; - } - return 'mydashboards'; } @@ -173,11 +135,7 @@ function ManageDashboards() { const {hasProjectAccess, projectsLoaded} = useHasProjectAccess(); - const sortOptions = getSortOptions({ - organization, - dashboardsLayout, - isOnlyPrebuilt, - }); + const sortOptions = getSortOptions({isOnlyPrebuilt}); const { data: dashboardsResponse, @@ -199,12 +157,7 @@ function ManageDashboards() { }, }), select: selectJsonWithHeaders, - enabled: - (hasProjectAccess || !projectsLoaded) && - !( - organization.features.includes('dashboards-starred-reordering') && - dashboardsLayout === TABLE - ), + enabled: hasProjectAccess || !projectsLoaded, }); const dashboardsWithoutPrebuiltConfigs = dashboardsResponse?.json; @@ -231,16 +184,6 @@ function ManageDashboards() { [dashboardsWithoutPrebuiltConfigs] ); - const ownedDashboards = useOwnedDashboards({ - query: decodeScalar(location.query.query, ''), - cursor: decodeScalar(location.query[OWNED_CURSOR_KEY], ''), - sort: getActiveSort()!.value, - enabled: - (hasProjectAccess || !projectsLoaded) && - organization.features.includes('dashboards-starred-reordering') && - dashboardsLayout === TABLE, - }); - const dashboardsPageLinks = dashboardsResponse?.headers.Link ?? ''; function setRowsAndColumns(containerWidth: number) { @@ -311,11 +254,7 @@ function ManageDashboards() { useEffect(() => { const urlSort = decodeScalar(location.query.sort); - const defaultSort = getDefaultSort({ - organization, - dashboardsLayout, - isOnlyPrebuilt, - }); + const defaultSort = getDefaultSort({isOnlyPrebuilt}); if (urlSort && !sortOptions.some(option => option.value === urlSort)) { // The sort option is not valid, so we need to set the default sort // in the URL @@ -335,11 +274,7 @@ function ManageDashboards() { ]); function getActiveSort() { - const defaultSort = getDefaultSort({ - organization, - dashboardsLayout, - isOnlyPrebuilt, - }); + const defaultSort = getDefaultSort({isOnlyPrebuilt}); const urlSort = decodeScalar(location.query.sort, defaultSort); if (urlSort) { @@ -533,12 +468,6 @@ function ManageDashboards() { rowCount={rowCount} columnCount={columnCount} /> - ) : organization.features.includes('dashboards-starred-reordering') ? ( - ) : ( {renderDashboards()} - {!( - organization.features.includes('dashboards-starred-reordering') && - dashboardsLayout === TABLE - ) && renderPagination()} + {renderPagination()} diff --git a/static/app/views/dashboards/manage/tableView/ownedDashboardsTable.tsx b/static/app/views/dashboards/manage/tableView/ownedDashboardsTable.tsx deleted file mode 100644 index 0be01a10791ae9..00000000000000 --- a/static/app/views/dashboards/manage/tableView/ownedDashboardsTable.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import {t} from 'sentry/locale'; - -import type {DashboardTableProps} from './table'; -import {DashboardTable} from './table'; - -export const OWNED_CURSOR_KEY = 'ownedCursor'; - -export function OwnedDashboardsTable({ - dashboards, - isLoading, - pageLinks, -}: Pick) { - return ( - - ); -} diff --git a/static/app/views/dashboards/manage/tableView/table.spec.tsx b/static/app/views/dashboards/manage/tableView/table.spec.tsx deleted file mode 100644 index 87cf4f27b9f05b..00000000000000 --- a/static/app/views/dashboards/manage/tableView/table.spec.tsx +++ /dev/null @@ -1,221 +0,0 @@ -import {DashboardFixture, DashboardListItemFixture} from 'sentry-fixture/dashboard'; -import {UserFixture} from 'sentry-fixture/user'; - -import { - render, - renderGlobalModal, - screen, - userEvent, - within, -} from 'sentry-test/reactTestingLibrary'; - -import {DashboardTable} from './table'; - -describe('DashboardTable', () => { - beforeEach(() => { - MockApiClient.clearMockResponses(); - MockApiClient.addMockResponse({ - url: '/organizations/org-slug/dashboards/', - method: 'GET', - body: {dashboards: []}, - }); - }); - - it('should render', () => { - render( - - ); - - expect(screen.getByText('My custom dashboards')).toBeInTheDocument(); - expect(screen.getByText('Test')).toBeInTheDocument(); - expect(screen.getByText('My Projects')).toBeInTheDocument(); - - // 0 widgets - expect(screen.getByText('0')).toBeInTheDocument(); - - // The dashboard is starred, so the button should prompt "Unstar" - expect(screen.getByLabelText('Unstar')).toBeInTheDocument(); - }); - - it('renders environments', () => { - render( - - ); - - expect(screen.getByText('production, staging')).toBeInTheDocument(); - }); - - it('should render last visited', () => { - const now = new Date().toISOString(); - render( - - ); - - expect(screen.getByText('My custom dashboard')).toBeInTheDocument(); - - const row = screen.getByTestId('table-row-0'); - - const lastVisitedCell = within(row).getByRole('time'); - expect(lastVisitedCell).toBeInTheDocument(); - - // Since the timestamp is rendered as a relative time, this - // matches the format of the "0ms ago" text more robustly in - // case the timestamp does not exactly match - const lastVisitedContent = lastVisitedCell.textContent; - expect(lastVisitedContent).toMatch(/\w+s ago$/); - }); - - it('renders release filters', () => { - render( - - ); - - const filterCells = screen.getAllByLabelText('release:[1.0.0]'); - expect(filterCells[0]).toHaveTextContent('release is 1.0.0'); - }); - - it('should update the dashboard favorite status', async () => { - const mockFavoriteRequest = MockApiClient.addMockResponse({ - url: '/organizations/org-slug/dashboards/1/favorite/', - method: 'PUT', - body: {isFavorited: true}, - }); - - render( - - ); - - const starButton = screen.getByLabelText('Star'); - await userEvent.click(starButton); - - expect(mockFavoriteRequest).toHaveBeenCalledWith( - '/organizations/org-slug/dashboards/1/favorite/', - expect.objectContaining({ - method: 'PUT', - data: {isFavorited: true}, - }) - ); - }); - - it('can duplicate a dashboard', async () => { - MockApiClient.addMockResponse({ - url: '/organizations/org-slug/dashboards/1/', - method: 'GET', - body: DashboardFixture([], { - id: '1', - }), - }); - const mockDuplicateRequest = MockApiClient.addMockResponse({ - url: '/organizations/org-slug/dashboards/', - method: 'POST', - body: DashboardListItemFixture({id: '2'}), - }); - - render( - - ); - - await userEvent.click(screen.getByLabelText('More options')); - await userEvent.click(screen.getByText('Duplicate')); - - expect(mockDuplicateRequest).toHaveBeenCalledWith( - '/organizations/org-slug/dashboards/', - expect.objectContaining({ - method: 'POST', - data: expect.objectContaining({ - title: 'Dashboard copy', - widgets: [], - }), - }) - ); - }); - - it('can delete a dashboard', async () => { - const mockDeleteRequest = MockApiClient.addMockResponse({ - url: '/organizations/org-slug/dashboards/1/', - method: 'DELETE', - }); - - render( - - ); - renderGlobalModal(); - - await userEvent.click(screen.getByLabelText('More options')); - await userEvent.click(screen.getByText('Delete')); - - expect( - await screen.findByText('Are you sure you want to delete this dashboard?') - ).toBeInTheDocument(); - await userEvent.click(await screen.findByText('Confirm')); - - expect(mockDeleteRequest).toHaveBeenCalledWith( - '/organizations/org-slug/dashboards/1/', - expect.objectContaining({ - method: 'DELETE', - }) - ); - }); -}); diff --git a/static/app/views/dashboards/manage/tableView/table.tsx b/static/app/views/dashboards/manage/tableView/table.tsx deleted file mode 100644 index b51872a1f66922..00000000000000 --- a/static/app/views/dashboards/manage/tableView/table.tsx +++ /dev/null @@ -1,227 +0,0 @@ -import styled from '@emotion/styled'; -import {useQueryClient} from '@tanstack/react-query'; - -import {UserAvatar} from '@sentry/scraps/avatar'; -import type {CursorHandler} from '@sentry/scraps/pagination'; -import {Pagination} from '@sentry/scraps/pagination'; -import {Tooltip} from '@sentry/scraps/tooltip'; - -import {updateDashboardFavorite} from 'sentry/actionCreators/dashboards'; -import {ActivityAvatar} from 'sentry/components/activity/item/avatar'; -import {openConfirmModal} from 'sentry/components/confirm'; -import {SavedEntityTable} from 'sentry/components/savedEntityTable'; -import {t} from 'sentry/locale'; -import {useApi} from 'sentry/utils/useApi'; -import {useNavigate} from 'sentry/utils/useNavigate'; -import {useOrganization} from 'sentry/utils/useOrganization'; -import {DashboardCreateLimitWrapper} from 'sentry/views/dashboards/createLimitWrapper'; -import {useDeleteDashboard} from 'sentry/views/dashboards/hooks/useDeleteDashboard'; -import {useDuplicateDashboard} from 'sentry/views/dashboards/hooks/useDuplicateDashboard'; -import {useResetDashboardLists} from 'sentry/views/dashboards/hooks/useResetDashboardLists'; -import type {DashboardListItem} from 'sentry/views/dashboards/types'; -import {PREBUILT_DASHBOARD_LABEL} from 'sentry/views/dashboards/types'; - -export interface DashboardTableProps { - cursorKey: string; - dashboards: DashboardListItem[]; - isLoading: boolean; - title: string; - pageLinks?: string; -} - -export function DashboardTable({ - dashboards, - isLoading, - title, - pageLinks, - cursorKey, -}: DashboardTableProps) { - const api = useApi(); - const queryClient = useQueryClient(); - const organization = useOrganization(); - const navigate = useNavigate(); - const resetDashboardLists = useResetDashboardLists(); - const handleDuplicateDashboard = useDuplicateDashboard({ - onSuccess: resetDashboardLists, - }); - const handleDeleteDashboard = useDeleteDashboard({ - onSuccess: resetDashboardLists, - }); - - const handleCursor: CursorHandler = (_cursor, pathname, query) => { - navigate({ - pathname, - query: {...query, [cursorKey]: _cursor}, - }); - }; - - return ( - - {title} - - - - {t('Name')} - - - {t('Project')} - - - {t('Envs')} - - - {t('Filter')} - - - {t('Widgets')} - - - {t('Creator')} - - - {t('Last Viewed')} - - - {t('Date Created')} - - - - } - isEmpty={dashboards.length === 0} - // TODO: DAIN-715 Update empty message - emptyMessage={t('No dashboards found')} - > - {dashboards.map((dashboard, index) => ( - - - { - await updateDashboardFavorite( - api, - queryClient, - organization, - dashboard.id, - !dashboard.isFavorited - ); - resetDashboardLists(); - }} - /> - - - - {dashboard.title} - - - - - - - - - - - - - {dashboard.widgetPreview.length} - - - {dashboard.createdBy === null ? ( - - - - ) : dashboard.createdBy ? ( - - ) : null} - - - - - - - - - - {({ - hasReachedDashboardLimit, - isLoading: isLoadingDashboardsLimit, - limitMessage, - }) => ( - handleDuplicateDashboard(dashboard, 'table'), - disabled: hasReachedDashboardLimit || isLoadingDashboardsLimit, - tooltip: limitMessage, - }, - ...(dashboard.createdBy === null - ? [] - : [ - { - key: 'delete', - label: t('Delete'), - priority: 'danger' as const, - onAction: () => { - openConfirmModal({ - message: t( - 'Are you sure you want to delete this dashboard?' - ), - priority: 'danger', - onConfirm: () => - handleDeleteDashboard(dashboard, 'table'), - }); - }, - }, - ]), - ]} - /> - )} - - - - ))} - - - - ); -} - -function getDashboardFiltersQuery(dashboard: DashboardListItem) { - // Dashboards only currently support release filters - return dashboard.filters?.release - ? `release:[${dashboard.filters.release.join(',')}]` - : ''; -} - -const Container = styled('div')` - container-type: inline-size; -`; - -// TODO: DAIN-719 Update the widths to be consistent with mockup -const SavedEntityTableWithColumns = styled(SavedEntityTable)` - grid-template-areas: 'star name project envs filter num-widgets created-by last-visited created actions'; - grid-template-columns: - 40px 20% minmax(auto, 120px) minmax(auto, 120px) minmax(auto, 200px) - minmax(auto, 120px) 80px auto auto 48px; -`; - -const TableHeading = styled('h2')` - display: flex; - justify-content: space-between; - align-items: center; - font-size: ${p => p.theme.font.size.xl}; - margin-top: ${p => p.theme.space['2xl']}; - margin-bottom: ${p => p.theme.space.lg}; -`; diff --git a/static/app/views/navigation/secondary/sections/dashboards/dashboardsNavigationItems.spec.tsx b/static/app/views/navigation/secondary/sections/dashboards/dashboardsNavigationItems.spec.tsx deleted file mode 100644 index 186f904ddb0f7a..00000000000000 --- a/static/app/views/navigation/secondary/sections/dashboards/dashboardsNavigationItems.spec.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import {DashboardListItemFixture} from 'sentry-fixture/dashboard'; - -import {render, screen} from 'sentry-test/reactTestingLibrary'; - -import {DashboardsNavigationItems} from 'sentry/views/navigation/secondary/sections/dashboards/dashboardsNavigationItems'; -import {SecondaryNavigationContextProvider} from 'sentry/views/navigation/secondaryNavigationContext'; - -describe('DashboardsNavigationItems', () => { - it('should render', () => { - render( - - - - ); - - expect(screen.getByText('Dashboard 1')).toBeInTheDocument(); - expect(screen.getByText('Dashboard 2')).toBeInTheDocument(); - }); -}); diff --git a/static/app/views/navigation/secondary/sections/dashboards/dashboardsNavigationItems.tsx b/static/app/views/navigation/secondary/sections/dashboards/dashboardsNavigationItems.tsx deleted file mode 100644 index 7f86275ad1b123..00000000000000 --- a/static/app/views/navigation/secondary/sections/dashboards/dashboardsNavigationItems.tsx +++ /dev/null @@ -1,94 +0,0 @@ -import * as Sentry from '@sentry/react'; - -import {Text} from '@sentry/scraps/text'; -import {Tooltip} from '@sentry/scraps/tooltip'; - -import {defined} from 'sentry/utils'; -import {useLocation} from 'sentry/utils/useLocation'; -import {useOrganization} from 'sentry/utils/useOrganization'; -import {useProjects} from 'sentry/utils/useProjects'; -import {useUser} from 'sentry/utils/useUser'; -import {useReorderStarredDashboards} from 'sentry/views/dashboards/hooks/useReorderStarredDashboards'; -import type {DashboardListItem} from 'sentry/views/dashboards/types'; -import {getIdFromLocation} from 'sentry/views/explore/contexts/pageParamsContext/id'; -import {SecondaryNavigation} from 'sentry/views/navigation/secondary/components'; - -type DashboardsNavigationItemsProps = { - initialDashboards: DashboardListItem[]; -}; - -export function DashboardsNavigationItems({ - initialDashboards, -}: DashboardsNavigationItemsProps) { - const organization = useOrganization(); - const user = useUser(); - const location = useLocation(); - const id = getIdFromLocation(location); - - const {projects} = useProjects(); - - const reorderStarredDashboards = useReorderStarredDashboards(); - - return ( - { - reorderStarredDashboards(newDashboards); - }} - > - {dashboard => { - const dashboardProjects = new Set((dashboard?.projects ?? []).map(String)); - if (!defined(dashboard?.projects)) { - logUndefinedDashboardValue(dashboard, { - organizationId: organization.id, - userId: user.id, - }); - } - - const dashboardProjectPlatforms = projects - .filter(p => dashboardProjects.has(p.id)) - .map(p => p.platform) - .filter(defined); - - return ( - - } - > - - - {dashboard.title} - - - - ); - }} - - ); -} - -function logUndefinedDashboardValue( - dashboard: DashboardListItem, - {organizationId, userId}: {organizationId: string; userId: string} -) { - Sentry.setTag('organization', organizationId); - Sentry.setTag('dashboard.id', dashboard.id); - Sentry.setTag('user.id', userId); - Sentry.captureMessage('dashboard.projects is undefined in starred sidebar', { - level: 'warning', - }); -} diff --git a/static/app/views/navigation/secondary/sections/dashboards/dashboardsSecondaryNavigation.spec.tsx b/static/app/views/navigation/secondary/sections/dashboards/dashboardsSecondaryNavigation.spec.tsx index 0f34f1b03621b1..c5dc4094373dfa 100644 --- a/static/app/views/navigation/secondary/sections/dashboards/dashboardsSecondaryNavigation.spec.tsx +++ b/static/app/views/navigation/secondary/sections/dashboards/dashboardsSecondaryNavigation.spec.tsx @@ -11,9 +11,7 @@ describe('DashboardsSecondaryNavigation', () => { let organization: Organization; beforeEach(() => { - organization = OrganizationFixture({ - features: ['dashboards-starred-reordering'], - }); + organization = OrganizationFixture(); MockApiClient.addMockResponse({ url: '/organizations/org-slug/group-search-views/starred/', @@ -21,7 +19,7 @@ describe('DashboardsSecondaryNavigation', () => { }); MockApiClient.addMockResponse({ - url: `/organizations/${organization.slug}/dashboards/starred/`, + url: `/organizations/${organization.slug}/dashboards/`, body: [ DashboardListItemFixture({ id: '9999', @@ -32,6 +30,7 @@ describe('DashboardsSecondaryNavigation', () => { title: 'Dashboard 1', }), ], + match: [MockApiClient.matchQuery({filter: 'onlyFavorites'})], }); }); diff --git a/static/app/views/navigation/secondary/sections/dashboards/dashboardsSecondaryNavigation.tsx b/static/app/views/navigation/secondary/sections/dashboards/dashboardsSecondaryNavigation.tsx index ee9c3484821c60..fc9900422c0b4b 100644 --- a/static/app/views/navigation/secondary/sections/dashboards/dashboardsSecondaryNavigation.tsx +++ b/static/app/views/navigation/secondary/sections/dashboards/dashboardsSecondaryNavigation.tsx @@ -16,7 +16,6 @@ import {DashboardFilter, PREBUILT_DASHBOARD_LABEL} from 'sentry/views/dashboards import type {DashboardListItem} from 'sentry/views/dashboards/types'; import {isPrimaryNavigationLinkActive} from 'sentry/views/navigation/primary/components'; import {SecondaryNavigation} from 'sentry/views/navigation/secondary/components'; -import {DashboardsNavigationItems} from 'sentry/views/navigation/secondary/sections/dashboards/dashboardsNavigationItems'; export function DashboardsSecondaryNavigation() { const organization = useOrganization(); @@ -80,17 +79,13 @@ export function DashboardsSecondaryNavigation() { title={t('Starred Dashboards')} > - {organization.features.includes('dashboards-starred-reordering') ? ( - - ) : ( - - )} +