Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat/infinity duration solves #32 #35

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
},
```
Expand Down
2 changes: 1 addition & 1 deletion plugin/gatsby-browser.js
Original file line number Diff line number Diff line change
@@ -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) => {
Expand Down
29 changes: 24 additions & 5 deletions plugin/gatsby-browser.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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", () => {
Expand All @@ -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,
});
});
});
});
});
14 changes: 9 additions & 5 deletions plugin/gatsby-node.js
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
23 changes: 18 additions & 5 deletions plugin/gatsby-node.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,18 @@ describe("pluginOptionsSchema", () => {
});

describe("duration", () => {
it("valid for infinite", async () => {
const options = {
duration: "infinite",
};
const { isValid } = await testPluginOptionsSchema(
pluginOptionsSchema,
options
);

expect(isValid).toBe(true);
});

it("valid for int over 4", async () => {
const options = {
duration: 10,
Expand Down Expand Up @@ -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 () => {
Expand All @@ -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 infinite string", async () => {
const options = {
duration: "test",
};
Expand All @@ -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 [infinite, number]`]);
});
});

Expand Down
9 changes: 7 additions & 2 deletions plugin/lib/snowfall.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
27 changes: 21 additions & 6 deletions plugin/lib/snowfall.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down