Skip to content

Commit

Permalink
refactor: WIP start migrating metricDeltaSelected.component
Browse files Browse the repository at this point in the history
Missing:
- tests for component
- test for including `this` in mapStateToThis
- return type of connect if mapDispatchtoThis is not given

refs: #2318
  • Loading branch information
shaman-apprentice committed Sep 11, 2021
1 parent bc58e65 commit 9148632
Show file tree
Hide file tree
Showing 10 changed files with 70 additions and 90 deletions.
5 changes: 3 additions & 2 deletions visualization/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ import { platformBrowserDynamic } from "@angular/platform-browser-dynamic"
import { UpgradeModule } from "@angular/upgrade/static"

import { SortingButtonComponent } from "./codeCharta/ui/sortingButton/sortingButton.component"
import { MetricDeltaSelectedComponent } from "./codeCharta/ui/metricDeltaSelected/metricDeltaSelected.component"

@NgModule({
imports: [BrowserModule, UpgradeModule],
declarations: [SortingButtonComponent],
entryComponents: [SortingButtonComponent]
declarations: [SortingButtonComponent, MetricDeltaSelectedComponent],
entryComponents: [SortingButtonComponent, MetricDeltaSelectedComponent]
})
export class AppModule {
constructor(@Inject(UpgradeModule) private upgrade: UpgradeModule) {}
Expand Down
36 changes: 30 additions & 6 deletions visualization/app/codeCharta/state/angular-redux/connect.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable unicorn/prevent-abbreviations */ // ts constructor needs `...args` as argument name

import { OnDestroy } from "@angular/core"
import { OnChanges, OnDestroy, SimpleChanges } from "@angular/core"
import { Action } from "redux"

import { CcState, Store } from "../store/store"
Expand All @@ -19,9 +19,8 @@ type ActionCreator = (...args: unknown[]) => Action
* By `mapStateToThis` returned `MappedState` is added to properties of returned class and reflects store's state automatically.
* By `mapDispatchToThis` returned `MappedDispatch` is added to properties of returned class but actual dispatches to store.
*/
// eslint-disable-next-line @typescript-eslint/ban-types
export const connect = <MappedState extends Record<string, unknown>, MappedDispatch extends Record<string, ActionCreator> = {}>(
mapStateToThis?: (state: CcState) => MappedState,
mapStateToThis?: (state: CcState, that?: This) => MappedState,
mapDispatchToThis?: MappedDispatch
): Constructor<ReturnType<typeof mapStateToThis> & MappedDispatch & { ngOnDestroy: () => void }> => {
const setStateToThis = (that: This, keys: string[], mappedState: ReturnType<typeof mapStateToThis>) => {
Expand All @@ -38,15 +37,35 @@ export const connect = <MappedState extends Record<string, unknown>, MappedDispa

// Ignore TS2322 as we dynamically add all properties of ReturnType<typeof mapDispatchToThis>, which we don't know ahead
// @ts-ignore - would be nice to only ignore TS2322, what is not possible yet (https://github.com/microsoft/TypeScript/issues/19139)
return class implements OnDestroy {
return class implements OnChanges, OnDestroy {
constructor() {
if (mapStateToThis) {
const initialMappedState = mapStateToThis(Store.store.getState())
const initialMappedState = mapStateToThis(Store.store.getState(), this)
const keysToTrack = Object.keys(initialMappedState)
setStateToThis(this, keysToTrack, initialMappedState)
unsubscribe = Store.store.subscribe(() => {
setStateToThis(this, keysToTrack, mapStateToThis(Store.store.getState()))
setStateToThis(this, keysToTrack, mapStateToThis(Store.store.getState(), this))
})

// todo add unit test for this
if (mapStateToThis.length === 2) {
const hasChangeOtherThanKeysToTrack = (changes: SimpleChanges) => {
for (const change in changes) {
if (!keysToTrack.includes(change)) {
return true
}
}
return false
}

const actualNgOnChanges = this.ngOnChanges
this.ngOnChanges = (changes: SimpleChanges) => {
Reflect.apply(actualNgOnChanges, this, [changes])
if (hasChangeOtherThanKeysToTrack(changes)) {
setStateToThis(this, keysToTrack, mapStateToThis(Store.store.getState(), this))
}
}
}
}

if (mapDispatchToThis) {
Expand All @@ -63,6 +82,11 @@ export const connect = <MappedState extends Record<string, unknown>, MappedDispa
}
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
ngOnChanges(_: SimpleChanges) {
// if not present in prototype Angular doesn't pick up this lifecycle hook
}

ngOnDestroy() {
// to enforce cleanup, even if it gets overwritten, it is set in constructor
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { CcState } from "../../store"

export const selectedBuildingIdSelector = (ccState: CcState) => ccState.lookUp.selectedBuildingId
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,7 @@ <h2 class="node-name-h2">
{{ $ctrl._viewModel.node.attributes[$ctrl._viewModel.primaryMetricKeys.node.area] | number: 0 }}
</span>
</div>
<metric-delta-selected-component
attributekey="$ctrl._viewModel.primaryMetricKeys.node.area"
></metric-delta-selected-component>
<cc-metric-delta-selected [attribute-key]="$ctrl._viewModel.primaryMetricKeys.node.area"></cc-metric-delta-selected>
<p class="metric-name">{{ $ctrl._viewModel.primaryMetricKeys.node.area }}</p>
</td>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,11 +161,13 @@ export class ThreeSceneService implements CodeMapPreRenderServiceSubscriber, Map

selectBuilding(building: CodeMapBuilding) {
this.getMapMesh().selectBuilding(building, this.folderLabelColorSelected)

// after clean up of custom broadcast fun through $rootScope we can probably remove this if condition
if (building.id !== this.selected?.id) this.storeService.dispatch(setSelectedBuildingId(building.id))

this.selected = building
this.highlightBuildings()
this.$rootScope.$broadcast(ThreeSceneService.BUILDING_SELECTED_EVENT, this.selected)
// after clean up of custom broadcast hell through $rootScope we can probably remove this if condition
if (building.id !== this.selected?.id) this.storeService.dispatch(setSelectedBuildingId(building.id))

if (this.mapGeometry.children[0]) {
this.selectMaterial(this.mapGeometry.children[0]["material"])
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
<span class="metric-delta" ng-show="$ctrl._viewModel.deltaValue" ng-style="$ctrl._viewModel.style">
&Delta;{{ $ctrl._viewModel.deltaValue | number: 0 }}
</span>
<span class="metric-delta" [hidden]="!deltaValue" [style.color]="color">&Delta;{{ deltaValue }}</span>
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
metric-delta-selected-component {
cc-metric-delta-selected {
.metric-delta {
margin-left: 3px;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,65 +1,26 @@
import "./metricDeltaSelected.component.scss"
import { CodeMapBuilding } from "../codeMap/rendering/codeMapBuilding"
import { IRootScopeService, ITimeoutService } from "angular"
import { BuildingSelectedEventSubscriber, ThreeSceneService } from "../codeMap/threeViewer/threeSceneService"
import { StoreService } from "../../state/store.service"
import { MapColorsService, MapColorsSubscriber } from "../../state/store/appSettings/mapColors/mapColors.service"

export class MetricDeltaSelectedController implements BuildingSelectedEventSubscriber, MapColorsSubscriber {
private static TIME_TO_INIT_BINDING = 50
private attributekey: string // angular bindings do not accept camelCase

private _viewModel: {
deltaValue: number
attributeKey: string
style: { color: string }
} = {
deltaValue: null,
attributeKey: null,
style: { color: null }
}

constructor(
private $rootScope: IRootScopeService,
private $timeout: ITimeoutService,
private threeSceneService: ThreeSceneService,
private storeService: StoreService
) {
"ngInject"
ThreeSceneService.subscribeToBuildingSelectedEvents(this.$rootScope, this)
MapColorsService.subscribe(this.$rootScope, this)
this.$timeout(() => {
this.onBuildingSelected(this.threeSceneService.getSelectedBuilding())
}, MetricDeltaSelectedController.TIME_TO_INIT_BINDING)
}

onMapColorsChanged() {
this.setDeltaColorClass()
}

onBuildingSelected(selectedBuilding?: CodeMapBuilding) {
this.setDeltaValue(selectedBuilding)
this.setDeltaColorClass()
}

private setDeltaValue(selectedBuilding?: CodeMapBuilding) {
if (selectedBuilding) {
this._viewModel.deltaValue = selectedBuilding.node.deltas?.[this.attributekey]
}
}

private setDeltaColorClass() {
const mapColors = this.storeService.getState().appSettings.mapColors
const color = this._viewModel.deltaValue > 0 ? mapColors.positiveDelta : mapColors.negativeDelta
this._viewModel.style = { color }
}
}

export const metricDeltaSelectedComponent = {
selector: "metricDeltaSelectedComponent",
template: require("./metricDeltaSelected.component.html"),
controller: MetricDeltaSelectedController,
bindings: {
attributekey: "="
}
import { Component, Input } from "@angular/core"
import { connect } from "../../state/angular-redux/connect"
import { selectedBuildingIdSelector } from "../../state/store/lookUp/selectedBuildingId/selectedBuildingId.selector"
import { CcState } from "../../state/store/store"

const ConnectedClass = connect((state: CcState, that: { attributeKey: string }) => {
if (that.attributeKey === undefined) return { deltaValue: undefined, color: "" }

const selectedBuildingId = selectedBuildingIdSelector(state)
const selectedBuildingNode = state.lookUp.idToBuilding.get(selectedBuildingId)
const deltaValue = selectedBuildingNode?.node.deltas?.[that.attributeKey]

const mapColors = state.appSettings.mapColors
const color = deltaValue > 0 ? mapColors.positiveDelta : mapColors.negativeDelta

return { deltaValue, color }
})

@Component({
selector: "cc-metric-delta-selected",
template: require("./metricDeltaSelected.component.html")
})
export class MetricDeltaSelectedComponent extends ConnectedClass {
// @ts-ignore - used within connect
@Input() private attributeKey
}

This file was deleted.

4 changes: 2 additions & 2 deletions visualization/app/codeCharta/ui/ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import "./metricValueHovered/metricValueHovered.module"
import "./downloadButton/downloadButton.module"
import "./screenshotButton/screenshotButton.module"
import "./globalSettingsButton/globalSettingsButton.module"
import "./metricDeltaSelected/metricDeltaSelected.module"
import "./nodePathPanel/nodePathPanel.module"
import "./attributeSideBar/attributeSideBar.module"
import "./edgeSettingsPanel/edgeSettingsPanel.module"
Expand Down Expand Up @@ -48,6 +47,7 @@ import "./layoutSelection/layoutSelection.module"
import "./maxTreeMapFiles/maxTreeMapFiles.module"
import "./sharpnessModeSelector/sharpnessModeSelector.module"
import { SortingButtonComponent } from "./sortingButton/sortingButton.component"
import { MetricDeltaSelectedComponent } from "./metricDeltaSelected/metricDeltaSelected.component"

angular
.module("app.codeCharta.ui", [
Expand All @@ -62,7 +62,6 @@ angular
"app.codeCharta.ui.downloadButton",
"app.codeCharta.ui.screenshotButton",
"app.codeCharta.ui.globalSettingsButton",
"app.codeCharta.ui.metricDeltaSelected",
"app.codeCharta.ui.nodePathPanel",
"app.codeCharta.ui.attributeSideBar",
"app.codeCharta.ui.edgeSettingsPanel",
Expand Down Expand Up @@ -100,3 +99,4 @@ angular
"app.codeCharta.ui.maxTreeMapFiles"
])
.directive("ccSortingButton", downgradeComponent({ component: SortingButtonComponent }))
.directive("ccMetricDeltaSelected", downgradeComponent({ component: MetricDeltaSelectedComponent }))

0 comments on commit 9148632

Please sign in to comment.