Skip to content

Commit

Permalink
Merge branch 'master' into dependabot/maven/org.codehaus.mojo-exec-ma…
Browse files Browse the repository at this point in the history
…ven-plugin-3.5.0
  • Loading branch information
bernd authored Oct 28, 2024
2 parents 6fdd592 + 1483003 commit 9e50760
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 76 deletions.
8 changes: 5 additions & 3 deletions graylog2-web-interface/src/components/common/Section.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ const FlexWrapper = styled.div(({ theme }) => css`
`);

type Props = React.PropsWithChildren<{
title: React.ReactNode,
title: string,
header?: React.ReactNode,
actions?: React.ReactNode,
headerLeftSection?: React.ReactNode,
collapsible?: boolean,
Expand All @@ -64,15 +65,15 @@ type Props = React.PropsWithChildren<{
/**
* Simple section component. Currently only a "filled" version exists.
*/
const Section = ({ title, actions, headerLeftSection, collapsible = false, defaultClosed = false, disableCollapseButton = false, children }: Props) => {
const Section = ({ title, header, actions, headerLeftSection, collapsible = false, defaultClosed = false, disableCollapseButton = false, children }: Props) => {
const [opened, { toggle }] = useDisclosure(!defaultClosed);
const onHeaderClick = () => (!disableCollapseButton && toggle());

return (
<Container $opened={opened} $collapsible={collapsible}>
<Header $opened={opened} $collapsible={collapsible} onClick={onHeaderClick}>
<FlexWrapper>
<h2>{title}</h2>
<h2>{header ?? title}</h2>
{headerLeftSection && <FlexWrapper onClick={(e) => { e.stopPropagation(); }}>{headerLeftSection}</FlexWrapper>}
</FlexWrapper>
<FlexWrapper>
Expand All @@ -82,6 +83,7 @@ const Section = ({ title, actions, headerLeftSection, collapsible = false, defau
bsStyle={opened ? 'primary' : 'default'}
onClick={toggle}
data-testid="collapseButton"
title={`Toggle ${title.toLowerCase()} section`}
disabled={disableCollapseButton}>
<Icon size="sm" name={opened ? 'keyboard_arrow_up' : 'keyboard_arrow_down'} />
</Button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,91 @@
* <http://www.mongodb.com/licensing/server-side-public-license>.
*/
import * as React from 'react';
import { useRef } from 'react';
import { useRef, useState, useEffect, useCallback } from 'react';
import debounce from 'lodash/debounce';

import Select from 'components/common/Select';
import { Spinner } from 'components/common';

type Props = React.ComponentProps<typeof Select>;
const DEFAULT_PAGINATION = { page: 1, perPage: 50, query: '' };

const PaginatedSelect = (props: Props) => {
type Pagination = {
page: number,
perPage: number,
query: string,
}

type PaginatedOptions = {
total: number,
pagination: Pagination,
list: Array<{ label: string, value: unknown }>,
}

type Props = Omit<React.ComponentProps<typeof Select>, 'options'> & {
onLoadOptions: (pagination: Pagination) => Promise<PaginatedOptions>,
}

const PaginatedSelect = ({ onLoadOptions, ...rest }: Props) => {
const selectRef = useRef();
const [paginatedOptions, setPaginatedOptions] = useState<PaginatedOptions | undefined>();
const [isLoading, setIsLoading] = useState(false);

const loadOptions = useCallback((pagination: Pagination, processResponse = (res: PaginatedOptions, _cur: PaginatedOptions) => res) => {
setIsLoading(true);

return onLoadOptions(pagination).then((res) => {
setPaginatedOptions((cur) => processResponse(res, cur));
setIsLoading(false);
});
}, [onLoadOptions]);

const handleSearch = debounce((newValue, actionMeta) => {
if (actionMeta.action === 'input-change') {
return loadOptions({ ...DEFAULT_PAGINATION, query: newValue });
}

if (actionMeta.action === 'menu-close') {
return loadOptions(DEFAULT_PAGINATION);
}

return Promise.resolve();
}, 400);

const handleLoadMore = debounce(() => {
const { pagination, total, list } = paginatedOptions;
const extendList = (res: PaginatedOptions, cur: PaginatedOptions) => ({
...res,
list: [...cur.list, ...res.list],
});

if (isLoading) {
return;
}

if (total > list.length) {
loadOptions({ ...pagination, page: pagination.page + 1, query: '' }, extendList);
}
}, 400);

useEffect(() => {
if (!paginatedOptions) {
onLoadOptions(DEFAULT_PAGINATION).then(setPaginatedOptions);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

if (!paginatedOptions) {
return <Spinner text="Loading options..." />;
}

return (
<Select ref={selectRef} async {...props} />
<Select {...rest}
ref={selectRef}
options={paginatedOptions.list}
onInputChange={handleSearch}
loadOptions={handleLoadMore}
async
total={paginatedOptions.total} />
);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ const RuleBlockForm = ({
<Select id={`existingBlock-select-${type}`}
name={`existingBlock-select-${type}`}
placeholder={`Add ${type}`}
aria-label={`Add ${type}`}
options={options}
optionRenderer={optionRenderer}
clearable={false}
Expand Down
83 changes: 15 additions & 68 deletions graylog2-web-interface/src/components/users/UsersSelectField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,99 +15,46 @@
* <http://www.mongodb.com/licensing/server-side-public-license>.
*/
import * as React from 'react';
import { useEffect, useState, useCallback } from 'react';
import debounce from 'lodash/debounce';
import type { PaginatedUsers } from 'src/stores/users/UsersStore';
import { useCallback } from 'react';

import UsersDomain from 'domainActions/users/UsersDomain';
import { isPermitted } from 'util/PermissionsMixin';
import { Spinner } from 'components/common';
import useCurrentUser from 'hooks/useCurrentUser';

import PaginatedSelect from '../common/Select/PaginatedSelect';

const DEFAULT_PAGINATION = { page: 1, perPage: 50, query: '', total: 0 };

const formatUsers = (users) => users.map((user) => ({ label: `${user.username} (${user.fullName})`, value: user.username }));

type Props = {
value: string,
onChange: (nextValue) => void,
value: string,
onChange: (nextValue) => void,
}

const UsersSelectField = ({ value, onChange }: Props) => {
const currentUser = useCurrentUser();
const [paginatedUsers, setPaginatedUsers] = useState<PaginatedUsers | undefined>();
const [isNextPageLoading, setIsNextPageLoading] = useState(false);
const [isSearching, setIsSearching] = useState(false);
const loadUsersPaginated = useCallback((pagination = DEFAULT_PAGINATION) => {
if (isPermitted(currentUser.permissions, 'users:list')) {
setIsNextPageLoading(true);

return UsersDomain.loadUsersPaginated(pagination).then((newPaginatedUser) => {
setIsNextPageLoading(false);

return newPaginatedUser;
const loadUsers = useCallback((pagination: { page: number, perPage: number, query: string }) => {
if (!isPermitted(currentUser.permissions, 'users:list')) {
return Promise.resolve({
pagination,
total: 0,
list: [],
});
}

return undefined;
return UsersDomain.loadUsersPaginated(pagination).then((results) => ({
total: results.pagination.total,
list: formatUsers(results.list.toArray()),
pagination,
}));
}, [currentUser.permissions]);

const loadUsers = (pagination, query = '') => {
loadUsersPaginated({ ...pagination, page: pagination.page + 1, query }).then((response) => {
setPaginatedUsers((prevUsers) => {
const list = prevUsers.list.concat(response.list);
const newPagination = { ...prevUsers.pagination, ...response.pagination };

return { ...prevUsers, list, pagination: newPagination } as PaginatedUsers;
});
});
};

const loadMoreOptions = debounce(() => {
const { pagination, pagination: { total }, list } = paginatedUsers;

if (total > list.count()) {
loadUsers(pagination);
}
}, 400);

useEffect(() => {
if (!paginatedUsers) {
loadUsersPaginated().then(setPaginatedUsers);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

const handleSearch = debounce((newValue, actionMeta) => {
if ((actionMeta.action === 'input-change')) {
setIsSearching(true);

loadUsersPaginated({ ...DEFAULT_PAGINATION, query: newValue }).then((results) => {
setIsSearching(true);
setPaginatedUsers(results);
});
} else if (actionMeta.action === 'menu-close') {
loadUsersPaginated().then(setPaginatedUsers);
}
}, 400);

if (!paginatedUsers) {
return <p><Spinner text="Loading User select..." /></p>;
}

const { list, pagination: { total } } = paginatedUsers;

return (
<PaginatedSelect id="user-select-list"
value={value}
placeholder="Select user(s)..."
options={formatUsers(list.toArray())}
onInputChange={handleSearch}
loadOptions={isNextPageLoading || isSearching ? () => {} : loadMoreOptions}
onLoadOptions={loadUsers}
multi
total={total}
onChange={onChange} />
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ const StreamsFilter = ({
<Select placeholder={placeholder}
disabled={disabled}
clearable={clearable}
inputProps={{ 'aria-label': placeholder }}
aria-label={placeholder}
displayKey="key"
inputId="streams-filter"
onChange={handleChange}
Expand Down

0 comments on commit 9e50760

Please sign in to comment.