From 46a537aa02c7f4e2fbed36e5b505fe0a1ce9b84e Mon Sep 17 00:00:00 2001 From: Victor Cinaglia Date: Tue, 18 Feb 2025 08:34:37 -0500 Subject: [PATCH] iam: Fix debouncing in user, team and service account pickers (#100830) iam: fix debouncing in user, team and service account pickers --- .../Select/ServiceAccountPicker.tsx | 59 +++++++++---------- .../app/core/components/Select/TeamPicker.tsx | 55 ++++++++--------- .../app/core/components/Select/UserPicker.tsx | 59 +++++++++---------- 3 files changed, 82 insertions(+), 91 deletions(-) diff --git a/public/app/core/components/Select/ServiceAccountPicker.tsx b/public/app/core/components/Select/ServiceAccountPicker.tsx index b1c0e476891..0bd0f412d3e 100644 --- a/public/app/core/components/Select/ServiceAccountPicker.tsx +++ b/public/app/core/components/Select/ServiceAccountPicker.tsx @@ -1,4 +1,5 @@ -import { debounce, DebouncedFuncLeading, isNil } from 'lodash'; +import debounce from 'debounce-promise'; +import { isNil } from 'lodash'; import { Component } from 'react'; import { SelectableValue } from '@grafana/data'; @@ -17,42 +18,38 @@ export interface State { } export class ServiceAccountPicker extends Component { - debouncedSearch: DebouncedFuncLeading; - constructor(props: Props) { super(props); this.state = { isLoading: false }; - this.search = this.search.bind(this); - - this.debouncedSearch = debounce(this.search, 300, { - leading: true, - trailing: true, - }); } - search(query?: string) { - this.setState({ isLoading: true }); + search = debounce( + async (query?: string) => { + this.setState({ isLoading: true }); - if (isNil(query)) { - query = ''; - } + if (isNil(query)) { + query = ''; + } - return getBackendSrv() - .get(`/api/serviceaccounts/search?query=${query}&perpage=100`) - .then((result: ServiceAccountsState) => { - return result.serviceAccounts.map((sa) => ({ - id: sa.id, - uid: sa.uid, - value: sa, - label: sa.login, - imgUrl: sa.avatarUrl, - login: sa.login, - })); - }) - .finally(() => { - this.setState({ isLoading: false }); - }); - } + return getBackendSrv() + .get(`/api/serviceaccounts/search?query=${query}&perpage=100`) + .then((result: ServiceAccountsState) => { + return result.serviceAccounts.map((sa) => ({ + id: sa.id, + uid: sa.uid, + value: sa, + label: sa.login, + imgUrl: sa.avatarUrl, + login: sa.login, + })); + }) + .finally(() => { + this.setState({ isLoading: false }); + }); + }, + 300, + { leading: true } + ); render() { const { className, onSelected, inputId } = this.props; @@ -66,7 +63,7 @@ export class ServiceAccountPicker extends Component { inputId={inputId} isLoading={isLoading} defaultOptions={true} - loadOptions={this.debouncedSearch} + loadOptions={this.search} onChange={onSelected} placeholder="Start typing to search for service accounts" noOptionsMessage="No service accounts found" diff --git a/public/app/core/components/Select/TeamPicker.tsx b/public/app/core/components/Select/TeamPicker.tsx index 32ef82fc15d..268f0642427 100644 --- a/public/app/core/components/Select/TeamPicker.tsx +++ b/public/app/core/components/Select/TeamPicker.tsx @@ -1,4 +1,5 @@ -import { debounce, DebouncedFuncLeading, isNil } from 'lodash'; +import debounce from 'debounce-promise'; +import { isNil } from 'lodash'; import { Component } from 'react'; import { SelectableValue } from '@grafana/data'; @@ -18,17 +19,9 @@ export interface State { } export class TeamPicker extends Component { - debouncedSearch: DebouncedFuncLeading; - constructor(props: Props) { super(props); this.state = { isLoading: false }; - this.search = this.search.bind(this); - - this.debouncedSearch = debounce(this.search, 300, { - leading: true, - trailing: true, - }); } componentDidMount(): void { @@ -50,28 +43,32 @@ export class TeamPicker extends Component { }); } - search(query?: string) { - this.setState({ isLoading: true }); + search = debounce( + async (query?: string) => { + this.setState({ isLoading: true }); - if (isNil(query)) { - query = ''; - } + if (isNil(query)) { + query = ''; + } - return getBackendSrv() - .get(`/api/teams/search?perpage=100&page=1&query=${query}`) - .then((result: { teams: Team[] }) => { - const teams: Array> = result.teams.map((team) => { - return { - value: team, - label: team.name, - imgUrl: team.avatarUrl, - }; + return getBackendSrv() + .get(`/api/teams/search?perpage=100&page=1&query=${query}`) + .then((result: { teams: Team[] }) => { + const teams: Array> = result.teams.map((team) => { + return { + value: team, + label: team.name, + imgUrl: team.avatarUrl, + }; + }); + + this.setState({ isLoading: false }); + return teams; }); - - this.setState({ isLoading: false }); - return teams; - }); - } + }, + 300, + { leading: true } + ); render() { const { onSelected, className } = this.props; @@ -81,7 +78,7 @@ export class TeamPicker extends Component { { - debouncedSearch: DebouncedFuncLeading; - constructor(props: Props) { super(props); this.state = { isLoading: false }; - this.search = this.search.bind(this); - - this.debouncedSearch = debounce(this.search, 300, { - leading: true, - trailing: true, - }); } - search(query?: string) { - this.setState({ isLoading: true }); + search = debounce( + async (query?: string) => { + this.setState({ isLoading: true }); - if (isNil(query)) { - query = ''; - } + if (isNil(query)) { + query = ''; + } - return getBackendSrv() - .get(`/api/org/users/lookup?query=${query}&limit=100`) - .then((result: OrgUser[]) => { - return result.map((user) => ({ - id: user.userId, - uid: user.uid, - value: user, - label: user.login, - imgUrl: user.avatarUrl, - login: user.login, - })); - }) - .finally(() => { - this.setState({ isLoading: false }); - }); - } + return getBackendSrv() + .get(`/api/org/users/lookup?query=${query}&limit=100`) + .then((result: OrgUser[]) => { + return result.map((user) => ({ + id: user.userId, + uid: user.uid, + value: user, + label: user.login, + imgUrl: user.avatarUrl, + login: user.login, + })); + }) + .finally(() => { + this.setState({ isLoading: false }); + }); + }, + 300, + { leading: true } + ); render() { const { className, onSelected, inputId } = this.props; @@ -66,7 +63,7 @@ export class UserPicker extends Component { inputId={inputId} isLoading={isLoading} defaultOptions={true} - loadOptions={this.debouncedSearch} + loadOptions={this.search} onChange={onSelected} placeholder="Start typing to search for user" noOptionsMessage="No users found"