Repository: hashicorp/vault
File: ui/app/components/namespace-picker.ts (TypeScript)

Code before:
  @service declare auth: AuthService;
  @service declare namespace: NamespaceService;
  @service declare router: Router;
  @service declare store: Store;

  // Show/hide refresh & manage namespaces buttons
  @tracked hasListPermissions = false;

  @tracked batchSize = 200;

  @tracked allNamespaces: NamespaceOption[] = [];
  @tracked hasNamespaces = false;
  @tracked searchInput = '';
  @tracked searchInputHelpText =
    "Enter a full path in the search bar and hit the 'Enter' ↵ key to navigate faster.";
  @tracked selected: NamespaceOption | null = null;

  constructor(owner: unknown, args: Record<string, never>) {
    super(owner, args);
    this.loadOptions();
  }

  private matchesPath(option: NamespaceOption, currentPath: string): boolean {
    // TODO: Revisit. A hardcoded check for "path" & "/path" seems hacky, but it fixes a breaking test:
    //  "Acceptance | Enterprise | namespaces: it shows nested namespaces if you log in with a namespace starting with a /"
    //  My assumption is that namespace shouldn't start with a "/", but is this a HVD thing? or is the test outdated?
    return option?.path === currentPath || `/${option?.path}` === currentPath;
  }

  private getSelected(options: NamespaceOption[], currentPath: string): NamespaceOption | undefined {
    return options.find((option) => this.matchesPath(option, currentPath));
  }

  private getOptions(namespace: any): NamespaceOption[] {
    /* Each namespace option has 3 properties: { id, path, and label }
     *   - id: node / namespace name (displayed when the namespace picker is closed)
     *   - path: full namespace path (used to navigate to the namespace)
     *   - label: text displayed inside the namespace picker dropdown (if root, then label = id, else label = path)
     *
     *  Example:
     *   | id       | path           | label          |
     *   | ---      | ----           | -----          |
     *   | 'root'   | ''             | 'root'         |
     *   | 'parent' | 'parent'       | 'parent'       |
     *   | 'child'  | 'parent/child' | 'parent/child' |
     */
    const options = [
      ...(namespace?.accessibleNamespaces || []).map((ns: string) => {
        const parts = ns.split('/');
        return { id: parts[parts.length - 1] || '', path: ns, label: ns };
      }),

Review comment (suggestion):
🗒️ Note: traced this back to [this PR](https://github.com/hashicorp/vault/pull/7186). The test in question was added to verify that the namespace picker works whether the user logs in with '/namespace' or 'namespace'. The namespace service strips out any prefixed '/', so `option?.path` will not have a proceeding '/'. We still have an enterprise test to ensure that the namespace picker works whether the user logs in w/ a '/' prefixed namespace or not.

Code after:
export default class NamespacePicker extends Component {
  @service declare auth: AuthService;
  @service declare namespace: NamespaceService;
  @service declare router: Router;
  @service declare store: Store;

  // Load 200 namespaces in the namespace picker at a time
  @tracked batchSize = 200;

  @tracked allNamespaces: NamespaceOption[] = [];
  @tracked canManageNamespaces = false; // Show/hide manage namespaces button
  @tracked canRefreshNamespaces = false; // Show/hide refresh list button
  @tracked errorLoadingNamespaces = '';
  @tracked hasNamespaces = false;
  @tracked searchInput = '';
  @tracked searchInputHelpText =
    "Enter a full path in the search bar and hit the 'Enter' ↵ key to navigate faster.";
  @tracked selected: NamespaceOption | null = null;

  constructor(owner: unknown, args: Record<string, never>) {
    super(owner, args);
    this.loadOptions();
  }

  private matchesPath(option: NamespaceOption, currentPath: string): boolean {
    return option?.path === currentPath;
  }

  private getSelected(options: NamespaceOption[], currentPath: string): NamespaceOption | undefined {
    return options.find((option) => this.matchesPath(option, currentPath));
  }

  private getOptions(namespace: NamespaceService): NamespaceOption[] {
    /* Each namespace option has 3 properties: { id, path, and label }
     *   - id: node / namespace name (displayed when the namespace picker is closed)
     *   - path: full namespace path (used to navigate to the namespace)
     *   - label: text displayed inside the namespace picker dropdown (if root, then label = id, else label = path)
     *
     *  Example:
     *   | id       | path           | label          |
     *   | ---      | ----           | -----          |
     *   | 'root'   | ''             | 'root'         |
     *   | 'parent' | 'parent'       | 'parent'       |
     *   | 'child'  | 'parent/child' | 'parent/child' |
     */
    const options = [
      ...(namespace?.accessibleNamespaces || []).map((ns: string) => {
        const parts = ns.split('/');
        return { id: parts[parts.length - 1] || '', path: ns, label: ns };
      }),
    ];