Skip to content

feat(Codecov): add Codecov repository picker #92068

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 4 commits into from
May 22, 2025
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
23 changes: 23 additions & 0 deletions static/app/components/codecov/repoPicker/repoPicker.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import {useSearchParams} from 'react-router-dom';

import {useCodecovContext} from 'sentry/components/codecov/context/codecovContext';
import {RepoSelector} from 'sentry/components/codecov/repoPicker/repoSelector';

export function RepoPicker() {
const {repository} = useCodecovContext();
const [searchParams, setSearchParams] = useSearchParams();

return (
<RepoSelector
repository={repository}
onChange={newRepository => {
const currentParams = Object.fromEntries(searchParams.entries());
const updatedParams = {
...currentParams,
repository: newRepository,
};
setSearchParams(updatedParams);
}}
/>
);
}
97 changes: 97 additions & 0 deletions static/app/components/codecov/repoPicker/repoSelector.tsx
Original file line number Diff line number Diff line change
@@ -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 value
*/
repository: string | null;
/**
* Optional trigger for the assignee selector. If nothing passed in,
* the default trigger will be used
*/
trigger?: (
props: Omit<React.HTMLAttributes<HTMLElement>, 'children'>,
isOpen: boolean
) => React.ReactNode;
}

export function RepoSelector({onChange, trigger, repository}: RepoSelectorProps) {
const options = useMemo((): Array<SelectOption<string>> => {
// 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<string> => {
return {
// TODO: ideally this has a unique id, possibly adjust set to an
// object when you have backend response
value,
label: <OptionLabel>{value}</OptionLabel>,
textValue: value,
};
});
}, [repository]);

const handleChange = useCallback<NonNullable<SingleSelectProps<string>['onChange']>>(
newSelected => {
onChange(newSelected.value);
},
[onChange]
);

return (
<CompactSelect
options={options}
value={repository ?? ''}
onChange={handleChange}
menuWidth={'16rem'}
trigger={
trigger ??
((triggerProps, isOpen) => {
const defaultLabel = options.some(item => item.value === repository)
? repository?.toUpperCase()
: t('Select Repo');

return (
<DropdownButton
isOpen={isOpen}
data-test-id="page-filter-codecov-repository-selector"
{...triggerProps}
>
<TriggerLabelWrap>
<TriggerLabel>{defaultLabel}</TriggerLabel>
</TriggerLabelWrap>
</DropdownButton>
);
})
}
/>
);
}

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;
}
`;
15 changes: 8 additions & 7 deletions static/app/views/codecov/tests/tests.spec.tsx
Original file line number Diff line number Diff line change
@@ -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(<TestsPage />, {
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();
});
});
Expand Down
8 changes: 7 additions & 1 deletion static/app/views/codecov/tests/tests.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import styled from '@emotion/styled';

import CodecovProvider from 'sentry/components/codecov/container/codecovParamsProvider';
import {DatePicker} from 'sentry/components/codecov/datePicker/datePicker';
// import TestAnalyticsTable from 'sentry/components/codecov/testAnalytics/testAnalyticsTable';
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';
Expand Down Expand Up @@ -72,6 +73,11 @@ export default function TestsPage() {
<DatePicker />
</PageFilterBar>
</PageFiltersContainer>
<CodecovProvider>
<PageFilterBar condensed>
<RepoPicker />
</PageFilterBar>
</CodecovProvider>
{/* TODO: Conditionally show these if the branch we're in is the main branch */}
<Summaries />
<TestAnalyticsTable response={fakeApiResponse} sort={sorts[0]} />
Expand Down
Loading