-
Notifications
You must be signed in to change notification settings - Fork 37
DashboardVariables: Use Combobox behind toggle #1011
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
packages/scenes/src/variables/components/VariableValueSelect.tsx
Outdated
Show resolved
Hide resolved
|
||
return ( | ||
<Combobox | ||
id={'var-' + key} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure where this var-
is added for the Select
packages/scenes/src/variables/components/VariableValueSelect.tsx
Outdated
Show resolved
Hide resolved
packages/scenes/src/variables/components/VariableValueSelect.tsx
Outdated
Show resolved
Hide resolved
packages/scenes/src/variables/components/VariableValueSelect.tsx
Outdated
Show resolved
Hide resolved
packages/scenes/src/variables/components/VariableValueSelect.tsx
Outdated
Show resolved
Hide resolved
packages/scenes/src/variables/components/VariableValueSelect.tsx
Outdated
Show resolved
Hide resolved
const onInputChange = useMemo(() => { | ||
if (!model.onSearchChange) { | ||
return; | ||
} | ||
return async (newInputValue: string) => { | ||
model.onSearchChange!(newInputValue); | ||
|
||
// Same functionality as in _updateOptionsBasedOnSearchFilter, although deboucning is managed in Combobox | ||
return (await lastValueFrom(model.getValueOptions({ searchFilter: newInputValue }))).map((o) => ({ | ||
value: o.value.toString(), | ||
label: o.label, | ||
})); | ||
}; | ||
}, [model]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this is working properly, and I lack the insight into how value updates are made in Scenes to make an informed decision
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any reason why this looks different from the one on line 73?
// If we don't use searchFilter in QueryVariable, don't bother to make a query. TODO: Proper typing | ||
// @ts-ignore | ||
if ('query' in model.state && !containsSearchFilter(model.state.query)) { | ||
return; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some feedback to do some proper typing on this would be nice. query
only exists on a subclass. I guess this breaks the class isolation somewhat.
Would setting this in the state instead make sense? Like model.state.async
or something similar, and set it in the QueryVariable constructor. By default QueryVariable only runs the initial query and the rest is filtered client side.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can simply call the function and use the same logic we have above (for the old select )
const res = model.onSearchChange!(newInputValue).then((result) => { | ||
return result.map((o) => ({ | ||
value: o.value.toString(), | ||
label: o.value.toString() === ALL_VARIABLE_VALUE ? ALL_VARIABLE_TEXT : o.label, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unclear how All
should be treated in this case.
packages/scenes/src/variables/components/VariableValueSelect.tsx
Outdated
Show resolved
Hide resolved
const onInputChange = useMemo(() => { | ||
if (!model.onSearchChange) { | ||
return; | ||
} | ||
return async (newInputValue: string) => { | ||
model.onSearchChange!(newInputValue); | ||
|
||
// Same functionality as in _updateOptionsBasedOnSearchFilter, although deboucning is managed in Combobox | ||
return (await lastValueFrom(model.getValueOptions({ searchFilter: newInputValue }))).map((o) => ({ | ||
value: o.value.toString(), | ||
label: o.label, | ||
})); | ||
}; | ||
}, [model]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any reason why this looks different from the one on line 73?
return (await lastValueFrom(model.getValueOptions({ searchFilter: newInputValue }))).map((o) => ({ | ||
value: o.value.toString(), | ||
label: o.label, | ||
})); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you can remove this part
// If we don't use searchFilter in QueryVariable, don't bother to make a query. TODO: Proper typing | ||
// @ts-ignore | ||
if ('query' in model.state && !containsSearchFilter(model.state.query)) { | ||
return; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can simply call the function and use the same logic we have above (for the old select )
return; | ||
} | ||
|
||
return async (newInputValue: string) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Think you can remove this is you simply called the function, like the current impl
return this._getNewOptions(searchFilter); | ||
} | ||
|
||
// Undefined for first call |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is undefined?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
THe return value. This is because of the debounce. It can be fixed by setting { leading: true }
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems hang / crash a browser when there are many options (many values) returned by the variable query:
Tested this a bit now with a prometheus variable query that uses __searchFilter,
With this query:
new QueryVariable({
name: 'namespace',
query: {
qryType: 1,
query: 'label_values({__name__=~"${__searchFilter}.*"},__name__)',
refId: 'PrometheusVariableQueryEditor-VariableQuery',
},
datasource: { uid: 'se-demo' },
}),
The call to onSearchChange and getValueOptions causes double queries (at least on first open).
Dan we really need to move the loading state from the label to input as well causing the input to jump around (not related to this PR, just an observation)
There is a possibility this is fixed with the latest changes to Combobox. This seems to only be an issue when first opened... |
A ton of event listeners are being created. I have no idea why. The basic use case is handled without any issues directly in Combobox. |
@tskarhed yea, it's strange, the combobox looks to recursive be rendering itself or something. thought it has something to do with passing options via the onInputChange but commented and it still hangs the same way |
The issue is that when the options are loaded the list virtualization isn't on somehow. No idea why. Maybe I'll test directly with 10k+ items in a dashboard. This issue doesn't appear in Prometheus or Storybook. |
I still don't know exactly why this issue appears here and nowhere else. However, it seems like the initial render left the maxHeight undefined, hence the list virtualization never kicked in. I fixed this by setting arbitrary initial dimensions (which are overridden soon after measuring things). |
Posting this recording for reference. Screen.Recording.2025-02-06.at.14.09.04.movIt doesn't seem to happen when having an empty value. I'm looking into it. |
@torkelo A call in onOpen in Combobox isn't debounced. A PR is open for it now grafana/grafana#100219 Other than that, is there anything blocking this PR, or do you feel okay approving it so we can start testing it in our instances? I assume the debounce PR will be in order soon enough. |
Co-authored-by: Josh Hunt <[email protected]>
//@ts-ignore | ||
model.onSearchChange && 'query' in model.state && containsSearchFilter(model.state.query), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We just need to fix this now, and the empty promise return of onSearchChange.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wonder how though, need to align this impl with the other and change how options are returned a bit
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about adding an isAsync
getter on MutliVariableValue
that defaults to false
, but for QueryVariable
has this statement?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Although it is a bit misnamed, as QueryVariable
always makes at least one async request.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tskarhed yea, we need a true async mode for QueryVariable where it will always have a default value, but needs some bigger changes
Closes: #96099
Related to #96091 and #98261
Followup issues: