Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor/2318/extract and migrate slider 2 #2787

Merged
merged 10 commits into from
Apr 26, 2022
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/)

## [unreleased] (Added 🚀 | Changed | Removed 🗑 | Fixed 🐞 | Chore 👨‍💻 👩‍💻)

### Fixed 🐞

- Update slider of area metric options correctly on changes of related input field [#2787](https://github.com/MaibornWolff/codecharta/pull/2787)

### Chore 👨‍💻 👩‍💻

- Introduce custom angular material theme [#2784](https://github.com/MaibornWolff/codecharta/pull/2784)
Expand Down
2 changes: 2 additions & 0 deletions visualization/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import { UploadCustomConfigButtonModule } from "./codeCharta/ui/customConfigs/up
import { UploadFilesButtonComponent } from "./codeCharta/ui/toolBar/uploadFilesButton/uploadFilesButton.component"
import { MetricTypeHoveredModule } from "./codeCharta/ui/metricChooser/metricTypeHovered/metricTypeHovered.module"
import { ResetSettingsButtonComponent } from "./codeCharta/ui/resetSettingsButton/resetSettingsButton.component"
import { SliderModule } from "./codeCharta/ui/slider/slider.module"

@NgModule({
imports: [
Expand All @@ -58,6 +59,7 @@ import { ResetSettingsButtonComponent } from "./codeCharta/ui/resetSettingsButto
TrackEventUsageDataEffect,
BlacklistSearchPatternEffect
]),
SliderModule,
AttributeSideBarModule,
MaterialModule,
FormsModule,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,10 @@
<md-slider-container class="md-block" title="Amount of space between buildings in percent">
<span class="slider-title"> Margin </span>
<md-slider
flex
step="1"
min="1"
max="100"
ng-model="$ctrl._viewModel.margin"
id="margin-slider"
ng-change="$ctrl.onChangeMarginSlider()"
class="md-primary"
>
</md-slider>
<md-input-container>
<input
flex
step="1"
min="1"
max="100"
ng-model="$ctrl._viewModel.margin"
ng-change="$ctrl.onChangeMarginSlider()"
aria-controls="margin-slider"
class="margin-input"
/>
</md-input-container>
</md-slider-container>
<cc-slider
title="Amount of space between buildings in percent"
[value]="$ctrl._viewModel.margin"
[on-change]="$ctrl.applyDebouncedMargin"
[min]="1"
[max]="100"
></cc-slider>

<div class="bottom-row">
<md-input-container class="default-margin">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,6 @@ cc-area-settings-panel {
padding-top: 3px;
}

md-slider-container {
position: relative;
.slider-title {
position: absolute;
top: 0;
font-size: 0.8em;
color: grey;
left: 4px;
}
.margin-input {
height: 24px;
}
}

.bottom-row {
align-items: center;
display: flex;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import { getService, instantiateModule } from "../../../../mocks/ng.mockhelper"
import { StoreService } from "../../state/store.service"
import { DynamicMarginService } from "../../state/store/appSettings/dynamicMargin/dynamicMargin.service"
import { MarginService } from "../../state/store/dynamicSettings/margin/margin.service"
import { setDynamicMargin } from "../../state/store/appSettings/dynamicMargin/dynamicMargin.actions"
import { FilesService } from "../../state/store/files/files.service"

describe("AreaSettingsPanelController", () => {
Expand Down Expand Up @@ -90,22 +89,14 @@ describe("AreaSettingsPanelController", () => {
})

describe("onChangeMarginSlider", () => {
it("should set dynamicMargin to false", () => {
storeService.dispatch(setDynamicMargin(true))

areaSettingsPanelController.onChangeMarginSlider()

expect(storeService.getState().appSettings.dynamicMargin).toBeFalsy()
})

it("should update margin and dynamicMargin in store", done => {
areaSettingsPanelController["_viewModel"].dynamicMargin = false
areaSettingsPanelController["_viewModel"].margin = 28

areaSettingsPanelController.onChangeMarginSlider()
areaSettingsPanelController.applyDebouncedMargin(42)

setTimeout(() => {
expect(storeService.getState().dynamicSettings.margin).toEqual(28)
expect(storeService.getState().dynamicSettings.margin).toEqual(42)
expect(storeService.getState().appSettings.dynamicMargin).toBeFalsy()
done()
}, AreaSettingsPanelController["DEBOUNCE_TIME"] + SOME_EXTRA_TIME)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { FilesService, FilesSelectionSubscriber } from "../../state/store/files/

export class AreaSettingsPanelController implements FilesSelectionSubscriber, DynamicMarginSubscriber, MarginSubscriber {
private static DEBOUNCE_TIME = 400
private readonly applyDebouncedMargin: () => void
applyDebouncedMargin: (margin: number) => void

private _viewModel: {
margin: number
Expand All @@ -26,8 +26,9 @@ export class AreaSettingsPanelController implements FilesSelectionSubscriber, Dy
MarginService.subscribe(this.$rootScope, this)
FilesService.subscribe(this.$rootScope, this)

this.applyDebouncedMargin = debounce(() => {
this.storeService.dispatch(setMargin(this._viewModel.margin))
this.applyDebouncedMargin = debounce((margin: number) => {
this.storeService.dispatch(setDynamicMargin(false))
this.storeService.dispatch(setMargin(margin))
}, AreaSettingsPanelController.DEBOUNCE_TIME)
}

Expand All @@ -47,11 +48,6 @@ export class AreaSettingsPanelController implements FilesSelectionSubscriber, Dy
private applyDynamicMargin() {
this.storeService.dispatch(setDynamicMargin(this._viewModel.dynamicMargin))
}

onChangeMarginSlider() {
this.applyDebouncedMargin()
this.storeService.dispatch(setDynamicMargin(false))
}
}

export const areaSettingsPanelComponent = {
Expand Down
8 changes: 8 additions & 0 deletions visualization/app/codeCharta/ui/slider/slider.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<div class="cc-slider-container">
<label>Margin</label>
<mat-slider [step]="this.step" [min]="this.min" [max]="this.max" [value]="this.value" (input)="this.handleSliderOnChange($event)">
</mat-slider>
<mat-form-field>
<input matInput type="number" [value]="this.value" (input)="this.handleInputOnChange($event)" />
</mat-form-field>
</div>
32 changes: 32 additions & 0 deletions visualization/app/codeCharta/ui/slider/slider.component.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
cc-slider {
width: 100%;

.cc-slider-container {
position: relative;
display: flex;

label {
position: absolute;
top: 0;
font-size: 0.8em;
color: grey;
left: 4px;
}

mat-slider {
flex: 1;
margin-right: 8px;
margin-left: -4px;
}

mat-form-field {
width: 50px;
height: 48px;

.mat-form-field-infix {
border-top-width: 0;
padding-top: 16px;
}
}
}
}
56 changes: 56 additions & 0 deletions visualization/app/codeCharta/ui/slider/slider.component.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { TestBed } from "@angular/core/testing"
import { render, screen } from "@testing-library/angular"
import userEvent from "@testing-library/user-event"
import { drag } from "../../util/testUtils/drag"
import { SliderComponent } from "./slider.component"
import { SliderModule } from "./slider.module"

describe("SliderComponent", () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [SliderModule]
})
})

it("should update given value on input changes", async () => {
let value = 21
await render(SliderComponent, {
excludeComponentDeclaration: true,
componentProperties: {
value,
onChange: (newValue: number) => {
value = newValue
},
min: 1,
max: 100
}
})

const inputField = screen.getByRole("spinbutton") as HTMLInputElement
userEvent.type(inputField, "42", { initialSelectionStart: 0, initialSelectionEnd: 2 })

expect(value).toBe(42)
})

it("should update given value on slider changes", async () => {
let value = 21
const { container } = await render(SliderComponent, {
excludeComponentDeclaration: true,
componentProperties: {
value,
onChange: (newValue: number) => {
value = newValue
},
min: 1,
max: 100
}
})

const sliderThumb = container.querySelector(".mat-slider-thumb")
await drag(sliderThumb, {
delta: { x: -100, y: 0 }
})

expect(value).toBe(1)
})
})
28 changes: 28 additions & 0 deletions visualization/app/codeCharta/ui/slider/slider.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import "./slider.component.scss"
import { Component, Input } from "@angular/core"
import { MatSliderChange } from "@angular/material/slider"

@Component({
selector: "cc-slider",
template: require("./slider.component.html")
})
export class SliderComponent {
@Input() value?: number
@Input() min: number
@Input() max: number
@Input() step?: number = 1
@Input() onChange: (number) => void

handleSliderOnChange($event: MatSliderChange) {
if ($event.value !== this.value) {
this.onChange($event.value)
}
}

handleInputOnChange($event: InputEvent) {
const newValue = Number.parseInt(($event.target as HTMLInputElement).value)
if (newValue !== this.value) {
this.onChange(newValue)
}
}
}
12 changes: 12 additions & 0 deletions visualization/app/codeCharta/ui/slider/slider.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { CommonModule } from "@angular/common"
import { NgModule } from "@angular/core"
import { MaterialModule } from "../../../material/material.module"
import { SliderComponent } from "./slider.component"

@NgModule({
imports: [MaterialModule, CommonModule],
declarations: [SliderComponent],
exports: [SliderComponent],
entryComponents: [SliderComponent]
})
export class SliderModule {}
2 changes: 2 additions & 0 deletions visualization/app/codeCharta/ui/ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import "./maxTreeMapFiles/maxTreeMapFiles.module"
import "./sharpnessModeSelector/sharpnessModeSelector.module"
import { Export3DMapButtonComponent } from "./export3DMapButton/export3DMapButton.component"
import { LegendPanelComponent } from "./legendPanel/legendPanel.component"
import { SliderComponent } from "./slider/slider.component"

angular
.module("app.codeCharta.ui", [
Expand Down Expand Up @@ -63,3 +64,4 @@ angular
])
.directive("ccExportThreedMapButton", downgradeComponent({ component: Export3DMapButtonComponent }))
.directive("ccLegendPanel", downgradeComponent({ component: LegendPanelComponent }))
.directive("ccSlider", downgradeComponent({ component: SliderComponent }))
51 changes: 51 additions & 0 deletions visualization/app/codeCharta/util/testUtils/drag.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Taken from https://testing-library.com/docs/example-drag/

import { fireEvent } from "@testing-library/dom"

function getElementClientCenter(element) {
const { left, top, width, height } = element.getBoundingClientRect()
return {
x: left + width / 2,
y: top + height / 2
}
}

const sleep = async ms =>
new Promise(resolve => {
setTimeout(resolve, ms)
})

type DragOptions = {
delta: { x: number; y: number }
steps?: number
duration?: number
}
export async function drag(element: Element, { delta, steps = 20, duration = 500 }: DragOptions) {
const from = getElementClientCenter(element)
const to = {
x: from.x + delta.x,
y: from.y + delta.y
}

const step = {
x: (to.x - from.x) / steps,
y: (to.y - from.y) / steps
}

const current = {
clientX: from.x,
clientY: from.y
}

fireEvent.mouseEnter(element, current)
fireEvent.mouseOver(element, current)
fireEvent.mouseMove(element, current)
fireEvent.mouseDown(element, current)
for (let index = 0; index < steps; index++) {
current.clientX += step.x
current.clientY += step.y
await sleep(duration / steps)
fireEvent.mouseMove(element, current)
}
fireEvent.mouseUp(element, current)
}
4 changes: 3 additions & 1 deletion visualization/app/material/material.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { MatFormFieldModule } from "@angular/material/form-field"
import { MatInputModule } from "@angular/material/input"
import { MatProgressSpinnerModule } from "@angular/material/progress-spinner"
import { MatListModule } from "@angular/material/list"
import { MatSliderModule } from "@angular/material/slider"

const materialModules = [
MatSelectModule,
Expand All @@ -32,7 +33,8 @@ const materialModules = [
MatFormFieldModule,
MatInputModule,
MatProgressSpinnerModule,
MatListModule
MatListModule,
MatSliderModule
]

@NgModule({
Expand Down
1 change: 1 addition & 0 deletions visualization/app/material/material.scss
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
@include mat.form-field-theme(theme.$cc-theme);
@include mat.list-theme(theme.$cc-theme);
@include mat.divider-theme(theme.$cc-theme);
@include mat.slider-theme(theme.$cc-theme);

@import "./matSelect.scss";
@import "./matMenu.scss";
Expand Down