From 3df1d800bc2af2437d4a45a65f3129fe028e916f Mon Sep 17 00:00:00 2001 From: Bryan Florkiewicz Date: Mon, 9 Oct 2023 18:44:33 -0400 Subject: [PATCH 01/38] RHCLOUD-28295 - update chrome to use autosuggest search API --- src/components/Search/SearchDescription.tsx | 6 +- src/components/Search/SearchGroup.tsx | 27 +++++---- src/components/Search/SearchInput.scss | 3 + src/components/Search/SearchInput.tsx | 67 ++++++++++----------- src/components/Search/SearchTitle.tsx | 13 ++-- src/components/Search/SearchTypes.ts | 29 ++++----- src/components/Search/parseHighlight.ts | 4 -- 7 files changed, 73 insertions(+), 76 deletions(-) delete mode 100644 src/components/Search/parseHighlight.ts diff --git a/src/components/Search/SearchDescription.tsx b/src/components/Search/SearchDescription.tsx index d6d5a6e63..a76e4cd71 100644 --- a/src/components/Search/SearchDescription.tsx +++ b/src/components/Search/SearchDescription.tsx @@ -1,17 +1,15 @@ import React from 'react'; import { Text, TextContent } from '@patternfly/react-core/dist/dynamic/components/Text'; -import parseHighlights from './parseHighlight'; import './SearchDescription.scss'; -const SearchDescription = ({ description, highlight = [] }: { highlight?: string[]; description: string }) => { - const parsedDescription = parseHighlights(description, highlight); +const SearchDescription = ({ description }: { description: string }) => { return ( ); diff --git a/src/components/Search/SearchGroup.tsx b/src/components/Search/SearchGroup.tsx index d6f646bc4..6a3b135df 100644 --- a/src/components/Search/SearchGroup.tsx +++ b/src/components/Search/SearchGroup.tsx @@ -3,21 +3,24 @@ import React from 'react'; import ChromeLink from '../ChromeLink'; import SearchDescription from './SearchDescription'; import SearchTitle from './SearchTitle'; -import { HighlightingResponseType, SearchResultItem } from './SearchTypes'; +import { AUTOSUGGEST_TERM_DELIMITER, SearchResultItem } from './SearchTypes'; -const SearchGroup = ({ items, highlighting }: { items: SearchResultItem[]; highlighting: HighlightingResponseType }) => { +const SearchGroup = ({ items }: { items: SearchResultItem[] }) => { return items.length > 0 ? ( - {items.map(({ id, allTitle, bundle_title, abstract, relative_uri }) => ( - } - description={} - key={id} - > - - - ))} + {items.map(({ term, payload }) => { + const [allTitle, bundle_title, abstract] = term.split(AUTOSUGGEST_TERM_DELIMITER); + return ( + } + description={} + key={crypto.randomUUID()} + > + + + ); + })} ) : null; }; diff --git a/src/components/Search/SearchInput.scss b/src/components/Search/SearchInput.scss index 077ec00fe..c57481b62 100644 --- a/src/components/Search/SearchInput.scss +++ b/src/components/Search/SearchInput.scss @@ -37,6 +37,9 @@ display: none; } } + small { + display: inline-block; + } } &__empty-state { .pf-v5-c-empty-state__icon { diff --git a/src/components/Search/SearchInput.tsx b/src/components/Search/SearchInput.tsx index 7c0d3d982..e55bf8316 100644 --- a/src/components/Search/SearchInput.tsx +++ b/src/components/Search/SearchInput.tsx @@ -9,7 +9,7 @@ import debounce from 'lodash/debounce'; import './SearchInput.scss'; import SearchGroup from './SearchGroup'; -import { HighlightingResponseType, SearchResponseType, SearchResultItem } from './SearchTypes'; +import { AUTOSUGGEST_HIGHLIGHT_TAG, AUTOSUGGEST_TERM_DELIMITER, SearchResponseType, SearchResultItem } from './SearchTypes'; import EmptySearchState from './EmptySearchState'; import { isProd } from '../../utils/common'; import { useSegment } from '../../analytics/useSegment'; @@ -36,22 +36,20 @@ const FUZZY_RANGE_TAG = 'FUZZY_RANGE_TAG'; */ const BASE_SEARCH = new URLSearchParams(); -BASE_SEARCH.append( - 'q', - `${REPLACE_TAG} OR *${REPLACE_TAG}~${FUZZY_RANGE_TAG} OR ${REPLACE_TAG}*~${FUZZY_RANGE_TAG} OR ${REPLACE_TAG}~${FUZZY_RANGE_TAG}` -); // add query replacement tag and enable fuzzy search with ~ and wildcards -BASE_SEARCH.append('fq', 'documentKind:ModuleDefinition'); // search for ModuleDefinition documents -BASE_SEARCH.append('rows', '10'); // request 10 results -BASE_SEARCH.append('hl', 'true'); // enable highlight -BASE_SEARCH.append('hl.method', 'original'); // choose highlight method -BASE_SEARCH.append('hl.fl', 'abstract'); // highlight description -BASE_SEARCH.append('hl.fl', 'allTitle'); // highlight title -BASE_SEARCH.append('hl.fl', 'bundle_title'); // highlight bundle title -BASE_SEARCH.append('hl.fl', 'bundle'); // highlight bundle id -BASE_SEARCH.append('hl.snippets', '3'); // enable up to 3 highlights in a single string -BASE_SEARCH.append('hl.mergeContiguous', 'true'); // Use only one highlight attribute to simply tag replacement. - -const BASE_URL = new URL(`https://access.${IS_PROD ? '' : 'stage.'}redhat.com/hydra/rest/search/platform/console/`); +BASE_SEARCH.append('redhat_client', 'console'); // required client id +BASE_SEARCH.append('q', REPLACE_TAG); // add query replacement tag and enable fuzzy search with ~ and wildcards +// BASE_SEARCH.append('fq', 'documentKind:ModuleDefinition'); // search for ModuleDefinition documents +BASE_SEARCH.append('suggest.count', '10'); // request 10 results +// BASE_SEARCH.append('hl', 'true'); // enable highlight +// BASE_SEARCH.append('hl.method', 'original'); // choose highlight method +// BASE_SEARCH.append('hl.fl', 'abstract'); // highlight description +// BASE_SEARCH.append('hl.fl', 'allTitle'); // highlight title +// BASE_SEARCH.append('hl.fl', 'bundle_title'); // highlight bundle title +// BASE_SEARCH.append('hl.fl', 'bundle'); // highlight bundle id +// BASE_SEARCH.append('hl.snippets', '3'); // enable up to 3 highlights in a single string +// BASE_SEARCH.append('hl.mergeContiguous', 'true'); // Use only one highlight attribute to simply tag replacement. + +const BASE_URL = new URL(`https://access.${IS_PROD ? '' : 'stage.'}redhat.com/hydra/proxy/gss-diag/rs/search/autosuggest`); // search API stopped receiving encoded search string BASE_URL.search = decodeURIComponent(BASE_SEARCH.toString()); const SEARCH_QUERY = BASE_URL.toString(); @@ -74,10 +72,9 @@ type SearchCategories = { }; const initialSearchState: SearchResponseType = { - docs: [], - maxScore: 0, - numFound: 0, - start: 0, + suggest: { + default: {}, + }, }; type SearchInputListener = { @@ -89,7 +86,6 @@ const SearchInput = ({ onStateChange }: SearchInputListener) => { const [searchValue, setSearchValue] = useState(''); const [isFetching, setIsFetching] = useState(false); const [searchResults, setSearchResults] = useState(initialSearchState); - const [highlighting, setHighlighting] = useState({}); const { ready, analytics } = useSegment(); const blockCloseEvent = useRef(false); @@ -99,19 +95,22 @@ const SearchInput = ({ onStateChange }: SearchInputListener) => { const containerRef = useRef(null); const { md } = useWindowWidth(); + const resultCount = searchResults?.suggest?.default[searchValue]?.numFound || 0; + // sort result items based on matched field and its priority const resultCategories = useMemo( () => - searchResults.docs.reduce( + (searchResults?.suggest?.default[searchValue]?.suggestions || []).reduce( (acc, curr) => { - if (highlighting[curr.id]?.allTitle) { + const [allTitle, , abstract] = curr.term.split(AUTOSUGGEST_TERM_DELIMITER); + if (allTitle.includes(AUTOSUGGEST_HIGHLIGHT_TAG)) { return { ...acc, highLevel: [...acc.highLevel, curr], }; } - if (highlighting[curr.id]?.abstract) { + if (abstract.includes(AUTOSUGGEST_HIGHLIGHT_TAG)) { return { ...acc, midLevel: [...acc.midLevel, curr], @@ -129,7 +128,7 @@ const SearchInput = ({ onStateChange }: SearchInputListener) => { lowLevel: [], } ), - [searchResults.docs, highlighting] + [searchResults, searchValue] ); const handleMenuKeys = (event: KeyboardEvent) => { @@ -155,7 +154,7 @@ const SearchInput = ({ onStateChange }: SearchInputListener) => { }; const onInputClick: SearchInputProps['onClick'] = () => { - if (!isOpen && searchResults.numFound > 0) { + if (!isOpen && resultCount > 0) { if (!md && isExpanded && searchValue !== '') { setIsOpen(true); onStateChange(true); @@ -216,10 +215,9 @@ const SearchInput = ({ onStateChange }: SearchInputListener) => { const handleFetch = (value = '') => { return fetch(SEARCH_QUERY.replaceAll(REPLACE_TAG, value).replaceAll(FUZZY_RANGE_TAG, value.length > 3 ? '2' : '1')) .then((r) => r.json()) - .then(({ response, highlighting }: { highlighting: HighlightingResponseType; response: SearchResponseType }) => { + .then((response: SearchResponseType) => { if (isMounted.current) { setSearchResults(response); - setHighlighting(highlighting); // make sure to calculate resize when switching from loading to sucess state handleWindowResize(); } @@ -280,7 +278,6 @@ const SearchInput = ({ onStateChange }: SearchInputListener) => { className={isExpanded ? 'pf-u-flex-grow-1' : 'chr-c-search__collapsed'} /> ); - const menu = ( @@ -291,14 +288,14 @@ const SearchInput = ({ onStateChange }: SearchInputListener) => { ) : ( <> - 0 ? `Top ${searchResults.docs.length} results` : undefined}> - - - + 0 ? `Top ${resultCount} results` : undefined}> + + + )} - {searchResults.numFound === 0 && !isFetching && } + {resultCount === 0 && !isFetching && } diff --git a/src/components/Search/SearchTitle.tsx b/src/components/Search/SearchTitle.tsx index 729ea9684..445e42f81 100644 --- a/src/components/Search/SearchTitle.tsx +++ b/src/components/Search/SearchTitle.tsx @@ -2,13 +2,16 @@ import React from 'react'; import { Text, TextContent } from '@patternfly/react-core/dist/dynamic/components/Text'; const SearchTitle = ({ title, bundleTitle }: { title: string; bundleTitle: string }) => { + const showBundleTitle = bundleTitle.replace(/\s/g, '').length > 0; return ( - - {title} - | - {bundleTitle} - + + {showBundleTitle && ( + + | + + )} + {showBundleTitle && } ); }; diff --git a/src/components/Search/SearchTypes.ts b/src/components/Search/SearchTypes.ts index 82f7738db..a60bf092e 100644 --- a/src/components/Search/SearchTypes.ts +++ b/src/components/Search/SearchTypes.ts @@ -1,23 +1,20 @@ export type SearchResultItem = { - abstract: string; - allTitle: string; - bundle: string[]; - bundle_title: string[]; - documentKind: string; - id: string; - relative_uri: string; - view_uri: string; + term: string; + weight: string; + payload: string; }; export type SearchResponseType = { - docs: SearchResultItem[]; - start: number; - numFound: number; - maxScore: number; + suggest: { + default: { + [recordId: string]: { + numFound: number; + suggestions: SearchResultItem[]; + }; + }; + }; }; -export type SearchHighlight = { allTitle?: string[]; abstract?: string[]; bundle_title?: string[]; bundle?: string[] }; +export const AUTOSUGGEST_HIGHLIGHT_TAG = ''; -export type HighlightingResponseType = { - [recordId: string]: SearchHighlight; -}; +export const AUTOSUGGEST_TERM_DELIMITER = '|'; diff --git a/src/components/Search/parseHighlight.ts b/src/components/Search/parseHighlight.ts deleted file mode 100644 index b52297901..000000000 --- a/src/components/Search/parseHighlight.ts +++ /dev/null @@ -1,4 +0,0 @@ -// merge multiple highlight marks into single string -const parseHighlights = (base: string, highlights: string[] = []) => (highlights.length === 0 ? base : highlights.join(' ')); - -export default parseHighlights; From 4cbd14a594483d1e8d4c7b62134858088badbbfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Maro=C5=A1i?= Date: Tue, 10 Oct 2023 13:29:11 +0200 Subject: [PATCH 02/38] Add name for context switcher dropdown. --- .../ContextSwitcher/ContextSwitcher.scss | 9 +++- src/components/ContextSwitcher/index.tsx | 45 +++++++++++++------ 2 files changed, 39 insertions(+), 15 deletions(-) diff --git a/src/components/ContextSwitcher/ContextSwitcher.scss b/src/components/ContextSwitcher/ContextSwitcher.scss index 5f849f63d..78e6bee8c 100644 --- a/src/components/ContextSwitcher/ContextSwitcher.scss +++ b/src/components/ContextSwitcher/ContextSwitcher.scss @@ -1,7 +1,8 @@ @import '~@patternfly/patternfly/sass-utilities/_all'; .chr-c-context-selector { --pf-v5-c-masthead--c-context-selector--Width: 224px; - .chr-c-content-personal-account { + .chr-c-content-account { + width: 100%; .account-label { display: flex; flex-direction: row; @@ -9,8 +10,14 @@ } } .pf-v5-c-context-selector__menu-list-item { + width: calc(224px - 16px); svg { font-size: 9px; } + small.account-name { + text-overflow: ellipsis; + overflow: hidden; + text-align: initial; + } } } diff --git a/src/components/ContextSwitcher/index.tsx b/src/components/ContextSwitcher/index.tsx index b92022798..8a747f7a3 100644 --- a/src/components/ContextSwitcher/index.tsx +++ b/src/components/ContextSwitcher/index.tsx @@ -35,11 +35,19 @@ export type ContextSwitcherProps = { className?: string; }; +// These attributes are present in the response based on the open API spec. +// TODO: Migrate to the new RBAC JS client when it is ready. +type CrossAccountRequestInternal = CrossAccountRequest & { + first_name?: string | null; + last_name?: string | null; + email: string; +}; + const ContextSwitcher = ({ user, className }: ContextSwitcherProps) => { const dispatch = useDispatch(); const intl = useIntl(); const isOpen = useSelector(({ chrome }: ReduxState) => chrome?.contextSwitcherOpen); - const [data, setData] = useState([]); + const [data, setData] = useState([]); const [searchValue, setSearchValue] = useState(''); const [selectedAccountNumber, setSelectedAccountNumber] = useState(user.identity.account_number); const onSelect = () => { @@ -98,7 +106,7 @@ const ContextSwitcher = ({ user, className }: ContextSwitcherProps) => { } } axios - .get<{ data: CrossAccountRequest[] }>('/api/rbac/v1/cross-account-requests/', { + .get<{ data: CrossAccountRequestInternal[] }>('/api/rbac/v1/cross-account-requests/', { params: { status: 'approved', order_by: '-created', @@ -109,7 +117,7 @@ const ContextSwitcher = ({ user, className }: ContextSwitcherProps) => { if (mounted) { setData( data - .reduce((acc, curr) => { + .reduce((acc, curr) => { const request = acc.find(({ target_account }) => target_account === curr.target_account); if (request) { return acc; @@ -148,16 +156,18 @@ const ContextSwitcher = ({ user, className }: ContextSwitcherProps) => { > {user && user?.identity?.account_number?.includes(searchValue) ? ( - + {user?.identity?.account_number} {user?.identity?.account_number === `${selectedAccountNumber}` && ( - - + + )} - {intl.formatMessage(messages.personalAccount)} + + {intl.formatMessage(messages.personalAccount)} + ) : ( @@ -165,14 +175,21 @@ const ContextSwitcher = ({ user, className }: ContextSwitcherProps) => { )} {filteredData?.length === 0 ? {intl.formatMessage(messages.noResults)} : } {filteredData ? ( - filteredData.map(({ target_account, request_id, end_date, target_org }) => ( + filteredData.map(({ target_account, request_id, end_date, target_org, email, first_name, last_name }) => ( handleItemClick(target_account, request_id, end_date, target_org)} key={request_id}> - {target_account} - {target_account === selectedAccountNumber && ( - - - - )} + + + {target_account} + {target_account === selectedAccountNumber && ( + + + + )} + + + {first_name && last_name ? `${first_name} ${last_name}` : email} + + )) ) : ( From ec2e2f59d25608fc18f4e176ef51e61644b77462 Mon Sep 17 00:00:00 2001 From: Bryan Florkiewicz Date: Thu, 19 Oct 2023 15:52:02 -0400 Subject: [PATCH 03/38] Use both APIs and remove wildcard matching and fuzzy matching --- src/components/Search/SearchDescription.tsx | 6 +- src/components/Search/SearchGroup.tsx | 34 ++++- src/components/Search/SearchInput.tsx | 147 +++++++++++++------- src/components/Search/SearchTypes.ts | 32 ++++- src/components/Search/parseHighlight.ts | 4 + 5 files changed, 159 insertions(+), 64 deletions(-) create mode 100644 src/components/Search/parseHighlight.ts diff --git a/src/components/Search/SearchDescription.tsx b/src/components/Search/SearchDescription.tsx index a76e4cd71..a530e30eb 100644 --- a/src/components/Search/SearchDescription.tsx +++ b/src/components/Search/SearchDescription.tsx @@ -2,14 +2,16 @@ import React from 'react'; import { Text, TextContent } from '@patternfly/react-core/dist/dynamic/components/Text'; import './SearchDescription.scss'; +import parseHighlights from './parseHighlight'; -const SearchDescription = ({ description }: { description: string }) => { +const SearchDescription = ({ description, highlight = [] }: { highlight?: string[]; description: string }) => { + const parsedDescription = parseHighlights(description, highlight); return ( ); diff --git a/src/components/Search/SearchGroup.tsx b/src/components/Search/SearchGroup.tsx index 6a3b135df..44bbdce93 100644 --- a/src/components/Search/SearchGroup.tsx +++ b/src/components/Search/SearchGroup.tsx @@ -3,19 +3,39 @@ import React from 'react'; import ChromeLink from '../ChromeLink'; import SearchDescription from './SearchDescription'; import SearchTitle from './SearchTitle'; -import { AUTOSUGGEST_TERM_DELIMITER, SearchResultItem } from './SearchTypes'; +import { + AUTOSUGGEST_TERM_DELIMITER, + HighlightingResponseType, + SearchAutoSuggestionResultItem, + SearchResultItem, + SearchResultItemAggregate, +} from './SearchTypes'; -const SearchGroup = ({ items }: { items: SearchResultItem[] }) => { +const SearchGroup = ({ items, highlighting }: { items: SearchResultItemAggregate[]; highlighting: HighlightingResponseType }) => { return items.length > 0 ? ( - {items.map(({ term, payload }) => { - const [allTitle, bundle_title, abstract] = term.split(AUTOSUGGEST_TERM_DELIMITER); + {items.map((item) => { + if (item.term) { + const { term, payload } = item as SearchAutoSuggestionResultItem; + const [allTitle, bundle_title, abstract] = term.split(AUTOSUGGEST_TERM_DELIMITER); + return ( + } + description={} + key={crypto.randomUUID()} + > + + + ); + } + const { id, allTitle, bundle_title, abstract, relative_uri } = item as SearchResultItem; return ( } - description={} - key={crypto.randomUUID()} + component={(props) => } + description={} + key={id} > diff --git a/src/components/Search/SearchInput.tsx b/src/components/Search/SearchInput.tsx index e55bf8316..84d901699 100644 --- a/src/components/Search/SearchInput.tsx +++ b/src/components/Search/SearchInput.tsx @@ -9,7 +9,15 @@ import debounce from 'lodash/debounce'; import './SearchInput.scss'; import SearchGroup from './SearchGroup'; -import { AUTOSUGGEST_HIGHLIGHT_TAG, AUTOSUGGEST_TERM_DELIMITER, SearchResponseType, SearchResultItem } from './SearchTypes'; +import { + AUTOSUGGEST_HIGHLIGHT_TAG, + AUTOSUGGEST_TERM_DELIMITER, + HighlightingResponseType, + SearchAutoSuggestionResponseType, + SearchResponseAggregate, + SearchResponseType, + SearchResultItemAggregate, +} from './SearchTypes'; import EmptySearchState from './EmptySearchState'; import { isProd } from '../../utils/common'; import { useSegment } from '../../analytics/useSegment'; @@ -21,7 +29,6 @@ export type SearchInputprops = { const IS_PROD = isProd(); const REPLACE_TAG = 'REPLACE_TAG'; -const FUZZY_RANGE_TAG = 'FUZZY_RANGE_TAG'; /** * The ?q is the search term. * ------ @@ -36,24 +43,33 @@ const FUZZY_RANGE_TAG = 'FUZZY_RANGE_TAG'; */ const BASE_SEARCH = new URLSearchParams(); -BASE_SEARCH.append('redhat_client', 'console'); // required client id -BASE_SEARCH.append('q', REPLACE_TAG); // add query replacement tag and enable fuzzy search with ~ and wildcards -// BASE_SEARCH.append('fq', 'documentKind:ModuleDefinition'); // search for ModuleDefinition documents -BASE_SEARCH.append('suggest.count', '10'); // request 10 results -// BASE_SEARCH.append('hl', 'true'); // enable highlight -// BASE_SEARCH.append('hl.method', 'original'); // choose highlight method -// BASE_SEARCH.append('hl.fl', 'abstract'); // highlight description -// BASE_SEARCH.append('hl.fl', 'allTitle'); // highlight title -// BASE_SEARCH.append('hl.fl', 'bundle_title'); // highlight bundle title -// BASE_SEARCH.append('hl.fl', 'bundle'); // highlight bundle id -// BASE_SEARCH.append('hl.snippets', '3'); // enable up to 3 highlights in a single string -// BASE_SEARCH.append('hl.mergeContiguous', 'true'); // Use only one highlight attribute to simply tag replacement. - -const BASE_URL = new URL(`https://access.${IS_PROD ? '' : 'stage.'}redhat.com/hydra/proxy/gss-diag/rs/search/autosuggest`); +BASE_SEARCH.append('q', `${REPLACE_TAG}`); // add query replacement tag and enable fuzzy search with ~ and wildcards +BASE_SEARCH.append('fq', 'documentKind:ModuleDefinition'); // search for ModuleDefinition documents +BASE_SEARCH.append('rows', '10'); // request 10 results +BASE_SEARCH.append('hl', 'true'); // enable highlight +BASE_SEARCH.append('hl.method', 'original'); // choose highlight method +BASE_SEARCH.append('hl.fl', 'abstract'); // highlight description +BASE_SEARCH.append('hl.fl', 'allTitle'); // highlight title +BASE_SEARCH.append('hl.fl', 'bundle_title'); // highlight bundle title +BASE_SEARCH.append('hl.fl', 'bundle'); // highlight bundle id +BASE_SEARCH.append('hl.snippets', '3'); // enable up to 3 highlights in a single string +BASE_SEARCH.append('hl.mergeContiguous', 'true'); // Use only one highlight attribute to simply tag replacement. + +const BASE_URL = new URL(`https://access.${IS_PROD ? '' : 'stage.'}redhat.com/hydra/rest/search/platform/console/`); // search API stopped receiving encoded search string BASE_URL.search = decodeURIComponent(BASE_SEARCH.toString()); const SEARCH_QUERY = BASE_URL.toString(); +const SUGGEST_SEARCH = new URLSearchParams(); +SUGGEST_SEARCH.append('redhat_client', 'console'); // required client id +SUGGEST_SEARCH.append('q', REPLACE_TAG); // add query replacement tag and enable fuzzy search with ~ and wildcards +SUGGEST_SEARCH.append('suggest.count', '10'); // request 10 results + +const SUGGEST_URL = new URL(`https://access.${IS_PROD ? '' : 'stage.'}redhat.com/hydra/proxy/gss-diag/rs/search/autosuggest`); +// search API stopped receiving encoded search string +SUGGEST_URL.search = decodeURIComponent(SUGGEST_SEARCH.toString()); +const SUGGEST_SEARCH_QUERY = SUGGEST_URL.toString(); + const getMaxMenuHeight = (menuElement?: HTMLDivElement | null) => { if (!menuElement) { return 0; @@ -66,12 +82,16 @@ const getMaxMenuHeight = (menuElement?: HTMLDivElement | null) => { }; type SearchCategories = { - highLevel: SearchResultItem[]; - midLevel: SearchResultItem[]; - lowLevel: SearchResultItem[]; + highLevel: SearchResultItemAggregate[]; + midLevel: SearchResultItemAggregate[]; + lowLevel: SearchResultItemAggregate[]; }; -const initialSearchState: SearchResponseType = { +const initialSearchState: SearchResponseAggregate = { + docs: [], + maxScore: 0, + numFound: 0, + start: 0, suggest: { default: {}, }, @@ -85,7 +105,8 @@ const SearchInput = ({ onStateChange }: SearchInputListener) => { const [isOpen, setIsOpen] = useState(false); const [searchValue, setSearchValue] = useState(''); const [isFetching, setIsFetching] = useState(false); - const [searchResults, setSearchResults] = useState(initialSearchState); + const [highlighting, setHighlighting] = useState({}); + const [searchResults, setSearchResults] = useState(initialSearchState); const { ready, analytics } = useSegment(); const blockCloseEvent = useRef(false); @@ -98,38 +119,48 @@ const SearchInput = ({ onStateChange }: SearchInputListener) => { const resultCount = searchResults?.suggest?.default[searchValue]?.numFound || 0; // sort result items based on matched field and its priority - const resultCategories = useMemo( - () => - (searchResults?.suggest?.default[searchValue]?.suggestions || []).reduce( - (acc, curr) => { - const [allTitle, , abstract] = curr.term.split(AUTOSUGGEST_TERM_DELIMITER); - if (allTitle.includes(AUTOSUGGEST_HIGHLIGHT_TAG)) { - return { - ...acc, - highLevel: [...acc.highLevel, curr], - }; - } - - if (abstract.includes(AUTOSUGGEST_HIGHLIGHT_TAG)) { - return { - ...acc, - midLevel: [...acc.midLevel, curr], - }; - } + const resultCategories = useMemo(() => { + const categories = (searchResults?.suggest?.default[searchValue]?.suggestions || []).reduce( + (acc, curr) => { + const [allTitle, , abstract] = curr.term.split(AUTOSUGGEST_TERM_DELIMITER); + if (allTitle.includes(AUTOSUGGEST_HIGHLIGHT_TAG)) { + return { + ...acc, + highLevel: [...acc.highLevel, curr], + }; + } + if (abstract.includes(AUTOSUGGEST_HIGHLIGHT_TAG)) { return { ...acc, - lowLevel: [...acc.lowLevel, curr], + midLevel: [...acc.midLevel, curr], }; - }, - { - highLevel: [], - midLevel: [], - lowLevel: [], } - ), - [searchResults, searchValue] - ); + + return { + ...acc, + lowLevel: [...acc.lowLevel, curr], + }; + }, + { + highLevel: [], + midLevel: [], + lowLevel: [], + } + ); + searchResults.docs.forEach((doc) => { + if (highlighting[doc.id]?.allTitle) { + categories.highLevel.push(doc); + } + + if (highlighting[doc.id]?.abstract) { + categories.midLevel.push(doc); + } + + categories.lowLevel.push(doc); + }); + return categories; + }, [searchResults, searchValue]); const handleMenuKeys = (event: KeyboardEvent) => { if (!isOpen) { @@ -213,11 +244,19 @@ const SearchInput = ({ onStateChange }: SearchInputListener) => { }, [isOpen, menuRef]); const handleFetch = (value = '') => { - return fetch(SEARCH_QUERY.replaceAll(REPLACE_TAG, value).replaceAll(FUZZY_RANGE_TAG, value.length > 3 ? '2' : '1')) + let results: SearchResponseAggregate = initialSearchState; + return fetch(SUGGEST_SEARCH_QUERY.replaceAll(REPLACE_TAG, value)) + .then((r) => r.json()) + .then((response: SearchAutoSuggestionResponseType) => { + results = { ...results, ...response }; + return fetch(SEARCH_QUERY.replaceAll(REPLACE_TAG, value)); + }) .then((r) => r.json()) - .then((response: SearchResponseType) => { + .then(({ response, highlighting }: { highlighting: HighlightingResponseType; response: SearchResponseType }) => { + results = { ...results, ...response }; if (isMounted.current) { - setSearchResults(response); + setSearchResults(results); + setHighlighting(highlighting); // make sure to calculate resize when switching from loading to sucess state handleWindowResize(); } @@ -289,9 +328,9 @@ const SearchInput = ({ onStateChange }: SearchInputListener) => { ) : ( <> 0 ? `Top ${resultCount} results` : undefined}> - - - + + + )} diff --git a/src/components/Search/SearchTypes.ts b/src/components/Search/SearchTypes.ts index a60bf092e..7476e2d0f 100644 --- a/src/components/Search/SearchTypes.ts +++ b/src/components/Search/SearchTypes.ts @@ -1,20 +1,50 @@ +import { EitherNotBoth } from '@openshift/dynamic-plugin-sdk'; + export type SearchResultItem = { + abstract: string; + allTitle: string; + bundle: string[]; + bundle_title: string[]; + documentKind: string; + id: string; + relative_uri: string; + view_uri: string; +}; + +export type SearchAutoSuggestionResultItem = { term: string; weight: string; payload: string; }; +export type SearchResultItemAggregate = EitherNotBoth; + export type SearchResponseType = { + docs: SearchResultItem[]; + start: number; + numFound: number; + maxScore: number; +}; + +export type SearchAutoSuggestionResponseType = { suggest: { default: { [recordId: string]: { numFound: number; - suggestions: SearchResultItem[]; + suggestions: SearchAutoSuggestionResultItem[]; }; }; }; }; +export type SearchResponseAggregate = SearchResponseType & SearchAutoSuggestionResponseType; + +export type SearchHighlight = { allTitle?: string[]; abstract?: string[]; bundle_title?: string[]; bundle?: string[] }; + +export type HighlightingResponseType = { + [recordId: string]: SearchHighlight; +}; + export const AUTOSUGGEST_HIGHLIGHT_TAG = ''; export const AUTOSUGGEST_TERM_DELIMITER = '|'; diff --git a/src/components/Search/parseHighlight.ts b/src/components/Search/parseHighlight.ts new file mode 100644 index 000000000..b52297901 --- /dev/null +++ b/src/components/Search/parseHighlight.ts @@ -0,0 +1,4 @@ +// merge multiple highlight marks into single string +const parseHighlights = (base: string, highlights: string[] = []) => (highlights.length === 0 ? base : highlights.join(' ')); + +export default parseHighlights; From d8ba38a0c1683c289a08ec341271e50896cbef25 Mon Sep 17 00:00:00 2001 From: Zein Sleiman Date: Mon, 23 Oct 2023 15:15:50 -0500 Subject: [PATCH 04/38] update the support case url --- src/components/Feedback/FeedbackError.tsx | 2 +- src/components/Feedback/FeedbackModal.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Feedback/FeedbackError.tsx b/src/components/Feedback/FeedbackError.tsx index 53fbc5d1f..489de0060 100644 --- a/src/components/Feedback/FeedbackError.tsx +++ b/src/components/Feedback/FeedbackError.tsx @@ -24,7 +24,7 @@ const FeedbackError = ({ onCloseModal }: FeedbackErrorProps) => { {intl.formatMessage(messages.somethingWentWrong)} {intl.formatMessage(messages.problemProcessingRequest)}{' '} - + {intl.formatMessage(messages.redHatSupport)} diff --git a/src/components/Feedback/FeedbackModal.tsx b/src/components/Feedback/FeedbackModal.tsx index 82b142c96..8b87aecf9 100644 --- a/src/components/Feedback/FeedbackModal.tsx +++ b/src/components/Feedback/FeedbackModal.tsx @@ -124,7 +124,7 @@ const FeedbackModal = memo(({ user }: FeedbackModalProps) => { modalDescription={ {intl.formatMessage(messages.describeReportBug)}{' '} - + {intl.formatMessage(messages.openSupportCase)} From 4bf1a78a5cd87ea5dda9d6bc0f3eb49e8548426f Mon Sep 17 00:00:00 2001 From: Zein Sleiman Date: Mon, 23 Oct 2023 15:27:20 -0500 Subject: [PATCH 05/38] lint --- src/components/Feedback/FeedbackModal.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/Feedback/FeedbackModal.tsx b/src/components/Feedback/FeedbackModal.tsx index 8b87aecf9..30c21706d 100644 --- a/src/components/Feedback/FeedbackModal.tsx +++ b/src/components/Feedback/FeedbackModal.tsx @@ -124,7 +124,11 @@ const FeedbackModal = memo(({ user }: FeedbackModalProps) => { modalDescription={ {intl.formatMessage(messages.describeReportBug)}{' '} - + {intl.formatMessage(messages.openSupportCase)} From a997a56f8cd0fb18231bef410998120c0f3f93d0 Mon Sep 17 00:00:00 2001 From: ewinchel Date: Tue, 24 Oct 2023 13:18:17 -0400 Subject: [PATCH 06/38] Add icon for AI/ML --- src/components/AllServices/AllServicesIcons.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/AllServices/AllServicesIcons.ts b/src/components/AllServices/AllServicesIcons.ts index a3fbb9407..1f1237120 100644 --- a/src/components/AllServices/AllServicesIcons.ts +++ b/src/components/AllServices/AllServicesIcons.ts @@ -7,7 +7,7 @@ import CloudUploadAltIcon from '@patternfly/react-icons/dist/dynamic/icons/cloud import CogIcon from '@patternfly/react-icons/dist/dynamic/icons/cog-icon'; import CreditCardIcon from '@patternfly/react-icons/dist/dynamic/icons/credit-card-icon'; import CubeIcon from '@patternfly/react-icons/dist/dynamic/icons/cube-icon'; -import DatabaseIcon from '@patternfly/react-icons/dist/dynamic/icons/database-icon'; +import LightBulbIcon from '@patternfly/react-icons/dist/dynamic/icons/lightbulb-icon'; import InfrastructureIcon from '@patternfly/react-icons/dist/dynamic/icons/infrastructure-icon'; import RocketIcon from '@patternfly/react-icons/dist/dynamic/icons/rocket-icon'; import ShoppingCartIcon from '@patternfly/react-icons/dist/dynamic/icons/shopping-cart-icon'; @@ -17,7 +17,7 @@ import MonitoringIcon from '@patternfly/react-icons/dist/dynamic/icons/monitorin const AllServicesIcons = { CloudUploadAltIcon, AutomationIcon, - DatabaseIcon, + LightBulbIcon, RocketIcon, UsersIcon, InfrastructureIcon, From 9b36b68aafbaf2c397023b6bf9e6ff46615c0435 Mon Sep 17 00:00:00 2001 From: Zein Sleiman Date: Wed, 25 Oct 2023 14:47:59 -0500 Subject: [PATCH 07/38] fix support options link --- src/components/Header/Tools.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Header/Tools.tsx b/src/components/Header/Tools.tsx index bf6b8685d..01afa0c2b 100644 --- a/src/components/Header/Tools.tsx +++ b/src/components/Header/Tools.tsx @@ -150,7 +150,7 @@ const Tools = () => { }, { title: intl.formatMessage(messages.supportOptions), - url: isITLessEnv ? 'https://redhatgov.servicenowservices.com/css' : 'https://access.redhat.com/support', + onClick: () => (window.location.href = isITLessEnv ? 'https://redhatgov.servicenowservices.com/css' : 'https://access.redhat.com/support'), }, { title: intl.formatMessage(messages.insightsRhelDocumentation), From da85907105bac5e839e836cda29ff207e5ed09e6 Mon Sep 17 00:00:00 2001 From: Zein Sleiman Date: Thu, 26 Oct 2023 09:07:41 -0500 Subject: [PATCH 08/38] make line more readable --- src/components/Header/Tools.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/Header/Tools.tsx b/src/components/Header/Tools.tsx index 01afa0c2b..6a854057e 100644 --- a/src/components/Header/Tools.tsx +++ b/src/components/Header/Tools.tsx @@ -130,6 +130,10 @@ const Tools = () => { } }, [user]); + const supportOptionsUrl = () => { + return isITLessEnv ? 'https://redhatgov.servicenowservices.com/css' : 'https://access.redhat.com/support'; + } + /* list out the items for the about menu */ const aboutMenuDropdownItems = [ { @@ -150,7 +154,7 @@ const Tools = () => { }, { title: intl.formatMessage(messages.supportOptions), - onClick: () => (window.location.href = isITLessEnv ? 'https://redhatgov.servicenowservices.com/css' : 'https://access.redhat.com/support'), + onClick: () => window.location.href = supportOptionsUrl(), }, { title: intl.formatMessage(messages.insightsRhelDocumentation), From 888ac00b5f29a07e87c7c4ad91ba0184429b99b2 Mon Sep 17 00:00:00 2001 From: Zein Sleiman Date: Thu, 26 Oct 2023 11:16:07 -0500 Subject: [PATCH 09/38] lint --- src/components/Header/Tools.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Header/Tools.tsx b/src/components/Header/Tools.tsx index 6a854057e..1fbf392d3 100644 --- a/src/components/Header/Tools.tsx +++ b/src/components/Header/Tools.tsx @@ -132,7 +132,7 @@ const Tools = () => { const supportOptionsUrl = () => { return isITLessEnv ? 'https://redhatgov.servicenowservices.com/css' : 'https://access.redhat.com/support'; - } + }; /* list out the items for the about menu */ const aboutMenuDropdownItems = [ @@ -154,7 +154,7 @@ const Tools = () => { }, { title: intl.formatMessage(messages.supportOptions), - onClick: () => window.location.href = supportOptionsUrl(), + onClick: () => (window.location.href = supportOptionsUrl()), }, { title: intl.formatMessage(messages.insightsRhelDocumentation), From 2dbda741d155615a2b2c02c906e718c91a8cce1e Mon Sep 17 00:00:00 2001 From: Bryan Florkiewicz Date: Thu, 26 Oct 2023 20:14:30 -0400 Subject: [PATCH 10/38] Attempt to pass autosuggest results into search solr endpoint --- src/components/Search/SearchInput.tsx | 40 ++++++++++++++++++++++----- src/components/Search/SearchTypes.ts | 8 +++++- 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/src/components/Search/SearchInput.tsx b/src/components/Search/SearchInput.tsx index 84d901699..7c99a7368 100644 --- a/src/components/Search/SearchInput.tsx +++ b/src/components/Search/SearchInput.tsx @@ -6,11 +6,15 @@ import { Spinner } from '@patternfly/react-core/dist/dynamic/components/Spinner' import { Popper } from '@patternfly/react-core/dist/dynamic/helpers/Popper/Popper'; import debounce from 'lodash/debounce'; +import uniq from 'lodash/uniq'; import './SearchInput.scss'; import SearchGroup from './SearchGroup'; import { - AUTOSUGGEST_HIGHLIGHT_TAG, + AUTOSUGGEST_BUNDLE_CLOSE_TAG, + AUTOSUGGEST_BUNDLE_OPEN_TAG, + AUTOSUGGEST_HIGHLIGHT_CLOSE_TAG, + AUTOSUGGEST_HIGHLIGHT_OPEN_TAG, AUTOSUGGEST_TERM_DELIMITER, HighlightingResponseType, SearchAutoSuggestionResponseType, @@ -123,14 +127,14 @@ const SearchInput = ({ onStateChange }: SearchInputListener) => { const categories = (searchResults?.suggest?.default[searchValue]?.suggestions || []).reduce( (acc, curr) => { const [allTitle, , abstract] = curr.term.split(AUTOSUGGEST_TERM_DELIMITER); - if (allTitle.includes(AUTOSUGGEST_HIGHLIGHT_TAG)) { + if (allTitle.includes(AUTOSUGGEST_HIGHLIGHT_OPEN_TAG)) { return { ...acc, highLevel: [...acc.highLevel, curr], }; } - if (abstract.includes(AUTOSUGGEST_HIGHLIGHT_TAG)) { + if (abstract.includes(AUTOSUGGEST_HIGHLIGHT_OPEN_TAG)) { return { ...acc, midLevel: [...acc.midLevel, curr], @@ -244,16 +248,38 @@ const SearchInput = ({ onStateChange }: SearchInputListener) => { }, [isOpen, menuRef]); const handleFetch = (value = '') => { - let results: SearchResponseAggregate = initialSearchState; return fetch(SUGGEST_SEARCH_QUERY.replaceAll(REPLACE_TAG, value)) .then((r) => r.json()) .then((response: SearchAutoSuggestionResponseType) => { - results = { ...results, ...response }; - return fetch(SEARCH_QUERY.replaceAll(REPLACE_TAG, value)); + let valueToSearch = value; + const autoSuggestions = (response?.suggest?.default[value]?.suggestions || []).map((suggestion) => { + const [allTitle, bundle_title, abstract] = suggestion.term.split(AUTOSUGGEST_TERM_DELIMITER); + // TODO it is not safe to assume only one field will have a highlight. Need to revisit this and above logic + let matched; + if (allTitle.includes(AUTOSUGGEST_HIGHLIGHT_OPEN_TAG)) { + matched = allTitle; + } else if (bundle_title.includes(AUTOSUGGEST_HIGHLIGHT_OPEN_TAG)) { + matched = bundle_title; + } else { + matched = abstract; + } + const result = matched + .replaceAll(AUTOSUGGEST_HIGHLIGHT_OPEN_TAG, '') + .replaceAll(AUTOSUGGEST_HIGHLIGHT_CLOSE_TAG, '') + .replaceAll(AUTOSUGGEST_BUNDLE_OPEN_TAG, '') + .replaceAll(AUTOSUGGEST_BUNDLE_CLOSE_TAG, '') + .trim(); + // wrap multiple terms in quotes - otherwise search treats each as an individual term to search + return `"${result}"`; + }); + if (autoSuggestions.length > 0) { + valueToSearch = uniq(autoSuggestions).join('+OR+'); + } + return fetch(SEARCH_QUERY.replaceAll(REPLACE_TAG, valueToSearch)); }) .then((r) => r.json()) .then(({ response, highlighting }: { highlighting: HighlightingResponseType; response: SearchResponseType }) => { - results = { ...results, ...response }; + const results: SearchResponseAggregate = { ...initialSearchState, ...response }; if (isMounted.current) { setSearchResults(results); setHighlighting(highlighting); diff --git a/src/components/Search/SearchTypes.ts b/src/components/Search/SearchTypes.ts index 7476e2d0f..c2684daa4 100644 --- a/src/components/Search/SearchTypes.ts +++ b/src/components/Search/SearchTypes.ts @@ -45,6 +45,12 @@ export type HighlightingResponseType = { [recordId: string]: SearchHighlight; }; -export const AUTOSUGGEST_HIGHLIGHT_TAG = ''; +export const AUTOSUGGEST_HIGHLIGHT_OPEN_TAG = ''; + +export const AUTOSUGGEST_HIGHLIGHT_CLOSE_TAG = ''; + +export const AUTOSUGGEST_BUNDLE_OPEN_TAG = '['; + +export const AUTOSUGGEST_BUNDLE_CLOSE_TAG = ']'; export const AUTOSUGGEST_TERM_DELIMITER = '|'; From d8eadf6568add991c7ef009936242a47fa50d7ea Mon Sep 17 00:00:00 2001 From: Karel Hala Date: Mon, 30 Oct 2023 13:55:05 +0100 Subject: [PATCH 11/38] Rename preview flag in redirects and use feature flag for integrations (#2675) --- src/components/Header/Tools.tsx | 3 +-- src/components/Routes/Routes.tsx | 18 +++++++++--------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/components/Header/Tools.tsx b/src/components/Header/Tools.tsx index bf6b8685d..514e33336 100644 --- a/src/components/Header/Tools.tsx +++ b/src/components/Header/Tools.tsx @@ -27,7 +27,6 @@ import { ReduxState } from '../../redux/store'; import BellIcon from '@patternfly/react-icons/dist/dynamic/icons/bell-icon'; import { toggleNotificationsDrawer } from '../../redux/actions'; import useWindowWidth from '../../hooks/useWindowWidth'; -import { usePreviewFlag } from '../../utils/usePreviewFlag'; const isITLessEnv = ITLess(); @@ -81,7 +80,7 @@ const Tools = () => { isRhosakEntitled: false, isDemoAcc: false, }); - const enableIntegrations = usePreviewFlag('platform.sources.integrations'); + const enableIntegrations = useFlag('platform.sources.integrations'); const { xs } = useWindowWidth(); const user = useSelector(({ chrome: { user } }: ReduxState) => user!); const unreadNotifications = useSelector(({ chrome: { notifications } }: ReduxState) => notifications.data.some((item) => !item.read)); diff --git a/src/components/Routes/Routes.tsx b/src/components/Routes/Routes.tsx index b547decd9..d8649b3ad 100644 --- a/src/components/Routes/Routes.tsx +++ b/src/components/Routes/Routes.tsx @@ -5,7 +5,7 @@ import ChromeRoute from '../ChromeRoute'; import NotFoundRoute from '../NotFoundRoute'; import LoadingFallback from '../../utils/loading-fallback'; import { ReduxState } from '../../redux/store'; -import { usePreviewFlag } from '../../utils/usePreviewFlag'; +import { useFlag } from '@unleash/proxy-client-react'; const INTEGRATION_SOURCES = 'platform.sources.integrations'; @@ -23,7 +23,7 @@ const redirects = [ { path: '/settings', to: '/settings/integrations', - previewFlag: { + featureFlag: { value: true, name: INTEGRATION_SOURCES, }, @@ -31,7 +31,7 @@ const redirects = [ { path: '/settings', to: '/settings/sources', - previewFlag: { + featureFlag: { value: false, name: INTEGRATION_SOURCES, }, @@ -67,8 +67,8 @@ export type RoutesProps = { }; const ChromeRoutes = ({ routesProps }: RoutesProps) => { - const enableIntegrations = usePreviewFlag(INTEGRATION_SOURCES); - const previewFlags = useMemo>(() => ({ INTEGRATION_SOURCES: enableIntegrations }), [enableIntegrations]); + const enableIntegrations = useFlag(INTEGRATION_SOURCES); + const featureFlags = useMemo>(() => ({ INTEGRATION_SOURCES: enableIntegrations }), [enableIntegrations]); const moduleRoutes = useSelector(({ chrome: { moduleRoutes } }: ReduxState) => moduleRoutes); const showBundleCatalog = localStorage.getItem('chrome:experimental:quickstarts') === 'true'; @@ -84,10 +84,10 @@ const ChromeRoutes = ({ routesProps }: RoutesProps) => { } /> )} - {redirects.map(({ path, to, previewFlag }) => { - if (previewFlag) { - const found = Object.keys(previewFlags).find((item) => item === previewFlag.name); - if (previewFlags[found as string] !== previewFlag.value) { + {redirects.map(({ path, to, featureFlag }) => { + if (featureFlag) { + const found = Object.keys(featureFlags).find((item) => item === featureFlag.name); + if (featureFlags[found as string] !== featureFlag.value) { return null; } } From ea517001ccb67bf684f99ea2d67763a5cb968ffa Mon Sep 17 00:00:00 2001 From: Martin Marosi Date: Wed, 1 Nov 2023 09:33:22 +0100 Subject: [PATCH 12/38] Bump chrome dependency --- package-lock.json | 21 +++++++++++++-------- package.json | 2 +- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index e45a73400..9f471c0bc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,7 +20,7 @@ "@patternfly/react-core": "^5.0.1", "@patternfly/react-icons": "^5.0.1", "@patternfly/react-tokens": "^5.0.1", - "@redhat-cloud-services/chrome": "^1.0.2", + "@redhat-cloud-services/chrome": "^1.0.3", "@redhat-cloud-services/entitlements-client": "1.2.0", "@redhat-cloud-services/frontend-components": "^4.0.10", "@redhat-cloud-services/frontend-components-notifications": "^4.0.2", @@ -3017,9 +3017,12 @@ } }, "node_modules/@redhat-cloud-services/chrome": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@redhat-cloud-services/chrome/-/chrome-1.0.2.tgz", - "integrity": "sha512-NDmtDcrRspMuuPlgEFMgYokj3B3w1xkUdeAlatSzF5Y2eNl3DMJpE3/DMNL+gFDq7U7S/7pu/EODGym7yz07Pg==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@redhat-cloud-services/chrome/-/chrome-1.0.3.tgz", + "integrity": "sha512-uSltwr50oZQ0878Cbnlt4zB/kkDhnZm6o3G3p+tVdd2dB7ZZ7FMSXRGPoQmItkgsQ/QwSXtg7i2aUhbb/gkglg==", + "dependencies": { + "lodash": "^4.17.21" + }, "peerDependencies": { "@scalprum/react-core": "^0.5.1", "react": "^18.2.0", @@ -29457,10 +29460,12 @@ } }, "@redhat-cloud-services/chrome": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@redhat-cloud-services/chrome/-/chrome-1.0.2.tgz", - "integrity": "sha512-NDmtDcrRspMuuPlgEFMgYokj3B3w1xkUdeAlatSzF5Y2eNl3DMJpE3/DMNL+gFDq7U7S/7pu/EODGym7yz07Pg==", - "requires": {} + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@redhat-cloud-services/chrome/-/chrome-1.0.3.tgz", + "integrity": "sha512-uSltwr50oZQ0878Cbnlt4zB/kkDhnZm6o3G3p+tVdd2dB7ZZ7FMSXRGPoQmItkgsQ/QwSXtg7i2aUhbb/gkglg==", + "requires": { + "lodash": "^4.17.21" + } }, "@redhat-cloud-services/entitlements-client": { "version": "1.2.0", diff --git a/package.json b/package.json index 94e294116..aa4c84cb9 100644 --- a/package.json +++ b/package.json @@ -135,7 +135,7 @@ "@patternfly/react-core": "^5.0.1", "@patternfly/react-icons": "^5.0.1", "@patternfly/react-tokens": "^5.0.1", - "@redhat-cloud-services/chrome": "^1.0.2", + "@redhat-cloud-services/chrome": "^1.0.3", "@redhat-cloud-services/entitlements-client": "1.2.0", "@redhat-cloud-services/frontend-components": "^4.0.10", "@redhat-cloud-services/frontend-components-notifications": "^4.0.2", From 66ad46acdc385c08d0dd6da94312785a2298630a Mon Sep 17 00:00:00 2001 From: Martin Marosi Date: Wed, 1 Nov 2023 15:31:06 +0100 Subject: [PATCH 13/38] Use autosuggest search API. --- cypress/e2e/release-gate/search.cy.tsx | 69 -------- src/components/Search/SearchGroup.tsx | 48 ------ src/components/Search/SearchInput.tsx | 208 ++++++++----------------- src/components/Search/SearchTypes.ts | 20 --- 4 files changed, 68 insertions(+), 277 deletions(-) delete mode 100644 cypress/e2e/release-gate/search.cy.tsx delete mode 100644 src/components/Search/SearchGroup.tsx diff --git a/cypress/e2e/release-gate/search.cy.tsx b/cypress/e2e/release-gate/search.cy.tsx deleted file mode 100644 index b88347640..000000000 --- a/cypress/e2e/release-gate/search.cy.tsx +++ /dev/null @@ -1,69 +0,0 @@ -const searchResponse = { - response: { - numFound: 2, - start: 0, - maxScore: 10.0, - docs: [ - { - id: 'hcc-module-/openshift/create-OPENSHIFT.cluster.create.azure', - view_uri: 'https://console.redhat.com/openshift/create', - documentKind: 'ModuleDefinition', - allTitle: 'Azure Red Hat OpenShift', - bundle: ['openshift'], - bundle_title: ['OpenShift'], - relative_uri: '/openshift/create', - alt_titles: ['ARO', 'Azure', 'OpenShift on Azure'], - abstract: 'https://console.redhat.com/openshift/create', - timestamp: '2023-08-22T17:01:31.717Z', - _version_: 1774949404248113152, - }, - { - id: 'hcc-module-/openshift/releases-openshift.releases', - view_uri: 'https://console.redhat.com/openshift/releases', - documentKind: 'ModuleDefinition', - allTitle: 'Releases', - bundle: ['openshift'], - bundle_title: ['OpenShift'], - relative_uri: '/openshift/releases', - icons: 'InfrastructureIcon', - abstract: 'View general information on the most recent OpenShift Container Platform release versions that you can install.', - timestamp: '2023-08-15T10:55:46.769Z', - _version_: 1774949404248113152, - }, - ], - }, - highlighting: { - 'hcc-module-/openshift/create-OPENSHIFT.cluster.create.azure': { - abstract: ['https://console.redhat.com/openshift/create'], - allTitle: ['Azure Red Hat OpenShift'], - bundle: ['openshift'], - }, - 'hcc-module-/openshift/releases-openshift.releases': { - abstract: ['View general information on the most recent OpenShift Container Platform release versions that you can install.'], - allTitle: ['Releases'], - bundle: ['openshift'], - }, - }, -}; - -describe('Search', () => { - it('search for openshift services', () => { - cy.login(); - cy.visit('/'); - cy.intercept( - { - method: 'GET', - url: '**/hydra/rest/search/**', - }, - searchResponse - ).as('search'); - cy.get('.chr-c-search__input').click().type('openshift'); - cy.wait('@search').its('response.statusCode').should('equal', 200); - cy.get('@search.all').should('have.length', 1); - cy.screenshot(); - cy.get('.chr-c-search__input').should('contain', 'Top 2 results'); - cy.get('.chr-c-search__input li').first().should('contain', 'Azure'); - cy.get('.chr-c-search__input li').last().should('contain', 'Releases').click(); - cy.url().should('contain', '/openshift/releases'); - }); -}); diff --git a/src/components/Search/SearchGroup.tsx b/src/components/Search/SearchGroup.tsx deleted file mode 100644 index 44bbdce93..000000000 --- a/src/components/Search/SearchGroup.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import { MenuGroup, MenuItem } from '@patternfly/react-core/dist/dynamic/components/Menu'; -import React from 'react'; -import ChromeLink from '../ChromeLink'; -import SearchDescription from './SearchDescription'; -import SearchTitle from './SearchTitle'; -import { - AUTOSUGGEST_TERM_DELIMITER, - HighlightingResponseType, - SearchAutoSuggestionResultItem, - SearchResultItem, - SearchResultItemAggregate, -} from './SearchTypes'; - -const SearchGroup = ({ items, highlighting }: { items: SearchResultItemAggregate[]; highlighting: HighlightingResponseType }) => { - return items.length > 0 ? ( - - {items.map((item) => { - if (item.term) { - const { term, payload } = item as SearchAutoSuggestionResultItem; - const [allTitle, bundle_title, abstract] = term.split(AUTOSUGGEST_TERM_DELIMITER); - return ( - } - description={} - key={crypto.randomUUID()} - > - - - ); - } - const { id, allTitle, bundle_title, abstract, relative_uri } = item as SearchResultItem; - return ( - } - description={} - key={id} - > - - - ); - })} - - ) : null; -}; - -export default SearchGroup; diff --git a/src/components/Search/SearchInput.tsx b/src/components/Search/SearchInput.tsx index 7c99a7368..5fd7dbfb9 100644 --- a/src/components/Search/SearchInput.tsx +++ b/src/components/Search/SearchInput.tsx @@ -1,31 +1,23 @@ -import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import React, { useCallback, useEffect, useRef, useState } from 'react'; import { Bullseye } from '@patternfly/react-core/dist/dynamic/layouts/Bullseye'; -import { Menu, MenuContent, MenuGroup, MenuList } from '@patternfly/react-core/dist/dynamic/components/Menu'; +import { Menu, MenuContent, MenuGroup, MenuItem, MenuList } from '@patternfly/react-core/dist/dynamic/components/Menu'; import { SearchInput as PFSearchInput, SearchInputProps } from '@patternfly/react-core/dist/dynamic/components/SearchInput'; import { Spinner } from '@patternfly/react-core/dist/dynamic/components/Spinner'; import { Popper } from '@patternfly/react-core/dist/dynamic/helpers/Popper/Popper'; import debounce from 'lodash/debounce'; import uniq from 'lodash/uniq'; +import uniqWith from 'lodash/uniqWith'; import './SearchInput.scss'; -import SearchGroup from './SearchGroup'; -import { - AUTOSUGGEST_BUNDLE_CLOSE_TAG, - AUTOSUGGEST_BUNDLE_OPEN_TAG, - AUTOSUGGEST_HIGHLIGHT_CLOSE_TAG, - AUTOSUGGEST_HIGHLIGHT_OPEN_TAG, - AUTOSUGGEST_TERM_DELIMITER, - HighlightingResponseType, - SearchAutoSuggestionResponseType, - SearchResponseAggregate, - SearchResponseType, - SearchResultItemAggregate, -} from './SearchTypes'; +import { AUTOSUGGEST_TERM_DELIMITER, SearchAutoSuggestionResponseType, SearchResponseType } from './SearchTypes'; import EmptySearchState from './EmptySearchState'; import { isProd } from '../../utils/common'; import { useSegment } from '../../analytics/useSegment'; import useWindowWidth from '../../hooks/useWindowWidth'; +import ChromeLink from '../ChromeLink'; +import SearchTitle from './SearchTitle'; +import SearchDescription from './SearchDescription'; export type SearchInputprops = { isExpanded?: boolean; @@ -33,6 +25,7 @@ export type SearchInputprops = { const IS_PROD = isProd(); const REPLACE_TAG = 'REPLACE_TAG'; +const REPLACE_COUNT_TAG = 'REPLACE_COUNT_TAG'; /** * The ?q is the search term. * ------ @@ -47,22 +40,13 @@ const REPLACE_TAG = 'REPLACE_TAG'; */ const BASE_SEARCH = new URLSearchParams(); -BASE_SEARCH.append('q', `${REPLACE_TAG}`); // add query replacement tag and enable fuzzy search with ~ and wildcards +BASE_SEARCH.append('q', `alt_titles:${REPLACE_TAG}`); // add query replacement tag and enable fuzzy search with ~ and wildcards BASE_SEARCH.append('fq', 'documentKind:ModuleDefinition'); // search for ModuleDefinition documents -BASE_SEARCH.append('rows', '10'); // request 10 results -BASE_SEARCH.append('hl', 'true'); // enable highlight -BASE_SEARCH.append('hl.method', 'original'); // choose highlight method -BASE_SEARCH.append('hl.fl', 'abstract'); // highlight description -BASE_SEARCH.append('hl.fl', 'allTitle'); // highlight title -BASE_SEARCH.append('hl.fl', 'bundle_title'); // highlight bundle title -BASE_SEARCH.append('hl.fl', 'bundle'); // highlight bundle id -BASE_SEARCH.append('hl.snippets', '3'); // enable up to 3 highlights in a single string -BASE_SEARCH.append('hl.mergeContiguous', 'true'); // Use only one highlight attribute to simply tag replacement. +BASE_SEARCH.append('rows', `${REPLACE_COUNT_TAG}`); // request 10 results const BASE_URL = new URL(`https://access.${IS_PROD ? '' : 'stage.'}redhat.com/hydra/rest/search/platform/console/`); // search API stopped receiving encoded search string BASE_URL.search = decodeURIComponent(BASE_SEARCH.toString()); -const SEARCH_QUERY = BASE_URL.toString(); const SUGGEST_SEARCH = new URLSearchParams(); SUGGEST_SEARCH.append('redhat_client', 'console'); // required client id @@ -85,20 +69,11 @@ const getMaxMenuHeight = (menuElement?: HTMLDivElement | null) => { return bodyHeight - menuTopOffset - 4; }; -type SearchCategories = { - highLevel: SearchResultItemAggregate[]; - midLevel: SearchResultItemAggregate[]; - lowLevel: SearchResultItemAggregate[]; -}; - -const initialSearchState: SearchResponseAggregate = { - docs: [], - maxScore: 0, - numFound: 0, - start: 0, - suggest: { - default: {}, - }, +type SearchItem = { + title: string; + bundleTitle: string; + description: string; + pathname: string; }; type SearchInputListener = { @@ -109,8 +84,7 @@ const SearchInput = ({ onStateChange }: SearchInputListener) => { const [isOpen, setIsOpen] = useState(false); const [searchValue, setSearchValue] = useState(''); const [isFetching, setIsFetching] = useState(false); - const [highlighting, setHighlighting] = useState({}); - const [searchResults, setSearchResults] = useState(initialSearchState); + const [searchItems, setSearchItems] = useState([]); const { ready, analytics } = useSegment(); const blockCloseEvent = useRef(false); @@ -120,51 +94,7 @@ const SearchInput = ({ onStateChange }: SearchInputListener) => { const containerRef = useRef(null); const { md } = useWindowWidth(); - const resultCount = searchResults?.suggest?.default[searchValue]?.numFound || 0; - - // sort result items based on matched field and its priority - const resultCategories = useMemo(() => { - const categories = (searchResults?.suggest?.default[searchValue]?.suggestions || []).reduce( - (acc, curr) => { - const [allTitle, , abstract] = curr.term.split(AUTOSUGGEST_TERM_DELIMITER); - if (allTitle.includes(AUTOSUGGEST_HIGHLIGHT_OPEN_TAG)) { - return { - ...acc, - highLevel: [...acc.highLevel, curr], - }; - } - - if (abstract.includes(AUTOSUGGEST_HIGHLIGHT_OPEN_TAG)) { - return { - ...acc, - midLevel: [...acc.midLevel, curr], - }; - } - - return { - ...acc, - lowLevel: [...acc.lowLevel, curr], - }; - }, - { - highLevel: [], - midLevel: [], - lowLevel: [], - } - ); - searchResults.docs.forEach((doc) => { - if (highlighting[doc.id]?.allTitle) { - categories.highLevel.push(doc); - } - - if (highlighting[doc.id]?.abstract) { - categories.midLevel.push(doc); - } - - categories.lowLevel.push(doc); - }); - return categories; - }, [searchResults, searchValue]); + const resultCount = searchItems.length; const handleMenuKeys = (event: KeyboardEvent) => { if (!isOpen) { @@ -247,57 +177,52 @@ const SearchInput = ({ onStateChange }: SearchInputListener) => { }; }, [isOpen, menuRef]); - const handleFetch = (value = '') => { - return fetch(SUGGEST_SEARCH_QUERY.replaceAll(REPLACE_TAG, value)) - .then((r) => r.json()) - .then((response: SearchAutoSuggestionResponseType) => { - let valueToSearch = value; - const autoSuggestions = (response?.suggest?.default[value]?.suggestions || []).map((suggestion) => { - const [allTitle, bundle_title, abstract] = suggestion.term.split(AUTOSUGGEST_TERM_DELIMITER); - // TODO it is not safe to assume only one field will have a highlight. Need to revisit this and above logic - let matched; - if (allTitle.includes(AUTOSUGGEST_HIGHLIGHT_OPEN_TAG)) { - matched = allTitle; - } else if (bundle_title.includes(AUTOSUGGEST_HIGHLIGHT_OPEN_TAG)) { - matched = bundle_title; - } else { - matched = abstract; - } - const result = matched - .replaceAll(AUTOSUGGEST_HIGHLIGHT_OPEN_TAG, '') - .replaceAll(AUTOSUGGEST_HIGHLIGHT_CLOSE_TAG, '') - .replaceAll(AUTOSUGGEST_BUNDLE_OPEN_TAG, '') - .replaceAll(AUTOSUGGEST_BUNDLE_CLOSE_TAG, '') - .trim(); - // wrap multiple terms in quotes - otherwise search treats each as an individual term to search - return `"${result}"`; - }); - if (autoSuggestions.length > 0) { - valueToSearch = uniq(autoSuggestions).join('+OR+'); - } - return fetch(SEARCH_QUERY.replaceAll(REPLACE_TAG, valueToSearch)); - }) - .then((r) => r.json()) - .then(({ response, highlighting }: { highlighting: HighlightingResponseType; response: SearchResponseType }) => { - const results: SearchResponseAggregate = { ...initialSearchState, ...response }; - if (isMounted.current) { - setSearchResults(results); - setHighlighting(highlighting); - // make sure to calculate resize when switching from loading to sucess state - handleWindowResize(); - } - if (ready && analytics) { - analytics.track('chrome.search-query', { query: value }); - } - }) - .finally(() => { - isMounted.current && setIsFetching(false); - }); + const handleFetch = async (value = '') => { + const response = (await fetch(SUGGEST_SEARCH_QUERY.replaceAll(REPLACE_TAG, value)).then((r) => r.json())) as SearchAutoSuggestionResponseType; + + const items = (response?.suggest?.default[value]?.suggestions || []).map((suggestion) => { + const [allTitle, bundleTitle, abstract] = suggestion.term.split(AUTOSUGGEST_TERM_DELIMITER); + const url = new URL(suggestion.payload); + const pathname = url.pathname; + const item = { + title: allTitle, + bundleTitle, + description: abstract, + pathname, + }; + // wrap multiple terms in quotes - otherwise search treats each as an individual term to search + return { item, allTitle }; + }); + const suggests = uniq(items.map(({ allTitle }) => allTitle.replace(/(|<\/b>)/gm, '').trim())); + let searchItems = items.map(({ item }) => item); + console.log(suggests); + if (items.length < 10) { + console.log({ value }); + const altTitleResults = (await fetch( + BASE_URL.toString() + .replaceAll(REPLACE_TAG, `(${suggests.join(' OR ')} OR ${value})`) + .replaceAll(REPLACE_COUNT_TAG, '10') + ).then((r) => r.json())) as { response: SearchResponseType }; + searchItems = searchItems.concat( + altTitleResults.response.docs.map((doc) => ({ + pathname: doc.relative_uri, + bundleTitle: doc.bundle_title[0], + title: doc.allTitle, + description: doc.abstract, + })) + ); + } + searchItems = uniqWith(searchItems, (a, b) => a.title.replace(/(|<\/b>)/gm, '').trim() === b.title.replace(/(|<\/b>)/gm, '').trim()); + setSearchItems(searchItems.slice(0, 10)); + isMounted.current && setIsFetching(false); + if (ready && analytics) { + analytics.track('chrome.search-query', { query: value }); + } }; const debouncedFetch = useCallback(debounce(handleFetch, 500), []); - const handleChange = (_e: any, value: string) => { + const handleChange: SearchInputProps['onChange'] = (_e, value) => { setSearchValue(value); setIsFetching(true); debouncedFetch(value); @@ -325,7 +250,7 @@ const SearchInput = ({ onStateChange }: SearchInputListener) => { onChange={handleChange} onClear={(ev) => { setSearchValue(''); - setSearchResults(initialSearchState); + setSearchItems([]); ev.stopPropagation(); setIsOpen(false); onStateChange(false); @@ -353,14 +278,17 @@ const SearchInput = ({ onStateChange }: SearchInputListener) => { ) : ( <> - 0 ? `Top ${resultCount} results` : undefined}> - - - + 0 ? `Top ${searchItems.length} results` : undefined}> + {searchItems.map((item, index) => ( + }> + + + + ))} )} - {resultCount === 0 && !isFetching && } + {searchItems.length === 0 && !isFetching && } diff --git a/src/components/Search/SearchTypes.ts b/src/components/Search/SearchTypes.ts index c2684daa4..8eb286720 100644 --- a/src/components/Search/SearchTypes.ts +++ b/src/components/Search/SearchTypes.ts @@ -1,5 +1,3 @@ -import { EitherNotBoth } from '@openshift/dynamic-plugin-sdk'; - export type SearchResultItem = { abstract: string; allTitle: string; @@ -17,8 +15,6 @@ export type SearchAutoSuggestionResultItem = { payload: string; }; -export type SearchResultItemAggregate = EitherNotBoth; - export type SearchResponseType = { docs: SearchResultItem[]; start: number; @@ -37,20 +33,4 @@ export type SearchAutoSuggestionResponseType = { }; }; -export type SearchResponseAggregate = SearchResponseType & SearchAutoSuggestionResponseType; - -export type SearchHighlight = { allTitle?: string[]; abstract?: string[]; bundle_title?: string[]; bundle?: string[] }; - -export type HighlightingResponseType = { - [recordId: string]: SearchHighlight; -}; - -export const AUTOSUGGEST_HIGHLIGHT_OPEN_TAG = ''; - -export const AUTOSUGGEST_HIGHLIGHT_CLOSE_TAG = ''; - -export const AUTOSUGGEST_BUNDLE_OPEN_TAG = '['; - -export const AUTOSUGGEST_BUNDLE_CLOSE_TAG = ']'; - export const AUTOSUGGEST_TERM_DELIMITER = '|'; From 9c8a4f98334d65c082469f80b555eedc054d8282 Mon Sep 17 00:00:00 2001 From: Zein Sleiman Date: Wed, 1 Nov 2023 15:02:18 -0500 Subject: [PATCH 14/38] fixes internal dropdown link not redirecting --- src/components/Header/UserToggle.tsx | 39 ++++++++++++++++------------ 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/src/components/Header/UserToggle.tsx b/src/components/Header/UserToggle.tsx index b04a655c0..e99378021 100644 --- a/src/components/Header/UserToggle.tsx +++ b/src/components/Header/UserToggle.tsx @@ -1,22 +1,23 @@ +import './UserToggle.scss'; + +import { Dropdown, DropdownItem, DropdownList } from '@patternfly/react-core/dist/dynamic/components/Dropdown'; +import { ITLess, ITLessCognito, getEnv, isProd as isProdEnv } from '../../utils/common'; import React, { useRef, useState } from 'react'; + +import ChromeLink from '../ChromeLink/ChromeLink'; import { Divider } from '@patternfly/react-core/dist/dynamic/components/Divider'; -import { Dropdown, DropdownItem, DropdownList } from '@patternfly/react-core/dist/dynamic/components/Dropdown'; +import { EllipsisVIcon } from '@patternfly/react-icons/dist/dynamic/icons/ellipsis-v-icon'; import { MenuToggle } from '@patternfly/react-core/dist/dynamic/components/MenuToggle'; -import { Tooltip } from '@patternfly/react-core/dist/dynamic/components/Tooltip'; import QuestionCircleIcon from '@patternfly/react-icons/dist/dynamic/icons/question-circle-icon'; -import UserIcon from './UserIcon'; -import { useSelector } from 'react-redux'; -import { ITLess, ITLessCognito, getEnv, isProd as isProdEnv } from '../../utils/common'; -import ChromeLink from '../ChromeLink/ChromeLink'; -import { useIntl } from 'react-intl'; -import messages from '../../locales/Messages'; import { ReduxState } from '../../redux/store'; -import { logout } from '../../jwt/jwt'; -import { cogLogout } from '../../cognito/auth'; -import { EllipsisVIcon } from '@patternfly/react-icons/dist/dynamic/icons/ellipsis-v-icon'; +import { Tooltip } from '@patternfly/react-core/dist/dynamic/components/Tooltip'; +import UserIcon from './UserIcon'; import classNames from 'classnames'; - -import './UserToggle.scss'; +import { cogLogout } from '../../cognito/auth'; +import { logout } from '../../jwt/jwt'; +import messages from '../../locales/Messages'; +import { useIntl } from 'react-intl'; +import { useSelector } from 'react-redux'; const buildItems = (username = '', isOrgAdmin?: boolean, accountNumber?: string, isInternal?: boolean, extraItems: React.ReactNode[] = []) => { const env = getEnv(); @@ -26,6 +27,7 @@ const buildItems = (username = '', isOrgAdmin?: boolean, accountNumber?: string, const prefix = isProd ? '' : `${env === 'ci' ? 'qa' : env}.`; const accountNumberTooltip = `${intl.formatMessage(messages.useAccountNumber)}`; const questionMarkRef = useRef(null); + isInternal = true; return [
@@ -92,9 +94,14 @@ const buildItems = (username = '', isOrgAdmin?: boolean, accountNumber?: string, , {isInternal && isProd && ( - - {intl.formatMessage(messages.internal)} - + ( + + {intl.formatMessage(messages.internal)} + + )} + /> )} , (ITLessCognito() ? cogLogout() : logout(true))}> From ed98aaf3a896501ba06edfe47a3b01bbb5e494ad Mon Sep 17 00:00:00 2001 From: Zein Sleiman Date: Wed, 1 Nov 2023 15:05:05 -0500 Subject: [PATCH 15/38] remove debug code --- src/components/Header/UserToggle.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/Header/UserToggle.tsx b/src/components/Header/UserToggle.tsx index e99378021..6848b60a0 100644 --- a/src/components/Header/UserToggle.tsx +++ b/src/components/Header/UserToggle.tsx @@ -27,7 +27,6 @@ const buildItems = (username = '', isOrgAdmin?: boolean, accountNumber?: string, const prefix = isProd ? '' : `${env === 'ci' ? 'qa' : env}.`; const accountNumberTooltip = `${intl.formatMessage(messages.useAccountNumber)}`; const questionMarkRef = useRef(null); - isInternal = true; return [
From d6f0a3808232ef15e585651e29ea1456ed51d29c Mon Sep 17 00:00:00 2001 From: Karel Hala Date: Thu, 2 Nov 2023 10:43:46 +0100 Subject: [PATCH 16/38] Allow subs navigation on production (#2677) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Martin Maroši --- src/components/AppFilter/useAppFilter.test.js | 2 +- src/components/AppFilter/useAppFilter.ts | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/AppFilter/useAppFilter.test.js b/src/components/AppFilter/useAppFilter.test.js index 9e7f2017d..b7b064ba9 100644 --- a/src/components/AppFilter/useAppFilter.test.js +++ b/src/components/AppFilter/useAppFilter.test.js @@ -91,7 +91,7 @@ describe('useAppFilter', () => { await act(async () => { result.current.setIsOpen(true); }); - expect(axiosGetSpy).toHaveBeenCalledTimes(10); + expect(axiosGetSpy).toHaveBeenCalledTimes(11); for (let index = 0; index < 8; index++) { expect(axiosGetSpy.mock.calls[index]).toEqual([ `/api/chrome-service/v1/static/stable/stage/navigation/${requiredBundles[index]}-navigation.json?ts=666`, diff --git a/src/components/AppFilter/useAppFilter.ts b/src/components/AppFilter/useAppFilter.ts index 476bf3cdd..a6aa82ce9 100644 --- a/src/components/AppFilter/useAppFilter.ts +++ b/src/components/AppFilter/useAppFilter.ts @@ -12,7 +12,7 @@ export type AppFilterBucket = { links: NavItem[]; }; -const previewBundles = ['subscriptions']; +const previewBundles = ['']; export const requiredBundles = [ 'application-services', @@ -24,6 +24,7 @@ export const requiredBundles = [ 'iam', 'quay', 'business-services', + 'subscriptions', ...(!isProd() ? previewBundles : isBeta() ? previewBundles : []), ]; From d6638e4d93640ef005ce07d7373c491138ed882e Mon Sep 17 00:00:00 2001 From: Karel Hala Date: Thu, 2 Nov 2023 15:11:57 +0100 Subject: [PATCH 17/38] Do not include empty bundle in navigations --- src/components/AppFilter/useAppFilter.test.js | 2 +- src/components/AppFilter/useAppFilter.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/AppFilter/useAppFilter.test.js b/src/components/AppFilter/useAppFilter.test.js index b7b064ba9..9e7f2017d 100644 --- a/src/components/AppFilter/useAppFilter.test.js +++ b/src/components/AppFilter/useAppFilter.test.js @@ -91,7 +91,7 @@ describe('useAppFilter', () => { await act(async () => { result.current.setIsOpen(true); }); - expect(axiosGetSpy).toHaveBeenCalledTimes(11); + expect(axiosGetSpy).toHaveBeenCalledTimes(10); for (let index = 0; index < 8; index++) { expect(axiosGetSpy.mock.calls[index]).toEqual([ `/api/chrome-service/v1/static/stable/stage/navigation/${requiredBundles[index]}-navigation.json?ts=666`, diff --git a/src/components/AppFilter/useAppFilter.ts b/src/components/AppFilter/useAppFilter.ts index a6aa82ce9..987b21802 100644 --- a/src/components/AppFilter/useAppFilter.ts +++ b/src/components/AppFilter/useAppFilter.ts @@ -26,7 +26,7 @@ export const requiredBundles = [ 'business-services', 'subscriptions', ...(!isProd() ? previewBundles : isBeta() ? previewBundles : []), -]; +].filter(Boolean); export const itLessBundles = ['openshift', 'insights', 'settings', 'iam']; From 84c22e5eca0c5626382a0b508dd4bce844de898a Mon Sep 17 00:00:00 2001 From: Martin Marosi Date: Fri, 3 Nov 2023 09:00:16 +0100 Subject: [PATCH 18/38] Use improved search suggester. --- src/components/Search/SearchInput.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Search/SearchInput.tsx b/src/components/Search/SearchInput.tsx index 5fd7dbfb9..bb308840c 100644 --- a/src/components/Search/SearchInput.tsx +++ b/src/components/Search/SearchInput.tsx @@ -52,6 +52,8 @@ const SUGGEST_SEARCH = new URLSearchParams(); SUGGEST_SEARCH.append('redhat_client', 'console'); // required client id SUGGEST_SEARCH.append('q', REPLACE_TAG); // add query replacement tag and enable fuzzy search with ~ and wildcards SUGGEST_SEARCH.append('suggest.count', '10'); // request 10 results +SUGGEST_SEARCH.append('suggest.dictionary', 'improvedInfixSuggester'); // console new suggest dictionary +SUGGEST_SEARCH.append('suggest.dictionary', 'default'); const SUGGEST_URL = new URL(`https://access.${IS_PROD ? '' : 'stage.'}redhat.com/hydra/proxy/gss-diag/rs/search/autosuggest`); // search API stopped receiving encoded search string @@ -195,9 +197,7 @@ const SearchInput = ({ onStateChange }: SearchInputListener) => { }); const suggests = uniq(items.map(({ allTitle }) => allTitle.replace(/(|<\/b>)/gm, '').trim())); let searchItems = items.map(({ item }) => item); - console.log(suggests); if (items.length < 10) { - console.log({ value }); const altTitleResults = (await fetch( BASE_URL.toString() .replaceAll(REPLACE_TAG, `(${suggests.join(' OR ')} OR ${value})`) From f10f1f74ccf7c9da88a5965b96ad9d9114ba94c6 Mon Sep 17 00:00:00 2001 From: Martin Marosi Date: Mon, 6 Nov 2023 12:41:03 +0100 Subject: [PATCH 19/38] Combine suggester restuls. --- src/components/Search/SearchInput.tsx | 42 +++++++++++++++++---------- src/components/Search/SearchTypes.ts | 6 ++++ 2 files changed, 33 insertions(+), 15 deletions(-) diff --git a/src/components/Search/SearchInput.tsx b/src/components/Search/SearchInput.tsx index bb308840c..9a826ba8e 100644 --- a/src/components/Search/SearchInput.tsx +++ b/src/components/Search/SearchInput.tsx @@ -10,7 +10,7 @@ import uniq from 'lodash/uniq'; import uniqWith from 'lodash/uniqWith'; import './SearchInput.scss'; -import { AUTOSUGGEST_TERM_DELIMITER, SearchAutoSuggestionResponseType, SearchResponseType } from './SearchTypes'; +import { AUTOSUGGEST_TERM_DELIMITER, SearchAutoSuggestionResponseType, SearchAutoSuggestionResultItem, SearchResponseType } from './SearchTypes'; import EmptySearchState from './EmptySearchState'; import { isProd } from '../../utils/common'; import { useSegment } from '../../analytics/useSegment'; @@ -82,6 +82,22 @@ type SearchInputListener = { onStateChange: (isOpen: boolean) => void; }; +function parseSuggestions(suggestions: SearchAutoSuggestionResultItem[] = []) { + return suggestions.map((suggestion) => { + const [allTitle, bundleTitle, abstract] = suggestion.term.split(AUTOSUGGEST_TERM_DELIMITER); + const url = new URL(suggestion.payload); + return { + item: { + title: allTitle, + bundleTitle, + description: abstract, + pathname: url.pathname, + }, + allTitle, + }; + }); +} + const SearchInput = ({ onStateChange }: SearchInputListener) => { const [isOpen, setIsOpen] = useState(false); const [searchValue, setSearchValue] = useState(''); @@ -182,25 +198,21 @@ const SearchInput = ({ onStateChange }: SearchInputListener) => { const handleFetch = async (value = '') => { const response = (await fetch(SUGGEST_SEARCH_QUERY.replaceAll(REPLACE_TAG, value)).then((r) => r.json())) as SearchAutoSuggestionResponseType; - const items = (response?.suggest?.default[value]?.suggestions || []).map((suggestion) => { - const [allTitle, bundleTitle, abstract] = suggestion.term.split(AUTOSUGGEST_TERM_DELIMITER); - const url = new URL(suggestion.payload); - const pathname = url.pathname; - const item = { - title: allTitle, - bundleTitle, - description: abstract, - pathname, - }; - // wrap multiple terms in quotes - otherwise search treats each as an individual term to search - return { item, allTitle }; - }); + // parse default suggester + // parse improved suggester + let items: { item: SearchItem; allTitle: string }[] = []; + items = items + .concat( + parseSuggestions(response?.suggest?.default[value]?.suggestions), + parseSuggestions(response?.suggest?.improvedInfixSuggester[value]?.suggestions) + ) + .slice(0, 10); const suggests = uniq(items.map(({ allTitle }) => allTitle.replace(/(|<\/b>)/gm, '').trim())); let searchItems = items.map(({ item }) => item); if (items.length < 10) { const altTitleResults = (await fetch( BASE_URL.toString() - .replaceAll(REPLACE_TAG, `(${suggests.join(' OR ')} OR ${value})`) + .replaceAll(REPLACE_TAG, `(${suggests.length > 0 ? suggests.join(' OR ') + ' OR ' : ''}${value})`) .replaceAll(REPLACE_COUNT_TAG, '10') ).then((r) => r.json())) as { response: SearchResponseType }; searchItems = searchItems.concat( diff --git a/src/components/Search/SearchTypes.ts b/src/components/Search/SearchTypes.ts index 8eb286720..7b44d7b20 100644 --- a/src/components/Search/SearchTypes.ts +++ b/src/components/Search/SearchTypes.ts @@ -24,6 +24,12 @@ export type SearchResponseType = { export type SearchAutoSuggestionResponseType = { suggest: { + improvedInfixSuggester: { + [recordId: string]: { + numFound: number; + suggestions: SearchAutoSuggestionResultItem[]; + }; + }; default: { [recordId: string]: { numFound: number; From 118754c717cddae8f09cb70c258e393b2e4131c0 Mon Sep 17 00:00:00 2001 From: Martin Marosi Date: Mon, 6 Nov 2023 14:09:25 +0100 Subject: [PATCH 20/38] Bump chrome package. --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9f471c0bc..a1d1c4885 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,7 +20,7 @@ "@patternfly/react-core": "^5.0.1", "@patternfly/react-icons": "^5.0.1", "@patternfly/react-tokens": "^5.0.1", - "@redhat-cloud-services/chrome": "^1.0.3", + "@redhat-cloud-services/chrome": "^1.0.4", "@redhat-cloud-services/entitlements-client": "1.2.0", "@redhat-cloud-services/frontend-components": "^4.0.10", "@redhat-cloud-services/frontend-components-notifications": "^4.0.2", @@ -3017,9 +3017,9 @@ } }, "node_modules/@redhat-cloud-services/chrome": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@redhat-cloud-services/chrome/-/chrome-1.0.3.tgz", - "integrity": "sha512-uSltwr50oZQ0878Cbnlt4zB/kkDhnZm6o3G3p+tVdd2dB7ZZ7FMSXRGPoQmItkgsQ/QwSXtg7i2aUhbb/gkglg==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@redhat-cloud-services/chrome/-/chrome-1.0.4.tgz", + "integrity": "sha512-lERy/qYzZM5Lpj6m+uE4Sh5KNQzfK6s6B35eiLt6eZ64Q3VPtgTe3louQtiBo2AoxJvnvl0LBE9HNv2wujrIyg==", "dependencies": { "lodash": "^4.17.21" }, @@ -29460,9 +29460,9 @@ } }, "@redhat-cloud-services/chrome": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@redhat-cloud-services/chrome/-/chrome-1.0.3.tgz", - "integrity": "sha512-uSltwr50oZQ0878Cbnlt4zB/kkDhnZm6o3G3p+tVdd2dB7ZZ7FMSXRGPoQmItkgsQ/QwSXtg7i2aUhbb/gkglg==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@redhat-cloud-services/chrome/-/chrome-1.0.4.tgz", + "integrity": "sha512-lERy/qYzZM5Lpj6m+uE4Sh5KNQzfK6s6B35eiLt6eZ64Q3VPtgTe3louQtiBo2AoxJvnvl0LBE9HNv2wujrIyg==", "requires": { "lodash": "^4.17.21" } diff --git a/package.json b/package.json index aa4c84cb9..ffcccdd60 100644 --- a/package.json +++ b/package.json @@ -135,7 +135,7 @@ "@patternfly/react-core": "^5.0.1", "@patternfly/react-icons": "^5.0.1", "@patternfly/react-tokens": "^5.0.1", - "@redhat-cloud-services/chrome": "^1.0.3", + "@redhat-cloud-services/chrome": "^1.0.4", "@redhat-cloud-services/entitlements-client": "1.2.0", "@redhat-cloud-services/frontend-components": "^4.0.10", "@redhat-cloud-services/frontend-components-notifications": "^4.0.2", From 83061105e057039736241a3d02c29ac3fe2f9982 Mon Sep 17 00:00:00 2001 From: Aneela Chagarlamudi Date: Mon, 6 Nov 2023 13:16:24 -0500 Subject: [PATCH 21/38] update to use keycloak in stage itless --- src/utils/common.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/utils/common.ts b/src/utils/common.ts index 690667db2..2de2ab8cc 100644 --- a/src/utils/common.ts +++ b/src/utils/common.ts @@ -34,12 +34,12 @@ export const DEFAULT_SSO_ROUTES = { }, frhStage: { url: ['console.stage.openshiftusgov.com'], - sso: 'https://ocm-ra-stage-domain.auth-fips.us-gov-west-1.amazoncognito.com/login', + sso: 'https://sso.stage.openshiftusgov.com/auth', portal: 'https://console.stage.openshiftusgov.com', }, frh: { url: ['console.openshiftusgov.com'], - sso: 'https://ocm-ra-stage-domain.auth-fips.us-gov-west-1.amazoncognito.com/login', + sso: 'https://ocm-ra-prod-domain.auth-fips.us-gov-west-1.amazoncognito.com/login', portal: 'https://console.openshiftusgov.com', }, ephem: { @@ -215,11 +215,11 @@ export function ITLess() { } export function ITLessCognito() { - return getEnv() === 'frhStage' || getEnv() === 'frh'; + return getEnv() === 'frh'; } export function ITLessKeycloak() { - return getEnv() === 'ephem' || getEnv() === 'int' || getEnv() === 'scr'; + return getEnv() === 'ephem' || getEnv() === 'int' || getEnv() === 'scr' || getEnv() === 'frhStage'; } export function updateDocumentTitle(title?: string, noSuffix = false) { From 2d4c38de0d200859550c58f3bc5620142e4e12da Mon Sep 17 00:00:00 2001 From: Aneela Chagarlamudi Date: Mon, 6 Nov 2023 13:53:15 -0500 Subject: [PATCH 22/38] Add beta build for ITLess env --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index ffcccdd60..e46b3b887 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "build:beta": "BETA=true npm run build", "build:dev": "NODE_ENV=development webpack --config config/webpack.config.js --mode=development", "build:res": "NODE_ENV=restricted webpack --config config/webpack.config.js --mode=production", + "build:res:beta": "BETA=true npm run build:res", "cypress": "cypress", "cypress:run": "cypress run --browser electron", "dev": "DEV_SERVER=true webpack serve --config config/webpack.config.js --mode=development", From cbc57b04922735c1d5372c3e8944132aeddf693f Mon Sep 17 00:00:00 2001 From: Aneela Chagarlamudi Date: Mon, 6 Nov 2023 13:59:32 -0500 Subject: [PATCH 23/38] update sat url for prod --- src/layouts/SatelliteToken.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/layouts/SatelliteToken.tsx b/src/layouts/SatelliteToken.tsx index 1f50c20fc..b7e770abe 100644 --- a/src/layouts/SatelliteToken.tsx +++ b/src/layouts/SatelliteToken.tsx @@ -8,6 +8,7 @@ import { List, ListComponent, ListItem, OrderType } from '@patternfly/react-core import { Masthead } from '@patternfly/react-core/dist/dynamic/components/Masthead'; import { Page, PageSection } from '@patternfly/react-core/dist/dynamic/components/Page'; import SatelliteTable from '../components/Satellite/SatelliteTable'; +import { getEnv } from '../utils/common'; const SatelliteToken: React.FC = () => { const [token, setToken] = useState(''); @@ -24,6 +25,8 @@ const SatelliteToken: React.FC = () => { setError(err); }); }; + const itlessProd = getEnv() === 'frh'; + const satelliteUrl = itlessProd ? 'https://mtls.console.openshiftusgov.com' : 'https://mtls.console.stage.openshiftusgov.com'; useEffect(() => { if (token.length < 1 && error === null) { generateToken(); @@ -70,7 +73,7 @@ const SatelliteToken: React.FC = () => { Step 2. You will prompted to enter the token from Step 1.`} - {`SATELLITE_RH_CLOUD_URL=https://mtls.console.stage.openshiftusgov.com org_id= foreman-rake rh_cloud:hybridcloud_register`} + {`SATELLITE_RH_CLOUD_URL=${satelliteUrl} org_id= foreman-rake rh_cloud:hybridcloud_register`} From 740cab4263fc2cee9bd39505df2ceae5a8e04898 Mon Sep 17 00:00:00 2001 From: Aneela Chagarlamudi Date: Mon, 6 Nov 2023 14:19:34 -0500 Subject: [PATCH 24/38] remove internal from all ITLess envs --- src/components/Header/Tools.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Header/Tools.tsx b/src/components/Header/Tools.tsx index f43ed0f62..308d901ef 100644 --- a/src/components/Header/Tools.tsx +++ b/src/components/Header/Tools.tsx @@ -264,7 +264,7 @@ const Tools = () => { )} - {isInternal && ( + {isInternal && !ITLess() && ( From 806e325a78355094bbb78f7ea7514eecac9fa5f8 Mon Sep 17 00:00:00 2001 From: Karel Hala Date: Tue, 7 Nov 2023 09:23:36 +0100 Subject: [PATCH 25/38] Change event WS type for drawer (#2686) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Martin Maroši --- src/hooks/useChromeServiceEvents.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hooks/useChromeServiceEvents.ts b/src/hooks/useChromeServiceEvents.ts index 41709b6f8..2833acaaf 100644 --- a/src/hooks/useChromeServiceEvents.ts +++ b/src/hooks/useChromeServiceEvents.ts @@ -6,7 +6,7 @@ import { UPDATE_NOTIFICATIONS } from '../redux/action-types'; import { getEncodedToken, setCookie } from '../jwt/jwt'; import { NotificationsPayload } from '../redux/store'; -const NOTIFICATION_DRAWER = 'notifications.drawer'; +const NOTIFICATION_DRAWER = 'com.redhat.console.notifications.drawer'; const SAMPLE_EVENT = 'sample.type'; const ALL_TYPES = [NOTIFICATION_DRAWER, SAMPLE_EVENT] as const; From 054b30e969b64b32b72a8b6d7dbc0ff6a6cec656 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Maro=C5=A1i?= Date: Tue, 7 Nov 2023 11:02:32 +0100 Subject: [PATCH 26/38] Unblock RH cypress hosts. (#2691) --- cypress.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cypress.config.ts b/cypress.config.ts index c112d4c4c..f4f28ab5c 100644 --- a/cypress.config.ts +++ b/cypress.config.ts @@ -31,7 +31,7 @@ export default defineConfig({ }, }, e2e: { - blockHosts: ['static.redhat.com', 'consent.trustarc.com', 'www.redhat.com/en/cms/ajax/site-switcher'], + blockHosts: ['consent.trustarc.com'], baseUrl: 'https://stage.foo.redhat.com:1337/beta', env: { E2E_USER: process.env.E2E_USER, From 22cca26fbb32608398302e206a8e429e5a34e5c0 Mon Sep 17 00:00:00 2001 From: Martin Marosi Date: Tue, 7 Nov 2023 12:22:03 +0100 Subject: [PATCH 27/38] Enable all services filtering by service group titles. --- .../AllServicesPage/AllServicesPage.cy.tsx | 74 +++ cypress/fixtures/services.json | 508 ++++++++++++++++++ cypress/fixtures/settings-navigation.json | 107 ++++ src/hooks/useAllServices.ts | 3 + src/hooks/useBreadcrumbsLinks.ts | 2 +- 5 files changed, 693 insertions(+), 1 deletion(-) create mode 100644 cypress/component/AllServicesPage/AllServicesPage.cy.tsx create mode 100644 cypress/fixtures/services.json create mode 100644 cypress/fixtures/settings-navigation.json diff --git a/cypress/component/AllServicesPage/AllServicesPage.cy.tsx b/cypress/component/AllServicesPage/AllServicesPage.cy.tsx new file mode 100644 index 000000000..03a55d685 --- /dev/null +++ b/cypress/component/AllServicesPage/AllServicesPage.cy.tsx @@ -0,0 +1,74 @@ +import React from 'react'; +import AllServices from '../../../src/layouts/AllServices'; +import { BrowserRouter } from 'react-router-dom'; +import { IntlProvider } from 'react-intl'; +import { Provider } from 'react-redux'; +import { createStore } from 'redux'; +import { ScalprumProvider } from '@scalprum/react-core'; +import { getVisibilityFunctions, initializeVisibilityFunctions } from '../../../src/utils/VisibilitySingleton'; +import userFixture from '../../fixtures/testUser.json'; +import { ChromeUser } from '@redhat-cloud-services/types'; + +describe('', () => { + beforeEach(() => { + // mock chrome and scalprum generic requests + cy.intercept('http://localhost:8080/api/chrome-service/v1/static/stable/stage/services/services.json', { + status: 200, + fixture: 'services.json', + }); + cy.intercept('http://localhost:8080/entry?cacheBuster=*', ''); + cy.intercept('http://localhost:8080/foo/bar.json', { + foo: { + entry: ['/entry'], + }, + }); + cy.intercept('http://localhost:8080/api/chrome-service/v1/static/stable/stage/navigation/settings-navigation.json?ts=*', { + status: 200, + fixture: 'settings-navigation.json', + }); + // cy.intercept('http://localhost:8080/api/chrome-service/v1/static/stable/stage/navigation/*-navigation.json?ts=*', { + // data: [], + // }); + }); + + it('should filter by service category title', () => { + initializeVisibilityFunctions({ + getToken: () => Promise.resolve(''), + getUser: () => Promise.resolve(userFixture as unknown as ChromeUser), + getUserPermissions: () => Promise.resolve([]), + }); + const visibilityFunctions = getVisibilityFunctions(); + const store = createStore(() => ({ + chrome: { + moduleRoutes: [ + { + path: '/test/link', + scope: 'foo', + module: 'bar', + }, + ], + }, + })); + cy.mount( + + + + + + + + + + ); + + cy.get('.pf-v5-c-text-input-group__text-input').type('consoleset'); + cy.contains('Console Settings').should('exist'); + }); +}); diff --git a/cypress/fixtures/services.json b/cypress/fixtures/services.json new file mode 100644 index 000000000..8087e49c4 --- /dev/null +++ b/cypress/fixtures/services.json @@ -0,0 +1,508 @@ +[ + { + "id": "dataset", + "icon": "LightBulbIcon", + "title": "AI/ML", + "description": "Create, manage, and migrate relational and non-relational databases.", + "links": [ + { + "isGroup": true, + "title": "Application and Data Services", + "links": [ + "application-services.dataScience" + ] + } + ] + }, + { + "id": "automation", + "title": "Automation", + "icon": "AutomationIcon", + "description": "Solve problems once, in one place, and scale up.", + "links": [ + { + "isGroup": true, + "title": "Red Hat Enterprise Linux", + "links": [ + "rhel.remediations", + "rhel.tasks" + ] + }, + { + "isGroup": true, + "title": "Ansible", + "links": [ + { + "title": "Ansible Hub", + "href": "/ansible/automation-hub/", + "icon": "AnsibleIcon" + }, + { + "title": "Automation Analytics", + "href": "/ansible/automation-analytics/reports", + "icon": "AnsibleIcon", + "description": "Plan, measure, and scale your automation using actionable data." + }, + "ansible.inventory", + { + "title": "Register Systems", + "filterable": false, + "href": "/ansible/registration", + "icon": "AnsibleIcon" + }, + "ansible.tasks" + ] + } + ] + }, + { + "id": "containers", + "title": "Containers", + "icon": "CubeIcon", + "description": "Run your applications in a consistent and isolated environment.", + "links": [ + { + "isGroup": true, + "title": "OpenShift", + "links": [ + { + "title": "Clusters", + "href": "/openshift", + "icon": "OpenShiftIcon", + "description": "View, Register, or Create an OpenShift Cluster." + } + ] + }, + { + "isGroup": true, + "title": "Quay.io", + "links": [ + "quay.quayOrganizations" + ] + }, + { + "isGroup": true, + "title": "Application and Data Services", + "links": [ + "application-services.trustedContentOverview", + "application-services.apiManagement" + ] + } + ] + }, + { + "id": "deploy", + "icon": "RocketIcon", + "title": "Deploy", + "description": "Create RHEL images, systems at the Edge, and OpenShift clusters.", + "links": [ + { + "isGroup": true, + "title": "Red Hat Enterprise Linux", + "links": [ + "rhel.imageBuilder" + ] + }, + { + "isGroup": true, + "title": "Quay.io", + "links": [ + "quay.quayRepositories" + ] + }, + { + "isGroup": true, + "title": "Edge", + "links": [ + { + "title": "Images", + "href": "/edge/manage-images", + "icon": "EdgeIcon", + "description": "Manage the lifecycle and enhance security of your RHEL systems at the edge." + } + ] + } + ] + }, + { + "id": "iam", + "icon": "UsersIcon", + "title": "Identity and Access Management", + "description": "Ensure that the right users have the appropriate access to technology resources.", + "links": [ + { + "isGroup": true, + "title": "IAM", + "links": [ + "iam.users", + "iam.groups", + "iam.authFactors" + ] + }, + { + "isGroup": true, + "title": "Application and Data Services", + "links": [ + { + "title": "Service Accounts", + "href": "/application-services/service-accounts", + "icon": "ServicesIcon", + "description": "Authenticate and connect securely to APIs from multiple services." + } + ] + } + ] + }, + { + "id": "integrations-notifications", + "icon": "BellIcon", + "title": "Integrations and Notifications", + "description": "Alerts users to events, using email and integrations such as webhooks.", + "links": [ + { + "isGroup": true, + "title": "Console Settings", + "links": [ + "settings.integrations", + "settings.notifications" + ] + } + ] + }, + { + "id": "inventories", + "icon": "BoxesIcon", + "title": "Inventories", + "description": "View OpenShift clusters, Edge systems, RHEL hosts, and your organization's subscriptions.", + "links": [ + { + "isGroup": true, + "title": "Red Hat Enterprise Linux", + "links": [ + "rhel.imageBuilder", + "rhel.subscriptionsInventory", + "rhel.inventory", + { + "title": "Inventory Groups", + "href": "/insights/inventory/groups", + "icon": "InsightsIcon" + } + ] + }, + { + "isGroup": true, + "title": "OpenShift", + "links": [ + "openshift.clusters" + ] + }, + { + "isGroup": true, + "title": "Ansible", + "links": [ + "ansible.inventory" + ] + }, + { + "isGroup": true, + "title": "Subscription Services", + "links": [ + "subscriptions.subscriptionsInventory" + ] + } + ] + }, + { + "id": "observe", + "icon": "ChartLineIcon", + "title": "Observability and Monitoring", + "description": "Monitor, troubleshoot, and improve application performance.", + "links": [ + { + "isGroup": true, + "title": "Red Hat Enterprise Linux", + "links": [ + "rhel.advisories", + "rhel.repositories", + { + "href": "/insights/patch/systems", + "title": "Patch", + "icon": "InsightsIcon" + }, + { + "href": "/insights/advisor/recommendations", + "title": "Advisor", + "icon": "InsightsIcon", + "subtitle": "Red Hat Insights for RHEL", + "description": "View details about your Red Hat Enterprise Linux systems." + }, + { + "href": "/insights/drift", + "title": "Drift", + "icon": "InsightsIcon", + "subtitle": "Red Hat Insights for RHEL", + "description": "Compare systems in your Red Hat Enterprise Linux inventory to one another or against a set baseline." + }, + "rhel.policies" + ] + }, + { + "isGroup": true, + "title": "OpenShift", + "links": [ + { + "href": "/openshift/insights/advisor/recommendations", + "icon": "OpenShiftIcon", + "title": "Advisor", + "subtitle": "Red Hat Insights for OpenShift", + "description": "See targeted recommendations to optimize your OpenShift clusters’ availability, performance, and security." + }, + { + "href": "/openshift/insights/vulnerability/cves", + "icon": "OpenShiftIcon", + "title": "Vulnerability", + "subtitle": "Red Hat Insights for OpenShift", + "description": "Identify and prioritize security vulnerabilities within your OpenShift clusters based on severity and frequency." + } + ] + }, + { + "isGroup": true, + "title": "Ansible", + "links": [ + { + "href": "/ansible/advisor/recommendations", + "title": "Advisor", + "subtitle": "Red Hat Insights for Ansible", + "icon": "AnsibleIcon", + "description": "See targeted recommendations to optimize your Ansible hosts’ availability, performance, and security." + }, + { + "title": "Drift", + "href": "/ansible/drift", + "icon": "AnsibleIcon", + "subtitle": "Red Hat Insights for Ansible", + "description": "Compare systems in your Ansible inventory to one another or against a set baseline." + }, + "ansible.policies" + ] + }, + { + "isGroup": true, + "title": "Subscription Services", + "links": [ + { + "id": "openshift", + "title": "Subscriptions Usage", + "icon": "OpenShiftIcon", + "href": "/subscriptions/usage/openshift" + } + ] + } + ] + }, + { + "id": "security", + "icon": "CloudSecurityIcon", + "title": "Security", + "description": "Meet your policy and compliance objectives.", + "links": [ + { + "isGroup": true, + "title": "Red Hat Enterprise Linux", + "links": [ + "rhel.advisories", + { + "href": "/insights/advisor/recommendations", + "title": "Advisor", + "icon": "InsightsIcon", + "subtitle": "Red Hat Insights for RHEL", + "description": "View details about your Red Hat Enterprise Linux systems." + }, + { + "href": "/insights/vulnerability/cves", + "icon": "InsightsIcon", + "title": "Vulnerability", + "subtitle": "Red Hat Insights for RHEL", + "description": "Identify and prioritize security vulnerabilities within your Red Hat Enterprise Linux systems based on severity and frequency." + }, + { + "href": "/insights/compliance/reports", + "icon": "InsightsIcon", + "title": "Compliance", + "subtitle":"Red Hat Insights for RHEL", + "description": "Evaluate your Red Hat Enterprise systems’ compliance with security or regulatory standards." + }, + { + "href": "/insights/malware/signatures", + "title": "Malware", + "icon": "InsightsIcon", + "subtitle": "Red Hat Insights for RHEL", + "description": "Identify potential malware on your Red Hat Enterprise Linux systems." + }, + "rhel.remediations" + ] + }, + { + "isGroup": true, + "title": "OpenShift", + "links": [ + { + "href": "/openshift/insights/vulnerability/cves", + "icon": "OpenShiftIcon", + "title": "Vulnerability", + "subtitle": "Red Hat Insights for OpenShift" + } + ] + }, + { + "isGroup": true, + "title": "Ansible", + "links": [ + "ansible.remediations" + ] + }, + { + "isGroup": true, + "title": "Quay.io", + "links": [ + "quay.quayOrganizations" + ] + }, + { + "isGroup": true, + "title": "Application and Data Services", + "links": [ + "application-services.trustedContentOverview", + { + "title": "Advanced Cluster Security", + "href": "/application-services/acs/overview", + "icon": "ACSIcon", + "description": "Securely build, deploy, and run your cloud applications using Kubernetes-native architecture." + } + ] + } + ] + }, + { + "id": "subscriptions", + "icon": "CreditCardIcon", + "title": "Subscriptions and Spend", + "description": "Subscription Services empower buying decisions. SaMS provides software usage reporting that is designed to make your subscription choices data-driven.", + "links": [ + { + "isGroup": true, + "title": "Red Hat Enterprise Linux", + "links": [ + { + "title": "Subscriptions", + "href": "/insights/subscriptions/rhel", + "icon": "OpenShiftIcon" + }, + { + "title": "Resource Optimization", + "filterable": false, + "href": "/insights/ros", + "icon": "OpenShiftIcon" + } + ] + }, + { + "isGroup": true, + "title": "OpenShift", + "links": [ + { + "title": "Subscriptions", + "href": "/openshift/subscriptions/openshift", + "icon": "OpenShiftIcon" + }, + { + "title": "Cost Management", + "filterable": false, + "href": "/openshift/cost-management", + "icon": "OpenShiftIcon" + } + ] + }, + { + "isGroup": true, + "title": "Subscription Services", + "links": [ + "subscriptions.subscriptionsInventory", + "subscriptions.subscriptionsUsage", + "hybridCommittedSpend.hybridCommittedSpend" + ] + } + ] + }, + { + "id": "systemConfiguration", + "icon": "CogIcon", + "title": "System Configuration", + "description": "Connect your RHEL systems to Hybrid Cloud Console services.", + "links": [ + { + "isGroup": true, + "title": "Red Hat Enterprise Linux", + "links": [ + { + "href": "/insights/connector", + "title": "Remote Host Configuration (RHC)", + "icon": "InsightsIcon", + "subtitle": "Red Hat Insights for RHEL", + "description": "Configure your systems to execute Ansible Playbooks." + }, + "settings.manageConfiguration", + "rhel.activationKeys", + "rhel.manifests", + "rhel.registerSystems", + "rhel.stalenessAndDeletion" + ] + }, + { + "isGroup": true, + "title": "Ansible", + "links": [ + { + "title": "Register Systems", + "filterable": false, + "href": "/ansible/registration", + "icon": "AnsibleIcon" + } + ] + }, + { + "isGroup": true, + "title": "Console Settings", + "links": [ + "settings.integrations", + "settings.notifications" + ] + } + ] + }, + { + "id": "tryBuy", + "icon": "ShoppingCartIcon", + "title": "Try and Buy", + "description": "Our no-cost trials help you gain hands-on experience, prepare for a certification, or assess if a product is right for your organization.", + "links": [ + "openshift.developerSandbox", + { + "isExternal": true, + "href": "https://marketplace.redhat.com/en-us", + "title": "Red Hat Marketplace", + "icon": "RHIcon", + "description": "Find, try, purchase, and deploy your software across clouds." + }, + { + "isExternal": true, + "href": "https://www.redhat.com/en/products/trials", + "title": "Red Hat Product Trials", + "icon": "RHIcon", + "description": "Gain hands-on experience and assess if a product is right for you in a no-cost trial." + } + ] + } + ] + \ No newline at end of file diff --git a/cypress/fixtures/settings-navigation.json b/cypress/fixtures/settings-navigation.json new file mode 100644 index 000000000..30ceaab4f --- /dev/null +++ b/cypress/fixtures/settings-navigation.json @@ -0,0 +1,107 @@ +{ + "id": "settings", + "title": "Settings", + "navItems": [ + { + "id": "sources", + "appId": "sources", + "title": "Integrations", + "description": "Integrations provide a way for applications to collect data outside of the Red Hat Hybrid Cloud Console through either a direct connection to the source or indirectly.", + "href": "/settings/integrations", + "icon": "PlaceholderIcon", + "alt_title": [ + "providers", + "cloud providers", + "cloud sources", + "Red Hat sources", + "integrations", + "cloud integrations", + "cloud connection", + "aws", + "Amazon", + "Google", + "cloud", + "IBM", + "Microsoft", + "secrets", + "Azure", + "Oracle", + "Infrastructure", + "prediction", + "cost", + "subscriptions", + "account", + "credentials" + ] + }, + { + "id": "learningResources", + "appId": "learningResources", + "title": "Learning Resources", + "href": "/settings/learning-resources" + }, + { + "id": "idmsvc", + "appId": "idmsvc", + "title": "Directory & Domain Services", + "description": "Directory and Domain Services for console.redhat.com", + "href": "/settings/idmsvc", + "icon": "PlaceholderIcon" + }, + { + "title": "Notifications", + "id": "notifications", + "description": "A standardized way of notifying users of events for supported services on the Hybrid Cloud Console.", + "expandable": true, + "routes": [ + { + "id": "notificationOverview", + "appId": "notifications", + "icon": "PlaceholderIcon", + "title": "Overview", + "href": "/settings/notifications" + }, + { + "id": "configureEvents", + "appId": "notifications", + "title": "Configure Events", + "href": "/settings/notifications/configure-events", + "permissions": [ + { + "method": "isOrgAdmin" + } + ] + }, + { + "id": "eventLog", + "appId": "notifications", + "title": "Event Log", + "href": "/settings/notifications/eventlog", + "permissions": [ + { + "method": "isOrgAdmin" + } + ] + }, + { + "id": "notificationsLog", + "appId": "notifications", + "title": "Notifications Log", + "href": "/settings/notifications/notificationslog", + "permissions": [ + { + "method": "featureFlag", + "args": ["platform.chrome.notifications-drawer", true] + } + ] + }, + { + "id": "myUserPreferences", + "appId": "notifications", + "title": "My User Preferences", + "href": "/settings/notifications/user-preferences" + } + ] + } + ] +} diff --git a/src/hooks/useAllServices.ts b/src/hooks/useAllServices.ts index f8266df6c..f75f82841 100644 --- a/src/hooks/useAllServices.ts +++ b/src/hooks/useAllServices.ts @@ -107,6 +107,9 @@ const filterAllServicesLinks = (links: (AllServicesLink | AllServicesGroup)[], f return links.reduce<(AllServicesLink | AllServicesGroup)[]>((acc, link) => { // groups have links nested, we have to filter them as well if (isAllServicesGroup(link)) { + if (matchStrings(link.title, filterValue)) { + return [...acc, link]; + } const groupLinks = filterAllServicesLinks(link.links, filterValue); // replace group links with filtered results const newGroup: AllServicesGroup = { diff --git a/src/hooks/useBreadcrumbsLinks.ts b/src/hooks/useBreadcrumbsLinks.ts index f75eb6437..4150f3b7b 100644 --- a/src/hooks/useBreadcrumbsLinks.ts +++ b/src/hooks/useBreadcrumbsLinks.ts @@ -24,7 +24,7 @@ const useBreadcrumbsLinks = () => { href: `/${bundleId}`, }, ]; - const activeNavSegment = navigation[bundleId]; + const activeNavSegment = navigation?.[bundleId]; if (activeNavSegment && isNavItems(activeNavSegment)) { const activeNavigation = extractNavItemGroups(activeNavSegment); const { activeItem, navItems } = findNavLeafPath(activeNavigation); From 6151a4560786df7a3ce376de58feb0db9552f588 Mon Sep 17 00:00:00 2001 From: Martin Marosi Date: Tue, 7 Nov 2023 16:54:38 +0100 Subject: [PATCH 28/38] Update dependecies. --- package-lock.json | 1002 +++++++-------------------------------------- 1 file changed, 139 insertions(+), 863 deletions(-) diff --git a/package-lock.json b/package-lock.json index a1d1c4885..b215000db 100644 --- a/package-lock.json +++ b/package-lock.json @@ -150,9 +150,9 @@ } }, "node_modules/@adobe/css-tools": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.3.0.tgz", - "integrity": "sha512-+RNNcQvw2V1bmnBTPAtOLfW/9mhH2vC67+rUSi5T8EtEWt6lEnGNY2GuhZ1/YwbgikT1TkhvidCDmN5Q5YCo/w==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.3.1.tgz", + "integrity": "sha512-/62yikz7NLScCGAAST5SHdnjaDJQBDq0M2muyRTpf2VQhw6StBg2ALiu73zSJQ4fMVLA+0uBhBHAle7Wg+2kSg==", "dev": true }, "node_modules/@ampproject/remapping": { @@ -219,12 +219,12 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.10.tgz", - "integrity": "sha512-/KKIMG4UEL35WmI9OlvMhurwtytjvXoFcGNrOvyG9zIzA8YmPjVtIZUf7b05+TPO7G7/GEmLHDaoCgACHl9hhA==", + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", "dev": true, "dependencies": { - "@babel/highlight": "^7.22.10", + "@babel/highlight": "^7.22.13", "chalk": "^2.4.2" }, "engines": { @@ -289,12 +289,12 @@ } }, "node_modules/@babel/generator": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.10.tgz", - "integrity": "sha512-79KIf7YiWjjdZ81JnLujDRApWtl7BxTqWD88+FFdQEIOG8LJ0etDOM7CXuIgGJa55sGOwZVwuEsaLEm0PJ5/+A==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", + "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", "dev": true, "dependencies": { - "@babel/types": "^7.22.10", + "@babel/types": "^7.23.0", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -320,22 +320,22 @@ } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", - "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", - "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dev": true, "dependencies": { - "@babel/template": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" }, "engines": { "node": ">=6.9.0" @@ -427,9 +427,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", - "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "dev": true, "engines": { "node": ">=6.9.0" @@ -459,12 +459,12 @@ } }, "node_modules/@babel/highlight": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.10.tgz", - "integrity": "sha512-78aUtVcT7MUscr0K5mIEnkwxPE0MaxkR5RxRwuHaQ+JuU5AmTPhY+do2mdzVTnIJJpyBglql2pehuBIWHug+WQ==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, @@ -473,9 +473,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.10.tgz", - "integrity": "sha512-lNbdGsQb9ekfsnjFGhEiF4hfFqGgfOP3H3d27re3n+CGhNuTSUEQdfWk556sTLNTloczcdM5TYF2LhzmDQKyvQ==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", + "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", "bin": { "parser": "bin/babel-parser.js" }, @@ -672,33 +672,33 @@ } }, "node_modules/@babel/template": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", - "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.22.5", - "@babel/parser": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.10.tgz", - "integrity": "sha512-Q/urqV4pRByiNNpb/f5OSv28ZlGJiFiiTh+GAHktbIrkPhPbl90+uW6SmpoLyZqutrg9AEaEf3Q/ZBRHBXgxig==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", + "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.22.10", - "@babel/generator": "^7.22.10", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.22.10", - "@babel/types": "^7.22.10", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -707,13 +707,13 @@ } }, "node_modules/@babel/types": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.10.tgz", - "integrity": "sha512-obaoigiLrlDZ7TUQln/8m4mSqIW2QFeOrCQc9r+xsaHGNoplVNYlRVpsfE8Vj35GEm2ZH4ZhrNYogs/3fj85kg==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" }, "engines": { @@ -2757,20 +2757,19 @@ "integrity": "sha512-8akdWzFpG384Q6Es8lzkfuhAlzVGrNK7TJqXGecHDAg8u1JsYn3+Nw6gLRviI88z8Kjxmg5YKirILjpclGxkIA==" }, "node_modules/@patternfly/quickstarts": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@patternfly/quickstarts/-/quickstarts-5.0.0.tgz", - "integrity": "sha512-G1nb0x+jmO/F2fVv+bymadvjO+RZWwY3foU+1VBqvI28vtr7M6isLj81Cz8cjjT0t0Z2CdMSVVfkwxoKQxMOgA==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@patternfly/quickstarts/-/quickstarts-5.1.0.tgz", + "integrity": "sha512-ql5Q8IlW+Yp1Y7rqDqrsoDhYrSv3/HTp+1BYg7n4vyOsTc9QL+PxW4+PI8mLLk/tB6Z93yS7l76b5gaACPgH2g==", "dependencies": { "@patternfly/react-catalog-view-extension": "^5.0.0", "dompurify": "^2.2.6", - "history": "^5.0.0", - "showdown": "1.8.6" + "history": "^5.0.0" }, "peerDependencies": { - "@patternfly/react-core": ">=4.115.2", + "@patternfly/react-core": ">=5.0.0", "react": ">=16.8.0", "react-dom": ">=16.8.0", - "showdown": ">=1.8.6" + "showdown": ">=2.1.0" } }, "node_modules/@patternfly/react-catalog-view-extension": { @@ -10442,14 +10441,6 @@ "node": ">= 0.12.0" } }, - "node_modules/code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/collect-v8-coverage": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", @@ -11401,14 +11392,6 @@ } } }, - "node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/decimal.js": { "version": "10.4.3", "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", @@ -14738,14 +14721,6 @@ "tslib": "^2.4.0" } }, - "node_modules/invert-kv": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", - "integrity": "sha512-xgs2NH9AE66ucSq4cNG1nhSFghr5l6tdL15Pk+jl46bmmBapgoaY/AacXyaDznAqmGL99TiLSQgO/XazFSKYeQ==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/ipaddr.js": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.1.0.tgz", @@ -18969,17 +18944,6 @@ "node": "> 0.8" } }, - "node_modules/lcid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", - "integrity": "sha512-YiGkH6EnGrDGqLMITnGjXtGmNtjoXw9SVUzcaos8RBi7Ps0VBylkq+vOcY9QE5poLasPCR849ucFUkl0UzUyOw==", - "dependencies": { - "invert-kv": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", @@ -19594,25 +19558,6 @@ "node": ">= 0.6" } }, - "node_modules/mem": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", - "integrity": "sha512-nOBDrc/wgpkd3X/JOhMqYR+/eLqlfLP4oQfoBA6QExIxEl+GU01oyEkwWyueyO8110pUKijtiHGhEmYoOn88oQ==", - "dependencies": { - "mimic-fn": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/mem/node_modules/mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", - "engines": { - "node": ">=4" - } - }, "node_modules/memfs": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", @@ -20312,14 +20257,6 @@ "url": "https://github.com/fb55/nth-check?sponsor=1" } }, - "node_modules/number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/nwsapi": { "version": "2.2.7", "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.7.tgz", @@ -20662,125 +20599,6 @@ "node": ">=8" } }, - "node_modules/os-locale": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", - "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", - "dependencies": { - "execa": "^0.7.0", - "lcid": "^1.0.0", - "mem": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/os-locale/node_modules/cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==", - "dependencies": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "node_modules/os-locale/node_modules/execa": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", - "integrity": "sha512-RztN09XglpYI7aBBrJCPW95jEH7YF1UEPOoX9yDhUTPdp7mK+CQvnLTuD10BNXZ3byLTu2uehZ8EcKT/4CGiFw==", - "dependencies": { - "cross-spawn": "^5.0.1", - "get-stream": "^3.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/os-locale/node_modules/get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==", - "engines": { - "node": ">=4" - } - }, - "node_modules/os-locale/node_modules/is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/os-locale/node_modules/lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dependencies": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "node_modules/os-locale/node_modules/npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", - "dependencies": { - "path-key": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/os-locale/node_modules/path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", - "engines": { - "node": ">=4" - } - }, - "node_modules/os-locale/node_modules/shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", - "dependencies": { - "shebang-regex": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/os-locale/node_modules/shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/os-locale/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, - "node_modules/os-locale/node_modules/yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==" - }, "node_modules/ospath": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/ospath/-/ospath-1.2.2.tgz", @@ -21252,9 +21070,9 @@ } }, "node_modules/postcss": { - "version": "8.4.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.27.tgz", - "integrity": "sha512-gY/ACJtJPSmUFPDCHtX78+01fHa64FaU4zaaWfuh1MhGJISufJAH4cun6k/8fwsHYeK4UQmENQK+tRLCFJE8JQ==", + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", "funding": [ { "type": "opencollective", @@ -21863,11 +21681,6 @@ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz", "integrity": "sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A==" }, - "node_modules/pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==" - }, "node_modules/psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", @@ -22711,6 +22524,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -22724,11 +22538,6 @@ "node": ">=0.10.0" } }, - "node_modules/require-main-filename": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", - "integrity": "sha512-IqSUtOVP4ksd1C/ej5zeEh/BIP2ajqpn8c5x+q99gvcIG/Qf0cud5raVnE/Dwd0ua9TXYDoDc0RE5hBSdz22Ug==" - }, "node_modules/requirejs": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.3.6.tgz", @@ -23390,11 +23199,6 @@ "node": ">= 0.8.0" } }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" - }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", @@ -23447,224 +23251,28 @@ } }, "node_modules/showdown": { - "version": "1.8.6", - "resolved": "https://registry.npmjs.org/showdown/-/showdown-1.8.6.tgz", - "integrity": "sha512-cOmS+LUIiyMxFo7OU3cgV+zTv43GItwlTwUPrpUd5dqdlZh8CJMVb8KxAMhr42J6exQwKTCHMxUiG74vamV1kA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/showdown/-/showdown-2.1.0.tgz", + "integrity": "sha512-/6NVYu4U819R2pUIk79n67SYgJHWCce0a5xTP979WbNp0FL9MN1I1QK662IDU1b6JzKTvmhgI7T7JYIxBi3kMQ==", + "peer": true, "dependencies": { - "yargs": "^10.0.3" + "commander": "^9.0.0" }, "bin": { "showdown": "bin/showdown.js" - } - }, - "node_modules/showdown/node_modules/ansi-regex": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", - "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", - "engines": { - "node": ">=4" - } - }, - "node_modules/showdown/node_modules/camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha512-FxAv7HpHrXbh3aPo4o2qxHay2lkLY3x5Mw3KeE4KQE8ysVfziWeRZDwcjauvwBSGEC/nXUPzZy8zeh4HokqOnw==", - "engines": { - "node": ">=4" - } - }, - "node_modules/showdown/node_modules/cliui": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", - "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", - "dependencies": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" - } - }, - "node_modules/showdown/node_modules/find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", - "dependencies": { - "locate-path": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/showdown/node_modules/get-caller-file": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", - "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" - }, - "node_modules/showdown/node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", - "engines": { - "node": ">=4" - } - }, - "node_modules/showdown/node_modules/locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", - "dependencies": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/showdown/node_modules/p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dependencies": { - "p-try": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/showdown/node_modules/p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", - "dependencies": { - "p-limit": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/showdown/node_modules/p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", - "engines": { - "node": ">=4" - } - }, - "node_modules/showdown/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", - "engines": { - "node": ">=4" - } - }, - "node_modules/showdown/node_modules/string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dependencies": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/showdown/node_modules/strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", - "dependencies": { - "ansi-regex": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/showdown/node_modules/wrap-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha512-vAaEaDM946gbNpH5pLVNR+vX2ht6n0Bt3GXwVB1AuAqZosOvHNF3P7wDnh8KLkSqgUh0uh77le7Owgoz+Z9XBw==", - "dependencies": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/showdown/node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/showdown/node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==", - "dependencies": { - "number-is-nan": "^1.0.0" }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/showdown/node_modules/wrap-ansi/node_modules/string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==", - "dependencies": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" + "funding": { + "type": "individual", + "url": "https://www.paypal.me/tiviesantos" } }, - "node_modules/showdown/node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", - "dependencies": { - "ansi-regex": "^2.0.0" - }, + "node_modules/showdown/node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "peer": true, "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/showdown/node_modules/y18n": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz", - "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==" - }, - "node_modules/showdown/node_modules/yargs": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-10.1.2.tgz", - "integrity": "sha512-ivSoxqBGYOqQVruxD35+EyCFDYNEFL/Uo6FcOnz+9xZdZzK0Zzw4r4KhbrME1Oo2gOggwJod2MnsdamSG7H9ig==", - "dependencies": { - "cliui": "^4.0.0", - "decamelize": "^1.1.1", - "find-up": "^2.1.0", - "get-caller-file": "^1.0.1", - "os-locale": "^2.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1", - "yargs-parser": "^8.1.0" - } - }, - "node_modules/showdown/node_modules/yargs-parser": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-8.1.0.tgz", - "integrity": "sha512-yP+6QqN8BmrgW2ggLtTbdrOyBNSI7zBa4IykmiV5R1wl1JWNxQvWhMfMdmzIYtKU7oP3OOInY/tl2ov3BDjnJQ==", - "dependencies": { - "camelcase": "^4.1.0" + "node": "^12.20.0 || >=14" } }, "node_modules/side-channel": { @@ -24163,14 +23771,6 @@ "node": ">=8" } }, - "node_modules/strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/strip-final-newline": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", @@ -26998,11 +26598,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/which-module": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", - "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==" - }, "node_modules/which-typed-array": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.11.tgz", @@ -27226,9 +26821,9 @@ "dev": true }, "@adobe/css-tools": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.3.0.tgz", - "integrity": "sha512-+RNNcQvw2V1bmnBTPAtOLfW/9mhH2vC67+rUSi5T8EtEWt6lEnGNY2GuhZ1/YwbgikT1TkhvidCDmN5Q5YCo/w==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.3.1.tgz", + "integrity": "sha512-/62yikz7NLScCGAAST5SHdnjaDJQBDq0M2muyRTpf2VQhw6StBg2ALiu73zSJQ4fMVLA+0uBhBHAle7Wg+2kSg==", "dev": true }, "@ampproject/remapping": { @@ -27293,12 +26888,12 @@ } }, "@babel/code-frame": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.10.tgz", - "integrity": "sha512-/KKIMG4UEL35WmI9OlvMhurwtytjvXoFcGNrOvyG9zIzA8YmPjVtIZUf7b05+TPO7G7/GEmLHDaoCgACHl9hhA==", + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", "dev": true, "requires": { - "@babel/highlight": "^7.22.10", + "@babel/highlight": "^7.22.13", "chalk": "^2.4.2" } }, @@ -27343,12 +26938,12 @@ } }, "@babel/generator": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.10.tgz", - "integrity": "sha512-79KIf7YiWjjdZ81JnLujDRApWtl7BxTqWD88+FFdQEIOG8LJ0etDOM7CXuIgGJa55sGOwZVwuEsaLEm0PJ5/+A==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", + "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", "dev": true, "requires": { - "@babel/types": "^7.22.10", + "@babel/types": "^7.23.0", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -27368,19 +26963,19 @@ } }, "@babel/helper-environment-visitor": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", - "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "dev": true }, "@babel/helper-function-name": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", - "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dev": true, "requires": { - "@babel/template": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" } }, "@babel/helper-hoist-variables": { @@ -27445,9 +27040,9 @@ "dev": true }, "@babel/helper-validator-identifier": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", - "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "dev": true }, "@babel/helper-validator-option": { @@ -27468,20 +27063,20 @@ } }, "@babel/highlight": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.10.tgz", - "integrity": "sha512-78aUtVcT7MUscr0K5mIEnkwxPE0MaxkR5RxRwuHaQ+JuU5AmTPhY+do2mdzVTnIJJpyBglql2pehuBIWHug+WQ==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", "chalk": "^2.4.2", "js-tokens": "^4.0.0" } }, "@babel/parser": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.10.tgz", - "integrity": "sha512-lNbdGsQb9ekfsnjFGhEiF4hfFqGgfOP3H3d27re3n+CGhNuTSUEQdfWk556sTLNTloczcdM5TYF2LhzmDQKyvQ==" + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", + "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==" }, "@babel/plugin-syntax-async-generators": { "version": "7.8.4", @@ -27618,42 +27213,42 @@ } }, "@babel/template": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", - "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", "dev": true, "requires": { - "@babel/code-frame": "^7.22.5", - "@babel/parser": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" } }, "@babel/traverse": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.10.tgz", - "integrity": "sha512-Q/urqV4pRByiNNpb/f5OSv28ZlGJiFiiTh+GAHktbIrkPhPbl90+uW6SmpoLyZqutrg9AEaEf3Q/ZBRHBXgxig==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", + "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", "dev": true, "requires": { - "@babel/code-frame": "^7.22.10", - "@babel/generator": "^7.22.10", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.22.10", - "@babel/types": "^7.22.10", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", "debug": "^4.1.0", "globals": "^11.1.0" } }, "@babel/types": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.10.tgz", - "integrity": "sha512-obaoigiLrlDZ7TUQln/8m4mSqIW2QFeOrCQc9r+xsaHGNoplVNYlRVpsfE8Vj35GEm2ZH4ZhrNYogs/3fj85kg==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", "dev": true, "requires": { "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" } }, @@ -29263,14 +28858,13 @@ "integrity": "sha512-8akdWzFpG384Q6Es8lzkfuhAlzVGrNK7TJqXGecHDAg8u1JsYn3+Nw6gLRviI88z8Kjxmg5YKirILjpclGxkIA==" }, "@patternfly/quickstarts": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@patternfly/quickstarts/-/quickstarts-5.0.0.tgz", - "integrity": "sha512-G1nb0x+jmO/F2fVv+bymadvjO+RZWwY3foU+1VBqvI28vtr7M6isLj81Cz8cjjT0t0Z2CdMSVVfkwxoKQxMOgA==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@patternfly/quickstarts/-/quickstarts-5.1.0.tgz", + "integrity": "sha512-ql5Q8IlW+Yp1Y7rqDqrsoDhYrSv3/HTp+1BYg7n4vyOsTc9QL+PxW4+PI8mLLk/tB6Z93yS7l76b5gaACPgH2g==", "requires": { "@patternfly/react-catalog-view-extension": "^5.0.0", "dompurify": "^2.2.6", - "history": "^5.0.0", - "showdown": "1.8.6" + "history": "^5.0.0" } }, "@patternfly/react-catalog-view-extension": { @@ -34050,11 +33644,6 @@ "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", "dev": true }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==" - }, "collect-v8-coverage": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", @@ -34791,11 +34380,6 @@ "ms": "2.1.2" } }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==" - }, "decimal.js": { "version": "10.4.3", "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", @@ -37309,11 +36893,6 @@ } } }, - "invert-kv": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", - "integrity": "sha512-xgs2NH9AE66ucSq4cNG1nhSFghr5l6tdL15Pk+jl46bmmBapgoaY/AacXyaDznAqmGL99TiLSQgO/XazFSKYeQ==" - }, "ipaddr.js": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.1.0.tgz", @@ -40519,14 +40098,6 @@ "resolved": "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz", "integrity": "sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw==" }, - "lcid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", - "integrity": "sha512-YiGkH6EnGrDGqLMITnGjXtGmNtjoXw9SVUzcaos8RBi7Ps0VBylkq+vOcY9QE5poLasPCR849ucFUkl0UzUyOw==", - "requires": { - "invert-kv": "^1.0.0" - } - }, "leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", @@ -40983,21 +40554,6 @@ "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", "dev": true }, - "mem": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", - "integrity": "sha512-nOBDrc/wgpkd3X/JOhMqYR+/eLqlfLP4oQfoBA6QExIxEl+GU01oyEkwWyueyO8110pUKijtiHGhEmYoOn88oQ==", - "requires": { - "mimic-fn": "^1.0.0" - }, - "dependencies": { - "mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==" - } - } - }, "memfs": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", @@ -41525,11 +41081,6 @@ "boolbase": "^1.0.0" } }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==" - }, "nwsapi": { "version": "2.2.7", "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.7.tgz", @@ -41778,100 +41329,6 @@ } } }, - "os-locale": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", - "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", - "requires": { - "execa": "^0.7.0", - "lcid": "^1.0.0", - "mem": "^1.1.0" - }, - "dependencies": { - "cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==", - "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "execa": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", - "integrity": "sha512-RztN09XglpYI7aBBrJCPW95jEH7YF1UEPOoX9yDhUTPdp7mK+CQvnLTuD10BNXZ3byLTu2uehZ8EcKT/4CGiFw==", - "requires": { - "cross-spawn": "^5.0.1", - "get-stream": "^3.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, - "get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==" - }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==" - }, - "lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", - "requires": { - "path-key": "^2.0.0" - } - }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==" - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==" - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "requires": { - "isexe": "^2.0.0" - } - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==" - } - } - }, "ospath": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/ospath/-/ospath-1.2.2.tgz", @@ -42236,9 +41693,9 @@ "dev": true }, "postcss": { - "version": "8.4.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.27.tgz", - "integrity": "sha512-gY/ACJtJPSmUFPDCHtX78+01fHa64FaU4zaaWfuh1MhGJISufJAH4cun6k/8fwsHYeK4UQmENQK+tRLCFJE8JQ==", + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", "requires": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", @@ -42669,11 +42126,6 @@ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz", "integrity": "sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A==" }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==" - }, "psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", @@ -43303,7 +42755,8 @@ "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==" + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true }, "require-from-string": { "version": "2.0.2", @@ -43311,11 +42764,6 @@ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "dev": true }, - "require-main-filename": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", - "integrity": "sha512-IqSUtOVP4ksd1C/ej5zeEh/BIP2ajqpn8c5x+q99gvcIG/Qf0cud5raVnE/Dwd0ua9TXYDoDc0RE5hBSdz22Ug==" - }, "requirejs": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.3.6.tgz", @@ -43799,11 +43247,6 @@ "send": "0.18.0" } }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" - }, "setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", @@ -43844,176 +43287,19 @@ "dev": true }, "showdown": { - "version": "1.8.6", - "resolved": "https://registry.npmjs.org/showdown/-/showdown-1.8.6.tgz", - "integrity": "sha512-cOmS+LUIiyMxFo7OU3cgV+zTv43GItwlTwUPrpUd5dqdlZh8CJMVb8KxAMhr42J6exQwKTCHMxUiG74vamV1kA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/showdown/-/showdown-2.1.0.tgz", + "integrity": "sha512-/6NVYu4U819R2pUIk79n67SYgJHWCce0a5xTP979WbNp0FL9MN1I1QK662IDU1b6JzKTvmhgI7T7JYIxBi3kMQ==", + "peer": true, "requires": { - "yargs": "^10.0.3" + "commander": "^9.0.0" }, "dependencies": { - "ansi-regex": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", - "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==" - }, - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha512-FxAv7HpHrXbh3aPo4o2qxHay2lkLY3x5Mw3KeE4KQE8ysVfziWeRZDwcjauvwBSGEC/nXUPzZy8zeh4HokqOnw==" - }, - "cliui": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", - "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", - "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" - } - }, - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", - "requires": { - "locate-path": "^2.0.0" - } - }, - "get-caller-file": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", - "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==" - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==" - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==" - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", - "requires": { - "ansi-regex": "^3.0.0" - } - }, - "wrap-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha512-vAaEaDM946gbNpH5pLVNR+vX2ht6n0Bt3GXwVB1AuAqZosOvHNF3P7wDnh8KLkSqgUh0uh77le7Owgoz+Z9XBw==", - "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==" - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==", - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==", - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", - "requires": { - "ansi-regex": "^2.0.0" - } - } - } - }, - "y18n": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz", - "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==" - }, - "yargs": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-10.1.2.tgz", - "integrity": "sha512-ivSoxqBGYOqQVruxD35+EyCFDYNEFL/Uo6FcOnz+9xZdZzK0Zzw4r4KhbrME1Oo2gOggwJod2MnsdamSG7H9ig==", - "requires": { - "cliui": "^4.0.0", - "decamelize": "^1.1.1", - "find-up": "^2.1.0", - "get-caller-file": "^1.0.1", - "os-locale": "^2.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1", - "yargs-parser": "^8.1.0" - } - }, - "yargs-parser": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-8.1.0.tgz", - "integrity": "sha512-yP+6QqN8BmrgW2ggLtTbdrOyBNSI7zBa4IykmiV5R1wl1JWNxQvWhMfMdmzIYtKU7oP3OOInY/tl2ov3BDjnJQ==", - "requires": { - "camelcase": "^4.1.0" - } + "commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "peer": true } } }, @@ -44417,11 +43703,6 @@ "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", "dev": true }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==" - }, "strip-final-newline": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", @@ -46775,11 +46056,6 @@ "is-weakset": "^2.0.1" } }, - "which-module": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", - "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==" - }, "which-typed-array": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.11.tgz", From d1e42233f3255376562487f012176d3b9133ca35 Mon Sep 17 00:00:00 2001 From: ewinchel Date: Tue, 7 Nov 2023 12:29:36 -0500 Subject: [PATCH 29/38] Restyle service menu --- .../AllServicesDropdown.cy.tsx | 4 +- cypress/e2e/release-gate/navigation.cy.ts | 2 +- .../AllServicesDropdown.scss | 35 +++-------- .../AllServicesDropdown/AllServicesMenu.tsx | 59 +++++++++---------- .../AllServicesDropdown/AllServicesTabs.tsx | 4 +- 5 files changed, 43 insertions(+), 61 deletions(-) diff --git a/cypress/component/AllServicesDropdown/AllServicesDropdown.cy.tsx b/cypress/component/AllServicesDropdown/AllServicesDropdown.cy.tsx index 6203698bf..760e1dea6 100644 --- a/cypress/component/AllServicesDropdown/AllServicesDropdown.cy.tsx +++ b/cypress/component/AllServicesDropdown/AllServicesDropdown.cy.tsx @@ -23,11 +23,11 @@ describe('', () => { it('should close all services dropdown in link matches current pathname', () => { function checkMenuClosed() { cy.get('.pf-v5-c-menu-toggle__text').click(); - cy.contains('Browse all services').should('exist'); + cy.contains('All services').should('exist'); cy.contains('Favorites').click(); cy.contains('Test section').click(); cy.contains('Test link').click(); - cy.contains('Browse all services').should('not.exist'); + cy.contains('All services').should('not.exist'); } cy.intercept('http://localhost:8080/api/chrome-service/v1/static/stable/stage/services/services.json', [ { diff --git a/cypress/e2e/release-gate/navigation.cy.ts b/cypress/e2e/release-gate/navigation.cy.ts index 7b74e120d..a7ea5d549 100644 --- a/cypress/e2e/release-gate/navigation.cy.ts +++ b/cypress/e2e/release-gate/navigation.cy.ts @@ -14,7 +14,7 @@ describe('Navigation', () => { cy.get('.chr-c-link-service-toggle').click(); // check if favorite services links exist - cy.contains('.pf-v5-c-tabs__link', 'My favorite services'); + cy.contains('.pf-v5-c-tabs__link', 'Favorites'); // click on all services cy.get('.chr-l-flex__item-browse-all-services a').click(); diff --git a/src/components/AllServicesDropdown/AllServicesDropdown.scss b/src/components/AllServicesDropdown/AllServicesDropdown.scss index 4bf344a14..3c1ee7fa2 100644 --- a/src/components/AllServicesDropdown/AllServicesDropdown.scss +++ b/src/components/AllServicesDropdown/AllServicesDropdown.scss @@ -13,11 +13,11 @@ .chr-c-page__services-nav-dropdown-menu { @media screen and (min-width: $pf-v5-global--breakpoint--2xl) { top: 70px; - height: calc(100vh - 70px); + height: 100vh; } @media screen and (max-width: $pf-v5-global--breakpoint--2xl) { top: 118px; - height: calc(100vh - 118px); + height: 100vh; } // pos has to be important to override PF styles that get lazy loaded! @@ -39,23 +39,14 @@ @media (max-width: $pf-v5-global--breakpoint--md) { .chr-c-panel-services-nav { - .chr-l-flex__item-browse-all-services { + .chr-l-stack__item-browse-all-services { background: var(--pf-v5-global--BackgroundColor--200); border-bottom: var(--pf-v5-global--BorderWidth--sm) solid var(--pf-v5-global--BorderColor--dark-100); } .pf-v5-c-tabs { background: var(--pf-v5-global--BackgroundColor--200); border-bottom: var(--pf-v5-global--BorderWidth--sm) solid var(--pf-v5-global--BorderColor--dark-100); - height: calc(100% - 80px); - } - .pf-v5-c-tabs__toggle-button { - width: 100%; - } - .pf-v5-c-tabs__toggle-button .pf-v5-c-button { - flex-direction: row-reverse; - justify-content: space-between; - padding: var(--pf-v5-global--spacer--sm); - width: 100%; + height: 100%; } .pf-v5-c-sidebar__panel { --pf-v5-l-flex--RowGap: 0; @@ -65,30 +56,22 @@ @media (min-width: $pf-v5-global--breakpoint--md) { .chr-c-panel-services-nav { - .chr-l-flex__item-browse-all-services { - border-top: var(--pf-v5-global--BorderWidth--sm) solid var(--pf-v5-global--BorderColor--dark-100); - bottom: 0; - position: absolute; + .chr-l-stack__item-browse-all-services { + border-bottom: var(--pf-v5-global--BorderWidth--sm) solid var(--pf-v5-global--BorderColor--dark-100); } - .pf-v5-c-sidebar { --pf-v5-c-sidebar__panel--md--FlexBasis: 20rem; --pf-v5-c-sidebar__panel--BackgroundColor: var(--pf-v5-global--BackgroundColor--200); &__main { - height: 80vh; + height: 630px; } &__content { - height: 80vh; + height: 630px; overflow: auto; } &__panel { - height: 80vh; - position: relative; + height: 630px; box-shadow: inset -4px 0 4px -4px rgba(0, 0, 0, 0.16); - .chr-l-flex__item-tabs { - overflow: auto; - padding-bottom: var(--pf-v5-global--spacer--2xl); - } .pf-v5-c-tabs { --pf-v5-c-tabs__item--m-current__link--after--BorderColor: transparent; --pf-v5-c-tabs--m-vertical__link--PaddingTop: var(--pf-v5-global--spacer--sm); diff --git a/src/components/AllServicesDropdown/AllServicesMenu.tsx b/src/components/AllServicesDropdown/AllServicesMenu.tsx index 913a5771e..dd43c27fb 100644 --- a/src/components/AllServicesDropdown/AllServicesMenu.tsx +++ b/src/components/AllServicesDropdown/AllServicesMenu.tsx @@ -2,7 +2,7 @@ import React, { Fragment } from 'react'; import { Backdrop } from '@patternfly/react-core/dist/dynamic/components/Backdrop'; import { Button } from '@patternfly/react-core/dist/dynamic/components/Button'; import { Card, CardBody, CardHeader } from '@patternfly/react-core/dist/dynamic/components/Card'; -import { FlexItem } from '@patternfly/react-core/dist/dynamic/layouts/Flex'; +import { Stack, StackItem } from '@patternfly/react-core/dist/dynamic/layouts/Stack'; import { Icon } from '@patternfly/react-core/dist/dynamic/components/Icon'; import { Panel, PanelMain } from '@patternfly/react-core/dist/dynamic/components/Panel'; import { Sidebar, SidebarContent, SidebarPanel } from '@patternfly/react-core/dist/dynamic/components/Sidebar'; @@ -11,7 +11,6 @@ import { Text, TextContent, TextVariants } from '@patternfly/react-core/dist/dyn import { Title } from '@patternfly/react-core/dist/dynamic/components/Title'; import ChromeLink from '../ChromeLink'; -import BookOpenIcon from '@patternfly/react-icons/dist/dynamic/icons/book-open-icon'; import TimesIcon from '@patternfly/react-icons/dist/dynamic/icons/times-icon'; import type { AllServicesSection } from '../AllServices/allServicesLinks'; import FavoriteServicesGallery from '../FavoriteServices/ServicesGallery'; @@ -67,34 +66,34 @@ const AllServicesMenu = ({ setIsOpen, isOpen, menuRef, linkSections, favoritedSe - - - - - - - - - Browse all services - - - - - - - + + + + + + + + + + + + + + + diff --git a/src/components/AllServicesDropdown/AllServicesTabs.tsx b/src/components/AllServicesDropdown/AllServicesTabs.tsx index 1e036e236..21ba0a948 100644 --- a/src/components/AllServicesDropdown/AllServicesTabs.tsx +++ b/src/components/AllServicesDropdown/AllServicesTabs.tsx @@ -83,8 +83,8 @@ const AllServicesTabs = ({ eventKey={FAVORITE_TAB_ID} title={ - My favorite services - + Favorites + From 146eaa009483dadb896f794299ca37a5966ab38a Mon Sep 17 00:00:00 2001 From: ewinchel Date: Tue, 7 Nov 2023 13:30:43 -0500 Subject: [PATCH 30/38] lint fix --- cypress/e2e/release-gate/navigation.cy.ts | 2 +- src/components/AllServicesDropdown/AllServicesMenu.tsx | 9 ++------- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/cypress/e2e/release-gate/navigation.cy.ts b/cypress/e2e/release-gate/navigation.cy.ts index a7ea5d549..7abcabbd1 100644 --- a/cypress/e2e/release-gate/navigation.cy.ts +++ b/cypress/e2e/release-gate/navigation.cy.ts @@ -17,7 +17,7 @@ describe('Navigation', () => { cy.contains('.pf-v5-c-tabs__link', 'Favorites'); // click on all services - cy.get('.chr-l-flex__item-browse-all-services a').click(); + cy.get('.chr-l-stack__item-browse-all-services a').click(); // get users link cy.get('p:contains("Users")').click(); diff --git a/src/components/AllServicesDropdown/AllServicesMenu.tsx b/src/components/AllServicesDropdown/AllServicesMenu.tsx index dd43c27fb..11363cce9 100644 --- a/src/components/AllServicesDropdown/AllServicesMenu.tsx +++ b/src/components/AllServicesDropdown/AllServicesMenu.tsx @@ -3,7 +3,6 @@ import { Backdrop } from '@patternfly/react-core/dist/dynamic/components/Backdro import { Button } from '@patternfly/react-core/dist/dynamic/components/Button'; import { Card, CardBody, CardHeader } from '@patternfly/react-core/dist/dynamic/components/Card'; import { Stack, StackItem } from '@patternfly/react-core/dist/dynamic/layouts/Stack'; -import { Icon } from '@patternfly/react-core/dist/dynamic/components/Icon'; import { Panel, PanelMain } from '@patternfly/react-core/dist/dynamic/components/Panel'; import { Sidebar, SidebarContent, SidebarPanel } from '@patternfly/react-core/dist/dynamic/components/Sidebar'; import { TabContent } from '@patternfly/react-core/dist/dynamic/components/Tabs'; @@ -68,15 +67,11 @@ const AllServicesMenu = ({ setIsOpen, isOpen, menuRef, linkSections, favoritedSe - + - + From bc6057d62a8630a7f0120f76853b30d10142c55e Mon Sep 17 00:00:00 2001 From: Martin Marosi Date: Wed, 8 Nov 2023 09:29:45 +0100 Subject: [PATCH 31/38] Add pendo badge click event tracking. --- src/components/RootApp/ScalprumRoot.tsx | 3 ++ src/hooks/useTrackPendoUsage.ts | 62 +++++++++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 src/hooks/useTrackPendoUsage.ts diff --git a/src/components/RootApp/ScalprumRoot.tsx b/src/components/RootApp/ScalprumRoot.tsx index f5fb7aa80..532c25027 100644 --- a/src/components/RootApp/ScalprumRoot.tsx +++ b/src/components/RootApp/ScalprumRoot.tsx @@ -32,6 +32,7 @@ import { ITLess, isBeta } from '../../utils/common'; import InternalChromeContext from '../../utils/internalChromeContext'; import useChromeServiceEvents from '../../hooks/useChromeServiceEvents'; import { populateNotifications } from '../../redux/actions'; +import useTrackPendoUsage from '../../hooks/useTrackPendoUsage'; const ProductSelection = lazy(() => import('../Stratosphere/ProductSelection')); @@ -59,6 +60,8 @@ const ScalprumRoot = memo( // initialize WS event handling useChromeServiceEvents(); + // track pendo usage + useTrackPendoUsage(); async function getNotifications() { try { diff --git a/src/hooks/useTrackPendoUsage.ts b/src/hooks/useTrackPendoUsage.ts new file mode 100644 index 000000000..1c0c2bea2 --- /dev/null +++ b/src/hooks/useTrackPendoUsage.ts @@ -0,0 +1,62 @@ +import { useSelector } from 'react-redux'; +import { ReduxState } from '../redux/store'; +import { useCallback, useEffect, useRef } from 'react'; +import { useSegment } from '../analytics/useSegment'; + +const badgeQuery = 'div[id^="_pendo-badge_"]'; +const RETRY_ATTEMPS = 10; +const RETRY_INTERVAL = 2000; +const SEGMENT_EVENT_NAME = 'pendo-badge-clicked'; + +const useTrackPendoUsage = () => { + const activeModule = useSelector((state) => state.chrome.activeModule); + const mutableData = useRef({ activeModule }); + const { analytics } = useSegment(); + const setupEventTracking = useCallback(() => { + analytics?.track(SEGMENT_EVENT_NAME, { + application: mutableData.current.activeModule, + }); + }, []); + + useEffect(() => { + let interval: NodeJS.Timeout | undefined = undefined; + let pendoBadgeElement: HTMLDivElement | null = document.querySelector(badgeQuery); + try { + // this feature is not critical, if any of the following fails, we just ignore it + mutableData.current.activeModule = activeModule; + let attempt = 0; + if (!pendoBadgeElement) { + // pendo badge was not loaded yet + // try a few times to find it, then give up + interval = setInterval(() => { + if (attempt < RETRY_ATTEMPS) { + attempt += 1; + pendoBadgeElement = document.querySelector(badgeQuery); + if (pendoBadgeElement) { + clearInterval(interval); + pendoBadgeElement.addEventListener('click', setupEventTracking); + } + } else { + clearInterval(interval); + } + }, RETRY_INTERVAL); + } else { + pendoBadgeElement.addEventListener('click', setupEventTracking); + } + } catch (error) { + console.error('Failed to setup pendo badge event tracking', error); + } + return () => { + if (interval) { + clearInterval(interval); + } + + if (pendoBadgeElement) { + pendoBadgeElement.removeEventListener('click', setupEventTracking); + pendoBadgeElement = null; + } + }; + }, [activeModule]); +}; + +export default useTrackPendoUsage; From 1440c859c1389a6b8dda414abe387a51c00bccda Mon Sep 17 00:00:00 2001 From: Bryan Florkiewicz Date: Wed, 8 Nov 2023 13:40:01 -0500 Subject: [PATCH 32/38] Update itless stage SSO URL --- src/utils/common.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/common.ts b/src/utils/common.ts index 2de2ab8cc..8334a0d1b 100644 --- a/src/utils/common.ts +++ b/src/utils/common.ts @@ -34,7 +34,7 @@ export const DEFAULT_SSO_ROUTES = { }, frhStage: { url: ['console.stage.openshiftusgov.com'], - sso: 'https://sso.stage.openshiftusgov.com/auth', + sso: 'https://sso.stage.openshiftusgov.com', portal: 'https://console.stage.openshiftusgov.com', }, frh: { From 0add15ae09cb22e921baf0f1f625ada259c63b21 Mon Sep 17 00:00:00 2001 From: Martin Marosi Date: Thu, 16 Nov 2023 12:51:54 +0100 Subject: [PATCH 33/38] Disable pendo on stage landing page. --- src/components/RootApp/ScalprumRoot.tsx | 3 ++ src/hooks/useDisablePendoOnLanding.ts | 49 +++++++++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 src/hooks/useDisablePendoOnLanding.ts diff --git a/src/components/RootApp/ScalprumRoot.tsx b/src/components/RootApp/ScalprumRoot.tsx index 532c25027..931ba5789 100644 --- a/src/components/RootApp/ScalprumRoot.tsx +++ b/src/components/RootApp/ScalprumRoot.tsx @@ -33,6 +33,7 @@ import InternalChromeContext from '../../utils/internalChromeContext'; import useChromeServiceEvents from '../../hooks/useChromeServiceEvents'; import { populateNotifications } from '../../redux/actions'; import useTrackPendoUsage from '../../hooks/useTrackPendoUsage'; +import useDisablePendoOnLanding from '../../hooks/useDisablePendoOnLanding'; const ProductSelection = lazy(() => import('../Stratosphere/ProductSelection')); @@ -62,6 +63,8 @@ const ScalprumRoot = memo( useChromeServiceEvents(); // track pendo usage useTrackPendoUsage(); + // disable guides on landing page + useDisablePendoOnLanding(); async function getNotifications() { try { diff --git a/src/hooks/useDisablePendoOnLanding.ts b/src/hooks/useDisablePendoOnLanding.ts new file mode 100644 index 000000000..b3545a066 --- /dev/null +++ b/src/hooks/useDisablePendoOnLanding.ts @@ -0,0 +1,49 @@ +import { useEffect } from 'react'; +import { useSelector } from 'react-redux'; +import { ReduxState } from '../redux/store'; +import { isProd } from '../utils/common'; +import { isITLessEnv } from '../utils/consts'; + +// interval timing is short because we want to catch the bubble before ASAP so it does not cover the VA button +const RETRY_ATTEMPS = 500; +const RETRY_INTERVAL = 50; + +const useDisablePendoOnLanding = () => { + const activeModule = useSelector((state: ReduxState) => state.chrome.activeModule); + + const toggleGuides = () => { + if (window.pendo && activeModule === 'landing') { + window.pendo.stopGuides(); + } else { + window.pendo?.startGuides(); + } + }; + + useEffect(() => { + let attempt = 0; + let interval: NodeJS.Timeout | undefined = undefined; + if (window.pendo) { + toggleGuides(); + } else { + interval = setInterval(() => { + if (attempt < RETRY_ATTEMPS) { + attempt += 1; + if (window.pendo) { + clearInterval(interval); + toggleGuides(); + } + } else { + clearInterval(interval); + } + }, RETRY_INTERVAL); + } + + return () => { + if (interval && !isProd() && !isITLessEnv) { + clearInterval(interval); + } + }; + }, [activeModule]); +}; + +export default useDisablePendoOnLanding; From db40e14ced915a978e1f88b7b382a98978e514c2 Mon Sep 17 00:00:00 2001 From: Martin Marosi Date: Thu, 16 Nov 2023 16:04:57 +0100 Subject: [PATCH 34/38] use conditional accessor to pendo --- src/hooks/useDisablePendoOnLanding.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hooks/useDisablePendoOnLanding.ts b/src/hooks/useDisablePendoOnLanding.ts index b3545a066..38c29933e 100644 --- a/src/hooks/useDisablePendoOnLanding.ts +++ b/src/hooks/useDisablePendoOnLanding.ts @@ -13,9 +13,9 @@ const useDisablePendoOnLanding = () => { const toggleGuides = () => { if (window.pendo && activeModule === 'landing') { - window.pendo.stopGuides(); + window.pendo.stopGuides?.(); } else { - window.pendo?.startGuides(); + window.pendo?.startGuides?.(); } }; From eb3a47b665eee18f136ab66486f0ed9f831fdd1e Mon Sep 17 00:00:00 2001 From: Martin Marosi Date: Thu, 16 Nov 2023 16:32:10 +0100 Subject: [PATCH 35/38] Add aditional logic for pendo guides stopping. --- src/hooks/useDisablePendoOnLanding.ts | 34 +++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/src/hooks/useDisablePendoOnLanding.ts b/src/hooks/useDisablePendoOnLanding.ts index 38c29933e..6d23904f3 100644 --- a/src/hooks/useDisablePendoOnLanding.ts +++ b/src/hooks/useDisablePendoOnLanding.ts @@ -8,15 +8,39 @@ import { isITLessEnv } from '../utils/consts'; const RETRY_ATTEMPS = 500; const RETRY_INTERVAL = 50; +function retry(fn: () => void, retriesLeft = 10, interval = 100) { + try { + return fn(); + } catch (error) { + console.error(error); + const newRetry = retriesLeft - 1; + if (newRetry > 0) { + setTimeout(() => { + retry(fn, newRetry, interval); + }, interval); + } + } +} + const useDisablePendoOnLanding = () => { const activeModule = useSelector((state: ReduxState) => state.chrome.activeModule); const toggleGuides = () => { - if (window.pendo && activeModule === 'landing') { - window.pendo.stopGuides?.(); - } else { - window.pendo?.startGuides?.(); - } + // push the call to the end of the event loop to make sure the pendo script is loaded and initialized + setTimeout(() => { + if (window.pendo && activeModule === 'landing') { + // pendo functions might not be ready for what ever reason, we will have to retry a few times to give it a shot + retry(() => { + window.pendo?.setGuidesDisabled?.(true); + window.pendo?.stopGuides?.(); + }); + } else if (window.pendo) { + retry(() => { + window.pendo?.setGuidesDisabled?.(false); + window.pendo?.startGuides?.(); + }); + } + }); }; useEffect(() => { From 8b07c1f331f4e5ed5f235bb6660951883b0118e8 Mon Sep 17 00:00:00 2001 From: Martin Marosi Date: Thu, 16 Nov 2023 16:51:36 +0100 Subject: [PATCH 36/38] Remove safe pendo handlers. --- src/hooks/useDisablePendoOnLanding.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/hooks/useDisablePendoOnLanding.ts b/src/hooks/useDisablePendoOnLanding.ts index 6d23904f3..63c49f862 100644 --- a/src/hooks/useDisablePendoOnLanding.ts +++ b/src/hooks/useDisablePendoOnLanding.ts @@ -31,13 +31,13 @@ const useDisablePendoOnLanding = () => { if (window.pendo && activeModule === 'landing') { // pendo functions might not be ready for what ever reason, we will have to retry a few times to give it a shot retry(() => { - window.pendo?.setGuidesDisabled?.(true); - window.pendo?.stopGuides?.(); + window.pendo?.setGuidesDisabled(true); + window.pendo?.stopGuides(); }); } else if (window.pendo) { retry(() => { - window.pendo?.setGuidesDisabled?.(false); - window.pendo?.startGuides?.(); + window.pendo?.setGuidesDisabled(false); + window.pendo?.startGuides(); }); } }); From 8fd5a7fe1a433f21a75256cdb39f177e5d2620d0 Mon Sep 17 00:00:00 2001 From: Filip Hlavac Date: Thu, 16 Nov 2023 17:46:44 +0100 Subject: [PATCH 37/38] Move console.err after pendo retries are exhausted --- src/hooks/useDisablePendoOnLanding.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/hooks/useDisablePendoOnLanding.ts b/src/hooks/useDisablePendoOnLanding.ts index 63c49f862..d47a00e7b 100644 --- a/src/hooks/useDisablePendoOnLanding.ts +++ b/src/hooks/useDisablePendoOnLanding.ts @@ -12,12 +12,13 @@ function retry(fn: () => void, retriesLeft = 10, interval = 100) { try { return fn(); } catch (error) { - console.error(error); const newRetry = retriesLeft - 1; if (newRetry > 0) { setTimeout(() => { retry(fn, newRetry, interval); }, interval); + } else { + console.error('Pendo retries exhausted: ', error); } } } From 852114641cc9cf6d7ae2ecc37248f89234af3727 Mon Sep 17 00:00:00 2001 From: Bryan Florkiewicz Date: Fri, 17 Nov 2023 13:31:05 -0500 Subject: [PATCH 38/38] Call initSuccess for cognito envs (causes cs-jwt to be set) --- src/jwt/jwt.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/jwt/jwt.ts b/src/jwt/jwt.ts index 2501a2b43..4417b660f 100644 --- a/src/jwt/jwt.ts +++ b/src/jwt/jwt.ts @@ -193,6 +193,7 @@ export const init = (options: JWTInitOptions, configSsoUrl?: string) => { } return getTokenWithAuthorizationCode().then((res) => { priv.setToken(res); + initSuccess(); token = res; return token; });