diff --git a/frontend/src/lib/components/PropertyFilters/PropertyFilter.js b/frontend/src/lib/components/PropertyFilters/PropertyFilter.js index 723956ff378..30e97274f9d 100644 --- a/frontend/src/lib/components/PropertyFilters/PropertyFilter.js +++ b/frontend/src/lib/components/PropertyFilters/PropertyFilter.js @@ -7,7 +7,7 @@ import { SelectGradientOverflow } from 'lib/components/SelectGradientOverflow' import { Link } from '../Link' import { PropertySelect } from './PropertySelect' import { OperatorValueSelect } from 'lib/components/PropertyFilters/OperatorValueSelect' -import { isOperatorMulti } from 'lib/utils' +import { isOperatorMulti, isOperatorRegex } from 'lib/utils' const { TabPane } = Tabs @@ -83,7 +83,11 @@ function PropertyPaneContents({ value={value} onChange={(newOperator, newValue) => { setThisFilter(propkey, newValue, newOperator, type) - if (newOperator && newValue && !isOperatorMulti(newOperator)) { + if ( + newOperator && + newValue && + !(isOperatorMulti(newOperator) || isOperatorRegex(newOperator)) + ) { onComplete() } }} diff --git a/frontend/src/lib/components/PropertyFilters/PropertyValue.js b/frontend/src/lib/components/PropertyFilters/PropertyValue.js index 22a5027d88a..351956ce453 100644 --- a/frontend/src/lib/components/PropertyFilters/PropertyValue.js +++ b/frontend/src/lib/components/PropertyFilters/PropertyValue.js @@ -1,5 +1,6 @@ import React, { useState, useEffect } from 'react' -import { Select } from 'antd' +import { AutoComplete, Select } from 'antd' +import { useThrottledCallback } from 'use-debounce' import api from '../../api' import { isMobile, isOperatorFlag, isOperatorMulti, isOperatorRegex, isValidRegex } from 'lib/utils' import { SelectGradientOverflow } from 'lib/components/SelectGradientOverflow' @@ -7,20 +8,20 @@ import { SelectGradientOverflow } from 'lib/components/SelectGradientOverflow' export function PropertyValue({ propertyKey, type, - endpoint, - placeholder, - style, + endpoint = undefined, + placeholder = undefined, + style = {}, bordered = true, onSet, value, operator, - outerOptions, + outerOptions = undefined, }) { const [input, setInput] = useState('') const [optionsCache, setOptionsCache] = useState({}) const [options, setOptions] = useState({}) - function loadPropertyValues(newInput) { + const loadPropertyValues = useThrottledCallback((newInput) => { if (type === 'cohort') { return } @@ -44,7 +45,7 @@ export function PropertyValue({ } ) } - } + }, 300) function setValue(newValue) { onSet(newValue) @@ -62,58 +63,90 @@ export function PropertyValue({ const validationError = getValidationError(operator, value) + const commonInputProps = { + autoFocus: !value && !isMobile(), + style: { width: '100%', ...style }, + value: value || placeholder, + loading: optionsCache[input] === 'loading', + onSearch: (newInput) => { + setInput(newInput) + if (!optionsCache[newInput] && !isOperatorFlag(operator)) { + loadPropertyValues(newInput) + } + }, + ['data-attr']: 'prop-val', + dropdownMatchSelectWidth: 350, + bordered, + placeholder, + allowClear: value, + onKeyDown: (e) => { + if (e.key === 'Escape') { + e.target.blur() + } + }, + } + return ( <> - { - if (isOperatorMulti(operator) && payload.length > 0) { - setValue(val) - } else { - setValue(payload?.value ?? null) - } - }} - value={value || placeholder} - loading={optionsCache[input] === 'loading'} - onSearch={(newInput) => { - setInput(newInput) - if (!optionsCache[newInput] && !isOperatorFlag(operator)) { - loadPropertyValues(newInput) - } - }} - data-attr="prop-val" - dropdownMatchSelectWidth={350} - bordered={bordered} - placeholder={placeholder} - allowClear={value} - onKeyDown={(e) => { - if (e.key === 'Escape') { - e.target.blur() - } - }} - > - {input && ( - - Specify: {input} - - )} - {displayOptions.map(({ name, id }, index) => ( - - {name === true && 'true'} - {name === false && 'false'} - {name} - - ))} - + {isOperatorRegex(operator) ? ( + { + setValue(val ?? null) + }} + > + {input && ( + + Specify: {input} + + )} + {displayOptions.map(({ name, id }, index) => ( + + {name === true && 'true'} + {name === false && 'false'} + {name} + + ))} + + ) : ( + { + if (isOperatorMulti(operator) && payload.length > 0) { + setValue(val) + } else { + setValue(payload?.value ?? null) + } + }} + > + {input && ( + + Specify: {input} + + )} + {displayOptions.map(({ name, id }, index) => ( + + {name === true && 'true'} + {name === false && 'false'} + {name} + + ))} + + )} {validationError &&

{validationError}

} ) diff --git a/package.json b/package.json index 5f5bc18feb8..5e0817d4a87 100644 --- a/package.json +++ b/package.json @@ -87,6 +87,7 @@ "resize-observer-polyfill": "^1.5.1", "rrweb": "^0.9.14", "sass": "^1.26.2", + "use-debounce": "^6.0.1", "zxcvbn": "^4.4.2" }, "devDependencies": { diff --git a/yarn.lock b/yarn.lock index 28a840ebd2c..7393d85cc2f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11946,6 +11946,11 @@ use-debounce@^5.2.1: resolved "https://registry.yarnpkg.com/use-debounce/-/use-debounce-5.2.1.tgz#7366543c769f1de3e92104dee64de5c4dfddfd33" integrity sha512-BQG5uEypYHd/ASF6imzYR8tJHh5qGn28oZG/5iVAbljV6MUrfyT4jzxA8co+L+WLCT1U8VBwzzvlb3CHmUDpEA== +use-debounce@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/use-debounce/-/use-debounce-6.0.1.tgz#ed1eb2b30189408fb9792ea2887f4c6c3cb401a3" + integrity sha512-kpvIxpa0vOLz/2I2sfNJ72mUeaT2CMNCu5BT1f2HkV9qZK27UVSOFf1sSSu+wjJE4TcR2VTXS2SM569+m3TN7Q== + use-local-storage-state@^6.0.0: version "6.0.3" resolved "https://registry.yarnpkg.com/use-local-storage-state/-/use-local-storage-state-6.0.3.tgz#65add61b8450b071354ce31b5a69b8908e69b497"