forked from primer/view_components
-
Notifications
You must be signed in to change notification settings - Fork 1
[DREAM-704] Tree view selection based on path identity breaks use cases where similar paths are allowed #475
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
HDinger
merged 6 commits into
main
from
bug/dream-704-tree-view-selection-based-on-path-identity-breaks-use-cases-where-similar-paths-are-allowed
Jun 24, 2026
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
098da7b
Keep the node object after selection instead of fetching it by the path
HDinger 773e344
Fix single-select toggle-off and form payload disambiguation
HDinger 1f5d29e
Adapt test to avoid being trapped in the focus zone
HDinger e00ca96
Pass nodeId in filterableTreeView as well
HDinger 1af01f9
Generating component snapshots
HDinger 798851b
Generating component snapshots
github-actions[bot] File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| --- | ||
| '@openproject/primer-view-components': minor | ||
| --- | ||
|
|
||
| Fix selection of nodes with the same path in Primer::Alpha::TreeView | ||
|
|
||
| When a `[role=treeitem]` element carries a `data-node-id` attribute, that id is now included as `nodeId` in the hidden form input payload (`{path, nodeId?, value?}`). Trees with duplicate-path nodes should set `data-node-id` to a stable unique identifier so the server can distinguish which node was selected. | ||
|
|
||
| **Breaking change in `TreeViewElement#checkOnlyAtPath`:** if the given path is not found the method is now a no-op. Previously it would uncheck all active nodes before failing to check the missing node, which could be used as an indirect "clear selection" mechanism. Use explicit `setNodeCheckedValue(node, 'false')` calls if that behaviour is needed. | ||
Binary file modified
BIN
+2 Bytes
(100%)
...en_project/filterable_tree_view/custom_segmented_control/dark_high_contrast.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -179,8 +179,8 @@ export class TreeViewElement extends HTMLElement { | |
| // behavior for these element types is user- or browser-defined | ||
| if (!(node instanceof HTMLDivElement)) return | ||
|
|
||
| const path = this.getNodePath(node) | ||
| const nodeInfo = this.infoFromNode(node, 'true') | ||
| const newCheckedValue = this.getNodeCheckedValue(node) === 'true' ? 'false' : 'true' | ||
| const nodeInfo = this.infoFromNode(node, newCheckedValue) | ||
|
|
||
| const checkSuccess = this.dispatchEvent( | ||
| new CustomEvent('treeViewBeforeNodeChecked', { | ||
|
|
@@ -192,10 +192,10 @@ export class TreeViewElement extends HTMLElement { | |
|
|
||
| if (!checkSuccess) return | ||
|
|
||
| if (this.getNodeCheckedValue(node) === 'true') { | ||
| if (newCheckedValue === 'false') { | ||
| this.setNodeCheckedValue(node, 'false') | ||
| } else { | ||
| this.checkOnlyAtPath(path) | ||
| this.#checkNodeOnly(node) | ||
| } | ||
|
|
||
| this.dispatchEvent( | ||
|
|
@@ -271,7 +271,11 @@ export class TreeViewElement extends HTMLElement { | |
| } else if (this.selectVariant(node) === 'single') { | ||
| event.preventDefault() | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (pre-existing) it looks like single-select sets |
||
|
|
||
| this.checkOnlyAtPath(this.getNodePath(node)) | ||
| if (this.getNodeCheckedValue(node) === 'true') { | ||
| this.setNodeCheckedValue(node, 'false') | ||
| } else { | ||
| this.#checkNodeOnly(node) | ||
| } | ||
| } else if (node instanceof HTMLAnchorElement) { | ||
| // simulate click on space | ||
| node.click() | ||
|
|
@@ -313,7 +317,7 @@ export class TreeViewElement extends HTMLElement { | |
| } | ||
|
|
||
| get activeNodes() { | ||
| return document.querySelectorAll('[aria-checked="true"]') | ||
| return this.querySelectorAll('[aria-checked="true"]') | ||
| } | ||
|
|
||
| expandAtPath(path: string[]) { | ||
|
|
@@ -352,11 +356,19 @@ export class TreeViewElement extends HTMLElement { | |
| } | ||
|
|
||
| checkOnlyAtPath(path: string[]) { | ||
| const node = this.nodeAtPath(path) | ||
| if (!node) return | ||
|
HDinger marked this conversation as resolved.
|
||
|
|
||
| this.#checkNodeOnly(node) | ||
| } | ||
|
|
||
| #checkNodeOnly(node: Element) { | ||
| for (const el of this.activeNodes) { | ||
| this.uncheckAtPath(this.getNodePath(el)) | ||
| if (el === node) continue | ||
| this.setNodeCheckedValue(el, 'false') | ||
|
HDinger marked this conversation as resolved.
|
||
| } | ||
|
|
||
| this.checkAtPath(path) | ||
| this.setNodeCheckedValue(node, 'true') | ||
|
HDinger marked this conversation as resolved.
|
||
| } | ||
|
|
||
| toggleCheckedAtPath(path: string[]) { | ||
|
|
@@ -479,10 +491,13 @@ export class TreeViewElement extends HTMLElement { | |
| newInput.removeAttribute('data-target') | ||
| newInput.removeAttribute('form') | ||
|
|
||
| const payload: {path: string[]; value?: string} = { | ||
| const payload: {path: string[]; nodeId?: string; value?: string} = { | ||
| path: this.getNodePath(node), | ||
| } | ||
|
|
||
| const nodeId = node.getAttribute('data-node-id') | ||
| if (nodeId) payload.nodeId = nodeId | ||
|
|
||
|
HDinger marked this conversation as resolved.
|
||
| const inputValue = this.getFormInputValueForNode(node) | ||
| if (inputValue) payload.value = inputValue | ||
|
|
||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
12 changes: 12 additions & 0 deletions
12
previews/primer/alpha/tree_view_preview/doubled_path.html.erb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| <div style="max-width: 400px"> | ||
| <%= render(Primer::Alpha::TreeView.new) do |tree_view| %> | ||
| <% tree_view.with_sub_tree(label: "src", expanded: true, select_variant: :single) do |sub_tree| %> | ||
| <% sub_tree.with_leaf(label: "button.rb", select_variant: :single) %> | ||
| <% sub_tree.with_leaf(label: "icon_button.rb", current: true, select_variant: :single) %> | ||
| <% end %> | ||
|
|
||
| <% tree_view.with_leaf(label: "action_menu.rb", select_variant: :single) %> | ||
| <% tree_view.with_leaf(label: "action_menu.rb", select_variant: :single) %> | ||
| <% tree_view.with_leaf(label: "action_menu.rb", select_variant: :single) %> | ||
| <% end %> | ||
| </div> |
14 changes: 7 additions & 7 deletions
14
previews/primer/alpha/tree_view_preview/form_input.html.erb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.