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

Display dynamic value #1992

Merged
merged 25 commits into from
Sep 21, 2023
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
48fb139
Initial commit
christophscheuing Jul 13, 2023
835d3be
Playing around with storybook and displayDynamicValueComponent
christophscheuing Jul 13, 2023
59abe46
Limited display-dynamic-value.component to percentage
christophscheuing Aug 3, 2023
aee81f0
Added decimalPlaces in display-percentage.component
christophscheuing Aug 3, 2023
456357c
Added decimalPlaces to display-percentage.component
christophscheuing Aug 3, 2023
b00bd6b
Renamed display-dynamic-value to display-dynamic-percentage
christophscheuing Aug 3, 2023
c5f5c2a
Merge remote-tracking branch 'origin/master' into display_dynamic_value
christophscheuing Aug 31, 2023
a9e78ed
Adoptions to new architecture
christophscheuing Aug 31, 2023
5a39da4
Trying to actually use the display-dynamic-percentage component
christophscheuing Aug 31, 2023
c5bf2e4
Showcasing in health-checkup and added tests
christophscheuing Sep 7, 2023
b34c817
Merge remote-tracking branch 'origin/master' into display_dynamic_value
christophscheuing Sep 7, 2023
96d28e1
Cleanup
christophscheuing Sep 7, 2023
5bc0b08
feat(core): configurable styles for whole platform (#1953)
TheSlimvReal Sep 8, 2023
22bd1d8
Merge branch 'master' into display_dynamic_value
sleidig Sep 8, 2023
e4e5dda
rename
sleidig Sep 8, 2023
7dfa6f7
Update src/app/core/basic-datatypes/entity/display-entity/display-ent…
sleidig Sep 8, 2023
530a955
Removal of this test
christophscheuing Sep 11, 2023
7701d09
Merge branch 'master' into display_dynamic_value
TheSlimvReal Sep 18, 2023
fe686ea
calculating result on every change detection
TheSlimvReal Sep 18, 2023
ca57d11
added demo showcase
TheSlimvReal Sep 18, 2023
90630ab
Merge branch 'master' into display_dynamic_value
TheSlimvReal Sep 20, 2023
c81d202
better property name for numberFormat
sleidig Sep 21, 2023
621e337
cleanup
sleidig Sep 21, 2023
a652d3e
Merge branch 'master' into display_dynamic_value
sleidig Sep 21, 2023
e5d8123
fix tests
sleidig Sep 21, 2023
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
498 changes: 201 additions & 297 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"@angular/platform-browser-dynamic": "^16.2.1",
"@angular/router": "^16.2.1",
"@angular/service-worker": "^16.2.1",
"@aytek/material-color-picker": "^1.0.4",
"@casl/ability": "^6.5.0",
"@casl/angular": "^8.2.1",
"@faker-js/faker": "^8.0.2",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
@use "src/styles/mixins/grid-layout";
@use "src/styles/variables/sizes";
@use "@angular/material" as mat;
@use "src/styles/variables/ndb-light-theme" as theme;
@use "src/styles/variables/colors";

.top-control {
position: sticky;
Expand All @@ -19,7 +18,7 @@
padding-right: sizes.$margin-main-view-right;
margin-top: - sizes.$margin-main-view-top;
padding-top: sizes.$margin-main-view-top;
background-color: mat.get-color-from-palette(theme.$primary, 50);
background-color: colors.$background;
}

.cards-list {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,9 @@
[class.inactive]="!entity.isActive"
class="truncate-text container"
>
<app-fa-dynamic-icon
*ngIf="!imgPath"
[icon]="icon"
class="margin-right-small"
/>
<img *ngIf="imgPath" [src]="imgPath" class="child-pic" alt="" />
<app-display-img [entity]="entity" imgProperty="photo" [defaultIcon]="icon" class="child-pic"></app-display-img>
<span class="font-size-rel">{{ entity?.toString() }}</span>
<span class="subnote" *ngIf="entity?.projectNumber">
({{ entity?.projectNumber }})</span
>
<span class="subnote" *ngIf="entity?.projectNumber"> ({{ entity?.projectNumber }})</span>
</span>

<ng-template #tooltip>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,7 @@
object-fit: cover;
margin-right: 4px;
vertical-align: middle;
}

.child-pic-large {
width: 80px;
height: 80px;
border-radius: 50%;
object-fit: cover;
overflow: hidden;
}

.inactive {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,25 @@ import { ComponentFixture, TestBed, waitForAsync } from "@angular/core/testing";
import { ChildBlockComponent } from "./child-block.component";
import { ChildrenService } from "../children.service";
import { Child } from "../model/child";
import { FileService } from "app/features/file/file.service";
import { of } from "rxjs";
import { FontAwesomeTestingModule } from "@fortawesome/angular-fontawesome/testing";
import { FileService } from "../../../features/file/file.service";

describe("ChildBlockComponent", () => {
let component: ChildBlockComponent;
let fixture: ComponentFixture<ChildBlockComponent>;
let mockChildrenService: jasmine.SpyObj<ChildrenService>;
let mockFileService: jasmine.SpyObj<FileService>;

beforeEach(waitForAsync(() => {
mockChildrenService = jasmine.createSpyObj("mockChildrenService", [
"getChild",
]);
mockChildrenService.getChild.and.resolveTo(new Child(""));
mockFileService = jasmine.createSpyObj(["loadFile"]);
mockFileService.loadFile.and.returnValue(of("success"));

TestBed.configureTestingModule({
imports: [ChildBlockComponent, FontAwesomeTestingModule],
providers: [
{ provide: ChildrenService, useValue: mockChildrenService },
{ provide: FileService, useValue: mockFileService },
{ provide: FileService, useValue: undefined },
],
}).compileComponents();
}));
Expand All @@ -40,24 +36,4 @@ describe("ChildBlockComponent", () => {
it("should create", () => {
expect(component).toBeTruthy();
});

it("should reset picture if child has none", async () => {
const withPicture = new Child();
withPicture.photo = "some-picture";
component.entity = withPicture;

await component.ngOnChanges({ entity: undefined });

expect(mockFileService.loadFile).toHaveBeenCalled();
expect(component.imgPath).toBeDefined();

mockFileService.loadFile.calls.reset();
// without picture
component.entity = new Child();

await component.ngOnChanges({ entity: undefined });

expect(mockFileService.loadFile).not.toHaveBeenCalled();
expect(component.imgPath).toBeUndefined();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,8 @@ import { DynamicComponent } from "../../../core/config/dynamic-components/dynami
import { NgIf } from "@angular/common";
import { TemplateTooltipDirective } from "../../../core/common-components/template-tooltip/template-tooltip.directive";
import { ChildBlockTooltipComponent } from "./child-block-tooltip/child-block-tooltip.component";
import { SafeUrl } from "@angular/platform-browser";
import { FileService } from "../../../features/file/file.service";
import { FaDynamicIconComponent } from "../../../core/common-components/fa-dynamic-icon/fa-dynamic-icon.component";
import { DisplayImgComponent } from "../../../features/file/display-img/display-img.component";

@DynamicComponent("ChildBlock")
@Component({
Expand All @@ -24,7 +23,7 @@ import { FaDynamicIconComponent } from "../../../core/common-components/fa-dynam
NgIf,
TemplateTooltipDirective,
ChildBlockTooltipComponent,
FaDynamicIconComponent,
DisplayImgComponent,
],
standalone: true,
})
Expand All @@ -38,7 +37,6 @@ export class ChildBlockComponent implements OnChanges {
/** prevent additional details to be displayed in a tooltip on mouse over */
@Input() tooltipDisabled: boolean;

imgPath: SafeUrl;
icon = Child.icon;

constructor(
Expand All @@ -50,11 +48,5 @@ export class ChildBlockComponent implements OnChanges {
if (changes.hasOwnProperty("entityId")) {
this.entity = await this.childrenService.getChild(this.entityId);
}
this.imgPath = undefined;
if (this.entity?.photo) {
this.fileService
.loadFile(this.entity, "photo")
.subscribe((res) => (this.imgPath = res));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,16 @@ export class HealthCheckupComponent implements OnInit {
tooltip: $localize`:Tooltip for BMI info:This is calculated using the height and the weight measure`,
additional: (entity: HealthCheck) => this.getBMI(entity),
},
{
id: "display-percentage",
label: "display-percentage",
view: "DisplayDynamicPercentage",
additional: {
actual: "weight",
total: "height",
decimalPlaces: 0,
},
},
christophscheuing marked this conversation as resolved.
Show resolved Hide resolved
],
};
@Input() entity: Child;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@ import { ConfigurableEnumValue } from "../../../core/basic-datatypes/configurabl
* providing special context for {@link Note} categories.
*/
export interface InteractionType extends ConfigurableEnumValue {
/** color highlighting the individual category */
color?: string;

/** whether the Note is a group type category that stores attendance details for each related person */
isMeeting?: boolean;
}
Expand Down
2 changes: 0 additions & 2 deletions src/app/core/alerts/_alert-style-classes.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
@use "@angular/material" as mat;
@use "src/styles/variables/ndb-light-theme" as theme;
@use "src/styles/variables/colors";

.ndb-alert {
Expand Down
25 changes: 24 additions & 1 deletion src/app/core/analytics/analytics.service.spec.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
import { TestBed } from "@angular/core/testing";

import { AnalyticsService } from "./analytics.service";
import { Angulartics2Matomo, Angulartics2Module } from "angulartics2";
import {
Angulartics2,
Angulartics2Matomo,
Angulartics2Module,
} from "angulartics2";
import { RouterTestingModule } from "@angular/router/testing";
import { ConfigService } from "../config/config.service";
import { UsageAnalyticsConfig } from "./usage-analytics-config";
import { Subject } from "rxjs";
import { Config } from "../config/config";
import { SiteSettingsService } from "../site-settings/site-settings.service";

describe("AnalyticsService", () => {
let service: AnalyticsService;

let mockConfigService: jasmine.SpyObj<ConfigService>;
const configUpdates = new Subject();
let mockMatomo: jasmine.SpyObj<Angulartics2Matomo>;
let mockAngulartics: jasmine.SpyObj<Angulartics2>;
let siteNameSubject = new Subject();

beforeEach(() => {
mockConfigService = jasmine.createSpyObj(
Expand All @@ -25,13 +32,21 @@ describe("AnalyticsService", () => {
"setUsername",
"startTracking",
]);
mockAngulartics = jasmine.createSpyObj([], {
setUserProperties: { next: jasmine.createSpy() },
});

TestBed.configureTestingModule({
imports: [Angulartics2Module.forRoot(), RouterTestingModule],
providers: [
AnalyticsService,
{ provide: ConfigService, useValue: mockConfigService },
{ provide: Angulartics2Matomo, useValue: mockMatomo },
{ provide: Angulartics2, useValue: mockAngulartics },
{
provide: SiteSettingsService,
useValue: { siteName: siteNameSubject },
},
],
});
service = TestBed.inject(AnalyticsService);
Expand Down Expand Up @@ -108,4 +123,12 @@ describe("AnalyticsService", () => {
testAnalyticsConfig2.url + "matomo.php",
]);
});

it("should set the hostname as the organisation", () => {
service.init();

expect(mockAngulartics.setUserProperties.next).toHaveBeenCalledWith({
dimension2: location.hostname,
});
});
});
7 changes: 1 addition & 6 deletions src/app/core/analytics/analytics.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
} from "./usage-analytics-config";
import { Angulartics2, Angulartics2Matomo } from "angulartics2";
import md5 from "md5";
import { UiConfig } from "../ui/ui-config";

/**
* Track usage analytics data and report it to a backend server like Matomo.
Expand Down Expand Up @@ -48,6 +47,7 @@ export class AnalyticsService {
window["_paq"].push(["trackPageView"]);
window["_paq"].push(["enableLinkTracking"]);
this.setVersion();
this.setOrganization(location.hostname);
this.setUser(undefined);
this.configService.configUpdates.subscribe(() => this.setConfigValues());
}
Expand Down Expand Up @@ -97,11 +97,6 @@ export class AnalyticsService {
if (site_id) {
window["_paq"].push(["setSiteId", site_id]);
}
const { site_name } =
this.configService.getConfig<UiConfig>("appConfig") || {};
if (site_name) {
this.setOrganization(site_name);
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,12 @@ describe("Schema data type: configurable-enum", () => {
const TEST_CONFIG: ConfigurableEnumConfig = [
{ id: "NONE", label: "" },
{ id: "TEST_1", label: "Category 1" },
{ id: "TEST_3", label: "Category 3", color: "#FFFFFF", isMeeting: true },
{
id: "TEST_3",
label: "Category 3",
color: "#FFFFFF",
isMeeting: true,
} as ConfigurableEnumValue,
];

@DatabaseEntity("ConfigurableEnumDatatypeTestEntity")
Expand All @@ -53,7 +58,11 @@ describe("Schema data type: configurable-enum", () => {
let enumService: jasmine.SpyObj<ConfigurableEnumService>;

beforeEach(waitForAsync(() => {
enumService = jasmine.createSpyObj(["getEnumValues", "preLoadEnums"]);
enumService = jasmine.createSpyObj([
"getEnumValues",
"preLoadEnums",
"cacheEnum",
]);
enumService.getEnumValues.and.returnValue(TEST_CONFIG);

TestBed.configureTestingModule({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Directive, Input, TemplateRef, ViewContainerRef } from "@angular/core";
import { ConfigurableEnumService } from "../configurable-enum.service";
import { ConfigurableEnumValue } from "../configurable-enum.interface";

/**
* Enumerate over all {@link ConfigurableEnumConfig} values for the given enum config id.
Expand Down Expand Up @@ -36,4 +37,19 @@ export class ConfigurableEnumDirective {
private viewContainerRef: ViewContainerRef,
private enumService: ConfigurableEnumService,
) {}

/**
* Make sure the template checker knows the type of the context with which the
* template of this directive will be rendered
* See {@link https://angular.io/guide/structural-directives#typing-the-directives-context}
* @param directive
* @param context
*/

static ngTemplateContextGuard(
directive: ConfigurableEnumDirective,
context: unknown,
): context is { $implicit: ConfigurableEnumValue } {
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export namespace Ordering {
* of 'greater' or 'less' than is dependent on the concrete enum.
*/
export interface HasOrdinal {
_ordinal: number;
_ordinal?: number;
}

export function hasOrdinalValue(value: any): value is HasOrdinal {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import { Ordering } from "./configurable-enum-ordering";
import HasOrdinal = Ordering.HasOrdinal;

/**
* Interface specifying overall object representing an enum with all its options
* as stored in the config database
Expand All @@ -10,7 +13,7 @@ export type ConfigurableEnumConfig<
* Mandatory properties of each option of an configurable enum
* the actual object can contain additional properties in the specific context of that enum (e.g. a `color` property)
*/
export interface ConfigurableEnumValue {
export interface ConfigurableEnumValue extends HasOrdinal {
/**
* identifier that is unique among all values of the same enum and does not change even when label or other things are edited
*/
Expand All @@ -33,19 +36,12 @@ export interface ConfigurableEnumValue {
isInvalidOption?: boolean;

/**
* Optionally any number of additional properties specific to a certain enum collection.
* optional styling class that should be applied when displaying this value
*/
[x: string]: any;
style?: string;
}

export const EMPTY: ConfigurableEnumValue = {
id: "",
label: "",
};

/**
* The prefix of all enum collection entries in the config database.
*
* This prefix is concatenated with the individual enum collection's id, resulting in the full config object id.
*/
export const CONFIGURABLE_ENUM_CONFIG_PREFIX = "enum:";
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { EntityAbility } from "../../permissions/ability/entity-ability";

@Injectable({ providedIn: "root" })
export class ConfigurableEnumService {
private enums: Map<string, ConfigurableEnum>;
private enums = new Map<string, ConfigurableEnum>();

constructor(
private entityMapper: EntityMapperService,
Expand All @@ -20,7 +20,6 @@ export class ConfigurableEnumService {

async preLoadEnums() {
const allEnums = await this.entityMapper.loadType(ConfigurableEnum);
this.enums = new Map();
allEnums.forEach((entity) => this.cacheEnum(entity));
}

Expand Down
Loading