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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions