diff --git a/cspell.json b/cspell.json index 366073da1e..f12c11bd22 100644 --- a/cspell.json +++ b/cspell.json @@ -131,7 +131,8 @@ "processstring", "typeahed", "minmax", - "jsons" + "jsons", + "Inplace" ], "dictionaries": [ "html", diff --git a/lib/core/form/components/inplace-form-input/inplace-form-input.component.html b/lib/core/form/components/inplace-form-input/inplace-form-input.component.html new file mode 100644 index 0000000000..ede1f948d7 --- /dev/null +++ b/lib/core/form/components/inplace-form-input/inplace-form-input.component.html @@ -0,0 +1,17 @@ +
+ + + + + + + +
diff --git a/lib/core/form/components/inplace-form-input/inplace-form-input.component.scss b/lib/core/form/components/inplace-form-input/inplace-form-input.component.scss new file mode 100644 index 0000000000..dce4f72dee --- /dev/null +++ b/lib/core/form/components/inplace-form-input/inplace-form-input.component.scss @@ -0,0 +1,35 @@ +.adf-inplace-input-container { + .mat-form-field-underline { + display: none; + } + + .mat-form-field-infix { + display: flex; + border-top: 0; + } + + &-error { + input { + border: 1px solid var(--theme-warn-color) !important; + } + } + + .adf-inplace-input-mat-form-field { + width: 100%; + } + + .adf-inplace-input { + padding: 7px; + border: 1px solid transparent; + border-radius: 4px; + + &:focus { + border: 1px solid var(--theme-primary-color); + } + + &:hover:not(:focus) { + border: 1px solid var(--theme-bg-hover-color); + background-color: var(--theme-bg-hover-color); + } + } +} diff --git a/lib/core/form/components/inplace-form-input/inplace-form-input.component.spec.ts b/lib/core/form/components/inplace-form-input/inplace-form-input.component.spec.ts new file mode 100644 index 0000000000..7d293c734c --- /dev/null +++ b/lib/core/form/components/inplace-form-input/inplace-form-input.component.spec.ts @@ -0,0 +1,72 @@ +/*! + * @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 { ComponentFixture, TestBed } from '@angular/core/testing'; +import { FormControl } from '@angular/forms'; +import { TranslateModule } from '@ngx-translate/core'; +import { ContentTestingModule } from 'content-services/src/lib/testing/content.testing.module'; +import { InplaceFormInputComponent } from './inplace-form-input.component'; + +describe('InplaceFormInputComponent', () => { + let component: InplaceFormInputComponent; + let fixture: ComponentFixture; + let formControl: FormControl; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + TranslateModule.forRoot(), + ContentTestingModule + ], + declarations: [InplaceFormInputComponent] + }).compileComponents(); + }); + + beforeEach(() => { + formControl = new FormControl(''); + fixture = TestBed.createComponent(InplaceFormInputComponent); + + component = fixture.componentInstance; + component.control = formControl; + }); + + it('should update form value', () => { + formControl.setValue('New Value'); + fixture.detectChanges(); + + const input = fixture.nativeElement.querySelector( + '[data-automation-id="adf-inplace-input"]' + ); + + expect(input.value).toBe('New Value'); + }); + + it('should show error', () => { + formControl.setValidators(() => ({ error: 'error' })); + formControl.setValue('value'); + formControl.markAsTouched(); + formControl.updateValueAndValidity(); + + fixture.detectChanges(); + + const error = fixture.nativeElement.querySelector( + '[data-automation-id="adf-inplace-input-error"]' + ); + + expect(error).toBeTruthy(); + }); +}); diff --git a/lib/core/form/components/inplace-form-input/inplace-form-input.component.ts b/lib/core/form/components/inplace-form-input/inplace-form-input.component.ts new file mode 100644 index 0000000000..f249dd8f39 --- /dev/null +++ b/lib/core/form/components/inplace-form-input/inplace-form-input.component.ts @@ -0,0 +1,34 @@ +/*! + * @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 { + Component, + Input, + ViewEncapsulation +} from '@angular/core'; +import { FormControl } from '@angular/forms'; + +@Component({ + selector: 'adf-inplace-form-input', + templateUrl: './inplace-form-input.component.html', + styleUrls: ['./inplace-form-input.component.scss'], + encapsulation: ViewEncapsulation.None +}) +export class InplaceFormInputComponent { + @Input() + control: FormControl; +} diff --git a/lib/core/form/form-base.module.ts b/lib/core/form/form-base.module.ts index 83f7225ca0..61f7f94f26 100644 --- a/lib/core/form/form-base.module.ts +++ b/lib/core/form/form-base.module.ts @@ -40,6 +40,7 @@ import { EditJsonDialogModule } from '../dialogs/edit-json/edit-json.dialog.modu import { A11yModule } from '@angular/cdk/a11y'; import { FlexLayoutModule } from '@angular/flex-layout'; import { ViewerModule } from '../viewer/viewer.module'; +import { InplaceFormInputComponent } from './components/inplace-form-input/inplace-form-input.component'; @NgModule({ imports: [ @@ -67,7 +68,8 @@ import { ViewerModule } from '../viewer/viewer.module'; StartFormCustomButtonDirective, ...WIDGET_DIRECTIVES, ...MASK_DIRECTIVE, - WidgetComponent + WidgetComponent, + InplaceFormInputComponent ], exports: [ ContentWidgetComponent, @@ -75,6 +77,7 @@ import { ViewerModule } from '../viewer/viewer.module'; FormListComponent, FormRendererComponent, StartFormCustomButtonDirective, + InplaceFormInputComponent, ...WIDGET_DIRECTIVES ] }) diff --git a/lib/core/form/public-api.ts b/lib/core/form/public-api.ts index e911256624..39fd7dadb1 100644 --- a/lib/core/form/public-api.ts +++ b/lib/core/form/public-api.ts @@ -18,6 +18,7 @@ export * from './components/form-field/form-field.component'; export * from './components/form-base.component'; export * from './components/form-list.component'; +export * from './components/inplace-form-input/inplace-form-input.component'; export * from './components/widgets/content/content.widget'; export * from './components/form-custom-button.directive'; export * from './components/form-renderer.component'; diff --git a/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.html b/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.html index 807ce1deae..6559913882 100755 --- a/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.html +++ b/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.html @@ -1,6 +1,8 @@ - - {{'ADF_CLOUD_PROCESS_LIST.ADF_CLOUD_START_PROCESS.FORM.TITLE' | translate}} + + {{'ADF_CLOUD_PROCESS_LIST.ADF_CLOUD_START_PROCESS.FORM.TITLE' | translate}} @@ -9,8 +11,13 @@
-
- + + {{ 'ADF_CLOUD_PROCESS_LIST.ADF_CLOUD_START_PROCESS.FORM.LABEL.TYPE' | translate }} -
@@ -34,22 +41,17 @@ - - {{ 'ADF_CLOUD_PROCESS_LIST.ADF_CLOUD_START_PROCESS.FORM.LABEL.NAME' | translate }} - - + + {{ 'ADF_CLOUD_PROCESS_LIST.ADF_CLOUD_START_PROCESS.ERROR.PROCESS_NAME_REQUIRED' | translate }} - - + + {{ 'ADF_CLOUD_PROCESS_LIST.ADF_CLOUD_START_PROCESS.ERROR.MAXIMUM_LENGTH' | translate : { characters : maxNameLength } }} - - + + {{ 'ADF_CLOUD_PROCESS_LIST.ADF_CLOUD_START_PROCESS.ERROR.SPACE_VALIDATOR' | translate }} - - + + diff --git a/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.scss b/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.scss index 41d24c524d..6d7e141d3b 100755 --- a/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.scss +++ b/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.scss @@ -1,9 +1,5 @@ .adf { &-start-process { - mat-form-field { - width: 100%; - } - .mat-form-field-label { color: var(--theme-colors-mat-grey-dark); } @@ -17,14 +13,17 @@ } } + &-select-process-form { + display: flex; + flex-direction: column; + } + &-title { padding-bottom: 1.25em; } &-process-input-container { - mat-form-field { - width: 100%; - } + margin: 0 7px; } &-process-input-autocomplete { @@ -37,7 +36,7 @@ } } - &-start-form-container { + &-form-container { .mat-card { box-shadow: none !important; padding: 0 !important; diff --git a/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.spec.ts b/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.spec.ts index fa1c822dea..db6f932d22 100755 --- a/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.spec.ts +++ b/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.spec.ts @@ -165,7 +165,7 @@ describe('StartProcessCloudComponent', () => { component.ngOnChanges({ appName: change }); fixture.detectChanges(); tick(); - typeValueInto('#processName', 'OLE'); + typeValueInto('[data-automation-id="adf-inplace-input"]', 'OLE'); typeValueInto('#processDefinitionName', 'processwithoutform2'); fixture.detectChanges(); tick(550); @@ -242,7 +242,7 @@ describe('StartProcessCloudComponent', () => { component.ngOnChanges({ appName: change }); fixture.detectChanges(); tick(); - typeValueInto('#processName', 'My new process with form'); + typeValueInto('[data-automation-id="adf-inplace-input"]', 'My new process with form'); typeValueInto('#processDefinitionName', 'processwithform'); fixture.detectChanges(); tick(550); @@ -269,7 +269,7 @@ describe('StartProcessCloudComponent', () => { fixture.detectChanges(); tick(); - typeValueInto('#processName', 'My new process with form'); + typeValueInto('[data-automation-id="adf-inplace-input"]', 'My new process with form'); typeValueInto('#processDefinitionName', 'processwithform'); fixture.detectChanges(); tick(550); @@ -297,7 +297,7 @@ describe('StartProcessCloudComponent', () => { fixture.detectChanges(); tick(); - typeValueInto('#processName', 'My new process with form'); + typeValueInto('[data-automation-id="adf-inplace-input"]', 'My new process with form'); typeValueInto('#processDefinitionName', 'processwithform'); fixture.detectChanges(); tick(550); @@ -327,7 +327,7 @@ describe('StartProcessCloudComponent', () => { fixture.detectChanges(); tick(); - typeValueInto('#processName', 'My new process with form'); + typeValueInto('[data-automation-id="adf-inplace-input"]', 'My new process with form'); typeValueInto('#processDefinitionName', 'processwithform'); fixture.detectChanges(); tick(4500); @@ -582,18 +582,6 @@ describe('StartProcessCloudComponent', () => { fixture.detectChanges(); }); - it('should have labels for process name and type', async () => { - component.appName = 'myApp'; - component.processDefinitionName = 'NewProcess 2'; - component.ngOnChanges({ appName: firstChange }); - - fixture.detectChanges(); - await fixture.whenStable(); - - const inputLabelsNodes = document.querySelectorAll('.adf-start-process .adf-process-input-container mat-label'); - expect(inputLabelsNodes.length).toBe(2); - }); - it('should have floating labels for process name and type', async () => { component.appName = 'myApp'; component.processDefinitionName = 'NewProcess 2'; @@ -862,5 +850,39 @@ describe('StartProcessCloudComponent', () => { expect(escapeKeyboardEvent.cancelBubble).toBe(true); }); + + it('should hide title', () => { + component.showTitle = false; + fixture.detectChanges(); + + const title = fixture.debugElement.query(By.css('.adf-title')); + + expect(title).toBeFalsy(); + }); + + it('should show title', () => { + const title = fixture.debugElement.query(By.css('.adf-title')); + + expect(title).toBeTruthy(); + }); + + it('should show process definition dropdown', () => { + component.processDefinitionList = fakeProcessDefinitions; + fixture.detectChanges(); + + const processDropdown = fixture.debugElement.query(By.css('[data-automation-id="adf-select-cloud-process-dropdown"]')); + + expect(processDropdown).toBeTruthy(); + }); + + it('should hide process definition dropdown', () => { + component.processDefinitionList = fakeProcessDefinitions; + component.showSelectProcessDropdown = false; + fixture.detectChanges(); + + const processDropdown = fixture.debugElement.query(By.css('#processDefinitionName')); + + expect(processDropdown).toBeFalsy(); + }); }); }); diff --git a/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.ts b/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.ts index daf4398449..ab9e61caf5 100755 --- a/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.ts @@ -74,6 +74,10 @@ export class StartProcessCloudComponent implements OnChanges, OnInit, OnDestroy @Input() showSelectProcessDropdown: boolean = true; + /** Show/hide title. */ + @Input() + showTitle: boolean = true; + /** Emitted when the process is successfully started. */ @Output() success = new EventEmitter(); @@ -369,8 +373,8 @@ export class StartProcessCloudComponent implements OnChanges, OnInit, OnDestroy return !!process.name ? process.name : process.key; } - get processInstanceName(): AbstractControl { - return this.processForm.get('processInstanceName'); + get processInstanceName(): FormControl { + return this.processForm.get('processInstanceName') as FormControl; } get processDefinition(): AbstractControl { diff --git a/lib/testing/src/lib/protractor/process-services-cloud/pages/start-process-cloud-component.page.ts b/lib/testing/src/lib/protractor/process-services-cloud/pages/start-process-cloud-component.page.ts index 37cc9b2680..d612a1615d 100644 --- a/lib/testing/src/lib/protractor/process-services-cloud/pages/start-process-cloud-component.page.ts +++ b/lib/testing/src/lib/protractor/process-services-cloud/pages/start-process-cloud-component.page.ts @@ -23,7 +23,7 @@ import { FormFields } from '../../core/pages/form/form-fields'; export class StartProcessCloudPage { defaultProcessName = $('input[id="processName"]'); - processNameInput = $('#processName'); + processNameInput = $('[data-automation-id="adf-inplace-input"]'); selectProcessDropdownArrow = $('button[id="adf-select-process-dropdown"]'); cancelProcessButton = $('#cancel_process'); formStartProcessButton = $('button[data-automation-id="adf-form-start process"]'); @@ -89,6 +89,8 @@ export class StartProcessCloudPage { async isStartProcessButtonEnabled(): Promise { await BrowserVisibility.waitUntilElementIsNotVisible(this.startProcessButtonDisabled); await BrowserVisibility.waitUntilElementIsVisible(this.startProcessButton); + await BrowserVisibility.waitUntilElementIsClickable(this.startProcessButton); + return this.startProcessButton.isEnabled(); }