diff --git a/workspaces/frontend/src/__tests__/cypress/cypress/tests/mocked/workspaces/filterWorkspacesTest.cy.ts b/workspaces/frontend/src/__tests__/cypress/cypress/tests/mocked/workspaces/filterWorkspacesTest.cy.ts index 075c8c3ca..1a2a53578 100644 --- a/workspaces/frontend/src/__tests__/cypress/cypress/tests/mocked/workspaces/filterWorkspacesTest.cy.ts +++ b/workspaces/frontend/src/__tests__/cypress/cypress/tests/mocked/workspaces/filterWorkspacesTest.cy.ts @@ -6,7 +6,7 @@ import { home } from '~/__tests__/cypress/cypress/pages/home'; const useFilter = (filterName: string, searchValue: string) => { cy.get("[id$='filter-workspaces-dropdown']").click(); cy.get(`[id$='filter-workspaces-dropdown-${filterName}']`).click(); - cy.get("[id$='filter-workspaces-search-input']").type(searchValue); + cy.get("[data-testid='filter-workspaces-search-input']").type(searchValue); cy.get("[class$='pf-v6-c-toolbar__group']").contains(filterName); cy.get("[class$='pf-v6-c-toolbar__group']").contains(searchValue); }; @@ -19,6 +19,7 @@ describe('Application', () => { cy.intercept('GET', '/api/v1/workspaces/default', { body: mockBFFResponse(mockWorkspaces), }); + cy.intercept('GET', '/api/namespaces/test-namespace/workspaces').as('getWorkspaces'); }); it('filter rows with single filter', () => { home.visit(); diff --git a/workspaces/frontend/src/app/components/FormFieldset.tsx b/workspaces/frontend/src/app/components/FormFieldset.tsx new file mode 100644 index 000000000..8869aef30 --- /dev/null +++ b/workspaces/frontend/src/app/components/FormFieldset.tsx @@ -0,0 +1,22 @@ +import React, { ReactNode } from 'react'; + +interface FormFieldsetProps { + component: ReactNode; + field?: string; + className?: string; +} + +const FormFieldset: React.FC = ({ component, field, className }) => ( +
+ {component} + +
+); + +export default FormFieldset; diff --git a/workspaces/frontend/src/app/components/ThemeAwareSearchInput.tsx b/workspaces/frontend/src/app/components/ThemeAwareSearchInput.tsx new file mode 100644 index 000000000..b0696f62a --- /dev/null +++ b/workspaces/frontend/src/app/components/ThemeAwareSearchInput.tsx @@ -0,0 +1,68 @@ +import * as React from 'react'; +import { SearchInput, SearchInputProps, TextInput } from '@patternfly/react-core'; +import FormFieldset from 'app/components/FormFieldset'; +import { isMUITheme } from 'app/const'; + +type ThemeAwareSearchInputProps = Omit & { + onChange: (value: string) => void; // Simplified onChange signature + onClear?: () => void; // Simplified optional onClear signature + fieldLabel?: string; // Additional prop for MUI FormFieldset label + 'data-testid'?: string; +}; + +const ThemeAwareSearchInput: React.FC = ({ + value, + onChange, + onClear, + fieldLabel, + placeholder, + isDisabled, + className, + style, + 'aria-label': ariaLabel = 'Search', + 'data-testid': dataTestId, + ...rest +}) => { + if (isMUITheme()) { + // Render MUI version using TextInput + FormFieldset + return ( + onChange(newValue)} // Adapt signature + isDisabled={isDisabled} + aria-label={ariaLabel} + data-testid={dataTestId} + style={style} + /> + } + /> + ); + } + + // Render PF version using SearchInput + return ( + onChange(newValue)} // Adapt signature + onClear={(event) => { + event.stopPropagation(); + onChange(''); + onClear?.(); // Adapt signature + }} + /> + ); +}; + +export default ThemeAwareSearchInput; diff --git a/workspaces/frontend/src/app/pages/WorkspaceKinds/WorkspaceKinds.tsx b/workspaces/frontend/src/app/pages/WorkspaceKinds/WorkspaceKinds.tsx index 805a79214..b1931883f 100644 --- a/workspaces/frontend/src/app/pages/WorkspaceKinds/WorkspaceKinds.tsx +++ b/workspaces/frontend/src/app/pages/WorkspaceKinds/WorkspaceKinds.tsx @@ -8,7 +8,6 @@ import { Brand, Tooltip, Label, - SearchInput, Toolbar, ToolbarContent, ToolbarItem, @@ -44,6 +43,7 @@ import { WorkspaceKind } from '~/shared/api/backendApiTypes'; import useWorkspaceKinds from '~/app/hooks/useWorkspaceKinds'; import { useWorkspaceCountPerKind } from '~/app/hooks/useWorkspaceCountPerKind'; import { WorkspaceKindsColumnNames } from '~/app/types'; +import ThemeAwareSearchInput from '~/app/components/ThemeAwareSearchInput'; export enum ActionType { ViewDetails, @@ -178,32 +178,6 @@ export const WorkspaceKinds: React.FunctionComponent = () => { [sortedWorkspaceKinds, onFilter], ); - // Set up name search input - const searchNameInput = React.useMemo( - () => ( - onSearchNameChange(value)} - onClear={() => onSearchNameChange('')} - /> - ), - [searchNameValue, onSearchNameChange], - ); - - // Set up description search input - const searchDescriptionInput = React.useMemo( - () => ( - onSearchDescriptionChange(value)} - onClear={() => onSearchDescriptionChange('')} - /> - ), - [searchDescriptionValue, onSearchDescriptionChange], - ); - // Set up status single select const [isStatusMenuOpen, setIsStatusMenuOpen] = React.useState(false); const statusToggleRef = React.useRef(null); @@ -509,7 +483,15 @@ export const WorkspaceKinds: React.FunctionComponent = () => { categoryName="Name" showToolbarItem={activeAttributeMenu === 'Name'} > - {searchNameInput} + + + { categoryName="Description" showToolbarItem={activeAttributeMenu === 'Description'} > - {searchDescriptionInput} + + + { - const navigate = useNavigate(); - const createWorkspace = useCallback(() => { - navigate('/workspaces/create'); - }, [navigate]); - const [workspaceKinds] = useWorkspaceKinds(); const kindLogoDict = buildKindLogoDictionary(workspaceKinds); const workspaceRedirectStatus = buildWorkspaceRedirectStatus(workspaceKinds); @@ -455,9 +448,6 @@ export const Workspaces: React.FunctionComponent = () => {
- diff --git a/workspaces/frontend/src/shared/components/Filter.tsx b/workspaces/frontend/src/shared/components/Filter.tsx index 882b59c97..3540c22a4 100644 --- a/workspaces/frontend/src/shared/components/Filter.tsx +++ b/workspaces/frontend/src/shared/components/Filter.tsx @@ -1,5 +1,6 @@ import * as React from 'react'; import { + Button, Menu, MenuContent, MenuItem, @@ -7,7 +8,6 @@ import { MenuToggle, MenuToggleElement, Popper, - SearchInput, Toolbar, ToolbarContent, ToolbarFilter, @@ -16,6 +16,9 @@ import { ToolbarToggleGroup, } from '@patternfly/react-core'; import { FilterIcon } from '@patternfly/react-icons'; +import { useNavigate } from 'react-router'; +import { useCallback } from 'react'; +import ThemeAwareSearchInput from '~/app/components/ThemeAwareSearchInput'; export interface FilterProps { id: string; @@ -41,6 +44,11 @@ const Filter: React.FC = ({ id, onFilter, columnNames }) => { const filterMenuRef = React.useRef(null); const filterContainerRef = React.useRef(null); + const navigate = useNavigate(); + const createWorkspace = useCallback(() => { + navigate('/workspaces/create'); + }, [navigate]); + const handleFilterMenuKeys = React.useCallback( (event: KeyboardEvent) => { if (!isFilterMenuOpen) { @@ -202,12 +210,13 @@ const Filter: React.FC = ({ id, onFilter, columnNames }) => { {filterDropdown} - onSearchChange(value)} - onClear={() => onSearchChange('')} + onChange={onSearchChange} + placeholder={`Filter by ${activeFilter.columnName}`} + fieldLabel={`Find by ${activeFilter.columnName}`} + aria-label={`Filter by ${activeFilter.columnName}`} /> {filters.map((filter) => ( @@ -222,6 +231,9 @@ const Filter: React.FC = ({ id, onFilter, columnNames }) => { ))} + diff --git a/workspaces/frontend/src/shared/style/MUI-theme.scss b/workspaces/frontend/src/shared/style/MUI-theme.scss index b973d5bf6..093bbf3a7 100644 --- a/workspaces/frontend/src/shared/style/MUI-theme.scss +++ b/workspaces/frontend/src/shared/style/MUI-theme.scss @@ -573,7 +573,7 @@ } -.mui-theme .pf-v6-c-progress-stepper__step.pf-m-info, +.mui-theme .pf-v6-c-progress-stepper__step.pf-m-info, .mui-theme .pf-v6-c-progress-stepper__step.pf-m-success { --pf-v6-c-progress-stepper__step-icon--BackgroundColor: var(--mui-palette-primary-main); --pf-v6-c-progress-stepper__step-icon--Color: var(--mui-palette-common-white); @@ -828,4 +828,13 @@ .mui-theme .pf-v6-c-masthead { padding-left: var(--kf-central-app-drawer-width); } +} + +.mui-theme .pf-v6-c-toolbar__group.pf-m-filter-group .pf-v6-c-form-control { + + // Override default form control padding to match button padding in this context + --pf-v6-c-form-control--PaddingBlockStart: var(--mui-spacing-8px); + --pf-v6-c-form-control--PaddingBlockEnd: var(--mui-spacing-8px); + + } \ No newline at end of file