From 3f20aa63df37b639f262fec12ba097b76cb37791 Mon Sep 17 00:00:00 2001 From: CTomlyn Date: Wed, 22 Jan 2025 10:16:48 -0800 Subject: [PATCH 1/3] #2316 Single click to add item from palette (#2317) Signed-off-by: CTomlyn --- .../src/object-model/config-utils.js | 3 +- .../src/palette/palette-content-list-item.jsx | 9 +++++ .../src/palette/palette-content-list.jsx | 3 ++ .../palette-dialog-content-grid-node.jsx | 21 ++++++++--- .../palette/palette-dialog-content-grid.jsx | 3 ++ .../src/palette/palette-dialog-content.jsx | 3 ++ .../src/palette/palette-dialog-under.jsx | 2 ++ .../src/palette/palette-dialog.jsx | 3 ++ .../palette-flyout-content-category.jsx | 2 ++ .../palette-flyout-content-filtered-list.jsx | 2 ++ .../src/palette/palette-flyout-content.jsx | 3 ++ .../src/palette/palette-flyout.jsx | 2 ++ .../common-canvas/src/palette/palette.jsx | 3 ++ ...y.js => palette-auto-node-placement.cy.js} | 35 +++++++++++++++++++ .../cypress/support/canvas/palette-cmds.js | 4 +++ canvas_modules/harness/src/client/App.js | 2 ++ .../sidepanel/canvas/sidepanel-canvas.jsx | 11 ++++++ docs/pages/01.02-palette.md | 5 ++- docs/pages/03.02.01-canvas-config.md | 3 ++ 19 files changed, 113 insertions(+), 6 deletions(-) rename canvas_modules/harness/cypress/e2e/canvas/{auto-node-placement.cy.js => palette-auto-node-placement.cy.js} (91%) diff --git a/canvas_modules/common-canvas/src/object-model/config-utils.js b/canvas_modules/common-canvas/src/object-model/config-utils.js index d5f0933977..2c3a637259 100644 --- a/canvas_modules/common-canvas/src/object-model/config-utils.js +++ b/canvas_modules/common-canvas/src/object-model/config-utils.js @@ -92,6 +92,7 @@ export default class ConfigUtils { enableWYSIWYGComments: false, enableKeyboardNavigation: false, enableAutoLinkOnlyFromSelNodes: false, + enableSingleClickAddFromPalette: false, enableContextToolbar: false, enableSaveZoom: "None", enableSnapToGridType: "None", @@ -105,7 +106,7 @@ export default class ConfigUtils { enableFocusOnMount: true, enableBoundingRectangles: false, // Not documented enableCanvasUnderlay: "None", // Not documented - enableParentClass: "", // Not documented + enableParentClass: "", enablePositionNodeOnRightFlyoutOpen: false, // May also be an object: { x: 5, y: 5 } emptyCanvasContent: null, dropZoneCanvasContent: null, diff --git a/canvas_modules/common-canvas/src/palette/palette-content-list-item.jsx b/canvas_modules/common-canvas/src/palette/palette-content-list-item.jsx index 4a2a6641da..b4e546f607 100644 --- a/canvas_modules/common-canvas/src/palette/palette-content-list-item.jsx +++ b/canvas_modules/common-canvas/src/palette/palette-content-list-item.jsx @@ -43,6 +43,7 @@ class PaletteContentListItem extends React.Component { this.onDragStart = this.onDragStart.bind(this); this.onDragEnd = this.onDragEnd.bind(this); + this.onClick = this.onClick.bind(this); this.onDoubleClick = this.onDoubleClick.bind(this); this.onMouseOver = this.onMouseOver.bind(this); this.onMouseLeave = this.onMouseLeave.bind(this); @@ -98,6 +99,12 @@ class PaletteContentListItem extends React.Component { this.createAutoNode(true); } + onClick() { + if (this.props.allowClickToAdd) { + this.createAutoNode(true); + } + } + onMouseOver(ev) { if (!this.props.isDisplaySearchResult && ev.buttons === 0) { const nodeTemplate = this.props.nodeTypeInfo.category.empty_text @@ -365,6 +372,7 @@ class PaletteContentListItem extends React.Component { onMouseDown={this.props.isEditingEnabled ? this.onMouseDown : null} onDragStart={this.props.isEditingEnabled ? this.onDragStart : null} onDragEnd={this.props.isEditingEnabled ? this.onDragEnd : null} + onClick={this.props.isEditingEnabled ? this.onClick : null} onDoubleClick={this.props.isEditingEnabled ? this.onDoubleClick : null} > {categoryLabel} @@ -386,6 +394,7 @@ PaletteContentListItem.propTypes = { tabIndex: PropTypes.number.isRequired, nextNodeInCategory: PropTypes.func, previousNodeInCategory: PropTypes.func, + allowClickToAdd: PropTypes.bool, isEditingEnabled: PropTypes.bool.isRequired, isPaletteWide: PropTypes.bool, isShowRanking: PropTypes.bool diff --git a/canvas_modules/common-canvas/src/palette/palette-content-list.jsx b/canvas_modules/common-canvas/src/palette/palette-content-list.jsx index 1a93d63c4d..948873739b 100644 --- a/canvas_modules/common-canvas/src/palette/palette-content-list.jsx +++ b/canvas_modules/common-canvas/src/palette/palette-content-list.jsx @@ -68,6 +68,7 @@ class PaletteContentList extends React.Component { nodeTypeInfo={{ nodeType: {}, category: this.props.category }} isDisplaySearchResult={false} canvasController={this.props.canvasController} + allowClickToAdd={this.props.allowClickToAdd} isPaletteWide={this.props.isPaletteWide} isEditingEnabled={this.props.isEditingEnabled} /> @@ -92,6 +93,7 @@ class PaletteContentList extends React.Component { isEditingEnabled={this.props.isEditingEnabled} nextNodeInCategory={this.nextNodeInCategory} previousNodeInCategory={this.previousNodeInCategory} + allowClickToAdd={this.props.allowClickToAdd} /> ); } @@ -111,6 +113,7 @@ PaletteContentList.propTypes = { canvasController: PropTypes.object.isRequired, isPaletteWide: PropTypes.bool, isEditingEnabled: PropTypes.bool.isRequired, + allowClickToAdd: PropTypes.bool }; export default PaletteContentList; diff --git a/canvas_modules/common-canvas/src/palette/palette-dialog-content-grid-node.jsx b/canvas_modules/common-canvas/src/palette/palette-dialog-content-grid-node.jsx index cb4f39602b..0a74b1af6c 100644 --- a/canvas_modules/common-canvas/src/palette/palette-dialog-content-grid-node.jsx +++ b/canvas_modules/common-canvas/src/palette/palette-dialog-content-grid-node.jsx @@ -37,6 +37,7 @@ class PaletteDialogContentGridNode extends React.Component { this.onDragStart = this.onDragStart.bind(this); this.onDragEnd = this.onDragEnd.bind(this); + this.onClick = this.onClick.bind(this); this.onDoubleClick = this.onDoubleClick.bind(this); this.onMouseOver = this.onMouseOver.bind(this); this.onMouseLeave = this.onMouseLeave.bind(this); @@ -72,13 +73,16 @@ class PaletteDialogContentGridNode extends React.Component { this.props.canvasController.nodeTemplateDragEnd(); } - onDoubleClick() { - if (this.props.canvasController.createAutoNode) { - const nodeTemplate = this.props.canvasController.convertNodeTemplate(this.props.nodeTemplate); - this.props.canvasController.createAutoNode(nodeTemplate); + onClick() { + if (this.props.allowClickToAdd) { + this.createAutoNode(); } } + onDoubleClick() { + this.createAutoNode(); + } + onMouseOver(ev) { if (ev.buttons === 0) { const nodeTemplate = this.props.category.empty_text @@ -99,6 +103,13 @@ class PaletteDialogContentGridNode extends React.Component { this.props.canvasController.closeTip(); } + createAutoNode() { + if (this.props.canvasController.createAutoNode) { + const nodeTemplate = this.props.canvasController.convertNodeTemplate(this.props.nodeTemplate); + this.props.canvasController.createAutoNode(nodeTemplate); + } + } + render() { let label = ""; if (has(this.props.nodeTemplate, "app_data.ui_data.label")) { @@ -147,6 +158,7 @@ class PaletteDialogContentGridNode extends React.Component { onMouseDown={this.props.isEditingEnabled ? this.onMouseDown : null} onDragStart={this.props.isEditingEnabled ? this.onDragStart : null} onDragEnd={this.props.isEditingEnabled ? this.onDragEnd : null} + onClick={this.props.isEditingEnabled ? this.onClick : null} onDoubleClick={this.props.isEditingEnabled ? this.onDoubleClick : null} className="palette-dialog-grid-node-outer" > @@ -167,6 +179,7 @@ PaletteDialogContentGridNode.propTypes = { category: PropTypes.object.isRequired, nodeTemplate: PropTypes.object.isRequired, canvasController: PropTypes.object.isRequired, + allowClickToAdd: PropTypes.bool, isEditingEnabled: PropTypes.bool.isRequired }; diff --git a/canvas_modules/common-canvas/src/palette/palette-dialog-content-grid.jsx b/canvas_modules/common-canvas/src/palette/palette-dialog-content-grid.jsx index 54e67a8c8f..7f77f51608 100644 --- a/canvas_modules/common-canvas/src/palette/palette-dialog-content-grid.jsx +++ b/canvas_modules/common-canvas/src/palette/palette-dialog-content-grid.jsx @@ -38,6 +38,7 @@ class PaletteDialogContentGrid extends React.Component { nodeTemplate={ {} } canvasController={this.props.canvasController} isEditingEnabled={this.props.isEditingEnabled} + allowClickToAdd={this.props.allowClickToAdd} /> ); } else { @@ -48,6 +49,7 @@ class PaletteDialogContentGrid extends React.Component { nodeTemplate={this.props.nodeTypes[idx]} canvasController={this.props.canvasController} isEditingEnabled={this.props.isEditingEnabled} + allowClickToAdd={this.props.allowClickToAdd} /> ); } @@ -65,6 +67,7 @@ PaletteDialogContentGrid.propTypes = { category: PropTypes.object.isRequired, nodeTypes: PropTypes.array.isRequired, isEditingEnabled: PropTypes.bool.isRequired, + allowClickToAdd: PropTypes.bool, canvasController: PropTypes.object.isRequired }; diff --git a/canvas_modules/common-canvas/src/palette/palette-dialog-content.jsx b/canvas_modules/common-canvas/src/palette/palette-dialog-content.jsx index c92efe9147..e85414b437 100644 --- a/canvas_modules/common-canvas/src/palette/palette-dialog-content.jsx +++ b/canvas_modules/common-canvas/src/palette/palette-dialog-content.jsx @@ -87,6 +87,7 @@ class PaletteDialogContent extends React.Component { nodeTypes={nodeTypes} canvasController={this.props.canvasController} isEditingEnabled={this.props.isEditingEnabled} + allowClickToAdd={this.props.allowClickToAdd} />) : ( ); return (
@@ -112,6 +114,7 @@ PaletteDialogContent.propTypes = { paletteJSON: PropTypes.object.isRequired, showGrid: PropTypes.bool.isRequired, canvasController: PropTypes.object.isRequired, + allowClickToAdd: PropTypes.bool, isEditingEnabled: PropTypes.bool.isRequired }; diff --git a/canvas_modules/common-canvas/src/palette/palette-dialog-under.jsx b/canvas_modules/common-canvas/src/palette/palette-dialog-under.jsx index 159c95c8fa..e2503cf587 100644 --- a/canvas_modules/common-canvas/src/palette/palette-dialog-under.jsx +++ b/canvas_modules/common-canvas/src/palette/palette-dialog-under.jsx @@ -542,6 +542,7 @@ class PaletteDialog extends React.Component { showGrid={this.state.showGrid} canvasController={this.props.canvasController} isEditingEnabled={this.props.isEditingEnabled} + allowClickToAdd={this.props.allowClickToAdd} />
@@ -557,6 +558,7 @@ PaletteDialog.propTypes = { canvasController: PropTypes.object.isRequired, containingDivId: PropTypes.string.isRequired, paletteJSON: PropTypes.object, + allowClickToAdd: PropTypes.bool, isEditingEnabled: PropTypes.bool }; diff --git a/canvas_modules/common-canvas/src/palette/palette-dialog.jsx b/canvas_modules/common-canvas/src/palette/palette-dialog.jsx index 910f010ac3..8f98d70445 100644 --- a/canvas_modules/common-canvas/src/palette/palette-dialog.jsx +++ b/canvas_modules/common-canvas/src/palette/palette-dialog.jsx @@ -36,6 +36,7 @@ class PaletteDialog extends React.Component { canvasController={this.props.canvasController} paletteJSON={this.props.paletteJSON} containingDivId={this.props.containingDivId} + allowClickToAdd={this.props.allowClickToAdd} isEditingEnabled={this.props.isEditingEnabled} />); } @@ -51,11 +52,13 @@ PaletteDialog.propTypes = { // Provided by redux paletteJSON: PropTypes.object, + allowClickToAdd: PropTypes.bool, isEditingEnabled: PropTypes.bool }; const mapStateToProps = (state, ownProps) => ({ paletteJSON: state.palette.content, + allowClickToAdd: state.canvasconfig.enableSingleClickAddFromPalette, isEditingEnabled: state.canvasconfig.enableEditingActions }); diff --git a/canvas_modules/common-canvas/src/palette/palette-flyout-content-category.jsx b/canvas_modules/common-canvas/src/palette/palette-flyout-content-category.jsx index 9896ff87bf..12f93c18fd 100644 --- a/canvas_modules/common-canvas/src/palette/palette-flyout-content-category.jsx +++ b/canvas_modules/common-canvas/src/palette/palette-flyout-content-category.jsx @@ -201,6 +201,7 @@ class PaletteFlyoutContentCategory extends React.Component { category={this.props.category} nodeTypeInfos={nodeTypeInfos} canvasController={this.props.canvasController} + allowClickToAdd={this.props.allowClickToAdd} isPaletteWide={this.props.isPaletteWide} isEditingEnabled={this.props.isEditingEnabled} /> @@ -241,6 +242,7 @@ class PaletteFlyoutContentCategory extends React.Component { PaletteFlyoutContentCategory.propTypes = { category: PropTypes.object.isRequired, canvasController: PropTypes.object.isRequired, + allowClickToAdd: PropTypes.bool, isPaletteWide: PropTypes.bool, isEditingEnabled: PropTypes.bool.isRequired, }; diff --git a/canvas_modules/common-canvas/src/palette/palette-flyout-content-filtered-list.jsx b/canvas_modules/common-canvas/src/palette/palette-flyout-content-filtered-list.jsx index 224a015755..ccff078b4b 100644 --- a/canvas_modules/common-canvas/src/palette/palette-flyout-content-filtered-list.jsx +++ b/canvas_modules/common-canvas/src/palette/palette-flyout-content-filtered-list.jsx @@ -56,6 +56,7 @@ class PaletteFlyoutContentFilteredList extends React.Component { canvasController={this.props.canvasController} isPaletteWide={this.props.isPaletteWide} isEditingEnabled={this.props.isEditingEnabled} + allowClickToAdd={this.props.allowClickToAdd} /> ); @@ -87,6 +88,7 @@ PaletteFlyoutContentFilteredList.propTypes = { intl: PropTypes.object.isRequired, nodeTypeInfos: PropTypes.array.isRequired, canvasController: PropTypes.object.isRequired, + allowClickToAdd: PropTypes.bool, isPaletteWide: PropTypes.bool.isRequired, isEditingEnabled: PropTypes.bool.isRequired, isNodeTypeInfosArrayTruncated: PropTypes.bool, diff --git a/canvas_modules/common-canvas/src/palette/palette-flyout-content.jsx b/canvas_modules/common-canvas/src/palette/palette-flyout-content.jsx index d461cb541d..fb0112a16c 100644 --- a/canvas_modules/common-canvas/src/palette/palette-flyout-content.jsx +++ b/canvas_modules/common-canvas/src/palette/palette-flyout-content.jsx @@ -77,6 +77,7 @@ class PaletteFlyoutContent extends React.Component { key={category.id} category={category} canvasController={this.props.canvasController} + allowClickToAdd={this.props.allowClickToAdd} isPaletteWide={this.props.isPaletteWide} isEditingEnabled={this.props.isEditingEnabled} /> @@ -103,6 +104,7 @@ class PaletteFlyoutContent extends React.Component { key={"filtered-nodes"} nodeTypeInfos={filteredNodeTypeInfos} canvasController={this.props.canvasController} + allowClickToAdd={this.props.allowClickToAdd} isPaletteWide={this.props.isPaletteWide} isEditingEnabled={this.props.isEditingEnabled} // isShowRanking // Uncomment this to show ranking for debuggig ranking algorithm @@ -162,6 +164,7 @@ PaletteFlyoutContent.propTypes = { canvasController: PropTypes.object.isRequired, paletteJSON: PropTypes.object.isRequired, paletteHeader: PropTypes.object, + allowClickToAdd: PropTypes.bool, isEditingEnabled: PropTypes.bool.isRequired, isPaletteWide: PropTypes.bool }; diff --git a/canvas_modules/common-canvas/src/palette/palette-flyout.jsx b/canvas_modules/common-canvas/src/palette/palette-flyout.jsx index 92cdaecbcc..0a4d71523c 100644 --- a/canvas_modules/common-canvas/src/palette/palette-flyout.jsx +++ b/canvas_modules/common-canvas/src/palette/palette-flyout.jsx @@ -45,6 +45,7 @@ class PaletteFlyout extends React.Component { canvasController={this.props.canvasController} paletteJSON={this.props.paletteJSON} paletteHeader={this.props.paletteHeader} + allowClickToAdd={this.props.allowClickToAdd} isEditingEnabled={this.props.isEditingEnabled} isPaletteWide={this.props.isPaletteWide} /> @@ -59,6 +60,7 @@ PaletteFlyout.propTypes = { canvasController: PropTypes.object.isRequired, paletteJSON: PropTypes.object.isRequired, paletteHeader: PropTypes.object, + allowClickToAdd: PropTypes.bool, isEditingEnabled: PropTypes.bool.isRequired, isPaletteWide: PropTypes.bool }; diff --git a/canvas_modules/common-canvas/src/palette/palette.jsx b/canvas_modules/common-canvas/src/palette/palette.jsx index 8adc5eb750..c3bec08f52 100644 --- a/canvas_modules/common-canvas/src/palette/palette.jsx +++ b/canvas_modules/common-canvas/src/palette/palette.jsx @@ -38,6 +38,7 @@ class Palette extends React.Component { canvasController={this.props.canvasController} paletteJSON={this.props.paletteJSON} paletteHeader={this.props.paletteHeader} + allowClickToAdd={this.props.allowClickToAdd} isEditingEnabled={this.props.isEditingEnabled} isPaletteWide={this.props.isPaletteWide} /> @@ -52,6 +53,7 @@ Palette.propTypes = { // Provided by redux paletteJSON: PropTypes.object, paletteHeader: PropTypes.object, + allowClickToAdd: PropTypes.bool, isEditingEnabled: PropTypes.bool, isPaletteWide: PropTypes.bool }; @@ -60,6 +62,7 @@ const mapStateToProps = (state, ownProps) => ({ paletteJSON: state.palette.content, isEditingEnabled: state.canvasconfig.enableEditingActions, paletteHeader: state.canvasconfig.enablePaletteHeader, + allowClickToAdd: state.canvasconfig.enableSingleClickAddFromPalette, isPaletteWide: state.canvasconfig.enablePaletteLayout === PALETTE_LAYOUT_NONE || (state.canvasconfig.enablePaletteLayout === PALETTE_LAYOUT_FLYOUT && state.palette.isOpen) diff --git a/canvas_modules/harness/cypress/e2e/canvas/auto-node-placement.cy.js b/canvas_modules/harness/cypress/e2e/canvas/palette-auto-node-placement.cy.js similarity index 91% rename from canvas_modules/harness/cypress/e2e/canvas/auto-node-placement.cy.js rename to canvas_modules/harness/cypress/e2e/canvas/palette-auto-node-placement.cy.js index 51916c5b78..1d92066515 100644 --- a/canvas_modules/harness/cypress/e2e/canvas/auto-node-placement.cy.js +++ b/canvas_modules/harness/cypress/e2e/canvas/palette-auto-node-placement.cy.js @@ -172,6 +172,41 @@ describe("Test auto layout variations", function() { }); }); +describe("Test auto layout with enableSingleClickAddFromPalette", function() { + beforeEach(() => { + cy.visit("/"); + cy.openCanvasPalette("modelerPalette.json"); + }); + + it("Test single click adds node to canvas when enableSingleClickAddFromPalette is true", function() { + cy.setCanvasConfig({ + "selectedSingleClickAddFromPalette": true + }); + + cy.clickToolbarPaletteOpen(); + + cy.clickCategory("Import"); + cy.clickNodeInCategory("Var. File", "Import"); + cy.verifyNodeTransform("Var. File", 50, 50); + cy.verifyNumberOfNodes(1); + cy.verifyNumberOfPortDataLinks(0); + }); + + it("Test single click doesn't add node to canvas when enableSingleClickAddFromPalette is false", function() { + cy.setCanvasConfig({ + "selectedSingleClickAddFromPalette": false + }); + + cy.clickToolbarPaletteOpen(); + + // Double click Var. File node on canvas + cy.clickCategory("Import"); + cy.clickNodeInCategory("Var. File", "Import"); + cy.verifyNumberOfNodes(0); + cy.verifyNumberOfPortDataLinks(0); + }); +}); + describe("Test that auto-nodes are added to an in-place expanded supernode", function() { beforeEach(() => { cy.visit("/"); diff --git a/canvas_modules/harness/cypress/support/canvas/palette-cmds.js b/canvas_modules/harness/cypress/support/canvas/palette-cmds.js index 5f3834c1ca..4679863cbf 100644 --- a/canvas_modules/harness/cypress/support/canvas/palette-cmds.js +++ b/canvas_modules/harness/cypress/support/canvas/palette-cmds.js @@ -98,6 +98,10 @@ Cypress.Commands.add("doubleClickNodeInCategory", (nodeLabel, categoryLabel) => cy.findNodeInCategory(nodeLabel, categoryLabel).dblclick(); }); +Cypress.Commands.add("clickNodeInCategory", (nodeLabel, categoryLabel) => { + cy.findNodeInCategory(nodeLabel, categoryLabel).click(); +}); + Cypress.Commands.add("hoverOverNodeInCategory", (nodeLabel, categoryLabel) => { cy.findNodeInCategory(nodeLabel, categoryLabel).trigger("mouseover", { buttons: 0 }); }); diff --git a/canvas_modules/harness/src/client/App.js b/canvas_modules/harness/src/client/App.js index a782180aee..3b38a1349b 100644 --- a/canvas_modules/harness/src/client/App.js +++ b/canvas_modules/harness/src/client/App.js @@ -262,6 +262,7 @@ class App extends React.Component { selectedNarrowPalette: true, selectedSchemaValidation: true, selectedAutoLinkOnlyFromSelNodes: false, + selectedSingleClickAddFromPalette: false, selectedBrowserEditMenu: true, selectedBoundingRectangles: false, selectedNodeLayout: null, @@ -2123,6 +2124,7 @@ class App extends React.Component { enableRaiseNodesToTopOnHover: this.state.selectedRaiseNodesToTopOnHover, enablePositionNodeOnRightFlyoutOpen: this.state.selectedPositionNodeOnRightFlyoutOpen, enableAutoLinkOnlyFromSelNodes: this.state.selectedAutoLinkOnlyFromSelNodes, + enableSingleClickAddFromPalette: this.state.selectedSingleClickAddFromPalette, enableKeyboardNavigation: this.state.selectedKeyboardNavigation, enableBrowserEditMenu: this.state.selectedBrowserEditMenu, tipConfig: this.state.selectedTipConfig, diff --git a/canvas_modules/harness/src/client/components/sidepanel/canvas/sidepanel-canvas.jsx b/canvas_modules/harness/src/client/components/sidepanel/canvas/sidepanel-canvas.jsx index f1fd6b71ea..b7bf0cb4a7 100644 --- a/canvas_modules/harness/src/client/components/sidepanel/canvas/sidepanel-canvas.jsx +++ b/canvas_modules/harness/src/client/components/sidepanel/canvas/sidepanel-canvas.jsx @@ -741,6 +741,15 @@ export default class SidePanelForms extends React.Component { /> ); + var enableSingleClickAddFromPalette = (
+ this.setStateValue(val, "selectedSingleClickAddFromPalette")} + /> +
); + var enableKeyboardNavigation = (
Nodes
{divider} {nodeFormatType} diff --git a/docs/pages/01.02-palette.md b/docs/pages/01.02-palette.md index 317ec6d5d3..e43b2fc2ba 100644 --- a/docs/pages/01.02-palette.md +++ b/docs/pages/01.02-palette.md @@ -8,7 +8,9 @@ The user can drag any of the palette nodes from the palette onto the canvas to c -Alternatively, the user can double click node templates to add, and automatically join, them to the current flow. When a palette node is double clicked, Common Canvas will look for the node at the end of the current flow of nodes and will add the node to the canvas to the right of the end node in the flow. Common Canvas will also automatically join the nodes together by creating a new link line. +Alternatively, the user can double click (or single click if [`enableSingleClickAddFromPalette`](03.02.01-canvas-config.md#enablesingleclickaddfrompalette) is set to `true`) node templates to add, and automatically join, them to the current flow. + +When a palette node is added in this way, Common Canvas will look for the node at the end of the current flow of nodes and will add the node to the canvas to the right of the end node in the flow. Common Canvas will also automatically join the nodes together by creating a new link line. The [`enableAutoLinkOnlyFromSelNodes`](03.02.01-canvas-config.md#enableautolinkonlyfromselnodes) canvas configuartion field can be used to customize which node is chosen for automatic linking. @@ -26,6 +28,7 @@ There are a number of configuration options that control the palette that are sp * [enablePaletteLayout](03.02.01-canvas-config.md#enablepalettelayout) * [enableNarrowPalette](03.02.01-canvas-config.md#enablenarrowpalette) +* [enableSingleClickAddFromPalette](03.02.01-canvas-config.md#enablesingleclickaddfrompalette) * [enableAutoLinkOnlyFromSelNodes](03.02.01-canvas-config.md#enableautolinkonlyfromselnodes) * [enablePaletteHeader](03.02.01-canvas-config.md#enablepaletteheader) diff --git a/docs/pages/03.02.01-canvas-config.md b/docs/pages/03.02.01-canvas-config.md index 2bf8fa9e72..6cfa1623cb 100644 --- a/docs/pages/03.02.01-canvas-config.md +++ b/docs/pages/03.02.01-canvas-config.md @@ -184,6 +184,9 @@ This is a boolean. The default is false. When set to true, Common Canvas will di #### **enablePaletteLayout** This can be: "Modal" or "Flyout" or "None". The default is "Flyout". "Flyout" displays a panel on the left side of the canvas containing the palette icons and "Modal" shows the palette icons in a dialog window. "None" stops the palette from appearing. +#### **enableSingleClickAddFromPalette** +This is a boolean. The default is false. When set to true, if the user clicks on a palette node in a category, the node will be automatically added to the canvas and, if possible, the node will be linked one of the nodes on the canvas. The node chosen can be controlled with the `enableAutoLinkOnlyFromSelNodes` configuraiton field. If `enableSingleClickAddFromPalette` is false, a single click on a node in the palette will not add it to the canvas. + #### **enableAutoLinkOnlyFromSelNodes** This is a boolean. The default is false. When set to true the auto-add function (where double clicking a node in the palette automatically adds it to the canvas) will only link up nodes when a node is already selected on the canvas and then, only if the selected node can be linked to the node that was double clicked. If false, the auto-add function will make a best guess at which node the double-clicked node should be added to. From b542e8425f5ad5938491367a1b2bfeb0490636e2 Mon Sep 17 00:00:00 2001 From: CTomlyn Date: Thu, 23 Jan 2025 09:40:00 -0800 Subject: [PATCH 2/3] #2319 Keyboard accessibility of palette tooltip (#2320) Signed-off-by: CTomlyn --- .../src/palette/palette-content-list-item.jsx | 47 ++++++++++++++----- .../src/palette/palette-content-list.jsx | 5 ++ .../palette-flyout-content-category.jsx | 30 ++++++++---- 3 files changed, 60 insertions(+), 22 deletions(-) diff --git a/canvas_modules/common-canvas/src/palette/palette-content-list-item.jsx b/canvas_modules/common-canvas/src/palette/palette-content-list-item.jsx index b4e546f607..4c6f954aea 100644 --- a/canvas_modules/common-canvas/src/palette/palette-content-list-item.jsx +++ b/canvas_modules/common-canvas/src/palette/palette-content-list-item.jsx @@ -55,12 +55,20 @@ class PaletteContentListItem extends React.Component { // Make sure the tip doesn't appear when starting to drag a node. this.props.canvasController.closeTip(); + // Sets the focus index on the parent palette-content-list so + // future key presses that move focus will work correctly. + if (this.props.setFocusIndex) { + this.props.setFocusIndex(); + } + // Prepare the ghost image on mouse down because asynchronous loading of // SVG files will be too slow if this is done in onDragStart. this.ghostData = this.props.canvasController.getGhostNode(this.props.nodeTypeInfo.nodeType); } onDragStart(ev) { + this.props.canvasController.closeTip(); + // We cannot use the dataTransfer object for the nodeTemplate because // the dataTransfer data is not available during dragOver events so we set // the nodeTemplate into the canvas controller. @@ -105,19 +113,9 @@ class PaletteContentListItem extends React.Component { } } - onMouseOver(ev) { - if (!this.props.isDisplaySearchResult && ev.buttons === 0) { - const nodeTemplate = this.props.nodeTypeInfo.category.empty_text - ? { app_data: { ui_data: { label: this.props.nodeTypeInfo.category.empty_text } } } - : this.props.nodeTypeInfo.nodeType; - - this.props.canvasController.openTip({ - id: "paletteTip_" + this.props.nodeTypeInfo.nodeType.op, - type: TIP_TYPE_PALETTE_ITEM, - targetObj: ev.currentTarget, - nodeTemplate: nodeTemplate, - category: this.props.nodeTypeInfo.category - }); + onMouseOver(evt) { + if (!this.props.isDisplaySearchResult && evt.buttons === 0) { + this.displayTip(); } } @@ -269,8 +267,30 @@ class PaletteContentListItem extends React.Component { this.setState({ showFullDescription: false }); } + // Sets the focus on this palette (node) item and displays + // a tooltip. This is called by the parent palette-content-list + // when the user moves focus to a new node using the keyboard, so + // we always show a tip for the keyboard user. focus() { this.itemRef.current.focus(); + this.displayTip(); + } + + // Display a tip for this palette (node) item. + displayTip() { + this.props.canvasController.closeTip(); + + const nodeTemplate = this.props.nodeTypeInfo.category.empty_text + ? { app_data: { ui_data: { label: this.props.nodeTypeInfo.category.empty_text } } } + : this.props.nodeTypeInfo.nodeType; + + this.props.canvasController.openTip({ + id: "paletteTip_" + this.props.nodeTypeInfo.nodeType.op, + type: TIP_TYPE_PALETTE_ITEM, + targetObj: this.itemRef.current, + nodeTemplate: nodeTemplate, + category: this.props.nodeTypeInfo.category + }); } // Returns true if this item is disabled and should not be draggable or double-clicked @@ -394,6 +414,7 @@ PaletteContentListItem.propTypes = { tabIndex: PropTypes.number.isRequired, nextNodeInCategory: PropTypes.func, previousNodeInCategory: PropTypes.func, + setFocusIndex: PropTypes.func, allowClickToAdd: PropTypes.bool, isEditingEnabled: PropTypes.bool.isRequired, isPaletteWide: PropTypes.bool, diff --git a/canvas_modules/common-canvas/src/palette/palette-content-list.jsx b/canvas_modules/common-canvas/src/palette/palette-content-list.jsx index 948873739b..1e76d91133 100644 --- a/canvas_modules/common-canvas/src/palette/palette-content-list.jsx +++ b/canvas_modules/common-canvas/src/palette/palette-content-list.jsx @@ -37,6 +37,10 @@ class PaletteContentList extends React.Component { this.contentItemRefs[this.currentFocusIndex].current.focus(); } + setFocusIndex(idx) { + this.currentFocusIndex = idx; + } + nextNodeInCategory(evt) { this.currentFocusIndex++; if (this.currentFocusIndex > this.contentItemRefs.length - 1) { @@ -93,6 +97,7 @@ class PaletteContentList extends React.Component { isEditingEnabled={this.props.isEditingEnabled} nextNodeInCategory={this.nextNodeInCategory} previousNodeInCategory={this.previousNodeInCategory} + setFocusIndex={this.setFocusIndex.bind(this, idx)} allowClickToAdd={this.props.allowClickToAdd} /> ); diff --git a/canvas_modules/common-canvas/src/palette/palette-flyout-content-category.jsx b/canvas_modules/common-canvas/src/palette/palette-flyout-content-category.jsx index 12f93c18fd..6b8f3bbc45 100644 --- a/canvas_modules/common-canvas/src/palette/palette-flyout-content-category.jsx +++ b/canvas_modules/common-canvas/src/palette/palette-flyout-content-category.jsx @@ -31,24 +31,24 @@ class PaletteFlyoutContentCategory extends React.Component { this.onMouseOver = this.onMouseOver.bind(this); this.onMouseLeave = this.onMouseLeave.bind(this); + this.onFocus = this.onFocus.bind(this); this.categoryClicked = this.categoryClicked.bind(this); this.categoryKeyPressed = this.categoryKeyPressed.bind(this); this.setPaletteCategory = this.setPaletteCategory.bind(this); } - onMouseOver(ev) { - this.props.canvasController.openTip({ - id: "paletteTip_" + this.props.category.id, - type: TIP_TYPE_PALETTE_CATEGORY, - targetObj: ev.currentTarget, - category: this.props.category - }); + onMouseOver(evt) { + this.displayTip(evt); } onMouseLeave() { this.props.canvasController.closeTip(); } + onFocus(evt) { + this.displayTip(evt); + } + getDisplayLabel() { if (this.props.isPaletteWide === true) { return this.props.category.label; @@ -63,7 +63,6 @@ class PaletteFlyoutContentCategory extends React.Component { } getInlineLoadingRenderCategory() { - // TODO - This loading functionality should be replaced with a skeleton // graphic to indicate the category is loading instead of using the // InlineLoading component. @@ -110,7 +109,7 @@ class PaletteFlyoutContentCategory extends React.Component { const content = this.getContent(); return ( {content} @@ -121,8 +120,10 @@ class PaletteFlyoutContentCategory extends React.Component { getTitleObj() { const itemImage = this.getItemImage(); const itemText = this.getItemText(); + this.catRef = React.createRef(); return (
Date: Fri, 24 Jan 2025 18:16:56 +0530 Subject: [PATCH 3/3] #2321 Single select table with moveable rows and no Delete icons (#2322) Signed-off-by: srikant --- .../controls/abstract-table.jsx | 3 +- .../structurelisteditor_paramDef.json | 270 ++++++++++++++++++ 2 files changed, 272 insertions(+), 1 deletion(-) diff --git a/canvas_modules/common-canvas/src/common-properties/controls/abstract-table.jsx b/canvas_modules/common-canvas/src/common-properties/controls/abstract-table.jsx index 60fc619ea5..38c240651b 100644 --- a/canvas_modules/common-canvas/src/common-properties/controls/abstract-table.jsx +++ b/canvas_modules/common-canvas/src/common-properties/controls/abstract-table.jsx @@ -744,7 +744,8 @@ export default class AbstractTable extends React.Component { const cell = this.buildChildItem(propertyName, rowIndex, tableState); columns.push(cell); } - if (this.props.control.rowSelection === ROW_SELECTION.SINGLE && !this.isReadonlyTable()) { + // Do not show delete icon if add_remove_rows is false (default is true) + if (this.props.control.rowSelection === ROW_SELECTION.SINGLE && !this.isReadonlyTable() && this.props.addRemoveRows) { const toolTip = PropertyUtils.formatMessage(this.reactIntl, MESSAGE_KEYS.TABLE_DELETEICON_TOOLTIP); const tooltipId = "tooltip-delete-row"; const deleteOption = ( diff --git a/canvas_modules/harness/test_resources/parameterDefs/structurelisteditor_paramDef.json b/canvas_modules/harness/test_resources/parameterDefs/structurelisteditor_paramDef.json index 73fcfc1abc..926777d0cf 100644 --- a/canvas_modules/harness/test_resources/parameterDefs/structurelisteditor_paramDef.json +++ b/canvas_modules/harness/test_resources/parameterDefs/structurelisteditor_paramDef.json @@ -107,6 +107,29 @@ "strawberry" ] ], + "inlineEditingTableError3": [ + [ + 1, + 1.234, + "Age >= 55", + "dog", + "apple" + ], + [ + 3, + 1.234, + "Age < 55", + "cat", + "orange" + ], + [ + 1, + 3.5, + "Age >= 55", + "pig", + "strawberry" + ] + ], "onPanelNotVisibleTable": [ [ 1, @@ -248,6 +271,11 @@ "type": "array[inlineEditingTableError2]", "default": [] }, + { + "id": "inlineEditingTableError3", + "type": "array[inlineEditingTableError3]", + "default": [] + }, { "id": "onPanelNotVisibleTable", "type": "array[onPanelNotVisibleTable]", @@ -504,6 +532,45 @@ } ] }, + { + "id": "inlineEditingTableError3", + "parameters": [ + { + "id": "valueName", + "type": "integer", + "default": "1", + "role": "new_column" + }, + { + "id": "doubleName", + "type": "double", + "default": "1.2", + "role": "new_column" + }, + { + "id": "condition", + "type": "string", + "default": "", + "role": "expression" + }, + { + "id": "dropdown", + "enum": [ + "dog", + "cat", + "pig", + "horse" + ], + "default": "yellow" + }, + { + "id": "textfield", + "type": "string", + "default": "", + "role": "new_column" + } + ] + }, { "id": "onPanelNotVisibleTable", "parameters": [ @@ -885,6 +952,18 @@ "default": "To generated a error: (integer Field = 2 OR double Field = 2.3) || condition field = help || animals field = horse || fruit field = pear" } }, + { + "parameter_ref": "inlineEditingTableError3", + "label": { + "default": "Values" + }, + "description": { + "default": "Complex table control list editor table input without delete button" + }, + "text_before": { + "default": "To generated a error: (integer Field = 2 OR double Field = 2.3) || condition field = help || animals field = horse || fruit field = pear" + } + }, { "parameter_ref": "onPanelNotVisibleTable", "label": { @@ -1342,6 +1421,76 @@ } ] }, + { + "complex_type_ref": "inlineEditingTableError3", + "row_selection": "single", + "add_remove_rows": false, + "parameters": [ + { + "parameter_ref": "valueName", + "label": { + "default": "Integer Field", + "resource_key": "expressionCellTable.name.label" + }, + "description": { + "resource_key": "expressionCellTable.name.desc" + }, + "width": 10, + "edit_style": "inline", + "summary": true + }, + { + "parameter_ref": "doubleName", + "label": { + "default": "Double Field", + "resource_key": "expressionCellTable.doubleName.label" + }, + "description": { + "resource_key": "expressionCellTable.doubleName.desc" + }, + "width": 10, + "edit_style": "inline", + "summary": true + }, + { + "parameter_ref": "condition", + "language": "CLEM", + "label": { + "resource_key": "expressionCellTable.description.label" + }, + "description": { + "resource_key": "expressionCellTable.description.desc" + }, + "width": 15, + "edit_style": "on_panel", + "place_holder_text": { + "default": "Enter condition expression" + } + }, + { + "parameter_ref": "dropdown", + "label": { + "resource_key": "dropdown.label" + }, + "description": { + "resource_key": "dropdown.desc" + }, + "width": 10, + "edit_style": "inline" + }, + { + "parameter_ref": "textfield", + "label": { + "resource_key": "textfield.label" + }, + "description": { + "resource_key": "textfield.desc" + }, + "width": 10, + "edit_style": "inline" + } + ] + }, { "complex_type_ref": "onPanelNotVisibleTable", "row_selection": "single", @@ -2009,6 +2158,21 @@ ] } ] + }, + { + "id": "inlineEditingTableError3-summary-panel", + "type": "summaryPanel", + "label": { + "default": "Configure Error 3 Inline Editing Table without delete row button" + }, + "group_info": [ + { + "id": "Inline editing2", + "parameter_refs": [ + "inlineEditingTableError3" + ] + } + ] } ] } @@ -2244,6 +2408,112 @@ } } }, + { + "validation": { + "id": "tableerror2test5", + "fail_message": { + "type": "error", + "focus_parameter_ref": "inlineEditingTableError3", + "message": { + "default": "This is a table control level error." + } + }, + "evaluate": { + "condition": { + "parameter_ref": "inlineEditingTableError3", + "op": "isEmpty" + } + } + } + }, + { + "validation": { + "id": "tableerror2test1", + "fail_message": { + "type": "error", + "focus_parameter_ref": "inlineEditingTableError3[0]", + "message": { + "resource_key": "invalid_subpanel_name", + "default": "fields are 2 or 2.3" + } + }, + "evaluate": { + "and": [ + { + "condition": { + "parameter_ref": "inlineEditingTableError3[0]", + "op": "notEquals", + "value": 2 + } + }, + { + "condition": { + "parameter_ref": "inlineEditingTableError3[1]", + "op": "notEquals", + "value": 2.3 + } + } + ] + } + } + }, + { + "validation": { + "id": "tableerror2test2", + "fail_message": { + "type": "error", + "focus_parameter_ref": "inlineEditingTableError3[2]", + "message": { + "default": "expression contains help" + } + }, + "evaluate": { + "condition": { + "parameter_ref": "inlineEditingTableError3[2]", + "op": "notContains", + "value": "help" + } + } + } + }, + { + "validation": { + "id": "tableerror2test3", + "fail_message": { + "type": "warning", + "focus_parameter_ref": "inlineEditingTableError3[3]", + "message": { + "default": "animal equals horse" + } + }, + "evaluate": { + "condition": { + "parameter_ref": "inlineEditingTableError3[3]", + "op": "notEquals", + "value": "horse" + } + } + } + }, + { + "validation": { + "id": "tableerror2test4", + "fail_message": { + "type": "error", + "focus_parameter_ref": "inlineEditingTableError3[4]", + "message": { + "default": "fruit equals pear" + } + }, + "evaluate": { + "condition": { + "parameter_ref": "inlineEditingTableError3[4]", + "op": "notEquals", + "value": "pear" + } + } + } + }, { "allow_change": { "parameter_refs": [