mirror of
https://github.com/Alfresco/alfresco-content-app.git
synced 2026-09-09 18:02:54 +00:00
[ACS-10294] KN: Personal Files: On activating the button, focus does not move into newly opened section (#5014)
This commit is contained in:
+1
@@ -9,6 +9,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<fieldset
|
<fieldset
|
||||||
|
#conditionRow
|
||||||
[attr.aria-labelledby]="'conditions-group-label-' + (childCondition ? 'nested' : 'main')"
|
[attr.aria-labelledby]="'conditions-group-label-' + (childCondition ? 'nested' : 'main')"
|
||||||
class="aca-rule-composite-condition__form__row"
|
class="aca-rule-composite-condition__form__row"
|
||||||
*ngFor="let control of conditionFormControls; let i = index">
|
*ngFor="let control of conditionFormControls; let i = index">
|
||||||
|
|||||||
+34
-1
@@ -22,7 +22,7 @@
|
|||||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
|
||||||
import { RuleCompositeConditionUiComponent } from './rule-composite-condition.ui-component';
|
import { RuleCompositeConditionUiComponent } from './rule-composite-condition.ui-component';
|
||||||
import { NoopTranslateModule, UnitTestingUtils } from '@alfresco/adf-core';
|
import { NoopTranslateModule, UnitTestingUtils } from '@alfresco/adf-core';
|
||||||
import { DebugElement } from '@angular/core';
|
import { DebugElement } from '@angular/core';
|
||||||
@@ -33,6 +33,7 @@ import {
|
|||||||
} from '../../mock/conditions.mock';
|
} from '../../mock/conditions.mock';
|
||||||
import { RuleSimpleConditionUiComponent } from './rule-simple-condition.ui-component';
|
import { RuleSimpleConditionUiComponent } from './rule-simple-condition.ui-component';
|
||||||
import { AlfrescoApiService, AlfrescoApiServiceMock } from '@alfresco/adf-content-services';
|
import { AlfrescoApiService, AlfrescoApiServiceMock } from '@alfresco/adf-content-services';
|
||||||
|
import { FocusTrapFactory } from '@angular/cdk/a11y';
|
||||||
|
|
||||||
describe('RuleCompositeConditionUiComponent', () => {
|
describe('RuleCompositeConditionUiComponent', () => {
|
||||||
let fixture: ComponentFixture<RuleCompositeConditionUiComponent>;
|
let fixture: ComponentFixture<RuleCompositeConditionUiComponent>;
|
||||||
@@ -166,4 +167,36 @@ describe('RuleCompositeConditionUiComponent', () => {
|
|||||||
expect(unitTestingUtils.getAllByCSS('[conditions-group-label-nested]')).toBeTruthy();
|
expect(unitTestingUtils.getAllByCSS('[conditions-group-label-nested]')).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Focus management', () => {
|
||||||
|
it('should focus the newly added simple condition', fakeAsync(() => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
const focusTrapFactory = TestBed.inject(FocusTrapFactory);
|
||||||
|
const focusTrapSpy = jasmine.createSpyObj('FocusTrap', ['focusFirstTabbableElement', 'destroy']);
|
||||||
|
spyOn(focusTrapFactory, 'create').and.returnValue(focusTrapSpy);
|
||||||
|
|
||||||
|
unitTestingUtils.clickByDataAutomationId('add-condition-button');
|
||||||
|
fixture.detectChanges();
|
||||||
|
tick();
|
||||||
|
|
||||||
|
expect(focusTrapFactory.create).toHaveBeenCalled();
|
||||||
|
expect(focusTrapSpy.focusFirstTabbableElement).toHaveBeenCalled();
|
||||||
|
expect(focusTrapSpy.destroy).toHaveBeenCalled();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should focus the newly added composite condition', fakeAsync(() => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
const focusTrapFactory = TestBed.inject(FocusTrapFactory);
|
||||||
|
const focusTrapSpy = jasmine.createSpyObj('FocusTrap', ['focusFirstTabbableElement', 'destroy']);
|
||||||
|
spyOn(focusTrapFactory, 'create').and.returnValue(focusTrapSpy);
|
||||||
|
|
||||||
|
unitTestingUtils.clickByDataAutomationId('add-group-button');
|
||||||
|
fixture.detectChanges();
|
||||||
|
tick();
|
||||||
|
|
||||||
|
expect(focusTrapFactory.create).toHaveBeenCalled();
|
||||||
|
expect(focusTrapSpy.focusFirstTabbableElement).toHaveBeenCalled();
|
||||||
|
expect(focusTrapSpy.destroy).toHaveBeenCalled();
|
||||||
|
}));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
+56
-10
@@ -22,7 +22,22 @@
|
|||||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Component, forwardRef, HostBinding, Input, OnChanges, OnDestroy, SimpleChanges, ViewEncapsulation } from '@angular/core';
|
import {
|
||||||
|
AfterViewInit,
|
||||||
|
Component,
|
||||||
|
DestroyRef,
|
||||||
|
ElementRef,
|
||||||
|
forwardRef,
|
||||||
|
HostBinding,
|
||||||
|
inject,
|
||||||
|
Input,
|
||||||
|
OnChanges,
|
||||||
|
QueryList,
|
||||||
|
SimpleChanges,
|
||||||
|
ViewChildren,
|
||||||
|
ViewEncapsulation
|
||||||
|
} from '@angular/core';
|
||||||
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||||
import { RuleCompositeCondition } from '../../model/rule-composite-condition.model';
|
import { RuleCompositeCondition } from '../../model/rule-composite-condition.model';
|
||||||
import { ControlValueAccessor, FormArray, FormControl, FormGroup, NG_VALUE_ACCESSOR, ReactiveFormsModule } from '@angular/forms';
|
import { ControlValueAccessor, FormArray, FormControl, FormGroup, NG_VALUE_ACCESSOR, ReactiveFormsModule } from '@angular/forms';
|
||||||
import { RuleSimpleCondition } from '../../model/rule-simple-condition.model';
|
import { RuleSimpleCondition } from '../../model/rule-simple-condition.model';
|
||||||
@@ -34,6 +49,7 @@ import { MatButtonModule } from '@angular/material/button';
|
|||||||
import { MatMenuModule } from '@angular/material/menu';
|
import { MatMenuModule } from '@angular/material/menu';
|
||||||
import { MatIconModule } from '@angular/material/icon';
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
import { RuleSimpleConditionUiComponent } from './rule-simple-condition.ui-component';
|
import { RuleSimpleConditionUiComponent } from './rule-simple-condition.ui-component';
|
||||||
|
import { FocusTrapFactory } from '@angular/cdk/a11y';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -60,16 +76,21 @@ import { RuleSimpleConditionUiComponent } from './rule-simple-condition.ui-compo
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class RuleCompositeConditionUiComponent implements ControlValueAccessor, OnDestroy, OnChanges {
|
export class RuleCompositeConditionUiComponent implements ControlValueAccessor, OnChanges, AfterViewInit {
|
||||||
@HostBinding('class.aca-secondaryBackground')
|
@HostBinding('class.aca-secondaryBackground')
|
||||||
@Input()
|
@Input()
|
||||||
secondaryBackground = false;
|
secondaryBackground = false;
|
||||||
|
|
||||||
@HostBinding('class.aca-childCompositeCondition')
|
@HostBinding('class.aca-childCompositeCondition')
|
||||||
@Input()
|
@Input()
|
||||||
childCondition = false;
|
childCondition = false;
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
readOnly = false;
|
readOnly = false;
|
||||||
|
|
||||||
|
@ViewChildren('conditionRow', { read: ElementRef })
|
||||||
|
private readonly conditionRows: QueryList<ElementRef<HTMLElement>>;
|
||||||
|
|
||||||
readonly isOrImplemented = false;
|
readonly isOrImplemented = false;
|
||||||
|
|
||||||
form = new FormGroup({
|
form = new FormGroup({
|
||||||
@@ -79,23 +100,31 @@ export class RuleCompositeConditionUiComponent implements ControlValueAccessor,
|
|||||||
simpleConditions: new FormArray([])
|
simpleConditions: new FormArray([])
|
||||||
});
|
});
|
||||||
|
|
||||||
private formSubscription = this.form.valueChanges.subscribe((value: any) => {
|
public invertedControl = this.form.get('inverted') as FormControl;
|
||||||
|
public booleanModeControl = this.form.get('booleanMode') as FormControl;
|
||||||
|
|
||||||
|
private readonly destroyRef = inject(DestroyRef);
|
||||||
|
private readonly focusTrapFactory = inject(FocusTrapFactory);
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.form.valueChanges.pipe(takeUntilDestroyed()).subscribe((value: RuleCompositeCondition) => {
|
||||||
this.onChange(value);
|
this.onChange(value);
|
||||||
this.onTouch();
|
this.onTouch();
|
||||||
});
|
});
|
||||||
|
}
|
||||||
public invertedControl = this.form.get('inverted') as FormControl;
|
|
||||||
public booleanModeControl = this.form.get('booleanMode') as FormControl;
|
|
||||||
|
|
||||||
get compositeConditionsFormArray(): FormArray {
|
get compositeConditionsFormArray(): FormArray {
|
||||||
return this.form.get('compositeConditions') as FormArray;
|
return this.form.get('compositeConditions') as FormArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
get simpleConditionsFormArray(): FormArray {
|
get simpleConditionsFormArray(): FormArray {
|
||||||
return this.form.get('simpleConditions') as FormArray;
|
return this.form.get('simpleConditions') as FormArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
get conditionFormControls(): FormControl[] {
|
get conditionFormControls(): FormControl[] {
|
||||||
return [...(this.compositeConditionsFormArray.controls as FormControl[]), ...(this.simpleConditionsFormArray.controls as FormControl[])];
|
return [...(this.compositeConditionsFormArray.controls as FormControl[]), ...(this.simpleConditionsFormArray.controls as FormControl[])];
|
||||||
}
|
}
|
||||||
|
|
||||||
get hasNoConditions(): boolean {
|
get hasNoConditions(): boolean {
|
||||||
return this.conditionFormControls.length === 0;
|
return this.conditionFormControls.length === 0;
|
||||||
}
|
}
|
||||||
@@ -103,6 +132,12 @@ export class RuleCompositeConditionUiComponent implements ControlValueAccessor,
|
|||||||
onChange: (condition: RuleCompositeCondition) => void = () => undefined;
|
onChange: (condition: RuleCompositeCondition) => void = () => undefined;
|
||||||
onTouch: () => void = () => undefined;
|
onTouch: () => void = () => undefined;
|
||||||
|
|
||||||
|
ngAfterViewInit(): void {
|
||||||
|
this.conditionRows.changes.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => {
|
||||||
|
this.focusLastCondition();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
writeValue(value: RuleCompositeCondition) {
|
writeValue(value: RuleCompositeCondition) {
|
||||||
this.form.get('inverted').setValue(value.inverted);
|
this.form.get('inverted').setValue(value.inverted);
|
||||||
this.form.get('booleanMode').setValue(value.booleanMode);
|
this.form.get('booleanMode').setValue(value.booleanMode);
|
||||||
@@ -148,10 +183,6 @@ export class RuleCompositeConditionUiComponent implements ControlValueAccessor,
|
|||||||
this.compositeConditionsFormArray.push(new FormControl(newCondition));
|
this.compositeConditionsFormArray.push(new FormControl(newCondition));
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
|
||||||
this.formSubscription.unsubscribe();
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnChanges(changes: SimpleChanges) {
|
ngOnChanges(changes: SimpleChanges) {
|
||||||
const readOnly = changes['readOnly']?.currentValue;
|
const readOnly = changes['readOnly']?.currentValue;
|
||||||
|
|
||||||
@@ -165,4 +196,19 @@ export class RuleCompositeConditionUiComponent implements ControlValueAccessor,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private focusLastCondition(): void {
|
||||||
|
const rows = this.conditionRows.toArray();
|
||||||
|
const lastRow = rows[rows.length - 1]?.nativeElement;
|
||||||
|
|
||||||
|
if (!lastRow) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
const focusTrap = this.focusTrapFactory.create(lastRow);
|
||||||
|
focusTrap.focusFirstTabbableElement();
|
||||||
|
focusTrap.destroy();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user