This is an automated email from the ASF dual-hosted git repository.
zehnder pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/streampipes.git
The following commit(s) were added to refs/heads/dev by this push:
new e48947e15b refacotr(#3116): refactor runtime resolvable tree input
into smaller subcomponents (#3130)
e48947e15b is described below
commit e48947e15b4289807e0306c7300f997f38c74605
Author: Philipp Zehnder <[email protected]>
AuthorDate: Mon Aug 12 18:16:13 2024 +0200
refacotr(#3116): refactor runtime resolvable tree input into smaller
subcomponents (#3130)
* refactor(#3116): Create component for button menu
* refactor(#3116): Move static property utils
* refactor(#3116): Add test to validate tree view of opc ua and buttons
* refactor(#3116): Extract button menu in static-tree-input
* refactor(#3116): Extract component selected-nodes
* refactor(#3116): Fix CSS for component selected-nodes
* refactor(#3116): Fix CSS for component selected-nodes
* refactor(#3116): Harmonized naming of component
* refactor(#3116): Extract static-runtime-resolvable-tree-input component
* refactor(#3116): Add test to show node details
* refactor(#3116): Add component for node details
* refactor(#3116): Add missing headers
* refactor(#3116): Add missing headers
---
ui/cypress/support/general/dataCy.ts | 21 +-
ui/cypress/support/utils/PipelineUtils.ts | 2 +-
ui/cypress/support/utils/connect/ConnectUtils.ts | 2 +-
.../utils/{ => userInput}/StaticPropertyUtils.ts | 17 +-
.../utils/userInput/TreeStaticPropertyUtils.ts | 99 ++++++++
.../tests/connect/opcAdapterConfiguration.spec.ts | 99 ++++++++
.../connectConfigurationTemplate.spec.ts | 2 +-
ui/src/app/core-ui/core-ui.module.ts | 10 +-
.../static-property.component.html | 4 +-
...ic-runtime-resolvable-tree-input.component.html | 59 +++++
...c-runtime-resolvable-tree-input.component.scss} | 28 +--
...tic-runtime-resolvable-tree-input.component.ts} | 119 +++-------
.../static-tree-input-browse-nodes.component.html | 142 ++++++++++++
.../static-tree-input-browse-nodes.component.scss} | 19 +-
.../static-tree-input-browse-nodes.component.ts | 160 +++++++++++++
.../static-tree-input-button-menu.component.html | 49 ++++
.../static-tree-input-button-menu.component.ts} | 33 ++-
.../static-tree-input-node-details.component.html | 29 +++
.../static-tree-input-node-details.component.ts} | 24 +-
...static-tree-input-selected-nodes.component.html | 40 ++++
.../static-tree-input-selected-nodes.component.ts} | 29 +--
.../static-tree-input-service.service.ts} | 29 +--
.../static-tree-input.component.html | 256 ---------------------
23 files changed, 813 insertions(+), 459 deletions(-)
diff --git a/ui/cypress/support/general/dataCy.ts
b/ui/cypress/support/general/dataCy.ts
index ff1d54beae..cb81205295 100644
--- a/ui/cypress/support/general/dataCy.ts
+++ b/ui/cypress/support/general/dataCy.ts
@@ -24,10 +24,19 @@ declare global {
}
}
-export const dataCy = (value: string, config?: any) => {
- if (config) {
- return cy.get(`[data-cy=${value}]`, config);
- } else {
- return cy.get(`[data-cy=${value}]`);
- }
+/**
+ * Selects elements based on the `data-cy` attribute.
+ *
+ * @param {string} value - The value of the `data-cy` attribute to match.
+ * @param {object} [config={}] - Optional configuration object for the Cypress
`get` command.
+ * @param {boolean} [startsWith=false] - If true, selects elements whose
`data-cy` attribute starts with the given value.
+ * @returns {Cypress.Chainable<JQuery<HTMLElement>>} - A chainable Cypress
object containing the matched elements.
+ */
+export const dataCy = (
+ value: string,
+ config: any = {},
+ startsWith: boolean = false,
+) => {
+ const selector = startsWith ? `[data-cy^=${value}]` : `[data-cy=${value}]`;
+ return cy.get(selector, config);
};
diff --git a/ui/cypress/support/utils/PipelineUtils.ts
b/ui/cypress/support/utils/PipelineUtils.ts
index 846dd478ca..b901e225de 100644
--- a/ui/cypress/support/utils/PipelineUtils.ts
+++ b/ui/cypress/support/utils/PipelineUtils.ts
@@ -17,7 +17,7 @@
*/
import { PipelineInput } from '../model/PipelineInput';
-import { StaticPropertyUtils } from './StaticPropertyUtils';
+import { StaticPropertyUtils } from './userInput/StaticPropertyUtils';
import { OutputStrategyUtils } from './OutputStrategyUtils';
import { PipelineElementInput } from '../model/PipelineElementInput';
diff --git a/ui/cypress/support/utils/connect/ConnectUtils.ts
b/ui/cypress/support/utils/connect/ConnectUtils.ts
index ca19602dfb..67556897e9 100644
--- a/ui/cypress/support/utils/connect/ConnectUtils.ts
+++ b/ui/cypress/support/utils/connect/ConnectUtils.ts
@@ -16,7 +16,7 @@
*
*/
-import { StaticPropertyUtils } from '../StaticPropertyUtils';
+import { StaticPropertyUtils } from '../userInput/StaticPropertyUtils';
import { AdapterInput } from '../../model/AdapterInput';
import { ConnectEventSchemaUtils } from './ConnectEventSchemaUtils';
import { DataLakeUtils } from '../datalake/DataLakeUtils';
diff --git a/ui/cypress/support/utils/StaticPropertyUtils.ts
b/ui/cypress/support/utils/userInput/StaticPropertyUtils.ts
similarity index 84%
rename from ui/cypress/support/utils/StaticPropertyUtils.ts
rename to ui/cypress/support/utils/userInput/StaticPropertyUtils.ts
index 25b2d200e5..f588f5db49 100644
--- a/ui/cypress/support/utils/StaticPropertyUtils.ts
+++ b/ui/cypress/support/utils/userInput/StaticPropertyUtils.ts
@@ -16,8 +16,8 @@
*
*/
-import { UserInput } from '../model/UserInput';
-import { TreeNode } from '../model/TreeNode';
+import { UserInput } from '../../model/UserInput';
+import { TreeStaticPropertyUtils } from './TreeStaticPropertyUtils';
export class StaticPropertyUtils {
public static input(configs: UserInput[]) {
@@ -45,7 +45,7 @@ export class StaticPropertyUtils {
} else if (config.type === 'slider') {
cy.dataCy(config.selector).type(config.value);
} else if (config.type === 'tree') {
- this.handleTreeNode(config.treeNode);
+ TreeStaticPropertyUtils.selectTreeNode(config.treeNode);
} else {
cy.dataCy(config.selector).type(config.value);
}
@@ -70,15 +70,4 @@ export class StaticPropertyUtils {
cy.get(cssClassName).click();
});
}
-
- private static handleTreeNode(treeNode: TreeNode) {
- if (treeNode.children && treeNode.children.length > 0) {
- cy.dataCy('expand-' + treeNode.name).click();
- treeNode.children.forEach(child => {
- this.handleTreeNode(child);
- });
- } else {
- cy.dataCy('select-' + treeNode.name).click();
- }
- }
}
diff --git a/ui/cypress/support/utils/userInput/TreeStaticPropertyUtils.ts
b/ui/cypress/support/utils/userInput/TreeStaticPropertyUtils.ts
new file mode 100644
index 0000000000..118a562542
--- /dev/null
+++ b/ui/cypress/support/utils/userInput/TreeStaticPropertyUtils.ts
@@ -0,0 +1,99 @@
+/*
+ * 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 { TreeNode } from '../../model/TreeNode';
+
+export class TreeStaticPropertyUtils {
+ /**
+ * Selects the @param treeNode in the tree view. If the tree node has
+ * children, it will expand the tree node and recursivly navigate through
+ * the selected node.
+ */
+ public static selectTreeNode(treeNode: TreeNode) {
+ if (treeNode.children && treeNode.children.length > 0) {
+ cy.dataCy('expand-' + treeNode.name).click();
+ treeNode.children.forEach(child => {
+ this.selectTreeNode(child);
+ });
+ } else {
+ cy.dataCy('select-' + treeNode.name).click();
+ }
+ }
+
+ /**
+ * Removes the selected node with the identifier @param nodeIdentifier.
+ * dataCy could not be used because often special characters are used in
+ * the nodeIdentifier.
+ */
+ public static removeSelectedNode(nodeIdentifier: string) {
+ cy.get('[data-cy="remove-' + nodeIdentifier + '"]').click();
+ }
+
+ /**
+ * Validates that the amount of nodes shown in the selected tab are equal
+ * to @param expectedAmount.
+ */
+ public static validateAmountOfSelectedNodes(expectedAmount: number) {
+ cy.dataCy('selected-node-', {}, true).should(
+ 'have.length',
+ expectedAmount,
+ );
+ }
+
+ /**
+ * Validates the number of node details metadata rows displayed.
+ */
+ public static validateAmountOfShownNodeDetailsMetaDataRows(
+ expectedAmount: number,
+ ) {
+ cy.dataCy('node-details-metadata-row-', {}, true).should(
+ 'have.length',
+ expectedAmount,
+ );
+ }
+
+ /**
+ * Select node to be shown in node details
+ */
+ public static showNodeDetails(nodeName: string) {
+ cy.dataCy(`show-node-details-${nodeName}`).click();
+ }
+
+ /**
+ * Unselect the node to be removed from node details view
+ */
+ public static hideNodeDetails(nodeName: string) {
+ cy.dataCy('hide-node-details-' + nodeName).click();
+ }
+
+ /**
+ * Validates that the @param nodeName is marked as selected in the
+ * tree view.
+ */
+ public static checkThatNodeIsSelectedInTree(nodeName: string) {
+ cy.dataCy('tree-node-' + nodeName).within(() => {
+ cy.get('i.material-icons')
+ .contains('remove_circle')
+ .should('exist');
+ });
+ }
+
+ public static clickClearAndReloadButton() {
+ cy.dataCy('clear-tree-node-selection').click();
+ }
+}
diff --git a/ui/cypress/tests/connect/opcAdapterConfiguration.spec.ts
b/ui/cypress/tests/connect/opcAdapterConfiguration.spec.ts
new file mode 100644
index 0000000000..57b10bd41a
--- /dev/null
+++ b/ui/cypress/tests/connect/opcAdapterConfiguration.spec.ts
@@ -0,0 +1,99 @@
+/*
+ * 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 { ConnectUtils } from '../../support/utils/connect/ConnectUtils';
+import { ParameterUtils } from '../../support/utils/ParameterUtils';
+import { AdapterBuilder } from '../../support/builder/AdapterBuilder';
+import { TreeNodeBuilder } from '../../support/builder/TreeNodeBuilder';
+import { StaticPropertyUtils } from
'../../support/utils/userInput/StaticPropertyUtils';
+import { TreeStaticPropertyUtils } from
'../../support/utils/userInput/TreeStaticPropertyUtils';
+
+describe('Test OPC-UA Adapter Pull Mode', () => {
+ beforeEach('Setup Test', () => {
+ cy.initStreamPipesTest();
+ });
+
+ it('Test OPC-UA Adapter Pull Mode', () => {
+ const adapterConfiguration = getAdapterBuilder();
+
+ // Set up initial configuration
+ ConnectUtils.goToConnect();
+ ConnectUtils.goToNewAdapterPage();
+ ConnectUtils.selectAdapter(adapterConfiguration.adapterType);
+ StaticPropertyUtils.input(adapterConfiguration.adapterConfiguration);
+
+ TreeStaticPropertyUtils.validateAmountOfSelectedNodes(2);
+
+ TreeStaticPropertyUtils.checkThatNodeIsSelectedInTree(
+ 'AlternatingBoolean',
+ );
+
+ // Test if node details view works
+
TreeStaticPropertyUtils.validateAmountOfShownNodeDetailsMetaDataRows(0);
+ TreeStaticPropertyUtils.showNodeDetails('StepUp');
+ TreeStaticPropertyUtils.validateAmountOfShownNodeDetailsMetaDataRows(
+ 10,
+ );
+ TreeStaticPropertyUtils.hideNodeDetails('StepUp');
+
TreeStaticPropertyUtils.validateAmountOfShownNodeDetailsMetaDataRows(0);
+
+ // Test if delete node works
+ TreeStaticPropertyUtils.removeSelectedNode(
+ 'ns=3\\;s=AlternatingBoolean',
+ );
+ TreeStaticPropertyUtils.validateAmountOfSelectedNodes(1);
+
+ // Test clear selection and reload button
+ TreeStaticPropertyUtils.clickClearAndReloadButton();
+ TreeStaticPropertyUtils.validateAmountOfSelectedNodes(0);
+ });
+});
+
+const getAdapterBuilder = () => {
+ const host: string = ParameterUtils.get('localhost', 'opcua');
+
+ const builder = AdapterBuilder.create('OPC_UA')
+ .setName('OPC UA Configuration Test')
+ .addInput('radio', 'adapter_type-pull_mode', '')
+ .addInput('input', 'undefined-PULLING_INTERVAL-0', '1000')
+ .addInput('radio', 'access_mode-none', '')
+ .addInput('radio', 'opc_host_or_url-url', '')
+ .addInput(
+ 'input',
+ 'undefined-OPC_SERVER_URL-0',
+ 'opc.tcp://' + host + ':50000',
+ )
+ .addTreeNode(
+ TreeNodeBuilder.create(
+ 'Objects',
+ TreeNodeBuilder.create(
+ 'OpcPlc',
+ TreeNodeBuilder.create(
+ 'Telemetry',
+ TreeNodeBuilder.create('Basic').addChildren(
+ TreeNodeBuilder.create('AlternatingBoolean'),
+ TreeNodeBuilder.create('StepUp'),
+ ),
+ ),
+ ),
+ ),
+ )
+ .setAutoAddTimestampPropery();
+
+ return builder.build();
+};
diff --git
a/ui/cypress/tests/pipelineElementConfigurationTemplate/connectConfigurationTemplate.spec.ts
b/ui/cypress/tests/pipelineElementConfigurationTemplate/connectConfigurationTemplate.spec.ts
index 76eb8d8e56..17b55649a3 100644
---
a/ui/cypress/tests/pipelineElementConfigurationTemplate/connectConfigurationTemplate.spec.ts
+++
b/ui/cypress/tests/pipelineElementConfigurationTemplate/connectConfigurationTemplate.spec.ts
@@ -17,7 +17,7 @@
*/
import { ConnectUtils } from '../../support/utils/connect/ConnectUtils';
-import { StaticPropertyUtils } from '../../support/utils/StaticPropertyUtils';
+import { StaticPropertyUtils } from
'../../support/utils/userInput/StaticPropertyUtils';
import { AdapterBuilder } from '../../support/builder/AdapterBuilder';
import { PipelineElementTemplateUtils } from
'../../support/utils/PipelineElementTemplateUtils';
diff --git a/ui/src/app/core-ui/core-ui.module.ts
b/ui/src/app/core-ui/core-ui.module.ts
index 85c08d4157..8b589c4b18 100644
--- a/ui/src/app/core-ui/core-ui.module.ts
+++ b/ui/src/app/core-ui/core-ui.module.ts
@@ -58,7 +58,7 @@ import { PipelineStartedStatusComponent } from
'./pipeline/pipeline-started-stat
import { ObjectPermissionDialogComponent } from
'./object-permission-dialog/object-permission-dialog.component';
import { StaticSlideToggleComponent } from
'./static-properties/static-slide-toggle/static-slide-toggle.component';
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
-import { StaticRuntimeResolvableTreeInputComponent } from
'./static-properties/static-runtime-resolvable-tree-input/static-tree-input.component';
+import { StaticRuntimeResolvableTreeInputComponent } from
'./static-properties/static-runtime-resolvable-tree-input/static-runtime-resolvable-tree-input.component';
import { MatTreeModule } from '@angular/material/tree';
import { PlatformServicesModule } from '@streampipes/platform-services';
import { SharedUiModule } from '@streampipes/shared-ui';
@@ -109,6 +109,10 @@ import { LoadingIndicatorComponent } from
'./loading-indicator/loading-indicator
import { StatusIndicatorComponent } from
'./status-indicator/status-indicator.component';
import { MultiStepStatusIndicatorComponent } from
'./multi-step-status-indicator/multi-step-status-indicator.component';
import { PipelineOperationStatusComponent } from
'./pipeline/pipeline-operation-status/pipeline-operation-status.component';
+import { StaticTreeInputButtonMenuComponent } from
'./static-properties/static-runtime-resolvable-tree-input/static-tree-input-button-menu/static-tree-input-button-menu.component';
+import { StaticTreeInputSelectedNodesComponent } from
'./static-properties/static-runtime-resolvable-tree-input/static-tree-input-selected-nodes/static-tree-input-selected-nodes.component';
+import { StaticTreeInputBrowseNodesComponent } from
'./static-properties/static-runtime-resolvable-tree-input/static-tree-input-browse-nodes/static-tree-input-browse-nodes.component';
+import { StaticTreeInputNodeDetailsComponent } from
'./static-properties/static-runtime-resolvable-tree-input/static-tree-input-node-details/static-tree-input-node-details.component';
@NgModule({
imports: [
@@ -183,9 +187,13 @@ import { PipelineOperationStatusComponent } from
'./pipeline/pipeline-operation-
StaticCodeInputComponent,
StaticOneOfInputComponent,
StaticRuntimeResolvableAnyInputComponent,
+ StaticTreeInputButtonMenuComponent,
+ StaticTreeInputSelectedNodesComponent,
StaticRuntimeResolvableGroupComponent,
StaticRuntimeResolvableOneOfInputComponent,
StaticRuntimeResolvableTreeInputComponent,
+ StaticTreeInputBrowseNodesComponent,
+ StaticTreeInputNodeDetailsComponent,
StaticSlideToggleComponent,
ErrorHintComponent,
AddToCollectionComponent,
diff --git
a/ui/src/app/core-ui/static-properties/static-property.component.html
b/ui/src/app/core-ui/static-properties/static-property.component.html
index 4ecbd7c49b..e416735c84 100644
--- a/ui/src/app/core-ui/static-properties/static-property.component.html
+++ b/ui/src/app/core-ui/static-properties/static-property.component.html
@@ -231,7 +231,7 @@
(updateEmitter)="emitUpdate($event)"
>
</sp-static-slide-toggle>
- <sp-runtime-resolvable-tree-input
+ <sp-static-runtime-resolvable-tree-input
*ngIf="isTreeInputStaticProperty(staticProperty)"
[deploymentConfiguration]="deploymentConfiguration"
[adapterId]="adapterId"
@@ -246,7 +246,7 @@
class="test fullWidth"
(updateEmitter)="emitUpdate($event)"
>
- </sp-runtime-resolvable-tree-input>
+ </sp-static-runtime-resolvable-tree-input>
</div>
</div>
<mat-divider></mat-divider>
diff --git
a/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-runtime-resolvable-tree-input.component.html
b/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-runtime-resolvable-tree-input.component.html
new file mode 100644
index 0000000000..bcb41d33a8
--- /dev/null
+++
b/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-runtime-resolvable-tree-input.component.html
@@ -0,0 +1,59 @@
+<!--
+ ~ 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 [formGroup]="parentForm" id="formWrapper" fxFlex="100" fxLayout="column">
+ <sp-static-tree-input-button-menu
+ [showOptions]="showOptions"
+ [loading]="loading"
+ (resetOptionsAndReload)="resetOptionsAndReload()"
+ (reload)="reload()"
+ >
+ </sp-static-tree-input-button-menu>
+
+ <div fxLayout="column" *ngIf="error" class="mt-10">
+ <sp-exception-message [message]="errorMessage"></sp-exception-message>
+ </div>
+
+ <div fxLayout="row" fxLayoutGap="15px">
+ <div fxFlex="30" class="tree-input-section" fxLayout="column">
+ <sp-static-tree-input-browse-nodes
+ #staticTreeInputBrowseNodesComponent
+ [staticProperty]="staticProperty"
+ (showNodeDetailsEmitter)="showNodeDetails($event)"
+
(loadOptionsFromRestApiEmitter)="loadOptionsFromRestApi($event)"
+ (performValidationEmitter)="performValidation()"
+ >
+ </sp-static-tree-input-browse-nodes>
+ </div>
+ <div fxFlex="40" class="tree-input-section" fxLayout="column">
+ <sp-static-tree-input-node-details
+ [nodeMetadata]="nodeDetails?.nodeMetadata"
+ >
+ </sp-static-tree-input-node-details>
+ </div>
+
+ <div class="tree-input-section" fxLayout="column" fxFlex="30">
+ <sp-static-tree-input-selected-nodes
+ [selectedNodesInternalNames]="
+ staticProperty.selectedNodesInternalNames
+ "
+ (removeSelectedNode)="removeSelectedNode($event)"
+ >
+ </sp-static-tree-input-selected-nodes>
+ </div>
+ </div>
+</div>
diff --git
a/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input.component.scss
b/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-runtime-resolvable-tree-input.component.scss
similarity index 85%
rename from
ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input.component.scss
rename to
ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-runtime-resolvable-tree-input.component.scss
index 4cb8975c33..ac5d7522bf 100644
---
a/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input.component.scss
+++
b/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-runtime-resolvable-tree-input.component.scss
@@ -54,6 +54,11 @@
min-height: 30px;
}
+.tree-input-section {
+ border: 1px solid var(--color-bg-3);
+ margin-top: 10px;
+}
+
.tree-input-header {
background: var(--color-bg-2);
height: 40px;
@@ -61,11 +66,6 @@
border-bottom: 1px solid var(--color-bg-3);
}
-.tree-input-section {
- border: 1px solid var(--color-bg-3);
- margin-top: 10px;
-}
-
.color-primary {
color: var(--color-primary);
}
@@ -84,21 +84,3 @@
.node-name {
font-weight: bold;
}
-
-.selected-node {
- padding: 10px;
- border-bottom: 1px solid var(--color-bg-3);
-}
-
-.selected-node:nth-child(odd) {
- background: var(--color-bg-1);
-}
-
-.selected-node:nth-child(even) {
- background: var(--color-bg-2);
-}
-
-.selected-node {
- padding: 5px;
- border: 1px solid var(--color-bg-3);
-}
diff --git
a/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input.component.ts
b/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-runtime-resolvable-tree-input.component.ts
similarity index 55%
rename from
ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input.component.ts
rename to
ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-runtime-resolvable-tree-input.component.ts
index 1f7dbf2010..32f77cfb1a 100644
---
a/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input.component.ts
+++
b/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-runtime-resolvable-tree-input.component.ts
@@ -24,42 +24,32 @@ import {
TreeInputNode,
} from '@streampipes/platform-services';
import { RuntimeResolvableService } from
'../static-runtime-resolvable-input/runtime-resolvable.service';
-import { NestedTreeControl } from '@angular/cdk/tree';
-import { MatTree, MatTreeNestedDataSource } from '@angular/material/tree';
import { UntypedFormControl } from '@angular/forms';
-import Tree from 'echarts/types/src/data/Tree';
+import { StaticTreeInputServiceService } from
'./static-tree-input-service.service';
+import { StaticTreeInputBrowseNodesComponent } from
'./static-tree-input-browse-nodes/static-tree-input-browse-nodes.component';
@Component({
- selector: 'sp-runtime-resolvable-tree-input',
- templateUrl: './static-tree-input.component.html',
- styleUrls: ['./static-tree-input.component.scss'],
+ selector: 'sp-static-runtime-resolvable-tree-input',
+ templateUrl: './static-runtime-resolvable-tree-input.component.html',
+ styleUrls: ['./static-runtime-resolvable-tree-input.component.scss'],
})
export class StaticRuntimeResolvableTreeInputComponent
extends
BaseRuntimeResolvableInput<RuntimeResolvableTreeInputStaticProperty>
implements OnInit
{
- treeControl = new NestedTreeControl<TreeInputNode>(node => node.children);
- dataSource = new MatTreeNestedDataSource<TreeInputNode>();
+ nodeDetails: TreeInputNode;
- selectedNodeMetadata: Record<string, string>;
- selectedNodeId: string;
+ @ViewChild('staticTreeInputBrowseNodesComponent')
+ staticTreeInputBrowseNodesComponent: StaticTreeInputBrowseNodesComponent;
- largeView = false;
-
- @ViewChild('tree') tree: MatTree<TreeInputNode>;
-
- constructor(runtimeResolvableService: RuntimeResolvableService) {
+ constructor(
+ runtimeResolvableService: RuntimeResolvableService,
+ private staticTreeInputServiceService: StaticTreeInputServiceService,
+ ) {
super(runtimeResolvableService);
}
- hasChild = (_: number, node: TreeInputNode) => !node.dataNode;
-
ngOnInit(): void {
- this.treeControl = new NestedTreeControl<TreeInputNode>(
- node => node.children,
- );
- this.dataSource = new MatTreeNestedDataSource<TreeInputNode>();
-
if (
this.staticProperty.nodes.length === 0 &&
(!this.staticProperty.dependsOn ||
@@ -67,7 +57,9 @@ export class StaticRuntimeResolvableTreeInputComponent
) {
this.loadOptionsFromRestApi();
} else if (this.staticProperty.nodes.length > 0) {
- this.dataSource.data = this.staticProperty.nodes;
+ this.staticTreeInputBrowseNodesComponent.updateNodes(
+ this.staticProperty.nodes,
+ );
this.showOptions = true;
}
super.onInit();
@@ -98,12 +90,12 @@ export class StaticRuntimeResolvableTreeInputComponent
}
} else {
this.staticProperty.nodes = staticProperty.nodes;
- this.dataSource.data = this.staticProperty.nodes;
+ this.staticTreeInputBrowseNodesComponent.updateNodes(
+ this.staticProperty.nodes,
+ );
}
- const data = this.dataSource.data.slice();
- this.dataSource.data = null;
- this.dataSource = new MatTreeNestedDataSource<TreeInputNode>();
- this.dataSource.data = [...data];
+ this.staticTreeInputBrowseNodesComponent.refreshTree();
+
this.performValidation();
}
@@ -131,15 +123,19 @@ export class StaticRuntimeResolvableTreeInputComponent
afterErrorReceived() {
this.staticProperty.nodes = [];
- this.dataSource.data = [];
+ this.staticTreeInputBrowseNodesComponent.updateNodes([]);
this.performValidation();
}
+ showNodeDetails(node: TreeInputNode) {
+ this.nodeDetails = node;
+ }
+
resetOptionsAndReload(): void {
this.staticProperty.nextBaseNodeToResolve = undefined;
this.staticProperty.selectedNodesInternalNames = [];
this.staticProperty.latestFetchedNodes = [];
- this.dataSource.data = [];
+ this.staticTreeInputBrowseNodesComponent.updateNodes([]);
this.loadOptionsFromRestApi();
}
@@ -147,66 +143,11 @@ export class StaticRuntimeResolvableTreeInputComponent
this.loadOptionsFromRestApi();
}
- loadChildren(node: TreeInputNode, expanded: boolean): void {
- this.staticProperty.nextBaseNodeToResolve = node.internalNodeName;
- if (expanded) {
- this.loadOptionsFromRestApi(node);
- }
- }
-
- addNode(node: TreeInputNode) {
- node.selected = true;
- this.staticProperty.selectedNodesInternalNames.push(
- node.internalNodeName,
- );
- this.performValidation();
- }
-
- addAllDirectChildren(node: TreeInputNode) {
- node.children.forEach(child => {
- if (child.dataNode && !this.existsSelectedNode(child)) {
- this.staticProperty.selectedNodesInternalNames.push(
- child.internalNodeName,
- );
- }
- });
- this.performValidation();
- }
-
- existsSelectedNode(node: TreeInputNode) {
- return (
- this.staticProperty.selectedNodesInternalNames.find(
- nodeName => nodeName === node.internalNodeName,
- ) !== undefined
- );
- }
-
- removeNode(node: TreeInputNode) {
- node.selected = false;
- const index = this.getSelectedNodeIndex(node.internalNodeName);
- this.staticProperty.selectedNodesInternalNames.splice(index, 1);
- this.performValidation();
- }
-
removeSelectedNode(selectedNodeInternalId: string): void {
- const index = this.getSelectedNodeIndex(selectedNodeInternalId);
- this.staticProperty.selectedNodesInternalNames.splice(index, 1);
- }
-
- isNodeSelected(node: TreeInputNode) {
- return this.getSelectedNodeIndex(node.internalNodeName) > -1;
- }
-
- getSelectedNodeIndex(internalNodeName: string) {
- return this.staticProperty.selectedNodesInternalNames.indexOf(
- internalNodeName,
- );
- }
-
- hasDataChildren(node: TreeInputNode) {
- return (
- node.children.length > 0 &&
- node.children.find(c => c.dataNode) !== undefined
+ const index = this.staticTreeInputServiceService.getSelectedNodeIndex(
+ this.staticProperty,
+ selectedNodeInternalId,
);
+ this.staticProperty.selectedNodesInternalNames.splice(index, 1);
}
}
diff --git
a/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input-browse-nodes/static-tree-input-browse-nodes.component.html
b/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input-browse-nodes/static-tree-input-browse-nodes.component.html
new file mode 100644
index 0000000000..0f3957558e
--- /dev/null
+++
b/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input-browse-nodes/static-tree-input-browse-nodes.component.html
@@ -0,0 +1,142 @@
+<!--
+~ 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="tree-input-header" fxLayoutAlign="start center">
+ <div fxLayout="row" fxFlex="100">
+ <div fxFlex fxLayoutAlign="start center"><b>Browse</b></div>
+ <div fxLayoutAlign="end center">
+ <button
+ mat-icon-button
+ color="accent"
+ (click)="largeView = !largeView"
+ >
+ <mat-icon *ngIf="!largeView">open_in_full</mat-icon>
+ <mat-icon *ngIf="largeView">close_fullscreen </mat-icon>
+ </button>
+ </div>
+ </div>
+</div>
+
+<mat-tree
+ [dataSource]="dataSource"
+ [treeControl]="treeControl"
+ #tree
+ class="sp-tree"
+ [ngClass]="largeView ? 'tree-large-height' : 'tree-normal-height'"
+>
+ <mat-tree-node *matTreeNodeDef="let node" matTreeNodeToggle>
+ <div
+ fxLayoutAlign="start center"
+ [attr.data-cy]="'tree-node-' + node.nodeName"
+ >
+ <i class="material-icons color-primary pr-5">wifi</i>
+ <i
+ class="material-icons pr-5 icon-button"
+ *ngIf="node.dataNode && !isNodeSelected(node)"
+ (click)="addNode(node)"
+ [attr.data-cy]="'select-' + node.nodeName"
+ matTooltip="Add node"
+ >add_circle</i
+ >
+ <i
+ class="material-icons pr-5 icon-button"
+ *ngIf="node.dataNode && isNodeSelected(node)"
+ (click)="removeNode(node)"
+ matTooltip="Remove node"
+ >remove_circle</i
+ >
+ <i
+ class="material-icons pr-5 icon-button"
+ *ngIf="selectedNodeId !== node.internalNodeName"
+ [attr.data-cy]="'show-node-details-' + node.nodeName"
+ (click)="showNodeDetails(node)"
+ matTooltip="Show details"
+ >visibility</i
+ >
+ <i
+ class="material-icons pr-5 icon-button"
+ *ngIf="selectedNodeId === node.internalNodeName"
+ [attr.data-cy]="'hide-node-details-' + node.nodeName"
+ (click)="hideNodeDetails()"
+ matTooltip="Show details"
+ >visibility_off</i
+ >
+ <span class="pr-5">{{ node.nodeName }}</span>
+ </div>
+ </mat-tree-node>
+ <mat-nested-tree-node *matTreeNodeDef="let node; when: hasChild">
+ <div class="mat-tree-node">
+ <button
+ mat-icon-button
+ matTreeNodeToggle
+ [attr.data-cy]="'expand-' + node.nodeName"
+ [attr.aria-label]="'Toggle ' + node.nodeName"
+ (click)="loadChildren(node, treeControl.isExpanded(node))"
+ >
+ <mat-icon class="mat-icon-rtl-mirror">
+ {{
+ treeControl.isExpanded(node)
+ ? 'expand_more'
+ : 'chevron_right'
+ }}
+ </mat-icon>
+ </button>
+ <div fxLayoutAlign="start center">
+ <i
+ class="material-icons color-primary pr-5"
+ *ngIf="!treeControl.isExpanded(node)"
+ >folder</i
+ >
+ <i
+ class="material-icons color-primary pr-5"
+ *ngIf="treeControl.isExpanded(node)"
+ >folder_open</i
+ >
+ <i
+ class="material-icons pr-5 icon-button"
+ *ngIf="
+ treeControl.isExpanded(node) && hasDataChildren(node)
+ "
+ matTooltip="Add all direct children"
+ (click)="addAllDirectChildren(node)"
+ >add_circle</i
+ >
+ <i
+ class="material-icons pr-5 icon-button"
+ *ngIf="selectedNodeId !== node.internalNodeName"
+ [attr.data-cy]="'show-node-details-' + node.nodeName"
+ (click)="showNodeDetails(node)"
+ matTooltip="Show details"
+ >visibility</i
+ >
+ <i
+ class="material-icons pr-5 icon-button"
+ *ngIf="selectedNodeId === node.internalNodeName"
+ [attr.data-cy]="'hide-node-details-' + node.nodeName"
+ (click)="hideNodeDetails()"
+ matTooltip="Show details"
+ >visibility_off</i
+ >
+ <span class="node-name pr-5"> {{ node.nodeName }}</span>
+ </div>
+ </div>
+ <div *ngIf="treeControl.isExpanded(node)" role="group">
+ <ng-container matTreeNodeOutlet></ng-container>
+ </div>
+ </mat-nested-tree-node>
+</mat-tree>
diff --git a/ui/cypress/support/general/dataCy.ts
b/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input-browse-nodes/static-tree-input-browse-nodes.component.scss
similarity index 71%
copy from ui/cypress/support/general/dataCy.ts
copy to
ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input-browse-nodes/static-tree-input-browse-nodes.component.scss
index ff1d54beae..949b23cb05 100644
--- a/ui/cypress/support/general/dataCy.ts
+++
b/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input-browse-nodes/static-tree-input-browse-nodes.component.scss
@@ -16,18 +16,11 @@
*
*/
-declare global {
- namespace Cypress {
- interface Chainable {
- dataCy: typeof dataCy;
- }
- }
+.tree-normal-height {
+ min-height: 300px;
+ max-height: 300px;
}
-export const dataCy = (value: string, config?: any) => {
- if (config) {
- return cy.get(`[data-cy=${value}]`, config);
- } else {
- return cy.get(`[data-cy=${value}]`);
- }
-};
+.tree-large-height {
+ min-height: 800px;
+}
diff --git
a/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input-browse-nodes/static-tree-input-browse-nodes.component.ts
b/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input-browse-nodes/static-tree-input-browse-nodes.component.ts
new file mode 100644
index 0000000000..de04432f80
--- /dev/null
+++
b/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input-browse-nodes/static-tree-input-browse-nodes.component.ts
@@ -0,0 +1,160 @@
+/*
+ * 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,
+ EventEmitter,
+ Input,
+ OnInit,
+ Output,
+ ViewChild,
+} from '@angular/core';
+import { NestedTreeControl } from '@angular/cdk/tree';
+import {
+ RuntimeResolvableTreeInputStaticProperty,
+ TreeInputNode,
+} from '@streampipes/platform-services';
+import { MatTree, MatTreeNestedDataSource } from '@angular/material/tree';
+import { StaticTreeInputServiceService } from
'../static-tree-input-service.service';
+
+@Component({
+ selector: 'sp-static-tree-input-browse-nodes',
+ templateUrl: './static-tree-input-browse-nodes.component.html',
+ styleUrls: [
+ './static-tree-input-browse-nodes.component.scss',
+ '../static-runtime-resolvable-tree-input.component.scss',
+ ],
+})
+export class StaticTreeInputBrowseNodesComponent implements OnInit {
+ @Input()
+ staticProperty: RuntimeResolvableTreeInputStaticProperty;
+
+ @Output()
+ showNodeDetailsEmitter: EventEmitter<TreeInputNode> =
+ new EventEmitter<TreeInputNode>();
+
+ @Output()
+ performValidationEmitter: EventEmitter<void> = new EventEmitter<void>();
+
+ @Output()
+ loadOptionsFromRestApiEmitter: EventEmitter<TreeInputNode> =
+ new EventEmitter<TreeInputNode>();
+
+ @ViewChild('tree')
+ tree: MatTree<TreeInputNode>;
+
+ largeView = false;
+ treeControl = new NestedTreeControl<TreeInputNode>(node => node.children);
+ dataSource = new MatTreeNestedDataSource<TreeInputNode>();
+
+ selectedNodeId: string;
+
+ hasChild = (_: number, node: TreeInputNode) => !node.dataNode;
+
+ constructor(
+ private staticTreeInputServiceService: StaticTreeInputServiceService,
+ ) {}
+
+ ngOnInit(): void {
+ this.treeControl = new NestedTreeControl<TreeInputNode>(
+ node => node.children,
+ );
+
+ this.dataSource = new MatTreeNestedDataSource<TreeInputNode>();
+ }
+
+ updateNodes(nodes: TreeInputNode[]) {
+ this.dataSource.data = nodes;
+ }
+
+ refreshTree() {
+ const data = this.dataSource.data.slice();
+ this.dataSource.data = null;
+ this.dataSource = new MatTreeNestedDataSource<TreeInputNode>();
+ this.dataSource.data = [...data];
+ }
+
+ loadChildren(node: TreeInputNode, expanded: boolean): void {
+ this.staticProperty.nextBaseNodeToResolve = node.internalNodeName;
+ if (expanded) {
+ this.loadOptionsFromRestApiEmitter.emit(node);
+ }
+ }
+
+ addNode(node: TreeInputNode) {
+ node.selected = true;
+ this.staticProperty.selectedNodesInternalNames.push(
+ node.internalNodeName,
+ );
+ this.performValidationEmitter.emit();
+ }
+
+ addAllDirectChildren(node: TreeInputNode) {
+ node.children.forEach(child => {
+ if (child.dataNode && !this.existsSelectedNode(child)) {
+ this.staticProperty.selectedNodesInternalNames.push(
+ child.internalNodeName,
+ );
+ }
+ });
+ this.performValidationEmitter.emit();
+ }
+
+ removeNode(node: TreeInputNode) {
+ node.selected = false;
+ const index = this.staticTreeInputServiceService.getSelectedNodeIndex(
+ this.staticProperty,
+ node.internalNodeName,
+ );
+ this.staticProperty.selectedNodesInternalNames.splice(index, 1);
+ this.performValidationEmitter.emit();
+ }
+
+ isNodeSelected(node: TreeInputNode) {
+ return (
+ this.staticTreeInputServiceService.getSelectedNodeIndex(
+ this.staticProperty,
+ node.internalNodeName,
+ ) > -1
+ );
+ }
+
+ showNodeDetails(node: TreeInputNode) {
+ this.selectedNodeId = node.internalNodeName;
+ this.showNodeDetailsEmitter.emit(node);
+ }
+
+ hideNodeDetails() {
+ this.selectedNodeId = undefined;
+ this.showNodeDetailsEmitter.emit(undefined);
+ }
+
+ hasDataChildren(node: TreeInputNode) {
+ return (
+ node.children.length > 0 &&
+ node.children.find(c => c.dataNode) !== undefined
+ );
+ }
+
+ existsSelectedNode(node: TreeInputNode) {
+ return (
+ this.staticProperty.selectedNodesInternalNames.find(
+ nodeName => nodeName === node.internalNodeName,
+ ) !== undefined
+ );
+ }
+}
diff --git
a/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input-button-menu/static-tree-input-button-menu.component.html
b/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input-button-menu/static-tree-input-button-menu.component.html
new file mode 100644
index 0000000000..bf008690bb
--- /dev/null
+++
b/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input-button-menu/static-tree-input-button-menu.component.html
@@ -0,0 +1,49 @@
+<!--
+ ~ 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 fxLayout="row" fxLayoutGap="10px" fxLayoutAlign="start center">
+ <div>
+ <button
+ mat-raised-button
+ color="accent"
+ class="small-button"
+ (click)="onResetOptionsAndReload()"
+ style="margin-right: 10px"
+ [disabled]="!showOptions"
+ data-cy="clear-tree-node-selection"
+ >
+ <span>Clear selection & reload</span>
+ </button>
+ <button
+ mat-raised-button
+ color="accent"
+ class="small-button"
+ (click)="onReload()"
+ style="margin-right: 10px"
+ [disabled]="!showOptions"
+ >
+ <span>Reload</span>
+ </button>
+ </div>
+ <div fxLayout="column" *ngIf="loading" class="mt-10">
+ <mat-spinner
+ color="accent"
+ [mode]="'indeterminate'"
+ [diameter]="20"
+ ></mat-spinner>
+ </div>
+</div>
diff --git a/ui/cypress/support/general/dataCy.ts
b/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input-button-menu/static-tree-input-button-menu.component.ts
similarity index 58%
copy from ui/cypress/support/general/dataCy.ts
copy to
ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input-button-menu/static-tree-input-button-menu.component.ts
index ff1d54beae..8249baebb3 100644
--- a/ui/cypress/support/general/dataCy.ts
+++
b/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input-button-menu/static-tree-input-button-menu.component.ts
@@ -15,19 +15,28 @@
* limitations under the License.
*
*/
+import { Component, EventEmitter, Input, Output } from '@angular/core';
-declare global {
- namespace Cypress {
- interface Chainable {
- dataCy: typeof dataCy;
- }
+@Component({
+ selector: 'sp-static-tree-input-button-menu',
+ templateUrl: './static-tree-input-button-menu.component.html',
+})
+export class StaticTreeInputButtonMenuComponent {
+ @Input()
+ showOptions: boolean;
+ @Input()
+ loading: boolean;
+
+ @Output()
+ resetOptionsAndReload = new EventEmitter<void>();
+ @Output()
+ reload = new EventEmitter<void>();
+
+ onResetOptionsAndReload() {
+ this.resetOptionsAndReload.emit();
}
-}
-export const dataCy = (value: string, config?: any) => {
- if (config) {
- return cy.get(`[data-cy=${value}]`, config);
- } else {
- return cy.get(`[data-cy=${value}]`);
+ onReload() {
+ this.reload.emit();
}
-};
+}
diff --git
a/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input-node-details/static-tree-input-node-details.component.html
b/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input-node-details/static-tree-input-node-details.component.html
new file mode 100644
index 0000000000..15d9b37faf
--- /dev/null
+++
b/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input-node-details/static-tree-input-node-details.component.html
@@ -0,0 +1,29 @@
+<!--
+ ~ 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="tree-input-header" fxLayoutAlign="start center">
+ <b>Node Details</b>
+</div>
+<div
+ class="node-metadata"
+ *ngFor="let metadata of nodeMetadata | keyvalue"
+ fxLayout="row"
+ [attr.data-cy]="'node-details-metadata-row-' + metadata.key"
+>
+ <div fxFlex="30">{{ metadata.key }}</div>
+ <div fxFlex="70">{{ metadata.value }}</div>
+</div>
diff --git a/ui/cypress/support/general/dataCy.ts
b/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input-node-details/static-tree-input-node-details.component.ts
similarity index 68%
copy from ui/cypress/support/general/dataCy.ts
copy to
ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input-node-details/static-tree-input-node-details.component.ts
index ff1d54beae..eb60640d75 100644
--- a/ui/cypress/support/general/dataCy.ts
+++
b/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input-node-details/static-tree-input-node-details.component.ts
@@ -16,18 +16,14 @@
*
*/
-declare global {
- namespace Cypress {
- interface Chainable {
- dataCy: typeof dataCy;
- }
- }
-}
+import { Component, Input } from '@angular/core';
-export const dataCy = (value: string, config?: any) => {
- if (config) {
- return cy.get(`[data-cy=${value}]`, config);
- } else {
- return cy.get(`[data-cy=${value}]`);
- }
-};
+@Component({
+ selector: 'sp-static-tree-input-node-details',
+ templateUrl: './static-tree-input-node-details.component.html',
+ styleUrl: '../static-runtime-resolvable-tree-input.component.scss',
+})
+export class StaticTreeInputNodeDetailsComponent {
+ @Input()
+ nodeMetadata: { [index: string]: any };
+}
diff --git
a/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input-selected-nodes/static-tree-input-selected-nodes.component.html
b/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input-selected-nodes/static-tree-input-selected-nodes.component.html
new file mode 100644
index 0000000000..968e782252
--- /dev/null
+++
b/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input-selected-nodes/static-tree-input-selected-nodes.component.html
@@ -0,0 +1,40 @@
+<!--
+ ~ 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="tree-input-header" fxLayoutAlign="start center">
+ <b>Selected Nodes</b>
+</div>
+<div *ngFor="let selectedNodeName of selectedNodesInternalNames">
+ <div
+ class="selected-node"
+ fxLayout="row"
+ fxLayoutAlign="start center"
+ [attr.data-cy]="'selected-node-' + selectedNodeName"
+ >
+ <span fxFlex>{{ selectedNodeName }}</span>
+ <div fxLayoutAlign="end center">
+ <button
+ mat-icon-button
+ color="accent"
+ [attr.data-cy]="'remove-' + selectedNodeName"
+ (click)="onRemoveSelectedNode(selectedNodeName)"
+ >
+ <i class="material-icons">remove</i>
+ </button>
+ </div>
+ </div>
+</div>
diff --git a/ui/cypress/support/general/dataCy.ts
b/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input-selected-nodes/static-tree-input-selected-nodes.component.ts
similarity index 58%
copy from ui/cypress/support/general/dataCy.ts
copy to
ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input-selected-nodes/static-tree-input-selected-nodes.component.ts
index ff1d54beae..d81825c36f 100644
--- a/ui/cypress/support/general/dataCy.ts
+++
b/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input-selected-nodes/static-tree-input-selected-nodes.component.ts
@@ -16,18 +16,21 @@
*
*/
-declare global {
- namespace Cypress {
- interface Chainable {
- dataCy: typeof dataCy;
- }
- }
-}
+import { Component, EventEmitter, Input, Output } from '@angular/core';
+
+@Component({
+ selector: 'sp-static-tree-input-selected-nodes',
+ templateUrl: './static-tree-input-selected-nodes.component.html',
+ styleUrl: '../static-runtime-resolvable-tree-input.component.scss',
+})
+export class StaticTreeInputSelectedNodesComponent {
+ @Input()
+ selectedNodesInternalNames: string[];
-export const dataCy = (value: string, config?: any) => {
- if (config) {
- return cy.get(`[data-cy=${value}]`, config);
- } else {
- return cy.get(`[data-cy=${value}]`);
+ @Output()
+ removeSelectedNode = new EventEmitter<string>();
+
+ onRemoveSelectedNode(selectedNodeName: string) {
+ this.removeSelectedNode.emit(selectedNodeName);
}
-};
+}
diff --git a/ui/cypress/support/general/dataCy.ts
b/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input-service.service.ts
similarity index 62%
copy from ui/cypress/support/general/dataCy.ts
copy to
ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input-service.service.ts
index ff1d54beae..05533f5503 100644
--- a/ui/cypress/support/general/dataCy.ts
+++
b/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input-service.service.ts
@@ -16,18 +16,21 @@
*
*/
-declare global {
- namespace Cypress {
- interface Chainable {
- dataCy: typeof dataCy;
- }
- }
-}
+import { Injectable } from '@angular/core';
+import { RuntimeResolvableTreeInputStaticProperty } from
'@streampipes/platform-services';
-export const dataCy = (value: string, config?: any) => {
- if (config) {
- return cy.get(`[data-cy=${value}]`, config);
- } else {
- return cy.get(`[data-cy=${value}]`);
+@Injectable({
+ providedIn: 'root',
+})
+export class StaticTreeInputServiceService {
+ constructor() {}
+
+ getSelectedNodeIndex(
+ staticProperty: RuntimeResolvableTreeInputStaticProperty,
+ internalNodeName: string,
+ ) {
+ return staticProperty.selectedNodesInternalNames.indexOf(
+ internalNodeName,
+ );
}
-};
+}
diff --git
a/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input.component.html
b/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input.component.html
deleted file mode 100644
index 1ca90b6a11..0000000000
---
a/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input.component.html
+++ /dev/null
@@ -1,256 +0,0 @@
-<!--
- ~ 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 [formGroup]="parentForm" id="formWrapper" fxFlex="100" fxLayout="column">
- <div fxLayout="row" fxLayoutGap="10px" fxLayoutAlign="start center">
- <div>
- <button
- mat-raised-button
- color="accent"
- class="small-button"
- (click)="resetOptionsAndReload()"
- style="margin-right: 10px"
- [disabled]="!showOptions"
- >
- <span>Clear selection & reload</span>
- </button>
- <button
- mat-raised-button
- color="accent"
- class="small-button"
- (click)="reload()"
- style="margin-right: 10px"
- [disabled]="!showOptions"
- >
- <span>Reload</span>
- </button>
- </div>
- <div fxLayout="column" *ngIf="loading" class="mt-10">
- <mat-spinner
- color="accent"
- [mode]="'indeterminate'"
- [diameter]="20"
- ></mat-spinner>
- </div>
- </div>
- <div fxLayout="column" *ngIf="error" class="mt-10">
- <sp-exception-message [message]="errorMessage"></sp-exception-message>
- </div>
- <div fxLayout="row" fxLayoutGap="15px">
- <div fxFlex="30" class="tree-input-section" fxLayout="column">
- <div class="tree-input-header" fxLayoutAlign="start center">
- <div fxLayout="row" fxFlex="100">
- <div fxFlex fxLayoutAlign="start
center"><b>Browse</b></div>
- <div fxLayoutAlign="end center">
- <button
- mat-icon-button
- color="accent"
- (click)="largeView = !largeView"
- >
- <mat-icon
*ngIf="!largeView">open_in_full</mat-icon>
- <mat-icon *ngIf="largeView"
- >close_fullscreen</mat-icon
- >
- </button>
- </div>
- </div>
- </div>
- <mat-tree
- [dataSource]="dataSource"
- [treeControl]="treeControl"
- #tree
- class="sp-tree"
- [ngClass]="
- largeView ? 'tree-large-height' : 'tree-normal-height'
- "
- >
- <mat-tree-node *matTreeNodeDef="let node" matTreeNodeToggle>
- <div
- [ngClass]="
- selectedNodeId === node.internalNodeName
- ? 'selected-node'
- : 'node'
- "
- fxLayoutAlign="start center"
- >
- <i class="material-icons color-primary pr-5">wifi</i>
- <i
- class="material-icons pr-5 icon-button"
- *ngIf="node.dataNode && !isNodeSelected(node)"
- (click)="addNode(node)"
- [attr.data-cy]="'select-' + node.nodeName"
- matTooltip="Add node"
- >add_circle</i
- >
- <i
- class="material-icons pr-5 icon-button"
- *ngIf="node.dataNode && isNodeSelected(node)"
- (click)="removeNode(node)"
- matTooltip="Remove node"
- >remove_circle</i
- >
- <i
- class="material-icons pr-5 icon-button"
- *ngIf="selectedNodeId !== node.internalNodeName"
- (click)="
- selectedNodeMetadata = node.nodeMetadata;
- selectedNodeId = node.internalNodeName
- "
- matTooltip="Show details"
- >visibility</i
- >
- <i
- class="material-icons pr-5 icon-button"
- *ngIf="selectedNodeId === node.internalNodeName"
- (click)="
- selectedNodeMetadata = undefined;
- selectedNodeId = undefined
- "
- matTooltip="Show details"
- >visibility_off</i
- >
- <span class="pr-5">{{ node.nodeName }}</span>
- </div>
- </mat-tree-node>
- <mat-nested-tree-node
- *matTreeNodeDef="let node; when: hasChild"
- >
- <div class="mat-tree-node">
- <button
- mat-icon-button
- matTreeNodeToggle
- [attr.data-cy]="'expand-' + node.nodeName"
- [attr.aria-label]="'Toggle ' + node.nodeName"
- (click)="
- loadChildren(node,
treeControl.isExpanded(node))
- "
- >
- <mat-icon class="mat-icon-rtl-mirror">
- {{
- treeControl.isExpanded(node)
- ? 'expand_more'
- : 'chevron_right'
- }}
- </mat-icon>
- </button>
- <div
- [ngClass]="
- selectedNodeId === node.internalNodeName
- ? 'selected-node'
- : 'node'
- "
- fxLayoutAlign="start center"
- >
- <i
- class="material-icons color-primary pr-5"
- *ngIf="!treeControl.isExpanded(node)"
- >folder</i
- >
- <i
- class="material-icons color-primary pr-5"
- *ngIf="treeControl.isExpanded(node)"
- >folder_open</i
- >
- <i
- class="material-icons pr-5 icon-button"
- *ngIf="
- treeControl.isExpanded(node) &&
- hasDataChildren(node)
- "
- matTooltip="Add all direct children"
- (click)="addAllDirectChildren(node)"
- >add_circle</i
- >
- <i
- class="material-icons pr-5 icon-button"
- *ngIf="selectedNodeId !==
node.internalNodeName"
- (click)="
- selectedNodeMetadata = node.nodeMetadata;
- selectedNodeId = node.internalNodeName
- "
- matTooltip="Show details"
- >visibility</i
- >
- <i
- class="material-icons pr-5 icon-button"
- *ngIf="selectedNodeId ===
node.internalNodeName"
- (click)="
- selectedNodeMetadata = undefined;
- selectedNodeId = undefined
- "
- matTooltip="Show details"
- >visibility_off</i
- >
- <span class="node-name pr-5"
- > {{ node.nodeName }}</span
- >
- </div>
- </div>
- <div
- [class.sp-tree-invisible]="
- !treeControl.isExpanded(node)
- "
- role="group"
- >
- <ng-container matTreeNodeOutlet></ng-container>
- </div>
- </mat-nested-tree-node>
- </mat-tree>
- </div>
- <div fxFlex="40" class="tree-input-section" fxLayout="column">
- <div class="tree-input-header" fxLayoutAlign="start center">
- <b>Node Details</b>
- </div>
- <div
- class="node-metadata"
- *ngFor="let metadata of selectedNodeMetadata | keyvalue"
- fxLayout="row"
- >
- <div fxFlex="30">{{ metadata.key }}</div>
- <div fxFlex="70">{{ metadata.value }}</div>
- </div>
- </div>
- <div fxFlex="30" class="tree-input-section" fxLayout="column">
- <div class="tree-input-header" fxLayoutAlign="start center">
- <b>Selected Nodes</b>
- </div>
- <div
- *ngFor="
- let selectedNode of
staticProperty.selectedNodesInternalNames
- "
- >
- <div
- class="selected-node"
- fxLayout="row"
- fxLayoutAlign="start center"
- >
- <span fxFlex>{{ selectedNode }}</span>
- <div fxLayoutAlign="end center">
- <button
- mat-icon-button
- color="accent"
- (click)="removeSelectedNode(selectedNode)"
- >
- <i class="material-icons">remove</i>
- </button>
- </div>
- </div>
- </div>
- </div>
- </div>
-</div>