Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
4fffa0e
feat: add nau basic customization (#1)
dcoa Jan 26, 2024
9046c4f
fix: organization logo display
Mar 19, 2024
e082170
style: fix lint issues
dcoa Jun 25, 2024
517cfba
fix: ENABLED_ORG_LOGO definition
dcoa Jul 1, 2024
faa4a78
fix: rename ENABLED_ORG_LOGO setting
Aug 20, 2024
ca69fcf
fix: github actions release flow
Sep 17, 2024
b838c55
feat: add header lang selector
dcoa Dec 11, 2024
b84a2ba
fix: reading supported language from mfe config api
igobranco Dec 11, 2024
3dfa2e0
fix: typo
igobranco Dec 11, 2024
fc0087f
feat: replace language selector from select to dropdown
igobranco Dec 11, 2024
2c44352
feat: alway show language selector on learning
igobranco Dec 11, 2024
c591474
docs: add language selector to README
igobranco Dec 11, 2024
baa97f7
feat: show mobile logo
igobranco Dec 11, 2024
2387cbb
feat: Language selector with optional compact
igobranco Dec 11, 2024
baa29bf
fix: tests
igobranco Dec 11, 2024
609a369
fix: display org logo when is enable
dcoa Dec 5, 2024
07f8310
feat: review header
igobranco Dec 12, 2024
dfa5d48
chore(release): v18.1.0
igobranco Dec 12, 2024
f03ad61
fix: only get org logo if user is authenticated
igobranco Jan 8, 2025
19eac66
fix: header language selector
igobranco Jan 9, 2025
39da93a
chore(release): v18.1.1
igobranco Jan 9, 2025
369ddff
fix: only get org logo if user is authenticated
igobranco Jan 9, 2025
f95eedd
fix: call organization API if user is authenticated
igobranco Jan 9, 2025
dc90243
chore(release): v18.1.2
igobranco Jan 9, 2025
9c777d0
feat: use the compact version of language select
igobranco Apr 30, 2025
c85a801
fix: wide header on mobile
May 3, 2025
ef01471
chore(release): v18.2.0
May 5, 2025
eed118a
fix: logo container height and alignment
May 14, 2025
f715167
chore(release): v18.2.1
May 15, 2025
6f926fa
fix: update import path for LanguageSelector in DesktopHeader
bra-i-am Nov 11, 2025
f552fb3
fix: linting errors
Dec 3, 2025
3f05ffe
fix: refactor tests to use history.pushState for URL changes
bra-i-am Dec 3, 2025
245a315
chore(release): v20.0.0
efortish Dec 3, 2025
9c42c03
feat: added new header help slot
DeimerM Apr 7, 2026
2420e36
chore(release): v20.1.0
ManuelStarDo Apr 28, 2026
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
13 changes: 5 additions & 8 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ name: Release CI
on:
push:
branches:
- master
- alpha
- nau/*.master

jobs:
release:
name: Release
Expand Down Expand Up @@ -38,10 +38,7 @@ jobs:
fail_ci_if_error: false
- name: Build
run: npm run build
- name: Release
uses: cycjimmy/semantic-release-action@v3
- name: Release to npmjs
uses: JS-DevTools/npm-publish@v3
with:
semantic_version: 16
env:
GITHUB_TOKEN: ${{ secrets.SEMANTIC_RELEASE_GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.SEMANTIC_RELEASE_NPM_TOKEN }}
token: ${{ secrets.NPM_AUTH_TOKEN }}
4 changes: 4 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ Environment Variables
* ``AUTHN_MINIMAL_HEADER`` - A boolean flag which hides the main menu, user menu, and logged-out
menu items when truthy. This is intended to be used in micro-frontends like
frontend-app-authentication in which these menus are considered distractions from the user's task.
* ``ENABLE_HEADER_LANG_SELECTOR`` - A boolean to enable the language selector in the header component.
* ``SITE_SUPPORTED_LANGUAGES`` - A list with all the languages to display in the selector.
* ``LOGO_URL_MOBILE`` - The URL of the site's logo for mobile. This logo is displayed in the header.


Installation
============
Expand Down
12 changes: 6 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@edx/frontend-component-header",
"version": "1.0.0-semantically-released",
"description": "The standard header for Open edX",
"name": "@nauedu/frontend-component-header",
"version": "20.1.0",
"description": "NAU header component for Open edX",
"main": "dist/index.js",
"publishConfig": {
"access": "public"
Expand All @@ -22,14 +22,14 @@
],
"repository": {
"type": "git",
"url": "git+https://github.com/openedx/frontend-component-header.git"
"url": "git+https://github.com/fccn/frontend-component-header-nau.git"
},
"author": "edX",
"license": "AGPL-3.0",
"bugs": {
"url": "https://github.com/openedx/frontend-component-header/issues"
"url": "https://github.com/fccn/frontend-component-header-nau/issues"
},
"homepage": "https://github.com/openedx/frontend-component-header#readme",
"homepage": "https://github.com/fccn/frontend-component-header-nau#readme",
"devDependencies": {
"@edx/brand": "npm:@openedx/brand-openedx@^1.2.2",
"@edx/browserslist-config": "^1.1.1",
Expand Down
5 changes: 3 additions & 2 deletions src/Header.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Responsive from 'react-responsive';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { AppContext } from '@edx/frontend-platform/react';
import {
APP_CONFIG_INITIALIZED,
APP_PUBSUB_INITIALIZED,
ensureConfig,
mergeConfig,
getConfig,
Expand All @@ -25,9 +25,10 @@ ensureConfig([
'ORDER_HISTORY_URL',
], 'Header component');

subscribe(APP_CONFIG_INITIALIZED, () => {
subscribe(APP_PUBSUB_INITIALIZED, () => {
mergeConfig({
AUTHN_MINIMAL_HEADER: !!process.env.AUTHN_MINIMAL_HEADER,
ENABLE_ORG_LOGO: !!process.env.ENABLE_ORG_LOGO,
}, 'Header additional config');
});

Expand Down
32 changes: 32 additions & 0 deletions src/LanguageSelector/data/api.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { getConfig } from '@edx/frontend-platform';
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
import { convertKeyNames, snakeCaseObject } from '@edx/frontend-platform/utils';

export async function patchPreferences(username, params) {
let processedParams = snakeCaseObject(params);
processedParams = convertKeyNames(processedParams, {
pref_lang: 'pref-lang',
});

await getAuthenticatedHttpClient()
.patch(`${getConfig().LMS_BASE_URL}/api/user/v1/preferences/${username}`, processedParams, {
headers: { 'Content-Type': 'application/merge-patch+json' },
});

return params;
}

export async function postSetLang(code) {
const formData = new FormData();
const requestConfig = {
headers: {
Accept: 'application/json',
'X-Requested-With': 'XMLHttpRequest',
},
};
const url = `${getConfig().LMS_BASE_URL}/i18n/setlang/`;
formData.append('language', code);

await getAuthenticatedHttpClient()
.post(url, formData, requestConfig);
}
98 changes: 98 additions & 0 deletions src/LanguageSelector/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import React from 'react';
import PropTypes from 'prop-types';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faGlobe } from '@fortawesome/free-solid-svg-icons';
import { publish } from '@edx/frontend-platform';
import {
getLocale, injectIntl, intlShape, FormattedMessage, LOCALE_CHANGED, handleRtl,
} from '@edx/frontend-platform/i18n';
import { Dropdown } from '@openedx/paragon';
import { logError } from '@edx/frontend-platform/logging';

import { patchPreferences, postSetLang } from './data/api';

const onLanguageSelected = async (username, selectedLanguageCode) => {
try {
if (username) {
await patchPreferences(username, { prefLang: selectedLanguageCode });
await postSetLang(selectedLanguageCode);
}
publish(LOCALE_CHANGED, getLocale());
handleRtl();
} catch (error) {
logError(error);
}
};

const LanguageSelector = ({
intl, options, authenticatedUser, compact,
}) => {
const languageLabel = (languageCode) => {
const option = options.find(({ value }) => value === languageCode);
return option ? option.label : null;
};

const handleChange = (languageCode, event) => {
const previousSiteLanguage = getLocale();
/* eslint-disable no-console */
console.debug(previousSiteLanguage, languageCode, authenticatedUser);

if (previousSiteLanguage !== languageCode) {
onLanguageSelected(authenticatedUser?.username, languageCode);
}

const languageLabelElement = event.target.parentElement.parentElement.querySelector('.languageLabel');
languageLabelElement.innerHTML = languageLabel(languageCode);
};

const currentLangLabel = languageLabel(intl.locale);
const showLabel = !(compact || false);

return (
<Dropdown className="language-selector">
<Dropdown.Toggle variant="outline-primary">
<FontAwesomeIcon icon={faGlobe} />
{showLabel && (
currentLangLabel ? (
<span className="pl-1 languageLabel">
{currentLangLabel}
</span>
) : (
<span className="pl-1">
<FormattedMessage
id="footer.languageForm.select.label"
defaultMessage="Choose Language"
description="The label for the laguage select part of the language selection form."
/>
</span>
)
)}
</Dropdown.Toggle>
<Dropdown.Menu>
{options.map(({ value, label }) => (
<Dropdown.Item key={value} eventKey={value} onSelect={handleChange}>
{label}
</Dropdown.Item>
))}
</Dropdown.Menu>
</Dropdown>
);
};

LanguageSelector.propTypes = {
authenticatedUser: PropTypes.shape({
username: PropTypes.string,
}).isRequired,
intl: intlShape.isRequired,
compact: PropTypes.bool,
options: PropTypes.arrayOf(PropTypes.shape({
value: PropTypes.string,
label: PropTypes.string,
})).isRequired,
};

LanguageSelector.defaultProps = {
compact: false,
};

export default injectIntl(LanguageSelector);
8 changes: 4 additions & 4 deletions src/__snapshots__/Header.test.jsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ exports[`<Header /> renders correctly for anonymous desktop 1`] = `
</a>
<nav
aria-label="Main"
className="nav main-nav"
className="nav main-nav mr-auto"
>
<a
className="nav-link"
Expand All @@ -40,7 +40,7 @@ exports[`<Header /> renders correctly for anonymous desktop 1`] = `
</nav>
<nav
aria-label="Secondary"
className="nav secondary-menu-container align-items-center ml-auto"
className="nav secondary-menu-container align-items-center"
>
<a
className="btn mr-2 btn-link"
Expand Down Expand Up @@ -225,7 +225,7 @@ exports[`<Header /> renders correctly for authenticated desktop 1`] = `
</a>
<nav
aria-label="Main"
className="nav main-nav"
className="nav main-nav mr-auto"
>
<a
className="nav-link"
Expand All @@ -237,7 +237,7 @@ exports[`<Header /> renders correctly for authenticated desktop 1`] = `
</nav>
<nav
aria-label="Secondary"
className="nav secondary-menu-container align-items-center ml-auto"
className="nav secondary-menu-container align-items-center"
>
<div
className="menu null"
Expand Down
14 changes: 12 additions & 2 deletions src/desktop-header/DesktopHeader.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { getConfig } from '@edx/frontend-platform';
import { getAuthenticatedUser } from '@edx/frontend-platform/auth';

// Local Components
import DesktopUserMenuToggleSlot
Expand All @@ -20,6 +21,7 @@ import { desktopUserMenuDataShape } from './DesktopHeaderUserMenu';
import messages from '../Header.messages';

// Assets
import LanguageSelector from '../LanguageSelector';

class DesktopHeader extends React.Component {
constructor(props) { // eslint-disable-line @typescript-eslint/no-useless-constructor
Expand Down Expand Up @@ -84,13 +86,21 @@ class DesktopHeader extends React.Component {
<LogoSlot {...logoProps} />
<nav
aria-label={intl.formatMessage(messages['header.label.main.nav'])}
className="nav main-nav"
className="nav main-nav mr-auto"
>
{this.renderMainMenu()}
</nav>
{getConfig().ENABLE_HEADER_LANG_SELECTOR && (
<div className="mx-2">
<LanguageSelector
options={JSON.parse(getConfig().SITE_SUPPORTED_LANGUAGES)}
authenticatedUser={getAuthenticatedUser()}
/>
</div>
)}
<nav
aria-label={intl.formatMessage(messages['header.label.secondary.nav'])}
className="nav secondary-menu-container align-items-center ml-auto"
className="nav secondary-menu-container align-items-center"
>
{loggedIn
? (
Expand Down
9 changes: 7 additions & 2 deletions src/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,13 @@ $rounded-pill: 50rem !default;
}
}

.user-dropdown .btn {
height: 3rem;
.user-dropdown {
.btn {
height: 3rem;
@media (map-get($grid-breakpoints, "sm")) {
padding: 0 0.5rem;
}
}
}
}

Expand Down
Loading