Made form readonly and fixed initialisation (#2070)

* add the ablity for dynamic component to become
disabeled when used in a <FormAccess component

* add ability to manually make form readonly

* added readonly + fixed form initialize

fixes: #1898

* Update src/components/form-access/FormAccess.tsx

Co-authored-by: Jon Koops <jonkoops@gmail.com>

Co-authored-by: Jon Koops <jonkoops@gmail.com>
This commit is contained in:
Erik Jan de Wit 2022-02-16 15:53:45 +01:00 committed by GitHub
parent a6904be9ff
commit c0a9b5cebc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 63 additions and 22 deletions

View file

@ -11,6 +11,7 @@ export const BooleanComponent = ({
label,
helpText,
defaultValue,
isDisabled = false,
}: ComponentProps) => {
const { t } = useTranslation("dynamic");
const { control } = useFormContext();
@ -32,6 +33,7 @@ export const BooleanComponent = ({
render={({ onChange, value }) => (
<Switch
id={name!}
isDisabled={isDisabled}
label={t("common:on")}
labelOff={t("common:off")}
isChecked={value === "true" || value === true}

View file

@ -18,6 +18,7 @@ export const ClientSelectComponent = ({
label,
helpText,
defaultValue,
isDisabled = false,
}: ComponentProps) => {
const { t } = useTranslation("dynamic");
const { control } = useFormContext();
@ -66,6 +67,7 @@ export const ClientSelectComponent = ({
variant={SelectVariant.typeahead}
onToggle={(open) => setOpen(open)}
isOpen={open}
isDisabled={isDisabled}
selections={value}
onFilter={(_, value) => {
setSearch(value);

View file

@ -9,13 +9,16 @@ type DynamicComponentProps = {
parentCallback?: (data: string[]) => void;
};
export const DynamicComponents = ({ properties }: DynamicComponentProps) => (
export const DynamicComponents = ({
properties,
...rest
}: DynamicComponentProps) => (
<>
{properties.map((property) => {
const componentType = property.type!;
if (isValidComponentType(componentType) && property.name !== "scopes") {
const Component = COMPONENTS[componentType];
return <Component key={property.name} {...property} />;
return <Component key={property.name} {...property} {...rest} />;
} else {
console.warn(`There is no editor registered for ${componentType}`);
}

View file

@ -17,6 +17,7 @@ export const ListComponent = ({
helpText,
defaultValue,
options,
isDisabled = false,
}: ComponentProps) => {
const { t } = useTranslation("dynamic");
const { control } = useFormContext();
@ -38,6 +39,7 @@ export const ListComponent = ({
render={({ onChange, value }) => (
<Select
toggleId={name}
isDisabled={isDisabled}
onToggle={(toggle) => setOpen(toggle)}
onSelect={(_, value) => {
onChange(value as string);

View file

@ -23,6 +23,7 @@ export const MultivaluedChipsComponent = ({
name,
label,
helpText,
isDisabled = false,
}: ComponentProps) => {
const { t } = useTranslation("dynamic");
const { control } = useFormContext();
@ -126,6 +127,7 @@ export const MultivaluedChipsComponent = ({
))}
</ChipGroup>
<Button
isDisabled={isDisabled}
data-testid="select-scope-button"
variant="secondary"
onClick={() => {

View file

@ -17,6 +17,7 @@ export const MultiValuedListComponent = ({
helpText,
defaultValue,
options,
isDisabled = false,
}: ComponentProps) => {
const { t } = useTranslation("dynamic");
const { control } = useFormContext();
@ -38,6 +39,7 @@ export const MultiValuedListComponent = ({
<Select
toggleId={name}
data-testid={name}
isDisabled={isDisabled}
chipGroupProps={{
numChips: 3,
expandedText: t("common:hide"),

View file

@ -20,6 +20,7 @@ export const MultivaluedRoleComponent = ({
name,
label,
helpText,
isDisabled = false,
}: ComponentProps) => {
const { t } = useTranslation("dynamic");
const { whoAmI } = useWhoAmI();
@ -73,6 +74,7 @@ export const MultivaluedRoleComponent = ({
rules={{ required: true }}
render={({ onChange, value }) => (
<Select
isDisabled={isDisabled}
onToggle={(isExpanded) => setOpen(isExpanded)}
isOpen={open}
className="kc-role-select"

View file

@ -20,6 +20,7 @@ import type { EditClientPolicyConditionParams } from "../../realm-settings/route
export const MultivaluedScopesComponent = ({
defaultValue,
name,
isDisabled = false,
}: ComponentProps) => {
const { t } = useTranslation("dynamic");
const { control } = useFormContext();
@ -111,6 +112,7 @@ export const MultivaluedScopesComponent = ({
))}
</ChipGroup>
<Button
isDisabled={isDisabled}
data-testid="select-scope-button"
variant="secondary"
onClick={() => {

View file

@ -10,6 +10,7 @@ export const MultiValuedStringComponent = ({
name,
label,
helpText,
isDisabled = false,
}: ComponentProps) => {
const { t } = useTranslation("dynamic");
@ -24,6 +25,7 @@ export const MultiValuedStringComponent = ({
<MultiLineInput
name={`config.${name}`}
aria-label={name}
isDisabled={isDisabled}
addButtonLabel={t("addMultivaluedLabel", {
fieldLabel: t(label!).toLowerCase(),
})}

View file

@ -24,7 +24,12 @@ const RealmClient = (realm: string): ClientRepresentation => ({
clientId: realm,
});
export const RoleComponent = ({ name, label, helpText }: ComponentProps) => {
export const RoleComponent = ({
name,
label,
helpText,
isDisabled = false,
}: ComponentProps) => {
const { t } = useTranslation("dynamic");
const adminClient = useAdminClient();
@ -143,6 +148,7 @@ export const RoleComponent = ({ name, label, helpText }: ComponentProps) => {
{clients && (
<Select
toggleId={`group-${name}`}
isDisabled={isDisabled}
onToggle={() => setClientsOpen(!clientsOpen)}
isOpen={clientsOpen}
variant={SelectVariant.typeahead}

View file

@ -12,6 +12,7 @@ export const ScriptComponent = ({
label,
helpText,
defaultValue,
isDisabled = false,
}: ComponentProps) => {
const { t } = useTranslation("dynamic");
const { control } = useFormContext();
@ -35,6 +36,7 @@ export const ScriptComponent = ({
<CodeEditor
id={name!}
data-testid={name}
isReadOnly={isDisabled}
type="text"
onChange={onChange}
code={value}

View file

@ -11,6 +11,7 @@ export const StringComponent = ({
label,
helpText,
defaultValue,
isDisabled = false,
}: ComponentProps) => {
const { t } = useTranslation("dynamic");
const { register } = useFormContext();
@ -26,6 +27,7 @@ export const StringComponent = ({
<TextInput
id={name!}
data-testid={name}
isDisabled={isDisabled}
ref={register()}
type="text"
name={`config.${name}`}

View file

@ -12,7 +12,10 @@ import { MultiValuedStringComponent } from "./MultivaluedStringComponent";
import { MultiValuedListComponent } from "./MultivaluedListComponent";
import { GroupComponent } from "./GroupComponent";
export type ComponentProps = Omit<ConfigPropertyRepresentation, "type">;
export type ComponentProps = Omit<ConfigPropertyRepresentation, "type"> & {
isDisabled?: boolean;
};
const ComponentTypes = [
"String",
"boolean",

View file

@ -41,6 +41,11 @@ export type FormAccessProps = FormProps & {
* @type {boolean}
*/
unWrap?: boolean;
/**
* Overwrite the fineGrainedAccess and make form regardless of access rights.
*/
isReadOnly?: boolean;
};
/**
@ -51,6 +56,7 @@ export const FormAccess: FunctionComponent<FormAccessProps> = ({
children,
role,
fineGrainedAccess = false,
isReadOnly = false,
unWrap = false,
...rest
}) => {
@ -107,7 +113,7 @@ export const FormAccess: FunctionComponent<FormAccessProps> = ({
});
};
const isDisabled = !hasAccess(role) && !fineGrainedAccess;
const isDisabled = isReadOnly || (!hasAccess(role) && !fineGrainedAccess);
return (
<>

View file

@ -24,7 +24,6 @@ import type ClientProfileRepresentation from "@keycloak/keycloak-admin-client/li
import { ClientProfileParams, toClientProfile } from "./routes/ClientProfile";
import { DynamicComponents } from "../components/dynamic/DynamicComponents";
import type { ExecutorParams } from "./routes/Executor";
import { convertToFormValues } from "../util";
type ExecutorForm = {
config: object;
@ -57,28 +56,27 @@ export default function ExecutorForm() {
ClientProfileRepresentation[]
>([]);
const [profiles, setProfiles] = useState<ClientProfileRepresentation[]>([]);
const form = useForm({ defaultValues });
const { control, setValue, handleSubmit } = form;
const form = useForm({ defaultValues, shouldUnregister: false });
const { control, reset, handleSubmit } = form;
const editMode = !!executorName;
const setupForm = (profiles: ClientProfileRepresentation[]) => {
const profile = profiles.find((profile) => profile.name === profileName);
const executor = profile?.executors?.find(
(executor) => executor.executor === executorName
);
if (executor) reset({ config: executor.configuration });
};
useFetch(
() =>
adminClient.clientPolicies.listProfiles({ includeGlobalProfiles: true }),
(profiles) => {
setGlobalProfiles(profiles.globalProfiles ?? []);
setProfiles(profiles.profiles ?? []);
setGlobalProfiles(profiles.globalProfiles!);
setProfiles(profiles.profiles!);
const profile = profiles.profiles!.find(
(profile) => profile.name === profileName
);
const profileExecutor = profile?.executors!.find(
(executor) => executor.executor === executorName
);
if (profileExecutor) {
convertToFormValues(profileExecutor, setValue);
}
setupForm(profiles.profiles!);
setupForm(profiles.globalProfiles!);
},
[]
);
@ -163,7 +161,12 @@ export default function ExecutorForm() {
divider
/>
<PageSection variant="light">
<FormAccess isHorizontal role="manage-realm" className="pf-u-mt-lg">
<FormAccess
isHorizontal
role="manage-realm"
className="pf-u-mt-lg"
isReadOnly={!!globalProfile}
>
<FormGroup
label={t("executorType")}
fieldId="kc-executorType"