From 86a316d9a7bcc43ff5314daf4616f8d7d0c19513 Mon Sep 17 00:00:00 2001 From: Benedicte Raae Date: Tue, 7 Dec 2021 13:48:23 +0100 Subject: [PATCH 1/4] feat: infinate duration --- plugin/gatsby-browser.js | 2 +- plugin/gatsby-browser.test.js | 29 ++++++++++++++++++++++++----- plugin/gatsby-node.js | 14 +++++++++----- plugin/gatsby-node.test.js | 23 ++++++++++++++++++----- plugin/lib/snowfall.js | 9 +++++++-- plugin/lib/snowfall.test.js | 27 +++++++++++++++++++++------ 6 files changed, 80 insertions(+), 24 deletions(-) diff --git a/plugin/gatsby-browser.js b/plugin/gatsby-browser.js index b737ceb..5d96c2e 100644 --- a/plugin/gatsby-browser.js +++ b/plugin/gatsby-browser.js @@ -1,6 +1,6 @@ // https://www.gatsbyjs.com/docs/reference/config-files/gatsby-browser/ -import { getCssVariable, isSeason } from "./lib/utils"; +import { isSeason } from "./lib/utils"; import snowfall from "./lib/snowfall"; export const onInitialClientRender = (_, pluginOptions) => { diff --git a/plugin/gatsby-browser.test.js b/plugin/gatsby-browser.test.js index 7a303e1..9e3e90b 100644 --- a/plugin/gatsby-browser.test.js +++ b/plugin/gatsby-browser.test.js @@ -25,11 +25,6 @@ describe("onInitialClientRender", () => { onInitialClientRender(null, pluginOptions); expect(snowfall).toBeCalledTimes(1); - expect(snowfall).toBeCalledWith({ - colors: ["fff"], - intensity: "blizzard", - duration: 10 * 1000, - }); }); it("does not activate snowfall outside the season", () => { @@ -48,5 +43,29 @@ describe("onInitialClientRender", () => { expect(snowfall).toBeCalledTimes(0); }); + + describe("duration option", () => { + it("activates snowfall when in season", () => { + const today = new Date(); + const pluginOptions = { + colors: ["fff"], + intensity: "blizzard", + duration: "infinity", + season: { + start: addDays(today, -1).toISOString(), + end: addDays(today, +1).toISOString(), + }, + }; + + onInitialClientRender(null, pluginOptions); + + expect(snowfall).toBeCalledTimes(1); + expect(snowfall).toBeCalledWith({ + colors: ["fff"], + intensity: "blizzard", + duration: NaN, + }); + }); + }); }); }); diff --git a/plugin/gatsby-node.js b/plugin/gatsby-node.js index af22903..24c26df 100644 --- a/plugin/gatsby-node.js +++ b/plugin/gatsby-node.js @@ -9,11 +9,15 @@ exports.pluginOptionsSchema = ({ Joi }) => { intensity: Joi.string() .valid("regular", "light", "blizzard") .default("regular"), - duration: Joi.number() - .integer() - .min(5) - .default(15) - .description("Duration of snowfall in seconds"), + duration: Joi.alternatives() + .try( + Joi.string().valid("infinite"), + Joi.number() + .integer() + .min(5) + .description("Duration of snowfall in seconds") + ) + .default(15), season: Joi.object() .keys({ start: Joi.date().required(), diff --git a/plugin/gatsby-node.test.js b/plugin/gatsby-node.test.js index 40906b1..5af39be 100644 --- a/plugin/gatsby-node.test.js +++ b/plugin/gatsby-node.test.js @@ -118,6 +118,18 @@ describe("pluginOptionsSchema", () => { }); describe("duration", () => { + it("valid for infinity", async () => { + const options = { + duration: "infinity", + }; + const { isValid } = await testPluginOptionsSchema( + pluginOptionsSchema, + options + ); + + expect(isValid).toBe(true); + }); + it("valid for int over 4", async () => { const options = { duration: 10, @@ -152,7 +164,9 @@ describe("pluginOptionsSchema", () => { ); expect(isValid).toBe(false); - expect(errors).toEqual([`"duration" must be greater than or equal to 5`]); + expect(errors).toEqual([ + `"duration" does not match any of the allowed types`, + ]); }); it("invalid for float", async () => { @@ -166,12 +180,11 @@ describe("pluginOptionsSchema", () => { expect(isValid).toBe(false); expect(errors).toEqual([ - `"duration" must be an integer`, - `"duration" must be greater than or equal to 5`, + `"duration" does not match any of the allowed types`, ]); }); - it("invalid for non int string", async () => { + it("invalid for non int, non infinity string", async () => { const options = { duration: "test", }; @@ -181,7 +194,7 @@ describe("pluginOptionsSchema", () => { ); expect(isValid).toBe(false); - expect(errors).toEqual([`"duration" must be a number`]); + expect(errors).toEqual([`"duration" must be one of [infinity, number]`]); }); }); diff --git a/plugin/lib/snowfall.js b/plugin/lib/snowfall.js index daf4f23..15321a5 100644 --- a/plugin/lib/snowfall.js +++ b/plugin/lib/snowfall.js @@ -3,11 +3,16 @@ import { randomInRange, getCssVariable } from "./utils"; export const animationFrame = (options, currentTimestamp = Date.now()) => { const { animationEnd, duration, skew = 1 } = options; - const { colors, intensity } = options; + const { colors = [], intensity } = options; const timeLeft = animationEnd - currentTimestamp; const newSkew = Math.max(0.8, skew - 0.001); - const ticks = Math.max(200, 500 * (timeLeft / duration)); + + // If duration is set to infinity durationEnd i NaN, + // resulting in timeLeft NaN + const ticks = isNaN(timeLeft) + ? Math.max(200, 500 * Math.random()) + : Math.max(200, 500 * (timeLeft / duration)); if (timeLeft <= 0) { return; diff --git a/plugin/lib/snowfall.test.js b/plugin/lib/snowfall.test.js index f503f40..df86bdb 100644 --- a/plugin/lib/snowfall.test.js +++ b/plugin/lib/snowfall.test.js @@ -22,24 +22,39 @@ describe("snowfall", () => { jest.clearAllMocks(); }); - it("not an infinite loop", () => { + it("not an infinite loop when duration is set", () => { // Will exeed maximum stack if infinite - snowfall(OPTIONS); + snowfall({ + duration: 1, + }); expect(confetti).toBeCalled(); expect(global.requestAnimationFrame).toBeCalled(); }); describe("animationFrame", () => { - it("when time left call confetti, recurrsion and get css variables", () => { - animationFrame({ animationEnd: 6, ...OPTIONS }, 5); + it("calls confetti and loops when time left", () => { + animationFrame({ animationEnd: 6 }, 5); + + expect(confetti).toBeCalledTimes(1); + expect(global.requestAnimationFrame).toBeCalledTimes(1); + }); + + it("calls getCssVariable when appropriate when time left", () => { + animationFrame( + { + animationEnd: 6, + colors: ["fff", "--snow-color-1", "--snow-color-2"], + }, + 5 + ); expect(confetti).toBeCalledTimes(1); expect(global.requestAnimationFrame).toBeCalledTimes(1); expect(getCssVariable).toBeCalledTimes(2); }); - it("when no time left do not call confetti and recurrsion", () => { - animationFrame({ animationEnd: 5, ...OPTIONS }, 5); + it("does not call confetti and does not loop when no time left", () => { + animationFrame({ animationEnd: 5 }, 5); expect(confetti).toBeCalledTimes(0); expect(global.requestAnimationFrame).toBeCalledTimes(0); From c076df16ef93fa5d90576bd8b2aca2abd2982e7a Mon Sep 17 00:00:00 2001 From: Benedicte Raae Date: Tue, 7 Dec 2021 13:52:07 +0100 Subject: [PATCH 2/4] docs: update duration section of readme --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f6b3452..526a248 100755 --- a/README.md +++ b/README.md @@ -60,14 +60,15 @@ Default value if non given: "regular" ### Duration -You can configure the duration of the snowfall (as seconds). -Default value if non given: 15 +You can choose the duration of the snowfall. +**Type:** Integer (seconds) or the string "infinite" +**Default:** `15` ``` { resolve: "@raae/gatsby-plugin-let-it-snow", options: { - duration: 10, + duration: 30, }, }, ``` From 8942154d811020401c09f5c4cbdfd77546f6319d Mon Sep 17 00:00:00 2001 From: Benedicte Raae Date: Tue, 7 Dec 2021 13:55:13 +0100 Subject: [PATCH 3/4] test: update to infinite --- plugin/gatsby-node.test.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugin/gatsby-node.test.js b/plugin/gatsby-node.test.js index 5af39be..83b7891 100644 --- a/plugin/gatsby-node.test.js +++ b/plugin/gatsby-node.test.js @@ -118,9 +118,9 @@ describe("pluginOptionsSchema", () => { }); describe("duration", () => { - it("valid for infinity", async () => { + it("valid for infinite", async () => { const options = { - duration: "infinity", + duration: "infinite", }; const { isValid } = await testPluginOptionsSchema( pluginOptionsSchema, @@ -184,7 +184,7 @@ describe("pluginOptionsSchema", () => { ]); }); - it("invalid for non int, non infinity string", async () => { + it("invalid for non int, non infinite string", async () => { const options = { duration: "test", }; @@ -194,7 +194,7 @@ describe("pluginOptionsSchema", () => { ); expect(isValid).toBe(false); - expect(errors).toEqual([`"duration" must be one of [infinity, number]`]); + expect(errors).toEqual([`"duration" must be one of [infinite, number]`]); }); }); From 2d24036333c971e2148b6e1dee2bb04f86b231a7 Mon Sep 17 00:00:00 2001 From: Benedicte Raae Date: Wed, 8 Dec 2021 16:38:09 +0100 Subject: [PATCH 4/4] docs: redme fix to duration section --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 526a248..9826aaf 100755 --- a/README.md +++ b/README.md @@ -60,8 +60,8 @@ Default value if non given: "regular" ### Duration -You can choose the duration of the snowfall. -**Type:** Integer (seconds) or the string "infinite" +You can choose the duration of the snowfall. +**Type:** Integer (seconds) or the string `"infinite"` **Default:** `15` ```