From d010e2ecff7ca0d4e6614745ee422c3db9fb08b1 Mon Sep 17 00:00:00 2001 From: Andrei Lakatos Date: Wed, 20 May 2026 08:18:54 -0400 Subject: [PATCH 1/4] Fixes #39340 - PageLayout accept JSX to title prop --- .../PF4/TableIndexPage/TableIndexPage.js | 248 ++++++++---------- .../PF4/TableIndexPage/TableIndexPage.scss | 13 +- .../__snapshots__/AuditsPage.test.js.snap | 16 ++ .../routes/common/PageLayout/PageLayout.js | 112 +++++--- .../common/PageLayout/PageLayout.test.js | 140 +++++++++- 5 files changed, 339 insertions(+), 190 deletions(-) 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 85482b432e..5cacd5a21e 100644 --- a/webpack/assets/javascripts/react_app/components/PF4/TableIndexPage/TableIndexPage.js +++ b/webpack/assets/javascripts/react_app/components/PF4/TableIndexPage/TableIndexPage.js @@ -9,10 +9,6 @@ import { ToolbarContent, ToolbarGroup, ToolbarItem, - PageSection, - PageSectionVariants, - TextContent, - Text, Icon, PaginationVariant, } from '@patternfly/react-core'; @@ -27,9 +23,8 @@ import { translate as __ } from '../../../common/I18n'; import { noop } from '../../../common/helpers'; import Pagination from '../../Pagination'; import { getControllerSearchProps, STATUS } from '../../../constants'; -import BreadcrumbBar from '../../BreadcrumbBar'; +import PageLayout from '../../../routes/common/PageLayout/PageLayout'; import SearchBar from '../../SearchBar'; -import Head from '../../Head'; import { ActionButtons } from './ActionButtons'; import './TableIndexPage.scss'; import { Table } from './Table/Table'; @@ -37,6 +32,7 @@ import { useSetParamsAndApiAndSearch, useTableIndexAPIResponse, } from './Table/TableIndexHooks'; + /** A page component that displays a table with data fetched from the API. It provides search and filtering functionality, and the ability to create new entries and export data. @@ -62,7 +58,6 @@ A page component that displays a table with data fetched from the API. It provid @param {boolean} {exportable} - whether or not to show export button @param {boolean} {hasHelpPage} - whether or not to show documentation button @param {React.ReactNode}{customHeader} - a custom header to be rendered instead of the default header -@param {string}{headerText} - DEPRECATED - the header text for the page @param {string}{header} -the header text for the page and the title @param {boolean} {isDeleteable} - whether or not entries can be deleted @param {boolean} {searchable} - whether or not the table can be searched @@ -101,7 +96,6 @@ const TableIndexPage = ({ exportable, hasHelpPage, customHeader, - headerText, header, isDeleteable, searchable, @@ -224,138 +218,120 @@ const TableIndexPage = ({ ...customActionButtons, ].filter(item => item); - header = headerText || header; - return ( -
- - {header} - - {breadcrumbOptions && ( - - - - )} - - {customHeader || ( - - - {header} - - - )} - - {beforeToolbarComponent} - - - - {searchable && ( - - {selectionToolbar} - - - - {status === STATUS.PENDING && ( - - - - )} - - )} - {(customToolbarItems || actionButtons.length > 0) && ( - - {actionButtons.length > 0 && ( - - - - )} - {customToolbarItems && customToolbarItems} - - )} - {total > 0 && ( - + + {searchable && ( + + {selectionToolbar} + + + + {status === STATUS.PENDING && ( + + + )} - - - - - {children || ( - - } - getActions={rowKebabItems} + + )} + {(customToolbarItems || actionButtons.length > 0) && ( + + {actionButtons.length > 0 && ( + + + + )} + {customToolbarItems && customToolbarItems} + + )} + {total > 0 && ( + - setAPIOptions({ - ...apiOptions, - params: { search }, - }) - } - columns={columns} - errorMessage={ - status === STATUS.ERROR && errorMessage ? errorMessage : null - } - isPending={status === STATUS.PENDING} - selectOne={selectOne} - isSelected={isSelected} - showCheckboxes={showCheckboxes} - rowSelectTd={rowSelectTd} - idColumn={idColumn} - customEmptyState={customEmptyState} - emptyMessage={emptyMessage} - emptyAction={emptyAction} + onChange={onPagination} /> )} - - + + + ); + + return ( + + {children || ( +
+ } + getActions={rowKebabItems} + itemCount={subtotal} + results={results} + url={apiUrl} + isDeleteable={isDeleteable} + refreshData={() => + setAPIOptions({ + ...apiOptions, + params: { search }, + }) + } + columns={columns} + errorMessage={ + status === STATUS.ERROR && errorMessage ? errorMessage : null + } + isPending={status === STATUS.PENDING} + selectOne={selectOne} + isSelected={isSelected} + showCheckboxes={showCheckboxes} + rowSelectTd={rowSelectTd} + idColumn={idColumn} + customEmptyState={customEmptyState} + emptyMessage={emptyMessage} + emptyAction={emptyAction} + /> + )} + ); }; @@ -399,7 +375,6 @@ TableIndexPage.propTypes = { replacementResponse: PropTypes.object, exportable: PropTypes.bool, hasHelpPage: PropTypes.bool, - headerText: PropTypes.string, header: PropTypes.string, customHeader: PropTypes.node, isDeleteable: PropTypes.bool, @@ -442,7 +417,6 @@ TableIndexPage.defaultProps = { exportable: false, hasHelpPage: false, header: '', - headerText: '', customHeader: undefined, isDeleteable: false, searchable: true, diff --git a/webpack/assets/javascripts/react_app/components/PF4/TableIndexPage/TableIndexPage.scss b/webpack/assets/javascripts/react_app/components/PF4/TableIndexPage/TableIndexPage.scss index cfc0317984..5bdfe288c0 100644 --- a/webpack/assets/javascripts/react_app/components/PF4/TableIndexPage/TableIndexPage.scss +++ b/webpack/assets/javascripts/react_app/components/PF4/TableIndexPage/TableIndexPage.scss @@ -1,10 +1,10 @@ #foreman-page { - .table-title-section { - padding-bottom: 0; - } - .table-toolbar-section { + .page-toolbar-section { padding-top: 16px; padding-bottom: 16px; + padding-left: 0; + padding-right: 0; + .table-toolbar { padding: 0; .pf-v5-c-toolbar__content { @@ -25,11 +25,6 @@ } } } - .table-section { - padding-top: 0; - padding-left: 0; - padding-right: 0; - } .pf-v5-c-toolbar__group { flex-grow: 1; &.pf-m-align-right { diff --git a/webpack/assets/javascripts/react_app/routes/Audits/AuditsPage/__tests__/__snapshots__/AuditsPage.test.js.snap b/webpack/assets/javascripts/react_app/routes/Audits/AuditsPage/__tests__/__snapshots__/AuditsPage.test.js.snap index df284245f6..05c6603458 100644 --- a/webpack/assets/javascripts/react_app/routes/Audits/AuditsPage/__tests__/__snapshots__/AuditsPage.test.js.snap +++ b/webpack/assets/javascripts/react_app/routes/Audits/AuditsPage/__tests__/__snapshots__/AuditsPage.test.js.snap @@ -2,9 +2,13 @@ exports[`AuditsPage rendering render audits page 1`] = ` { - const title = ( + const titleSectionBody = customHeader ?? ( {header} @@ -38,8 +45,18 @@ const PageLayout = ({ ); + const showStandaloneTitleSection = + alwaysShowStandaloneTitleSection || searchable || !toolbarButtons; + + const toolbarSectionShowsDefaultToolbar = + !customToolbar && + (searchable || beforeToolbarComponent || isLoading || toolbarButtons); + + const showToolbarSection = + toolbarSectionShowsDefaultToolbar || Boolean(customToolbar); + return ( - <> +
{header} @@ -53,61 +70,67 @@ const PageLayout = ({ )} - {(searchable || !toolbarButtons) && ( + {showStandaloneTitleSection && ( - +
{titleSectionBody}
)} - {(searchable || - beforeToolbarComponent || - isLoading || - toolbarButtons) && ( + {beforeToolbarOutsideSection} + + {showToolbarSection && ( {beforeToolbarComponent} - - - - {!searchable && toolbarButtons && title} - {searchable && ( - - )} - - {isLoading && ( - - + {customToolbar || ( + + + + {!searchable && + toolbarButtons && + !alwaysShowStandaloneTitleSection && + titleSectionBody} + {searchable && ( + + )} - )} - - {toolbarButtons} - - - + {isLoading && ( + + + + )} + + {toolbarButtons} + + + + )} )} {children} - +
); }; PageLayout.propTypes = { children: PropTypes.node.isRequired, searchable: PropTypes.bool.isRequired, - header: PropTypes.string, searchProps: PropTypes.shape({ autocomplete: PropTypes.shape({ results: PropTypes.array, @@ -147,9 +170,17 @@ PageLayout.propTypes = { ), }), toolbarButtons: PropTypes.node, + customToolbar: PropTypes.node, + header: PropTypes.string, + customHeader: PropTypes.node, + alwaysShowStandaloneTitleSection: PropTypes.bool, onSearch: PropTypes.func, searchQuery: PropTypes.string, + searchBarInitialQuery: PropTypes.string, + restrictedSearchQuery: PropTypes.func, + bookmarksPosition: PropTypes.string, beforeToolbarComponent: PropTypes.node, + beforeToolbarOutsideSection: PropTypes.node, isLoading: PropTypes.bool, pageSectionType: PropTypes.string, }; @@ -157,13 +188,20 @@ PageLayout.propTypes = { PageLayout.defaultProps = { searchProps: {}, header: '', + customHeader: null, searchQuery: '', + searchBarInitialQuery: undefined, + restrictedSearchQuery: undefined, + bookmarksPosition: undefined, customBreadcrumbs: null, toolbarButtons: null, + customToolbar: null, breadcrumbOptions: null, isLoading: false, - onSearch: searchQuery => changeQuery({ search: searchQuery.trim(), page: 1 }), + onSearch: sq => changeQuery({ search: sq.trim(), page: 1 }), beforeToolbarComponent: null, + beforeToolbarOutsideSection: null, + alwaysShowStandaloneTitleSection: false, pageSectionType: 'default', }; diff --git a/webpack/assets/javascripts/react_app/routes/common/PageLayout/PageLayout.test.js b/webpack/assets/javascripts/react_app/routes/common/PageLayout/PageLayout.test.js index ca99fc63c1..81e9f9d3c8 100644 --- a/webpack/assets/javascripts/react_app/routes/common/PageLayout/PageLayout.test.js +++ b/webpack/assets/javascripts/react_app/routes/common/PageLayout/PageLayout.test.js @@ -4,6 +4,7 @@ import { screen } from '@testing-library/react'; import PageLayout from './PageLayout'; import '@testing-library/jest-dom'; +import { breadcrumbBar } from '../../../components/BreadcrumbBar/BreadcrumbBar.fixtures'; import { pageLayoutMock } from './PageLayout.fixtures'; import { rtlHelpers } from '../../../common/rtlTestHelpers'; @@ -11,7 +12,7 @@ const { renderWithStore } = rtlHelpers; jest.unmock('react-helmet'); describe('PageLayout', () => { - it('should render header text', () => { + it('renders string header as title when searchable is false (no SearchBar)', () => { const header = 'My Header'; const { getByText } = renderWithStore( @@ -30,7 +31,35 @@ describe('PageLayout', () => { expect(screen.queryAllByLabelText('Search')).toHaveLength(0); }); - it('should have Search', () => { + it('renders customHeader node inside title breadcrumb region', () => { + function CompositeHeader() { + return ( + + Scoped Hosts view + + ); + } + + renderWithStore( + + } + searchable={false} + > +
Content
+
+
+ ); + + const composite = screen.getByTestId('composite-header'); + expect(composite).toBeInTheDocument(); + expect(composite.closest('#page-title')).toBeInTheDocument(); + expect(screen.getByText('Hosts')).toBeInTheDocument(); + }); + + it('renders SearchBar in toolbar when searchable is true', () => { const onSearchMock = jest.fn(); const { getByLabelText } = renderWithStore( @@ -47,7 +76,7 @@ describe('PageLayout', () => { expect(getByLabelText('Search')).toBeInTheDocument(); }); - it('should render custom breadcrumbs', () => { + it('renders customBreadcrumbs when provided', () => { const customBreadcrumbs =
test Breadcrumbs
; const { getByText } = renderWithStore( @@ -64,7 +93,43 @@ describe('PageLayout', () => { expect(breadcrumbsElement).toBeInTheDocument(); }); - it('should render toolbar buttons', () => { + it('renders BreadcrumbBar from breadcrumbOptions', () => { + renderWithStore( + + +
Content
+
+
+ ); + expect(screen.getByText('root')).toBeInTheDocument(); + }); + + it('renders beforeToolbarComponent inside toolbar PageSection', () => { + renderWithStore( + + Before toolbar + } + > +
Content
+
+
+ ); + expect(screen.getByTestId('before-toolbar')).toHaveTextContent( + 'Before toolbar' + ); + }); + + it('renders toolbarButtons in toolbar', () => { const toolbarButtons = ; const { getByText } = renderWithStore( @@ -81,7 +146,7 @@ describe('PageLayout', () => { expect(buttonElement).toBeInTheDocument(); }); - it('should render content', () => { + it('renders children in main content PageSection', () => { const { getByText } = renderWithStore( @@ -93,7 +158,7 @@ describe('PageLayout', () => { expect(contentElement).toBeInTheDocument(); }); - it('should show spinner when isLoading is true', () => { + it('shows toolbar spinner when isLoading is true', () => { const { container } = renderWithStore( @@ -104,7 +169,7 @@ describe('PageLayout', () => { expect(container.querySelector('#toolbar-spinner')).toBeInTheDocument(); }); - it('should not show spinner when isLoading is false', () => { + it('does not render toolbar spinner when isLoading is false', () => { const { container } = renderWithStore( @@ -114,4 +179,65 @@ describe('PageLayout', () => { ); expect(container.querySelector('#toolbar-spinner')).toBeNull(); }); + + it('renders beforeToolbarOutsideSection outside toolbar PageSection', () => { + renderWithStore( + + TB} + beforeToolbarOutsideSection={ +
Outside
+ } + > +
Content
+
+
+ ); + expect(screen.getByTestId('outside-toolbar')).toHaveTextContent('Outside'); + expect(screen.getByText('TB')).toBeInTheDocument(); + }); + + it('renders customToolbar instead of built-in toolbar (skips SearchBar)', () => { + renderWithStore( + + Custom} + header="Index" + > +
Content
+
+
+ ); + expect(screen.getByTestId('custom-toolbar-slot')).toHaveTextContent( + 'Custom' + ); + expect(screen.queryByLabelText('Search input')).not.toBeInTheDocument(); + }); + + it('keeps single standalone title heading when alwaysShowStandaloneTitleSection with toolbarButtons', () => { + renderWithStore( + + Action} + alwaysShowStandaloneTitleSection + > +
Content
+
+
+ ); + expect(screen.getAllByRole('heading', { level: 1 })).toHaveLength(1); + expect(screen.getAllByRole('heading', { level: 1 })[0]).toHaveTextContent( + 'Standalone title' + ); + }); }); From d278bc549c2eebbfdbf066178c5778162637106d Mon Sep 17 00:00:00 2001 From: Andrei Lakatos Date: Tue, 2 Jun 2026 09:04:33 -0400 Subject: [PATCH 2/4] Refs #39340 - remove unnecessary props and changes --- .../PF4/TableIndexPage/TableIndexPage.js | 9 +--- .../__snapshots__/AuditsPage.test.js.snap | 8 ---- .../routes/common/PageLayout/PageLayout.js | 32 ++----------- .../common/PageLayout/PageLayout.test.js | 45 ++----------------- 4 files changed, 8 insertions(+), 86 deletions(-) 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 5cacd5a21e..9dd4a33a61 100644 --- a/webpack/assets/javascripts/react_app/components/PF4/TableIndexPage/TableIndexPage.js +++ b/webpack/assets/javascripts/react_app/components/PF4/TableIndexPage/TableIndexPage.js @@ -275,14 +275,7 @@ const TableIndexPage = ({ header={header} customHeader={customHeader} searchable={searchable} - searchProps={searchProps} - searchQuery={search} - onSearch={onSearch} - searchBarInitialQuery="" - restrictedSearchQuery={restrictedSearchQuery} - bookmarksPosition={bookmarksPosition} - beforeToolbarOutsideSection={beforeToolbarComponent} - alwaysShowStandaloneTitleSection + beforeToolbarComponent={beforeToolbarComponent} customToolbar={customToolbar} > {children || ( diff --git a/webpack/assets/javascripts/react_app/routes/Audits/AuditsPage/__tests__/__snapshots__/AuditsPage.test.js.snap b/webpack/assets/javascripts/react_app/routes/Audits/AuditsPage/__tests__/__snapshots__/AuditsPage.test.js.snap index 05c6603458..a5432cbc40 100644 --- a/webpack/assets/javascripts/react_app/routes/Audits/AuditsPage/__tests__/__snapshots__/AuditsPage.test.js.snap +++ b/webpack/assets/javascripts/react_app/routes/Audits/AuditsPage/__tests__/__snapshots__/AuditsPage.test.js.snap @@ -2,9 +2,7 @@ exports[`AuditsPage rendering render audits page 1`] = ` ); - const showStandaloneTitleSection = - alwaysShowStandaloneTitleSection || searchable || !toolbarButtons; + const showStandaloneTitleSection = searchable || !toolbarButtons; const toolbarSectionShowsDefaultToolbar = - !customToolbar && - (searchable || beforeToolbarComponent || isLoading || toolbarButtons); + !customToolbar && (searchable || isLoading || toolbarButtons); const showToolbarSection = toolbarSectionShowsDefaultToolbar || Boolean(customToolbar); @@ -76,22 +69,18 @@ const PageLayout = ({ )} - {beforeToolbarOutsideSection} + {beforeToolbarComponent} {showToolbarSection && ( - {beforeToolbarComponent} {customToolbar || ( - {!searchable && - toolbarButtons && - !alwaysShowStandaloneTitleSection && - titleSectionBody} + {!searchable && toolbarButtons && titleSectionBody} {searchable && ( )} @@ -173,14 +159,9 @@ PageLayout.propTypes = { customToolbar: PropTypes.node, header: PropTypes.string, customHeader: PropTypes.node, - alwaysShowStandaloneTitleSection: PropTypes.bool, onSearch: PropTypes.func, searchQuery: PropTypes.string, - searchBarInitialQuery: PropTypes.string, - restrictedSearchQuery: PropTypes.func, - bookmarksPosition: PropTypes.string, beforeToolbarComponent: PropTypes.node, - beforeToolbarOutsideSection: PropTypes.node, isLoading: PropTypes.bool, pageSectionType: PropTypes.string, }; @@ -190,9 +171,6 @@ PageLayout.defaultProps = { header: '', customHeader: null, searchQuery: '', - searchBarInitialQuery: undefined, - restrictedSearchQuery: undefined, - bookmarksPosition: undefined, customBreadcrumbs: null, toolbarButtons: null, customToolbar: null, @@ -200,8 +178,6 @@ PageLayout.defaultProps = { isLoading: false, onSearch: sq => changeQuery({ search: sq.trim(), page: 1 }), beforeToolbarComponent: null, - beforeToolbarOutsideSection: null, - alwaysShowStandaloneTitleSection: false, pageSectionType: 'default', }; diff --git a/webpack/assets/javascripts/react_app/routes/common/PageLayout/PageLayout.test.js b/webpack/assets/javascripts/react_app/routes/common/PageLayout/PageLayout.test.js index 81e9f9d3c8..b855d696df 100644 --- a/webpack/assets/javascripts/react_app/routes/common/PageLayout/PageLayout.test.js +++ b/webpack/assets/javascripts/react_app/routes/common/PageLayout/PageLayout.test.js @@ -109,13 +109,14 @@ describe('PageLayout', () => { expect(screen.getByText('root')).toBeInTheDocument(); }); - it('renders beforeToolbarComponent inside toolbar PageSection', () => { + it('renders beforeToolbarComponent between title and toolbar sections', () => { renderWithStore( TB} beforeToolbarComponent={
Before toolbar
} @@ -127,6 +128,7 @@ describe('PageLayout', () => { expect(screen.getByTestId('before-toolbar')).toHaveTextContent( 'Before toolbar' ); + expect(screen.getByText('TB')).toBeInTheDocument(); }); it('renders toolbarButtons in toolbar', () => { @@ -180,26 +182,6 @@ describe('PageLayout', () => { expect(container.querySelector('#toolbar-spinner')).toBeNull(); }); - it('renders beforeToolbarOutsideSection outside toolbar PageSection', () => { - renderWithStore( - - TB} - beforeToolbarOutsideSection={ -
Outside
- } - > -
Content
-
-
- ); - expect(screen.getByTestId('outside-toolbar')).toHaveTextContent('Outside'); - expect(screen.getByText('TB')).toBeInTheDocument(); - }); - it('renders customToolbar instead of built-in toolbar (skips SearchBar)', () => { renderWithStore( @@ -219,25 +201,4 @@ describe('PageLayout', () => { ); expect(screen.queryByLabelText('Search input')).not.toBeInTheDocument(); }); - - it('keeps single standalone title heading when alwaysShowStandaloneTitleSection with toolbarButtons', () => { - renderWithStore( - - Action} - alwaysShowStandaloneTitleSection - > -
Content
-
-
- ); - expect(screen.getAllByRole('heading', { level: 1 })).toHaveLength(1); - expect(screen.getAllByRole('heading', { level: 1 })[0]).toHaveTextContent( - 'Standalone title' - ); - }); }); From f79523ae5e1905644c2d02fc299ec0a079b1838d Mon Sep 17 00:00:00 2001 From: Andrei Lakatos Date: Tue, 2 Jun 2026 11:50:59 -0400 Subject: [PATCH 3/4] Refs #39340 - remove use of searchable prop, fix styles for the page --- .../components/PF4/TableIndexPage/TableIndexPage.js | 2 +- .../PF4/TableIndexPage/TableIndexPage.scss | 13 ++++++++----- .../routes/common/PageLayout/PageLayout.js | 8 ++++---- 3 files changed, 13 insertions(+), 10 deletions(-) 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 9dd4a33a61..57abcce06e 100644 --- a/webpack/assets/javascripts/react_app/components/PF4/TableIndexPage/TableIndexPage.js +++ b/webpack/assets/javascripts/react_app/components/PF4/TableIndexPage/TableIndexPage.js @@ -274,7 +274,7 @@ const TableIndexPage = ({ breadcrumbOptions={breadcrumbOptions} header={header} customHeader={customHeader} - searchable={searchable} + searchable={false} beforeToolbarComponent={beforeToolbarComponent} customToolbar={customToolbar} > diff --git a/webpack/assets/javascripts/react_app/components/PF4/TableIndexPage/TableIndexPage.scss b/webpack/assets/javascripts/react_app/components/PF4/TableIndexPage/TableIndexPage.scss index 5bdfe288c0..426d454415 100644 --- a/webpack/assets/javascripts/react_app/components/PF4/TableIndexPage/TableIndexPage.scss +++ b/webpack/assets/javascripts/react_app/components/PF4/TableIndexPage/TableIndexPage.scss @@ -1,9 +1,7 @@ #foreman-page { .page-toolbar-section { - padding-top: 16px; - padding-bottom: 16px; - padding-left: 0; - padding-right: 0; + padding-top: var(--pf-v5-global--spacer--md); + padding-bottom: var(--pf-v5-global--spacer--md); .table-toolbar { padding: 0; @@ -15,7 +13,7 @@ display: block; } .table-toolbar-actions { - padding-left: 16px; + padding-left: var(--pf-v5-global--spacer--md); } } .pf-v5-c-toolbar__group { @@ -25,6 +23,11 @@ } } } + .page-content-section { + padding-top: 0; + padding-left: 0; + padding-right: 0; + } .pf-v5-c-toolbar__group { flex-grow: 1; &.pf-m-align-right { diff --git a/webpack/assets/javascripts/react_app/routes/common/PageLayout/PageLayout.js b/webpack/assets/javascripts/react_app/routes/common/PageLayout/PageLayout.js index 25d2230b0e..a43b5939be 100644 --- a/webpack/assets/javascripts/react_app/routes/common/PageLayout/PageLayout.js +++ b/webpack/assets/javascripts/react_app/routes/common/PageLayout/PageLayout.js @@ -49,7 +49,7 @@ const PageLayout = ({ toolbarSectionShowsDefaultToolbar || Boolean(customToolbar); return ( -
+ <> {header} @@ -107,10 +107,10 @@ const PageLayout = ({ )} )} - + {children} -
+ ); }; @@ -176,7 +176,7 @@ PageLayout.defaultProps = { customToolbar: null, breadcrumbOptions: null, isLoading: false, - onSearch: sq => changeQuery({ search: sq.trim(), page: 1 }), + onSearch: newSearch => changeQuery({ search: newSearch.trim(), page: 1 }), beforeToolbarComponent: null, pageSectionType: 'default', }; From 39ba78465ebf6c210f7d6e10b8302797511ce5c2 Mon Sep 17 00:00:00 2001 From: Andrei Lakatos Date: Tue, 2 Jun 2026 12:49:05 -0400 Subject: [PATCH 4/4] Refs #39340 - add css wrapper for tableindexpage to scope css --- .../PF4/TableIndexPage/TableIndexPage.js | 112 +++++++++--------- .../PF4/TableIndexPage/TableIndexPage.scss | 2 +- .../routes/common/PageLayout/PageLayout.js | 7 +- 3 files changed, 64 insertions(+), 57 deletions(-) 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 57abcce06e..d9dedd85ac 100644 --- a/webpack/assets/javascripts/react_app/components/PF4/TableIndexPage/TableIndexPage.js +++ b/webpack/assets/javascripts/react_app/components/PF4/TableIndexPage/TableIndexPage.js @@ -270,61 +270,63 @@ const TableIndexPage = ({ ); return ( - - {children || ( -
- } - getActions={rowKebabItems} - itemCount={subtotal} - results={results} - url={apiUrl} - isDeleteable={isDeleteable} - refreshData={() => - setAPIOptions({ - ...apiOptions, - params: { search }, - }) - } - columns={columns} - errorMessage={ - status === STATUS.ERROR && errorMessage ? errorMessage : null - } - isPending={status === STATUS.PENDING} - selectOne={selectOne} - isSelected={isSelected} - showCheckboxes={showCheckboxes} - rowSelectTd={rowSelectTd} - idColumn={idColumn} - customEmptyState={customEmptyState} - emptyMessage={emptyMessage} - emptyAction={emptyAction} - /> - )} - +
+ + {children || ( +
+ } + getActions={rowKebabItems} + itemCount={subtotal} + results={results} + url={apiUrl} + isDeleteable={isDeleteable} + refreshData={() => + setAPIOptions({ + ...apiOptions, + params: { search }, + }) + } + columns={columns} + errorMessage={ + status === STATUS.ERROR && errorMessage ? errorMessage : null + } + isPending={status === STATUS.PENDING} + selectOne={selectOne} + isSelected={isSelected} + showCheckboxes={showCheckboxes} + rowSelectTd={rowSelectTd} + idColumn={idColumn} + customEmptyState={customEmptyState} + emptyMessage={emptyMessage} + emptyAction={emptyAction} + /> + )} + + ); }; diff --git a/webpack/assets/javascripts/react_app/components/PF4/TableIndexPage/TableIndexPage.scss b/webpack/assets/javascripts/react_app/components/PF4/TableIndexPage/TableIndexPage.scss index 426d454415..fd7cefb6b3 100644 --- a/webpack/assets/javascripts/react_app/components/PF4/TableIndexPage/TableIndexPage.scss +++ b/webpack/assets/javascripts/react_app/components/PF4/TableIndexPage/TableIndexPage.scss @@ -1,4 +1,4 @@ -#foreman-page { +.table-index-page { .page-toolbar-section { padding-top: var(--pf-v5-global--spacer--md); padding-bottom: var(--pf-v5-global--spacer--md); diff --git a/webpack/assets/javascripts/react_app/routes/common/PageLayout/PageLayout.js b/webpack/assets/javascripts/react_app/routes/common/PageLayout/PageLayout.js index a43b5939be..44f7bc8d34 100644 --- a/webpack/assets/javascripts/react_app/routes/common/PageLayout/PageLayout.js +++ b/webpack/assets/javascripts/react_app/routes/common/PageLayout/PageLayout.js @@ -107,7 +107,11 @@ const PageLayout = ({ )} )} - + {children} @@ -164,6 +168,7 @@ PageLayout.propTypes = { beforeToolbarComponent: PropTypes.node, isLoading: PropTypes.bool, pageSectionType: PropTypes.string, + className: PropTypes.string, }; PageLayout.defaultProps = {