Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,61 @@ import LoaderPage from '~/components/jsx-helpers/loader-page';
import ClippedImage from '~/components/clipped-image/clipped-image';
import './foundation.scss';

type FunderData = {
funderName: string;
url?: string;
};

type ImageData = {
file: string;
};

type FoundationGroupData = {
groupTitle: string;
description: string;
funders: FunderData[];
image?: ImageData;
};

type FoundationPageData = {
bannerImage: {
meta: {
downloadUrl: string;
};
};
bannerHeading: string;
bannerDescription: string;
funderGroups: FoundationGroupData[];
disclaimer: string;
};

const slug = 'pages/supporters';

function Funder({data}) {
return (
data.url ?
<a href={data.url}>{data.funderName}</a> :
<span>{data.funderName}</span>
function Funder({data}: {data: FunderData}) {
return data.url ? (
<a href={data.url}>{data.funderName}</a>
) : (
<span>{data.funderName}</span>
);
}

function Funders({data}) {
function Funders({data}: {data: FunderData[]}) {
return (
<div className="funders">
{data.map((f, i) => <Funder key={i} data={f} />)}
{data.map((f, i) => (
<Funder key={i} data={f} />
))}
</div>
);
}

function FundersWithImage({data, image}) {
function FundersWithImage({
data,
image
}: {
data: FunderData[];
image: ImageData;
}) {
return (
<div className="funders-with-image">
<Funders data={data} />
Expand All @@ -31,21 +67,21 @@ function FundersWithImage({data, image}) {
);
}

function FoundationGroup({data}) {
function FoundationGroup({data}: {data: FoundationGroupData}) {
return (
<div className="funder-group">
<h2>{data.groupTitle}</h2>
<div className="description">{data.description}</div>
{
data.image?.file ?
<FundersWithImage data={data.funders} image={data.image} /> :
<Funders data={data.funders} />
}
{data.image?.file ? (
<FundersWithImage data={data.funders} image={data.image} />
) : (
<Funders data={data.funders} />
)}
</div>
);
}

function FoundationPage({data: model}) {
function FoundationPage({data: model}: {data: FoundationPageData}) {
return (
<React.Fragment>
<div className="banner">
Expand All @@ -57,7 +93,9 @@ function FoundationPage({data: model}) {
</div>
</div>
</div>
{model.funderGroups.map((g, i) => <FoundationGroup key={i} data={g} />)}
{model.funderGroups.map((g, i) => (
<FoundationGroup key={i} data={g} />
))}
<div className="disclaimer">{model.disclaimer}</div>
</React.Fragment>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,37 @@ import LoaderPage from '~/components/jsx-helpers/loader-page';
import sample from 'lodash/sample';
import './institutional-partnership-application.scss';

function TestimonialBlock({data}) {
const testimonials = [
type Testimonial = {
block: string;
name: string;
address1: string;
address2: string;
};

type TestimonialData = {
quote: string;
quoteAuthor: string;
quoteTitle: string;
quoteSchool: string;
applicationQuote: string;
applicationQuoteAuthor: string;
applicationQuoteTitle: string;
applicationQuoteSchool: string;
};

type ProgramSection = {
heading: string;
description: string;
};

type ApplicationPageData = {
headingYear: string;
heading: string;
programTabContent: [ProgramSection[]];
} & TestimonialData;

function TestimonialBlock({data}: {data: TestimonialData}) {
const testimonials: Testimonial[] = [
{
block: data.quote,
name: data.quoteAuthor,
Expand All @@ -25,12 +54,14 @@ function TestimonialBlock({data}) {
<div className="testimonial">
<div className="boxed">
<div className="testimonial-box">
<p className="text-block">{testimonial.block}</p>
<p className="text-block">{testimonial?.block}</p>
<div className="writer-info">
<p>
<strong>-{testimonial.name}</strong><br />
{testimonial.address1}<br />
{testimonial.address2}
<strong>-{testimonial?.name}</strong>
<br />
{testimonial?.address1}
<br />
{testimonial?.address2}
</p>
</div>
</div>
Expand All @@ -39,18 +70,20 @@ function TestimonialBlock({data}) {
);
}

function ProgramDetails({model}) {
function ProgramDetails({model}: {model: [ProgramSection[]]}) {
return (
model[0].map((section) =>
<div key={section.heading}>
<h2>{section.heading}</h2>
<RawHTML html={section.description} />
</div>
)
<>
{model[0].map((section) => (
<div key={section.heading}>
<h2>{section.heading}</h2>
<RawHTML html={section.description} />
</div>
))}
</>
);
}

function ApplicationPage({data}) {
function ApplicationPage({data}: {data: ApplicationPageData}) {
return (
<React.Fragment>
<div className="hero">
Expand All @@ -62,7 +95,7 @@ function ApplicationPage({data}) {
<div className="text-content">
<ProgramDetails model={data.programTabContent} />
</div>
<TestimonialBlock selectedLabel="Application" data={data} />
<TestimonialBlock data={data} />
</React.Fragment>
);
}
Expand All @@ -71,7 +104,8 @@ export default function ApplicationLoader() {
return (
<main className="institutional-page page">
<LoaderPage
slug="pages/institutional-partnership" Child={ApplicationPage}
slug="pages/institutional-partnership"
Child={ApplicationPage}
doDocumentSetup
/>
</main>
Expand Down
90 changes: 90 additions & 0 deletions test/src/pages/foundation.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import React from 'react';
import {render, screen} from '@testing-library/preact';
import {describe, it} from '@jest/globals';
import FoundationPage from '~/pages/foundation/foundation';
import MemoryRouter from '~/../../test/helpers/future-memory-router';

const mockFoundationData = {
bannerImage: {
meta: {
downloadUrl: 'https://example.com/banner.jpg'
}
},
bannerHeading: 'Our Supporters',
bannerDescription:
'<p>Thanks to generous funders who support our mission.</p>',
funderGroups: [
{
groupTitle: 'Major Funders',
description: 'Organizations that provide significant support',
funders: [
{
funderName: 'Test Foundation',
url: 'https://example.com'
},
{
funderName: 'Another Supporter'
}
]
},
{
groupTitle: 'Secondary Funders',
description: 'Organizations that provide support',
image: {file: '/path/to/image'},
funders: []
}
],
disclaimer: 'All funders listed have provided support to OpenStax.'
};

global.fetch = jest.fn().mockImplementation((args: [string]) => {
const payload = args.includes('pages/supporters') ? mockFoundationData : {};

return Promise.resolve({
ok: true,
json() {
return Promise.resolve(payload);
}
});
});

describe('foundation page', () => {
it('displays funder groups', async () => {
render(
<MemoryRouter initialEntries={['/foundation']}>
<FoundationPage />
</MemoryRouter>
);
await screen.findByText('Our Supporters');
await screen.findByText('Major Funders');
await screen.findByText(
'Organizations that provide significant support'
);
});

it('displays funders with and without links', async () => {
render(
<MemoryRouter initialEntries={['/foundation']}>
<FoundationPage />
</MemoryRouter>
);
const linkedFunder = await screen.findByRole('link', {
name: 'Test Foundation'
});

expect(linkedFunder.getAttribute('href')).toBe('https://example.com');

await screen.findByText('Another Supporter');
});

it('displays disclaimer', async () => {
render(
<MemoryRouter initialEntries={['/foundation']}>
<FoundationPage />
</MemoryRouter>
);
await screen.findByText(
'All funders listed have provided support to OpenStax.'
);
});
});
90 changes: 90 additions & 0 deletions test/src/pages/institutional-partnership-application.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import React from 'react';
import {render, screen} from '@testing-library/preact';
import '@testing-library/jest-dom';
import {describe, it} from '@jest/globals';
import ApplicationPage from '~/pages/institutional-partnership-application/institutional-partnership-application';
import MemoryRouter from '~/../../test/helpers/future-memory-router';

const mockApplicationData = {
headingYear: '2024',
heading: '<strong>Institutional Partnership Application</strong>',
programTabContent: [
[
{
heading: 'Program Overview',
description:
'<p>Learn about our institutional partnership program.</p>'
},
{
heading: 'Benefits',
description:
'<p>Discover the benefits of partnering with us.</p>'
}
]
],
quote: 'OpenStax has transformed our teaching approach.',
quoteAuthor: 'Dr. Jane Smith',
quoteTitle: 'Professor of Biology',
quoteSchool: 'State University',
applicationQuote: 'The application process was straightforward.',
applicationQuoteAuthor: 'Prof. John Doe',
applicationQuoteTitle: 'Department Chair',
applicationQuoteSchool: 'Community College'
};

global.fetch = jest.fn().mockImplementation((args: [string]) => {
const payload = args.includes('pages/institutional-partnership')
? mockApplicationData
: {};

return Promise.resolve({
ok: true,
json() {
return Promise.resolve(payload);
}
});
});

function Component() {
return (
<MemoryRouter
initialEntries={['/institutional-partnership-application']}
>
<ApplicationPage />
</MemoryRouter>
);
}

describe('institutional partnership application page', () => {
it('displays program sections', async () => {
render(<Component />);
await screen.findByText('Program Overview');
await screen.findByText('Benefits');
});

it('displays testimonial content', async () => {
render(<Component />);

// Since lodash sample() picks randomly, we need to check for either testimonial
// We'll check for elements that should always be present
const testimonialSection = await screen.findByRole('main');

expect(testimonialSection).toBeInTheDocument();

// The testimonial block should contain one of our test quotes
const testimonialContainer = await screen.findByText(
(content, element) => {
return element?.className === 'testimonial-box' || false;
}
);

expect(testimonialContainer).toBeInTheDocument();
});

it('has proper main container class', async () => {
render(<Component />);
const mainElement = await screen.findByRole('main');

expect(mainElement).toHaveClass('institutional-page', 'page');
});
});