issue 19582 (#19749)
* fixed paginate subgroups + nav * render matched sub groups and fix pagination when searching
This commit is contained in:
parent
0ddc71d987
commit
3b65f1163d
1 changed files with 168 additions and 121 deletions
|
@ -61,6 +61,8 @@ export const GroupPickerDialog = ({
|
|||
const [max, setMax] = useState(10);
|
||||
const [first, setFirst] = useState(0);
|
||||
|
||||
const [count, setCount] = useState(0);
|
||||
|
||||
const currentGroup = () => navigation[navigation.length - 1];
|
||||
|
||||
useFetch(
|
||||
|
@ -68,13 +70,14 @@ export const GroupPickerDialog = ({
|
|||
let group;
|
||||
let groups;
|
||||
let existingUserGroups;
|
||||
let count = 0;
|
||||
if (!groupId) {
|
||||
groups = await adminClient.groups.find({
|
||||
first,
|
||||
max: max + 1,
|
||||
max: max + (isSearching ? 0 : 1),
|
||||
search: isSearching ? filter : "",
|
||||
});
|
||||
} else {
|
||||
} else if (!navigation.map(({ id }) => id).includes(groupId)) {
|
||||
group = await adminClient.groups.findOne({ id: groupId });
|
||||
if (!group) {
|
||||
throw new Error(t("common:notFound"));
|
||||
|
@ -82,24 +85,32 @@ export const GroupPickerDialog = ({
|
|||
groups = group.subGroups!;
|
||||
}
|
||||
|
||||
if (isSearching) {
|
||||
count = (await adminClient.groups.count({ search: filter, top: true }))
|
||||
.count;
|
||||
}
|
||||
|
||||
if (id) {
|
||||
existingUserGroups = await adminClient.users.listGroups({
|
||||
id,
|
||||
});
|
||||
}
|
||||
|
||||
return { group, groups, existingUserGroups };
|
||||
return { group, groups, existingUserGroups, count };
|
||||
},
|
||||
async ({ group: selectedGroup, groups, existingUserGroups }) => {
|
||||
async ({ group: selectedGroup, groups, existingUserGroups, count }) => {
|
||||
setJoinedGroups(existingUserGroups || []);
|
||||
if (selectedGroup) {
|
||||
setNavigation([...navigation, selectedGroup]);
|
||||
}
|
||||
|
||||
if (groups) {
|
||||
groups.forEach((group: SelectableGroup) => {
|
||||
group.checked = !!selectedRows.find((r) => r.id === group.id);
|
||||
});
|
||||
setGroups(groups);
|
||||
}
|
||||
setCount(count);
|
||||
},
|
||||
[groupId, filter, first, max]
|
||||
);
|
||||
|
@ -111,25 +122,6 @@ export const GroupPickerDialog = ({
|
|||
].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 (
|
||||
<Modal
|
||||
variant={isSearching ? ModalVariant.medium : ModalVariant.small}
|
||||
|
@ -161,7 +153,10 @@ export const GroupPickerDialog = ({
|
|||
]}
|
||||
>
|
||||
<PaginatingTableToolbar
|
||||
count={groups.length}
|
||||
count={
|
||||
(isSearching ? count : groups.length) -
|
||||
(groupId || isSearching ? first : 0)
|
||||
}
|
||||
first={first}
|
||||
max={max}
|
||||
onNextClick={setFirst}
|
||||
|
@ -189,6 +184,8 @@ export const GroupPickerDialog = ({
|
|||
onClick={() => {
|
||||
setGroupId(undefined);
|
||||
setNavigation([]);
|
||||
setFirst(0);
|
||||
setMax(10);
|
||||
}}
|
||||
>
|
||||
{t("groups")}
|
||||
|
@ -203,6 +200,8 @@ export const GroupPickerDialog = ({
|
|||
onClick={() => {
|
||||
setGroupId(group.id);
|
||||
setNavigation([...navigation].slice(0, i));
|
||||
setFirst(0);
|
||||
setMax(10);
|
||||
}}
|
||||
>
|
||||
{group.name}
|
||||
|
@ -213,7 +212,88 @@ export const GroupPickerDialog = ({
|
|||
))}
|
||||
</Breadcrumb>
|
||||
<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
|
||||
className={`join-group-dialog-row-${
|
||||
isRowDisabled(group) ? "disabled" : ""
|
||||
|
@ -222,17 +302,14 @@ export const GroupPickerDialog = ({
|
|||
key={group.id}
|
||||
id={group.id}
|
||||
onClick={(e) => {
|
||||
const g = isSearching ? expandGroup(group, filter) : group;
|
||||
if (isRowDisabled(g)) return;
|
||||
if (isRowDisabled(group)) return;
|
||||
if (type === "selectOne") {
|
||||
setGroupId(g.id);
|
||||
onSelect(group.id!);
|
||||
} else if (
|
||||
hasSubgroups(group) &&
|
||||
(e.target as HTMLInputElement).type !== "checkbox"
|
||||
) {
|
||||
setGroupId(
|
||||
isSearching ? expandGroup(group, filter).id : group.id
|
||||
);
|
||||
onSelect(group.id!);
|
||||
setIsSearching(false);
|
||||
}
|
||||
}}
|
||||
|
@ -254,14 +331,9 @@ export const GroupPickerDialog = ({
|
|||
group.checked = checked;
|
||||
let newSelectedRows: SelectableGroup[] = [];
|
||||
if (!group.checked) {
|
||||
newSelectedRows = selectedRows.filter(
|
||||
(r) => r.id !== group.id
|
||||
);
|
||||
newSelectedRows = selectedRows.filter((r) => r.id !== group.id);
|
||||
} else {
|
||||
newSelectedRows = [
|
||||
...selectedRows,
|
||||
isSearching ? expandGroup(group, filter) : group,
|
||||
];
|
||||
newSelectedRows = [...selectedRows, group];
|
||||
}
|
||||
|
||||
setSelectedRows(newSelectedRows);
|
||||
|
@ -277,10 +349,7 @@ export const GroupPickerDialog = ({
|
|||
className="keycloak-groups-group-path"
|
||||
>
|
||||
{isSearching ? (
|
||||
<GroupPath
|
||||
id={`select-${group.name}`}
|
||||
group={expandGroup(group, filter)}
|
||||
/>
|
||||
<GroupPath id={`select-${group.name}`} group={group} />
|
||||
) : (
|
||||
<span id={`select-${group.name}`}>{group.name}</span>
|
||||
)}
|
||||
|
@ -293,35 +362,13 @@ export const GroupPickerDialog = ({
|
|||
aria-label={t("groupName")}
|
||||
isPlainButtonAction
|
||||
>
|
||||
{((hasSubgroups(group) && canBrowse) ||
|
||||
type === "selectOne") && (
|
||||
<Button
|
||||
isDisabled
|
||||
variant="link"
|
||||
aria-label={t("common:select")}
|
||||
>
|
||||
{((hasSubgroups(group) && canBrowse) || type === "selectOne") && (
|
||||
<Button isDisabled variant="link" aria-label={t("common:select")}>
|
||||
<AngleRightIcon />
|
||||
</Button>
|
||||
)}
|
||||
</DataListAction>
|
||||
</DataListItemRow>
|
||||
</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>
|
||||
);
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue