From 2e58cf7def7244a6ed4daf558d9be23714164686 Mon Sep 17 00:00:00 2001 From: graphieros Date: Wed, 11 Nov 2026 07:21:03 +0100 Subject: [PATCH 1/3] chore(deps): bump vue-data-ui from 3.28.7 to 3.29.1 --- package.json | 2 +- pnpm-lock.yaml | 10 +++++----- pnpm-workspace.yaml | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 57243f1d29..d4d7c45d72 100644 --- a/package.json +++ b/package.json @@ -113,7 +113,7 @@ "vite-plugin-pwa": "2.0.0", "vite-plus": "catalog:vite-plus", "vue": "3.5.42", - "vue-data-ui": "3.28.7", + "vue-data-ui": "3.29.1", "vue-router": "5.2.0" }, "devDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 62df4acf20..5808e48986 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -277,8 +277,8 @@ importers: specifier: 3.5.42 version: 3.5.42(typescript-native-bridge@6.0.3-bridge.13.tsgo.7.0.2) vue-data-ui: - specifier: 3.28.7 - version: 3.28.7(vue@3.5.42) + specifier: 3.29.1 + version: 3.29.1(vue@3.5.42) vue-router: specifier: 5.2.0 version: 5.2.0(@voidzero-dev/vite-plus-core@1.4.0)(@vue/compiler-sfc@3.5.42)(esbuild@0.28.1)(rolldown@1.2.4)(rollup@4.62.2)(vue@3.5.42)(webpack@5.108.4) @@ -10806,8 +10806,8 @@ packages: vue-component-type-helpers@3.3.12: resolution: {integrity: sha512-HwCqmgTfLFskQc4P5TRnbduCMyPat2Ezu0elLWOGn0dD/6BKHP15B5bxce6aDi+E4kblPtru8amIBQzu0Di3qQ==} - vue-data-ui@3.28.7: - resolution: {integrity: sha512-QZP0SYW/7RoKVdvER1hDcQSnXfkIIpqde3zc6X6m0c8mP0UNS5MwP+/VJtDtsfnijhWz8BTDUZG7ij6Rwx+M+A==} + vue-data-ui@3.29.1: + resolution: {integrity: sha512-jEYmsZH94Pv6bHWlDf3tgzvJdCAxcNSLuL/4GuwQ5TrXHLmvwXxcID7HYyDbGnQUwXXMIBqrWD96qTZnoc+cbg==} peerDependencies: jspdf: '>=3.0.1' vue: 3.5.42 @@ -23846,7 +23846,7 @@ snapshots: vue-component-type-helpers@3.3.12: {} - vue-data-ui@3.28.7(vue@3.5.42): + vue-data-ui@3.29.1(vue@3.5.42): dependencies: vue: 3.5.42(typescript-native-bridge@6.0.3-bridge.13.tsgo.7.0.2) diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index c7e313844c..35041352bb 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -51,7 +51,7 @@ minimumReleaseAgeExclude: - '@nuxt/nitro-server@4.5.2' - '@nuxt/schema@4.5.2' - '@nuxt/vite-builder@4.5.2' - - vue-data-ui@3.28.7 + - vue-data-ui@3.29.1 minimumReleaseAgeExcludePrune: true From 387d8411525925e849e9177ab9e2df4833e3effc Mon Sep 17 00:00:00 2001 From: graphieros Date: Wed, 11 Nov 2026 07:21:41 +0100 Subject: [PATCH 2/3] chore: add translations --- i18n/locales/en.json | 2 ++ i18n/locales/fr-FR.json | 2 ++ i18n/schema.json | 6 ++++++ 3 files changed, 10 insertions(+) diff --git a/i18n/locales/en.json b/i18n/locales/en.json index babf83d554..acb9f3e050 100644 --- a/i18n/locales/en.json +++ b/i18n/locales/en.json @@ -779,9 +779,11 @@ "table_available": "A data table for this chart is available below.", "table_caption": "Chart data table" }, + "table_view": "Table view", "chart_view_toggle": "Toggle view", "chart_view_combined": "Combined view", "chart_view_split": "Split view", + "date": "Date", "granularity": "Granularity", "granularity_daily": "Daily", "granularity_weekly": "Weekly", diff --git a/i18n/locales/fr-FR.json b/i18n/locales/fr-FR.json index 4a1c6efd80..909d3f375a 100644 --- a/i18n/locales/fr-FR.json +++ b/i18n/locales/fr-FR.json @@ -649,9 +649,11 @@ "table_available": "Un tableau de données pour ce graphique est disponible ci-dessous.", "table_caption": "Tableau de données du graphique" }, + "table_view": "Afficher le tableau", "chart_view_toggle": "Changer de vue", "chart_view_combined": "Vue groupée", "chart_view_split": "Vue séparée", + "date": "Date", "granularity": "Granularité", "granularity_daily": "Par Jour", "granularity_weekly": "Par Semaine", diff --git a/i18n/schema.json b/i18n/schema.json index cb101c8a59..fcc89367f2 100644 --- a/i18n/schema.json +++ b/i18n/schema.json @@ -2341,6 +2341,9 @@ }, "additionalProperties": false }, + "table_view": { + "type": "string" + }, "chart_view_toggle": { "type": "string" }, @@ -2350,6 +2353,9 @@ "chart_view_split": { "type": "string" }, + "date": { + "type": "string" + }, "granularity": { "type": "string" }, From ce0d6507d33e12ba64edb9d560b5e30e3b8dcb15 Mon Sep 17 00:00:00 2001 From: graphieros Date: Wed, 11 Nov 2026 07:22:39 +0100 Subject: [PATCH 3/3] feat(a11y): replace chart hidden data tables with a togglable table view --- app/components/Compare/FacetBarChart.vue | 1 + app/components/Compare/FacetScatterChart.vue | 3 + .../Package/DailyDownloadsHeatmap.vue | 397 +++++++----- app/components/Package/TimelineChart.vue | 6 + app/components/Package/TrendsChart.vue | 591 ++++++++++-------- app/components/Package/TrendsDataTable.vue | 113 ++++ .../Package/VersionDistribution.vue | 3 + test/nuxt/a11y.spec.ts | 179 ++++++ 8 files changed, 857 insertions(+), 436 deletions(-) create mode 100644 app/components/Package/TrendsDataTable.vue diff --git a/app/components/Compare/FacetBarChart.vue b/app/components/Compare/FacetBarChart.vue index bc52fda4f2..79e373888d 100644 --- a/app/components/Compare/FacetBarChart.vue +++ b/app/components/Compare/FacetBarChart.vue @@ -142,6 +142,7 @@ const config = computed(() => { return { theme: isDarkMode.value ? 'dark' : '', a11y: { + includeTable: true, translations: { keyboardNavigation: $t('package.trends.chart_assistive_text.keyboard_navigation_vertical'), tableAvailable: $t('package.trends.chart_assistive_text.table_available'), diff --git a/app/components/Compare/FacetScatterChart.vue b/app/components/Compare/FacetScatterChart.vue index 634a2cbaab..dd87cd8a91 100644 --- a/app/components/Compare/FacetScatterChart.vue +++ b/app/components/Compare/FacetScatterChart.vue @@ -136,6 +136,9 @@ function buildExportFilename(extension: string): string { const config = computed(() => { return { + a11y: { + includeTable: true, + }, theme: isDarkMode.value ? 'dark' : '', userOptions: { buttons: { diff --git a/app/components/Package/DailyDownloadsHeatmap.vue b/app/components/Package/DailyDownloadsHeatmap.vue index ab4bdef01b..0991d6fb1d 100644 --- a/app/components/Package/DailyDownloadsHeatmap.vue +++ b/app/components/Package/DailyDownloadsHeatmap.vue @@ -26,6 +26,13 @@ type DailyDataPoint = { const props = defineProps<{ packageName: string }>() const chartRef = useTemplateRef('chartRef') +const showDataTable = shallowRef(false) +const chartPanel = useTemplateRef('chartPanel') +const { height: renderedChartHeight } = useElementSize(chartPanel) +const lastChartHeight = shallowRef(0) +watch(renderedChartHeight, height => { + if (height > 0) lastChartHeight.value = height +}) const { locale, localeProperties } = useI18n() const isRTL = computed(() => localeProperties.value.dir === 'rtl') @@ -219,6 +226,40 @@ const heatmap = computed(() => { const dataset = computed(() => heatmap.value.dataset) +const tableRows = computed(() => { + const yesterday = addDays(toUtcDay(new Date()), -1) + const { weekStarts, dataset: rows, reportedDates, anomalousDates } = heatmap.value + const maximum = Math.max(0, ...rows.flatMap(row => row.values)) + + return weekStarts.flatMap((weekStart, weekIndex) => + WEEK_DAYS.flatMap((_, dayIndex) => { + const date = addDays(parseIsoDate(weekStart), dayIndex) + if (date > yesterday) return [] + const key = toIsoDate(date) + const rawValue = rows[dayIndex]?.values[weekIndex] ?? 0 + const value = + reportedDates.has(key) && !anomalousDates.has(key) && rawValue > 0 ? rawValue : null + return [ + { + date: date.getTime(), + value, + color: + value == null + ? colors.value.bg + : `color-mix(in srgb, ${accent.value} ${maximum > 0 ? (value / maximum) * 100 : 0}%, ${colors.value.bg})`, + }, + ] + }), + ) +}) + +const tableDataset = computed(() => [ + { + name: props.packageName, + series: tableRows.value.map(row => row.value), + }, +]) + function computeMinMax(value: number, maxVal: number, date: string | null) { return { value: compactNumberFormatter.value.format(value), @@ -607,174 +648,208 @@ function getLegendContent(svg: VueUiHeatmapSvgSlotProps['svg']) {
- -
- - - - -