Skip to content
Merged
19 changes: 0 additions & 19 deletions src/components/Navbar/NavbarBody/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,13 @@ import LanguageSelector from '@/components/Navbar/LanguageSelector';
import NavbarLogoWrapper from '@/components/Navbar/Logo/NavbarLogoWrapper';
import NavigationDrawer from '@/components/Navbar/NavigationDrawer/NavigationDrawer';
import SearchDrawer from '@/components/Navbar/SearchDrawer/SearchDrawer';
import SettingsDrawer from '@/components/Navbar/SettingsDrawer/SettingsDrawer';
import Button, { ButtonShape, ButtonVariant } from '@/dls/Button/Button';
import Spinner from '@/dls/Spinner/Spinner';
import IconMenu from '@/icons/menu.svg';
import IconSearch from '@/icons/search.svg';
import IconSettings from '@/icons/settings.svg';
import {
setIsSearchDrawerOpen,
setIsNavigationDrawerOpen,
setIsSettingsDrawerOpen,
setDisableSearchDrawerTransition,
} from '@/redux/slices/navbar';
import { logEvent } from '@/utils/eventLogger';
Expand Down Expand Up @@ -58,11 +55,6 @@ const NavbarBody: React.FC = () => {
dispatch({ type: setDisableSearchDrawerTransition.type, payload: false });
};

const openSettingsDrawer = () => {
logDrawerOpenEvent('settings');
dispatch({ type: setIsSettingsDrawerOpen.type, payload: true });
};

return (
<div className={styles.itemsContainer}>
<div className={styles.centerVertically}>
Expand All @@ -87,17 +79,6 @@ const NavbarBody: React.FC = () => {
<>
<ProfileAvatarButton />
<LanguageSelector />
<Button
tooltip={t('settings.title')}
shape={ButtonShape.Circle}
variant={ButtonVariant.Ghost}
onClick={openSettingsDrawer}
ariaLabel={t('aria.change-settings')}
id="settings-button"
>
<IconSettings />
</Button>
<SettingsDrawer />
</>
<>
<Button
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.settingsButtonIcon {
color: var(--color-blue-buttons-and-icons);
fill: var(--color-blue-buttons-and-icons);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import React from 'react';

import useTranslation from 'next-translate/useTranslation';
import { useDispatch } from 'react-redux';

import styles from './SettingsButton.module.scss';

import Button, { ButtonShape, ButtonVariant } from '@/dls/Button/Button';
import IconSettings from '@/icons/settings.svg';
import { setIsSettingsDrawerOpen } from '@/redux/slices/navbar';
import { logEvent } from '@/utils/eventLogger';

interface SettingsButtonProps {
className?: string;
ariaId?: string;
}

const SettingsButton: React.FC<SettingsButtonProps> = ({
className,
ariaId = 'settings-button',
}) => {
const { t } = useTranslation('common');
const dispatch = useDispatch();
const openSettings = () => {
logEvent('drawer_settings_open');
dispatch(setIsSettingsDrawerOpen(true));
};

return (
<Button
className={className}
tooltip={t('settings.title')}
shape={ButtonShape.Circle}
variant={ButtonVariant.Ghost}
ariaLabel={t('aria.change-settings')}
id={ariaId}
onClick={openSettings}
>
<IconSettings className={styles.settingsButtonIcon} />
</Button>
);
};

export default SettingsButton;
47 changes: 30 additions & 17 deletions src/components/QuranReader/ContextMenu/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, { useMemo } from 'react';

import classNames from 'classnames';

Expand All @@ -11,9 +11,11 @@ import ChapterNavigation from './components/ChapterNavigation';
import MobileReadingTabs from './components/MobileReadingTabs';
import PageInfo from './components/PageInfo';
import ProgressBar from './components/ProgressBar';
import SettingsButton from './components/SettingsButton';
import useContextMenuState from './hooks/useContextMenuState';
import styles from './styles/ContextMenu.module.scss';

import SettingsDrawer from '@/components/Navbar/SettingsDrawer/SettingsDrawer';
import { SwitchSize, SwitchVariant } from '@/dls/Switch/Switch';
import { Mushaf } from '@/types/QuranReader';
import { isMobile } from '@/utils/responsive';
Expand Down Expand Up @@ -48,14 +50,15 @@ const ContextMenu: React.FC = (): JSX.Element | null => {
handleSidebarToggle,
} = useContextMenuState();

const isMobileView = useMemo(() => isMobile(), []);
const isMobileScrolledView = !showNavbar && isMobileView;
const isNotMobileOrScrolledView = !showNavbar || !isMobileView;

// Early return if no verse key (SSR or first render)
if (!verseKey || !chapterData) {
return null;
}

const isMobileScrolledView = !showNavbar && isMobile();
const isNotMobileOrScrolledView = !showNavbar || !isMobile();

return (
<div
className={classNames(styles.container, {
Expand Down Expand Up @@ -84,13 +87,16 @@ const ContextMenu: React.FC = (): JSX.Element | null => {
<div className={styles.sectionsContainer}>
{/* Chapter Navigation Section */}
<div className={styles.section}>
<div className={styles.row}>
<ChapterNavigation
chapterName={chapterData.transliteratedName}
isSidebarNavigationVisible={isSidebarNavigationVisible}
onToggleSidebar={handleSidebarToggle}
chapterNumber={getChapterNumberFromKey(verseKey)}
/>
<div className={classNames(styles.row, styles.chapterNavigationRow)}>
<div className={styles.chapterNavigationWrapper}>
<ChapterNavigation
chapterName={chapterData.transliteratedName}
isSidebarNavigationVisible={isSidebarNavigationVisible}
onToggleSidebar={handleSidebarToggle}
chapterNumber={getChapterNumberFromKey(verseKey)}
/>
{showNavbar && <SettingsButton className={styles.settingsNextToChapter} />}
</div>
</div>
</div>

Expand All @@ -116,12 +122,17 @@ const ContextMenu: React.FC = (): JSX.Element | null => {
[styles.hideReadingPreferenceSectionOnMobile]: showNavbar,
})}
>
<ReadingPreferenceSwitcher
isIconsOnly={isMobileScrolledView}
size={SwitchSize.XSmall}
type={ReadingPreferenceSwitcherType.ContextMenu}
variant={SwitchVariant.Alternative}
/>
<div className={styles.readingPreferenceContainer}>
<ReadingPreferenceSwitcher
isIconsOnly={isMobileScrolledView}
size={SwitchSize.XSmall}
type={ReadingPreferenceSwitcherType.ContextMenu}
variant={SwitchVariant.Alternative}
/>
{(!isMobileView || !showNavbar) && (
<SettingsButton className={styles.settingsNextToSwitcher} />
)}
</div>
</div>
</div>

Expand All @@ -134,6 +145,8 @@ const ContextMenu: React.FC = (): JSX.Element | null => {

{/* Reading progress bar */}
{isNotMobileOrScrolledView && <ProgressBar progress={progress} />}

<SettingsDrawer />
</div>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,17 @@ $pixel: 1px;
padding-block: calc(var(--spacing-medium) / 2);
display: flex;
align-items: center;
padding-inline: calc(1.3 * var(--spacing-large));
padding-inline: var(--spacing-medium3-px);
z-index: var(--z-index-default);
position: relative;
background: var(--color-background-elevated);

@include breakpoints.tablet {
padding-inline: calc(1.2 * var(--spacing-mega));
padding-inline: var(--spacing-xlarge-px);
}

@include breakpoints.smallerThanTablet {
padding-inline: var(--spacing-medium3-px);
}
}

Expand Down Expand Up @@ -100,6 +105,51 @@ $pixel: 1px;
}
}

.readingPreferenceContainer {
display: flex;
align-items: center;
column-gap: var(--spacing-medium);
}

.chapterNavigationRow {
align-items: center;
flex-direction: row;
}

.chapterNavigationWrapper {
display: flex;
align-items: center;
column-gap: var(--spacing-medium);
width: 100%;
justify-content: space-between;

@include breakpoints.tablet {
padding-inline: var(--spacing-medium2-px);
}
}

.visibleContainer .chapterNavigationWrapper {
@include breakpoints.smallerThanTablet {
width: 90vw;
}
}

.settingsNextToChapter {
display: inline-flex;

@include breakpoints.tablet {
display: none;
}

@include breakpoints.smallerThanTablet {
display: inline-flex;
}
}

.settingsNextToSwitcher {
display: inline-flex;
}

.hideReadingPreferenceSectionOnMobile {
@include breakpoints.smallerThanTablet {
visibility: hidden;
Expand Down Expand Up @@ -153,6 +203,7 @@ $pixel: 1px;
color: var(--color-text-default);
}
}

.bookmarkedIcon {
color: var(--color-text-default);
}
Expand All @@ -173,6 +224,7 @@ $pixel: 1px;
align-items: center;
cursor: pointer;
}

.chevronIconContainer {
display: inline-flex;
align-items: center;
Expand All @@ -189,6 +241,7 @@ $pixel: 1px;
.rotateAuto {
transition: var(--transition-fast);
transform: rotate(0);

@include breakpoints.tablet {
transition: var(--transition-fast);
transform: rotate(180deg);
Expand All @@ -197,6 +250,7 @@ $pixel: 1px;

.disabledOnMobile {
pointer-events: none;

@include breakpoints.tablet {
pointer-events: inherit;
}
Expand All @@ -207,4 +261,4 @@ $pixel: 1px;
display: flex;
align-items: center;
font-weight: var(--font-weight-semibold);
}
}
37 changes: 37 additions & 0 deletions tests/integration/quran-reader/settings-gear.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { test, expect } from '@playwright/test';

test('desktop: settings gear opens the settings drawer', async ({ page }) => {
await page.setViewportSize({ width: 1280, height: 900 });
await page.goto('/1');

const settingsButtons = page.locator('#settings-button');
await expect(settingsButtons.last()).toBeVisible();
await settingsButtons.last().click();

await expect(page.locator('#settings-drawer-container')).toBeVisible();
await expect(page.locator('#settings-drawer-body')).toBeVisible();
});

test('mobile: gear works before and after scroll (navbar hidden)', async ({ page }) => {
await page.setViewportSize({ width: 390, height: 844 });
await page.goto('/1');

const settingsButtons = page.locator('#settings-button');
await expect(settingsButtons.first()).toBeVisible();
await settingsButtons.first().click();
await expect(page.locator('#settings-drawer-container')).toBeVisible();
await expect(page.locator('#settings-drawer-body')).toBeVisible();

await page
.locator('#settings-drawer-container')
.getByRole('button', { name: /close drawer/i })
.first()
.click();

await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight));

await expect(settingsButtons.last()).toBeVisible();
await settingsButtons.last().click();
await expect(page.locator('#settings-drawer-container')).toBeVisible();
await expect(page.locator('#settings-drawer-body')).toBeVisible();
});