Repository: hashicorp/vault
File: ui/app/components/namespace-picker.js (JavaScript)

Code before:

    const ns = this.namespace;
    const oldNS = this.oldNamespace;
    if (!oldNS || ns !== oldNS) {
      this.setForAnimation.perform();
      this.fetchListCapability.perform();
    }
    this.set('oldNamespace', ns);
  },

  fetchListCapability: task(function* () {
    try {
      const capability = yield this.store.findRecord('capabilities', 'sys/namespaces/');
      this.set('listCapability', capability);
      this.set('canList', true);
    } catch (e) {
      // If error out on findRecord call it's because you don't have permissions
      // and therefore don't have permission to manage namespaces
      this.set('canList', false);
    }
  }),
  setForAnimation: task(function* () {
    const leaves = this.menuLeaves;
    const lastLeaves = this.lastMenuLeaves;
    if (!lastLeaves) {
      this.set('lastMenuLeaves', leaves);
      yield timeout(0);
      return;
    }
    const isAdding = leaves.length > lastLeaves.length;
    const changedLeaves = isAdding ? leaves : lastLeaves;
    const [changedLeaf] = changedLeaves.slice(-1);
    this.set('isAdding', isAdding);
    this.set('changedLeaf', changedLeaf);

    // if we're adding we want to render immediately an animate it in
    // if we're not adding, we need time to move the item out before
    // a rerender removes it
    if (isAdding) {
      this.set('lastMenuLeaves', leaves);
      yield timeout(0);
      return;
    }
    yield timeout(ANIMATION_DURATION);
    this.set('lastMenuLeaves', leaves);
  }).drop(),

  isAnimating: alias('setForAnimation.isRunning'),

  namespacePath: alias('namespaceService.path'),


Review comment (suggestion):
_Very_ unimportant nit. 

```suggestion
      // TODO: Some users (including HCP Admin User) should never see the root namespace. Address this in a followup PR.
```

Code after:
  #matchesPath(option, currentNamespace) {
    // 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 === currentNamespace?.path || `/${option?.path}` === currentNamespace?.path;
  }

  #getSelected(options, currentNamespace) {
    return options.find((option) => this.#matchesPath(option, currentNamespace));
  }

  #getOptions(namespace) {
    /* 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'                  |
     *   | namespace1 | 'namespace1'            | 'namespace1'            |
     *   | namespace2 | 'namespace1/namespace2' | 'namespace1/namespace2' |
     */
    return [
      // TODO: Some users (including HDS Admin User) should never see the root namespace. Address this in a followup PR.
      { id: 'root', path: '', label: 'root' },
      ...(namespace?.accessibleNamespaces || []).map((ns) => {
        const parts = ns.split('/');
        return { id: parts[parts.length - 1], path: ns, label: ns };
      }),
    ];
  }

  @action
  async loadOptions() {
    // TODO: namespace service's findNamespacesForUser will never throw an error.
    //  Check with design to determine if we should continue to ignore or handle an error situation here.
    await this.namespace?.findNamespacesForUser.perform();

    this.options = this.#getOptions(this.namespace);
    this.selected = this.#getSelected(this.options, this.namespace);
  }

  @action
  async onChange(selectedOption) {
    // TODO: redirect to selected namespace
    this.selected = selectedOption;
  }
}