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:
@@ -1,4 +1,5 @@
|
|||||||
import { debounce, DebouncedFuncLeading, isNil } from 'lodash';
|
import debounce from 'debounce-promise';
|
||||||
|
import { isNil } from 'lodash';
|
||||||
import { Component } from 'react';
|
import { Component } from 'react';
|
||||||
|
|
||||||
import { SelectableValue } from '@grafana/data';
|
import { SelectableValue } from '@grafana/data';
|
||||||
@@ -17,42 +18,38 @@ export interface State {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class ServiceAccountPicker extends Component<Props, State> {
|
export class ServiceAccountPicker extends Component<Props, State> {
|
||||||
debouncedSearch: DebouncedFuncLeading<typeof this.search>;
|
|
||||||
|
|
||||||
constructor(props: Props) {
|
constructor(props: Props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = { isLoading: false };
|
this.state = { isLoading: false };
|
||||||
this.search = this.search.bind(this);
|
|
||||||
|
|
||||||
this.debouncedSearch = debounce(this.search, 300, {
|
|
||||||
leading: true,
|
|
||||||
trailing: true,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
search(query?: string) {
|
search = debounce(
|
||||||
this.setState({ isLoading: true });
|
async (query?: string) => {
|
||||||
|
this.setState({ isLoading: true });
|
||||||
|
|
||||||
if (isNil(query)) {
|
if (isNil(query)) {
|
||||||
query = '';
|
query = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
return getBackendSrv()
|
return getBackendSrv()
|
||||||
.get(`/api/serviceaccounts/search?query=${query}&perpage=100`)
|
.get(`/api/serviceaccounts/search?query=${query}&perpage=100`)
|
||||||
.then((result: ServiceAccountsState) => {
|
.then((result: ServiceAccountsState) => {
|
||||||
return result.serviceAccounts.map((sa) => ({
|
return result.serviceAccounts.map((sa) => ({
|
||||||
id: sa.id,
|
id: sa.id,
|
||||||
uid: sa.uid,
|
uid: sa.uid,
|
||||||
value: sa,
|
value: sa,
|
||||||
label: sa.login,
|
label: sa.login,
|
||||||
imgUrl: sa.avatarUrl,
|
imgUrl: sa.avatarUrl,
|
||||||
login: sa.login,
|
login: sa.login,
|
||||||
}));
|
}));
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
this.setState({ isLoading: false });
|
this.setState({ isLoading: false });
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
|
300,
|
||||||
|
{ leading: true }
|
||||||
|
);
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { className, onSelected, inputId } = this.props;
|
const { className, onSelected, inputId } = this.props;
|
||||||
@@ -66,7 +63,7 @@ export class ServiceAccountPicker extends Component<Props, State> {
|
|||||||
inputId={inputId}
|
inputId={inputId}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
defaultOptions={true}
|
defaultOptions={true}
|
||||||
loadOptions={this.debouncedSearch}
|
loadOptions={this.search}
|
||||||
onChange={onSelected}
|
onChange={onSelected}
|
||||||
placeholder="Start typing to search for service accounts"
|
placeholder="Start typing to search for service accounts"
|
||||||
noOptionsMessage="No service accounts found"
|
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 { Component } from 'react';
|
||||||
|
|
||||||
import { SelectableValue } from '@grafana/data';
|
import { SelectableValue } from '@grafana/data';
|
||||||
@@ -18,17 +19,9 @@ export interface State {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class TeamPicker extends Component<Props, State> {
|
export class TeamPicker extends Component<Props, State> {
|
||||||
debouncedSearch: DebouncedFuncLeading<typeof this.search>;
|
|
||||||
|
|
||||||
constructor(props: Props) {
|
constructor(props: Props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = { isLoading: false };
|
this.state = { isLoading: false };
|
||||||
this.search = this.search.bind(this);
|
|
||||||
|
|
||||||
this.debouncedSearch = debounce(this.search, 300, {
|
|
||||||
leading: true,
|
|
||||||
trailing: true,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount(): void {
|
componentDidMount(): void {
|
||||||
@@ -50,28 +43,32 @@ export class TeamPicker extends Component<Props, State> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
search(query?: string) {
|
search = debounce(
|
||||||
this.setState({ isLoading: true });
|
async (query?: string) => {
|
||||||
|
this.setState({ isLoading: true });
|
||||||
|
|
||||||
if (isNil(query)) {
|
if (isNil(query)) {
|
||||||
query = '';
|
query = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
return getBackendSrv()
|
return getBackendSrv()
|
||||||
.get(`/api/teams/search?perpage=100&page=1&query=${query}`)
|
.get(`/api/teams/search?perpage=100&page=1&query=${query}`)
|
||||||
.then((result: { teams: Team[] }) => {
|
.then((result: { teams: Team[] }) => {
|
||||||
const teams: Array<SelectableValue<Team>> = result.teams.map((team) => {
|
const teams: Array<SelectableValue<Team>> = result.teams.map((team) => {
|
||||||
return {
|
return {
|
||||||
value: team,
|
value: team,
|
||||||
label: team.name,
|
label: team.name,
|
||||||
imgUrl: team.avatarUrl,
|
imgUrl: team.avatarUrl,
|
||||||
};
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
this.setState({ isLoading: false });
|
||||||
|
return teams;
|
||||||
});
|
});
|
||||||
|
},
|
||||||
this.setState({ isLoading: false });
|
300,
|
||||||
return teams;
|
{ leading: true }
|
||||||
});
|
);
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { onSelected, className } = this.props;
|
const { onSelected, className } = this.props;
|
||||||
@@ -81,7 +78,7 @@ export class TeamPicker extends Component<Props, State> {
|
|||||||
<AsyncSelect
|
<AsyncSelect
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
defaultOptions={true}
|
defaultOptions={true}
|
||||||
loadOptions={this.debouncedSearch}
|
loadOptions={this.search}
|
||||||
value={value}
|
value={value}
|
||||||
onChange={onSelected}
|
onChange={onSelected}
|
||||||
className={className}
|
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 { Component } from 'react';
|
||||||
|
|
||||||
import { SelectableValue } from '@grafana/data';
|
import { SelectableValue } from '@grafana/data';
|
||||||
@@ -17,42 +18,38 @@ export interface State {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class UserPicker extends Component<Props, State> {
|
export class UserPicker extends Component<Props, State> {
|
||||||
debouncedSearch: DebouncedFuncLeading<typeof this.search>;
|
|
||||||
|
|
||||||
constructor(props: Props) {
|
constructor(props: Props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = { isLoading: false };
|
this.state = { isLoading: false };
|
||||||
this.search = this.search.bind(this);
|
|
||||||
|
|
||||||
this.debouncedSearch = debounce(this.search, 300, {
|
|
||||||
leading: true,
|
|
||||||
trailing: true,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
search(query?: string) {
|
search = debounce(
|
||||||
this.setState({ isLoading: true });
|
async (query?: string) => {
|
||||||
|
this.setState({ isLoading: true });
|
||||||
|
|
||||||
if (isNil(query)) {
|
if (isNil(query)) {
|
||||||
query = '';
|
query = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
return getBackendSrv()
|
return getBackendSrv()
|
||||||
.get(`/api/org/users/lookup?query=${query}&limit=100`)
|
.get(`/api/org/users/lookup?query=${query}&limit=100`)
|
||||||
.then((result: OrgUser[]) => {
|
.then((result: OrgUser[]) => {
|
||||||
return result.map((user) => ({
|
return result.map((user) => ({
|
||||||
id: user.userId,
|
id: user.userId,
|
||||||
uid: user.uid,
|
uid: user.uid,
|
||||||
value: user,
|
value: user,
|
||||||
label: user.login,
|
label: user.login,
|
||||||
imgUrl: user.avatarUrl,
|
imgUrl: user.avatarUrl,
|
||||||
login: user.login,
|
login: user.login,
|
||||||
}));
|
}));
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
this.setState({ isLoading: false });
|
this.setState({ isLoading: false });
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
|
300,
|
||||||
|
{ leading: true }
|
||||||
|
);
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { className, onSelected, inputId } = this.props;
|
const { className, onSelected, inputId } = this.props;
|
||||||
@@ -66,7 +63,7 @@ export class UserPicker extends Component<Props, State> {
|
|||||||
inputId={inputId}
|
inputId={inputId}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
defaultOptions={true}
|
defaultOptions={true}
|
||||||
loadOptions={this.debouncedSearch}
|
loadOptions={this.search}
|
||||||
onChange={onSelected}
|
onChange={onSelected}
|
||||||
placeholder="Start typing to search for user"
|
placeholder="Start typing to search for user"
|
||||||
noOptionsMessage="No users found"
|
noOptionsMessage="No users found"
|
||||||
|
|||||||
Reference in New Issue
Block a user