diff --git a/docs/process-services-cloud/components/group-cloud.component.md b/docs/process-services-cloud/components/group-cloud.component.md index 7967a002b4c..0303b10ba41 100644 --- a/docs/process-services-cloud/components/group-cloud.component.md +++ b/docs/process-services-cloud/components/group-cloud.component.md @@ -42,6 +42,7 @@ Searches Groups. | mode | [`ComponentSelectionMode`](../../../lib/process-services-cloud/src/lib/types.ts) | "single" | Group selection mode (single/multiple). | | preSelectGroups | [`IdentityGroupModel`](../../../lib/core/models/identity-group.model.ts)`[]` | \[] | Array of groups to be pre-selected. This pre-selects all groups in multi selection mode and only the first group of the array in single selection mode. | | readOnly | `boolean` | false | Show the info in readonly mode | +| required | `boolean` | false | Mark this field as required | | roles | `string[]` | \[] | Role names of the groups to be listed. | | searchGroupsControl | `FormControl` | | FormControl to search the group | | title | `string` | | Title of the field | diff --git a/docs/process-services-cloud/components/people-cloud.component.md b/docs/process-services-cloud/components/people-cloud.component.md index d069e737cdd..021b6123888 100644 --- a/docs/process-services-cloud/components/people-cloud.component.md +++ b/docs/process-services-cloud/components/people-cloud.component.md @@ -30,6 +30,7 @@ Allows one or more users to be selected (with auto-suggestion) based on the inpu | mode | [`ComponentSelectionMode`](../../../lib/process-services-cloud/src/lib/types.ts) | "single" | User selection mode (single/multiple). | | preSelectUsers | [`IdentityUserModel`](../../../lib/core/models/identity-user.model.ts)`[]` | \[] | Array of users to be pre-selected. All users in the array are pre-selected in multi selection mode, but only the first user is pre-selected in single selection mode. Mandatory properties are: id, email, username | | readOnly | `boolean` | false | Show the info in readonly mode | +| required | `boolean` | false | Mark this field as required | | roles | `string[]` | | Role names of the users to be listed. | | searchUserCtrl | `FormControl` | | FormControl to search the user | | title | `string` | | Placeholder translation key | diff --git a/e2e-playwright/process-services-cloud/specs/groups-cloud.e2e.ts b/e2e-playwright/process-services-cloud/specs/groups-cloud.e2e.ts index f79d257eb3e..0d837201e22 100644 --- a/e2e-playwright/process-services-cloud/specs/groups-cloud.e2e.ts +++ b/e2e-playwright/process-services-cloud/specs/groups-cloud.e2e.ts @@ -40,11 +40,12 @@ test.describe('Groups component stories tests', () => { }); test('Invalid Preselected Groups', async ({ processServicesCloud, groupComponent }) => { - const expectedWarningMessage = 'warning No group found with the name invalid groups'; + const expectedWarningMessage = 'No group found with the name invalid groups'; + const expectedWarningIcon = 'error_outline'; await processServicesCloud.navigateTo({ componentName: 'group', story: 'invalid-preselected-groups' }); - await expect(groupComponent.error.content).toContainText(expectedWarningMessage); + await expect(groupComponent.error.content).toContainText(expectedWarningIcon + expectedWarningMessage); }); }); diff --git a/e2e-playwright/process-services-cloud/specs/people-cloud.e2e.ts b/e2e-playwright/process-services-cloud/specs/people-cloud.e2e.ts index ff8d9b5c291..555aa9c1d9a 100644 --- a/e2e-playwright/process-services-cloud/specs/people-cloud.e2e.ts +++ b/e2e-playwright/process-services-cloud/specs/people-cloud.e2e.ts @@ -39,11 +39,12 @@ test.describe('People component stories tests', () => { }); test('Invalid Preselected Users', async ({ processServicesCloud, peopleComponent }) => { - const expectedWarningMessage = 'warning No user found with the username invalid user'; + const expectedWarningMessage = 'No user found with the username invalid user'; + const expectedWarningIcon = 'error_outline'; await processServicesCloud.navigateTo({ componentName: 'people', story: 'invalid-preselected-users' }); - await expect(peopleComponent.error.content).toContainText(expectedWarningMessage); + await expect(peopleComponent.error.content).toContainText(expectedWarningIcon + expectedWarningMessage); }); test('Excluded Users', async ({ processServicesCloud, peopleComponent }) => { diff --git a/lib/core/form/components/form-renderer.component.spec.ts b/lib/core/form/components/form-renderer.component.spec.ts index 9f349b407f7..57766a4748d 100644 --- a/lib/core/form/components/form-renderer.component.spec.ts +++ b/lib/core/form/components/form-renderer.component.spec.ts @@ -45,43 +45,39 @@ import { By } from '@angular/platform-browser'; import { DebugElement } from '@angular/core'; const typeIntoInput = (targetInput: HTMLInputElement, message: string) => { - expect(targetInput).not.toBeNull('Expected input to set to be valid and not null'); + expect(targetInput).toBeTruthy('Expected input to set to be valid and not null'); targetInput.value = message; targetInput.dispatchEvent(new Event('input')); }; const typeIntoDate = (targetInput: DebugElement, date: { srcElement: { value: string } }) => { - expect(targetInput).not.toBeNull('Expected input to set to be valid and not null'); + expect(targetInput).toBeTruthy('Expected input to set to be valid and not null'); targetInput.triggerEventHandler('change', date); }; const expectElementToBeHidden = (targetElement: HTMLElement): void => { - expect(targetElement).not.toBeNull(); - expect(targetElement).toBeDefined(); + expect(targetElement).toBeTruthy(); expect(targetElement.hidden).toBe(true, `${targetElement.id} should be hidden but it is not`); }; const expectElementToBeVisible = (targetElement: HTMLElement): void => { - expect(targetElement).not.toBeNull(); - expect(targetElement).toBeDefined(); + expect(targetElement).toBeTruthy(); expect(targetElement.hidden).toBe(false, `${targetElement.id} should be visibile but it is not`); }; const expectInputElementValueIs = (targetElement: HTMLInputElement, value: string): void => { - expect(targetElement).not.toBeNull(); - expect(targetElement).toBeDefined(); + expect(targetElement).toBeTruthy(); expect(targetElement.value).toBe(value, `invalid value for ${targetElement.name}`); }; const expectElementToBeInvalid = (fieldId: string, fixture: ComponentFixture): void => { const invalidElementContainer = fixture.nativeElement.querySelector(`#field-${fieldId}-container .adf-invalid`); - expect(invalidElementContainer).not.toBeNull(); - expect(invalidElementContainer).toBeDefined(); + expect(invalidElementContainer).toBeTruthy(); }; const expectElementToBeValid = (fieldId: string, fixture: ComponentFixture): void => { const invalidElementContainer = fixture.nativeElement.querySelector(`#field-${fieldId}-container .adf-invalid`); - expect(invalidElementContainer).toBeNull(); + expect(invalidElementContainer).toBeFalsy(); }; describe('Form Renderer Component', () => { @@ -407,6 +403,12 @@ describe('Form Renderer Component', () => { const numberInputRequired: HTMLInputElement = fixture.nativeElement.querySelector('#Number0x8cbv'); expectElementToBeVisible(numberInputRequired); + expectElementToBeValid('Number0x8cbv', fixture); + + numberInputRequired.dispatchEvent(new Event('blur')); + fixture.detectChanges(); + await fixture.whenStable(); + expectElementToBeInvalid('Number0x8cbv', fixture); typeIntoInput(numberInputRequired, '5'); @@ -444,6 +446,7 @@ describe('Form Renderer Component', () => { expectElementToBeVisible(numberInputElement); expectElementToBeValid('Number0him2z', fixture); + numberInputElement.dispatchEvent(new Event('blur')); typeIntoInput(numberInputElement, '9'); fixture.detectChanges(); await fixture.whenStable(); diff --git a/lib/core/form/components/widgets/amount/amount.widget.html b/lib/core/form/components/widgets/amount/amount.widget.html index 198eae35dba..63f4322862d 100644 --- a/lib/core/form/components/widgets/amount/amount.widget.html +++ b/lib/core/form/components/widgets/amount/amount.widget.html @@ -1,8 +1,8 @@
+ [attr.for]="field.id">{{field.name | translate }}* {{ currency }}   + [disabled]="field.readOnly" + (blur)="markAsTouched()"> -
diff --git a/lib/core/form/components/widgets/amount/amount.widget.spec.ts b/lib/core/form/components/widgets/amount/amount.widget.spec.ts index a6a6707e618..8e842a289d6 100644 --- a/lib/core/form/components/widgets/amount/amount.widget.spec.ts +++ b/lib/core/form/components/widgets/amount/amount.widget.spec.ts @@ -20,14 +20,16 @@ import { FormFieldModel } from './../core/form-field.model'; import { AmountWidgetComponent, ADF_AMOUNT_SETTINGS } from './amount.widget'; import { setupTestBed } from '../../../../testing/setup-test-bed'; import { FormBaseModule } from '../../../form-base.module'; -import { FormModel } from '../core'; +import { FormFieldTypes } from '../core/form-field-types'; import { CoreTestingModule } from '../../../../testing/core.testing.module'; import { TranslateModule } from '@ngx-translate/core'; +import { FormModel } from '../core/form.model'; describe('AmountWidgetComponent', () => { let widget: AmountWidgetComponent; let fixture: ComponentFixture; + let element: HTMLElement; setupTestBed({ imports: [ @@ -39,8 +41,8 @@ describe('AmountWidgetComponent', () => { beforeEach(() => { fixture = TestBed.createComponent(AmountWidgetComponent); - widget = fixture.componentInstance; + element = fixture.nativeElement; }); it('should setup currency from field', () => { @@ -78,6 +80,38 @@ describe('AmountWidgetComponent', () => { widget.ngOnInit(); expect(widget.placeholder).toBe('1234'); }); + + describe('when is required', () => { + + beforeEach(() => { + widget.field = new FormFieldModel( new FormModel({ taskId: '' }), { + type: FormFieldTypes.AMOUNT, + required: true + }); + }); + + it('should be marked as invalid after interaction', async () => { + const amount = fixture.nativeElement.querySelector('input'); + expect(element.querySelector('.adf-invalid')).toBeFalsy(); + + amount.dispatchEvent(new Event('blur')); + + fixture.detectChanges(); + await fixture.whenStable(); + + expect(fixture.nativeElement.querySelector('.adf-invalid')).toBeTruthy(); + }); + + it('should be able to display label with asterisk', async () => { + fixture.detectChanges(); + await fixture.whenStable(); + + const asterisk: HTMLElement = element.querySelector('.adf-asterisk'); + + expect(asterisk).toBeTruthy(); + expect(asterisk.textContent).toEqual('*'); + }); + }); }); describe('AmountWidgetComponent - rendering', () => { diff --git a/lib/core/form/components/widgets/checkbox/checkbox.widget.html b/lib/core/form/components/widgets/checkbox/checkbox.widget.html index 44c9886fe03..ca292a9a613 100644 --- a/lib/core/form/components/widgets/checkbox/checkbox.widget.html +++ b/lib/core/form/components/widgets/checkbox/checkbox.widget.html @@ -1,16 +1,19 @@
+ [class.adf-invalid]="!field.isValid && isTouched()"> {{field.name | translate }} - * + * + +
diff --git a/lib/core/form/components/widgets/checkbox/checkbox.widget.spec.ts b/lib/core/form/components/widgets/checkbox/checkbox.widget.spec.ts index 5fea34247a2..d72faa5d8b5 100644 --- a/lib/core/form/components/widgets/checkbox/checkbox.widget.spec.ts +++ b/lib/core/form/components/widgets/checkbox/checkbox.widget.spec.ts @@ -21,9 +21,9 @@ import { FormFieldModel } from '../core/form-field.model'; import { FormModel } from '../core/form.model'; import { CheckboxWidgetComponent } from './checkbox.widget'; import { setupTestBed } from '../../../../testing/setup-test-bed'; -import { FormBaseModule } from 'core/form/form-base.module'; +import { FormBaseModule } from '../../../form-base.module'; import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; -import { TranslateLoaderService } from 'core/services'; +import { TranslateLoaderService } from '../../../../services/translate-loader.service'; import { MatCheckboxModule } from '@angular/material/checkbox'; import { CoreTestingModule } from '../../../../testing'; import { MatTooltipModule } from '@angular/material/tooltip'; @@ -69,11 +69,27 @@ describe('CheckboxWidgetComponent', () => { }); }); - it('should be marked as invalid when required', async () => { + it('should be marked as invalid when required after interaction', async () => { + const checkbox = element.querySelector('mat-checkbox'); + expect(element.querySelector('.adf-invalid')).toBeFalsy(); + + checkbox.dispatchEvent(new Event('click')); + checkbox.dispatchEvent(new Event('click')); + + fixture.detectChanges(); + await fixture.whenStable(); + + expect(element.querySelector('.adf-invalid')).toBeTruthy(); + }); + + it('should be able to display label with asterisk', async () => { fixture.detectChanges(); await fixture.whenStable(); - expect(element.querySelector('.adf-invalid')).not.toBeNull(); + const asterisk: HTMLElement = element.querySelector('.adf-asterisk'); + + expect(asterisk).toBeTruthy(); + expect(asterisk.textContent).toEqual('*'); }); it('should be checked if boolean true is passed', fakeAsync(() => { diff --git a/lib/core/form/components/widgets/date-time/date-time.widget.html b/lib/core/form/components/widgets/date-time/date-time.widget.html index 6d6d4d5e56a..50210dfbed0 100644 --- a/lib/core/form/components/widgets/date-time/date-time.widget.html +++ b/lib/core/form/components/widgets/date-time/date-time.widget.html @@ -1,6 +1,6 @@ -
+
- + - + { @@ -106,6 +107,38 @@ describe('DateTimeWidgetComponent', () => { expect(widget.onFieldChanged).toHaveBeenCalledWith(field); }); + describe('when is required', () => { + + beforeEach(() => { + widget.field = new FormFieldModel( new FormModel({ taskId: '' }), { + type: FormFieldTypes.DATETIME, + required: true + }); + }); + + it('should be marked as invalid after interaction', async () => { + const dateTimeInput = fixture.nativeElement.querySelector('input'); + expect(fixture.nativeElement.querySelector('.adf-invalid')).toBeFalsy(); + + dateTimeInput.dispatchEvent(new Event('blur')); + + fixture.detectChanges(); + await fixture.whenStable(); + + expect(fixture.nativeElement.querySelector('.adf-invalid')).toBeTruthy(); + }); + + it('should be able to display label with asterisk', async () => { + fixture.detectChanges(); + await fixture.whenStable(); + + const asterisk: HTMLElement = element.querySelector('.adf-asterisk'); + + expect(asterisk).toBeTruthy(); + expect(asterisk.textContent).toEqual('*'); + }); + }); + describe('template check', () => { it('should show visible date widget', async () => { diff --git a/lib/core/form/components/widgets/date/date.widget.html b/lib/core/form/components/widgets/date/date.widget.html index 7c40a06a649..266e80e6350 100644 --- a/lib/core/form/components/widgets/date/date.widget.html +++ b/lib/core/form/components/widgets/date/date.widget.html @@ -1,17 +1,19 @@ -
+
- + + [placeholder]="field.placeholder" + (blur)="markAsTouched()"> - + { @@ -98,6 +99,38 @@ describe('DateWidgetComponent', () => { expect(widget.onFieldChanged).toHaveBeenCalledWith(field); }); + describe('when is required', () => { + + beforeEach(() => { + widget.field = new FormFieldModel( new FormModel({ taskId: '' }), { + type: FormFieldTypes.DATE, + required: true + }); + }); + + it('should be marked as invalid after interaction', async () => { + const dateInput = fixture.nativeElement.querySelector('input'); + expect(fixture.nativeElement.querySelector('.adf-invalid')).toBeFalsy(); + + dateInput.dispatchEvent(new Event('blur')); + + fixture.detectChanges(); + await fixture.whenStable(); + + expect(fixture.nativeElement.querySelector('.adf-invalid')).toBeTruthy(); + }); + + it('should be able to display label with asterix', async () => { + fixture.detectChanges(); + await fixture.whenStable(); + + const asterisk: HTMLElement = element.querySelector('.adf-asterisk'); + + expect(asterisk).toBeTruthy(); + expect(asterisk.textContent).toEqual('*'); + }); + }); + describe('template check', () => { afterEach(() => { diff --git a/lib/core/form/components/widgets/dropdown/dropdown.widget.html b/lib/core/form/components/widgets/dropdown/dropdown.widget.html index fa2d9eb593c..c77a1db3e92 100644 --- a/lib/core/form/components/widgets/dropdown/dropdown.widget.html +++ b/lib/core/form/components/widgets/dropdown/dropdown.widget.html @@ -1,12 +1,13 @@
- + [class.adf-invalid]="!field.isValid && isTouched()" [class.adf-readonly]="field.readOnly"> + + (ngModelChange)="onFieldChanged(field)" + (blur)="markAsTouched()"> {{opt.name}} @@ -15,6 +16,6 @@ -
diff --git a/lib/core/form/components/widgets/dropdown/dropdown.widget.spec.ts b/lib/core/form/components/widgets/dropdown/dropdown.widget.spec.ts index 3f94b0a913a..f343c3b0f30 100644 --- a/lib/core/form/components/widgets/dropdown/dropdown.widget.spec.ts +++ b/lib/core/form/components/widgets/dropdown/dropdown.widget.spec.ts @@ -130,22 +130,26 @@ describe('DropdownWidgetComponent', () => { }); }); - it('should be able to display label with asterix', async () => { - const label = 'MyLabel123'; - widget.field.name = label; - + it('should be able to display label with asterisk', async () => { fixture.detectChanges(); await fixture.whenStable(); - expect(element.querySelector('label').innerText).toBe(label + '*'); + const asterisk: HTMLElement = element.querySelector('.adf-asterisk'); + + expect(asterisk).toBeTruthy(); + expect(asterisk.textContent).toEqual('*'); }); - it('should be invalid if no default option', async () => { + it('should be invalid if no default option after interaction', async () => { + expect(element.querySelector('.adf-invalid')).toBeFalsy(); + + const dropdownSelect = element.querySelector('.adf-select'); + dropdownSelect.dispatchEvent(new Event('blur')); + fixture.detectChanges(); await fixture.whenStable(); - expect(element.querySelector('.adf-invalid')).toBeDefined(); - expect(element.querySelector('.adf-invalid')).not.toBeNull(); + expect(element.querySelector('.adf-invalid')).toBeTruthy(); }); it('should be valid if default option', async () => { @@ -155,7 +159,7 @@ describe('DropdownWidgetComponent', () => { fixture.detectChanges(); await fixture.whenStable(); - expect(element.querySelector('.adf-invalid')).toBeNull(); + expect(element.querySelector('.adf-invalid')).toBeFalsy(); }); }); diff --git a/lib/core/form/components/widgets/dropdown/dropdown.widget.ts b/lib/core/form/components/widgets/dropdown/dropdown.widget.ts index 38a8d6bc68f..7db578acd15 100644 --- a/lib/core/form/components/widgets/dropdown/dropdown.widget.ts +++ b/lib/core/form/components/widgets/dropdown/dropdown.widget.ts @@ -113,4 +113,7 @@ export class DropdownWidgetComponent extends WidgetComponent implements OnInit { return this.field.type === 'readonly'; } + showRequiredMessage(): boolean { + return (this.isInvalidFieldRequired() || this.field.value === 'empty') && this.isTouched(); + } } diff --git a/lib/core/form/components/widgets/dynamic-table/dynamic-table.widget.html b/lib/core/form/components/widgets/dynamic-table/dynamic-table.widget.html index ac7d467fcec..f8b46cb3170 100644 --- a/lib/core/form/components/widgets/dynamic-table/dynamic-table.widget.html +++ b/lib/core/form/components/widgets/dynamic-table/dynamic-table.widget.html @@ -1,6 +1,6 @@
-
{{content.name | translate }}*
+
{{content.name | translate }}*
diff --git a/lib/core/form/components/widgets/error/error.component.html b/lib/core/form/components/widgets/error/error.component.html index 6a42f7b1148..99ae5edcba0 100644 --- a/lib/core/form/components/widgets/error/error.component.html +++ b/lib/core/form/components/widgets/error/error.component.html @@ -1,9 +1,10 @@ -
-
+
+
+ error_outline
{{error.message | translate:translateParameters}}
- warning
-
-
{{required}}
+
+ error_outline +
{{required}}
diff --git a/lib/core/form/components/widgets/error/error.component.scss b/lib/core/form/components/widgets/error/error.component.scss index 92b5d6ea4ca..faa6bfac4e2 100644 --- a/lib/core/form/components/widgets/error/error.component.scss +++ b/lib/core/form/components/widgets/error/error.component.scss @@ -1,3 +1,3 @@ -.adf-error-text { - width: 85%; +.adf-error { + display: flex; } diff --git a/lib/core/form/components/widgets/error/error.component.spec.ts b/lib/core/form/components/widgets/error/error.component.spec.ts new file mode 100644 index 00000000000..bbcea3bc595 --- /dev/null +++ b/lib/core/form/components/widgets/error/error.component.spec.ts @@ -0,0 +1,73 @@ +/*! + * @license + * Copyright 2019 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { SimpleChange, SimpleChanges } from '@angular/core'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { setupTestBed } from '../../../../testing/setup-test-bed'; +import { ErrorWidgetComponent } from './error.component'; +import { CoreTestingModule } from '../../../../testing'; +import { ErrorMessageModel } from '..'; + +describe('ErrorWidgetComponent', () => { + + let widget: ErrorWidgetComponent; + let fixture: ComponentFixture; + let element: HTMLElement; + + setupTestBed({ + imports: [ + CoreTestingModule + ] + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ErrorWidgetComponent); + widget = fixture.componentInstance; + element = fixture.nativeElement; + }); + const errorMessage: string = 'fake-error'; + const errorMessageModel: ErrorMessageModel = new ErrorMessageModel({message: errorMessage}); + const errorChanges: SimpleChanges = { + error: new SimpleChange(errorMessageModel, errorMessageModel, false) + }; + + it('should display proper error icon', async () => { + widget.ngOnChanges(errorChanges); + + await fixture.whenStable(); + fixture.detectChanges(); + + const errorIcon = element.querySelector('.adf-error-icon').textContent; + expect(errorIcon).toEqual('error_outline'); + }); + + it('should set subscriptAnimationState value', () => { + widget.ngOnChanges(errorChanges); + + expect(widget.subscriptAnimationState).toEqual('enter'); + }); + + it('should check proper error message', async () => { + widget.ngOnChanges(errorChanges); + + await fixture.whenStable(); + fixture.detectChanges(); + + const requiredErrorText = element.querySelector('.adf-error-text').textContent; + expect(requiredErrorText).toEqual(errorMessage); + }); +}); diff --git a/lib/core/form/components/widgets/error/error.component.ts b/lib/core/form/components/widgets/error/error.component.ts index f15e781e032..6bc611a3963 100644 --- a/lib/core/form/components/widgets/error/error.component.ts +++ b/lib/core/form/components/widgets/error/error.component.ts @@ -59,7 +59,7 @@ export class ErrorWidgetComponent extends WidgetComponent implements OnChanges { translateParameters: any = null; - _subscriptAnimationState: string = ''; + subscriptAnimationState: string = ''; constructor(public formService: FormService) { super(formService); @@ -68,13 +68,13 @@ export class ErrorWidgetComponent extends WidgetComponent implements OnChanges { ngOnChanges(changes: SimpleChanges) { if (changes['required']) { this.required = changes.required.currentValue; - this._subscriptAnimationState = 'enter'; + this.subscriptAnimationState = 'enter'; } if (changes['error'] && changes['error'].currentValue) { if (changes.error.currentValue.isActive()) { this.error = changes.error.currentValue; this.translateParameters = this.error.getAttributesAsJsonObj(); - this._subscriptAnimationState = 'enter'; + this.subscriptAnimationState = 'enter'; } } } diff --git a/lib/core/form/components/widgets/file-viewer/file-viewer.widget.html b/lib/core/form/components/widgets/file-viewer/file-viewer.widget.html index 95e3b0497fe..00c6489b02b 100644 --- a/lib/core/form/components/widgets/file-viewer/file-viewer.widget.html +++ b/lib/core/form/components/widgets/file-viewer/file-viewer.widget.html @@ -1,6 +1,6 @@
- diff --git a/lib/core/form/components/widgets/form.theme.scss b/lib/core/form/components/widgets/form.theme.scss index 544d66495c3..de0ec421144 100644 --- a/lib/core/form/components/widgets/form.theme.scss +++ b/lib/core/form/components/widgets/form.theme.scss @@ -5,8 +5,12 @@ ul > li > form-field > .adf-focus { } } +.mat-form-field-label { + color: var(--theme-colors-mat-grey-dark) !important; +} + .adf { - &-error-text-container { + &-error-container { height: 20px; margin-top: -12px; } @@ -16,40 +20,35 @@ ul > li > form-field > .adf-focus { height: 16px; font-size: var(--theme-caption-font-size); line-height: 1.33; - float: left; color: var(--theme-warn-color); } &-error-icon { - float: right; font-size: var(--theme-adf-icon-1-font-size); color: var(--theme-warn-color); } &-label { - color: rgb(186, 186, 186); + color: var(--theme-secondary-text-color); + } + + &-asterisk { + padding-left: 2px; + color: var(--theme-warn-color); } &-invalid { - .mat-form-field-underline { - background-color: #f44336 !important; + .mat-checkbox-layout { + padding-bottom: 12px; } - .mat-checkbox { - color: var(--theme-warn-color); - - .mat-checkbox-frame { - border-color: var(--theme-warn-color); - } + .mat-form-field-underline { + background-color: var(--theme-warn-color) !important; } .mat-select { - &-value { - color: var(--theme-warn-color); - } - &-arrow { - color: var(--theme-warn-color); + color: var(--theme-secondary-text-color) !important; } } @@ -58,20 +57,12 @@ ul > li > form-field > .adf-focus { } .mat-form-field-prefix { - color: var(--theme-warn-color); + color: var(--theme-secondary-text-color); } .adf-input { border-color: var(--theme-warn-color); } - - .adf-label { - color: var(--theme-warn-color); - - &::after { - background-color: var(--theme-warn-color); - } - } } } diff --git a/lib/core/form/components/widgets/functional-group/functional-group.widget.html b/lib/core/form/components/widgets/functional-group/functional-group.widget.html index 9e044c6b75a..6fcfbd6d6e9 100644 --- a/lib/core/form/components/widgets/functional-group/functional-group.widget.html +++ b/lib/core/form/components/widgets/functional-group/functional-group.widget.html @@ -1,11 +1,11 @@
- + - +
diff --git a/lib/core/form/components/widgets/functional-group/functional-group.widget.spec.ts b/lib/core/form/components/widgets/functional-group/functional-group.widget.spec.ts index ffa7b4338c4..881c443e43e 100644 --- a/lib/core/form/components/widgets/functional-group/functional-group.widget.spec.ts +++ b/lib/core/form/components/widgets/functional-group/functional-group.widget.spec.ts @@ -24,12 +24,14 @@ import { FunctionalGroupWidgetComponent } from './functional-group.widget'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { CoreTestingModule, setupTestBed } from '../../../../testing'; import { TranslateModule } from '@ngx-translate/core'; +import { FormFieldTypes } from '../core/form-field-types'; describe('FunctionalGroupWidgetComponent', () => { let fixture: ComponentFixture; let component: FunctionalGroupWidgetComponent; let formService: FormService; let getWorkflowGroupsSpy: jasmine.Spy; + let element: HTMLElement; const groups: GroupModel[] = [ { id: '1', name: 'group 1' }, { id: '2', name: 'group 2' } @@ -49,6 +51,7 @@ describe('FunctionalGroupWidgetComponent', () => { fixture = TestBed.createComponent(FunctionalGroupWidgetComponent); component = fixture.componentInstance; component.field = new FormFieldModel(new FormModel()); + element = fixture.nativeElement; fixture.detectChanges(); }); @@ -148,4 +151,36 @@ describe('FunctionalGroupWidgetComponent', () => { await typeIntoInput('123'); expect(getWorkflowGroupsSpy).not.toHaveBeenCalled(); }); + + describe('when is required', () => { + + beforeEach(() => { + component.field = new FormFieldModel( new FormModel({ taskId: '' }), { + type: FormFieldTypes.FUNCTIONAL_GROUP, + required: true + }); + }); + + it('should be marked as invalid after interaction', async () => { + const functionalGroupInput = fixture.nativeElement.querySelector('input'); + expect(fixture.nativeElement.querySelector('.adf-invalid')).toBeFalsy(); + + functionalGroupInput.dispatchEvent(new Event('blur')); + + fixture.detectChanges(); + await fixture.whenStable(); + + expect(fixture.nativeElement.querySelector('.adf-invalid')).toBeTruthy(); + }); + + it('should be able to display label with asterisk', async () => { + fixture.detectChanges(); + await fixture.whenStable(); + + const asterisk: HTMLElement = element.querySelector('.adf-asterisk'); + + expect(asterisk).toBeTruthy(); + expect(asterisk.textContent).toEqual('*'); + }); + }); }); diff --git a/lib/core/form/components/widgets/hyperlink/hyperlink.widget.html b/lib/core/form/components/widgets/hyperlink/hyperlink.widget.html index 05e6d61eb34..8a75d32156f 100644 --- a/lib/core/form/components/widgets/hyperlink/hyperlink.widget.html +++ b/lib/core/form/components/widgets/hyperlink/hyperlink.widget.html @@ -1,5 +1,5 @@