diff --git a/src/components/InventoryGroups/Modals/AddHostToGroupModal.js b/src/components/InventoryGroups/Modals/AddHostToGroupModal.js index 0d8dc8eb5..36ea5541d 100644 --- a/src/components/InventoryGroups/Modals/AddHostToGroupModal.js +++ b/src/components/InventoryGroups/Modals/AddHostToGroupModal.js @@ -3,9 +3,8 @@ import PropTypes from 'prop-types'; import Modal from './Modal'; import { addHostToGroup } from '../utils/api'; import apiWithToast from '../utils/apiWithToast'; -import { useDispatch } from 'react-redux'; +import { useDispatch, useSelector } from 'react-redux'; import { CreateGroupButton } from '../SmallComponents/CreateGroupButton'; -import SearchInput from './SearchInput'; import { fetchGroups } from '../../../store/inventory-actions'; import { addHostSchema } from './ModalSchemas/schemes'; import CreateGroupModal from './CreateGroupModal'; @@ -16,22 +15,24 @@ const AddHostToGroupModal = ({ modalState, reloadData }) => { + const dispatch = useDispatch(); //we have to fetch groups to make them available in state useEffect(() => { dispatch(fetchGroups()); - }, []); - const [isCreateGroupModalOpen, setIsCreateGroupModalOpen] = useState(false); + const groups = useSelector(({ groups }) => groups?.data?.results); + + const [isCreateGroupModalOpen, setIsCreateGroupModalOpen] = useState(false); const handleAddDevices = (values) => { const { group } = values; const statusMessages = { onSuccess: { title: 'Success', - description: `System(s) have been added to ${group.toString()} successfully` + description: `System(s) have been added to ${group.name} successfully` }, - onError: { title: 'Error', description: `Failed to add ${modalState.name} to ${modalState.groupName}` } + onError: { title: 'Error', description: `Failed to add ${modalState.name} to ${group.name}` } }; apiWithToast( @@ -48,11 +49,8 @@ const AddHostToGroupModal = ({ closeModal={() => setIsModalOpen(false)} title="Add to group" submitLabel="Add" - schema={addHostSchema(modalState.name)} + schema={addHostSchema(modalState.name, groups)} additionalMappers={{ - 'search-input': { - component: SearchInput - }, 'create-group-btn': { component: CreateGroupButton, closeModal: () => { @@ -70,6 +68,10 @@ const AddHostToGroupModal = ({ isModalOpen={isCreateGroupModalOpen} setIsModalOpen={setIsCreateGroupModalOpen} reloadData={() => console.log('data reloaded')} + //modal before prop tells create group modal that it should + //reopen add host modal when user closes create group modal + modalBefore={true} + setterOfModalBefore={setIsModalOpen} /> )} diff --git a/src/components/InventoryGroups/Modals/CreateGroupModal.js b/src/components/InventoryGroups/Modals/CreateGroupModal.js index 48dd78803..678e20d68 100644 --- a/src/components/InventoryGroups/Modals/CreateGroupModal.js +++ b/src/components/InventoryGroups/Modals/CreateGroupModal.js @@ -13,7 +13,9 @@ import awesomeDebouncePromise from 'awesome-debounce-promise'; const CreateGroupModal = ({ isModalOpen, setIsModalOpen, - reloadData + reloadData, + modalBefore = false, + setterOfModalBefore }) => { const dispatch = useDispatch(); @@ -46,10 +48,19 @@ const CreateGroupModal = ({ return createGroupSchema(d); }, []); + const onClose = () => { + if (modalBefore) { + setIsModalOpen(false); + setterOfModalBefore(true); + } else { + setIsModalOpen(false); + } + }; + return ( setIsModalOpen(false)} + closeModal={onClose} title="Create group" submitLabel="Create" schema={schema} @@ -64,5 +75,7 @@ export default CreateGroupModal; CreateGroupModal.propTypes = { isModalOpen: PropTypes.bool, setIsModalOpen: PropTypes.func, - reloadData: PropTypes.func + reloadData: PropTypes.func, + modalBefore: PropTypes.bool, + setterOfModalBefore: PropTypes.func }; diff --git a/src/components/InventoryGroups/Modals/Modal.js b/src/components/InventoryGroups/Modals/Modal.js index a562a8860..71bcd3300 100644 --- a/src/components/InventoryGroups/Modals/Modal.js +++ b/src/components/InventoryGroups/Modals/Modal.js @@ -52,6 +52,7 @@ const RepoModal = ({ closeModal(); }} onCancel={() => closeModal()} + subscription={{ values: true }} /> ); @@ -70,7 +71,6 @@ RepoModal.propTypes = { size: PropTypes.string, additionalMappers: PropTypes.object, titleIconVariant: PropTypes.any, - validatorMapper: PropTypes.object, customFormTemplate: PropTypes.node }; diff --git a/src/components/InventoryGroups/Modals/ModalSchemas/schemes.js b/src/components/InventoryGroups/Modals/ModalSchemas/schemes.js index 02f6c5512..7d52f187e 100644 --- a/src/components/InventoryGroups/Modals/ModalSchemas/schemes.js +++ b/src/components/InventoryGroups/Modals/ModalSchemas/schemes.js @@ -53,7 +53,7 @@ const createDescription = (systemName) => { //this is a custom schema that is passed via additional mappers to the Modal component //it allows to create custom item types in the modal -export const addHostSchema = (systemName) => ({ +export const addHostSchema = (systemName, groups) => ({ fields: [ { component: componentTypes.PLAIN_TEXT, @@ -61,12 +61,20 @@ export const addHostSchema = (systemName) => ({ label: createDescription(systemName) }, { - component: 'search-input', + component: 'select', name: 'group', label: 'Select a group', + simpleValue: true, + isSearchable: true, // enables typeahead isRequired: true, + isClearable: true, + placeholder: 'Type or click to select a group', + options: (groups || []).map(({ id, name }) => ({ + label: name, + value: { name, id } + })), validate: [{ type: validatorTypes.REQUIRED }] }, - { component: 'create-group-btn', name: 'create-group-btn' } + { component: 'create-group-btn', name: 'create-group-btn', isRequired: true } ] }); diff --git a/src/components/InventoryGroups/Modals/SearchInput.js b/src/components/InventoryGroups/Modals/SearchInput.js deleted file mode 100644 index ee08987c4..000000000 --- a/src/components/InventoryGroups/Modals/SearchInput.js +++ /dev/null @@ -1,113 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import { useSelector } from 'react-redux'; -import { - HelperText, - HelperTextItem, - Select, - SelectOption -} from '@patternfly/react-core'; -import useFormApi from '@data-driven-forms/react-form-renderer/use-form-api'; - -const SearchInput = () => { - const { change } = useFormApi(); - const [isLoading, setIsLoading] = useState(true); - //fetch data from the store - const storeGroups = useSelector(({ groups }) => groups?.data?.results); - - //select options is a constructed array of objects with values for dropdown - const [selectOptions, setSelectOptions] = useState([]); - //when storeGroups is changed - we create selectOptions - useEffect(() => { - setSelectOptions( - (storeGroups || []).reduce((acc, group) => { - acc.push({ - DeviceGroup: { - ID: group.id, - Name: group.name, - UpdatedAt: group.updated_at, - CreatedAt: group.created_at - } - }); - return acc; - }, []) - ); - setIsLoading(false); - }, [storeGroups]); - - const [isOpen, setIsOpen] = useState(false); - const [selected, setSelected] = useState(null); - const [searchTerm, setSearchTerm] = useState(''); - - const onToggle = (isOpen) => { - setIsOpen(isOpen); - }; - - const updateSelection = (value) => { - // Update state when an option has been selected. - setSelected(value); - setIsOpen(false); - //this is requried to make select component pass the saved data up to the modal - change('group', value); - }; - - const clearSelection = () => { - setSearchTerm(''); - updateSelection(null); - setIsOpen(false); - }; - - const onSelect = (_event, selection, isPlaceholder) => { - if (isPlaceholder) { - clearSelection(); - } - else { - updateSelection(selection); - } - }; - - return ( - <> - - {!isLoading && !selected && isOpen && selectOptions.length ? ( - - Over {selectOptions.length} results found. Refine your search. - - ) : ( - - Select a group - - )} - - - - ); -}; - -export default SearchInput; diff --git a/src/components/InventoryGroups/Modals/__tests__/SearchInput.test.js b/src/components/InventoryGroups/Modals/__tests__/SearchInput.test.js deleted file mode 100644 index b5d927ab4..000000000 --- a/src/components/InventoryGroups/Modals/__tests__/SearchInput.test.js +++ /dev/null @@ -1,26 +0,0 @@ -/* eslint-disable camelcase */ - -import React from 'react'; -import { render, screen, fireEvent } from '@testing-library/react'; -import SearchInput from '../SearchInput'; -import { Provider } from 'react-redux'; -import configureStore from 'redux-mock-store'; -import groups from '../../../../../cypress/fixtures/groups.json'; - -describe('SearchInput', () => { - let mockStore; - const initialStore = { - groups - }; - beforeEach(() => { - mockStore = configureStore(); - }); - - test('displays select options when the user clicks on the component', async () => { - const store = mockStore(initialStore); - render(); - fireEvent.click(screen.getByRole('textbox', { placeholder: 'Type or click to select a group' })); - const options = await screen.findAllByRole('option'); - expect(options).toHaveLength(1); - }); -});