@nauedu/frontend-component-header 20.0.0 → 20.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/DesktopHeader.js +242 -0
- package/dist/DesktopHeader.js.map +1 -0
- package/dist/Header.js +9 -13
- package/dist/Header.js.map +1 -1
- package/dist/Header.test.jsx +0 -3
- package/dist/Icons.js +1 -1
- package/dist/LanguageSelector/data/api.js +5 -13
- package/dist/LanguageSelector/data/api.js.map +1 -1
- package/dist/LanguageSelector/index.js +4 -12
- package/dist/LanguageSelector/index.js.map +1 -1
- package/dist/Logo.js +23 -10
- package/dist/Logo.js.map +1 -1
- package/dist/Menu/Menu.js +11 -11
- package/dist/Menu/Menu.js.map +1 -1
- package/dist/Menu/menu.scss +6 -6
- package/dist/MobileHeader.js +244 -0
- package/dist/MobileHeader.js.map +1 -0
- package/dist/__snapshots__/Header.test.jsx.snap +0 -2
- package/dist/index.scss +15 -27
- package/dist/learning-header/AnonymousUserMenu.js +8 -11
- package/dist/learning-header/AnonymousUserMenu.js.map +1 -1
- package/dist/learning-header/AuthenticatedUserDropdown.js +24 -32
- package/dist/learning-header/AuthenticatedUserDropdown.js.map +1 -1
- package/dist/learning-header/LearningHeader.js +14 -15
- package/dist/learning-header/LearningHeader.js.map +1 -1
- package/dist/learning-header/data/api.js +4 -12
- package/dist/learning-header/data/api.js.map +1 -1
- package/dist/learning-header/data/api.test.js +95 -92
- package/dist/learning-header/data/api.test.js.map +1 -1
- package/dist/learning-header/messages.js +0 -5
- package/dist/learning-header/messages.js.map +1 -1
- package/dist/setupTest.js +2 -0
- package/dist/studio-header/BrandNav.js +21 -0
- package/dist/studio-header/BrandNav.js.map +1 -0
- package/dist/studio-header/CourseLockUp.js +45 -0
- package/dist/studio-header/CourseLockUp.js.map +1 -0
- package/dist/studio-header/HeaderBody.js +131 -0
- package/dist/studio-header/HeaderBody.js.map +1 -0
- package/dist/studio-header/MobileHeader.js +79 -0
- package/dist/studio-header/MobileHeader.js.map +1 -0
- package/dist/studio-header/MobileMenu.js +45 -0
- package/dist/studio-header/MobileMenu.js.map +1 -0
- package/dist/studio-header/NavDropdownMenu.js +30 -0
- package/dist/studio-header/NavDropdownMenu.js.map +1 -0
- package/dist/studio-header/StudioHeader.js +72 -0
- package/dist/studio-header/StudioHeader.js.map +1 -0
- package/dist/studio-header/StudioHeader.scss +10 -8
- package/dist/studio-header/UserMenu.js +55 -0
- package/dist/studio-header/UserMenu.js.map +1 -0
- package/dist/studio-header/{index.ts → index.js} +1 -1
- package/dist/studio-header/index.js.map +1 -0
- package/dist/studio-header/{messages.ts → messages.js} +14 -15
- package/dist/studio-header/messages.js.map +1 -0
- package/dist/studio-header/utils.js +29 -0
- package/dist/studio-header/utils.js.map +1 -0
- package/dist/test-utils.js +4 -12
- package/dist/test-utils.js.map +1 -1
- package/package.json +1 -1
- package/dist/desktop-header/DesktopHeader.js +0 -172
- package/dist/desktop-header/DesktopHeader.js.map +0 -1
- package/dist/desktop-header/DesktopHeaderMainOrSecondaryMenu.js +0 -51
- package/dist/desktop-header/DesktopHeaderMainOrSecondaryMenu.js.map +0 -1
- package/dist/desktop-header/DesktopHeaderUserMenu.js +0 -49
- package/dist/desktop-header/DesktopHeaderUserMenu.js.map +0 -1
- package/dist/desktop-header/DesktopLoggedOutItems.js +0 -22
- package/dist/desktop-header/DesktopLoggedOutItems.js.map +0 -1
- package/dist/desktop-header/DesktopUserMenuToggle.js +0 -25
- package/dist/desktop-header/DesktopUserMenuToggle.js.map +0 -1
- package/dist/frontend-platform.d.ts +0 -41
- package/dist/learning-header/LearningHeaderCourseInfo.js +0 -24
- package/dist/learning-header/LearningHeaderCourseInfo.js.map +0 -1
- package/dist/learning-header/LearningHeaderHelpLink.js +0 -13
- package/dist/learning-header/LearningHeaderHelpLink.js.map +0 -1
- package/dist/learning-header/LearningHeaderUserMenuItems.js +0 -20
- package/dist/learning-header/LearningHeaderUserMenuItems.js.map +0 -1
- package/dist/learning-header/LearningLoggedOutButtons.js +0 -24
- package/dist/learning-header/LearningLoggedOutButtons.js.map +0 -1
- package/dist/learning-header/LearningUserMenuToggle.js +0 -26
- package/dist/learning-header/LearningUserMenuToggle.js.map +0 -1
- package/dist/mobile-header/MobileHeader.js +0 -197
- package/dist/mobile-header/MobileHeader.js.map +0 -1
- package/dist/mobile-header/MobileHeaderMainMenu.js +0 -46
- package/dist/mobile-header/MobileHeaderMainMenu.js.map +0 -1
- package/dist/mobile-header/MobileHeaderUserMenu.js +0 -38
- package/dist/mobile-header/MobileHeaderUserMenu.js.map +0 -1
- package/dist/mobile-header/MobileLoggedOutItems.js +0 -27
- package/dist/mobile-header/MobileLoggedOutItems.js.map +0 -1
- package/dist/mobile-header/MobileUserMenuToggle.js +0 -19
- package/dist/mobile-header/MobileUserMenuToggle.js.map +0 -1
- package/dist/plugin-slots/CourseInfoSlot/README.md +0 -128
- package/dist/plugin-slots/CourseInfoSlot/images/add_custom_components_before_and_after_course_info.png +0 -0
- package/dist/plugin-slots/CourseInfoSlot/images/replace_course_info_with_custom_component.png +0 -0
- package/dist/plugin-slots/CourseInfoSlot/images/replace_course_title.png +0 -0
- package/dist/plugin-slots/CourseInfoSlot/index.js +0 -32
- package/dist/plugin-slots/CourseInfoSlot/index.js.map +0 -1
- package/dist/plugin-slots/DesktopHeaderSlot/README.md +0 -44
- package/dist/plugin-slots/DesktopHeaderSlot/images/desktop_header_custom_component.png +0 -0
- package/dist/plugin-slots/DesktopHeaderSlot/index.js +0 -16
- package/dist/plugin-slots/DesktopHeaderSlot/index.js.map +0 -1
- package/dist/plugin-slots/DesktopLoggedOutItemsSlot/README.md +0 -137
- package/dist/plugin-slots/DesktopLoggedOutItemsSlot/images/desktop_logged_out_items_custom_component.png +0 -0
- package/dist/plugin-slots/DesktopLoggedOutItemsSlot/images/desktop_logged_out_items_custom_components_before_after.png +0 -0
- package/dist/plugin-slots/DesktopLoggedOutItemsSlot/images/desktop_logged_out_items_modify_items.png +0 -0
- package/dist/plugin-slots/DesktopLoggedOutItemsSlot/index.js +0 -20
- package/dist/plugin-slots/DesktopLoggedOutItemsSlot/index.js.map +0 -1
- package/dist/plugin-slots/DesktopMainMenuSlot/README.md +0 -137
- package/dist/plugin-slots/DesktopMainMenuSlot/images/desktop_main_menu_custom_component.png +0 -0
- package/dist/plugin-slots/DesktopMainMenuSlot/images/desktop_main_menu_custom_components_before_after.png +0 -0
- package/dist/plugin-slots/DesktopMainMenuSlot/images/desktop_main_menu_modify_items.png +0 -0
- package/dist/plugin-slots/DesktopMainMenuSlot/index.js +0 -20
- package/dist/plugin-slots/DesktopMainMenuSlot/index.js.map +0 -1
- package/dist/plugin-slots/DesktopSecondaryMenuSlot/README.md +0 -132
- package/dist/plugin-slots/DesktopSecondaryMenuSlot/images/desktop_secondary_menu_custom_component.png +0 -0
- package/dist/plugin-slots/DesktopSecondaryMenuSlot/images/desktop_secondary_menu_custom_components_before_after.png +0 -0
- package/dist/plugin-slots/DesktopSecondaryMenuSlot/images/desktop_secondary_menu_modify_items.png +0 -0
- package/dist/plugin-slots/DesktopSecondaryMenuSlot/index.js +0 -20
- package/dist/plugin-slots/DesktopSecondaryMenuSlot/index.js.map +0 -1
- package/dist/plugin-slots/DesktopUserMenuSlot/README.md +0 -144
- package/dist/plugin-slots/DesktopUserMenuSlot/images/desktop_user_menu_custom_component.png +0 -0
- package/dist/plugin-slots/DesktopUserMenuSlot/images/desktop_user_menu_custom_components_before_after.png +0 -0
- package/dist/plugin-slots/DesktopUserMenuSlot/images/desktop_user_menu_modify_items.png +0 -0
- package/dist/plugin-slots/DesktopUserMenuSlot/index.js +0 -20
- package/dist/plugin-slots/DesktopUserMenuSlot/index.js.map +0 -1
- package/dist/plugin-slots/DesktopUserMenuToggleSlot/README.md +0 -74
- package/dist/plugin-slots/DesktopUserMenuToggleSlot/images/desktop_user_menu_custom_component.png +0 -0
- package/dist/plugin-slots/DesktopUserMenuToggleSlot/images/desktop_user_menu_modified_toggle.png +0 -0
- package/dist/plugin-slots/DesktopUserMenuToggleSlot/index.js +0 -19
- package/dist/plugin-slots/DesktopUserMenuToggleSlot/index.js.map +0 -1
- package/dist/plugin-slots/LearningHelpSlot/README.md +0 -44
- package/dist/plugin-slots/LearningHelpSlot/images/learning_help_custom_component.png +0 -0
- package/dist/plugin-slots/LearningHelpSlot/index.js +0 -11
- package/dist/plugin-slots/LearningHelpSlot/index.js.map +0 -1
- package/dist/plugin-slots/LearningLoggedOutItemsSlot/README.md +0 -135
- package/dist/plugin-slots/LearningLoggedOutItemsSlot/images/learning_logged_out_items_custom_component.png +0 -0
- package/dist/plugin-slots/LearningLoggedOutItemsSlot/images/learning_logged_out_items_custom_components_before_after.png +0 -0
- package/dist/plugin-slots/LearningLoggedOutItemsSlot/images/learning_logged_out_items_modified_items.png +0 -0
- package/dist/plugin-slots/LearningLoggedOutItemsSlot/index.js +0 -18
- package/dist/plugin-slots/LearningLoggedOutItemsSlot/index.js.map +0 -1
- package/dist/plugin-slots/LearningUserMenuSlot/README.md +0 -133
- package/dist/plugin-slots/LearningUserMenuSlot/images/learning_user_menu_custom_component.png +0 -0
- package/dist/plugin-slots/LearningUserMenuSlot/images/learning_user_menu_custom_components_before_after.png +0 -0
- package/dist/plugin-slots/LearningUserMenuSlot/images/learning_user_menu_modified_items.png +0 -0
- package/dist/plugin-slots/LearningUserMenuSlot/index.js +0 -18
- package/dist/plugin-slots/LearningUserMenuSlot/index.js.map +0 -1
- package/dist/plugin-slots/LearningUserMenuToggleSlot/README.md +0 -74
- package/dist/plugin-slots/LearningUserMenuToggleSlot/images/learning_user_menu_toggle_custom_component.png +0 -0
- package/dist/plugin-slots/LearningUserMenuToggleSlot/images/learning_user_menu_toggle_modified_items.png +0 -0
- package/dist/plugin-slots/LearningUserMenuToggleSlot/index.js +0 -19
- package/dist/plugin-slots/LearningUserMenuToggleSlot/index.js.map +0 -1
- package/dist/plugin-slots/LogoSlot/README.md +0 -72
- package/dist/plugin-slots/LogoSlot/index.js +0 -27
- package/dist/plugin-slots/LogoSlot/index.js.map +0 -1
- package/dist/plugin-slots/MobileHeaderSlot/README.md +0 -44
- package/dist/plugin-slots/MobileHeaderSlot/images/mobile_header_custom_component.png +0 -0
- package/dist/plugin-slots/MobileHeaderSlot/index.js +0 -16
- package/dist/plugin-slots/MobileHeaderSlot/index.js.map +0 -1
- package/dist/plugin-slots/MobileLoggedOutItemsSlot/README.md +0 -136
- package/dist/plugin-slots/MobileLoggedOutItemsSlot/images/mobile_logged_out_items_custom_component.png +0 -0
- package/dist/plugin-slots/MobileLoggedOutItemsSlot/images/mobile_logged_out_items_custom_components_before_after.png +0 -0
- package/dist/plugin-slots/MobileLoggedOutItemsSlot/images/mobile_logged_out_items_modify_items.png +0 -0
- package/dist/plugin-slots/MobileLoggedOutItemsSlot/index.js +0 -20
- package/dist/plugin-slots/MobileLoggedOutItemsSlot/index.js.map +0 -1
- package/dist/plugin-slots/MobileMainMenuSlot/README.md +0 -136
- package/dist/plugin-slots/MobileMainMenuSlot/images/mobile_main_menu_custom_component.png +0 -0
- package/dist/plugin-slots/MobileMainMenuSlot/images/mobile_main_menu_custom_components_before_after.png +0 -0
- package/dist/plugin-slots/MobileMainMenuSlot/images/mobile_main_menu_modify_items.png +0 -0
- package/dist/plugin-slots/MobileMainMenuSlot/index.js +0 -20
- package/dist/plugin-slots/MobileMainMenuSlot/index.js.map +0 -1
- package/dist/plugin-slots/MobileUserMenuSlot/README.md +0 -144
- package/dist/plugin-slots/MobileUserMenuSlot/images/mobile_user_menu_custom_component.png +0 -0
- package/dist/plugin-slots/MobileUserMenuSlot/images/mobile_user_menu_custom_components_before_after.png +0 -0
- package/dist/plugin-slots/MobileUserMenuSlot/images/mobile_user_menu_modify_items.png +0 -0
- package/dist/plugin-slots/MobileUserMenuSlot/index.js +0 -20
- package/dist/plugin-slots/MobileUserMenuSlot/index.js.map +0 -1
- package/dist/plugin-slots/MobileUserMenuToggleSlot/README.md +0 -74
- package/dist/plugin-slots/MobileUserMenuToggleSlot/images/mobile_user_menu_toggle_custom_component.png +0 -0
- package/dist/plugin-slots/MobileUserMenuToggleSlot/images/mobile_user_menu_toggle_modified_items.png +0 -0
- package/dist/plugin-slots/MobileUserMenuToggleSlot/index.js +0 -19
- package/dist/plugin-slots/MobileUserMenuToggleSlot/index.js.map +0 -1
- package/dist/plugin-slots/README.md +0 -26
- package/dist/studio-header/BrandNav.test.tsx +0 -40
- package/dist/studio-header/BrandNav.tsx +0 -25
- package/dist/studio-header/CourseLockUp.test.tsx +0 -59
- package/dist/studio-header/CourseLockUp.tsx +0 -56
- package/dist/studio-header/HeaderBody.test.tsx +0 -102
- package/dist/studio-header/HeaderBody.tsx +0 -196
- package/dist/studio-header/MobileHeader.tsx +0 -74
- package/dist/studio-header/MobileMenu.test.tsx +0 -81
- package/dist/studio-header/MobileMenu.tsx +0 -50
- package/dist/studio-header/NavDropdownMenu.test.tsx +0 -67
- package/dist/studio-header/NavDropdownMenu.tsx +0 -42
- package/dist/studio-header/StudioHeader.test.tsx +0 -220
- package/dist/studio-header/StudioHeader.tsx +0 -84
- package/dist/studio-header/UserMenu.tsx +0 -69
- package/dist/studio-header/utils.ts +0 -37
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { render, screen, fireEvent } from '@testing-library/react';
|
|
3
|
-
import '@testing-library/jest-dom/extend-expect';
|
|
4
|
-
import { IntlProvider } from '@edx/frontend-platform/i18n';
|
|
5
|
-
import { MemoryRouter } from 'react-router-dom';
|
|
6
|
-
|
|
7
|
-
import HeaderBody from './HeaderBody';
|
|
8
|
-
import messages from './messages';
|
|
9
|
-
|
|
10
|
-
const mockOnNavigate = jest.fn();
|
|
11
|
-
const mockSearchButtonAction = jest.fn();
|
|
12
|
-
const mockToggleModalPopup = jest.fn();
|
|
13
|
-
const mockSetModalPopupTarget = jest.fn();
|
|
14
|
-
|
|
15
|
-
const defaultProps = {
|
|
16
|
-
studioBaseUrl: 'https://example.com',
|
|
17
|
-
logoutUrl: 'https://example.com/logout',
|
|
18
|
-
onNavigate: mockOnNavigate,
|
|
19
|
-
setModalPopupTarget: mockSetModalPopupTarget,
|
|
20
|
-
toggleModalPopup: mockToggleModalPopup,
|
|
21
|
-
searchButtonAction: mockSearchButtonAction,
|
|
22
|
-
username: 'testuser',
|
|
23
|
-
authenticatedUserAvatar: 'avatar.png',
|
|
24
|
-
isAdmin: true,
|
|
25
|
-
isMobile: false,
|
|
26
|
-
isHiddenMainMenu: false,
|
|
27
|
-
mainMenuDropdowns: [],
|
|
28
|
-
logo: 'logo.png',
|
|
29
|
-
logoAltText: 'Test Logo',
|
|
30
|
-
number: '101',
|
|
31
|
-
org: 'EDX',
|
|
32
|
-
title: 'Test Course',
|
|
33
|
-
outlineLink: '/courses/edx/course-101',
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
const RootWrapper = (props) => (
|
|
37
|
-
<MemoryRouter>
|
|
38
|
-
<IntlProvider locale="en" messages={{}}>
|
|
39
|
-
<HeaderBody {...props} />
|
|
40
|
-
</IntlProvider>
|
|
41
|
-
</MemoryRouter>
|
|
42
|
-
);
|
|
43
|
-
|
|
44
|
-
describe('HeaderBody Component', () => {
|
|
45
|
-
afterEach(() => {
|
|
46
|
-
jest.clearAllMocks();
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it('renders the logo and brand navigation', () => {
|
|
50
|
-
render(<RootWrapper {...defaultProps} />);
|
|
51
|
-
|
|
52
|
-
const logoImage = screen.getByAltText(defaultProps.logoAltText);
|
|
53
|
-
expect(logoImage).toBeInTheDocument();
|
|
54
|
-
expect(logoImage).toHaveAttribute('src', defaultProps.logo);
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it('renders course lockup information', () => {
|
|
58
|
-
render(<RootWrapper {...defaultProps} />);
|
|
59
|
-
|
|
60
|
-
const courseTitle = screen.getByText(defaultProps.title);
|
|
61
|
-
const courseOrgNumber = screen.getByText(`${defaultProps.org} ${defaultProps.number}`);
|
|
62
|
-
|
|
63
|
-
expect(courseTitle).toBeInTheDocument();
|
|
64
|
-
expect(courseOrgNumber).toBeInTheDocument();
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
it('renders a course lock-up link with the correct outline URL', () => {
|
|
68
|
-
render(<RootWrapper {...defaultProps} />);
|
|
69
|
-
|
|
70
|
-
const courseLockUpLink = screen.getByTestId('course-lock-up-block');
|
|
71
|
-
expect(courseLockUpLink.getAttribute('href')).toBe(defaultProps.outlineLink);
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
it('displays search button and triggers searchButtonAction on click', () => {
|
|
75
|
-
render(<RootWrapper {...defaultProps} />);
|
|
76
|
-
|
|
77
|
-
const searchButton = screen.getByLabelText(messages['header.label.search.nav'].defaultMessage);
|
|
78
|
-
expect(searchButton).toBeInTheDocument();
|
|
79
|
-
|
|
80
|
-
fireEvent.click(searchButton);
|
|
81
|
-
expect(mockSearchButtonAction).toHaveBeenCalled();
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
it('displays user menu with username and avatar', () => {
|
|
85
|
-
render(<RootWrapper {...defaultProps} />);
|
|
86
|
-
|
|
87
|
-
const userMenu = screen.getByText(defaultProps.username);
|
|
88
|
-
const avatarImage = screen.getByAltText(defaultProps.username);
|
|
89
|
-
|
|
90
|
-
expect(userMenu).toBeInTheDocument();
|
|
91
|
-
expect(avatarImage).toHaveAttribute('src', defaultProps.authenticatedUserAvatar);
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
it('toggles mobile menu popup when button is clicked in mobile view', () => {
|
|
95
|
-
render(<RootWrapper {...defaultProps} isMobile isModalPopupOpen={false} />);
|
|
96
|
-
|
|
97
|
-
const menuButton = screen.getByTestId('mobile-menu-button');
|
|
98
|
-
fireEvent.click(menuButton);
|
|
99
|
-
|
|
100
|
-
expect(mockToggleModalPopup).toHaveBeenCalled();
|
|
101
|
-
});
|
|
102
|
-
});
|
|
@@ -1,196 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import PropTypes from 'prop-types';
|
|
3
|
-
import { useIntl } from '@edx/frontend-platform/i18n';
|
|
4
|
-
import classNames from 'classnames';
|
|
5
|
-
import {
|
|
6
|
-
ActionRow,
|
|
7
|
-
Button,
|
|
8
|
-
Container,
|
|
9
|
-
Icon,
|
|
10
|
-
IconButton,
|
|
11
|
-
Nav,
|
|
12
|
-
Row,
|
|
13
|
-
} from '@openedx/paragon';
|
|
14
|
-
import { Close, MenuIcon, Search } from '@openedx/paragon/icons';
|
|
15
|
-
|
|
16
|
-
import CourseLockUp from './CourseLockUp';
|
|
17
|
-
import UserMenu from './UserMenu';
|
|
18
|
-
import BrandNav from './BrandNav';
|
|
19
|
-
import NavDropdownMenu from './NavDropdownMenu';
|
|
20
|
-
import messages from './messages';
|
|
21
|
-
|
|
22
|
-
const HeaderBody = ({
|
|
23
|
-
logo,
|
|
24
|
-
logoAltText,
|
|
25
|
-
number,
|
|
26
|
-
org,
|
|
27
|
-
title,
|
|
28
|
-
username,
|
|
29
|
-
isAdmin,
|
|
30
|
-
studioBaseUrl,
|
|
31
|
-
logoutUrl,
|
|
32
|
-
authenticatedUserAvatar,
|
|
33
|
-
isMobile,
|
|
34
|
-
setModalPopupTarget,
|
|
35
|
-
toggleModalPopup,
|
|
36
|
-
isModalPopupOpen,
|
|
37
|
-
isHiddenMainMenu,
|
|
38
|
-
mainMenuDropdowns,
|
|
39
|
-
outlineLink,
|
|
40
|
-
searchButtonAction,
|
|
41
|
-
containerProps,
|
|
42
|
-
}) => {
|
|
43
|
-
const intl = useIntl();
|
|
44
|
-
|
|
45
|
-
const renderBrandNav = (
|
|
46
|
-
<BrandNav
|
|
47
|
-
{...{
|
|
48
|
-
studioBaseUrl,
|
|
49
|
-
logo,
|
|
50
|
-
logoAltText,
|
|
51
|
-
}}
|
|
52
|
-
/>
|
|
53
|
-
);
|
|
54
|
-
|
|
55
|
-
const { className: containerClassName, ...restContainerProps } = containerProps || {};
|
|
56
|
-
|
|
57
|
-
return (
|
|
58
|
-
<Container
|
|
59
|
-
size="xl"
|
|
60
|
-
className={classNames('px-2.5', containerClassName)}
|
|
61
|
-
{...restContainerProps}
|
|
62
|
-
>
|
|
63
|
-
<ActionRow as="header">
|
|
64
|
-
{isHiddenMainMenu ? (
|
|
65
|
-
<Row className="flex-nowrap ml-4">
|
|
66
|
-
{renderBrandNav}
|
|
67
|
-
</Row>
|
|
68
|
-
) : (
|
|
69
|
-
<>
|
|
70
|
-
{isMobile ? (
|
|
71
|
-
<Button
|
|
72
|
-
ref={setModalPopupTarget}
|
|
73
|
-
className="d-inline-flex align-items-center"
|
|
74
|
-
variant="tertiary"
|
|
75
|
-
onClick={toggleModalPopup}
|
|
76
|
-
iconBefore={isModalPopupOpen ? Close : MenuIcon}
|
|
77
|
-
data-testid="mobile-menu-button"
|
|
78
|
-
>
|
|
79
|
-
Menu
|
|
80
|
-
</Button>
|
|
81
|
-
) : (
|
|
82
|
-
<div className="w-25">
|
|
83
|
-
<Row className="m-0 flex-nowrap align-items-center">
|
|
84
|
-
{renderBrandNav}
|
|
85
|
-
<CourseLockUp
|
|
86
|
-
{...{
|
|
87
|
-
outlineLink,
|
|
88
|
-
number,
|
|
89
|
-
org,
|
|
90
|
-
title,
|
|
91
|
-
}}
|
|
92
|
-
/>
|
|
93
|
-
</Row>
|
|
94
|
-
</div>
|
|
95
|
-
)}
|
|
96
|
-
{isMobile ? (
|
|
97
|
-
<>
|
|
98
|
-
<ActionRow.Spacer />
|
|
99
|
-
{renderBrandNav}
|
|
100
|
-
</>
|
|
101
|
-
) : (
|
|
102
|
-
<Nav data-testid="desktop-menu" className="ml-2">
|
|
103
|
-
{mainMenuDropdowns.map(dropdown => {
|
|
104
|
-
const { id, buttonTitle, items } = dropdown;
|
|
105
|
-
return (
|
|
106
|
-
<NavDropdownMenu
|
|
107
|
-
key={id}
|
|
108
|
-
{...{
|
|
109
|
-
id, buttonTitle, items,
|
|
110
|
-
}}
|
|
111
|
-
/>
|
|
112
|
-
);
|
|
113
|
-
})}
|
|
114
|
-
</Nav>
|
|
115
|
-
)}
|
|
116
|
-
</>
|
|
117
|
-
)}
|
|
118
|
-
<ActionRow.Spacer />
|
|
119
|
-
{searchButtonAction && (
|
|
120
|
-
<Nav>
|
|
121
|
-
<IconButton
|
|
122
|
-
src={Search}
|
|
123
|
-
iconAs={Icon}
|
|
124
|
-
onClick={searchButtonAction}
|
|
125
|
-
aria-label={intl.formatMessage(messages['header.label.search.nav'])}
|
|
126
|
-
alt={intl.formatMessage(messages['header.label.search.nav'])}
|
|
127
|
-
/>
|
|
128
|
-
</Nav>
|
|
129
|
-
)}
|
|
130
|
-
<Nav>
|
|
131
|
-
<UserMenu
|
|
132
|
-
{...{
|
|
133
|
-
username,
|
|
134
|
-
studioBaseUrl,
|
|
135
|
-
logoutUrl,
|
|
136
|
-
authenticatedUserAvatar,
|
|
137
|
-
isAdmin,
|
|
138
|
-
isMobile,
|
|
139
|
-
}}
|
|
140
|
-
/>
|
|
141
|
-
</Nav>
|
|
142
|
-
</ActionRow>
|
|
143
|
-
</Container>
|
|
144
|
-
);
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
HeaderBody.propTypes = {
|
|
148
|
-
studioBaseUrl: PropTypes.string.isRequired,
|
|
149
|
-
logoutUrl: PropTypes.string.isRequired,
|
|
150
|
-
setModalPopupTarget: PropTypes.func,
|
|
151
|
-
toggleModalPopup: PropTypes.func,
|
|
152
|
-
isModalPopupOpen: PropTypes.bool,
|
|
153
|
-
number: PropTypes.string,
|
|
154
|
-
org: PropTypes.string,
|
|
155
|
-
title: PropTypes.string,
|
|
156
|
-
logo: PropTypes.string,
|
|
157
|
-
logoAltText: PropTypes.string,
|
|
158
|
-
authenticatedUserAvatar: PropTypes.string,
|
|
159
|
-
username: PropTypes.string,
|
|
160
|
-
isAdmin: PropTypes.bool,
|
|
161
|
-
isMobile: PropTypes.bool,
|
|
162
|
-
isHiddenMainMenu: PropTypes.bool,
|
|
163
|
-
mainMenuDropdowns: PropTypes.arrayOf(PropTypes.shape({
|
|
164
|
-
id: PropTypes.string,
|
|
165
|
-
buttonTitle: PropTypes.node,
|
|
166
|
-
items: PropTypes.arrayOf(PropTypes.shape({
|
|
167
|
-
href: PropTypes.string,
|
|
168
|
-
title: PropTypes.node,
|
|
169
|
-
})),
|
|
170
|
-
})),
|
|
171
|
-
outlineLink: PropTypes.string,
|
|
172
|
-
searchButtonAction: PropTypes.func,
|
|
173
|
-
containerProps: PropTypes.shape(Container.propTypes),
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
HeaderBody.defaultProps = {
|
|
177
|
-
setModalPopupTarget: null,
|
|
178
|
-
toggleModalPopup: null,
|
|
179
|
-
isModalPopupOpen: false,
|
|
180
|
-
logo: null,
|
|
181
|
-
logoAltText: null,
|
|
182
|
-
number: '',
|
|
183
|
-
org: '',
|
|
184
|
-
title: '',
|
|
185
|
-
authenticatedUserAvatar: null,
|
|
186
|
-
username: null,
|
|
187
|
-
isAdmin: false,
|
|
188
|
-
isMobile: false,
|
|
189
|
-
isHiddenMainMenu: false,
|
|
190
|
-
mainMenuDropdowns: [],
|
|
191
|
-
outlineLink: null,
|
|
192
|
-
searchButtonAction: null,
|
|
193
|
-
containerProps: {},
|
|
194
|
-
};
|
|
195
|
-
|
|
196
|
-
export default HeaderBody;
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import React, { useState } from 'react';
|
|
2
|
-
import PropTypes from 'prop-types';
|
|
3
|
-
import { useToggle, ModalPopup } from '@openedx/paragon';
|
|
4
|
-
import HeaderBody from './HeaderBody';
|
|
5
|
-
import MobileMenu from './MobileMenu';
|
|
6
|
-
|
|
7
|
-
const MobileHeader = ({
|
|
8
|
-
mainMenuDropdowns,
|
|
9
|
-
...props
|
|
10
|
-
}) => {
|
|
11
|
-
const [isOpen, , close, toggle] = useToggle(false);
|
|
12
|
-
const [target, setTarget] = useState(null);
|
|
13
|
-
|
|
14
|
-
return (
|
|
15
|
-
<>
|
|
16
|
-
{/* @ts-expect-error The type of 'props' is any until we convert from propTypes to TypeScript interface/types */}
|
|
17
|
-
<HeaderBody
|
|
18
|
-
{...props}
|
|
19
|
-
isMobile
|
|
20
|
-
setModalPopupTarget={setTarget}
|
|
21
|
-
toggleModalPopup={toggle}
|
|
22
|
-
isModalPopupOpen={isOpen}
|
|
23
|
-
/>
|
|
24
|
-
<ModalPopup
|
|
25
|
-
hasArrow
|
|
26
|
-
placement="bottom"
|
|
27
|
-
positionRef={target}
|
|
28
|
-
isOpen={isOpen}
|
|
29
|
-
onClose={close}
|
|
30
|
-
onEscapeKey={close}
|
|
31
|
-
className="mobile-menu-container"
|
|
32
|
-
>
|
|
33
|
-
<MobileMenu {...{ mainMenuDropdowns }} />
|
|
34
|
-
</ModalPopup>
|
|
35
|
-
</>
|
|
36
|
-
);
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
MobileHeader.propTypes = {
|
|
40
|
-
studioBaseUrl: PropTypes.string.isRequired, // eslint-disable-line react/no-unused-prop-types
|
|
41
|
-
logoutUrl: PropTypes.string.isRequired, // eslint-disable-line react/no-unused-prop-types
|
|
42
|
-
number: PropTypes.string, // eslint-disable-line react/no-unused-prop-types
|
|
43
|
-
org: PropTypes.string, // eslint-disable-line react/no-unused-prop-types
|
|
44
|
-
title: PropTypes.string, // eslint-disable-line react/no-unused-prop-types
|
|
45
|
-
logo: PropTypes.string, // eslint-disable-line react/no-unused-prop-types
|
|
46
|
-
logoAltText: PropTypes.string, // eslint-disable-line react/no-unused-prop-types
|
|
47
|
-
authenticatedUserAvatar: PropTypes.string, // eslint-disable-line react/no-unused-prop-types
|
|
48
|
-
username: PropTypes.string, // eslint-disable-line react/no-unused-prop-types
|
|
49
|
-
isAdmin: PropTypes.bool, // eslint-disable-line react/no-unused-prop-types
|
|
50
|
-
mainMenuDropdowns: PropTypes.arrayOf(PropTypes.shape({
|
|
51
|
-
id: PropTypes.string,
|
|
52
|
-
buttonTitle: PropTypes.node,
|
|
53
|
-
items: PropTypes.arrayOf(PropTypes.shape({
|
|
54
|
-
href: PropTypes.string,
|
|
55
|
-
title: PropTypes.node,
|
|
56
|
-
})),
|
|
57
|
-
})),
|
|
58
|
-
outlineLink: PropTypes.string, // eslint-disable-line react/no-unused-prop-types
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
MobileHeader.defaultProps = {
|
|
62
|
-
logo: null,
|
|
63
|
-
logoAltText: null,
|
|
64
|
-
number: null,
|
|
65
|
-
org: null,
|
|
66
|
-
title: null,
|
|
67
|
-
authenticatedUserAvatar: null,
|
|
68
|
-
username: null,
|
|
69
|
-
isAdmin: false,
|
|
70
|
-
mainMenuDropdowns: [],
|
|
71
|
-
outlineLink: null,
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
export default MobileHeader;
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { render, screen, fireEvent } from '@testing-library/react';
|
|
3
|
-
import { MemoryRouter } from 'react-router-dom';
|
|
4
|
-
|
|
5
|
-
import '@testing-library/jest-dom/extend-expect';
|
|
6
|
-
import MobileMenu from './MobileMenu';
|
|
7
|
-
|
|
8
|
-
const mockOnNavigate = jest.fn();
|
|
9
|
-
|
|
10
|
-
const defaultProps = {
|
|
11
|
-
mainMenuDropdowns: [
|
|
12
|
-
{
|
|
13
|
-
id: 'menu1',
|
|
14
|
-
buttonTitle: 'Menu 1',
|
|
15
|
-
items: [
|
|
16
|
-
{ href: '/menu1/item1', title: 'Item 1' },
|
|
17
|
-
{ href: '/menu1/item2', title: 'Item 2' },
|
|
18
|
-
],
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
id: 'menu2',
|
|
22
|
-
buttonTitle: 'Menu 2',
|
|
23
|
-
items: [
|
|
24
|
-
{ href: 'https://external-link.com', title: 'External Link' },
|
|
25
|
-
],
|
|
26
|
-
},
|
|
27
|
-
],
|
|
28
|
-
onNavigate: mockOnNavigate,
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
const RootWrapper = (props) => (
|
|
32
|
-
<MemoryRouter>
|
|
33
|
-
<MobileMenu {...props} />
|
|
34
|
-
</MemoryRouter>
|
|
35
|
-
);
|
|
36
|
-
|
|
37
|
-
describe('MobileMenu Component', () => {
|
|
38
|
-
afterEach(() => {
|
|
39
|
-
jest.clearAllMocks();
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
test('renders the mobile menu with dropdowns and items', () => {
|
|
43
|
-
render(<RootWrapper {...defaultProps} />);
|
|
44
|
-
|
|
45
|
-
const menu1Title = screen.getByText('Menu 1');
|
|
46
|
-
const menu2Title = screen.getByText('Menu 2');
|
|
47
|
-
|
|
48
|
-
expect(menu1Title).toBeInTheDocument();
|
|
49
|
-
expect(menu2Title).toBeInTheDocument();
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
test('navigates to internal URL when item is clicked', () => {
|
|
53
|
-
render(<RootWrapper {...defaultProps} />);
|
|
54
|
-
|
|
55
|
-
const menu1Title = screen.getByText(defaultProps.mainMenuDropdowns[0].buttonTitle);
|
|
56
|
-
fireEvent.click(menu1Title);
|
|
57
|
-
|
|
58
|
-
const menuItem = screen.getByText(defaultProps.mainMenuDropdowns[0].items[0].title);
|
|
59
|
-
expect(menuItem.getAttribute('href')).toBe(defaultProps.mainMenuDropdowns[0].items[0].href);
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
test('navigates to an external URL when external link is clicked', () => {
|
|
63
|
-
render(<RootWrapper {...defaultProps} />);
|
|
64
|
-
|
|
65
|
-
const menu2Title = screen.getByText(defaultProps.mainMenuDropdowns[1].buttonTitle);
|
|
66
|
-
fireEvent.click(menu2Title);
|
|
67
|
-
|
|
68
|
-
const externalLink = screen.getByText(defaultProps.mainMenuDropdowns[1].items[0].title);
|
|
69
|
-
expect(externalLink.getAttribute('href')).toBe(defaultProps.mainMenuDropdowns[1].items[0].href);
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
test('renders empty state when there are no dropdowns', () => {
|
|
73
|
-
render(<RootWrapper mainMenuDropdowns={[]} onNavigate={mockOnNavigate} />);
|
|
74
|
-
|
|
75
|
-
const mobileMenu = screen.getByTestId('mobile-menu');
|
|
76
|
-
expect(mobileMenu).toBeInTheDocument();
|
|
77
|
-
|
|
78
|
-
const menuItems = screen.queryAllByRole('listitem');
|
|
79
|
-
expect(menuItems.length).toBe(0);
|
|
80
|
-
});
|
|
81
|
-
});
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import PropTypes from 'prop-types';
|
|
3
|
-
import { Collapsible } from '@openedx/paragon';
|
|
4
|
-
import { Link } from 'react-router-dom';
|
|
5
|
-
|
|
6
|
-
const MobileMenu = ({ mainMenuDropdowns }) => (
|
|
7
|
-
<div
|
|
8
|
-
className="ml-4 p-2 bg-light-100 border border-gray-200 small rounded"
|
|
9
|
-
data-testid="mobile-menu"
|
|
10
|
-
>
|
|
11
|
-
<div>
|
|
12
|
-
{mainMenuDropdowns.map(dropdown => {
|
|
13
|
-
const { id, buttonTitle, items } = dropdown;
|
|
14
|
-
return (
|
|
15
|
-
<Collapsible
|
|
16
|
-
className="border-light-100"
|
|
17
|
-
title={buttonTitle}
|
|
18
|
-
key={id}
|
|
19
|
-
>
|
|
20
|
-
<ul className="p-0" style={{ listStyleType: 'none' }}>
|
|
21
|
-
{items.map(item => (
|
|
22
|
-
<li className="mobile-menu-item">
|
|
23
|
-
<Link to={item.href}>
|
|
24
|
-
{item.title}
|
|
25
|
-
</Link>
|
|
26
|
-
</li>
|
|
27
|
-
))}
|
|
28
|
-
</ul>
|
|
29
|
-
</Collapsible>
|
|
30
|
-
);
|
|
31
|
-
})}
|
|
32
|
-
</div>
|
|
33
|
-
</div>
|
|
34
|
-
);
|
|
35
|
-
|
|
36
|
-
MobileMenu.propTypes = {
|
|
37
|
-
mainMenuDropdowns: PropTypes.arrayOf(PropTypes.shape({
|
|
38
|
-
id: PropTypes.string,
|
|
39
|
-
buttonTitle: PropTypes.node,
|
|
40
|
-
items: PropTypes.arrayOf(PropTypes.shape({
|
|
41
|
-
href: PropTypes.string,
|
|
42
|
-
title: PropTypes.node,
|
|
43
|
-
})),
|
|
44
|
-
})),
|
|
45
|
-
};
|
|
46
|
-
MobileMenu.defaultProps = {
|
|
47
|
-
mainMenuDropdowns: [],
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
export default MobileMenu;
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { render, screen, fireEvent } from '@testing-library/react';
|
|
3
|
-
import '@testing-library/jest-dom/extend-expect';
|
|
4
|
-
import { MemoryRouter } from 'react-router-dom';
|
|
5
|
-
|
|
6
|
-
import NavDropdownMenu from './NavDropdownMenu';
|
|
7
|
-
|
|
8
|
-
const defaultProps = {
|
|
9
|
-
id: 'menu-id',
|
|
10
|
-
buttonTitle: 'Menu',
|
|
11
|
-
items: [
|
|
12
|
-
{ href: '/item1', title: 'Item 1' },
|
|
13
|
-
{ href: 'https://external.com', title: 'External Link' },
|
|
14
|
-
],
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
const RootWrapper = (props) => (
|
|
18
|
-
<MemoryRouter>
|
|
19
|
-
<NavDropdownMenu {...props} />
|
|
20
|
-
</MemoryRouter>
|
|
21
|
-
);
|
|
22
|
-
|
|
23
|
-
describe('NavDropdownMenu Component', () => {
|
|
24
|
-
afterEach(() => {
|
|
25
|
-
jest.clearAllMocks();
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
test('renders the dropdown button with correct title', () => {
|
|
29
|
-
render(<NavDropdownMenu {...defaultProps} />);
|
|
30
|
-
|
|
31
|
-
const dropdownButton = screen.getByRole('button', { name: defaultProps.buttonTitle });
|
|
32
|
-
expect(dropdownButton).toBeInTheDocument();
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
test('renders all dropdown items', () => {
|
|
36
|
-
render(<RootWrapper {...defaultProps} />);
|
|
37
|
-
|
|
38
|
-
const dropdownButton = screen.getByRole('button', { name: defaultProps.buttonTitle });
|
|
39
|
-
fireEvent.click(dropdownButton);
|
|
40
|
-
|
|
41
|
-
const item1 = screen.getByText(defaultProps.items[0].title);
|
|
42
|
-
const externalLink = screen.getByText(defaultProps.items[1].title);
|
|
43
|
-
|
|
44
|
-
expect(item1).toBeInTheDocument();
|
|
45
|
-
expect(externalLink).toBeInTheDocument();
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
test('calls onNavigate with the correct URL for internal link', () => {
|
|
49
|
-
render(<RootWrapper {...defaultProps} />);
|
|
50
|
-
|
|
51
|
-
const dropdownButton = screen.getByRole('button', { name: defaultProps.buttonTitle });
|
|
52
|
-
fireEvent.click(dropdownButton);
|
|
53
|
-
|
|
54
|
-
const item1 = screen.getByText(defaultProps.items[0].title);
|
|
55
|
-
expect(item1.getAttribute('href')).toBe(defaultProps.items[0].href);
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
test('navigates to external URL when external link is clicked', () => {
|
|
59
|
-
render(<RootWrapper {...defaultProps} />);
|
|
60
|
-
|
|
61
|
-
const dropdownButton = screen.getByRole('button', { name: defaultProps.buttonTitle });
|
|
62
|
-
fireEvent.click(dropdownButton);
|
|
63
|
-
|
|
64
|
-
const externalLink = screen.getByText(defaultProps.items[1].title);
|
|
65
|
-
expect(externalLink.getAttribute('href')).toBe(defaultProps.items[1].href);
|
|
66
|
-
});
|
|
67
|
-
});
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import PropTypes from 'prop-types';
|
|
3
|
-
import {
|
|
4
|
-
Dropdown,
|
|
5
|
-
DropdownButton,
|
|
6
|
-
} from '@openedx/paragon';
|
|
7
|
-
import { Link } from 'react-router-dom';
|
|
8
|
-
|
|
9
|
-
const NavDropdownMenu = ({
|
|
10
|
-
id,
|
|
11
|
-
buttonTitle,
|
|
12
|
-
items,
|
|
13
|
-
}) => (
|
|
14
|
-
<DropdownButton
|
|
15
|
-
id={id}
|
|
16
|
-
title={buttonTitle}
|
|
17
|
-
variant="outline-primary"
|
|
18
|
-
className="mr-2"
|
|
19
|
-
>
|
|
20
|
-
{items.map(item => (
|
|
21
|
-
<Dropdown.Item
|
|
22
|
-
as={Link}
|
|
23
|
-
key={`${item.title}-dropdown-item`}
|
|
24
|
-
to={item.href}
|
|
25
|
-
className="small"
|
|
26
|
-
>
|
|
27
|
-
{item.title}
|
|
28
|
-
</Dropdown.Item>
|
|
29
|
-
))}
|
|
30
|
-
</DropdownButton>
|
|
31
|
-
);
|
|
32
|
-
|
|
33
|
-
NavDropdownMenu.propTypes = {
|
|
34
|
-
id: PropTypes.string.isRequired,
|
|
35
|
-
buttonTitle: PropTypes.node.isRequired,
|
|
36
|
-
items: PropTypes.arrayOf(PropTypes.shape({
|
|
37
|
-
href: PropTypes.string.isRequired,
|
|
38
|
-
title: PropTypes.node.isRequired,
|
|
39
|
-
})).isRequired,
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
export default NavDropdownMenu;
|