Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
7ac8d63
fix: forward triggerProps id to custom trigger
TkDodo Dec 17, 2025
5ea1fdb
feat: SelectTrigger.IconButton
TkDodo Dec 17, 2025
a8d0c35
ref: do not make children required for SelectTrigger.Button
TkDodo Dec 17, 2025
1474c3c
ref: knip
TkDodo Dec 17, 2025
07a418e
Merge branch 'tkdodo/ref/separate-iconButton-trigger' into tkdodo/ref…
TkDodo Dec 17, 2025
e2ffc8c
Merge branch 'tkdodo/fix/id-forward-on-compactSelect-trigger' into tk…
TkDodo Dec 17, 2025
806d97b
ref: remove triggerProps from compactSelect
TkDodo Dec 17, 2025
894bc46
ref: triggerProps to trigger migration (done with codemod)
TkDodo Dec 17, 2025
6e4be77
ref: inline triggerProps to make codemod work
TkDodo Dec 17, 2025
192acbb
Merge branch 'master' into tkdodo/ref/remove-compactSelect-triggerProps
TkDodo Dec 17, 2025
eda05b5
ref: triggerProps to trigger migration (done with codemod)
TkDodo Dec 17, 2025
519a412
fix: imports
TkDodo Dec 17, 2025
e281afa
Merge branch 'master' into tkdodo/ref/remove-compactSelect-triggerProps
TkDodo Dec 18, 2025
e74500d
fix: tracePreferencesDropdown
TkDodo Dec 18, 2025
2714433
fix: choiceMapperField
TkDodo Dec 18, 2025
06b681e
fix: aggregateSelector
TkDodo Dec 18, 2025
fe5f189
fix: another aggregateSelector
TkDodo Dec 18, 2025
76da95c
fix: HybridFilter
TkDodo Dec 18, 2025
3d44069
Merge branch 'master' into tkdodo/ref/remove-compactSelect-triggerProps
TkDodo Dec 18, 2025
adf7156
fix: use a SelectTrigger.IconButton over Button with empty children
TkDodo Dec 18, 2025
c5b6c16
fix: bring back missing props spreading
TkDodo Dec 18, 2025
3782df4
fix: remove double stringification of prefix
TkDodo Dec 18, 2025
e9e1e5e
fix: don't overwrite children
TkDodo Dec 18, 2025
ab0d6d5
fix: make triggerLabel required
TkDodo Dec 18, 2025
33f22a5
fix: don't fall back to undefined/null
TkDodo Dec 18, 2025
398d339
fix: displaying the current option's label is the default behavior
TkDodo Dec 18, 2025
ec6acd7
fix: disallow rendering of undefined and null in a SelectTrigger
TkDodo Dec 18, 2025
1a405d1
ref: ✂️ knip
TkDodo Dec 18, 2025
b2a872d
Merge branch 'master' into tkdodo/ref/remove-compactSelect-triggerProps
TkDodo Dec 18, 2025
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
12 changes: 7 additions & 5 deletions static/app/components/charts/intervalSelector.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import {useState} from 'react';

import {SelectTrigger} from '@sentry/scraps/compactSelect/trigger';

import {getInterval} from 'sentry/components/charts/utils';
import {CompactSelect, type SelectOption} from 'sentry/components/core/compactSelect';
import {
Expand Down Expand Up @@ -225,11 +227,11 @@ export default function IntervalSelector({
size="sm"
position="bottom-end"
menuWidth={200}
triggerProps={{
prefix: t('Interval'),
borderless: true,
children: interval,
}}
trigger={triggerProps => (
<SelectTrigger.Button {...triggerProps} prefix={t('Interval')} borderless>
{interval}
</SelectTrigger.Button>
)}
disabled={false}
/>
);
Expand Down
23 changes: 14 additions & 9 deletions static/app/components/charts/optionSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import {Fragment, useMemo} from 'react';
import styled from '@emotion/styled';
import type {DistributedOmit} from 'type-fest';

import {SelectTrigger} from '@sentry/scraps/compactSelect/trigger';

import {FeatureBadge} from 'sentry/components/core/badge';
import type {
MultipleSelectProps,
Expand Down Expand Up @@ -125,15 +127,18 @@ function OptionSelector({
options={mappedOptions}
isOptionDisabled={isOptionDisabled}
position="bottom-end"
triggerProps={{
borderless: true,
prefix: (
<Fragment>
{title}
{defined(featureType) ? <StyledFeatureBadge type={featureType} /> : null}
</Fragment>
),
}}
trigger={triggerProps => (
<SelectTrigger.Button
{...triggerProps}
borderless
prefix={
<Fragment>
{title}
{defined(featureType) ? <StyledFeatureBadge type={featureType} /> : null}
</Fragment>
}
/>
)}
/>
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import {useMemo, useRef, useState} from 'react';
import styled from '@emotion/styled';

import {SelectTrigger} from '@sentry/scraps/compactSelect/trigger';

import NegativeSpaceContainer from 'sentry/components/container/negativeSpaceContainer';
import {ButtonBar} from 'sentry/components/core/button/buttonBar';
import {CompactSelect} from 'sentry/components/core/compactSelect';
Expand Down Expand Up @@ -103,9 +105,11 @@
</p>

<Controls>
<DatePageFilter triggerProps={{prefix: 'Time Window'}} />

Check failure on line 108 in static/app/components/checkInTimeline/checkInTimeline.stories.tsx

View workflow job for this annotation

GitHub Actions / typescript

Type '{ triggerProps: { prefix: string; }; }' is not assignable to type 'IntrinsicAttributes & { css?: Interpolation<Theme>; } & DatePageFilterProps'.
<CompactSelect
triggerProps={{prefix: 'Spacing'}}
trigger={triggerProps => (
<SelectTrigger.Button {...triggerProps} prefix="Spacing" />
)}
options={[
{value: 60, label: '1 Minute'},
{value: 60 * 5, label: '5 Minute'},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,9 @@ describe('CompactSelect', () => {
it('displays trigger button with prefix', async () => {
render(
<CompactSelect
triggerProps={{prefix: 'Prefix'}}
trigger={triggerProps => (
<SelectTrigger.Button {...triggerProps} prefix="Prefix" />
)}
value="opt_one"
onChange={jest.fn()}
options={[
Expand Down Expand Up @@ -773,7 +775,9 @@ describe('CompactSelect', () => {
render(
<CompactSelect
grid
triggerProps={{prefix: 'Prefix'}}
trigger={triggerProps => (
<SelectTrigger.Button {...triggerProps} prefix="Prefix" />
)}
value="opt_one"
options={[
{value: 'opt_one', label: 'Option One'},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -349,9 +349,7 @@ export default Storybook.story('CompactSelect', story => {
<Storybook.Grid>
<CompactSelect
size="md"
triggerProps={{
prefix: t('Character'),
}}
trigger={props => <SelectTrigger.Button {...props} prefix={t('Character')} />}
value={values}
onChange={handleValueChange}
options={options}
Expand All @@ -361,9 +359,7 @@ export default Storybook.story('CompactSelect', story => {
/>
<CompactSelect
size="sm"
triggerProps={{
prefix: t('Character'),
}}
trigger={props => <SelectTrigger.Button {...props} prefix={t('Character')} />}
value={values}
onChange={handleValueChange}
options={options}
Expand All @@ -373,9 +369,7 @@ export default Storybook.story('CompactSelect', story => {
/>
<CompactSelect
size="xs"
triggerProps={{
prefix: t('Character'),
}}
trigger={props => <SelectTrigger.Button {...props} prefix={t('Character')} />}
value={values}
onChange={handleValueChange}
options={options}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@ export function CompactSelect<Value extends SelectKey>({
emptyMessage,
size = 'md',
closeOnSelect,
triggerProps,
menuWidth,
...controlProps
}: SelectProps<Value>) {
Expand Down Expand Up @@ -165,7 +164,7 @@ export function CompactSelect<Value extends SelectKey>({
menuWidth={menuWidth ?? measuredMenuWidth}
// decrease height to 1px during measuring so that scrollbars are shown & measured
menuHeight={needsMeasuring ? '1px' : undefined}
triggerProps={{...triggerProps, id: triggerId}}
triggerId={triggerId}
disabled={controlDisabled}
grid={grid}
size={size}
Expand Down
32 changes: 20 additions & 12 deletions static/app/components/core/compactSelect/composite.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -148,12 +148,16 @@ export default Storybook.story('CompositeSelect', story => {
</CompositeSelect>

<CompactSelect
triggerProps={{
size: 'sm',
icon: <IconStar />,
children: 'Compact Select Single Select',
showChevron: false,
}}
trigger={triggerProps => (
<SelectTrigger.Button
{...triggerProps}
size="sm"
icon={<IconStar />}
showChevron={false}
>
{'Compact Select Single Select'}
</SelectTrigger.Button>
)}
value={drink}
onChange={selection => setDrink(selection.value)}
options={[
Expand All @@ -164,12 +168,16 @@ export default Storybook.story('CompositeSelect', story => {

<CompactSelect
multiple
triggerProps={{
size: 'sm',
icon: <IconStar />,
children: 'Compact Select Multiple Select',
showChevron: false,
}}
trigger={triggerProps => (
<SelectTrigger.Button
{...triggerProps}
size="sm"
icon={<IconStar />}
showChevron={false}
>
{'Compact Select Multiple Select'}
</SelectTrigger.Button>
)}
value={drinks}
onChange={selection => setDrinks(selection.map(s => s.value))}
options={[
Expand Down
38 changes: 12 additions & 26 deletions static/app/components/core/compactSelect/control.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,17 @@ import {useBoundaryContext} from '@sentry/scraps/boundaryContext';
import {Badge} from 'sentry/components/core/badge';
import {Button} from 'sentry/components/core/button';
import {Input} from 'sentry/components/core/input';
import DropdownButton from 'sentry/components/dropdownButton';
import LoadingIndicator from 'sentry/components/loadingIndicator';
import {Overlay, PositionWrapper} from 'sentry/components/overlay';
import {t} from 'sentry/locale';
import {space} from 'sentry/styles/space';
import {defined} from 'sentry/utils';
import type {FormSize} from 'sentry/utils/theme';
import type {UseOverlayProps} from 'sentry/utils/useOverlay';
import useOverlay from 'sentry/utils/useOverlay';
import usePrevious from 'sentry/utils/usePrevious';

import type {SingleListProps} from './list';
import {type ButtonTriggerProps, type SelectTriggerProps} from './trigger';
import {SelectTrigger, type SelectTriggerProps} from './trigger';
import type {SelectKey, SelectOptionOrSection} from './types';

// autoFocus react attribute is sync called on render, this causes
Expand Down Expand Up @@ -185,11 +183,7 @@ export interface ControlProps
* won't work correctly.
*/
trigger?: (props: SelectTriggerProps, isOpen: boolean) => React.ReactNode;

/**
* Props to be passed to the default trigger button.
*/
triggerProps?: Partial<ButtonTriggerProps>;
triggerId?: string;
}

/**
Expand All @@ -199,7 +193,7 @@ export function Control({
// Control props
autoFocus,
trigger,
triggerProps: {children: triggerLabelProp, ...triggerProps} = {},
triggerId,
isOpen,
onClose,
isDismissable,
Expand Down Expand Up @@ -417,10 +411,6 @@ export function Control({
* selected, then a count badge will appear.
*/
const triggerLabel: React.ReactNode = useMemo(() => {
if (defined(triggerLabelProp)) {
return triggerLabelProp;
}

const values = Array.isArray(value) ? value : [value];
const options = items
.flatMap(item => {
Expand All @@ -441,7 +431,7 @@ export function Control({
)}
</Fragment>
);
}, [triggerLabelProp, value, items]);
}, [value, items]);

const {keyboardProps: triggerKeyboardProps} = useKeyboard({
onKeyDown: e => {
Expand Down Expand Up @@ -473,23 +463,19 @@ export function Control({

const theme = useTheme();

const mergedTriggerProps = mergeProps(
{id: triggerId, children: triggerLabel},
triggerKeyboardProps,
overlayTriggerProps
);

return (
<SelectContext value={contextValue}>
<ControlWrap {...wrapperProps}>
{trigger ? (
trigger(
mergeProps({id: triggerProps.id}, triggerKeyboardProps, overlayTriggerProps),
overlayIsOpen
)
trigger(mergedTriggerProps, overlayIsOpen)
) : (
<DropdownButton
size={size}
{...mergeProps(triggerProps, triggerKeyboardProps, overlayTriggerProps)}
isOpen={overlayIsOpen}
disabled={disabled}
>
{triggerLabel}
</DropdownButton>
<SelectTrigger.Button {...mergedTriggerProps} />
)}
<StyledPositionWrapper
visible={overlayIsOpen}
Expand Down
6 changes: 4 additions & 2 deletions static/app/components/core/compactSelect/trigger.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@ type TriggerEl =
type: 'only use SelectTrigger for the trigger prop!';
});

export type SelectTriggerProps = React.HTMLAttributes<TriggerEl> & {
export type SelectTriggerProps = Omit<React.HTMLAttributes<TriggerEl>, 'children'> & {
children: NonNullable<React.ReactNode>;
ref?: React.Ref<TriggerEl>;
};

export type ButtonTriggerProps = DistributedOmit<DropdownButtonProps, 'ref'> & {
type ButtonTriggerProps = DistributedOmit<DropdownButtonProps, 'ref' | 'children'> & {
children: NonNullable<React.ReactNode>;
ref?: React.Ref<TriggerEl>;
};

Expand Down
23 changes: 14 additions & 9 deletions static/app/components/customResolutionModal.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {Fragment, useMemo, useState} from 'react';
import styled from '@emotion/styled';

import {SelectTrigger} from '@sentry/scraps/compactSelect/trigger';
import {Flex} from '@sentry/scraps/layout';

import type {ModalRenderProps} from 'sentry/actionCreators/modal';
Expand Down Expand Up @@ -165,15 +166,19 @@ function CustomResolutionModal(props: CustomResolutionModalProps) {
}}
menuTitle={t('Version')}
menuWidth={548}
triggerProps={{
prefix: t('Version'),
'aria-label': t('Version'),
children: version
? undefined
: isFetching
? t('Loading\u2026')
: t('Select a version'),
}}
trigger={triggerProps => (
<SelectTrigger.Button
{...triggerProps}
prefix={t('Version')}
aria-label={t('Version')}
>
{version
? triggerProps.children
: isFetching
? t('Loading\u2026')
: t('Select a version')}
</SelectTrigger.Button>
)}
onClose={() => setSearchQuery('')}
/>
{selectionError ? <ErrorText role="alert">{selectionError}</ErrorText> : null}
Expand Down
6 changes: 5 additions & 1 deletion static/app/components/discover/transactionsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import {Component, Fragment, useContext, useEffect} from 'react';
import styled from '@emotion/styled';
import type {Location, LocationDescriptor} from 'history';

import {SelectTrigger} from '@sentry/scraps/compactSelect/trigger';

import GuideAnchor from 'sentry/components/assistant/guideAnchor';
import {LinkButton} from 'sentry/components/core/button/linkButton';
import {CompactSelect} from 'sentry/components/core/compactSelect';
Expand Down Expand Up @@ -294,7 +296,9 @@ class _TransactionsList extends Component<Props> {
<Fragment>
<div>
<CompactSelect
triggerProps={{prefix: t('Filter'), size: 'xs'}}
trigger={triggerProps => (
<SelectTrigger.Button {...triggerProps} prefix={t('Filter')} size="xs" />
)}
value={selected.value}
options={options}
onChange={opt => handleDropdownChange(opt.value)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ export function BreadcrumbsDrawer({
{...props}
{...getFocusProps(BreadcrumbControlOptions.FILTER)}
>
{filters.length > 0 ? filters.length : null}
{filters.length > 0 ? filters.length : ''}
</SelectTrigger.Button>
)}
/>
Expand Down
Loading
Loading