From bc39865f357a840f928fb34a93f58ad4c088120c Mon Sep 17 00:00:00 2001 From: Partha Aji Date: Tue, 17 Oct 2023 18:11:41 -0400 Subject: [PATCH] Refs #36793 - Refactored based on suggestions --- .../react_app/components/HostsIndex/index.js | 131 ++++++++++++++++-- .../PF4/TableIndexPage/ActionButtons.js | 59 +++++--- .../PF4/TableIndexPage/ActionKebab.js | 24 +--- .../PF4/TableIndexPage/Table/Table.js | 27 +--- .../PF4/TableIndexPage/Table/TableHooks.js | 7 +- .../PF4/TableIndexPage/TableIndexPage.js | 115 ++++----------- 6 files changed, 201 insertions(+), 162 deletions(-) diff --git a/webpack/assets/javascripts/react_app/components/HostsIndex/index.js b/webpack/assets/javascripts/react_app/components/HostsIndex/index.js index e318b2c4cda9..84e00cdc82f9 100644 --- a/webpack/assets/javascripts/react_app/components/HostsIndex/index.js +++ b/webpack/assets/javascripts/react_app/components/HostsIndex/index.js @@ -1,29 +1,140 @@ import React from 'react'; import { CubeIcon } from '@patternfly/react-icons'; +import { Td } from '@patternfly/react-table'; +import { + Dropdown, + KebabToggle, + DropdownItem, + ToolbarItem, +} from '@patternfly/react-core'; import { translate as __ } from '../../common/I18n'; import TableIndexPage from '../PF4/TableIndexPage/TableIndexPage'; +import { ActionKebab } from '../PF4/TableIndexPage/ActionKebab'; import { HOSTS_API_PATH, API_REQUEST_KEY } from '../../routes/Hosts/constants'; +import { useAPI } from '../../common/hooks/API/APIHooks'; +import { useBulkSelect } from '../PF4/TableIndexPage/Table/TableHooks'; +import SelectAllCheckbox from '../PF4/TableIndexPage/Table/SelectAllCheckbox'; +import { getPageStats } from '../PF4/TableIndexPage/Table/helpers'; +import { STATUS } from '../../constants'; const HostsIndex = () => { const columns = { name: { title: __('Name'), - wrapper: ({ id, name }) => ({name}), + wrapper: ({ id, name }) => {name}, isSorted: true, }, }; + const defaultParams = { search: '' }; // search || + + const response = useAPI( + 'get', + HOSTS_API_PATH.includes('include_permissions') + ? HOSTS_API_PATH + : `${HOSTS_API_PATH}?include_permissions=true`, + { + ...{ key: API_REQUEST_KEY }, + params: defaultParams, + } + ); + + const { + response: { + search: apiSearchQuery, + can_create: canCreate, + results, + total, + per_page: perPage, + page, + subtotal, + message: errorMessage, + }, + status = STATUS.PENDING, + setAPIOptions, + } = response; + + const { pageRowCount } = getPageStats({ total, page, perPage }); + + const { + updateSearchQuery, + fetchBulkParams, + ...selectAllOptions + } = useBulkSelect({ + results, + metadata: {}, + }); + + const { + selectAll, + selectPage, + selectNone, + selectedCount, + selectOne, + areAllRowsOnPageSelected, + areAllRowsSelected, + isSelected, + } = selectAllOptions; const computeContentSource = search => `/change_host_content_source?search=${search}`; - const customActionKebabs = [ - { - title: __('Change content source'), - icon: , - computeHref: computeContentSource, - }, + const item = { + title: __('Change content source'), + icon: , + computeHref: computeContentSource, + }; + + const selectionToolbar = ( + + + + ); + + const rowSelectNodeFunc = result => ( + { + selectOne(isSelecting, result.id); + }, + isSelected: isSelected(result.id), + disable: false, + }} + /> + ); + + const computeHref = () => { + if (selectedCount > 0) { + return `/change_host_content_source?search=${fetchBulkParams()}`; + } + return ''; + }; + + const actionNode = [ + + {item.icon} {item.title} + , ]; + const customToolbarItems = ; + return ( { controller="hosts" isDeleteable columns={columns} - displaySelectAllCheckbox - customActionKebabs={customActionKebabs} creatable={false} + response={response} + customToolbarItems={customToolbarItems} + selectionToolbar={selectionToolbar} + rowSelectNodeFunc={rowSelectNodeFunc} /> ); }; diff --git a/webpack/assets/javascripts/react_app/components/PF4/TableIndexPage/ActionButtons.js b/webpack/assets/javascripts/react_app/components/PF4/TableIndexPage/ActionButtons.js index fe4c287ca79c..c285d27d042c 100644 --- a/webpack/assets/javascripts/react_app/components/PF4/TableIndexPage/ActionButtons.js +++ b/webpack/assets/javascripts/react_app/components/PF4/TableIndexPage/ActionButtons.js @@ -1,6 +1,11 @@ -import React from 'react'; +import React, { useState } from 'react'; import PropTypes from 'prop-types'; -import { Button } from '@patternfly/react-core'; +import { + Button, + Dropdown, + KebabToggle, + DropdownItem, +} from '@patternfly/react-core'; /** * Generate a button or a dropdown of buttons @@ -10,20 +15,43 @@ import { Button } from '@patternfly/react-core'; */ export const ActionButtons = ({ buttons: originalButtons }) => { const buttons = [...originalButtons]; + const [isOpen, setIsOpen] = useState(false); if (!buttons.length) return null; - - const pfButtons = buttons.map(button => ( - - )); - - return <>{pfButtons}; + const firstButton = buttons.shift(); + return ( + <> + + {buttons.length > 0 && ( + + } + isOpen={isOpen} + isPlain + dropdownItems={buttons.map(button => ( + + {button.icon} {button.title} + + ))} + /> + )} + + ); }; ActionButtons.propTypes = { @@ -32,7 +60,6 @@ ActionButtons.propTypes = { action: PropTypes.object, title: PropTypes.string, icon: PropTypes.node, - isDisabled: PropTypes.bool, }) ), }; diff --git a/webpack/assets/javascripts/react_app/components/PF4/TableIndexPage/ActionKebab.js b/webpack/assets/javascripts/react_app/components/PF4/TableIndexPage/ActionKebab.js index 0238acf399fc..bfce80789a67 100644 --- a/webpack/assets/javascripts/react_app/components/PF4/TableIndexPage/ActionKebab.js +++ b/webpack/assets/javascripts/react_app/components/PF4/TableIndexPage/ActionKebab.js @@ -8,8 +8,7 @@ import { Dropdown, KebabToggle, DropdownItem } from '@patternfly/react-core'; * @param {Object} action action to preform when the button is click can be href with data-method or Onclick * @return {Function} button component or splitbutton component */ -export const ActionKebab = ({ items: originalItems }) => { - const items = [...originalItems]; +export const ActionKebab = ({ items }) => { const [isOpen, setIsOpen] = useState(false); if (!items.length) return null; return ( @@ -25,17 +24,7 @@ export const ActionKebab = ({ items: originalItems }) => { } isOpen={isOpen} isPlain - dropdownItems={items.map(item => ( - - {item.icon} {item.title} - - ))} + dropdownItems={items} /> )} @@ -43,14 +32,7 @@ export const ActionKebab = ({ items: originalItems }) => { }; ActionKebab.propTypes = { - items: PropTypes.arrayOf( - PropTypes.shape({ - action: PropTypes.object, - title: PropTypes.string, - icon: PropTypes.node, - isDisabled: PropTypes.bool, - }) - ), + items: PropTypes.arrayOf(PropTypes.node), }; ActionKebab.defaultProps = { diff --git a/webpack/assets/javascripts/react_app/components/PF4/TableIndexPage/Table/Table.js b/webpack/assets/javascripts/react_app/components/PF4/TableIndexPage/Table/Table.js index 2b511c6b4efa..a6de46c2c606 100644 --- a/webpack/assets/javascripts/react_app/components/PF4/TableIndexPage/Table/Table.js +++ b/webpack/assets/javascripts/react_app/components/PF4/TableIndexPage/Table/Table.js @@ -29,9 +29,7 @@ export const Table = ({ url, isPending, isEmbedded, - displaySelectAllCheckbox, - isSelected, - selectOne, + rowSelectNodeFunc, }) => { const columnsToSortParams = {}; Object.keys(columns).forEach(key => { @@ -86,7 +84,7 @@ export const Table = ({ - {displaySelectAllCheckbox && } + {rowSelectNodeFunc && } {columnNamesKeys.map(k => ( ( - {displaySelectAllCheckbox && ( - { - selectOne(isSelecting, result.id); - }, - isSelected: isSelected(result.id), - disable: false, - }} - /> - )} + {rowSelectNodeFunc && rowSelectNodeFunc(result)} {columnNamesKeys.map(k => ( {columns[k].wrapper ? columns[k].wrapper(result) : result[k]} @@ -183,9 +170,7 @@ Table.propTypes = { url: PropTypes.string.isRequired, isPending: PropTypes.bool.isRequired, isEmbedded: PropTypes.bool, - displaySelectAllCheckbox: PropTypes.bool, - isSelected: PropTypes.func, - selectOne: PropTypes.func, + rowSelectNodeFunc: PropTypes.func, }; Table.defaultProps = { @@ -193,9 +178,7 @@ Table.defaultProps = { isDeleteable: false, itemCount: 0, getActions: null, + rowSelectNodeFunc: undefined, results: [], isEmbedded: false, - displaySelectAllCheckbox: false, - selectOne: noop, - isSelected: noop, }; diff --git a/webpack/assets/javascripts/react_app/components/PF4/TableIndexPage/Table/TableHooks.js b/webpack/assets/javascripts/react_app/components/PF4/TableIndexPage/Table/TableHooks.js index 20c2ad30d276..a5506cea1754 100644 --- a/webpack/assets/javascripts/react_app/components/PF4/TableIndexPage/Table/TableHooks.js +++ b/webpack/assets/javascripts/react_app/components/PF4/TableIndexPage/Table/TableHooks.js @@ -215,7 +215,6 @@ export const useBulkSelect = ({ }; const selectAll = checked => { - console.log({ checked }); setSelectAllMode(checked); if (checked) { exclusionSet.clear(); @@ -225,7 +224,6 @@ export const useBulkSelect = ({ }; const fetchBulkParams = (idColumnName = idColumn) => { - console.log('fetchBulkParams'); const searchQueryWithExclusionSet = () => { const query = [ searchQuery, @@ -233,7 +231,6 @@ export const useBulkSelect = ({ !isEmpty(exclusionSet) && `${idColumnName} !^ (${[...exclusionSet].join(',')})`, ]; - console.log(query.filter(item => item).join(' and ')); return query.filter(item => item).join(' and '); }; @@ -242,8 +239,6 @@ export const useBulkSelect = ({ throw new Error('Cannot build a search query with no items selected'); return `${idColumnName} ^ (${[...inclusionSet].join(',')})`; }; - - console.log({ selectAllMode }); return selectAllMode ? searchQueryWithExclusionSet() : searchQueryWithInclusionSet(); @@ -299,4 +294,4 @@ export const useUrlParams = () => { searchParam, ...urlParams, }; -}; \ No newline at end of file +}; diff --git a/webpack/assets/javascripts/react_app/components/PF4/TableIndexPage/TableIndexPage.js b/webpack/assets/javascripts/react_app/components/PF4/TableIndexPage/TableIndexPage.js index 7a1d7f69613a..8212be7d2130 100644 --- a/webpack/assets/javascripts/react_app/components/PF4/TableIndexPage/TableIndexPage.js +++ b/webpack/assets/javascripts/react_app/components/PF4/TableIndexPage/TableIndexPage.js @@ -3,6 +3,7 @@ import PropTypes from 'prop-types'; import { QuestionCircleIcon } from '@patternfly/react-icons'; import { useHistory } from 'react-router-dom'; import URI from 'urijs'; +import { noop } from '../../../common/helpers'; import { Spinner, Toolbar, @@ -14,6 +15,7 @@ import { TextContent, Text, PaginationVariant, + Button, } from '@patternfly/react-core'; import Pagination from '../../Pagination'; @@ -31,7 +33,6 @@ import BreadcrumbBar from '../../BreadcrumbBar'; import SearchBar from '../../SearchBar'; import Head from '../../Head'; import { ActionButtons } from './ActionButtons'; -import { ActionKebab } from './ActionKebab'; import './TableIndexPage.scss'; import { Table } from './Table/Table'; import { useBulkSelect } from './Table/TableHooks'; @@ -85,7 +86,9 @@ const TableIndexPage = ({ isDeleteable, searchable, children, - displaySelectAllCheckbox, + selectionToolbar, + rowSelectNodeFunc, + response, }) => { const history = useHistory(); const { location: { search: historySearch } = {} } = history || {}; @@ -115,39 +118,24 @@ const TableIndexPage = ({ }, status = STATUS.PENDING, setAPIOptions, - } = useAPI( - 'get', - apiUrl.includes('include_permissions') - ? apiUrl - : `${apiUrl}?include_permissions=true`, - { - ...apiOptions, - params: defaultParams, - } - ); - const { pageRowCount } = getPageStats({ total, page, perPage }); - const { - updateSearchQuery, - fetchBulkParams, - ...selectAllOptions - } = useBulkSelect({ - results, - metadata: {}, - }); + } = + response.length > 0 + ? response + : useAPI( + 'get', + apiUrl.includes('include_permissions') + ? apiUrl + : `${apiUrl}?include_permissions=true`, + { + ...apiOptions, + params: defaultParams, + } + ); const onPagination = newPagination => { setParamsAndAPI({ ...params, ...newPagination }); }; - const { - selectAll, - selectPage, - selectNone, - selectedCount, - areAllRowsOnPageSelected, - areAllRowsSelected, - } = selectAllOptions; - const memoDefaultSearchProps = useMemo( () => getControllerSearchProps(controller), [controller] @@ -176,33 +164,6 @@ const TableIndexPage = ({ } }; - const processCustomElementActions = buttons => - buttons.map(button => { - const responseButton = { ...button }; - - if (selectedCount === 0) { - responseButton.isDisabled = true; - } - - if ( - displaySelectAllCheckbox && - responseButton.computeHref && - selectedCount > 0 - ) { - responseButton.action = { - href: responseButton.computeHref(fetchBulkParams()), - }; - } - return responseButton; - }); - - const additionalActionButtons = processCustomElementActions( - customActionButtons - ); - const additionalActionKebabs = processCustomElementActions( - customActionKebabs - ); - const actionButtons = [ creatable && canCreate && { @@ -220,7 +181,7 @@ const TableIndexPage = ({ icon: , action: { href: customHelpURL || helpURL() }, }, - ...additionalActionButtons, + ...customActionButtons, ].filter(item => item); return ( @@ -246,22 +207,7 @@ const TableIndexPage = ({ {searchable && ( - {displaySelectAllCheckbox && ( - - - - )} + {selectionToolbar} )} - {additionalActionKebabs.length > 0 && ( - - - - - - )} - {customToolbarItems && ( {customToolbarItems} )} @@ -328,8 +266,7 @@ const TableIndexPage = ({ status === STATUS.ERROR && errorMessage ? errorMessage : null } isPending={status === STATUS.PENDING} - {...selectAllOptions} - displaySelectAllCheckbox={displaySelectAllCheckbox} + rowSelectNodeFunc={rowSelectNodeFunc} /> )} @@ -369,19 +306,20 @@ TableIndexPage.propTypes = { controller: PropTypes.string, creatable: PropTypes.bool, customActionButtons: PropTypes.array, - customActionKebabs: PropTypes.array, customCreateAction: PropTypes.func, customExportURL: PropTypes.string, customHelpURL: PropTypes.string, customSearchProps: PropTypes.object, customToolbarItems: PropTypes.node, + response: PropTypes.object, exportable: PropTypes.bool, hasHelpPage: PropTypes.bool, header: PropTypes.string, isDeleteable: PropTypes.bool, searchable: PropTypes.bool, children: PropTypes.node, - displaySelectAllCheckbox: PropTypes.bool, + selectionToolbar: PropTypes.node, + rowSelectNodeFunc: PropTypes.func, }; TableIndexPage.defaultProps = { @@ -391,9 +329,9 @@ TableIndexPage.defaultProps = { columns: null, children: null, controller: '', + response: {}, creatable: true, customActionButtons: [], - customActionKebabs: [], customCreateAction: null, customExportURL: '', customHelpURL: '', @@ -404,7 +342,8 @@ TableIndexPage.defaultProps = { header: '', isDeleteable: false, searchable: true, - displaySelectAllCheckbox: false, + selectionToolbar: null, + rowSelectNodeFunc: noop, }; export default TableIndexPage;