From ad206baafb4b29358598c684b6d81853adb89806 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Mon, 4 Apr 2022 17:17:14 +0200 Subject: [PATCH] Format components. --- .../shared/forms/array-item.component.ts | 12 +- .../src/app/framework/utils/types.spec.ts | 25 ++++ frontend/src/app/framework/utils/types.ts | 38 ++++++ .../state/contents.forms.visitors.spec.ts | 30 +++-- .../shared/state/contents.forms.visitors.ts | 117 ++++++++++-------- 5 files changed, 163 insertions(+), 59 deletions(-) diff --git a/frontend/src/app/features/content/shared/forms/array-item.component.ts b/frontend/src/app/features/content/shared/forms/array-item.component.ts index 38b161586..afff0429d 100644 --- a/frontend/src/app/features/content/shared/forms/array-item.component.ts +++ b/frontend/src/app/features/content/shared/forms/array-item.component.ts @@ -140,9 +140,14 @@ export class ArrayItemComponent implements OnChanges { function getTitle(formModel: ObjectFormBase) { const value = formModel.form.value; const values: string[] = []; + + let valueLength = 0; if (Types.is(formModel, ComponentForm) && formModel.schema) { - values.push(formModel.schema.displayName); + const formatted = formModel.schema.displayName; + + values.push(formatted); + valueLength += formatted.length; } if (Types.is(formModel.field, RootFieldDto)) { @@ -154,6 +159,11 @@ function getTitle(formModel: ObjectFormBase) { if (formatted) { values.push(formatted); + valueLength += formatted.length; + + if (valueLength > 30) { + break; + } } } } diff --git a/frontend/src/app/framework/utils/types.spec.ts b/frontend/src/app/framework/utils/types.spec.ts index a360c53e6..e74dd3062 100644 --- a/frontend/src/app/framework/utils/types.spec.ts +++ b/frontend/src/app/framework/utils/types.spec.ts @@ -265,6 +265,31 @@ describe('Types', () => { array: [4, 8], }); }); + + it('Should format true', () => { + const result = Types.booleanToString(true); + + expect(result).toEqual('Yes'); + }); + + it('Should format false', () => { + const result = Types.booleanToString(false); + + expect(result).toEqual('No'); + }); + + it('Should format object', () => { + const input = { + field1: 'Hello', + field2: 1, + field3: true, + field4: [], + }; + + const result = Types.objectToString(input); + + expect(result).toEqual('Hello, 1, Yes'); + }); }); class MyClass { diff --git a/frontend/src/app/framework/utils/types.ts b/frontend/src/app/framework/utils/types.ts index f858212bf..1d12545ba 100644 --- a/frontend/src/app/framework/utils/types.ts +++ b/frontend/src/app/framework/utils/types.ts @@ -205,4 +205,42 @@ export module Types { return target; } + + export function booleanToString(value: boolean) { + return value ? 'Yes' : 'No'; + } + + export function objectToString(value: Object, exclude?: string[], maxLength = 100) { + let result = ''; + + for (const [key, property] of Object.entries(value)) { + let formatted = ''; + + if (exclude && exclude.indexOf(key) >= 0) { + continue; + } + + if (Types.isString(property)) { + formatted = property; + } else if (Types.isNumber(property)) { + formatted = `${property}`; + } else if (Types.isBoolean(property)) { + formatted = booleanToString(property); + } + + if (formatted) { + if (result.length > 0) { + result += ', '; + } + + result += formatted; + } + + if (result.length >= maxLength) { + break; + } + } + + return result; + } } diff --git a/frontend/src/app/shared/state/contents.forms.visitors.spec.ts b/frontend/src/app/shared/state/contents.forms.visitors.spec.ts index 98ba1b76a..94ac72fc0 100644 --- a/frontend/src/app/shared/state/contents.forms.visitors.spec.ts +++ b/frontend/src/app/shared/state/contents.forms.visitors.spec.ts @@ -90,8 +90,16 @@ describe('ComponentField', () => { expect(FieldFormatter.format(field, null)).toBe(''); }); - it('should format to constant', () => { - expect(FieldFormatter.format(field, {})).toBe('{ Component }'); + it('should format to constant when empty', () => { + expect(FieldFormatter.format(field, { schemaId: '123' })).toBe('Component'); + }); + + it('should format to constant when values give', () => { + expect(FieldFormatter.format(field, { schemaId: '123', field1: 'Hello', field2: 1 })).toBe('Component: Hello, 1'); + }); + + it('should format to constant when result is empty', () => { + expect(FieldFormatter.format(field, { schemaId: '123', field1: [] })).toBe('Component'); }); it('should return default value as null', () => { @@ -240,6 +248,10 @@ describe('GeolocationField', () => { expect(FieldFormatter.format(field, null)).toBe(''); }); + it('should format to empty string if other type', () => { + expect(FieldFormatter.format(field, 'Text')).toBe(''); + }); + it('should format to latitude and longitude', () => { expect(FieldFormatter.format(field, { latitude: 42, longitude: 3.14 })).toBe('3.14, 42'); }); @@ -280,6 +292,10 @@ describe('NumberField', () => { expect(FieldFormatter.format(field, null)).toBe(''); }); + it('should format to empty string if other type', () => { + expect(FieldFormatter.format(field, 'Text')).toBe(''); + }); + it('should format to number', () => { expect(FieldFormatter.format(field, 42)).toEqual('42'); }); @@ -393,7 +409,7 @@ describe('StringField', () => { it('should not format to preview image if not unsplash image', () => { const field2 = createField({ properties: createProperties('String', { editor: 'StockPhoto' }) }); - expect(FieldFormatter.format(field2, 'https://images.com/123?x', true)).toBe('https://images.com/123?x'); + expect(FieldFormatter.format(field2, 'https://images.com/123?x', true)).toEqual(new HtmlValue('')); }); it('should return default value from properties', () => { @@ -420,12 +436,12 @@ describe('TagsField', () => { expect(FieldFormatter.format(field, null)).toBe(''); }); - it('should format to asset count', () => { - expect(FieldFormatter.format(field, ['hello', 'squidex', 'cms'])).toBe('hello, squidex, cms'); + it('should format to empty string if other type', () => { + expect(FieldFormatter.format(field, 'Text')).toBe(''); }); - it('should return zero formatting if other type', () => { - expect(FieldFormatter.format(field, 1)).toBe(''); + it('should format to asset count', () => { + expect(FieldFormatter.format(field, ['hello', 'squidex', 'cms'])).toBe('hello, squidex, cms'); }); it('should return default value from properties', () => { diff --git a/frontend/src/app/shared/state/contents.forms.visitors.ts b/frontend/src/app/shared/state/contents.forms.visitors.ts index 8560d5c03..e1f462482 100644 --- a/frontend/src/app/shared/state/contents.forms.visitors.ts +++ b/frontend/src/app/shared/state/contents.forms.visitors.ts @@ -119,16 +119,26 @@ export class FieldFormatter implements FieldPropertiesVisitor { return this.formatArray('Asset', 'Assets'); } + public visitComponents(_: ComponentsFieldPropertiesDto): string { + return this.formatArray('Component', 'Components'); + } + + public visitReferences(_: ReferencesFieldPropertiesDto): string { + return this.formatArray('Reference', 'References'); + } + public visitBoolean(_: BooleanFieldPropertiesDto): string { - return this.value ? 'Yes' : 'No'; + return Types.booleanToString(this.value); } public visitComponent(_: ComponentFieldPropertiesDto): string { - return '{ Component }'; - } + const inner = Types.objectToString(this.value, ['schemaId'], 100); - public visitComponents(_: ComponentsFieldPropertiesDto): string { - return this.formatArray('Component', 'Components'); + if (inner.length > 0) { + return `Component: ${inner}`; + } else { + return 'Component'; + } } public visitDateTime(properties: DateTimeFieldPropertiesDto): FieldValue { @@ -145,15 +155,19 @@ export class FieldFormatter implements FieldPropertiesVisitor { } } - public visitGeolocation(_: GeolocationFieldPropertiesDto): string { - return `${this.value.longitude}, ${this.value.latitude}`; - } - public visitJson(_: JsonFieldPropertiesDto): string { return ''; } + public visitUI(_: UIFieldPropertiesDto): any { + return ''; + } + public visitNumber(properties: NumberFieldPropertiesDto): FieldValue { + if (!Types.isNumber(this.value)) { + return ''; + } + if (Types.isNumber(this.value) && properties.editor === 'Stars' && this.allowHtml) { if (this.value <= 0 || this.value > 6) { return new HtmlValue(`★ ${this.value}`); @@ -167,47 +181,48 @@ export class FieldFormatter implements FieldPropertiesVisitor { return new HtmlValue(html); } } + return `${this.value}`; } - public visitReferences(_: ReferencesFieldPropertiesDto): string { - return this.formatArray('Reference', 'References'); + public visitGeolocation(_: GeolocationFieldPropertiesDto): string { + if (!Types.isObject(this.value)) { + return ''; + } + + return `${this.value.longitude}, ${this.value.latitude}`; } public visitTags(_: TagsFieldPropertiesDto): string { - if (this.value.length) { - return this.value.join(', '); - } else { + if (!Types.isArrayOfString(this.value)) { return ''; } + + return this.value.join(', '); } public visitString(properties: StringFieldPropertiesDto): any { - if (properties.editor === 'StockPhoto' && this.allowHtml && this.value) { - const src = thumbnail(this.value, undefined, 50); + if (!Types.isString(this.value)) { + return ''; + } - if (src) { - return new HtmlValue(``); - } + if (properties.editor === 'StockPhoto' && this.allowHtml && this.value) { + return new HtmlValue(``); } return this.value; } - public visitUI(_: UIFieldPropertiesDto): any { - return ''; - } - private formatArray(singularName: string, pluralName: string) { - if (Types.isArray(this.value)) { - if (this.value.length > 1) { - return `${this.value.length} ${pluralName}`; - } else if (this.value.length === 1) { - return `1 ${singularName}`; - } + if (!Types.isArray(this.value)) { + return `0 ${pluralName}`; } - return `0 ${pluralName}`; + if (this.value.length > 1) { + return `${this.value.length} ${pluralName}`; + } else { + return `1 ${singularName}`; + } } } @@ -222,7 +237,7 @@ export function thumbnail(url: string, width?: number, height?: number) { } } - return undefined; + return url; } export class FieldsValidators implements FieldPropertiesVisitor> { @@ -265,14 +280,6 @@ export class FieldsValidators implements FieldPropertiesVisitor { - return []; - } - - public visitComponent(_: ComponentFieldPropertiesDto): ReadonlyArray { - return []; - } - public visitComponents(properties: ComponentsFieldPropertiesDto): ReadonlyArray { const validators: ValidatorFn[] = [ ValidatorsEx.betweenLength(properties.minItems, properties.maxItems), @@ -285,18 +292,6 @@ export class FieldsValidators implements FieldPropertiesVisitor { - return []; - } - - public visitGeolocation(_: GeolocationFieldPropertiesDto): ReadonlyArray { - return []; - } - - public visitJson(_: JsonFieldPropertiesDto): ReadonlyArray { - return []; - } - public visitNumber(properties: NumberFieldPropertiesDto): ReadonlyArray { const validators: ValidatorFn[] = [ ValidatorsEx.between(properties.minValue, properties.maxValue), @@ -363,6 +358,26 @@ export class FieldsValidators implements FieldPropertiesVisitor { + return []; + } + + public visitComponent(_: ComponentFieldPropertiesDto): ReadonlyArray { + return []; + } + + public visitDateTime(_: DateTimeFieldPropertiesDto): ReadonlyArray { + return []; + } + + public visitGeolocation(_: GeolocationFieldPropertiesDto): ReadonlyArray { + return []; + } + + public visitJson(_: JsonFieldPropertiesDto): ReadonlyArray { + return []; + } + public visitUI(_: UIFieldPropertiesDto): ReadonlyArray { return []; }