diff --git a/.rubocop_todo/rspec/before_all_role_assignment.yml b/.rubocop_todo/rspec/before_all_role_assignment.yml
index 20ec3996b0795dbb068aba55c5cb46063cf2df1b..0e66a529b74d0cec1e3e06778de4e9eb1d2b3e06 100644
--- a/.rubocop_todo/rspec/before_all_role_assignment.yml
+++ b/.rubocop_todo/rspec/before_all_role_assignment.yml
@@ -703,7 +703,6 @@ RSpec/BeforeAllRoleAssignment:
     - 'ee/spec/services/vulnerability_issue_links/bulk_create_service_spec.rb'
     - 'ee/spec/services/vulnerability_issue_links/delete_service_spec.rb'
     - 'ee/spec/services/vulnerability_merge_request_links/create_service_spec.rb'
-    - 'ee/spec/services/work_items/widgets/health_status_service/update_service_spec.rb'
     - 'ee/spec/services/work_items/widgets/iteration_service/update_service_spec.rb'
     - 'ee/spec/services/work_items/widgets/progress_service/update_service_spec.rb'
     - 'ee/spec/services/work_items/widgets/status_service/update_service_spec.rb'
diff --git a/.rubocop_todo/rspec/named_subject.yml b/.rubocop_todo/rspec/named_subject.yml
index 6cff49a0981a4c4292ca84cfec96a0d97c262622..eb4e7ab06bbef415dd032e925f1afeed3040783b 100644
--- a/.rubocop_todo/rspec/named_subject.yml
+++ b/.rubocop_todo/rspec/named_subject.yml
@@ -1140,7 +1140,6 @@ RSpec/NamedSubject:
     - 'ee/spec/services/vulnerability_external_issue_links/create_service_spec.rb'
     - 'ee/spec/services/vulnerability_feedback/create_service_spec.rb'
     - 'ee/spec/services/work_items/update_service_spec.rb'
-    - 'ee/spec/services/work_items/widgets/health_status_service/update_service_spec.rb'
     - 'ee/spec/services/work_items/widgets/iteration_service/update_service_spec.rb'
     - 'ee/spec/services/work_items/widgets/progress_service/update_service_spec.rb'
     - 'ee/spec/services/work_items/widgets/status_service/update_service_spec.rb'
diff --git a/app/services/issuable/callbacks/base.rb b/app/services/issuable/callbacks/base.rb
index 368dd76c16c0c143c59098e061f810cb0cd7fce6..5ac7ccf54fc3b1a119c254fd77b4753ea97a368b 100644
--- a/app/services/issuable/callbacks/base.rb
+++ b/app/services/issuable/callbacks/base.rb
@@ -5,7 +5,7 @@ module Callbacks
     class Base
       include Gitlab::Allowable
 
-      def initialize(issuable:, current_user:, params:)
+      def initialize(issuable:, current_user:, params: {})
         @issuable = issuable
         @current_user = current_user
         @params = params
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 5e706605e0af7fdfae351056380b7f2f07cf104f..fbae22b968136747f92d2284cce2368a614fb4ff 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -9442,9 +9442,11 @@ Input type: `WorkItemCreateInput`
 | Name | Type | Description |
 | ---- | ---- | ----------- |
 | <a id="mutationworkitemcreateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
+| <a id="mutationworkitemcreatecolorwidget"></a>`colorWidget` | [`WorkItemWidgetColorInput`](#workitemwidgetcolorinput) | Input for color widget. |
 | <a id="mutationworkitemcreateconfidential"></a>`confidential` | [`Boolean`](#boolean) | Sets the work item confidentiality. |
 | <a id="mutationworkitemcreatedescription"></a>`description` **{warning-solid}** | [`String`](#string) | **Deprecated:** use description widget instead. Deprecated in GitLab 16.9. |
 | <a id="mutationworkitemcreatedescriptionwidget"></a>`descriptionWidget` | [`WorkItemWidgetDescriptionInput`](#workitemwidgetdescriptioninput) | Input for description widget. |
+| <a id="mutationworkitemcreatehealthstatuswidget"></a>`healthStatusWidget` | [`WorkItemWidgetHealthStatusInput`](#workitemwidgethealthstatusinput) | Input for health status widget. |
 | <a id="mutationworkitemcreatehierarchywidget"></a>`hierarchyWidget` | [`WorkItemWidgetHierarchyCreateInput`](#workitemwidgethierarchycreateinput) | Input for hierarchy widget. |
 | <a id="mutationworkitemcreateiterationwidget"></a>`iterationWidget` | [`WorkItemWidgetIterationInput`](#workitemwidgetiterationinput) | Iteration widget of the work item. |
 | <a id="mutationworkitemcreatemilestonewidget"></a>`milestoneWidget` | [`WorkItemWidgetMilestoneInput`](#workitemwidgetmilestoneinput) | Input for milestone widget. |
diff --git a/ee/app/graphql/ee/mutations/work_items/create.rb b/ee/app/graphql/ee/mutations/work_items/create.rb
index 8c8816fab60feeff2f7e4bc09662eb5a7d8ec15e..70dd6ec333eac7a0dddeab4c4c4a63e9c7bf7a28 100644
--- a/ee/app/graphql/ee/mutations/work_items/create.rb
+++ b/ee/app/graphql/ee/mutations/work_items/create.rb
@@ -7,6 +7,11 @@ module Create
         extend ActiveSupport::Concern
 
         prepended do
+          argument :health_status_widget,
+            ::Types::WorkItems::Widgets::HealthStatusInputType,
+            required: false,
+            description: 'Input for health status widget.'
+
           argument :iteration_widget,
             ::Types::WorkItems::Widgets::IterationInputType,
             required: false,
@@ -17,6 +22,10 @@ module Create
             required: false,
             description: 'Input for rolledup dates widget.',
             alpha: { milestone: '16.9' }
+
+          argument :color_widget, ::Types::WorkItems::Widgets::ColorInputType,
+            required: false,
+            description: 'Input for color widget.'
         end
       end
     end
diff --git a/ee/app/services/work_items/callbacks/color.rb b/ee/app/services/work_items/callbacks/color.rb
index aac5fd2bb5d7c4e0e8bee6e61c8ea4c4ff0ce1e7..59f063ff6450fb9d72e1790a2ecae967f9006fad 100644
--- a/ee/app/services/work_items/callbacks/color.rb
+++ b/ee/app/services/work_items/callbacks/color.rb
@@ -2,7 +2,7 @@
 
 module WorkItems
   module Callbacks
-    class Color < ::WorkItems::Callbacks::Base
+    class Color < Base
       ALLOWED_PARAMS = %i[color skip_system_notes].freeze
 
       def after_initialize
diff --git a/ee/app/services/work_items/callbacks/health_status.rb b/ee/app/services/work_items/callbacks/health_status.rb
new file mode 100644
index 0000000000000000000000000000000000000000..0ff7c4d09a0eb6c8e434ee8be57374e72a02f632
--- /dev/null
+++ b/ee/app/services/work_items/callbacks/health_status.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+module WorkItems
+  module Callbacks
+    class HealthStatus < Base
+      def after_initialize
+        params[:health_status] = nil if excluded_in_new_type?
+        return unless params.key?(:health_status) && can_set_health_status?
+
+        work_item.health_status = params[:health_status]
+      end
+
+      private
+
+      def can_set_health_status?
+        work_item.resource_parent&.feature_available?(:issuable_health_status) && has_permission?(:admin_work_item)
+      end
+    end
+  end
+end
diff --git a/ee/app/services/work_items/widgets/health_status_service/update_service.rb b/ee/app/services/work_items/widgets/health_status_service/update_service.rb
deleted file mode 100644
index c307ca085d10dee9d6a8be48f9da3d9c26fe5fd2..0000000000000000000000000000000000000000
--- a/ee/app/services/work_items/widgets/health_status_service/update_service.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-# frozen_string_literal: true
-
-module WorkItems
-  module Widgets
-    module HealthStatusService
-      class UpdateService < WorkItems::Widgets::BaseService
-        def before_update_callback(params:)
-          params[:health_status] = nil if new_type_excludes_widget?
-
-          return unless params&.key?(:health_status)
-          return unless health_status_available? && has_permission?(:admin_work_item)
-
-          work_item.health_status = params[:health_status]
-        end
-
-        def health_status_available?
-          work_item.resource_parent&.feature_available?(:issuable_health_status)
-        end
-      end
-    end
-  end
-end
diff --git a/ee/spec/requests/api/graphql/mutations/work_items/create_spec.rb b/ee/spec/requests/api/graphql/mutations/work_items/create_spec.rb
index a31f6a8c50d6be9a4d8a6b8bda75bd8cae76f329..f5787e726c10a7f50546afb82e5eb9df4026f279 100644
--- a/ee/spec/requests/api/graphql/mutations/work_items/create_spec.rb
+++ b/ee/spec/requests/api/graphql/mutations/work_items/create_spec.rb
@@ -12,71 +12,17 @@
   let(:mutation_response) { graphql_mutation_response(:work_item_create) }
   let(:widgets_response) { mutation_response['workItem']['widgets'] }
 
-  RSpec.shared_examples 'creates work item' do
-    context 'when setting iteration on work item creation' do
-      let_it_be(:cadence) { create(:iterations_cadence, group: group) }
-      let_it_be(:iteration) { create(:iteration, iterations_cadence: cadence) }
-
-      let(:input) do
-        {
-          title: 'new title',
-          workItemTypeId: WorkItems::Type.default_by_type(:task).to_global_id.to_s,
-          iterationWidget: { 'iterationId' => iteration.to_global_id.to_s }
-        }
-      end
-
-      before do
-        stub_licensed_features(iterations: true)
-      end
-
-      it "sets the work item's iteration", :aggregate_failures do
-        expect do
-          post_graphql_mutation(mutation, current_user: current_user)
-        end.to change { WorkItem.count }.by(1)
-
-        expect(response).to have_gitlab_http_status(:success)
-        expect(widgets_response).to include(
-          {
-            'type' => 'ITERATION',
-            'iteration' => { 'id' => iteration.to_global_id.to_s }
-          }
-        )
-      end
-
-      context 'when iterations feature is unavailable' do
-        before do
-          stub_licensed_features(iterations: false)
-        end
-
-        # TODO: https://gitlab.com/gitlab-org/gitlab/-/issues/383322
-        # We prefer to return an error rather than nil when authorization for an object fails.
-        # Here the authorization fails due to the unavailability of the licensed feature.
-        # Because the object to be authorized gets loaded via argument inside an InputObject,
-        # we need to add an additional hook to Types::BaseInputObject so errors are raised.
-        it 'returns nil' do
-          expect do
-            post_graphql_mutation(mutation, current_user: current_user)
-          end.to change { WorkItem.count }.by(0)
-
-          expect(mutation_response).to be_nil
-        end
-      end
-    end
-
-    context 'when creating a key result' do
-      let_it_be(:parent) { create(:work_item, :objective, **container_params) }
+  context 'when user has permissions to create a work item' do
+    let(:current_user) { developer }
 
+    shared_examples 'creates work item with iteration widget' do
       let(:fields) do
         <<~FIELDS
           workItem {
-            id
-            workItemType {
-              id
-            }
             widgets {
               type
-              ... on WorkItemWidgetHierarchy {
-                parent {
+              ... on WorkItemWidgetIteration {
+                iteration {
                   id
                 }
               }
@@ -86,22 +32,23 @@
         FIELDS
       end
 
-      let(:input) do
-        {
-          title: 'key result',
-          workItemTypeId: WorkItems::Type.default_by_type(:key_result).to_global_id.to_s,
-          hierarchyWidget: { 'parentId' => parent.to_global_id.to_s }
-        }
-      end
+      context 'when setting iteration on work item creation' do
+        let_it_be(:cadence) { create(:iterations_cadence, group: group) }
+        let_it_be(:iteration) { create(:iteration, iterations_cadence: cadence) }
 
-      let(:widgets_response) { mutation_response['workItem']['widgets'] }
+        let(:input) do
+          {
+            title: 'new title',
+            workItemTypeId: WorkItems::Type.default_by_type(:task).to_global_id.to_s,
+            iterationWidget: { 'iterationId' => iteration.to_global_id.to_s }
+          }
+        end
 
-      context 'when okrs are available' do
         before do
-          stub_licensed_features(okrs: true)
+          stub_licensed_features(iterations: true)
         end
 
-        it 'creates the work item' do
+        it "sets the work item's iteration", :aggregate_failures do
           expect do
             post_graphql_mutation(mutation, current_user: current_user)
           end.to change { WorkItem.count }.by(1)
@@ -109,206 +56,380 @@
           expect(response).to have_gitlab_http_status(:success)
           expect(widgets_response).to include(
             {
-              'parent' => { 'id' => parent.to_global_id.to_s },
-              'type' => 'HIERARCHY'
+              'type' => 'ITERATION',
+              'iteration' => { 'id' => iteration.to_global_id.to_s }
             }
           )
         end
+
+        context 'when iterations feature is unavailable' do
+          before do
+            stub_licensed_features(iterations: false)
+          end
+
+          # TODO: https://gitlab.com/gitlab-org/gitlab/-/issues/383322
+          # We prefer to return an error rather than nil when authorization for an object fails.
+          # Here the authorization fails due to the unavailability of the licensed feature.
+          # Because the object to be authorized gets loaded via argument inside an InputObject,
+          # we need to add an additional hook to Types::BaseInputObject so errors are raised.
+          it 'returns nil' do
+            expect do
+              post_graphql_mutation(mutation, current_user: current_user)
+            end.to change { WorkItem.count }.by(0)
+
+            expect(mutation_response).to be_nil
+          end
+        end
+      end
+
+      context 'when creating a key result' do
+        let_it_be(:parent) { create(:work_item, :objective, **container_params) }
+
+        let(:fields) do
+          <<~FIELDS
+            workItem {
+              id
+              workItemType {
+                id
+              }
+              widgets {
+                type
+                ... on WorkItemWidgetHierarchy {
+                  parent {
+                    id
+                  }
+                }
+              }
+            }
+            errors
+          FIELDS
+        end
+
+        let(:input) do
+          {
+            title: 'key result',
+            workItemTypeId: WorkItems::Type.default_by_type(:key_result).to_global_id.to_s,
+            hierarchyWidget: { 'parentId' => parent.to_global_id.to_s }
+          }
+        end
+
+        let(:widgets_response) { mutation_response['workItem']['widgets'] }
+
+        context 'when okrs are available' do
+          before do
+            stub_licensed_features(okrs: true)
+          end
+
+          it 'creates the work item' do
+            expect do
+              post_graphql_mutation(mutation, current_user: current_user)
+            end.to change { WorkItem.count }.by(1)
+
+            expect(response).to have_gitlab_http_status(:success)
+            expect(widgets_response).to include(
+              {
+                'parent' => { 'id' => parent.to_global_id.to_s },
+                'type' => 'HIERARCHY'
+              }
+            )
+          end
+        end
+
+        context 'when okrs are not available' do
+          before do
+            stub_licensed_features(okrs: false)
+          end
+
+          it 'returns error' do
+            expect do
+              post_graphql_mutation(mutation, current_user: current_user)
+            end.to not_change(WorkItem, :count)
+
+            expect(mutation_response['errors'])
+              .to contain_exactly(/cannot be added: is not allowed to add this type of parent/)
+            expect(mutation_response['workItem']).to be_nil
+          end
+        end
       end
 
-      context 'when okrs are not available' do
+      context 'when group_webhooks feature is available', :aggregate_failures do
+        let(:input) do
+          {
+            title: 'new title',
+            workItemTypeId: WorkItems::Type.default_by_type(:task).to_global_id.to_s
+          }
+        end
+
         before do
-          stub_licensed_features(okrs: false)
+          stub_licensed_features(group_webhooks: true)
+          create(:group_hook, issues_events: true, group: group)
         end
 
-        it 'returns error' do
+        it 'creates a work item' do
           expect do
             post_graphql_mutation(mutation, current_user: current_user)
-          end.to not_change(WorkItem, :count)
+          end.to change { WorkItem.count }.by(1)
 
-          expect(mutation_response['errors'])
-            .to contain_exactly(/cannot be added: is not allowed to add this type of parent/)
-          expect(mutation_response['workItem']).to be_nil
+          expect(response).to have_gitlab_http_status(:success)
         end
       end
     end
 
-    context 'when group_webhooks feature is available', :aggregate_failures do
-      let(:input) do
-        {
-          title: 'new title',
-          workItemTypeId: WorkItems::Type.default_by_type(:task).to_global_id.to_s
-        }
-      end
+    context 'when creating work items in a project' do
+      context 'with projectPath' do
+        let_it_be(:container_params) { { project: project } }
+        let(:mutation) { graphql_mutation(:workItemCreate, input.merge(projectPath: project.full_path), fields) }
+        let(:work_item_type) { :task }
 
-      before do
-        stub_licensed_features(group_webhooks: true)
-        create(:group_hook, issues_events: true, group: group)
+        it_behaves_like 'creates work item with iteration widget'
       end
 
-      it 'creates a work item' do
-        expect do
-          post_graphql_mutation(mutation, current_user: current_user)
-        end.to change { WorkItem.count }.by(1)
+      context 'with namespacePath' do
+        let_it_be(:container_params) { { project: project } }
+        let(:mutation) { graphql_mutation(:workItemCreate, input.merge(namespacePath: project.full_path), fields) }
+        let(:work_item_type) { :task }
 
-        expect(response).to have_gitlab_http_status(:success)
+        it_behaves_like 'creates work item with iteration widget'
       end
     end
-  end
 
-  context 'when user has permissions to create a work item' do
-    let(:current_user) { developer }
+    context 'when creating work items in a group' do
+      let_it_be(:container_params) { { namespace: group } }
+      let(:mutation) { graphql_mutation(:workItemCreate, input.merge(namespacePath: group.full_path), fields) }
+      let(:work_item_type) { :epic }
 
-    context 'with iteration widget input' do
-      let(:fields) do
-        <<~FIELDS
+      it_behaves_like 'creates work item with iteration widget'
+
+      context 'with rolledup dates widget input' do
+        before do
+          stub_licensed_features(epics: true)
+        end
+
+        let(:fields) do
+          <<~FIELDS
           workItem {
             widgets {
               type
-              ... on WorkItemWidgetIteration {
-                iteration {
-                  id
+                ... on WorkItemWidgetRolledupDates {
+                  startDate
+                  startDateFixed
+                  startDateIsFixed
+                  startDateSourcingWorkItem {
+                    id
+                  }
+                  startDateSourcingMilestone {
+                    id
+                  }
+                  dueDate
+                  dueDateFixed
+                  dueDateIsFixed
+                  startDateSourcingWorkItem {
+                    id
+                  }
+                  dueDateSourcingMilestone {
+                    id
+                  }
                 }
-              }
             }
           }
           errors
-        FIELDS
-      end
+          FIELDS
+        end
 
-      context 'when creating work items in a project' do
-        context 'with projectPath' do
-          let_it_be(:container_params) { { project: project } }
-          let(:mutation) { graphql_mutation(:workItemCreate, input.merge(projectPath: project.full_path), fields) }
+        context "when the work_items_rolledup_dates feature flag is disabled" do
+          before do
+            stub_feature_flags(work_items_rolledup_dates: false)
+          end
 
-          it_behaves_like 'creates work item'
-        end
+          let(:start_date) { 5.days.ago.to_date }
+          let(:due_date) { 5.days.from_now.to_date }
 
-        context 'with namespacePath' do
-          let_it_be(:container_params) { { project: project } }
-          let(:mutation) { graphql_mutation(:workItemCreate, input.merge(namespacePath: project.full_path), fields) }
+          let(:input) do
+            {
+              title: "some WI",
+              workItemTypeId: WorkItems::Type.default_by_type(:epic).to_gid.to_s,
+              rolledupDatesWidget: {
+                startDateFixed: start_date.to_s,
+                dueDateFixed: due_date.to_s
+              }
+            }
+          end
 
-          it_behaves_like 'creates work item'
+          it "does not set the work item's start and due date" do
+            expect { post_graphql_mutation(mutation, current_user: current_user) }
+              .to change { WorkItem.count }.by(1)
+
+            expect(response).to have_gitlab_http_status(:success)
+            expect(widgets_response).to include(
+              "type" => "ROLLEDUP_DATES",
+              "dueDate" => nil,
+              "dueDateFixed" => nil,
+              "dueDateIsFixed" => nil,
+              "dueDateSourcingMilestone" => nil,
+              "startDate" => nil,
+              "startDateFixed" => nil,
+              "startDateIsFixed" => nil,
+              "startDateSourcingMilestone" => nil,
+              "startDateSourcingWorkItem" => nil
+            )
+          end
         end
-      end
 
-      context 'when creating work items in a group' do
-        let_it_be(:container_params) { { namespace: group } }
-        let(:mutation) { graphql_mutation(:workItemCreate, input.merge(namespacePath: group.full_path), fields) }
+        context "with fixed dates" do
+          let(:start_date) { 5.days.ago.to_date }
+          let(:due_date) { 5.days.from_now.to_date }
 
-        it_behaves_like 'creates work item'
+          let(:input) do
+            {
+              title: "some WI",
+              workItemTypeId: WorkItems::Type.default_by_type(:epic).to_gid.to_s,
+              rolledupDatesWidget: {
+                startDateIsFixed: true,
+                startDateFixed: start_date.to_s,
+                dueDateIsFixed: true,
+                dueDateFixed: due_date.to_s
+              }
+            }
+          end
 
-        context "with rolledup dates widget input" do
-          before do
-            stub_licensed_features(epics: true)
+          it "sets the work item's start and due date" do
+            expect { post_graphql_mutation(mutation, current_user: current_user) }
+              .to change { WorkItem.count }
+              .by(1)
+
+            expect(response).to have_gitlab_http_status(:success)
+            expect(widgets_response).to include(
+              "type" => "ROLLEDUP_DATES",
+              "dueDate" => due_date.to_s,
+              "dueDateFixed" => due_date.to_s,
+              "dueDateIsFixed" => true,
+              "dueDateSourcingMilestone" => nil,
+              "startDate" => start_date.to_s,
+              "startDateFixed" => start_date.to_s,
+              "startDateIsFixed" => true,
+              "startDateSourcingMilestone" => nil,
+              "startDateSourcingWorkItem" => nil
+            )
           end
+        end
+      end
+
+      context 'with health status widget input' do
+        let(:new_status) { 'onTrack' }
+        let(:input) do
+          {
+            title: "some WI",
+            workItemTypeId: WorkItems::Type.default_by_type(:epic).to_gid.to_s,
+            healthStatusWidget: { healthStatus: new_status }
+          }
+        end
 
-          let(:fields) do
-            <<~FIELDS
+        let(:fields) do
+          <<~FIELDS
             workItem {
               widgets {
                 type
-                  ... on WorkItemWidgetRolledupDates {
-                    startDate
-                    startDateFixed
-                    startDateIsFixed
-                    startDateSourcingWorkItem {
-                      id
-                    }
-                    startDateSourcingMilestone {
-                      id
-                    }
-                    dueDate
-                    dueDateFixed
-                    dueDateIsFixed
-                    startDateSourcingWorkItem {
-                      id
-                    }
-                    dueDateSourcingMilestone {
-                      id
-                    }
-                  }
+                ... on WorkItemWidgetHealthStatus {
+                  healthStatus
+                }
               }
             }
             errors
-            FIELDS
-          end
+          FIELDS
+        end
 
-          context "when the work_items_rolledup_dates feature flag is disabled" do
-            before do
-              stub_feature_flags(work_items_rolledup_dates: false)
-            end
+        context 'when issuable_health_status is licensed' do
+          before do
+            stub_licensed_features(epics: true, issuable_health_status: true)
+          end
 
-            let(:start_date) { 5.days.ago.to_date }
-            let(:due_date) { 5.days.from_now.to_date }
+          it 'sets value for the health status widget' do
+            expect { post_graphql_mutation(mutation, current_user: current_user) }.to change { WorkItem.count }.by(1)
 
-            let(:input) do
+            expect(response).to have_gitlab_http_status(:success)
+            expect(mutation_response['workItem']['widgets']).to include(
               {
-                title: "some WI",
-                workItemTypeId: WorkItems::Type.default_by_type(:epic).to_gid.to_s,
-                rolledupDatesWidget: {
-                  startDateFixed: start_date.to_s,
-                  dueDateFixed: due_date.to_s
+                'healthStatus' => 'onTrack',
+                'type' => 'HEALTH_STATUS'
+              }
+            )
+          end
+        end
+
+        context 'when issuable_health_status is unlicensed' do
+          before do
+            stub_licensed_features(epics: true, issuable_health_status: false)
+          end
+
+          it 'returns an error' do
+            expect do
+              post_graphql_mutation(mutation, current_user: current_user)
+            end.to change { WorkItem.count }.by(0)
+
+            expect(mutation_response).to be_nil
+            expect(graphql_errors).to include(a_hash_including(
+              'message' => "Following widget keys are not supported by Epic type: [:health_status_widget]"
+            ))
+          end
+        end
+      end
+
+      context 'with color widget input' do
+        let(:new_color) { '#346465' }
+        let(:input) do
+          {
+            title: "some WI",
+            workItemTypeId: WorkItems::Type.default_by_type(:epic).to_gid.to_s,
+            colorWidget: { color: new_color }
+          }
+        end
+
+        let(:fields) do
+          <<~FIELDS
+            workItem {
+              widgets {
+                type
+                ... on WorkItemWidgetColor {
+                  color
                 }
               }
-            end
-
-            it "does not set the work item's start and due date" do
-              expect { post_graphql_mutation(mutation, current_user: current_user) }
-                .to change { WorkItem.count }.by(1)
-
-              expect(response).to have_gitlab_http_status(:success)
-              expect(widgets_response).to include(
-                "type" => "ROLLEDUP_DATES",
-                "dueDate" => nil,
-                "dueDateFixed" => nil,
-                "dueDateIsFixed" => nil,
-                "dueDateSourcingMilestone" => nil,
-                "startDate" => nil,
-                "startDateFixed" => nil,
-                "startDateIsFixed" => nil,
-                "startDateSourcingMilestone" => nil,
-                "startDateSourcingWorkItem" => nil
-              )
-            end
+            }
+            errors
+          FIELDS
+        end
+
+        context 'when epic_colors is licensed' do
+          before do
+            stub_licensed_features(epics: true, epic_colors: true)
           end
 
-          context "with fixed dates" do
-            let(:start_date) { 5.days.ago.to_date }
-            let(:due_date) { 5.days.from_now.to_date }
+          it 'sets value for color widget' do
+            expect { post_graphql_mutation(mutation, current_user: current_user) }.to change { WorkItem.count }.by(1)
 
-            let(:input) do
+            expect(response).to have_gitlab_http_status(:success)
+            expect(mutation_response['workItem']['widgets']).to include(
               {
-                title: "some WI",
-                workItemTypeId: WorkItems::Type.default_by_type(:epic).to_gid.to_s,
-                rolledupDatesWidget: {
-                  startDateIsFixed: true,
-                  startDateFixed: start_date.to_s,
-                  dueDateIsFixed: true,
-                  dueDateFixed: due_date.to_s
-                }
+                'color' => new_color,
+                'type' => 'COLOR'
               }
-            end
-
-            it "sets the work item's start and due date" do
-              expect { post_graphql_mutation(mutation, current_user: current_user) }
-                .to change { WorkItem.count }
-                .by(1)
-
-              expect(response).to have_gitlab_http_status(:success)
-              expect(widgets_response).to include(
-                "type" => "ROLLEDUP_DATES",
-                "dueDate" => due_date.to_s,
-                "dueDateFixed" => due_date.to_s,
-                "dueDateIsFixed" => true,
-                "dueDateSourcingMilestone" => nil,
-                "startDate" => start_date.to_s,
-                "startDateFixed" => start_date.to_s,
-                "startDateIsFixed" => true,
-                "startDateSourcingMilestone" => nil,
-                "startDateSourcingWorkItem" => nil
-              )
-            end
+            )
+          end
+        end
+
+        context 'when epic_colors is unlicensed' do
+          before do
+            stub_licensed_features(epics: true, epic_colors: false)
+          end
+
+          it 'returns an error' do
+            expect do
+              post_graphql_mutation(mutation, current_user: current_user)
+            end.to change { WorkItem.count }.by(0)
+
+            expect(mutation_response).to be_nil
+            expect(graphql_errors).to include(a_hash_including(
+              'message' => "Following widget keys are not supported by Epic type: [:color_widget]"
+            ))
           end
         end
       end
diff --git a/ee/spec/services/work_items/callbacks/health_status_spec.rb b/ee/spec/services/work_items/callbacks/health_status_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..eb20479d2f17fa92c5363fe4ba80fd46b8a0ce2b
--- /dev/null
+++ b/ee/spec/services/work_items/callbacks/health_status_spec.rb
@@ -0,0 +1,83 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe WorkItems::Callbacks::HealthStatus, feature_category: :team_planning do
+  let_it_be(:user) { create(:user) }
+  let_it_be(:reporter) { create(:user) }
+  let_it_be(:group) { create(:group, reporters: reporter) }
+  let_it_be_with_reload(:work_item) do
+    create(:work_item, :epic, namespace: group, author: user, health_status: :on_track)
+  end
+
+  let(:current_user) { reporter }
+  let(:params) { {} }
+  let(:callback) { described_class.new(issuable: work_item, current_user: current_user, params: params) }
+
+  describe '#after_initialize' do
+    subject(:after_initialize_callback) { callback.after_initialize }
+
+    shared_examples 'work item and health status is unchanged' do
+      it 'does not change work item health status value' do
+        expect { after_initialize_callback }
+          .to not_change { work_item.health_status }
+                .and not_change { work_item.updated_at }
+      end
+    end
+
+    context 'when issuable_health_status feature is licensed' do
+      before do
+        stub_licensed_features(issuable_health_status: true)
+      end
+
+      context 'when health_status param is present' do
+        context 'when health_status param is valid' do
+          let(:params) { { health_status: :needs_attention } }
+
+          it 'updates work item health status value' do
+            expect { after_initialize_callback }.to change { work_item.health_status }.to('needs_attention')
+          end
+        end
+
+        context 'when widget does not exist in new type' do
+          let(:params) { {} }
+
+          before do
+            allow(callback).to receive(:excluded_in_new_type?).and_return(true)
+          end
+
+          it "sets the work item's health status as nil" do
+            expect { callback.after_initialize }.to change { work_item.health_status }.from('on_track').to(nil)
+          end
+        end
+      end
+
+      context 'when health_status param is not present' do
+        let(:params) { {} }
+
+        it_behaves_like 'work item and health status is unchanged'
+      end
+
+      context 'when param value is the same as the work item health status' do
+        let(:params) { { health_status: :on_track } }
+
+        it_behaves_like 'work item and health status is unchanged'
+      end
+
+      context 'when user cannot admin_work_item' do
+        let(:current_user) { user }
+        let(:params) { { health_status: :needs_attention } }
+
+        it_behaves_like 'work item and health status is unchanged'
+      end
+    end
+
+    context 'when issuable_health_status feature is unlicensed' do
+      before do
+        stub_licensed_features(issuable_health_status: false)
+      end
+
+      it_behaves_like 'work item and health status is unchanged'
+    end
+  end
+end
diff --git a/ee/spec/services/work_items/widgets/health_status_service/update_service_spec.rb b/ee/spec/services/work_items/widgets/health_status_service/update_service_spec.rb
deleted file mode 100644
index 5bb929c7d299085effb0fab482f7e089ca22d2fd..0000000000000000000000000000000000000000
--- a/ee/spec/services/work_items/widgets/health_status_service/update_service_spec.rb
+++ /dev/null
@@ -1,71 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe WorkItems::Widgets::HealthStatusService::UpdateService, feature_category: :team_planning do
-  let_it_be(:user) { create(:user) }
-  let_it_be(:project) { create(:project) }
-  let_it_be_with_reload(:work_item) { create(:work_item, project: project, author: user, health_status: nil) }
-
-  let(:widget) { work_item.widgets.find { |widget| widget.is_a?(WorkItems::Widgets::HealthStatus) } }
-  let(:new_health_status) { :on_track }
-  let(:params) { { health_status: new_health_status } }
-
-  describe '#update' do
-    let(:service) { described_class.new(widget: widget, current_user: user) }
-
-    subject(:update_health_status) { service.before_update_callback(params: params) }
-
-    before do
-      stub_licensed_features(issuable_health_status: true)
-    end
-
-    shared_examples 'health_status is unchanged' do
-      it 'does not change the health_status of the work item' do
-        expect { update_health_status }
-          .to not_change { work_item.health_status }
-      end
-    end
-
-    context 'when it has issuable_health_status license' do
-      context 'when health_status param is not present' do
-        let(:params) { {} }
-
-        it_behaves_like 'health_status is unchanged'
-      end
-
-      context 'when user can not admin work item' do
-        before do
-          project.add_guest(user)
-        end
-
-        it_behaves_like 'health_status is unchanged'
-      end
-
-      context 'when user can admin the work item' do
-        before do
-          project.add_reporter(user)
-        end
-
-        it 'sets the health_status for the work item and triggers subscription' do
-          update_health_status
-
-          expect(work_item.health_status).to eq('on_track')
-        end
-
-        context 'when widget does not exist in new type' do
-          let(:params) { {} }
-
-          before do
-            allow(service).to receive(:new_type_excludes_widget?).and_return(true)
-            work_item.health_status = 'on_track'
-          end
-
-          it "resets the work item's health status" do
-            expect { subject }.to change { work_item.health_status }.from('on_track').to(nil)
-          end
-        end
-      end
-    end
-  end
-end