Skip to content

Commit

Permalink
feat: new worksheet: numbers to words
Browse files Browse the repository at this point in the history
  • Loading branch information
asartalo committed Mar 5, 2022
1 parent 75a392f commit f63c59f
Show file tree
Hide file tree
Showing 24 changed files with 666 additions and 179 deletions.
1 change: 1 addition & 0 deletions cypress/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ declare namespace Cypress {
visitAdditionFillTheBlanks(): Chainable<AUTWindow>;
visitWorksheetPatterns(): Chainable<AUTWindow>;
visitWorksheetPlaceValues(): Chainable<AUTWindow>;
visitWorksheetNumbersToWords(): Chainable<AUTWindow>;
}

interface Chainable<Subject> {
Expand Down
33 changes: 21 additions & 12 deletions cypress/integration/navigation.spec.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
function goBackHome() {
cy.findByRole('banner').within(() => {
cy.findByText('Printables').click();
});
}

it('can visit all subpages', () => {
cy.visitHome();
cy.contains('Printables');
cy.findByRole('link', { name: /calendar/i }).click();
cy.findByRole('button', { name: /print calendar/i });

// Back home
cy.findByRole('banner').within(() => {
cy.findByText('Printables').click();
});
goBackHome();

cy.contains(/Printable Materials for Education/i);

// Addition Worksheets
Expand All @@ -19,10 +23,7 @@ it('can visit all subpages', () => {
cy.findByRole('heading', { name: /addition.+fill.+blank/i });
});

// Back home
cy.findByRole('banner').within(() => {
cy.findByText('Printables').click();
});
goBackHome();

// Pattern Worksheets
cy.findByRole('list', { name: /worksheets/i }).within(() => {
Expand All @@ -33,10 +34,7 @@ it('can visit all subpages', () => {
cy.findByRole('heading', { name: /patterns/i });
});

// Back home
cy.findByRole('banner').within(() => {
cy.findByText('Printables').click();
});
goBackHome();

// Place Value Worksheets
cy.findByRole('list', { name: /worksheets/i }).within(() => {
Expand All @@ -46,4 +44,15 @@ it('can visit all subpages', () => {
cy.withinCustomizeForm(() => {
cy.findByRole('heading', { name: /place values/i });
});

goBackHome();

// Numbers to Words
cy.findByRole('list', { name: /worksheets/i }).within(() => {
cy.findByRole('link', { name: /numbers to words/i }).click();
});

cy.withinCustomizeForm(() => {
cy.findByRole('heading', { name: /numbers to words/i });
});
});
15 changes: 15 additions & 0 deletions cypress/integration/worksheet_numbers_to_words.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
it('can create numbers to words worksheet', () => {
cy.visitWorksheetNumbersToWords();

cy.findByLabelText(/number of problems/i).clearType('7');
cy.setNumberRange('number-range-slider', 10, 99);
cy.withinPreview(() => {
cy.findByRole('list', { name: 'Problems' }).within((subject) => {
cy.wrap(subject).findAllByRole('listitem')
.should('have.length', 7)
.each(($li) => {
cy.wrap($li).contains(/[a-z\-\s]+:\s+_+/);
});
});
});
});
1 change: 1 addition & 0 deletions cypress/support/commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ const paths = {
additionFillTheBlanks: '/addition-fill-the-blanks',
worksheetPatterns: '/worksheet-patterns',
worksheetPlaceValues: '/worksheet-place-values',
worksheetNumbersToWords: '/worksheet-numbers-to-words',
};

const pathNames = Object.keys(paths);
Expand Down
2 changes: 2 additions & 0 deletions src/components/AppWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { createTheme, makeStyles, MuiThemeProvider } from '@material-ui/core';
import BaseStyle from './BaseStyle';
import PrintablesAppBar from './PrintablesAppBar';
import ScrollToTop from './ScrollToTop';
import NumbersToWordsPage from '../pages/numbersToWords/NumbersToWordsPage';

const MainPage = lazy(() => import('../pages/main/MainPage'));
const CalendarPage = lazy(() => import('../pages/calendar/CalendarPage'));
Expand Down Expand Up @@ -48,6 +49,7 @@ function AppWrapper(): JSX.Element {
<Route path="/addition-fill-the-blanks" element={<AdditionFillTheBlanksPage />} />
<Route path="/worksheet-patterns" element={<PatternsPage />} />
<Route path="/worksheet-place-values" element={<PlaceValuesPage />} />
<Route path="/worksheet-numbers-to-words" element={<NumbersToWordsPage />} />
</Routes>
</main>
</MuiThemeProvider>
Expand Down
17 changes: 15 additions & 2 deletions src/components/Blank.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,24 @@ const styles = makeStyles(() => ({
minWidth: '1.6em', // 32px
textAlign: 'center',

'&.problem-blank-wide': {
minWidth: '2.6em',
paddingLeft: '0.6em',
paddingRight: '0.6em',
},

'& > .underline': {
color: 'transparent',
},
},
}));

type BlankWidth = 'short' | 'wide';

export interface BlankProps {
answer: string | number | Stringable;
showAnswer: boolean;
width?: BlankWidth;
}

const underlines = (length: number): string => {
Expand All @@ -31,10 +40,10 @@ const underlines = (length: number): string => {
return str;
};

function Blank({ answer, showAnswer }: BlankProps): JSX.Element {
function Blank({ answer, showAnswer, width }: BlankProps): JSX.Element {
const classes = styles();
return (
<span className={`problem-blank ${classes.problemBlank}`}>
<span className={`problem-blank problem-blank-${width ?? 'short'} ${classes.problemBlank}`}>
{
showAnswer
? answer
Expand All @@ -48,4 +57,8 @@ function Blank({ answer, showAnswer }: BlankProps): JSX.Element {
);
}

Blank.defaultProps = {
width: 'short',
};

export default Blank;
57 changes: 40 additions & 17 deletions src/components/MultiPaperPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,22 @@ export type Builder<T> = (item: T, index: number, array: T[] | undefined) => JSX

export type Props = Record<string, unknown>;
export type PropsCallback = (props: Props, options: PropsCallbackOptions) => Props;

type WrapperWithPropsCallback = ElementType & {
propsCallback: PropsCallback;
};

// eslint-disable-next-line @typescript-eslint/no-explicit-any
function isWrapperWithPropsCallback(obj: any): obj is WrapperWithPropsCallback {
return typeof obj === 'function'
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
&& obj.propsCallback !== undefined
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
&& typeof obj.propsCallback === 'function';
}

interface WrapperBuilder<T> {
wrapper?: ElementType | null;
wrapper?: WrapperWithPropsCallback | ElementType | null;
wrapperProps?: Props;
wrapperPropsCallback?: PropsCallback;
data: T[];
Expand All @@ -38,12 +52,21 @@ function wrappedContent<T>({
instanceIndex, memberIndex,
}: WrapperBuilderArgs<T>): ReactNode {
if (wrapper !== null) {
let propsCallback = wrapperPropsCallback;
if (isWrapperWithPropsCallback(wrapper)) {
propsCallback = (props, options) =>
// eslint-disable-next-line implicit-arrow-linebreak
wrapperPropsCallback(
wrapper.propsCallback(props, options),
options,
);
}
return createElement(
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
wrapper!,
{
...wrapperPropsCallback(
wrapperProps || {},
...propsCallback(
wrapperProps ?? {},
{ instanceIndex, memberIndex },
),
},
Expand Down Expand Up @@ -141,7 +164,7 @@ function MultiPaperPage<T>({
}
setAttemptsTofix(attemptsToFix + 1);

// eslint-disable-next-line react-hooks/exhaustive-deps
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [dataPages, options]);

let count = 0;
Expand All @@ -153,23 +176,23 @@ function MultiPaperPage<T>({
const rendered = (
<PaperPage
pageId={`${index + 1}`}
// eslint-disable-next-line react/no-array-index-key
// eslint-disable-next-line react/no-array-index-key
key={`page-${index}`}
ready={isReady}
>
{ index === 0 ? header : null }
{index === 0 ? header : null}
{
wrappedContent<T>({
wrapper,
wrapperProps,
wrapperPropsCallback: wrapperPropsCallback || passThrough,
data: dataPage,
renderItems: builder,
instanceIndex: index,
memberIndex: count,
})
}
{ index === dataPages.length - 1 ? footer : null }
wrappedContent<T>({
wrapper,
wrapperProps,
wrapperPropsCallback: wrapperPropsCallback || passThrough,
data: dataPage,
renderItems: builder,
instanceIndex: index,
memberIndex: count,
})
}
{index === dataPages.length - 1 ? footer : null}
</PaperPage>
);
count += dataPage.length;
Expand Down
67 changes: 67 additions & 0 deletions src/components/ProblemList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import makeStyles from '@material-ui/core/styles/makeStyles';
import React, { CSSProperties, ReactNode } from 'react';
import { Props, PropsCallback } from './MultiPaperPage';

interface ProblemListProps {
children: ReactNode;
className?: string;
columns?: number;
style?: CSSProperties;
label?: string;
}

const styles = makeStyles(() => ({
// All em units equivalent are based on a 20px font size base
list: {
margin: '5mm 0 0 0',
padding: 0,
columnCount: 2,
columnWidth: 'auto',
counterReset: 'problem',

'& > li': {
counterIncrement: 'problem',
},

'& > li::marker': {
content: 'counter(problem) "."',
},
},
}));

function ProblemList({
children, className, columns, style, label,
}: ProblemListProps): JSX.Element {
const classes = styles();
const cols = columns ?? 1;
const paddedClassName = className ? ` ${className}` : '';
const styleUsed = { ...style, columns: cols };

return (
<ol
className={`problems ${classes.list}${paddedClassName}`}
style={styleUsed}
aria-label={label ?? 'Problems'}
>
{children}
</ol>
);
}

const propsCallback: PropsCallback = (inputProps: Props, { memberIndex }) => ({
...inputProps,
style: {
counterReset: `problem ${memberIndex}`,
},
});

ProblemList.propsCallback = propsCallback;

ProblemList.defaultProps = {
className: null,
columns: 1,
style: null,
label: null,
};

export default ProblemList;
51 changes: 51 additions & 0 deletions src/components/ProblemListItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { makeStyles } from '@material-ui/core';
import React, { ReactNode } from 'react';

interface ProblemListItemProps {
children: ReactNode;
label?: string;
fontSize?: number;
className?: string;
}

const defaultFontSize = 20;
const defaultLabel = 'Problem';

const styles = makeStyles(() => ({
// All em units equivalent are based on a 20px font size base
li: {
padding: '1.15em 0 1.15em 1.15em', // '6mm 0 6mm 6mm', // 23px
marginLeft: '1.9em', // '10mm', // 38px
'-webkit-column-break-inside': 'avoid',
pabeBreakInside: 'avoid',
breakInside: 'avoid',

'&::marker': {
fontSize: '0.8em', // 16px
},
},
}));

function ProblemListItem({
children, label, fontSize, className,
}: ProblemListItemProps): JSX.Element {
const classes = styles();
const paddedClassName = className ? ` ${className}` : '';
return (
<li
className={`${classes.li}${paddedClassName}`}
aria-label={label ?? defaultLabel}
style={{ fontSize: `${fontSize ?? defaultFontSize}px` }}
>
{children}
</li>
);
}

ProblemListItem.defaultProps = {
fontSize: defaultFontSize,
className: '',
label: defaultLabel,
};

export default ProblemListItem;
Loading

0 comments on commit f63c59f

Please sign in to comment.