Skip to content

Commit 507a225

Browse files
QF-2751: Show the settings/preferences icon on the surah header (#2554)
* Refactor Navbar and ContextMenu to integrate SettingsButton and SettingsDrawer - Removed SettingsDrawer from NavbarBody and added SettingsButton to ContextMenu for better accessibility. - Introduced a new SettingsButton component to handle opening the SettingsDrawer. - Updated ContextMenu layout to accommodate the new SettingsButton while maintaining responsiveness. * Update ContextMenu styles for improved layout and responsiveness - Adjusted padding values for better alignment in the ContextMenu. - Added new styles for reading preferences and chapter navigation components. - Enhanced responsiveness for chapter navigation and settings display on mobile devices. * Add event logging and integration tests for SettingsButton functionality - Integrated event logging to track when the settings drawer is opened. - Created integration tests to verify the visibility and functionality of the settings drawer on both desktop and mobile views. * Update ContextMenu to clarify SettingsDrawer rendering - Changed comment to specify that the SettingsDrawer component is being rendered in the ContextMenu. * Refactor ContextMenu and SettingsButton for improved performance and clarity * Fix logic in ContextMenu for mobile view handling and update SettingsButton props interface * Update ContextMenu styles for improved responsiveness and layout adjustments * Add SettingsDrawer to NavbarBody and remove from ContextMenu for improved structure
1 parent 15650e7 commit 507a225

File tree

6 files changed

+172
-39
lines changed

6 files changed

+172
-39
lines changed

src/components/Navbar/NavbarBody/index.tsx

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,22 @@ import dynamic from 'next/dynamic';
44
import useTranslation from 'next-translate/useTranslation';
55
import { useDispatch } from 'react-redux';
66

7+
import SettingsDrawer from '../SettingsDrawer/SettingsDrawer';
8+
79
import styles from './NavbarBody.module.scss';
810
import ProfileAvatarButton from './ProfileAvatarButton';
911

1012
import LanguageSelector from '@/components/Navbar/LanguageSelector';
1113
import NavbarLogoWrapper from '@/components/Navbar/Logo/NavbarLogoWrapper';
1214
import NavigationDrawer from '@/components/Navbar/NavigationDrawer/NavigationDrawer';
1315
import SearchDrawer from '@/components/Navbar/SearchDrawer/SearchDrawer';
14-
import SettingsDrawer from '@/components/Navbar/SettingsDrawer/SettingsDrawer';
1516
import Button, { ButtonShape, ButtonVariant } from '@/dls/Button/Button';
1617
import Spinner from '@/dls/Spinner/Spinner';
1718
import IconMenu from '@/icons/menu.svg';
1819
import IconSearch from '@/icons/search.svg';
19-
import IconSettings from '@/icons/settings.svg';
2020
import {
2121
setIsSearchDrawerOpen,
2222
setIsNavigationDrawerOpen,
23-
setIsSettingsDrawerOpen,
2423
setDisableSearchDrawerTransition,
2524
} from '@/redux/slices/navbar';
2625
import { logEvent } from '@/utils/eventLogger';
@@ -58,11 +57,6 @@ const NavbarBody: React.FC = () => {
5857
dispatch({ type: setDisableSearchDrawerTransition.type, payload: false });
5958
};
6059

61-
const openSettingsDrawer = () => {
62-
logDrawerOpenEvent('settings');
63-
dispatch({ type: setIsSettingsDrawerOpen.type, payload: true });
64-
};
65-
6660
return (
6761
<div className={styles.itemsContainer}>
6862
<div className={styles.centerVertically}>
@@ -87,17 +81,6 @@ const NavbarBody: React.FC = () => {
8781
<>
8882
<ProfileAvatarButton />
8983
<LanguageSelector />
90-
<Button
91-
tooltip={t('settings.title')}
92-
shape={ButtonShape.Circle}
93-
variant={ButtonVariant.Ghost}
94-
onClick={openSettingsDrawer}
95-
ariaLabel={t('aria.change-settings')}
96-
id="settings-button"
97-
>
98-
<IconSettings />
99-
</Button>
100-
<SettingsDrawer />
10184
</>
10285
<>
10386
<Button
@@ -112,6 +95,7 @@ const NavbarBody: React.FC = () => {
11295
</Button>
11396
<SearchDrawer />
11497
<SidebarNavigation />
98+
<SettingsDrawer />
11599
</>
116100
</div>
117101
</div>
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.settingsButtonIcon {
2+
color: var(--color-blue-buttons-and-icons);
3+
fill: var(--color-blue-buttons-and-icons);
4+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import React from 'react';
2+
3+
import useTranslation from 'next-translate/useTranslation';
4+
import { useDispatch } from 'react-redux';
5+
6+
import styles from './SettingsButton.module.scss';
7+
8+
import Button, { ButtonShape, ButtonVariant } from '@/dls/Button/Button';
9+
import IconSettings from '@/icons/settings.svg';
10+
import { setIsSettingsDrawerOpen } from '@/redux/slices/navbar';
11+
import { logEvent } from '@/utils/eventLogger';
12+
13+
interface SettingsButtonProps {
14+
className?: string;
15+
ariaId?: string;
16+
}
17+
18+
const SettingsButton: React.FC<SettingsButtonProps> = ({
19+
className,
20+
ariaId = 'settings-button',
21+
}) => {
22+
const { t } = useTranslation('common');
23+
const dispatch = useDispatch();
24+
const openSettings = () => {
25+
logEvent('drawer_settings_open');
26+
dispatch(setIsSettingsDrawerOpen(true));
27+
};
28+
29+
return (
30+
<Button
31+
className={className}
32+
tooltip={t('settings.title')}
33+
shape={ButtonShape.Circle}
34+
variant={ButtonVariant.Ghost}
35+
ariaLabel={t('aria.change-settings')}
36+
id={ariaId}
37+
onClick={openSettings}
38+
>
39+
<IconSettings className={styles.settingsButtonIcon} />
40+
</Button>
41+
);
42+
};
43+
44+
export default SettingsButton;

src/components/QuranReader/ContextMenu/index.tsx

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React from 'react';
1+
import React, { useMemo } from 'react';
22

33
import classNames from 'classnames';
44

@@ -11,6 +11,7 @@ import ChapterNavigation from './components/ChapterNavigation';
1111
import MobileReadingTabs from './components/MobileReadingTabs';
1212
import PageInfo from './components/PageInfo';
1313
import ProgressBar from './components/ProgressBar';
14+
import SettingsButton from './components/SettingsButton';
1415
import useContextMenuState from './hooks/useContextMenuState';
1516
import styles from './styles/ContextMenu.module.scss';
1617

@@ -48,14 +49,15 @@ const ContextMenu: React.FC = (): JSX.Element | null => {
4849
handleSidebarToggle,
4950
} = useContextMenuState();
5051

52+
const isMobileView = useMemo(() => isMobile(), []);
53+
const isMobileScrolledView = !showNavbar && isMobileView;
54+
const isNotMobileOrScrolledView = !showNavbar || !isMobileView;
55+
5156
// Early return if no verse key (SSR or first render)
5257
if (!verseKey || !chapterData) {
5358
return null;
5459
}
5560

56-
const isMobileScrolledView = !showNavbar && isMobile();
57-
const isNotMobileOrScrolledView = !showNavbar || !isMobile();
58-
5961
return (
6062
<div
6163
className={classNames(styles.container, {
@@ -84,13 +86,16 @@ const ContextMenu: React.FC = (): JSX.Element | null => {
8486
<div className={styles.sectionsContainer}>
8587
{/* Chapter Navigation Section */}
8688
<div className={styles.section}>
87-
<div className={styles.row}>
88-
<ChapterNavigation
89-
chapterName={chapterData.transliteratedName}
90-
isSidebarNavigationVisible={isSidebarNavigationVisible}
91-
onToggleSidebar={handleSidebarToggle}
92-
chapterNumber={getChapterNumberFromKey(verseKey)}
93-
/>
89+
<div className={classNames(styles.row, styles.chapterNavigationRow)}>
90+
<div className={styles.chapterNavigationWrapper}>
91+
<ChapterNavigation
92+
chapterName={chapterData.transliteratedName}
93+
isSidebarNavigationVisible={isSidebarNavigationVisible}
94+
onToggleSidebar={handleSidebarToggle}
95+
chapterNumber={getChapterNumberFromKey(verseKey)}
96+
/>
97+
{showNavbar && <SettingsButton className={styles.settingsNextToChapter} />}
98+
</div>
9499
</div>
95100
</div>
96101

@@ -116,12 +121,17 @@ const ContextMenu: React.FC = (): JSX.Element | null => {
116121
[styles.hideReadingPreferenceSectionOnMobile]: showNavbar,
117122
})}
118123
>
119-
<ReadingPreferenceSwitcher
120-
isIconsOnly={isMobileScrolledView}
121-
size={SwitchSize.XSmall}
122-
type={ReadingPreferenceSwitcherType.ContextMenu}
123-
variant={SwitchVariant.Alternative}
124-
/>
124+
<div className={styles.readingPreferenceContainer}>
125+
<ReadingPreferenceSwitcher
126+
isIconsOnly={isMobileScrolledView}
127+
size={SwitchSize.XSmall}
128+
type={ReadingPreferenceSwitcherType.ContextMenu}
129+
variant={SwitchVariant.Alternative}
130+
/>
131+
{(!isMobileView || !showNavbar) && (
132+
<SettingsButton className={styles.settingsNextToSwitcher} />
133+
)}
134+
</div>
125135
</div>
126136
</div>
127137

src/components/QuranReader/ContextMenu/styles/ContextMenu.module.scss

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,17 @@ $pixel: 1px;
6262
padding-block: calc(var(--spacing-medium) / 2);
6363
display: flex;
6464
align-items: center;
65-
padding-inline: calc(1.3 * var(--spacing-large));
65+
padding-inline: var(--spacing-medium3-px);
6666
z-index: var(--z-index-default);
6767
position: relative;
6868
background: var(--color-background-elevated);
69+
6970
@include breakpoints.tablet {
70-
padding-inline: calc(1.2 * var(--spacing-mega));
71+
padding-inline: var(--spacing-xlarge-px);
72+
}
73+
74+
@include breakpoints.smallerThanTablet {
75+
padding-inline: var(--spacing-medium3-px);
7176
}
7277
}
7378

@@ -100,6 +105,51 @@ $pixel: 1px;
100105
}
101106
}
102107

108+
.readingPreferenceContainer {
109+
display: flex;
110+
align-items: center;
111+
column-gap: var(--spacing-medium);
112+
}
113+
114+
.chapterNavigationRow {
115+
align-items: center;
116+
flex-direction: row;
117+
}
118+
119+
.chapterNavigationWrapper {
120+
display: flex;
121+
align-items: center;
122+
column-gap: var(--spacing-medium);
123+
width: 100%;
124+
justify-content: space-between;
125+
126+
@include breakpoints.tablet {
127+
padding-inline: var(--spacing-medium2-px);
128+
}
129+
}
130+
131+
.visibleContainer .chapterNavigationWrapper {
132+
@include breakpoints.smallerThanTablet {
133+
width: 90vw;
134+
}
135+
}
136+
137+
.settingsNextToChapter {
138+
display: inline-flex;
139+
140+
@include breakpoints.tablet {
141+
display: none;
142+
}
143+
144+
@include breakpoints.smallerThanTablet {
145+
display: inline-flex;
146+
}
147+
}
148+
149+
.settingsNextToSwitcher {
150+
display: inline-flex;
151+
}
152+
103153
.hideReadingPreferenceSectionOnMobile {
104154
@include breakpoints.smallerThanTablet {
105155
visibility: hidden;
@@ -153,6 +203,7 @@ $pixel: 1px;
153203
color: var(--color-text-default);
154204
}
155205
}
206+
156207
.bookmarkedIcon {
157208
color: var(--color-text-default);
158209
}
@@ -173,6 +224,7 @@ $pixel: 1px;
173224
align-items: center;
174225
cursor: pointer;
175226
}
227+
176228
.chevronIconContainer {
177229
display: inline-flex;
178230
align-items: center;
@@ -189,6 +241,7 @@ $pixel: 1px;
189241
.rotateAuto {
190242
transition: var(--transition-fast);
191243
transform: rotate(0);
244+
192245
@include breakpoints.tablet {
193246
transition: var(--transition-fast);
194247
transform: rotate(180deg);
@@ -197,6 +250,7 @@ $pixel: 1px;
197250

198251
.disabledOnMobile {
199252
pointer-events: none;
253+
200254
@include breakpoints.tablet {
201255
pointer-events: inherit;
202256
}
@@ -207,4 +261,4 @@ $pixel: 1px;
207261
display: flex;
208262
align-items: center;
209263
font-weight: var(--font-weight-semibold);
210-
}
264+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { test, expect } from '@playwright/test';
2+
3+
test('desktop: settings gear opens the settings drawer', async ({ page }) => {
4+
await page.setViewportSize({ width: 1280, height: 900 });
5+
await page.goto('/1');
6+
7+
const settingsButtons = page.locator('#settings-button');
8+
await expect(settingsButtons.last()).toBeVisible();
9+
await settingsButtons.last().click();
10+
11+
await expect(page.locator('#settings-drawer-container')).toBeVisible();
12+
await expect(page.locator('#settings-drawer-body')).toBeVisible();
13+
});
14+
15+
test('mobile: gear works before and after scroll (navbar hidden)', async ({ page }) => {
16+
await page.setViewportSize({ width: 390, height: 844 });
17+
await page.goto('/1');
18+
19+
const settingsButtons = page.locator('#settings-button');
20+
await expect(settingsButtons.first()).toBeVisible();
21+
await settingsButtons.first().click();
22+
await expect(page.locator('#settings-drawer-container')).toBeVisible();
23+
await expect(page.locator('#settings-drawer-body')).toBeVisible();
24+
25+
await page
26+
.locator('#settings-drawer-container')
27+
.getByRole('button', { name: /close drawer/i })
28+
.first()
29+
.click();
30+
31+
await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight));
32+
33+
await expect(settingsButtons.last()).toBeVisible();
34+
await settingsButtons.last().click();
35+
await expect(page.locator('#settings-drawer-container')).toBeVisible();
36+
await expect(page.locator('#settings-drawer-body')).toBeVisible();
37+
});

0 commit comments

Comments
 (0)