diff --git a/libs/ngrx-entity-relationship/e2e/custom-keys-for-entities.spec.ts b/libs/ngrx-entity-relationship/e2e/custom-keys-for-entities.spec.ts new file mode 100644 index 000000000..69169e457 --- /dev/null +++ b/libs/ngrx-entity-relationship/e2e/custom-keys-for-entities.spec.ts @@ -0,0 +1,85 @@ +import { + ngrxEntityRelationshipReducer, + reduceGraph, + relatedEntitySelector, + rootEntitySelector, + stateKeys, +} from 'ngrx-entity-relationship'; + +describe('custom-keys-for-entities', () => { + interface Item { + id: string; + name: string; + parentId?: string; + parent?: Item; + } + + const state: { + records: { + ids: Array; + byId: Record; + }; + } = { + records: { + ids: [], + byId: {}, + }, + }; + + const selectItemsState = (s: typeof state) => s.records; + const featureSelector = stateKeys(selectItemsState, 'byId'); + + const sItem = rootEntitySelector(featureSelector); + const sItemParent = relatedEntitySelector(featureSelector, 'parentId', 'parent'); + + const selector = sItem(sItemParent()); + + it('handles custom values correctly', () => { + const action = reduceGraph({ + data: [ + { + id: 'i1', + name: '1', + parentId: 'i2', + parent: { + id: 'i2', + name: '2', + }, + }, + ], + selector, + }); + + const reducer = ngrxEntityRelationshipReducer(((s: typeof state) => s) as any); + const testingState = reducer(state, action); + + // data has been reduced. + expect(testingState).toEqual({ + records: { + ids: ['i1', 'i2'], + byId: { + i1: { + id: 'i1', + name: '1', + parentId: 'i2', + }, + i2: { + id: 'i2', + name: '2', + }, + }, + }, + }); + + // data has been reached. + expect(selector(testingState, 'i1')).toEqual({ + id: 'i1', + name: '1', + parentId: 'i2', + parent: { + id: 'i2', + name: '2', + }, + }); + }); +}); diff --git a/libs/ngrx-entity-relationship/e2e/custom-keys-no-id.spec.ts b/libs/ngrx-entity-relationship/e2e/custom-keys-no-id.spec.ts new file mode 100644 index 000000000..550c8abdc --- /dev/null +++ b/libs/ngrx-entity-relationship/e2e/custom-keys-no-id.spec.ts @@ -0,0 +1,82 @@ +import { + ngrxEntityRelationshipReducer, + reduceGraph, + relatedEntitySelector, + rootEntitySelector, + stateKeys, +} from 'ngrx-entity-relationship'; + +describe('custom-keys-no-id', () => { + interface Item { + id: string; + name: string; + parentId?: string; + parent?: Item; + } + + const state: { + records: { + byId: Record; + }; + } = { + records: { + byId: {}, + }, + }; + + const selectItemsState = (s: typeof state) => s.records; + const featureSelector = stateKeys(selectItemsState, 'byId'); + + const sItem = rootEntitySelector(featureSelector); + const sItemParent = relatedEntitySelector(featureSelector, 'parentId', 'parent'); + + const selector = sItem(sItemParent()); + + it('handles custom values correctly', () => { + const action = reduceGraph({ + data: [ + { + id: 'i1', + name: '1', + parentId: 'i2', + parent: { + id: 'i2', + name: '2', + }, + }, + ], + selector, + }); + + const reducer = ngrxEntityRelationshipReducer(((s: typeof state) => s) as any); + const testingState = reducer(state, action); + + // data has been reduced. + expect(testingState).toEqual({ + records: { + byId: { + i1: { + id: 'i1', + name: '1', + parentId: 'i2', + }, + i2: { + id: 'i2', + name: '2', + }, + }, + }, + }); + + // data has been reached. + expect(selector(testingState, 'i1')).toEqual({ + id: 'i1', + name: '1', + parentId: 'i2', + parent: { + id: 'i2', + name: '2', + }, + }); + }); +}); diff --git a/libs/ngrx-entity-relationship/src/lib/stateKeys.ts b/libs/ngrx-entity-relationship/src/lib/stateKeys.ts index 57e118db0..b88d7601e 100644 --- a/libs/ngrx-entity-relationship/src/lib/stateKeys.ts +++ b/libs/ngrx-entity-relationship/src/lib/stateKeys.ts @@ -1,5 +1,22 @@ import {ENTITY_STATE, ENTITY_STATE_CUSTOM, STORE_SELECTOR} from './types'; +export function stateKeys< + SELECTOR extends STORE_SELECTOR, + STORE extends SELECTOR extends STORE_SELECTOR ? U : never, + SLICE extends SELECTOR extends STORE_SELECTOR ? U : never, + E_KEY extends keyof SLICE, + ENTITY extends SLICE extends ENTITY_STATE_CUSTOM ? U : never +>(selector: SELECTOR, entitiesKey: E_KEY): STORE_SELECTOR>; + +export function stateKeys< + SELECTOR extends STORE_SELECTOR, + STORE extends SELECTOR extends STORE_SELECTOR ? U : never, + SLICE extends SELECTOR extends STORE_SELECTOR ? U : never, + E_KEY extends keyof SLICE, + I_KEY extends keyof SLICE, + ENTITY extends SLICE extends ENTITY_STATE_CUSTOM ? U : never +>(selector: SELECTOR, entitiesKey: E_KEY, idsKey: I_KEY): STORE_SELECTOR>; + export function stateKeys< SELECTOR extends STORE_SELECTOR, STORE extends SELECTOR extends STORE_SELECTOR ? U : never, @@ -7,11 +24,11 @@ export function stateKeys< E_KEY extends keyof SLICE, I_KEY extends keyof SLICE, ENTITY extends SLICE extends ENTITY_STATE_CUSTOM ? U : never ->(selector: SELECTOR, entitiesKey: E_KEY, idsKey: I_KEY): STORE_SELECTOR> { +>(selector: SELECTOR, entitiesKey: E_KEY, idsKey?: I_KEY): STORE_SELECTOR> { const callback: STORE_SELECTOR> = (s: STORE): ENTITY_STATE => { const slice = selector(s); return { - ids: slice[idsKey], + ids: idsKey ? slice[idsKey] : slice.ids, entities: slice[entitiesKey], }; }; diff --git a/libs/ngrx-entity-relationship/src/lib/types.ts b/libs/ngrx-entity-relationship/src/lib/types.ts index 701fdb673..3e61eecdb 100644 --- a/libs/ngrx-entity-relationship/src/lib/types.ts +++ b/libs/ngrx-entity-relationship/src/lib/types.ts @@ -30,11 +30,11 @@ export type ENTITY_STATE_CUSTOM; + [key in IK]?: Array; }; export interface ENTITY_STATE { - ids: Array; + ids?: Array; entities: { [id in ID_TYPES]?: E; };