-
Notifications
You must be signed in to change notification settings - Fork 64
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(ESSNTL-3729): added new actions to kebab and new modal #1794
Merged
Merged
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
b5a7a90
feat(ESSNTL-3729): added new actions to dropdown and new modal
Fewwy 1042318
feat(inventory groups): added filter for search and another modal
Fewwy ff2a819
Merge remote-tracking branch 'upstream/master' into essntl-3729
Fewwy b042b27
feat(inventory groups): fixed jest test
Fewwy 1589da8
feat(inventory groups): fixing jest test
Fewwy 4a5e055
feat(inventorygroups): fixed couple issues and passed props to modal
Fewwy bbe538a
feat(inventory groups): fixed pr after review
Fewwy cd8f0f2
feat(inventory groups): add conditional render for the remove group m…
Fewwy f11065a
Merge remote-tracking branch 'upstream/master' into essntl-3729
Fewwy 38596b5
Merge remote-tracking branch 'upstream/master' into essntl-3729
Fewwy 7bd6ebc
feat(inventory groups): fixes and improvements
Fewwy 9904c68
Merge branch 'master' into essntl-3729
Fewwy 96d3720
Attempt to fix SearchInput
gkarat File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
92 changes: 92 additions & 0 deletions
92
src/components/InventoryGroups/Modals/AddHostToGroupModal.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
import React, { useEffect, useState } from 'react'; | ||
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 { CreateGroupButton } from '../SmallComponents/CreateGroupButton'; | ||
import SearchInput from './SearchInput'; | ||
import { fetchGroups } from '../../../store/inventory-actions'; | ||
import { addHostSchema } from './ModalSchemas/schemes'; | ||
import CreateGroupModal from './CreateGroupModal'; | ||
|
||
const AddHostToGroupModal = ({ | ||
isModalOpen, | ||
setIsModalOpen, | ||
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 handleAddDevices = (values) => { | ||
const { group } = values; | ||
const statusMessages = { | ||
onSuccess: { | ||
title: 'Success', | ||
description: `System(s) have been added to ${group.toString()} successfully` | ||
}, | ||
onError: { title: 'Error', description: `Failed to add ${modalState.name} to ${modalState.groupName}` } | ||
}; | ||
|
||
apiWithToast( | ||
dispatch, | ||
() => addHostToGroup(group.groupId, modalState.id), | ||
statusMessages | ||
); | ||
}; | ||
|
||
return ( | ||
<> | ||
<Modal | ||
isModalOpen={isModalOpen} | ||
closeModal={() => setIsModalOpen(false)} | ||
title="Add to group" | ||
submitLabel="Add" | ||
schema={addHostSchema(modalState.name)} | ||
additionalMappers={{ | ||
'search-input': { | ||
component: SearchInput | ||
}, | ||
'create-group-btn': { | ||
component: CreateGroupButton, | ||
closeModal: () => { | ||
setIsCreateGroupModalOpen(true); | ||
setIsModalOpen(false); | ||
} | ||
} | ||
}} | ||
initialValues={modalState} | ||
onSubmit={handleAddDevices} | ||
reloadData={reloadData} | ||
/> | ||
{isCreateGroupModalOpen && ( | ||
<CreateGroupModal | ||
isModalOpen={isCreateGroupModalOpen} | ||
setIsModalOpen={setIsCreateGroupModalOpen} | ||
reloadData={() => console.log('data reloaded')} | ||
/> | ||
)} | ||
</> | ||
); | ||
}; | ||
|
||
AddHostToGroupModal.propTypes = { | ||
modalState: PropTypes.shape({ | ||
id: PropTypes.string, | ||
name: PropTypes.string, | ||
groupName: PropTypes.string | ||
}), | ||
isModalOpen: PropTypes.bool, | ||
setIsModalOpen: PropTypes.func, | ||
reloadData: PropTypes.func, | ||
setIsCreateGroupModalOpen: PropTypes.func, | ||
deviceIds: PropTypes.array | ||
}; | ||
|
||
export default AddHostToGroupModal; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
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 ( | ||
<> | ||
<HelperText> | ||
{!isLoading && !selected && isOpen && selectOptions.length ? ( | ||
<HelperTextItem variant="warning" className="pf-u-font-weight-bold"> | ||
Over {selectOptions.length} results found. Refine your search. | ||
</HelperTextItem> | ||
) : ( | ||
<HelperTextItem className="pf-u-font-weight-bold"> | ||
Select a group | ||
</HelperTextItem> | ||
)} | ||
</HelperText> | ||
<Select | ||
variant="typeahead" | ||
typeAheadAriaLabel="Select a group" | ||
onToggle={onToggle} | ||
onSelect={onSelect} | ||
onClear={clearSelection} | ||
selections={selected ? selected : searchTerm} | ||
isOpen={isOpen} | ||
aria-labelledby="typeahead-select-id-1" | ||
placeholderText="Type or click to select a group" | ||
isInputValuePersisted={true} | ||
maxHeight={'180px'} | ||
> | ||
{selectOptions.length === 0 | ||
? [] | ||
: selectOptions.map(({ DeviceGroup }) => ( | ||
<SelectOption | ||
key={DeviceGroup.ID} | ||
value={{ | ||
toString: () => DeviceGroup.Name, | ||
groupId: DeviceGroup.ID | ||
}} | ||
{...(DeviceGroup.description && { | ||
description: DeviceGroup.description | ||
})} | ||
/> | ||
))} | ||
</Select> | ||
</> | ||
); | ||
}; | ||
|
||
export default SearchInput; |
26 changes: 26 additions & 0 deletions
26
src/components/InventoryGroups/Modals/__tests__/SearchInput.test.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
/* 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(<Provider store={store}><SearchInput /></Provider>); | ||
fireEvent.click(screen.getByRole('textbox', { placeholder: 'Type or click to select a group' })); | ||
const options = await screen.findAllByRole('option'); | ||
expect(options).toHaveLength(1); | ||
}); | ||
}); |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This component makes some really multiple and ineffecient computations. I can see with profiler that the component and the list is re-rendered too many times (has constantly brown edges). Also the behavior is with the huge delays (it's well seen on the video)
Screencast.from.03-29-2023.11.23.18.AM.webm