Skip to content

Commit 4ffb5a8

Browse files
committed
Fix period selector preset ownership
1 parent 7db74fb commit 4ffb5a8

11 files changed

Lines changed: 361 additions & 247 deletions

plugins/CoreHome/vue/dist/CoreHome.umd.js

Lines changed: 115 additions & 125 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

plugins/CoreHome/vue/dist/CoreHome.umd.min.js

Lines changed: 40 additions & 40 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*!
2+
* Matomo - free/libre analytics platform
3+
*
4+
* @link https://matomo.org
5+
* @license https://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
6+
*/
7+
8+
import { mount } from '@vue/test-utils';
9+
import DateRangePicker from './DateRangePicker.vue';
10+
11+
describe('DateRangePicker', () => {
12+
function mountPicker(customProps = {}) {
13+
return mount(DateRangePicker, {
14+
props: {
15+
startDate: '2026-03-11',
16+
endDate: '2026-06-08',
17+
disabled: false,
18+
...customProps,
19+
},
20+
global: {
21+
mocks: {
22+
translate: (key: string) => key,
23+
},
24+
stubs: {
25+
DatePicker: true,
26+
},
27+
},
28+
});
29+
}
30+
31+
it('does not emit rangeChange when syncing new prop values from the parent', async () => {
32+
const wrapper = mountPicker();
33+
34+
expect(wrapper.emitted('rangeChange')).toBeUndefined();
35+
36+
await wrapper.setProps({
37+
startDate: '2026-05-10',
38+
endDate: '2026-06-08',
39+
});
40+
41+
expect(wrapper.emitted('rangeChange')).toBeUndefined();
42+
});
43+
44+
it('emits rangeChange for direct user edits', async () => {
45+
const wrapper = mountPicker();
46+
47+
(wrapper.vm as unknown as { setStartRangeDateFromStr: (value: string) => void })
48+
.setStartRangeDateFromStr('2026-05-10');
49+
50+
expect(wrapper.emitted('rangeChange')?.[0]?.[0]).toEqual({
51+
start: '2026-05-10',
52+
end: '2026-06-08',
53+
});
54+
});
55+
});

plugins/CoreHome/vue/src/DateRangePicker/DateRangePicker.vue

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -129,16 +129,13 @@ export default defineComponent({
129129
watch: {
130130
startDate() {
131131
this.startDateText = this.startDate;
132-
this.setStartRangeDateFromStr(this.startDate);
132+
this.syncStartRangeDateFromProp(this.startDate);
133133
},
134134
endDate() {
135135
this.endDateText = this.endDate;
136-
this.setEndRangeDateFromStr(this.endDate);
136+
this.syncEndRangeDateFromProp(this.endDate);
137137
},
138138
},
139-
mounted() {
140-
this.rangeChanged(); // emit with initial range pair
141-
},
142139
methods: {
143140
setStartRangeDate(date: Date) {
144141
this.fromPickerSelectedDate = date;
@@ -176,7 +173,7 @@ export default defineComponent({
176173
end: this.endDate,
177174
});
178175
},
179-
setStartRangeDateFromStr(dateStr?: string) {
176+
syncStartRangeDateFromProp(dateStr?: string) {
180177
this.startDateInvalid = true;
181178
182179
let startDateParsed: Date|null = null;
@@ -191,11 +188,15 @@ export default defineComponent({
191188
if (startDateParsed) {
192189
this.fromPickerSelectedDate = startDateParsed;
193190
this.startDateInvalid = false;
194-
191+
}
192+
},
193+
setStartRangeDateFromStr(dateStr?: string) {
194+
this.syncStartRangeDateFromProp(dateStr);
195+
if (!this.startDateInvalid) {
195196
this.rangeChanged();
196197
}
197198
},
198-
setEndRangeDateFromStr(dateStr?: string) {
199+
syncEndRangeDateFromProp(dateStr?: string) {
199200
this.endDateInvalid = true;
200201
201202
let endDateParsed: Date|null = null;
@@ -210,7 +211,11 @@ export default defineComponent({
210211
if (endDateParsed) {
211212
this.toPickerSelectedDate = endDateParsed;
212213
this.endDateInvalid = false;
213-
214+
}
215+
},
216+
setEndRangeDateFromStr(dateStr?: string) {
217+
this.syncEndRangeDateFromProp(dateStr);
218+
if (!this.endDateInvalid) {
214219
this.rangeChanged();
215220
}
216221
},

plugins/CoreHome/vue/src/PeriodSelector/PeriodSelector.hashSync.spec.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,29 @@ describe('PeriodSelector hash sync', () => {
269269
(MatomoUrl as any).url.value = originalUrl;
270270
});
271271

272+
it('does not preserve rolling ownership for explicit dates that match a preset', () => {
273+
const vm: any = {
274+
periodsFiltered: ['day', 'week', 'month', 'year', 'range'],
275+
uiSelection: { type: 'preset', id: 'thisMonth' },
276+
selectedPeriod: 'month',
277+
committedPeriod: 'month',
278+
committedAnchorDate: new Date('2026-02-16'),
279+
appliedRangeStartDate: '2026-02-01',
280+
appliedRangeEndDate: '2026-02-16',
281+
setUiSelection(selection: { type: string; id: string }, source: string|null) {
282+
this.uiSelection = selection;
283+
this.lastInteractionSource = source;
284+
},
285+
clearPresetSelection() {
286+
this.pendingPresetSelection = null;
287+
},
288+
};
289+
290+
methods.applyUiSelectionFromHash.call(vm, 'month', '2026-02-16', null);
291+
292+
expect(vm.uiSelection).toEqual({ type: 'period', id: 'month' });
293+
});
294+
272295
it('re-syncs staged preset when only report context changes', () => {
273296
const originalUrl = (MatomoUrl as any).url.value;
274297
const originalInitTopControls = window.initTopControls;
@@ -302,6 +325,7 @@ describe('PeriodSelector hash sync', () => {
302325
id: 'last7days',
303326
period: 'range',
304327
date: '2026-02-12,2026-02-18',
328+
urlDate: 'last7',
305329
startDate: new Date('2026-02-12'),
306330
endDate: new Date('2026-02-18'),
307331
};

plugins/CoreHome/vue/src/PeriodSelector/PeriodSelector.mountedOwnership.spec.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ describe('CoreHome/PeriodSelector/PeriodSelector mounted ownership interactions'
148148
id: 'today',
149149
period: 'day',
150150
date: '2026-02-18',
151+
urlDate: 'today',
151152
selectedDate: new Date('2026-02-18'),
152153
startDate: new Date('2026-02-18'),
153154
endDate: new Date('2026-02-18'),
@@ -160,6 +161,7 @@ describe('CoreHome/PeriodSelector/PeriodSelector mounted ownership interactions'
160161
id: 'lastWeekMonSun',
161162
period: 'week',
162163
date: '2026-02-09',
164+
urlDate: 'lastweek',
163165
selectedDate: new Date('2026-02-09'),
164166
startDate: new Date('2026-02-09'),
165167
endDate: new Date('2026-02-15'),
@@ -177,6 +179,7 @@ describe('CoreHome/PeriodSelector/PeriodSelector mounted ownership interactions'
177179
id: 'last30days',
178180
period: 'range',
179181
date: '2026-01-20,2026-02-18',
182+
urlDate: 'last30',
180183
selectedDate: new Date('2026-02-18'),
181184
startDate: new Date('2026-01-20'),
182185
endDate: new Date('2026-02-18'),
@@ -200,6 +203,7 @@ describe('CoreHome/PeriodSelector/PeriodSelector mounted ownership interactions'
200203
id: 'last30days',
201204
period: 'range',
202205
date: '2026-01-20,2026-02-18',
206+
urlDate: 'last30',
203207
selectedDate: new Date('2026-02-18'),
204208
startDate: new Date('2026-01-20'),
205209
endDate: new Date('2026-02-18'),
@@ -225,6 +229,7 @@ describe('CoreHome/PeriodSelector/PeriodSelector mounted ownership interactions'
225229
id: 'last30days',
226230
period: 'range',
227231
date: '2026-01-20,2026-02-18',
232+
urlDate: 'last30',
228233
selectedDate: new Date('2026-02-18'),
229234
startDate: new Date('2026-01-20'),
230235
endDate: new Date('2026-02-18'),

plugins/CoreHome/vue/src/PeriodSelector/PeriodSelector.selection.spec.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,6 @@ describe('PeriodSelector', () => {
7373
appliedRangeEndDate: '2026-02-18',
7474
isRangeValid: false,
7575
commitSelectionToUrl: jest.fn(),
76-
getCurrentSelectionDateString: methods.getCurrentSelectionDateString,
7776
setUiSelection(selection: { type: string; id: string }, source: string|null) {
7877
this.uiSelection = selection;
7978
this.lastInteractionSource = source;
@@ -84,6 +83,7 @@ describe('PeriodSelector', () => {
8483
id: 'last7days',
8584
period: 'range',
8685
date: '2026-02-12,2026-02-18',
86+
urlDate: 'last7',
8787
selectedDate: new Date('2026-02-18'),
8888
startDate: new Date('2026-02-12'),
8989
endDate: new Date('2026-02-18'),
@@ -117,7 +117,6 @@ describe('PeriodSelector', () => {
117117
appliedRangeEndDate: '2026-02-18',
118118
isRangeValid: false,
119119
commitSelectionToUrl: jest.fn(),
120-
getCurrentSelectionDateString: methods.getCurrentSelectionDateString,
121120
setUiSelection(selection: { type: string; id: string }, source: string|null) {
122121
this.uiSelection = selection;
123122
this.lastInteractionSource = source;
@@ -128,6 +127,7 @@ describe('PeriodSelector', () => {
128127
id: 'yesterday',
129128
period: 'day',
130129
date: '2026-02-17',
130+
urlDate: 'yesterday',
131131
selectedDate: new Date('2026-02-17'),
132132
startDate: new Date('2026-02-17'),
133133
endDate: new Date('2026-02-17'),
@@ -161,6 +161,7 @@ describe('PeriodSelector', () => {
161161
id: 'last7days',
162162
period: 'range',
163163
date: '2026-02-12,2026-02-18',
164+
urlDate: 'last7',
164165
selectedDate: new Date('2026-02-18'),
165166
startDate: new Date('2026-02-12'),
166167
endDate: new Date('2026-02-18'),
@@ -184,6 +185,7 @@ describe('PeriodSelector', () => {
184185
id: 'last30days',
185186
period: 'range',
186187
date: '2026-01-20,2026-02-18',
188+
urlDate: 'last30',
187189
selectedDate: new Date('2026-02-18'),
188190
startDate: presetStart,
189191
endDate: presetEnd,
@@ -200,7 +202,7 @@ describe('PeriodSelector', () => {
200202
expect(vm.uiSelection).toEqual({ type: 'period', id: 'range' });
201203
expect(vm.appliedRangeStartDate).toBe('2026-01-20');
202204
expect(vm.appliedRangeEndDate).toBe('2026-02-18');
203-
expectCommitSelection(vm, '2026-01-20,2026-02-18', 'range');
205+
expectCommitSelection(vm, 'last30', 'range');
204206
});
205207

206208
it('applies concrete single-date values when a single preset is applied', () => {
@@ -210,6 +212,7 @@ describe('PeriodSelector', () => {
210212
id: 'thisWeekMonToday',
211213
period: 'week',
212214
date: '2026-02-16',
215+
urlDate: 'today',
213216
selectedDate: new Date('2026-02-16'),
214217
startDate: new Date('2026-02-16'),
215218
endDate: new Date('2026-02-18'),
@@ -224,7 +227,7 @@ describe('PeriodSelector', () => {
224227
expect(vm.committedPeriod).toBe('week');
225228
expect(vm.committedAnchorDate).toEqual(new Date('2026-02-16'));
226229
expect(vm.uiSelection).toEqual({ type: 'period', id: 'week' });
227-
expectCommitSelection(vm, '2026-02-16', 'week');
230+
expectCommitSelection(vm, 'today', 'week');
228231
});
229232

230233
it('applies the concrete staged range even when the visible range is clamped', () => {
@@ -234,6 +237,7 @@ describe('PeriodSelector', () => {
234237
id: 'last7days',
235238
period: 'range',
236239
date: '2026-02-12,2026-02-18',
240+
urlDate: 'last7',
237241
selectedDate: new Date('2026-02-18'),
238242
startDate: new Date('2026-02-12'),
239243
endDate: new Date('2026-02-18'),
@@ -246,7 +250,7 @@ describe('PeriodSelector', () => {
246250
callOnApplyClicked(vm);
247251

248252
expect(vm.committedPeriod).toBe('range');
249-
expectCommitSelection(vm, '2026-02-12,2026-02-18', 'range');
253+
expectCommitSelection(vm, 'last7', 'range');
250254
expect(vm.commitSelectionToUrl).not.toHaveBeenCalledWith('2026-02-14,2026-02-15', 'range');
251255
});
252256

@@ -276,6 +280,7 @@ describe('PeriodSelector', () => {
276280
id: 'last7days',
277281
period: 'range',
278282
date: '2026-02-12,2026-02-18',
283+
urlDate: 'last7',
279284
selectedDate: new Date('2026-02-18'),
280285
startDate: new Date('2026-02-12'),
281286
endDate: new Date('2026-02-18'),
@@ -629,7 +634,6 @@ describe('PeriodSelector', () => {
629634
committedAnchorDate: new Date('2026-06-01'),
630635
appliedRangeStartDate: '2026-06-01',
631636
appliedRangeEndDate: '2026-06-01',
632-
getCurrentSelectionDateString: methods.getCurrentSelectionDateString,
633637
};
634638

635639
try {
@@ -683,6 +687,7 @@ describe('PeriodSelector', () => {
683687
id: 'last30days',
684688
period: 'range',
685689
date: '2026-01-20,2026-02-18',
690+
urlDate: 'last30',
686691
selectedDate: new Date('2026-02-18'),
687692
startDate: new Date('2026-01-20'),
688693
endDate: new Date('2026-02-18'),
@@ -753,6 +758,7 @@ describe('PeriodSelector', () => {
753758
id: 'last30days',
754759
period: 'range',
755760
date: '2026-01-20,2026-02-18',
761+
urlDate: 'last30',
756762
selectedDate: new Date('2026-02-18'),
757763
startDate: new Date('2026-01-20'),
758764
endDate: new Date('2026-02-18'),
@@ -774,6 +780,7 @@ describe('PeriodSelector', () => {
774780
id: 'last30days',
775781
period: 'range',
776782
date: '2026-01-20,2026-02-18',
783+
urlDate: 'last30',
777784
selectedDate: new Date('2026-02-18'),
778785
startDate: new Date('2026-01-20'),
779786
endDate: new Date('2026-02-18'),

0 commit comments

Comments
 (0)