Skip to content

Top navigation: parent MailResource highlights as active when on Events or Suppressions sub-pages #78

@illia-sapryga

Description

@illia-sapryga

Summary

In a Filament v5 panel that uses topNavigation(), all three resources registered by MailsPlugin (MailResource, EventResource, SuppressionResource) share the same URL prefix (admin/mails/, admin/mails/events, admin/mails/suppressions). Combined with Filament's default getNavigationItemActiveRoutePattern(), the parent MailResource ends up matching its child resources' route names, so two navigation entries are shown as active simultaneously.

Repro

  1. Install backstage/mails ^3.0 on a Filament v5 panel using ->topNavigation().
  2. Register the plugin: ->plugin(MailsPlugin::make()).
  3. Visit /admin/mails/events (or /admin/mails/suppressions).
  4. Open the "Emails" navigation dropdown.

Both the parent Emails group's "Emails" item and the Events (or Suppressions) child item are rendered with the active/green styling.

Expected

Only the item corresponding to the current page should be highlighted.

Cause

Filament's default in Resource\Concerns\HasNavigation::getNavigationItemActiveRoutePattern():

public static function getNavigationItemActiveRoutePattern(): string | array
{
    return static::getRouteBaseName() . '.*';
}

For MailResource this becomes filament.<panel>.resources.mails.* — which greedily matches filament.<panel>.resources.mails.events.index and filament.<panel>.resources.mails.suppressions.index. Both belong to sibling resources that happen to live under the same URL/base-name prefix.

Proposed fix

Override getNavigationItemActiveRoutePattern() on MailResource to enumerate only the route names that genuinely belong to that resource, so the wildcard doesn't bleed into siblings:

public static function getNavigationItemActiveRoutePattern(): string | array
{
    return [
        static::getRouteBaseName() . '.index',
        static::getRouteBaseName() . '.create',
        static::getRouteBaseName() . '.edit',
        static::getRouteBaseName() . '.view',
    ];
}

(Adjust the list to whichever pages are actually defined in getPages().)

A symmetric override on EventResource / SuppressionResource is unnecessary because their base names already include the parent's prefix; only the parent is greedy.

Environment

  • backstage/mails: v3.0.14
  • filament/filament: v5.2.1
  • Laravel: v12
  • PHP: 8.4

Happy to send a PR if helpful.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions