Skip to content

Commit

Permalink
[BMD] Update voter-facing screen layouts to make room for language pi…
Browse files Browse the repository at this point in the history
…cker (#4589)
  • Loading branch information
kofi-q authored Feb 8, 2024
1 parent 5bb53e5 commit e84e2da
Show file tree
Hide file tree
Showing 23 changed files with 490 additions and 298 deletions.
12 changes: 10 additions & 2 deletions apps/mark-scan/frontend/src/components/centered_page_layout.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';

import { Font, H1, Main, ReadOnLoad, Screen } from '@votingworks/ui';
import { ButtonFooter } from '@votingworks/mark-flow-ui';
import { VoterScreen, ButtonFooter } from '@votingworks/mark-flow-ui';

export interface CenteredPageLayoutProps {
buttons?: React.ReactNode;
Expand All @@ -22,10 +22,18 @@ export function CenteredPageLayout(
</Font>
);

if (voterFacing) {
return (
<VoterScreen actionButtons={buttons} centerContent padded>
<ReadOnLoad>{mainContent}</ReadOnLoad>
</VoterScreen>
);
}

return (
<Screen>
<Main padded centerChild>
{voterFacing ? <ReadOnLoad>{mainContent}</ReadOnLoad> : mainContent}
{mainContent}
</Main>
{buttons && <ButtonFooter>{buttons}</ButtonFooter>}
</Screen>
Expand Down
15 changes: 10 additions & 5 deletions apps/mark-scan/frontend/src/lib/assistive_technology.test.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { MemoryHardware, ALL_PRECINCTS_SELECTION } from '@votingworks/utils';
import { electionGeneralDefinition } from '@votingworks/fixtures';
import userEvent from '@testing-library/user-event';
import { render, screen } from '../../test/react_testing_library';
import { render, screen, waitFor } from '../../test/react_testing_library';

import { App } from '../app';
import { advanceTimersAndPromises } from '../../test/helpers/timers';
Expand Down Expand Up @@ -66,11 +66,16 @@ it('accessible controller handling works', async () => {
expect(getActiveElement()).toHaveTextContent(contest0candidate1.name);
userEvent.keyboard('[ArrowUp]');
expect(getActiveElement()).toHaveTextContent(contest0candidate0.name);

// test the edge case of rolling over
userEvent.keyboard('[ArrowUp]');
expect(document.activeElement!.textContent).toEqual('Next');
userEvent.keyboard('[ArrowDown]');
expect(getActiveElement()).toHaveTextContent(contest0candidate0.name);
await waitFor(() => {
userEvent.keyboard('[ArrowUp]');
expect(getActiveElement()).toHaveTextContent(contest0candidate1.name);
});
await waitFor(() => {
userEvent.keyboard('[ArrowDown]');
expect(getActiveElement()).toHaveTextContent(contest0candidate0.name);
});

userEvent.keyboard('[ArrowRight]');
await advanceTimersAndPromises();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,11 @@ import {
P,
appStrings,
} from '@votingworks/ui';
import { DisplaySettingsButton } from '@votingworks/mark-flow-ui';
import { CenteredPageLayout } from '../components/centered_page_layout';

export function BallotSuccessfullyCastPage(): JSX.Element {
const settingsButton = <DisplaySettingsButton />;
return (
<CenteredPageLayout voterFacing buttons={settingsButton}>
<CenteredPageLayout voterFacing>
<FullScreenIconWrapper>
<Icons.Done color="success" />
</FullScreenIconWrapper>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,6 @@
import {
Main,
Screen,
H1,
P,
Button,
Icons,
ReadOnLoad,
appStrings,
} from '@votingworks/ui';
import { ButtonFooter } from '@votingworks/mark-flow-ui';
import React from 'react';
import { H1, P, Button, Icons, ReadOnLoad, appStrings } from '@votingworks/ui';
import { VoterScreen } from '@votingworks/mark-flow-ui';
import { PortraitStepInnerContainer } from './portrait_step_inner_container';

interface Props {
Expand All @@ -21,24 +13,26 @@ export function ConfirmExitPatDeviceIdentificationPage({
onPressContinue,
}: Props): JSX.Element {
return (
<Screen>
<Main centerChild>
<PortraitStepInnerContainer>
<ReadOnLoad>
<Icons.Done color="success" />
<H1>{appStrings.titleBmdPatCalibrationConfirmExitScreen()}</H1>
<P>{appStrings.instructionsBmdPatCalibrationConfirmExitScreen()}</P>
</ReadOnLoad>
</PortraitStepInnerContainer>
</Main>
<ButtonFooter>
<Button icon="Previous" onPress={onPressBack}>
{appStrings.buttonBack()}
</Button>
<Button variant="primary" rightIcon="Next" onPress={onPressContinue}>
{appStrings.buttonContinueVoting()}
</Button>
</ButtonFooter>
</Screen>
<VoterScreen
centerContent
actionButtons={
<React.Fragment>
<Button icon="Previous" onPress={onPressBack}>
{appStrings.buttonBack()}
</Button>
<Button variant="primary" rightIcon="Next" onPress={onPressContinue}>
{appStrings.buttonContinueVoting()}
</Button>
</React.Fragment>
}
>
<PortraitStepInnerContainer>
<ReadOnLoad>
<Icons.Done color="success" />
<H1>{appStrings.titleBmdPatCalibrationConfirmExitScreen()}</H1>
<P>{appStrings.instructionsBmdPatCalibrationConfirmExitScreen()}</P>
</ReadOnLoad>
</PortraitStepInnerContainer>
</VoterScreen>
);
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import {
Main,
Screen,
P,
Font,
Button,
ReadOnLoad as BaseReadOnLoad,
appStrings,
} from '@votingworks/ui';
import { useCallback, useState, useEffect } from 'react';
import { ButtonFooter } from '@votingworks/mark-flow-ui';
import { VoterScreen } from '@votingworks/mark-flow-ui';
import styled from 'styled-components';
import {
DiagnosticScreenHeader,
Expand Down Expand Up @@ -78,26 +76,26 @@ export function PatDeviceIdentificationPage({
};

return (
<Screen>
<Main centerChild>
<ReadOnLoad>
<DiagnosticScreenHeader>
<P>
<Font weight="bold">
{appStrings.titleBmdPatCalibrationIdentificationPage()}
</Font>
<br />
{statusStrings[currentStepId]}
</P>
</DiagnosticScreenHeader>
<StepContainer fullWidth>{steps[currentStepId]}</StepContainer>
</ReadOnLoad>
</Main>
<ButtonFooter>
<VoterScreen
actionButtons={
<Button onPress={onExitCalibration}>
{appStrings.buttonBmdSkipPatCalibration()}
</Button>
</ButtonFooter>
</Screen>
}
centerContent
>
<ReadOnLoad>
<DiagnosticScreenHeader>
<P>
<Font weight="bold">
{appStrings.titleBmdPatCalibrationIdentificationPage()}
</Font>
<br />
{statusStrings[currentStepId]}
</P>
</DiagnosticScreenHeader>
<StepContainer fullWidth>{steps[currentStepId]}</StepContainer>
</ReadOnLoad>
</VoterScreen>
);
}
80 changes: 36 additions & 44 deletions apps/mark-scan/frontend/src/pages/validate_ballot_page.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,18 @@
/* istanbul ignore file - placeholder component that will change */
import { useContext } from 'react';
import React from 'react';

import styled from 'styled-components';
import {
Screen,
H1,
WithScrollButtons,
Main,
Button,
appStrings,
AudioOnly,
ReadOnLoad,
} from '@votingworks/ui';

import { assert } from '@votingworks/basics';
import {
ButtonFooter,
DisplaySettingsButton,
Review,
} from '@votingworks/mark-flow-ui';
import { VoterScreen, Review } from '@votingworks/mark-flow-ui';
import {
getElectionDefinition,
getInterpretation,
Expand All @@ -36,15 +30,13 @@ export function ValidateBallotPage(): JSX.Element | null {
const getInterpretationQuery = getInterpretation.useQuery();
const getElectionDefinitionQuery = getElectionDefinition.useQuery();
// We use the contest data stored in BallotContext but vote data from the interpreted ballot
const { contests, precinctId, resetBallot } = useContext(BallotContext);
const { contests, precinctId, resetBallot } = React.useContext(BallotContext);

assert(
typeof precinctId !== 'undefined',
'precinctId is required to render ValidateBallotPage'
);

const settingsButton = <DisplaySettingsButton />;

const invalidateBallotMutation = invalidateBallot.useMutation();
const validateBallotMutation = validateBallot.useMutation();
function invalidateBallotCallback() {
Expand Down Expand Up @@ -76,38 +68,38 @@ export function ValidateBallotPage(): JSX.Element | null {
const { votes } = interpretation;

return (
<Screen>
<Main flexColumn>
<ContentHeader>
<H1>{appStrings.titleBmdReviewScreen()}</H1>
<AudioOnly>
{appStrings.instructionsBmdReviewPageNavigation()}{' '}
{appStrings.instructionsBmdScanReviewConfirmation()}
</AudioOnly>
</ContentHeader>
<WithScrollButtons>
<Review
election={electionDefinition.election}
contests={contests}
precinctId={precinctId}
votes={votes}
selectionsAreEditable={false}
/>
</WithScrollButtons>
</Main>
<ButtonFooter>
{settingsButton}
<Button
variant="danger"
icon="Danger"
onPress={invalidateBallotCallback}
>
{appStrings.buttonBallotIsIncorrect()}
</Button>
<Button onPress={validateBallotCallback}>
{appStrings.buttonBallotIsCorrect()}
</Button>
</ButtonFooter>
</Screen>
<VoterScreen
actionButtons={
<React.Fragment>
<Button
variant="danger"
icon="Danger"
onPress={invalidateBallotCallback}
>
{appStrings.buttonBallotIsIncorrect()}
</Button>
<Button onPress={validateBallotCallback}>
{appStrings.buttonBallotIsCorrect()}
</Button>
</React.Fragment>
}
>
<ContentHeader>
<H1>{appStrings.titleBmdReviewScreen()}</H1>
<AudioOnly>
{appStrings.instructionsBmdReviewPageNavigation()}{' '}
{appStrings.instructionsBmdScanReviewConfirmation()}
</AudioOnly>
</ContentHeader>
<WithScrollButtons>
<Review
election={electionDefinition.election}
contests={contests}
precinctId={precinctId}
votes={votes}
selectionsAreEditable={false}
/>
</WithScrollButtons>
</VoterScreen>
);
}
15 changes: 11 additions & 4 deletions apps/mark/frontend/src/lib/gamepad.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
fireEvent,
render,
screen,
waitFor,
} from '../../test/react_testing_library';
import { App } from '../app';

Expand Down Expand Up @@ -87,10 +88,16 @@ it('gamepad controls work', async () => {
expect(getActiveElement()).toHaveTextContent(contest0candidate0.name);

// test the edge case of rolling over
handleGamepadButtonDown('DPadUp');
expect(document.activeElement!.textContent).toEqual('Next');
handleGamepadButtonDown('DPadDown');
expect(getActiveElement()).toHaveTextContent(contest0candidate0.name);

await waitFor(() => {
handleGamepadButtonDown('DPadUp');
expect(getActiveElement()).toHaveTextContent(contest0candidate1.name);
});

await waitFor(() => {
handleGamepadButtonDown('DPadDown');
expect(getActiveElement()).toHaveTextContent(contest0candidate0.name);
});

handleGamepadButtonDown('DPadRight');
await advanceTimersAndPromises();
Expand Down
8 changes: 4 additions & 4 deletions apps/mark/integration-testing/e2e/scroll_buttons.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,18 +75,18 @@ test('configure, open polls, and test contest scroll buttons', async ({
})
.click();

await page.getByText(/contest number: 1/i).waitFor();
await page
.getByText(/contest number: 1/i)
.first() // Rendered multiple times as visible and audio-only elements.
.waitFor();
await page.getByRole('button', { name: /next/i }).click();
await page.getByText(/contest number: 2/i).waitFor();
await page.getByRole('button', { name: /next/i }).click();
await page.getByText(/contest number: 3/i).waitFor();

await expect(page.getByText('Brad Plunkard')).toBeInViewport();

expect(await findMoreButtons(page)).toHaveLength(0);

await page.getByRole('button', { name: /next/i }).click();
await page.getByText(/contest number: 4/i).waitFor();

// first candidate in the list should be visible
await expect(page.getByText('Charlene Franz')).toBeInViewport();
Expand Down
Loading

0 comments on commit e84e2da

Please sign in to comment.