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 748f4dd7..968d2269 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 @@ -16,13 +16,21 @@ describe('Application', () => { cy.intercept('GET', '/api/v1/namespaces', { body: mockBFFResponse(mockNamespaces), }); + cy.intercept('GET', '/api/v1/workspaces', { + body: mockBFFResponse(mockWorkspaces), + }).as('getWorkspaces'); 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(); + + // Wait for the API call before trying to interact with the UI + cy.wait('@getWorkspaces'); + useFilter('name', 'Name', 'My'); cy.get("[id$='workspaces-table-content']").find('tr').should('have.length', 2); cy.get("[id$='workspaces-table-row-1']").contains('My First Jupyter Notebook'); diff --git a/workspaces/frontend/src/app/components/WorkspaceTable.tsx b/workspaces/frontend/src/app/components/WorkspaceTable.tsx index 8be07a03..a29bf1bb 100644 --- a/workspaces/frontend/src/app/components/WorkspaceTable.tsx +++ b/workspaces/frontend/src/app/components/WorkspaceTable.tsx @@ -1,5 +1,4 @@ -import React, { useCallback, useImperativeHandle, useMemo, useRef, useState } from 'react'; -import { PageSection } from '@patternfly/react-core/dist/esm/components/Page'; +import React, { useCallback, useImperativeHandle, useMemo, useState } from 'react'; import { TimestampTooltipVariant, Timestamp, @@ -14,6 +13,20 @@ import { Tooltip } from '@patternfly/react-core/dist/esm/components/Tooltip'; import { Bullseye } from '@patternfly/react-core/dist/esm/layouts/Bullseye'; import { Button } from '@patternfly/react-core/dist/esm/components/Button'; import { Icon } from '@patternfly/react-core/dist/esm/components/Icon'; +import { + Toolbar, + ToolbarContent, + ToolbarItem, + ToolbarGroup, + ToolbarFilter, + ToolbarToggleGroup, +} from '@patternfly/react-core/dist/esm/components/Toolbar'; +import { + Select, + SelectList, + SelectOption, +} from '@patternfly/react-core/dist/esm/components/Select'; +import { MenuToggle } from '@patternfly/react-core/dist/esm/components/MenuToggle'; import { Table, Thead, @@ -25,18 +38,14 @@ import { ActionsColumn, IActions, } from '@patternfly/react-table/dist/esm/components/Table'; +import { FilterIcon } from '@patternfly/react-icons/dist/esm/icons/filter-icon'; import { InfoCircleIcon } from '@patternfly/react-icons/dist/esm/icons/info-circle-icon'; import { ExclamationTriangleIcon } from '@patternfly/react-icons/dist/esm/icons/exclamation-triangle-icon'; import { TimesCircleIcon } from '@patternfly/react-icons/dist/esm/icons/times-circle-icon'; import { QuestionCircleIcon } from '@patternfly/react-icons/dist/esm/icons/question-circle-icon'; import { formatDistanceToNow } from 'date-fns/formatDistanceToNow'; import { Workspace, WorkspaceState } from '~/shared/api/backendApiTypes'; -import { - DataFieldKey, - defineDataFields, - FilterableDataFieldKey, - SortableDataFieldKey, -} from '~/app/filterableDataHelper'; +import { DataFieldKey, defineDataFields, SortableDataFieldKey } from '~/app/filterableDataHelper'; import { useTypedNavigate } from '~/app/routerHelper'; import { buildKindLogoDictionary, @@ -44,8 +53,7 @@ import { } from '~/app/actions/WorkspaceKindsActions'; import useWorkspaceKinds from '~/app/hooks/useWorkspaceKinds'; import { WorkspaceConnectAction } from '~/app/pages/Workspaces/WorkspaceConnectAction'; -import CustomEmptyState from '~/shared/components/CustomEmptyState'; -import Filter, { FilteredColumn, FilterRef } from '~/shared/components/Filter'; +import ThemeAwareSearchInput from '~/app/components/ThemeAwareSearchInput'; import WithValidImage from '~/shared/components/WithValidImage'; import ImageFallback from '~/shared/components/ImageFallback'; import { @@ -53,12 +61,12 @@ import { formatWorkspaceIdleState, } from '~/shared/utilities/WorkspaceUtils'; import { ExpandedWorkspaceRow } from '~/app/pages/Workspaces/ExpandedWorkspaceRow'; +import CustomEmptyState from '~/shared/components/CustomEmptyState'; const { fields: wsTableColumns, keyArray: wsTableColumnKeyArray, sortableKeyArray: sortableWsTableColumnKeyArray, - filterableKeyArray: filterableWsTableColumnKeyArray, } = defineDataFields({ name: { label: 'Name', isFilterable: true, isSortable: true, width: 35 }, image: { label: 'Image', isFilterable: true, isSortable: true, width: 25 }, @@ -73,21 +81,40 @@ const { }); export type WorkspaceTableColumnKeys = DataFieldKey; -type WorkspaceTableFilterableColumnKeys = FilterableDataFieldKey; type WorkspaceTableSortableColumnKeys = SortableDataFieldKey; -export type WorkspaceTableFilteredColumn = FilteredColumn; interface WorkspaceTableProps { workspaces: Workspace[]; canCreateWorkspaces?: boolean; canExpandRows?: boolean; - initialFilters?: WorkspaceTableFilteredColumn[]; hiddenColumns?: WorkspaceTableColumnKeys[]; rowActions?: (workspace: Workspace) => IActions; } +const allFiltersConfig = { + name: { label: 'Name', placeholder: 'Filter by name' }, + kind: { label: 'Kind', placeholder: 'Filter by kind' }, + image: { label: 'Image', placeholder: 'Filter by image' }, + state: { label: 'State', placeholder: 'Filter by state' }, + namespace: { label: 'Namespace' }, + idleGpu: { label: 'Idle GPU' }, +} as const; + +// Defines which of the above filters should appear in the dropdown +const dropdownFilterKeys = ['name', 'kind', 'image', 'state'] as const; + +const filterConfigs = dropdownFilterKeys.map((key) => ({ + key, + label: allFiltersConfig[key].label, + placeholder: allFiltersConfig[key].placeholder!, // '!' asserts placeholder is not undefined here +})); + +type FilterKey = keyof typeof allFiltersConfig; +type FilterLabel = (typeof allFiltersConfig)[FilterKey]['label']; + export interface WorkspaceTableRef { - addFilter: (filter: WorkspaceTableFilteredColumn) => void; + clearAllFilters: () => void; + setFilter: (key: FilterKey, value: string) => void; } const WorkspaceTable = React.forwardRef( @@ -96,7 +123,6 @@ const WorkspaceTable = React.forwardRef( workspaces, canCreateWorkspaces = true, canExpandRows = true, - initialFilters = [], hiddenColumns = [], rowActions = () => [], }, @@ -104,7 +130,15 @@ const WorkspaceTable = React.forwardRef( ) => { const [workspaceKinds] = useWorkspaceKinds(); const [expandedWorkspacesNames, setExpandedWorkspacesNames] = useState([]); - const [filters, setFilters] = useState(initialFilters); + const [filters, setFilters] = useState>({ + name: '', + kind: '', + image: '', + state: '', + namespace: '', + idleGpu: '', + }); + const [activeSortColumnKey, setActiveSortColumnKey] = useState(null); const [activeSortDirection, setActiveSortDirection] = useState<'asc' | 'desc' | null>(null); @@ -112,10 +146,66 @@ const WorkspaceTable = React.forwardRef( const [perPage, setPerPage] = useState(10); const navigate = useTypedNavigate(); - const filterRef = useRef(null); const kindLogoDict = buildKindLogoDictionary(workspaceKinds); const workspaceRedirectStatus = buildWorkspaceRedirectStatus(workspaceKinds); + // Use the derived FilterLabel type for the active menu + const [activeAttributeMenu, setActiveAttributeMenu] = useState('Name'); + const [isAttributeMenuOpen, setIsAttributeMenuOpen] = useState(false); + + const handleFilterChange = useCallback((key: FilterKey, value: string) => { + setFilters((prev) => ({ ...prev, [key]: value })); + }, []); + + const clearAllFilters = useCallback(() => { + setFilters({ + name: '', + kind: '', + image: '', + state: '', + namespace: '', + idleGpu: '', + }); + }, []); + + const onAttributeToggleClick = useCallback(() => { + setIsAttributeMenuOpen((prev) => !prev); + }, []); + + const attributeDropdown = useMemo( + () => ( + + ), + [isAttributeMenuOpen, activeAttributeMenu, onAttributeToggleClick], + ); + const visibleColumnKeys: WorkspaceTableColumnKeys[] = useMemo( () => hiddenColumns.length @@ -129,39 +219,32 @@ const WorkspaceTable = React.forwardRef( [visibleColumnKeys], ); - const visibleFilterableColumnKeys: WorkspaceTableFilterableColumnKeys[] = useMemo( - () => filterableWsTableColumnKeyArray.filter((col) => visibleColumnKeys.includes(col)), - [visibleColumnKeys], - ); - - const visibleFilterableColumnMap = useMemo( - () => - Object.fromEntries( - visibleFilterableColumnKeys.map((key) => [key, wsTableColumns[key].label]), - ) as Record, - [visibleFilterableColumnKeys], - ); - useImperativeHandle(ref, () => ({ - addFilter: (newFilter: WorkspaceTableFilteredColumn) => { - if (!visibleFilterableColumnKeys.includes(newFilter.columnKey)) { - return; - } - - setFilters((prev) => { - const existingIndex = prev.findIndex((f) => f.columnKey === newFilter.columnKey); - if (existingIndex !== -1) { - return prev.map((f, i) => (i === existingIndex ? newFilter : f)); - } - return [...prev, newFilter]; - }); - }, + clearAllFilters, + setFilter: handleFilterChange, })); const createWorkspace = useCallback(() => { navigate('workspaceCreate'); }, [navigate]); + const emptyState = useMemo( + () => , + [clearAllFilters], + ); + + const filterableProperties: Record string> = useMemo( + () => ({ + name: (ws) => ws.name, + kind: (ws) => ws.workspaceKind.name, + image: (ws) => ws.podTemplate.options.imageConfig.current.displayName, + state: (ws) => ws.state, + namespace: (ws) => ws.namespace, + idleGpu: (ws) => formatWorkspaceIdleState(ws), + }), + [], + ); + const setWorkspaceExpanded = (workspace: Workspace, isExpanding = true) => setExpandedWorkspacesNames((prevExpanded) => { const newExpandedWorkspacesNames = prevExpanded.filter( @@ -179,37 +262,29 @@ const WorkspaceTable = React.forwardRef( if (workspaces.length === 0) { return []; } - - return filters.reduce((result, filter) => { - let searchValueInput: RegExp; - try { - searchValueInput = new RegExp(filter.value, 'i'); - } catch { - searchValueInput = new RegExp(filter.value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'i'); + const testRegex = (value: string, searchValue: string) => { + if (!searchValue) { + return true; } + try { + return new RegExp(searchValue, 'i').test(value); + } catch { + return new RegExp(searchValue.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'i').test(value); + } + }; - return result.filter((ws) => { - switch (filter.columnKey as WorkspaceTableFilterableColumnKeys) { - case 'name': - return ws.name.match(searchValueInput); - case 'kind': - return ws.workspaceKind.name.match(searchValueInput); - case 'namespace': - return ws.namespace.match(searchValueInput); - case 'image': - return ws.podTemplate.options.imageConfig.current.displayName.match(searchValueInput); - case 'state': - return ws.state.match(searchValueInput); - case 'gpu': - return formatResourceFromWorkspace(ws, 'gpu').match(searchValueInput); - case 'idleGpu': - return formatWorkspaceIdleState(ws).match(searchValueInput); - default: - return true; - } - }); - }, workspaces); - }, [workspaces, filters]); + const activeFilters = Object.entries(filters).filter(([, value]) => value); + if (activeFilters.length === 0) { + return workspaces; + } + + return workspaces.filter((ws) => + activeFilters.every(([key, searchValue]) => { + const propertyGetter = filterableProperties[key as FilterKey]; + return testRegex(propertyGetter(ws), searchValue); + }), + ); + }, [workspaces, filters, filterableProperties]); // Column sorting @@ -317,8 +392,6 @@ const WorkspaceTable = React.forwardRef( } }; - // Redirect Status Icons - const getRedirectStatusIcon = (level: string | undefined, message: string) => { switch (level) { case 'Info': @@ -383,22 +456,68 @@ const WorkspaceTable = React.forwardRef( }; return ( - + <> - - Create workspace - - ) - } - /> + + + } breakpoint="xl"> + + {attributeDropdown} + {filterConfigs.map(({ key, label, placeholder }) => ( + handleFilterChange(key, '')} + deleteLabelGroup={() => handleFilterChange(key, '')} + categoryName={label} + showToolbarItem={activeAttributeMenu === label} + > + + handleFilterChange(key, value)} + placeholder={placeholder} + fieldLabel={placeholder} + aria-label={placeholder} + data-testid="filter-workspaces-search-input" + /> + + + ))} + {Object.entries(filters).map(([key, value]) => { + // Check if the key is not in the dropdown config and has a value + const isWsSummaryFilter = !filterConfigs.some((config) => config.key === key); + if (!isWsSummaryFilter || !value) { + return null; + } + + return ( + handleFilterChange(key as FilterKey, '')} + categoryName={allFiltersConfig[key as FilterKey].label} + // eslint-disable-next-line react/no-children-prop + children={undefined} + /> + ); + })} + {canCreateWorkspaces && ( + + + + )} + + + + ( ))} {sortedWorkspaces.length === 0 && ( - - - - - + + + )}
- - filterRef.current?.clearAll()} /> - -
+ {emptyState} +
( onSetPage={onSetPage} onPerPageSelect={onPerPageSelect} /> -
+ ); }, ); diff --git a/workspaces/frontend/src/app/pages/WorkspaceKinds/WorkspaceKinds.tsx b/workspaces/frontend/src/app/pages/WorkspaceKinds/WorkspaceKinds.tsx index d0868e40..ea3bff29 100644 --- a/workspaces/frontend/src/app/pages/WorkspaceKinds/WorkspaceKinds.tsx +++ b/workspaces/frontend/src/app/pages/WorkspaceKinds/WorkspaceKinds.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import React, { useCallback, useMemo, useState } from 'react'; import { Drawer, DrawerContent, @@ -17,13 +17,11 @@ import { ToolbarToggleGroup, } from '@patternfly/react-core/dist/esm/components/Toolbar'; import { - Menu, - MenuContent, - MenuList, - MenuItem, -} from '@patternfly/react-core/dist/esm/components/Menu'; + Select, + SelectList, + SelectOption, +} from '@patternfly/react-core/dist/esm/components/Select'; import { MenuToggle } from '@patternfly/react-core/dist/esm/components/MenuToggle'; -import { Popper } from '@patternfly/react-core/helpers'; import { Bullseye } from '@patternfly/react-core/dist/esm/layouts/Bullseye'; import { Button } from '@patternfly/react-core/dist/esm/components/Button'; import { @@ -200,110 +198,40 @@ export const WorkspaceKinds: React.FunctionComponent = () => { // Set up status single select const [isStatusMenuOpen, setIsStatusMenuOpen] = useState(false); - const statusToggleRef = useRef(null); - const statusMenuRef = useRef(null); - const statusContainerRef = useRef(null); - const handleStatusMenuKeys = useCallback( - (event: KeyboardEvent) => { - if (isStatusMenuOpen && statusMenuRef.current?.contains(event.target as Node)) { - if (event.key === 'Escape' || event.key === 'Tab') { - setIsStatusMenuOpen(!isStatusMenuOpen); - statusToggleRef.current?.focus(); - } - } - }, - [isStatusMenuOpen], - ); + const onStatusSelect = ( + _event: React.MouseEvent | undefined, + value: string | number | undefined, + ) => { + if (typeof value === 'undefined') { + return; + } + setStatusSelection(value.toString()); + setIsStatusMenuOpen(false); + }; - const handleStatusClickOutside = useCallback( - (event: MouseEvent) => { - if (isStatusMenuOpen && !statusMenuRef.current?.contains(event.target as Node)) { - setIsStatusMenuOpen(false); - } - }, - [isStatusMenuOpen], - ); - - useEffect(() => { - window.addEventListener('keydown', handleStatusMenuKeys); - window.addEventListener('click', handleStatusClickOutside); - return () => { - window.removeEventListener('keydown', handleStatusMenuKeys); - window.removeEventListener('click', handleStatusClickOutside); - }; - }, [isStatusMenuOpen, statusMenuRef, handleStatusClickOutside, handleStatusMenuKeys]); - - const onStatusToggleClick = useCallback((ev: React.MouseEvent) => { - ev.stopPropagation(); - setTimeout(() => { - const firstElement = statusMenuRef.current?.querySelector('li > button:not(:disabled)'); - if (firstElement) { - (firstElement as HTMLElement).focus(); - } - }, 0); - setIsStatusMenuOpen((prev) => !prev); - }, []); - - const onStatusSelect = useCallback( - (event: React.MouseEvent | undefined, itemId: string | number | undefined) => { - if (typeof itemId === 'undefined') { - return; - } - - setStatusSelection(itemId.toString()); - setIsStatusMenuOpen((prev) => !prev); - }, - [], - ); - - const statusToggle = useMemo( - () => ( - - Filter by status - - ), - [isStatusMenuOpen, onStatusToggleClick], - ); - - const statusMenu = useMemo( - () => ( - - - - Deprecated - Active - - - - ), - [statusSelection, onStatusSelect], - ); - - const statusSelect = useMemo( - () => ( -
- -
- ), - [statusToggle, statusMenu, isStatusMenuOpen], + const statusSelect = ( + ); // Set up attribute selector @@ -311,108 +239,33 @@ export const WorkspaceKinds: React.FunctionComponent = () => { 'Name', ); const [isAttributeMenuOpen, setIsAttributeMenuOpen] = useState(false); - const attributeToggleRef = useRef(null); - const attributeMenuRef = useRef(null); - const attributeContainerRef = useRef(null); - const handleAttributeMenuKeys = useCallback( - (event: KeyboardEvent) => { - if (!isAttributeMenuOpen) { - return; - } - if ( - attributeMenuRef.current?.contains(event.target as Node) || - attributeToggleRef.current?.contains(event.target as Node) - ) { - if (event.key === 'Escape' || event.key === 'Tab') { - setIsAttributeMenuOpen(!isAttributeMenuOpen); - attributeToggleRef.current?.focus(); - } - } - }, - [isAttributeMenuOpen], - ); - - const handleAttributeClickOutside = useCallback( - (event: MouseEvent) => { - if (isAttributeMenuOpen && !attributeMenuRef.current?.contains(event.target as Node)) { + const attributeDropdown = ( + ); const emptyState = useMemo( diff --git a/workspaces/frontend/src/app/pages/WorkspaceKinds/summary/WorkspaceKindSummary.tsx b/workspaces/frontend/src/app/pages/WorkspaceKinds/summary/WorkspaceKindSummary.tsx index c1f2b94a..18367fca 100644 --- a/workspaces/frontend/src/app/pages/WorkspaceKinds/summary/WorkspaceKindSummary.tsx +++ b/workspaces/frontend/src/app/pages/WorkspaceKinds/summary/WorkspaceKindSummary.tsx @@ -5,10 +5,7 @@ import { Stack, StackItem } from '@patternfly/react-core/dist/esm/layouts/Stack' import { Breadcrumb } from '@patternfly/react-core/dist/esm/components/Breadcrumb'; import { BreadcrumbItem } from '@patternfly/react-core/dist/esm/components/Breadcrumb/BreadcrumbItem'; import { useTypedLocation, useTypedParams } from '~/app/routerHelper'; -import WorkspaceTable, { - WorkspaceTableFilteredColumn, - WorkspaceTableRef, -} from '~/app/components/WorkspaceTable'; +import WorkspaceTable, { WorkspaceTableRef } from '~/app/components/WorkspaceTable'; import { useWorkspacesByKind } from '~/app/hooks/useWorkspaces'; import WorkspaceKindSummaryExpandableCard from '~/app/pages/WorkspaceKinds/summary/WorkspaceKindSummaryExpandableCard'; import { DEFAULT_POLLING_RATE_MS } from '~/app/const'; @@ -37,11 +34,17 @@ const WorkspaceKindSummary: React.FC = () => { const tableRowActions = useWorkspaceRowActions([{ id: 'viewDetails' }]); const onAddFilter = useCallback( - (filter: WorkspaceTableFilteredColumn) => { + (columnKey: string, value: string) => { if (!workspaceTableRef.current) { return; } - workspaceTableRef.current.addFilter(filter); + // Map to valid filter keys from WorkspaceTable + const validKeys = ['name', 'kind', 'image', 'state', 'namespace', 'idleGpu'] as const; + type ValidKey = (typeof validKeys)[number]; + + if (validKeys.includes(columnKey as ValidKey)) { + workspaceTableRef.current.setFilter(columnKey as ValidKey, value); + } }, [workspaceTableRef], ); diff --git a/workspaces/frontend/src/app/pages/WorkspaceKinds/summary/WorkspaceKindSummaryExpandableCard.tsx b/workspaces/frontend/src/app/pages/WorkspaceKinds/summary/WorkspaceKindSummaryExpandableCard.tsx index 66e4af9d..6373c059 100644 --- a/workspaces/frontend/src/app/pages/WorkspaceKinds/summary/WorkspaceKindSummaryExpandableCard.tsx +++ b/workspaces/frontend/src/app/pages/WorkspaceKinds/summary/WorkspaceKindSummaryExpandableCard.tsx @@ -20,7 +20,6 @@ import { groupWorkspacesByNamespaceAndGpu, YesNoValue, } from '~/shared/utilities/WorkspaceUtils'; -import { WorkspaceTableFilteredColumn } from '~/app/components/WorkspaceTable'; const TOP_GPU_CONSUMERS_LIMIT = 2; @@ -28,7 +27,7 @@ interface WorkspaceKindSummaryExpandableCardProps { workspaces: Workspace[]; isExpanded: boolean; onExpandToggle: () => void; - onAddFilter: (filter: WorkspaceTableFilteredColumn) => void; + onAddFilter: (columnKey: string, value: string) => void; } const WorkspaceKindSummaryExpandableCard: React.FC = ({ @@ -73,7 +72,7 @@ const WorkspaceKindSummaryExpandableCard: React.FC { - onAddFilter({ columnKey: 'idleGpu', value: YesNoValue.Yes }); + onAddFilter('idleGpu', YesNoValue.Yes); }} > {filterIdleWorkspacesWithGpu(workspaces).length} @@ -141,7 +140,7 @@ const SectionDivider: React.FC = () => ( interface NamespaceConsumerProps { namespace: string; gpuCount: number; - onAddFilter: (filter: WorkspaceTableFilteredColumn) => void; + onAddFilter: (columnKey: string, value: string) => void; } const NamespaceGpuConsumer: React.FC = ({ @@ -154,7 +153,7 @@ const NamespaceGpuConsumer: React.FC = ({ variant="link" isInline onClick={() => { - onAddFilter({ columnKey: 'namespace', value: namespace }); + onAddFilter('namespace', namespace); }} > {namespace} diff --git a/workspaces/frontend/src/shared/style/MUI-theme.scss b/workspaces/frontend/src/shared/style/MUI-theme.scss index ddaf6135..9d98e53c 100644 --- a/workspaces/frontend/src/shared/style/MUI-theme.scss +++ b/workspaces/frontend/src/shared/style/MUI-theme.scss @@ -510,8 +510,8 @@ .mui-theme .pf-v6-c-menu { --pf-v6-c-menu--BoxShadow: var(--mui-shadows-8); --pf-v6-c-menu--BorderRadius: var(--mui-shape-borderRadius); - --pf-v6-c-menu--PaddingBlockStart: var(--mui-spacing); - --pf-v6-c-menu--PaddingBlockEnd: var(--mui-spacing); + --pf-v6-c-menu--PaddingBlockStart: none; + --pf-v6-c-menu--PaddingBlockEnd: none; --pf-v6-c-menu--PaddingInlineStart: var(--mui-spacing); --pf-v6-c-menu--PaddingInlineEnd: var(--mui-spacing); --pf-v6-c-menu__item--PaddingBlockStart: var(--mui-menu__item--PaddingBlockStart); @@ -546,6 +546,8 @@ font-weight: var(--mui-button-font-weight); letter-spacing: 0.02857em; + height: 37px; + } .mui-theme .pf-v6-c-menu-toggle__button { @@ -919,7 +921,7 @@ margin-block-end: var(--mui-spacing-16px); } -.workspacekind-file-upload { +.mui-theme .workspacekind-file-upload { height: 100%; .pf-v6-c-file-upload__file-details { @@ -928,7 +930,31 @@ } /* Workaround for Toggle group header in Workspace Kind Form */ -.workspace-kind-form-header .pf-v6-c-toggle-group__button.pf-m-selected { +.mui-theme .workspace-kind-form-header .pf-v6-c-toggle-group__button.pf-m-selected { background-color: #e0f0ff; color: var(--pf-t--color--black); } + + +.mui-theme .pf-v6-c-menu__item { + &.pf-m-selected { + --pf-v6-c-menu__item--BackgroundColor: rgba( + var(--mui-palette-primary-mainChannel, 25 118 210) / + var(--mui-palette-action-selectedOpacity, 0.08) + ); + --pf-v6-c-menu__item--FontWeight: var(--mui-button-font-weight); + + .pf-v6-c-menu__item-select-icon { + visibility: hidden; + } + } +} + +.mui-theme button.pf-v6-c-menu-toggle { + // Use box-shadow to create a border effect without affecting the layout + &.pf-m-expanded, + &:focus { + box-shadow: 0 0 0 2px var(--mui-palette-primary-main); + outline: none; // Remove default browser outline + } +}