Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ export function getPredefinedValues({
];
}

function tokenSupportsMultipleValues(
export function tokenSupportsMultipleValues(
token: TokenResult<Token.FILTER>,
keys: TagCollection,
fieldDefinition: FieldDefinition | null
Expand Down
3 changes: 2 additions & 1 deletion static/app/views/dashboards/globalFilter/addFilter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export const DATASET_CHOICES = new Map<WidgetType, string>([

const UNSUPPORTED_FIELD_KINDS = [FieldKind.FUNCTION];
const UNSUPPORTED_FIELD_VALUE_TYPES = [FieldValueType.DATE];
const IGNORE_DEFAULT_VALUES = [FieldValueType.STRING, FieldValueType.BOOLEAN];

export function getDatasetLabel(dataset: WidgetType) {
return DATASET_CHOICES.get(dataset) ?? '';
Expand Down Expand Up @@ -129,7 +130,7 @@ function AddFilter({globalFilters, getSearchBarData, onAddFilter}: AddFilterProp
const fieldDefinition = fieldDefinitionMap.get(selectedFilterKey.key) ?? null;
const valueType = fieldDefinition?.valueType;

if (valueType && valueType !== FieldValueType.STRING) {
if (valueType && !IGNORE_DEFAULT_VALUES.includes(valueType)) {
defaultFilterValue = getInitialFilterText(
selectedFilterKey.key,
fieldDefinition,
Expand Down
116 changes: 80 additions & 36 deletions static/app/views/dashboards/globalFilter/filterSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@ import {useEffect, useMemo, useState} from 'react';
import styled from '@emotion/styled';
import isEqual from 'lodash/isEqual';

import type {SelectOption} from '@sentry/scraps/compactSelect';
import {CompactSelect, type SelectOption} from '@sentry/scraps/compactSelect';
import {Flex} from '@sentry/scraps/layout';

import {Button} from 'sentry/components/core/button';
import {HybridFilter} from 'sentry/components/organizations/hybridFilter';
import {getPredefinedValues} from 'sentry/components/searchQueryBuilder/tokens/filter/valueCombobox';
import {
getPredefinedValues,
tokenSupportsMultipleValues,
} from 'sentry/components/searchQueryBuilder/tokens/filter/valueCombobox';
import {MutableSearch} from 'sentry/components/searchSyntax/mutableSearch';
import {t} from 'sentry/locale';
import {space} from 'sentry/styles/space';
Expand Down Expand Up @@ -64,6 +67,10 @@ function FilterSelector({
[globalFilter, fieldDefinition]
);

const canSelectMultipleValues = filterToken
? tokenSupportsMultipleValues(filterToken, datasetFilterKeys, fieldDefinition)
: true;

// Retrieve predefined values if the tag has any
const predefinedValues = useMemo(() => {
if (!filterToken) {
Expand Down Expand Up @@ -105,6 +112,15 @@ function FilterSelector({
const {data: fetchedFilterValues, isFetching} = queryResult;

const options = useMemo(() => {
if (predefinedValues && !canSelectMultipleValues) {
return predefinedValues.flatMap(section =>
section.suggestions.map(suggestion => ({
label: suggestion.value,
value: suggestion.value,
}))
);
}

const optionMap = new Map<string, SelectOption<string>>();
const fixedOptionMap = new Map<string, SelectOption<string>>();
const addOption = (value: string, map: Map<string, SelectOption<string>>) =>
Expand Down Expand Up @@ -139,6 +155,7 @@ function FilterSelector({
activeFilterValues,
stagedFilterValues,
searchQuery,
canSelectMultipleValues,
]);

const handleChange = (opts: string[]) => {
Expand All @@ -162,6 +179,64 @@ function FilterSelector({
});
};

const renderMenuHeaderTrailingItems = ({closeOverlay}: any) => (
<Flex gap="md">
{activeFilterValues.length > 0 && (
<StyledButton
aria-label={t('Clear Selections')}
size="zero"
borderless
onClick={() => {
setSearchQuery('');
handleChange([]);
closeOverlay();
}}
>
{t('Clear')}
</StyledButton>
)}
<StyledButton
aria-label={t('Remove Filter')}
size="zero"
onClick={() => onRemoveFilter(globalFilter)}
>
{t('Remove Filter')}
</StyledButton>
</Flex>
);

const renderFilterSelectorTrigger = () => (
<FilterSelectorTrigger
globalFilter={globalFilter}
activeFilterValues={initialValues}
options={options}
queryResult={queryResult}
/>
);

if (!canSelectMultipleValues) {
return (
<CompactSelect
multiple={false}
disabled={false}
options={options}
value={activeFilterValues.length > 0 ? activeFilterValues[0] : undefined}
onChange={option => {
const newValue = option?.value;
handleChange(newValue ? [newValue] : []);
}}
onClose={() => {
setStagedFilterValues([]);
}}
menuTitle={t('%s Filter', getDatasetLabel(dataset))}
menuHeaderTrailingItems={renderMenuHeaderTrailingItems}
triggerProps={{
children: renderFilterSelectorTrigger(),
}}
/>
);
}

return (
<HybridFilter
checkboxPosition="leading"
Expand All @@ -177,7 +252,7 @@ function FilterSelector({
setStagedFilterValues(value);
}}
sizeLimit={30}
menuWidth={400}
maxMenuWidth={500}
onClose={() => {
setSearchQuery('');
setStagedFilterValues([]);
Expand All @@ -187,40 +262,9 @@ function FilterSelector({
isFetching ? t('Loading filter values...') : t('No filter values found')
}
menuTitle={t('%s Filter', getDatasetLabel(dataset))}
menuHeaderTrailingItems={({closeOverlay}: any) => (
<Flex gap="md">
{activeFilterValues.length > 0 && (
<StyledButton
aria-label={t('Clear Selections')}
size="zero"
borderless
onClick={() => {
setSearchQuery('');
handleChange([]);
closeOverlay();
}}
>
{t('Clear')}
</StyledButton>
)}
<StyledButton
aria-label={t('Remove Filter')}
size="zero"
onClick={() => onRemoveFilter(globalFilter)}
>
{t('Remove Filter')}
</StyledButton>
</Flex>
)}
menuHeaderTrailingItems={renderMenuHeaderTrailingItems}
triggerProps={{
children: (
<FilterSelectorTrigger
globalFilter={globalFilter}
activeFilterValues={initialValues}
options={options}
queryResult={queryResult}
/>
),
children: renderFilterSelectorTrigger(),
}}
/>
);
Expand Down
Loading