diff --git a/ee/app/assets/javascripts/product_analytics/dashboards/components/analytics_dashboard_list.vue b/ee/app/assets/javascripts/product_analytics/dashboards/components/analytics_dashboard_list.vue index b5594413d3cd26a793e2185c87bf1e007a782b26..4e80fa3bc72d1739149e04f537da7810cdfbe59d 100644 --- a/ee/app/assets/javascripts/product_analytics/dashboards/components/analytics_dashboard_list.vue +++ b/ee/app/assets/javascripts/product_analytics/dashboards/components/analytics_dashboard_list.vue @@ -1,39 +1,76 @@ <script> -import { GlIcon, GlLabel } from '@gitlab/ui'; +import { GlAvatar, GlIcon, GlLabel, GlLink } from '@gitlab/ui'; +import { helpPagePath } from '~/helpers/help_page_helper'; import jsonList from '../gl_dashboards/analytics_dashboards.json'; +import { I18N_DASHBOARD_LIST } from '../constants'; export default { name: 'AnalyticsDashboard', components: { + GlAvatar, GlIcon, GlLabel, + GlLink, }, data() { return { dashboards: jsonList.internalDashboards, }; }, + methods: { + routeToDashboard(dashboardId) { + return this.$router.push(dashboardId); + }, + }, + i18n: I18N_DASHBOARD_LIST, + helpPageUrl: helpPagePath('user/product_analytics/index', { + anchor: 'product-analytics-dashboards', + }), }; </script> <template> <div> - <ul class="content-list"> - <li v-for="dashboard in dashboards" :key="dashboard.id"> - <div class="gl-float-left gl-mr-4"> + <h2 data-testid="title">{{ $options.i18n.title }}</h2> + <p data-testid="description"> + {{ $options.i18n.description }} + <gl-link data-testid="help-link" :href="$options.helpPageUrl">{{ + $options.i18n.learnMore + }}</gl-link> + </p> + <ul class="content-list gl-border-t gl-border-gray-50"> + <li + v-for="dashboard in dashboards" + :key="dashboard.id" + data-testid="dashboard-list-item" + class="gl-display-flex! gl-px-5! gl-align-items-center gl-hover-cursor-pointer gl-hover-bg-blue-50" + @click="routeToDashboard(dashboard.id)" + > + <div class="gl-float-left gl-mr-4 gl-display-flex gl-align-items-center"> <gl-icon data-testid="dashboard-icon" - :name="dashboard.icon || 'dashboard'" - class="gl-text-gray-200" - :size="32" + name="project" + class="gl-text-gray-200 gl-mr-3" + :size="16" /> + <gl-avatar :entity-name="dashboard.title" shape="rect" :size="32" /> </div> - <router-link data-testid="dashboard-link" class="title" :to="dashboard.id">{{ - dashboard.title - }}</router-link> - <div> - <div data-testid="dashboard-description" class="gl-display-inline"> - {{ dashboard.description }} + <div + class="gl-display-flex gl-align-items-center gl-justify-content-space-between gl-flex-grow-1" + > + <div class="gl-display-flex gl-flex-direction-column"> + <router-link + data-testid="dashboard-link" + class="gl-font-weight-bold gl-line-height-normal" + :to="dashboard.id" + >{{ dashboard.title }}</router-link + > + <p + data-testid="dashboard-description" + class="gl-line-height-normal gl-m-0 gl-text-gray-500" + > + {{ dashboard.description }} + </p> </div> <div class="gl-float-right"> <gl-label diff --git a/ee/app/assets/javascripts/product_analytics/dashboards/constants.js b/ee/app/assets/javascripts/product_analytics/dashboards/constants.js new file mode 100644 index 0000000000000000000000000000000000000000..e7ca40f7b718ca8a89ab716fc6e336d2b118f5ec --- /dev/null +++ b/ee/app/assets/javascripts/product_analytics/dashboards/constants.js @@ -0,0 +1,9 @@ +import { s__, __ } from '~/locale'; + +export const I18N_DASHBOARD_LIST = { + title: s__('ProductAnalytics|Product analytics dashboards'), + description: s__( + 'ProductAnalytics|Dashboards are created by editing the projects dashboard files.', + ), + learnMore: __('Learn more.'), +}; diff --git a/ee/spec/frontend/product_analytics/dashboards/components/analytics_dashboard_list_spec.js b/ee/spec/frontend/product_analytics/dashboards/components/analytics_dashboard_list_spec.js index 50833a761ec3063a305460d9d8299cc3ecdf9055..ccb889729b1b67a81bb9f2bc003d3289bc68a8a0 100644 --- a/ee/spec/frontend/product_analytics/dashboards/components/analytics_dashboard_list_spec.js +++ b/ee/spec/frontend/product_analytics/dashboards/components/analytics_dashboard_list_spec.js @@ -1,6 +1,8 @@ import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import AnalyticsDashboardList from 'ee/product_analytics/dashboards/components/analytics_dashboard_list.vue'; +import { I18N_DASHBOARD_LIST } from 'ee/product_analytics/dashboards/constants'; import jsonList from 'ee/product_analytics/dashboards/gl_dashboards/analytics_dashboards.json'; +import { helpPagePath } from '~/helpers/help_page_helper'; describe('AnalyticsDashboardList', () => { let wrapper; @@ -9,14 +11,25 @@ describe('AnalyticsDashboardList', () => { const findRouterLinks = () => wrapper.findAllByTestId('dashboard-link'); const findRouterIcons = () => wrapper.findAllByTestId('dashboard-icon'); const findRouterLabels = () => wrapper.findAllByTestId('dashboard-label'); + const findListItems = () => wrapper.findAllByTestId('dashboard-list-item'); + const findPageTitle = () => wrapper.findByTestId('title'); + const findPageDescription = () => wrapper.findByTestId('description'); + const findHelpLink = () => wrapper.findByTestId('help-link'); const NUMBER_OF_DASHBOARDS = jsonList.internalDashboards.length; + const $router = { + push: jest.fn(), + }; + const createWrapper = () => { wrapper = shallowMountExtended(AnalyticsDashboardList, { stubs: { RouterLink: true, }, + mocks: { + $router, + }, }); }; @@ -25,6 +38,23 @@ describe('AnalyticsDashboardList', () => { createWrapper(); }); + it('should render the page title', () => { + expect(findPageTitle().text()).toBe(I18N_DASHBOARD_LIST.title); + }); + + it('should render the page description', () => { + expect(findPageDescription().text()).toContain(I18N_DASHBOARD_LIST.description); + }); + + it('should render the help link', () => { + expect(findHelpLink().text()).toBe(I18N_DASHBOARD_LIST.learnMore); + expect(findHelpLink().attributes('href')).toBe( + helpPagePath('user/product_analytics/index', { + anchor: 'product-analytics-dashboards', + }), + ); + }); + it('should render titles', () => { expect(findRouterLinks()).toHaveLength(NUMBER_OF_DASHBOARDS); expect(findRouterLinks().at(0).element.innerText).toContain('Overview'); @@ -40,13 +70,18 @@ describe('AnalyticsDashboardList', () => { }); it('should render icons', () => { - expect(findRouterIcons()).toHaveLength(NUMBER_OF_DASHBOARDS); - expect(findRouterIcons().at(0).props('name')).toBe('dashboard'); + expect(findRouterIcons().at(0).props('name')).toBe('project'); }); it('should render label', () => { expect(findRouterLabels()).toHaveLength(1); expect(findRouterLabels().at(0).props('title')).toBe('Audience'); }); + + it('should route to the dashboard when a list item is clicked', async () => { + await findListItems().at(0).trigger('click'); + + expect($router.push).toHaveBeenCalledWith('dashboard_overview'); + }); }); }); diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 47cfacbdada5bd27505a4eb5db34dc76261690e7..849fe9aaba5e1a03bb1734034ca0e7c1cd7c8c93 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -31348,9 +31348,15 @@ msgstr "" msgid "ProductAnalytics|Audience" msgstr "" +msgid "ProductAnalytics|Dashboards are created by editing the projects dashboard files." +msgstr "" + msgid "ProductAnalytics|New Analytics Widget Title" msgstr "" +msgid "ProductAnalytics|Product analytics dashboards" +msgstr "" + msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already." msgstr ""