Skip to content

Commit

Permalink
Add functionality for Selectors
Browse files Browse the repository at this point in the history
The Select[1] components need to be adapted to
take data from the metadata. This will be
applied for the fields 'Radius proxy configuration'
and 'External IdP configuration'.

[1]- http://v4-archive.patternfly.org/v4/components/select
Signed-off-by: Carla Martinez <[email protected]>
  • Loading branch information
carma12 committed Aug 29, 2023
1 parent dfc98d5 commit c4a806b
Show file tree
Hide file tree
Showing 11 changed files with 235 additions and 80 deletions.
82 changes: 82 additions & 0 deletions src/components/Form/IpaSelect.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import React from "react";
// PatternFly
import {
Select,
SelectOption,
SelectOptionObject,
SelectVariant,
} from "@patternfly/react-core";
// Utils
import {
IPAParamDefinition,
getParamProperties,
} from "src/utils/ipaObjectUtils";
import { updateIpaObject } from "src/utils/userUtils";
import { NO_SELECTION_OPTION } from "src/utils/constUtils";

interface IPAParamDefinitionSelect extends IPAParamDefinition {
id?: string;
value?: string;
setIpaObject?: (ipaObject: Record<string, unknown>) => void;
variant?: "single" | "checkbox" | "typeahead" | "typeaheadmulti";
options: string[];
selections?: string | SelectOptionObject | (string | SelectOptionObject)[];
ariaLabelledBy?: string;
}

const IpaSelect = (props: IPAParamDefinitionSelect) => {
// Obtains the metadata of the parameter
const { required, readOnly, value } = getParamProperties(props);

// Handle selected value
let valueSelected = NO_SELECTION_OPTION;
if (value !== undefined && value && value !== "") {
valueSelected = value.toString();
}

const ipaObject = props.ipaObject || {};

const [isOpen, setIsOpen] = React.useState(false);

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const onSelect = (selection: any) => {
let valueToUpdate = "";

if (selection.target.textContent !== NO_SELECTION_OPTION) {
valueToUpdate = selection.target.textContent as string;
}

if (ipaObject && props.setIpaObject !== undefined) {
updateIpaObject(ipaObject, props.setIpaObject, valueToUpdate, props.name);
}

setIsOpen(false);
};

// Provide empty option at the beginning of the list
const optionsToSelect: string[] = [...(props.options || [])];
optionsToSelect.unshift(NO_SELECTION_OPTION);

return (
<Select
id={props.id}
name={props.name}
variant={props.variant || SelectVariant.single}
aria-label={props.name}
onToggle={setIsOpen}
onSelect={onSelect}
selections={valueSelected}
isOpen={isOpen}
aria-labelledby={props.ariaLabelledBy || props.id}
readOnly={readOnly}
isDisabled={readOnly}
required={required}
>
{optionsToSelect.map((option, index) => {
return <SelectOption key={index} value={option} />;
})}
</Select>
);
};

export default IpaSelect;
11 changes: 10 additions & 1 deletion src/components/UserSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@ import {
// Icons
import OutlinedQuestionCircleIcon from "@patternfly/react-icons/dist/esm/icons/outlined-question-circle-icon";
// Data types
import { Metadata, User } from "src/utils/datatypes/globalDataTypes";
import {
Metadata,
User,
IDPServer,
RadiusServer,
} from "src/utils/datatypes/globalDataTypes";
// Layouts
import ToolbarLayout from "src/components/layouts/ToolbarLayout";
import TitleLayout from "src/components/layouts/TitleLayout";
Expand Down Expand Up @@ -53,6 +58,8 @@ export interface PropsToUserSettings {
isDataLoading?: boolean;
modifiedValues: () => Partial<User>;
onResetValues: () => void;
radiusProxyData?: RadiusServer[];
idpData?: IDPServer[];
from: "active-users" | "stage-users" | "preserved-users";
}

Expand Down Expand Up @@ -254,6 +261,8 @@ const UserSettings = (props: PropsToUserSettings) => {
onUserChange={props.onUserChange}
metadata={props.metadata}
onRefresh={props.onRefresh}
radiusProxyConf={props.radiusProxyData || []}
idpConf={props.idpData || []}
/>
<TitleLayout
key={2}
Expand Down
101 changes: 32 additions & 69 deletions src/components/UsersSections/UsersAccountSettings.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import React, { useState } from "react";
import React, { useEffect, useState } from "react";
// PatternFly
import {
Flex,
Expand All @@ -8,15 +8,17 @@ import {
FormGroup,
TextInput,
Checkbox,
Select,
SelectVariant,
SelectOption,
DropdownItem,
CalendarMonth,
Button,
} from "@patternfly/react-core";
// Data types
import { IDPServer, Metadata, User } from "src/utils/datatypes/globalDataTypes";
import {
IDPServer,
Metadata,
User,
RadiusServer,
} from "src/utils/datatypes/globalDataTypes";
// Layouts
import SecondaryButton from "src/components/layouts/SecondaryButton";
import DataTimePickerLayout from "src/components/layouts/Calendar/DataTimePickerLayout";
Expand All @@ -30,12 +32,15 @@ import CertificateMappingDataModal from "src/components/modals/CertificateMappin
import { asRecord } from "src/utils/userUtils";
// Form
import IpaTextInput from "src/components/Form/IpaTextInput";
import IpaSelect from "../Form/IpaSelect";

interface PropsToUsersAccountSettings {
user: Partial<User>;
onUserChange: (element: Partial<User>) => void;
metadata: Metadata;
onRefresh: () => void;
radiusProxyConf: RadiusServer[];
idpConf: IDPServer[];
}

// Generic data to pass to the Textbox adder
Expand All @@ -54,6 +59,14 @@ const UsersAccountSettings = (props: PropsToUsersAccountSettings) => {
props.onUserChange
);

// Dropdown 'Radius proxy configuration'
const radiusProxyList = props.radiusProxyConf.map((item) =>
item.cn.toString()
);

// Dropdown 'External IdP configuration'
const idpConfOptions = props.idpConf.map((item) => item.cn.toString());

const [principalAliasList, setPrincipalAliasList] = useState<ElementData[]>([
{
id: 0,
Expand Down Expand Up @@ -415,38 +428,6 @@ const UsersAccountSettings = (props: PropsToUsersAccountSettings) => {
/>
);

// Dropdown 'Radius proxy configuration'
const [isRadiusConfOpen, setIsRadiusConfOpen] = useState(false);
const [radiusConfSelected, setRadiusConfSelected] = useState("");
const radiusConfOptions = [
{ value: "Option 1", disabled: false },
{ value: "Option 2", disabled: false },
{ value: "Option 3", disabled: false },
];
const radiusConfOnToggle = (isOpen: boolean) => {
setIsRadiusConfOpen(isOpen);
};

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const radiusConfOnSelect = (selection: any) => {
setRadiusConfSelected(selection.target.textContent);
setIsRadiusConfOpen(false);
};

// Dropdown 'External IdP configuration'
const [isIdpConfOpen, setIsIdpConfOpen] = useState(false);
const [idpConfSelected, setIdpConfSelected] = useState("");
const [idpConfOptions] = useState<IDPServer[]>([]);

const idpConfOnToggle = (isOpen: boolean) => {
setIsIdpConfOpen(isOpen);
};

const idpConfOnSelect = (selection: any) => {
setIdpConfSelected(selection.target.textContent);
setIsIdpConfOpen(false);
};

// Messages for the popover
const certificateMappingDataMessage = () => (
<div>
Expand Down Expand Up @@ -703,26 +684,15 @@ const UsersAccountSettings = (props: PropsToUsersAccountSettings) => {
label="Radius proxy configuration"
fieldId="radius-proxy-configuration"
>
<Select
<IpaSelect
id="radius-proxy-configuration"
name="ipatokenradiusconfiglink"
variant={SelectVariant.single}
placeholderText=" "
aria-label="Select Input with descriptions"
onToggle={radiusConfOnToggle}
onSelect={radiusConfOnSelect}
selections={radiusConfSelected}
isOpen={isRadiusConfOpen}
aria-labelledby="radius-proxy-conf"
>
{radiusConfOptions.map((option, index) => (
<SelectOption
isDisabled={option.disabled}
key={index}
value={option.value}
/>
))}
</Select>
options={radiusProxyList}
ipaObject={ipaObject}
setIpaObject={recordOnChange}
objectName="user"
metadata={props.metadata}
/>
</FormGroup>
<FormGroup
label="Radius proxy username"
Expand All @@ -740,22 +710,15 @@ const UsersAccountSettings = (props: PropsToUsersAccountSettings) => {
label="External IdP configuration"
fieldId="external-idp-configuration"
>
<Select
<IpaSelect
id="external-idp-configuration"
name="ipaidpconfiglink"
variant={SelectVariant.single}
placeholderText=" "
aria-label="Select Input with descriptions"
onToggle={idpConfOnToggle}
onSelect={idpConfOnSelect}
selections={idpConfSelected}
isOpen={isIdpConfOpen}
aria-labelledby="external-idp-conf"
>
{idpConfOptions.map((option, index) => (
<SelectOption key={index} value={option.cn} />
))}
</Select>
options={idpConfOptions}
ipaObject={ipaObject}
setIpaObject={recordOnChange}
objectName="user"
metadata={props.metadata}
/>
</FormGroup>
<FormGroup
label="External IdP user identifier"
Expand Down
34 changes: 31 additions & 3 deletions src/hooks/useUserSettingsData.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,18 @@ import { useState, useEffect } from "react";

// RPC
import {
useGetIdpServerQuery,
useGetObjectMetadataQuery,
useGetRadiusProxyQuery,
useGetUsersFullDataQuery,
} from "src/services/rpc";

import { Metadata, User } from "src/utils/datatypes/globalDataTypes";
// Data types
import {
IDPServer,
Metadata,
RadiusServer,
User,
} from "src/utils/datatypes/globalDataTypes";

type UserSettingsData = {
isLoading: boolean;
Expand All @@ -22,6 +29,8 @@ type UserSettingsData = {
certData?: Record<string, unknown>;
refetch: () => void;
modifiedValues: () => Partial<User>;
radiusServers: RadiusServer[];
idpServers: IDPServer[];
};

const useUserSettingsData = (userId: string): UserSettingsData => {
Expand All @@ -30,27 +39,45 @@ const useUserSettingsData = (userId: string): UserSettingsData => {
const metadata = metadataQuery.data || {};
const metadataLoading = metadataQuery.isLoading;

// [API call] User
const userFullDataQuery = useGetUsersFullDataQuery(userId);
const userFullData = userFullDataQuery.data;
const isFullDataLoading = userFullDataQuery.isLoading;

// [API call] RADIUS proxy server
const radiusProxyQuery = useGetRadiusProxyQuery();
const radiusProxyData = radiusProxyQuery.data;
const isRadiusProxyLoading = radiusProxyQuery.isLoading;

// [API call] IdP server
const idpQuery = useGetIdpServerQuery();
const idpData = idpQuery.data;
const isIdpLoading = idpQuery.isLoading;

const [modified, setModified] = useState(false);

// Data displayed and modified by the user
const [user, setUser] = useState<Partial<User>>({});

useEffect(() => {
if (userFullData && !userFullDataQuery.isFetching) {
setUser({ ...userFullData.user });
}
}, [userFullData, userFullDataQuery.isFetching]);

const settingsData = {
isLoading: metadataLoading || isFullDataLoading,
isLoading:
metadataLoading ||
isFullDataLoading ||
isRadiusProxyLoading ||
isIdpLoading,
isFetching: userFullDataQuery.isFetching,
modified,
metadata,
user,
setUser,
radiusServers: radiusProxyData || [],
idpServers: idpData || [],
refetch: userFullDataQuery.refetch,
} as UserSettingsData;

Expand Down Expand Up @@ -78,6 +105,7 @@ const useUserSettingsData = (userId: string): UserSettingsData => {
};
settingsData.modifiedValues = getModifiedValues;

// Detect any change in 'originalUser' and 'user' objects
useEffect(() => {
if (!userFullData || !userFullData.user) {
return;
Expand Down
2 changes: 2 additions & 0 deletions src/pages/ActiveUsers/ActiveUsersTabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ const ActiveUsersTabs = () => {
isModified={userSettingsData.modified}
onResetValues={userSettingsData.resetValues}
modifiedValues={userSettingsData.modifiedValues}
radiusProxyData={userSettingsData.radiusServers}
idpData={userSettingsData.idpServers}
from="active-users"
/>
</Tab>
Expand Down
Loading

0 comments on commit c4a806b

Please sign in to comment.