From 579bce6b1e8dcb68b119228126bac7ef2d41582c Mon Sep 17 00:00:00 2001 From: Lauri Date: Thu, 6 Feb 2025 13:53:41 +0200 Subject: [PATCH 01/10] added v2 of scrollTop --- .../ScrollTop/ui/v2/ScrollTop.module.scss | 39 ++++++++++ .../ScrollTop/ui/v2/ScrollTop.stories.tsx | 50 ++++++++++++ .../ScrollTop/ui/v2/ScrollTop.test.tsx | 78 +++++++++++++++++++ .../features/ScrollTop/ui/v2/ScrollTop.tsx | 40 ++++++++++ 4 files changed, 207 insertions(+) create mode 100644 frontend-next-migration/src/features/ScrollTop/ui/v2/ScrollTop.module.scss create mode 100644 frontend-next-migration/src/features/ScrollTop/ui/v2/ScrollTop.stories.tsx create mode 100644 frontend-next-migration/src/features/ScrollTop/ui/v2/ScrollTop.test.tsx create mode 100644 frontend-next-migration/src/features/ScrollTop/ui/v2/ScrollTop.tsx diff --git a/frontend-next-migration/src/features/ScrollTop/ui/v2/ScrollTop.module.scss b/frontend-next-migration/src/features/ScrollTop/ui/v2/ScrollTop.module.scss new file mode 100644 index 000000000..0c6032576 --- /dev/null +++ b/frontend-next-migration/src/features/ScrollTop/ui/v2/ScrollTop.module.scss @@ -0,0 +1,39 @@ +.ScrollTop { + --primary-color: #FFA100; + --white: #FAF9F6; + --black: #121212; + --border-desktop: 4px; + + color: var(--black); + border: var(--border-desktop) solid var(--black); + border-radius: var(--border-radius-custom); + z-index: var(--navbar-z-index); + background-color: var(--primary-color); + cursor: pointer; + width: 70px; + height: 70px; + position: fixed; + bottom: 117px; // 32px + 70px + 15px + 70px + right: 32px; + opacity: 0; + visibility: hidden; + transition: opacity 0.2s ease-in-out, visibility 0.2s ease-in-out; + transform: rotate(90deg); + font-size: 70px !important; + padding: 4px auto; +} +.ScrollTop::before { + position: absolute; + top:0px; + left:0px; + content:'\226A'; +} + +.show { + opacity: 1; + visibility: visible; +} + + + +// 70px x 70px, bottom 102, right 32px \ No newline at end of file diff --git a/frontend-next-migration/src/features/ScrollTop/ui/v2/ScrollTop.stories.tsx b/frontend-next-migration/src/features/ScrollTop/ui/v2/ScrollTop.stories.tsx new file mode 100644 index 000000000..add9879f4 --- /dev/null +++ b/frontend-next-migration/src/features/ScrollTop/ui/v2/ScrollTop.stories.tsx @@ -0,0 +1,50 @@ +import { ComponentStory, ComponentMeta } from '@storybook/react'; +import React from 'react'; +import { ScrollTop } from './ScrollTop'; + +export default { + title: 'features/ScrollTopV2', + component: ScrollTop, + args: { + className: '', + children: 'UP', + }, +} as ComponentMeta; + +const Template: ComponentStory = (args) => { + return ( + <> +
+ Scroll down to see the button +
+ +
+ ☏ +
+ + ); +}; + +export const Default = Template.bind({}); diff --git a/frontend-next-migration/src/features/ScrollTop/ui/v2/ScrollTop.test.tsx b/frontend-next-migration/src/features/ScrollTop/ui/v2/ScrollTop.test.tsx new file mode 100644 index 000000000..27137ecef --- /dev/null +++ b/frontend-next-migration/src/features/ScrollTop/ui/v2/ScrollTop.test.tsx @@ -0,0 +1,78 @@ +import { render, screen, fireEvent } from '@testing-library/react'; +import * as hooks from '@/shared/lib/hooks'; +import * as i18n from '@/shared/i18n'; +import { ScrollTop } from './ScrollTop'; + +// Mocking the hooks used in the ScrollTop component +jest.mock('@/shared/lib/hooks', () => ({ + useCurrentYPosition: jest.fn(), // Mock the useCurrentYPosition hook +})); + +jest.mock('@/shared/i18n', () => ({ + useClientTranslation: jest.fn(), // Mock the useClientTranslation hook +})); + +describe('ScrollTop', () => { + const mockScrollTo = jest.fn(); // Mock function to simulate scrolling + let originalScrollTo: typeof window.scrollTo; // Store original window.scrollTo function + + beforeAll(() => { + // Before all tests, replace window.scrollTo with the mock function + originalScrollTo = window.scrollTo; + window.scrollTo = mockScrollTo; + }); + + afterAll(() => { + // Restore original window.scrollTo function after all tests + window.scrollTo = originalScrollTo; + }); + + beforeEach(() => { + // Clear all mocks before each test to ensure clean state + jest.clearAllMocks(); + // Mock translation function to return the key as the translation + (i18n.useClientTranslation as jest.Mock).mockReturnValue({ t: (key: string) => key }); + }); + + it('renders correctly with default props', () => { + // Mock the hook to simulate being at the top of the page + (hooks.useCurrentYPosition as jest.Mock).mockReturnValue(0); + + render(); // Render the ScrollTop component + + // Check if the button is in the document and has the correct text and class + const button = screen.getByTestId('scroll-to-top-btn'); + expect(button).toBeInTheDocument(); + expect(button).toHaveClass('ScrollTop'); + }); + + it('shows button when scrolled down', () => { + // Mock the hook to simulate scrolling down the page + (hooks.useCurrentYPosition as jest.Mock).mockReturnValue(window.innerHeight / 4); + render(); // Render the component + + // Verify that the button is visible + expect(screen.getByTestId('scroll-to-top-btn')).toHaveClass('show'); + }); + + it('hides button when scrolled up', () => { + // Mock the hook to simulate being at the top of the page + (hooks.useCurrentYPosition as jest.Mock).mockReturnValue(0); + render(); // Render the component + + // Verify that the button is hidden + expect(screen.getByTestId('scroll-to-top-btn')).not.toHaveClass('show'); + }); + + it('scrolls to top when button is clicked', () => { + // Mock the hook to simulate scrolling down the page + (hooks.useCurrentYPosition as jest.Mock).mockReturnValue(window.innerHeight / 4); + render(); // Render the component + + const button = screen.getByTestId('scroll-to-top-btn'); // Get the button element + fireEvent.click(button); // Simulate a click on the button + + // Check if the mock scrollTo function was called with correct parameters + expect(mockScrollTo).toHaveBeenCalledWith({ top: 0, behavior: 'smooth' }); + }); +}); diff --git a/frontend-next-migration/src/features/ScrollTop/ui/v2/ScrollTop.tsx b/frontend-next-migration/src/features/ScrollTop/ui/v2/ScrollTop.tsx new file mode 100644 index 000000000..4e5070f50 --- /dev/null +++ b/frontend-next-migration/src/features/ScrollTop/ui/v2/ScrollTop.tsx @@ -0,0 +1,40 @@ +'use client'; +import { memo, useCallback, useEffect, useState } from 'react'; +import { classNames } from '@/shared/lib/classNames/classNames'; +import { Button, ButtonSize, ButtonTheme } from '@/shared/ui/Button/Button'; +import { useCurrentYPosition } from '@/shared/lib/hooks'; +import cls from './ScrollTop.module.scss'; + +interface ScrollTopProps { + className?: string; +} + +export const ScrollTop = memo(({ className = '' }: ScrollTopProps) => { + const currentYPosition = useCurrentYPosition(); + + const handleButtonClick = useCallback(() => { + window.scrollTo({ top: 0, behavior: 'smooth' }); + }, []); + + const [showButton, setShowButton] = useState(false); + + useEffect(() => { + if (currentYPosition > window.innerHeight / 6) { + setShowButton(true); + } else { + setShowButton(false); + } + }, [currentYPosition]); + + return ( +