diff --git a/app/assets/main.css b/app/assets/main.css index e824da64da..bb6b830399 100644 --- a/app/assets/main.css +++ b/app/assets/main.css @@ -17,6 +17,7 @@ --fg: oklch(0.982 0 0); --fg-muted: oklch(0.849 0 0); --fg-subtle: oklch(0.773 0 0); + --fg-error: oklch(70.4% 0.191 22.216); /* border, separator colors */ --border: oklch(0.269 0 0); @@ -109,6 +110,7 @@ --fg: oklch(0.046 0 0); --fg-muted: oklch(0.198 0 0); --fg-subtle: oklch(0.28 0 0); + --fg-error: oklch(50.5% 0.213 27.518); --border: oklch(0.8514 0 0); --border-subtle: oklch(0.922 0 0); diff --git a/app/components/Package/TimelineChart.vue b/app/components/Package/TimelineChart.vue index 05464cf9ca..d18377771d 100644 --- a/app/components/Package/TimelineChart.vue +++ b/app/components/Package/TimelineChart.vue @@ -72,7 +72,8 @@ function addEvaluationFlags( ...entry, events, hasPositive: events.some(event => event.state === 'success'), - hasNegative: events.some(event => event.state === 'warn' || event.state === 'error'), + hasNegative: events.some(event => event.state === 'warn'), + hasError: events.some(event => event.state === 'error'), } }) } @@ -103,6 +104,7 @@ const convertedData = computed(() => { events: [], hasPositive: false, hasNegative: false, + hasError: false, } }) @@ -621,6 +623,7 @@ type TimelineSourceItem = { events?: SubEvent[] hasPositive?: boolean hasNegative?: boolean + hasError?: boolean } type TimelineSvgDataItem = VueUiXyDatasetLineItem & { @@ -656,6 +659,7 @@ function getDatapointPlots( const hasPositive = datapoint.hasPositive === true const hasNegative = datapoint.hasNegative === true + const hasError = datapoint.hasError === true return [ { @@ -663,7 +667,7 @@ function getDatapointPlots( index, x: plot.x, y: plot.y, - offsetY: markerKey === 'negative' && hasPositive && hasNegative ? 20 : 0, + offsetY: hasError ? 0 : markerKey === 'negative' && hasPositive && hasNegative ? 20 : 0, }, ] }) @@ -707,30 +711,40 @@ function getActiveVersionDatapointBar( ) } +// If a data point also has an error, the positive icon will not be shown function getPositiveDatapointPlots( item: TimelineDatasetItem, zoomOffset: number, ): TimelineMarkerItem[] { return getDatapointPlots( item, - datapoint => datapoint.hasPositive === true, + datapoint => datapoint.hasPositive === true && datapoint.hasError !== true, 'positive', zoomOffset, ) } +// If a data point also has an error, the negative icon will not be shown function getNegativeDatapointPlots( item: TimelineDatasetItem, zoomOffset: number, ): TimelineMarkerItem[] { return getDatapointPlots( item, - datapoint => datapoint.hasNegative === true, + datapoint => datapoint.hasNegative === true && datapoint.hasError !== true, 'negative', zoomOffset, ) } +// If a data point has an error, only this icon will be shown +function getErrorDatapointPlots( + item: TimelineDatasetItem, + zoomOffset: number, +): TimelineMarkerItem[] { + return getDatapointPlots(item, datapoint => datapoint.hasError === true, 'error', zoomOffset) +} + const indexSelection = computed(() => { if (props.selectedVersion == null) return null return orderedConvertedData.value.findIndex(v => v.version === props.selectedVersion) @@ -989,6 +1003,7 @@ const timelineMetricTabs = computed(() => [ " :markersPositive="getPositiveDatapointPlots(svg.data[0], svg.slicer.start)" :markersNegative="getNegativeDatapointPlots(svg.data[0], svg.slicer.start)" + :markersError="getErrorDatapointPlots(svg.data[0], svg.slicer.start)" :colors :gradientColors="E18E_GRADIENT_COLORS" :pauseAnimations="shouldPauseChartAnimations || loading" diff --git a/app/components/Package/TimelineChartXySvgSlot.vue b/app/components/Package/TimelineChartXySvgSlot.vue index ec38d6badd..e9e2886930 100644 --- a/app/components/Package/TimelineChartXySvgSlot.vue +++ b/app/components/Package/TimelineChartXySvgSlot.vue @@ -8,6 +8,7 @@ const props = defineProps<{ watermark?: string markersPositive: TimelineMarkerItem[] markersNegative: TimelineMarkerItem[] + markersError: TimelineMarkerItem[] colors: Record gradientColors: string[] pauseAnimations: boolean @@ -82,5 +83,27 @@ const svgElementTransitionClass = computed(() => [ :class="svgElementTransitionClass" /> + + + + + + diff --git a/app/composables/useColors.ts b/app/composables/useColors.ts index 87bbb64c5d..4715e77210 100644 --- a/app/composables/useColors.ts +++ b/app/composables/useColors.ts @@ -28,6 +28,7 @@ const colorVariables = [ '--fg', '--fg-muted', '--fg-subtle', + '--fg-error', ] as const function readCssVariable(element: HTMLElement, variableName: string): string { diff --git a/app/utils/charts.ts b/app/utils/charts.ts index fa22005868..d752008317 100644 --- a/app/utils/charts.ts +++ b/app/utils/charts.ts @@ -489,6 +489,7 @@ export type EnrichedTimelineSizeCacheEntry = ConvertedTimelineSizeCacheEntry & { events: SubEvent[] hasPositive: boolean hasNegative: boolean + hasError: boolean } export type TimelineChartMetric = 'totalSize' | 'dependencyCount' | 'dependencySize' diff --git a/test/nuxt/a11y.spec.ts b/test/nuxt/a11y.spec.ts index ef2212a7bb..24cc1df740 100644 --- a/test/nuxt/a11y.spec.ts +++ b/test/nuxt/a11y.spec.ts @@ -2464,6 +2464,7 @@ describe('component accessibility audits', () => { watermark: 'npmx', markersPositive: [], markersNegative: [], + markersError: [], colors: { bg: '#FFFFFF', accent: '#FF0000' }, pauseAnimations: false, gradientColors: [