diff --git a/app/assets/javascripts/vue_shared/components/customizable_dashboard/dashboard_layout.stories.js b/app/assets/javascripts/vue_shared/components/customizable_dashboard/dashboard_layout.stories.js
index 3b34db6c21e05439070c77b05562fa77775c46b9..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644
--- a/app/assets/javascripts/vue_shared/components/customizable_dashboard/dashboard_layout.stories.js
+++ b/app/assets/javascripts/vue_shared/components/customizable_dashboard/dashboard_layout.stories.js
@@ -1,126 +0,0 @@
-import DashboardLayout from './dashboard_layout.vue';
-import ExtendedDashboardPanel from './extended_dashboard_panel.vue';
-
-export default {
- component: DashboardLayout,
- title: 'vue_shared/components/customizable_dashboard/dashboard_layout',
-};
-
-const dashboardConfig = {
- title: 'Dashboard title',
- description: 'Dashboards made easy with a snap grid system',
- panels: [
- {
- id: '1',
- title: 'Dashboard panel',
- gridAttributes: {
- width: 6,
- height: 1,
- yPos: 0,
- xPos: 3,
- },
- },
- {
- id: '2',
- title: 'Another dashboard panel',
- gridAttributes: {
- width: 3,
- height: 2,
- yPos: 1,
- xPos: 1,
- },
- },
- {
- id: '3',
- title: 'I can be placed anywhere on the grid',
- gridAttributes: {
- width: 4,
- height: 1,
- yPos: 2,
- xPos: 7,
- },
- },
- ],
-};
-
-const Template = (args, { argTypes }) => ({
- components: { DashboardLayout, ExtendedDashboardPanel },
- props: Object.keys(argTypes),
- template: `
-
-
-
-
- Your visualization here
-
-
-
-
- No dashboard panels here 🕵
-
-
- `,
-});
-
-const SlotsTemplate = (args, { argTypes }) => ({
- components: { DashboardLayout },
- props: Object.keys(argTypes),
- template: `
-
-
-
- Custom dashboard #title 🚀
-
-
-
-
- This is the #description slot.
-
-
-
-
- Add your dashboard-level filters in the #filters slot.
-
-
-
- #actions
-
-
-
- Dashboard alerts go in the #alert slot.
-
-
-
-
-
-
- This dashboard has no panels
-
-
-
- This is a custom #footer!
-
-
-
- `,
-});
-
-export const Default = Template.bind({});
-Default.args = {
- config: { ...dashboardConfig },
-};
-
-export const EmptyState = Template.bind({});
-EmptyState.args = {
- config: {
- ...dashboardConfig,
- panels: [],
- },
-};
-
-export const Slots = SlotsTemplate.bind({});
-Slots.args = {
- config: { ...dashboardConfig },
-};
diff --git a/app/assets/javascripts/vue_shared/components/customizable_dashboard/dashboard_layout.vue b/app/assets/javascripts/vue_shared/components/customizable_dashboard/dashboard_layout.vue
deleted file mode 100644
index 74ff4966448002bc9250e60c8e7e8a3984e80988..0000000000000000000000000000000000000000
--- a/app/assets/javascripts/vue_shared/components/customizable_dashboard/dashboard_layout.vue
+++ /dev/null
@@ -1,106 +0,0 @@
-
-
-
-
-
-
-
-
-
- {{ config.title }}
-
-
-
-
-
-
-
- {{ config.description }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/doc/development/fe_guide/dashboard_layout_framework.md b/doc/development/fe_guide/dashboard_layout_framework.md
index 494660aea059e87a0268f6d589d4846c99f212f0..cef3ef2f58358eb74cd448f3077406e69ec33250 100644
--- a/doc/development/fe_guide/dashboard_layout_framework.md
+++ b/doc/development/fe_guide/dashboard_layout_framework.md
@@ -29,8 +29,6 @@ To render dashboard layouts it's recommended to use the [GlDashboardLayout](http
component. It provides an easy way to render dashboards using
a configuration which aligns with our [Pajamas guidelines](https://design.gitlab.com/patterns/dashboards/).
-Note that GlDashboardLayout supplants the deprecated `dashboard_layout.vue` component in the vue shared directory.
-
### Panel guidelines
You are free to
@@ -51,7 +49,7 @@ and can keep existing visualizations. A typical migration path could look like t
1. Create a new dashboard using GlDashboardLayout and `extended_dashboard_panel.vue`.
1. Create a dashboard config object that mimics your old dashboard layout.
1. Optionally, use GlDashboardLayout's slots to render your dashboard's
-filters, actions, or custom title or description.
+ filters, actions, or custom title or description.
1. Ensure your new dashboard, panels, and visualizations render correctly.
1. Remove the feature flag and your old dashboard.
@@ -62,4 +60,6 @@ for an example on how to render existing visualization components using the dash
Real world implementations and migrations using the GlDashboardLayout component:
-- New security dashboard added in MR [!191974](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/191974)
+- New group security dashboard added in MR [!191974](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/191974)
+- New project security dashboard added in MR [!197626](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/197626)
+- New compliance center added in MR [!195759](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/195759)
diff --git a/ee/app/assets/javascripts/compliance_dashboard/components/dashboard/compliance_dashboard.vue b/ee/app/assets/javascripts/compliance_dashboard/components/dashboard/compliance_dashboard.vue
index b644ee929e9a8c77c4ba494f92da8996858404d7..90ca52659df64341bda72018dc7abf754667f9c6 100644
--- a/ee/app/assets/javascripts/compliance_dashboard/components/dashboard/compliance_dashboard.vue
+++ b/ee/app/assets/javascripts/compliance_dashboard/components/dashboard/compliance_dashboard.vue
@@ -1,10 +1,10 @@
-
+
@@ -243,5 +243,5 @@ export default {
-
+
diff --git a/ee/app/assets/javascripts/security_dashboard/components/shared/group_security_dashboard_new.vue b/ee/app/assets/javascripts/security_dashboard/components/shared/group_security_dashboard_new.vue
index 670dcc39bda10ac148d71e324393a3382a1988e5..6c1fe9acc326a6fbebca34ddc00aec8b946f56f9 100644
--- a/ee/app/assets/javascripts/security_dashboard/components/shared/group_security_dashboard_new.vue
+++ b/ee/app/assets/javascripts/security_dashboard/components/shared/group_security_dashboard_new.vue
@@ -1,7 +1,7 @@
-
+
-
+
diff --git a/ee/app/assets/javascripts/security_dashboard/components/shared/project_security_dashboard_new.vue b/ee/app/assets/javascripts/security_dashboard/components/shared/project_security_dashboard_new.vue
index 9a1feaad90fe47a8de957e7385392499f192394a..0de6b407c1d8bc1d5221106ef1ad0f74d7691319 100644
--- a/ee/app/assets/javascripts/security_dashboard/components/shared/project_security_dashboard_new.vue
+++ b/ee/app/assets/javascripts/security_dashboard/components/shared/project_security_dashboard_new.vue
@@ -1,12 +1,12 @@
-
+
-
+
diff --git a/ee/spec/frontend/compliance_dashboard/components/dashboard/compliance_dashboard_spec.js b/ee/spec/frontend/compliance_dashboard/components/dashboard/compliance_dashboard_spec.js
index 599ac6c27315ec28315f763b5710c4d44f4e8064..8c05bd4058a5594fbe005d18ce6537c9e2b70797 100644
--- a/ee/spec/frontend/compliance_dashboard/components/dashboard/compliance_dashboard_spec.js
+++ b/ee/spec/frontend/compliance_dashboard/components/dashboard/compliance_dashboard_spec.js
@@ -1,6 +1,7 @@
import { shallowMount } from '@vue/test-utils';
import Vue, { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
+import { GlDashboardLayout } from '@gitlab/ui';
import { createAlert } from '~/alert';
import { getSystemColorScheme } from '~/lib/utils/css_utils';
import waitForPromises from 'helpers/wait_for_promises';
@@ -10,7 +11,6 @@ import FrameworkCoverage from 'ee/compliance_dashboard/components/dashboard/fram
import FailedRequirements from 'ee/compliance_dashboard/components/dashboard/failed_requirements.vue';
import FailedControls from 'ee/compliance_dashboard/components/dashboard/failed_controls.vue';
import FrameworksNeedsAttention from 'ee/compliance_dashboard/components/dashboard/frameworks_needs_attention.vue';
-import DashboardLayout from '~/vue_shared/components/customizable_dashboard/dashboard_layout.vue';
import frameworkCoverageQuery from 'ee/compliance_dashboard/components/dashboard/graphql/framework_coverage.query.graphql';
import failedRequirementsQuery from 'ee/compliance_dashboard/components/dashboard/graphql/failed_requirements.query.graphql';
import failedControlsQuery from 'ee/compliance_dashboard/components/dashboard/graphql/failed_controls.query.graphql';
@@ -122,7 +122,7 @@ describe('Compliance dashboard', () => {
.fn()
.mockImplementation(() => new Promise(() => {}));
- const getDashboardConfig = () => wrapper.findComponent(DashboardLayout).props('config');
+ const getDashboardConfig = () => wrapper.findComponent(GlDashboardLayout).props('config');
function createComponent() {
const apolloProvider = createMockApollo([
diff --git a/ee/spec/frontend/security_dashboard/components/shared/group_security_dashboard_new_spec.js b/ee/spec/frontend/security_dashboard/components/shared/group_security_dashboard_new_spec.js
index a8ae3f4fc1526b5c01682614a21946868408e30c..ca6fecc349b418832ce8ef84abcb4dc2ed29ea1f 100644
--- a/ee/spec/frontend/security_dashboard/components/shared/group_security_dashboard_new_spec.js
+++ b/ee/spec/frontend/security_dashboard/components/shared/group_security_dashboard_new_spec.js
@@ -1,7 +1,7 @@
import { nextTick } from 'vue';
+import { GlDashboardLayout } from '@gitlab/ui';
import { markRaw } from '~/lib/utils/vue3compat/mark_raw';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import DashboardLayout from '~/vue_shared/components/customizable_dashboard/dashboard_layout.vue';
import { OPERATORS_OR } from '~/vue_shared/components/filtered_search_bar/constants';
import FilteredSearch from 'ee/security_dashboard/components/shared/security_dashboard_filtered_search/filtered_search.vue';
import GroupSecurityDashboardNew from 'ee/security_dashboard/components/shared/group_security_dashboard_new.vue';
@@ -26,7 +26,7 @@ describe('Group Security Dashboard (new version) - Component', () => {
});
};
- const findDashboardLayout = () => wrapper.findComponent(DashboardLayout);
+ const findDashboardLayout = () => wrapper.findComponent(GlDashboardLayout);
const findFilteredSearch = () => wrapper.findComponent(FilteredSearch);
const getDashboardConfig = () => findDashboardLayout().props('config');
const getFirstPanel = () => getDashboardConfig().panels[0];
diff --git a/ee/spec/frontend/security_dashboard/components/shared/project_security_dashboard_new_spec.js b/ee/spec/frontend/security_dashboard/components/shared/project_security_dashboard_new_spec.js
index 1280a0e3a0298c3b2c02151e91b3d21496f1ad17..503ded9c7ada079c4a3cc280fa4abd6e3d68e8c7 100644
--- a/ee/spec/frontend/security_dashboard/components/shared/project_security_dashboard_new_spec.js
+++ b/ee/spec/frontend/security_dashboard/components/shared/project_security_dashboard_new_spec.js
@@ -1,5 +1,5 @@
+import { GlDashboardLayout } from '@gitlab/ui';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import DashboardLayout from '~/vue_shared/components/customizable_dashboard/dashboard_layout.vue';
import ProjectSecurityDashboardNew from 'ee/security_dashboard/components/shared/project_security_dashboard_new.vue';
import ProjectVulnerabilitiesOverTimePanel from 'ee/security_dashboard/components/shared/project_vulnerabilities_over_time_panel.vue';
@@ -18,7 +18,7 @@ describe('Project Security Dashboard (new version) - Component', () => {
});
};
- const findDashboardLayout = () => wrapper.findComponent(DashboardLayout);
+ const findDashboardLayout = () => wrapper.findComponent(GlDashboardLayout);
const getDashboardConfig = () => findDashboardLayout().props('config');
const getFirstPanel = () => getDashboardConfig().panels[0];
diff --git a/ee/spec/frontend_integration/security_dashboard/__snapshots__/security_dashboard_init_integration_spec.js.snap b/ee/spec/frontend_integration/security_dashboard/__snapshots__/security_dashboard_init_integration_spec.js.snap
index 6a9fec09e5b88a533bd62a4db3d545dedb137656..d1f6937d6f48c5becd7811f61bdfc2067a765746 100644
--- a/ee/spec/frontend_integration/security_dashboard/__snapshots__/security_dashboard_init_integration_spec.js.snap
+++ b/ee/spec/frontend_integration/security_dashboard/__snapshots__/security_dashboard_init_integration_spec.js.snap
@@ -235,51 +235,52 @@ exports[`Security Dashboard default states sets up group-level with \`groupSecur
>
-
-
-
-
-
- Vulnerabilities over time
-
-
-
-
-
-
-
+
+
+ Vulnerabilities over time
+
+
+
+
+
@@ -521,51 +522,52 @@ exports[`Security Dashboard default states sets up project-level with \`projectS
>
-
-
-
-
-
- Vulnerabilities over time
-
-
-
-
-
-
-
+
+
+ Vulnerabilities over time
+
+
+
+
+
diff --git a/spec/frontend/vue_shared/components/customizable_dashboard/dashboard_layout_spec.js b/spec/frontend/vue_shared/components/customizable_dashboard/dashboard_layout_spec.js
deleted file mode 100644
index de5640210a16ec3015852c119cd2cafa253ddf41..0000000000000000000000000000000000000000
--- a/spec/frontend/vue_shared/components/customizable_dashboard/dashboard_layout_spec.js
+++ /dev/null
@@ -1,174 +0,0 @@
-import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import DashboardLayout from '~/vue_shared/components/customizable_dashboard/dashboard_layout.vue';
-import GridstackWrapper from '~/vue_shared/components/customizable_dashboard/gridstack_wrapper.vue';
-
-const dashboardConfig = {
- title: 'Dashboard title',
- description: 'This is my dashboard description',
- panels: [
- {
- id: '1',
- title: 'A dashboard panel',
- gridAttributes: {
- width: 6,
- height: 1,
- yPos: 0,
- xPos: 3,
- },
- },
- ],
-};
-
-describe('CustomizableDashboard', () => {
- /** @type {import('helpers/vue_test_utils_helper').ExtendedWrapper} */
- let wrapper;
-
- const findTitle = () => wrapper.findByTestId('title');
- const findDescription = () => wrapper.findByTestId('description');
- const findActionsContainer = () => wrapper.findByTestId('actions-container');
- const findFiltersContainer = () => wrapper.findByTestId('filters-container');
- const findGrid = () => wrapper.findComponent(GridstackWrapper);
-
- const panelSlotSpy = jest.fn();
- const emptyStateSlotSpy = jest.fn();
-
- const createWrapper = (props = {}, scopedSlots = {}) => {
- wrapper = shallowMountExtended(DashboardLayout, {
- propsData: {
- config: dashboardConfig,
- ...props,
- },
- scopedSlots: {
- panel: panelSlotSpy,
- 'empty-state': emptyStateSlotSpy,
- ...scopedSlots,
- },
- });
- };
-
- afterEach(() => {
- panelSlotSpy.mockRestore();
- emptyStateSlotSpy.mockRestore();
- });
-
- describe('default behaviour', () => {
- beforeEach(() => {
- createWrapper();
- });
-
- it('renders the dashboard title', () => {
- expect(findTitle().text()).toContain('Dashboard title');
- });
-
- it('renders the dashboard description', () => {
- expect(findDescription().text()).toContain('This is my dashboard description');
- });
-
- it('renders the dashboard grid with the config', () => {
- expect(findGrid().props('value')).toMatchObject(dashboardConfig);
- });
-
- it('renders the panel slot for each panel', () => {
- expect(panelSlotSpy).toHaveBeenCalledTimes(dashboardConfig.panels.length);
- });
-
- it('does not render the empty state', () => {
- expect(emptyStateSlotSpy).not.toHaveBeenCalled();
- });
-
- it('does not render the filter or actions containers', () => {
- expect(findFiltersContainer().exists()).toBe(false);
- expect(findActionsContainer().exists()).toBe(false);
- });
- });
-
- describe('when a dashboard has no panels', () => {
- beforeEach(() => {
- createWrapper({
- config: {
- ...dashboardConfig,
- panels: undefined,
- },
- });
- });
-
- it('does not render the dashboard grid', () => {
- expect(findGrid().exists()).toBe(false);
- });
-
- it('renders the empty state', () => {
- expect(emptyStateSlotSpy).toHaveBeenCalled();
- });
- });
-
- describe('when a dashboard has no description', () => {
- beforeEach(() => {
- createWrapper({
- config: {
- ...dashboardConfig,
- description: undefined,
- },
- });
- });
-
- it('does not render the dashboard description', () => {
- expect(findDescription().exists()).toBe(false);
- });
- });
-
- describe('when a dashboard has title and description slots', () => {
- const titleSlotSpy = jest.fn();
- const descriptionSlotSpy = jest.fn();
-
- beforeEach(() => {
- createWrapper(
- {},
- {
- title() {
- titleSlotSpy();
- return this.$createElement('div');
- },
- description() {
- descriptionSlotSpy();
- return this.$createElement('div');
- },
- },
- );
- });
-
- afterEach(() => {
- titleSlotSpy.mockRestore();
- descriptionSlotSpy.mockRestore();
- });
-
- it('renders the title slot and not the config title', () => {
- expect(titleSlotSpy).toHaveBeenCalled();
- expect(findTitle().exists()).toBe(false);
- });
-
- it('renders the description slot and not the config description', () => {
- expect(descriptionSlotSpy).toHaveBeenCalled();
- expect(findDescription().exists()).toBe(false);
- });
- });
-
- describe('when a dashboard has actions slot content', () => {
- beforeEach(() => {
- createWrapper({}, { actions: 'actions
' });
- });
-
- it('renders the action slots', () => {
- expect(findActionsContainer().exists()).toBe(true);
- });
- });
-
- describe('when a dashboard has filters slot content', () => {
- beforeEach(() => {
- createWrapper({}, { filters: 'filters
' });
- });
-
- it('renders the filters container', () => {
- expect(findFiltersContainer().exists()).toBe(true);
- });
- });
-});