Skip to content

Commit

Permalink
feat: new dashboard tile header (#1802)
Browse files Browse the repository at this point in the history
* feat: new dashboard tile header

- introduce a header to dashboard tiles
- render title and kebab menu for tile actions in this header
- behind FF no need for major version upgrade

* fix: component test

* fix: tighten up gap in topn table

* fix: show description in header in stead of kebab menu for golden signals and topn table

* fix: one more style tweak

* fix: style

* fix: component test

* fix: address comments

* fix: component test

* fix: address comments

* fix: tweak styles

* fix: comment
  • Loading branch information
filipgutica authored Nov 22, 2024
1 parent f0ba1f7 commit 811423a
Show file tree
Hide file tree
Showing 16 changed files with 266 additions and 239 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ describe('<AnalyticsChart />', () => {
beforeEach(() => {
cy.viewport(1280, 800)
cy.stub(composables, 'useEvaluateFeatureFlag').returns({
evaluateFeatureFlag: () => true,
evaluateFeatureFlag: () => false,
})
})

Expand Down Expand Up @@ -254,52 +254,6 @@ describe('<AnalyticsChart />', () => {
chartTitle: 'Requests',
},
})

cy.getTestId('chart-action-menu').should('not.exist')
})

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',
},
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-modal').should('not.exist')
})
})

it('Renders an "Export" button, and tabulated data in the modal preview', () => {
Expand All @@ -314,19 +268,10 @@ describe('<AnalyticsChart />', () => {
},
})

cy.getTestId('chart-action-menu').should('exist')

cy.get('.chart-header').trigger('mouseenter')
cy.getTestId('kebab-action-menu').should('be.visible')

// eslint-disable-next-line cypress/unsafe-to-chain-command
cy.getTestId('kebab-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')
})
cy.getTestId('csv-export-button').click()
cy.getTestId('csv-export-modal').should('exist')
cy.get('.modal-content .vitals-table').should('exist')
})

it('multi dimension bar charts have "tooltipContext"', () => {
Expand Down
163 changes: 10 additions & 153 deletions packages/analytics/analytics-chart/src/components/AnalyticsChart.vue
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
<template>
<div
class="analytics-chart-shell"
:class="{ 'is-hovering': isHovering }"
@mouseenter="handleMouseEnter"
@mouseleave="handleMouseLeave"
>
<div
v-if="hasValidChartData && resultSetTruncated && maxEntitiesShown || (!hasKebabMenuAccess && (allowCsvExport || props.chartTitle))"
class="chart-header"
>
<div
Expand All @@ -15,9 +13,11 @@
>
{{ chartTitle }}
</div>
<div class="chart-header-icons-wrapper">
<div
v-if="hasValidChartData && resultSetTruncated && maxEntitiesShown"
class="chart-header-icons-wrapper"
>
<KTooltip
v-if="hasValidChartData && resultSetTruncated && maxEntitiesShown"
class="tooltip"
max-width="500"
placement="right"
Expand All @@ -33,63 +33,14 @@
</KTooltip>
</div>
<div
v-if="allowCsvExport && hasValidChartData && !hasKebabMenuAccess"
v-if="!hasKebabMenuAccess && allowCsvExport && hasValidChartData"
class="chart-export-button"
>
<CsvExportButton
:data="rawChartData"
:filename-prefix="filenamePrefix"
/>
</div>
<!-- More actions menu -->
<KDropdown
v-if="hasKebabMenuAccess && hasMenuOptions"
class="dropdown"
data-testid="chart-action-menu"
>
<button
appearance="none"
:aria-label="i18n.t('more_actions')"
class="kebab-action-menu"
data-testid="kebab-action-menu"
>
<MoreIcon
:color="KUI_COLOR_TEXT_NEUTRAL"
:size="KUI_ICON_SIZE_40"
/>
</button>
<template #items>
<KDropdownItem
v-if="!!goToExplore"
data-testid="chart-jump-to-explore"
>
<a :href="goToExplore">
{{ i18n.t('jumpToExplore') }}
</a>
</KDropdownItem>
<KDropdownItem
v-if="allowCsvExport && hasValidChartData"
class="chart-export-button"
data-testid="chart-csv-export"
@click="exportCsv()"
>
<span
class="chart-export-trigger"
data-testid="csv-export-button"
>
{{ i18n.t('csvExport.exportAsCsv') }}
</span>
</KDropdownItem>
<slot name="menu-items" />
</template>
</KDropdown>
<!-- Keep outside of dropdown, so we can independently affect its visibility -->
<CsvExportModal
v-if="exportModalVisible"
:chart-data="rawChartData"
:filename="csvFilename"
@toggle-modal="setExportModalVisibility"
/>
</div>
<KEmptyState
v-if="!hasValidChartData"
Expand Down Expand Up @@ -164,14 +115,13 @@ import { ChartLegendPosition } from '../enums'
import StackedBarChart from './chart-types/StackedBarChart.vue'
import DoughnutChart from './chart-types/DoughnutChart.vue'
import type { PropType } from 'vue'
import { computed, provide, toRef, ref } from 'vue'
import { computed, provide, toRef } from 'vue'
import { msToGranularity } from '@kong-ui-public/analytics-utilities'
import type { AbsoluteTimeRangeV4, ExploreAggregations, ExploreResultV4, GranularityValues } from '@kong-ui-public/analytics-utilities'
import { hasMillisecondTimestamps, defaultStatusCodeColors } from '../utils'
import TimeSeriesChart from './chart-types/TimeSeriesChart.vue'
import { KUI_COLOR_TEXT_NEUTRAL, KUI_COLOR_TEXT_WARNING, KUI_ICON_SIZE_40 } from '@kong/design-tokens'
import { MoreIcon, WarningIcon } from '@kong/icons'
import CsvExportModal from './CsvExportModal.vue'
import { KUI_COLOR_TEXT_WARNING, KUI_ICON_SIZE_40 } from '@kong/design-tokens'
import { WarningIcon } from '@kong/icons'
import CsvExportButton from './CsvExportButton.vue'
const props = defineProps({
Expand All @@ -180,11 +130,6 @@ const props = defineProps({
required: false,
default: false,
},
goToExplore: {
type: String,
required: false,
default: '',
},
chartData: {
type: Object as PropType<ExploreResultV4>,
required: true,
Expand Down Expand Up @@ -243,11 +188,6 @@ const props = defineProps({
required: false,
default: false,
},
showMenu: {
type: Boolean,
required: false,
default: false,
},
})
const emit = defineEmits<{
Expand All @@ -260,8 +200,6 @@ const { evaluateFeatureFlag } = composables.useEvaluateFeatureFlag()
const hasKebabMenuAccess = evaluateFeatureFlag('ma-3043-analytics-chart-kebab-menu', false)
const rawChartData = toRef(props, 'chartData')
const isHovering = ref(false)
const computedChartData = computed(() => {
return isTimeSeriesChart.value
Expand All @@ -281,15 +219,6 @@ const computedChartData = computed(() => {
).value
})
const exportModalVisible = ref(false)
const setExportModalVisibility = (val: boolean) => {
exportModalVisible.value = val
}
const csvFilename = computed<string>(() => props.filenamePrefix || i18n.t('csvExport.defaultFilename'))
const exportCsv = () => {
setExportModalVisibility(true)
}
const timeRangeMs = computed<number | undefined>(() => {
if (!props.chartData?.meta) {
return 0
Expand Down Expand Up @@ -377,8 +306,6 @@ const hasValidChartData = computed(() => {
return props.chartData && props.chartData.meta && props.chartData.data.length
})
const hasMenuOptions = computed(() => (props.allowCsvExport && hasValidChartData.value) || !!props.goToExplore || props.showMenu)
const timeSeriesGranularity = computed<GranularityValues>(() => {
if (!props.chartData.meta.granularity_ms) {
Expand Down Expand Up @@ -438,22 +365,6 @@ const chartTooltipSortFn = computed(() => {
}
})
const chartHeaderPosition = computed(() => {
return props.chartTitle || !hasKebabMenuAccess || (resultSetTruncated.value && maxEntitiesShown.value) ? 'relative' : 'absolute'
})
const chartHeaderWidth = computed(() => {
return chartHeaderPosition.value === 'relative' ? '100%' : '2ch'
})
const handleMouseEnter = () => {
isHovering.value = true
}
const handleMouseLeave = () => {
isHovering.value = false
}
provide('showLegendValues', showLegendValues)
provide('legendPosition', toRef(props, 'legendPosition'))
Expand All @@ -468,13 +379,6 @@ provide('legendPosition', toRef(props, 'legendPosition'))
position: relative;
width: 100%;
&.is-hovering {
.chart-header :deep(.popover-trigger-wrapper) {
opacity: 1;
visibility: visible;
}
}
.analytics-chart-parent {
height: inherit;
width: inherit;
Expand All @@ -490,17 +394,10 @@ provide('legendPosition', toRef(props, 'legendPosition'))
align-items: center;
display: flex;
justify-content: flex-start;
padding-bottom: var(--kui-space-60, $kui-space-60);
position: v-bind('chartHeaderPosition');
right: 0;
width: v-bind('chartHeaderWidth');
width: 100%;
z-index: 999;
&:hover :deep(.popover-trigger-wrapper) {
opacity: 1;
visibility: visible;
}
.chart-header-icons-wrapper {
display: flex;
justify-content: end;
Expand Down Expand Up @@ -528,45 +425,5 @@ 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);
:deep(.popover-trigger-wrapper) {
opacity: 0;
transform: fade(0, -10px);
transition: opacity 0.3s ease, transform 0.3s ease, visibility 0.3s;
visibility: hidden;
}
.kebab-action-menu {
background: $kui-color-background-transparent;
border: none;
color: inherit;
cursor: pointer;
height: 100%;
}
li.k-dropdown-item {
a {
text-decoration: none;
}
}
a {
color: $kui-color-text;
&:hover {
color: $kui-color-text;
text-decoration: none;
}
}
&:hover {
cursor: pointer;
}
}
}
</style>
Original file line number Diff line number Diff line change
Expand Up @@ -234,9 +234,8 @@ const getValue = (record: AnalyticsExploreRecord): string => {
.kong-ui-public-top-n-table {
border: none !important;
height: 100%;
max-height: 100%;
padding: 0 !important;
.top-n-card-description {
color: $kui-color-text-neutral;
font-size: $kui-font-size-20;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
:class="cardSize"
>
<div
v-if="props.containerTitle"
v-if="props.containerTitle || props.containerDescription"
class="container-title"
>
{{ props.containerTitle }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,6 @@ const dashboardConfig: DashboardConfig = {
{
definition: {
chart: {
chartTitle: 'Timeseries line chart of mock data',
type: 'timeseries_line',
},
query: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const delayedResponse = <T>(response: T): Promise<T> => {

const queryFn = async (query: ExploreQuery): Promise<ExploreResultV4> => {
console.log('Querying data:', query)
if (query.dimensions && query.dimensions.findIndex(d => d === 'time') > -1) {
if (query.dimensions && query.dimensions.includes('time')) {
return await delayedResponse(
generateSingleMetricTimeSeriesData(
{ name: 'requests', unit: 'count' },
Expand Down
Loading

0 comments on commit 811423a

Please sign in to comment.