diff --git a/app/assets/javascripts/ml/experiment_tracking/components/performance_graph.vue b/app/assets/javascripts/ml/experiment_tracking/components/performance_graph.vue index f0fc346e43a965c718e781a2d0f659ace7e0c970..0457c722d83eaf4c3d9c8d34166cd303ec1383da 100644 --- a/app/assets/javascripts/ml/experiment_tracking/components/performance_graph.vue +++ b/app/assets/javascripts/ml/experiment_tracking/components/performance_graph.vue @@ -1,10 +1,12 @@ <script> import { GlLineChart } from '@gitlab/ui/dist/charts'; +import { GlEmptyState } from '@gitlab/ui'; import { s__ } from '~/locale'; +import { CREATE_EXPERIMENT_HELP_PATH } from '~/ml/experiment_tracking/routes/experiments/index/constants'; export default { name: 'PerformanceGraph', - components: { GlLineChart }, + components: { GlLineChart, GlEmptyState }, props: { candidates: { type: Array, @@ -14,6 +16,11 @@ export default { type: Array, required: true, }, + emptyStateSvgPath: { + type: String, + required: false, + default: '', + }, }, data() { return { @@ -24,6 +31,11 @@ export default { i18n: { xAxisLabel: s__('ExperimentTracking|Candidate'), yAxisLabel: s__('ExperimentTracking|Metric value'), + createNewCandidateLabel: s__('ExperimentTracking|Create candidate using MLflow'), + emptyStateLabel: s__('ExperimentTracking|No candidates'), + emptyStateDescriptionLabel: s__( + 'ExperimentTracking|Performance graph will be shown when candidates with logged metrics are available', + ), }, computed: { graphData() { @@ -55,6 +67,12 @@ export default { toolbox: { show: true }, }; }, + showGraph() { + return this.candidates.length > 0 && this.metricNames.length > 0; + }, + }, + constants: { + CREATE_EXPERIMENT_HELP_PATH, }, methods: { formatTooltipText(params) { @@ -70,6 +88,7 @@ export default { <template> <gl-line-chart + v-if="showGraph" :data="graphData" :option="graphOptions" show-legend @@ -87,4 +106,14 @@ export default { </div> </template> </gl-line-chart> + <gl-empty-state + v-else + :title="$options.i18n.emptyStateLabel" + :secondary-button-text="$options.i18n.createNewCandidateLabel" + :secondary-button-link="$options.constants.CREATE_EXPERIMENT_HELP_PATH" + :svg-path="emptyStateSvgPath" + :svg-height="null" + :description="$options.i18n.emptyStateDescriptionLabel" + class="gl-py-8" + /> </template> diff --git a/locale/gitlab.pot b/locale/gitlab.pot index e6a28a0a7271e6bb0d3be0fa0bcf3dd79d41982e..e1b600752a8d0f055b38cc421fcd87c12c682d6b 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -22932,12 +22932,21 @@ msgstr "" msgid "ExperimentTracking|Candidate" msgstr "" +msgid "ExperimentTracking|Create candidate using MLflow" +msgstr "" + msgid "ExperimentTracking|Metric value" msgstr "" +msgid "ExperimentTracking|No candidates" +msgstr "" + msgid "ExperimentTracking|Performance" msgstr "" +msgid "ExperimentTracking|Performance graph will be shown when candidates with logged metrics are available" +msgstr "" + msgid "Experiments" msgstr "" diff --git a/spec/frontend/ml/experiment_tracking/components/performance_graph_spec.js b/spec/frontend/ml/experiment_tracking/components/performance_graph_spec.js index 48f8f2a94b2b46c2c817e49f47068275ce24b609..be67912f9cec466137c594ea86afa94cc0942ea0 100644 --- a/spec/frontend/ml/experiment_tracking/components/performance_graph_spec.js +++ b/spec/frontend/ml/experiment_tracking/components/performance_graph_spec.js @@ -1,4 +1,5 @@ import { GlLineChart } from '@gitlab/ui/dist/charts'; +import { GlEmptyState } from '@gitlab/ui'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import PerformanceGraph from '~/ml/experiment_tracking/components/performance_graph.vue'; import { MOCK_CANDIDATES } from '../routes/experiments/show/mock_data'; @@ -12,12 +13,14 @@ describe('PerformanceGraph', () => { propsData: { candidates, metricNames, + emptyStateSvgPath: 'illustrations/status/status-new-md.svg', }, }); }; const findGraph = () => wrapper.findComponent(PerformanceGraph); const findLineChart = () => findGraph().findComponent(GlLineChart); + const findEmptyState = () => wrapper.findComponent(GlEmptyState); describe('rendering', () => { it('renders the component', () => { @@ -25,6 +28,7 @@ describe('PerformanceGraph', () => { expect(findGraph().props('candidates')).toEqual(MOCK_CANDIDATES); expect(findGraph().props('metricNames')).toEqual(MOCK_METRICS); + expect(findEmptyState().exists()).toBe(false); }); it('renders the correct data', () => { @@ -39,4 +43,27 @@ describe('PerformanceGraph', () => { expect(findLineChart().props('data')[2].data.length).toBe(1); }); }); + + describe('empty state', () => { + it('should show empty state if candidates are missing', () => { + createWrapper([], MOCK_METRICS); + + expect(findLineChart().exists()).toBe(false); + expect(findEmptyState().exists()).toBe(true); + }); + + it('should show empty state if metric names are missing', () => { + createWrapper(MOCK_CANDIDATES, []); + + expect(findLineChart().exists()).toBe(false); + expect(findEmptyState().exists()).toBe(true); + }); + + it('should show empty state if candidates and metric names are missing', () => { + createWrapper([], []); + + expect(findLineChart().exists()).toBe(false); + expect(findEmptyState().exists()).toBe(true); + }); + }); });