diff --git a/src/HanziWriter.ts b/src/HanziWriter.ts index 60d20ecb..1730164c 100644 --- a/src/HanziWriter.ts +++ b/src/HanziWriter.ts @@ -405,6 +405,12 @@ export default class HanziWriter { }); } + skipQuizStroke() { + if (this._quiz) { + this._quiz.nextStroke(); + } + } + cancelQuiz() { if (this._quiz) { this._quiz.cancel(); diff --git a/src/Quiz.ts b/src/Quiz.ts index 2c1ba36f..61695b06 100644 --- a/src/Quiz.ts +++ b/src/Quiz.ts @@ -127,8 +127,11 @@ export default class Quiz { } else { this._handleFailure(meta); - const { showHintAfterMisses, highlightColor, strokeHighlightSpeed } = - this._options!; + const { + showHintAfterMisses, + highlightColor, + strokeHighlightSpeed, + } = this._options!; if ( showHintAfterMisses !== false && @@ -178,13 +181,12 @@ export default class Quiz { }; } - _handleSuccess(meta: StrokeMatchResultMeta) { + nextStroke() { if (!this._options) return; const { strokes, symbol } = this._character; const { - onCorrectStroke, onComplete, highlightOnComplete, strokeFadeDuration, @@ -193,10 +195,6 @@ export default class Quiz { strokeHighlightDuration, } = this._options; - onCorrectStroke?.({ - ...this._getStrokeData({ isCorrect: true, meta }), - }); - let animation: GenericMutation[] = characterActions.showStroke( 'main', this._currentStrokeIndex, @@ -228,6 +226,18 @@ export default class Quiz { this._renderState.run(animation); } + _handleSuccess(meta: StrokeMatchResultMeta) { + if (!this._options) return; + + const { onCorrectStroke } = this._options; + + onCorrectStroke?.({ + ...this._getStrokeData({ isCorrect: true, meta }), + }); + + this.nextStroke(); + } + _handleFailure(meta: StrokeMatchResultMeta) { this._mistakesOnStroke += 1; this._totalMistakes += 1; diff --git a/src/__tests__/HanziWriter-test.ts b/src/__tests__/HanziWriter-test.ts index 37e6a25e..62b5db30 100644 --- a/src/__tests__/HanziWriter-test.ts +++ b/src/__tests__/HanziWriter-test.ts @@ -1140,6 +1140,27 @@ describe('HanziWriter', () => { }); }); + describe('skipQuizStroke', () => { + it('returns if there is not active quiz', async () => { + document.body.innerHTML = '
'; + const writer = HanziWriter.create('target', '人', { charDataLoader }); + await writer._withDataPromise; + expect(writer.skipQuizStroke()).toBe(undefined); + expect(writer._quiz).toBe(undefined); + }); + + it('skips the current stroke if a quiz is active', async () => { + document.body.innerHTML = '
'; + const writer = HanziWriter.create('target', '人', { charDataLoader }); + writer.quiz(); + await resolvePromises(); + const quiz = writer._quiz!; + writer.skipQuizStroke(); + expect(quiz.nextStroke).toHaveBeenCalledTimes(1); + expect(quiz._handleSuccess).not.toHaveBeenCalled(); + }); + }); + describe('mouse and touch events', () => { let writer: HanziWriter; beforeEach(async () => { diff --git a/src/__tests__/Quiz-test.ts b/src/__tests__/Quiz-test.ts index 5b662e75..d2723e6d 100644 --- a/src/__tests__/Quiz-test.ts +++ b/src/__tests__/Quiz-test.ts @@ -956,6 +956,40 @@ describe('Quiz', () => { }); }); + describe('nextStroke', () => { + it('shows the current stroke and moves to the next stroke', async () => { + const renderState = createRenderState(); + const quiz = new Quiz( + char, + renderState, + new Positioner({ padding: 20, width: 200, height: 200 }), + ); + const onCorrectStroke = jest.fn(); + const onMistake = jest.fn(); + const onComplete = jest.fn(); + quiz.startQuiz(Object.assign({}, opts, { onCorrectStroke, onComplete, onMistake })); + clock.tick(1000); + await resolvePromises(); + + expect(quiz._currentStrokeIndex).toBe(0); + quiz.nextStroke(); + await resolvePromises(); + + expect(quiz._userStroke).toBeUndefined(); + expect(quiz._isActive).toBe(true); + expect(quiz._currentStrokeIndex).toBe(1); + expect(onCorrectStroke).not.toHaveBeenCalled(); + expect(onMistake).not.toHaveBeenCalled(); + expect(onComplete).not.toHaveBeenCalled(); + + clock.tick(1000); + await resolvePromises(); + + expect(renderState.state.character.main.strokes[0].opacity).toBe(1); + expect(renderState.state.character.main.strokes[1].opacity).toBe(0); + }); + }); + it('doesnt leave strokes partially drawn if the users finishes the quiz really fast', async () => { (strokeMatches as any).mockImplementation(() => ({ isMatch: true,