You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by sc...@apache.org on 2024/02/16 15:01:11 UTC

(nifi) branch main updated: NIFI-12795: (#8416)

This is an automated email from the ASF dual-hosted git repository.

scottyaslan pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nifi.git


The following commit(s) were added to refs/heads/main by this push:
     new 6f6ddf8960 NIFI-12795: (#8416)
6f6ddf8960 is described below

commit 6f6ddf89609298a9735747bf0e8713eba888b45d
Author: Matt Gilman <ma...@gmail.com>
AuthorDate: Fri Feb 16 10:01:04 2024 -0500

    NIFI-12795: (#8416)
    
    - Including embedded help documentation.
    - Support linking to specific help documentation from the canvas and component listings.
---
 .../src/main/nifi/src/app/app-routing.module.ts    |  6 ++
 .../src/main/nifi/src/app/app.module.ts            |  4 +-
 .../feature/documentation-routing.module.ts        | 33 ++++++++++
 .../feature/documentation.component.html           | 27 +++++++++
 .../feature/documentation.component.scss           | 16 +++++
 .../feature/documentation.component.spec.ts        | 52 ++++++++++++++++
 .../feature/documentation.component.ts             | 70 ++++++++++++++++++++++
 .../documentation/feature/documentation.module.ts  | 30 ++++++++++
 .../service/canvas-context-menu.service.ts         | 24 ++++++--
 .../controller-services.component.html             |  1 +
 .../controller-services.component.ts               | 14 +++++
 .../flow-analysis-rule-table.component.html        |  5 +-
 .../flow-analysis-rule-table.component.ts          | 10 +++-
 .../flow-analysis-rules.component.html             |  1 +
 .../flow-analysis-rules.component.ts               | 14 +++++
 .../management-controller-services.component.html  |  1 +
 .../management-controller-services.component.ts    | 14 +++++
 .../parameter-providers-table.component.html       | 38 ++++++------
 .../parameter-providers-table.component.ts         |  9 ++-
 .../parameter-providers.component.html             |  1 +
 .../parameter-providers.component.ts               | 14 +++++
 .../reporting-task-table.component.html            |  5 +-
 .../reporting-task-table.component.ts              |  7 +++
 .../reporting-tasks/reporting-tasks.component.html |  1 +
 .../reporting-tasks/reporting-tasks.component.ts   | 14 +++++
 .../state/documentation/documentation.actions.ts   | 28 +++++++++
 .../state/documentation/documentation.effects.ts   | 41 +++++++++++++
 .../state/documentation/documentation.reducer.ts   | 36 +++++++++++
 .../state/documentation/documentation.selectors.ts | 26 ++++++++
 .../main/nifi/src/app/state/documentation/index.ts | 26 ++++++++
 .../src/main/nifi/src/app/state/index.ts           |  6 +-
 .../controller-service-table.component.html        | 12 ++--
 .../controller-service-table.component.ts          |  9 ++-
 .../ui/common/navigation/navigation.component.html |  7 ++-
 34 files changed, 564 insertions(+), 38 deletions(-)

diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/app-routing.module.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/app-routing.module.ts
index 08cb918730..c603a46e22 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/app-routing.module.ts
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/app-routing.module.ts
@@ -28,6 +28,12 @@ const routes: Routes = [
         path: 'error',
         loadChildren: () => import('./pages/error/feature/error.module').then((m) => m.ErrorModule)
     },
+    {
+        path: 'documentation',
+        canMatch: [authenticationGuard],
+        loadChildren: () =>
+            import('./pages/documentation/feature/documentation.module').then((m) => m.DocumentationModule)
+    },
     {
         path: 'settings',
         canMatch: [authenticationGuard],
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/app.module.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/app.module.ts
index 02adc60d74..d985d2b986 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/app.module.ts
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/app.module.ts
@@ -45,6 +45,7 @@ import { ComponentStateEffects } from './state/component-state/component-state.e
 import { ErrorEffects } from './state/error/error.effects';
 import { MatSnackBarModule } from '@angular/material/snack-bar';
 import { PipesModule } from './pipes/pipes.module';
+import { DocumentationEffects } from './state/documentation/documentation.effects';
 
 @NgModule({
     declarations: [AppComponent],
@@ -71,7 +72,8 @@ import { PipesModule } from './pipes/pipes.module';
             StatusHistoryEffects,
             ControllerServiceStateEffects,
             SystemDiagnosticsEffects,
-            ComponentStateEffects
+            ComponentStateEffects,
+            DocumentationEffects
         ),
         StoreDevtoolsModule.instrument({
             maxAge: 25,
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/documentation/feature/documentation-routing.module.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/documentation/feature/documentation-routing.module.ts
new file mode 100644
index 0000000000..c370157282
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/documentation/feature/documentation-routing.module.ts
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 { NgModule } from '@angular/core';
+import { RouterModule, Routes } from '@angular/router';
+import { Documentation } from './documentation.component';
+
+const routes: Routes = [
+    {
+        path: '',
+        component: Documentation
+    }
+];
+
+@NgModule({
+    imports: [RouterModule.forChild(routes)],
+    exports: [RouterModule]
+})
+export class DocumentationRoutingModule {}
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/documentation/feature/documentation.component.html b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/documentation/feature/documentation.component.html
new file mode 100644
index 0000000000..a3e97b046c
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/documentation/feature/documentation.component.html
@@ -0,0 +1,27 @@
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements.  See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to You 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.
+  -->
+
+<div class="pb-5 flex flex-col h-screen justify-between">
+    <header class="nifi-header">
+        <navigation></navigation>
+    </header>
+    @if (frameSource) {
+        <iframe class="flex-1" [src]="frameSource"></iframe>
+    } @else {
+        <iframe class="flex-1" src="../nifi-docs/documentation"></iframe>
+    }
+</div>
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/documentation/feature/documentation.component.scss b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/documentation/feature/documentation.component.scss
new file mode 100644
index 0000000000..2944f98194
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/documentation/feature/documentation.component.scss
@@ -0,0 +1,16 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/documentation/feature/documentation.component.spec.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/documentation/feature/documentation.component.spec.ts
new file mode 100644
index 0000000000..d32dd36e10
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/documentation/feature/documentation.component.spec.ts
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 { Documentation } from './documentation.component';
+import { RouterModule } from '@angular/router';
+import { RouterTestingModule } from '@angular/router/testing';
+import { Component } from '@angular/core';
+import { provideMockStore } from '@ngrx/store/testing';
+import { initialState } from '../../../state/documentation/documentation.reducer';
+
+describe('Documentation', () => {
+    let component: Documentation;
+    let fixture: ComponentFixture<Documentation>;
+
+    @Component({
+        selector: 'navigation',
+        standalone: true,
+        template: ''
+    })
+    class MockNavigation {}
+
+    beforeEach(() => {
+        TestBed.configureTestingModule({
+            declarations: [Documentation],
+            imports: [RouterModule, RouterTestingModule, MockNavigation],
+            providers: [provideMockStore({ initialState })]
+        });
+        fixture = TestBed.createComponent(Documentation);
+        component = fixture.componentInstance;
+        fixture.detectChanges();
+    });
+
+    it('should create', () => {
+        expect(component).toBeTruthy();
+    });
+});
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/documentation/feature/documentation.component.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/documentation/feature/documentation.component.ts
new file mode 100644
index 0000000000..2631acb217
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/documentation/feature/documentation.component.ts
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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, OnDestroy, SecurityContext } from '@angular/core';
+import { NiFiState } from '../../../state';
+import { Store } from '@ngrx/store';
+import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';
+import { HttpParams } from '@angular/common/http';
+import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
+import { selectDocumentationParameters } from '../../../state/documentation/documentation.selectors';
+import { DocumentationParameters } from '../../../state/documentation';
+import { clearDocumentationParameters } from '../../../state/documentation/documentation.actions';
+
+@Component({
+    selector: 'documentation',
+    templateUrl: './documentation.component.html',
+    styleUrls: ['./documentation.component.scss']
+})
+export class Documentation implements OnDestroy {
+    frameSource!: SafeResourceUrl | null;
+
+    constructor(
+        private store: Store<NiFiState>,
+        private domSanitizer: DomSanitizer
+    ) {
+        this.store
+            .select(selectDocumentationParameters)
+            .pipe(takeUntilDestroyed())
+            .subscribe((params) => {
+                this.frameSource = this.getFrameSource(params);
+            });
+    }
+
+    private getFrameSource(params: DocumentationParameters | null): SafeResourceUrl | null {
+        let url = '../nifi-docs/documentation';
+
+        if (params) {
+            if (Object.keys(params).length > 0) {
+                const queryParams: string = new HttpParams({ fromObject: params }).toString();
+                url = `${url}?${queryParams}`;
+            }
+
+            const sanitizedUrl = this.domSanitizer.sanitize(SecurityContext.URL, url);
+
+            if (sanitizedUrl) {
+                return this.domSanitizer.bypassSecurityTrustResourceUrl(sanitizedUrl);
+            }
+        }
+
+        return null;
+    }
+
+    ngOnDestroy(): void {
+        this.store.dispatch(clearDocumentationParameters());
+    }
+}
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/documentation/feature/documentation.module.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/documentation/feature/documentation.module.ts
new file mode 100644
index 0000000000..f4fc05c7d1
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/documentation/feature/documentation.module.ts
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { Documentation } from './documentation.component';
+import { DocumentationRoutingModule } from './documentation-routing.module';
+import { MatDialogModule } from '@angular/material/dialog';
+import { Navigation } from '../../../ui/common/navigation/navigation.component';
+
+@NgModule({
+    declarations: [Documentation],
+    exports: [Documentation],
+    imports: [CommonModule, MatDialogModule, DocumentationRoutingModule, Navigation]
+})
+export class DocumentationModule {}
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/service/canvas-context-menu.service.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/service/canvas-context-menu.service.ts
index 96c236eb63..45fb9b262e 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/service/canvas-context-menu.service.ts
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/service/canvas-context-menu.service.ts
@@ -57,6 +57,7 @@ import {
 } from '../../../ui/common/context-menu/context-menu.component';
 import { promptEmptyQueueRequest, promptEmptyQueuesRequest } from '../state/queue/queue.actions';
 import { getComponentStateAndOpenDialog } from '../../../state/component-state/component-state.actions';
+import { navigateToComponentDocumentation } from '../../../state/documentation/documentation.actions';
 
 @Injectable({ providedIn: 'root' })
 export class CanvasContextMenu implements ContextMenuDefinitionProvider {
@@ -717,13 +718,26 @@ export class CanvasContextMenu implements ContextMenuDefinitionProvider {
             },
             {
                 condition: (selection: any) => {
-                    // TODO - hasUsage
-                    return false;
+                    return (
+                        this.canvasUtils.canRead(selection) &&
+                        selection.size() === 1 &&
+                        this.canvasUtils.isProcessor(selection)
+                    );
                 },
                 clazz: 'fa fa-book',
-                text: 'View usage',
-                action: () => {
-                    // TODO - showUsage
+                text: 'View documentation',
+                action: (selection: any) => {
+                    const selectionData = selection.datum();
+                    this.store.dispatch(
+                        navigateToComponentDocumentation({
+                            params: {
+                                select: selectionData.component.type,
+                                group: selectionData.component.bundle.group,
+                                artifact: selectionData.component.bundle.artifact,
+                                version: selectionData.component.bundle.version
+                            }
+                        })
+                    );
                 }
             },
             {
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/controller-service/controller-services.component.html b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/controller-service/controller-services.component.html
index 99219457a3..97225eef18 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/controller-service/controller-services.component.html
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/controller-service/controller-services.component.html
@@ -47,6 +47,7 @@
                                 [flowConfiguration]="flowConfiguration"
                                 [canModifyParent]="canModifyParent(serviceState.breadcrumb)"
                                 (selectControllerService)="selectControllerService($event)"
+                                (viewControllerServiceDocumentation)="viewControllerServiceDocumentation($event)"
                                 (configureControllerService)="configureControllerService($event)"
                                 (enableControllerService)="enableControllerService($event)"
                                 (disableControllerService)="disableControllerService($event)"
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/controller-service/controller-services.component.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/controller-service/controller-services.component.ts
index ae73e57e57..e074ebdae9 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/controller-service/controller-services.component.ts
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/controller-service/controller-services.component.ts
@@ -46,6 +46,7 @@ import { selectFlowConfiguration } from '../../../../state/flow-configuration/fl
 import { NiFiState } from '../../../../state';
 import { loadFlowConfiguration } from '../../../../state/flow-configuration/flow-configuration.actions';
 import { getComponentStateAndOpenDialog } from '../../../../state/component-state/component-state.actions';
+import { navigateToComponentDocumentation } from '../../../../state/documentation/documentation.actions';
 
 @Component({
     selector: 'controller-services',
@@ -160,6 +161,19 @@ export class ControllerServices implements OnInit, OnDestroy {
         };
     }
 
+    viewControllerServiceDocumentation(entity: ControllerServiceEntity): void {
+        this.store.dispatch(
+            navigateToComponentDocumentation({
+                params: {
+                    select: entity.component.type,
+                    group: entity.component.bundle.group,
+                    artifact: entity.component.bundle.artifact,
+                    version: entity.component.bundle.version
+                }
+            })
+        );
+    }
+
     configureControllerService(entity: ControllerServiceEntity): void {
         this.store.dispatch(
             navigateToEditService({
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rule-table/flow-analysis-rule-table.component.html b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rule-table/flow-analysis-rule-table.component.html
index db61502a21..579c18998a 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rule-table/flow-analysis-rule-table.component.html
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rule-table/flow-analysis-rule-table.component.html
@@ -31,7 +31,10 @@
                 <td mat-cell *matCellDef="let item">
                     @if (canRead(item)) {
                         <div class="flex items-center gap-x-3">
-                            <div class="pointer fa fa-book" title="Usage"></div>
+                            <div
+                                class="pointer fa fa-book"
+                                (click)="viewDocumentationClicked(item, $event)"
+                                title="View Documentation"></div>
                             <!-- TODO - handle read only in configure component? -->
                             @if (hasComments(item)) {
                                 <div>
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rule-table/flow-analysis-rule-table.component.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rule-table/flow-analysis-rule-table.component.ts
index 7615703532..5c3e4702b3 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rule-table/flow-analysis-rule-table.component.ts
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rule-table/flow-analysis-rule-table.component.ts
@@ -51,10 +51,13 @@ export class FlowAnalysisRuleTable {
     @Input() set flowAnalysisRules(flowAnalysisRuleEntities: FlowAnalysisRuleEntity[]) {
         this.dataSource.data = this.sortFlowAnalysisRules(flowAnalysisRuleEntities, this.sort);
     }
+
     @Input() selectedFlowAnalysisRuleId!: string;
     @Input() currentUser!: CurrentUser;
 
     @Output() selectFlowAnalysisRule: EventEmitter<FlowAnalysisRuleEntity> = new EventEmitter<FlowAnalysisRuleEntity>();
+    @Output() viewFlowAnalysisRuleDocumentation: EventEmitter<FlowAnalysisRuleEntity> =
+        new EventEmitter<FlowAnalysisRuleEntity>();
     @Output() deleteFlowAnalysisRule: EventEmitter<FlowAnalysisRuleEntity> = new EventEmitter<FlowAnalysisRuleEntity>();
     @Output() configureFlowAnalysisRule: EventEmitter<FlowAnalysisRuleEntity> =
         new EventEmitter<FlowAnalysisRuleEntity>();
@@ -123,6 +126,11 @@ export class FlowAnalysisRuleTable {
         return !!entity.operatePermissions?.canWrite;
     }
 
+    viewDocumentationClicked(entity: FlowAnalysisRuleEntity, event: MouseEvent): void {
+        event.stopPropagation();
+        this.viewFlowAnalysisRuleDocumentation.next(entity);
+    }
+
     hasComments(entity: FlowAnalysisRuleEntity): boolean {
         return !this.nifiCommon.isBlank(entity.component.comments);
     }
@@ -236,7 +244,7 @@ export class FlowAnalysisRuleTable {
             this.isDisabled(entity) &&
             this.canRead(entity) &&
             this.canWrite(entity) &&
-            entity.component.multipleVersionsAvailable === true
+            entity.component.multipleVersionsAvailable
         );
     }
 
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rules.component.html b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rules.component.html
index 2224bac896..db79394f5f 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rules.component.html
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rules.component.html
@@ -37,6 +37,7 @@
                         [flowAnalysisRules]="flowAnalysisRuleState.flowAnalysisRules"
                         (configureFlowAnalysisRule)="configureFlowAnalysisRule($event)"
                         (selectFlowAnalysisRule)="selectFlowAnalysisRule($event)"
+                        (viewFlowAnalysisRuleDocumentation)="viewFlowAnalysisRuleDocumentation($event)"
                         (enableFlowAnalysisRule)="enableFlowAnalysisRule($event)"
                         (disableFlowAnalysisRule)="disableFlowAnalysisRule($event)"
                         (viewStateFlowAnalysisRule)="viewStateFlowAnalysisRule($event)"
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rules.component.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rules.component.ts
index aa452e4558..c9622614ae 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rules.component.ts
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rules.component.ts
@@ -41,6 +41,7 @@ import { selectCurrentUser } from '../../../../state/current-user/current-user.s
 import { NiFiState } from '../../../../state';
 import { FlowAnalysisRuleEntity, FlowAnalysisRulesState } from '../../state/flow-analysis-rules';
 import { getComponentStateAndOpenDialog } from '../../../../state/component-state/component-state.actions';
+import { navigateToComponentDocumentation } from '../../../../state/documentation/documentation.actions';
 
 @Component({
     selector: 'flow-analysis-rules',
@@ -106,6 +107,19 @@ export class FlowAnalysisRules implements OnInit, OnDestroy {
         );
     }
 
+    viewFlowAnalysisRuleDocumentation(entity: FlowAnalysisRuleEntity): void {
+        this.store.dispatch(
+            navigateToComponentDocumentation({
+                params: {
+                    select: entity.component.type,
+                    group: entity.component.bundle.group,
+                    artifact: entity.component.bundle.artifact,
+                    version: entity.component.bundle.version
+                }
+            })
+        );
+    }
+
     enableFlowAnalysisRule(entity: FlowAnalysisRuleEntity): void {
         this.store.dispatch(
             enableFlowAnalysisRule({
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/management-controller-services/management-controller-services.component.html b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/management-controller-services/management-controller-services.component.html
index 8b4e61ee9c..daf29d4257 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/management-controller-services/management-controller-services.component.html
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/management-controller-services/management-controller-services.component.html
@@ -40,6 +40,7 @@
                         [canModifyParent]="canModifyParent(currentUser)"
                         [flowConfiguration]="(flowConfiguration$ | async)!"
                         (selectControllerService)="selectControllerService($event)"
+                        (viewControllerServiceDocumentation)="viewControllerServiceDocumentation($event)"
                         (configureControllerService)="configureControllerService($event)"
                         (enableControllerService)="enableControllerService($event)"
                         (disableControllerService)="disableControllerService($event)"
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/management-controller-services/management-controller-services.component.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/management-controller-services/management-controller-services.component.ts
index 9dcf48d00d..5e470106f6 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/management-controller-services/management-controller-services.component.ts
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/management-controller-services/management-controller-services.component.ts
@@ -45,6 +45,7 @@ import { selectFlowConfiguration } from '../../../../state/flow-configuration/fl
 import { loadFlowConfiguration } from '../../../../state/flow-configuration/flow-configuration.actions';
 import { CurrentUser } from '../../../../state/current-user';
 import { getComponentStateAndOpenDialog } from '../../../../state/component-state/component-state.actions';
+import { navigateToComponentDocumentation } from '../../../../state/documentation/documentation.actions';
 
 @Component({
     selector: 'management-controller-services',
@@ -110,6 +111,19 @@ export class ManagementControllerServices implements OnInit, OnDestroy {
         return true;
     }
 
+    viewControllerServiceDocumentation(entity: ControllerServiceEntity): void {
+        this.store.dispatch(
+            navigateToComponentDocumentation({
+                params: {
+                    select: entity.component.type,
+                    group: entity.component.bundle.group,
+                    artifact: entity.component.bundle.artifact,
+                    version: entity.component.bundle.version
+                }
+            })
+        );
+    }
+
     configureControllerService(entity: ControllerServiceEntity): void {
         this.store.dispatch(
             navigateToEditService({
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/parameter-providers-table/parameter-providers-table.component.html b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/parameter-providers-table/parameter-providers-table.component.html
index ffba272780..c32575eea1 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/parameter-providers-table/parameter-providers-table.component.html
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/parameter-providers-table/parameter-providers-table.component.html
@@ -31,25 +31,29 @@
                     <ng-container matColumnDef="moreDetails">
                         <th mat-header-cell *matHeaderCellDef></th>
                         <td mat-cell *matCellDef="let item">
-                            <div class="flex items-center gap-x-3">
-                                <!-- TODO: open details -->
-                                <div class="pointer fa fa-info-circle" title="View details"></div>
+                            @if (canRead(item)) {
+                                <div class="flex items-center gap-x-3">
+                                    <!-- TODO: open details -->
+                                    <div class="pointer fa fa-info-circle" title="View details"></div>
 
-                                <!-- TODO: open documentation -->
-                                <div class="pointer fa fa-book" title="View Documentation"></div>
+                                    <div
+                                        class="pointer fa fa-book"
+                                        (click)="viewDocumentationClicked(item, $event)"
+                                        title="View Documentation"></div>
 
-                                <!-- Validation Errors -->
-                                @if (hasErrors(item)) {
-                                    <div>
-                                        <div
-                                            class="pointer fa fa-warning has-errors"
-                                            nifiTooltip
-                                            [delayClose]="false"
-                                            [tooltipComponentType]="ValidationErrorsTip"
-                                            [tooltipInputData]="getValidationErrorsTipData(item)"></div>
-                                    </div>
-                                }
-                            </div>
+                                    <!-- Validation Errors -->
+                                    @if (hasErrors(item)) {
+                                        <div>
+                                            <div
+                                                class="pointer fa fa-warning has-errors"
+                                                nifiTooltip
+                                                [delayClose]="false"
+                                                [tooltipComponentType]="ValidationErrorsTip"
+                                                [tooltipInputData]="getValidationErrorsTipData(item)"></div>
+                                        </div>
+                                    }
+                                </div>
+                            }
                         </td>
                     </ng-container>
 
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/parameter-providers-table/parameter-providers-table.component.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/parameter-providers-table/parameter-providers-table.component.ts
index eef6a5bd39..65fcf4a9eb 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/parameter-providers-table/parameter-providers-table.component.ts
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/parameter-providers-table/parameter-providers-table.component.ts
@@ -72,6 +72,8 @@ export class ParameterProvidersTable {
 
     @Output() selectParameterProvider: EventEmitter<ParameterProviderEntity> =
         new EventEmitter<ParameterProviderEntity>();
+    @Output() viewParameterProviderDocumentation: EventEmitter<ParameterProviderEntity> =
+        new EventEmitter<ParameterProviderEntity>();
     @Output() configureParameterProvider: EventEmitter<ParameterProviderEntity> =
         new EventEmitter<ParameterProviderEntity>();
     @Output() deleteParameterProvider: EventEmitter<ParameterProviderEntity> =
@@ -129,6 +131,11 @@ export class ParameterProvidersTable {
         return false;
     }
 
+    viewDocumentationClicked(entity: ParameterProviderEntity, event: MouseEvent): void {
+        event.stopPropagation();
+        this.viewParameterProviderDocumentation.next(entity);
+    }
+
     formatName(entity: ParameterProviderEntity): string {
         return this.canRead(entity) ? entity.component.name : entity.id;
     }
@@ -142,7 +149,7 @@ export class ParameterProvidersTable {
     }
 
     hasErrors(entity: ParameterProviderEntity): boolean {
-        return this.canRead(entity) && !this.nifiCommon.isEmpty(entity.component.validationErrors);
+        return !this.nifiCommon.isEmpty(entity.component.validationErrors);
     }
 
     getValidationErrorsTipData(entity: ParameterProviderEntity): ValidationErrorsTipInput | null {
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/parameter-providers.component.html b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/parameter-providers.component.html
index 6fa57f81a0..7f1f1e2ce1 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/parameter-providers.component.html
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/parameter-providers.component.html
@@ -42,6 +42,7 @@
                         (deleteParameterProvider)="deleteParameterProvider($event)"
                         (configureParameterProvider)="openConfigureParameterProviderDialog($event)"
                         (fetchParameterProvider)="fetchParameterProviderParameters($event)"
+                        (viewParameterProviderDocumentation)="viewParameterProviderDocumentation($event)"
                         (selectParameterProvider)="selectParameterProvider($event)"></parameter-providers-table>
                 </div>
                 <div class="flex justify-between">
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/parameter-providers.component.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/parameter-providers.component.ts
index 18c5534bb5..8c9660ce05 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/parameter-providers.component.ts
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/parameter-providers.component.ts
@@ -34,6 +34,7 @@ import { initialParameterProvidersState } from '../../state/parameter-providers/
 import { switchMap, take } from 'rxjs';
 import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
 import { isDefinedAndNotNull } from '../../../../state/shared';
+import { navigateToComponentDocumentation } from '../../../../state/documentation/documentation.actions';
 
 @Component({
     selector: 'parameter-providers',
@@ -130,6 +131,19 @@ export class ParameterProviders implements OnInit, OnDestroy {
         );
     }
 
+    viewParameterProviderDocumentation(parameterProvider: ParameterProviderEntity): void {
+        this.store.dispatch(
+            navigateToComponentDocumentation({
+                params: {
+                    select: parameterProvider.component.type,
+                    group: parameterProvider.component.bundle.group,
+                    artifact: parameterProvider.component.bundle.artifact,
+                    version: parameterProvider.component.bundle.version
+                }
+            })
+        );
+    }
+
     deleteParameterProvider(parameterProvider: ParameterProviderEntity) {
         this.store.dispatch(
             ParameterProviderActions.promptParameterProviderDeletion({
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/reporting-tasks/reporting-task-table/reporting-task-table.component.html b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/reporting-tasks/reporting-task-table/reporting-task-table.component.html
index 01d6def1b7..b19f5a2fb6 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/reporting-tasks/reporting-task-table/reporting-task-table.component.html
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/reporting-tasks/reporting-task-table/reporting-task-table.component.html
@@ -31,7 +31,10 @@
                 <td mat-cell *matCellDef="let item">
                     @if (canRead(item)) {
                         <div class="flex items-center gap-x-3">
-                            <div class="pointer fa fa-book" title="Usage"></div>
+                            <div
+                                class="pointer fa fa-book"
+                                (click)="viewDocumentationClicked(item, $event)"
+                                title="View Documentation"></div>
                             <!-- TODO - handle read only in configure component? -->
                             @if (hasComments(item)) {
                                 <div>
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/reporting-tasks/reporting-task-table/reporting-task-table.component.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/reporting-tasks/reporting-task-table/reporting-task-table.component.ts
index 26e8c874d8..8323ce2216 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/reporting-tasks/reporting-task-table/reporting-task-table.component.ts
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/reporting-tasks/reporting-task-table/reporting-task-table.component.ts
@@ -49,6 +49,8 @@ export class ReportingTaskTable {
     @Input() currentUser!: CurrentUser;
 
     @Output() selectReportingTask: EventEmitter<ReportingTaskEntity> = new EventEmitter<ReportingTaskEntity>();
+    @Output() viewReportingTaskDocumentation: EventEmitter<ReportingTaskEntity> =
+        new EventEmitter<ReportingTaskEntity>();
     @Output() deleteReportingTask: EventEmitter<ReportingTaskEntity> = new EventEmitter<ReportingTaskEntity>();
     @Output() startReportingTask: EventEmitter<ReportingTaskEntity> = new EventEmitter<ReportingTaskEntity>();
     @Output() configureReportingTask: EventEmitter<ReportingTaskEntity> = new EventEmitter<ReportingTaskEntity>();
@@ -79,6 +81,11 @@ export class ReportingTaskTable {
         return !!entity.operatePermissions?.canWrite;
     }
 
+    viewDocumentationClicked(entity: ReportingTaskEntity, event: MouseEvent): void {
+        event.stopPropagation();
+        this.viewReportingTaskDocumentation.next(entity);
+    }
+
     hasComments(entity: ReportingTaskEntity): boolean {
         return !this.nifiCommon.isBlank(entity.component.comments);
     }
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/reporting-tasks/reporting-tasks.component.html b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/reporting-tasks/reporting-tasks.component.html
index fb86c3977d..4ca91dead4 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/reporting-tasks/reporting-tasks.component.html
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/reporting-tasks/reporting-tasks.component.html
@@ -39,6 +39,7 @@
                         (configureReportingTask)="configureReportingTask($event)"
                         (viewStateReportingTask)="viewStateReportingTask($event)"
                         (selectReportingTask)="selectReportingTask($event)"
+                        (viewReportingTaskDocumentation)="viewReportingTaskDocumentation($event)"
                         (deleteReportingTask)="deleteReportingTask($event)"
                         (stopReportingTask)="stopReportingTask($event)"
                         (startReportingTask)="startReportingTask($event)"></reporting-task-table>
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/reporting-tasks/reporting-tasks.component.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/reporting-tasks/reporting-tasks.component.ts
index fd8c6471cf..f09f628db7 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/reporting-tasks/reporting-tasks.component.ts
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/reporting-tasks/reporting-tasks.component.ts
@@ -43,6 +43,7 @@ import { NiFiState } from '../../../../state';
 import { loadFlowConfiguration } from '../../../../state/flow-configuration/flow-configuration.actions';
 import { selectFlowConfiguration } from '../../../../state/flow-configuration/flow-configuration.selectors';
 import { getComponentStateAndOpenDialog } from '../../../../state/component-state/component-state.actions';
+import { navigateToComponentDocumentation } from '../../../../state/documentation/documentation.actions';
 
 @Component({
     selector: 'reporting-tasks',
@@ -110,6 +111,19 @@ export class ReportingTasks implements OnInit, OnDestroy {
         );
     }
 
+    viewReportingTaskDocumentation(entity: ReportingTaskEntity): void {
+        this.store.dispatch(
+            navigateToComponentDocumentation({
+                params: {
+                    select: entity.component.type,
+                    group: entity.component.bundle.group,
+                    artifact: entity.component.bundle.artifact,
+                    version: entity.component.bundle.version
+                }
+            })
+        );
+    }
+
     deleteReportingTask(entity: ReportingTaskEntity): void {
         this.store.dispatch(
             promptReportingTaskDeletion({
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/documentation/documentation.actions.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/documentation/documentation.actions.ts
new file mode 100644
index 0000000000..4c3389d027
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/documentation/documentation.actions.ts
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 { createAction, props } from '@ngrx/store';
+import { DocumentationParameters } from './index';
+
+export const navigateToComponentDocumentation = createAction(
+    '[Documentation] Navigate To Component Documentation',
+    props<{
+        params: DocumentationParameters;
+    }>()
+);
+
+export const clearDocumentationParameters = createAction('[Documentation] Clear Documentation Parameters');
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/documentation/documentation.effects.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/documentation/documentation.effects.ts
new file mode 100644
index 0000000000..945f8a0edd
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/documentation/documentation.effects.ts
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 { Injectable } from '@angular/core';
+import { Actions, createEffect, ofType } from '@ngrx/effects';
+import * as DocumentationActions from './documentation.actions';
+import { tap } from 'rxjs';
+import { Router } from '@angular/router';
+
+@Injectable()
+export class DocumentationEffects {
+    constructor(
+        private actions$: Actions,
+        private router: Router
+    ) {}
+
+    navigateToComponentDocumentation$ = createEffect(
+        () =>
+            this.actions$.pipe(
+                ofType(DocumentationActions.navigateToComponentDocumentation),
+                tap(() => {
+                    this.router.navigate(['/documentation']);
+                })
+            ),
+        { dispatch: false }
+    );
+}
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/documentation/documentation.reducer.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/documentation/documentation.reducer.ts
new file mode 100644
index 0000000000..9b3e379fef
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/documentation/documentation.reducer.ts
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 { createReducer, on } from '@ngrx/store';
+import { DocumentationState } from './index';
+import { clearDocumentationParameters, navigateToComponentDocumentation } from './documentation.actions';
+
+export const initialState: DocumentationState = {
+    documentationParameters: null
+};
+
+export const documentationReducer = createReducer(
+    initialState,
+    on(navigateToComponentDocumentation, (state, { params }) => ({
+        ...state,
+        documentationParameters: params
+    })),
+    on(clearDocumentationParameters, (state) => ({
+        ...state,
+        documentationParameters: null
+    }))
+);
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/documentation/documentation.selectors.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/documentation/documentation.selectors.ts
new file mode 100644
index 0000000000..e33cbbdbdb
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/documentation/documentation.selectors.ts
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 { createFeatureSelector, createSelector } from '@ngrx/store';
+import { documentationFeatureKey, DocumentationState } from './index';
+
+export const selectDocumentationState = createFeatureSelector<DocumentationState>(documentationFeatureKey);
+
+export const selectDocumentationParameters = createSelector(
+    selectDocumentationState,
+    (state: DocumentationState) => state.documentationParameters
+);
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/documentation/index.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/documentation/index.ts
new file mode 100644
index 0000000000..cd3d311d41
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/documentation/index.ts
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+export const documentationFeatureKey = 'documentation';
+
+export interface DocumentationParameters {
+    [key: string]: string;
+}
+
+export interface DocumentationState {
+    documentationParameters: DocumentationParameters | null;
+}
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/index.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/index.ts
index ed546d4a04..450aa5ff7f 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/index.ts
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/index.ts
@@ -35,6 +35,8 @@ import { componentStateFeatureKey, ComponentStateState } from './component-state
 import { componentStateReducer } from './component-state/component-state.reducer';
 import { errorFeatureKey, ErrorState } from './error';
 import { errorReducer } from './error/error.reducer';
+import { documentationFeatureKey, DocumentationState } from './documentation';
+import { documentationReducer } from './documentation/documentation.reducer';
 
 export interface NiFiState {
     router: RouterReducerState;
@@ -47,6 +49,7 @@ export interface NiFiState {
     [controllerServiceStateFeatureKey]: ControllerServiceState;
     [systemDiagnosticsFeatureKey]: SystemDiagnosticsState;
     [componentStateFeatureKey]: ComponentStateState;
+    [documentationFeatureKey]: DocumentationState;
 }
 
 export const rootReducers: ActionReducerMap<NiFiState> = {
@@ -59,5 +62,6 @@ export const rootReducers: ActionReducerMap<NiFiState> = {
     [statusHistoryFeatureKey]: statusHistoryReducer,
     [controllerServiceStateFeatureKey]: controllerServiceStateReducer,
     [systemDiagnosticsFeatureKey]: systemDiagnosticsReducer,
-    [componentStateFeatureKey]: componentStateReducer
+    [componentStateFeatureKey]: componentStateReducer,
+    [documentationFeatureKey]: documentationReducer
 };
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/controller-service/controller-service-table/controller-service-table.component.html b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/controller-service/controller-service-table/controller-service-table.component.html
index 949f5cabad..06ceff163c 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/controller-service/controller-service-table/controller-service-table.component.html
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/controller-service/controller-service-table/controller-service-table.component.html
@@ -31,13 +31,11 @@
                 <td mat-cell *matCellDef="let item">
                     @if (canRead(item)) {
                         <div class="flex items-center gap-x-3">
-                            <div class="pointer fa fa-book" title="Usage"></div>
-                            <!--
-              nifiTooltip dynamically inserts the tooltip component
-              into the dom. the dom placement caused shifting of icons
-              because of the gap between items. simple solution is to
-              just wrap the target.
-              -->
+                            <div
+                                class="pointer fa fa-book"
+                                (click)="viewDocumentationClicked(item, $event)"
+                                title="View Documentation"></div>
+
                             @if (hasComments(item)) {
                                 <div>
                                     <div
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/controller-service/controller-service-table/controller-service-table.component.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/controller-service/controller-service-table/controller-service-table.component.ts
index 58216189bc..d70f294ad0 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/controller-service/controller-service-table/controller-service-table.component.ts
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/controller-service/controller-service-table/controller-service-table.component.ts
@@ -72,6 +72,8 @@ export class ControllerServiceTable {
 
     @Output() selectControllerService: EventEmitter<ControllerServiceEntity> =
         new EventEmitter<ControllerServiceEntity>();
+    @Output() viewControllerServiceDocumentation: EventEmitter<ControllerServiceEntity> =
+        new EventEmitter<ControllerServiceEntity>();
     @Output() deleteControllerService: EventEmitter<ControllerServiceEntity> =
         new EventEmitter<ControllerServiceEntity>();
     @Output() configureControllerService: EventEmitter<ControllerServiceEntity> =
@@ -117,6 +119,11 @@ export class ControllerServiceTable {
         };
     }
 
+    viewDocumentationClicked(entity: ControllerServiceEntity, event: MouseEvent): void {
+        event.stopPropagation();
+        this.viewControllerServiceDocumentation.next(entity);
+    }
+
     hasErrors(entity: ControllerServiceEntity): boolean {
         return !this.nifiCommon.isEmpty(entity.component.validationErrors);
     }
@@ -236,7 +243,7 @@ export class ControllerServiceTable {
             this.isDisabled(entity) &&
             this.canRead(entity) &&
             this.canWrite(entity) &&
-            entity.component.multipleVersionsAvailable === true
+            entity.component.multipleVersionsAvailable
         );
     }
 
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/navigation/navigation.component.html b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/navigation/navigation.component.html
index 84af0f8b11..4ec377753d 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/navigation/navigation.component.html
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/navigation/navigation.component.html
@@ -136,7 +136,7 @@
                             <mat-divider></mat-divider>
                         }
                     }
-                    <button mat-menu-item class="global-menu-item">
+                    <button mat-menu-item class="global-menu-item" [routerLink]="['/documentation']">
                         <i class="fa fa-fw fa-question-circle mr-2"></i>
                         Help
                     </button>
@@ -144,7 +144,10 @@
                         <i class="fa fa-fw fa-info-circle mr-2"></i>
                         About
                     </button>
-                    <button mat-menu-item [matMenuTriggerFor]="theming">Appearance</button>
+                    <button mat-menu-item [matMenuTriggerFor]="theming">
+                        <i class="fa fa-fw mr-2"></i>
+                        Appearance
+                    </button>
                 </mat-menu>
                 <mat-menu #theming="matMenu" xPosition="before">
                     <button mat-menu-item class="global-menu-item" (click)="toggleTheme(LIGHT_THEME)">