issue 19582 (#19749)

* fixed paginate subgroups + nav

* render matched sub groups and fix pagination when searching
This commit is contained in:
Erik Jan de Wit 2023-04-19 21:35:36 +02:00 committed by GitHub
parent 0ddc71d987
commit 3b65f1163d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -61,6 +61,8 @@ export const GroupPickerDialog = ({
const [max, setMax] = useState(10); const [max, setMax] = useState(10);
const [first, setFirst] = useState(0); const [first, setFirst] = useState(0);
const [count, setCount] = useState(0);
const currentGroup = () => navigation[navigation.length - 1]; const currentGroup = () => navigation[navigation.length - 1];
useFetch( useFetch(
@ -68,13 +70,14 @@ export const GroupPickerDialog = ({
let group; let group;
let groups; let groups;
let existingUserGroups; let existingUserGroups;
let count = 0;
if (!groupId) { if (!groupId) {
groups = await adminClient.groups.find({ groups = await adminClient.groups.find({
first, first,
max: max + 1, max: max + (isSearching ? 0 : 1),
search: isSearching ? filter : "", search: isSearching ? filter : "",
}); });
} else { } else if (!navigation.map(({ id }) => id).includes(groupId)) {
group = await adminClient.groups.findOne({ id: groupId }); group = await adminClient.groups.findOne({ id: groupId });
if (!group) { if (!group) {
throw new Error(t("common:notFound")); throw new Error(t("common:notFound"));
@ -82,24 +85,32 @@ export const GroupPickerDialog = ({
groups = group.subGroups!; groups = group.subGroups!;
} }
if (isSearching) {
count = (await adminClient.groups.count({ search: filter, top: true }))
.count;
}
if (id) { if (id) {
existingUserGroups = await adminClient.users.listGroups({ existingUserGroups = await adminClient.users.listGroups({
id, id,
}); });
} }
return { group, groups, existingUserGroups }; return { group, groups, existingUserGroups, count };
}, },
async ({ group: selectedGroup, groups, existingUserGroups }) => { async ({ group: selectedGroup, groups, existingUserGroups, count }) => {
setJoinedGroups(existingUserGroups || []); setJoinedGroups(existingUserGroups || []);
if (selectedGroup) { if (selectedGroup) {
setNavigation([...navigation, selectedGroup]); setNavigation([...navigation, selectedGroup]);
} }
if (groups) {
groups.forEach((group: SelectableGroup) => { groups.forEach((group: SelectableGroup) => {
group.checked = !!selectedRows.find((r) => r.id === group.id); group.checked = !!selectedRows.find((r) => r.id === group.id);
}); });
setGroups(groups); setGroups(groups);
}
setCount(count);
}, },
[groupId, filter, first, max] [groupId, filter, first, max]
); );
@ -111,25 +122,6 @@ export const GroupPickerDialog = ({
].some((group) => group === row?.id); ].some((group) => group === row?.id);
}; };
const hasSubgroups = (group: GroupRepresentation) =>
group.subGroups?.length !== 0;
const expandGroup = (
group: GroupRepresentation,
name: string
): GroupRepresentation => {
if (group.name?.includes(name)) {
return group;
}
if (group.subGroups) {
for (const g of group.subGroups) {
const found = expandGroup(g, name);
return found;
}
}
return group;
};
return ( return (
<Modal <Modal
variant={isSearching ? ModalVariant.medium : ModalVariant.small} variant={isSearching ? ModalVariant.medium : ModalVariant.small}
@ -161,7 +153,10 @@ export const GroupPickerDialog = ({
]} ]}
> >
<PaginatingTableToolbar <PaginatingTableToolbar
count={groups.length} count={
(isSearching ? count : groups.length) -
(groupId || isSearching ? first : 0)
}
first={first} first={first}
max={max} max={max}
onNextClick={setFirst} onNextClick={setFirst}
@ -189,6 +184,8 @@ export const GroupPickerDialog = ({
onClick={() => { onClick={() => {
setGroupId(undefined); setGroupId(undefined);
setNavigation([]); setNavigation([]);
setFirst(0);
setMax(10);
}} }}
> >
{t("groups")} {t("groups")}
@ -203,6 +200,8 @@ export const GroupPickerDialog = ({
onClick={() => { onClick={() => {
setGroupId(group.id); setGroupId(group.id);
setNavigation([...navigation].slice(0, i)); setNavigation([...navigation].slice(0, i));
setFirst(0);
setMax(10);
}} }}
> >
{group.name} {group.name}
@ -213,7 +212,88 @@ export const GroupPickerDialog = ({
))} ))}
</Breadcrumb> </Breadcrumb>
<DataList aria-label={t("groups")} isCompact> <DataList aria-label={t("groups")} isCompact>
{groups.slice(0, max).map((group: SelectableGroup) => ( {groups
.slice(groupId ? first : 0, max + (groupId ? first : 0))
.map((group: SelectableGroup) => (
<>
<GroupRow
key={group.id}
group={group}
isRowDisabled={isRowDisabled}
onSelect={setGroupId}
type={type}
isSearching={isSearching}
setIsSearching={setIsSearching}
selectedRows={selectedRows}
setSelectedRows={setSelectedRows}
canBrowse={canBrowse}
/>
{isSearching &&
group.subGroups?.length !== 0 &&
group.subGroups!.map((g) => (
<GroupRow
key={g.id}
group={g}
isRowDisabled={isRowDisabled}
onSelect={setGroupId}
type={type}
isSearching={isSearching}
setIsSearching={setIsSearching}
selectedRows={selectedRows}
setSelectedRows={setSelectedRows}
canBrowse={canBrowse}
/>
))}
</>
))}
</DataList>
{groups.length === 0 && !isSearching && (
<ListEmptyState
hasIcon={false}
message={t("groups:moveGroupEmpty")}
instructions={t("groups:moveGroupEmptyInstructions")}
/>
)}
{groups.length === 0 && isSearching && (
<ListEmptyState
message={t("common:noSearchResults")}
instructions={t("common:noSearchResultsInstructions")}
/>
)}
</PaginatingTableToolbar>
</Modal>
);
};
type GroupRowProps = {
group: SelectableGroup;
type: "selectOne" | "selectMany";
isRowDisabled: (row?: GroupRepresentation) => boolean;
isSearching: boolean;
setIsSearching: (value: boolean) => void;
onSelect: (groupId: string) => void;
selectedRows: SelectableGroup[];
setSelectedRows: (groups: SelectableGroup[]) => void;
canBrowse: boolean;
};
const GroupRow = ({
group,
type,
isRowDisabled,
isSearching,
setIsSearching,
onSelect,
selectedRows,
setSelectedRows,
canBrowse,
}: GroupRowProps) => {
const { t } = useTranslation();
const hasSubgroups = (group: GroupRepresentation) =>
group.subGroups?.length !== 0;
return (
<DataListItem <DataListItem
className={`join-group-dialog-row-${ className={`join-group-dialog-row-${
isRowDisabled(group) ? "disabled" : "" isRowDisabled(group) ? "disabled" : ""
@ -222,17 +302,14 @@ export const GroupPickerDialog = ({
key={group.id} key={group.id}
id={group.id} id={group.id}
onClick={(e) => { onClick={(e) => {
const g = isSearching ? expandGroup(group, filter) : group; if (isRowDisabled(group)) return;
if (isRowDisabled(g)) return;
if (type === "selectOne") { if (type === "selectOne") {
setGroupId(g.id); onSelect(group.id!);
} else if ( } else if (
hasSubgroups(group) && hasSubgroups(group) &&
(e.target as HTMLInputElement).type !== "checkbox" (e.target as HTMLInputElement).type !== "checkbox"
) { ) {
setGroupId( onSelect(group.id!);
isSearching ? expandGroup(group, filter).id : group.id
);
setIsSearching(false); setIsSearching(false);
} }
}} }}
@ -254,14 +331,9 @@ export const GroupPickerDialog = ({
group.checked = checked; group.checked = checked;
let newSelectedRows: SelectableGroup[] = []; let newSelectedRows: SelectableGroup[] = [];
if (!group.checked) { if (!group.checked) {
newSelectedRows = selectedRows.filter( newSelectedRows = selectedRows.filter((r) => r.id !== group.id);
(r) => r.id !== group.id
);
} else { } else {
newSelectedRows = [ newSelectedRows = [...selectedRows, group];
...selectedRows,
isSearching ? expandGroup(group, filter) : group,
];
} }
setSelectedRows(newSelectedRows); setSelectedRows(newSelectedRows);
@ -277,10 +349,7 @@ export const GroupPickerDialog = ({
className="keycloak-groups-group-path" className="keycloak-groups-group-path"
> >
{isSearching ? ( {isSearching ? (
<GroupPath <GroupPath id={`select-${group.name}`} group={group} />
id={`select-${group.name}`}
group={expandGroup(group, filter)}
/>
) : ( ) : (
<span id={`select-${group.name}`}>{group.name}</span> <span id={`select-${group.name}`}>{group.name}</span>
)} )}
@ -293,35 +362,13 @@ export const GroupPickerDialog = ({
aria-label={t("groupName")} aria-label={t("groupName")}
isPlainButtonAction isPlainButtonAction
> >
{((hasSubgroups(group) && canBrowse) || {((hasSubgroups(group) && canBrowse) || type === "selectOne") && (
type === "selectOne") && ( <Button isDisabled variant="link" aria-label={t("common:select")}>
<Button
isDisabled
variant="link"
aria-label={t("common:select")}
>
<AngleRightIcon /> <AngleRightIcon />
</Button> </Button>
)} )}
</DataListAction> </DataListAction>
</DataListItemRow> </DataListItemRow>
</DataListItem> </DataListItem>
))}
</DataList>
{groups.length === 0 && !isSearching && (
<ListEmptyState
hasIcon={false}
message={t("groups:moveGroupEmpty")}
instructions={t("groups:moveGroupEmptyInstructions")}
/>
)}
{groups.length === 0 && isSearching && (
<ListEmptyState
message={t("common:noSearchResults")}
instructions={t("common:noSearchResultsInstructions")}
/>
)}
</PaginatingTableToolbar>
</Modal>
); );
}; };