Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import { LazyInject } from 'core-app/shared/helpers/angular/lazy-inject.decorato
import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service';
import { editFieldContainerClass } from 'core-app/shared/components/fields/display/display-field-renderer';
import { WorkPackagesListService } from 'core-app/features/work-packages/components/wp-list/wp-list.service';
import { FocusPosition } from 'core-app/features/work-packages/components/wp-fast-table/handlers/state/keyboard/focus-position';

export const activeFieldContainerClassName = 'inline-edit--active-field';
export const activeFieldClassName = 'inline-edit--field';
Expand All @@ -68,8 +69,13 @@ export class TableEditForm extends EditForm<WorkPackageResource> {

@LazyInject() wpListService:WorkPackagesListService;

/** The focus position of the cell the first active field was opened in; null outside rendered occurrences. */
public origin:FocusPosition|null = null;

private submittedOrigin:FocusPosition|null = null;

// Use cell builder to reset edit fields
private cellBuilder = new CellBuilder(this.injector);
private cellBuilder = new CellBuilder(this.injector, this.table.configuration);

// Subscription
private resourceSubscription:Subscription = this
Expand Down Expand Up @@ -109,10 +115,26 @@ export class TableEditForm extends EditForm<WorkPackageResource> {
return this.rowContainer?.querySelector<HTMLTableCellElement>(`.${tdClassName}.${fieldName}`);
}

public override async activate(fieldName:string, noWarnings = false):Promise<void|EditFieldHandler> {
try {
return await super.activate(fieldName, noWarnings);
} finally {
if (!this.hasActiveFields()) {
this.origin = null;
}
}
}

public activateField(form:EditForm, schema:IFieldSchema, fieldName:string, errors:string[]):Promise<EditFieldHandler> {
return this.waitForContainer(fieldName)
.then((cell) => {
this.assertActivationActive();
this.table.editing.forms[this.workPackageId] ??= this;
if (!this.hasActiveFields()) {
this.origin = this.originOf(fieldName);
} else if (this.origin) {
this.table.keyboardNavigation?.recordPosition(this.origin);
}
// Forcibly set the width since the edit field may otherwise
// be given more width. Thereby preserve a minimum width of 150.
// To avoid flickering content, the padding is removed, too.
Expand Down Expand Up @@ -155,9 +177,28 @@ export class TableEditForm extends EditForm<WorkPackageResource> {
td.classList.remove(editModeClassName);

if (focus) {
this.FocusHelper.focus(cell);
this.returnFocusFrom(cell);
}
}

if (!this.hasActiveFields()) {
this.origin = null;
}
}

public override async submit():Promise<WorkPackageResource> {
const origin = this.origin;
const resource = await super.submit();
this.submittedOrigin = origin;
return resource;
}

public override onSubmitComplete():void {
const origin = this.submittedOrigin;
this.submittedOrigin = null;
if (origin && !this.table.destroyed && !this.hasActiveFields()) {
this.table.keyboardNavigation?.returnFocus(origin);
}
}

public async requireVisible(fieldName:string):Promise<void> {
Expand Down Expand Up @@ -224,6 +265,29 @@ export class TableEditForm extends EditForm<WorkPackageResource> {
});
}

private originOf(fieldName:string):FocusPosition|null {
const navigation = this.table.keyboardNavigation;
const td = this.findCell(fieldName);
if (!td || !navigation) {
return null;
}

const origin = navigation.positionOfCell(td);
if (origin) {
navigation.recordPosition(origin);
}
return origin;
}

private returnFocusFrom(container:HTMLElement):void {
const navigation = this.table.keyboardNavigation;
if (!navigation) {
this.FocusHelper.focus(container);
} else if (this.origin) {
navigation.returnFocus(this.origin);
}
}

private get rowContainer() {
return this.table.tableAndTimelineContainer.querySelector(`.${this.classIdentifier}-table`);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ import {
editFieldContainerClass,
} from 'core-app/shared/components/fields/display/display-field-renderer';
import { Injector } from '@angular/core';
import { WorkPackageTableConfiguration } from 'core-app/features/work-packages/components/wp-table/wp-table-configuration';
import { keyboardNavigationEnabled, isNavigableColumn, navigableCellClassName } from 'core-app/features/work-packages/components/wp-fast-table/handlers/state/keyboard/roving-tabindex';
import { QueryColumn } from 'core-app/features/work-packages/components/wp-query/query-column';
import { SchemaCacheService } from 'core-app/core/schemas/schema-cache.service';
import { LazyInject } from 'core-app/shared/helpers/angular/lazy-inject.decorator';
Expand All @@ -46,16 +48,21 @@ export class CellBuilder {

@LazyInject(WorkPackageViewBaselineService) wpTableBaseline:WorkPackageViewBaselineService;

public fieldRenderer = new DisplayFieldRenderer(this.injector, 'table');
public fieldRenderer:DisplayFieldRenderer;

constructor(public injector:Injector) {
constructor(public injector:Injector, private readonly configuration?:WorkPackageTableConfiguration) {
this.fieldRenderer = new DisplayFieldRenderer(this.injector, 'table', { tabbable: !keyboardNavigationEnabled(this.configuration) });
}

public build(workPackage:WorkPackageResource, column:QueryColumn) {
const td = document.createElement('td');
const attribute = column.id;
td.classList.add(tdClassName, attribute);

if (this.configuration && keyboardNavigationEnabled(this.configuration) && isNavigableColumn(column, this.configuration)) {
td.classList.add(navigableCellClassName);
}

if (attribute === 'subject') {
td.classList.add('-max');
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import { GroupObject } from 'core-app/features/hal/resources/wp-collection-resou
import { groupName } from './grouped-rows-helpers';
import { ProjectPhaseDisplayField } from 'core-app/shared/components/fields/display/field-types/project-phase-display-field.module';
import idFromLink from 'core-app/features/hal/helpers/id-from-link';
import { navigableCellClassName } from 'core-app/features/work-packages/components/wp-fast-table/handlers/state/keyboard/roving-tabindex';

export function groupClassNameFor(group:GroupObject) {
return `group-${group.identifier}`;
Expand All @@ -44,7 +45,7 @@ export class GroupHeaderBuilder {

public text:{ collapse:string, expand:string };

constructor(public readonly injector:Injector) {
constructor(public readonly injector:Injector, private readonly keyboardNavigationEnabled = false) {
this.text = {
collapse: this.I18n.t('js.label_collapse'),
expand: this.I18n.t('js.label_expand'),
Expand All @@ -71,8 +72,12 @@ export class GroupHeaderBuilder {
row.id = `wp-table-rowgroup-${group.index}`;
row.dataset.groupIndex = (group.index).toString();
row.dataset.groupIdentifier = group.identifier;
const cellAttributes = this.keyboardNavigationEnabled ? ` class="-no-highlighting ${navigableCellClassName}" tabindex="-1"` : ' class="-no-highlighting"';
if (this.keyboardNavigationEnabled) {
row.tabIndex = -1;
}
row.innerHTML = `
<td colspan="${colspan}" class="-no-highlighting">
<td colspan="${colspan}"${cellAttributes}>
<div class="expander icon-context ${togglerIconClass}">
<span class="sr-only">${escape(text)}</span>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
// See COPYRIGHT and LICENSE files for more details.
//++

import { isNavigableColumn, keyboardNavigationEnabled, navigableCellClassName } from 'core-app/features/work-packages/components/wp-fast-table/handlers/state/keyboard/roving-tabindex';
import { LazyInject } from 'core-app/shared/helpers/angular/lazy-inject.decorator';
import { SingleRowBuilder } from 'core-app/features/work-packages/components/wp-fast-table/builders/rows/single-row-builder';
import { IFieldSchema } from 'core-app/shared/components/fields/field.base';
Expand Down Expand Up @@ -59,6 +60,10 @@ export class GroupSumsBuilder extends SingleRowBuilder {
public renderColumns(sums:GroupObject['sums'], tr:HTMLTableRowElement) {
this.augmentedColumns.forEach((column, i:number) => {
const td = document.createElement('td');
const { configuration } = this.workPackageTable;
if (keyboardNavigationEnabled(configuration) && isNavigableColumn(column, configuration)) {
td.classList.add(navigableCellClassName);
}
const div = this.renderContent(sums, column.id, this.sumsSchema[column.id] as IFieldSchema);

if (i === 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ export class GroupedRenderPass extends PlainRenderPass {
if (nextGroup && groupsChanged) {
const groupClass = groupClassNameFor(nextGroup);
const rowElement = this.headerBuilder.buildGroupRow(nextGroup, this.colspan);
this.appendNonWorkPackageRow(groupOccurrenceKey(nextGroup.index, 'header'), rowElement, groupClass, [groupRowClass]);
this.appendNonWorkPackageRow(groupOccurrenceKey(nextGroup.index, 'header'), rowElement, groupClass, [groupRowClass], false, nextGroup.identifier);
currentGroup = nextGroup;
}

Expand Down Expand Up @@ -184,7 +184,7 @@ export class GroupedRenderPass extends PlainRenderPass {
const { additionalClasses, hidden } = this.groupRowState(group);

rowElement.classList.add(...additionalClasses);
this.appendNonWorkPackageRow(groupOccurrenceKey(group.index, 'sums'), rowElement, groupClass, additionalClasses, hidden);
this.appendNonWorkPackageRow(groupOccurrenceKey(group.index, 'sums'), rowElement, groupClass, additionalClasses, hidden, group.identifier);
}

private groupRowState(group:GroupObject):{ additionalClasses:string[], hidden:boolean } {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import type { RenderPassOptions } from '../../primary-render-pass';
import { GroupedRenderPass } from './grouped-render-pass';
import { groupedRowClassName, groupIdentifier } from './grouped-rows-helpers';
import type { OccurrenceKey } from '../../../rendered-occurrence-ledger';
import { keyboardNavigationEnabled } from 'core-app/features/work-packages/components/wp-fast-table/handlers/state/keyboard/roving-tabindex';

export class GroupedRowsBuilder extends RowsBuilder {
// Injections
Expand Down Expand Up @@ -81,7 +82,7 @@ export class GroupedRowsBuilder extends RowsBuilder {
}

public buildRows(options:RenderPassOptions) {
const builder = new GroupHeaderBuilder(this.injector);
const builder = new GroupHeaderBuilder(this.injector, keyboardNavigationEnabled(this.workPackageTable.configuration));
return new GroupedRenderPass(
this.injector,
this.workPackageTable,
Expand All @@ -94,9 +95,9 @@ export class GroupedRowsBuilder extends RowsBuilder {
/**
* Refresh the group expansion state
*/
public refreshExpansionState() {
public refreshExpansionState():ReadonlyMap<OccurrenceKey, boolean> {
const groups = this.getGroupData();
const builder = new GroupHeaderBuilder(this.injector);
const builder = new GroupHeaderBuilder(this.injector, keyboardNavigationEnabled(this.workPackageTable.configuration));
const updates = new Map<OccurrenceKey, boolean>();

this.workPackageTable.tableAndTimelineContainer
Expand Down Expand Up @@ -137,7 +138,7 @@ export class GroupedRowsBuilder extends RowsBuilder {
});
});

this.workPackageTable.setHidden(updates);
return updates;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
//++

import { fireEvent, waitFor, within } from '@testing-library/dom';
import { groupOccurrenceKey } from '../../../rendered-occurrence-ledger';
import { buildTable, TableHarness } from '../../../testing/table-harness';

const newStatus = { href: '/api/v3/statuses/1' };
Expand Down Expand Up @@ -119,4 +120,12 @@ describe('Grouped table sums rows', () => {
expect(sumsTimelineRow(0)).toBeVisible();
expect(sumsTimelineRow(1)).toBeVisible();
});

it('tags group header and sums occurrences with the group identifier', () => {
const identifier = harness.groupHeader(1).dataset.groupIdentifier;

expect(identifier).toBeTruthy();
expect(harness.table.ledger.byKey(groupOccurrenceKey(1, 'header'))?.groupIdentifier).toBe(identifier);
expect(harness.table.ledger.byKey(groupOccurrenceKey(1, 'sums'))?.groupIdentifier).toBe(identifier);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {
} from 'core-app/features/work-packages/components/wp-fast-table/helpers/wp-table-hierarchy-helpers';
import { WorkPackageViewHierarchiesService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-hierarchy.service';
import { LazyInject } from 'core-app/shared/helpers/angular/lazy-inject.decorator';
import { keyboardNavigationEnabled } from 'core-app/features/work-packages/components/wp-fast-table/handlers/state/keyboard/roving-tabindex';

export const indicatorCollapsedClass = '-hierarchy-collapsed';
export const hierarchyCellClassName = 'wp-table--hierarchy-span';
Expand Down Expand Up @@ -172,6 +173,7 @@ export class SingleHierarchyRowBuilder extends SingleRowBuilder {
private buildHierarchyIndicator(workPackage:WorkPackageResource, row:HTMLTableRowElement, level:number):HTMLElement {
const hierarchyIndicator = document.createElement('span');
const collapsed = this.wpTableHierarchies.collapsed(workPackage.id!);
const tabindex = keyboardNavigationEnabled(this.workPackageTable.configuration) ? '-1' : '0';
const indicatorWidth = `${hierarchyBaseIndentation + (hierarchyIndentation * level)}px`;
hierarchyIndicator.classList.add(hierarchyCellClassName);
hierarchyIndicator.style.width = indicatorWidth;
Expand All @@ -180,7 +182,7 @@ export class SingleHierarchyRowBuilder extends SingleRowBuilder {
if (this.parentsWithVisibleChildren[workPackage.id!]) {
const className = collapsed ? indicatorCollapsedClass : '';
hierarchyIndicator.innerHTML = `
<a href tabindex="0" role="button" class="wp-table--hierarchy-indicator ${className}">
<a href tabindex="${tabindex}" role="button" class="wp-table--hierarchy-indicator ${className}">
<span class="wp-table--hierarchy-indicator-icon" aria-hidden="true"></span>
<span class="wp-table--hierarchy-indicator-expanded sr-only">${this.text.expanded(
level,
Expand All @@ -192,7 +194,7 @@ export class SingleHierarchyRowBuilder extends SingleRowBuilder {
`;
} else {
hierarchyIndicator.innerHTML = `
<span tabindex="0" class="wp-table--leaf-indicator">
<span tabindex="${tabindex}" class="wp-table--leaf-indicator">
<span class="sr-only">${this.text.leaf(level)}</span>
</span>
`;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ workPackage:WorkPackageResource,
classIdentifier:string,
additionalClasses:string[] = [],
hidden = false,
groupIdentifier?:string,
) {
row.classList.add(classIdentifier);
this.registerAppended(row, {
Expand All @@ -255,6 +256,7 @@ workPackage:WorkPackageResource,
workPackageId: null,
renderType: 'primary',
hidden,
groupIdentifier,
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ import { States } from 'core-app/core/states/states.service';
import { WorkPackageRelationsService } from '../../wp-relations/wp-relations.service';
import { QueryColumn, queryColumnTypes } from '../../wp-query/query-column';
import { tdClassName } from './cell-builder';
import { keyboardNavigationEnabled, navigableCellClassName } from 'core-app/features/work-packages/components/wp-fast-table/handlers/state/keyboard/roving-tabindex';
import { WorkPackageTableConfiguration } from 'core-app/features/work-packages/components/wp-table/wp-table-configuration';

export const relationCellTdClassName = 'wp-table--relation-cell-td';
export const relationCellIndicatorClassName = 'wp-table--relation-indicator';
Expand All @@ -46,14 +48,18 @@ export class RelationCellbuilder {

@LazyInject() wpTableRelationColumns:WorkPackageViewRelationColumnsService;

constructor(public readonly injector:Injector) {
constructor(public readonly injector:Injector, private readonly configuration?:WorkPackageTableConfiguration) {
}

public build(workPackage:WorkPackageResource, column:QueryColumn) {
const td = document.createElement('td');
td.classList.add(tdClassName, relationCellTdClassName, column.id);
td.dataset.columnId = column.id;

if (keyboardNavigationEnabled(this.configuration)) {
td.classList.add(navigableCellClassName);
}

// Get current expansion and value state
const expanded = this.wpTableRelationColumns.getExpandFor(workPackage.id!) === column.id;
const relationState = this.wpRelations.state(workPackage.id!).value;
Expand Down Expand Up @@ -90,7 +96,7 @@ export class RelationCellbuilder {
const indicator = document.createElement('span');
indicator.classList.add(relationCellIndicatorClassName);
indicator.setAttribute('aria-hidden', 'true');
indicator.setAttribute('tabindex', '0');
indicator.setAttribute('tabindex', keyboardNavigationEnabled(this.configuration) ? '-1' : '0');

return indicator;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ import { LazyInject } from 'core-app/shared/helpers/angular/lazy-inject.decorato
import { States } from 'core-app/core/states/states.service';
import { commonRowClassName, SingleRowBuilder, tableRowClassName } from '../rows/single-row-builder';
import { tdClassName } from '../cell-builder';
import { keyboardNavigationEnabled, navigableCellClassName } from '../../handlers/state/keyboard/roving-tabindex';
import { removeTabStops } from 'core-app/shared/helpers/dom/remove-tab-stops';
import { WorkPackageTable } from '../../wp-fast-table';
import { isRelationColumn, QueryColumn } from '../../../wp-query/query-column';

Expand Down Expand Up @@ -94,6 +96,9 @@ public readonly injector:Injector,
const tr = document.createElement('tr');
tr.dataset.workPackageId = to.id!;
tr.dataset.classIdentifier = identifier;
if (keyboardNavigationEnabled(this.workPackageTable.configuration)) {
tr.setAttribute('tabindex', '-1');
}

tr.classList.add(
commonRowClassName,
Expand Down Expand Up @@ -128,12 +133,19 @@ relationGroupClass(from.id!),
relationLabel.textContent = typeLabel;

row.querySelector(`.${relationCellClassName}`)!.innerHTML = '';
row.querySelector(`.${relationCellClassName}.${columnId}`)!.append(relationLabel);
const labelCell = row.querySelector<HTMLElement>(`.${relationCellClassName}.${columnId}`)!;
labelCell.append(relationLabel);
if (keyboardNavigationEnabled(this.workPackageTable.configuration)) {
removeTabStops(labelCell);
}
}

protected emptyRelationCell(column:QueryColumn) {
const cell = document.createElement('td');
cell.classList.add(relationCellClassName, tdClassName, column.id);
if (keyboardNavigationEnabled(this.workPackageTable.configuration)) {
cell.classList.add(navigableCellClassName);
}

return cell;
}
Expand Down
Loading
Loading