Skip to content

Conversation

ku5ic
Copy link
Contributor

@ku5ic ku5ic commented Oct 7, 2025

Feature: Pinned Navigation Items

Related JIRA Story: WEB-5364

Summary

This PR introduces the ability for users to pin and unpin navigation menu items to a personalized "Pinned" section at the top of the sidebar.
The feature improves navigation efficiency, supports persistent user preferences, and provides clear visual feedback for pin and unpin actions.

SCREENSHOTS Screenshot 2025-10-08 at 14 12 32 Screenshot 2025-10-08 at 14 12 56

Changes

  • Pin / Unpin functionality

    • Added a pin icon to all main and submenu items (visible on hover).
    • Clicking the icon pins the item to a new "Pinned Section" at the top of the navigation.
    • Items can be unpinned either from their source or directly from the pinned section.
    • Original items remain in place with a pinned indicator.
  • Pinned Section

    • Always visible at the top of the navigation.
    • Displays items in the order they were pinned (FIFO).
    • Shows an unpin icon on hover for quick removal.
  • Persistence

    • User’s pinned items are saved in their profile/preferences.
    • Pinned state is restored automatically on login.
    • Works across browsers and sessions (if user preferences are synced).
  • UI & Feedback

    • Dynamic pin icon states (outlined vs filled) for clarity.
    • Immediate visual updates on pin and unpin.
    • Smooth user experience without reloads.

Implementation Details

  • Added new hooks and state management for per-user pinned item tracking.
  • Integrated persistence with existing user preferences API.
  • Updated navigation renderer to dynamically inject pinned items.
  • Ensured hover behavior and visual consistency across components.
  • Maintained backward compatibility with existing navigation logic.

How to Test

  1. Hover over any main or submenu item and click the pin icon.
    The item should appear in the pinned section at the top.
  2. Refresh or log out/in.
    Pinned items should remain visible.
  3. Hover over a pinned item and click the unpin icon.
    The item should be removed from the pinned section but remain in its original location.
  4. Verify that pinned items appear in the order they were pinned.

Out of Scope

  • Drag-and-drop reordering of pinned items (to be considered in a future iteration).

Checklist

  • Pin and unpin functionality implemented
  • Pinned section added to navigation
  • Persistence integrated via user preferences
  • UI feedback for pin and unpin actions
  • Tested across sessions and browsers
  • Code linted, formatted, and reviewed

ku5ic added 4 commits October 6, 2025 15:28
Introduced ability to pin/unpin sidebar menu items in admin UI navigation.
Added PinnableMenuItem and PinnedMenuItems components. Updated MenuConfig
and related components to support the `pinnable` property. Enabled pinning
for CMS content models and groups. Updated Properties to handle `pinnable`
attribute.

Refs: WEB-5364
Renamed the variable pinnableItems to pinnableStates for clarity and
consistency in the PinnedMenuItems component. This improves code
readability by better reflecting the data structure's purpose.
Pin icon in menu items is now visible on hover or when the item is pinned.
Pinned menu items are rendered with PinnableMenuItem for consistent UX.

Refs: WEB-5364
@Pavel910 Pavel910 added this to the 5.44.0 milestone Oct 7, 2025
ku5ic added 3 commits October 7, 2025 09:29
Remove unused PinOffIcon and always display PinIcon for pinnable menu items.
Add detailed JSDoc to PinnableMenuItem for improved developer clarity. The
pin/unpin action now uses a single icon, streamlining UI consistency.

Refs: WEB-5364
Refactored PinnedMenuItems to optimize filtering and rendering of pinned
menu items. Now uses useMemo for pinnable menus and keys, and only
renders the "Pinned" group if there are pinned items. Enhanced
documentation and clarified prop usage. This improves performance and
code readability.

Refs: WEB-5364
Added the `pinnable={true}` prop to various settings-related `Menu` components
across multiple modules. This enables users to pin these menu items for easier
access and improved navigation within the admin interface.

Refs: WEB-5364
@SvenAlHamad
Copy link
Contributor

On comment...when pinned there should be a "Pinned" section and a "Webiny" section. See screenshot.
CleanShot 2025-10-07 at 10 12 39@2x

ku5ic added 2 commits October 7, 2025 12:01
Refactored pinnable menu item logic to store both pinned state and order in
localStorage. Added hooks and utility functions for managing pinned menu items
and their order. Updated PinnedMenuItems to display pinned items in user-defined
order. Improved documentation and code clarity.

Refs: WEB-5364
Added a new Menu.Group labeled "Webiny" to the PinnedMenuItems component.
This provides a dedicated section for Webiny-related menu items in the admin
UI navigation, improving organization and clarity for users.

Refs: WEB-5364
@ku5ic
Copy link
Contributor Author

ku5ic commented Oct 7, 2025

On comment...when pinned there should be a "Pinned" section and a "Webiny" section. See screenshot. CleanShot 2025-10-07 at 10 12 39@2x

Added with fca3d7b

@ku5ic ku5ic marked this pull request as ready for review October 8, 2025 08:04
@ku5ic ku5ic requested review from Pavel910 and adrians5j October 8, 2025 08:04
@SvenAlHamad
Copy link
Contributor

Based on the last video, you're missing the icon on the pinned entries. Every entry that's pinned should have an icon. The icon you need to use it the icon from the parent group where that entry is placed.

Copy link
Collaborator

@Pavel910 Pavel910 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a tiny change on exported function name, and we're good to go 👍

* @param name - The unique name of the menu item.
* @returns The localStorage key string for the pinned state.
*/
export const PINNED_KEY = (name: string) => `navigation/${name}/pinned`;
Copy link
Collaborator

@Pavel910 Pavel910 Oct 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This type of case is reserved for constants, like you have PINNED_ORDER_KEY later in the code. If it's a function, export it as createPinnedKey or getPinnedKey. You could even go as far as calling it usePinnedKey, name it as a hook, which will make the code very easy to read later where you use this.

* <PinnedMenuItems menuItems={menus} />
*/
export const PinnedMenuItems = ({ menuItems }: PinnedMenuItemsProps) => {
const pinnableMenus = useMemo(() => getPinnableMenus(menuItems), [menuItems]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would say that most of these useMemo are not necessary. You can just get everything you need inside last useMemo (pinnedItems).
What you cant get inside of that useMemo (local storage), fetch outside.

ku5ic added 6 commits October 8, 2025 13:11
Pinned menu items now display the icon of their parent menu, improving
visual consistency and navigation clarity. Added logic to extract and
render parent icons in the pinned section.
Removes the `icon` prop from `PinnableMenuItem` and instead passes the icon
directly to the menu element via `React.cloneElement`. This simplifies the
component API and ensures icons are handled consistently within menu items.
Ensure that the menu item's element is always defined before cloning it.
This prevents runtime errors when rendering pinned menu items with missing
elements. The non-null assertion improves type safety and UI stability.
Pinned menu items and their groups are now hidden when the sidebar is not
pinned. The pin icon's visibility also depends on both the menu item's and
sidebar's pinned state, improving UI consistency.
Renamed the PINNED_KEY function to pinnedKey in PinnableMenuItem and updated
all references in related files for consistency. This improves naming
conventions and code readability in the navigation components.
Pinned menu items now sort with missing items placed at the end, preventing
incorrect ordering when items are not found in the pinned order array.
Removed unnecessary useMemo for pinnableMenus and pinnableKeys to simplify
component logic.
* @returns The localStorage key string for the pinned state.
*/
export const PINNED_KEY = (name: string) => `navigation/${name}/pinned`;
export const pinnedKey = (name: string) => `navigation/${name}/pinned`;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const key = createPinnedKey(whatever)

ku5ic added 3 commits October 9, 2025 10:27
Refactored PinnedMenuItems to memoize pinnableMenus, pinnableKeys, and
pinnedOrder together, reducing redundant computations and improving
performance. This change ensures dependencies are managed efficiently
and state updates are more predictable.
Refactored sidebar state variable from `pinned` to `expanded` in navigation
components for improved clarity. Updated related conditional rendering and
class logic to use the new naming convention.
Renamed the `pinnedKey` function to `createPinnedKey` in navigation components
for improved clarity and consistency. Updated all references in related files.
No functional changes were made.
@ku5ic ku5ic requested a review from Pavel910 October 9, 2025 10:12
@ku5ic ku5ic self-assigned this Oct 9, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants