NIFI-13112: (#8711)

- Fixing linking to non canvas items.
- Adding a tooltip to render search match details.

This closes #8711
This commit is contained in:
Matt Gilman 2024-05-01 12:00:02 -04:00 committed by GitHub
parent 0e373e9c21
commit 2c43a706f8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 195 additions and 23 deletions

View File

@ -128,7 +128,6 @@
} }
"></ng-container> "></ng-container>
} }
<!-- TODO - Handling linking to Controller Services, Parameter Providers, Parameter Contexts, and Parameters -->
@if (controllerServiceNodeResults.length > 0) { @if (controllerServiceNodeResults.length > 0) {
<ng-container <ng-container
*ngTemplateOutlet=" *ngTemplateOutlet="
@ -186,31 +185,81 @@
#searchInput="cdkOverlayOrigin" #searchInput="cdkOverlayOrigin"
formControlName="searchBar" /> formControlName="searchBar" />
<ng-template #renderResults let-results let-header="header" let-icon="icon" let-path="path"> <ng-template #renderResults let-results let-header="header" let-icon="icon" let-path="path">
<li class="flex items-center"> @if (results.length > 0) {
<span class="icon mr-1" [class]="icon"></span> <li class="flex items-center">
<span class="font-medium">{{ header }}</span> <span class="icon mr-1 accent-color" [class]="icon"></span>
</li> <span class="font-medium">{{ header }}</span>
<!-- TODO - Consider showing more context of match like existing UI --> </li>
@for (result of results; track result) { @for (result of results; track result) {
<li class="ml-2 py-1"> <li class="ml-4 py-1 flex gap-x-2 items-center">
@if (!result.parentGroup) { <i
<a [routerLink]="['/process-groups', result.id]"> class="fa fa-info-circle on-surface-medium"
{{ result.name }} nifiTooltip
</a> [tooltipComponentType]="SearchMatchTip"
} @else { [tooltipInputData]="getSearchMatchTipInput(result)"
@if (result.parentGroup.id == currentProcessGroupId) { [delayClose]="false"></i>
@if (header === 'Parameters') {
<a <a
(click)="componentLinkClicked(path, result.id)" class="w-full overflow-ellipsis overflow-hidden whitespace-nowrap"
[routerLink]="['/process-groups', result.parentGroup.id, path, result.id]"> [title]="result.name"
[routerLink]="['/parameter-contexts', result.parentGroup.id]">
{{ result.name }}
</a>
} @else if (header === 'Parameter Contexts') {
<a
class="w-full overflow-ellipsis overflow-hidden whitespace-nowrap"
[title]="result.name"
[routerLink]="['/parameter-contexts', result.id]">
{{ result.name }}
</a>
} @else if (header === 'Parameter Providers') {
<a
class="w-full overflow-ellipsis overflow-hidden whitespace-nowrap"
[title]="result.name"
[routerLink]="['/settings', 'parameter-providers', result.parentGroup.id]">
{{ result.name }}
</a>
} @else if (header === 'Controller Services') {
<a
class="w-full overflow-ellipsis overflow-hidden whitespace-nowrap"
[title]="result.name"
[routerLink]="[
'/process-groups',
result.parentGroup.id,
'controller-services',
result.id
]">
{{ result.name ? result.name : result.id }} {{ result.name ? result.name : result.id }}
</a> </a>
} @else { } @else {
<a [routerLink]="['/process-groups', result.parentGroup.id, path, result.id]"> @if (!result.parentGroup) {
{{ result.name ? result.name : result.id }} <a
</a> class="w-full overflow-ellipsis overflow-hidden whitespace-nowrap"
[title]="result.name"
[routerLink]="['/process-groups', result.id]">
{{ result.name }}
</a>
} @else {
@if (result.parentGroup.id == currentProcessGroupId) {
<a
class="w-full overflow-ellipsis overflow-hidden whitespace-nowrap"
(click)="componentLinkClicked(path, result.id)"
[title]="result.name"
[routerLink]="['/process-groups', result.parentGroup.id, path, result.id]">
{{ result.name ? result.name : result.id }}
</a>
} @else {
<a
class="w-full overflow-ellipsis overflow-hidden whitespace-nowrap"
[title]="result.name"
[routerLink]="['/process-groups', result.parentGroup.id, path, result.id]">
{{ result.name ? result.name : result.id }}
</a>
}
}
} }
} </li>
</li> }
} }
</ng-template> </ng-template>
</form> </form>

View File

@ -27,7 +27,7 @@ import {
OriginConnectionPosition, OriginConnectionPosition,
OverlayConnectionPosition OverlayConnectionPosition
} from '@angular/cdk/overlay'; } from '@angular/cdk/overlay';
import { ComponentType } from '../../../../../../state/shared'; import { ComponentType, SearchMatchTipInput } from '../../../../../../state/shared';
import { NgTemplateOutlet } from '@angular/common'; import { NgTemplateOutlet } from '@angular/common';
import { RouterLink } from '@angular/router'; import { RouterLink } from '@angular/router';
import { MatFormFieldModule } from '@angular/material/form-field'; import { MatFormFieldModule } from '@angular/material/form-field';
@ -37,6 +37,8 @@ import { Store } from '@ngrx/store';
import { centerSelectedComponents, setAllowTransition } from '../../../../state/flow/flow.actions'; import { centerSelectedComponents, setAllowTransition } from '../../../../state/flow/flow.actions';
import { selectCurrentRoute } from '../../../../../../state/router/router.selectors'; import { selectCurrentRoute } from '../../../../../../state/router/router.selectors';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { NifiTooltipDirective } from '../../../../../../ui/common/tooltips/nifi-tooltip.directive';
import { SearchMatchTip } from '../../../../../../ui/common/tooltips/search-match-tip/search-match-tip.component';
@Component({ @Component({
selector: 'search', selector: 'search',
@ -50,11 +52,13 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
NgTemplateOutlet, NgTemplateOutlet,
RouterLink, RouterLink,
MatFormFieldModule, MatFormFieldModule,
MatInputModule MatInputModule,
NifiTooltipDirective
] ]
}) })
export class Search implements OnInit { export class Search implements OnInit {
protected readonly ComponentType = ComponentType; protected readonly ComponentType = ComponentType;
protected readonly SearchMatchTip = SearchMatchTip;
@Input() currentProcessGroupId: string = initialState.id; @Input() currentProcessGroupId: string = initialState.id;
@ViewChild('searchInput') searchInput!: CdkOverlayOrigin; @ViewChild('searchInput') searchInput!: CdkOverlayOrigin;
@ -190,6 +194,12 @@ export class Search implements OnInit {
this.parameterResults = []; this.parameterResults = [];
} }
getSearchMatchTipInput(result: ComponentSearchResult): SearchMatchTipInput {
return {
matches: result.matches
};
}
componentLinkClicked(componentType: ComponentType, id: string): void { componentLinkClicked(componentType: ComponentType, id: string): void {
if (componentType == this.selectedComponentType && id == this.selectedComponentId) { if (componentType == this.selectedComponentType && id == this.selectedComponentId) {
this.store.dispatch(centerSelectedComponents({ request: { allowTransition: true } })); this.store.dispatch(centerSelectedComponents({ request: { allowTransition: true } }));

View File

@ -229,6 +229,10 @@ export interface UnorderedListTipInput {
items: string[]; items: string[];
} }
export interface SearchMatchTipInput {
matches: string[];
}
export interface ControllerServiceApi { export interface ControllerServiceApi {
type: string; type: string;
bundle: Bundle; bundle: Bundle;

View File

@ -0,0 +1,25 @@
<!--
~ 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="tooltip">
Search matched:
<ul>
@for (match of data?.matches; track match) {
<li>{{ match }}</li>
}
</ul>
</div>

View File

@ -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.
*/

View File

@ -0,0 +1,38 @@
/*
* 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 { SearchMatchTip } from './search-match-tip.component';
describe('SearchMatchTip', () => {
let component: SearchMatchTip;
let fixture: ComponentFixture<SearchMatchTip>;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [SearchMatchTip]
});
fixture = TestBed.createComponent(SearchMatchTip);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -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 { Component, Input } from '@angular/core';
import { SearchMatchTipInput } from '../../../../state/shared';
@Component({
selector: 'search-match-tip',
standalone: true,
templateUrl: './search-match-tip.component.html',
imports: [],
styleUrls: ['./search-match-tip.component.scss']
})
export class SearchMatchTip {
@Input() data: SearchMatchTipInput | undefined;
}