@memberjunction/ng-list-management 5.42.0 → 5.44.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. package/dist/lib/components/list-delta-confirm/list-delta-confirm.component.js +32 -36
  2. package/dist/lib/components/list-delta-confirm/list-delta-confirm.component.js.map +1 -1
  3. package/dist/lib/components/list-management-dialog/list-management-dialog.component.js +73 -94
  4. package/dist/lib/components/list-management-dialog/list-management-dialog.component.js.map +1 -1
  5. package/dist/lib/components/list-share-dialog/list-share-dialog.component.d.ts +5 -0
  6. package/dist/lib/components/list-share-dialog/list-share-dialog.component.d.ts.map +1 -1
  7. package/dist/lib/components/list-share-dialog/list-share-dialog.component.js +20 -22
  8. package/dist/lib/components/list-share-dialog/list-share-dialog.component.js.map +1 -1
  9. package/dist/lib/components/save-view-as-list-dialog/save-view-as-list-dialog.component.js +72 -74
  10. package/dist/lib/components/save-view-as-list-dialog/save-view-as-list-dialog.component.js.map +1 -1
  11. package/dist/lib/module.d.ts +1 -1
  12. package/dist/lib/module.d.ts.map +1 -1
  13. package/dist/lib/module.js +7 -1
  14. package/dist/lib/module.js.map +1 -1
  15. package/package.json +12 -11
  16. package/dist/__tests__/index.test.d.ts +0 -2
  17. package/dist/__tests__/index.test.d.ts.map +0 -1
  18. package/dist/__tests__/index.test.js +0 -7
  19. package/dist/__tests__/index.test.js.map +0 -1
  20. package/dist/__tests__/list-models.test.d.ts +0 -2
  21. package/dist/__tests__/list-models.test.d.ts.map +0 -1
  22. package/dist/__tests__/list-models.test.js +0 -86
  23. package/dist/__tests__/list-models.test.js.map +0 -1
  24. package/dist/lib/components/list-invitations/__tests__/list-invitations.component.test.d.ts +0 -2
  25. package/dist/lib/components/list-invitations/__tests__/list-invitations.component.test.d.ts.map +0 -1
  26. package/dist/lib/components/list-invitations/__tests__/list-invitations.component.test.js +0 -145
  27. package/dist/lib/components/list-invitations/__tests__/list-invitations.component.test.js.map +0 -1
@@ -2,7 +2,7 @@ import { NgModule } from '@angular/core';
2
2
  import { CommonModule } from '@angular/common';
3
3
  import { FormsModule } from '@angular/forms';
4
4
  // MJ UI Components
5
- import { MJButtonDirective, MJDialogComponent, MJDialogActionsComponent, MJDropdownComponent } from '@memberjunction/ng-ui-components';
5
+ import { MJButtonDirective, MJDialogComponent, MJDialogActionsComponent, MJDropdownComponent, MJEmptyStateComponent, MJAlertComponent } from '@memberjunction/ng-ui-components';
6
6
  // MemberJunction imports
7
7
  import { ContainerDirectivesModule } from '@memberjunction/ng-container-directives';
8
8
  import { SharedGenericModule } from '@memberjunction/ng-shared-generic';
@@ -36,6 +36,8 @@ export class ListManagementModule {
36
36
  MJDialogComponent,
37
37
  MJDialogActionsComponent,
38
38
  MJDropdownComponent,
39
+ MJEmptyStateComponent,
40
+ MJAlertComponent,
39
41
  ContainerDirectivesModule,
40
42
  SharedGenericModule] });
41
43
  }
@@ -62,6 +64,8 @@ export class ListManagementModule {
62
64
  MJDialogComponent,
63
65
  MJDialogActionsComponent,
64
66
  MJDropdownComponent,
67
+ MJEmptyStateComponent,
68
+ MJAlertComponent,
65
69
  ContainerDirectivesModule,
66
70
  SharedGenericModule
67
71
  ],
@@ -100,6 +104,8 @@ export class ListManagementModule {
100
104
  MJDialogComponent,
101
105
  MJDialogActionsComponent,
102
106
  MJDropdownComponent,
107
+ MJEmptyStateComponent,
108
+ MJAlertComponent,
103
109
  ContainerDirectivesModule,
104
110
  SharedGenericModule], exports: [AudienceSourcePickerComponent,
105
111
  AudienceSourceSummaryComponent,
@@ -1 +1 @@
1
- {"version":3,"file":"module.js","sourceRoot":"","sources":["../../src/lib/module.ts"],"names":[],"mappings":"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","sourcesContent":["import { NgModule } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { FormsModule } from '@angular/forms';\n\n// MJ UI Components\nimport {\n MJButtonDirective,\n MJDialogComponent,\n MJDialogActionsComponent,\n MJDropdownComponent\n} from '@memberjunction/ng-ui-components';\n\n// MemberJunction imports\nimport { ContainerDirectivesModule } from '@memberjunction/ng-container-directives';\nimport { SharedGenericModule } from '@memberjunction/ng-shared-generic';\n\n// Components\nimport { AudienceSourcePickerComponent } from './components/audience-source-picker/audience-source-picker.component';\nimport { AudienceSourceSummaryComponent } from './components/audience-source-summary/audience-source-summary.component';\nimport { ListAuditLogComponent } from './components/list-audit-log/list-audit-log.component';\nimport { ListDeltaConfirmComponent } from './components/list-delta-confirm/list-delta-confirm.component';\nimport { ListInvitationsComponent } from './components/list-invitations/list-invitations.component';\nimport { ListManagementDialogComponent } from './components/list-management-dialog/list-management-dialog.component';\nimport { ListShareDialogComponent } from './components/list-share-dialog/list-share-dialog.component';\nimport { ListStatsComponent } from './components/list-stats/list-stats.component';\nimport { ListsSharedWithMeComponent } from './components/shared-with-me/shared-with-me.component';\nimport { SaveViewAsListDialogComponent } from './components/save-view-as-list-dialog/save-view-as-list-dialog.component';\nimport { TagChipsComponent } from './components/tag-chips/tag-chips.component';\n\n// Services\nimport { ListManagementService } from './services/list-management.service';\nimport { ListSharingService } from './services/list-sharing.service';\n\n/**\n * Module providing list management components for MemberJunction.\n */\n@NgModule({\n declarations: [\n AudienceSourcePickerComponent,\n AudienceSourceSummaryComponent,\n ListAuditLogComponent,\n ListDeltaConfirmComponent,\n ListInvitationsComponent,\n ListManagementDialogComponent,\n ListShareDialogComponent,\n ListStatsComponent,\n ListsSharedWithMeComponent,\n SaveViewAsListDialogComponent,\n TagChipsComponent\n ],\n imports: [\n CommonModule,\n FormsModule,\n MJButtonDirective,\n MJDialogComponent,\n MJDialogActionsComponent,\n MJDropdownComponent,\n ContainerDirectivesModule,\n SharedGenericModule\n ],\n exports: [\n AudienceSourcePickerComponent,\n AudienceSourceSummaryComponent,\n ListAuditLogComponent,\n ListDeltaConfirmComponent,\n ListInvitationsComponent,\n ListManagementDialogComponent,\n ListShareDialogComponent,\n ListStatsComponent,\n ListsSharedWithMeComponent,\n SaveViewAsListDialogComponent,\n TagChipsComponent\n ],\n providers: [\n ListManagementService,\n ListSharingService\n ]\n})\nexport class ListManagementModule { }\n"]}
1
+ {"version":3,"file":"module.js","sourceRoot":"","sources":["../../src/lib/module.ts"],"names":[],"mappings":"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","sourcesContent":["import { NgModule } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { FormsModule } from '@angular/forms';\n\n// MJ UI Components\nimport {\n MJButtonDirective,\n MJDialogComponent,\n MJDialogActionsComponent,\n MJDropdownComponent,\n MJEmptyStateComponent,\n MJAlertComponent\n} from '@memberjunction/ng-ui-components';\n\n// MemberJunction imports\nimport { ContainerDirectivesModule } from '@memberjunction/ng-container-directives';\nimport { SharedGenericModule } from '@memberjunction/ng-shared-generic';\n\n// Components\nimport { AudienceSourcePickerComponent } from './components/audience-source-picker/audience-source-picker.component';\nimport { AudienceSourceSummaryComponent } from './components/audience-source-summary/audience-source-summary.component';\nimport { ListAuditLogComponent } from './components/list-audit-log/list-audit-log.component';\nimport { ListDeltaConfirmComponent } from './components/list-delta-confirm/list-delta-confirm.component';\nimport { ListInvitationsComponent } from './components/list-invitations/list-invitations.component';\nimport { ListManagementDialogComponent } from './components/list-management-dialog/list-management-dialog.component';\nimport { ListShareDialogComponent } from './components/list-share-dialog/list-share-dialog.component';\nimport { ListStatsComponent } from './components/list-stats/list-stats.component';\nimport { ListsSharedWithMeComponent } from './components/shared-with-me/shared-with-me.component';\nimport { SaveViewAsListDialogComponent } from './components/save-view-as-list-dialog/save-view-as-list-dialog.component';\nimport { TagChipsComponent } from './components/tag-chips/tag-chips.component';\n\n// Services\nimport { ListManagementService } from './services/list-management.service';\nimport { ListSharingService } from './services/list-sharing.service';\n\n/**\n * Module providing list management components for MemberJunction.\n */\n@NgModule({\n declarations: [\n AudienceSourcePickerComponent,\n AudienceSourceSummaryComponent,\n ListAuditLogComponent,\n ListDeltaConfirmComponent,\n ListInvitationsComponent,\n ListManagementDialogComponent,\n ListShareDialogComponent,\n ListStatsComponent,\n ListsSharedWithMeComponent,\n SaveViewAsListDialogComponent,\n TagChipsComponent\n ],\n imports: [\n CommonModule,\n FormsModule,\n MJButtonDirective,\n MJDialogComponent,\n MJDialogActionsComponent,\n MJDropdownComponent,\n MJEmptyStateComponent,\n MJAlertComponent,\n ContainerDirectivesModule,\n SharedGenericModule\n ],\n exports: [\n AudienceSourcePickerComponent,\n AudienceSourceSummaryComponent,\n ListAuditLogComponent,\n ListDeltaConfirmComponent,\n ListInvitationsComponent,\n ListManagementDialogComponent,\n ListShareDialogComponent,\n ListStatsComponent,\n ListsSharedWithMeComponent,\n SaveViewAsListDialogComponent,\n TagChipsComponent\n ],\n providers: [\n ListManagementService,\n ListSharingService\n ]\n})\nexport class ListManagementModule { }\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@memberjunction/ng-list-management",
3
- "version": "5.42.0",
3
+ "version": "5.44.0",
4
4
  "description": "MemberJunction: Angular Components for managing entity list membership with beautiful, responsive UI",
5
5
  "main": "./dist/public-api.js",
6
6
  "typings": "./dist/public-api.d.ts",
@@ -23,6 +23,7 @@
23
23
  "devDependencies": {
24
24
  "@angular/compiler": "21.1.3",
25
25
  "@angular/compiler-cli": "21.1.3",
26
+ "@memberjunction/ng-test-utils": "5.44.0",
26
27
  "vitest": "^4.0.18"
27
28
  },
28
29
  "peerDependencies": {
@@ -33,16 +34,16 @@
33
34
  "rxjs": "^7.8.2"
34
35
  },
35
36
  "dependencies": {
36
- "@memberjunction/core": "5.42.0",
37
- "@memberjunction/core-entities": "5.42.0",
38
- "@memberjunction/global": "5.42.0",
39
- "@memberjunction/graphql-dataprovider": "5.42.0",
40
- "@memberjunction/lists-base": "5.42.0",
41
- "@memberjunction/ng-base-types": "5.42.0",
42
- "@memberjunction/ng-container-directives": "5.42.0",
43
- "@memberjunction/ng-shared": "5.42.0",
44
- "@memberjunction/ng-shared-generic": "5.42.0",
45
- "@memberjunction/ng-ui-components": "5.42.0",
37
+ "@memberjunction/core": "5.44.0",
38
+ "@memberjunction/core-entities": "5.44.0",
39
+ "@memberjunction/global": "5.44.0",
40
+ "@memberjunction/graphql-dataprovider": "5.44.0",
41
+ "@memberjunction/lists-base": "5.44.0",
42
+ "@memberjunction/ng-base-types": "5.44.0",
43
+ "@memberjunction/ng-container-directives": "5.44.0",
44
+ "@memberjunction/ng-shared": "5.44.0",
45
+ "@memberjunction/ng-shared-generic": "5.44.0",
46
+ "@memberjunction/ng-ui-components": "5.44.0",
46
47
  "tslib": "^2.8.1"
47
48
  },
48
49
  "sideEffects": false,
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=index.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/index.test.ts"],"names":[],"mappings":""}
@@ -1,7 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- describe('@memberjunction/ng-list-management', () => {
3
- it('should have a passing test', () => {
4
- expect(true).toBe(true);
5
- });
6
- });
7
- //# sourceMappingURL=index.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.test.js","sourceRoot":"","sources":["../../src/__tests__/index.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAE9C,QAAQ,CAAC,oCAAoC,EAAE,GAAG,EAAE;IAClD,EAAE,CAAC,4BAA4B,EAAE,GAAG,EAAE;QACpC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { describe, it, expect } from 'vitest';\n\ndescribe('@memberjunction/ng-list-management', () => {\n it('should have a passing test', () => {\n expect(true).toBe(true);\n });\n});\n"]}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=list-models.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"list-models.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/list-models.test.ts"],"names":[],"mappings":""}
@@ -1,86 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- describe('ListManagementDialogConfig', () => {
3
- it('should construct add mode config', () => {
4
- const config = {
5
- mode: 'add',
6
- entityId: 'entity-1',
7
- entityName: 'MJ: Users',
8
- recordIds: ['rec-1', 'rec-2']
9
- };
10
- expect(config.mode).toBe('add');
11
- expect(config.recordIds).toHaveLength(2);
12
- });
13
- it('should support all mode types', () => {
14
- const modes = ['add', 'remove', 'manage'];
15
- expect(modes).toHaveLength(3);
16
- });
17
- it('should accept optional fields', () => {
18
- const config = {
19
- mode: 'manage',
20
- entityId: 'e1',
21
- entityName: 'Items',
22
- recordIds: ['r1'],
23
- allowCreate: true,
24
- allowRemove: true,
25
- showMembership: false,
26
- preSelectedListIds: ['l1'],
27
- dialogTitle: 'Manage Lists'
28
- };
29
- expect(config.allowCreate).toBe(true);
30
- expect(config.dialogTitle).toBe('Manage Lists');
31
- });
32
- });
33
- describe('ListManagementResult', () => {
34
- it('should represent cancelled result', () => {
35
- const result = {
36
- action: 'cancel',
37
- added: [],
38
- removed: [],
39
- newListsCreated: []
40
- };
41
- expect(result.action).toBe('cancel');
42
- });
43
- it('should represent applied result with operations', () => {
44
- const result = {
45
- action: 'apply',
46
- added: [{ listId: 'l1', listName: 'Favorites', recordIds: ['r1'] }],
47
- removed: [],
48
- newListsCreated: []
49
- };
50
- expect(result.added).toHaveLength(1);
51
- expect(result.added[0].listName).toBe('Favorites');
52
- });
53
- });
54
- describe('BatchOperationResult', () => {
55
- it('should track success, failed, and skipped counts', () => {
56
- const result = {
57
- success: 8,
58
- failed: 1,
59
- skipped: 2,
60
- errors: ['Record r5 already in list']
61
- };
62
- expect(result.success).toBe(8);
63
- expect(result.errors).toHaveLength(1);
64
- });
65
- });
66
- describe('Filter and sort types', () => {
67
- it('should support all filter tabs', () => {
68
- const tabs = ['all', 'my-lists', 'shared', 'recent'];
69
- expect(tabs).toHaveLength(4);
70
- });
71
- it('should support all sort options', () => {
72
- const sorts = ['name', 'recent', 'item-count'];
73
- expect(sorts).toHaveLength(3);
74
- });
75
- });
76
- describe('CreateListConfig', () => {
77
- it('should construct with required fields', () => {
78
- const config = {
79
- name: 'My New List',
80
- entityId: 'e1'
81
- };
82
- expect(config.name).toBe('My New List');
83
- expect(config.description).toBeUndefined();
84
- });
85
- });
86
- //# sourceMappingURL=list-models.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"list-models.test.js","sourceRoot":"","sources":["../../src/__tests__/list-models.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAa9C,QAAQ,CAAC,4BAA4B,EAAE,GAAG,EAAE;IAC1C,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;QAC1C,MAAM,MAAM,GAA+B;YACzC,IAAI,EAAE,KAAK;YACX,QAAQ,EAAE,UAAU;YACpB,UAAU,EAAE,WAAW;YACvB,SAAS,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC;SAC9B,CAAC;QACF,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAChC,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+BAA+B,EAAE,GAAG,EAAE;QACvC,MAAM,KAAK,GAAyC,CAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;QAChF,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAChC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+BAA+B,EAAE,GAAG,EAAE;QACvC,MAAM,MAAM,GAA+B;YACzC,IAAI,EAAE,QAAQ;YACd,QAAQ,EAAE,IAAI;YACd,UAAU,EAAE,OAAO;YACnB,SAAS,EAAE,CAAC,IAAI,CAAC;YACjB,WAAW,EAAE,IAAI;YACjB,WAAW,EAAE,IAAI;YACjB,cAAc,EAAE,KAAK;YACrB,kBAAkB,EAAE,CAAC,IAAI,CAAC;YAC1B,WAAW,EAAE,cAAc;SAC5B,CAAC;QACF,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtC,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,sBAAsB,EAAE,GAAG,EAAE;IACpC,EAAE,CAAC,mCAAmC,EAAE,GAAG,EAAE;QAC3C,MAAM,MAAM,GAAyB;YACnC,MAAM,EAAE,QAAQ;YAChB,KAAK,EAAE,EAAE;YACT,OAAO,EAAE,EAAE;YACX,eAAe,EAAE,EAAE;SACpB,CAAC;QACF,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACvC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;QACzD,MAAM,MAAM,GAAyB;YACnC,MAAM,EAAE,OAAO;YACf,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,SAAS,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YACnE,OAAO,EAAE,EAAE;YACX,eAAe,EAAE,EAAE;SACpB,CAAC;QACF,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACrC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,sBAAsB,EAAE,GAAG,EAAE;IACpC,EAAE,CAAC,kDAAkD,EAAE,GAAG,EAAE;QAC1D,MAAM,MAAM,GAAyB;YACnC,OAAO,EAAE,CAAC;YACV,MAAM,EAAE,CAAC;YACT,OAAO,EAAE,CAAC;YACV,MAAM,EAAE,CAAC,2BAA2B,CAAC;SACtC,CAAC;QACF,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC/B,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,uBAAuB,EAAE,GAAG,EAAE;IACrC,EAAE,CAAC,gCAAgC,EAAE,GAAG,EAAE;QACxC,MAAM,IAAI,GAAoB,CAAC,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;QACtE,MAAM,CAAC,IAAI,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAC/B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iCAAiC,EAAE,GAAG,EAAE;QACzC,MAAM,KAAK,GAAqB,CAAC,MAAM,EAAE,QAAQ,EAAE,YAAY,CAAC,CAAC;QACjE,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAChC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;IAChC,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAC/C,MAAM,MAAM,GAAqB;YAC/B,IAAI,EAAE,aAAa;YACnB,QAAQ,EAAE,IAAI;SACf,CAAC;QACF,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QACxC,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,aAAa,EAAE,CAAC;IAC7C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { describe, it, expect } from 'vitest';\nimport type {\n ListManagementDialogConfig,\n ListManagementResult,\n ListOperationDetail,\n ListItemViewModel,\n ListFilterTab,\n ListSortOption,\n CreateListConfig,\n BatchOperationResult,\n RecordMembershipInfo\n} from '../lib/models/list-management.models';\n\ndescribe('ListManagementDialogConfig', () => {\n it('should construct add mode config', () => {\n const config: ListManagementDialogConfig = {\n mode: 'add',\n entityId: 'entity-1',\n entityName: 'MJ: Users',\n recordIds: ['rec-1', 'rec-2']\n };\n expect(config.mode).toBe('add');\n expect(config.recordIds).toHaveLength(2);\n });\n\n it('should support all mode types', () => {\n const modes: ListManagementDialogConfig['mode'][] = ['add', 'remove', 'manage'];\n expect(modes).toHaveLength(3);\n });\n\n it('should accept optional fields', () => {\n const config: ListManagementDialogConfig = {\n mode: 'manage',\n entityId: 'e1',\n entityName: 'Items',\n recordIds: ['r1'],\n allowCreate: true,\n allowRemove: true,\n showMembership: false,\n preSelectedListIds: ['l1'],\n dialogTitle: 'Manage Lists'\n };\n expect(config.allowCreate).toBe(true);\n expect(config.dialogTitle).toBe('Manage Lists');\n });\n});\n\ndescribe('ListManagementResult', () => {\n it('should represent cancelled result', () => {\n const result: ListManagementResult = {\n action: 'cancel',\n added: [],\n removed: [],\n newListsCreated: []\n };\n expect(result.action).toBe('cancel');\n });\n\n it('should represent applied result with operations', () => {\n const result: ListManagementResult = {\n action: 'apply',\n added: [{ listId: 'l1', listName: 'Favorites', recordIds: ['r1'] }],\n removed: [],\n newListsCreated: []\n };\n expect(result.added).toHaveLength(1);\n expect(result.added[0].listName).toBe('Favorites');\n });\n});\n\ndescribe('BatchOperationResult', () => {\n it('should track success, failed, and skipped counts', () => {\n const result: BatchOperationResult = {\n success: 8,\n failed: 1,\n skipped: 2,\n errors: ['Record r5 already in list']\n };\n expect(result.success).toBe(8);\n expect(result.errors).toHaveLength(1);\n });\n});\n\ndescribe('Filter and sort types', () => {\n it('should support all filter tabs', () => {\n const tabs: ListFilterTab[] = ['all', 'my-lists', 'shared', 'recent'];\n expect(tabs).toHaveLength(4);\n });\n\n it('should support all sort options', () => {\n const sorts: ListSortOption[] = ['name', 'recent', 'item-count'];\n expect(sorts).toHaveLength(3);\n });\n});\n\ndescribe('CreateListConfig', () => {\n it('should construct with required fields', () => {\n const config: CreateListConfig = {\n name: 'My New List',\n entityId: 'e1'\n };\n expect(config.name).toBe('My New List');\n expect(config.description).toBeUndefined();\n });\n});\n"]}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=list-invitations.component.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"list-invitations.component.test.d.ts","sourceRoot":"","sources":["../../../../../src/lib/components/list-invitations/__tests__/list-invitations.component.test.ts"],"names":[],"mappings":""}
@@ -1,145 +0,0 @@
1
- import { describe, it, expect, beforeEach, vi } from 'vitest';
2
- /**
3
- * Recompute-trigger coverage for ListInvitationsComponent.visibleInvitations
4
- * (PR #2841): the precomputed per-tab list is rebuilt only when the source data
5
- * (`invitations`) or the active tab changes — NOT on every change-detection pass
6
- * (a getter there would allocate a fresh map+filter each CD tick).
7
- *
8
- * The recompute also hydrates the Pending→Expired client boundary: a Pending
9
- * invitation whose ExpiresAt is in the past surfaces under the Expired tab
10
- * without waiting for the server-side flip.
11
- *
12
- * Pins:
13
- * (1) recompute runs on data load (loadInvitations calls it),
14
- * (2) recompute runs on tab change (setTab),
15
- * (3) the Pending→Expired hydration boundary is applied during recompute,
16
- * (4) visibleInvitations is a precomputed FIELD (stable identity between reads),
17
- * (5) countFor reflects the same hydration boundary.
18
- *
19
- * inject(ChangeDetectorRef) is stubbed; BaseAngularComponent is mocked.
20
- */
21
- vi.mock('@angular/core', () => ({
22
- Component: () => (t) => t,
23
- Input: () => () => { },
24
- Output: () => () => { },
25
- EventEmitter: class {
26
- emit() { }
27
- },
28
- ChangeDetectorRef: class {
29
- detectChanges() { }
30
- markForCheck() { }
31
- },
32
- ChangeDetectionStrategy: { OnPush: 1 },
33
- OnInit: class {
34
- },
35
- inject: () => ({ detectChanges() { }, markForCheck() { } }),
36
- }));
37
- vi.mock('@memberjunction/ng-base-types', () => {
38
- class MockBaseAngularComponent {
39
- Provider = null;
40
- get ProviderToUse() { return this.Provider; }
41
- }
42
- return { BaseAngularComponent: MockBaseAngularComponent };
43
- });
44
- vi.mock('@memberjunction/core', () => ({ RunView: class {
45
- } }));
46
- vi.mock('@memberjunction/graphql-dataprovider', () => ({
47
- GraphQLDataProvider: class {
48
- },
49
- GraphQLListsClient: class {
50
- },
51
- }));
52
- import { ListInvitationsComponent } from '../list-invitations.component';
53
- function row(id, status, expiresInMs) {
54
- return {
55
- ID: id,
56
- Email: `${id}@example.com`,
57
- Role: 'Viewer',
58
- Status: status,
59
- ExpiresAt: new Date(Date.now() + expiresInMs),
60
- CreatedAt: new Date(),
61
- Token: `tok-${id}`,
62
- };
63
- }
64
- function makeComponent() {
65
- return new ListInvitationsComponent();
66
- }
67
- /** Set source data + run the (private) recompute, mirroring loadInvitations(). */
68
- function loadData(component, rows) {
69
- component.invitations = rows;
70
- component.recomputeVisibleInvitations();
71
- }
72
- const HOUR = 3_600_000;
73
- describe('ListInvitationsComponent — visibleInvitations recompute', () => {
74
- let component;
75
- beforeEach(() => {
76
- component = makeComponent();
77
- });
78
- it('recomputes visibleInvitations on data load (default Pending tab)', () => {
79
- loadData(component, [
80
- row('p1', 'Pending', +HOUR),
81
- row('a1', 'Accepted', +HOUR),
82
- ]);
83
- expect(component.visibleInvitations.map((i) => i.ID)).toEqual(['p1']);
84
- });
85
- it('recomputes when the active tab changes (setTab)', () => {
86
- loadData(component, [
87
- row('p1', 'Pending', +HOUR),
88
- row('a1', 'Accepted', +HOUR),
89
- row('r1', 'Revoked', +HOUR),
90
- ]);
91
- expect(component.activeTab).toBe('Pending');
92
- expect(component.visibleInvitations.map((i) => i.ID)).toEqual(['p1']);
93
- component.setTab('Accepted');
94
- expect(component.visibleInvitations.map((i) => i.ID)).toEqual(['a1']);
95
- component.setTab('Revoked');
96
- expect(component.visibleInvitations.map((i) => i.ID)).toEqual(['r1']);
97
- });
98
- it('hydrates a past-due Pending invite as Expired (the client boundary)', () => {
99
- loadData(component, [
100
- row('p-live', 'Pending', +HOUR), // still valid
101
- row('p-stale', 'Pending', -HOUR), // expired in the past
102
- ]);
103
- // Pending tab shows only the still-live one
104
- expect(component.visibleInvitations.map((i) => i.ID)).toEqual(['p-live']);
105
- // Expired tab surfaces the past-due Pending one without a server flip
106
- component.setTab('Expired');
107
- expect(component.visibleInvitations.map((i) => i.ID)).toEqual(['p-stale']);
108
- });
109
- it('does NOT reclassify an Accepted/Revoked row even when its ExpiresAt is in the past', () => {
110
- loadData(component, [
111
- row('a-old', 'Accepted', -HOUR),
112
- row('r-old', 'Revoked', -HOUR),
113
- ]);
114
- component.setTab('Expired');
115
- expect(component.visibleInvitations).toHaveLength(0);
116
- component.setTab('Accepted');
117
- expect(component.visibleInvitations.map((i) => i.ID)).toEqual(['a-old']);
118
- component.setTab('Revoked');
119
- expect(component.visibleInvitations.map((i) => i.ID)).toEqual(['r-old']);
120
- });
121
- it('countFor applies the same Pending→Expired hydration boundary', () => {
122
- loadData(component, [
123
- row('p-live', 'Pending', +HOUR),
124
- row('p-stale', 'Pending', -HOUR),
125
- row('a1', 'Accepted', +HOUR),
126
- ]);
127
- expect(component.countFor('Pending')).toBe(1); // only the live one
128
- expect(component.countFor('Expired')).toBe(1); // the stale Pending one
129
- expect(component.countFor('Accepted')).toBe(1);
130
- expect(component.countFor('Revoked')).toBe(0);
131
- });
132
- it('visibleInvitations is a precomputed field — re-reading it (no recompute) returns the same array', () => {
133
- loadData(component, [row('p1', 'Pending', +HOUR)]);
134
- const first = component.visibleInvitations;
135
- const second = component.visibleInvitations;
136
- expect(second).toBe(first); // stable identity (not re-allocated per access)
137
- });
138
- it('re-load with new data refreshes the visible list', () => {
139
- loadData(component, [row('p1', 'Pending', +HOUR)]);
140
- expect(component.visibleInvitations.map((i) => i.ID)).toEqual(['p1']);
141
- loadData(component, [row('p2', 'Pending', +HOUR), row('p3', 'Pending', +HOUR)]);
142
- expect(component.visibleInvitations.map((i) => i.ID)).toEqual(['p2', 'p3']);
143
- });
144
- });
145
- //# sourceMappingURL=list-invitations.component.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"list-invitations.component.test.js","sourceRoot":"","sources":["../../../../../src/lib/components/list-invitations/__tests__/list-invitations.component.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { describe, it, expect, beforeEach, vi } from 'vitest';\n\n/**\n * Recompute-trigger coverage for ListInvitationsComponent.visibleInvitations\n * (PR #2841): the precomputed per-tab list is rebuilt only when the source data\n * (`invitations`) or the active tab changes — NOT on every change-detection pass\n * (a getter there would allocate a fresh map+filter each CD tick).\n *\n * The recompute also hydrates the Pending→Expired client boundary: a Pending\n * invitation whose ExpiresAt is in the past surfaces under the Expired tab\n * without waiting for the server-side flip.\n *\n * Pins:\n * (1) recompute runs on data load (loadInvitations calls it),\n * (2) recompute runs on tab change (setTab),\n * (3) the Pending→Expired hydration boundary is applied during recompute,\n * (4) visibleInvitations is a precomputed FIELD (stable identity between reads),\n * (5) countFor reflects the same hydration boundary.\n *\n * inject(ChangeDetectorRef) is stubbed; BaseAngularComponent is mocked.\n */\n\nvi.mock('@angular/core', () => ({\n Component: () => (t: Function) => t,\n Input: () => () => {},\n Output: () => () => {},\n EventEmitter: class { emit() {} },\n ChangeDetectorRef: class { detectChanges() {} markForCheck() {} },\n ChangeDetectionStrategy: { OnPush: 1 },\n OnInit: class {},\n inject: () => ({ detectChanges() {}, markForCheck() {} }),\n}));\n\nvi.mock('@memberjunction/ng-base-types', () => {\n class MockBaseAngularComponent {\n Provider: unknown = null;\n get ProviderToUse(): unknown { return this.Provider; }\n }\n return { BaseAngularComponent: MockBaseAngularComponent };\n});\n\nvi.mock('@memberjunction/core', () => ({ RunView: class {} }));\nvi.mock('@memberjunction/graphql-dataprovider', () => ({\n GraphQLDataProvider: class {},\n GraphQLListsClient: class {},\n}));\n\nimport { ListInvitationsComponent } from '../list-invitations.component';\n\ntype Status = 'Pending' | 'Accepted' | 'Expired' | 'Revoked';\ninterface Row {\n ID: string;\n Email: string;\n Role: 'Editor' | 'Viewer';\n Status: Status;\n ExpiresAt: Date;\n CreatedAt: Date;\n Token: string;\n}\n\nfunction row(id: string, status: Status, expiresInMs: number): Row {\n return {\n ID: id,\n Email: `${id}@example.com`,\n Role: 'Viewer',\n Status: status,\n ExpiresAt: new Date(Date.now() + expiresInMs),\n CreatedAt: new Date(),\n Token: `tok-${id}`,\n };\n}\n\nfunction makeComponent(): ListInvitationsComponent {\n return new ListInvitationsComponent();\n}\n\n/** Set source data + run the (private) recompute, mirroring loadInvitations(). */\nfunction loadData(component: ListInvitationsComponent, rows: Row[]): void {\n (component as unknown as { invitations: Row[] }).invitations = rows;\n (component as unknown as { recomputeVisibleInvitations: () => void }).recomputeVisibleInvitations();\n}\n\nconst HOUR = 3_600_000;\n\ndescribe('ListInvitationsComponent — visibleInvitations recompute', () => {\n let component: ListInvitationsComponent;\n\n beforeEach(() => {\n component = makeComponent();\n });\n\n it('recomputes visibleInvitations on data load (default Pending tab)', () => {\n loadData(component, [\n row('p1', 'Pending', +HOUR),\n row('a1', 'Accepted', +HOUR),\n ]);\n expect(component.visibleInvitations.map((i) => i.ID)).toEqual(['p1']);\n });\n\n it('recomputes when the active tab changes (setTab)', () => {\n loadData(component, [\n row('p1', 'Pending', +HOUR),\n row('a1', 'Accepted', +HOUR),\n row('r1', 'Revoked', +HOUR),\n ]);\n expect(component.activeTab).toBe('Pending');\n expect(component.visibleInvitations.map((i) => i.ID)).toEqual(['p1']);\n\n component.setTab('Accepted');\n expect(component.visibleInvitations.map((i) => i.ID)).toEqual(['a1']);\n\n component.setTab('Revoked');\n expect(component.visibleInvitations.map((i) => i.ID)).toEqual(['r1']);\n });\n\n it('hydrates a past-due Pending invite as Expired (the client boundary)', () => {\n loadData(component, [\n row('p-live', 'Pending', +HOUR), // still valid\n row('p-stale', 'Pending', -HOUR), // expired in the past\n ]);\n // Pending tab shows only the still-live one\n expect(component.visibleInvitations.map((i) => i.ID)).toEqual(['p-live']);\n\n // Expired tab surfaces the past-due Pending one without a server flip\n component.setTab('Expired');\n expect(component.visibleInvitations.map((i) => i.ID)).toEqual(['p-stale']);\n });\n\n it('does NOT reclassify an Accepted/Revoked row even when its ExpiresAt is in the past', () => {\n loadData(component, [\n row('a-old', 'Accepted', -HOUR),\n row('r-old', 'Revoked', -HOUR),\n ]);\n component.setTab('Expired');\n expect(component.visibleInvitations).toHaveLength(0);\n component.setTab('Accepted');\n expect(component.visibleInvitations.map((i) => i.ID)).toEqual(['a-old']);\n component.setTab('Revoked');\n expect(component.visibleInvitations.map((i) => i.ID)).toEqual(['r-old']);\n });\n\n it('countFor applies the same Pending→Expired hydration boundary', () => {\n loadData(component, [\n row('p-live', 'Pending', +HOUR),\n row('p-stale', 'Pending', -HOUR),\n row('a1', 'Accepted', +HOUR),\n ]);\n expect(component.countFor('Pending')).toBe(1); // only the live one\n expect(component.countFor('Expired')).toBe(1); // the stale Pending one\n expect(component.countFor('Accepted')).toBe(1);\n expect(component.countFor('Revoked')).toBe(0);\n });\n\n it('visibleInvitations is a precomputed field — re-reading it (no recompute) returns the same array', () => {\n loadData(component, [row('p1', 'Pending', +HOUR)]);\n const first = component.visibleInvitations;\n const second = component.visibleInvitations;\n expect(second).toBe(first); // stable identity (not re-allocated per access)\n });\n\n it('re-load with new data refreshes the visible list', () => {\n loadData(component, [row('p1', 'Pending', +HOUR)]);\n expect(component.visibleInvitations.map((i) => i.ID)).toEqual(['p1']);\n loadData(component, [row('p2', 'Pending', +HOUR), row('p3', 'Pending', +HOUR)]);\n expect(component.visibleInvitations.map((i) => i.ID)).toEqual(['p2', 'p3']);\n });\n});\n"]}