From 328d29aed3cfcf9c14264f595aa7d20b043facae Mon Sep 17 00:00:00 2001
From: Agnes Slota <aslota@gitlab.com>
Date: Fri, 7 Mar 2025 04:44:14 +1100
Subject: [PATCH] Fetch allowed statuses from widget definitions

As part of the first iteration of the configurable
statuses initiative, we would like to:
* return statuses from the system-defined lifecycle
that match the work item type via GraphQL API,
* only expose the data to the Premium and
Ultimate users.
---
 .../widgets/custom_status_resolver.rb         |  5 +-
 .../work_items/widgets/custom_status_type.rb  | 22 +++--
 app/models/group.rb                           |  5 ++
 app/models/project.rb                         |  5 ++
 config/feature_flags/wip/work_item_status.yml |  9 ++
 doc/api/graphql/reference/_index.md           | 14 ++--
 .../widgets/custom_status_resolver.rb         | 37 +++++++++
 .../models/gitlab_subscriptions/features.rb   |  1 +
 .../widgets/custom_status_resolver_spec.rb    | 82 +++++++++++++++++++
 .../widgets/custom_status_type_spec.rb        | 13 +++
 ee/spec/models/ee/group_spec.rb               | 24 ++++++
 ee/spec/models/ee/project_spec.rb             | 24 ++++++
 .../work_item_types_shared_context.rb         |  2 +-
 .../widgets/custom_status_resolver_spec.rb    | 53 ++++++++++++
 .../widgets/custom_status_type_spec.rb        |  4 +-
 spec/models/group_spec.rb                     |  6 ++
 spec/models/project_spec.rb                   | 14 +++-
 .../work_item_types_shared_context.rb         | 47 +++++++++--
 .../work_item_type_list_shared_examples.rb    | 19 +++++
 19 files changed, 353 insertions(+), 33 deletions(-)
 create mode 100644 config/feature_flags/wip/work_item_status.yml
 create mode 100644 ee/app/graphql/ee/resolvers/work_items/widgets/custom_status_resolver.rb
 create mode 100644 ee/spec/graphql/resolvers/work_items/widgets/custom_status_resolver_spec.rb
 create mode 100644 ee/spec/graphql/types/work_items/widgets/custom_status_type_spec.rb
 create mode 100644 spec/graphql/resolvers/work_items/widgets/custom_status_resolver_spec.rb

diff --git a/app/graphql/resolvers/work_items/widgets/custom_status_resolver.rb b/app/graphql/resolvers/work_items/widgets/custom_status_resolver.rb
index 24ac11c39f719..ece6cf242583c 100644
--- a/app/graphql/resolvers/work_items/widgets/custom_status_resolver.rb
+++ b/app/graphql/resolvers/work_items/widgets/custom_status_resolver.rb
@@ -7,10 +7,11 @@ class CustomStatusResolver < BaseResolver
         type ::Types::WorkItems::Widgets::CustomStatusType.connection_type, null: true
 
         def resolve
-          # Implement during https://gitlab.com/gitlab-org/gitlab/-/issues/498393
-          [::WorkItems::Widgets::CustomStatus.new(nil), ::WorkItems::Widgets::CustomStatus.new(nil)]
+          []
         end
       end
     end
   end
 end
+
+Resolvers::WorkItems::Widgets::CustomStatusResolver.prepend_mod
diff --git a/app/graphql/types/work_items/widgets/custom_status_type.rb b/app/graphql/types/work_items/widgets/custom_status_type.rb
index 4e04259020d3e..7a53a45c85049 100644
--- a/app/graphql/types/work_items/widgets/custom_status_type.rb
+++ b/app/graphql/types/work_items/widgets/custom_status_type.rb
@@ -12,22 +12,30 @@ class CustomStatusType < BaseObject
 
         implements ::Types::WorkItems::WidgetInterface
 
-        # TODO change the ID to CustomStatus model ID while implementing
-        # https://gitlab.com/gitlab-org/gitlab/-/issues/498393
-        field :id, ::Types::GlobalIDType[::WorkItems::Widgets::CustomStatus],
-          null: false,
+        field :id, Types::GlobalIDType,
+          null: true,
           experiment: { milestone: '17.8' },
-          description: 'ID of the Custom Status.'
+          description: 'ID of the custom status.'
 
         field :name, GraphQL::Types::String,
           null: true,
           experiment: { milestone: '17.8' },
-          description: 'Name of the Custom Status.'
+          description: 'Name of the custom status.'
 
         field :icon_name, GraphQL::Types::String,
           null: true,
           experiment: { milestone: '17.8' },
-          description: 'Icon name of the Custom Status.'
+          description: 'Icon name of the custom status.'
+
+        field :color, GraphQL::Types::String,
+          null: true,
+          experiment: { milestone: '17.10' },
+          description: 'Color of the custom status.'
+
+        field :position, GraphQL::Types::Int,
+          null: true,
+          experiment: { milestone: '17.10' },
+          description: 'Position of the custom status within its category.'
       end
       # rubocop:enable Graphql/AuthorizeTypes
     end
diff --git a/app/models/group.rb b/app/models/group.rb
index 9c04bf0277d38..76c312b5fe4b5 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -1004,6 +1004,11 @@ def work_items_alpha_feature_flag_enabled?
     feature_flag_enabled_for_self_or_ancestor?(:work_items_alpha)
   end
 
+  def work_item_status_feature_available?
+    feature_flag_enabled_for_self_or_ancestor?(:work_item_status, type: :wip) &&
+      licensed_feature_available?(:work_item_custom_status)
+  end
+
   def continue_indented_text_feature_flag_enabled?
     feature_flag_enabled_for_self_or_ancestor?(:continue_indented_text, type: :wip)
   end
diff --git a/app/models/project.rb b/app/models/project.rb
index bd055a8a4105a..01a8a8f48c6d4 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -3313,6 +3313,11 @@ def work_items_alpha_feature_flag_enabled?
     group&.work_items_alpha_feature_flag_enabled? || Feature.enabled?(:work_items_alpha)
   end
 
+  def work_item_status_feature_available?
+    (group&.work_item_status_feature_available? || Feature.enabled?(:work_item_status, type: :wip)) &&
+      licensed_feature_available?(:work_item_custom_status)
+  end
+
   def glql_integration_feature_flag_enabled?
     group&.glql_integration_feature_flag_enabled? || Feature.enabled?(:glql_integration, self)
   end
diff --git a/config/feature_flags/wip/work_item_status.yml b/config/feature_flags/wip/work_item_status.yml
new file mode 100644
index 0000000000000..863454fc2957c
--- /dev/null
+++ b/config/feature_flags/wip/work_item_status.yml
@@ -0,0 +1,9 @@
+---
+name: work_item_status
+feature_issue_url: https://gitlab.com/groups/gitlab-org/-/epics/5099
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/182225
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/521286
+milestone: '17.10'
+group: group::project management
+type: wip
+default_enabled: false
diff --git a/doc/api/graphql/reference/_index.md b/doc/api/graphql/reference/_index.md
index 300523bb5d4a1..fdc72b5b92142 100644
--- a/doc/api/graphql/reference/_index.md
+++ b/doc/api/graphql/reference/_index.md
@@ -39901,9 +39901,11 @@ Represents Custom Status widget.
 
 | Name | Type | Description |
 | ---- | ---- | ----------- |
-| <a id="workitemwidgetcustomstatusiconname"></a>`iconName` {{< icon name="warning-solid" >}} | [`String`](#string) | **Introduced** in GitLab 17.8. **Status**: Experiment. Icon name of the Custom Status. |
-| <a id="workitemwidgetcustomstatusid"></a>`id` {{< icon name="warning-solid" >}} | [`WorkItemsWidgetsCustomStatusID!`](#workitemswidgetscustomstatusid) | **Introduced** in GitLab 17.8. **Status**: Experiment. ID of the Custom Status. |
-| <a id="workitemwidgetcustomstatusname"></a>`name` {{< icon name="warning-solid" >}} | [`String`](#string) | **Introduced** in GitLab 17.8. **Status**: Experiment. Name of the Custom Status. |
+| <a id="workitemwidgetcustomstatuscolor"></a>`color` {{< icon name="warning-solid" >}} | [`String`](#string) | **Introduced** in GitLab 17.10. **Status**: Experiment. Color of the custom status. |
+| <a id="workitemwidgetcustomstatusiconname"></a>`iconName` {{< icon name="warning-solid" >}} | [`String`](#string) | **Introduced** in GitLab 17.8. **Status**: Experiment. Icon name of the custom status. |
+| <a id="workitemwidgetcustomstatusid"></a>`id` {{< icon name="warning-solid" >}} | [`GlobalID`](#globalid) | **Introduced** in GitLab 17.8. **Status**: Experiment. ID of the custom status. |
+| <a id="workitemwidgetcustomstatusname"></a>`name` {{< icon name="warning-solid" >}} | [`String`](#string) | **Introduced** in GitLab 17.8. **Status**: Experiment. Name of the custom status. |
+| <a id="workitemwidgetcustomstatusposition"></a>`position` {{< icon name="warning-solid" >}} | [`Int`](#int) | **Introduced** in GitLab 17.10. **Status**: Experiment. Position of the custom status within its category. |
 | <a id="workitemwidgetcustomstatustype"></a>`type` | [`WorkItemWidgetType`](#workitemwidgettype) | Widget type. |
 
 ### `WorkItemWidgetDefinitionAssignees`
@@ -45393,12 +45395,6 @@ A `WorkItemsTypeID` is a global ID. It is encoded as a string.
 
 An example `WorkItemsTypeID` is: `"gid://gitlab/WorkItems::Type/1"`.
 
-### `WorkItemsWidgetsCustomStatusID`
-
-A `WorkItemsWidgetsCustomStatusID` is a global ID. It is encoded as a string.
-
-An example `WorkItemsWidgetsCustomStatusID` is: `"gid://gitlab/WorkItems::Widgets::CustomStatus/1"`.
-
 ## Abstract types
 
 Abstract types (unions and interfaces) are ways the schema can represent
diff --git a/ee/app/graphql/ee/resolvers/work_items/widgets/custom_status_resolver.rb b/ee/app/graphql/ee/resolvers/work_items/widgets/custom_status_resolver.rb
new file mode 100644
index 0000000000000..2142fd2e28b7b
--- /dev/null
+++ b/ee/app/graphql/ee/resolvers/work_items/widgets/custom_status_resolver.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+module EE
+  module Resolvers
+    module WorkItems
+      module Widgets
+        module CustomStatusResolver
+          extend ::Gitlab::Utils::Override
+
+          override :resolve
+          def resolve
+            return [] unless work_item_status_feature_available?
+
+            # As part of iteration 1, we only support system defined statuses
+            # Custom lifecycle based on namespace will be supported in iteration 2
+            lifecycle_for(object.work_item_type)&.statuses || []
+          end
+
+          private
+
+          def work_item_status_feature_available?
+            root_ancestor&.try(:work_item_status_feature_available?)
+          end
+
+          def root_ancestor
+            context[:resource_parent]&.root_ancestor
+          end
+
+          def lifecycle_for(work_item_type)
+            base_type = work_item_type.base_type.to_sym
+            ::WorkItems::Statuses::SystemDefined::Lifecycle.of_work_item_base_type(base_type)
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/ee/app/models/gitlab_subscriptions/features.rb b/ee/app/models/gitlab_subscriptions/features.rb
index 89316ee19eaf8..9c5ae2ed14dd2 100644
--- a/ee/app/models/gitlab_subscriptions/features.rb
+++ b/ee/app/models/gitlab_subscriptions/features.rb
@@ -187,6 +187,7 @@ class Features
       default_roles_assignees
       ci_component_usages_in_projects
       branch_rule_squash_options
+      work_item_custom_status
     ].freeze
 
     ULTIMATE_FEATURES = %i[
diff --git a/ee/spec/graphql/resolvers/work_items/widgets/custom_status_resolver_spec.rb b/ee/spec/graphql/resolvers/work_items/widgets/custom_status_resolver_spec.rb
new file mode 100644
index 0000000000000..bdaeb9e9c73bc
--- /dev/null
+++ b/ee/spec/graphql/resolvers/work_items/widgets/custom_status_resolver_spec.rb
@@ -0,0 +1,82 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Resolvers::WorkItems::Widgets::CustomStatusResolver, feature_category: :team_planning do
+  include GraphqlHelpers
+
+  let_it_be(:group) { create(:group) }
+  let_it_be(:project) { create(:project, group: group) }
+  let_it_be(:current_user) { create(:user) }
+  let_it_be(:task_type) { create(:work_item_type, :task) }
+  let_it_be(:widget_definition) do
+    create(:widget_definition, widget_type: :custom_status, work_item_type: task_type, name: 'TesT Widget')
+  end
+
+  shared_examples 'returns system defined statuses' do
+    it 'fetches allowed custom statuses for a given work item type' do
+      system_defined_statuses = ::WorkItems::Statuses::SystemDefined::Status.all
+
+      expect(resolve_custom_statuses.items.map(&:name)).to eq(system_defined_statuses.map(&:name))
+    end
+  end
+
+  shared_examples 'does not return system defined statuses' do
+    it 'returns an empty array' do
+      expect(resolve_custom_statuses&.items).to eq([])
+    end
+  end
+
+  describe '#resolve' do
+    let(:resource_parent) { group }
+
+    before do
+      stub_licensed_features(work_item_custom_status: true)
+    end
+
+    context 'with group' do
+      it_behaves_like 'returns system defined statuses'
+    end
+
+    context 'with project' do
+      let(:resource_parent) { project }
+
+      it_behaves_like 'returns system defined statuses'
+    end
+
+    context 'with unsupported namespace' do
+      let(:resource_parent) { current_user.namespace }
+
+      it_behaves_like 'does not return system defined statuses'
+    end
+
+    context 'when work item type is not supported' do
+      let_it_be(:epic_type) { create(:work_item_type, :epic) }
+      let_it_be(:widget_definition) do
+        create(:widget_definition, widget_type: :custom_status, work_item_type: epic_type, name: 'TesT Widget')
+      end
+
+      it_behaves_like 'does not return system defined statuses'
+    end
+
+    context 'with work_item_status feature flag disabled' do
+      before do
+        stub_feature_flags(work_item_status: false)
+      end
+
+      it_behaves_like 'does not return system defined statuses'
+    end
+
+    context 'with work_item_status licensed feature disabled' do
+      before do
+        stub_licensed_features(work_item_custom_status: false)
+      end
+
+      it_behaves_like 'does not return system defined statuses'
+    end
+  end
+
+  def resolve_custom_statuses(args = {}, context = { current_user: current_user, resource_parent: resource_parent })
+    resolve(described_class, obj: widget_definition, args: args, ctx: context)
+  end
+end
diff --git a/ee/spec/graphql/types/work_items/widgets/custom_status_type_spec.rb b/ee/spec/graphql/types/work_items/widgets/custom_status_type_spec.rb
new file mode 100644
index 0000000000000..6d5ed400bcc8a
--- /dev/null
+++ b/ee/spec/graphql/types/work_items/widgets/custom_status_type_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Types::WorkItems::Widgets::CustomStatusType, feature_category: :team_planning do
+  it 'exposes the expected fields' do
+    expected_fields = %i[id name icon_name color position]
+
+    expected_fields.each do |field|
+      expect(described_class).to have_graphql_field(field)
+    end
+  end
+end
diff --git a/ee/spec/models/ee/group_spec.rb b/ee/spec/models/ee/group_spec.rb
index 8b0270960bdb8..9d24a9551b608 100644
--- a/ee/spec/models/ee/group_spec.rb
+++ b/ee/spec/models/ee/group_spec.rb
@@ -4322,6 +4322,30 @@ def webhook_headers
     end
   end
 
+  describe '#work_item_status_feature_available?' do
+    subject { group.work_item_status_feature_available? }
+
+    before do
+      stub_feature_flags(work_item_status: true)
+    end
+
+    context 'when work_item_status licensed feature is enabled' do
+      before do
+        stub_licensed_features(work_item_custom_status: true)
+      end
+
+      it { is_expected.to be true }
+    end
+
+    context 'when work_item_status licensed feature is disabled' do
+      before do
+        stub_licensed_features(work_item_custom_status: false)
+      end
+
+      it { is_expected.to be false }
+    end
+  end
+
   describe '#can_manage_extensions_marketplace_for_enterprise_users?' do
     let_it_be(:root_group) { create(:group) }
     let_it_be(:child_group) { create(:group, parent: root_group) }
diff --git a/ee/spec/models/ee/project_spec.rb b/ee/spec/models/ee/project_spec.rb
index 15a74de4cc980..4c129e268cb75 100644
--- a/ee/spec/models/ee/project_spec.rb
+++ b/ee/spec/models/ee/project_spec.rb
@@ -235,6 +235,30 @@
       end
     end
 
+    describe '#work_item_status_feature_available?' do
+      subject { project.work_item_status_feature_available? }
+
+      before do
+        stub_feature_flags(work_item_status: true)
+      end
+
+      context 'when work_item_status licensed feature is enabled' do
+        before do
+          stub_licensed_features(work_item_custom_status: true)
+        end
+
+        it { is_expected.to be true }
+      end
+
+      context 'when work_item_status licensed feature is disabled' do
+        before do
+          stub_licensed_features(work_item_custom_status: false)
+        end
+
+        it { is_expected.to be false }
+      end
+    end
+
     context 'import_state dependant predicate method' do
       shared_examples 'returns expected values' do
         context 'when project lacks a import_state relation' do
diff --git a/ee/spec/support/shared_contexts/requests/api/graphql/work_items/work_item_types_shared_context.rb b/ee/spec/support/shared_contexts/requests/api/graphql/work_items/work_item_types_shared_context.rb
index 6313b33bdd918..70687b37f6ab4 100644
--- a/ee/spec/support/shared_contexts/requests/api/graphql/work_items/work_item_types_shared_context.rb
+++ b/ee/spec/support/shared_contexts/requests/api/graphql/work_items/work_item_types_shared_context.rb
@@ -31,7 +31,7 @@
         }
         ... on WorkItemWidgetDefinitionCustomStatus {
           allowedCustomStatuses {
-            nodes { id name iconName }
+            nodes { id name iconName color position }
           }
         }
       }
diff --git a/spec/graphql/resolvers/work_items/widgets/custom_status_resolver_spec.rb b/spec/graphql/resolvers/work_items/widgets/custom_status_resolver_spec.rb
new file mode 100644
index 0000000000000..70ef0b94a5494
--- /dev/null
+++ b/spec/graphql/resolvers/work_items/widgets/custom_status_resolver_spec.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Resolvers::WorkItems::Widgets::CustomStatusResolver, feature_category: :team_planning do
+  include GraphqlHelpers
+
+  let_it_be(:group) { create(:group) }
+  let_it_be(:project) { create(:project, group: group) }
+  let_it_be(:current_user) { create(:user) }
+  let_it_be(:task_type) { create(:work_item_type, :task) }
+  let_it_be(:widget_definition) do
+    create(:widget_definition, widget_type: :custom_status, work_item_type: task_type, name: 'TesT Widget')
+  end
+
+  shared_examples 'does not return system defined statuses' do
+    it 'returns an empty array' do
+      expect(resolve_custom_statuses&.items).to eq([])
+    end
+  end
+
+  describe '#resolve' do
+    let(:resource_parent) { group }
+
+    context 'with group' do
+      it_behaves_like 'does not return system defined statuses'
+    end
+
+    context 'with project' do
+      let(:resource_parent) { project }
+
+      it_behaves_like 'does not return system defined statuses'
+    end
+
+    context 'with unsupported namespace' do
+      let(:resource_parent) { current_user.namespace }
+
+      it_behaves_like 'does not return system defined statuses'
+    end
+
+    context 'with work_item_status feature flag disabled' do
+      before do
+        stub_feature_flags(work_item_status: false)
+      end
+
+      it_behaves_like 'does not return system defined statuses'
+    end
+  end
+
+  def resolve_custom_statuses(args = {}, context = { current_user: current_user, resource_parent: resource_parent })
+    resolve(described_class, obj: widget_definition, args: args, ctx: context)
+  end
+end
diff --git a/spec/graphql/types/work_items/widgets/custom_status_type_spec.rb b/spec/graphql/types/work_items/widgets/custom_status_type_spec.rb
index a7821cc2ab999..6d5ed400bcc8a 100644
--- a/spec/graphql/types/work_items/widgets/custom_status_type_spec.rb
+++ b/spec/graphql/types/work_items/widgets/custom_status_type_spec.rb
@@ -2,9 +2,9 @@
 
 require 'spec_helper'
 
-RSpec.describe Types::WorkItems::Widgets::CustomStatusType, feature_category: :service_desk do
+RSpec.describe Types::WorkItems::Widgets::CustomStatusType, feature_category: :team_planning do
   it 'exposes the expected fields' do
-    expected_fields = %i[id name icon_name]
+    expected_fields = %i[id name icon_name color position]
 
     expected_fields.each do |field|
       expect(described_class).to have_graphql_field(field)
diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb
index 0cb7212d4f1dc..cacd9d5563504 100644
--- a/spec/models/group_spec.rb
+++ b/spec/models/group_spec.rb
@@ -4063,6 +4063,12 @@ def define_cache_expectations(cache_key)
     end
   end
 
+  describe '#work_item_status_feature_available?' do
+    subject { group.work_item_status_feature_available? }
+
+    it { is_expected.to be false }
+  end
+
   describe '#continue_indented_text_feature_flag_enabled?' do
     it_behaves_like 'checks self and root ancestor feature flag' do
       let(:feature_flag) { :continue_indented_text }
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 52a43daf0a533..43c8e06bb060d 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -5767,9 +5767,9 @@ def has_external_wiki
   end
 
   describe '#predefined_project_variables' do
-    let_it_be(:project) { create(:project, :repository) }
+    let(:project_with_pre_defined_var) { create(:project, :repository) }
 
-    subject { project.predefined_project_variables.to_runner_variables }
+    subject { project_with_pre_defined_var.predefined_project_variables.to_runner_variables }
 
     specify do
       expect(subject).to include(
@@ -5779,7 +5779,7 @@ def has_external_wiki
 
     context 'when ci config path is overridden' do
       before do
-        project.update!(ci_config_path: 'random.yml')
+        project_with_pre_defined_var.update!(ci_config_path: 'random.yml')
       end
 
       it do
@@ -9164,6 +9164,14 @@ def has_external_wiki
     end
   end
 
+  describe '#work_item_status_feature_available?' do
+    let_it_be(:group_project) { create(:project, :in_subgroup) }
+
+    it "return false" do
+      expect(group_project.work_item_status_feature_available?).to be false
+    end
+  end
+
   describe 'serialization' do
     let(:object) { build(:project) }
 
diff --git a/spec/support/shared_contexts/requests/api/graphql/work_items/work_item_types_shared_context.rb b/spec/support/shared_contexts/requests/api/graphql/work_items/work_item_types_shared_context.rb
index 403e7645f64b9..e492f935ccccc 100644
--- a/spec/support/shared_contexts/requests/api/graphql/work_items/work_item_types_shared_context.rb
+++ b/spec/support/shared_contexts/requests/api/graphql/work_items/work_item_types_shared_context.rb
@@ -21,7 +21,7 @@
         }
         ... on WorkItemWidgetDefinitionCustomStatus {
           allowedCustomStatuses {
-            nodes { id name iconName }
+            nodes { id name iconName color position }
           }
         }
       }
@@ -72,7 +72,7 @@ def widgets_for(work_item_type, resource_parent)
 
       if widget == WorkItems::Widgets::CustomStatus
         next custom_status_widget_attributes(work_item_type,
-          base_attributes)
+          base_attributes, resource_parent)
       end
 
       next base_attributes unless widget_attributes[widget.type]
@@ -96,17 +96,46 @@ def hierarchy_widget_attributes(work_item_type, base_attributes, resource_parent
       .merge({ 'allowedChildTypes' => { 'nodes' => child_types }, 'allowedParentTypes' => { 'nodes' => parent_types } })
   end
 
-  def custom_status_widget_attributes(_work_item_type, base_attributes)
+  def custom_status_widget_attributes(_work_item_type, base_attributes, resource_parent)
+    unless resource_parent&.root_ancestor&.try(:work_item_status_feature_available?)
+      return base_attributes.merge({ 'allowedCustomStatuses' => { 'nodes' => [] } })
+    end
+
     statuses = [
       {
-        'id' => 'gid://gitlab/WorkItems::Widgets::CustomStatus/10',
-        'name' => 'Custom Status',
-        'iconName' => 'custom_status icon'
+        'id' => 'gid://gitlab/WorkItems::Statuses::SystemDefined::Status/1',
+        'name' => 'To do',
+        'iconName' => 'status-waiting',
+        'color' => '#535158',
+        'position' => 0
+      },
+      {
+        'id' => 'gid://gitlab/WorkItems::Statuses::SystemDefined::Status/2',
+        'name' => 'In progress',
+        'iconName' => 'status-running',
+        'color' => '#0b5cad',
+        'position' => 0
+      },
+      {
+        'id' => 'gid://gitlab/WorkItems::Statuses::SystemDefined::Status/3',
+        'name' => 'Done',
+        'iconName' => 'status-success',
+        'color' => '#23663b',
+        'position' => 0
+      },
+      {
+        'id' => 'gid://gitlab/WorkItems::Statuses::SystemDefined::Status/4',
+        'name' => "Won't do",
+        'iconName' => 'status-cancelled',
+        'color' => '#ae1901',
+        'position' => 0
       },
       {
-        'id' => 'gid://gitlab/WorkItems::Widgets::CustomStatus/10',
-        'name' => 'Custom Status',
-        'iconName' => 'custom_status icon'
+        'id' => 'gid://gitlab/WorkItems::Statuses::SystemDefined::Status/5',
+        'name' => 'Duplicate',
+        'iconName' => 'status-cancelled',
+        'color' => '#ae1901',
+        'position' => 10
       }
     ]
 
diff --git a/spec/support/shared_examples/requests/api/graphql/work_item_type_list_shared_examples.rb b/spec/support/shared_examples/requests/api/graphql/work_item_type_list_shared_examples.rb
index 28ac13d8d1feb..2833013135e2a 100644
--- a/spec/support/shared_examples/requests/api/graphql/work_item_type_list_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/graphql/work_item_type_list_shared_examples.rb
@@ -33,6 +33,25 @@
       )
     end
 
+    if Gitlab.ee?
+      it 'returns the allowed custom statuses' do
+        stub_licensed_features(work_item_custom_status: true)
+        post_graphql(query, current_user: current_user)
+
+        work_item_types = graphql_data_at(parent_key, :workItemTypes, :nodes)
+        custom_status_widgets = work_item_types.flat_map do |work_item_type|
+          work_item_type['widgetDefinitions'].select { |widget| widget['type'] == 'CUSTOM_STATUS' }
+        end
+
+        expect(custom_status_widgets).to be_present
+        custom_status_widgets.each do |widget|
+          expect(widget['allowedCustomStatuses']).to be_present
+          expect(widget['allowedCustomStatuses']['nodes']).to all(include('id', 'name', 'iconName', 'color',
+            'position'))
+        end
+      end
+    end
+
     it 'prevents N+1 queries' do
       # Destroy 2 existing types
       WorkItems::Type.by_type([:issue, :task]).delete_all
-- 
GitLab