Skip to content

Commit

Permalink
Merge pull request #120 from marcaufderheyde/ux/mobile-nav-drawer
Browse files Browse the repository at this point in the history
[CAN-143] Merge Mobile Drawer into Develop
  • Loading branch information
fabie37 authored Sep 19, 2024
2 parents 8f6fc70 + 0901daf commit d062586
Show file tree
Hide file tree
Showing 26 changed files with 573 additions and 148 deletions.
45 changes: 45 additions & 0 deletions .github/workflows/develop-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: Develop Branch CI

on:
pull_request:
branches: ['develop']

jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Detect package manager
id: detect-package-manager
run: |
if [ -f "${{ github.workspace }}/package.json" ]; then
echo "manager=npm" >> $GITHUB_OUTPUT
echo "command=ci" >> $GITHUB_OUTPUT
echo "runner=npx --no-install" >> $GITHUB_OUTPUT
exit 0
elif [ -f "${{ github.workspace }}/yarn.lock" ]; then
echo "manager=yarn" >> $GITHUB_OUTPUT
echo "command=install" >> $GITHUB_OUTPUT
echo "runner=yarn" >> $GITHUB_OUTPUT
exit 0
else
echo "Unable to determine package manager"
exit 1
fi
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '20.9.0'
cache: ${{ steps.detect-package-manager.outputs.manager }}

- name: Install dependencies
run: ${{ steps.detect-package-manager.outputs.manager }} ${{ steps.detect-package-manager.outputs.command }}

- name: Build project
run: ${{ steps.detect-package-manager.outputs.runner }} next build

- name: Run tests
run: ${{ steps.detect-package-manager.outputs.manager }} run test
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,8 @@ To maintain consistency across the project, we follow these coding conventions:

## Code Reviews

Across code reviews in the codebase emojis from the [Code Review Emoji Guide](https://github.com/erikthedeveloper/code-review-emoji-guide) are used.
- Across code reviews in the codebase emojis from the [Code Review Emoji Guide](https://github.com/erikthedeveloper/code-review-emoji-guide) are used.
- Code reviews require at least one approval in order to be merged.

## Contributing

Expand Down
4 changes: 2 additions & 2 deletions app/[locale]/(withoutheaderfooter)/clubs/clubs-content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export default function ClubsContent() {
<div>
<Navbar isOnMap={true} />
<div>
<div className="hidden lg:flex">
<div className='hidden lg:flex'>
<OpenStreetMap
showHRInfo={showHRFilter}
isDesktopMap={true}
Expand All @@ -31,7 +31,7 @@ export default function ClubsContent() {
setShowHRFilter={setShowHRFilter}
/>
</div>
<div className="lg:hidden flex">
<div className='lg:hidden flex'>
<OpenStreetMap
showHRInfo={showHRFilter}
isDesktopMap={false}
Expand Down
2 changes: 1 addition & 1 deletion app/components/AutoScaler/AutoScaler.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jest.mock('@/app/helpers/useWindowSize', () => ({
default: jest.fn(() => ({ width: 800, height: 600 })),
}));

jest.mock('./helpers/classNameMatcher', () => ({
jest.mock('./helpers/ClassNameMatcher', () => ({
__esModule: true,
default: {
getCustomWidth: jest.fn((className: string) => '200'),
Expand Down
40 changes: 24 additions & 16 deletions app/components/CustomPopup/CustomPopup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,19 +41,27 @@ export default function CustomPopup({
const localActive = useLocale();

return (
<div className="w-[400px] bg-white flex flex-col h-full">
<div className="grid grid-rows-[1fr_6fr] grid-cols-[1fr_5fr_1fr] justify-items-center items-center my-2">
<Image
src={club.imageUrl}
alt={`${club.name} Club Picture`}
height={180}
width={300}
layout="raw"
<div className='w-[400px] bg-white flex flex-col h-full'>
<div className='grid grid-rows-[1fr_6fr] grid-cols-[1fr_5fr_1fr] justify-items-center items-center my-2'>
<a
href={`/${localActive}/clubs/${club.slug}`}
className={
styles.mapCardImage +
' col-start-2 col-end-3 row-start-1 row-end-3 h-[180px]'
styles.mapCardLink +
' col-start-2 col-end-3 row-start-1 row-end-3'
}
/>
>
<Image
src={club.imageUrl}
alt={`${club.name} Club Picture`}
height={180}
width={300}
layout='raw'
className={
styles.mapCardImage +
' col-start-2 col-end-3 row-start-1 row-end-3 h-[180px]'
}
/>
</a>
<button
className={
styles.closeButton +
Expand All @@ -64,7 +72,7 @@ export default function CustomPopup({
<Close color={'#828282'} />
</button>
</div>
<div className="flex flex-col items-center align-middle text-center mx-3">
<div className='flex flex-col items-center align-middle text-center mx-3'>
<a
href={`/${localActive}/clubs/${club.slug}`}
className={styles.mapCardLink}
Expand All @@ -73,20 +81,20 @@ export default function CustomPopup({
</a>
<br />
</div>
<div className="flex flex-row gap-2 flex-wrap mx-3">
<div className='flex flex-row gap-2 flex-wrap mx-3'>
{club.offerings
?.toString()
.split(',')
.map((offering) => (
<div
key={offering}
className="bg-lime-500 text-white rounded-xl py-1 px-2 self-center overflow-ellipsis"
className='bg-lime-500 text-white rounded-xl py-1 px-2 self-center overflow-ellipsis'
>
{offering}
</div>
))}
</div>
<div className="h-[100%] grid grid-cols-1 grid-rows-[1fr_auto_1fr] justify-items-center">
<div className='h-[100%] grid grid-cols-1 grid-rows-[1fr_auto_1fr] justify-items-center'>
<div
className={
'row-start-4 row-end-4 inline-flex py-2 px-4 md:py-3'
Expand All @@ -100,7 +108,7 @@ export default function CustomPopup({
triangleColor={'white'}
onClickFunction={() => switchPreviousClub()}
/>
<div className="bg-lime-500 flex text-white text-base p-2 w-[60px] justify-center">
<div className='bg-lime-500 flex text-white text-base p-2 w-[60px] justify-center'>
<div>
{
(((clubIndex + 1) as unknown as string) +
Expand Down
77 changes: 77 additions & 0 deletions app/components/Drawer/Drawer.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import React, { ReactNode } from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom';

type DrawerProps = {
isOpen: boolean;
onClose: () => void;
children: ReactNode;
className?: string;
};

const MockDrawer: React.FC<DrawerProps> = ({
isOpen,
onClose,
children,
className,
}) => {
if (!isOpen) return null;
return (
<div data-testid="mock-drawer" className={className}>
<div data-testid="mock-close" onClick={onClose}>
Close
</div>
<div data-testid="mock-content">{children}</div>
</div>
);
};

jest.mock('./Drawer', () => ({
__esModule: true,
default: jest.fn((props: DrawerProps) => MockDrawer(props)),
}));

const Drawer = require('./Drawer').default;

describe('Drawer Component', () => {
const mockOnClose = jest.fn();
const defaultProps: DrawerProps = {
isOpen: true,
onClose: mockOnClose,
children: <div>Drawer Content</div>,
};

beforeEach(() => {
jest.clearAllMocks();
});

it('renders correctly when open', () => {
render(<Drawer {...defaultProps} />);
expect(screen.getByTestId('mock-drawer')).toBeInTheDocument();
expect(screen.getByText('Drawer Content')).toBeInTheDocument();
});

it('does not render when closed', () => {
render(<Drawer {...defaultProps} isOpen={false} />);
expect(screen.queryByTestId('mock-drawer')).not.toBeInTheDocument();
});

it('calls onClose when close button is clicked', () => {
render(<Drawer {...defaultProps} />);
fireEvent.click(screen.getByTestId('mock-close'));
expect(mockOnClose).toHaveBeenCalled();
});

it('applies custom className', () => {
render(<Drawer {...defaultProps} className="custom-class" />);
expect(screen.getByTestId('mock-drawer')).toHaveClass('custom-class');
});

it('renders children correctly', () => {
const customContent = (
<div data-testid="custom-content">Custom Content</div>
);
render(<Drawer {...defaultProps}>{customContent}</Drawer>);
expect(screen.getByTestId('custom-content')).toBeInTheDocument();
});
});
153 changes: 153 additions & 0 deletions app/components/Drawer/Drawer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
'use client';

import { PropsWithChildren, FunctionComponent, useRef } from 'react';
import Close from '../Close/Close';
import Swipeable from '../OpenStreetMap/Swipeable';
import { Transition, TransitionStatus } from 'react-transition-group';

type DrawerProps = {
readonly children: React.ReactElement;
readonly className?: string;
readonly isOpen: boolean;
readonly onClose: () => void;
readonly closeButtonColor?: string;
};

type Position = {
x: string;
y: string;
};

const Drawer: FunctionComponent<DrawerProps> = (
props: PropsWithChildren<DrawerProps>
) => {
const transformDuration: number = 300;
const backgroundDelay: number = 100;
const drawerContainerRef = useRef(null);
const drawerContainerDefaultTransitionStyle: React.CSSProperties = {
transform: `translateX(100%)`,
transition: `transform ${transformDuration}ms ease`,
};
const drawerContainerTransitionStyles: Record<
TransitionStatus,
React.CSSProperties
> = {
entering: {
transform: `translateX(0)`,
},
entered: {
transform: `translateX(0)`,
},
exiting: {
transform: `translateX(100%)`,
transition: `transform ${transformDuration}ms ease ${transformDuration + backgroundDelay}ms`,
},
exited: {
transform: `translateX(100%)`,
transition: `transform ${transformDuration}ms ease ${transformDuration + backgroundDelay}ms`,
},
unmounted: {
transform: `translateX(100%)`,
transition: `transform ${transformDuration}ms ease ${transformDuration + backgroundDelay}ms `,
},
};

const drawerRef = useRef(null);
const drawerDelay: number = 400;
const drawerDefaultTransitionStyle: React.CSSProperties = {
transform: `translateX(100vw)`,
transition: `transform ${transformDuration}ms ease `,
};
const drawerTransitionStyles: Record<
TransitionStatus,
React.CSSProperties
> = {
entering: {
transform: `translateX(calc(100vw - 100%))`,
transition: `transform ${transformDuration}ms ease ${drawerDelay}ms`,
},
entered: {
transform: `translateX(calc(100vw - 100%))`,
transition: `transform ${transformDuration}ms ease ${drawerDelay}ms`,
},
exiting: {
transform: `translateX(100vw)`,
},
exited: {
transform: `translateX(100vw)`,
},
unmounted: {
transform: `translateX(100vw)`,
},
};

return (
<Swipeable
onRightSwipe={() => {
props.onClose();
}}
>
<Transition
nodeRef={drawerContainerRef}
in={props.isOpen}
timeout={transformDuration * 3}
unmountOnExit={true}
>
{(state) => (
<div
onClick={() => {
props.onClose();
}}
ref={drawerContainerRef}
style={{
...drawerContainerDefaultTransitionStyle,
...drawerContainerTransitionStyles[state],
}}
className={
'fixed z-50 min-w-full min-h-full top-0 left-0 bg-[#B6CF54]'
}
>
<Transition
nodeRef={drawerRef}
timeout={transformDuration}
in={props.isOpen}
unmountOnExit={true}
appear={true}
>
{(state) => (
<div
ref={drawerRef}
onClick={(e) => {
e.stopPropagation();
}}
style={{
...drawerDefaultTransitionStyle,
...drawerTransitionStyles[state],
}}
className={
'fixed min-h-full bg-transparent min-w-[80%] top-0 left-0 drop-shadow-lg ' +
props.className
}
>
<div
onClick={props.onClose}
className='absolute top-[2em] right-[2em] z-[2]'
>
<Close
color={
props.closeButtonColor ?? 'grey'
}
/>
</div>
{props.children}
</div>
)}
</Transition>
</div>
)}
</Transition>
</Swipeable>
);
};

export default Drawer;
Loading

0 comments on commit d062586

Please sign in to comment.