Skip to content

Commit 15f2231

Browse files
committed
fix: adapt keyboad shortcuts and chartFilter
1 parent a0aaf4e commit 15f2231

File tree

9 files changed

+104
-63
lines changed

9 files changed

+104
-63
lines changed

app/components/Package/TrendsChart.vue

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ const props = withDefaults(
5353
5454
const { locale } = useI18n()
5555
const { accentColors, selectedAccentColor } = useAccentColor()
56-
const { settings } = useSettings()
56+
const { localSettings } = useUserLocalSettings()
5757
const { copy, copied } = useClipboard()
5858
5959
const colorMode = useColorMode()
@@ -963,7 +963,7 @@ const effectiveDataSingle = computed<EvolutionData>(() => {
963963
964964
if (isDownloadsMetric.value && data.length) {
965965
const pkg = effectivePackageNames.value[0] ?? props.packageName ?? ''
966-
if (settings.value.chartFilter.anomaliesFixed) {
966+
if (localSettings.value.chartFilter.anomaliesFixed) {
967967
data = applyBlocklistCorrection({
968968
data,
969969
packageName: pkg,
@@ -973,7 +973,7 @@ const effectiveDataSingle = computed<EvolutionData>(() => {
973973
974974
return applyDataCorrection(
975975
data as Array<{ value: number }>,
976-
settings.value.chartFilter,
976+
localSettings.value.chartFilter,
977977
) as EvolutionData
978978
}
979979
@@ -1016,12 +1016,12 @@ const chartData = computed<{
10161016
for (const pkg of names) {
10171017
let data = state.evolutionsByPackage[pkg] ?? []
10181018
if (isDownloadsMetric.value && data.length) {
1019-
if (settings.value.chartFilter.anomaliesFixed) {
1019+
if (localSettings.value.chartFilter.anomaliesFixed) {
10201020
data = applyBlocklistCorrection({ data, packageName: pkg, granularity })
10211021
}
10221022
data = applyDataCorrection(
10231023
data as Array<{ value: number }>,
1024-
settings.value.chartFilter,
1024+
localSettings.value.chartFilter,
10251025
) as EvolutionData
10261026
}
10271027
const points = extractSeriesPoints(granularity, data)
@@ -1787,10 +1787,10 @@ watch(selectedMetric, value => {
17871787
<label class="flex flex-col gap-1 flex-1">
17881788
<span class="text-2xs font-mono text-fg-subtle tracking-wide uppercase">
17891789
{{ $t('package.trends.average_window') }}
1790-
<span class="text-fg-muted">({{ settings.chartFilter.averageWindow }})</span>
1790+
<span class="text-fg-muted">({{ localSettings.chartFilter.averageWindow }})</span>
17911791
</span>
17921792
<input
1793-
v-model.number="settings.chartFilter.averageWindow"
1793+
v-model.number="localSettings.chartFilter.averageWindow"
17941794
type="range"
17951795
min="0"
17961796
max="20"
@@ -1801,10 +1801,10 @@ watch(selectedMetric, value => {
18011801
<label class="flex flex-col gap-1 flex-1">
18021802
<span class="text-2xs font-mono text-fg-subtle tracking-wide uppercase">
18031803
{{ $t('package.trends.smoothing') }}
1804-
<span class="text-fg-muted">({{ settings.chartFilter.smoothingTau }})</span>
1804+
<span class="text-fg-muted">({{ localSettings.chartFilter.smoothingTau }})</span>
18051805
</span>
18061806
<input
1807-
v-model.number="settings.chartFilter.smoothingTau"
1807+
v-model.number="localSettings.chartFilter.smoothingTau"
18081808
type="range"
18091809
min="0"
18101810
max="20"
@@ -1869,7 +1869,7 @@ watch(selectedMetric, value => {
18691869
:class="{ 'opacity-50 pointer-events-none': !hasAnomalies }"
18701870
>
18711871
<input
1872-
v-model="settings.chartFilter.anomaliesFixed"
1872+
v-model="localSettings.chartFilter.anomaliesFixed"
18731873
type="checkbox"
18741874
:disabled="!hasAnomalies"
18751875
class="accent-[var(--accent-color,var(--fg-subtle))]"

app/components/Package/WeeklyDownloadStats.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,14 +188,14 @@ watch(
188188
const correctedDownloads = computed<WeeklyDataPoint[]>(() => {
189189
let data = weeklyDownloads.value as WeeklyDataPoint[]
190190
if (!data.length) return data
191-
if (settings.value.chartFilter.anomaliesFixed) {
191+
if (localSettings.value.chartFilter.anomaliesFixed) {
192192
data = applyBlocklistCorrection({
193193
data,
194194
packageName: props.packageName,
195195
granularity: 'weekly',
196196
}) as WeeklyDataPoint[]
197197
}
198-
data = applyDataCorrection(data, settings.value.chartFilter) as WeeklyDataPoint[]
198+
data = applyDataCorrection(data, localSettings.value.chartFilter) as WeeklyDataPoint[]
199199
return data
200200
})
201201

app/composables/useUserLocalSettings.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ export interface UserLocalSettings {
66
connector: {
77
autoOpenURL: boolean
88
}
9+
chartFilter: {
10+
averageWindow: number
11+
smoothingTau: number
12+
anomaliesFixed: boolean
13+
}
914
}
1015

1116
const STORAGE_KEY = 'npmx-settings'
@@ -17,6 +22,11 @@ const DEFAULT_USER_LOCAL_SETTINGS: UserLocalSettings = {
1722
connector: {
1823
autoOpenURL: false,
1924
},
25+
chartFilter: {
26+
averageWindow: 0,
27+
smoothingTau: 1,
28+
anomaliesFixed: true,
29+
},
2030
}
2131

2232
let localSettingsRef: Ref<UserLocalSettings> | null = null
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
export const useKeyboardShortcuts = createSharedComposable(function useKeyboardShortcuts() {
2+
const { preferences } = useUserPreferencesState()
3+
const enabled = computed(() => preferences.value.keyboardShortcuts ?? true)
4+
5+
if (import.meta.client) {
6+
watch(
7+
enabled,
8+
value => {
9+
if (value) {
10+
delete document.documentElement.dataset.kbdShortcuts
11+
} else {
12+
document.documentElement.dataset.kbdShortcuts = 'false'
13+
}
14+
},
15+
{ immediate: true },
16+
)
17+
}
18+
19+
return enabled
20+
})

app/pages/settings.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ const setLocale: typeof setNuxti18nLocale = locale => {
324324
<SettingsToggle
325325
:label="$t('settings.keyboard_shortcuts_enabled')"
326326
:description="$t('settings.keyboard_shortcuts_enabled_description')"
327-
v-model="settings.keyboardShortcuts"
327+
v-model="keyboardShortcutsEnabled"
328328
/>
329329
</div>
330330
</section>

shared/schemas/userPreferences.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ export const UserPreferencesSchema = object({
2626
colorModePreference: optional(nullable(ColorModePreferenceSchema)),
2727
/** Search provider for package search: 'npm' or 'algolia' */
2828
searchProvider: optional(SearchProviderSchema),
29+
/** Whether keyboard shortcuts are enabled globally */
30+
keyboardShortcuts: optional(boolean()),
2931
/** Timestamp of last update (ISO 8601) - managed by server */
3032
updatedAt: optional(string()),
3133
})
@@ -49,7 +51,8 @@ export const DEFAULT_USER_PREFERENCES: Required<Omit<UserPreferences, 'updatedAt
4951
hidePlatformPackages: true,
5052
selectedLocale: null,
5153
colorModePreference: null,
52-
searchProvider: 'algolia',
54+
searchProvider: import.meta.test ? 'npm' : 'algolia',
55+
keyboardShortcuts: true,
5356
}
5457

5558
export const USER_PREFERENCES_STORAGE_BASE = 'npmx-kv-user-preferences'

test/e2e/interactions.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ test.describe('Keyboard Shortcuts', () => {
271271
test.describe('Keyboard Shortcuts disabled', () => {
272272
test.beforeEach(async ({ page }) => {
273273
await page.addInitScript(() => {
274-
localStorage.setItem('npmx-settings', JSON.stringify({ keyboardShortcuts: false }))
274+
localStorage.setItem('npmx-user-preferences', JSON.stringify({ keyboardShortcuts: false }))
275275
})
276276
})
277277

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
2+
import { nextTick } from 'vue'
3+
import { DEFAULT_USER_PREFERENCES } from '#shared/schemas/userPreferences'
4+
5+
describe('useKeyboardShortcuts', () => {
6+
beforeEach(() => {
7+
localStorage.clear()
8+
// Reset preferences to defaults
9+
const { preferences } = useUserPreferencesState()
10+
preferences.value = { ...DEFAULT_USER_PREFERENCES }
11+
})
12+
13+
afterEach(() => {
14+
delete document.documentElement.dataset.kbdShortcuts
15+
})
16+
17+
it('should return true by default', () => {
18+
const enabled = useKeyboardShortcuts()
19+
expect(enabled.value).toBe(true)
20+
})
21+
22+
it('should return false when preference is disabled', () => {
23+
const { preferences } = useUserPreferencesState()
24+
preferences.value = { ...preferences.value, keyboardShortcuts: false }
25+
26+
const enabled = useKeyboardShortcuts()
27+
expect(enabled.value).toBe(false)
28+
})
29+
30+
it('should reactively update when preferences change', () => {
31+
const enabled = useKeyboardShortcuts()
32+
const { preferences } = useUserPreferencesState()
33+
34+
expect(enabled.value).toBe(true)
35+
36+
preferences.value = { ...preferences.value, keyboardShortcuts: false }
37+
expect(enabled.value).toBe(false)
38+
39+
preferences.value = { ...preferences.value, keyboardShortcuts: true }
40+
expect(enabled.value).toBe(true)
41+
})
42+
43+
it('should set data-kbd-shortcuts attribute when disabled', async () => {
44+
const { preferences } = useUserPreferencesState()
45+
46+
useKeyboardShortcuts()
47+
48+
preferences.value = { ...preferences.value, keyboardShortcuts: false }
49+
await nextTick()
50+
expect(document.documentElement.dataset.kbdShortcuts).toBe('false')
51+
52+
preferences.value = { ...preferences.value, keyboardShortcuts: true }
53+
await nextTick()
54+
expect(document.documentElement.dataset.kbdShortcuts).toBeUndefined()
55+
})
56+
})

test/nuxt/composables/use-settings.spec.ts

Lines changed: 0 additions & 48 deletions
This file was deleted.

0 commit comments

Comments
 (0)