+
{name && }
{children}
diff --git a/src/app/components/toggle/toggle-control-bar.js b/src/app/components/toggle/toggle-control-bar.js
index dbddac65b..232c1f05d 100644
--- a/src/app/components/toggle/toggle-control-bar.js
+++ b/src/app/components/toggle/toggle-control-bar.js
@@ -17,6 +17,7 @@ export default function ToggleControlBar({ Indicator, children }) {
[isOpen]
);
const focusRef = useRefToFocusAfterClose();
+ const listboxId = `lbid-${Math.floor(Math.random() * 1010101)}`;
React.useEffect(() => {
if (isOpen) {
@@ -41,8 +42,11 @@ export default function ToggleControlBar({ Indicator, children }) {
onClick={() => toggle()}
onKeyDown={onKeyDown}
ref={focusRef}
+ role="combobox"
+ aria-controls={listboxId}
+ aria-haspopup={listboxId}
>
-
{children}
+
{children}
);
diff --git a/src/app/components/vertical-list/vertical-list.js b/src/app/components/vertical-list/vertical-list.js
index cfc4cd215..1d0d4317e 100644
--- a/src/app/components/vertical-list/vertical-list.js
+++ b/src/app/components/vertical-list/vertical-list.js
@@ -38,6 +38,7 @@ function RenderInContext({items, RenderItem, vListRef, onSelect, onCancel}) {
{
items.map((item, i) =>
diff --git a/src/app/pages/contact/form.tsx b/src/app/pages/contact/form.tsx
index 0800f2e9a..38ce8b788 100644
--- a/src/app/pages/contact/form.tsx
+++ b/src/app/pages/contact/form.tsx
@@ -49,13 +49,10 @@ function LabeledInputWithInvalidMessage({
const [message, setMessage] = useState('');
useEffect(() => {
- const el = ref?.current?.querySelector
('[name]');
+ };
- if (!el) {
- return () => null; // Get assertDefined() instead
- }
const updateMessage = () => setMessage(el.validationMessage);
updateMessage();
diff --git a/test/src/pages/contact.test.tsx b/test/src/pages/contact.test.tsx
new file mode 100644
index 000000000..0ac58d57f
--- /dev/null
+++ b/test/src/pages/contact.test.tsx
@@ -0,0 +1,163 @@
+import React from 'react';
+import {fireEvent, render, screen, waitFor} from '@testing-library/preact';
+import {describe, it} from '@jest/globals';
+import userEvent from '@testing-library/user-event';
+import ContactPage from '~/pages/contact/contact';
+import Form from '~/pages/contact/form';
+import * as UPD from '~/helpers/use-page-data';
+import {MemoryRouter} from 'react-router-dom';
+import {SalesforceContextProvider} from '~/contexts/salesforce';
+import * as SFF from '~/components/salesforce-form/salesforce-form';
+
+const spyUsePageData = jest.spyOn(UPD, 'default');
+/* eslint-disable max-len */
+const pageData = {
+ id: 318,
+ meta: {
+ slug: 'contact',
+ seoTitle:
+ 'Let us know if you have any questions or comments about our content',
+ searchDescription:
+ 'Do you have a question or comment about OpenStax and want to let us know? Fill out our Contact Us form. We would love to hear your thoughts!',
+ type: 'pages.ContactUs',
+ detailUrl: 'https://dev.openstax.org/apps/cms/api/v2/pages/318/',
+ htmlUrl: 'https://dev.openstax.org/contact/',
+ showInMenus: false,
+ firstPublishedAt: '2016-06-23T10:25:21.881000-05:00',
+ aliasOf: null,
+ parent: {
+ id: 29,
+ meta: {
+ type: 'pages.HomePage',
+ detailUrl: 'https://dev.openstax.org/apps/cms/api/v2/pages/29/',
+ htmlUrl: 'https://dev.openstax.org/'
+ },
+ title: 'Openstax Homepage'
+ },
+ locale: 'en'
+ },
+ title: 'Contact Us',
+ tagline: 'If you have any questions or feedback, drop us a line!',
+ mailingHeader: 'Mailing Address',
+ mailingAddress:
+ 'OpenStax
Rice University
6100 Main Street MS-375
Houston, TX 77005
',
+ customerService:
+ 'Need help?
Visit our Support Center.
',
+ promoteImage: null,
+ slug: 'pages/contact'
+};
+
+function Component({path='/contact'}) {
+ return (
+
+
+
+
+
+ );
+}
+
+jest.spyOn(SFF, 'default').mockImplementation(MockSfForm);
+
+describe('contact page', () => {
+ const user = userEvent.setup();
+
+ it('returns null until data', () => {
+ spyUsePageData.mockReturnValue(undefined);
+ const {container} = render();
+
+ expect(container.innerHTML).toBe('');
+ });
+ it('displays the form', async () => {
+ spyUsePageData.mockReturnValue(pageData);
+ render();
+
+ const getLoadingText = () => screen.getByText('Loading...');
+
+ await waitFor(() => expect(getLoadingText).toThrow());
+ const inputs = screen.getAllByRole('textbox');
+
+ expect(inputs).toHaveLength(3);
+ // Fill out the form
+ await user.click(screen.getByRole('combobox'));
+ const polskaOption = screen.getByRole('option', {
+ name: 'OpenStax Polska'
+ });
+
+ await user.click(polskaOption as HTMLElement);
+ const [nameBox, emailBox, messageBox] = [
+ 'Your Name',
+ 'Your Email Address',
+ 'Your Message'
+ ].map((name) => screen.getByRole('textbox', {name}));
+ const submitButton = screen.getByRole('button', {name: 'Submit'});
+
+ fireEvent.change(nameBox, {target: {value: 'Username'}});
+ fireEvent.change(emailBox, {target: {value: 'somebody@example.com'}});
+ fireEvent.change(messageBox, {target: {value: 'message text'}});
+
+ await user.click(submitButton);
+ });
+ it('displays as embedded', async () => {
+ const setSubmitted = jest.fn();
+
+ spyUsePageData.mockReturnValue(pageData);
+ render(
+
+
+
+
+
+ );
+ const getLoadingText = () => screen.getByText('Loading...');
+
+ await waitFor(() => expect(getLoadingText).toThrow());
+ const [questionBox, topicBox] = screen.getAllByRole('combobox');
+
+ expect(questionBox.textContent).toBe('OpenStax Assignable');
+ await user.click(topicBox);
+ await user.click(screen.getByRole('option', {
+ name: 'Grade book'
+ }));
+
+ const [nameBox, emailBox, messageBox] = [
+ 'Your Name',
+ 'Your Email Address',
+ 'Your Message'
+ ].map((name) => screen.getByRole('textbox', {name}));
+ const submitButton = screen.getByRole('button', {name: 'Submit'});
+
+ fireEvent.change(nameBox, {target: {value: 'Username'}});
+ fireEvent.change(emailBox, {target: {value: 'somebody@example.com'}});
+ fireEvent.change(messageBox, {target: {value: 'message text'}});
+
+ await user.click(submitButton);
+ expect(setSubmitted).toHaveBeenCalledWith(true);
+ });
+});
+
+function MockSfForm({children, postTo, afterSubmit}: React.PropsWithChildren<{
+ postTo: string;
+ afterSubmit: () => void;
+}>) {
+ const onSubmit = React.useCallback(
+ (event: Pick) => {
+ event?.preventDefault();
+ afterSubmit();
+ },
+ [afterSubmit]
+ );
+
+ return (
+
+
+
+ );
+}