Skip to content

Commit

Permalink
Halloween Skate logo and invalid buses as bats (#1314)
Browse files Browse the repository at this point in the history
* Halloween Skate logo and invalid buses as bats

---------

Co-authored-by: Hannah Purcell <[email protected]>
Co-authored-by: Hannah Purcell <[email protected]>
  • Loading branch information
3 people authored Oct 30, 2024
1 parent 9dfb933 commit 9fb7e8f
Show file tree
Hide file tree
Showing 11 changed files with 124 additions and 11 deletions.
15 changes: 13 additions & 2 deletions assets/css/nav/_top_nav.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.75rem 1.5rem;
padding: 0;

background-color: $color-gray-50;

Expand All @@ -14,7 +14,6 @@

.c-top-nav__logo {
width: 2.5rem;
height: 1.5rem;

svg {
width: 100%;
Expand All @@ -23,14 +22,26 @@
}

.c-top-nav__logo-icon {
&:not(.c-top-nav__logo-halloween-icon) {
margin-left: 1.5rem;
}
svg {
fill: $color-eggplant-900;
}
}

.c-top-nav__logo-halloween-icon {
background-color: #f4b347;
padding: 0 0.875rem;
@include media-breakpoint-up(lg) {
margin-left: 1.5rem;
}
}

.c-top-nav__right-items {
display: flex;
align-items: center;
padding-right: 1.5rem;

:not(:last-child) {
padding-right: 1.5rem;
Expand Down
23 changes: 22 additions & 1 deletion assets/src/components/nav/topNav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { LoggedInAs } from "../loggedInAs"
import getEmailAddress from "../../userEmailAddress"
import { CircleButton } from "../circleButton"
import { UserAvatar } from "../userAvatar"
import { todayIsHalloween } from "../../helpers/date"

const TopNav = (): JSX.Element => {
const email = getEmailAddress()
Expand All @@ -17,7 +18,11 @@ const TopNav = (): JSX.Element => {
return (
<div className="c-top-nav">
<Link className="c-top-nav__logo" to="/" title="Skate">
<LogoIcon className="c-top-nav__logo-icon" />
{todayIsHalloween() ? (
<HalloweenIcon className="c-top-nav__logo-icon c-top-nav__logo-halloween-icon" />
) : (
<LogoIcon className="c-top-nav__logo-icon" />
)}
</Link>
<ul className="c-top-nav__right-items">
<li>
Expand Down Expand Up @@ -69,4 +74,20 @@ const TopNav = (): JSX.Element => {
)
}

export const HalloweenIcon = (props: BsIcon.SvgProps) => (
<svg
viewBox="0 -25 55.49 72"
xmlns="http://www.w3.org/2000/svg"
aria-hidden
{...props}
>
<path d="m6.74 17.1a42.17 42.17 0 0 0 18.33 3.31 2 2 0 0 1 2 2v1.59a2 2 0 0 0 1.93 2h3.83a2 2 0 0 0 1.93-2v-2a2 2 0 0 1 1.56-2 75.22 75.22 0 0 0 12.55-3.47 2 2 0 0 1 2.13 3.19c-3.66 4.47-10.74 9.71-24 9.71-12 0-18.82-4.83-22.46-9.18a2 2 0 0 1 2.2-3.15z" />
<g fillRule="evenodd">
<path d="m19.37 11a1.71 1.71 0 0 1 -1.68 1.41h-12.16a1.68 1.68 0 0 1 -1.2-.5 1.67 1.67 0 0 1 -.5-1.2 1.73 1.73 0 0 1 .31-1l6.08-8.6a1.68 1.68 0 0 1 1.1-.69 1.66 1.66 0 0 1 1.27.28 1.6 1.6 0 0 1 .41.41l6.08 8.6a1.71 1.71 0 0 1 .29 1.29z" />
<path d="m51.37 11a1.71 1.71 0 0 1 -1.68 1.41h-12.16a1.68 1.68 0 0 1 -1.2-.5 1.67 1.67 0 0 1 -.5-1.2 1.73 1.73 0 0 1 .31-1l6.08-8.6a1.68 1.68 0 0 1 1.1-.69 1.66 1.66 0 0 1 1.27.28 1.6 1.6 0 0 1 .41.41l6.08 8.6a1.71 1.71 0 0 1 .29 1.29z" />
<path d="m31 16.84a.7.7 0 0 1 -.29.46.69.69 0 0 1 -.41.13h-5.09a.71.71 0 0 1 -.5-.21.68.68 0 0 1 -.21-.5.76.76 0 0 1 .13-.41l2.54-3.58a.7.7 0 0 1 .45-.29.72.72 0 0 1 .53.12.69.69 0 0 1 .17.17l2.54 3.58a.72.72 0 0 1 .14.53z" />
</g>
</svg>
)

export default TopNav
46 changes: 42 additions & 4 deletions assets/src/components/vehicleIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,19 @@ import React, { ReactElement } from "react"
import Tippy from "@tippyjs/react"
import "tippy.js/dist/tippy.css"
import { joinClasses } from "../helpers/dom"
import { DrawnStatus, statusClasses } from "../models/vehicleStatus"
import {
DrawnStatus,
OnTimeStatus,
statusClasses,
} from "../models/vehicleStatus"
import { AlertIconStyle, IconAlertCircleSvgNode } from "./iconAlertCircle"
import { runIdToLabel } from "../helpers/vehicleLabel"
import { isGhost } from "../models/vehicle"
import { Ghost, RunId, Vehicle, VehicleInScheduledService } from "../realtime"
import { BlockId, ViaVariant } from "../schedule.d"
import { scheduleAdherenceLabelString } from "./propertiesPanel/header"
import { UserSettings } from "../userSettings"
import { todayIsHalloween } from "../helpers/date"
import {
directionOnLadder,
getLadderDirectionForRoute,
Expand Down Expand Up @@ -279,6 +284,8 @@ export const VehicleIconSvgNode = React.memo(
) : null}
{status === "ghost" ? (
<GhostIcon size={size} variant={variant} />
) : isBat(status) ? (
<Bat size={size} />
) : (
<Triangle size={size} orientation={orientation} />
)}
Expand Down Expand Up @@ -320,6 +327,32 @@ const Triangle = React.memo(
}
)

const isBat = (
status: OnTimeStatus | "off-course" | "ghost" | "plain" | "logged-out"
) => status === "off-course" && todayIsHalloween()

const Bat = ({ size }: { size: Size }) => {
const scale = scaleBatForSize(size)
return (
<path
d="m33.19 37.76a1.53 1.53 0 0 0 1.11.31 1.48 1.48 0 0 0 1-.58c1.21-1.62 2.54-2.42 3.83-2.41 2.51.06 5 2.94 5.78 4.05a1.5 1.5 0 0 0 1.55.61 1.47 1.47 0 0 0 1.12-1.23c3.27-23.76-13.42-30.31-13.58-30.37a1.47 1.47 0 0 0 -2 1.54c.58 4.94-.09 8.32-2 9.8a3.11 3.11 0 0 1 -.54.35v-5a1.47 1.47 0 1 0 -2.93 0v4.17a4.38 4.38 0 0 0 -4.81 0v-4.2a1.47 1.47 0 1 0 -2.93 0v5.2a3.79 3.79 0 0 1 -.91-.53c-1.87-1.48-2.54-4.86-2-9.8a1.47 1.47 0 0 0 -2-1.54c-.04.07-16.73 6.62-13.45 30.38a1.47 1.47 0 0 0 1.12 1.23 1.5 1.5 0 0 0 1.55-.61c.75-1.11 3.25-4 5.77-4.05 1.35 0 2.63.78 3.84 2.41a1.47 1.47 0 0 0 2.1.27s4.82-2.91 8 1.61a1.43 1.43 0 0 0 2.39-.12c1.33-2.25 3.9-4.65 7.99-1.49z"
// Move the center to 0,0
transform={`scale(${scale}) translate(-24,-28)`}
/>
)
}

const scaleBatForSize = (size: Size): number => {
switch (size) {
case Size.Small:
return 0.38
case Size.Medium:
return 0.5
case Size.Large:
return 1
}
}

const GhostIcon = React.memo(
({ size, variant }: { size: Size; variant?: string }) => {
// No orientation argument, because the ghost icon is always right side up.
Expand Down Expand Up @@ -437,18 +470,23 @@ const Variant = React.memo(
status: DrawnStatus
}) => {
const scale = scaleForSize(size)
const bat = isBat(status)
const isSideways =
orientation === Orientation.Left || orientation === Orientation.Right

// space between the triangle base and the variant letter
let margin = 0
switch (size) {
case Size.Small:
margin = status === "ghost" ? 4 : 2
margin = status === "ghost" || bat ? 4 : 2
if (bat && isSideways) margin++
break
case Size.Medium:
margin = status === "ghost" ? 8 : 4
margin = status === "ghost" || bat ? 8 : 4
break
case Size.Large:
margin = status === "ghost" ? 12 : 6
margin = status === "ghost" || bat ? 12 : 6
if (bat && isSideways) margin += 2
break
}

Expand Down
2 changes: 2 additions & 0 deletions assets/src/helpers/date.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const todayIsHalloween = (today: Date = new Date()): boolean =>
today.getMonth() === 9 && today.getDate() === 31
5 changes: 5 additions & 0 deletions assets/tests/components/app.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ import { viewFactory } from "../factories/pagePanelStateFactory"
import userEvent from "@testing-library/user-event"
import { mockUsePanelState } from "../testHelpers/usePanelStateMocks"

// Avoid Halloween
jest
.useFakeTimers({ doNotFake: ["setTimeout"] })
.setSystemTime(new Date("2018-08-15T17:41:21.000Z"))

jest.mock("../../src/hooks/useDataStatus", () => ({
__esModule: true,
default: jest.fn(() => "good"),
Expand Down
5 changes: 5 additions & 0 deletions assets/tests/components/appStateWrapper.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ import React from "react"
import { render } from "@testing-library/react"
import AppStateWrapper from "../../src/components/appStateWrapper"

// Avoid Halloween
jest
.useFakeTimers({ doNotFake: ["setTimeout"] })
.setSystemTime(new Date("2024-08-29T20:00:00"))

jest.mock("userTestGroups", () => ({
__esModule: true,
default: jest.fn(() => []),
Expand Down
5 changes: 5 additions & 0 deletions assets/tests/components/ladder.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ import { render } from "@testing-library/react"
import userEvent from "@testing-library/user-event"
import { mockUsePanelState } from "../testHelpers/usePanelStateMocks"

// Avoid Halloween
jest
.useFakeTimers({ doNotFake: ["setTimeout"] })
.setSystemTime(new Date("2024-08-29T20:00:00"))

jest.mock("../../src/hooks/useVehicles", () => ({
__esModule: true,
default: () => ({}),
Expand Down
5 changes: 5 additions & 0 deletions assets/tests/components/propertiesPanel/header.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ import routeFactory from "../../factories/route"
import routeTabFactory from "../../factories/routeTab"
import userEvent from "@testing-library/user-event"

// Avoid Halloween
jest
.useFakeTimers({ doNotFake: ["setTimeout"] })
.setSystemTime(new Date("2024-08-29T20:00:00"))

jest.spyOn(Date, "now").mockImplementation(() => 234000)

const setTabMode = jest.fn()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import {
VehicleInScheduledService,
} from "../../../src/realtime"
import { Route } from "../../../src/schedule"
import * as dateTime from "../../../src/util/dateTime"
import { vehicleFactory, invalidVehicleFactory } from "../../factories/vehicle"
import { render, screen } from "@testing-library/react"
import "@testing-library/jest-dom/jest-globals"
Expand All @@ -32,9 +31,10 @@ import { closeButton } from "../../testHelpers/selectors/components/closeButton"
import { MemoryRouter } from "react-router-dom"
import { loading } from "../../../src/util/fetchResult"

// Avoid Halloween for off-course vehicles
jest
.spyOn(dateTime, "now")
.mockImplementation(() => new Date("2018-08-15T17:41:21.000Z"))
.useFakeTimers({ doNotFake: ["setTimeout"] })
.setSystemTime(new Date("2018-08-15T17:41:21.000Z"))

jest.spyOn(Date, "now").mockImplementation(() => 234000)

Expand Down
7 changes: 6 additions & 1 deletion assets/tests/components/vehicleIcon.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { test, expect } from "@jest/globals"
import { test, expect, jest } from "@jest/globals"
import React from "react"
import renderer from "react-test-renderer"
import VehicleIcon, {
Expand All @@ -9,6 +9,11 @@ import VehicleIcon, {
import { AlertIconStyle } from "../../src/components/iconAlertCircle"
import { defaultUserSettings } from "../../src/userSettings"

// Avoid Halloween
jest
.useFakeTimers({ doNotFake: ["setTimeout"] })
.setSystemTime(new Date("2024-08-29T20:00:00"))

test("renders in all directions and sizes", () => {
const tree = renderer
.create(
Expand Down
16 changes: 16 additions & 0 deletions assets/tests/helpers/date.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { describe, expect, test } from "@jest/globals"
import { todayIsHalloween } from "../../src/helpers/date"

describe("todayIsHalloween", () => {
test("returns true on Halloween", () => {
const halloweenDate = new Date("October 31, 2021 12:00:00")

expect(todayIsHalloween(halloweenDate)).toBeTruthy()
})

test("returns false on other days", () => {
const nonHalloweenDate = new Date("October 30, 2021 12:00:00")

expect(todayIsHalloween(nonHalloweenDate)).toBeFalsy()
})
})

0 comments on commit 9fb7e8f

Please sign in to comment.