diff --git a/src/app/pages/details/common/get-this-title-files/give-before-pdf/use-give-links.ts b/src/app/pages/details/common/get-this-title-files/give-before-pdf/use-give-links.ts
index 38efe4bb2..b5106b94b 100644
--- a/src/app/pages/details/common/get-this-title-files/give-before-pdf/use-give-links.ts
+++ b/src/app/pages/details/common/get-this-title-files/give-before-pdf/use-give-links.ts
@@ -9,13 +9,15 @@ export default function useGiveLinks() {
}
function getLinks(search: string) {
- if (search.includes('Instructor')) {
+ const keys = Array.from(new URLSearchParams(search).keys()).map((k) => k.toLowerCase());
+
+ if (keys.includes('instructor resources')) {
return [
'https://riceconnect.rice.edu/donation/support-openstax-instructor-resources',
'https://riceconnect.rice.edu/donation/support-openstax-instructor-resources-b'
];
}
- if (search.includes('Student')) {
+ if (keys.includes('student resources')) {
return [
'https://riceconnect.rice.edu/donation/support-openstax-student-resources',
'https://riceconnect.rice.edu/donation/support-openstax-student-resources-b'
diff --git a/src/app/pages/details/common/resource-box/left-content.tsx b/src/app/pages/details/common/resource-box/left-content.tsx
index 88fc76046..0cf6608c5 100644
--- a/src/app/pages/details/common/resource-box/left-content.tsx
+++ b/src/app/pages/details/common/resource-box/left-content.tsx
@@ -82,11 +82,12 @@ const iconLookup: {[key: string]: IconDefinition} = {
// string says nothing about the resource there.
function useVariant(): VariantValue {
const {search} = useLocation();
+ const keys = Array.from(new URLSearchParams(search).keys()).map((k) => k.toLowerCase());
- if (search.includes('Instructor')) {
+ if (keys.includes('instructor resources')) {
return 'Instructor resource';
}
- if (search.includes('Student')) {
+ if (keys.includes('student resources')) {
return 'Student resource';
}
return '? resource';
diff --git a/src/app/pages/details/common/tab-utils.ts b/src/app/pages/details/common/tab-utils.ts
index 97e6c02be..e1af9058d 100644
--- a/src/app/pages/details/common/tab-utils.ts
+++ b/src/app/pages/details/common/tab-utils.ts
@@ -1,17 +1,23 @@
export function findSelectedTab(labels: string[]) {
const possibleTabs = Array.from(new window.URLSearchParams(window.location.search).keys());
- return labels.find((label) => possibleTabs.includes(label)) || labels[0];
+ return (
+ labels.find((label) =>
+ possibleTabs.some((tab) => tab.toLowerCase() === label.toLowerCase())
+ ) || labels[0]
+ );
}
export function replaceSearchTerm(labels: string[], newValue: string) {
+ const lowerLabels = labels.map((label) => label.toLowerCase());
const possibleTabs = Array.from(new window.URLSearchParams(window.location.search).keys());
- const index = possibleTabs.findIndex((t) => labels.includes(t));
+ const firstIndex = possibleTabs.findIndex((tab) => lowerLabels.includes(tab.toLowerCase()));
+ const filtered = possibleTabs.filter((tab) => !lowerLabels.includes(tab.toLowerCase()));
- if (index < 0) {
- possibleTabs.unshift(encodeURIComponent(newValue));
+ if (firstIndex < 0) {
+ filtered.unshift(newValue);
} else {
- possibleTabs[index] = encodeURIComponent(newValue);
+ filtered.splice(firstIndex, 0, newValue);
}
- return `?${possibleTabs.join('&')}`;
+ return `?${filtered.map((tab) => encodeURIComponent(tab)).join('&')}`;
}
diff --git a/test/src/pages/details/common/tab-utils.test.ts b/test/src/pages/details/common/tab-utils.test.ts
index 12d9c5d1e..58cac57d0 100644
--- a/test/src/pages/details/common/tab-utils.test.ts
+++ b/test/src/pages/details/common/tab-utils.test.ts
@@ -1,17 +1,47 @@
-import { replaceSearchTerm } from '~/pages/details/common/tab-utils';
+import {findSelectedTab, replaceSearchTerm} from '~/pages/details/common/tab-utils';
+
+function setSearch(search: string) {
+ Reflect.defineProperty(window, 'location', {
+ writable: true,
+ value: { search }
+ });
+}
describe('tab-utils', () => {
const labels = ['one', 'two'];
const newValue = 'three';
it('adds new param if no tabs in search params', () => {
+ setSearch('');
expect(replaceSearchTerm(labels, newValue)).toBe('?three');
});
it('replaces param if tab is in search params', () => {
- Reflect.defineProperty(window, 'location', {
- writable: true,
- value: { search: '?fluff&two' }
- });
- expect(replaceSearchTerm(labels, newValue)).toBe('?fluff&three');
+ setSearch('?fluff&two');
+ expect(replaceSearchTerm(labels, newValue)).toBe('?fluff&three');
+ });
+ it('replaces param when the key case does not match a label', () => {
+ setSearch('?TWO');
+ expect(replaceSearchTerm(labels, newValue)).toBe('?three');
+ });
+
+ describe('findSelectedTab', () => {
+ const tabLabels = ['Book details', 'Instructor resources', 'Student resources'];
+
+ it('selects the matching tab', () => {
+ setSearch('?Instructor%20resources');
+ expect(findSelectedTab(tabLabels)).toBe('Instructor resources');
+ });
+ it('matches a lower-case deep link', () => {
+ setSearch('?instructor%20resources');
+ expect(findSelectedTab(tabLabels)).toBe('Instructor resources');
+ });
+ it('falls back to the first label when no key matches', () => {
+ setSearch('?nothing');
+ expect(findSelectedTab(tabLabels)).toBe('Book details');
+ });
+ it('replaces a lower-case key instead of keeping it', () => {
+ setSearch('?instructor%20resources');
+ expect(replaceSearchTerm(tabLabels, 'Student resources')).toBe('?Student%20resources');
+ });
});
});
diff --git a/test/src/pages/details/left-content.test.tsx b/test/src/pages/details/left-content.test.tsx
index 9791b8ab1..0d8dc5d9d 100644
--- a/test/src/pages/details/left-content.test.tsx
+++ b/test/src/pages/details/left-content.test.tsx
@@ -80,7 +80,7 @@ describe('left-content', () => {
});
const model = {link, ...baseModel, ...{iconType: 'unlock'}};
- render();
+ render();
const foundLink = screen.getByRole('link');
expect(foundLink.textContent).toBe('button-label');
@@ -93,14 +93,16 @@ describe('left-content', () => {
});
const model = {link, ...baseModel, iconType: 'download'};
- render();
+ render();
const foundLink = screen.getByRole('link');
expect(foundLink.textContent).toBe('button-label');
await user.click(foundLink);
- await screen.findByText('Give today');
+ const giveToday = await screen.findByText('Give today');
const downloadLink = await screen.findByText('Go to your resource');
+ expect(giveToday.closest('a')?.getAttribute('href')).toContain('support-openstax-instructor-resources');
+ expect(document.querySelector('[data-nudge-placement="Instructor resource"]')).toBeTruthy();
await user.click(downloadLink);
});
it('tracks a download for a signed-in non-instructor', async () => {
@@ -113,14 +115,16 @@ describe('left-content', () => {
});
const model = {link, ...baseModel, iconType: 'download'};
- render();
+ render();
const foundLink = screen.getByRole('link');
expect(foundLink.textContent).toBe('button-label');
await user.click(foundLink);
- await screen.findByText('Give today');
+ const giveToday = await screen.findByText('Give today');
const downloadLink = await screen.findByText('Go to your resource');
+ expect(giveToday.closest('a')?.getAttribute('href')).toContain('support-openstax-student-resources');
+ expect(document.querySelector('[data-nudge-placement="Student resource"]')).toBeTruthy();
await user.click(downloadLink);
expect(trackLink).toHaveBeenCalledWith(expect.anything(), '1');
@@ -159,9 +163,11 @@ describe('left-content', () => {
expect(foundLink.textContent).toBe('button-label');
await user.click(foundLink);
- await screen.findByText('Give today');
+ const giveToday = await screen.findByText('Give today');
const downloadLink = await screen.findByText('Go to your resource');
+ expect(giveToday.closest('a')?.getAttribute('href')).toContain('support-openstax-subject');
+ expect(document.querySelector('[data-nudge-placement="? resource"]')).toBeTruthy();
await user.click(downloadLink);
});
it('handles unknown icon and unknown search', async () => {