This commit is contained in:
Andras Schmelczer 2026-05-12 22:13:07 +01:00
parent 11711c57e6
commit 81a16f543c
21 changed files with 29072 additions and 1913 deletions

View file

@ -9,6 +9,7 @@ import { createElectionVoteShareFilterKey } from './election-filter';
import { createEthnicityFilterKey } from './ethnicity-filter';
import {
POI_COUNT_2KM_FILTER_NAME,
TRANSPORT_DISTANCE_FILTER_NAME,
createPoiDistanceFilterKey,
createPoiFilterKey,
} from './poi-distance-filter';
@ -225,13 +226,13 @@ describe('url-state', () => {
it('round-trips repeated amenity distance filters with dedicated URL params', () => {
const park = createPoiDistanceFilterKey('Distance to nearest park (km)', 3);
const tesco = createPoiDistanceFilterKey('Distance to nearest Tesco (km)', 4);
const grocery = createPoiDistanceFilterKey('Distance to nearest grocery store (km)', 4);
const params = stateToParams(
null,
{
[park]: [0, 0.4],
[tesco]: [0, 1.5],
[grocery]: [0, 1.5],
},
[],
new Set(),
@ -240,7 +241,7 @@ describe('url-state', () => {
expect(params.getAll('amenityDistance')).toEqual([
'Distance%20to%20nearest%20park%20(km):0:0.4',
'Distance%20to%20nearest%20Tesco%20(km):0:1.5',
'Distance%20to%20nearest%20grocery%20store%20(km):0:1.5',
]);
expect(params.getAll('filter')).toEqual([]);
@ -249,7 +250,60 @@ describe('url-state', () => {
expect(state.filters).toEqual({
[createPoiDistanceFilterKey('Distance to nearest park (km)', 0)]: [0, 0.4],
[createPoiDistanceFilterKey('Distance to nearest Tesco (km)', 1)]: [0, 1.5],
[createPoiDistanceFilterKey('Distance to nearest grocery store (km)', 1)]: [0, 1.5],
});
});
it('round-trips transport distance filters with dedicated URL params', () => {
const busStop = createPoiFilterKey(
TRANSPORT_DISTANCE_FILTER_NAME,
'Distance to nearest amenity (Bus stop) (km)',
3
);
const params = stateToParams(
null,
{
[busStop]: [0, 0.3],
},
[],
new Set(),
'area'
);
expect(params.getAll('transportDistance')).toEqual([
'Distance%20to%20nearest%20amenity%20(Bus%20stop)%20(km):0:0.3',
]);
expect(params.getAll('amenityDistance')).toEqual([]);
expect(params.getAll('filter')).toEqual([]);
window.history.replaceState({}, '', `/?${params.toString()}`);
const state = parseUrlState();
expect(state.filters).toEqual({
[createPoiFilterKey(
TRANSPORT_DISTANCE_FILTER_NAME,
'Distance to nearest amenity (Bus stop) (km)',
0
)]: [0, 0.3],
});
});
it('migrates legacy transport distance amenity params into transport filters', () => {
window.history.replaceState(
{},
'',
'/?amenityDistance=Distance%20to%20nearest%20amenity%20(Bus%20stop)%20(km):0:0.3'
);
const state = parseUrlState();
expect(state.filters).toEqual({
[createPoiFilterKey(
TRANSPORT_DISTANCE_FILTER_NAME,
'Distance to nearest amenity (Bus stop) (km)',
0
)]: [0, 0.3],
});
});
@ -279,8 +333,9 @@ describe('url-state', () => {
const state = parseUrlState();
expect(state.filters).toEqual({
[createPoiFilterKey(POI_COUNT_2KM_FILTER_NAME, 'Number of amenities (Cafe) within 2km', 0)]:
[2, 8],
[createPoiFilterKey(POI_COUNT_2KM_FILTER_NAME, 'Number of amenities (Cafe) within 2km', 0)]: [
2, 8,
],
});
});

View file

@ -38,13 +38,12 @@ import {
} from './ethnicity-filter';
import {
POI_DISTANCE_FILTER_NAME,
TRANSPORT_DISTANCE_FILTER_NAME,
POI_COUNT_2KM_FILTER_NAME,
POI_COUNT_5KM_FILTER_NAME,
createPoiFilterKey,
createPoiDistanceFilterKey,
getPoiDistanceFeatureName,
getPoiFilterName,
isPoiDistanceFeatureName,
isPoiDistanceFilterName,
type PoiFilterName,
} from './poi-distance-filter';
@ -68,6 +67,7 @@ function parseFilters(params: URLSearchParams): FeatureFilters {
const voteShareParams = params.getAll('voteShare');
const ethnicityParams = params.getAll('ethnicity');
const amenityDistanceParams = params.getAll('amenityDistance');
const transportDistanceParams = params.getAll('transportDistance');
const amenityCount2KmParams = params.getAll('amenityCount2km');
const amenityCount5KmParams = params.getAll('amenityCount5km');
if (
@ -77,6 +77,7 @@ function parseFilters(params: URLSearchParams): FeatureFilters {
voteShareParams.length === 0 &&
ethnicityParams.length === 0 &&
amenityDistanceParams.length === 0 &&
transportDistanceParams.length === 0 &&
amenityCount2KmParams.length === 0 &&
amenityCount5KmParams.length === 0
) {
@ -159,44 +160,51 @@ function parseFilters(params: URLSearchParams): FeatureFilters {
filters[createEthnicityFilterKey(featureName, index)] = [min, max];
});
amenityDistanceParams.forEach((entry, index) => {
const parts = entry.split(':');
if (parts.length < 3) return;
const featureName = decodeURIComponent(parts.slice(0, -2).join(':'));
const min = Number(parts[parts.length - 2]);
const max = Number(parts[parts.length - 1]);
if (!isPoiDistanceFeatureName(featureName) || isNaN(min) || isNaN(max)) {
return;
}
filters[createPoiDistanceFilterKey(featureName, index)] = [min, max];
});
const parsePoiCountParams = (
entries: string[],
filterName: PoiFilterName,
startIndex: number
) => {
const parsePoiParams = (entries: string[], filterName: PoiFilterName, startIndex: number) => {
entries.forEach((entry, index) => {
const parts = entry.split(':');
if (parts.length < 3) return;
const featureName = decodeURIComponent(parts.slice(0, -2).join(':'));
const min = Number(parts[parts.length - 2]);
const max = Number(parts[parts.length - 1]);
if (getPoiFilterName(featureName) !== filterName || isNaN(min) || isNaN(max)) {
const targetFilterName = getPoiFilterName(featureName);
const canMigrateTransportDistance =
filterName === POI_DISTANCE_FILTER_NAME &&
targetFilterName === TRANSPORT_DISTANCE_FILTER_NAME;
if (
!targetFilterName ||
(targetFilterName !== filterName && !canMigrateTransportDistance) ||
isNaN(min) ||
isNaN(max)
) {
return;
}
filters[createPoiFilterKey(filterName, featureName, startIndex + index)] = [min, max];
filters[createPoiFilterKey(targetFilterName, featureName, startIndex + index)] = [min, max];
});
};
const parsePoiCountParams = (
entries: string[],
filterName: PoiFilterName,
startIndex: number
) => {
parsePoiParams(entries, filterName, startIndex);
};
parsePoiParams(amenityDistanceParams, POI_DISTANCE_FILTER_NAME, 0);
parsePoiParams(
transportDistanceParams,
TRANSPORT_DISTANCE_FILTER_NAME,
amenityDistanceParams.length
);
parsePoiCountParams(
amenityCount2KmParams,
POI_COUNT_2KM_FILTER_NAME,
amenityDistanceParams.length
amenityDistanceParams.length + transportDistanceParams.length
);
parsePoiCountParams(
amenityCount5KmParams,
POI_COUNT_5KM_FILTER_NAME,
amenityDistanceParams.length + amenityCount2KmParams.length
amenityDistanceParams.length + transportDistanceParams.length + amenityCount2KmParams.length
);
return filters;
@ -349,11 +357,10 @@ export function stateToParams(
? 'amenityCount2km'
: filterName === POI_COUNT_5KM_FILTER_NAME
? 'amenityCount5km'
: 'amenityDistance';
params.append(
paramName,
`${encodeURIComponent(amenityDistanceFeatureName)}:${min}:${max}`
);
: filterName === TRANSPORT_DISTANCE_FILTER_NAME
? 'transportDistance'
: 'amenityDistance';
params.append(paramName, `${encodeURIComponent(amenityDistanceFeatureName)}:${min}:${max}`);
continue;
}
@ -410,6 +417,7 @@ export function summarizeParams(queryString: string): string {
const voteShareParams = params.getAll('voteShare');
const ethnicityParams = params.getAll('ethnicity');
const amenityDistanceParams = params.getAll('amenityDistance');
const transportDistanceParams = params.getAll('transportDistance');
const amenityCount2KmParams = params.getAll('amenityCount2km');
const amenityCount5KmParams = params.getAll('amenityCount5km');
if (
@ -419,6 +427,7 @@ export function summarizeParams(queryString: string): string {
voteShareParams.length > 0 ||
ethnicityParams.length > 0 ||
amenityDistanceParams.length > 0 ||
transportDistanceParams.length > 0 ||
amenityCount2KmParams.length > 0 ||
amenityCount5KmParams.length > 0
) {
@ -429,7 +438,8 @@ export function summarizeParams(queryString: string): string {
if (isSpecificCrimeFeatureName(name)) return SPECIFIC_CRIMES_FILTER_NAME;
if (isElectionVoteShareFeatureName(name)) return ELECTION_VOTE_SHARE_FILTER_NAME;
if (isEthnicityFeatureName(name)) return ETHNICITIES_FILTER_NAME;
if (isPoiDistanceFeatureName(name)) return POI_DISTANCE_FILTER_NAME;
const poiFilterName = getPoiFilterName(name);
if (poiFilterName) return poiFilterName;
return name;
})
.filter((n) => n);
@ -446,6 +456,9 @@ export function summarizeParams(queryString: string): string {
for (let i = 0; i < amenityDistanceParams.length; i++) {
filterNames.push(POI_DISTANCE_FILTER_NAME);
}
for (let i = 0; i < transportDistanceParams.length; i++) {
filterNames.push(TRANSPORT_DISTANCE_FILTER_NAME);
}
for (let i = 0; i < amenityCount2KmParams.length; i++) {
filterNames.push(POI_COUNT_2KM_FILTER_NAME);
}