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: ` - - - - - `, -}); - -const SlotsTemplate = (args, { argTypes }) => ({ - components: { DashboardLayout }, - props: Object.keys(argTypes), - template: ` - - - - - - - - - - - `, -}); - -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 @@ - - 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 @@ 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); - }); - }); -});