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

rfellows 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 1d41ee6d4c6 NIFI-16463 Added a rule count to UpdateAttribute Advanced 
view. (#11814)
1d41ee6d4c6 is described below

commit 1d41ee6d4c62383f9f1e1039e8d31470d053e814
Author: dan-s1 <[email protected]>
AuthorDate: Wed Oct 21 08:27:16 2026 -0400

    NIFI-16463 Added a rule count to UpdateAttribute Advanced view. (#11814)
    
    * NIFI-16463 Added a rule count to UpdateAttribute Advanced view.
    
    * NIFI-16463 Added import and corrected assertion of which text is not 
present.
    
    * NIFI-16463 Removed used of privatge method.
    
    * NIFI-16463 Removed use of fakeAsync where tick is not used.
    
    * NIFI-16463 Review
    
    * NIFI-16463 Change to prevent "No Matching rules" when editing a rule.
    
    This closes #11814
---
 .../ui/rule-listing/rule-listing.component.html    | 236 +++++++++++----------
 .../ui/rule-listing/rule-listing.component.spec.ts |  77 ++++++-
 2 files changed, 196 insertions(+), 117 deletions(-)

diff --git 
a/nifi-frontend/src/main/frontend/apps/update-attribute/src/app/pages/update-attribute/ui/rule-listing/rule-listing.component.html
 
b/nifi-frontend/src/main/frontend/apps/update-attribute/src/app/pages/update-attribute/ui/rule-listing/rule-listing.component.html
index 9001da64ea0..56335a63386 100644
--- 
a/nifi-frontend/src/main/frontend/apps/update-attribute/src/app/pages/update-attribute/ui/rule-listing/rule-listing.component.html
+++ 
b/nifi-frontend/src/main/frontend/apps/update-attribute/src/app/pages/update-attribute/ui/rule-listing/rule-listing.component.html
@@ -75,125 +75,131 @@
                 }
             </div>
         </div>
-        @if (filteredRulesList.length === 0 && !newRule) {
+        @if (!newRule && rulesList.length > 0) {
+            <div class="tertiary-color font-medium">
+                Displaying {{ filteredRulesList.length }} of {{ 
rulesList.length }}
+            </div>
+        }
+
+        @if (!newRule && rulesList.length > 0 && filteredRulesList.length === 
0) {
             <div class="unset">No matching rules</div>
+        }
+
+        @if (allowRuleReordering) {
+            <mat-accordion
+                [multi]="true"
+                cdkDropList
+                [cdkDropListDisabled]="!isEditable"
+                [cdkDropListData]="rulesList"
+                (cdkDropListDropped)="reorderRules($event)">
+                @for (rule of filteredRulesList; track rule.id) {
+                    <mat-expansion-panel cdkDrag 
cdkDragPreviewContainer="parent">
+                        <mat-expansion-panel-header
+                            class="reorder-header"
+                            [collapsedHeight]="'42px'"
+                            [expandedHeight]="'42px'">
+                            <mat-panel-title>
+                                <div class="flex justify-between items-center">
+                                    <span class="grip -mt-0.5 mr-5 
cursor-grab"></span>
+                                    <div class="flex-1 truncate">
+                                        {{ rule.name }}
+                                    </div>
+                                </div>
+                            </mat-panel-title>
+                            <mat-panel-description>
+                                {{ rule.comments }}
+                            </mat-panel-description>
+                        </mat-expansion-panel-header>
+                        <edit-rule
+                            [id]="rule.id"
+                            [name]="rule.name"
+                            [existingRuleNames]="getExistingRuleNames()"
+                            [comments]="rule.comments"
+                            [conditions]="rule.conditions"
+                            [actions]="rule.actions"
+                            [editable]="false"></edit-rule>
+                    </mat-expansion-panel>
+                }
+            </mat-accordion>
         } @else {
-            @if (allowRuleReordering) {
-                <mat-accordion
-                    [multi]="true"
-                    cdkDropList
-                    [cdkDropListDisabled]="!isEditable"
-                    [cdkDropListData]="rulesList"
-                    (cdkDropListDropped)="reorderRules($event)">
-                    @for (rule of filteredRulesList; track rule.id) {
-                        <mat-expansion-panel cdkDrag 
cdkDragPreviewContainer="parent">
-                            <mat-expansion-panel-header
-                                class="reorder-header"
-                                [collapsedHeight]="'42px'"
-                                [expandedHeight]="'42px'">
-                                <mat-panel-title>
-                                    <div class="flex justify-between 
items-center">
-                                        <span class="grip -mt-0.5 mr-5 
cursor-grab"></span>
-                                        <div class="flex-1 truncate">
-                                            {{ rule.name }}
-                                        </div>
+            <mat-accordion [multi]="true">
+                @for (rule of filteredRulesList; track rule.id) {
+                    <mat-expansion-panel
+                        #expansionPanel
+                        (opened)="panelOpened()"
+                        (closed)="panelClosed()"
+                        [disabled]="isDirty(rule.id)">
+                        <mat-expansion-panel-header
+                            class="editable-header"
+                            [collapsedHeight]="'42px'"
+                            [expandedHeight]="'42px'">
+                            <mat-panel-title>
+                                {{ rule.name }}
+                            </mat-panel-title>
+                            <mat-panel-description>
+                                <div class="flex justify-between items-center">
+                                    <div class="flex-1 truncate">
+                                        {{ rule.comments }}
                                     </div>
-                                </mat-panel-title>
-                                <mat-panel-description>
-                                    {{ rule.comments }}
-                                </mat-panel-description>
-                            </mat-expansion-panel-header>
-                            <edit-rule
-                                [id]="rule.id"
-                                [name]="rule.name"
-                                [existingRuleNames]="getExistingRuleNames()"
-                                [comments]="rule.comments"
-                                [conditions]="rule.conditions"
-                                [actions]="rule.actions"
-                                [editable]="false"></edit-rule>
-                        </mat-expansion-panel>
-                    }
-                </mat-accordion>
-            } @else {
-                <mat-accordion [multi]="true">
-                    @for (rule of filteredRulesList; track rule.id) {
-                        <mat-expansion-panel
-                            #expansionPanel
-                            (opened)="panelOpened()"
-                            (closed)="panelClosed()"
-                            [disabled]="isDirty(rule.id)">
-                            <mat-expansion-panel-header
-                                class="editable-header"
-                                [collapsedHeight]="'42px'"
-                                [expandedHeight]="'42px'">
-                                <mat-panel-title>
-                                    {{ rule.name }}
-                                </mat-panel-title>
-                                <mat-panel-description>
-                                    <div class="flex justify-between 
items-center">
-                                        <div class="flex-1 truncate">
-                                            {{ rule.comments }}
-                                        </div>
-                                        @if (isEditable) {
-                                            <button
-                                                mat-icon-button
-                                                type="button"
-                                                
[matMenuTriggerFor]="actionMenu"
-                                                
(click)="$event.stopPropagation()"
-                                                
(keydown)="$event.stopPropagation()"
-                                                class="flex items-center 
justify-center icon global-menu">
-                                                <i class="fa 
fa-ellipsis-v"></i>
+                                    @if (isEditable) {
+                                        <button
+                                            mat-icon-button
+                                            type="button"
+                                            [matMenuTriggerFor]="actionMenu"
+                                            (click)="$event.stopPropagation()"
+                                            
(keydown)="$event.stopPropagation()"
+                                            class="flex items-center 
justify-center icon global-menu">
+                                            <i class="fa fa-ellipsis-v"></i>
+                                        </button>
+                                        <mat-menu #actionMenu="matMenu" 
xPosition="before">
+                                            <button mat-menu-item title="Clone 
Rule" (click)="cloneRule(rule)">
+                                                <i class="fa fa-clone 
primary-color mr-2"></i>
+                                                Clone
                                             </button>
-                                            <mat-menu #actionMenu="matMenu" 
xPosition="before">
-                                                <button mat-menu-item 
title="Clone Rule" (click)="cloneRule(rule)">
-                                                    <i class="fa fa-clone 
primary-color mr-2"></i>
-                                                    Clone
-                                                </button>
-                                                <button mat-menu-item 
title="Delete" (click)="deleteRule(rule)">
-                                                    <i class="fa fa-trash 
primary-color mr-2"></i>
-                                                    Delete
-                                                </button>
-                                            </mat-menu>
-                                        }
-                                    </div>
-                                </mat-panel-description>
-                            </mat-expansion-panel-header>
-                            <edit-rule
-                                [id]="rule.id"
-                                [name]="rule.name"
-                                [existingRuleNames]="getExistingRuleNames()"
-                                [comments]="rule.comments"
-                                [conditions]="rule.conditions"
-                                [actions]="rule.actions"
-                                [editable]="isEditable"
-                                [ruleUpdate]="(getRuleUpdates(rule.id) | 
async)!"
-                                (editRule)="editRule($event)"
-                                (dirty)="setDirty(rule.id, $event)"
-                                (exit)="expansionPanel.close()"></edit-rule>
-                        </mat-expansion-panel>
-                    }
-                    @if (newRule) {
-                        <mat-expansion-panel expanded="true" disabled="true">
-                            <mat-expansion-panel-header>
-                                <mat-panel-title class="items-center">
-                                    <span class="unset">New rule</span>
-                                </mat-panel-title>
-                            </mat-expansion-panel-header>
-                            <edit-rule
-                                class="new-rule"
-                                [name]="newRule.name"
-                                [existingRuleNames]="getExistingRuleNames()"
-                                [comments]="newRule.comments"
-                                [conditions]="newRule.conditions"
-                                [actions]="newRule.actions"
-                                [editable]="isEditable"
-                                (afterInit)="newRuleExpanded()"
-                                (addRule)="createRule($event)"
-                                (exit)="cancelNewRule()"></edit-rule>
-                        </mat-expansion-panel>
-                    }
-                </mat-accordion>
-            }
+                                            <button mat-menu-item 
title="Delete" (click)="deleteRule(rule)">
+                                                <i class="fa fa-trash 
primary-color mr-2"></i>
+                                                Delete
+                                            </button>
+                                        </mat-menu>
+                                    }
+                                </div>
+                            </mat-panel-description>
+                        </mat-expansion-panel-header>
+                        <edit-rule
+                            [id]="rule.id"
+                            [name]="rule.name"
+                            [existingRuleNames]="getExistingRuleNames()"
+                            [comments]="rule.comments"
+                            [conditions]="rule.conditions"
+                            [actions]="rule.actions"
+                            [editable]="isEditable"
+                            [ruleUpdate]="(getRuleUpdates(rule.id) | async)!"
+                            (editRule)="editRule($event)"
+                            (dirty)="setDirty(rule.id, $event)"
+                            (exit)="expansionPanel.close()"></edit-rule>
+                    </mat-expansion-panel>
+                }
+                @if (newRule) {
+                    <mat-expansion-panel expanded="true" disabled="true">
+                        <mat-expansion-panel-header>
+                            <mat-panel-title class="items-center">
+                                <span class="unset">New rule</span>
+                            </mat-panel-title>
+                        </mat-expansion-panel-header>
+                        <edit-rule
+                            class="new-rule"
+                            [name]="newRule.name"
+                            [existingRuleNames]="getExistingRuleNames()"
+                            [comments]="newRule.comments"
+                            [conditions]="newRule.conditions"
+                            [actions]="newRule.actions"
+                            [editable]="isEditable"
+                            (afterInit)="newRuleExpanded()"
+                            (addRule)="createRule($event)"
+                            (exit)="cancelNewRule()"></edit-rule>
+                    </mat-expansion-panel>
+                }
+            </mat-accordion>
         }
     </div>
 }
diff --git 
a/nifi-frontend/src/main/frontend/apps/update-attribute/src/app/pages/update-attribute/ui/rule-listing/rule-listing.component.spec.ts
 
b/nifi-frontend/src/main/frontend/apps/update-attribute/src/app/pages/update-attribute/ui/rule-listing/rule-listing.component.spec.ts
index dc792930260..5e590f831c4 100644
--- 
a/nifi-frontend/src/main/frontend/apps/update-attribute/src/app/pages/update-attribute/ui/rule-listing/rule-listing.component.spec.ts
+++ 
b/nifi-frontend/src/main/frontend/apps/update-attribute/src/app/pages/update-attribute/ui/rule-listing/rule-listing.component.spec.ts
@@ -26,12 +26,22 @@ import { initialState as advancedUiParametersInitialState } 
from '../../state/ad
 import { updateAttributeFeatureKey } from '../../state';
 import { advancedUiParametersFeatureKey } from 
'../../state/advanced-ui-parameters';
 import { evaluationContextFeatureKey } from '../../state/evaluation-context';
-import { rulesFeatureKey } from '../../state/rules';
+import { Rule, rulesFeatureKey } from '../../state/rules';
+import { fakeAsync, tick } from '@angular/core/testing';
 
 describe('RuleListing', () => {
     let component: RuleListing;
     let fixture: ComponentFixture<RuleListing>;
 
+    const createRule = (id: string, name: string): Rule =>
+        ({
+            id,
+            name,
+            comments: '',
+            conditions: [],
+            actions: []
+        }) as Rule;
+
     beforeEach(async () => {
         await TestBed.configureTestingModule({
             imports: [RuleListing],
@@ -50,10 +60,73 @@ describe('RuleListing', () => {
 
         fixture = TestBed.createComponent(RuleListing);
         component = fixture.componentInstance;
-        fixture.detectChanges();
     });
 
     it('should create', () => {
+        fixture.detectChanges();
         expect(component).toBeTruthy();
     });
+
+    it('should display the number of filtered rules and total rules', () => {
+        component.rules = [createRule('1', 'Rule One'), createRule('2', 'Rule 
Two')];
+
+        fixture.detectChanges();
+
+        expect(fixture.nativeElement.textContent).toContain('Displaying 2 of 
2');
+    });
+
+    it('should display one matching rule out of four when filtered', 
fakeAsync(() => {
+        component.rules = [
+            createRule('1', 'Apple'),
+            createRule('2', 'Apricot'),
+            createRule('3', 'Banana'),
+            createRule('4', 'Cherry')
+        ];
+
+        component.searchForm.get('searchRules')?.setValue('Ba');
+        tick(500);
+        fixture.detectChanges();
+
+        expect(fixture.nativeElement.textContent).toContain('Displaying 1 of 
4');
+    }));
+
+    it('should display two matching rules out of four when filtered', 
fakeAsync(() => {
+        component.rules = [
+            createRule('1', 'Apple'),
+            createRule('2', 'Apricot'),
+            createRule('3', 'Banana'),
+            createRule('4', 'Cherry')
+        ];
+
+        component.searchForm.get('searchRules')?.setValue('Ap');
+        tick(500);
+        fixture.detectChanges();
+
+        expect(fixture.nativeElement.textContent).toContain('Displaying 2 of 
4');
+    }));
+
+    it('should display zero matching rules when no rules match the search', 
fakeAsync(() => {
+        component.rules = [
+            createRule('1', 'Apple'),
+            createRule('2', 'Apricot'),
+            createRule('3', 'Banana'),
+            createRule('4', 'Cherry')
+        ];
+
+        component.searchForm.get('searchRules')?.setValue('Plum');
+        tick(500);
+        fixture.detectChanges();
+
+        expect(fixture.nativeElement.textContent).toContain('Displaying 0 of 
4');
+        expect(fixture.nativeElement.textContent).toContain('No matching 
rules');
+    }));
+
+    it('should display nothing when there are no rules', () => {
+        component.rules = [];
+
+        fixture.detectChanges();
+
+        // The empty-list template may not display the count at all.
+        expect(fixture.nativeElement.textContent).not.toContain('Displaying');
+    });
 });

Reply via email to