@@ -124,10 +118,9 @@ const numberFormatter = useNumberFormatter()
+ {{ $t('action_bar.selection', selectedPackages.length) }} +
+ ++ {{ $t('filters.table.no_packages') }} +
+| + {{ getColumnLabel('selection') }} + |
|
+
+ |
|
diff --git a/app/components/Package/TableRow.vue b/app/components/Package/TableRow.vue
index cb9f5f300c..bbe864c7d5 100644
--- a/app/components/Package/TableRow.vue
+++ b/app/components/Package/TableRow.vue
@@ -17,6 +17,10 @@ const pkg = computed(() => props.result.package)
const score = computed(() => props.result.score)
const updatedDate = computed(() => props.result.package.date)
+const { isPackageSelected, togglePackageSelection, isMaxSelected } = usePackageSelection()
+const isSelected = computed
+ |
| {{ $t('search.title') }}- {
:disabled-sort-keys="disabledSortKeys"
search-context
@toggle-column="toggleColumn"
+ @toggle-selection="openSelectionView"
@reset-columns="resetColumns"
@clear-filter="handleClearFilter"
@clear-all-filters="clearAllFilters"
diff --git a/app/router.options.ts b/app/router.options.ts
index a6d9db7ccd..7038e91150 100644
--- a/app/router.options.ts
+++ b/app/router.options.ts
@@ -1,19 +1,29 @@
import type { RouterConfig } from 'nuxt/schema'
export default {
- scrollBehavior(to, _from, savedPosition) {
+ scrollBehavior(to, from, savedPosition) {
// If the browser has a saved position (e.g. back/forward navigation), restore it
-
if (savedPosition) {
return savedPosition
}
+
+ // Scroll to top when main search query changes
+ if (to.path === '/search' && to.query.q !== from.query.q) {
+ return { left: 0, top: 0 }
+ }
+
+ // Don't scroll for other param changes (filters, pagination, etc.)
+ if (to.path === from.path) {
+ return false
+ }
+
// If navigating to a hash anchor, scroll to it
if (to.hash) {
const { scrollMargin } = to.meta
return {
el: to.hash,
behavior: 'smooth',
- top: typeof scrollMargin == 'number' ? scrollMargin : 70,
+ top: typeof scrollMargin === 'number' ? scrollMargin : 70,
}
}
diff --git a/i18n/locales/en.json b/i18n/locales/en.json
index 5bc57ea17e..35a8e8b872 100644
--- a/i18n/locales/en.json
+++ b/i18n/locales/en.json
@@ -351,7 +351,9 @@
"published": "Published",
"weekly_downloads": "Weekly downloads",
"keywords": "Keywords",
- "license": "License"
+ "license": "License",
+ "select": "Select package",
+ "select_maximum": "Maximum {count} packages can be selected"
},
"versions": {
"title": "Versions",
@@ -839,6 +841,7 @@
"secure": "Without warnings",
"insecure": "With warnings"
},
+ "view_selected": "View selected",
"sort": {
"label": "Sort packages",
"toggle_direction": "Toggle sort direction",
@@ -872,7 +875,8 @@
"popularity_score": "Popularity score",
"maintenance_score": "Maintenance score",
"combined_score": "Combined score",
- "security": "Security"
+ "security": "Security",
+ "selection": "Select package"
},
"view_mode": {
"label": "View mode",
@@ -1346,5 +1350,10 @@
"all": "all"
}
}
+ },
+ "action_bar": {
+ "title": "action bar",
+ "selection": "0 selected | 1 selected | {count} selected",
+ "shortcut": "Press \"{key}\" to focus actions"
}
}
diff --git a/i18n/locales/it-IT.json b/i18n/locales/it-IT.json
index 76506bb846..81888ec25f 100644
--- a/i18n/locales/it-IT.json
+++ b/i18n/locales/it-IT.json
@@ -142,6 +142,9 @@
},
"scroll_to_top": "Torna su"
},
+ "profile": {
+ "invite": {}
+ },
"package": {
"not_found": "Pacchetto Non Trovato",
"not_found_message": "Impossibile trovare il pacchetto.",
@@ -229,9 +232,9 @@
"title": "Inizia",
"pm_label": "Gestore di pacchetti",
"copy_command": "Copia comando di installazione",
- "view_types": "Vedi {package}",
"copy_dev_command": "Copia comando di installazione dev",
- "dev_dependency_hint": "Di solito installato come dev dependency"
+ "dev_dependency_hint": "Di solito installato come dev dependency",
+ "view_types": "Vedi {package}"
},
"create": {
"title": "Crea nuovo progetto",
@@ -298,7 +301,8 @@
"more_tagged": "{count} altri taggati",
"all_covered": "Tutte le versioni sono coperte dai tag sopra",
"deprecated_title": "{version} (deprecato)",
- "view_all": "Visualizza {count} versione | Visualizza tutte le {count} versioni"
+ "view_all": "Visualizza {count} versione | Visualizza tutte le {count} versioni",
+ "copy_alt": {}
},
"dependencies": {
"title": "Dipendenza ({count}) | Dipendenze ({count})",
@@ -352,7 +356,8 @@
"y_axis_label": "{granularity} {facet}",
"items": {
"downloads": "Download"
- }
+ },
+ "copy_alt": {}
},
"downloads": {
"title": "Download settimanali",
@@ -795,6 +800,8 @@
"managers": "gestori di pacchetti"
}
},
+ "sponsors": {},
+ "oss_partners": {},
"team": {},
"contributors": {
"title": "{count} Collaboratore | {count} Collaboratori",
@@ -985,7 +992,9 @@
"up_to_you": "A tua scelta!"
},
"trends": {}
- }
+ },
+ "file_filter_option": {},
+ "filter": {}
},
"privacy_policy": {
"title": "Informativa sulla privacy",
@@ -1072,5 +1081,6 @@
"measures": {},
"limitations": {},
"contact": {}
- }
+ },
+ "action_bar": {}
}
diff --git a/i18n/schema.json b/i18n/schema.json
index 4ed08a60b2..f18d627696 100644
--- a/i18n/schema.json
+++ b/i18n/schema.json
@@ -1059,6 +1059,12 @@
},
"license": {
"type": "string"
+ },
+ "select": {
+ "type": "string"
+ },
+ "select_maximum": {
+ "type": "string"
}
},
"additionalProperties": false
@@ -2521,6 +2527,9 @@
},
"additionalProperties": false
},
+ "view_selected": {
+ "type": "string"
+ },
"sort": {
"type": "object",
"properties": {
@@ -2622,6 +2631,9 @@
},
"security": {
"type": "string"
+ },
+ "selection": {
+ "type": "string"
}
},
"additionalProperties": false
@@ -4045,6 +4057,21 @@
},
"additionalProperties": false
},
+ "action_bar": {
+ "type": "object",
+ "properties": {
+ "title": {
+ "type": "string"
+ },
+ "selection": {
+ "type": "string"
+ },
+ "shortcut": {
+ "type": "string"
+ }
+ },
+ "additionalProperties": false
+ },
"$schema": {
"type": "string"
}
diff --git a/shared/types/npm-registry.ts b/shared/types/npm-registry.ts
index 250a9218e6..b9e2e4d79c 100644
--- a/shared/types/npm-registry.ts
+++ b/shared/types/npm-registry.ts
@@ -128,8 +128,8 @@ export interface NpmSearchResponse {
export interface NpmSearchResult {
package: NpmSearchPackage
- score: NpmSearchScore
- searchScore: number
+ score?: NpmSearchScore
+ searchScore?: number
/** Download counts (weekly/monthly) */
downloads?: {
weekly?: number
diff --git a/shared/types/preferences.ts b/shared/types/preferences.ts
index 1714c404d3..f23f61c8cb 100644
--- a/shared/types/preferences.ts
+++ b/shared/types/preferences.ts
@@ -20,6 +20,7 @@ export type ColumnId =
| 'maintenanceScore'
| 'combinedScore'
| 'security'
+ | 'selection'
export interface ColumnConfig {
id: ColumnId
diff --git a/test/nuxt/a11y.spec.ts b/test/nuxt/a11y.spec.ts
index 2a5c18c742..c81c52ea1c 100644
--- a/test/nuxt/a11y.spec.ts
+++ b/test/nuxt/a11y.spec.ts
@@ -217,6 +217,9 @@ import {
DiffSkipBlock,
DiffTable,
DiffViewerPanel,
+ PackageActionBar,
+ PackageSelectionView,
+ PackageSelectionCheckbox,
} from '#components'
// Server variant components must be imported directly to test the server-side render
@@ -3510,6 +3513,57 @@ describe('component accessibility audits', () => {
expect(results.violations).toEqual([])
})
})
+
+ describe('PackageActionBar', () => {
+ it('should have no accessibility violations', async () => {
+ const component = await mountSuspended(PackageActionBar)
+ const results = await runAxe(component)
+ expect(results.violations).toEqual([])
+ })
+ })
+
+ describe('PackageSelectionView', () => {
+ it('should have no accessibility violations', async () => {
+ const component = await mountSuspended(PackageSelectionView)
+ const results = await runAxe(component)
+ expect(results.violations).toEqual([])
+ })
+
+ it('should have no accessibility violations changing view mode', async () => {
+ const component = await mountSuspended(PackageSelectionView, {
+ props: {
+ viewMode: 'table',
+ },
+ })
+ const results = await runAxe(component)
+ expect(results.violations).toEqual([])
+ })
+ })
+
+ describe('PackageSelectionCheckbox', () => {
+ it('should have no accessibility violations', async () => {
+ const component = await mountSuspended(PackageSelectionCheckbox, {
+ props: {
+ packageName: 'nuxt',
+ checked: false,
+ },
+ })
+ const results = await runAxe(component)
+ expect(results.violations).toEqual([])
+ })
+
+ it('should have no accessibility violations when disabled', async () => {
+ const component = await mountSuspended(PackageSelectionCheckbox, {
+ props: {
+ packageName: 'nuxt',
+ checked: false,
+ disabled: true,
+ },
+ })
+ const results = await runAxe(component)
+ expect(results.violations).toEqual([])
+ })
+ })
})
function applyTheme(colorMode: string, bgTheme: string | null) {
diff --git a/test/unit/shared/types/index.spec.ts b/test/unit/shared/types/index.spec.ts
index 4cd8447c80..0ad0c4de31 100644
--- a/test/unit/shared/types/index.spec.ts
+++ b/test/unit/shared/types/index.spec.ts
@@ -64,6 +64,6 @@ describe('npm registry types', () => {
expect(response.total).toBe(1)
expect(response.objects[0]?.package.name).toBe('test-package')
- expect(response.objects[0]?.score.final).toBe(0.9)
+ expect(response.objects[0]?.score?.final).toBe(0.9)
})
})
|
|---|