diff --git a/packages/analytics/analytics-chart/sandbox/pages/TimeSeriesChartDemo.vue b/packages/analytics/analytics-chart/sandbox/pages/TimeSeriesChartDemo.vue
index e50e982a9b..5bb3b3da73 100644
--- a/packages/analytics/analytics-chart/sandbox/pages/TimeSeriesChartDemo.vue
+++ b/packages/analytics/analytics-chart/sandbox/pages/TimeSeriesChartDemo.vue
@@ -204,6 +204,7 @@
:chart-data="(exploreResult)"
:chart-options="analyticsChartOptions"
chart-title="Request count by Status Code"
+ :go-to-explore="(exploreLink)"
:legend-position="legendPosition"
:show-annotations="showAnnotationsToggle"
:show-legend-values="showLegendValuesToggle"
@@ -330,6 +331,8 @@ const exportCsv = () => {
setModalVisibility(true)
}
+const exploreLink: string = 'https://cloud.konghq.tech/us/analytics/explorer'
+
const exploreResultText = ref('')
const hasError = computed(() => !isValidJson(exploreResultText.value))
const isValid = computed(() => exploreResultText.value !== undefined &&
diff --git a/packages/analytics/analytics-chart/src/components/AnalyticsChart.cy.ts b/packages/analytics/analytics-chart/src/components/AnalyticsChart.cy.ts
index 10e94a9cba..fdb0f693d2 100644
--- a/packages/analytics/analytics-chart/src/components/AnalyticsChart.cy.ts
+++ b/packages/analytics/analytics-chart/src/components/AnalyticsChart.cy.ts
@@ -22,6 +22,9 @@ function mouseMove(x1: number, y1: number, x2: number, y2: number, duration: num
describe('', () => {
beforeEach(() => {
cy.viewport(1280, 800)
+ cy.stub(composables, 'useEvaluateFeatureFlag').returns({
+ evaluateFeatureFlag: () => true,
+ })
})
it('Renders a line chart for total requests count with status code dimension', () => {
@@ -251,13 +254,37 @@ describe('', () => {
chartTitle: 'Requests',
},
})
- cy.getTestId('csv-export-button').should('not.exist')
+
+ cy.getTestId('chart-action-menu').should('not.exist')
})
- it('does not render an "Export" button if chart data is present but prop is set to `false`', () => {
+ it('renders the kebab menu with only the "Jump to Explore" link if no data is provided', () => {
+ cy.mount(AnalyticsChart, {
+ props: {
+ allowCsvExport: true,
+ goToExplore: 'https://cloud.konghq.tech/us/analytics/explorer',
+ chartData: emptyExploreResult,
+ chartOptions: {
+ type: 'timeseries_line',
+ },
+ chartTitle: 'Requests',
+ },
+ })
+
+ cy.getTestId('chart-action-menu').should('exist')
+
+ // eslint-disable-next-line cypress/unsafe-to-chain-command
+ cy.getTestId('chart-action-menu').click().then(() => {
+ cy.getTestId('chart-jump-to-explore').should('exist')
+ cy.getTestId('csv-export-button').should('not.exist')
+ })
+ })
+
+ it('does not render an "Export" link in the kebab actions if chart data is present but prop is set to `false`', () => {
cy.mount(AnalyticsChart, {
props: {
allowCsvExport: false,
+ goToExplore: 'https://cloud.konghq.tech/us/analytics/explorer',
chartData: exploreResult,
chartOptions: {
type: 'timeseries_line',
@@ -265,7 +292,14 @@ describe('', () => {
chartTitle: 'Requests',
},
})
- cy.getTestId('csv-export-button').should('not.exist')
+
+ cy.getTestId('chart-action-menu').should('exist')
+
+ // eslint-disable-next-line cypress/unsafe-to-chain-command
+ cy.getTestId('chart-action-menu').click().then(() => {
+ cy.getTestId('chart-jump-to-explore').should('exist')
+ cy.getTestId('csv-export-modal').should('not.exist')
+ })
})
it('Renders an "Export" button, and tabulated data in the modal preview', () => {
@@ -280,10 +314,13 @@ describe('', () => {
},
})
- cy.getTestId('csv-export-button').should('exist')
+ cy.getTestId('chart-action-menu').should('exist')
// eslint-disable-next-line cypress/unsafe-to-chain-command
- cy.getTestId('csv-export-button').click().then(() => {
+ cy.getTestId('chart-action-menu').click().then(() => {
+ cy.getTestId('chart-jump-to-explore').should('not.exist')
+
+ cy.getTestId('csv-export-button').click()
cy.getTestId('csv-export-modal').should('exist')
cy.get('.modal-content .vitals-table').should('exist')
})
diff --git a/packages/analytics/analytics-chart/src/components/AnalyticsChart.vue b/packages/analytics/analytics-chart/src/components/AnalyticsChart.vue
index b1b3dba506..6cf40329ff 100644
--- a/packages/analytics/analytics-chart/src/components/AnalyticsChart.vue
+++ b/packages/analytics/analytics-chart/src/components/AnalyticsChart.vue
@@ -28,7 +28,7 @@
+
+
+
+
+
+
+ {{ i18n.t('jumpToExplore') }}
+
+
+
+
+ {{ i18n.t('csvExport.exportButton') }}
+
+
+
+
+
+
,
required: true,
@@ -190,6 +237,9 @@ const emit = defineEmits<{
}>()
const { i18n } = composables.useI18n()
+const { evaluateFeatureFlag } = composables.useEvaluateFeatureFlag()
+
+const hasKebabMenuAccess = evaluateFeatureFlag('ma-3043-analytics-chart-kebab-menu', false)
const rawChartData = toRef(props, 'chartData')
@@ -211,6 +261,15 @@ const computedChartData = computed(() => {
).value
})
+const exportModalVisible = ref(false)
+const setExportModalVisibility = (val: boolean) => {
+ exportModalVisible.value = val
+}
+const csvFilename = computed(() => props.filenamePrefix || i18n.t('csvExport.defaultFilename'))
+const exportCsv = () => {
+ setExportModalVisibility(true)
+}
+
const timeRangeMs = computed(() => {
if (!props.chartData?.meta) {
return 0
@@ -302,6 +361,8 @@ const showChartHeader = computed(() => {
return (hasValidChartData.value && resultSetTruncated.value && maxEntitiesShown.value) || props.chartTitle || (props.allowCsvExport && hasValidChartData.value)
})
+const hasMenuOptions = computed(() => (props.allowCsvExport && hasValidChartData.value) || !!props.goToExplore)
+
const timeSeriesGranularity = computed(() => {
if (!props.chartData.meta.granularity_ms) {
@@ -413,6 +474,31 @@ provide('legendPosition', toRef(props, 'legendPosition'))
margin-left: var(--kui-space-50, $kui-space-50);
margin-top: var(--kui-space-10, $kui-space-10);
}
+
+ // Action menu
+ .dropdown {
+ display: flex;
+ margin-left: var(--kui-space-auto, $kui-space-auto);
+ margin-right: var(--kui-space-0, $kui-space-0);
+
+ li.k-dropdown-item {
+ a {
+ text-decoration: none;
+ }
+ }
+ a {
+ color: $kui-color-text;
+
+ &:hover {
+ color: $kui-color-text;
+ text-decoration: none;
+ }
+ }
+
+ &:hover {
+ cursor: pointer;
+ }
+ }
}
diff --git a/packages/analytics/analytics-chart/src/composables/index.ts b/packages/analytics/analytics-chart/src/composables/index.ts
index bd0e80b2a8..49fa8d9331 100644
--- a/packages/analytics/analytics-chart/src/composables/index.ts
+++ b/packages/analytics/analytics-chart/src/composables/index.ts
@@ -9,6 +9,7 @@ import useExploreResultToDatasets from './useExploreResultToDatasets'
import useExploreResultToTimeDataset from './useExploreResultToTimeDatasets'
import useReportChartDataForSynthetics from './useReportChartDataForSynthetics'
import useTranslatedUnits from './useTranslatedUnits'
+import useEvaluateFeatureFlag from './useEvauluateFeatureFlag'
// All composables must be exported as part of the default object for Cypress test stubs
export default {
@@ -23,4 +24,5 @@ export default {
useI18n,
useReportChartDataForSynthetics,
useTranslatedUnits,
+ useEvaluateFeatureFlag,
}
diff --git a/packages/analytics/analytics-chart/src/composables/useEvauluateFeatureFlag.ts b/packages/analytics/analytics-chart/src/composables/useEvauluateFeatureFlag.ts
new file mode 100644
index 0000000000..83c117f20f
--- /dev/null
+++ b/packages/analytics/analytics-chart/src/composables/useEvauluateFeatureFlag.ts
@@ -0,0 +1,19 @@
+import type { AnalyticsBridge } from '@kong-ui-public/analytics-utilities'
+import { inject } from 'vue'
+import { INJECT_QUERY_PROVIDER } from '../constants'
+
+export default function useEvaluateFeatureFlag() {
+
+ const queryBridge: AnalyticsBridge | undefined = inject(INJECT_QUERY_PROVIDER)
+
+ const evaluateFeatureFlag = (key: string, defaultValue: boolean) => {
+ if (!queryBridge) {
+ return defaultValue
+ }
+ return queryBridge.evaluateFeatureFlagFn(key, defaultValue)
+ }
+
+ return {
+ evaluateFeatureFlag,
+ }
+}
diff --git a/packages/analytics/analytics-chart/src/constants/index.ts b/packages/analytics/analytics-chart/src/constants/index.ts
new file mode 100644
index 0000000000..3063d75fd6
--- /dev/null
+++ b/packages/analytics/analytics-chart/src/constants/index.ts
@@ -0,0 +1 @@
+export const INJECT_QUERY_PROVIDER = 'analytics-query-provider'
diff --git a/packages/analytics/analytics-chart/src/locales/en.json b/packages/analytics/analytics-chart/src/locales/en.json
index 24e4fa1fc6..2475cdf977 100644
--- a/packages/analytics/analytics-chart/src/locales/en.json
+++ b/packages/analytics/analytics-chart/src/locales/en.json
@@ -5,6 +5,7 @@
"entityNoName": "no-name",
"debug": "debug",
"total": "Total",
+ "jumpToExplore": "Jump to Explore",
"chartUnits": {
"ms": "ms",
"bytes": "Byte{plural}",
diff --git a/packages/analytics/analytics-chart/src/types/chart-data.ts b/packages/analytics/analytics-chart/src/types/chart-data.ts
index b133f84fd9..6c6cec1199 100644
--- a/packages/analytics/analytics-chart/src/types/chart-data.ts
+++ b/packages/analytics/analytics-chart/src/types/chart-data.ts
@@ -39,6 +39,7 @@ export interface LegendValueEntry {
*/
export interface EnhancedLegendItem extends LegendItem {
value: LegendValueEntry
+ text: string
}
/**