Skip to content

Commit

Permalink
feat(signals): allow disabling clear selection on filter an sort
Browse files Browse the repository at this point in the history
Add to withEntitiesSingleSelection and withEntitiesMultiSelection, flags cleanOnFilter and
clearOnRemoteSort to allow disabling clear  onfi filter an remotete sort

Fix #97
  • Loading branch information
Gabriel Guerrero authored and gabrielguerrero committed Jun 7, 2024
1 parent 3dc8a9e commit 11b4f7d
Show file tree
Hide file tree
Showing 4 changed files with 172 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ describe('withEntitiesLocalFilter', () => {
});
}));

it(' should resetPage to and selection when filter is executed', fakeAsync(() => {
it(' should reset page to and selection when filter is executed', fakeAsync(() => {
TestBed.runInInjectionContext(() => {
const Store = signalStore(
withEntities({
Expand Down Expand Up @@ -206,6 +206,56 @@ describe('withEntitiesLocalFilter', () => {
});
}));

it(' should not reset selection when filter is executed if configured that way', fakeAsync(() => {
TestBed.runInInjectionContext(() => {
const Store = signalStore(
withEntities({
entity,
}),
withEntitiesSingleSelection({
entity,
clearOnFilter: false,
clearOnRemoteSort: false,
}),
withEntitiesMultiSelection({
entity,
clearOnFilter: false,
clearOnRemoteSort: false,
}),
withEntitiesLocalFilter({
entity,
defaultFilter: { search: '', foo: 'bar' },
filterFn: (entity, filter) =>
!filter?.search ||
entity?.name.toLowerCase().includes(filter?.search.toLowerCase()),
}),
);
const store = new Store();
patchState(store, setAllEntities(mockProducts));
store.selectEntity({ id: mockProducts[0].id });
store.selectEntities({ ids: [mockProducts[2].id, mockProducts[3].id] });
expect(store.entitySelected()).toEqual(mockProducts[0]);
expect(store.entitiesSelected?.()).toEqual([
mockProducts[2],
mockProducts[3],
]);

store.filterEntities({
filter: { search: 'zero' },
patch: true,
});
tick(400);
// check selection was not reset
expect(store.entitySelected()).toEqual(mockProducts[0]);
expect(store.entitiesSelected?.()).toEqual([
mockProducts[2],
mockProducts[3],
]);
// check filter
expect(store.entities().length).toEqual(2);
});
}));

it(' should rename props when collection param is set', fakeAsync(() => {
TestBed.runInInjectionContext(() => {
const Store = signalStore(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ import { getEntitiesMultiSelectionKeys } from './with-entities-multi-selection.u
* @param config
* @param config.entity - the entity type
* @param config.collection - the collection name
* @param config.clearOnFilter - Clear the selected entity when the filter changes (default: true)
* @param config.clearOnRemoteSort - Clear the selected entity when the remote sort changes (default: true)
*
* @example
* const entity = type<Product>();
Expand All @@ -67,6 +69,8 @@ export function withEntitiesMultiSelection<
Entity extends { id: string | number },
>(config: {
entity: Entity;
clearOnFilter?: boolean;
clearOnRemoteSort?: boolean;
}): SignalStoreFeature<
{
state: EntityState<Entity>;
Expand All @@ -89,6 +93,8 @@ export function withEntitiesMultiSelection<
* @param config
* @param config.entity - the entity type
* @param config.collection - the collection name
* @param config.clearOnFilter - Clear the selected entity when the filter changes (default: true)
* @param config.clearOnRemoteSort - Clear the selected entity when the remote sort changes (default: true)
*
* @example
* const entity = type<Product>();
Expand All @@ -115,7 +121,9 @@ export function withEntitiesMultiSelection<
Collection extends string,
>(config: {
entity: Entity;
collection?: Collection;
collection: Collection;
clearOnFilter?: boolean;
clearOnRemoteSort?: boolean;
}): SignalStoreFeature<
// TODO: the problem seems be with the state pro, when set to empty
// it works but is it has a namedstate it doesnt
Expand All @@ -137,26 +145,8 @@ export function withEntitiesMultiSelection<
>(config: {
entity: Entity;
collection?: Collection;
}): SignalStoreFeature<
// TODO: the problem seems be with the state pro, when set to empty
// it works but is it has a namedstate it doesnt
{
state: NamedEntityState<Entity, any>;
signals: NamedEntitySignals<Entity, Collection>;
methods: {};
},
{
state: NamedEntitiesMultiSelectionState<Collection>;
signals: NamedEntitiesMultiSelectionComputed<Entity, Collection>;
methods: NamedEntitiesMultiSelectionMethods<Collection>;
}
>;
export function withEntitiesMultiSelection<
Entity extends { id: string | number },
Collection extends string,
>(config: {
entity: Entity;
collection?: Collection;
clearOnFilter?: boolean;
clearOnRemoteSort?: boolean;
}): SignalStoreFeature<any, any> {
const { entityMapKey, idsKey } = getWithEntitiesKeys(config);
const {
Expand Down Expand Up @@ -212,11 +202,24 @@ export function withEntitiesMultiSelection<
};
}),
withEventHandler((state) => {
return [
onEvent(entitiesFilterChanged, entitiesRemoteSortChanged, () =>
clearEntitiesSelection(state, selectedIdsMapKey),
),
];
const clearOnFilter = config?.clearOnFilter ?? true;
const clearOnRemoteSort = config?.clearOnRemoteSort ?? true;
const events = [];
if (clearOnFilter) {
events.push(
onEvent(entitiesFilterChanged, () => {
clearEntitiesSelection(state, selectedIdsMapKey);
}),
);
}
if (clearOnRemoteSort) {
events.push(
onEvent(entitiesRemoteSortChanged, () => {
clearEntitiesSelection(state, selectedIdsMapKey);
}),
);
}
return events;
}),
withMethods((state: Record<string, Signal<unknown>>) => {
const selectedIdsMap = state[selectedIdsMapKey] as Signal<
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ import { getEntitiesSingleSelectionKeys } from './with-entities-single-selection
* @param config
* @param config.collection - The collection name
* @param config.entity - The entity type
* @param config.clearOnFilter - Clear the selected entity when the filter changes (default: true)
* @param config.clearOnRemoteSort - Clear the selected entity when the remote sort changes (default: true)
*
* @example
* const entity = type<Product>();
Expand Down Expand Up @@ -69,6 +71,8 @@ export function withEntitiesSingleSelection<
Entity extends { id: string | number },
>(config?: {
entity: Entity;
clearOnFilter?: boolean;
clearOnRemoteSort?: boolean;
}): SignalStoreFeature<
{
state: EntityState<Entity>;
Expand All @@ -88,6 +92,8 @@ export function withEntitiesSingleSelection<
* @param config
* @param config.collection - The collection name
* @param config.entity - The entity type
* @param config.clearOnFilter - Clear the selected entity when the filter changes (default: true)
* @param config.clearOnRemoteSort - Clear the selected entity when the remote sort changes (default: true)
*
* @example
* const entity = type<Product>();
Expand Down Expand Up @@ -119,6 +125,8 @@ export function withEntitiesSingleSelection<
>(config?: {
entity: Entity;
collection?: Collection;
clearOnFilter?: boolean;
clearOnRemoteSort?: boolean;
}): SignalStoreFeature<
// TODO: the problem seems be with the state pro, when set to empty
// it works but is it has a namedstate it doesnt
Expand All @@ -139,6 +147,8 @@ export function withEntitiesSingleSelection<
>(config?: {
entity: Entity;
collection?: Collection;
clearOnFilter?: boolean;
clearOnRemoteSort?: boolean;
}): SignalStoreFeature<any, any> {
const { entityMapKey } = getWithEntitiesKeys(config);
const {
Expand Down Expand Up @@ -190,12 +200,26 @@ export function withEntitiesSingleSelection<
};
}),
withEventHandler((state) => {
return [
onEvent(entitiesFilterChanged, entitiesRemoteSortChanged, () => {
const deselectEntity = state[deselectEntityKey] as () => void;
deselectEntity();
}),
];
const clearOnFilter = config?.clearOnFilter ?? true;
const clearOnRemoteSort = config?.clearOnRemoteSort ?? true;
const events = [];
if (clearOnFilter) {
events.push(
onEvent(entitiesFilterChanged, () => {
const deselectEntity = state[deselectEntityKey] as () => void;
deselectEntity();
}),
);
}
if (clearOnRemoteSort) {
events.push(
onEvent(entitiesRemoteSortChanged, () => {
const deselectEntity = state[deselectEntityKey] as () => void;
deselectEntity();
}),
);
}
return events;
}),
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ describe('withEntitiesRemoteSort', () => {
});
}));

it(' should resetPage to and selection when sort is executed', fakeAsync(() => {
it(' should reset page to and selection when sort is executed', fakeAsync(() => {
TestBed.runInInjectionContext(() => {
const Store = signalStore(
withEntities({
Expand Down Expand Up @@ -193,4 +193,65 @@ describe('withEntitiesRemoteSort', () => {
expect(store.entities().length).toEqual(mockProducts.length);
});
}));

it('should not reset selection when sort is executed if configured that way', fakeAsync(() => {
TestBed.runInInjectionContext(() => {
const Store = signalStore(
withEntities({
entity,
}),
withCallStatus({ initialValue: 'loading' }),
withEntitiesSingleSelection({
entity,
clearOnFilter: false,
clearOnRemoteSort: false,
}),
withEntitiesMultiSelection({
entity,
clearOnFilter: false,
clearOnRemoteSort: false,
}),
withEntitiesRemoteSort({
entity,
defaultSort: { field: 'name', direction: 'asc' },
}),
withEntitiesLoadingCall({
fetchEntities: ({ entitiesSort }) => {
let result = [...mockProducts];
if (entitiesSort()?.field) {
result = sortData(result, {
field: entitiesSort()?.field as any,
direction: entitiesSort().direction,
});
}

return Promise.resolve({ entities: result, total: result.length });
},
}),
);
const store = new Store();
TestBed.flushEffects();
tick(400);
store.selectEntity({ id: mockProducts[0].id });
store.selectEntities({ ids: [mockProducts[2].id, mockProducts[3].id] });
expect(store.entitySelected()).toEqual(mockProducts[0]);
expect(store.entitiesSelected?.()).toEqual([
mockProducts[2],
mockProducts[3],
]);

store.sortEntities({
sort: { field: 'price', direction: 'desc' },
});
tick(400);
// check selection is not reset
expect(store.entitySelected()).toEqual(mockProducts[0]);
expect(store.entitiesSelected?.()).toEqual([
mockProducts[2],
mockProducts[3],
]);
// check filter
expect(store.entities().length).toEqual(mockProducts.length);
});
}));
});

0 comments on commit 11b4f7d

Please sign in to comment.