Skip to content
7 changes: 5 additions & 2 deletions src/app/content/hooks/receiveContent.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -41,6 +42,8 @@ const hookBody: ActionHookBody<typeof receivePage | typeof locationChange> = (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(
Expand All @@ -57,7 +60,7 @@ const hookBody: ActionHookBody<typeof receivePage | typeof locationChange> = (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);
Expand Down
84 changes: 84 additions & 0 deletions src/app/content/utils/seoUtils.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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');
});
});
18 changes: 16 additions & 2 deletions src/app/content/utils/seoUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,14 +111,28 @@ export const getPageDescription = (services: Pick<AppServices, 'archiveLoader'>,
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}`;
}
Expand Down
Loading