Skip to content

Commit

Permalink
feat: worksheet subtraction fill in the blanks
Browse files Browse the repository at this point in the history
Closes #76.
  • Loading branch information
asartalo committed Apr 30, 2022
1 parent db06c72 commit 0b7ca25
Show file tree
Hide file tree
Showing 20 changed files with 406 additions and 70 deletions.
1 change: 1 addition & 0 deletions cypress/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ declare namespace Cypress {
visitWorksheetAdditionSubtraction(): Chainable<AUTWindow>;
visitWorksheetVerticalAddition(): Chainable<AUTWindow>;
visitWorksheetSubtractionWithFigures(): Chainable<AUTWindow>;
visitWorksheetSubtractionFillInTheBlanks(): Chainable<AUTWindow>;
}

interface Chainable<Subject> {
Expand Down
8 changes: 4 additions & 4 deletions cypress/integration/addition_fill_the_blanks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,22 @@ it('can create fill in the blanks addition worksheets', () => {
cy.withinPreview(() => {
cy.contains('2 + 3 = ');
});
cy.findByLabelText('Blank').select('Addends');
cy.findByLabelText(/blank position/i).select('Addends');
cy.withinPreview(() => {
cy.contains(/_ \+ [23] = [56]/);
});

cy.findByLabelText('Blank').select('Random');
cy.findByLabelText(/blank position/i).select('Random');
cy.withinPreview(() => {
cy.contains(/_ \+ [23] = [56]/);
cy.contains(/[23] \+ _+ = [56]/);
cy.contains(/[23] \+ [23] = _/);
});

cy.findByLabelText('Problem Generation').select('Custom Addends');
cy.findByLabelText(/problem.+generation/i).select('Custom Addends');
cy.setNumberRange(/addend a/i, 2, 3);
cy.setNumberRange(/addend b/i, 4, 5);
cy.findByLabelText('Blank').select('Sum');
cy.findByLabelText(/blank position/i).select('Sum');
cy.findByLabelText(/number of problems/i).clearType('50');
cy.withinPreview(() => {
cy.get('ol.problems').find('li').each(($li) => {
Expand Down
6 changes: 6 additions & 0 deletions cypress/integration/navigation.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ it('can visit all subpages', () => {

goBackHome();

// Subtraction
clickWorksheetLink(/subtraction.+fill.+blank/i);
cy.hasCustomizeFormHeading(/subtraction.+fill.+blank/i);

goBackHome();

// Pattern Worksheets
clickWorksheetLink(/patterns/i);
cy.hasCustomizeFormHeading(/patterns/i);
Expand Down
4 changes: 2 additions & 2 deletions cypress/integration/save_printable_settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ it('can save changes in settings', () => {
cy.visitAdditionFillTheBlanks();
cy.findByLabelText(/number of problems/i).clearType('40');
cy.setNumberRange(/number range/i, 1, 5);
cy.findByLabelText('Blank').select('Addends');
cy.findByLabelText(/blank.+position/i).select('Addends');

cy.visitCalendar();
cy.findByLabelText(/month/i).select('December');
Expand All @@ -14,7 +14,7 @@ it('can save changes in settings', () => {
cy.findByLabelText(/number of problems/i).invoke('val').should('equal', '40');
cy.findByLabelText(/from/i).invoke('val').should('equal', '1');
cy.findByLabelText(/^to/i).invoke('val').should('equal', '5');
cy.findByLabelText('Blank').invoke('val').should('equal', 'addends');
cy.findByLabelText(/blank.+position/i).invoke('val').should('equal', 'addends');

cy.visitCalendar();
cy.findByLabelText(/month/i).invoke('val').should('equal', '11');
Expand Down
37 changes: 37 additions & 0 deletions cypress/integration/worksheet_subtraction_fill_in_the_blanks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
it('can create subtraction fill in the blanks worksheets', () => {
cy.visitWorksheetSubtractionFillInTheBlanks();

cy.findByLabelText(/count/i).clearType('15');

cy.findByLabelText(/problem.+generation/i).should('have.value', 'minuend');
cy.findByLabelText(/blank position/i).select('Random');
cy.setNumberRange(/minuend/i, 3, 4);

cy.withinPreview(() => {
cy.contains(/[34]\s+-\s+[0-4]\s+=\s+_+/);
cy.contains(/_+\s+-\s+[0-4]\s+=\s+\d/);
});

cy.findByLabelText(/problem.+generation/i).select('Subtrahend and Difference');
cy.setNumberRange(/subtrahend/i, 3, 4);
cy.setNumberRange(/difference/i, 5, 6);
cy.findByLabelText(/count/i).clearType('28');
cy.findByLabelText(/columns/i).clearType('3');
cy.findByLabelText(/blank position/i).select('Difference');

cy.withinPreview(() => {
// Problems
cy.problemListItems()
.should('have.length', 28)
.each(($li) => {
cy.wrap($li).contains(/([8-9]|10)\s+-\s+[3-6]\s+=\s+_+/);
});

// Answers
cy.answerListItems()
.should('have.length', 28)
.each(($li) => {
cy.wrap($li).contains(/([8-9]|10)\s+-\s+[3-6]\s+=\s+\d/);
});
});
});
2 changes: 1 addition & 1 deletion cypress/integration/worksheet_subtraction_with_figures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ it('can create subtraction with figures worksheets', () => {
cy.answerListItems()
.should('have.length', 30)
.each(($li) => {
// TODO: Why does the following not work?
// TODO: Change it back when https://github.com/cypress-io/cypress/issues/21108 is fixed
// cy.wrap($li).contains(/[6-8]\s+-\s+[23]\s+=\s+[45]/);
cy.wrap($li).contains(/[6-8]\s+-\s+[23]\s+=\s+\d/);
});
Expand Down
1 change: 1 addition & 0 deletions cypress/support/commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ const paths = {
worksheetAdditionSubtraction: '/worksheet-addition-subtraction',
worksheetVerticalAddition: '/worksheet-vertical-addition',
worksheetSubtractionWithFigures: '/worksheet-subtraction-with-figures',
worksheetSubtractionFillInTheBlanks: '/worksheet-subtraction-fill-in-the-blanks',
};

const pathNames = Object.keys(paths);
Expand Down
7 changes: 6 additions & 1 deletion src/components/forms/NumberRangeSlider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,12 @@ function NumberRangeSlider(options: NumberRangeSliderProps): JSX.Element {
{label}
</Typography>

<Grid container spacing={2} alignItems="center" className={classes.inputGrid}>
<Grid
container
spacing={2}
alignItems="center"
className={classes.inputGrid}
>
<Grid item xs={6}>
<SmallNumberField
id={`${id}-from`}
Expand Down
3 changes: 3 additions & 0 deletions src/components/math/SubtractionSentenceBasic.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import Subtraction from '../../lib/math/Subtraction';
import blankOrNumberGenerator from './blankOrNumberGenerator';

export type SubtractionBlankPosition = 'difference' | 'minuend' | 'subtrahend';
export const subtractionBlankPositions: SubtractionBlankPosition[] = [
'difference', 'minuend', 'subtrahend',
];

interface SubtractionBasicProps {
subtraction: Subtraction;
Expand Down
7 changes: 6 additions & 1 deletion src/lib/linkMap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import { SectionLinks } from './LinkAndLoaderInterface';
const HomePage = lazy(() => import('../pages/main/HomePage'));
const CalendarPage = lazy(() => import('../pages/calendar/CalendarPage'));
const AdditionFillTheBlanksPage = lazy(() => import('../pages/additionFillTheBlanks/AdditionFillTheBlanksPage'));
const VerticalAdditionPage = lazy(() => import('../pages/verticalAddition/VerticalAdditionPage'));
const AdditionSubtractionPage = lazy(() => import('../pages/additionSubtraction/AdditionSubtractionPage'));
const SubtractionWithFiguresPage = lazy(() => import('../pages/subtractionWithFigures/SubtractionWithFiguresPage'));
const VerticalAdditionPage = lazy(() => import('../pages/verticalAddition/VerticalAdditionPage'));
const SubtractionFillInTheBlanksPage = lazy(() => import('../pages/subtractionFillInTheBlanks/SubtractionFillInTheBlanksPage'));
const PatternsPage = lazy(() => import('../pages/patterns/PatternsPage'));
const PlaceValuesPage = lazy(() => import('../pages/placeValues/PlaceValuesPage'));
const NumbersToWordsPage = lazy(() => import('../pages/numbersToWords/NumbersToWordsPage'));
Expand Down Expand Up @@ -55,6 +56,10 @@ export const mathLinks: SectionLinks = new Map([
text: 'Subtraction with Figures',
loader: SubtractionWithFiguresPage,
}],
['/worksheet-subtraction-fill-in-the-blanks', {
text: 'Subtraction: Fill in the Blanks',
loader: SubtractionFillInTheBlanksPage,
}],
['/worksheet-patterns', {
text: 'Patterns',
loader: PatternsPage,
Expand Down
54 changes: 54 additions & 0 deletions src/lib/math/generateSubtractionProblems.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { randomGenerator } from '../RandomNumberGenerator';
import tryByKey from '../tryByKey';
import Subtraction from './Subtraction';
import Range from '../Range';

export type ProblemGeneration = 'minuend' | 'subtrahend and difference';

export interface SubtractionProblemsProps {
count: number;
problemGeneration: ProblemGeneration;
minuend: Range;
subtrahend: Range;
difference: Range;
}

export function generateProblemsFromMinuend(minuendRange: Range, count: number): Subtraction[] {
const problems: Subtraction[] = [];
const limitedRetries = tryByKey();
for (let i = 0; problems.length < count; i++) {
const { from, to } = minuendRange;
const minuend = randomGenerator.integer(to, from);
const subtrahend = randomGenerator.integer(minuend, 0);
limitedRetries(`${minuend}:${subtrahend}`, () => {
problems.push(Subtraction.create({ minuend, subtrahend }));
});
}
return problems;
}

export function generateProblemsFromSubAndDiff(
subRange: Range,
diffRange: Range,
count: number,
): Subtraction[] {
const problems: Subtraction[] = [];
const limitedRetries = tryByKey();
for (let i = 0; problems.length < count; i++) {
const subtrahend = randomGenerator.integer(subRange.to, subRange.from);
const difference = randomGenerator.integer(diffRange.to, diffRange.from);
limitedRetries(`${subtrahend}:${difference}`, () => {
problems.push(Subtraction.create({ subtrahend, difference }));
});
}
return problems;
}

export default function generateSubtractionProblems({
count, problemGeneration, minuend, subtrahend, difference,
}: SubtractionProblemsProps): Array<Subtraction> {
if (problemGeneration === 'minuend') {
return generateProblemsFromMinuend(minuend, count);
}
return generateProblemsFromSubAndDiff(subtrahend, difference, count);
}
2 changes: 1 addition & 1 deletion src/pages/additionFillTheBlanks/CustomizeAftbForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ function CustomizeAftbForm({
}

<SelectField
label="Blank"
label="Blank Position"
name="blankStrategy"
value={data.blankStrategy}
onChange={changeBlankStrategy}
Expand Down
9 changes: 3 additions & 6 deletions src/pages/additionFillTheBlanks/PreviewAftb.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ import AdditionSentence, {
import AftbData, { BlankPositionStrategy } from './AftbData';
import WorksheetHeader from '../../components/WorksheetHeader';
import WorksheetFooter from '../../components/WorksheetFooter';
import { randomGenerator } from '../../lib/RandomNumberGenerator';
import PageTitle from '../../elements/PageTitle';
import ProblemList from '../../components/ProblemList';
import Addition from '../../lib/math/Addition';
import { AdditionBlankPosition } from '../../components/math/AdditionSentenceBasic';
import randomElement from '../../lib/randomElement';

interface PreviewAftbProps {
aftbData: AftbData;
Expand All @@ -19,13 +19,10 @@ interface PreviewAftbProps {
function blankTypeFromStrategy(blankStrategy: BlankPositionStrategy): AdditionBlankPosition {
switch (blankStrategy) {
case 'addends':
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
return blankTypesAddends[
randomGenerator.integer(blankTypesAddends.length - 1)
] as AdditionBlankPosition;
return randomElement(blankTypesAddends);

case 'random':
return blankTypes[randomGenerator.integer(blankTypes.length - 1)];
return randomElement(blankTypes);

default:
return 'sum';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import React from 'react';
import { Divider } from '@material-ui/core';
import CustomizeForm from '../../components/forms/CustomizeForm';
import SubtractionFillInTheBlanksData, {
BlankPosition, blankPositions, ProblemGeneration, problemGenerationOptions,
} from './SubtractionFillInTheBlanksData';
import NumberField from '../../components/forms/NumberField';
import numberOrEmpty from '../../lib/numberOrEmpty';
import SelectField from '../../components/forms/SelectField';
import stringMapToOptions from '../../components/forms/stringMapToOptions';
import NumberRangeSlider from '../../components/forms/NumberRangeSlider';
import FontSizeField from '../../components/forms/FontSizeField';

interface CustomizeSubtractionFillInTheBlanksFormProps {
onChange: (data: SubtractionFillInTheBlanksData) => void;
data: SubtractionFillInTheBlanksData;
}

function CustomizeSubtractionFillInTheBlanksForm({
data, onChange,
}: CustomizeSubtractionFillInTheBlanksFormProps): JSX.Element {
const generationFields = data.problemGeneration === 'minuend'
? (
<NumberRangeSlider
label="Minuend"
id="numberRangeMinuend"
from={data.minuend.from}
to={data.minuend.to}
magnitude={2}
onChange={(minuend) => onChange({ ...data, minuend })}
/>
)
: (
<>
<NumberRangeSlider
label="Subtrahend"
id="numberRangeSubtrahend"
from={data.subtrahend.from}
to={data.subtrahend.to}
magnitude={2}
onChange={(subtrahend) => onChange({ ...data, subtrahend })}
/>
<NumberRangeSlider
label="Difference"
id="numberRangeDifference"
from={data.difference.from}
to={data.difference.to}
magnitude={2}
onChange={(difference) => onChange({ ...data, difference })}
/>
</>
);
return (
<CustomizeForm name="Worksheet">
<NumberField
name="count"
label="Count"
value={numberOrEmpty(data.count)}
onChange={(count) => onChange({ ...data, count })}
/>
<SelectField
name="problemGeneration"
label="Problem Generation"
value={data.problemGeneration}
onChange={(value: unknown) => {
onChange({
...data,
problemGeneration: value as ProblemGeneration,
});
}}
>
{stringMapToOptions(problemGenerationOptions)}
</SelectField>

{generationFields}

<SelectField
name="blankPosition"
label="Blank Position"
value={data.blankPosition}
onChange={(value: unknown) => {
onChange({
...data,
blankPosition: value as BlankPosition,
});
}}
>
{stringMapToOptions(blankPositions)}
</SelectField>

<Divider variant="middle" />
<NumberField
name="columns"
label="Columns"
value={numberOrEmpty(data.columns)}
onChange={(columns) => onChange({ ...data, columns })}
/>
<FontSizeField
value={data.fontSize}
onChange={(fontSize) => onChange({ ...data, fontSize })}
/>
</CustomizeForm>
);
}

export default CustomizeSubtractionFillInTheBlanksForm;
Loading

0 comments on commit 0b7ca25

Please sign in to comment.