From 737c6f09a8ef73aaa364fb9ee8f77f4f5a01e44d Mon Sep 17 00:00:00 2001 From: Adrian Date: Wed, 21 May 2025 13:36:13 -0700 Subject: [PATCH 1/2] feat: add Codecov repository picker --- .../codecov/repoPicker/repoPicker.tsx | 21 ++++ .../codecov/repoPicker/repoSelector.tsx | 97 +++++++++++++++++++ static/app/views/codecov/tests/tests.tsx | 7 ++ 3 files changed, 125 insertions(+) create mode 100644 static/app/components/codecov/repoPicker/repoPicker.tsx create mode 100644 static/app/components/codecov/repoPicker/repoSelector.tsx diff --git a/static/app/components/codecov/repoPicker/repoPicker.tsx b/static/app/components/codecov/repoPicker/repoPicker.tsx new file mode 100644 index 00000000000000..f3d17d58babe15 --- /dev/null +++ b/static/app/components/codecov/repoPicker/repoPicker.tsx @@ -0,0 +1,21 @@ +import {useCodecovContext} from 'sentry/components/codecov/context/codecovContext'; +import {RepoSelector} from 'sentry/components/codecov/repoPicker/repoSelector'; +import {useLocation} from 'sentry/utils/useLocation'; +import {useNavigate} from 'sentry/utils/useNavigate'; + +export function RepoPicker() { + const {repository} = useCodecovContext(); + const location = useLocation(); + const navigate = useNavigate(); + + return ( + { + const currentParams = new URLSearchParams(location.search); + currentParams.set('repository', newRepository); + navigate(`${location.pathname}?${currentParams.toString()}`, {replace: true}); + }} + /> + ); +} diff --git a/static/app/components/codecov/repoPicker/repoSelector.tsx b/static/app/components/codecov/repoPicker/repoSelector.tsx new file mode 100644 index 00000000000000..3a152d56da9cfb --- /dev/null +++ b/static/app/components/codecov/repoPicker/repoSelector.tsx @@ -0,0 +1,97 @@ +import {useCallback, useMemo} from 'react'; +import styled from '@emotion/styled'; + +import type {SelectOption, SingleSelectProps} from 'sentry/components/core/compactSelect'; +import {CompactSelect} from 'sentry/components/core/compactSelect'; +import DropdownButton from 'sentry/components/dropdownButton'; +import {t} from 'sentry/locale'; + +const CODECOV_PLACEHOLDER_REPOS = ['test repo 1', 'test-repo-2', 'Test Repo 3']; + +export interface RepoSelectorProps { + onChange: (data: string) => void; + /** + * Repository date value + */ + repository: string | null; + /** + * Optional trigger for the assignee selector. If nothing passed in, + * the default trigger will be used + */ + trigger?: ( + props: Omit, 'children'>, + isOpen: boolean + ) => React.ReactNode; +} + +export function RepoSelector({onChange, trigger, repository}: RepoSelectorProps) { + const options = useMemo((): Array> => { + // TODO: When API is ready, replace placeholder w/ api response + const repoSet = new Set([ + ...(repository ? [repository] : []), + ...(CODECOV_PLACEHOLDER_REPOS.length ? CODECOV_PLACEHOLDER_REPOS : []), + ]); + + return [...repoSet].map((value): SelectOption => { + return { + // TODO: ideally this has a unique id, possibly adjust set to an + // object when you have backend response + value, + label: {value}, + textValue: value, + }; + }); + }, [repository]); + + const handleChange = useCallback['onChange']>>( + newSelected => { + onChange(newSelected.value); + }, + [onChange] + ); + + return ( + { + const defaultLabel = options.some(item => item.value === repository) + ? repository?.toUpperCase() + : t('Select Repo'); + + return ( + + + {defaultLabel} + + + ); + }) + } + /> + ); +} + +const TriggerLabelWrap = styled('span')` + position: relative; + min-width: 0; +`; + +const TriggerLabel = styled('span')` + ${p => p.theme.overflowEllipsis} + width: auto; +`; + +const OptionLabel = styled('span')` + div { + margin: 0; + } +`; diff --git a/static/app/views/codecov/tests/tests.tsx b/static/app/views/codecov/tests/tests.tsx index 17a681b73669d0..3a0d150c89a4b4 100644 --- a/static/app/views/codecov/tests/tests.tsx +++ b/static/app/views/codecov/tests/tests.tsx @@ -1,6 +1,8 @@ import styled from '@emotion/styled'; +import CodecovProvider from 'sentry/components/codecov/container/codecovParamsProvider'; import {DatePicker} from 'sentry/components/codecov/datePicker/datePicker'; +import {RepoPicker} from 'sentry/components/codecov/repoPicker/repoPicker'; import PageFilterBar from 'sentry/components/organizations/pageFilterBar'; import PageFiltersContainer from 'sentry/components/organizations/pageFilters/container'; import {space} from 'sentry/styles/space'; @@ -24,6 +26,11 @@ export default function TestsPage() { + + + + + {/* TODO: Conditionally show these if the branch we're in is the main branch */} From bcc102f61bddba0fa485262570fadcdd0e73b5e9 Mon Sep 17 00:00:00 2001 From: Adrian Date: Thu, 22 May 2025 13:48:53 -0700 Subject: [PATCH 2/2] adjust logic for updating url params --- .../components/codecov/repoPicker/repoPicker.tsx | 16 +++++++++------- .../codecov/repoPicker/repoSelector.tsx | 2 +- static/app/views/codecov/tests/tests.spec.tsx | 15 ++++++++------- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/static/app/components/codecov/repoPicker/repoPicker.tsx b/static/app/components/codecov/repoPicker/repoPicker.tsx index f3d17d58babe15..7c4ca9d07d0462 100644 --- a/static/app/components/codecov/repoPicker/repoPicker.tsx +++ b/static/app/components/codecov/repoPicker/repoPicker.tsx @@ -1,20 +1,22 @@ +import {useSearchParams} from 'react-router-dom'; + import {useCodecovContext} from 'sentry/components/codecov/context/codecovContext'; import {RepoSelector} from 'sentry/components/codecov/repoPicker/repoSelector'; -import {useLocation} from 'sentry/utils/useLocation'; -import {useNavigate} from 'sentry/utils/useNavigate'; export function RepoPicker() { const {repository} = useCodecovContext(); - const location = useLocation(); - const navigate = useNavigate(); + const [searchParams, setSearchParams] = useSearchParams(); return ( { - const currentParams = new URLSearchParams(location.search); - currentParams.set('repository', newRepository); - navigate(`${location.pathname}?${currentParams.toString()}`, {replace: true}); + const currentParams = Object.fromEntries(searchParams.entries()); + const updatedParams = { + ...currentParams, + repository: newRepository, + }; + setSearchParams(updatedParams); }} /> ); diff --git a/static/app/components/codecov/repoPicker/repoSelector.tsx b/static/app/components/codecov/repoPicker/repoSelector.tsx index 3a152d56da9cfb..a7231b37eb4b19 100644 --- a/static/app/components/codecov/repoPicker/repoSelector.tsx +++ b/static/app/components/codecov/repoPicker/repoSelector.tsx @@ -11,7 +11,7 @@ const CODECOV_PLACEHOLDER_REPOS = ['test repo 1', 'test-repo-2', 'Test Repo 3']; export interface RepoSelectorProps { onChange: (data: string) => void; /** - * Repository date value + * Repository value */ repository: string | null; /** diff --git a/static/app/views/codecov/tests/tests.spec.tsx b/static/app/views/codecov/tests/tests.spec.tsx index e23684875cc26e..96bb177c35dbc5 100644 --- a/static/app/views/codecov/tests/tests.spec.tsx +++ b/static/app/views/codecov/tests/tests.spec.tsx @@ -1,19 +1,20 @@ -import {OrganizationFixture} from 'sentry-fixture/organization'; - import {render, screen} from 'sentry-test/reactTestingLibrary'; import TestsPage from 'sentry/views/codecov/tests/tests'; -const COVERAGE_FEATURE = 'codecov-ui'; - describe('CoveragePageWrapper', () => { describe('when the wrapper is used', () => { - it('renders the passed children', () => { + it('renders the passed children', async () => { render(, { - organization: OrganizationFixture({features: [COVERAGE_FEATURE]}), + initialRouterConfig: { + location: { + pathname: '/codecov/tests', + query: {codecovPeriod: '7d'}, + }, + }, }); - const testsAnalytics = screen.getByText('Test Analytics'); + const testsAnalytics = await screen.findByText('Test Analytics'); expect(testsAnalytics).toBeInTheDocument(); }); });