Skip to content

Commit

Permalink
Full-screen panels on small screens (#2289)
Browse files Browse the repository at this point in the history
* refactor: put old breakpoints in map rather than separate constants

* feat: initial change for properties panel

* feat: hide nav when view is open below 800px breakpoint

* feat: swings view full width below 800px

* feat: notifications drawer full width below 800px

* feat: show back buttons below 800px

* refactor: put isViewOpen logic into usePanelStateForViewState hook

* fixup! refactor: put isViewOpen logic into usePanelStateForViewState hook

* feat: also make late view fullscreen at 800px breakpoint

* refactor: better use of factories in usePanelState tests

Co-authored-by: Kayla Firestack <[email protected]>

* fixup! refactor: better use of factories in usePanelState tests

* refactor: use hidden attribute rather than style

---------

Co-authored-by: Kayla Firestack <[email protected]>
  • Loading branch information
lemald and firestack authored Nov 30, 2023
1 parent f46664a commit 312ac9e
Show file tree
Hide file tree
Showing 20 changed files with 177 additions and 124 deletions.
4 changes: 2 additions & 2 deletions assets/css/_ladder_page.scss
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ $color-route-tabs-separator: #9fa2ac;
padding-left: $route-picker-width;
}

@media screen and (max-width: $mobile-max-width) {
@media screen and (max-width: map-get($old-breakpoints, "mobile-max-width")) {
.c-ladder-page--picker-container-visible {
padding-left: $mobile-route-picker-width;
}
}

@media screen and (max-width: $mobile-max-width) {
@media screen and (max-width: map-get($old-breakpoints, "mobile-max-width")) {
.c-ladder-page--picker-container-visible {
.c-ladder-page__tab-bar-and-ladders {
@include blur;
Expand Down
4 changes: 2 additions & 2 deletions assets/css/_late_view.scss
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ $late-view-background-color: $color-bg-light;
top: 0;
z-index: map-get($z-page-layout-context, "view");

@media screen and (max-width: 700px) {
width: 100%;
@media screen and (max-width: map-get($breakpoints, "max-mobile-landscape-tablet-portrait-width")) {
width: 100vw;
}
}

Expand Down
4 changes: 2 additions & 2 deletions assets/css/_map_page.scss
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ $z-map-page-context: (
z-index: map-get($z-map-page-context, "map");

.c-map-page__input-and-results--visible ~ & {
@media screen and (max-width: $mobile-portrait-max-width) {
@media screen and (max-width: map-get($old-breakpoints, "mobile-portrait-max-width")) {
filter: blur(3.25px);
}
}
Expand Down Expand Up @@ -102,7 +102,7 @@ $z-map-page-context: (
display: none;

.c-map-page__input-and-results--visible ~ & {
@media screen and (max-width: $mobile-portrait-max-width) {
@media screen and (max-width: map-get($old-breakpoints, "mobile-portrait-max-width")) {
// Make button clickable
display: block;
// Force Flex + Posistion to fill area
Expand Down
6 changes: 3 additions & 3 deletions assets/css/_notification_drawer.scss
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@
width: 23.5rem;
z-index: map-get($z-page-layout-context, "view");

@media screen and (max-width: $mobile-max-width) {
width: 100%;
@media screen and (max-width: map-get($breakpoints, "max-mobile-landscape-tablet-portrait-width")) {
width: 100vw;
}
@media screen and (max-width: $mobile-portrait-max-width) {
@media screen and (max-width: map-get($breakpoints, "max-mobile-width")) {
position: fixed;
bottom: 0;
}
Expand Down
2 changes: 1 addition & 1 deletion assets/css/_picker_container.scss
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
display: none;
}

@media screen and (max-width: $mobile-max-width) {
@media screen and (max-width: map-get($old-breakpoints, "mobile-max-width")) {
.c-picker-container {
--picker-container-width: min(
#{$mobile-route-picker-width},
Expand Down
8 changes: 2 additions & 6 deletions assets/css/_properties_panel.scss
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,10 @@
width: 23.5rem;
z-index: map-get($z-page-layout-context, "properties-panel");

@media screen and (max-width: $mobile-max-width) {
@media screen and (max-width: map-get($breakpoints, "max-mobile-landscape-tablet-portrait-width")) {
width: 100vw;

.l-nav__app-content & {
width: 100%;
}
}
@media screen and (max-width: $mobile-portrait-max-width) {
@media screen and (max-width: map-get($breakpoints, "max-mobile-width")) {
position: fixed;
bottom: 0;
}
Expand Down
2 changes: 1 addition & 1 deletion assets/css/_search_page.scss
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ $z-search-page-context: (
padding: 0.625rem 1rem 0 1rem;
}

@media screen and (max-width: $mobile-max-width) {
@media screen and (max-width: map-get($old-breakpoints, "mobile-max-width")) {
.c-search-page {
flex-direction: column;
}
Expand Down
6 changes: 6 additions & 0 deletions assets/css/_skate_ui.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
$breakpoints: (
"max-mobile-width": 480px,
"max-mobile-landscape-tablet-portrait-width": 800px,
"max-tablet-width": 1340px,
);

// #region Text Styles [v2]

// https://www.notion.so/mbta-downtown-crossing/2022-12-19-Type-Styles-6f6c90eb449248b7a954e234b718137f
Expand Down
9 changes: 4 additions & 5 deletions assets/css/_swings_view.scss
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,18 @@
}

// Full width on mobile portrait, to be updated with more responsive designs
@media screen and (max-width: 480px) {
@media screen and (max-width: map-get($breakpoints, "max-mobile-landscape-tablet-portrait-width")) {
.l-nav__app-content & {
width: 100%;
width: 100vw;

.c-swings-view__table {
width: 100%;
}
}
}
@media screen and (max-width: $mobile-portrait-max-width) {
@media screen and (max-width: map-get($breakpoints, "max-mobile-width")) {
position: fixed;
bottom: 0;
width: 100vw;
}
}

Expand Down Expand Up @@ -215,7 +214,7 @@
}
}

@media screen and (max-width: $mobile-max-width) {
@media screen and (max-width: map-get($old-breakpoints, "mobile-max-width")) {
.c-swings-view__header {
padding-left: 1.75rem;
}
Expand Down
2 changes: 1 addition & 1 deletion assets/css/_ui_kit.scss
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,7 @@ $font-family-route-pill: "Helvetica Neue", Helvetica, Arial, sans-serif;
filter: blur(5px);
}

@media screen and (max-width: $mobile-max-width) {
@media screen and (max-width: map-get($old-breakpoints, "mobile-max-width")) {
.blurred-mobile {
@include blur;
}
Expand Down
7 changes: 4 additions & 3 deletions assets/css/app.scss
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
$non-mobile-min-width: 768px;
$mobile-max-width: $non-mobile-min-width - 1;
$mobile-portrait-max-width: 480px;
$old-breakpoints: (
"mobile-max-width": 767px,
"mobile-portrait-max-width": 480px,
);
$tab-bar-width: 3rem;
$drawer-tab-width: 2rem;
$route-picker-width: 21.875rem;
Expand Down
10 changes: 8 additions & 2 deletions assets/src/components/nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import LeftNav from "./nav/leftNav"
import TopNav from "./nav/topNav"
import MobilePortraitNav from "./nav/mobilePortraitNav"
import { StateDispatchContext } from "../contexts/stateDispatchContext"
import { usePanelStateFromStateDispatchContext } from "../hooks/usePanelState"

interface Props {
children?: React.ReactNode
Expand All @@ -15,19 +16,24 @@ const Nav: React.FC<Props> = ({ children }) => {
const [, dispatch] = useContext(StateDispatchContext)
const deviceType = useScreenSize()

const { isViewOpen } = usePanelStateFromStateDispatchContext()

switch (deviceType) {
case "mobile":
return (
<div className="l-nav--narrow">
<div className="l-nav__app-content">{children}</div>
<MobilePortraitNav />
<MobilePortraitNav isViewOpen={isViewOpen} />
</div>
)
case "mobile_landscape_tablet_portrait":
return (
<div className="l-nav--medium">
<div className="l-nav__app-content">{children}</div>
<div className="l-nav__nav-bar l-nav__nav-bar--left">
<div
className="l-nav__nav-bar l-nav__nav-bar--left"
hidden={isViewOpen}
>
<LeftNav
toggleMobileMenu={() => dispatch(toggleMobileMenu())}
defaultToCollapsed={true}
Expand Down
16 changes: 7 additions & 9 deletions assets/src/components/nav/mobilePortraitNav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,18 @@ import { StateDispatchContext } from "../../contexts/stateDispatchContext"
import { toggleMobileMenu } from "../../state"
import BottomNavMobile from "./bottomNavMobile"
import TopNavMobile from "./topNavMobile"
import { OpenView } from "../../state/pagePanelState"
import { usePanelStateFromStateDispatchContext } from "../../hooks/usePanelState"

const MobilePortraitNav = (): JSX.Element => {
const MobilePortraitNav = ({
isViewOpen,
}: {
isViewOpen: boolean
}): JSX.Element => {
const [state, dispatch] = useContext(StateDispatchContext)

const { mobileMenuIsOpen, routeTabs } = state
const {
currentView: { openView, selectedVehicleOrGhost },
openNotificationDrawer,
openSwingsView,
} = usePanelStateFromStateDispatchContext()

const isViewOpen = openView !== OpenView.None || selectedVehicleOrGhost
const { openNotificationDrawer, openSwingsView } =
usePanelStateFromStateDispatchContext()

const navVisibilityStyle = isViewOpen ? "hidden" : "visible"

Expand Down
5 changes: 4 additions & 1 deletion assets/src/components/viewHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,14 @@ const ViewHeader: ViewHeaderType = ({
}): JSX.Element => {
const deviceType = useScreenSize()

const screenSizeAllowsBackButton =
deviceType === "mobile" || deviceType === "mobile_landscape_tablet_portrait"

return (
<div className="c-view-header">
{backlinkToView &&
backlinkToView !== OpenView.None &&
deviceType === "mobile" ? (
screenSizeAllowsBackButton ? (
<button
className="c-view-header__backlink"
onClick={followBacklink}
Expand Down
8 changes: 7 additions & 1 deletion assets/src/hooks/usePanelState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { TabMode } from "../components/propertiesPanel/tabPanels"
import { StateDispatchContext } from "../contexts/stateDispatchContext"
import { Dispatch } from "../state"
import {
OpenView,
PagePath,
VehicleType,
ViewState,
Expand Down Expand Up @@ -37,8 +38,9 @@ export const usePanelStateForViewState = (
view: ViewState,
dispatch: Dispatch
) => {
const currentView = view.state[view.currentPath]
return {
currentView: view.state[view.currentPath],
currentView,
setPath: (path: PagePath) => {
if (path !== view.currentPath) {
dispatch(setPath(path))
Expand All @@ -52,5 +54,9 @@ export const usePanelStateForViewState = (
openNotificationDrawer: () => dispatch(openNotificaitonDrawer()),
closeView: () => dispatch(closeView()),
openPreviousView: () => dispatch(openPreviousView()),
isViewOpen:
currentView.openView !== OpenView.None ||
(currentView.selectedVehicleOrGhost && true) ||
false,
}
}
33 changes: 27 additions & 6 deletions assets/tests/components/nav.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import Nav from "../../src/components/nav"
import useScreenSize from "../../src/hooks/useScreenSize"
import getTestGroups from "../../src/userTestGroups"
import { TestGroups } from "../../src/userInTestGroup"
import { mockUsePanelState } from "../testHelpers/usePanelStateMocks"

jest.mock("../../src/hooks/useScreenSize", () => ({
__esModule: true,
Expand All @@ -19,13 +20,16 @@ jest.mock("userTestGroups", () => ({
default: jest.fn(() => []),
}))

jest.mock("../../src/hooks/usePanelState")

beforeEach(() => {
mockUsePanelState({ isViewOpen: false })
;(getTestGroups as jest.Mock).mockReturnValue([])
})

describe("Nav", () => {
test("renders mobile nav content", () => {
;(useScreenSize as jest.Mock).mockImplementationOnce(() => "mobile")
jest.mocked(useScreenSize).mockReturnValueOnce("mobile")

const result = render(
<BrowserRouter>
Expand All @@ -38,9 +42,9 @@ describe("Nav", () => {
})

test("renders mobile landscape / tablet portrait nav content", () => {
;(useScreenSize as jest.Mock).mockImplementationOnce(
() => "mobile_landscape_tablet_portrait"
)
jest
.mocked(useScreenSize)
.mockReturnValueOnce("mobile_landscape_tablet_portrait")

const result = render(
<BrowserRouter>
Expand All @@ -52,8 +56,23 @@ describe("Nav", () => {
expect(result.queryByText("Route Ladders")).toBeNull()
})

test("renders mobile landscape / tablet portrait nav content with nav elements hidden when a view is open", () => {
mockUsePanelState({ isViewOpen: true })
jest
.mocked(useScreenSize)
.mockReturnValueOnce("mobile_landscape_tablet_portrait")

const result = render(
<BrowserRouter>
<Nav>Hello, world!</Nav>
</BrowserRouter>
)

expect(result.getByTitle("Route Ladders")).not.toBeVisible()
})

test("renders tablet nav content", () => {
;(useScreenSize as jest.Mock).mockImplementationOnce(() => "tablet")
jest.mocked(useScreenSize).mockReturnValueOnce("tablet")

const result = render(
<BrowserRouter>
Expand All @@ -66,7 +85,7 @@ describe("Nav", () => {
})

test("renders nav item with title 'Search Map' if in map test group", () => {
;(getTestGroups as jest.Mock).mockReturnValue([TestGroups.MapBeta])
jest.mocked(getTestGroups).mockReturnValue([TestGroups.MapBeta])

render(
<BrowserRouter>
Expand All @@ -79,6 +98,8 @@ describe("Nav", () => {
})

test("renders desktop nav content", () => {
jest.mocked(useScreenSize).mockReturnValueOnce("desktop")

const result = render(
<BrowserRouter>
<Nav>Hello, world!</Nav>
Expand Down
Loading

0 comments on commit 312ac9e

Please sign in to comment.