Skip to content

Commit

Permalink
Refactor/2318/extract and migrate slider 2 (#2787)
Browse files Browse the repository at this point in the history
extract and migrate a common slider component for panel settings

close #2786
ref #2318
  • Loading branch information
shaman-apprentice committed Apr 26, 2022
1 parent 700dfe5 commit 6190747
Show file tree
Hide file tree
Showing 15 changed files with 212 additions and 60 deletions.
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

0 comments on commit 6190747

Please sign in to comment.