From 25533ecb21f9a2f1ee26d79aec59fc83908e6ba1 Mon Sep 17 00:00:00 2001 From: Dante Soares Date: Fri, 10 May 2024 13:46:37 -0500 Subject: [PATCH 01/26] DISCO-100 Added lighthouse automated test and prerenderspec (#2188) * Added lighthouse prerenderspec * Added missing lighthouse target percentages * Fixed prerenderspec lint * Allow lighthouse prerenderspec to run against absolute urls and targets set from the env * Added script to run lighthouse on top pages * Save automated lighthouse reports to disk and compare later * Download/save lighthouse reports * Use production bucket and fix lint * Moved automated lighthouse to Concourse * Fixed lint * Use CI image for lighthouse * Remove input mapping and instead use a different name for the input and output * Renamed previous report to most recent report and removed unused code * Use the correct image for deno * Added .ts extension for import from deno * Use the entry script instead of deno * Removed more deno * Added network timeout for yarn install * Ensure puppeteer is setup before loading browserutils * Launch Chrome with --no-sandbox * Try to set the global browser variable properly * Attempt to circumvent puppeteer check * Load url function's PORT from src/config instead of jest-puppeteer config * Attempt to use process.exit() to make the script exit successfully * Use correct dir for concourse inputs/outputs * Remove - from the beginning of filenames --------- Co-authored-by: staxly[bot] <35789409+staxly[bot]@users.noreply.github.com> --- concourse/book-scan/script.bash | 2 +- concourse/create-test-plan-configs/task.yml | 2 +- concourse/lighthouse/script.bash | 12 ++++++ concourse/lighthouse/task.yml | 26 +++++++++++++ package.json | 2 +- script/lighthouse.ts | 42 +++++++++++++++++++++ src/app/content/content.browserspec.ts | 7 +++- src/index.browserspec.ts | 7 +++- src/index.prerenderspec.ts | 1 - src/lighthouse.prerenderspec.ts | 23 +++++++++++ src/test/browserutils.ts | 20 +++------- src/test/lighthouse.ts | 32 ++++++++++++++++ src/test/url.ts | 5 +++ 13 files changed, 160 insertions(+), 21 deletions(-) create mode 100755 concourse/lighthouse/script.bash create mode 100644 concourse/lighthouse/task.yml create mode 100755 script/lighthouse.ts create mode 100644 src/lighthouse.prerenderspec.ts create mode 100644 src/test/lighthouse.ts create mode 100644 src/test/url.ts diff --git a/concourse/book-scan/script.bash b/concourse/book-scan/script.bash index 4ebd30710a..3fff4a8f6e 100755 --- a/concourse/book-scan/script.bash +++ b/concourse/book-scan/script.bash @@ -6,6 +6,6 @@ cloudfront_environment=$(< cloudfront-environment/version.txt) cd rex-web -yarn +yarn install --network-timeout 60000 node script/entry.js domVisitor errorsExist --rootUrl="https://$cloudfront_environment" --queryString="validateLinks" diff --git a/concourse/create-test-plan-configs/task.yml b/concourse/create-test-plan-configs/task.yml index 41e2419d3d..861594db39 100644 --- a/concourse/create-test-plan-configs/task.yml +++ b/concourse/create-test-plan-configs/task.yml @@ -30,5 +30,5 @@ run: - | DEST=$(pwd)/test-plans cd rex-web/concourse/create-test-plan-configs - yarn install + yarn install --network-timeout 60000 ./script.js $DEST diff --git a/concourse/lighthouse/script.bash b/concourse/lighthouse/script.bash new file mode 100755 index 0000000000..bbe032aee1 --- /dev/null +++ b/concourse/lighthouse/script.bash @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +set -euxo pipefail + +base_dir=$(pwd) + +cd rex-web + +yarn install --network-timeout 60000 + +node script/entry.js lighthouse --pages="$LIGHTHOUSE_PAGES" \ + --mostRecentReportDir="$base_dir/$LIGHTHOUSE_MOST_RECENT_REPORT_DIR" \ + --reportDir="$base_dir/$LIGHTHOUSE_REPORT_DIR" diff --git a/concourse/lighthouse/task.yml b/concourse/lighthouse/task.yml new file mode 100644 index 0000000000..bd11e11979 --- /dev/null +++ b/concourse/lighthouse/task.yml @@ -0,0 +1,26 @@ +--- +platform: linux + +image_resource: + type: docker-image + source: + password: ((dockerhub-password)) + username: ((dockerhub-username)) + repository: openstax/rex-web + tag: CI-2023-08-10 + +inputs: +- name: rex-web +- name: most-recent-lighthouse-report +outputs: +- name: lighthouse-report + +params: + AWS_ACCESS_KEY_ID: ((prod-aws-access-key-id)) + AWS_SECRET_ACCESS_KEY: ((prod-aws-secret-access-key)) + LIGHTHOUSE_PAGES: ((lighthouse-pages)) + LIGHTHOUSE_MOST_RECENT_REPORT_DIR: most-recent-lighthouse-report + LIGHTHOUSE_REPORT_DIR: lighthouse-report + +run: + path: rex-web/concourse/lighthouse/script.bash diff --git a/package.json b/package.json index 853baf33e9..020c3dd526 100644 --- a/package.json +++ b/package.json @@ -88,7 +88,7 @@ "test:prerender:specs": "REACT_APP_ENV=test SERVER_MODE=built jest --testPathPattern=\"(\\.|/)prerenderspec\\.tsx?\" --config jest-puppeteer.config.json", "test:prerender:browser": "REACT_APP_ENV=test SERVER_MODE=built jest --testPathPattern=\"(\\.|/)browserspec\\.tsx?\" --config jest-puppeteer.config.json -i", "test:prerender:screenshots": "REACT_APP_ENV=test SERVER_MODE=built jest --testPathPattern=\"(\\.|/)screenshotspec\\.tsx?\" --config jest-puppeteer.config.json", - "test:lighthouse-manual": "REACT_APP_ENV=test lighthouse --view --config-path=./src/test/audits/index.js", + "test:lighthouse:manual": "REACT_APP_ENV=test lighthouse --view --config-path=./src/test/audits/index.js", "analyze:bundle": "craco build && source-map-explorer 'build/static/js/*.js' -m", "analyze:dom": "node ./script/entry.js domVisitor", "heroku-postbuild": "npm run-script build:clean" diff --git a/script/lighthouse.ts b/script/lighthouse.ts new file mode 100755 index 0000000000..386c863c06 --- /dev/null +++ b/script/lighthouse.ts @@ -0,0 +1,42 @@ +import { readFile, writeFile } from 'fs'; +import puppeteer from 'puppeteer'; +import asyncPool from 'tiny-async-pool'; +import argv from 'yargs'; +import { checkLighthouse, ScoreTargets } from '../src/test/lighthouse'; + +const { + pages, mostRecentReportDir, reportDir, +} = argv.string('pages').string('mostRecentReportDir').string('reportDir').argv; + +async function run() { + if (!pages) { throw new Error('You must specify some --pages to test'); } + + const browser = await puppeteer.launch({ args: ['--no-sandbox'] }); + + const pageArray: string[] = JSON.parse(pages); + + await asyncPool(1, pageArray, async(pageUrl) => { + const filename = `${pageUrl.replace(/[^a-z0-9]+/gi, '-').replace(/^-+/i, '')}.json`; + const targets = mostRecentReportDir ? await new Promise( + (resolve) => readFile( + `${mostRecentReportDir}/${filename}`, + { encoding: 'utf8' }, + (err, data) => err ? resolve(undefined) : resolve(JSON.parse(data)) + ) + ) : undefined; + + const result = await checkLighthouse(browser, pageUrl, targets); + + if (reportDir) { + await new Promise((resolve) => writeFile( + `${reportDir}/${filename}`, + JSON.stringify(result), () => resolve() + )); + } + }); +} + +run().then(() => process.exit(), (err) => { + console.error(err); // tslint:disable-line:no-console + process.exit(1); +}); diff --git a/src/app/content/content.browserspec.ts b/src/app/content/content.browserspec.ts index 37f09a601e..3f14a20d8c 100644 --- a/src/app/content/content.browserspec.ts +++ b/src/app/content/content.browserspec.ts @@ -40,7 +40,12 @@ describe('content', () => { }); it('a11y lighthouse check', async() => { - await checkLighthouse(browser, TEST_LONG_PAGE_URL); + await checkLighthouse(browser, TEST_LONG_PAGE_URL, { + accessibility: 1, + 'best-practices': 0.88, + customAccessibility: 1, + seo: 1, + }); }); it(`when clicking a toc link: diff --git a/src/index.browserspec.ts b/src/index.browserspec.ts index 66a1694b54..af2db7dd6e 100644 --- a/src/index.browserspec.ts +++ b/src/index.browserspec.ts @@ -46,5 +46,10 @@ describe('Browser sanity tests', () => { }); it('a11y lighthouse check', async() => { - await checkLighthouse(browser, TEST_PAGE_URL); + await checkLighthouse(browser, TEST_PAGE_URL, { + accessibility: 1, + 'best-practices': 0.79, + customAccessibility: 1, + seo: 0.69, + }); }); diff --git a/src/index.prerenderspec.ts b/src/index.prerenderspec.ts index d09ca58f05..0b9564af69 100644 --- a/src/index.prerenderspec.ts +++ b/src/index.prerenderspec.ts @@ -3,7 +3,6 @@ import fetch from 'node-fetch'; import { url } from './test/browserutils'; describe('Prerender sanity tests', () => { - it('has a release manifest', async() => { const release = await fetch(url('rex/release.json')) .then((response) => response.text()); diff --git a/src/lighthouse.prerenderspec.ts b/src/lighthouse.prerenderspec.ts new file mode 100644 index 0000000000..7d849398c2 --- /dev/null +++ b/src/lighthouse.prerenderspec.ts @@ -0,0 +1,23 @@ +/** @jest-environment puppeteer */ +import asyncPool from 'tiny-async-pool'; +import { checkLighthouse } from './test/browserutils'; + +const TEST_PAGE_WITHOUT_MATH = '/books/book-slug-1/pages/2-test-page-3'; +const TEST_PAGE_WITH_LINKS_NAME = '1-introduction-to-science-and-the-realm-of-physics-physical-quantities-and-units'; +const TEST_PAGE_WITH_LINKS = '/books/book-slug-1/pages/' + TEST_PAGE_WITH_LINKS_NAME; +const TEST_PAGE_WITH_FIGURE = '/books/book-slug-1/pages/test-page-for-generic-styles'; +const LIGHTHOUSE_PAGES = [ TEST_PAGE_WITHOUT_MATH, TEST_PAGE_WITH_LINKS, TEST_PAGE_WITH_FIGURE ]; +const LIGHTHOUSE_TARGETS = { + accessibility: 0.97, + 'best-practices': 0.88, + customAccessibility: 1, + seo: 0.9, +}; + +describe('lighthouse', () => { + it('matches or exceeds target scores', async() => { + await asyncPool(1, LIGHTHOUSE_PAGES, async(pageUrl) => { + await checkLighthouse(browser, pageUrl, LIGHTHOUSE_TARGETS); + }); + }); +}); diff --git a/src/test/browserutils.ts b/src/test/browserutils.ts index 3aada85864..efb48f4a45 100644 --- a/src/test/browserutils.ts +++ b/src/test/browserutils.ts @@ -1,6 +1,9 @@ -import lighthouse from 'lighthouse'; import puppeteer from 'puppeteer'; -import * as lighthouseConfig from './audits'; +import url from './url'; +import { checkLighthouse, ScoreTargets } from './lighthouse'; + +export { checkLighthouse, url }; +export type { ScoreTargets }; // jest-puppeteer will expose the `page` and `browser` globals to Jest tests. declare global { @@ -42,8 +45,6 @@ export const setTallerDesktopViewport = (target: puppeteer.Page) => export const setDesktopViewport = (target: puppeteer.Page) => target.setViewport({height: 874, width: desktopWidth}); export const setMobileViewport = (target: puppeteer.Page) => target.setViewport({height: 731, width: 411}); -export const url = (path: string) => `http://localhost:${puppeteerConfig.server.port}/${path.replace(/^\/+/, '')}`; - const calmHooks = (target: puppeteer.Page) => target.evaluate(() => { if (window && window.__APP_ASYNC_HOOKS) { return window.__APP_ASYNC_HOOKS.calm(); @@ -114,14 +115,3 @@ export const h1Content = (target: puppeteer.Page) => target.evaluate(() => { const h1 = document && document.querySelector('h1'); return h1 && h1.textContent; }); - -export const checkLighthouse = async(target: puppeteer.Browser, urlPath: string) => { - - const port = Number((new URL(target.wsEndpoint())).port); - const { lhr } = await lighthouse(url(urlPath), {port}, lighthouseConfig); - - expect(lhr.categories.customAccessibility.score).toBeGreaterThanOrEqual(1); - expect(lhr.categories.accessibility.score).toBeGreaterThanOrEqual(1); - expect(lhr.categories.seo.score).toBeGreaterThanOrEqual(0.69); - expect(lhr.categories['best-practices'].score).toBeGreaterThanOrEqual(0.79); -}; diff --git a/src/test/lighthouse.ts b/src/test/lighthouse.ts new file mode 100644 index 0000000000..1e26b7c76b --- /dev/null +++ b/src/test/lighthouse.ts @@ -0,0 +1,32 @@ +import lighthouse from 'lighthouse'; +import { Browser } from 'puppeteer'; +import * as lighthouseConfig from './audits'; +import url from './url'; + +type Categories = Awaited>['lhr']['categories']; +export type ScoreTargets = { [key in keyof Categories]?: number }; + +const testedCategories: Array = [ + 'accessibility', 'best-practices', 'customAccessibility', 'pwa', 'seo', +]; + +export const checkLighthouse = async(target: Browser, urlPath: string, scoreTargets?: ScoreTargets) => { + const absoluteUrl = urlPath.startsWith('https://') || urlPath.startsWith('http://') ? urlPath : url(urlPath); + const port = Number((new URL(target.wsEndpoint())).port); + const { lhr } = await lighthouse(absoluteUrl, {port}, lighthouseConfig); + + const result: ScoreTargets = {}; + testedCategories.forEach((category) => { + const { score } = lhr.categories[category]; + if (scoreTargets) { + const minScore = scoreTargets[category]; + + if (minScore && score < minScore) { + throw new Error(`${category} score of ${score} was less than the minimum of ${minScore}`); + } + } + result[category] = score; + }); + + return result; +}; diff --git a/src/test/url.ts b/src/test/url.ts new file mode 100644 index 0000000000..efc1c45984 --- /dev/null +++ b/src/test/url.ts @@ -0,0 +1,5 @@ +import { PORT } from '../config'; + +const url = (path: string) => `http://localhost:${PORT}/${path.replace(/^\/+/, '')}`; + +export default url; From 151ed1fcf8e4a3938be740d2e7e3a2a343ea0079 Mon Sep 17 00:00:00 2001 From: Malar-Natarajan <40276461+Malar-Natarajan@users.noreply.github.com> Date: Tue, 14 May 2024 14:04:47 -0500 Subject: [PATCH 02/26] FE Selenium test: Updates to smoke tests (#2237) * update search results * discard modal updates * correct typos --- pytest-selenium/pages/content.py | 13 ++ pytest-selenium/tests/test_highlighting_56.py | 30 ++-- pytest-selenium/utils/books.json | 138 +++++++++--------- pytest-selenium/utils/intlbooks.json | 26 ++-- 4 files changed, 106 insertions(+), 101 deletions(-) diff --git a/pytest-selenium/pages/content.py b/pytest-selenium/pages/content.py index 985b9055a2..39d7a89769 100644 --- a/pytest-selenium/pages/content.py +++ b/pytest-selenium/pages/content.py @@ -28,6 +28,7 @@ BOUNDING_RECTANGLE = "return arguments[0].getBoundingClientRect();" COMPUTED_STYLES = "return window.getComputedStyle(arguments[0]){field};" ELEMENT_SELECT = "return document.querySelector('{selector}');" +FOCUSED_HIGHLIGHT_LOCATOR = "[data-highlight-id='{n}'][aria-current=true]" class ContentError(Exception): @@ -746,6 +747,18 @@ def highlight_ids(self) -> List[str]: set([highlight.get_attribute("data-highlight-id") for highlight in self.highlights]) ) + def highlight_focused(self, highlight_id) -> bool: + """Return True if the highlight is focused. + + :return: ``True`` if the highlight is focused + :rtype: bool + + """ + focused_highlight_locator = self.find_elements( + By.CSS_SELECTOR, FOCUSED_HIGHLIGHT_LOCATOR.format(n=highlight_id) + ) + return bool(focused_highlight_locator) + @property def images(self) -> List[WebElement]: """Return the content images. diff --git a/pytest-selenium/tests/test_highlighting_56.py b/pytest-selenium/tests/test_highlighting_56.py index dc3ffadef3..919660a54d 100644 --- a/pytest-selenium/tests/test_highlighting_56.py +++ b/pytest-selenium/tests/test_highlighting_56.py @@ -66,8 +66,7 @@ def test_modal_for_unsaved_notes_appears_on_clicking_another_highlight( assert book.content.highlight_box.is_open, "Highlight box not open" assert book.content.highlight_box.is_edit_box - highlight = book.content.get_highlight(by_id=id_2)[0] - assert "focus" in highlight.get_attribute("class"), "highlight is not in focus" + assert book.content.highlight_focused(id_2), "highlight is not in focus" assert book.content.highlight_box.note == note # WHEN: click the 1st highlight again @@ -79,8 +78,7 @@ def test_modal_for_unsaved_notes_appears_on_clicking_another_highlight( # THEN: Unsaved note is abandoned and the highlight box is opened for the 1st highlight assert book.content.highlight_box.is_open, "Highlight box not open" - highlight = book.content.get_highlight(by_id=id_1)[0] - assert "focus" in highlight.get_attribute("class"), "highlight is not in focus" + assert book.content.highlight_focused(id_1), "highlight is not in focus" assert book.content.highlight_box.note == "" @@ -127,8 +125,7 @@ def test_modal_for_unsaved_notes_appears_on_page_navigation_using_toc( # THEN: The modal is closed and the unsaved note is retained on the page assert book.content.highlight_box.is_open, "Highlight box not open" assert book.content.highlight_box.is_edit_box - highlight = book.content.get_highlight(by_id=highlight_id)[0] - assert "focus" in highlight.get_attribute("class"), "highlight is not in focus" + assert book.content.highlight_focused(highlight_id), "highlight is not in focus" assert book.content.highlight_box.note == note # WHEN: Click the TOC link again @@ -202,8 +199,7 @@ def test_modal_for_unsaved_notes_appears_on_page_navigation_using_prev_link( # THEN: The modal is closed and the unsaved note is retained on the page assert book.content.highlight_box.is_open, "Highlight box not open" assert book.content.highlight_box.is_edit_box - highlight = book.content.get_highlight(by_id=highlight_id)[0] - assert "focus" in highlight.get_attribute("class"), "highlight is not in focus" + assert book.content.highlight_focused(highlight_id), "highlight is not in focus" assert book.content.highlight_box.note == note # WHEN: Click previous link again @@ -277,8 +273,7 @@ def test_modal_for_unsaved_notes_appears_on_page_navigation_using_next_link( # THEN: The modal is closed and the unsaved note is retained on the page assert book.content.highlight_box.is_open, "Highlight box not open" assert book.content.highlight_box.is_edit_box - highlight = book.content.get_highlight(by_id=highlight_id)[0] - assert "focus" in highlight.get_attribute("class"), "highlight is not in focus" + assert book.content.highlight_focused(highlight_id), "highlight is not in focus" assert book.content.highlight_box.note == note # WHEN: Click next link again @@ -351,8 +346,7 @@ def test_modal_for_unsaved_notes_appears_on_clicking_book_title( # THEN: The modal is closed and the unsaved note is retained on the page assert book.content.highlight_box.is_open, "Highlight box not open" assert book.content.highlight_box.is_edit_box - highlight = book.content.get_highlight(by_id=highlight_id)[0] - assert "focus" in highlight.get_attribute("class"), "highlight is not in focus" + assert book.content.highlight_focused(highlight_id), "highlight is not in focus" assert book.content.highlight_box.note == note # WHEN: Click on book title again @@ -424,8 +418,7 @@ def test_modal_for_unsaved_notes_appears_on_selecting_new_text( # THEN: The modal is closed and the unsaved note is retained in the page assert book.content.highlight_box.is_open, "Highlight box not open" assert book.content.highlight_box.is_edit_box - highlight = book.content.get_highlight(by_id=id_1)[0] - assert "focus" in highlight.get_attribute("class"), "highlight is not in focus" + assert book.content.highlight_focused(id_1), "highlight is not in focus" assert book.content.highlight_box.note == note # WHEN: Select some text in the page again @@ -500,8 +493,7 @@ def test_modal_for_unsaved_notes_appears_on_clicking_search_result_same_page( # THEN: The modal is closed and the unsaved note is retained in the page assert book.content.highlight_box.is_open, "Highlight box not open" assert book.content.highlight_box.is_edit_box - highlight = book.content.get_highlight(by_id=id_1)[0] - assert "focus" in highlight.get_attribute("class"), "highlight is not in focus" + assert book.content.highlight_focused(id_1), "highlight is not in focus" assert book.content.highlight_box.note == note # WHEN: Click the same search result again @@ -512,6 +504,7 @@ def test_modal_for_unsaved_notes_appears_on_clicking_search_result_same_page( book.wait_for_page_to_load() # THEN: Unsaved note of the user highlight is abandoned + highlight = book.content.get_highlight(by_id=id_1)[0] assert not selenium.execute_script(HAS_INDICATOR, highlight), "note is saved for the highlight" # AND: The selected search result is highlighted @@ -573,8 +566,7 @@ def test_modal_for_unsaved_notes_appears_on_clicking_search_result_different_pag # THEN: The modal is closed and the unsaved note is retained in the page assert book.content.highlight_box.is_open, "Highlight box not open" assert book.content.highlight_box.is_edit_box - highlight = book.content.get_highlight(by_id=id_1)[0] - assert "focus" in highlight.get_attribute("class"), "highlight is not in focus" + assert book.content.highlight_focused(id_1), "highlight is not in focus" assert book.content.highlight_box.note == note # WHEN: Click the same search result again @@ -654,7 +646,7 @@ def test_modal_for_unsaved_notes_appears_on_clicking_content_links( assert book.content.highlight_box.is_open, "Highlight box not open" assert book.content.highlight_box.is_edit_box highlight = book.content.get_highlight(by_id=id_1)[0] - assert "focus" in highlight.get_attribute("class"), "highlight is not in focus" + assert book.content.highlight_focused(id_1), "highlight is not in focus" assert book.content.highlight_box.note == note # WHEN: Click the same link again diff --git a/pytest-selenium/utils/books.json b/pytest-selenium/utils/books.json index 0e56d5077b..5cf49e20be 100644 --- a/pytest-selenium/utils/books.json +++ b/pytest-selenium/utils/books.json @@ -1,26 +1,26 @@ { "algebra-and-trigonometry": { "default_page": "1-introduction-to-prerequisites", - "search_term": "Graphs of Parabolas", - "chapter_search_results_total": 6, + "search_term": "figure", + "chapter_search_results_total": 1011, "rkt_search_results_total": 0 }, "algebra-and-trigonometry-2e": { "default_page": "1-introduction-to-prerequisites", "search_term": "quadratic equations", - "chapter_search_results_total": 58, - "rkt_search_results_total": 2 + "chapter_search_results_total": 157, + "rkt_search_results_total": 3 }, "american-government-2e": { "default_page": "1-introduction", "search_term": "hypodermic theory", - "chapter_search_results_total": 1, + "chapter_search_results_total": 3, "rkt_search_results_total": 1 }, "american-government-3e": { "default_page": "1-introduction", "search_term": "Commercial fishers", - "chapter_search_results_total": 2, + "chapter_search_results_total": 4, "rkt_search_results_total": 0 }, "anatomy-and-physiology": { @@ -32,13 +32,13 @@ "anatomy-and-physiology-2e": { "default_page": "1-introduction", "search_term": "Figure 2.22a", - "chapter_search_results_total": 3, + "chapter_search_results_total": 6, "rkt_search_results_total": 0 }, "astronomy": { "default_page": "1-introduction", "search_term": "leap year", - "chapter_search_results_total": 23, + "chapter_search_results_total": 12, "rkt_search_results_total": 0 }, "astronomy-2e": { @@ -50,13 +50,13 @@ "biology-2e": { "default_page": "1-introduction", "search_term": "evolution theory", - "chapter_search_results_total": 46, + "chapter_search_results_total": 341, "rkt_search_results_total": 0 }, "biology-ap-courses": { "default_page": "1-introduction", "search_term": "Virus", - "chapter_search_results_total": 177, + "chapter_search_results_total": 406, "rkt_search_results_total": 10 }, "business-ethics": { @@ -74,19 +74,19 @@ "chemistry-2e": { "default_page": "1-introduction", "search_term": "molecule", - "chapter_search_results_total": 422, - "rkt_search_results_total": 4 + "chapter_search_results_total": 1554, + "rkt_search_results_total": 14 }, "chemistry-atoms-first-2e": { "default_page": "1-introduction", "search_term": "coffee", - "chapter_search_results_total": 31, + "chapter_search_results_total": 35, "rkt_search_results_total": 0 }, "college-algebra": { "default_page": "1-introduction-to-prerequisites", "search_term": "hyperbola", - "chapter_search_results_total": 113, + "chapter_search_results_total": 134, "rkt_search_results_total": 2 }, "college-algebra-2e": { @@ -104,31 +104,31 @@ "college-algebra-corequisite-support-2e": { "default_page": "1-introduction-to-prerequisites", "search_term": "table", - "chapter_search_results_total": 262, + "chapter_search_results_total": 283, "rkt_search_results_total": 0 }, "college-physics": { "default_page": "1-introduction-to-science-and-the-realm-of-physics-physical-quantities-and-units", "search_term": "Newton's first law", - "chapter_search_results_total": 49, - "rkt_search_results_total": 3 + "chapter_search_results_total": 18, + "rkt_search_results_total": 1 }, "college-physics-ap-courses": { "default_page": "1-connection-for-ap-r-courses", "search_term": "kinetic energy", - "chapter_search_results_total": 1025, + "chapter_search_results_total": 1061, "rkt_search_results_total": 11 }, "concepts-biology": { "default_page": "1-introduction", "search_term": "Cell", - "chapter_search_results_total": 792, + "chapter_search_results_total": 1634, "rkt_search_results_total": 23 }, "calculus-volume-1": { "default_page": "1-introduction", "search_term": "number", - "chapter_search_results_total": 210, + "chapter_search_results_total": 280, "rkt_search_results_total": 2 }, "calculus-volume-2": { @@ -140,37 +140,37 @@ "calculus-volume-3": { "default_page": "1-introduction", "search_term": "zero vector", - "chapter_search_results_total": 102, + "chapter_search_results_total": 9, "rkt_search_results_total": 1 }, "college-success": { "default_page": "1-introduction", "search_term": "Shira\u2019s career path", - "chapter_search_results_total": 9, + "chapter_search_results_total": 10, "rkt_search_results_total": 0 }, "entrepreneurship": { "default_page": "1-introduction", "search_term": "Business Model", - "chapter_search_results_total": 343, - "rkt_search_results_total": 8 + "chapter_search_results_total": 399, + "rkt_search_results_total": 7 }, "elementary-algebra-2e": { "default_page": "1-introduction", "search_term": "common denominator", - "chapter_search_results_total": 66, + "chapter_search_results_total": 75, "rkt_search_results_total": 1 }, "intermediate-algebra-2e": { "default_page": "1-introduction", "search_term": "quadratic equations and functions", - "chapter_search_results_total": 6, + "chapter_search_results_total": 5, "rkt_search_results_total": 0 }, "introduction-anthropology": { "default_page": "1-introduction", "search_term": "orientalism", - "chapter_search_results_total": 13, + "chapter_search_results_total": 17, "rkt_search_results_total": 1 }, "introductory-business-statistics": { @@ -182,25 +182,25 @@ "introduction-business": { "default_page": "1-introduction", "search_term": "Buyer behavior", - "chapter_search_results_total": 15, + "chapter_search_results_total": 16, "rkt_search_results_total": 1 }, "introduction-intellectual-property": { "default_page": "1-introduction", "search_term": "plant patent", - "chapter_search_results_total": 15, + "chapter_search_results_total": 35, "rkt_search_results_total": 0 }, "introduction-sociology-2e": { "default_page": "1-introduction-to-sociology", "search_term": "certificates or degrees", - "chapter_search_results_total": 4, + "chapter_search_results_total": 2, "rkt_search_results_total": 0 }, "introduction-sociology-3e": { "default_page": "1-introduction", "search_term": "Berger, 1963", - "chapter_search_results_total": 5, + "chapter_search_results_total": 9, "rkt_search_results_total": 0 }, "introductory-statistics": { @@ -212,91 +212,91 @@ "microbiology": { "default_page": "1-introduction", "search_term": "ecosystems", - "chapter_search_results_total": 18, + "chapter_search_results_total": 21, "rkt_search_results_total": 0 }, "organizational-behavior": { "default_page": "1-introduction", "search_term": "organizational development", - "chapter_search_results_total": 44, - "rkt_search_results_total": 2 + "chapter_search_results_total": 154, + "rkt_search_results_total": 3 }, "physics": { "default_page": "1-introduction", "search_term": "linear relationship", - "chapter_search_results_total": 10, - "rkt_search_results_total": 0 + "chapter_search_results_total": 25, + "rkt_search_results_total": 1 }, "precalculus": { "default_page": "1-introduction-to-functions", "search_term": "Pythagorean Identities", - "chapter_search_results_total": 18, - "rkt_search_results_total": 1 + "chapter_search_results_total": 36, + "rkt_search_results_total": 2 }, "precalculus-2e": { "default_page": "1-introduction-to-functions", "search_term": "one-to-one function", - "chapter_search_results_total": 404, - "rkt_search_results_total": 3 + "chapter_search_results_total": 41, + "rkt_search_results_total": 1 }, "prealgebra-2e": { "default_page": "1-introduction", "search_term": "Whole Numbers", - "chapter_search_results_total": 160, + "chapter_search_results_total": 148, "rkt_search_results_total": 1 }, "principles-financial-accounting": { "default_page": "1-why-it-matters", "search_term": "Explain the Pricing of Long-Term Liabilities", - "chapter_search_results_total": 7, + "chapter_search_results_total": 3, "rkt_search_results_total": 0 }, "principles-economics-2e": { "default_page": "1-introduction", "search_term": "Elasticity", - "chapter_search_results_total": 166, + "chapter_search_results_total": 182, "rkt_search_results_total": 11 }, "principles-macroeconomics-2e": { "default_page": "1-introduction", "search_term": "modern economic growth", - "chapter_search_results_total": 14, + "chapter_search_results_total": 87, "rkt_search_results_total": 1 }, "principles-microeconomics-2e": { "default_page": "1-introduction", "search_term": "Explicit costs", - "chapter_search_results_total": 18, + "chapter_search_results_total": 28, "rkt_search_results_total": 1 }, "principles-macroeconomics-ap-courses-2e": { "default_page": "1-introduction", "search_term": "adjustable-rate mortgage", - "chapter_search_results_total": 14, + "chapter_search_results_total": 16, "rkt_search_results_total": 1 }, "principles-microeconomics-ap-courses-2e": { "default_page": "1-introduction", "search_term": "positive externality", - "chapter_search_results_total": 24, - "rkt_search_results_total": 1 + "chapter_search_results_total": 56, + "rkt_search_results_total": 3 }, "principles-managerial-accounting": { "default_page": "1-why-it-matters", "search_term": "relevant range", - "chapter_search_results_total": 51, + "chapter_search_results_total": 53, "rkt_search_results_total": 3 }, "principles-management": { "default_page": "1-introduction", "search_term": "plan is a decision to carry out a particular action", - "chapter_search_results_total": 11, + "chapter_search_results_total": 2, "rkt_search_results_total": 0 }, "psychology-2e": { "default_page": "1-introduction", "search_term": "event schema", - "chapter_search_results_total": 15, + "chapter_search_results_total": 25, "rkt_search_results_total": 1 }, "statistics": { @@ -320,13 +320,13 @@ "university-physics-volume-3": { "default_page": "1-introduction", "search_term": "interference fringes", - "chapter_search_results_total": 61, + "chapter_search_results_total": 73, "rkt_search_results_total": 1 }, "us-history": { "default_page": "1-introduction", "search_term": "PATRIOTS", - "chapter_search_results_total": 20, + "chapter_search_results_total": 61, "rkt_search_results_total": 0 }, "writing-guide": { @@ -338,13 +338,13 @@ "college-physics-ap-courses-2e": { "default_page": "1-connection-for-ap-r-courses", "search_term": "Big Idea", - "chapter_search_results_total": 178, + "chapter_search_results_total": 154, "rkt_search_results_total": 0 }, "principles-economics-3e": { "default_page": "1-introduction", "search_term": "Age", - "chapter_search_results_total": 73, + "chapter_search_results_total": 94, "rkt_search_results_total": 0 }, "principles-finance": { @@ -356,31 +356,31 @@ "principles-macroeconomics-3e": { "default_page": "1-introduction", "search_term": "Age", - "chapter_search_results_total": 49, + "chapter_search_results_total": 66, "rkt_search_results_total": 0 }, "introduction-political-science": { "default_page": "1-introduction", "search_term": "China", - "chapter_search_results_total": 97, + "chapter_search_results_total": 104, "rkt_search_results_total": 0 }, "principles-microeconomics-3e": { "default_page": "1-introduction", "search_term": "Bring it Home", - "chapter_search_results_total": 10, + "chapter_search_results_total": 6, "rkt_search_results_total": 0 }, "introduction-philosophy": { "default_page": "1-introduction", "search_term": "high school", - "chapter_search_results_total": 4, + "chapter_search_results_total": 3, "rkt_search_results_total": 0 }, "world-history-volume-2": { "default_page": "1-introduction", "search_term": "Winston Churchill", - "chapter_search_results_total": 15, + "chapter_search_results_total": 16, "rkt_search_results_total": 0 }, "college-physics-2e": { @@ -392,7 +392,7 @@ "workplace-software-skills": { "default_page": "1-chapter-scenario", "search_term": "\"click and drag the file\"", - "chapter_search_results_total": 1, + "chapter_search_results_total": 10, "rkt_search_results_total": 0 }, "world-history-volume-1": { @@ -404,49 +404,49 @@ "organic-chemistry": { "default_page": "1-why-this-chapter", "search_term": "Zwitterion", - "chapter_search_results_total": 7, + "chapter_search_results_total": 12, "rkt_search_results_total": 0 }, "contemporary-mathematics": { "default_page": "1-introduction", "search_term": "well-defined sets", - "chapter_search_results_total": 8, + "chapter_search_results_total": 11, "rkt_search_results_total": 0 }, "introductory-statistics-2e": { "default_page": "1-introduction", "search_term": "Student's t-Distribution", - "chapter_search_results_total": 71, + "chapter_search_results_total": 43, "rkt_search_results_total": 2 }, "introductory-business-statistics-2e": { "default_page": "1-introduction", "search_term": "(df)", - "chapter_search_results_total": 69, + "chapter_search_results_total": 71, "rkt_search_results_total": 1 }, "college-success-concise": { "default_page": "1-introduction", "search_term": "résumé", - "chapter_search_results_total": 1, + "chapter_search_results_total": 42, "rkt_search_results_total": 0 }, "preparing-for-college-success": { "default_page": "1-introduction", "search_term": "liberal arts courses", - "chapter_search_results_total": 10, + "chapter_search_results_total": 2, "rkt_search_results_total": 0 }, "principles-marketing": { "default_page": "1-unit-introduction", "search_term": "Procter & Gamble", - "chapter_search_results_total": 16, + "chapter_search_results_total": 19, "rkt_search_results_total": 0 }, "introduction-python-programming": { "default_page": "1-introduction", "search_term": "print()", - "chapter_search_results_total": 75, + "chapter_search_results_total": 106, "rkt_search_results_total": 0 } } diff --git a/pytest-selenium/utils/intlbooks.json b/pytest-selenium/utils/intlbooks.json index f82ff99e14..870ee7c388 100644 --- a/pytest-selenium/utils/intlbooks.json +++ b/pytest-selenium/utils/intlbooks.json @@ -8,37 +8,37 @@ "psychologia-polska": { "default_page": "1-wprowadzenie", "search_term": "MIT", - "chapter_search_results_total": 18, + "chapter_search_results_total": 21, "rkt_search_results_total": 0 }, "fizyka-dla-szkół-wyższych-tom-1": { "default_page": "1-wstep", "search_term": "galaktyka", - "chapter_search_results_total": 5, + "chapter_search_results_total": 48, "rkt_search_results_total": 0 }, "fizyka-dla-szkół-wyższych-tom-2": { "default_page": "1-wstep", "search_term": "gwiazdy", - "chapter_search_results_total": 2, + "chapter_search_results_total": 4, "rkt_search_results_total": 0 }, "fizyka-dla-szkół-wyższych-tom-3": { "default_page": "1-wstep", "search_term": "fale", - "chapter_search_results_total": 117, - "rkt_search_results_total": 2 + "chapter_search_results_total": 859, + "rkt_search_results_total": 9 }, "física-universitaria-volumen-1": { "default_page": "1-introduccion", "search_term": "ambos", - "chapter_search_results_total": 149, + "chapter_search_results_total": 221, "rkt_search_results_total": 0 }, "física-universitaria-volumen-2": { "default_page": "1-introduccion", "search_term": "del Sol", - "chapter_search_results_total": 112, + "chapter_search_results_total": 131, "rkt_search_results_total": 0 }, "física-universitaria-volumen-3": { @@ -50,13 +50,13 @@ "química-2ed": { "default_page": "1-introduccion", "search_term": "Hacer café", - "chapter_search_results_total": 1, + "chapter_search_results_total": 8, "rkt_search_results_total": 0 }, "química-comenzando-átomos-2ed": { "default_page": "1-introduccion", "search_term": "el jabón", - "chapter_search_results_total": 25, + "chapter_search_results_total": 93, "rkt_search_results_total": 0 }, "cálculo-volumen-1": { @@ -80,19 +80,19 @@ "introducción-estadística": { "default_page": "1-introduccion", "search_term": "mejor estimación", - "chapter_search_results_total": 16, + "chapter_search_results_total": 11, "rkt_search_results_total": 0 }, "introducción-estadística-empresarial": { "default_page": "1-introduccion", "search_term": "Informática", - "chapter_search_results_total": 3, - "rkt_search_results_total": 0 + "chapter_search_results_total": 220, + "rkt_search_results_total": 1 }, "precálculo-2ed": { "default_page": "1-introduccion", "search_term": "las empresas", - "chapter_search_results_total": 12, + "chapter_search_results_total": 4, "rkt_search_results_total": 0 } } From 48d7194f5bf21ef6db82d634ebe6a0b9db074bf4 Mon Sep 17 00:00:00 2001 From: Josiah Ivey Date: Thu, 16 May 2024 08:32:23 -0700 Subject: [PATCH 03/26] update pipeline to 20240506.185246 (#2236) Co-authored-by: Beth Shook --- src/config.archive-url.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.archive-url.json b/src/config.archive-url.json index c578f7568e..55e5c6416b 100644 --- a/src/config.archive-url.json +++ b/src/config.archive-url.json @@ -1,4 +1,4 @@ { - "REACT_APP_ARCHIVE": "20240226.174525", + "REACT_APP_ARCHIVE": "20240506.185246", "REACT_APP_ARCHIVE_URL_BASE": "/apps/archive/" } From af21da16d164ac2ba5a102b15df1675920a3f0b3 Mon Sep 17 00:00:00 2001 From: Roy Johnson Date: Thu, 16 May 2024 14:59:56 -0500 Subject: [PATCH 04/26] Add aria expanded to using this guide button (#2205) * Add button type, aria-expanded, aria-controls to Using This Guide button [DISCO-39] * Remove superfluous layer --------- Co-authored-by: staxly[bot] <35789409+staxly[bot]@users.noreply.github.com> --- .../UsingThisGuide/UsingThisGuideButton.tsx | 29 +-- .../UsingThisGuide.spec.tsx.snap | 156 +++++++--------- .../__snapshots__/Filters.spec.tsx.snap | 171 ++++++++---------- 3 files changed, 157 insertions(+), 199 deletions(-) diff --git a/src/app/content/studyGuides/components/UsingThisGuide/UsingThisGuideButton.tsx b/src/app/content/studyGuides/components/UsingThisGuide/UsingThisGuideButton.tsx index c2225119a3..8458e33a6c 100644 --- a/src/app/content/studyGuides/components/UsingThisGuide/UsingThisGuideButton.tsx +++ b/src/app/content/studyGuides/components/UsingThisGuide/UsingThisGuideButton.tsx @@ -19,7 +19,7 @@ const QuestionIcon = styled(QuestionCircle)` `; // tslint:disable-next-line:variable-name -const UsingThisGuideButtonInnerStyles = styled.div` +export const UsingThisGuideButtonWrapper = styled(PlainButton)` display: flex; align-items: center; justify-content: center; @@ -28,29 +28,19 @@ const UsingThisGuideButtonInnerStyles = styled.div` color: ${theme.color.primary.gray.base}; position: relative; padding: ${buttonPaddingTopDesktop}rem ${mobilePaddingSides}rem ${buttonPaddingBottomDesktop}rem; - outline: none; - ${({isOpen}) => isOpen && css` - color: ${theme.color.white}; - `} - ${theme.breakpoints.mobile(css` - padding-right: 1.4rem; - padding-left: 1.4rem; - `)} - ${disablePrint} -`; - -// tslint:disable-next-line:variable-name -export const UsingThisGuideButtonWrapper = styled(PlainButton)` - position: relative; height: 100%; margin-right: 3.2rem; margin-top: ${buttonMarginTopDesktop}rem; ${({isOpen}) => isOpen && css` background: ${theme.color.black}; + color: ${theme.color.white}; `} ${theme.breakpoints.mobile(css` margin-right: 0.8rem; + padding-right: 1.4rem; + padding-left: 1.4rem; `)} + ${disablePrint} `; // tslint:disable-next-line:variable-name @@ -73,15 +63,16 @@ const UsingThisGuideButton = (props: Props) => { const text = useIntl().formatMessage({id: 'i18n:studyguides:popup:using-this-guide'}); return - - - {text} - + + {text} ; }; diff --git a/src/app/content/studyGuides/components/UsingThisGuide/__snapshots__/UsingThisGuide.spec.tsx.snap b/src/app/content/studyGuides/components/UsingThisGuide/__snapshots__/UsingThisGuide.spec.tsx.snap index f6dba4ce88..b754c1bf3e 100644 --- a/src/app/content/studyGuides/components/UsingThisGuide/__snapshots__/UsingThisGuide.spec.tsx.snap +++ b/src/app/content/studyGuides/components/UsingThisGuide/__snapshots__/UsingThisGuide.spec.tsx.snap @@ -191,18 +191,23 @@ exports[`Using this guide renders using this guide banner correctly 1`] = ` `; exports[`Using this guide renders using this guide button correctly (when banner closed) 1`] = ` -.c2 { +.c1 { display: inline-block; vertical-align: -.125em; overflow: hidden; } -.c3 { +.c2 { height: 1.7rem; width: 1.7rem; } -.c1 { +.c0 { + cursor: pointer; + border: none; + margin: 0; + padding: 0; + background: none; display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; @@ -220,22 +225,12 @@ exports[`Using this guide renders using this guide button correctly (when banner color: #5e6062; position: relative; padding: 1.2rem 1.6rem 1.8rem; - outline: none; -} - -.c0 { - cursor: pointer; - border: none; - margin: 0; - padding: 0; - background: none; - position: relative; height: 100%; margin-right: 3.2rem; margin-top: 0.6rem; } -.c4 { +.c3 { font-size: 1.6rem; line-height: 2.5rem; font-weight: 600; @@ -243,75 +238,74 @@ exports[`Using this guide renders using this guide button correctly (when banner } @media screen and (max-width:75em) { - .c1 { + .c0 { + margin-right: 0.8rem; padding-right: 1.4rem; padding-left: 1.4rem; } } @media print { - .c1 { - display: none; - } -} - -@media screen and (max-width:75em) { .c0 { - margin-right: 0.8rem; + display: none; } } @media screen and (max-width:75em) { - .c4 { + .c3 { display: none; } } `; exports[`Using this guide renders using this guide button correctly (when banner open) 1`] = ` -.c2 { +.c1 { display: inline-block; vertical-align: -.125em; overflow: hidden; } -.c3 { +.c2 { height: 1.7rem; width: 1.7rem; } -.c1 { +.c0 { + cursor: pointer; + border: none; + margin: 0; + padding: 0; + background: none; display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; @@ -329,24 +323,14 @@ exports[`Using this guide renders using this guide button correctly (when banner color: #5e6062; position: relative; padding: 1.2rem 1.6rem 1.8rem; - outline: none; - color: #fff; -} - -.c0 { - cursor: pointer; - border: none; - margin: 0; - padding: 0; - background: none; - position: relative; height: 100%; margin-right: 3.2rem; margin-top: 0.6rem; background: #000; + color: #fff; } -.c4 { +.c3 { font-size: 1.6rem; line-height: 2.5rem; font-weight: 600; @@ -354,58 +338,52 @@ exports[`Using this guide renders using this guide button correctly (when banner } @media screen and (max-width:75em) { - .c1 { + .c0 { + margin-right: 0.8rem; padding-right: 1.4rem; padding-left: 1.4rem; } } @media print { - .c1 { - display: none; - } -} - -@media screen and (max-width:75em) { .c0 { - margin-right: 0.8rem; + display: none; } } @media screen and (max-width:75em) { - .c4 { + .c3 { display: none; } } `; diff --git a/src/app/content/studyGuides/components/__snapshots__/Filters.spec.tsx.snap b/src/app/content/studyGuides/components/__snapshots__/Filters.spec.tsx.snap index 5d74f91572..c0f950983b 100644 --- a/src/app/content/studyGuides/components/__snapshots__/Filters.spec.tsx.snap +++ b/src/app/content/studyGuides/components/__snapshots__/Filters.spec.tsx.snap @@ -180,7 +180,7 @@ input:checked + .c21 .c22 { color: #0064A0; } -.c61 { +.c60 { cursor: pointer; border: none; margin: 0; @@ -190,13 +190,13 @@ input:checked + .c21 .c22 { margin-right: 0.4rem; } -.c61 svg { +.c60 svg { height: 0.8rem; width: 0.8rem; color: #5e6062; } -.c62 { +.c61 { color: #424242; font-weight: 300; color: #5e6062; @@ -208,7 +208,7 @@ input:checked + .c21 .c22 { line-height: 1.5rem; } -.c60 { +.c59 { margin-right: 3.2rem; display: -webkit-box; display: -webkit-flex; @@ -222,7 +222,7 @@ input:checked + .c21 .c22 { height: 4rem; } -.c59 { +.c58 { color: #424242; font-size: 1.4rem; display: -webkit-box; @@ -633,7 +633,7 @@ input:checked + .c21 .c22 { margin: 0 1.6rem 0 1.6rem; } -.c11 .c63:last-child { +.c11 .c62:last-child { border-bottom: none; } @@ -716,7 +716,7 @@ input:checked + .c21 .c22 { margin: 0px 0px 0px 0.5rem; } -.c51 { +.c50 { outline: none; position: relative; padding: 2rem 2.4rem; @@ -725,12 +725,12 @@ input:checked + .c21 .c22 { margin-bottom: 1rem; } -.c55 { +.c54 { width: 100%; padding: 0 4.2rem; } -.c53 { +.c52 { color: #424242; font-size: 3.6rem; line-height: 4rem; @@ -745,7 +745,7 @@ input:checked + .c21 .c22 { color: #fff; } -.c56 { +.c55 { cursor: pointer; border: none; margin: 0; @@ -770,14 +770,14 @@ input:checked + .c21 .c22 { right: 2.4rem; } -.c57 { +.c56 { color: #000; height: 2.8rem; width: 2.8rem; padding: 0.4rem; } -.c52 { +.c51 { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; @@ -788,7 +788,7 @@ input:checked + .c21 .c22 { align-items: center; } -.c54 { +.c53 { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; @@ -799,12 +799,17 @@ input:checked + .c21 .c22 { justify-content: center; } -.c49 { +.c48 { height: 1.7rem; width: 1.7rem; } -.c48 { +.c47 { + cursor: pointer; + border: none; + margin: 0; + padding: 0; + background: none; display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; @@ -822,24 +827,14 @@ input:checked + .c21 .c22 { color: #5e6062; position: relative; padding: 1.2rem 1.6rem 1.8rem; - outline: none; - color: #fff; -} - -.c47 { - cursor: pointer; - border: none; - margin: 0; - padding: 0; - background: none; - position: relative; height: 100%; margin-right: 3.2rem; margin-top: 0.6rem; background: #000; + color: #fff; } -.c50 { +.c49 { font-size: 1.6rem; line-height: 2.5rem; font-weight: 600; @@ -890,13 +885,13 @@ input:checked + .c21 .c22 { } @media print { - .c61 { + .c60 { display: none; } } @media print { - .c62 { + .c61 { max-width: -webkit-max-content; max-width: -moz-max-content; max-width: max-content; @@ -904,13 +899,13 @@ input:checked + .c21 .c22 { } @media screen and (max-width:75em) { - .c59 { + .c58 { padding: 0 2.4rem 0.4rem 2.4rem; } } @media print { - .c59 { + .c58 { margin: 0; } } @@ -945,7 +940,7 @@ input:checked + .c21 .c22 { } @media print { - .c0 > *:not(.c58) { + .c0 > *:not(.c57) { display: none; } } @@ -1001,26 +996,26 @@ input:checked + .c21 .c22 { } @media screen and (max-width:30em) { - .c51 { + .c50 { padding: 1rem 1.6rem; } } @media print { - .c51 { + .c50 { display: none; } } @media screen and (max-width:50em) { - .c55 { + .c54 { max-width: 30rem; padding: 0; } } @media screen and (max-width:75em) { - .c53 { + .c52 { color: #424242; font-size: 1.6rem; line-height: 2rem; @@ -1035,40 +1030,35 @@ input:checked + .c21 .c22 { } @media screen and (max-width:30em) { - .c56 { + .c55 { top: 1rem; right: 1.6rem; } } @media screen and (max-width:75em) { - .c57 { + .c56 { height: 1.6rem; width: 1.6rem; } } @media screen and (max-width:75em) { - .c48 { + .c47 { + margin-right: 0.8rem; padding-right: 1.4rem; padding-left: 1.4rem; } } @media print { - .c48 { - display: none; - } -} - -@media screen and (max-width:75em) { .c47 { - margin-right: 0.8rem; + display: none; } } @media screen and (max-width:75em) { - .c50 { + .c49 { display: none; } } @@ -1773,51 +1763,50 @@ input:checked + .c21 .c22 {

Using This Guide

The image presents the structure of study guides. The first element of each block summarizes, clarifies, or enriches key content. The second element is taken directly from the book. On the left side, there is a color label depending on the content. @@ -1834,14 +1823,14 @@ input:checked + .c21 .c22 {
2 Test Chapter 2", @@ -1905,11 +1894,11 @@ input:checked + .c21 .c22 { />
  • Important Concepts
  • Connections & Relationships
  • Enrichment
  • Key Terms From f10a818b313085912012ff623c4851bf9a53af6e Mon Sep 17 00:00:00 2001 From: OpenStax Jenkins Bot Date: Fri, 17 May 2024 10:29:54 -0600 Subject: [PATCH 05/26] update content (#2239) Co-authored-by: Malar-Natarajan <40276461+Malar-Natarajan@users.noreply.github.com> --- src/config.books.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.books.json b/src/config.books.json index 1f8ec32d91..3b4b22b4c6 100644 --- a/src/config.books.json +++ b/src/config.books.json @@ -327,7 +327,7 @@ "defaultVersion": "aaf07d5" }, "7db4d55f-5c0e-4c05-8b72-841685444165": { - "defaultVersion": "a770108" + "defaultVersion": "ba2f95c" }, "9331ec14-7ea7-4415-a8dd-ee34f0c5f9c4": { "defaultVersion": "a770108" From 6e35fe01ef4d949c08ac7d35135932b00804fe89 Mon Sep 17 00:00:00 2001 From: OpenStax Jenkins Bot Date: Fri, 17 May 2024 11:26:16 -0600 Subject: [PATCH 06/26] update content (#2241) Co-authored-by: Malar-Natarajan <40276461+Malar-Natarajan@users.noreply.github.com> --- src/config.books.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/config.books.json b/src/config.books.json index 3b4b22b4c6..19e5a1a41e 100644 --- a/src/config.books.json +++ b/src/config.books.json @@ -331,5 +331,8 @@ }, "9331ec14-7ea7-4415-a8dd-ee34f0c5f9c4": { "defaultVersion": "a770108" + }, + "2f805b43-bce4-4379-add2-4fbf46700e4c": { + "defaultVersion": "ba2f95c" } } From 1741a98a4245db60a62c03361ba90265cab1e400 Mon Sep 17 00:00:00 2001 From: OpenStax Jenkins Bot Date: Fri, 17 May 2024 11:58:14 -0600 Subject: [PATCH 07/26] update content (#2240) Co-authored-by: Malar-Natarajan <40276461+Malar-Natarajan@users.noreply.github.com> Co-authored-by: Josiah Ivey --- src/config.books.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.books.json b/src/config.books.json index 19e5a1a41e..a2ed1f30bc 100644 --- a/src/config.books.json +++ b/src/config.books.json @@ -330,7 +330,7 @@ "defaultVersion": "ba2f95c" }, "9331ec14-7ea7-4415-a8dd-ee34f0c5f9c4": { - "defaultVersion": "a770108" + "defaultVersion": "ba2f95c" }, "2f805b43-bce4-4379-add2-4fbf46700e4c": { "defaultVersion": "ba2f95c" From 32562dc1a5dfa50212610751fcdee830523040bc Mon Sep 17 00:00:00 2001 From: Roy Johnson Date: Mon, 20 May 2024 10:55:16 -0500 Subject: [PATCH 08/26] Add warning for overlapping highlights (#2215) * Fix typo * Set toast message for overlapping highlights * Fix Polish translation * Update snapshot --------- Co-authored-by: Malar-Natarajan <40276461+Malar-Natarajan@users.noreply.github.com> --- src/app/content/components/Page.spec.tsx | 4 ++-- src/app/content/components/Page/PageComponent.tsx | 2 +- .../content/components/Page/PageToasts.spec.tsx | 4 ++-- .../content/components/Page/highlightManager.ts | 13 ++++++++----- .../components/popUp/ToastNotifications.spec.tsx | 4 ++-- src/app/content/highlights/components/Note.tsx | 2 +- .../components/__snapshots__/Note.spec.tsx.snap | 2 +- src/app/content/highlights/errors.ts | 14 +++++++------- .../highlights/hooks/createHighlight.spec.ts | 2 +- .../hooks/initializeMyHighlightsSummary.spec.ts | 4 ++-- .../highlights/hooks/loadHighlights.spec.ts | 2 +- src/app/content/highlights/hooks/loadMore.spec.ts | 2 +- .../highlights/hooks/printHighlights.spec.ts | 2 +- .../hooks/receiveDeleteHighlight.spec.ts | 2 +- .../highlights/hooks/updateHighlight.spec.ts | 6 +++--- src/app/messages/en/messages.json | 1 + src/app/messages/es/messages.json | 1 + src/app/messages/pl/messages.json | 1 + .../components/ToastNotifications/constants.ts | 2 +- .../components/ToastNotifications/index.spec.tsx | 4 ++-- 20 files changed, 40 insertions(+), 34 deletions(-) diff --git a/src/app/content/components/Page.spec.tsx b/src/app/content/components/Page.spec.tsx index 5dd3e1083c..16a35d54ea 100644 --- a/src/app/content/components/Page.spec.tsx +++ b/src/app/content/components/Page.spec.tsx @@ -1081,7 +1081,7 @@ describe('Page', () => { await new Promise((resolve) => setImmediate(resolve)); expect(dispatch).toHaveBeenCalledWith( - addToast(toastMessageKeys.higlights.failure.search, {destination: 'page'})); + addToast(toastMessageKeys.highlights.failure.search, {destination: 'page'})); dispatch.mockClear(); const errorModalCloseButton = root.querySelector('[data-testid=banner-body] button'); @@ -1099,7 +1099,7 @@ describe('Page', () => { await new Promise((resolve) => setImmediate(resolve)); expect(dispatch).not.toHaveBeenCalledWith( - addToast(toastMessageKeys.higlights.failure.search, {destination: 'page'})); + addToast(toastMessageKeys.highlights.failure.search, {destination: 'page'})); dateMock.mockRestore(); }); diff --git a/src/app/content/components/Page/PageComponent.tsx b/src/app/content/components/Page/PageComponent.tsx index 0684c7c719..7850a49880 100644 --- a/src/app/content/components/Page/PageComponent.tsx +++ b/src/app/content/components/Page/PageComponent.tsx @@ -126,7 +126,7 @@ export default class PageComponent extends Component { public onHighlightSelect: HighlightUpdateOptions['onSelect'] = (selectedHighlight) => { if (!selectedHighlight) { - this.props.addToast(toastMessageKeys.higlights.failure.search, {destination: 'page'}); + this.props.addToast(toastMessageKeys.highlights.failure.search, {destination: 'page'}); } }; diff --git a/src/app/content/components/Page/PageToasts.spec.tsx b/src/app/content/components/Page/PageToasts.spec.tsx index 5ba4a53c12..47d1a12688 100644 --- a/src/app/content/components/Page/PageToasts.spec.tsx +++ b/src/app/content/components/Page/PageToasts.spec.tsx @@ -30,7 +30,7 @@ describe('PageToasts', () => { }); it('matches snapshots with toasts', () => { - store.dispatch(addToast(toastMessageKeys.higlights.failure.create, {destination: 'page'})); + store.dispatch(addToast(toastMessageKeys.highlights.failure.create, {destination: 'page'})); const toasts = groupedToastNotifications(store.getState()).page; if (!toasts) { @@ -48,7 +48,7 @@ describe('PageToasts', () => { }); it('matches snapshot with toasts when mobile toolbar is open', () => { - store.dispatch(addToast(toastMessageKeys.higlights.failure.create, {destination: 'page'})); + store.dispatch(addToast(toastMessageKeys.highlights.failure.create, {destination: 'page'})); store.dispatch(openMobileToolbar()); const component = renderer.create( diff --git a/src/app/content/components/Page/highlightManager.ts b/src/app/content/components/Page/highlightManager.ts index e2f2445367..090cda9021 100644 --- a/src/app/content/components/Page/highlightManager.ts +++ b/src/app/content/components/Page/highlightManager.ts @@ -24,11 +24,11 @@ import * as select from '../../selectors'; import { expandClosestSolution } from '../../utils/domUtils'; import attachHighlight from '../utils/attachHighlight'; import { erase, highlightData, insertPendingCardInOrder, isUnknownHighlightData, updateStyle } from './highlightUtils'; +import { addToast } from '../../../notifications/actions'; export interface HighlightManagerServices { getProp: () => HighlightProp; setPendingHighlight: (highlight: Highlight) => void; - clearPendingHighlight: () => void; highlighter: Highlighter; container: HTMLElement; } @@ -94,6 +94,11 @@ const onSelectHighlight = ( highlight: Highlight | undefined ) => defer(async() => { if (highlights.length > 0 || !highlight) { + appServices.dispatch( + addToast('i18n:notification:toast:highlights:select-overlap', { + destination: 'page', + }) + ); return; } @@ -107,7 +112,7 @@ const onSelectHighlight = ( }); const createHighlighter = ( - highlightManagerServices: Omit, + highlightManagerServices: Omit, appServices: AppServices & MiddlewareAPI, intl: IntlShape ) => { @@ -152,7 +157,6 @@ export interface UpdateOptions { // tslint:disable-next-line: max-line-length export default (container: HTMLElement, getProp: () => HighlightProp, appServices: AppServices & MiddlewareAPI, intl: IntlShape) => { - let highlighter: Highlighter; let pendingHighlight: Highlight | undefined; let scrollTargetHighlightIdThatWasHandled: string; let setListHighlighter = (_highlighter: Highlighter): void => undefined; @@ -209,13 +213,12 @@ export default (container: HTMLElement, getProp: () => HighlightProp, appService }; const highlightManagerServices = { - clearPendingHighlight, container, getProp, setPendingHighlight, }; - highlighter = createHighlighter(highlightManagerServices, appServices, intl); + const highlighter = createHighlighter(highlightManagerServices, appServices, intl); setListHighlighter(highlighter); return { diff --git a/src/app/content/components/popUp/ToastNotifications.spec.tsx b/src/app/content/components/popUp/ToastNotifications.spec.tsx index 51cd39c616..540534d26d 100644 --- a/src/app/content/components/popUp/ToastNotifications.spec.tsx +++ b/src/app/content/components/popUp/ToastNotifications.spec.tsx @@ -26,13 +26,13 @@ describe('ToastNotifications', () => { it('matches snapshots with toasts', () => { const toasts: ToastNotification[] = [{ destination: 'myHighlights', - messageKey: toastMessageKeys.higlights.failure.delete, + messageKey: toastMessageKeys.highlights.failure.delete, shouldAutoDismiss: true, timestamp: 1, }, { destination: 'myHighlights', errorId: 'error-id', - messageKey: toastMessageKeys.higlights.failure.update.annotation, + messageKey: toastMessageKeys.highlights.failure.update.annotation, shouldAutoDismiss: true, timestamp: 2, }]; diff --git a/src/app/content/highlights/components/Note.tsx b/src/app/content/highlights/components/Note.tsx index 1c9a66e984..2a59d8a26b 100644 --- a/src/app/content/highlights/components/Note.tsx +++ b/src/app/content/highlights/components/Note.tsx @@ -84,7 +84,7 @@ const Note = ({onChange, onFocus, note, textareaRef, edit = false}: Props) => { React.useEffect(setTextAreaHeight, [note, setTextAreaHeight]); return ( - +