Skip to content

Commit

Permalink
KeywordList search attribute support (#2420)
Browse files Browse the repository at this point in the history
* Account for KeywordList in query and filter formatting utils and add tests

* Add ListFilter to filter search

* Add unround and external content options to ChipInput

* Add KeywordList option to SearchAttributeInput

* Add payloadToString util

* Derive customSearchAttributeOptions

* Remove onMount in ListFilter
  • Loading branch information
laurakwhit authored Nov 20, 2024
1 parent 49063e4 commit 303d7f1
Show file tree
Hide file tree
Showing 26 changed files with 360 additions and 129 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { translate } from '$lib/i18n/translate';
import type { SearchAttribute } from '$lib/types';
import { decodePayloadAttributes } from '$lib/utilities/decode-payload';
import { payloadToString } from '$lib/utilities/payload-to-string';
import { pluralize } from '$lib/utilities/pluralize';
export let searchAttributes: SearchAttribute;
Expand All @@ -23,12 +24,13 @@
{#if searchAttributeCount}
<ul class="w-full">
{#each Object.entries(indexedFields) as [searchAttrName, searchAttrValue]}
{@const value = payloadToString(searchAttrValue)}
<li
class="flex flex-wrap items-center gap-2 border-b py-2 last-of-type:border-b-0"
>
<span class="break-all">{searchAttrName}</span>
<span class="surface-subtle select-all rounded-sm p-1 leading-4"
>{searchAttrValue}</span
>{value}</span
>
</li>
{/each}
Expand Down
7 changes: 3 additions & 4 deletions src/lib/components/search-attribute-filter/filter-list.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,7 @@

<div class="flex flex-wrap gap-2" class:pt-2={visibleFilters.length}>
{#each visibleFilters as workflowFilter, i (`${workflowFilter.attribute}-${i}`)}
{@const { attribute, type, value, conditional, customDate } =
workflowFilter}
{@const { attribute, value, conditional, customDate } = workflowFilter}
{#if attribute}
<div in:fade data-testid="{workflowFilter.attribute}-{i}">
<Chip
Expand Down Expand Up @@ -119,7 +118,7 @@
{#if isNullConditional(conditional)}
{conditional}
{value}
{:else if isDateTimeFilter({ attribute, type })}
{:else if isDateTimeFilter(workflowFilter)}
{#if customDate}
{formatDateTimeRange(value, $timeFormat, $relativeTime)}
{:else}
Expand All @@ -133,7 +132,7 @@
{isStartsWith(conditional)
? translate('common.starts-with').toLocaleLowerCase()
: conditional}
{isTextFilter({ attribute, type }) ? `"${value}"` : value}
{isTextFilter(workflowFilter) ? `"${value}"` : value}
{/if}
</span>
{/if}
Expand Down
34 changes: 18 additions & 16 deletions src/lib/components/search-attribute-filter/index.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
isBooleanFilter,
isDateTimeFilter,
isDurationFilter,
isListFilter,
isNumberFilter,
isStatusFilter,
isTextFilter,
Expand All @@ -45,6 +46,7 @@
import DateTimeFilter from './datetime-filter.svelte';
import DurationFilter from './duration-filter.svelte';
import FilterList from './filter-list.svelte';
import ListFilter from './list-filter.svelte';
import NumberFilter from './number-filter.svelte';
import SearchAttributeMenu from './search-attribute-menu.svelte';
import StatusFilter from './status-filter.svelte';
Expand All @@ -59,9 +61,8 @@
const activeQueryIndex = writable<number>(null);
const focusedElementId = writable<string>('');
$: ({ attribute, type } = $filter);
$: searchParamQuery = $page.url.searchParams.get('query');
$: showClearAllButton = showFilter && filters.length && !attribute;
$: showClearAllButton = showFilter && filters.length && !$filter.attribute;
setContext<FilterContext>(FILTER_CONTEXT, {
filter,
Expand Down Expand Up @@ -105,7 +106,7 @@
function updateFocusedElementId() {
if ($activeQueryIndex !== null) {
$focusedElementId = getFocusedElementId({ attribute, type });
$focusedElementId = getFocusedElementId($filter);
}
}
Expand Down Expand Up @@ -134,7 +135,7 @@
}
function handleKeyUp(event: KeyboardEvent) {
if (event.key === 'Escape' && !isTextFilter({ attribute, type })) {
if (event.key === 'Escape' && !isTextFilter($filter)) {
resetFilter();
}
}
Expand All @@ -145,56 +146,57 @@
<slot />
{#if showFilter}
<div
class="flex items-center"
class="flex"
class:filter={!showClearAllButton}
on:keyup={handleKeyUp}
role="none"
>
{#if isStatusFilter(attribute)}
{#if isStatusFilter($filter)}
<StatusFilter bind:filters />
{:else}
<SearchAttributeMenu {filters} {options} />
{/if}

{#if attribute}
{#if isTextFilter({ attribute, type })}
{#if $filter.attribute}
{#if isTextFilter($filter)}
<div
class="flex w-full items-center"
in:fly={{ x: -100, duration: 150 }}
>
<TextFilter />
<CloseFilter />
</div>
<!-- TODO: Add KeywordList support -->
<!-- {:else if isListFilter(attribute)}
{:else if isListFilter($filter)}
<div class="w-full" in:fly={{ x: -100, duration: 150 }}>
<ListFilter />
</div> -->
{:else if isDurationFilter(attribute)}
<ListFilter>
<CloseFilter />
</ListFilter>
</div>
{:else if isDurationFilter($filter)}
<div
class="flex w-full items-center"
in:fly={{ x: -100, duration: 150 }}
>
<DurationFilter />
<CloseFilter />
</div>
{:else if isNumberFilter({ attribute, type })}
{:else if isNumberFilter($filter)}
<div
class="flex w-full items-center"
in:fly={{ x: -100, duration: 150 }}
>
<NumberFilter />
<CloseFilter />
</div>
{:else if isDateTimeFilter({ attribute, type })}
{:else if isDateTimeFilter($filter)}
<div
class="flex w-full items-center"
in:fly={{ x: -100, duration: 150 }}
>
<DateTimeFilter />
<CloseFilter />
</div>
{:else if isBooleanFilter({ attribute, type })}
{:else if isBooleanFilter($filter)}
<div
class="flex w-full items-center"
in:fly={{ x: -100, duration: 150 }}
Expand Down
34 changes: 24 additions & 10 deletions src/lib/components/search-attribute-filter/list-filter.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,14 @@
const { filter, handleSubmit } = getContext<FilterContext>(FILTER_CONTEXT);
let list: string[] = [];
$: ({ value } = $filter);
$: list =
value.length > 0
? value
.slice(1, -1)
.split(', ')
.map((v) => v.slice(1, -1))
: [];
function onSubmit() {
$filter.conditional = 'IN';
Expand All @@ -19,21 +26,28 @@
}
</script>

<div class="flex">
<form class="flex grow" on:submit|preventDefault={onSubmit}>
<ChipInput
label={$filter.attribute}
labelHidden
id="list-filter"
bind:chips={list}
class="w-full rounded-none"
class="w-full"
removeChipButtonLabel={(chip) =>
translate('workflows.remove-keyword-label', { keyword: chip })}
placeholder="{translate('common.type-or-paste-in')} {$filter.attribute}"
unroundLeft
unroundRight
external
/>
<Button
variant="secondary"
borderRadiusModifier="square-left"
disabled={!list.length}
on:click={onSubmit}>{translate('common.submit')}</Button
>
</div>
<div class="flex h-fit items-center">
<Button
borderRadiusModifier="square-left"
disabled={!list.length}
type="submit"
>
{translate('common.search')}
</Button>
<slot />
</div>
</form>
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,7 @@
import type { SearchAttributeFilter } from '$lib/models/search-attribute-filters';
import type { SearchAttributeOption } from '$lib/stores/search-attributes';
import type { SearchAttributeType } from '$lib/types/workflows';
import {
getFocusedElementId,
isListFilter,
} from '$lib/utilities/query/search-attribute-filter';
import { getFocusedElementId } from '$lib/utilities/query/search-attribute-filter';
import { emptyFilter } from '$lib/utilities/query/to-list-workflow-filters';
import { FILTER_CONTEXT, type FilterContext } from './index.svelte';
Expand All @@ -38,18 +35,14 @@
function handleNewQuery(value: string, type: SearchAttributeType) {
searchAttributeValue = '';
filter.set({ ...emptyFilter(), attribute: value, conditional: '=', type });
$focusedElementId = getFocusedElementId({ attribute: value, type });
$focusedElementId = getFocusedElementId($filter);
}
let searchAttributeValue = '';
// TODO: Add KeywordList support
$: _options = options.filter(
({ value, type }) => !isListFilter({ attribute: value, type }),
);
$: filteredOptions = !searchAttributeValue
? _options
: _options.filter((option) =>
? options
: options.filter((option) =>
option.value.toLowerCase().includes(searchAttributeValue.toLowerCase()),
);
</script>
Expand Down
12 changes: 3 additions & 9 deletions src/lib/components/search-attribute-filter/status-filter.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,7 @@
const { filter, resetFilter } = getContext<FilterContext>(FILTER_CONTEXT);
const open = writable(true);
$: _filters = [...filters];
$: statusFilters = _filters.filter((filter) =>
isStatusFilter(filter.attribute),
);
$: statusFilters = _filters.filter((filter) => isStatusFilter(filter));
function apply() {
filters = _filters;
Expand Down Expand Up @@ -61,9 +59,7 @@
if (status === 'All') {
_filters = filters.filter((f) => f.attribute !== 'ExecutionStatus');
} else if (statusFilters.find((s) => s.value === status)) {
const nonStatusFilters = filters.filter(
(f) => !isStatusFilter(f.attribute),
);
const nonStatusFilters = filters.filter((f) => !isStatusFilter(f));
_filters = [
...nonStatusFilters,
...mapStatusesToFilters(
Expand All @@ -74,9 +70,7 @@
if (!statusFilters.length) {
_filters = [..._filters, mapStatusToFilter(status)];
} else {
const nonStatusFilters = _filters.filter(
(f) => !isStatusFilter(f.attribute),
);
const nonStatusFilters = _filters.filter((f) => !isStatusFilter(f));
_filters = [
...nonStatusFilters,
...mapStatusesToFilters([
Expand Down
8 changes: 6 additions & 2 deletions src/lib/components/workflow/add-search-attributes.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,11 @@
on:click={addSearchAttribute}
disabled={!searchAttributes.length ||
attributesToAdd.length === searchAttributes.length ||
attributesToAdd.filter((a) => a.value === '' || a.value === null).length >
0}>{translate('workflows.add-search-attribute')}</Button
attributesToAdd.filter(
(a) =>
a.value === '' ||
a.value === null ||
(Array.isArray(a.value) && a.value.length === 0),
).length > 0}>{translate('workflows.add-search-attribute')}</Button
>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
let second = '';
onMount(() => {
if (value) {
if (value && (typeof value === 'string' || typeof value === 'number')) {
const datetime = new Date(value);
const utcDate = new Date(
datetime.getUTCFullYear(),
Expand Down
22 changes: 9 additions & 13 deletions src/lib/components/workflow/search-attribute-input/index.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,40 +4,34 @@
import Select from '$lib/holocene/select/select.svelte';
import { translate } from '$lib/i18n/translate';
import {
customSearchAttributeOptions,
customSearchAttributes,
type SearchAttributeInput,
} from '$lib/stores/search-attributes';
import {
SEARCH_ATTRIBUTE_TYPE,
type SearchAttributes,
} from '$lib/types/workflows';
import { SEARCH_ATTRIBUTE_TYPE } from '$lib/types/workflows';
import DatetimeInput from './datetime-input.svelte';
import ListInput from './list-input.svelte';
import NumberInput from './number-input.svelte';
import TextInput from './text-input.svelte';
export let attributesToAdd: SearchAttributeInput[] = [];
export let searchAttributes: SearchAttributes = $customSearchAttributes;
export let attribute: SearchAttributeInput;
export let onRemove: (attribute: string) => void;
$: type = searchAttributes[attribute.attribute];
$: searchAttributesOptions = [...Object.entries(searchAttributes)]
.map(([key, value]) => ({ label: key, value: key, type: value }))
.filter(({ type }) => type !== 'KeywordList');
$: type = $customSearchAttributes[attribute.attribute];
$: isDisabled = (value: string) => {
return !!attributesToAdd.find((a) => a.attribute === value);
};
const handleAttributeChange = (attr: string) => {
if (type !== searchAttributes[attr]) {
if (type !== $customSearchAttributes[attr]) {
attribute.value = null;
}
};
</script>

<div class="flex items-start gap-2">
<div class="flex items-end gap-2">
<div class="min-w-fit">
<Select
id="search-attribute"
Expand All @@ -47,7 +41,7 @@
bind:value={attribute.attribute}
onChange={handleAttributeChange}
>
{#each searchAttributesOptions as { value, label, type }}
{#each $customSearchAttributeOptions as { value, label, type }}
<Option disabled={isDisabled(value)} {value} description={type}
>{label}</Option
>
Expand All @@ -67,6 +61,8 @@
<DatetimeInput bind:value={attribute.value} />
{:else if type === SEARCH_ATTRIBUTE_TYPE.INT || type === SEARCH_ATTRIBUTE_TYPE.DOUBLE}
<NumberInput bind:value={attribute.value} />
{:else if type === SEARCH_ATTRIBUTE_TYPE.KEYWORDLIST}
<ListInput bind:value={attribute.value} />
{:else}
<TextInput bind:value={attribute.value} />
{/if}
Expand Down
Loading

0 comments on commit 303d7f1

Please sign in to comment.