codecharta-visualization 1.88.0 → 1.89.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 (124) hide show
  1. package/app/app.module.ts +20 -10
  2. package/app/codeCharta/codeCharta.component.html +0 -2
  3. package/app/codeCharta/codeCharta.model.ts +1 -0
  4. package/app/codeCharta/services/ajs-upgraded-providers.ts +13 -0
  5. package/app/codeCharta/state/angular-redux/effects/createEffect.spec.ts +37 -0
  6. package/app/codeCharta/state/angular-redux/effects/createEffect.ts +25 -0
  7. package/app/codeCharta/state/angular-redux/effects/effects.module.ts +36 -0
  8. package/app/codeCharta/state/angular-redux/ofType.ts +11 -0
  9. package/app/codeCharta/state/angular-redux/state.ts +12 -0
  10. package/app/codeCharta/state/angular-redux/store.ts +1 -1
  11. package/app/codeCharta/state/effects/addBlacklistItemsIfNotResultsInEmptyMap/addBlacklistItemsIfNotResultsInEmptyMap.effect.spec.ts +60 -0
  12. package/app/codeCharta/state/effects/addBlacklistItemsIfNotResultsInEmptyMap/addBlacklistItemsIfNotResultsInEmptyMap.effect.ts +56 -0
  13. package/app/codeCharta/state/effects/addBlacklistItemsIfNotResultsInEmptyMap/resultsInEmptyMap.ts +22 -0
  14. package/app/codeCharta/state/effects/nodeContextMenu/excludeButton/excludeButton.component.html +3 -0
  15. package/app/codeCharta/state/effects/nodeContextMenu/excludeButton/excludeButton.component.ts +26 -0
  16. package/app/codeCharta/state/effects/nodeContextMenu/flattenButtons/flattenButtons.component.html +13 -0
  17. package/app/codeCharta/state/effects/nodeContextMenu/flattenButtons/flattenButtons.component.spec.ts +55 -0
  18. package/app/codeCharta/state/effects/nodeContextMenu/flattenButtons/flattenButtons.component.ts +34 -0
  19. package/app/codeCharta/state/effects/nodeContextMenu/flattenButtons/flattenButtons.module.ts +11 -0
  20. package/app/codeCharta/{ui → state/effects}/nodeContextMenu/focusButtons/focusButtons.component.html +0 -0
  21. package/app/codeCharta/{ui → state/effects}/nodeContextMenu/focusButtons/focusButtons.component.spec.ts +2 -2
  22. package/app/codeCharta/{ui → state/effects}/nodeContextMenu/focusButtons/focusButtons.component.ts +5 -5
  23. package/app/codeCharta/{ui → state/effects}/nodeContextMenu/focusButtons/focusButtons.module.ts +1 -1
  24. package/app/codeCharta/{ui → state/effects}/nodeContextMenu/focusButtons/isNodeFocused.pipe.spec.ts +0 -0
  25. package/app/codeCharta/{ui → state/effects}/nodeContextMenu/focusButtons/isNodeFocused.pipe.ts +2 -2
  26. package/app/codeCharta/state/effects/nodeContextMenu/highlightButtons/highlightButtons.component.html +6 -0
  27. package/app/codeCharta/state/effects/nodeContextMenu/highlightButtons/highlightButtons.component.spec.ts +64 -0
  28. package/app/codeCharta/state/effects/nodeContextMenu/highlightButtons/highlightButtons.component.ts +39 -0
  29. package/app/codeCharta/state/effects/nodeContextMenu/highlightButtons/highlightButtons.module.ts +11 -0
  30. package/app/codeCharta/{ui → state/effects}/nodeContextMenu/markFolderRow/markFolderRow.component.html +3 -3
  31. package/app/codeCharta/{ui → state/effects}/nodeContextMenu/markFolderRow/markFolderRow.component.scss +0 -0
  32. package/app/codeCharta/{ui → state/effects}/nodeContextMenu/markFolderRow/markFolderRow.component.spec.ts +2 -2
  33. package/app/codeCharta/{ui → state/effects}/nodeContextMenu/markFolderRow/markFolderRow.component.ts +6 -6
  34. package/app/codeCharta/{ui → state/effects}/nodeContextMenu/markFolderRow/markFolderRow.module.ts +1 -1
  35. package/app/codeCharta/{ui → state/effects}/nodeContextMenu/markFolderRow/selectors/markFolderItems.selector.spec.ts +0 -0
  36. package/app/codeCharta/state/effects/nodeContextMenu/markFolderRow/selectors/markFolderItems.selector.ts +29 -0
  37. package/app/codeCharta/{ui → state/effects}/nodeContextMenu/nodeContextMenu.e2e.ts +4 -16
  38. package/app/codeCharta/{ui → state/effects}/nodeContextMenu/nodeContextMenu.po.ts +1 -1
  39. package/app/codeCharta/state/effects/nodeContextMenu/nodeContextMenu.service.spec.ts +78 -0
  40. package/app/codeCharta/state/effects/nodeContextMenu/nodeContextMenu.service.ts +72 -0
  41. package/app/codeCharta/state/effects/nodeContextMenu/nodeContextMenuCard/lastPartOfNodePath.pipe.spec.ts +11 -0
  42. package/app/codeCharta/state/effects/nodeContextMenu/nodeContextMenuCard/lastPartOfNodePath.pipe.ts +8 -0
  43. package/app/codeCharta/state/effects/nodeContextMenu/nodeContextMenuCard/nodeContextMenuCard.component.html +17 -0
  44. package/app/codeCharta/{ui/nodeContextMenu/nodeContextMenu.component.scss → state/effects/nodeContextMenu/nodeContextMenuCard/nodeContextMenuCard.component.scss} +6 -11
  45. package/app/codeCharta/state/effects/nodeContextMenu/nodeContextMenuCard/nodeContextMenuCard.component.spec.ts +49 -0
  46. package/app/codeCharta/state/effects/nodeContextMenu/nodeContextMenuCard/nodeContextMenuCard.component.ts +19 -0
  47. package/app/codeCharta/state/effects/nodeContextMenu/nodeContextMenuCard/nodeContextMenuCard.module.ts +18 -0
  48. package/app/codeCharta/state/effects/nodeContextMenu/openNodeContextMenu.effect.spec.ts +32 -0
  49. package/app/codeCharta/state/effects/nodeContextMenu/openNodeContextMenu.effect.ts +33 -0
  50. package/app/codeCharta/state/effects/nodeContextMenu/rightClickedCodeMapNode.selector.ts +8 -0
  51. package/app/codeCharta/state/effects/unfocusNodesOnLoadingMap.effect.spec.ts +47 -0
  52. package/app/codeCharta/state/effects/unfocusNodesOnLoadingMap.effect.ts +18 -0
  53. package/app/codeCharta/state/selectors/allNecessaryRenderDataAvailable/areAllNecessaryRenderDataAvailable.selector.ts +30 -0
  54. package/app/codeCharta/state/selectors/allNecessaryRenderDataAvailable/utils/areDynamicSettingsAvailable.spec.ts +36 -0
  55. package/app/codeCharta/state/selectors/allNecessaryRenderDataAvailable/utils/areDynamicSettingsAvailable.ts +10 -0
  56. package/app/codeCharta/state/selectors/allNecessaryRenderDataAvailable/utils/areMetricsAvailable.spec.ts +13 -0
  57. package/app/codeCharta/state/selectors/allNecessaryRenderDataAvailable/utils/areMetricsAvailable.ts +8 -0
  58. package/app/codeCharta/state/store/appSettings/appSettings.actions.ts +2 -0
  59. package/app/codeCharta/state/store/appSettings/appSettings.reducer.ts +2 -0
  60. package/app/codeCharta/state/store/appSettings/invertArea/invertArea.actions.ts +32 -0
  61. package/app/codeCharta/state/store/appSettings/invertArea/invertArea.reducer.spec.ts +26 -0
  62. package/app/codeCharta/state/store/appSettings/invertArea/invertArea.reducer.ts +12 -0
  63. package/app/codeCharta/state/store/appSettings/invertArea/invertArea.selector.ts +3 -0
  64. package/app/codeCharta/state/store/appSettings/isLoadingMap/isLoadingMap.reducer.spec.ts +14 -3
  65. package/app/codeCharta/state/store/appSettings/isLoadingMap/isLoadingMap.reducer.ts +41 -6
  66. package/app/codeCharta/state/store/fileSettings/blacklist/blacklist.actions.ts +33 -2
  67. package/app/codeCharta/state/store/fileSettings/blacklist/blacklist.reducer.spec.ts +17 -1
  68. package/app/codeCharta/state/store/fileSettings/blacklist/blacklist.reducer.ts +3 -1
  69. package/app/codeCharta/state/store/fileSettings/blacklist/blacklist.service.spec.ts +1 -19
  70. package/app/codeCharta/state/store/fileSettings/blacklist/blacklist.service.ts +5 -28
  71. package/app/codeCharta/state/store/lookUp/idToNode/idToNode.selector.ts +4 -0
  72. package/app/codeCharta/state/store/lookUp/lookUp.selector.ts +3 -0
  73. package/app/codeCharta/state/store/metricData/nodeMetricData/nodeMetricData.service.ts +0 -4
  74. package/app/codeCharta/state/store/store.ts +5 -1
  75. package/app/codeCharta/state/store.service.spec.ts +20 -7
  76. package/app/codeCharta/state/store.service.ts +3 -36
  77. package/app/codeCharta/ui/areaSettingsPanel/areaSettingsPanel.component.html +3 -1
  78. package/app/codeCharta/ui/areaSettingsPanel/areaSettingsPanel.component.scss +6 -2
  79. package/app/codeCharta/ui/areaSettingsPanel/areaSettingsPanel.component.ts +1 -1
  80. package/app/codeCharta/ui/areaSettingsPanel/areaSettingsPanel.module.ts +3 -0
  81. package/app/codeCharta/ui/areaSettingsPanel/areaSettingsPanel.po.ts +4 -4
  82. package/app/codeCharta/ui/areaSettingsPanel/invertAreaOption/invertAreaOption.component.html +1 -0
  83. package/app/codeCharta/ui/areaSettingsPanel/invertAreaOption/invertAreaOption.component.scss +26 -0
  84. package/app/codeCharta/ui/areaSettingsPanel/invertAreaOption/invertAreaOption.component.spec.ts +29 -0
  85. package/app/codeCharta/ui/areaSettingsPanel/invertAreaOption/invertAreaOption.component.ts +24 -0
  86. package/app/codeCharta/ui/attributeSideBar/attributeTypeSelector/aggregationType.pipe.spec.ts +19 -0
  87. package/app/codeCharta/ui/attributeSideBar/attributeTypeSelector/{aggregationSymbol.pipe.ts → aggregationType.pipe.ts} +4 -4
  88. package/app/codeCharta/ui/attributeSideBar/attributeTypeSelector/attributeTypeSelector.component.html +4 -12
  89. package/app/codeCharta/ui/attributeSideBar/attributeTypeSelector/attributeTypeSelector.component.scss +11 -20
  90. package/app/codeCharta/ui/attributeSideBar/attributeTypeSelector/attributeTypeSelector.component.spec.ts +8 -15
  91. package/app/codeCharta/ui/attributeSideBar/attributeTypeSelector/attributeTypeSelector.module.ts +2 -2
  92. package/app/codeCharta/ui/codeMap/codeMap.preRender.service.spec.ts +1 -9
  93. package/app/codeCharta/ui/codeMap/codeMap.preRender.service.ts +9 -32
  94. package/app/codeCharta/ui/dialogs/dialogs.ts +3 -0
  95. package/app/codeCharta/ui/dialogs/errorDialog/errorDialog.component.html +7 -0
  96. package/app/codeCharta/ui/dialogs/errorDialog/errorDialog.component.ts +9 -0
  97. package/app/codeCharta/ui/mapTreeView/mapTreeView.level.e2e.ts +1 -1
  98. package/app/codeCharta/ui/mapTreeView/mapTreeView.level.po.ts +1 -1
  99. package/app/codeCharta/ui/ribbonBar/ribbonBar.component.html +2 -2
  100. package/app/codeCharta/ui/ribbonBar/ribbonBar.e2e.ts +3 -3
  101. package/app/codeCharta/ui/searchBar/searchBar.component.html +5 -0
  102. package/app/codeCharta/ui/searchBar/searchBar.component.scss +11 -0
  103. package/app/codeCharta/ui/searchBar/searchBar.component.spec.ts +10 -0
  104. package/app/codeCharta/ui/searchBar/searchBar.component.ts +1 -1
  105. package/app/codeCharta/ui/sortingOption/sortingOption.component.spec.ts +1 -1
  106. package/app/codeCharta/ui/ui.ts +0 -2
  107. package/app/codeCharta/util/algorithm/treeMapLayout/treeMapGenerator.spec.ts +1 -1
  108. package/app/codeCharta/util/algorithm/treeMapLayout/treeMapGenerator.ts +14 -7
  109. package/app/codeCharta/util/customConfigBuilder.ts +1 -0
  110. package/app/codeCharta/util/dataMocks.ts +2 -0
  111. package/app/codeCharta/util/reduxHelper.ts +11 -0
  112. package/app/material/material.module.ts +17 -1
  113. package/conf/webpack.loaders.js +4 -1
  114. package/dist/webpack/bundle.js +4582 -950
  115. package/dist/webpack/bundle.js.map +1 -1
  116. package/package.json +2 -2
  117. package/app/codeCharta/ui/attributeSideBar/attributeTypeSelector/aggregationSymbol.pipe.spec.ts +0 -19
  118. package/app/codeCharta/ui/nodeContextMenu/markFolderRow/selectors/markFolderItems.selector.ts +0 -29
  119. package/app/codeCharta/ui/nodeContextMenu/markFolderRow/selectors/rightClickedNode.selector.spec.ts +0 -15
  120. package/app/codeCharta/ui/nodeContextMenu/markFolderRow/selectors/rightClickedNode.selector.ts +0 -9
  121. package/app/codeCharta/ui/nodeContextMenu/nodeContextMenu.component.html +0 -45
  122. package/app/codeCharta/ui/nodeContextMenu/nodeContextMenu.component.spec.ts +0 -365
  123. package/app/codeCharta/ui/nodeContextMenu/nodeContextMenu.component.ts +0 -198
  124. package/app/codeCharta/ui/nodeContextMenu/nodeContextMenu.module.ts +0 -15
@@ -0,0 +1,64 @@
1
+ import { render, screen } from "@testing-library/angular"
2
+ import { TestBed } from "@angular/core/testing"
3
+ import userEvent from "@testing-library/user-event"
4
+ import { HighlightButtonsModule } from "./highlightButtons.module"
5
+ import { ThreeSceneServiceToken } from "../../../../services/ajs-upgraded-providers"
6
+ import { HighlightButtonsComponent } from "./highlightButtons.component"
7
+ import { VALID_FILE_NODE_WITH_ID as codeMapNode } from "../../../../util/dataMocks"
8
+ import { State } from "../../../angular-redux/state"
9
+ import { CodeMapNode } from "../../../../codeCharta.model"
10
+
11
+ describe("flattenButtonsComponent", () => {
12
+ let threeSceneService
13
+ let state
14
+
15
+ beforeEach(() => {
16
+ threeSceneService = createMockedThreeSceneService()
17
+ state = createMockedState()
18
+ TestBed.configureTestingModule({
19
+ imports: [HighlightButtonsModule],
20
+ providers: [
21
+ { provide: ThreeSceneServiceToken, useValue: threeSceneService },
22
+ { provide: State, useValue: state }
23
+ ]
24
+ })
25
+ })
26
+
27
+ it("should let a user permanently highlight and remove permanently highlight of a node", async () => {
28
+ state.getValue().lookUp.idToBuilding.set(codeMapNode.id, { id: codeMapNode.id, node: codeMapNode })
29
+
30
+ const { rerender } = await render(HighlightButtonsComponent, {
31
+ excludeComponentDeclaration: true,
32
+ componentProperties: { codeMapNode }
33
+ })
34
+
35
+ expect(screen.queryByText("REMOVE HIGHLIGHT")).toBe(null)
36
+ userEvent.click(screen.getByText("KEEP HIGHLIGHT"))
37
+
38
+ rerender({ codeMapNode: null })
39
+ rerender({ codeMapNode })
40
+
41
+ expect(screen.queryByText("KEEP HIGHLIGHT")).toBe(null)
42
+ userEvent.click(screen.getByText("REMOVE HIGHLIGHT"))
43
+
44
+ rerender({ codeMapNode: null })
45
+ rerender({ codeMapNode })
46
+
47
+ expect(screen.queryByText("REMOVE HIGHLIGHT")).toBe(null)
48
+ expect(screen.queryByText("KEEP HIGHLIGHT")).not.toBe(null)
49
+ })
50
+
51
+ function createMockedThreeSceneService() {
52
+ const highlighted = new Map()
53
+ return {
54
+ addNodeAndChildrenToConstantHighlight: (node: CodeMapNode) => highlighted.set(node.id, node),
55
+ removeNodeAndChildrenFromConstantHighlight: (node: CodeMapNode) => highlighted.delete(node.id),
56
+ getConstantHighlight: () => highlighted
57
+ }
58
+ }
59
+
60
+ function createMockedState() {
61
+ const idToBuilding = new Map()
62
+ return { getValue: () => ({ lookUp: { idToBuilding } }) }
63
+ }
64
+ })
@@ -0,0 +1,39 @@
1
+ import { Component, Inject, Input, OnChanges, SimpleChanges } from "@angular/core"
2
+ import { CodeMapNode } from "../../../../codeCharta.model"
3
+ import { ThreeSceneServiceToken } from "../../../../services/ajs-upgraded-providers"
4
+ import { State } from "../../../angular-redux/state"
5
+ import { ThreeSceneService } from "../../../../ui/codeMap/threeViewer/threeSceneService"
6
+
7
+ @Component({
8
+ selector: "cc-highlight-buttons",
9
+ template: require("./highlightButtons.component.html")
10
+ })
11
+ export class HighlightButtonsComponent implements OnChanges {
12
+ @Input() codeMapNode: CodeMapNode
13
+
14
+ isHighlighted: boolean
15
+
16
+ constructor(@Inject(ThreeSceneServiceToken) private threeSceneService: ThreeSceneService, @Inject(State) private state: State) {}
17
+
18
+ ngOnChanges(changes: SimpleChanges): void {
19
+ if (changes.codeMapNode) this.isHighlighted = this.calculateIsHighlighted()
20
+ }
21
+
22
+ addNodeToConstantHighlight() {
23
+ this.threeSceneService.addNodeAndChildrenToConstantHighlight(this.codeMapNode)
24
+ }
25
+
26
+ removeNodeFromConstantHighlight() {
27
+ this.threeSceneService.removeNodeAndChildrenFromConstantHighlight(this.codeMapNode)
28
+ }
29
+
30
+ private calculateIsHighlighted() {
31
+ if (!this.codeMapNode) return false
32
+
33
+ const { lookUp } = this.state.getValue()
34
+ const codeMapBuilding = lookUp.idToBuilding.get(this.codeMapNode.id)
35
+ if (!codeMapBuilding) return false
36
+
37
+ return this.threeSceneService.getConstantHighlight().has(codeMapBuilding.id)
38
+ }
39
+ }
@@ -0,0 +1,11 @@
1
+ import { CommonModule } from "@angular/common"
2
+ import { NgModule } from "@angular/core"
3
+ import { MaterialModule } from "../../../../../material/material.module"
4
+ import { HighlightButtonsComponent } from "./highlightButtons.component"
5
+
6
+ @NgModule({
7
+ imports: [CommonModule, MaterialModule],
8
+ declarations: [HighlightButtonsComponent],
9
+ exports: [HighlightButtonsComponent]
10
+ })
11
+ export class HighlightButtonsModule {}
@@ -1,8 +1,8 @@
1
- <div class="row" *ngIf="node$ | async as node">
1
+ <div class="row" *ngIf="codeMapNode$ | async as codeMapNode">
2
2
  <ng-container *ngFor="let markFolderItem of markFolderItems$ | async">
3
3
  <button
4
4
  [style.background-color]="markFolderItem.color"
5
- (click)="markFolderItem.isMarked ? unmarkFolder(node.path) : markFolder(node.path, markFolderItem.color)"
5
+ (click)="markFolderItem.isMarked ? unmarkFolder(codeMapNode.path) : markFolder(codeMapNode.path, markFolderItem.color)"
6
6
  class="colorButton"
7
7
  title="Colorize folder"
8
8
  >
@@ -12,7 +12,7 @@
12
12
 
13
13
  <cc-color-picker
14
14
  [hexColor]="'#ff0000'"
15
- (onColorChange)="markFolder(node.path, $event)"
15
+ (onColorChange)="markFolder(codeMapNode.path, $event)"
16
16
  [triggerTemplate]="colorPickerTriggerTemplate"
17
17
  ></cc-color-picker>
18
18
  </div>
@@ -3,8 +3,8 @@ import { fireEvent, render } from "@testing-library/angular"
3
3
  import { MarkFolderRowComponent } from "./markFolderRow.component"
4
4
  import { MarkFolderRowModule } from "./markFolderRow.module"
5
5
 
6
- jest.mock("./selectors/rightClickedNode.selector", () => ({
7
- rightClickedNodeSelector: () => ({ path: "/root" })
6
+ jest.mock("../rightClickedCodeMapNode.selector", () => ({
7
+ rightClickedCodeMapNodeSelector: () => ({ path: "/root" })
8
8
  }))
9
9
 
10
10
  describe("markFolderRow component", () => {
@@ -1,11 +1,11 @@
1
1
  import "./markFolderRow.component.scss"
2
2
  import { Component, Inject } from "@angular/core"
3
- import { Store } from "../../../state/angular-redux/store"
4
- import { markPackages, unmarkPackage } from "../../../state/store/fileSettings/markedPackages/markedPackages.actions"
3
+ import { Store } from "../../../angular-redux/store"
4
+ import { markPackages, unmarkPackage } from "../../../store/fileSettings/markedPackages/markedPackages.actions"
5
5
  import { Observable } from "rxjs"
6
6
  import { MarkFolderItem, markFolderItemsSelector } from "./selectors/markFolderItems.selector"
7
- import { Node } from "../../../codeCharta.model"
8
- import { rightClickedNodeSelector } from "./selectors/rightClickedNode.selector"
7
+ import { CodeMapNode } from "../../../../codeCharta.model"
8
+ import { rightClickedCodeMapNodeSelector } from "../rightClickedCodeMapNode.selector"
9
9
 
10
10
  @Component({
11
11
  selector: "cc-mark-folder-row",
@@ -13,11 +13,11 @@ import { rightClickedNodeSelector } from "./selectors/rightClickedNode.selector"
13
13
  })
14
14
  export class MarkFolderRowComponent {
15
15
  markFolderItems$: Observable<MarkFolderItem[]>
16
- node$: Observable<Node>
16
+ codeMapNode$: Observable<CodeMapNode>
17
17
 
18
18
  constructor(@Inject(Store) private store: Store) {
19
19
  this.markFolderItems$ = store.select(markFolderItemsSelector)
20
- this.node$ = store.select(rightClickedNodeSelector)
20
+ this.codeMapNode$ = store.select(rightClickedCodeMapNodeSelector)
21
21
  }
22
22
 
23
23
  markFolder(path: string, color: string) {
@@ -1,6 +1,6 @@
1
1
  import { CommonModule } from "@angular/common"
2
2
  import { NgModule } from "@angular/core"
3
- import { ColorPickerModule } from "../../colorPicker/colorPicker.module"
3
+ import { ColorPickerModule } from "../../../../ui/colorPicker/colorPicker.module"
4
4
  import { MarkFolderRowComponent } from "./markFolderRow.component"
5
5
 
6
6
  @NgModule({
@@ -0,0 +1,29 @@
1
+ import { MarkedPackage } from "../../../../../codeCharta.model"
2
+ import { createSelector } from "../../../../angular-redux/createSelector"
3
+ import { mapColorsSelector } from "../../../../store/appSettings/mapColors/mapColors.selector"
4
+ import { markedPackagesSelector } from "../../../../store/fileSettings/markedPackages/markedPackages.selector"
5
+ import { findIndexOfMarkedPackageOrParent } from "../../../../store/fileSettings/markedPackages/util/findIndexOfMarkedPackageOrParent"
6
+ import { CcState } from "../../../../store/store"
7
+ import { rightClickedCodeMapNodeSelector } from "../../rightClickedCodeMapNode.selector"
8
+
9
+ export type MarkFolderItem = {
10
+ color: string
11
+ isMarked: boolean
12
+ }
13
+
14
+ const markingColorsSelector = createSelector([mapColorsSelector], mapColors => mapColors.markingColors)
15
+
16
+ export const _getMarkFolderItems = (node: { path?: string } | null, markingColors: string[], markedPackages: MarkedPackage[]) => {
17
+ if (node === null) return markingColors.map(color => ({ color, isMarked: false }))
18
+
19
+ const nodeIndexWithinMarkedPackages = findIndexOfMarkedPackageOrParent(markedPackages, node.path)
20
+ return markingColors.map(color => ({
21
+ color,
22
+ isMarked: nodeIndexWithinMarkedPackages !== -1 && color === markedPackages[nodeIndexWithinMarkedPackages].color
23
+ }))
24
+ }
25
+
26
+ export const markFolderItemsSelector: (state: CcState) => MarkFolderItem[] = createSelector(
27
+ [rightClickedCodeMapNodeSelector, markingColorsSelector, markedPackagesSelector],
28
+ _getMarkFolderItems
29
+ )
@@ -1,36 +1,24 @@
1
- import { goto } from "../../../puppeteer.helper"
1
+ import { goto } from "../../../../puppeteer.helper"
2
2
  import { NodeContextMenuPageObject } from "./nodeContextMenu.po"
3
- import { SearchPanelModeSelectorPageObject } from "../searchPanelModeSelector/searchPanelModeSelector.po"
4
- import { MapTreeViewLevelPageObject } from "../mapTreeView/mapTreeView.level.po"
5
- import { CodeMapPageObject } from "../codeMap/codeMap.po"
6
- import { ERROR_MESSAGES } from "../../util/fileValidator"
7
- import { DialogErrorPageObject } from "../dialog/dialog.error.po"
3
+ import { SearchPanelModeSelectorPageObject } from "../../../ui/searchPanelModeSelector/searchPanelModeSelector.po"
4
+ import { MapTreeViewLevelPageObject } from "../../../ui/mapTreeView/mapTreeView.level.po"
5
+ import { CodeMapPageObject } from "../../../ui/codeMap/codeMap.po"
8
6
 
9
7
  describe("NodeContextMenu", () => {
10
8
  let contextMenu: NodeContextMenuPageObject
11
9
  let searchPanelModeSelector: SearchPanelModeSelectorPageObject
12
10
  let mapTreeViewLevel: MapTreeViewLevelPageObject
13
11
  let codeMap: CodeMapPageObject
14
- let dialogError: DialogErrorPageObject
15
12
 
16
13
  beforeEach(async () => {
17
14
  contextMenu = new NodeContextMenuPageObject()
18
15
  searchPanelModeSelector = new SearchPanelModeSelectorPageObject()
19
16
  mapTreeViewLevel = new MapTreeViewLevelPageObject()
20
17
  codeMap = new CodeMapPageObject()
21
- dialogError = new DialogErrorPageObject()
22
18
 
23
19
  await goto()
24
20
  })
25
21
 
26
- it("should show error message when user excludes all files", async () => {
27
- await searchPanelModeSelector.toggleTreeView()
28
- await mapTreeViewLevel.openContextMenu("/root")
29
- await contextMenu.clickOnExclude()
30
-
31
- expect(await dialogError.getMessage()).toEqual(`${ERROR_MESSAGES.blacklistError}`)
32
- })
33
-
34
22
  it("right clicking a folder should open a context menu with color options", async () => {
35
23
  await searchPanelModeSelector.toggleTreeView()
36
24
  await mapTreeViewLevel.openContextMenu("/root")
@@ -1,4 +1,4 @@
1
- import { clickButtonOnPageElement } from "../../../puppeteer.helper"
1
+ import { clickButtonOnPageElement } from "../../../../puppeteer.helper"
2
2
 
3
3
  export class NodeContextMenuPageObject {
4
4
  async hasColorButtons() {
@@ -0,0 +1,78 @@
1
+ import { ApplicationInitStatus } from "@angular/core"
2
+ import { TestBed } from "@angular/core/testing"
3
+ import { fireEvent } from "@testing-library/angular"
4
+ import { MaterialModule } from "../../../../material/material.module"
5
+ import { EffectsModule } from "../../angular-redux/effects/effects.module"
6
+ import { NodeContextMenuService } from "./nodeContextMenu.service"
7
+ import { NodeContextMenuCardModule } from "./nodeContextMenuCard/nodeContextMenuCard.module"
8
+ import { OpenNodeContextMenuEffect } from "./openNodeContextMenu.effect"
9
+
10
+ describe("nodeContextMenuService", () => {
11
+ let mockedWheelTargetElement
12
+
13
+ beforeEach(async () => {
14
+ mockedWheelTargetElement = { addEventListener: jest.fn(), removeEventListener: jest.fn() }
15
+ // @ts-ignore
16
+ jest.spyOn(document, "getElementById").mockImplementation(() => mockedWheelTargetElement)
17
+
18
+ TestBed.configureTestingModule({
19
+ imports: [MaterialModule, EffectsModule.forRoot([OpenNodeContextMenuEffect]), NodeContextMenuCardModule],
20
+ providers: [NodeContextMenuService]
21
+ })
22
+ await TestBed.inject(ApplicationInitStatus).donePromise
23
+ })
24
+
25
+ it("should close on document click", () => {
26
+ const nodeContextMenu = TestBed.inject(NodeContextMenuService)
27
+ const closeSpy = jest.spyOn(nodeContextMenu, "close")
28
+ nodeContextMenu.open(10, 10)
29
+
30
+ document.dispatchEvent(new MouseEvent("click"))
31
+
32
+ expect(closeSpy).toHaveBeenCalled()
33
+ })
34
+
35
+ it("should not close when clicking within (its) color picker", () => {
36
+ const nodeContextMenu = TestBed.inject(NodeContextMenuService)
37
+ const closeSpy = jest.spyOn(nodeContextMenu, "close")
38
+ nodeContextMenu.open(10, 10)
39
+
40
+ const mockedMouseEvent = { composedPath: () => [{ nodeName: "COLOR-CHROME" }] } as unknown as MouseEvent
41
+ nodeContextMenu["onLeftClickHideNodeContextMenu"](mockedMouseEvent)
42
+
43
+ expect(closeSpy).not.toHaveBeenCalled()
44
+ })
45
+
46
+ it("should not close when opening its color picker", () => {
47
+ const nodeContextMenu = TestBed.inject(NodeContextMenuService)
48
+ const closeSpy = jest.spyOn(nodeContextMenu, "close")
49
+ nodeContextMenu.open(10, 10)
50
+
51
+ const mockedMouseEvent = { composedPath: () => [{ nodeName: "CC-COLOR-PICKER" }] } as unknown as MouseEvent
52
+ nodeContextMenu["onLeftClickHideNodeContextMenu"](mockedMouseEvent)
53
+
54
+ expect(closeSpy).not.toHaveBeenCalled()
55
+ })
56
+
57
+ it("should remove all listeners when closing", () => {
58
+ const documentRemoveEventListenerSpy = jest.spyOn(document, "removeEventListener")
59
+ const nodeContextMenu = TestBed.inject(NodeContextMenuService)
60
+
61
+ nodeContextMenu.close()
62
+
63
+ expect(documentRemoveEventListenerSpy).toHaveBeenCalledWith("click", nodeContextMenu["onLeftClickHideNodeContextMenu"], true)
64
+ expect(documentRemoveEventListenerSpy).toHaveBeenCalledWith("mousedown", nodeContextMenu["onRightClickHideNodeContextMenu"], true)
65
+ expect(mockedWheelTargetElement.removeEventListener).toHaveBeenCalledWith("wheel", nodeContextMenu.close, true)
66
+ })
67
+
68
+ it("should prevent default browser context menu to open", () => {
69
+ const nodeContextMenu = TestBed.inject(NodeContextMenuService)
70
+ nodeContextMenu.open(10, 10)
71
+ let wasDefaultContextMenuPrevented
72
+ document.addEventListener("contextmenu", event => {
73
+ wasDefaultContextMenuPrevented = event.defaultPrevented
74
+ })
75
+ fireEvent.contextMenu(nodeContextMenu["overlayReference"].overlayElement)
76
+ expect(wasDefaultContextMenuPrevented).toBe(true)
77
+ })
78
+ })
@@ -0,0 +1,72 @@
1
+ import { Overlay, OverlayRef } from "@angular/cdk/overlay"
2
+ import { ComponentPortal } from "@angular/cdk/portal"
3
+ import { Inject, Injectable } from "@angular/core"
4
+ import { Store } from "../../angular-redux/store"
5
+ import { setRightClickedNodeData } from "../../store/appStatus/rightClickedNodeData/rightClickedNodeData.actions"
6
+ import { NodeContextMenuCardComponent } from "./nodeContextMenuCard/nodeContextMenuCard.component"
7
+
8
+ @Injectable({ providedIn: "root" })
9
+ export class NodeContextMenuService {
10
+ private overlayReference: OverlayRef | null = null
11
+
12
+ constructor(@Inject(Overlay) private overlay: Overlay, @Inject(Store) private store: Store) {}
13
+
14
+ open(x: number, y: number) {
15
+ const positionStrategy = this.overlay
16
+ .position()
17
+ .flexibleConnectedTo({ x, y })
18
+ .withPositions([
19
+ {
20
+ originX: "end",
21
+ originY: "bottom",
22
+ overlayX: "start",
23
+ overlayY: "top"
24
+ }
25
+ ])
26
+
27
+ this.overlayReference = this.overlay.create({
28
+ positionStrategy,
29
+ scrollStrategy: this.overlay.scrollStrategies.close()
30
+ })
31
+
32
+ this.overlayReference.attach(new ComponentPortal(NodeContextMenuCardComponent))
33
+
34
+ this.overlayReference.overlayElement.addEventListener("contextmenu", event => {
35
+ event.preventDefault()
36
+ })
37
+
38
+ document.addEventListener("click", this.onLeftClickHideNodeContextMenu, false)
39
+ document.addEventListener("mousedown", this.onRightClickHideNodeContextMenu, true)
40
+ document.getElementById("codeMap").addEventListener("wheel", this.close, true)
41
+ }
42
+
43
+ close = () => {
44
+ document.removeEventListener("click", this.onLeftClickHideNodeContextMenu, true)
45
+ document.removeEventListener("mousedown", this.onRightClickHideNodeContextMenu, true)
46
+ document.getElementById("codeMap").removeEventListener("wheel", this.close, true)
47
+
48
+ this.store.dispatch(setRightClickedNodeData(null))
49
+
50
+ if (this.overlayReference) {
51
+ this.overlayReference.dispose()
52
+ this.overlayReference = null
53
+ }
54
+ }
55
+
56
+ private onLeftClickHideNodeContextMenu = (mouseEvent: MouseEvent) => {
57
+ if (this.isEventFromColorPicker(mouseEvent)) return
58
+
59
+ this.close()
60
+ }
61
+
62
+ private onRightClickHideNodeContextMenu = event => {
63
+ if (event.button !== 2) return
64
+ // Close on right click down, to close before the map gets potential moved by right clicked
65
+ this.close()
66
+ }
67
+
68
+ private isEventFromColorPicker(mouseEvent: MouseEvent) {
69
+ const elements = mouseEvent.composedPath() as Node[]
70
+ return elements.some(element => element?.nodeName === "CC-COLOR-PICKER" || element?.nodeName === "COLOR-CHROME")
71
+ }
72
+ }
@@ -0,0 +1,11 @@
1
+ import { LastPartOfNodePathPipe } from "./lastPartOfNodePath.pipe"
2
+
3
+ describe("lastPartOfNodePathPipe", () => {
4
+ it("should shorten children with '...'-prefix", () => {
5
+ expect(new LastPartOfNodePathPipe().transform("/root/child.ts")).toBe(".../child.ts")
6
+ })
7
+
8
+ it("should not shorten a top level entry", () => {
9
+ expect(new LastPartOfNodePathPipe().transform("/root")).toBe("/root")
10
+ })
11
+ })
@@ -0,0 +1,8 @@
1
+ import { Pipe, PipeTransform } from "@angular/core"
2
+
3
+ @Pipe({ name: "lastPartOfNodePath" })
4
+ export class LastPartOfNodePathPipe implements PipeTransform {
5
+ transform(path: string) {
6
+ return `${path.lastIndexOf("/") === 0 ? "" : "..."}${path.slice(path.lastIndexOf("/"))}`
7
+ }
8
+ }
@@ -0,0 +1,17 @@
1
+ <mat-card id="codemap-context-menu" *ngIf="codeMapNode$ | async; let codeMapNode">
2
+ <div *ngIf="codeMapNode?.children?.length > 0">
3
+ <cc-mark-folder-row></cc-mark-folder-row>
4
+ <mat-divider></mat-divider>
5
+ </div>
6
+
7
+ <div class="button-group">
8
+ <button mat-button disabled="true" title="{{ codeMapNode.path }}">
9
+ {{ codeMapNode.path | lastPartOfNodePath }}
10
+ </button>
11
+
12
+ <cc-focus-buttons [codeMapNode]="codeMapNode"></cc-focus-buttons>
13
+ <cc-flatten-buttons [codeMapNode]="codeMapNode"></cc-flatten-buttons>
14
+ <cc-highlight-buttons [codeMapNode]="codeMapNode"></cc-highlight-buttons>
15
+ <cc-exclude-button [codeMapNode]="codeMapNode"></cc-exclude-button>
16
+ </div>
17
+ </mat-card>
@@ -1,26 +1,21 @@
1
- .cdk-overlay-container {
2
- /* overwrite z-index of below #codemap-context-menu */
3
- z-index: 2000 !important;
4
- }
5
-
6
- node-context-menu-component {
7
- #codemap-context-menu {
8
- position: absolute;
9
- z-index: 2000;
10
- }
1
+ #codemap-context-menu {
2
+ padding: 2px;
11
3
 
12
4
  .button-group {
13
5
  display: inline-grid;
6
+ width: 100%;
14
7
  }
15
8
 
16
9
  .png-icon {
10
+ width: 17px;
11
+ height: 17px;
17
12
  margin: 5px auto auto;
18
13
  padding: 0;
19
14
  display: inline-block;
20
15
  background-repeat: no-repeat no-repeat;
21
16
  pointer-events: none;
22
17
  }
23
- .md-button:not(.colorButton),
18
+
24
19
  [mat-button] {
25
20
  width: 100%;
26
21
  text-align: left;
@@ -0,0 +1,49 @@
1
+ import { TestBed } from "@angular/core/testing"
2
+ import { render, screen } from "@testing-library/angular"
3
+ import { mocked } from "ts-jest/utils"
4
+ import { ThreeSceneServiceToken } from "../../../../services/ajs-upgraded-providers"
5
+ import { VALID_FILE_NODE_WITH_ID, VALID_NODE_WITH_PATH } from "../../../../util/dataMocks"
6
+ import { rightClickedCodeMapNodeSelector } from "../rightClickedCodeMapNode.selector"
7
+ import { NodeContextMenuCardComponent } from "./nodeContextMenuCard.component"
8
+ import { NodeContextMenuCardModule } from "./nodeContextMenuCard.module"
9
+
10
+ jest.mock("../rightClickedCodeMapNode.selector", () => ({
11
+ rightClickedCodeMapNodeSelector: jest.fn()
12
+ }))
13
+ const mockedRightClickedCodeMapNodeSelector = mocked(rightClickedCodeMapNodeSelector)
14
+
15
+ describe("NodeContextMenuCardComponent", () => {
16
+ beforeEach(() => {
17
+ TestBed.configureTestingModule({
18
+ imports: [NodeContextMenuCardModule],
19
+ providers: [
20
+ {
21
+ provide: ThreeSceneServiceToken,
22
+ useValue: {
23
+ addNodeAndChildrenToConstantHighlight: jest.fn(),
24
+ removeNodeAndChildrenFromConstantHighlight: jest.fn(),
25
+ getConstantHighlight: jest.fn()
26
+ }
27
+ }
28
+ ]
29
+ })
30
+ })
31
+
32
+ it("should not display mark folder option if node is a leaf", async () => {
33
+ mockedRightClickedCodeMapNodeSelector.mockImplementation(() => VALID_FILE_NODE_WITH_ID)
34
+ await render(NodeContextMenuCardComponent, { excludeComponentDeclaration: true })
35
+ expect(screen.queryByTitle("Colorize folder")).toBe(null)
36
+ })
37
+
38
+ it("should display all information", async () => {
39
+ mockedRightClickedCodeMapNodeSelector.mockImplementation(() => VALID_NODE_WITH_PATH)
40
+ await render(NodeContextMenuCardComponent, { excludeComponentDeclaration: true })
41
+
42
+ expect(screen.queryByText("/root")).not.toBe(null)
43
+ expect(screen.queryByText("FOCUS")).not.toBe(null)
44
+ expect(screen.queryByText("FLATTEN")).not.toBe(null)
45
+ expect(screen.queryByText("KEEP HIGHLIGHT")).not.toBe(null)
46
+ expect(screen.queryByText("EXCLUDE")).not.toBe(null)
47
+ expect(screen.queryAllByTitle("Colorize folder").length).toBe(5)
48
+ })
49
+ })
@@ -0,0 +1,19 @@
1
+ import "./nodeContextMenuCard.component.scss"
2
+ import { Component, Inject, OnInit } from "@angular/core"
3
+ import { Store } from "../../../angular-redux/store"
4
+ import { rightClickedCodeMapNodeSelector } from "../rightClickedCodeMapNode.selector"
5
+ import { Observable } from "rxjs"
6
+ import { CodeMapNode } from "../../../../codeCharta.model"
7
+
8
+ @Component({
9
+ template: require("./nodeContextMenuCard.component.html")
10
+ })
11
+ export class NodeContextMenuCardComponent implements OnInit {
12
+ codeMapNode$: Observable<CodeMapNode>
13
+
14
+ constructor(@Inject(Store) private store: Store) {}
15
+
16
+ ngOnInit(): void {
17
+ this.codeMapNode$ = this.store.select(rightClickedCodeMapNodeSelector)
18
+ }
19
+ }
@@ -0,0 +1,18 @@
1
+ import { CommonModule } from "@angular/common"
2
+ import { NgModule } from "@angular/core"
3
+ import { MaterialModule } from "../../../../../material/material.module"
4
+ import { ExcludeButtonComponent } from "../excludeButton/excludeButton.component"
5
+ import { FlattenButtonsModule } from "../flattenButtons/flattenButtons.module"
6
+ import { FocusButtonsModule } from "../focusButtons/focusButtons.module"
7
+ import { HighlightButtonsModule } from "../highlightButtons/highlightButtons.module"
8
+ import { MarkFolderRowModule } from "../markFolderRow/markFolderRow.module"
9
+ import { LastPartOfNodePathPipe } from "./lastPartOfNodePath.pipe"
10
+ import { NodeContextMenuCardComponent } from "./nodeContextMenuCard.component"
11
+
12
+ @NgModule({
13
+ imports: [CommonModule, MaterialModule, FlattenButtonsModule, FocusButtonsModule, HighlightButtonsModule, MarkFolderRowModule],
14
+ declarations: [NodeContextMenuCardComponent, LastPartOfNodePathPipe, ExcludeButtonComponent],
15
+ exports: [NodeContextMenuCardComponent],
16
+ entryComponents: [NodeContextMenuCardComponent]
17
+ })
18
+ export class NodeContextMenuCardModule {}
@@ -0,0 +1,32 @@
1
+ import { ApplicationInitStatus } from "@angular/core"
2
+ import { TestBed } from "@angular/core/testing"
3
+ import { Action } from "redux"
4
+ import { Subject } from "rxjs"
5
+ import { EffectsModule } from "../../angular-redux/effects/effects.module"
6
+ import { setRightClickedNodeData } from "../../store/appStatus/rightClickedNodeData/rightClickedNodeData.actions"
7
+ import { NodeContextMenuService } from "./nodeContextMenu.service"
8
+ import { OpenNodeContextMenuEffect } from "./openNodeContextMenu.effect"
9
+
10
+ describe("OpenNodeContextMenuEffect", () => {
11
+ let openSpy
12
+ beforeEach(async () => {
13
+ openSpy = jest.fn()
14
+ EffectsModule.actions$ = new Subject<Action>()
15
+ TestBed.configureTestingModule({
16
+ imports: [EffectsModule.forRoot([OpenNodeContextMenuEffect])],
17
+ providers: [{ provide: NodeContextMenuService, useValue: { open: openSpy } }]
18
+ })
19
+ await TestBed.inject(ApplicationInitStatus).donePromise
20
+ })
21
+
22
+ it("should ignore empty rightClickedNodeData", () => {
23
+ EffectsModule.actions$.next(setRightClickedNodeData(null))
24
+ expect(openSpy).not.toHaveBeenCalled()
25
+ })
26
+
27
+ it("should open node context menu", () => {
28
+ EffectsModule.actions$.next(setRightClickedNodeData({ nodeId: 1, xPositionOfRightClickEvent: 2, yPositionOfRightClickEvent: 3 }))
29
+ expect(openSpy).toHaveBeenCalledTimes(1)
30
+ expect(openSpy).toHaveBeenCalledWith(2, 3)
31
+ })
32
+ })
@@ -0,0 +1,33 @@
1
+ import { Inject, Injectable } from "@angular/core"
2
+ import { createEffect } from "../../angular-redux/effects/createEffect"
3
+ import { Actions, ActionsToken } from "../../angular-redux/effects/effects.module"
4
+ import { ofType } from "../../angular-redux/ofType"
5
+ import { NodeContextMenuService } from "./nodeContextMenu.service"
6
+ import {
7
+ RightClickedNodeDataActions,
8
+ SetRightClickedNodeDataAction
9
+ } from "../../store/appStatus/rightClickedNodeData/rightClickedNodeData.actions"
10
+ import { tap } from "rxjs"
11
+
12
+ @Injectable()
13
+ export class OpenNodeContextMenuEffect {
14
+ constructor(
15
+ @Inject(ActionsToken) private actions$: Actions,
16
+ @Inject(NodeContextMenuService) private nodeContextMenu: NodeContextMenuService
17
+ ) {}
18
+
19
+ openNodeContextMenu$ = createEffect(
20
+ () =>
21
+ this.actions$.pipe(
22
+ ofType<SetRightClickedNodeDataAction>(RightClickedNodeDataActions.SET_RIGHT_CLICKED_NODE_DATA),
23
+ tap(rightClickedNodeData => {
24
+ if (rightClickedNodeData.payload)
25
+ this.nodeContextMenu.open(
26
+ rightClickedNodeData.payload.xPositionOfRightClickEvent,
27
+ rightClickedNodeData.payload.yPositionOfRightClickEvent
28
+ )
29
+ })
30
+ ),
31
+ { dispatch: false }
32
+ )
33
+ }