iam: Fix debouncing in user, team and service account pickers (#100830)

iam: fix debouncing in user, team and service account pickers
This commit is contained in:
Victor Cinaglia
2025-02-18 08:34:37 -05:00
committed by GitHub
parent 678d1847b8
commit 46a537aa02
3 changed files with 82 additions and 91 deletions
@@ -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<Props, State> {
debouncedSearch: DebouncedFuncLeading<typeof this.search>;
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<Props, State> {
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"
@@ -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<Props, State> {
debouncedSearch: DebouncedFuncLeading<typeof this.search>;
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<Props, State> {
});
}
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<SelectableValue<Team>> = 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<SelectableValue<Team>> = 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<Props, State> {
<AsyncSelect
isLoading={isLoading}
defaultOptions={true}
loadOptions={this.debouncedSearch}
loadOptions={this.search}
value={value}
onChange={onSelected}
className={className}
@@ -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 UserPicker extends Component<Props, State> {
debouncedSearch: DebouncedFuncLeading<typeof this.search>;
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<Props, State> {
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"