Skip to content

Commit

Permalink
fix: withEntitiesSyncToRouteQueryParams should requires to have sort,…
Browse files Browse the repository at this point in the history
…filter and pagination

A bug in withEntitiesSyncToRouteQueryParams, was making it only work if the store have withEntities
for all 3 sort, filter and pagination, this fix allows to have any one or two

fix #140
  • Loading branch information
Gabriel Guerrero authored and gabrielguerrero committed Sep 23, 2024
1 parent 8da3833 commit b79659c
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -112,13 +112,15 @@ export function getQueryMapperForEntitiesFilter<Filter>(config?: {
const filter = store[filterKey] as Signal<
EntitiesFilterState<any>['entitiesFilter']
>;
return computed(() =>
config?.filterMapper
? (config?.filterMapper.filterToQueryParams(filter()) as any)
: {
filter: JSON.stringify({ ...filter() }),
},
);
return filter
? computed(() =>
config?.filterMapper
? (config?.filterMapper.filterToQueryParams(filter()) as any)
: {
filter: JSON.stringify({ ...filter() }),
},
)
: null;
},
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,11 @@ export function getQueryMapperForEntitiesPagination(config?: {
const pagination = store[paginationKey] as Signal<
EntitiesPaginationLocalState['entitiesPagination']
>;
return computed(() => ({
page: (pagination().currentPage + 1).toString(),
}));
return pagination
? computed(() => ({
page: (pagination().currentPage + 1).toString(),
}))
: null;
},
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,12 @@ export function getQueryMapperForEntitiesSort(config?: {
const sort = store[sortKey] as Signal<
EntitiesSortState<any>['entitiesSort']
>;
return computed(() => ({
sortBy: sort().field as string,
sortDirection: sort().direction,
}));
return sort
? computed(() => ({
sortBy: sort().field as string,
sortDirection: sort().direction,
}))
: null;
},
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,47 @@ describe('withEntitiesSyncToRouteQueryParams', () => {
});
}));

it('filter store and url when there is no sort or pagination', fakeAsync(() => {
const Store = signalStore(
withEntities({ entity }),
withCallStatus({ initialValue: 'loading' }),
withEntitiesLocalFilter({
entity,
defaultFilter: { search: '', foo: 'bar' },
filterFn: (entity, filter) =>
!filter?.search ||
entity?.name.toLowerCase().includes(filter?.search.toLowerCase()),
}),
withEntitiesLoadingCall({
fetchEntities: ({}) => {
let result = [...mockProducts.slice(0, 40)];
const total = result.length;
const response = { entities: result, total };
return of(response);
},
}),
withEntitiesSyncToRouteQueryParams({ entity }),
);
const { store, router } = init({
Store,
queryParams: { filter: JSON.stringify({ search: 'foo', foo: 'bar' }) },
});
expect(store.entitiesFilter()).toEqual({ search: 'foo', foo: 'bar' });

store.filterEntities({
filter: { search: 'foo3', foo: 'bar4' },
forceLoad: true,
});
tick(400);
expect(router.navigate).toBeCalledWith([], {
relativeTo: expect.anything(),
queryParams: expect.objectContaining({
filter: JSON.stringify({ search: 'foo3', foo: 'bar4' }),
}),
queryParamsHandling: 'merge',
});
}));

it('filter url query params should update store, with custom filterMapper', () => {
const Store = signalStore(
localStoreFeature(),
Expand Down Expand Up @@ -358,6 +399,46 @@ describe('withEntitiesSyncToRouteQueryParams', () => {
queryParamsHandling: 'merge',
});
}));

it('changes on entities sort should store and url if no filter or pagination present', fakeAsync(() => {
const Store = signalStore(
withEntities({ entity }),
withCallStatus({ initialValue: 'loading' }),
withEntitiesLocalSort({
entity,
defaultSort: { field: 'name', direction: 'asc' },
}),
withEntitiesLoadingCall({
fetchEntities: ({}) => {
let result = [...mockProducts.slice(0, 40)];
const total = result.length;
const response = { entities: result, total };
return of(response);
},
}),
withEntitiesSyncToRouteQueryParams({ entity }),
);
const { store, router } = init({
Store,
queryParams: { sortBy: 'description', sortDirection: 'desc' },
});
expect(store.entitiesSort()).toEqual({
field: 'description',
direction: 'desc',
});
store.sortEntities({
sort: { field: 'name', direction: 'asc' },
});
tick(400);
expect(router.navigate).toBeCalledWith([], {
relativeTo: expect.anything(),
queryParams: expect.objectContaining({
sortBy: 'name',
sortDirection: 'asc',
}),
queryParamsHandling: 'merge',
});
}));
});

describe('entities single selection', () => {
Expand Down Expand Up @@ -474,6 +555,47 @@ describe('withEntitiesSyncToRouteQueryParams', () => {
queryParamsHandling: 'merge',
});
}));

it('changes on entities page should sync to store and url when there is no filter or sort', fakeAsync(() => {
const load = new Subject<boolean>();
const Store = signalStore(
withEntities({ entity }),
withCallStatus({ initialValue: 'loading' }),
withEntitiesLocalPagination({ entity, pageSize: 10 }),
withEntitiesLoadingCall({
fetchEntities: ({}) => {
let result = [...mockProducts.slice(0, 40)];
const total = result.length;
const response = { entities: result, total };
return load
? load.pipe(
filter(Boolean),
map(() => response),
)
: of(response);
},
}),
withEntitiesSyncToRouteQueryParams({ entity }),
);
const { store, router } = init({
Store,
queryParams: { page: '2' },
});
TestBed.flushEffects();
load.next(true);
tick(400);
expect(store.entitiesPagination().currentPage).toEqual(1);

store.loadEntitiesPage({ pageIndex: 2 });
tick(400);
expect(router.navigate).toBeCalledWith([], {
relativeTo: expect.anything(),
queryParams: expect.objectContaining({
page: '3',
}),
queryParamsHandling: 'merge',
});
}));
});

it('multiple url and state changes should sync correctly', fakeAsync(() => {
Expand Down

0 comments on commit b79659c

Please sign in to comment.