From e26caf7fc00c6fe93d10244ab1b0ea552f53805f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=9C=E5=86=A0=E9=AD=81?= Date: Sun, 6 Sep 2026 21:02:21 +0900 Subject: [PATCH] fix(flex-fields): make DateTimeViewComponent respect DateTime.InputMode DateTimeViewComponent always rendered value | shortDateTime, ignoring the field's DateTime.InputMode configuration. Date/Month-mode fields therefore showed a spurious time part in every read-only context, including Table columns dispatched through ff-table-view. Mirror DateTimeControlComponent's approach: look up the configured mode's format in DATE_INPUT_MODE_FORMATS and format with DatePipe, falling back to the shortDateTime pipe when no configuration/mode is available. Fixes #234 --- CHANGELOG.md | 17 +++++ .../date/date-time-view.component.html | 4 +- .../date/date-time-view.component.spec.ts | 76 +++++++++++++++++-- .../date/date-time-view.component.ts | 21 ++++- 4 files changed, 108 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 22a20799..8ff83171 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -80,6 +80,23 @@ so it stays clear which part of the repository actually moved. family-wide: `true` silences every bundle loaded through this service, in every `@dignite/ng.flex-fields*` package. (#232) +### Fixed + +#### flex-fields + +- **`DateTimeViewComponent`, the read-only view for `DateTime` fields, ignored the field's + `DateTime.InputMode` configuration and always rendered `value | shortDateTime`.** A field configured + for `InputMode = Date` or `InputMode = Month` therefore showed a spurious time part in every + read-only context — a bare field, or a `Table` column rendered through `ff-table-view`, which + dispatches to this same component via `ff-flex-field-view`. The edit-mode counterpart, + `DateTimeControlComponent`, already read `configuration['DateTime.InputMode']`, looked up the + matching Angular `DatePipe` format string in `DATE_INPUT_MODE_FORMATS`, and formatted with it — the + view component never did the same lookup despite receiving the same `field.configuration` on its + `fields` input. `DateTimeViewComponent` now performs that lookup itself, injecting `DatePipe` the + same way, and falls back to the `shortDateTime` pipe only when no `fields` input is bound or the + configured mode isn't in the table, so existing usages that never pass `field.configuration` keep + rendering exactly as before. + ## [10.0.0-rc.16] - 2026-09-05 ### Fixed diff --git a/flex-fields/angular/projects/flex-fields/src/lib/field-types/date/date-time-view.component.html b/flex-fields/angular/projects/flex-fields/src/lib/field-types/date/date-time-view.component.html index 25875d4b..a223c75e 100644 --- a/flex-fields/angular/projects/flex-fields/src/lib/field-types/date/date-time-view.component.html +++ b/flex-fields/angular/projects/flex-fields/src/lib/field-types/date/date-time-view.component.html @@ -1,10 +1,10 @@ @if (showInList) { - {{ value | shortDateTime }} + {{ formattedValue ?? (value | shortDateTime) }} } @else {
-
{{ value | shortDateTime }}
+
{{ formattedValue ?? (value | shortDateTime) }}
} diff --git a/flex-fields/angular/projects/flex-fields/src/lib/field-types/date/date-time-view.component.spec.ts b/flex-fields/angular/projects/flex-fields/src/lib/field-types/date/date-time-view.component.spec.ts index aa45d99c..bf39757d 100644 --- a/flex-fields/angular/projects/flex-fields/src/lib/field-types/date/date-time-view.component.spec.ts +++ b/flex-fields/angular/projects/flex-fields/src/lib/field-types/date/date-time-view.component.spec.ts @@ -1,7 +1,36 @@ import { TestBed } from '@angular/core/testing'; import { ConfigStateService } from '@abp/ng.core'; import { CoreTestingModule } from '@abp/ng.core/testing'; +import { FlexFieldValue } from '../../models'; import { DateTimeViewComponent } from './date-time-view.component'; +import { DateTimeInputMode } from './date-time-input-mode'; + +function fieldValue(overrides: Partial = {}): FlexFieldValue { + return { + field: { + id: '1', + name: 'publishedAt', + displayName: 'Published', + fieldTypeName: 'DateTime', + configuration: {}, + }, + required: false, + searchable: false, + ...overrides, + }; +} + +function withMode(mode: DateTimeInputMode): FlexFieldValue { + return fieldValue({ + field: { + id: '1', + name: 'publishedAt', + displayName: 'Published', + fieldTypeName: 'DateTime', + configuration: { 'DateTime.InputMode': mode }, + }, + }); +} describe('DateTimeViewComponent', () => { beforeEach(() => { @@ -20,22 +49,57 @@ describe('DateTimeViewComponent', () => { } as unknown as Parameters[0]); }); - function render(value: unknown, showInList = false) { + function render(value: unknown, options: { showInList?: boolean; fields?: FlexFieldValue } = {}) { const fixture = TestBed.createComponent(DateTimeViewComponent); fixture.componentRef.setInput('value', value); - fixture.componentRef.setInput('showInList', showInList); + fixture.componentRef.setInput('showInList', options.showInList ?? false); + if (options.fields) { + fixture.componentRef.setInput('fields', options.fields); + } fixture.detectChanges(); return fixture; } + // Local-time construction, not an ISO `Z` string: DatePipe formats in local time, so building the + // expectation from the same local wall-clock value keeps the test independent of the machine's TZ. + const localDateTime = new Date(2026, 7, 17, 10, 30, 0); + it('renders nothing for an unset value instead of throwing', () => { expect(() => render('')).not.toThrow(); }); - it('renders a formatted value both in and out of list mode', () => { - const localDateTime = new Date(2026, 7, 17, 10, 30, 0); + it('falls back to the short date-time format when no configuration is available', () => { + expect(render(localDateTime, { showInList: true }).nativeElement.textContent).toContain('2026'); + expect(render(localDateTime, { showInList: false }).nativeElement.textContent).toContain('2026'); + }); + + it('formats the value to the Date input mode, without a time part', () => { + const text = render(localDateTime, { fields: withMode(DateTimeInputMode.Date) }).nativeElement.textContent; + + expect(text).toContain('2026-08-17'); + expect(text).not.toContain('10:30'); + }); + + it('formats the value to the DateTime input mode', () => { + const text = render(localDateTime, { fields: withMode(DateTimeInputMode.DateTime) }).nativeElement.textContent; + + expect(text).toContain('2026-08-17 10:30:00'); + }); + + it('formats the value to the Month input mode, without a day part', () => { + const text = render(localDateTime, { fields: withMode(DateTimeInputMode.Month) }).nativeElement.textContent; + + expect(text).toContain('2026-08'); + expect(text).not.toContain('2026-08-17'); + }); + + it('applies the configured input mode in list mode too, e.g. a Table-nested column', () => { + const text = render(localDateTime, { + showInList: true, + fields: withMode(DateTimeInputMode.Date), + }).nativeElement.textContent; - expect(render(localDateTime, true).nativeElement.textContent).toContain('2026'); - expect(render(localDateTime, false).nativeElement.textContent).toContain('2026'); + expect(text).toContain('2026-08-17'); + expect(text).not.toContain('10:30'); }); }); diff --git a/flex-fields/angular/projects/flex-fields/src/lib/field-types/date/date-time-view.component.ts b/flex-fields/angular/projects/flex-fields/src/lib/field-types/date/date-time-view.component.ts index 5c1abff4..b6ceb145 100644 --- a/flex-fields/angular/projects/flex-fields/src/lib/field-types/date/date-time-view.component.ts +++ b/flex-fields/angular/projects/flex-fields/src/lib/field-types/date/date-time-view.component.ts @@ -1,14 +1,24 @@ -import { Component, Input } from '@angular/core'; +import { Component, inject, Input } from '@angular/core'; import { CoreModule } from '@abp/ng.core'; +import { DatePipe } from '@angular/common'; import { FlexFieldValue } from '../../models'; +import { DATE_INPUT_MODE_FORMATS } from './date-time-configuration'; +import { DateTimeInputMode } from './date-time-input-mode'; -/** Displays the value of a `DateTime` field read-only, in the user's short date-time format. */ +/** + * Displays the value of a `DateTime` field read-only, formatted per its `DateTime.InputMode` + * configuration (mirroring `date-time-control.component.ts`'s edit-mode formatting). Falls back to + * the user's short date-time format when no configuration is available to read a mode from. + */ @Component({ selector: 'ff-date-time-view', templateUrl: './date-time-view.component.html', imports: [CoreModule], + providers: [DatePipe], }) export class DateTimeViewComponent { + private readonly datePipe = inject(DatePipe); + /** Renders bare, without the label wrapper, for use inside a table cell. */ @Input() showInList = false; @@ -18,4 +28,11 @@ export class DateTimeViewComponent { @Input() type?: string; @Input() value: unknown = ''; + + /** `undefined` when `fields` carries no known `InputMode`, so the template falls back to `shortDateTime`. */ + get formattedValue(): string | null | undefined { + const mode = this.fields?.field.configuration['DateTime.InputMode'] as DateTimeInputMode; + const format = DATE_INPUT_MODE_FORMATS[mode]?.format; + return format ? this.datePipe.transform(this.value as string, format) : undefined; + } }