diff --git a/frontend/packages/console-app/src/components/data-view/ConsoleDataView.scss b/frontend/packages/console-app/src/components/data-view/ConsoleDataView.scss new file mode 100644 index 00000000000..96af174138e --- /dev/null +++ b/frontend/packages/console-app/src/components/data-view/ConsoleDataView.scss @@ -0,0 +1,7 @@ +// Hide the filter category selector when there's only one filter. +// We use CSS instead of TypeScript prop changes because PatternFly's +// DataViewFilters component doesn't expose a prop to hide the category selector. +// This CSS-only solution avoids forking or patching the upstream component. +.co-console-data-view-single-filter .pf-v6-c-toolbar__group.pf-m-filter-group > *:first-child { + display: none !important; +} diff --git a/frontend/packages/console-app/src/components/data-view/ConsoleDataView.tsx b/frontend/packages/console-app/src/components/data-view/ConsoleDataView.tsx index 620fb2f89b3..979390be811 100644 --- a/frontend/packages/console-app/src/components/data-view/ConsoleDataView.tsx +++ b/frontend/packages/console-app/src/components/data-view/ConsoleDataView.tsx @@ -1,5 +1,6 @@ import type { FC, ReactNode } from 'react'; import { useCallback, useMemo, useState } from 'react'; +import './ConsoleDataView.scss'; import { ResponsiveAction, ResponsiveActions, @@ -10,11 +11,11 @@ import { DataView, DataViewState, DataViewTable, - DataViewTextFilter, DataViewToolbar, } from '@patternfly/react-data-view'; import DataViewFilters from '@patternfly/react-data-view/dist/cjs/DataViewFilters'; import { ColumnsIcon, UndoIcon } from '@patternfly/react-icons'; +import { css } from '@patternfly/react-styles'; import { InnerScrollContainer, Tbody, Td, Tr } from '@patternfly/react-table'; import { useTranslation } from 'react-i18next'; import type { @@ -26,6 +27,7 @@ import { LazyColumnManagementModalOverlay } from '@console/internal/components/m import { EmptyBox } from '@console/shared/src/components/empty-state/EmptyBox'; import { StatusBox } from '@console/shared/src/components/status/StatusBox'; import { DataViewLabelFilter } from './DataViewLabelFilter'; +import { DataViewTextFilter } from './DataViewTextFilter'; import { useConsoleDataViewData } from './useConsoleDataViewData'; import { useConsoleDataViewFilters } from './useConsoleDataViewFilters'; @@ -170,7 +172,10 @@ export const ConsoleDataView = < loadError={loadError} skeleton={
} > - + 0 && ( diff --git a/frontend/packages/console-app/src/components/data-view/DataViewLabelFilter.tsx b/frontend/packages/console-app/src/components/data-view/DataViewLabelFilter.tsx index a522dfb8d15..304a011dc57 100644 --- a/frontend/packages/console-app/src/components/data-view/DataViewLabelFilter.tsx +++ b/frontend/packages/console-app/src/components/data-view/DataViewLabelFilter.tsx @@ -45,7 +45,7 @@ export const DataViewLabelFilter = ({ applyLabelFilters([]); }} > -
+
{ diff --git a/frontend/packages/console-app/src/components/data-view/DataViewTextFilter.tsx b/frontend/packages/console-app/src/components/data-view/DataViewTextFilter.tsx new file mode 100644 index 00000000000..43e59a3d9f2 --- /dev/null +++ b/frontend/packages/console-app/src/components/data-view/DataViewTextFilter.tsx @@ -0,0 +1,55 @@ +import type { FormEvent } from 'react'; +import { useEffect, useState } from 'react'; +import { ToolbarFilter } from '@patternfly/react-core'; +import { useSearchParams } from 'react-router'; +import { TextFilter } from '@console/internal/components/factory/text-filter'; + +type DataViewTextFilterProps = { + title: string; + filterId: string; + placeholder: string; + onChange?: (key: string, selectedValue: string) => void; + showToolbarItem?: boolean; +}; + +export const DataViewTextFilter = ({ + title, + filterId, + placeholder, + onChange, + showToolbarItem, +}: DataViewTextFilterProps) => { + const [searchParams] = useSearchParams(); + const [inputText, setInputText] = useState(searchParams.get(filterId) ?? ''); + + // Sync local state with URL changes + useEffect(() => { + setInputText(searchParams.get(filterId) ?? ''); + }, [searchParams, filterId]); + + const handleChange = (_event: FormEvent, value: string) => { + setInputText(value); + onChange?.(filterId, value); + }; + + const handleDeleteChip = () => { + setInputText(''); + onChange?.(filterId, ''); + }; + + return ( + + + + ); +}; diff --git a/frontend/packages/helm-plugin/integration-tests/support/pages/helm/helm-page.ts b/frontend/packages/helm-plugin/integration-tests/support/pages/helm/helm-page.ts index e1c4a243603..7d940ef8066 100644 --- a/frontend/packages/helm-plugin/integration-tests/support/pages/helm/helm-page.ts +++ b/frontend/packages/helm-plugin/integration-tests/support/pages/helm/helm-page.ts @@ -12,7 +12,7 @@ export const helmPage = { search: (name: string) => { cy.get(helmPO.filters).within(() => cy.get('.pf-v6-c-menu-toggle').first().click()); cy.get('.pf-v6-c-menu__list-item').contains('Name').click(); - cy.get('[aria-label="Name filter"]').clear().type(name); + cy.get('[aria-label="Filter by name"]').clear().type(name); }, verifyHelmReleasesDisplayed: () => cy.get(helmPO.table).should('be.visible'), clickHelmReleaseName: (name: string) => cy.get(`a[title="${name}"]`).click(), diff --git a/frontend/packages/integration-tests-cypress/tests/app/filtering-and-searching.cy.ts b/frontend/packages/integration-tests-cypress/tests/app/filtering-and-searching.cy.ts index 56597cd74f1..c745fc1c325 100644 --- a/frontend/packages/integration-tests-cypress/tests/app/filtering-and-searching.cy.ts +++ b/frontend/packages/integration-tests-cypress/tests/app/filtering-and-searching.cy.ts @@ -8,6 +8,24 @@ import * as yamlEditor from '../../views/yaml-editor'; const SEARCH_NAMESPACE = 'openshift-authentication-operator'; const SEARCH_DEPLOYMENT_NAME = 'authentication-operator'; +const SINGLE_FILTER_GROUP_SELECTOR = + '.co-console-data-view-single-filter .pf-v6-c-toolbar__group.pf-m-filter-group'; + +const verifySingleFilterCategoryHidden = (expectedToggles: number) => { + cy.get('[data-test="data-view-table"]').should('exist'); + cy.get(SINGLE_FILTER_GROUP_SELECTOR) + .find('.pf-v6-c-menu-toggle') + .should('have.length', expectedToggles); + + if (expectedToggles === 1) { + cy.get(SINGLE_FILTER_GROUP_SELECTOR).find('.pf-v6-c-menu-toggle').should('not.be.visible'); + } else { + cy.get(SINGLE_FILTER_GROUP_SELECTOR) + .find('.pf-v6-c-menu-toggle') + .first() + .should('not.be.visible'); + } +}; describe('Filtering and Searching', () => { let WORKLOAD_NAME; @@ -105,4 +123,15 @@ describe('Filtering and Searching', () => { }); listPage.dvRows.countShouldBe(3); }); + + it('ConsoleDataView filter toolbar should not display a filter select', () => { + cy.log('when a text filter is the only filter'); + cy.visit('/settings/cluster/alertmanagerconfig?page=1&perPage=50'); + verifySingleFilterCategoryHidden(1); + + cy.log('when a select filter is the only filter'); + cy.visit('/search/all-namespaces?page=1&perPage=50&kind=core~v1~Pod'); + listPage.dvRows.shouldBeLoaded(); + verifySingleFilterCategoryHidden(2); + }); }); diff --git a/frontend/packages/integration-tests-cypress/tests/cluster-settings/alertmanager/receivers/email.cy.ts b/frontend/packages/integration-tests-cypress/tests/cluster-settings/alertmanager/receivers/email.cy.ts index 09ac5cc724a..e691b7b0f01 100644 --- a/frontend/packages/integration-tests-cypress/tests/cluster-settings/alertmanager/receivers/email.cy.ts +++ b/frontend/packages/integration-tests-cypress/tests/cluster-settings/alertmanager/receivers/email.cy.ts @@ -49,7 +49,7 @@ describe('Alertmanager: Email Receiver Form', () => { alertmanager.save(); cy.log('verify Email Receiver was created correctly'); - alertmanager.validateCreation(receiverName); + alertmanager.validateCreation(receiverName, 'integration-types', 'routing-labels'); alertmanager.visitYAMLPage(); yamlEditor.getEditorContent().then((content) => { const configs = getGlobalsAndReceiverConfig(receiverName, configName, content); diff --git a/frontend/packages/integration-tests-cypress/tests/cluster-settings/alertmanager/receivers/pagerduty.cy.ts b/frontend/packages/integration-tests-cypress/tests/cluster-settings/alertmanager/receivers/pagerduty.cy.ts index ba3e1f856de..608c885cebe 100644 --- a/frontend/packages/integration-tests-cypress/tests/cluster-settings/alertmanager/receivers/pagerduty.cy.ts +++ b/frontend/packages/integration-tests-cypress/tests/cluster-settings/alertmanager/receivers/pagerduty.cy.ts @@ -50,7 +50,7 @@ describe('Alertmanager: PagerDuty Receiver Form', () => { alertmanager.save(); cy.log('verify PagerDuty Receiver was created correctly'); - alertmanager.validateCreation(receiverName); + alertmanager.validateCreation(receiverName, 'integration-types', 'routing-labels'); cy.log('update pagerduty_url'); listPage.dvRows.clickKebabAction(receiverName, 'Edit Receiver'); diff --git a/frontend/packages/integration-tests-cypress/tests/cluster-settings/alertmanager/receivers/slack.cy.ts b/frontend/packages/integration-tests-cypress/tests/cluster-settings/alertmanager/receivers/slack.cy.ts index e7c8c7f3eaf..3e8d679d814 100644 --- a/frontend/packages/integration-tests-cypress/tests/cluster-settings/alertmanager/receivers/slack.cy.ts +++ b/frontend/packages/integration-tests-cypress/tests/cluster-settings/alertmanager/receivers/slack.cy.ts @@ -52,7 +52,7 @@ describe('Alertmanager: Slack Receiver Form', () => { alertmanager.save(); cy.log('verify Slack Receiver was created correctly'); - alertmanager.validateCreation(receiverName); + alertmanager.validateCreation(receiverName, 'integration-types', 'routing-labels'); alertmanager.visitYAMLPage(); yamlEditor.getEditorContent().then((content) => { const configs = getGlobalsAndReceiverConfig(receiverName, configName, content); diff --git a/frontend/packages/integration-tests-cypress/tests/cluster-settings/alertmanager/receivers/webhook.cy.ts b/frontend/packages/integration-tests-cypress/tests/cluster-settings/alertmanager/receivers/webhook.cy.ts index 7edaa453844..0ec694a7b91 100644 --- a/frontend/packages/integration-tests-cypress/tests/cluster-settings/alertmanager/receivers/webhook.cy.ts +++ b/frontend/packages/integration-tests-cypress/tests/cluster-settings/alertmanager/receivers/webhook.cy.ts @@ -34,7 +34,7 @@ describe('Alertmanager: Webhook Receiver Form', () => { alertmanager.save(); cy.log('verify Webhook Receiver was created correctly'); - alertmanager.validateCreation(receiverName); + alertmanager.validateCreation(receiverName, 'integration-types', 'routing-labels'); alertmanager.visitYAMLPage(); yamlEditor.getEditorContent().then((content) => { const configs = getGlobalsAndReceiverConfig(receiverName, configName, content); diff --git a/frontend/packages/integration-tests-cypress/views/alertmanager.ts b/frontend/packages/integration-tests-cypress/views/alertmanager.ts index 66f7716af9c..cba660b292b 100644 --- a/frontend/packages/integration-tests-cypress/views/alertmanager.ts +++ b/frontend/packages/integration-tests-cypress/views/alertmanager.ts @@ -68,10 +68,14 @@ export const alertmanager = { cy.exec( `kubectl patch secret 'alertmanager-main' -n 'openshift-monitoring' --type='json' -p='[{ op: 'replace', path: '/data/alertmanager.yaml', value: ${defaultAlertmanagerYaml}}]'`, ), - save: () => cy.byTestID('save-changes').should('be.enabled').click(), + save: () => { + cy.byTestID('save-changes').should('be.enabled').click(); + // wait for the changes to rollout + // eslint-disable-next-line cypress/no-unnecessary-waiting + cy.wait(10000); + }, showAdvancedConfiguration: () => cy.byTestID('advanced-configuration').find('button').click(), validateCreation: (receiverName: string, typeCellName: string, labelCellName: string) => { - listPage.dvFilter.byName(receiverName); listPage.dvRows.shouldExist(receiverName); listPage.dvRows.shouldExist(receiverName, typeCellName); listPage.dvRows.shouldExist(receiverName, labelCellName); diff --git a/frontend/packages/integration-tests-cypress/views/list-page.ts b/frontend/packages/integration-tests-cypress/views/list-page.ts index 121db421453..7cf97dcb4aa 100644 --- a/frontend/packages/integration-tests-cypress/views/list-page.ts +++ b/frontend/packages/integration-tests-cypress/views/list-page.ts @@ -62,7 +62,7 @@ export const listPage = { cy.get('.pf-v6-c-menu-toggle').first().click(), ); cy.get('.pf-v6-c-menu__list-item').contains('Name').click(); - cy.get('[aria-label="Name filter"]').clear().type(name); + cy.get('[aria-label="Filter by name"]').clear().type(name); }, by: (checkboxLabel: string) => { cy.get('[data-ouia-component-id="DataViewCheckboxFilter"]').click(); diff --git a/frontend/public/components/_autocomplete.scss b/frontend/public/components/_autocomplete.scss index b5f47209a5c..0cfb06685c7 100644 --- a/frontend/public/components/_autocomplete.scss +++ b/frontend/public/components/_autocomplete.scss @@ -2,12 +2,6 @@ position: relative; width: 100%; z-index: var(--pf-t--global--z-index--sm); - @media (max-width: $screen-xs-min) { - max-width: calc(100% - 95px); - } - @media (min-width: $screen-xs-min) and (max-width: $screen-sm-min) { - max-width: 200px; - } } .co-suggestion-box__suggestions { @@ -17,9 +11,6 @@ gap: var(--pf-t--global--spacer--gap--group--vertical); position: absolute; width: 100%; - @media (min-width: $screen-xs-min) and (max-width: $screen-sm-min) { - max-width: 200px; - } } .co-suggestion-box__suggestions--shadowed { diff --git a/frontend/public/components/_filter-toolbar.scss b/frontend/public/components/_filter-toolbar.scss index 30fb246ffaa..644725d4ff3 100644 --- a/frontend/public/components/_filter-toolbar.scss +++ b/frontend/public/components/_filter-toolbar.scss @@ -1,29 +1,3 @@ -.co-filter-dropdown__item { - display: inline-flex; - pointer-events: none; -} - -.co-filter-dropdown__list-item { - list-style: none; -} - -/* No way to reach this ul */ -.co-filter-dropdown-group > ul { - margin-left: 0; - padding-left: 0; -} - -.co-filter-dropdown-item { - display: inline-flex; - margin: var(--pf-t--global--spacer--xs) 0; -} - .co-filter-dropdown-item__name { padding: 0 var(--pf-t--global--spacer--xs); } - -@media (min-width: $pf-v6-global--breakpoint--md) { - .co-filter-group { - width: 350px !important; // enable full placeholder text to display - } -} diff --git a/frontend/public/components/filter-toolbar.tsx b/frontend/public/components/filter-toolbar.tsx index 831a78a2e8b..3a45d126fa4 100644 --- a/frontend/public/components/filter-toolbar.tsx +++ b/frontend/public/components/filter-toolbar.tsx @@ -444,7 +444,7 @@ export const FilterToolbar: FC = ({ }} categoryName={translatedNameFilterTitle} > -
+
{showSearchFiltersDropdown && (