mirror of
https://github.com/opf/openproject.git
synced 2026-06-13 19:20:00 +00:00
Merge pull request #23256 from opf/fix/eslint-whitespace-errors
Fix auto-correctable eslint errors in `frontend/`
This commit is contained in:
@@ -42,7 +42,7 @@ import {
|
||||
states: OPENPROJECT_ROUTES,
|
||||
useHash: false,
|
||||
config: uiRouterConfiguration,
|
||||
} as any),
|
||||
}),
|
||||
],
|
||||
providers: [
|
||||
FirstRouteService,
|
||||
|
||||
@@ -37,7 +37,7 @@ export function getMetaContent<T extends string|null>(
|
||||
defaultValue?:T
|
||||
):string|T {
|
||||
const content = getMetaElement(name)?.content ?? defaultValue ?? '';
|
||||
return content as string|T;
|
||||
return content;
|
||||
}
|
||||
|
||||
export function getMetaValue(name:string, key:string):string;
|
||||
@@ -48,5 +48,5 @@ export function getMetaValue<T extends string|null>(
|
||||
defaultValue?:T
|
||||
):string|T {
|
||||
const value = getMetaElement(name)?.dataset[key] ?? defaultValue ?? '';
|
||||
return value as string|T;
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -195,7 +195,7 @@ export class AttachmentsResourceService extends ResourceStoreService<IAttachment
|
||||
}
|
||||
|
||||
if (isNewResource(resource)) {
|
||||
return this.configurationService.prepareAttachmentURL as string|null;
|
||||
return this.configurationService.prepareAttachmentURL;
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
+1
-1
@@ -54,7 +54,7 @@ export class EditableQueryPropsComponent implements OnInit {
|
||||
})();
|
||||
|
||||
this.externalQuery.show({
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
|
||||
currentQuery: queryProperties,
|
||||
urlParams: this.urlParams,
|
||||
callback: (queryProps:string) => {
|
||||
|
||||
+1
-1
@@ -42,7 +42,7 @@ import { IfcModelsDataService } from 'core-app/features/bim/ifc_models/pages/vie
|
||||
aria-hidden="true"></span>
|
||||
</a>
|
||||
}
|
||||
|
||||
|
||||
`,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
selector: 'op-bcf-manage-ifc-button',
|
||||
|
||||
@@ -184,7 +184,7 @@ export class BoardListComponent extends AbstractWidgetComponent implements OnIni
|
||||
this.resource.isNewWidget = false;
|
||||
|
||||
// Set initial selection if split view open
|
||||
const detailsMatch = window.location.pathname.match(/\/details\/(\d+)/);
|
||||
const detailsMatch = /\/details\/(\d+)/.exec(window.location.pathname);
|
||||
if (detailsMatch) {
|
||||
this.wpViewSelectionService.initializeSelection([detailsMatch[1]]);
|
||||
}
|
||||
|
||||
@@ -171,7 +171,7 @@ export class OpWorkPackagesCalendarService extends UntilDestroyedMixin {
|
||||
|
||||
let queryId:string|null = null;
|
||||
if (this.urlParams.query_id) {
|
||||
queryId = this.urlParams.query_id as string;
|
||||
queryId = this.urlParams.query_id;
|
||||
}
|
||||
// We derive the necessary props in the following cases
|
||||
// 1. We load a queryId with no props
|
||||
@@ -199,7 +199,7 @@ export class OpWorkPackagesCalendarService extends UntilDestroyedMixin {
|
||||
// There might also be a query_id but the settings persisted in it are overwritten by the props.
|
||||
if (this.urlParams.query_props) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
const oldQueryProps:Record<string, unknown> = JSON.parse(this.urlParams.query_props as string);
|
||||
const oldQueryProps:Record<string, unknown> = JSON.parse(this.urlParams.query_props);
|
||||
|
||||
// Update the date period of the calendar in the filter
|
||||
const newQueryProps = {
|
||||
@@ -254,7 +254,7 @@ export class OpWorkPackagesCalendarService extends UntilDestroyedMixin {
|
||||
}
|
||||
|
||||
public get initialView():string|undefined {
|
||||
return this.urlParams.cview as string|undefined;
|
||||
return this.urlParams.cview;
|
||||
}
|
||||
|
||||
dateEditable(wp:WorkPackageResource):boolean {
|
||||
@@ -425,7 +425,7 @@ export class OpWorkPackagesCalendarService extends UntilDestroyedMixin {
|
||||
}
|
||||
|
||||
private get initialDate():string|undefined {
|
||||
const date = this.urlParams.cdate as string|undefined;
|
||||
const date = this.urlParams.cdate;
|
||||
if (date) {
|
||||
return this.timezoneService.formattedISODate(date);
|
||||
}
|
||||
|
||||
@@ -239,6 +239,7 @@ export class TimeEntryCalendarComponent implements AfterViewInit, OnDestroy {
|
||||
}
|
||||
|
||||
protected fetchTimeEntries(start:Moment, end:Moment):Promise<CollectionResource<TimeEntryResource>> {
|
||||
// eslint-disable-next-line @typescript-eslint/prefer-optional-chain
|
||||
if (!this.memoizedTimeEntries
|
||||
|| this.memoizedTimeEntries.start.valueOf() !== start.valueOf()
|
||||
|| this.memoizedTimeEntries.end.valueOf() !== end.valueOf()) {
|
||||
|
||||
@@ -217,7 +217,7 @@ export class WorkPackagesCalendarComponent extends UntilDestroyedMixin implement
|
||||
allDay: false,
|
||||
className: 'fc-event-clickable op-wp-calendar--meeting-resource',
|
||||
meeting,
|
||||
} as EventInput;
|
||||
};
|
||||
});
|
||||
|
||||
successCallback(events);
|
||||
@@ -262,7 +262,7 @@ export class WorkPackagesCalendarComponent extends UntilDestroyedMixin implement
|
||||
],
|
||||
// DnD configuration
|
||||
selectable: true,
|
||||
select: this.handleDateClicked.bind(this) as unknown,
|
||||
select: this.handleDateClicked.bind(this),
|
||||
eventResizableFromStart: true,
|
||||
editable: true,
|
||||
displayEventTime: true,
|
||||
|
||||
+1
-1
@@ -538,7 +538,7 @@ export class TeamPlannerComponent extends UntilDestroyedMixin implements OnInit,
|
||||
resources: skeletonResources,
|
||||
resourceAreaWidth: this.isMobile ? '60px' : '180px',
|
||||
resourceOrder: 'title',
|
||||
select: this.handleDateClicked.bind(this) as unknown,
|
||||
select: this.handleDateClicked.bind(this),
|
||||
// DnD configuration
|
||||
editable: true,
|
||||
droppable: true,
|
||||
|
||||
+5
-5
@@ -60,13 +60,13 @@ export class FilterDateTimeValueComponent extends AbstractDateTimeValueControlle
|
||||
return this.filter.values[0];
|
||||
}
|
||||
|
||||
public get valueString() {
|
||||
return this.filter.values[0].toString();
|
||||
public set value(val) {
|
||||
this.filter.values = [val as string]; // eslint-disable-line @typescript-eslint/no-unnecessary-type-assertion
|
||||
this.filterChanged.emit(this.filter);
|
||||
}
|
||||
|
||||
public set value(val) {
|
||||
this.filter.values = [val as string];
|
||||
this.filterChanged.emit(this.filter);
|
||||
public get valueString() {
|
||||
return this.filter.values[0].toString();
|
||||
}
|
||||
|
||||
public get lowerBoundary():Moment|null {
|
||||
|
||||
+4
-4
@@ -135,12 +135,12 @@ describe('WorkPackageFilterValues', () => {
|
||||
setupTestBed();
|
||||
});
|
||||
|
||||
it('it should not apply the first value (Regression #30817)', (() => {
|
||||
it('should not apply the first value (Regression #30817)', () => {
|
||||
subject.applyDefaultsFromFilters(changeset);
|
||||
|
||||
expect(changeset.changedAttributes.length).toEqual(0);
|
||||
expect(changeset.value<HalResource>('type').href).toEqual('/api/v3/types/1');
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
describe('with the second type applied', () => {
|
||||
@@ -158,12 +158,12 @@ describe('WorkPackageFilterValues', () => {
|
||||
setupTestBed();
|
||||
});
|
||||
|
||||
it('it should not keep the second value (Regression #30817)', (() => {
|
||||
it('should not keep the second value (Regression #30817)', () => {
|
||||
subject.applyDefaultsFromFilters(changeset);
|
||||
|
||||
expect(changeset.changedAttributes.length).toEqual(0);
|
||||
expect(changeset.value<HalResource>('type').href).toEqual('/api/v3/types/2');
|
||||
}));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+6
-2
@@ -50,7 +50,9 @@ export class WorkPackageStateLinksHandler implements TableEventHandler {
|
||||
|
||||
// Locate the details link from event
|
||||
const target = evt.target as HTMLElement;
|
||||
const element = target.closest(this.SELECTOR) as HTMLElement;
|
||||
const element = target.closest<HTMLElement>(this.SELECTOR);
|
||||
if (!element) { return true; }
|
||||
|
||||
const state = element.dataset.wpState;
|
||||
const workPackageId = element.dataset.workPackageId;
|
||||
|
||||
@@ -66,7 +68,9 @@ export class WorkPackageStateLinksHandler implements TableEventHandler {
|
||||
// not matter what other rows are (de-)selected below.
|
||||
// Thus save that row for the details view button.
|
||||
// Locate the row from event
|
||||
const row = target.closest(`.${tableRowClassName}`) as HTMLElement;
|
||||
const row = target.closest<HTMLElement>(`.${tableRowClassName}`);
|
||||
if (!row) { return true; }
|
||||
|
||||
const classIdentifier = row.dataset.classIdentifier!;
|
||||
const [index] = view.workPackageTable.findRenderedRow(classIdentifier);
|
||||
|
||||
|
||||
+2
-1
@@ -187,7 +187,8 @@ export class WorkPackageInlineCreateComponent extends UntilDestroyedMixin implem
|
||||
this.untilDestroyed(),
|
||||
)
|
||||
.subscribe((wp:WorkPackageResource) => {
|
||||
if (this.currentWorkPackage && this.currentWorkPackage.__initialized_at === wp.__initialized_at) {
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
if (this.currentWorkPackage?.__initialized_at === wp.__initialized_at) {
|
||||
// Remove row and focus
|
||||
this.resetRow();
|
||||
|
||||
|
||||
+57
-35
@@ -36,23 +36,37 @@ import { SchemaResource } from 'core-app/features/hal/resources/schema-resource'
|
||||
import { QuerySortByResource } from 'core-app/features/hal/resources/query-sort-by-resource';
|
||||
import { QueryGroupByResource } from 'core-app/features/hal/resources/query-group-by-resource';
|
||||
import { QueryColumn } from '../wp-query/query-column';
|
||||
import { HalResource } from 'core-app/features/hal/resources/hal-resource';
|
||||
import { SchemaAttributeObject } from 'core-app/features/hal/resources/schema-attribute-object';
|
||||
import { QueryFilterInstanceResource } from 'core-app/features/hal/resources/query-filter-instance-resource';
|
||||
|
||||
interface QueryFormSchemaProperties {
|
||||
columns:SchemaAttributeObject<QueryColumn>;
|
||||
sortBy:SchemaAttributeObject<QuerySortByResource>;
|
||||
groupBy:SchemaAttributeObject<QueryGroupByResource>;
|
||||
}
|
||||
|
||||
type QueryFormSchema = SchemaResource & QueryFormSchemaProperties;
|
||||
|
||||
@Injectable()
|
||||
export class WorkPackagesListInvalidQueryService {
|
||||
protected halResourceService = inject(HalResourceService);
|
||||
|
||||
|
||||
public restoreQuery(query:QueryResource, form:QueryFormResource) {
|
||||
this.restoreFilters(query, form.payload, form.schema);
|
||||
this.restoreColumns(query, form.payload, form.schema);
|
||||
this.restoreSortBy(query, form.payload, form.schema);
|
||||
this.restoreGroupBy(query, form.payload, form.schema);
|
||||
this.restoreOtherProperties(query, form.payload);
|
||||
const payload = form.payload as QueryResource;
|
||||
const schema = form.schema as QueryFormSchema;
|
||||
this.restoreFilters(query, payload, form.filtersSchemas);
|
||||
this.restoreColumns(query, payload, schema);
|
||||
this.restoreSortBy(query, payload, schema);
|
||||
this.restoreGroupBy(query, payload, schema);
|
||||
this.restoreOtherProperties(query, payload);
|
||||
}
|
||||
|
||||
private restoreFilters(query:QueryResource, payload:QueryResource, querySchema:SchemaResource) {
|
||||
let filters = _.map((payload.filters), (filter) => {
|
||||
const filterInstanceSchema = _.find(querySchema.filtersSchemas.elements, (schema:QueryFilterInstanceSchemaResource) => (schema.filter.allowedValues as QueryFilterResource[])[0].href === filter.filter.href);
|
||||
private restoreFilters(query:QueryResource, payload:QueryResource, filtersSchemas:QueryFilterInstanceSchemaResource[]) {
|
||||
const filters = payload.filters.map((filter) => {
|
||||
const filterInstanceSchema = filtersSchemas.find(
|
||||
(schema) => (schema.filter.allowedValues as QueryFilterResource[])[0].href === filter.filter.href,
|
||||
);
|
||||
|
||||
if (!filterInstanceSchema) {
|
||||
return null;
|
||||
@@ -60,56 +74,64 @@ export class WorkPackagesListInvalidQueryService {
|
||||
|
||||
const recreatedFilter = filterInstanceSchema.getFilter();
|
||||
|
||||
const operator = _.find(filterInstanceSchema.operator.allowedValues, (operator) => operator.href === filter.operator.href);
|
||||
const operator = (filterInstanceSchema.operator.allowedValues as HalResource[]).find(
|
||||
(op) => op.href === filter.operator.href,
|
||||
);
|
||||
|
||||
if (operator) {
|
||||
recreatedFilter.operator = operator;
|
||||
recreatedFilter.operator = operator as typeof recreatedFilter.operator;
|
||||
}
|
||||
|
||||
recreatedFilter.values.length = 0;
|
||||
_.each(filter.values, (value) => recreatedFilter.values.push(value));
|
||||
recreatedFilter.values = filter.values.slice();
|
||||
|
||||
return recreatedFilter;
|
||||
});
|
||||
|
||||
filters = _.compact(filters);
|
||||
}).filter((f):f is QueryFilterInstanceResource => f != null);
|
||||
|
||||
// clear filters while keeping reference
|
||||
query.filters.length = 0;
|
||||
_.each(filters, (filter) => query.filters.push(filter));
|
||||
filters.forEach((filter) => query.filters.push(filter));
|
||||
}
|
||||
|
||||
private restoreColumns(query:QueryResource, stubQuery:QueryResource, schema:SchemaResource) {
|
||||
let columns = _.map(stubQuery.columns, (column) => _.find((schema.columns.allowedValues as QueryColumn[]), (candidate) => candidate.href === column.href));
|
||||
|
||||
columns = _.compact(columns);
|
||||
private restoreColumns(query:QueryResource, stubQuery:QueryResource, schema:QueryFormSchema) {
|
||||
const columns = stubQuery.columns
|
||||
.map((column) => (schema.columns.allowedValues as QueryColumn[]).find((candidate) => candidate.href === column.href))
|
||||
.filter((column):column is QueryColumn => column != null);
|
||||
|
||||
query.columns.length = 0;
|
||||
_.each(columns, (column) => query.columns.push(column!));
|
||||
columns.forEach((column) => query.columns.push(column));
|
||||
}
|
||||
|
||||
private restoreSortBy(query:QueryResource, stubQuery:QueryResource, schema:SchemaResource) {
|
||||
let sortBys = _.map((stubQuery.sortBy), (sortBy) => _.find((schema.sortBy.allowedValues as QuerySortByResource[]), (candidate) => candidate.href === sortBy.href)!);
|
||||
|
||||
sortBys = _.compact(sortBys);
|
||||
private restoreSortBy(query:QueryResource, stubQuery:QueryResource, schema:QueryFormSchema) {
|
||||
const sortBys = stubQuery.sortBy
|
||||
.map((sortBy) => (schema.sortBy.allowedValues as QuerySortByResource[]).find((candidate) => candidate.href === sortBy.href))
|
||||
.filter((sortBy):sortBy is QuerySortByResource => sortBy != null);
|
||||
|
||||
query.sortBy.length = 0;
|
||||
_.each(sortBys, (sortBy) => query.sortBy.push(sortBy));
|
||||
sortBys.forEach((sortBy) => query.sortBy.push(sortBy));
|
||||
}
|
||||
|
||||
private restoreGroupBy(query:QueryResource, stubQuery:QueryResource, schema:SchemaResource) {
|
||||
const groupBy = _.find((schema.groupBy.allowedValues as QueryGroupByResource[]), (candidate) => stubQuery.groupBy && stubQuery.groupBy.href === candidate.href) as any;
|
||||
private restoreGroupBy(query:QueryResource, stubQuery:QueryResource, schema:QueryFormSchema) {
|
||||
const groupBy = (schema.groupBy.allowedValues as QueryGroupByResource[]).find(
|
||||
(candidate) => stubQuery.groupBy?.href === candidate.href,
|
||||
);
|
||||
|
||||
query.groupBy = groupBy;
|
||||
}
|
||||
|
||||
private restoreOtherProperties(query:QueryResource, stubQuery:QueryResource) {
|
||||
_.without(Object.keys(stubQuery.$source), '_links', 'filters').forEach((property:any) => {
|
||||
query[property] = stubQuery[property];
|
||||
});
|
||||
const source = stubQuery.$source as Record<string, unknown>;
|
||||
const links = (source._links ?? {}) as Record<string, unknown>;
|
||||
|
||||
_.without(Object.keys(stubQuery.$source._links), 'columns', 'groupBy', 'sortBy').forEach((property:any) => {
|
||||
query[property] = stubQuery[property];
|
||||
});
|
||||
Object.keys(source)
|
||||
.filter((key) => key !== '_links' && key !== 'filters')
|
||||
.forEach((property) => {
|
||||
(query as Record<string, unknown>)[property] = (stubQuery as Record<string, unknown>)[property];
|
||||
});
|
||||
|
||||
Object.keys(links)
|
||||
.filter((key) => key !== 'columns' && key !== 'groupBy' && key !== 'sortBy')
|
||||
.forEach((property) => {
|
||||
(query as Record<string, unknown>)[property] = (stubQuery as Record<string, unknown>)[property];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -366,7 +366,7 @@ export class WorkPackagesListService {
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||
if (!currentForm || !query.$links.update || query.$links.update.href !== currentForm.href) {
|
||||
if (!currentForm || query.$links.update?.href !== currentForm.href) {
|
||||
return this.loadForm(query);
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -89,7 +89,7 @@ export class WorkPackageStatesInitializationService {
|
||||
const schema:QuerySchemaResource = form.schema as any;
|
||||
|
||||
_.each(schema.filtersSchemas.elements, (schema) => {
|
||||
this.states.schemas.get(schema.href!).putValue(schema as any);
|
||||
this.states.schemas.get(schema.href!).putValue(schema);
|
||||
});
|
||||
|
||||
this.wpTableFilters.initializeFilters(query, schema);
|
||||
|
||||
@@ -59,7 +59,7 @@ import { HalResourceService } from 'core-app/features/hal/services/hal-resource.
|
||||
import { ResourceChangeset } from 'core-app/shared/components/fields/changeset/resource-changeset';
|
||||
import { AttachmentsResourceService } from 'core-app/core/state/attachments/attachments.service';
|
||||
import { AttachmentCollectionResource } from 'core-app/features/hal/resources/attachment-collection-resource';
|
||||
import { HalSource, HalSourceLink } from 'core-app/features/hal/interfaces';
|
||||
import { HalSource } from 'core-app/features/hal/interfaces';
|
||||
|
||||
export const newWorkPackageHref = '/api/v3/work_packages/new';
|
||||
|
||||
@@ -371,7 +371,7 @@ export class WorkPackageCreateService extends UntilDestroyedMixin {
|
||||
} else if (!value) {
|
||||
payload._links[attribute] = { href: null };
|
||||
} else {
|
||||
payload._links[attribute] = value as unknown as HalSourceLink;
|
||||
payload._links[attribute] = value;
|
||||
}
|
||||
delete payload[attribute];
|
||||
});
|
||||
|
||||
@@ -373,7 +373,7 @@ export class UrlParamsHelperService {
|
||||
queryData.sortBy = this.buildV3GetSortByFromQuery(query);
|
||||
queryData.timestamps = query.timestamps.join(',');
|
||||
|
||||
return _.extend(additionalParams, queryData) as Partial<QueryRequestParams>;
|
||||
return _.extend(additionalParams, queryData);
|
||||
}
|
||||
|
||||
public queryFilterValueToParam(value:HalResource|string|boolean):string {
|
||||
|
||||
-1
@@ -102,7 +102,6 @@ export class WorkPackageRelationsComponent extends UntilDestroyedMixin implement
|
||||
const updateWorkPackage = !!form.dataset?.updateWorkPackage;
|
||||
|
||||
if (updateWorkPackage) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||
if (event.detail?.success) {
|
||||
// Update the work package
|
||||
void this.apiV3Service
|
||||
|
||||
+1
-1
@@ -69,7 +69,7 @@ export class WpTableConfigurationRelationSelectorComponent implements OnInit {
|
||||
|
||||
private async initializeRelationFilters():Promise<void> {
|
||||
await this.wpTableFilters.onReady();
|
||||
this.availableRelationFilters = this.relationFiltersOf(this.wpTableFilters.availableFilters) as QueryFilterResource[];
|
||||
this.availableRelationFilters = this.relationFiltersOf(this.wpTableFilters.availableFilters);
|
||||
this.setSelectedRelationFilter();
|
||||
this.cdRef.markForCheck();
|
||||
}
|
||||
|
||||
+1
-1
@@ -89,7 +89,7 @@ export abstract class WorkPackageEmbeddedBaseComponent extends WorkPackagesViewB
|
||||
const query = this.querySpace.query.value!;
|
||||
this.wpStatesInitialization.applyToQuery(query);
|
||||
|
||||
return this.urlParamsHelper.buildV3GetQueryFromQueryResource(query) as object;
|
||||
return this.urlParamsHelper.buildV3GetQueryFromQueryResource(query);
|
||||
}
|
||||
|
||||
public buildUrlParams() {
|
||||
|
||||
+1
-1
@@ -109,7 +109,7 @@ export class SortHeaderDirective extends UntilDestroyedMixin implements AfterVie
|
||||
.subscribe(() => {
|
||||
const latestSortElement = this.wpTableSortBy.current[0];
|
||||
|
||||
if (!latestSortElement || this.headerColumn.href !== latestSortElement.column.href) {
|
||||
if (this.headerColumn.href !== latestSortElement?.column.href) {
|
||||
this.currentSortDirection = null;
|
||||
} else {
|
||||
this.currentSortDirection = latestSortElement.direction;
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ export class OpUnlinkTableAction extends OpTableAction {
|
||||
identifier,
|
||||
title,
|
||||
applicable,
|
||||
onClick) as OpTableAction;
|
||||
onClick);
|
||||
}
|
||||
|
||||
public buildElement() {
|
||||
|
||||
+5
-3
@@ -83,8 +83,8 @@ export class TimelineCellRenderer {
|
||||
}
|
||||
|
||||
public isEmpty(wp:WorkPackageResource) {
|
||||
const start = moment(wp.startDate as any);
|
||||
const due = moment(wp.dueDate as any);
|
||||
const start = moment(wp.startDate);
|
||||
const due = moment(wp.dueDate);
|
||||
const noStartAndDueValues = _.isNaN(start.valueOf()) && _.isNaN(due.valueOf());
|
||||
return noStartAndDueValues;
|
||||
}
|
||||
@@ -240,7 +240,9 @@ export class TimelineCellRenderer {
|
||||
*/
|
||||
public update(element:HTMLDivElement, labels:WorkPackageCellLabels|null, renderInfo:RenderInfo):boolean {
|
||||
const { change } = renderInfo;
|
||||
const bar = element.querySelector(`.${timelineBackgroundElementClass}`) as HTMLElement;
|
||||
const bar = element.querySelector<HTMLElement>(`.${timelineBackgroundElementClass}`);
|
||||
if (!bar) { return false; }
|
||||
|
||||
let start = moment(change.projectedResource.startDate);
|
||||
let due = moment(change.projectedResource.dueDate);
|
||||
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ export class TimelineMilestoneCellRenderer extends TimelineCellRenderer {
|
||||
}
|
||||
|
||||
public isEmpty(wp:WorkPackageResource) {
|
||||
const date = moment(wp.date as any);
|
||||
const date = moment(wp.date);
|
||||
return _.isNaN(date.valueOf());
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -131,7 +131,7 @@ export class WorkPackageTableTimelineRelations extends UntilDestroyedMixin imple
|
||||
)
|
||||
.subscribe((list) => {
|
||||
// ... make sure that the corresponding relations are loaded ...
|
||||
const wps = _.compact(list.map((row) => row.workPackageId) as string[]);
|
||||
const wps = _.compact(list.map((row) => row.workPackageId));
|
||||
void this.wpRelations.requireAll(wps);
|
||||
});
|
||||
|
||||
|
||||
+1
-1
@@ -91,6 +91,6 @@ export class WorkPackageViewGroupByService extends WorkPackageQueryStateService<
|
||||
|
||||
public isCurrentlyGroupedBy(column:QueryColumn):boolean {
|
||||
const cur = this.current;
|
||||
return !!(cur && cur.id === column.id);
|
||||
return cur?.id === column.id;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -40,7 +40,7 @@ export class WorkPackageViewHighlightingService extends WorkPackageQueryStateSer
|
||||
}
|
||||
|
||||
public get current():WorkPackageViewHighlight {
|
||||
const value = this.lastUpdatedState.getValueOr({ mode: 'inline' } as WorkPackageViewHighlight);
|
||||
const value = this.lastUpdatedState.getValueOr({ mode: 'inline' } as WorkPackageViewHighlight); // eslint-disable-line @typescript-eslint/no-unnecessary-type-assertion
|
||||
return this.filteredValue(value);
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -158,7 +158,7 @@ export class WorkPackageViewTimelineService extends WorkPackageQueryStateService
|
||||
* @param update
|
||||
*/
|
||||
private modify(update:Partial<WorkPackageTimelineState>) {
|
||||
this.update({ ...this.current, ...update } as WorkPackageTimelineState);
|
||||
this.update({ ...this.current, ...update });
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -33,7 +33,7 @@ import { PathHelperService } from 'core-app/core/path-helper/path-helper.service
|
||||
import { UrlParamsHelperService } from 'core-app/features/work-packages/components/wp-query/url-params-helper';
|
||||
import { ToastService } from 'core-app/shared/components/toaster/toast.service';
|
||||
import { I18nService } from 'core-app/core/i18n/i18n.service';
|
||||
import { HalDeletedEvent, HalEventsService } from 'core-app/features/hal/services/hal-events.service';
|
||||
import { HalEventsService } from 'core-app/features/hal/services/hal-events.service';
|
||||
import { States } from 'core-app/core/states/states.service';
|
||||
import { resolveNumericId } from 'core-app/features/work-packages/helpers/work-package-id-resolvers';
|
||||
|
||||
@@ -69,7 +69,7 @@ export class WorkPackageService {
|
||||
.then(() => {
|
||||
this.toastService.addSuccess(this.text.successful_delete);
|
||||
|
||||
ids.forEach((id) => this.halEvents.push({ _type: 'WorkPackage', id }, { eventType: 'deleted' } as HalDeletedEvent));
|
||||
ids.forEach((id) => this.halEvents.push({ _type: 'WorkPackage', id }, { eventType: 'deleted' }));
|
||||
|
||||
const routeWpId = this.$state.params.workPackageId as string;
|
||||
const numericId = resolveNumericId(this.states, routeWpId);
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ describe('AttributeHelpTextModalService', () => {
|
||||
let dialog:HTMLDialogElement|null;
|
||||
|
||||
beforeEach(() => {
|
||||
fetchSpy = vi.spyOn(window, 'fetch') as unknown as Mock<Window['fetch']>;
|
||||
fetchSpy = vi.spyOn(window, 'fetch'); // eslint-disable-line @typescript-eslint/no-unsafe-assignment
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
|
||||
+3
-4
@@ -7,7 +7,6 @@ import { of, map } from 'rxjs';
|
||||
import { NgSelectModule } from '@ng-select/ng-select';
|
||||
|
||||
import { OpAutocompleterComponent } from './op-autocompleter.component';
|
||||
import { TOpAutocompleterResource } from './typings';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
|
||||
|
||||
@@ -93,11 +92,11 @@ describe('autocompleter', () => {
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(OpAutocompleterComponent);
|
||||
getOptionsFnSpy = vi.fn().mockImplementation((searchTerm:string) => {
|
||||
getOptionsFnSpy = vi.fn().mockImplementation((searchTerm:string) => { // eslint-disable-line @typescript-eslint/no-unsafe-assignment
|
||||
return of(workPackagesStub).pipe(map((wps) => wps.filter((wp) => searchTerm !== '' && wp.subject.includes(searchTerm))));
|
||||
}) as unknown as Mock;
|
||||
});
|
||||
|
||||
fixture.componentInstance.resource = 'work_packages' as TOpAutocompleterResource;
|
||||
fixture.componentInstance.resource = 'work_packages';
|
||||
fixture.componentInstance.filters = [];
|
||||
fixture.componentInstance.searchKey = 'typeahead';
|
||||
fixture.componentInstance.appendTo = 'body';
|
||||
|
||||
+1
-1
@@ -124,7 +124,7 @@ export class TimeEntriesWorkPackageAutocompleterComponent extends OpAutocomplete
|
||||
const base = this.filters ?? [];
|
||||
const isRecent = this.mode === 'recent';
|
||||
if (isRecent && this.recentWorkPackageIds.length > 0) {
|
||||
return [...base, { name: 'id', operator: '=', values: this.recentWorkPackageIds } as IAPIFilter];
|
||||
return [...base, { name: 'id', operator: '=', values: this.recentWorkPackageIds }];
|
||||
}
|
||||
|
||||
return base;
|
||||
|
||||
+1
-1
@@ -264,7 +264,7 @@ export class OpBasicRangeDatePickerComponent implements OnInit, ControlValueAcce
|
||||
|
||||
private appendToBodyOrDialog():HTMLElement|undefined {
|
||||
if (this.inDialog) {
|
||||
return document.querySelector(`#${this.inDialog}`) as HTMLElement;
|
||||
return document.querySelector<HTMLElement>(`#${this.inDialog}`)!;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
|
||||
+1
-1
@@ -236,7 +236,7 @@ export class OpBasicSingleDatePickerComponent implements ControlValueAccessor, O
|
||||
|
||||
private appendToBodyOrDialog():HTMLElement|undefined {
|
||||
if (this.inDialog) {
|
||||
return document.querySelector(`#${this.inDialog}`) as HTMLElement;
|
||||
return document.querySelector<HTMLElement>(`#${this.inDialog}`)!;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
|
||||
+5
-2
@@ -58,8 +58,11 @@ export class CKEditorSetupService {
|
||||
const editorClass = type === 'constrained' ? window.OPConstrainedEditor : window.OPClassicEditor;
|
||||
wrapper.classList.add(`ckeditor-type-${type}`);
|
||||
|
||||
const toolbarWrapper = wrapper.querySelector('.document-editor__toolbar')!;
|
||||
const contentWrapper = wrapper.querySelector('.document-editor__editable') as HTMLElement;
|
||||
const toolbarWrapper = wrapper.querySelector<HTMLElement>('.document-editor__toolbar');
|
||||
const contentWrapper = wrapper.querySelector<HTMLElement>('.document-editor__editable');
|
||||
if (!toolbarWrapper || !contentWrapper) {
|
||||
throw new Error('Missing CKEditor wrapper elements.');
|
||||
}
|
||||
const config = this.createConfig(context, initialData);
|
||||
|
||||
return this
|
||||
|
||||
@@ -91,27 +91,27 @@ export class DisplayFieldService extends AbstractFieldService<DisplayField, IDis
|
||||
// We handle multi value fields differently in the single view context
|
||||
const isCustomMultiLinesField = ['[]CustomOption'].includes(schema.type);
|
||||
if (context.container === 'single-view' && isCustomMultiLinesField) {
|
||||
return new MultipleLinesCustomOptionsDisplayField(fieldName, context) as DisplayField;
|
||||
return new MultipleLinesCustomOptionsDisplayField(fieldName, context);
|
||||
}
|
||||
|
||||
const isHierarchyItemsField = ['CustomField::Hierarchy::Item'].includes(schema.type);
|
||||
if (context.container === 'single-view' && isHierarchyItemsField) {
|
||||
return new HierarchyItemDisplayField(fieldName, context) as DisplayField;
|
||||
return new HierarchyItemDisplayField(fieldName, context);
|
||||
}
|
||||
|
||||
const isMultilineHierarchyItemsField = ['[]CustomField::Hierarchy::Item'].includes(schema.type);
|
||||
if (context.container === 'single-view' && isMultilineHierarchyItemsField) {
|
||||
return new MultipleLinesHierarchyItemDisplayField(fieldName, context) as DisplayField;
|
||||
return new MultipleLinesHierarchyItemDisplayField(fieldName, context);
|
||||
}
|
||||
|
||||
// Separate class seems not needed (merge with []CustomOption above?)
|
||||
const isVersionMultiLinesField = ['[]Version'].includes(schema.type);
|
||||
if (context.container === 'single-view' && isVersionMultiLinesField) {
|
||||
return new MultipleLinesCustomOptionsDisplayField(fieldName, context) as DisplayField;
|
||||
return new MultipleLinesCustomOptionsDisplayField(fieldName, context);
|
||||
}
|
||||
const isUserMultiLinesField = ['[]User'].includes(schema.type);
|
||||
if (context.container === 'single-view' && isUserMultiLinesField) {
|
||||
return new MultipleLinesUserFieldModule(fieldName, context) as DisplayField;
|
||||
return new MultipleLinesUserFieldModule(fieldName, context);
|
||||
}
|
||||
|
||||
// We handle progress differently in the timeline
|
||||
|
||||
+1
-2
@@ -7,7 +7,6 @@ import { PathHelperService } from 'core-app/core/path-helper/path-helper.service
|
||||
import { DisplayFieldContext } from 'core-app/shared/components/fields/display/display-field.service';
|
||||
import { HalResource } from 'core-app/features/hal/resources/hal-resource';
|
||||
import { IFieldSchema } from 'core-app/shared/components/fields/field.base';
|
||||
import { Injector } from '@angular/core';
|
||||
|
||||
describe('WorkPackageIdDisplayField', () => {
|
||||
let field:WorkPackageIdDisplayField;
|
||||
@@ -37,7 +36,7 @@ describe('WorkPackageIdDisplayField', () => {
|
||||
|
||||
const mockInjector = {
|
||||
get: (token:unknown, notFoundValue?:unknown) => serviceMap.get(token) ?? notFoundValue ?? {},
|
||||
} as unknown as Injector;
|
||||
};
|
||||
|
||||
field = new WorkPackageIdDisplayField('id', {
|
||||
injector: mockInjector,
|
||||
|
||||
+1
-1
@@ -78,7 +78,7 @@ export class WorkPackageSpentTimeDisplayField extends WorkDisplayField {
|
||||
// Link to the cost report having the work package filter preselected. No grouping.
|
||||
const href = URI(
|
||||
this.PathHelper.projectTimeEntriesPath(
|
||||
project.identifier as string,
|
||||
project.identifier,
|
||||
),
|
||||
)
|
||||
.search(
|
||||
|
||||
@@ -30,7 +30,7 @@ import { ApplicationRef, Injector } from '@angular/core';
|
||||
import { EditForm } from 'core-app/shared/components/fields/edit/edit-form/edit-form';
|
||||
import { HalResource } from 'core-app/features/hal/resources/hal-resource';
|
||||
import { EditFieldHandler } from 'core-app/shared/components/fields/edit/editing-portal/edit-field-handler';
|
||||
import { IFieldSchema } from 'core-app/shared/components/fields/field.base';
|
||||
import { vi } from 'vitest';
|
||||
|
||||
class TestEditForm extends EditForm<HalResource> {
|
||||
constructor(injector:Injector, private readonly requireVisibleSpy:(fieldName:string) => Promise<void>, private readonly activateFieldSpy:() => Promise<EditFieldHandler>, private readonly resetSpy:(fieldName:string, focus?:boolean) => void) {
|
||||
@@ -58,7 +58,7 @@ describe('EditForm', () => {
|
||||
it('does not require visibility twice for newly erroneous inactive fields', async () => {
|
||||
const tick = vi.fn();
|
||||
const requireVisible = vi.fn().mockResolvedValue(undefined);
|
||||
const activateField = vi.fn().mockResolvedValue({} as EditFieldHandler);
|
||||
const activateField = vi.fn().mockResolvedValue({});
|
||||
const reset = vi.fn();
|
||||
const injector = {
|
||||
get: vi.fn().mockImplementation((token:unknown) => {
|
||||
@@ -68,7 +68,7 @@ describe('EditForm', () => {
|
||||
|
||||
throw new Error(`Unexpected token: ${String(token)}`);
|
||||
}),
|
||||
} as unknown as Injector;
|
||||
};
|
||||
|
||||
const form = new TestEditForm(injector, requireVisible, activateField, reset);
|
||||
const change = {
|
||||
@@ -77,7 +77,7 @@ describe('EditForm', () => {
|
||||
ofProperty: vi.fn().mockReturnValue({
|
||||
writable: true,
|
||||
name: 'Foo',
|
||||
} as IFieldSchema),
|
||||
}),
|
||||
},
|
||||
getForm: vi.fn().mockResolvedValue(undefined),
|
||||
};
|
||||
|
||||
+1
-1
@@ -98,7 +98,7 @@ export class HalResourceEditFieldHandler extends EditFieldHandler {
|
||||
}
|
||||
|
||||
public focus(setClickOffset?:number) {
|
||||
const target = this.element.querySelector('.inline-edit--field') as HTMLElement;
|
||||
const target = this.element.querySelector<HTMLElement>('.inline-edit--field');
|
||||
|
||||
if (!target) {
|
||||
debugLog(`Tried to focus on ${this.fieldName}, but element does not (yet) exist.`);
|
||||
|
||||
+5
-4
@@ -53,7 +53,7 @@ export class ProjectEditFieldComponent extends EditFieldComponent implements OnI
|
||||
readonly http = inject(HttpClient);
|
||||
readonly halResourceService = inject(HalResourceService);
|
||||
|
||||
isNew = isNewResource(this.resource);
|
||||
isNew = isNewResource(this.resource as { id:string | null });
|
||||
|
||||
url:string;
|
||||
|
||||
@@ -84,9 +84,10 @@ export class ProjectEditFieldComponent extends EditFieldComponent implements OnI
|
||||
{ name: 'active', operator: '=' as FilterOperator, values: ['t'] },
|
||||
];
|
||||
|
||||
if (isNewResource(this.resource) && this.change.value('type')) {
|
||||
const typeId = idFromLink((this.change.value('type') as { href:string }).href);
|
||||
filters.push({ name: 'type_id', operator: '=' as FilterOperator, values: [typeId] });
|
||||
const type = this.change.value<{ href:string }|null>('type');
|
||||
if (isNewResource(this.resource as { id:string | null }) && type) {
|
||||
const typeId = idFromLink(type.href);
|
||||
filters.push({ name: 'type_id', operator: '=', values: [typeId] });
|
||||
}
|
||||
|
||||
return filters;
|
||||
|
||||
+2
-3
@@ -27,7 +27,6 @@
|
||||
//++
|
||||
|
||||
import { AfterViewInit, ChangeDetectorRef, Directive, ElementRef, EventEmitter, Input, OnDestroy, Output, inject } from '@angular/core';
|
||||
import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource';
|
||||
import { HalResource } from 'core-app/features/hal/resources/hal-resource';
|
||||
import { HalEventsService } from 'core-app/features/hal/services/hal-events.service';
|
||||
import { ToastService } from 'core-app/shared/components/toaster/toast.service';
|
||||
@@ -132,11 +131,11 @@ export class ModalWithTurboContentDirective implements AfterViewInit, OnDestroy
|
||||
|
||||
if (fetchResponse?.succeeded) {
|
||||
this.halEvents.push(
|
||||
this.resource as WorkPackageResource,
|
||||
this.resource,
|
||||
{ eventType: 'updated' },
|
||||
);
|
||||
|
||||
void this.apiV3Service.work_packages.id(this.resource as WorkPackageResource).refresh();
|
||||
void this.apiV3Service.work_packages.id(this.resource).refresh();
|
||||
|
||||
this.successfulUpdate.emit();
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ export class AttributeModelLoaderService {
|
||||
shareReplay(1),
|
||||
);
|
||||
|
||||
state.clearAndPutFromPromise(firstValueFrom(observable) as PromiseLike<HalResource>);
|
||||
state.clearAndPutFromPromise(firstValueFrom(observable));
|
||||
|
||||
return observable;
|
||||
}
|
||||
|
||||
@@ -399,7 +399,7 @@ export class GridAreaService {
|
||||
}
|
||||
|
||||
public resetAreas(ignoredArea:GridWidgetArea|null = null) {
|
||||
this.widgetAreas.filter((area) => !ignoredArea || area.guid !== ignoredArea.guid).forEach((area) => area.reset());
|
||||
this.widgetAreas.filter((area) => area.guid !== ignoredArea?.guid).forEach((area) => area.reset());
|
||||
|
||||
this.numRows = this.resource.rowCount;
|
||||
this.numColumns = this.resource.columnCount;
|
||||
|
||||
@@ -88,7 +88,7 @@ export class GridResizeService {
|
||||
}
|
||||
|
||||
public isResized(area:GridWidgetArea) {
|
||||
return this.resizedArea && this.resizedArea.guid === area.guid;
|
||||
return this.resizedArea?.guid === area.guid;
|
||||
}
|
||||
|
||||
public isPassive(area:GridWidgetArea) {
|
||||
|
||||
+1
-1
@@ -41,7 +41,7 @@ export class WorkPackageCreateSettingsMenuDirective extends OpContextMenuTrigger
|
||||
readonly halEditing = inject(HalResourceEditingService);
|
||||
|
||||
override readonly placement = 'bottom-end';
|
||||
|
||||
|
||||
protected open(evt:Event) {
|
||||
const wp = this.states.workPackages.get('new').value;
|
||||
|
||||
|
||||
@@ -88,7 +88,7 @@ export class PersistentToggleComponent implements OnInit {
|
||||
window.OpenProject.guardedLocalStorage(this.identifier, (!!isNowHidden).toString());
|
||||
|
||||
const targetNotification = this.targetNotification;
|
||||
if (!targetNotification) return;
|
||||
if (!targetNotification) return;
|
||||
|
||||
if (isNowHidden) {
|
||||
slideUp(targetNotification, 400);
|
||||
|
||||
+3
-3
@@ -102,11 +102,11 @@ export class SearchableProjectListService {
|
||||
// such as favorites or the preloaded projects (current project, selected projects)
|
||||
// in a filtered view, it's legitimate for them to be missing, thus we skip extra fetching if a search text is present
|
||||
if(!loadingEnabled || searchText.length > 0) {
|
||||
return of([projects, false as boolean]);
|
||||
return of([projects, false]);
|
||||
}
|
||||
|
||||
return this.pipeConcatProjects(projects, this.preloadProjectIds.concat(favoriteIds))
|
||||
.pipe(map((p) => [p, true as boolean]));
|
||||
.pipe(map((p) => [p, true]));
|
||||
}),
|
||||
switchMap(([projects, enhancePreloadedProjects]:[IProject[],boolean]) => {
|
||||
// These can be fetched in parallel to ancestors, since they share ancestors with preloadProjectIds entries and thus
|
||||
@@ -299,7 +299,7 @@ export class SearchableProjectListService {
|
||||
|
||||
const listParent = findSearchableListParent(event.currentTarget as HTMLElement);
|
||||
const focused = document.activeElement;
|
||||
(listParent?.querySelector('.spot-list--item-action_active') as HTMLElement)?.click();
|
||||
listParent?.querySelector<HTMLElement>('.spot-list--item-action_active')?.click();
|
||||
(focused as HTMLElement)?.focus();
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ export class OpSearchHighlightDirective implements AfterViewChecked {
|
||||
let el = this.elementRef.nativeElement as HTMLElement;
|
||||
const highlightedElement = el.querySelector('.op-search-highlight');
|
||||
|
||||
if (!!highlightedElement && this.query && highlightedElement.innerHTML.toLocaleLowerCase() === this.query.toLocaleLowerCase()) {
|
||||
if (!!highlightedElement && highlightedElement.innerHTML.toLocaleLowerCase() === this.query?.toLocaleLowerCase()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -118,7 +118,7 @@ describe('parseChronicDuration', () => {
|
||||
});
|
||||
|
||||
/* The cecile case */
|
||||
it('it parses 2h15 correctly to 2h 15 minutes even when the default unit is hours', () => {
|
||||
it('parses 2h15 correctly to 2h 15 minutes even when the default unit is hours', () => {
|
||||
expect(parseChronicDuration('2h15', { defaultUnit: 'hours' })).toBe(2 * 3600 + 15 * 60);
|
||||
});
|
||||
|
||||
|
||||
@@ -120,9 +120,9 @@ export class DomAutoscrollService {
|
||||
public getElementsUnderPoint():HTMLElement[] {
|
||||
const underPoint = [];
|
||||
|
||||
for (let i = 0; i < this.elements.length; i++) {
|
||||
if (this.inside(this.point, this.elements[i])) {
|
||||
underPoint.push(this.elements[i] as HTMLElement);
|
||||
for (const element of this.elements) {
|
||||
if (this.inside(this.point, element)) {
|
||||
underPoint.push(element as HTMLElement); // eslint-disable-line @typescript-eslint/no-unnecessary-type-assertion
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -183,7 +183,7 @@ export class SpotDropModalComponent implements OnDestroy {
|
||||
(this.focusGrabber.nativeElement as HTMLElement).focus();
|
||||
}
|
||||
|
||||
private onGlobalClick = this.close.bind(this) as () => void;
|
||||
private onGlobalClick = this.close.bind(this);
|
||||
|
||||
ngOnDestroy():void {
|
||||
if (this.opened) {
|
||||
|
||||
@@ -117,9 +117,12 @@ export default class CustomFieldsController extends Controller {
|
||||
addOption() {
|
||||
const count = this.customOptionRowTargets.length;
|
||||
const last = this.customOptionRowTargets[count - 1];
|
||||
if (!last) { return false; }
|
||||
|
||||
const dup = last.cloneNode(true) as HTMLElement;
|
||||
|
||||
const input = dup.querySelector('.custom-option-value input') as HTMLInputElement;
|
||||
const input = dup.querySelector<HTMLInputElement>('.custom-option-value input');
|
||||
if (!input) { return false; }
|
||||
|
||||
input.setAttribute('name', `custom_field[custom_options_attributes][${count}][value]`);
|
||||
input.setAttribute('id', `custom_field_custom_options_attributes_${count}_value`);
|
||||
@@ -129,8 +132,9 @@ export default class CustomFieldsController extends Controller {
|
||||
.querySelector('.custom-option-id')
|
||||
?.remove();
|
||||
|
||||
const defaultValueCheckbox = dup.querySelector('input[type="checkbox"]') as HTMLInputElement;
|
||||
const defaultValueHidden = dup.querySelector('input[type="hidden"]') as HTMLInputElement;
|
||||
const defaultValueCheckbox = dup.querySelector<HTMLInputElement>('input[type="checkbox"]');
|
||||
const defaultValueHidden = dup.querySelector<HTMLInputElement>('input[type="hidden"]');
|
||||
if (!defaultValueCheckbox || !defaultValueHidden) { return false; }
|
||||
|
||||
defaultValueHidden.setAttribute('name', `custom_field[custom_options_attributes][${count}][default_value]`);
|
||||
defaultValueHidden.removeAttribute('id');
|
||||
|
||||
@@ -85,10 +85,12 @@ export default class HierarchyItemController extends Controller {
|
||||
|
||||
if (event.dataTransfer) {
|
||||
const origin = event.dataTransfer.getData('application/dragkey');
|
||||
const originElement = document.querySelector(`[data-hierarchy-item-id='${origin}']`) as HTMLElement;
|
||||
const originElement = document.querySelector<HTMLElement>(`[data-hierarchy-item-id='${origin}']`);
|
||||
if (!originElement) { return; }
|
||||
|
||||
originElement.style.opacity = '1';
|
||||
|
||||
if (targetElement.dataset.hierarchyItemId === (originElement as HTMLElement).dataset.hierarchyItemId) {
|
||||
if (targetElement.dataset.hierarchyItemId === originElement.dataset.hierarchyItemId) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -83,8 +83,8 @@ export default class ProgressTrackingController extends Controller {
|
||||
}
|
||||
|
||||
getSelectedMode() {
|
||||
const checkedRadio = this.progressCalculationModeRadioGroupTarget.querySelector('input:checked') as HTMLInputElement;
|
||||
return checkedRadio?.value || '';
|
||||
const checkedRadio = this.progressCalculationModeRadioGroupTarget.querySelector<HTMLInputElement>('input:checked');
|
||||
return checkedRadio?.value ?? '';
|
||||
}
|
||||
|
||||
getWarningMessageHtml():string {
|
||||
|
||||
@@ -65,7 +65,7 @@ export default class RegistrationController extends Controller {
|
||||
}
|
||||
|
||||
getSelectedOption() {
|
||||
const checkedRadio = this.selfRegistrationRadioGroupTarget.querySelector('input[type="radio"]:checked') as HTMLInputElement;
|
||||
return checkedRadio?.value || '';
|
||||
const checkedRadio = this.selfRegistrationRadioGroupTarget.querySelector<HTMLInputElement>('input[type="radio"]:checked');
|
||||
return checkedRadio?.value ?? '';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,7 +129,8 @@ export default class BudgetSubformController extends Controller {
|
||||
const row = this.element.querySelector(`#${row_identifier}`)!;
|
||||
const body = new FormData();
|
||||
body.append('element_id', row_identifier);
|
||||
body.append('fixed_date', (document.querySelector('#budget_fixed_date') as HTMLInputElement).value);
|
||||
const fixedDateInput = document.querySelector<HTMLInputElement>('#budget_fixed_date');
|
||||
body.append('fixed_date', fixedDateInput?.value ?? '');
|
||||
|
||||
row.querySelectorAll('.budget-item-value').forEach((itemValue:HTMLInputElement|HTMLSelectElement) => {
|
||||
body.append(itemValue.dataset.requestKey!, (itemValue.value || '0'));
|
||||
|
||||
@@ -36,12 +36,11 @@ export default class extends Controller {
|
||||
}
|
||||
|
||||
hide(event:MouseEvent) {
|
||||
const section = (event.target as HTMLElement).closest('.hide-section') as HTMLElement;
|
||||
if (section) {
|
||||
section.hidden = true;
|
||||
}
|
||||
const section = (event.target as HTMLElement).closest<HTMLElement>('.hide-section');
|
||||
if (!section) { return; }
|
||||
|
||||
const name = (section as HTMLElement).dataset.name!;
|
||||
section.hidden = true;
|
||||
const name = section.dataset.name!;
|
||||
this.toggleOption(name);
|
||||
}
|
||||
|
||||
|
||||
@@ -33,8 +33,7 @@ export default class MainMenuController extends Controller {
|
||||
|
||||
targetLi.querySelector<HTMLElement>('li > a, .tree-menu--title')?.focus();
|
||||
|
||||
const backArrow = targetLi.querySelector('.main-menu--arrow-left-to-project') as HTMLElement;
|
||||
backArrow.focus();
|
||||
targetLi.querySelector<HTMLElement>('.main-menu--arrow-left-to-project')?.focus();
|
||||
this.markActive(targetLi.dataset.name!);
|
||||
}
|
||||
|
||||
|
||||
@@ -337,8 +337,10 @@ export default class MyTimeTrackingController extends Controller {
|
||||
|
||||
const colgroup = document.createElement('colgroup');
|
||||
const col = document.createElement('col');
|
||||
const otherCol = document.querySelector('.fc-scrollgrid-section-header .fc-col-header col') as HTMLElement;
|
||||
col.style.width = otherCol?.style?.width;
|
||||
const otherCol = document.querySelector<HTMLTableColElement>('.fc-scrollgrid-section-header .fc-col-header col');
|
||||
if (otherCol) {
|
||||
col.style.width = otherCol.style.width;
|
||||
}
|
||||
|
||||
const tbody = document.createElement('tbody');
|
||||
tbody.setAttribute('role', 'presentation');
|
||||
|
||||
@@ -213,12 +213,12 @@ export default class BulkSelectionController extends Controller {
|
||||
});
|
||||
}
|
||||
|
||||
private get selectedPermissions() {
|
||||
private get selectedPermissions():string[] {
|
||||
return this.selectedRoleButtons.map((button) => {
|
||||
const label = button.querySelector('.Button-label')!;
|
||||
|
||||
return label.textContent;
|
||||
}) as string[];
|
||||
return label.textContent ?? '';
|
||||
});
|
||||
}
|
||||
|
||||
private get selectedRoleButtons() {
|
||||
|
||||
@@ -84,9 +84,9 @@ export default class SortByConfigController extends Controller {
|
||||
this.parentForm = this.sortByFieldTarget.closest('form');
|
||||
|
||||
if (this.parentForm) {
|
||||
this.pageTarget = this.parentForm.querySelector('input[data-sort-by-config-target="page"]')!;
|
||||
|
||||
if (this.pageTarget) {
|
||||
const pageTarget = this.parentForm.querySelector<HTMLInputElement>('input[data-sort-by-config-target="page"]');
|
||||
if (pageTarget) {
|
||||
this.pageTarget = pageTarget;
|
||||
this.parentForm.addEventListener('submit', this.onFormSubmit.bind(this));
|
||||
}
|
||||
}
|
||||
@@ -116,7 +116,8 @@ export default class SortByConfigController extends Controller {
|
||||
|
||||
fieldChanged(event:Event):void {
|
||||
const target = event.target as HTMLElement;
|
||||
const row = target.closest('div[data-sort-by-config-target="inputRow"]') as HTMLElement;
|
||||
const row = target.closest<HTMLElement>('div[data-sort-by-config-target="inputRow"]');
|
||||
if (!row) { return; }
|
||||
|
||||
this.manageRow(row);
|
||||
|
||||
@@ -208,18 +209,20 @@ export default class SortByConfigController extends Controller {
|
||||
}
|
||||
|
||||
getSelectedField(row:HTMLElement):string|null {
|
||||
const selectedField = row.querySelector('select[name="sort_field"]') as HTMLSelectElement;
|
||||
return selectedField?.value || null;
|
||||
const selectedField = row.querySelector<HTMLSelectElement>('select[name="sort_field"]');
|
||||
return selectedField?.value ?? null;
|
||||
}
|
||||
|
||||
getSelectedDirection(row:HTMLElement):string|null {
|
||||
const selectedSegment = row.querySelector('li.SegmentedControl-item--selected > button');
|
||||
return selectedSegment?.getAttribute('data-direction') || null;
|
||||
return selectedSegment?.getAttribute('data-direction') ?? null;
|
||||
}
|
||||
|
||||
unsetField(row:HTMLElement):void {
|
||||
const select = row.querySelector('select[name="sort_field"]') as HTMLSelectElement;
|
||||
select.value = '';
|
||||
const select = row.querySelector<HTMLSelectElement>('select[name="sort_field"]');
|
||||
if (select) {
|
||||
select.value = '';
|
||||
}
|
||||
}
|
||||
|
||||
unsetDirection(row:HTMLElement):void {
|
||||
@@ -283,8 +286,8 @@ export default class SortByConfigController extends Controller {
|
||||
disableSelectedFieldsForOtherSelects():void {
|
||||
this.inputRowTargets.forEach((row) => {
|
||||
const selectedFieldsInOtherRows = this.getAllSelectedFields(row);
|
||||
const otherSelect = row.querySelector('select[name="sort_field"]')!;
|
||||
otherSelect.querySelectorAll('option').forEach((option) => {
|
||||
const otherSelect = row.querySelector<HTMLSelectElement>('select[name="sort_field"]');
|
||||
otherSelect?.querySelectorAll('option').forEach((option) => {
|
||||
option.disabled = selectedFieldsInOtherRows.includes(option.value);
|
||||
});
|
||||
});
|
||||
|
||||
+6
-4
@@ -534,14 +534,16 @@ export default class PreviewController extends DialogPreviewController {
|
||||
}
|
||||
|
||||
private focusOnOpen() {
|
||||
const banner = document.querySelector('.wp-datepicker--banner') as HTMLElement;
|
||||
const banner = document.querySelector<HTMLElement>('.wp-datepicker--banner');
|
||||
if (banner) {
|
||||
banner.setAttribute('tabindex', '-1');
|
||||
banner.focus();
|
||||
} else {
|
||||
const tabs = document.querySelector('.wp-datepicker-dialog--UnderlineNav') as HTMLElement;
|
||||
tabs.setAttribute('tabindex', '-1');
|
||||
tabs.focus();
|
||||
const tabs = document.querySelector<HTMLElement>('.wp-datepicker-dialog--UnderlineNav');
|
||||
if (tabs) {
|
||||
tabs.setAttribute('tabindex', '-1');
|
||||
tabs.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -10,10 +10,10 @@ export default class GeneratePdfController extends Controller {
|
||||
const data = target.options[target.selectedIndex].dataset;
|
||||
const template = target.options[target.selectedIndex].value;
|
||||
|
||||
const formControl = target.closest('.FormControl')!;
|
||||
const captionElement = formControl.querySelector('.FormControl-caption') as HTMLElement;
|
||||
const formControl = target.closest<HTMLElement>('.FormControl');
|
||||
const captionElement = formControl?.querySelector<HTMLElement>('.FormControl-caption');
|
||||
if (captionElement) {
|
||||
captionElement.innerText = (data.caption || '');
|
||||
captionElement.innerText = (data.caption ?? '');
|
||||
}
|
||||
this.inputGroupsTargets.forEach((inputGroup:HTMLElement) => {
|
||||
if (inputGroup.dataset.template === template) {
|
||||
|
||||
@@ -16,7 +16,7 @@ export function addTurboGlobalListeners() {
|
||||
const runOnRenderAndLoad = () => {
|
||||
// Add to content if warnings displayed
|
||||
if (document.querySelector('.warning-bar--item')) {
|
||||
const content = document.querySelector('#content') as HTMLElement;
|
||||
const content = document.querySelector<HTMLElement>('#content');
|
||||
if (content) {
|
||||
content.style.marginBottom = '100px';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user