Skip to content

Commit e27a48d

Browse files
author
ci bot
committed
Merge branch 'filter-select-dropdown' into 'enterprise'
feat(ui): allow filtering timezone dropdown See merge request dkinternal/testgen/dataops-testgen!305
2 parents 745a709 + e41338a commit e27a48d

2 files changed

Lines changed: 65 additions & 11 deletions

File tree

testgen/ui/components/frontend/js/components/select.js

Lines changed: 64 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,20 +21,22 @@
2121
* @property {string?} style
2222
* @property {string?} testId
2323
* @property {number?} portalClass
24+
* @property {boolean?} filterable
2425
* @property {('normal' | 'inline')?} triggerStyle
2526
*/
2627
import van from '../van.min.js';
2728
import { getRandomId, getValue, loadStylesheet, isState, isEqual } from '../utils.js';
2829
import { Portal } from './portal.js';
2930
import { Icon } from './icon.js';
3031

31-
const { div, i, label, span } = van.tags;
32+
const { div, i, input, label, span } = van.tags;
3233

3334
const Select = (/** @type {Properties} */ props) => {
3435
loadStylesheet('select', stylesheet);
3536

3637
const domId = van.derive(() => props.id?.val ?? getRandomId());
3738
const opened = van.state(false);
39+
const optionsFilter = van.state('');
3840
const options = van.derive(() => {
3941
const options = getValue(props.options) ?? [];
4042
const allowNull = getValue(props.allowNull);
@@ -48,6 +50,27 @@ const Select = (/** @type {Properties} */ props) => {
4850

4951
return options;
5052
});
53+
const filteredOptions = van.derive(() => {
54+
const allOptions = getValue(options);
55+
const isFilterable = getValue(props.filterable);
56+
const filterTerm = getValue(optionsFilter);
57+
if (isFilterable && filterTerm.length) {
58+
const filteredOptions_ = [];
59+
for (let i = 0; i < allOptions.length; i++) {
60+
const option = allOptions[i];
61+
if (option.label === filterTerm) {
62+
return allOptions;
63+
}
64+
65+
if (option.label.toLowerCase().includes(filterTerm.toLowerCase())) {
66+
filteredOptions_.push(option);
67+
}
68+
}
69+
return filteredOptions_;
70+
}
71+
return allOptions;
72+
});
73+
5174
const value = isState(props.value) ? props.value : van.state(props.value ?? null);
5275
const initialSelection = options.val?.find((op) => op.value === value.val);
5376
const valueLabel = van.state(initialSelection?.label ?? '');
@@ -58,6 +81,16 @@ const Select = (/** @type {Properties} */ props) => {
5881
value.val = option.value;
5982
};
6083

84+
const filterOptions = (/** @type InputEvent */ event) => {
85+
optionsFilter.val = event.target.value;
86+
};
87+
88+
const showPortal = (/** @type Event */ event) => {
89+
event.stopPropagation();
90+
event.stopImmediatePropagation();
91+
opened.val = getValue(props.disabled) ? false : true;
92+
};
93+
6194
van.derive(() => {
6295
const currentOptions = getValue(options);
6396
const previousValue = value.oldVal;
@@ -82,8 +115,8 @@ const Select = (/** @type {Properties} */ props) => {
82115
id: domId,
83116
class: () => `flex-column fx-gap-1 text-caption tg-select--label ${getValue(props.disabled) ? 'disabled' : ''}`,
84117
style: () => `width: ${props.width ? getValue(props.width) + 'px' : 'auto'}; ${getValue(props.style)}`,
85-
onclick: van.derive(() => !getValue(props.disabled) ? () => opened.val = !opened.val : null),
86118
'data-testid': getValue(props.testId) ?? '',
119+
onclick: showPortal,
87120
},
88121
span(
89122
{ class: 'flex-row fx-gap-1', 'data-testid': 'select-label' },
@@ -111,17 +144,27 @@ const Select = (/** @type {Properties} */ props) => {
111144
style: () => getValue(props.height) ? `height: ${getValue(props.height)}px;` : '',
112145
'data-testid': 'select-input',
113146
},
114-
() => div(
115-
{ class: 'tg-select--field--content', 'data-testid': 'select-input-display' },
116-
valueIcon.val
117-
? Icon({ classes: 'mr-2' }, valueIcon.val)
118-
: undefined,
119-
valueLabel.val,
120-
),
147+
() => {
148+
return div(
149+
{ class: 'tg-select--field--content', 'data-testid': 'select-input-display' },
150+
valueIcon.val
151+
? Icon({ classes: 'mr-2' }, valueIcon.val)
152+
: undefined,
153+
getValue(props.filterable)
154+
? input({
155+
id: `tg-select--field--${getRandomId()}`,
156+
value: valueLabel.val,
157+
onkeyup: filterOptions,
158+
})
159+
: valueLabel.val,
160+
);
161+
},
121162
div(
122163
{ class: 'tg-select--field--icon', 'data-testid': 'select-input-trigger' },
123164
i(
124-
{ class: 'material-symbols-rounded' },
165+
{
166+
class: 'material-symbols-rounded',
167+
},
125168
'expand_more',
126169
),
127170
),
@@ -134,7 +177,7 @@ const Select = (/** @type {Properties} */ props) => {
134177
class: () => `tg-select--options-wrapper mt-1 ${getValue(props.portalClass) ?? ''}`,
135178
'data-testid': 'select-options',
136179
},
137-
getValue(options).map(option =>
180+
getValue(filteredOptions).map(option =>
138181
div(
139182
{
140183
class: () => `tg-select--option ${getValue(value) === option.value ? 'selected' : ''}`,
@@ -196,6 +239,16 @@ stylesheet.replace(`
196239
font-weight: 500;
197240
}
198241
242+
.tg-select--field--content > input {
243+
border: unset !important;
244+
background: transparent !important;
245+
outline: none !important;
246+
width: 100%;
247+
font-weight: 500;
248+
font-family: 'Roboto', 'Helvetica Neue', sans-serif;
249+
color: var(--primary-text-color);
250+
}
251+
199252
.tg-select--field--icon {
200253
display: flex;
201254
align-items: center;

testgen/ui/components/frontend/js/pages/schedule_list.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ const ScheduleList = (/** @type Properties */ props) => {
8888
options: timezones.map(tz_ => ({label: tz_, value: tz_})),
8989
value: newScheduleForm.timezone,
9090
allowNull: false,
91+
filterable: true,
9192
onChange: (value) => {
9293
newScheduleForm.timezone.val = value;
9394
if (newScheduleForm.expression.val && newScheduleForm.timezone.val) {

0 commit comments

Comments
 (0)