Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
a35c570
Created numberRangeFilterComponent
christophscheuing Apr 11, 2024
211b46a
Playing around
christophscheuing Apr 18, 2024
aa26a1b
Merge remote-tracking branch 'origin/master' into number_range_filter
christophscheuing May 2, 2024
ab412c9
Working on it
christophscheuing Jun 20, 2024
a9730d9
Merge remote-tracking branch 'origin/master' into number_range_filter
christophscheuing Jun 20, 2024
c2d56d2
WIP
christophscheuing Jun 20, 2024
52002b2
number-range as MatFormFieldControl
sleidig Jun 27, 2024
55f4065
WIP: Trying to implement validation
christophscheuing Jul 5, 2024
f1cb6e0
WIP
christophscheuing Jul 11, 2024
2a9b01a
fix custom-form-control directive
sleidig Jul 11, 2024
f954f9a
Merge remote-tracking branch 'origin/master' into number_range_filter
christophscheuing Jul 25, 2024
bbd934d
Validation working both at rangeInputComponent and numberRangeFilterC…
christophscheuing Jul 25, 2024
e15d507
WIP: established functionality of number range filter
christophscheuing Aug 8, 2024
3298374
Merge remote-tracking branch 'origin/master' into number_range_filter
christophscheuing Sep 5, 2024
61f7484
Adoption to stricter Typescript checking
christophscheuing Sep 5, 2024
d3edd88
Merge remote-tracking branch 'origin/master' into number_range_filter
christophscheuing Sep 19, 2024
7050631
Merge remote-tracking branch 'origin/master' into number_range_filter
christophscheuing Oct 17, 2024
ab14ff1
Added tests for numbers filter
christophscheuing Oct 17, 2024
63de237
Merge remote-tracking branch 'origin/master' into number_range_filter
christophscheuing Oct 24, 2024
28f96e9
Added numberOfTeachers for Schools and further adoptions
christophscheuing Oct 24, 2024
70a0449
Removed unused imports
christophscheuing Oct 24, 2024
fc926df
new storybook format
sleidig Oct 25, 2024
972ab4c
Merge branch 'master' into number_range_filter
sleidig Oct 25, 2024
2a83602
Update src/app/core/basic-datatypes/number/edit-number/edit-number.co…
christophscheuing Oct 27, 2024
955dd71
Update src/app/core/basic-datatypes/number/number-range-filter/range-…
christophscheuing Oct 27, 2024
59d10c2
Some adoptions to PR review
christophscheuing Oct 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export class DemoSchoolGenerator extends DemoDataGenerator<Entity> {
$localize`:School demo timing:11 a.m. - 4 p.m.`,
$localize`:School demo timing:6:30-11:00 and 11:30-16:00`,
]);

school["numberOfTeachers"] = faker.number.int({ min: 3, max: 75 });
school["address"] = faker.geoAddress();

data.push(school);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { applicationConfig, Meta, StoryObj } from "@storybook/angular";
import { Entity } from "app/core/entity/model/entity";
import { provideAnimations } from "@angular/platform-browser/animations";
import { DateRangeFilterComponent } from "./date-range-filter.component";
import { DateFilter } from "app/core/filter/filters/dateFilter";
import { provideNativeDateAdapter } from "@angular/material/core";

export default {
title: "Core/> App Layout/Filter/Date Range Filter",
component: DateRangeFilterComponent<Entity>,
decorators: [
applicationConfig({
providers: [provideAnimations(), provideNativeDateAdapter()],
}),
],
} as Meta;

const filterConfig: DateFilter<Entity> = new DateFilter<Entity>(
"x",
"Demo Date Filter",
[],
);

export const Default: StoryObj<DateRangeFilterComponent<Entity>> = {
args: {
filterConfig,
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<mat-form-field>
<mat-label>{{ filterConfig.label || filterConfig.name }}</mat-label>
<app-range-input
[formControl]="formControl"
[activateValidation]="true"
></app-range-input>
</mat-form-field>
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
div {
display: flex;
}
input {
border: none;
background: none;
padding: 0;
outline: none;
font: inherit;
text-align: center;
color: currentColor;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { ComponentFixture, TestBed } from "@angular/core/testing";

import { NumberRangeFilterComponent } from "./number-range-filter.component";
import { Entity } from "app/core/entity/model/entity";
import { NumberFilter } from "app/core/filter/filters/numberFilter";
import { NoopAnimationsModule } from "@angular/platform-browser/animations";

describe("NumberRangeFilterComponent", () => {
let component: NumberRangeFilterComponent<Entity>;
let fixture: ComponentFixture<NumberRangeFilterComponent<Entity>>;

let filterConfig: NumberFilter<Entity>;

beforeEach(async () => {
filterConfig = new NumberFilter<Entity>("x", "Demo Number Filter");

await TestBed.configureTestingModule({
imports: [NumberRangeFilterComponent, NoopAnimationsModule],
}).compileComponents();

fixture = TestBed.createComponent(NumberRangeFilterComponent);
component = fixture.componentInstance;

component.filterConfig = filterConfig;

fixture.detectChanges();
});

it("should create", () => {
expect(component).toBeTruthy();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { Component, Input } from "@angular/core";
import { Entity } from "../../../entity/model/entity";
import { MatFormFieldModule } from "@angular/material/form-field";
import { FormControl, ReactiveFormsModule } from "@angular/forms";
import {
NumericRange,
RangeInputComponent,
} from "./range-input/range-input.component";
import { NumberFilter } from "../../../filter/filters/numberFilter";

@Component({
selector: "app-date-range-filter",
templateUrl: "./number-range-filter.component.html",
styleUrls: ["./number-range-filter.component.scss"],
standalone: true,
imports: [MatFormFieldModule, ReactiveFormsModule, RangeInputComponent],
})
export class NumberRangeFilterComponent<T extends Entity> {
@Input() filterConfig: NumberFilter<T>;

formControl: FormControl<NumericRange>;
from: number;
to: number;

ngOnInit() {
this.formControl = new FormControl<NumericRange>({
from: Number(this.filterConfig.selectedOptionValues[0]),
to: Number(this.filterConfig.selectedOptionValues[1]),
});
this.formControl.valueChanges.subscribe((value) => {
this.filterConfig.selectedOptionValues = [
this.formControl.value.from?.toString() ?? "",
this.formControl.value.to?.toString() ?? "",
];

this.filterConfig.selectedOptionChange.emit(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we could add a unit test to ensure a formControl change correctly updates the filterConfig (and emits selectedOptionChange)

this.filterConfig.selectedOptionValues,
);
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { applicationConfig, Meta, StoryObj } from "@storybook/angular";
import { NumberRangeFilterComponent } from "./number-range-filter.component";
import { Entity } from "app/core/entity/model/entity";
import { NumberFilter } from "app/core/filter/filters/numberFilter";
import { provideAnimations } from "@angular/platform-browser/animations";

export default {
title: "Core/> App Layout/Filter/Number Range Filter",
component: NumberRangeFilterComponent<Entity>,
decorators: [
applicationConfig({
providers: [provideAnimations()],
}),
],
} as Meta;

const filterConfig: NumberFilter<Entity> = new NumberFilter<Entity>(
"numberFilter",
);
filterConfig.label = "Demo Number Filter";

export const Default: StoryObj<NumberRangeFilterComponent<Entity>> = {
args: {
filterConfig,
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<div [formGroup]="formGroup" class="container">
<input formControlName="from" type="number" class="input-element" />
<span>&ndash;</span>
<input formControlName="to" type="number" class="input-element" />
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
.container {
display: flex;
}

.input-element {
border: none;
background: none;
padding: 0;
outline: none;
font: inherit;
text-align: center;
color: currentColor;

max-width: calc(50% - 10px);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { ComponentFixture, TestBed } from "@angular/core/testing";

import { RangeInputComponent } from "./range-input.component";

describe("RangeInputComponent", () => {
let component: RangeInputComponent;
let fixture: ComponentFixture<RangeInputComponent>;

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [RangeInputComponent],
}).compileComponents();

fixture = TestBed.createComponent(RangeInputComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it("should create", () => {
expect(component).toBeTruthy();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { Component, ElementRef, Input, Optional, Self } from "@angular/core";
import {
FormControl,
FormControlDirective,
FormGroup,
FormGroupDirective,
NgControl,
NgForm,
ReactiveFormsModule,
ValidationErrors,
ValidatorFn,
} from "@angular/forms";
import { ErrorStateMatcher } from "@angular/material/core";
import { MatFormFieldControl } from "@angular/material/form-field";
import { CustomFormControlDirective } from "app/core/common-components/basic-autocomplete/custom-form-control.directive";
import { MatInput } from "@angular/material/input";

@Component({
selector: "app-range-input",
standalone: true,
imports: [MatInput, ReactiveFormsModule],
templateUrl: "./range-input.component.html",
styleUrl: "./range-input.component.scss",
providers: [
{ provide: MatFormFieldControl, useExisting: RangeInputComponent },
],
})
export class RangeInputComponent extends CustomFormControlDirective<NumericRange> {
formGroup: FormGroup = new FormGroup({
from: new FormControl(),
to: new FormControl(),
});

@Input() override set value(value: NumericRange) {
// update the internal formGroup when the value changes from the outside
this.formGroup.setValue(value, { emitEvent: false });
super.value = value;
}
override get value(): NumericRange {
return super.value;
}

/**
* Validation (activated by default) ensures the component has a sensible range
* (e.g. "from" <= "to")
* or otherwise marks the formControl invalid
*/
@Input() activateValidation: boolean = true;

constructor(
elementRef: ElementRef<HTMLElement>,
errorStateMatcher: ErrorStateMatcher,
@Optional() @Self() ngControl: NgControl,
@Optional() parentForm: NgForm,
@Optional() parentFormGroup: FormGroupDirective,
@Optional() private formControlDirective: FormControlDirective,
) {
super(
elementRef,
errorStateMatcher,
ngControl,
parentForm,
parentFormGroup,
);

this.formGroup.valueChanges.subscribe((value) => {
this.value = value;
});
}

private validatorFunction: ValidatorFn = (): ValidationErrors | null => {
if (
this.value.from != undefined &&
this.value.to != undefined &&
this.value.from > this.value.to
) {
return {
fromGreaterThanTo: "The 'from' value is greater than the 'to' value.",
};
} else {
return null;
}
};

ngAfterViewInit() {
if (this.activateValidation) {
this.formControlDirective.form.addValidators([this.validatorFunction]);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we could add a unit test for this

}
}
}

export class NumericRange {
constructor(
public from: number,
public to: number,
) {}
}
16 changes: 14 additions & 2 deletions src/app/core/config/config-fix.ts
Original file line number Diff line number Diff line change
Expand Up @@ -387,8 +387,16 @@ export const defaultJsonConfig = {
},
"privateSchool",
"language",
"numberOfTeachers",
],
filters: [
{ id: "privateSchool" },
{
id: "numberOfTeachers",
type: "number-range",
label: "Number of Teachers",
},
],
filters: [{ id: "privateSchool" }],
},
},
"view:school/:id": {
Expand All @@ -407,7 +415,7 @@ export const defaultJsonConfig = {
{ fields: ["name", "privateSchool", "parentSchool"] },
{ fields: ["address", "phone"] },
{ fields: ["language", "timing"] },
{ fields: ["remarks"] },
{ fields: ["remarks", "numberOfTeachers"] },
],
},
},
Expand Down Expand Up @@ -1093,6 +1101,10 @@ export const defaultJsonConfig = {
dataType: "string",
label: $localize`:Label for the timing of a school:School Timing`,
},
numberOfTeachers: {
dataType: "number",
label: $localize`:Label for a school attribute:Number of Teachers`,
},
remarks: {
dataType: "string",
label: $localize`:Label for the remarks for a school:Remarks`,
Expand Down
2 changes: 2 additions & 0 deletions src/app/core/entity-list/EntityListConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ export interface GroupConfig {
export type FilterConfig<T = any> =
| BasicFilterConfig
| BooleanFilterConfig
| NumberFilterConfig
| PrebuiltFilterConfig<T>
| ConfigurableEnumFilterConfig<T>;

Expand All @@ -86,6 +87,7 @@ export interface BooleanFilterConfig extends BasicFilterConfig {
true: string;
false: string;
}
export interface NumberFilterConfig extends BasicFilterConfig {}

export interface PrebuiltFilterConfig<T> extends BasicFilterConfig {
options: FilterSelectionOption<T>[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { DateFilter } from "../filters/dateFilter";
import { BooleanFilter } from "../filters/booleanFilter";
import { ConfigurableEnumFilter } from "../filters/configurableEnumFilter";
import { EntityFilter } from "../filters/entityFilter";
import { NumberFilter } from "../filters/numberFilter";

@Injectable({
providedIn: "root",
Expand Down Expand Up @@ -66,6 +67,12 @@ export class FilterGeneratorService {
label,
filterConfig as BooleanFilterConfig,
);
} else if (type == "number-range") {
filter = new NumberFilter(
filterConfig.id,
filterConfig.label || schema.label,
// filterConfig as NumberFilterConfig,
);
} else if (type == "prebuilt") {
filter = new SelectableFilter(
filterConfig.id,
Expand Down
Loading
Loading