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

Feat/table component #2786

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"@angular/core": "~17.2.2",
"@angular/elements": "^17.2.2",
"@angular/forms": "~17.2.2",
"@angular/material": "^17",
"@angular/platform-browser": "~17.2.2",
"@angular/platform-browser-dynamic": "~17.2.2",
"@angular/router": "~17.2.2",
Expand Down Expand Up @@ -122,6 +123,7 @@
"@angular-eslint/eslint-plugin-template": "17.2.1",
"@angular-eslint/schematics": "17.2.1",
"@angular-eslint/template-parser": "17.2.1",
"@angular/cdk": "^17",
"@angular/cli": "~17.2.1",
"@angular/compiler": "~17.2.2",
"@angular/compiler-cli": "~17.2.2",
Expand Down
3 changes: 3 additions & 0 deletions src/app/shared/components/template/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import { TmplRadioGroupComponent } from "./radio-group/radio-group.component";
import { TmplSimpleCheckboxComponent } from "./simple-checkbox/simple-checkbox.component";
import { TmplSliderComponent } from "./slider/slider.component";
import { TmplSubtitleComponent } from "./subtitle";
import { TmplTableComponent } from "./table/table.component";
import { TmplTaskCardComponent } from "./task-card/task-card.component";
import { TmplTaskProgressBarComponent } from "./task-progress-bar/task-progress-bar.component";
import { TmplTextAreaComponent } from "./text-area/text-area.component";
Expand Down Expand Up @@ -113,6 +114,7 @@ export const TEMPLATE_COMPONENTS = [
TmplSimpleCheckboxComponent,
TmplSliderComponent,
TmplSubtitleComponent,
TmplTableComponent,
TmplTaskCardComponent,
TmplTaskProgressBarComponent,
TmplTextAreaComponent,
Expand Down Expand Up @@ -186,6 +188,7 @@ const COMMON_COMPONENT_MAPPING = {
slider: TmplSliderComponent,
square_button: SquareIconButtonComponent,
subtitle: TmplSubtitleComponent,
table: TmplTableComponent,
task_card: TmplTaskCardComponent,
task_progress_bar: TmplTaskProgressBarComponent,
text_area: TmplTextAreaComponent,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<div class="table-container">
<table mat-table [dataSource]="dataSource" matSort>
<ng-container *ngFor="let column of columnsToDisplay" [matColumnDef]="column">
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ column }}</th>
<td mat-cell *matCellDef="let element">{{ element[column] }}</td>
</ng-container>

<tr mat-header-row *matHeaderRowDef="columnsToDisplay; sticky: true"></tr>
<tr mat-row *matRowDef="let row; columns: columnsToDisplay"></tr>
</table>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.table-container {
width: 100%;
max-height: 80vh;
overflow: auto;

tr[mat-header-row] {
background-color: var(--ion-background-color);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import {
AfterViewInit,
ChangeDetectorRef,
Component,
computed,
effect,
OnInit,
ViewChild,
} from "@angular/core";
import { TemplateBaseComponent } from "../base";
import { FlowTypes } from "packages/data-models";
import { getBooleanParamFromTemplateRow } from "src/app/shared/utils";
import { AppDataService } from "src/app/shared/services/data/app-data.service";
import { MatTableDataSource } from "@angular/material/table";
import { MatSort } from "@angular/material/sort";

interface ITableParams {
/** TEMPLATE PARAMETER: show_id. Display the data lists's ID column in the table. Default false */
showId: boolean;
}

@Component({
selector: "plh-table",
templateUrl: "./table.component.html",
styleUrls: ["./table.component.scss"],
})
export class TmplTableComponent extends TemplateBaseComponent implements AfterViewInit {
params = computed(() => this.getParams(this.parameterList()));
dataRows = computed(() => this.getDataRowsFromValue(this.value()));
Copy link
Member

Choose a reason for hiding this comment

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

nit(blocking): the getDataRows from value function is async, so this computed will not behave as expected (no such thing as async computed). Instead it would be better to use an effect to respond to value changes and set the dataRows as a signal. I think it still kinda works in the frontend due to the additional cdr calls, but I'd consider that to be more of a bug than expected behaviour


columnsToDisplay = [];
dataSource: MatTableDataSource<any>;

@ViewChild(MatSort) sort: MatSort;

constructor(
private appDataService: AppDataService,
private cdr: ChangeDetectorRef
) {
super();
effect(async () => {
const data = (await this.dataRows()) || [];
this.dataSource = new MatTableDataSource(data);
this.columnsToDisplay = this.getColumnsToDisplayFromData(data);
if (this.sort) {
this.dataSource.sort = this.sort;
}
this.cdr.detectChanges();
});
}

ngAfterViewInit() {
if (this.dataSource) {
this.dataSource.sort = this.sort;
}
}

private getParams(authorParams: FlowTypes.TemplateRow["parameter_list"]): ITableParams {
return {
showId: getBooleanParamFromTemplateRow(this._row, "show_id", false),
};
}

private async getDataRowsFromValue(dataListName: string) {
// TODO: handle data passed as @data format, and query dynamic data to return live data (see data-items handling)
if (typeof dataListName !== "string") {
console.log("[TABLE COMPONENT] Value must be a data list name string");
return [];
}
const dataList = await this.appDataService.getSheet("data_list", dataListName);
return dataList?.rows;
}

private getColumnsToDisplayFromData(data: FlowTypes.Data_listRow[]): string[] {
if (!data || !data.length) return [];
// Infer columns from first data row
const columnNames = Object.keys(data[0]);
return this.params().showId
? columnNames
: columnNames.filter((columnName) => columnName !== "id");
}
}
12 changes: 8 additions & 4 deletions src/app/shared/components/template/template.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { NouisliderModule } from "ng2-nouislider";
import { RouterModule } from "@angular/router";
import { SwiperModule } from "swiper/angular";
import { NgxExtendedPdfViewerModule } from "ngx-extended-pdf-viewer";
import { MatTableModule } from "@angular/material/table";
import { MatSortModule } from "@angular/material/sort";

import { SharedPipesModule } from "../../pipes";
import { TooltipDirective } from "../common/directives/tooltip.directive";
Expand All @@ -26,14 +28,16 @@ import { DEMO_COMPONENTS } from "packages/components/demo";
imports: [
CommonModule,
FormsModule,
ReactiveFormsModule,
IonicModule,
SharedPipesModule,
NouisliderModule,
LottieModule,
MatSortModule,
MatTableModule,
Copy link
Member

Choose a reason for hiding this comment

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

nit(non-blocking): I can't remember how well we've implemented optimisation for this module, but might be more viable to include the imports in the table component itself and turn into a standalone component so that the modules are easier to remove from build when the component is not needed

NgxExtendedPdfViewerModule,
NouisliderModule,
ReactiveFormsModule,
RouterModule,
SharedPipesModule,
SwiperModule,
NgxExtendedPdfViewerModule,
TemplatePipesModule,
],
exports: [
Expand Down
Loading
Loading