From aadca97beb43dc8b4499df58f41f6a2a487d0920 Mon Sep 17 00:00:00 2001
From: Miguel Rincon <mrincon@gitlab.com>
Date: Wed, 12 Mar 2025 09:33:15 +0100
Subject: [PATCH] Update page headings in the CI/CD analytics pages

As a small improvement to the Project CI/CD analytics page,
update the page heading sizes and title positions to accurately
reflect the user's position in the GitLab UI.

Changelog: changed
---
 .../pipelines/charts/components/app.vue       |  4 +--
 .../charts/components/dashboard_header.vue    | 13 +++++++
 .../charts/components/pipeline_charts.vue     |  8 +++--
 .../charts/components/pipeline_charts_new.vue |  6 +++-
 app/views/projects/pipelines/charts.html.haml |  2 ++
 .../dora/components/dora_chart_header.vue     | 35 ++++++++++++-------
 .../components/dora_charts_header_spec.js     |  2 +-
 locale/gitlab.pot                             |  3 --
 .../components/dashboard_header_spec.js       | 30 ++++++++++++++++
 9 files changed, 80 insertions(+), 23 deletions(-)
 create mode 100644 app/assets/javascripts/projects/pipelines/charts/components/dashboard_header.vue
 create mode 100644 spec/frontend/projects/pipelines/charts/components/dashboard_header_spec.js

diff --git a/app/assets/javascripts/projects/pipelines/charts/components/app.vue b/app/assets/javascripts/projects/pipelines/charts/components/app.vue
index b48c6d7103064..3233eecc2f98b 100644
--- a/app/assets/javascripts/projects/pipelines/charts/components/app.vue
+++ b/app/assets/javascripts/projects/pipelines/charts/components/app.vue
@@ -21,7 +21,7 @@ export default {
       import('ee_component/dora/components/change_failure_rate_charts.vue'),
     ProjectQualitySummary: () => import('ee_component/project_quality_summary/app.vue'),
   },
-  piplelinesTabEvent: 'p_analytics_ci_cd_pipelines',
+  pipelinesTabEvent: 'p_analytics_ci_cd_pipelines',
   deploymentFrequencyTabEvent: 'p_analytics_ci_cd_deployment_frequency',
   leadTimeTabEvent: 'p_analytics_ci_cd_lead_time',
   timeToRestoreServiceTabEvent: 'visit_ci_cd_time_to_restore_service_tab',
@@ -94,7 +94,7 @@ export default {
       <gl-tab
         :title="__('Pipelines')"
         data-testid="pipelines-tab"
-        @click="trackEvent($options.piplelinesTabEvent)"
+        @click="trackEvent($options.pipelinesTabEvent)"
       >
         <component :is="pipelineChartsComponent" />
       </gl-tab>
diff --git a/app/assets/javascripts/projects/pipelines/charts/components/dashboard_header.vue b/app/assets/javascripts/projects/pipelines/charts/components/dashboard_header.vue
new file mode 100644
index 0000000000000..3a0b1498d73b4
--- /dev/null
+++ b/app/assets/javascripts/projects/pipelines/charts/components/dashboard_header.vue
@@ -0,0 +1,13 @@
+<script>
+export default {
+  name: 'DashboardHeader',
+};
+</script>
+<template>
+  <div>
+    <h2 class="gl-heading-2 gl-mt-3">
+      <slot></slot>
+    </h2>
+    <slot name="description"></slot>
+  </div>
+</template>
diff --git a/app/assets/javascripts/projects/pipelines/charts/components/pipeline_charts.vue b/app/assets/javascripts/projects/pipelines/charts/components/pipeline_charts.vue
index 629d3835f72ff..c6d3bc33b6dce 100644
--- a/app/assets/javascripts/projects/pipelines/charts/components/pipeline_charts.vue
+++ b/app/assets/javascripts/projects/pipelines/charts/components/pipeline_charts.vue
@@ -20,6 +20,7 @@ import {
 } from '../constants';
 import getPipelineCountByStatus from '../graphql/queries/get_pipeline_count_by_status.query.graphql';
 import getProjectPipelineStatistics from '../graphql/queries/get_project_pipeline_statistics.query.graphql';
+import DashboardHeader from './dashboard_header.vue';
 import StatisticsList from './statistics_list.vue';
 
 const defaultAnalyticsValues = {
@@ -51,6 +52,7 @@ export default {
     GlColumnChart,
     GlChartSeriesLabel,
     GlSkeletonLoader,
+    DashboardHeader,
     StatisticsList,
     CiCdAnalyticsCharts,
   },
@@ -316,9 +318,9 @@ export default {
     <gl-alert v-if="showFailureAlert" :variant="failure.variant" @dismiss="hideAlert">{{
       failure.text
     }}</gl-alert>
-    <div class="gl-mb-3">
-      <h4>{{ s__('PipelineCharts|CI/CD Analytics') }}</h4>
-    </div>
+    <dashboard-header>
+      {{ s__('PipelineCharts|Pipelines') }}
+    </dashboard-header>
     <gl-skeleton-loader v-if="loading" :lines="5" />
     <statistics-list v-else :counts="formattedCounts" />
     <h4>{{ __('Pipelines charts') }}</h4>
diff --git a/app/assets/javascripts/projects/pipelines/charts/components/pipeline_charts_new.vue b/app/assets/javascripts/projects/pipelines/charts/components/pipeline_charts_new.vue
index 0730b3225b8ab..50a39d691f1b7 100644
--- a/app/assets/javascripts/projects/pipelines/charts/components/pipeline_charts_new.vue
+++ b/app/assets/javascripts/projects/pipelines/charts/components/pipeline_charts_new.vue
@@ -10,6 +10,7 @@ import {
   DATE_RANGE_LAST_180_DAYS,
 } from '../constants';
 import getPipelineAnalytics from '../graphql/queries/get_pipeline_analytics.query.graphql';
+import DashboardHeader from './dashboard_header.vue';
 import StatisticsList from './statistics_list.vue';
 import PipelineDurationChart from './pipeline_duration_chart.vue';
 import PipelineStatusChart from './pipeline_status_chart.vue';
@@ -18,6 +19,7 @@ export default {
   components: {
     GlCollapsibleListbox,
     GlFormGroup,
+    DashboardHeader,
     StatisticsList,
     PipelineDurationChart,
     PipelineStatusChart,
@@ -103,7 +105,9 @@ export default {
 </script>
 <template>
   <div>
-    <h2>{{ s__('PipelineCharts|Pipelines') }}</h2>
+    <dashboard-header>
+      {{ s__('PipelineCharts|Pipelines') }}
+    </dashboard-header>
     <div class="gl-mb-4 gl-bg-subtle gl-p-4 gl-pb-2">
       <gl-form-group :label="__('Date range')" label-for="date-range">
         <gl-collapsible-listbox
diff --git a/app/views/projects/pipelines/charts.html.haml b/app/views/projects/pipelines/charts.html.haml
index 706886d7b3166..87dd209ee529f 100644
--- a/app/views/projects/pipelines/charts.html.haml
+++ b/app/views/projects/pipelines/charts.html.haml
@@ -1,5 +1,7 @@
 - page_title _('CI/CD Analytics')
 
+= render ::Layouts::PageHeadingComponent.new(_("CI/CD Analytics"))
+
 #js-project-pipelines-charts-app{ data: { project_path: @project.full_path,
   project_id: @project.id,
   should_render_dora_charts: should_render_dora_charts.to_s,
diff --git a/ee/app/assets/javascripts/dora/components/dora_chart_header.vue b/ee/app/assets/javascripts/dora/components/dora_chart_header.vue
index baed9b0854b29..41dcdc937a1ce 100644
--- a/ee/app/assets/javascripts/dora/components/dora_chart_header.vue
+++ b/ee/app/assets/javascripts/dora/components/dora_chart_header.vue
@@ -1,5 +1,7 @@
 <script>
 import { GlLink, GlSprintf } from '@gitlab/ui';
+
+import DashboardHeader from '~/projects/pipelines/charts/components/dashboard_header.vue';
 import { environmentTierDocumentationHref } from './static_data/shared';
 
 export default {
@@ -7,6 +9,7 @@ export default {
   components: {
     GlLink,
     GlSprintf,
+    DashboardHeader,
   },
   props: {
     headerText: {
@@ -27,18 +30,24 @@ export default {
 </script>
 <template>
   <div>
-    <h4 class="gl-my-4">{{ headerText }}</h4>
-    <p data-testid="help-text">
-      <gl-sprintf :message="chartDescriptionText">
-        <template #link="{ content }">
-          <gl-link :href="$options.environmentTierDocumentationHref" target="_blank"
-            ><code>{{ content }}</code></gl-link
-          >
-        </template>
-      </gl-sprintf>
-      <gl-link :href="chartDocumentationHref" target="_blank">
-        {{ __('Learn more.') }}
-      </gl-link>
-    </p>
+    <dashboard-header>
+      <template #default>
+        {{ headerText }}
+      </template>
+      <template #description>
+        <p data-testid="help-text">
+          <gl-sprintf :message="chartDescriptionText">
+            <template #link="{ content }">
+              <gl-link :href="$options.environmentTierDocumentationHref" target="_blank"
+                ><code>{{ content }}</code></gl-link
+              >
+            </template>
+          </gl-sprintf>
+          <gl-link :href="chartDocumentationHref" target="_blank">
+            {{ __('Learn more.') }}
+          </gl-link>
+        </p>
+      </template>
+    </dashboard-header>
   </div>
 </template>
diff --git a/ee/spec/frontend/dora/components/dora_charts_header_spec.js b/ee/spec/frontend/dora/components/dora_charts_header_spec.js
index d13cc158a0b30..732055513e427 100644
--- a/ee/spec/frontend/dora/components/dora_charts_header_spec.js
+++ b/ee/spec/frontend/dora/components/dora_charts_header_spec.js
@@ -24,7 +24,7 @@ describe('dora_chart_header.vue', () => {
   });
 
   it('renders the header text', () => {
-    const actualText = wrapper.find('h4').text();
+    const actualText = wrapper.find('h2').text();
 
     expect(actualText).toBe(mockHeaderText);
   });
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index d21a1fafa82aa..cb039bb3a0725 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -42588,9 +42588,6 @@ msgstr ""
 msgid "PipelineCharts|An unknown error occurred while processing CI/CD analytics."
 msgstr ""
 
-msgid "PipelineCharts|CI/CD Analytics"
-msgstr ""
-
 msgid "PipelineCharts|Failure rate"
 msgstr ""
 
diff --git a/spec/frontend/projects/pipelines/charts/components/dashboard_header_spec.js b/spec/frontend/projects/pipelines/charts/components/dashboard_header_spec.js
new file mode 100644
index 0000000000000..1ab74a5f32bfc
--- /dev/null
+++ b/spec/frontend/projects/pipelines/charts/components/dashboard_header_spec.js
@@ -0,0 +1,30 @@
+import { shallowMount } from '@vue/test-utils';
+import DashboardHeader from '~/projects/pipelines/charts/components/dashboard_header.vue';
+
+describe('DashboardHeader', () => {
+  let wrapper;
+
+  const createComponent = ({ ...options }) => {
+    wrapper = shallowMount(DashboardHeader, { ...options });
+  };
+
+  it('shows heading', () => {
+    createComponent({
+      slots: {
+        default: 'My Heading',
+      },
+    });
+
+    expect(wrapper.find('h2').text()).toBe('My Heading');
+  });
+
+  it('shows description', () => {
+    createComponent({
+      slots: {
+        description: '<p>My Description</p>',
+      },
+    });
+
+    expect(wrapper.find('p').text()).toContain('My Description');
+  });
+});
-- 
GitLab