Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add user menu slots in header #577

Merged
merged 1 commit into from
Aug 2, 2024
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
6 changes: 6 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ Usage

``import Header, { messages } from '@edx/frontend-component-header-edx';``

Plugins
=======
This component can be customized using `Frontend Plugin Framework <https://github.com/openedx/frontend-plugin-framework>`_.

The parts of this component that can be customized in that manner are documented `here </src/plugin-slots>`_.

Development
===========

Expand Down
50 changes: 50 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
"@fortawesome/free-regular-svg-icons": "^6.6.0",
"@fortawesome/free-solid-svg-icons": "^6.6.0",
"@fortawesome/react-fontawesome": "^0.2.0",
"@openedx/frontend-plugin-framework": "^1.2.1",
"@openedx/paragon": "^21.11.0",
"@reduxjs/toolkit": "1.9.7",
"axios-mock-adapter": "1.22.0",
Expand Down
4 changes: 4 additions & 0 deletions src/DesktopHeader.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { getConfig } from '@edx/frontend-platform';
import { AvatarButton, Dropdown } from '@openedx/paragon';

// Local Components
import UserMenuGroupItemSlot from './plugin-slots/UserMenuGroupItemSlot';
import UserMenuGroupSlot from './plugin-slots/UserMenuGroupSlot';
import UserMenuItem from './common/UserMenuItem';
import { Menu, MenuTrigger, MenuContent } from './Menu';
import { LinkedLogo, Logo } from './Logo';
Expand Down Expand Up @@ -104,10 +106,12 @@ class DesktopHeader extends React.Component {
/>
</Dropdown.Item>
)}
<UserMenuGroupSlot />
{userMenu.map((group, index) => (
// eslint-disable-next-line react/jsx-no-comment-textnodes,react/no-array-index-key
<React.Fragment key={index}>
{group.heading && <Dropdown.Header>{group.heading}</Dropdown.Header>}
{index === 0 && (<UserMenuGroupItemSlot />)}
{group.items.map(({
type, content, href, disabled, isActive, onClick,
}) => (
Expand Down
9 changes: 8 additions & 1 deletion src/MobileHeader.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { getConfig } from '@edx/frontend-platform';

// Local Components
import { AvatarButton } from '@openedx/paragon';
import UserMenuGroupSlot from './plugin-slots/UserMenuGroupSlot';
import UserMenuGroupItemSlot from './plugin-slots/UserMenuGroupItemSlot';
import { Menu, MenuTrigger, MenuContent } from './Menu';
import { LinkedLogo, Logo } from './Logo';
import UserMenuItem from './common/UserMenuItem';
Expand Down Expand Up @@ -97,7 +99,12 @@ class MobileHeader extends React.Component {
))
));

return userInfoItem ? [userInfoItem, ...userMenuItems] : userMenuItems;
const userMenuGroupSlot = <UserMenuGroupSlot />;
const userMenuGroupItemSlot = <UserMenuGroupItemSlot />;

return userInfoItem
? [userInfoItem, userMenuGroupSlot, userMenuGroupItemSlot, ...userMenuItems]
: [userMenuGroupSlot, userMenuGroupItemSlot, ...userMenuItems];
}

renderLoggedOutItems() {
Expand Down
4 changes: 4 additions & 0 deletions src/plugin-slots/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# `frontend-component-header-edx` Plugin Slots

* [`header_user_menu_group_slot`](./UserMenuGroupSlot/)
* [`header_user_menu_group_item_slot`](./UserMenuGroupItemSlot/)
72 changes: 72 additions & 0 deletions src/plugin-slots/UserMenuGroupItemSlot/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Header User Menu Group Slot

### Slot ID: `header_user_menu_group_item_slot`

## Description

This slot allows you to insert a user menu item into a group within the header's user menu for both desktop and mobile screens.

Note: Ensure the slot is provided with appropriate JSX that can render smoothly on both desktop and mobile screens.

## Example

The following ``env.config.jsx`` demonstrates how to insert a user menu item into a group within the header's user menu
for both desktop and mobile screens.

**Default Behaviour:**

Desktop:
![Screenshot of Default Header_User_Menu](./images/default_user_menu_desktop.png)

Mobile:
![Screenshot of Default Header_User_Menu](./images/default_user_menu_mobile.png)

**Inserted a user menu group:**

Desktop:
![Screenshot of Inserted_User_Menu_Group](./images/inserted_user_menu_item_desktop.png)

Mobile:
![Screenshot of Inserted_User_Menu_Group](./images/inserted_user_menu_item_mobile.png)

```jsx
import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework';
import { breakpoints, Dropdown, useWindowSize } from '@openedx/paragon';

const config = {
pluginSlots: {
header_user_menu_group_slot: {
plugins: [
{
// Insert some user menu group item
op: PLUGIN_OPERATIONS.Insert,
widget: {
id: 'user_menu_group',
type: DIRECT_PLUGIN,
RenderWidget: () => {
const { width } = useWindowSize();
const isMobile = width <= breakpoints.small.maxWidth;
if (!isMobile) {
return (
<Dropdown.Item as="a" href="#">
User Menu Group Item
</Dropdown.Item>
);
}
return (
<li className="nav-item">
<a href="#" className="nav-link">
User Menu Group Item
</a>
</li>
);
},
},
},
],
},
},
}

export default config;
```
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions src/plugin-slots/UserMenuGroupItemSlot/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import React from 'react';

import { PluginSlot } from '@openedx/frontend-plugin-framework';

const UserMenuGroupItemSlot = () => (
<PluginSlot
id="header_user_menu_group_item_slot"
/>
);

export default UserMenuGroupItemSlot;
86 changes: 86 additions & 0 deletions src/plugin-slots/UserMenuGroupSlot/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# Header User Menu Group Slot

### Slot ID: `header_user_menu_group_slot`

## Description

This slot is used to insert a user menu group in the header's user menu for both desktop and mobile screens.

Note: Ensure the slot is provided with appropriate JSX that can render smoothly on both desktop and mobile screens.

## Example

The following ``env.config.jsx`` demonstrates how to insert a user menu group into the header's user menu
for both mobile and desktop screens.

**Default Behaviour:**

Desktop:
![Screenshot of Default Header_User_Menu_Desktop](./images/default_user_menu_desktop.png)

Mobile:
![Screenshot of Default Header_User_Menu_Mobile](./images/default_user_menu_mobile.png)

**Inserted a user menu group:**

Desktop:
![Screenshot of Inserted_User_Menu_Group_Desktop](./images/inserted_user_menu_desktop.png)

Mobile:
![Screenshot of Inserted_User_Menu_Group_Mobile](./images/inserted_user_menu_mobile.png)

```jsx
import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework';
import { breakpoints, Dropdown, useWindowSize } from '@openedx/paragon';

const config = {
pluginSlots: {
header_user_menu_group_slot: {
plugins: [
{
// Insert some user menu group
op: PLUGIN_OPERATIONS.Insert,
widget: {
id: 'user_menu_group',
type: DIRECT_PLUGIN,
RenderWidget: () => {
const { width } = useWindowSize();
const isMobile = width <= breakpoints.small.maxWidth;
if (!isMobile) {
return (
<>
<Dropdown.Header>User Menu Group Header</Dropdown.Header>
<Dropdown.Item as="a" href="#" className="active">
User Menu Group Item - Active
</Dropdown.Item>
<Dropdown.Item as="a" href="#">
User Menu Group Item 2
</Dropdown.Item>
<Dropdown.Divider />
</>
);
}
return (
<>
<li className="nav-item">
<a href="#" className="nav-link active">
User Menu Group Item - Active
</a>
</li>
<li className="nav-item">
<a href="#" className="nav-link active">
User Menu Group Item 2
</a>
</li>
</>
);
},
},
},
],
},
},
}

export default config;
```
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions src/plugin-slots/UserMenuGroupSlot/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import React from 'react';

import { PluginSlot } from '@openedx/frontend-plugin-framework';

const UserMenuGroupSlot = () => (
<PluginSlot
id="header_user_menu_group_slot"
/>
);

export default UserMenuGroupSlot;
Loading