diff --git a/projects/ngx/declarative-ui/resource-field/resource-field.component.spec.ts b/projects/ngx/declarative-ui/resource-field/resource-field.component.spec.ts index 2aebfb94..59f4d86e 100644 --- a/projects/ngx/declarative-ui/resource-field/resource-field.component.spec.ts +++ b/projects/ngx/declarative-ui/resource-field/resource-field.component.spec.ts @@ -370,6 +370,27 @@ describe('ResourceField', () => { expect(emitted).toHaveLength(0); }); + + it('keeps navigation buttons enabled and emitting when the resource is unavailable', () => { + const field: FieldDefinition = { + property: 'action', + uiSettings: { + displayAs: 'button', + buttonSettings: { action: 'navigate' }, + }, + }; + const { fixture, component } = setup(field, { isAvailable: false }); + const emitted: ResourceFieldButtonClickEvent[] = []; + component.buttonClick.subscribe((event) => emitted.push(event)); + + expect(component.buttonDisabled()).toBe(false); + + q(fixture, 'ui5-button')?.dispatchEvent( + new MouseEvent('click', { bubbles: true }), + ); + + expect(emitted).toHaveLength(1); + }); }); describe('displayAs: tag', () => { diff --git a/projects/ngx/declarative-ui/resource-field/resource-field.component.ts b/projects/ngx/declarative-ui/resource-field/resource-field.component.ts index 6d56749e..6c5072c5 100644 --- a/projects/ngx/declarative-ui/resource-field/resource-field.component.ts +++ b/projects/ngx/declarative-ui/resource-field/resource-field.component.ts @@ -85,7 +85,14 @@ export class ResourceField< isBoolLike = computed(() => this.boolValue() !== undefined); isUrlValue = computed(() => this.checkValidUrl(this.stringValue())); testId = computed(() => `resource-field-${this.fieldDefinition().property}`); - buttonDisabled = computed(() => this.resource()?.isAvailable === false); + // Only mutating actions are disabled on unavailable resources; navigation + // and custom actions must keep working so a not-ready resource can still be + // opened and inspected. + buttonDisabled = computed(() => { + const action = this.uiSettings()?.buttonSettings?.action; + const isMutation = action === 'update' || action === 'delete'; + return isMutation && this.resource()?.isAvailable === false; + }); buttonAccessibleName = computed( () => (this.buttonDisabled() ? this.resource()?.accessibleName : undefined) ?? diff --git a/projects/ngx/declarative-ui/table-card/declarative-table-card.component.ts b/projects/ngx/declarative-ui/table-card/declarative-table-card.component.ts index 9ef5de49..8aa58dc9 100644 --- a/projects/ngx/declarative-ui/table-card/declarative-table-card.component.ts +++ b/projects/ngx/declarative-ui/table-card/declarative-table-card.component.ts @@ -182,11 +182,15 @@ export class DeclarativeTableCard { } onButtonClick(event: ResourceFieldButtonClickEvent): void { - if (event.resource?.isAvailable === false) { + const action = event.field.uiSettings?.buttonSettings?.action; + + // Block only mutating actions on unavailable resources; navigation and + // custom actions must keep working so a not-ready resource can still be + // opened and inspected. + const isMutation = action === 'update' || action === 'delete'; + if (isMutation && event.resource?.isAvailable === false) { return; } - - const action = event.field.uiSettings?.buttonSettings?.action; if (action === 'update' && event.resource) { this.pendingResource.set(event.resource); void this.openEditDialog(event.resource); diff --git a/projects/ngx/declarative-ui/table/declarative-table/declarative-table.component.html b/projects/ngx/declarative-ui/table/declarative-table/declarative-table.component.html index c2b00f73..1bb99ee6 100644 --- a/projects/ngx/declarative-ui/table/declarative-table/declarative-table.component.html +++ b/projects/ngx/declarative-ui/table/declarative-table/declarative-table.component.html @@ -28,7 +28,7 @@ @for (column of viewColumns(); track columnTrackBy(column, $index)) { diff --git a/projects/ngx/declarative-ui/table/declarative-table/declarative-table.component.scss b/projects/ngx/declarative-ui/table/declarative-table/declarative-table.component.scss index 453b5ac5..c9ac170b 100644 --- a/projects/ngx/declarative-ui/table/declarative-table/declarative-table.component.scss +++ b/projects/ngx/declarative-ui/table/declarative-table/declarative-table.component.scss @@ -4,7 +4,6 @@ .disabled { filter: brightness(0.92); - pointer-events: none; ui5-table-cell:not(:first-child) { color: var(--sapContent_DisabledTextColor, #6a6d70); diff --git a/projects/ngx/declarative-ui/table/declarative-table/declarative-table.component.spec.ts b/projects/ngx/declarative-ui/table/declarative-table/declarative-table.component.spec.ts index 5f55c130..f417cad6 100644 --- a/projects/ngx/declarative-ui/table/declarative-table/declarative-table.component.spec.ts +++ b/projects/ngx/declarative-ui/table/declarative-table/declarative-table.component.spec.ts @@ -233,7 +233,7 @@ describe('DeclarativeTable', () => { expect(emitted[0]).toEqual(resource); }); - it('does not emit tableRowClicked for an unavailable resource', () => { + it('emits tableRowClicked for an unavailable resource so it can still be inspected', () => { const resource = { id: '1', name: 'Alice', @@ -250,7 +250,8 @@ describe('DeclarativeTable', () => { const row = el(fixture, 'generic-table-row-0') as HTMLElement; row.click(); - expect(emitted).toHaveLength(0); + expect(emitted).toHaveLength(1); + expect(emitted[0]).toEqual(resource); }); }); diff --git a/projects/ngx/declarative-ui/table/declarative-table/declarative-table.component.ts b/projects/ngx/declarative-ui/table/declarative-table/declarative-table.component.ts index 40af170a..b84daacb 100644 --- a/projects/ngx/declarative-ui/table/declarative-table/declarative-table.component.ts +++ b/projects/ngx/declarative-ui/table/declarative-table/declarative-table.component.ts @@ -73,9 +73,9 @@ export class DeclarativeTable { viewColumns = computed(() => processGroupFields(this.columns())); onRowClick(item: T): void { - if (item.isAvailable !== false) { - this.tableRowClicked.emit(item); - } + // Unavailable rows stay visually dimmed but remain clickable: a resource + // that is not ready is exactly the one a user needs to open and inspect. + this.tableRowClicked.emit(item); } isPagerMode = computed(() => this.loadMode() === 'pager');