Fix search in linked groups projects dropdown

What does this MR do and why?

Fixes a bug in the Linked Groups Projects dropdown (used when selecting projects from groups linked to a Security Policy Project) where searching for a project that wasn't in the first page of results returned no matches.

Bug

When a linked group contains more than 20 projects, searching for any project beyond the first 20 returns "No results" — even though the project exists.

Steps to reproduce (before the fix):

  1. Open a policy editor where the scope dropdown is backed by LinkedGroupsProjectsDropdown (e.g. a linked group with ≥ 21 projects).
  2. Type the name of a project that sits at position 21 or later in that group.
  3. The dropdown shows nothing.

Root cause

The underlying GraphQL query get_spp_linked_groups_projects.query.graphql has asymmetric pagination:

securityPolicyProjectLinkedGroups(first: 20, after: $after) {  # paginated, has pageInfo
  nodes {
    projects(includeSubgroups: true, first: 20, search: $search, ids: $projectIds) {
      # no cursor, no count, no pageInfo — capped at 20 per group
      nodes {  }
    }
  }
  pageInfo {  }
}

The outer securityPolicyProjectLinkedGroups is paginated with pageInfo, but the inner projects is capped at first: 20 with no cursor, no count, and no pageInfo.

The component used the group-level hasNextPage to infer whether all projects were loaded:

// before
allProjectsLoaded() {
  return !this.hasNextPage;
},
reactiveVariables() {
  if (this.allProjectsLoaded) {
    return this.pathVariable;   // ← drops `search` entirely
  }
  return { ...this.pathVariable, search: this.searchTerm };
},

When all linked groups fit in a single page (the common case), hasNextPage was false, so allProjectsLoaded became true, so the search variable was dropped from the Apollo query. The Apollo query therefore did not re-run when the user typed, and only client-side filtering over the initially loaded 20 projects ran — missing everything past the first 20.

Fix

Force allProjectsLoaded to always be false so the search term always flows through to the backend:

// after
allProjectsLoaded() {
  return false;
},

And simplified the now-redundant allProjectsCount computed (it was just projects.length).

Why this is the right fix, not something smarter: Because the query exposes no cursor and no count for projects within a group, the frontend genuinely has no way to know whether it has loaded "all" projects for a group. The safe default is to always treat search as server-side.

Minor tradeoff: The footer's "Scroll to load more items" info icon (show-info-icon="!allProjectsLoaded") now shows unconditionally when withProjectCount is enabled. This is mildly misleading in the "there really are no more items" case, but given the underlying query can't distinguish those states, it's the conservative choice and matches the truth for any group with > 20 projects.

References

Screenshots or screen recordings

Before After

How to set up and validate locally

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

Related to #562260 (closed)

Merge request reports

Loading