diff --git a/packages/ui-react/src/components/ErrorPage/ErrorPage.tsx b/packages/ui-react/src/components/ErrorPage/ErrorPage.tsx index 94911cbb3..d0971b3f4 100644 --- a/packages/ui-react/src/components/ErrorPage/ErrorPage.tsx +++ b/packages/ui-react/src/components/ErrorPage/ErrorPage.tsx @@ -38,7 +38,9 @@ export const ErrorPageWithoutTranslation = ({ title, children, message, learnMor
{alt} -

{title || 'An error occurred'}

+

+ {title || 'An error occurred'} +

{message || 'Try refreshing this page or come back later.'}

{children} diff --git a/packages/ui-react/src/components/ErrorPage/__snapshots__/ErrorPage.test.tsx.snap b/packages/ui-react/src/components/ErrorPage/__snapshots__/ErrorPage.test.tsx.snap index 7dea1eabe..018946364 100644 --- a/packages/ui-react/src/components/ErrorPage/__snapshots__/ErrorPage.test.tsx.snap +++ b/packages/ui-react/src/components/ErrorPage/__snapshots__/ErrorPage.test.tsx.snap @@ -14,6 +14,7 @@ exports[` > renders and matches snapshot 1`] = ` src="/images/logo.png" />

This is the title diff --git a/packages/ui-react/src/pages/Search/Search.tsx b/packages/ui-react/src/pages/Search/Search.tsx index c43896d41..30004fac1 100644 --- a/packages/ui-react/src/pages/Search/Search.tsx +++ b/packages/ui-react/src/pages/Search/Search.tsx @@ -1,4 +1,4 @@ -import React, { useEffect } from 'react'; +import React, { useEffect, useMemo } from 'react'; import { useParams } from 'react-router'; import { Helmet } from 'react-helmet'; import { useTranslation } from 'react-i18next'; @@ -34,7 +34,19 @@ const Search = () => { // User const { user, subscription } = useAccountStore(({ user, subscription }) => ({ user, subscription }), shallow); - const getURL = (playlistItem: PlaylistItem) => mediaURL({ media: playlistItem, playlistId: features?.searchPlaylist }); + const getURL = (playlistItem: PlaylistItem) => + mediaURL({ + media: playlistItem, + playlistId: features?.searchPlaylist, + }); + + const title = useMemo(() => { + if (isFetching) return t('heading'); + if (!query) return t('start_typing'); + if (!playlist?.playlist.length) return t('no_results_heading', { query }); + + return t('title', { count: playlist.playlist.length, query }); + }, [isFetching, playlist?.playlist.length, query, t]); // Update the search bar query to match the route param on mount useEffect(() => { @@ -66,19 +78,26 @@ const Search = () => { } if (!query) { - return ; + return ; } if (!playlist?.playlist.length) { return ( - -

{t('suggestions')}

-
    -
  • {t('tip_one')}
  • -
  • {t('tip_two')}
  • -
  • {t('tip_three')}
  • -
-
+ <> + + + {title} - {siteName} + + + +

{t('suggestions')}

+
    +
  • {t('tip_one')}
  • +
  • {t('tip_two')}
  • +
  • {t('tip_three')}
  • +
+
+ ); } @@ -86,11 +105,13 @@ const Search = () => {
- {t('title', { count: playlist.playlist.length, query })} - {siteName} + {title} - {siteName}
-

{t('heading')}

+

+ {title} +

> renders and matches snapshot error dialog 1`] = ` src="/images/logo.png" />

app_config_not_found diff --git a/platforms/web/test-e2e/tests/search_test.ts b/platforms/web/test-e2e/tests/search_test.ts index 50c1439df..1f45d1c62 100644 --- a/platforms/web/test-e2e/tests/search_test.ts +++ b/platforms/web/test-e2e/tests/search_test.ts @@ -54,33 +54,36 @@ Scenario('Closing search return to original page (@mobile-only)', async ({ I }) }); Scenario('I can type a search phrase in the search bar', async ({ I }) => { + const searchTerm = 'Caminandes'; await openSearch(I); - I.fillField(searchBarLocator, 'Caminandes'); + I.fillField(searchBarLocator, searchTerm); I.seeElement(clearSearchLocator); - checkSearchResults(I, ['Caminandes 1', 'Caminandes 2', 'Caminandes 3']); + checkSearchResults(I, searchTerm, 3, ['Caminandes 1', 'Caminandes 2', 'Caminandes 3']); I.click(clearSearchLocator); assert.strictEqual('', await I.grabValueFrom(searchBarLocator)); - checkSearchResults(I, []); + I.dontSee('Search results'); I.see(emptySearchPrompt); }); Scenario('I can search by partial match', async ({ I }) => { + const searchTerm = 'ani'; await openSearch(I); - I.fillField(searchBarLocator, 'ani'); + I.fillField(searchBarLocator, searchTerm); I.seeElement(clearSearchLocator); - checkSearchResults(I, ['Minecraft Animation Workshop', 'Animating the Throw', 'Primitive Animals']); + checkSearchResults(I, searchTerm, 5, ['Minecraft Animation Workshop', 'Animating the Throw', 'Primitive Animals']); }); Scenario('I get empty search results when no videos match', async ({ I }) => { + const searchTerm = 'Axdfsdfgfgfd'; await openSearch(I); - I.fillField(searchBarLocator, 'Axdfsdfgfgfd'); + I.fillField(searchBarLocator, searchTerm); I.seeElement(clearSearchLocator); - checkSearchResults(I, []); + checkSearchResults(I, searchTerm, 0, []); I.see('No results found for "Axdfsdfgfgfd"'); I.see('Suggestions:'); @@ -124,17 +127,17 @@ Scenario('I can clear the search phrase manually', async ({ I }) => { I.dontSee('Suggestions:'); }); -function checkSearchResults(I: CodeceptJS.I, expectedResults: string[]) { +function checkSearchResults(I: CodeceptJS.I, searchTerm: string, expectedResults: number, searchMatches: string[]) { I.dontSee('Blender Channel'); I.dontSee('All Films'); - if (expectedResults.length > 0) { - I.see('Search results'); + if (expectedResults > 0) { + I.see(`${expectedResults} results for "${searchTerm}"`, 'h2'); I.dontSee(emptySearchPrompt); I.dontSee('No results found'); - expectedResults.forEach((result) => I.see(result)); + searchMatches.forEach((result) => I.see(result)); } else { - I.dontSee('Search results'); + I.see(`No results found for "${searchTerm}"`, 'h1'); I.dontSeeElement('div[class*="cell"]'); I.dontSeeElement('div[class*="card"]'); I.dontSeeElement('div[class*="poster"]');