diff --git a/src/app/content/hooks/receiveContent.ts b/src/app/content/hooks/receiveContent.ts index e8f44ff04a..e5dbdd61e8 100644 --- a/src/app/content/hooks/receiveContent.ts +++ b/src/app/content/hooks/receiveContent.ts @@ -1,9 +1,10 @@ +import queryString from 'query-string'; import { setHead } from '../../head/actions'; import { initialState as headInitialState } from '../../head/reducer'; import { Link } from '../../head/types'; import createIntl from '../../messages/createIntl'; import { locationChange } from '../../navigation/actions'; -import { pathname } from '../../navigation/selectors'; +import { pathname, query } from '../../navigation/selectors'; import theme from '../../theme'; import { ActionHookBody } from '../../types'; import { receivePage } from '../actions'; @@ -41,6 +42,8 @@ const hookBody: ActionHookBody = (se const loadingBook = select.loadingBook(state); const loadingPage = select.loadingPage(state); const currentPath = pathname(state); + const queryParams = queryString.stringify(query(state)); + const queryParamsWithPrefix = queryParams ? `?${queryParams}` : ''; if (!page || !book) { dispatch( @@ -57,7 +60,7 @@ const hookBody: ActionHookBody = (se const locale = book.language; const intl = await createIntl(locale); - const title = createTitle(page, book, intl); + const title = createTitle(page, book, intl, queryParamsWithPrefix); const description = getPageDescription(services, intl, book, page); const canonical = await getCanonicalUrlParams(archiveLoader, osWebLoader, book, page.id); const canonicalUrl = canonical && contentRoute.getUrl(canonical); diff --git a/src/app/content/utils/seoUtils.spec.ts b/src/app/content/utils/seoUtils.spec.ts index 0bbbaf8c3d..cbda27769e 100644 --- a/src/app/content/utils/seoUtils.spec.ts +++ b/src/app/content/utils/seoUtils.spec.ts @@ -144,3 +144,87 @@ describe('createTitle', () => { expect(title).toEqual('3 page1 - book | OpenStax'); }); }); +describe('createTitle (modal param)', () => { + const intl = createIntl(); + + it('returns modal title for MH modal param', () => { + const page = makeArchiveSection('page1'); + const book = { + title: 'book', + tree: makeArchiveTree('book', [page]), + }; + // params string with modal=MH + const params = 'modal=MH'; + const title = createTitle(page as any as Page, book as any as Book, intl, params); + expect(title).toEqual('My Highlights and Notes | OpenStax'); + }); + + it('returns modal title for KS modal param', () => { + const page = makeArchiveSection('page1'); + const book = { + title: 'book', + tree: makeArchiveTree('book', [page]), + }; + // params string with modal=KS + const params = 'modal=KS'; + const title = createTitle(page as any as Page, book as any as Book, intl, params); + expect(title).toEqual('REX Keyboard Shortcuts | OpenStax'); + }); + + it('returns modal title for PQ modal param', () => { + const page = makeArchiveSection('page1'); + const book = { + title: 'book', + tree: makeArchiveTree('book', [page]), + }; + // params string with modal=PQ + const params = 'modal=PQ'; + const title = createTitle(page as any as Page, book as any as Book, intl, params); + expect(title).toEqual('REX Practice Questions | OpenStax'); + }); + + it('returns modal title for SG modal param', () => { + const page = makeArchiveSection('page1'); + const book = { + title: 'book', + tree: makeArchiveTree('book', [page]), + }; + // params string with modal=SG + const params = 'modal=SG'; + const title = createTitle(page as any as Page, book as any as Book, intl, params); + expect(title).toEqual('REX Study Guides | OpenStax'); + }); + + it('returns normal title if modal param is not present', () => { + const page = makeArchiveSection('page1'); + const book = { + title: 'book', + tree: makeArchiveTree('book', [page]), + }; + const params = ''; + const title = createTitle(page as any as Page, book as any as Book, intl, params); + expect(title).toEqual('page1 - book | OpenStax'); + }); + + it('returns normal title if modal param is unknown', () => { + const page = makeArchiveSection('page1'); + const book = { + title: 'book', + tree: makeArchiveTree('book', [page]), + }; + const params = 'modal=UNKNOWN'; + const title = createTitle(page as any as Page, book as any as Book, intl, params); + expect(title).toEqual('page1 - book | OpenStax'); + }); + + it('returns modal title if modal param is present among other params', () => { + const page = makeArchiveSection('page1'); + const book = { + title: 'book', + tree: makeArchiveTree('book', [page]), + }; + const params = 'foo=bar&modal=MH&baz=qux'; + const title = createTitle(page as any as Page, book as any as Book, intl, params); + expect(title).toEqual('My Highlights and Notes | OpenStax'); + }); +}); diff --git a/src/app/content/utils/seoUtils.ts b/src/app/content/utils/seoUtils.ts index d5be3391cb..4f3cbe595b 100644 --- a/src/app/content/utils/seoUtils.ts +++ b/src/app/content/utils/seoUtils.ts @@ -111,14 +111,28 @@ export const getPageDescription = (services: Pick, return pageDescription || intl.formatMessage({id: 'i18n:metadata:description'}); }; -export const createTitle = (page: Page, book: Book, intl: IntlShape): string => { +const modalTitles = { + 'MH': 'My Highlights and Notes', + 'KS': 'REX Keyboard Shortcuts', + 'PQ': 'REX Practice Questions', + 'SG': 'REX Study Guides', +}; + +export const createTitle = (page: Page, book: Book, intl: IntlShape, params?: string): string => { const node = assertDefined( findArchiveTreeNodeById(book.tree, page.id), `couldn't find node for a page id: ${page.id}` ); const [nodeNumber, nodeTitle] = splitTitleParts(node.title); const title = `${nodeTitle} - ${book.title} | OpenStax`; - + const searchParams = params ? new URLSearchParams(params) : undefined; + const modalParam = searchParams?.get('modal'); + const modalTitle = modalParam && modalParam in modalTitles + ? modalTitles[modalParam as keyof typeof modalTitles] + : null; + if (modalTitle) { + return `${modalTitle} | OpenStax`; + } if (nodeNumber) { return `${nodeNumber} ${title}`; }