From 1be088d2b2b28963ee7f92772ac2dc7a115aea02 Mon Sep 17 00:00:00 2001 From: mgold1234 Date: Sun, 7 Jan 2024 21:34:25 +0200 Subject: [PATCH] Oscap: change oscap file to typeSript file this commit convert oscap file from JavaScript to TypeScript --- .../CreateImageWizard/formComponents/Oscap.js | 265 ------------- .../formComponents/Oscap.tsx | 372 ++++++++++++++++++ 2 files changed, 372 insertions(+), 265 deletions(-) delete mode 100644 src/Components/CreateImageWizard/formComponents/Oscap.js create mode 100644 src/Components/CreateImageWizard/formComponents/Oscap.tsx diff --git a/src/Components/CreateImageWizard/formComponents/Oscap.js b/src/Components/CreateImageWizard/formComponents/Oscap.js deleted file mode 100644 index 8a74eb6e7..000000000 --- a/src/Components/CreateImageWizard/formComponents/Oscap.js +++ /dev/null @@ -1,265 +0,0 @@ -import React, { useEffect, useState } from 'react'; - -import useFieldApi from '@data-driven-forms/react-form-renderer/use-field-api'; -import useFormApi from '@data-driven-forms/react-form-renderer/use-form-api'; -import { - Alert, - FormGroup, - Spinner, - Popover, - TextContent, - Text, - Button, -} from '@patternfly/react-core'; -import { - Select, - SelectOption, - SelectVariant, -} from '@patternfly/react-core/deprecated'; -import { HelpIcon } from '@patternfly/react-icons'; -import PropTypes from 'prop-types'; - -import OscapProfileInformation from './OscapProfileInformation'; - -import { - useGetOscapCustomizationsQuery, - useGetOscapProfilesQuery, -} from '../../../store/imageBuilderApi'; -import { reinitFileSystemConfiguratioStep } from '../steps/fileSystemConfiguration'; -import { reinitPackagesStep } from '../steps/packages'; - -/** - * Every time there is change on this form step's state, reinitialise the steps - * that are depending on it. This will ensure that if the user goes back and - * change their mind, going forward again leaves them in a coherent and workable - * form state. - */ -const reinitDependingSteps = (change) => { - reinitFileSystemConfiguratioStep(change); - reinitPackagesStep(change); -}; - -/** - * Component for the user to select the profile to apply to their image. - * The selected profile will be stored in the `oscap-profile` form state variable. - * The Component is shown or not depending on the ShowSelector variable. - */ -const ProfileSelector = ({ input }) => { - const { change, getState } = useFormApi(); - const [profile, setProfile] = useState(getState()?.values?.['oscap-profile']); - const [profileName, setProfileName] = useState(''); - const [isOpen, setIsOpen] = useState(false); - const { - data: profiles, - isFetching, - isSuccess, - isError, - refetch, - } = useGetOscapProfilesQuery({ - distribution: getState()?.values?.['release'], - }); - - const { data } = useGetOscapCustomizationsQuery( - { - distribution: getState()?.values?.['release'], - profile: profile, - }, - { - skip: !profile, - } - ); - useEffect(() => { - if (data) { - setProfileName(data?.openscap?.profile_name); - } - }, [data]); - - const handleToggle = () => { - if (!isOpen) { - refetch(); - } - setIsOpen(!isOpen); - }; - - const handleClear = () => { - setProfile(undefined); - change(input.name, undefined); - setProfileName(undefined); - reinitDependingSteps(change); - }; - - const handleSelect = (_, selection) => { - setProfile(selection); - setIsOpen(false); - change(input.name, selection); - reinitDependingSteps(change); - change('file-system-config-radio', 'manual'); - }; - - return ( - - OpenSCAP profile - - - To run a manual compliance scan in OpenSCAP, download this - image. - - - } - > - - - - } - > - - {isError && ( - - Cannot get the list of profiles - - )} - - ); -}; - -const OScapNoneOption = ({ setProfileName }) => { - return ( - { - setProfileName('None'); - }} - > -

{'None'}

-
- ); -}; - -OScapNoneOption.propTypes = { - setProfileName: PropTypes.any, -}; - -const OScapSelectOption = ({ profile_id, setProfileName, input }) => { - const { getState } = useFormApi(); - const { data } = useGetOscapCustomizationsQuery({ - distribution: getState()?.values?.['release'], - profile: profile_id, - }); - if ( - input && - !data?.openscap?.profile_name.toLowerCase().includes(input.toLowerCase()) - ) { - return null; - } - - return ( - { - setProfileName(data?.openscap?.profile_name); - }} - > -

{data?.openscap?.profile_name}

-
- ); -}; - -OScapSelectOption.propTypes = { - profile_id: PropTypes.string, - setProfileName: PropTypes.any, - input: PropTypes.string, -}; - -ProfileSelector.propTypes = { - input: PropTypes.any, - showSelector: PropTypes.bool, -}; - -/** - * Component to prompt the use with two choices: - * - to add a profile, in which case the ProfileSelector will allow the user to - * pick a profile to be stored in the `oscap-profile` variable. - * - to not add a profile, in which case the `oscap-profile` form state goes - * undefined. - */ -const AddProfile = ({ input }) => { - return ( - <> - - - - ); -}; - -AddProfile.propTypes = { - input: PropTypes.object, -}; - -export const Oscap = ({ ...props }) => { - const { input } = useFieldApi(props); - return ; -}; diff --git a/src/Components/CreateImageWizard/formComponents/Oscap.tsx b/src/Components/CreateImageWizard/formComponents/Oscap.tsx new file mode 100644 index 000000000..3b6e8e036 --- /dev/null +++ b/src/Components/CreateImageWizard/formComponents/Oscap.tsx @@ -0,0 +1,372 @@ +import React, {FormEvent, useEffect, useState} from 'react'; + +import useFieldApi, {UseFieldApiConfig} from '@data-driven-forms/react-form-renderer/use-field-api'; +import useFormApi from '@data-driven-forms/react-form-renderer/use-form-api'; +import { + Alert, + FormGroup, + Spinner, + Popover, + TextContent, + Text, + Button, + Select, + SelectOption, + MenuToggleElement, + TextInputGroup, + TextInputGroupMain, + MenuToggle, + TextInputGroupUtilities, + SelectOptionProps, +} from '@patternfly/react-core'; +import { SelectVariant } from '@patternfly/react-core/deprecated' +import { HelpIcon } from '@patternfly/react-icons'; +import PropTypes from 'prop-types'; + +import OscapProfileInformation from './OscapProfileInformation'; + +import { + DistributionProfileItem, + useGetOscapCustomizationsQuery, + useGetOscapProfilesQuery, +} from '../../../store/imageBuilderApi'; +import { reinitFileSystemConfiguratioStep } from '../steps/fileSystemConfiguration'; +import { reinitPackagesStep } from '../steps/packages'; + +/** + * Every time there is change on this form step's state, reinitialise the steps + * that are depending on it. This will ensure that if the user goes back and + * change their mind, going forward again leaves them in a coherent and workable + * form state. + */ +const reinitDependingSteps = (change: any) => { + reinitFileSystemConfiguratioStep(change); + reinitPackagesStep(change); +}; + +/** + * Component for the user to select the profile to apply to their image. + * The selected profile will be stored in the `oscap-profile` form state variable. + * The Component is shown or not depending on the ShowSelector variable. + */ +const ProfileSelector =({ input }: { input: any }) => { + const { change, getState } = useFormApi(); + const [profile, setProfile] = useState(getState()?.values?.['oscap-profile']); + const [profileName, setProfileName] = useState(''); + const [filterValue, setFilterValue] = React.useState(''); + const [selected, setSelected] = React.useState(''); + const [isOpen, setIsOpen] = useState(false); + const [inputValue, setInputValue] = React.useState(''); + const [selectOptions, setSelectOptions] = React.useState([]); + const [focusedItemIndex, setFocusedItemIndex] = React.useState(null); + const [activeItem, setActiveItem] = React.useState(null); + const textInputRef = React.useRef(); + const { + data: profiles, + isFetching, + isSuccess, + isError, + refetch, + } = useGetOscapProfilesQuery({ + distribution: getState()?.values?.['release'], + }); + + const { data } = useGetOscapCustomizationsQuery( + { + distribution: getState()?.values?.['release'], + profile: profile, + }, + { + skip: !profile, + } + ); + + useEffect(() => { + if (data && data.openscap && typeof data.openscap.profile_name === 'string') { + setProfileName(data.openscap.profile_name); + } else { + setProfileName(''); + } + }, [data]); + + const handleToggle = () => { + if (!isOpen) { + refetch(); + } + setIsOpen(!isOpen); + }; + + const handleClear = () => { + setProfile(undefined); + change(input.name, undefined); + setProfileName(undefined); + reinitDependingSteps(change); + }; + + const handleSelect =(_: any, selection: string) => { + setProfile(selection); + setIsOpen(false); + change(input.name, selection); + reinitDependingSteps(change); + change('file-system-config-radio', 'manual'); + }; + + const onTextInputChange = (_event: React.FormEvent, value: string) => { + setInputValue(value); + setFilterValue(value); + }; + + + const handleMenuArrowKeys = (key: string) => { + let indexToFocus; + + if (isOpen) { + if (key === 'ArrowUp') { + // When no index is set or at the first index, focus to the last, otherwise decrement focus index + if (focusedItemIndex === null || focusedItemIndex === 0) { + indexToFocus = selectOptions.length - 1; + } else { + indexToFocus = focusedItemIndex - 1; + } + } + + if (key === 'ArrowDown') { + // When no index is set or at the last index, focus to the first, otherwise increment focus index + if (focusedItemIndex === null || focusedItemIndex === selectOptions.length - 1) { + indexToFocus = 0; + } else { + indexToFocus = focusedItemIndex + 1; + } + } + + setFocusedItemIndex(indexToFocus); + const focusedItem = selectOptions.filter((option) => !option.isDisabled)[indexToFocus]; + setActiveItem(`select-typeahead-${focusedItem.value.replace(' ', '-')}`); + } + }; + + + const onInputKeyDown = (event: React.KeyboardEvent) => { + const enabledMenuItems = selectOptions.filter((option) => !option.isDisabled); + const [firstMenuItem] = enabledMenuItems; + const focusedItem = focusedItemIndex ? enabledMenuItems[focusedItemIndex] : firstMenuItem; + + switch (event.key) { + // Select the first available option + case 'Enter': + if (isOpen && focusedItem.value !== 'no results') { + setInputValue(String(focusedItem.children)); + setFilterValue(''); + setSelected(String(focusedItem.children)); + } + + setIsOpen((prevIsOpen) => !prevIsOpen); + setFocusedItemIndex(null); + setActiveItem(null); + + break; + case 'Tab': + case 'Escape': + setIsOpen(false); + setActiveItem(null); + break; + case 'ArrowUp': + case 'ArrowDown': + event.preventDefault(); + handleMenuArrowKeys(event.key); + break; + } + }; + + const toggle = (toggleRef: React.Ref) => ( + + + + + + {!!inputValue && ( + + )} + + + + ); + + return ( + + OpenSCAP profile + + + To run a manual compliance scan in OpenSCAP, download this + image. + + + } + > + + + + } + > + + + {isError && ( + + Cannot get the list of profiles + + )} + + ); +}; + +const OScapNoneOption = ({ setProfileName }: { setProfileName: (name: string) => void }) => { + return ( + { + setProfileName('None'); + }} + > +

{'None'}

+
+ ); +}; + + +const OScapSelectOption = ({ profile_id, setProfileName, input }: { profile_id: DistributionProfileItem; setProfileName: (name: string)=> void; input?: string }) => { + const { getState } = useFormApi(); + const { data } = useGetOscapCustomizationsQuery({ + distribution: getState()?.values?.['release'], + profile: profile_id, + }); + if (input) { + const profileName = data?.openscap?.profile_name; + if (profileName && !profileName.toLowerCase().includes(input.toLowerCase())) { + return null; + } + } + + return ( + { + setProfileName(data?.openscap?.profile_name || 'Default Value'); + }} + > +

{data?.openscap?.profile_name}

+
+ ); +}; + +/** + * Component to prompt the use with two choices: + * - to add a profile, in which case the ProfileSelector will allow the user to + * pick a profile to be stored in the `oscap-profile` variable. + * - to not add a profile, in which case the `oscap-profile` form state goes + * undefined. + */ +const AddProfile =({ input }: { input: any }) => { + return ( + <> + + + + ); +}; + + +interface OscapProps extends UseFieldApiConfig { + +} + +export const Oscap = (props: OscapProps) => { + const { input } = useFieldApi(props); + return ; +};