diff --git a/src/app/components/language-selector/language-selector.tsx b/src/app/components/language-selector/language-selector.tsx index d221524f3..8db87ada3 100644 --- a/src/app/components/language-selector/language-selector.tsx +++ b/src/app/components/language-selector/language-selector.tsx @@ -72,7 +72,7 @@ export default function LanguageSelector({ }: { LeadIn: () => React.JSX.Element; otherLocales: string[]; - LinkPresentation: LinkPresentationType; + LinkPresentation?: LinkPresentationType; addPolish?: boolean; }) { if (addPolish) { diff --git a/src/app/pages/404/404.js b/src/app/pages/404/404.tsx similarity index 100% rename from src/app/pages/404/404.js rename to src/app/pages/404/404.tsx diff --git a/src/app/pages/a-page-template/a-page-template.js b/src/app/pages/a-page-template/a-page-template.js deleted file mode 100644 index d823f5181..000000000 --- a/src/app/pages/a-page-template/a-page-template.js +++ /dev/null @@ -1,21 +0,0 @@ -import React from 'react'; -import LoaderPage from '~/components/jsx-helpers/loader-page'; -import './a-page-template.scss'; - -const slug = 'books/biology-2e'; - -function Pagename({data: {heading}}) { - return ( -
-

{heading}

-
- ); -} - -export default function PagenameLoader() { - return ( -
- -
- ); -} diff --git a/src/app/pages/a-page-template/a-page-template.scss b/src/app/pages/a-page-template/a-page-template.scss deleted file mode 100644 index ec07f5f5e..000000000 --- a/src/app/pages/a-page-template/a-page-template.scss +++ /dev/null @@ -1,11 +0,0 @@ -@import 'pattern-library/core/pattern-library/headers'; - -.a-page-template { - h1 { - color: os-color(red); - } - - .content { - @extend %content; - } -} diff --git a/src/app/pages/partners/partner-details/info-request-form/school-selector.js b/src/app/pages/partners/partner-details/info-request-form/school-selector.tsx similarity index 56% rename from src/app/pages/partners/partner-details/info-request-form/school-selector.js rename to src/app/pages/partners/partner-details/info-request-form/school-selector.tsx index c71392017..0e6ce93de 100644 --- a/src/app/pages/partners/partner-details/info-request-form/school-selector.js +++ b/src/app/pages/partners/partner-details/info-request-form/school-selector.tsx @@ -2,8 +2,7 @@ import React from 'react'; import useMatchingSchools from '~/models/use-school-suggestion-list'; import {FilteringSelect} from '~/components/form-elements/form-elements'; -// Left as JS because I couldn't get the tests to cover these -export default function SchoolSelector({value, setValue}) { +export default function SchoolSelector({value, setValue}: {value: string; setValue: (value: string) => void}) { const {schoolIsOk, schoolOptions} = useMatchingSchools(value); return ( @@ -18,17 +17,25 @@ export default function SchoolSelector({value, setValue}) { required: true, value, autoComplete: 'off', - onChange({target}) {setValue(target.value);} + onChange: useSetValueFromTarget(setValue) }} - accept={(option) => setValue(option.value)} + accept={useAcceptValue(setValue)} accepted={schoolIsOk} /> ); } -export function useDoSubmit(afterSubmit) { - return React.useCallback((form) => { +export function useAcceptValue(setValue: (value: string) => void) { + return React.useCallback(({value}: {value: string}) => setValue(value), [setValue]); +} + +export function useSetValueFromTarget(setValue: (value: string) => void) { + return React.useCallback(({target}: React.ChangeEvent) => setValue(target.value), [setValue]); +} + +export function useDoSubmit(afterSubmit: () => void) { + return React.useCallback((form: HTMLFormElement) => { form.submit(); afterSubmit(); }, [afterSubmit]); diff --git a/src/app/pages/press/page-context.tsx b/src/app/pages/press/page-context.tsx index ec2eb58a7..4fd22460b 100644 --- a/src/app/pages/press/page-context.tsx +++ b/src/app/pages/press/page-context.tsx @@ -7,7 +7,14 @@ type FeaturedInRollup = { image: string; }; -// This will need more filling in as other components move to TSX +export type TestimonialData = { + image: { + title: string; + file: string; + }, + testimonial: string; +}; + type PressPageData = { meta: object; title: string; @@ -55,6 +62,7 @@ type PressPageData = { author: string; }; }; + testimonials: [TestimonialData[]] }; function useContextValue() { diff --git a/src/app/pages/press/testimonials/testimonials.js b/src/app/pages/press/testimonials/testimonials.tsx similarity index 64% rename from src/app/pages/press/testimonials/testimonials.js rename to src/app/pages/press/testimonials/testimonials.tsx index f875089ab..5b9bdbb87 100644 --- a/src/app/pages/press/testimonials/testimonials.js +++ b/src/app/pages/press/testimonials/testimonials.tsx @@ -1,10 +1,13 @@ import React from 'react'; -import usePageContext from '../page-context'; +import usePageContext, {TestimonialData} from '../page-context'; import ClippedImage from '~/components/clipped-image/clipped-image'; import Carousel from '~/components/carousel/carousel'; +import {assertDefined} from '~/helpers/data'; import './testimonials.scss'; -function Card({data: {image, testimonial}}) { +function Card({data}: {data: TestimonialData}) { + const {image, testimonial} = data; + return (
@@ -18,14 +21,15 @@ function Card({data: {image, testimonial}}) { } export default function Testimonials() { - const {testimonials: [testimonials]} = usePageContext(); + const ctx = assertDefined(usePageContext()); + const {testimonials: [testimonials]} = ctx; return (

Making an impact

- {testimonials.map((c) => ( - + {testimonials.map((c: TestimonialData) => ( + ))}
diff --git a/src/app/pages/subjects/new/context.ts b/src/app/pages/subjects/new/context.ts index e63c05ec6..db1c029d3 100644 --- a/src/app/pages/subjects/new/context.ts +++ b/src/app/pages/subjects/new/context.ts @@ -26,6 +26,7 @@ type SubjectsPageData = { translations: Array<{value: LocaleEntry[]}>; books?: { bookState: string; + subjects: string[]; }[]; aboutBlurbs?: ReturnType; heading: string; diff --git a/src/app/pages/subjects/new/import-language-selector-section.js b/src/app/pages/subjects/new/import-language-selector-section.js new file mode 100644 index 000000000..79f610f90 --- /dev/null +++ b/src/app/pages/subjects/new/import-language-selector-section.js @@ -0,0 +1,3 @@ +import LanguageSelectorSection from './language-selector-section'; + +export default LanguageSelectorSection; diff --git a/src/app/pages/subjects/new/language-selector-section.js b/src/app/pages/subjects/new/language-selector-section.tsx similarity index 82% rename from src/app/pages/subjects/new/language-selector-section.js rename to src/app/pages/subjects/new/language-selector-section.tsx index ba2ff4e8e..e3c2007f9 100644 --- a/src/app/pages/subjects/new/language-selector-section.js +++ b/src/app/pages/subjects/new/language-selector-section.tsx @@ -2,7 +2,7 @@ import React from 'react'; import LanguageSelector from '~/components/language-selector/language-selector'; import {FormattedMessage} from 'react-intl'; -export default function LanguageSelectorSection(props) { +export default function LanguageSelectorSection(props: Omit[0], 'LeadIn'>) { return (
diff --git a/src/app/pages/subjects/new/subjects.tsx b/src/app/pages/subjects/new/subjects.tsx index 60df2e39b..81b1844a0 100644 --- a/src/app/pages/subjects/new/subjects.tsx +++ b/src/app/pages/subjects/new/subjects.tsx @@ -9,7 +9,7 @@ import {AllSubjectsAboutOpenStax} from './about-openstax'; import LoadSubject from './specific/specific'; import './subjects.scss'; -const importLanguageSelector = () => import('./language-selector-section.js'); +const importLanguageSelector = () => import('./import-language-selector-section.js'); const importSubjectsListing = () => import('./import-subjects-listing.js'); const importTutorAd = () => import('./import-tutor-ad.js'); const importInfoBoxes = () => import('./import-info-boxes.js'); diff --git a/src/app/pages/subjects/new/use-categorized-books.js b/src/app/pages/subjects/new/use-categorized-books.js deleted file mode 100644 index 8099169ff..000000000 --- a/src/app/pages/subjects/new/use-categorized-books.js +++ /dev/null @@ -1,30 +0,0 @@ -import useSubjectsContext from './context'; -import useSubjectCategoryContext from '~/contexts/subject-category'; - -export default function useCategorizedBooks() { - const {books} = useSubjectsContext(); - const categories = useSubjectCategoryContext(); - const result = {}; - const addLabels = () => { - for (const category of categories) { - if (result[category.cms]) { - result[category.cms].label = category.html; - } - } - }; - - for (const book of books) { - book.subjects.forEach((cmsCategory) => { - if (!(cmsCategory in result)) { - result[cmsCategory] = []; - } - if (!result[cmsCategory].includes(book)) { - result[cmsCategory].push(book); - } - }); - } - - addLabels(); - - return result; -} diff --git a/test/src/pages/partners/info-request-form.test.tsx b/test/src/pages/partners/info-request-form.test.tsx index 9ecabe98a..ed2c26cf2 100644 --- a/test/src/pages/partners/info-request-form.test.tsx +++ b/test/src/pages/partners/info-request-form.test.tsx @@ -7,6 +7,11 @@ import * as SFBC from '~/components/multiselect/book-tags/sf-book-context'; import sfBooks from '~/../../test/src/data/sf-all-books'; import InfoRequestForm from '~/pages/partners/partner-details/info-request-form/info-request-form'; import {SearchContextProvider} from '~/pages/partners/search-context'; +import { + useAcceptValue, + useSetValueFromTarget, + useDoSubmit +} from '~/pages/partners/partner-details/info-request-form/school-selector'; async function renderForm() { render( @@ -57,19 +62,8 @@ describe('partners/info-rquest-form', () => { await user.click(screen.getAllByRole('radio', {name: 'No'})[1]); await user.click(screen.getByRole('button', {name: 'Next'})); - - // -- The book selector is not working right in tests, so all of this - // -- can't be tested until that gets figured out. - // const schoolSelector = await screen.findByRole('textbox', {name: 'School'}); - // const roleSelector = screen.getByRole('combobox'); - // const studentField = screen.getByRole('spinbutton'); - - // await user.click(roleSelector); - // await user.click(screen.getByRole('option', {name: 'Other'})); - // await user.type(schoolSelector, 'Ric'); - // await user.type(studentField, '12'); - - // await user.click(screen.getByRole('button', {name: 'Submit', hidden: true})); + // Going past the first page is unreliable in testing, so the pieces + // are tested separately }); it('does a partner type beginning with a vowel', () => { mockPC.mockReturnValue(mockPC2); @@ -77,3 +71,48 @@ describe('partners/info-rquest-form', () => { screen.getByText('is an abnormal', {exact: false}); }); }); + +describe('school-selector utilities', () => { + it('useAcceptValue sets a value', () => { + const setValue = jest.fn(); + + function Component() { + const accept = useAcceptValue(setValue); + + accept({value: 'a value'}); + return null; + } + + render(); + expect(setValue).toHaveBeenCalledWith('a value'); + }); + it('useSetValueFromTarget sets a value', () => { + const setValue = jest.fn(); + + function Component() { + const accept = useSetValueFromTarget(setValue); + + accept({target: {value: 'a value'}} as unknown as React.ChangeEvent); + return null; + } + + render(); + expect(setValue).toHaveBeenCalledWith('a value'); + }); + it('useDoSubmit calls form.submit and afterSubmit', () => { + const form = {submit: jest.fn()} as unknown as HTMLFormElement; + const afterSubmit = jest.fn(); + + function Component() { + const doSubmit = useDoSubmit(afterSubmit); + + doSubmit(form); + + return null; + } + + render(); + expect(form.submit).toHaveBeenCalled(); + expect(afterSubmit).toHaveBeenCalled(); + }); +});