From 2fe8f06edafc2d2503759336e961a4e7b021fe50 Mon Sep 17 00:00:00 2001 From: Christoph Jerolimov Date: Fri, 1 Sep 2023 11:08:40 +0200 Subject: [PATCH] Fix crash when filtering the quick start catalog --- .../quick-starts/PfQuickStartCatalogPage.tsx | 190 ++++++++++++++++++ .../quick-starts/QuickStartCatalogPage.tsx | 2 +- 2 files changed, 191 insertions(+), 1 deletion(-) create mode 100644 frontend/packages/console-app/src/components/quick-starts/PfQuickStartCatalogPage.tsx diff --git a/frontend/packages/console-app/src/components/quick-starts/PfQuickStartCatalogPage.tsx b/frontend/packages/console-app/src/components/quick-starts/PfQuickStartCatalogPage.tsx new file mode 100644 index 00000000000..77a4483df12 --- /dev/null +++ b/frontend/packages/console-app/src/components/quick-starts/PfQuickStartCatalogPage.tsx @@ -0,0 +1,190 @@ +// Copied PfQuickStartCatalogPage from @patternfly/quickstarts#2.4.0 +// +// Origin source: +// https://github.com/patternfly/patternfly-quickstarts/blob/v2.4.0/packages/module/src/QuickStartCatalogPage.tsx +// +// Changes: +// 1. Removed local QuickStartCatalogEmptyState component and import this, +// and all other related QuickStart components directly from @patternfly/quickstarts +// 2. Prefixed QuickStartCatalogPage component with Pf +// 3. Fix bug https://issues.redhat.com/browse/OCPBUGS-13359 by accepting a string +// and (new) an input event in the `onSearchInputChange` callback. +// See also https://github.com/patternfly/patternfly-quickstarts/pull/237 +// and the underlaying PF change: https://github.com/patternfly/patternfly-react/pull/8516 +// +// This workaround/file should be removed as part of https://issues.redhat.com/browse/ODC-7381. + +import * as React from 'react'; +import { + EmptyBox, + LoadingBox, + clearFilterParams, + filterQuickStarts, + setQueryArgument, + QuickStart, + QuickStartCatalog, + QuickStartCatalogFilter, + QuickStartContext, + QuickStartContextValues, + QuickStartCatalogEmptyState, +} from '@patternfly/quickstarts'; +import { Divider, Text } from '@patternfly/react-core'; + +export type PfQuickStartCatalogPageProps = { + quickStarts?: QuickStart[]; + showFilter?: boolean; + sortFnc?: (q1: QuickStart, q2: QuickStart) => number; + title?: string; + hint?: string; + showTitle?: boolean; +}; + +export const PfQuickStartCatalogPage: React.FC = ({ + quickStarts, + showFilter, + sortFnc = (q1, q2) => q1.spec.displayName.localeCompare(q2.spec.displayName), + title, + hint, + showTitle = true, +}) => { + // eslint-disable-next-line react-hooks/exhaustive-deps + const sortFncCallback = React.useCallback(sortFnc, []); + const { + allQuickStarts = [], + setAllQuickStarts, + allQuickStartStates, + getResource, + filter, + setFilter, + loading, + useQueryParams, + } = React.useContext(QuickStartContext); + + React.useEffect(() => { + // passed through prop, not context + if (quickStarts && JSON.stringify(quickStarts) !== JSON.stringify(allQuickStarts)) { + setAllQuickStarts(quickStarts); + } + }, [quickStarts, allQuickStarts, setAllQuickStarts]); + + const initialFilteredQuickStarts = showFilter + ? filterQuickStarts( + allQuickStarts, + filter.keyword, + filter.status.statusFilters, + allQuickStartStates, + ).sort(sortFncCallback) + : allQuickStarts; + + const [filteredQuickStarts, setFilteredQuickStarts] = React.useState(initialFilteredQuickStarts); + React.useEffect(() => { + const filteredQs = showFilter + ? filterQuickStarts( + allQuickStarts, + filter.keyword, + filter.status.statusFilters, + allQuickStartStates, + ).sort(sortFncCallback) + : allQuickStarts; + // also needs a check whether the content of the QS changed + if ( + filteredQs.length !== filteredQuickStarts.length || + JSON.stringify(filteredQs) !== JSON.stringify(filteredQuickStarts) + ) { + setFilteredQuickStarts(filteredQs); + } + }, [ + allQuickStarts, + allQuickStartStates, + showFilter, + filter.keyword, + filter.status.statusFilters, + sortFncCallback, + filteredQuickStarts, + ]); + + const clearFilters = () => { + setFilter('keyword', ''); + setFilter('status', []); + clearFilterParams(); + setFilteredQuickStarts( + allQuickStarts.sort((q1, q2) => q1.spec.displayName.localeCompare(q2.spec.displayName)), + ); + }; + + const onSearchInputChange = (searchValue) => { + if (typeof searchValue !== 'string' && searchValue?.target) { + // eslint-disable-next-line no-param-reassign + searchValue = searchValue.target.value; + if (useQueryParams) { + setQueryArgument('keyword', searchValue); + } + } + const result = filterQuickStarts( + allQuickStarts, + searchValue, + filter.status.statusFilters, + allQuickStartStates, + ).sort((q1, q2) => q1.spec.displayName.localeCompare(q2.spec.displayName)); + if (searchValue !== filter.keyword) { + setFilter('keyword', searchValue); + } + if (result.length !== filteredQuickStarts.length) { + setFilteredQuickStarts(result); + } + }; + + const onStatusChange = (statusList) => { + const result = filterQuickStarts( + allQuickStarts, + filter.keyword, + statusList, + allQuickStartStates, + ).sort((q1, q2) => q1.spec.displayName.localeCompare(q2.spec.displayName)); + if (JSON.stringify(statusList) !== JSON.stringify(filter.status)) { + setFilter('status', statusList); + } + if (result.length !== filteredQuickStarts.length) { + setFilteredQuickStarts(result); + } + }; + + if (loading) { + return ; + } + + if (!allQuickStarts || allQuickStarts.length === 0) { + return ; + } + + return ( +
+ {showTitle && ( +
+ + {title || getResource('Quick Starts')} + + {hint &&
{hint}
} +
+ )} + {showTitle && } + {showFilter && ( + <> + + + + )} + <> + {filteredQuickStarts.length === 0 ? ( + + ) : ( + + )} + +
+ ); +}; diff --git a/frontend/packages/console-app/src/components/quick-starts/QuickStartCatalogPage.tsx b/frontend/packages/console-app/src/components/quick-starts/QuickStartCatalogPage.tsx index 7c7c2a311e7..cfef73f94ce 100644 --- a/frontend/packages/console-app/src/components/quick-starts/QuickStartCatalogPage.tsx +++ b/frontend/packages/console-app/src/components/quick-starts/QuickStartCatalogPage.tsx @@ -1,9 +1,9 @@ import * as React from 'react'; -import { QuickStartCatalogPage as PfQuickStartCatalogPage } from '@patternfly/quickstarts'; import { Helmet } from 'react-helmet'; import { useTranslation } from 'react-i18next'; import { LoadingBox } from '@console/internal/components/utils'; import QuickStartsLoader from './loader/QuickStartsLoader'; +import { PfQuickStartCatalogPage } from './PfQuickStartCatalogPage'; import './QuickStartCatalogPage.scss';