diff --git a/package.json b/package.json
index fa4ebee027..ad0fea1596 100644
--- a/package.json
+++ b/package.json
@@ -31,7 +31,7 @@
"moment": "^2.29.1",
"react": "^16.8.5",
"react-dom": "^16.8.5",
- "react-hook-form": "^6.8.2",
+ "react-hook-form": "^6.8.3",
"react-i18next": "^11.7.0",
"react-router-dom": "^5.2.0",
"use-react-router-breadcrumbs": "^1.0.5"
diff --git a/src/components/form-access/__tests__/__snapshots__/FormAccess.test.tsx.snap b/src/components/form-access/__tests__/__snapshots__/FormAccess.test.tsx.snap
index 55821eba20..9debc1f2b2 100644
--- a/src/components/form-access/__tests__/__snapshots__/FormAccess.test.tsx.snap
+++ b/src/components/form-access/__tests__/__snapshots__/FormAccess.test.tsx.snap
@@ -90,11 +90,14 @@ exports[` render normal form 1`] = `
"fieldArrayNamesRef": Object {
"current": Set {},
},
+ "fieldArrayValuesRef": Object {
+ "current": Object {},
+ },
"fieldsRef": Object {
"current": Object {
"consentRequired": Object {
"ref": Object {
- "focus": undefined,
+ "focus": [Function],
"name": "consentRequired",
},
},
@@ -113,6 +116,18 @@ exports[` render normal form 1`] = `
"fieldsWithValidationRef": Object {
"current": Object {},
},
+ "formState": Object {
+ "dirtyFields": Object {},
+ "errors": Object {},
+ "isDirty": false,
+ "isSubmitSuccessful": false,
+ "isSubmitted": false,
+ "isSubmitting": false,
+ "isValid": false,
+ "isValidating": false,
+ "submitCount": 0,
+ "touched": Object {},
+ },
"formStateRef": Object {
"current": Object {
"dirtyFields": Object {},
@@ -122,14 +137,13 @@ exports[` render normal form 1`] = `
"isSubmitted": false,
"isSubmitting": false,
"isValid": false,
+ "isValidating": false,
"submitCount": 0,
"touched": Object {},
},
},
"getValues": [Function],
- "isWatchAllRef": Object {
- "current": false,
- },
+ "isFormDirty": [Function],
"mode": Object {
"isOnAll": false,
"isOnBlur": false,
@@ -143,16 +157,21 @@ exports[` render normal form 1`] = `
},
"readFormStateRef": Object {
"current": Object {
- "dirtyFields": false,
- "isDirty": false,
- "isSubmitting": false,
- "isValid": false,
- "touched": false,
+ "constructor": true,
+ "dirtyFields": true,
+ "errors": true,
+ "isDirty": true,
+ "isSubmitSuccessful": true,
+ "isSubmitted": true,
+ "isSubmitting": true,
+ "isValid": true,
+ "isValidating": true,
+ "submitCount": true,
+ "touched": true,
},
},
"register": [Function],
"removeFieldEventListener": [Function],
- "renderWatchedInputs": [Function],
"resetFieldArrayFunctionRef": Object {
"current": Object {},
},
@@ -164,6 +183,7 @@ exports[` render normal form 1`] = `
"trigger": [Function],
"unregister": [Function],
"updateFormState": [Function],
+ "updateWatchedValue": [Function],
"useWatchFieldsRef": Object {
"current": Object {},
},
@@ -174,9 +194,6 @@ exports[` render normal form 1`] = `
"current": Object {},
},
"validateResolver": undefined,
- "watchFieldsRef": Object {
- "current": Set {},
- },
"watchInternal": [Function],
}
}
diff --git a/src/components/table-toolbar/KeycloakDataTable.tsx b/src/components/table-toolbar/KeycloakDataTable.tsx
index 5c162a5cd0..6b8a8b803b 100644
--- a/src/components/table-toolbar/KeycloakDataTable.tsx
+++ b/src/components/table-toolbar/KeycloakDataTable.tsx
@@ -84,6 +84,7 @@ export type DataListProps = {
isPaginated?: boolean;
ariaLabelKey: string;
searchPlaceholderKey: string;
+ setRefresher?: (refresher: () => void) => void;
columns: Field[];
actions?: Action[];
actionResolver?: IActionsResolver;
@@ -118,6 +119,7 @@ export function KeycloakDataTable({
searchPlaceholderKey,
isPaginated = false,
onSelect,
+ setRefresher,
canSelectAll = false,
loader,
columns,
@@ -138,6 +140,10 @@ export function KeycloakDataTable({
const [key, setKey] = useState(0);
const refresh = () => setKey(new Date().getTime());
+ useEffect(() => {
+ setRefresher && setRefresher(refresh);
+ }, []);
+
useEffect(() => {
return asyncStateFetch(
async () => {
diff --git a/src/components/view-header/ViewHeader.tsx b/src/components/view-header/ViewHeader.tsx
index 7a7a54725d..5d6aa4e78a 100644
--- a/src/components/view-header/ViewHeader.tsx
+++ b/src/components/view-header/ViewHeader.tsx
@@ -24,6 +24,7 @@ export type ViewHeaderProps = {
titleKey: string;
badge?: string;
subKey: string;
+ actionsDropdownId?: string;
subKeyLinkProps?: ButtonProps;
dropdownItems?: ReactElement[];
lowerDropdownItems?: any;
@@ -33,6 +34,7 @@ export type ViewHeaderProps = {
};
export const ViewHeader = ({
+ actionsDropdownId,
titleKey,
badge,
subKey,
@@ -99,7 +101,10 @@ export const ViewHeader = ({
+
{t("common:action")}
}
diff --git a/src/realm-roles/AssociatedRolesModal.tsx b/src/realm-roles/AssociatedRolesModal.tsx
index 403a89d192..b520718b8c 100644
--- a/src/realm-roles/AssociatedRolesModal.tsx
+++ b/src/realm-roles/AssociatedRolesModal.tsx
@@ -12,6 +12,8 @@ import { boolFormatter } from "../util";
export type AssociatedRolesModalProps = {
open: boolean;
toggleDialog: () => void;
+ onConfirm: (newReps: RoleRepresentation[]) => void;
+ existingCompositeRoles: RoleRepresentation[];
};
const attributesToArray = (attributes: { [key: string]: string }): any => {
@@ -40,9 +42,17 @@ export const AssociatedRolesModal = (props: AssociatedRolesModalProps) => {
const loader = async () => {
const allRoles = await adminClient.roles.find();
- const roles = allRoles.filter((x) => x.name != name);
+ const existingAdditionalRoles = await adminClient.roles.getCompositeRoles({
+ id,
+ });
- return roles;
+ return allRoles.filter((role: RoleRepresentation) => {
+ return (
+ existingAdditionalRoles.find(
+ (existing: RoleRepresentation) => existing.name === role.name
+ ) === undefined && role.name !== name
+ );
+ });
};
useEffect(() => {
@@ -76,10 +86,12 @@ export const AssociatedRolesModal = (props: AssociatedRolesModalProps) => {
actions={[