Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions galasa-ui/src/components/headers/GalasaMenuItems.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright contributors to the Galasa project
*
* SPDX-License-Identifier: EPL-2.0
*/
'use client';

import { HeaderMenuItem } from '@carbon/react';
import { useFeatureFlags } from '@/contexts/FeatureFlagContext';
import { FEATURE_FLAGS } from '@/utils/featureFlags';
import { useTranslations } from 'next-intl';

export default function GalasaMenuItems() {
const { isFeatureEnabled } = useFeatureFlags();
const translations = useTranslations('PageHeader');

return (
<>
<HeaderMenuItem href="/users">{translations('users')}</HeaderMenuItem>
{isFeatureEnabled(FEATURE_FLAGS.TEST_RUNS) && (
<HeaderMenuItem href="/test-runs">{translations('testRuns')}</HeaderMenuItem>
)}
</>
);
}
62 changes: 29 additions & 33 deletions galasa-ui/src/components/headers/LanguageSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

'use client';

import React, { useState, useTransition } from 'react';
import { useState, useTransition } from 'react';
import { OverflowMenu, OverflowMenuItem } from '@carbon/react';
import { setUserLocale } from '@/utils/locale';
import { useLocale, useTranslations } from 'next-intl';
Expand All @@ -26,7 +26,7 @@ export default function LanguageSelector() {
languages.find((lang) => lang.value === locale) || languages[0]
);

const [isPending, startTransition] = useTransition();
const [, startTransition] = useTransition();
const router = useRouter();
const translations = useTranslations('LanguageSelector');

Expand All @@ -46,36 +46,32 @@ export default function LanguageSelector() {
};

return (
<div data-floating-menu-container>
<OverflowMenu
data-floating-menu-container
className={styles.overflowMenu}
focusTrap={true}
align="bottom"
flipped
renderIcon={() => <Wikis className={styles.renderIcon} />}
size="lg"
iconDescription={`${translations('tooltip')}: ${selectedLanguage.text}`}
aria-label="Filter menu"
tooltipAlignment="center"
tooltipPosition="bottom"
>
{languages.map((language) => (
<OverflowMenuItem
key={language.id}
className={styles.overflowMenuItem}
itemText={
<div className={styles.overflowMenuItemText}>
<span className={styles.languageText}>{language.text}</span>
{selectedLanguage.id === language.id && (
<Checkmark size={16} className={styles.checkmark} />
)}
</div>
}
onClick={() => handleLanguageChange({ selectedItem: language })}
/>
))}
</OverflowMenu>
</div>
<OverflowMenu
data-floating-menu-container
className={styles.overflowMenu}
focusTrap={true}
align="bottom"
flipped
renderIcon={() => <Wikis className={styles.renderIcon} />}
size="lg"
iconDescription={`${translations('tooltip')}: ${selectedLanguage.text}`}
aria-label="Filter menu"
>
{languages.map((language) => (
<OverflowMenuItem
key={language.id}
className={styles.overflowMenuItem}
itemText={
<div className={styles.overflowMenuItemText}>
<span className={styles.languageText}>{language.text}</span>
{selectedLanguage.id === language.id && (
<Checkmark size={16} className={styles.checkmark} />
)}
</div>
}
onClick={() => handleLanguageChange({ selectedItem: language })}
/>
))}
</OverflowMenu>
);
}
60 changes: 37 additions & 23 deletions galasa-ui/src/components/headers/PageHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,45 +5,59 @@
*/
'use client';

import {
Header,
HeaderName,
SkipToContent,
Theme,
HeaderNavigation,
HeaderMenuItem,
} from '@carbon/react';
import { Header, HeaderName, SkipToContent, Theme, HeaderNavigation } from '@carbon/react';
import PageHeaderMenu from './PageHeaderMenu';
import Image from 'next/image';
import galasaLogo from '@/assets/images/galasaLogo.png';
import Link from 'next/link';
import { useFeatureFlags } from '@/contexts/FeatureFlagContext';
import { FEATURE_FLAGS } from '@/utils/featureFlags';
import { useTranslations } from 'next-intl';
import { SideNav } from '@carbon/react';
import { SideNavItems } from '@carbon/react';
import { HeaderSideNavItems } from '@carbon/react';
import { HeaderMenuButton } from '@carbon/react';
import { useState } from 'react';
import styles from '@/styles/headers/PageHeader.module.css';
import GalasaMenuItems from './GalasaMenuItems';

export default function PageHeader({ galasaServiceName }: { galasaServiceName: string }) {
const { isFeatureEnabled } = useFeatureFlags();
const translations = useTranslations('PageHeader');
const [isSideNavExpanded, setIsSideNavExpanded] = useState(false);

const onClickSideNavExpand = () => {
setIsSideNavExpanded(!isSideNavExpanded);
};

return (
<Theme theme="g90">
<Header aria-label="Galasa Ecosystem">
<SkipToContent />

<Link href={'/'} style={{ paddingLeft: '0.5rem' }}>
<Image src={galasaLogo} width={28} height={28} alt="Galasa logo" />
</Link>
<HeaderMenuButton
aria-label={isSideNavExpanded ? 'Close menu' : 'Open menu'}
onClick={onClickSideNavExpand}
isActive={isSideNavExpanded}
aria-expanded={isSideNavExpanded}
/>

<HeaderName href="/" prefix="">
Galasa
<span className={styles.headerName} aria-label="Header name">
<Image src={galasaLogo} width={28} height={28} alt="Galasa logo" /> Galasa
</span>
</HeaderName>

<HeaderNavigation aria-label="Galasa menu bar navigation">
<HeaderMenuItem href="/users">{translations('users')}</HeaderMenuItem>
{isFeatureEnabled(FEATURE_FLAGS.TEST_RUNS) && (
<HeaderMenuItem href="/test-runs">{translations('testRuns')}</HeaderMenuItem>
)}
<GalasaMenuItems />
</HeaderNavigation>

<SideNav
aria-label="Side navigation"
expanded={isSideNavExpanded}
isPersistent={false}
onSideNavBlur={onClickSideNavExpand}
>
<SideNavItems>
<HeaderSideNavItems>
<GalasaMenuItems />
</HeaderSideNavItems>
</SideNavItems>
</SideNav>

<PageHeaderMenu galasaServiceName={galasaServiceName} />
</Header>
</Theme>
Expand Down
5 changes: 4 additions & 1 deletion galasa-ui/src/components/headers/PageHeaderMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { handleDeleteCookieApiOperation } from '@/utils/logout';
import LanguageSelector from './LanguageSelector';
import { useTranslations } from 'next-intl';
import ThemeSelector from './ThemeSelector';
import styles from '@/styles/headers/PageHeader.module.css';

function PageHeaderMenu({ galasaServiceName }: { galasaServiceName: string }) {
const translations = useTranslations('PageHeaderMenu');
Expand All @@ -30,7 +31,9 @@ function PageHeaderMenu({ galasaServiceName }: { galasaServiceName: string }) {
<HeaderGlobalBar data-testid="header-menu">
<LanguageSelector />
<ThemeSelector />
<HeaderName prefix="">{galasaServiceName}</HeaderName>
<HeaderName id={styles.serviceName} prefix="">
{galasaServiceName}
</HeaderName>
<OverflowMenu
data-floating-menu-container
selectorPrimaryFocus={'.optionOne'}
Expand Down
43 changes: 12 additions & 31 deletions galasa-ui/src/components/headers/ThemeSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,10 @@

'use client';

import React, { useTransition } from 'react';
import styles from '@/styles/headers/Selector.module.css';
import { useTransition } from 'react';
import { ThemeType, useTheme } from '@/contexts/ThemeContext';
import { Sun, Moon, Laptop } from '@carbon/icons-react';
import { Tooltip } from '@carbon/react';
import { Theme } from '@carbon/react';
import { HeaderGlobalAction } from '@carbon/react';

const themeOptions: { id: ThemeType; label: string; icon: React.ReactNode; tooltip: string }[] = [
{ id: 'light', label: 'Light', icon: <Sun size={20} />, tooltip: 'Switch to light mode' },
Expand All @@ -26,42 +24,25 @@ const themeOptions: { id: ThemeType; label: string; icon: React.ReactNode; toolt

export default function ThemeSelector() {
const { theme, setTheme } = useTheme();
const [isPending, startTransition] = useTransition();
const [, startTransition] = useTransition();
const idx = themeOptions.findIndex((o) => o.id === theme);
const currentTheme = themeOptions[idx] || themeOptions[0];
const next = themeOptions[(idx + 1) % themeOptions.length];

const cycleTheme = () => {
startTransition(() => {
setTheme(next.id as ThemeType);
setTheme(next.id);
});
};
let current: 'g10' | 'g90';

if (theme === 'light') {
current = 'g10';
} else if (theme === 'dark') {
current = 'g90';
} else if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
current = 'g90';
} else {
current = 'g10';
}

return (
<div className={styles.themeSwitcher}>
<Theme theme={current} className={styles.themeContainer}>
<Tooltip label={next.tooltip} align="bottom">
<button
onClick={cycleTheme}
className={styles.iconButton + (currentTheme.id === theme ? ` ${styles.active}` : '')}
disabled={isPending}
aria-label={currentTheme.label}
>
{currentTheme.icon}
</button>
</Tooltip>
</Theme>
</div>
<HeaderGlobalAction
data-floating-menu-container
aria-label={next.tooltip}
tooltipAlignment="center"
Comment thread
eamansour marked this conversation as resolved.
onClick={cycleTheme}
>
{currentTheme.icon}
</HeaderGlobalAction>
);
}
18 changes: 18 additions & 0 deletions galasa-ui/src/styles/headers/PageHeader.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* Copyright contributors to the Galasa project
*
* SPDX-License-Identifier: EPL-2.0
*/

@media (max-width: 768px) {
#serviceName {
display: none;
padding: 0;
}
}

.headerName {
display: flex;
align-items: center;
gap: 5px;
}
51 changes: 0 additions & 51 deletions galasa-ui/src/styles/headers/Selector.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,57 +3,6 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
.container {
margin-right: 1rem;
display: flex; /* Removed the extra comma */
align-items: center;
}

.language {
margin-right: 0.5rem;
fill: white;
}

.dropdown {
background-color: transparent; /* Corrected to background-color */
color: white;
width: 120px;
}

.themeSwitcher {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.25rem 0.5rem;
width: fit-content;
background-color: #262626;
}

.iconButton {
background: #262626;
border: none;
padding: 0.85rem;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition:
background-color 0.2s ease,
color 0.2s ease;
}

.iconButton:hover {
background-color: #474747;
}

.iconButton:disabled {
cursor: not-allowed;
opacity: 0.6;
}

.active {
color: var(--cds-icon-on-color);
}

.overflowMenu {
outline: none !important;
Expand Down
Loading
Loading