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

Staging > Main #670

Merged
merged 7 commits into from
Nov 22, 2024
Merged
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
193 changes: 20 additions & 173 deletions .eleventy.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ const markdownItAnchor = require("markdown-it-anchor");
const yaml = require("js-yaml");
const { sassPlugin } = require("esbuild-sass-plugin");
const svgSprite = require("eleventy-plugin-svg-sprite");
const { imageShortcode, imageWithClassShortcode } = require("./config");
const {
isValidGitBranch,
isValidTwitterHandle,
Expand All @@ -20,6 +19,13 @@ const {
isValidVerificationToken,
uswdsIconWithSize,
numberWithCommas,
sortByProp,
readableDate,
getStateFromDates,
htmlDateString,
minNumber,
uswdsIcon,
imageWithClassShortcode,
} = require("./js/global.js");

require("dotenv").config();
Expand Down Expand Up @@ -90,6 +96,11 @@ module.exports = function (config) {
baseUrl = new URL(hosts.undefined).href.replace(/\/$/, "");
}

// If BASEURL env variable exists, update pathPrefix to the BASEURL
if (process.env.BASEURL) {
pathPrefix = process.env.BASEURL;
}

config.addGlobalData("baseUrl", baseUrl);
config.addGlobalData("site.baseUrl", baseUrl);

Expand Down Expand Up @@ -139,160 +150,15 @@ module.exports = function (config) {
);
}

// Template function used to sort a collection by a certain property
// Ex: {% assign sortedJobs = collection.jobs | sortByProp: "title" %}
function sortByProp(values, prop) {
let vals = [...values];
return vals.sort((a, b) => {
if (typeof a[prop] == "string" && typeof b[prop] == "string") {
return a[prop].localeCompare(b[prop]);
} else {
return Math.sign(a[prop] - b[prop]);
}
});
}

// Get Date and Time as Seconds
// Datetime format: YYYY-MM-DD HH:MM
config.addLiquidShortcode("getDateTimeinSeconds", getDateTimeinSeconds);
function getDateTimeinSeconds(datetime) {
// Split the datetime string into date and time parts
const dateParts = datetime.split(" ");
const date = dateParts[0];
const time = dateParts[1];

// Extract hours, minutes, and AM/PM
let hours = parseInt(time.slice(0, time.length - 2).split(":")[0], 10); // Adjusted to capture full hour
const minutes = time.length === 6 ? time.slice(2, 4) : time.slice(3, 5);
const amPm = time.slice(-2).toLowerCase(); // Handle AM/PM case

// Convert hours to 24-hour format
if (amPm === "pm" && hours !== 12) {
hours += 12;
} else if (amPm === "am" && hours === 12) {
hours = 0;
}

// Format the datetime string for timestamp conversion
const formattedDatetime = `${date} ${String(hours).padStart(2, "0")}:${minutes} ET`;

// Convert to timestamp (in seconds)
const timestamp = Math.floor(new Date(formattedDatetime).getTime() / 1000);

return timestamp;
}

// Get State From Dates
config.addLiquidShortcode("getStateFromDates", getStateFromDates);
function getStateFromDates(opens, closes) {
if (!opens && !closes) {
return "unknown";
}

// Get the current date in "America/New_York" timezone
let now_date = new Date(
new Date().toLocaleString("en-US", { timeZone: "America/New_York" }),
);

// Parse the 'opens' date in UTC and convert to local time
let opens_date = opens ? new Date(opens) : null;

// Parse the 'closes' date in UTC and set time to 11:59:59 PM in local time
let closes_date = null;
if (closes) {
closes_date = new Date(closes);
// Set the time to 11:59:59 PM in local time
closes_date.setHours(23, 59, 59, 999);
}

// Convert opens_date and closes_date to local time for comparison
if (opens_date) {
// Adjust opens_date to local timezone
opens_date = new Date(
opens_date.toLocaleString("en-US", { timeZone: "America/New_York" }),
);

// Adjust closes_date to local timezone
if (closes_date) {
closes_date = new Date(
closes_date.toLocaleString("en-US", { timeZone: "America/New_York" }),
);
}

// Check if it's open or closed
let isOpen = now_date >= opens_date;
let isClosed = closes_date && now_date > closes_date;

if (isOpen && !isClosed) {
return "open";
} else if (isClosed) {
return "closed";
} else {
return "upcoming";
}
}

return "unknown"; // Default fallback if no conditions are met
}

config.addFilter("stateFromDates", getStateFromDates);
config.addFilter("sortByProp", sortByProp);

config.addFilter("readableDate", (dateObj) => {
return DateTime.fromJSDate(dateObj, { zone: "America/New_York" }).toFormat(
"dd LLL yyyy",
);
});

// https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#valid-date-string
config.addFilter("htmlDateString", (dateObj) => {
if (dateObj !== undefined && dateObj !== null) {
let dateTime = DateTime.fromJSDate(dateObj);

// If working locally, add one day to the date to match what is in the actual environments.
if (baseUrl.includes("localhost")) {
dateTime = dateTime.plus({ days: 1 });
return dateTime.toFormat("yyyy-LL-dd");
} else {
return dateTime.toFormat("yyyy-LL-dd");
}
}
});

// Get the first `n` elements of a collection.
config.addFilter("head", (array, n) => {
if (!Array.isArray(array) || array.length === 0) {
return [];
}
if (n < 0) {
return array.slice(n);
}

return array.slice(0, n);
});

// Return the smallest number argument
config.addFilter("min", (...numbers) => {
return Math.min.apply(null, numbers);
});

function filterTagList(tags) {
return (tags || []).filter(
(tag) => ["all", "nav", "post", "posts"].indexOf(tag) === -1,
);
}

config.addFilter("filterTagList", filterTagList);

// Create an array of all tags
config.addCollection("tagList", function (collection) {
let tagSet = new Set();
collection.getAll().forEach((item) => {
(item.data.tags || []).forEach((tag) => tagSet.add(tag));
});

return filterTagList([...tagSet]);
});
config.addFilter("readableDate", readableDate);
config.addFilter("htmlDateString", htmlDateString);
config.addFilter("min", minNumber);
config.addFilter("numberWithCommas", numberWithCommas);
config.addLiquidShortcode("image_with_class", imageWithClassShortcode);
config.addLiquidShortcode("uswds_icon", uswdsIcon);
config.addLiquidShortcode("uswds_icon_with_size", uswdsIconWithSize);
config.addLiquidShortcode("getStateFromDates", getStateFromDates);

let markdownLibrary = markdownIt({
html: true,
Expand Down Expand Up @@ -331,25 +197,6 @@ module.exports = function (config) {
ghostMode: false,
});

// Set image shortcodes
config.addLiquidShortcode("image", imageShortcode);
config.addLiquidShortcode("image_with_class", imageWithClassShortcode);
config.addLiquidShortcode("uswds_icon", function (name) {
return `
<svg class="usa-icon" aria-hidden="true" role="img">
<use xlink:href="#svg-${name}"></use>
</svg>`;
});

config.addLiquidShortcode("uswds_icon_with_size", uswdsIconWithSize);

config.addFilter("numberWithCommas", numberWithCommas);

// If BASEURL env variable exists, update pathPrefix to the BASEURL
if (process.env.BASEURL) {
pathPrefix = process.env.BASEURL;
}

return {
dataTemplateEngine: "liquid",

Expand Down
12 changes: 5 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,6 @@ See the [11ty docs](https://www.11ty.dev/docs/filters/url/)
All of your images will be stored in the `_img/` directory. To reference your
images in your templates you can use the `shortcodes` built into the template.

For referencing an image without a style class, you will pass the template
shortcode the image's source path and the alternative image name in that order, i.e.,

```NJK
{% image "_img/my-image.png" "My PNG Image Alternative Name" %}
```

For referencing an image with a style class, you will pass the template
shortcode the image's source path, class names, and the alternative image name in
that order, i.e.,
Expand All @@ -80,6 +73,11 @@ that order, i.e.,
{% image_with_class "_img/my-image.png" "img-class another-class" "My PNG Image Alternative Name" %}
```

If the image does not have a style class, simply pass an empty string, i.e.,
```NJK
{% image_with_class "_img/my-image.png" "" "My PNG Image Alternative Name" %}
```

### Referencing USWDS Sprite Icons

USWDS has sprite icons available for use. Here is the
Expand Down
2 changes: 1 addition & 1 deletion _includes/menu.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
>
<div class="usa-nav__inner">
<button class="usa-nav__close">
{% image "_img/close.svg" "close" %}
{% image_with_class "_img/close.svg" "" "close" %}
</button>
<ul class="usa-nav__primary usa-accordion">
{% for nav_item in primary_navigation %}
Expand Down
50 changes: 50 additions & 0 deletions _tests/getStateFromDates.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
const { getStateFromDates } = require("../js/global");

describe("getStateFromDates", () => {
beforeEach(() => {
// Mock the system time to ensure consistent results
jest.useFakeTimers().setSystemTime(new Date("2024-11-21T12:00:00Z")); // Mock current time: 7 AM ET
});

afterEach(() => {
jest.useRealTimers(); // Restore real timers
});

test('should return "unknown" if both opens and closes are undefined', () => {
expect(getStateFromDates(null, null)).toBe("unknown");
expect(getStateFromDates(undefined, undefined)).toBe("unknown");
});

test('should return "upcoming" if now is before opens', () => {
const opens = "2024-11-22T00:00:00Z"; // Opens tomorrow at midnight UTC
expect(getStateFromDates(opens, null)).toBe("upcoming");
});

test('should return "open" if now is after opens and before closes', () => {
const opens = "2024-11-20T00:00:00Z"; // Opened yesterday at midnight UTC
const closes = "2024-11-22T00:00:00Z"; // Closes tomorrow at midnight UTC
expect(getStateFromDates(opens, closes)).toBe("open");
});

test('should return "closed" if now is after closes', () => {
const opens = "2024-11-19T00:00:00Z"; // Opened two days ago at midnight UTC
const closes = "2024-11-20T23:59:59Z"; // Closed yesterday at 11:59:59 PM UTC
expect(getStateFromDates(opens, closes)).toBe("closed");
});

test("should handle cases with only opens defined", () => {
const opens = "2024-11-20T00:00:00Z"; // Opened yesterday at midnight UTC
expect(getStateFromDates(opens, null)).toBe("open");
});

test("should handle cases with only closes defined", () => {
const closes = "2024-11-22T00:00:00Z"; // Closes tomorrow at midnight UTC
expect(getStateFromDates(null, closes)).toBe("unknown"); // No opens means "unknown"
});

test("should handle edge cases for opens and closes on the same day", () => {
const opens = "2024-11-21T00:00:00Z"; // Opens today at midnight UTC
const closes = "2024-11-21T23:59:59Z"; // Closes today at 11:59:59 PM UTC
expect(getStateFromDates(opens, closes)).toBe("open"); // Current time is 7 AM ET
});
});
Loading
Loading