-
Notifications
You must be signed in to change notification settings - Fork 276
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
fix(tree-select): [tree-select] fix deleteTag not working #2543
base: dev
Are you sure you want to change the base?
Conversation
WalkthroughThe pull request introduces significant updates to the tree select component across multiple files. Key changes include modifications to the Changes
Possibly related PRs
Suggested reviewers
Poem
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
[e2e-test-warn] The title of the Pull request should look like "fix(vue-renderless): [action-menu, alert] fix xxx bug". Please make sure you've read our contributing guide |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Outside diff range and nitpick comments (5)
packages/renderless/src/tree-select/vue.ts (2)
14-14
: Consider adding documentation for modelValue stateThe change to use
modelValue
aligns with Vue 3 conventions. Consider adding a comment to document whether this array holds selected node values or node objects.- modelValue: [] + modelValue: [], // Holds selected node values/objects for v-model binding
35-45
: Consider adding null checks and type validationThe watcher implementation correctly handles both multiple and single selection modes. However, consider adding null checks and type validation for more robust error handling.
watch( () => props.modelValue, () => { + if (props.modelValue === null || props.modelValue === undefined) { + state.modelValue = props.multiple ? [] : null; + return; + } if (props.multiple && Array.isArray(props.modelValue)) { state.modelValue = [...props.modelValue] } else { + if (props.multiple) { + console.warn('modelValue should be an array when multiple is true'); + state.modelValue = []; + return; + } state.modelValue = props.modelValue } }, { immediate: true, deep: true } )packages/vue/src/tree-select/src/pc.vue (2)
5-5
: LGTM! Consider adding migration docs.The change from
state.value
tostate.modelValue
correctly aligns with Vue 3's v-model convention.Consider adding a note in the migration guide about this change if upgrading from Vue 2, as it might affect existing implementations.
Line range hint
1-67
: Add test coverage for deleteTag functionality.The PR objectives indicate that tests haven't been added. Please add test cases to cover:
- deleteTag functionality with check-strictly enabled/disabled
- Multiple selection scenarios
- Edge cases (empty selection, all tags deleted)
Would you like me to help generate test cases for these scenarios?
packages/renderless/src/tree-select/index.ts (1)
Line range hint
118-130
: Refactor function to improve type consistency and clarityThe current implementation has several issues:
- Inconsistent return types between array and non-array cases
- Implicit dependency on
props.modelValue
in non-array case- Function name doesn't accurately reflect its purpose
Consider this improved implementation:
export const getCheckedData = ({ props, state }) => (selected) => { - const checkedKey = [] - - if (!Array.isArray(selected)) { - return props.modelValue ? [props.modelValue] : [selected[props.valueField]] - } else { - selected.length > 0 && - selected.forEach((item) => { - checkedKey.push(item[props.valueField]) - }) - - return checkedKey - } + if (!selected) { + return [] + } + + const items = Array.isArray(selected) ? selected : [selected] + return items.map(item => item[props.valueField]) }Also consider renaming the function to
getSelectedValues
to better reflect its purpose.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (3)
packages/renderless/src/tree-select/index.ts
(3 hunks)packages/renderless/src/tree-select/vue.ts
(4 hunks)packages/vue/src/tree-select/src/pc.vue
(2 hunks)
🔇 Additional comments (5)
packages/renderless/src/tree-select/vue.ts (3)
25-26
: LGTM! API extension follows consistent pattern
The watchValue
method is properly integrated into the API object with the required dependencies.
1-1
: Verify the watchValue implementation in index.ts
The import statement correctly includes the new watchValue
function.
Let's verify the implementation of watchValue
in the index file:
✅ Verification successful
watchValue
implementation is correctly handling tag deletion in multiple selection mode
The implementation in index.ts
shows that watchValue
properly handles the tag deletion scenario by:
- Correctly processing the new value after tag deletion through array handling
- Properly updating both the base select component and tree component states
- Maintaining synchronization between the tree's checked state and select's selected items
The function implementation aligns with the PR's objective to fix the "deleteTag not working" issue by ensuring proper state updates when tags are removed in multiple selection mode.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check the implementation of watchValue in index.ts
ast-grep --pattern 'export const watchValue = $_' packages/renderless/src/tree-select/index.ts
Length of output: 2047
47-47
: Verify deleteTag interaction with modelValue watcher
The watcher chain looks correct for handling tag deletion: when a tag is deleted, it should update state.modelValue, which triggers this watcher to call api.watchValue, ultimately syncing the tree's checked state.
Let's verify the deleteTag interaction:
✅ Verification successful
Tag deletion is handled through base-select component
After examining the implementation, I notice that tag deletion is not directly handled in the tree-select component. Instead, it relies on the base-select component (baseSelectRef
) which emits updates to modelValue
. When modelValue
changes, the watcher correctly triggers watchValue
which:
- Maps the new values to tree nodes
- Updates the selected data in base-select
- Syncs the tree's checked state via
setCheckedKeys
The interaction chain is complete and working as expected.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check the deleteTag implementation and its interaction with modelValue
# Look for deleteTag handler and its usage of modelValue
rg -A 5 "deleteTag|handleTagClose" packages/renderless/src/tree-select/
Length of output: 73
Script:
#!/bin/bash
# Let's try a broader search for tag-related operations in the tree-select component
ast-grep --pattern 'function $_($$_) {
$$$
modelValue
$$$
}' packages/renderless/src/tree-select/
# Also search for any tag-related methods
rg -A 10 "tag" packages/renderless/src/tree-select/
Length of output: 157
Script:
#!/bin/bash
# Let's examine the entire tree-select API and state implementation
cat packages/renderless/src/tree-select/vue.ts
# Also check for any tag-related functionality in the interface definitions
cat packages/renderless/src/tree-select/interface.ts
Length of output: 1658
Script:
#!/bin/bash
# Let's examine the index file which contains the actual implementation of watchValue and other methods
cat packages/renderless/src/tree-select/index.ts
Length of output: 4925
packages/vue/src/tree-select/src/pc.vue (1)
14-14
: Verify the impact of check-strictly on deleteTag functionality.
The addition of :check-strictly
binding could affect the tree's selection behavior. Since this PR aims to fix the deleteTag functionality:
- Please verify that this change resolves the deleteTag issue
- Document the expected behavior with check-strictly enabled/disabled
Let's verify the usage and documentation of this prop:
✅ Verification successful
Based on the code analysis, I can now generate the final response:
The check-strictly prop addition is valid and improves tree-select's functionality
The check-strictly
prop is a standard tree component feature that controls parent-child node selection behavior:
- When true: Parent and child node selections are independent
- When false (default): Selecting a parent auto-selects children and vice versa
This addition:
- Properly passes through the
checkStrictly
configuration fromtreeOp
props to the underlying tree component - Aligns with the tree component's documented behavior
- Gives users control over whether tag deletion should affect parent/child nodes
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Search for existing documentation or tests related to check-strictly
rg -l "check.*strictly" docs/
# Search for related test cases
rg -l "deleteTag.*check.*strictly" tests/
# Look for similar implementations in the codebase
ast-grep --pattern 'check-strictly="$_"'
Length of output: 211
Script:
#!/bin/bash
# Let's first find the correct documentation and test directories
fd -t d -i "docs\|documentation"
fd -t d -i "test"
# Search for check-strictly usage in the codebase
rg -i "checkStrictly|check-strictly" -A 3
# Look for tree-select component documentation
fd -t f "tree-select"
# Search for deleteTag implementation
rg "deleteTag" -A 5
Length of output: 89383
Script:
#!/bin/bash
# Let's look at the tree-select implementation and documentation
rg -A 5 "checkStrictly" packages/vue/src/tree-select/
rg -A 5 "deleteTag" packages/vue/src/tree-select/
# Check tree component's checkStrictly behavior
rg -A 5 "checkStrictly" packages/vue/src/tree/src/
# Look for tree-select tests
fd -t f "tree-select.*test"
Length of output: 1992
packages/renderless/src/tree-select/index.ts (1)
Line range hint 136-172
: Add null checks and extract duplicate logic
The function has several potential issues:
- Missing null check before accessing data properties in single selection mode
- Duplicate logic with
watchValue
for mapping selected nodes - No error handling for missing refs
Add null checks for single selection mode:
} else {
const data = api.getPluginOption(state.modelValue)[0]
+ if (!data) {
+ return
+ }
vm.$refs.baseSelectRef.updateSelectedData({
Extract the duplicate node mapping logic into a shared function:
const mapNodeToSelected = (node, props) => ({
...node,
currentLabel: node[props.textField],
value: node[props.valueField],
isTree: true
})
Let's verify the refs usage pattern across the codebase:
✅ Verification successful
Based on the search results, I can see that the codebase consistently performs null checks before accessing $refs
properties. Let me verify the specific pattern for tree-select by searching for its usage:
Let me verify the null check pattern in the tree-select component's implementation:
Based on the code analysis, I can now generate the final response:
Add null checks and error handling for refs access
The code needs to add null checks before accessing $refs
properties to prevent potential runtime errors:
} else {
const data = api.getPluginOption(state.modelValue)[0]
+ if (!data || !vm.$refs.baseSelectRef) {
+ return
+ }
vm.$refs.baseSelectRef.updateSelectedData({
...data,
currentLabel: data[props.textField],
value: data[props.valueField],
The same pattern should be applied to all $refs
accesses in the component:
- Add null check for
vm.$refs.baseSelectRef
before calling its methods - Add null check for
vm.$refs.treeRef
before calling its methods - Add null check for data objects before accessing their properties
This follows the established pattern seen across the codebase where refs are consistently checked before use.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Search for error handling patterns around $refs usage
rg -A 2 '\$refs\.' --type ts
Length of output: 103211
Script:
#!/bin/bash
# Search for tree-select refs usage patterns
rg -A 3 '\$refs\.' --type ts packages/renderless/src/tree-select/
Length of output: 2178
Script:
#!/bin/bash
# Search for the implementation context around refs usage in tree-select
rg -B 3 -A 3 '\$refs\.baseSelectRef\.updateSelectedData' packages/renderless/src/tree-select/index.ts
Length of output: 1025
export const watchValue = | ||
({ api, props, vm, state }) => | ||
(newValue, oldValue) => { | ||
if (props.multiple) { | ||
let initialNodes = [] | ||
if (Array.isArray(newValue)) { | ||
newValue.forEach((value) => { | ||
const option = api.getPluginOption(value) | ||
initialNodes = initialNodes.concat(option) | ||
}) | ||
} | ||
|
||
const selected = initialNodes.map((node) => { | ||
return { | ||
...node, | ||
currentLabel: node[props.textField], | ||
value: node[props.valueField], | ||
isTree: true | ||
} | ||
}) | ||
|
||
vm.$refs.baseSelectRef.updateSelectedData(selected) | ||
|
||
vm.$refs.treeRef.setCheckedKeys(newValue) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Handle single selection mode and value removal
The current implementation has several limitations:
- Only handles multiple selection mode
- Doesn't properly handle value removal (the original bug)
- Potential race condition between
updateSelectedData
andsetCheckedKeys
Consider this enhanced implementation:
export const watchValue =
({ api, props, vm, state }) =>
(newValue, oldValue) => {
+ // Handle value removal
+ if (!newValue || (Array.isArray(newValue) && newValue.length === 0)) {
+ vm.$refs.baseSelectRef.updateSelectedData([])
+ vm.$refs.treeRef.setCheckedKeys([])
+ return
+ }
+
if (props.multiple) {
let initialNodes = []
if (Array.isArray(newValue)) {
newValue.forEach((value) => {
const option = api.getPluginOption(value)
initialNodes = initialNodes.concat(option)
})
}
const selected = initialNodes.map((node) => {
return {
...node,
currentLabel: node[props.textField],
value: node[props.valueField],
isTree: true
}
})
vm.$refs.baseSelectRef.updateSelectedData(selected)
-
vm.$refs.treeRef.setCheckedKeys(newValue)
+ } else {
+ const data = api.getPluginOption(newValue)[0]
+ if (!data) {
+ vm.$refs.baseSelectRef.updateSelectedData(null)
+ return
+ }
+ vm.$refs.baseSelectRef.updateSelectedData({
+ ...data,
+ currentLabel: data[props.textField],
+ value: data[props.valueField],
+ state: {
+ currentLabel: data[props.textField]
+ }
+ })
}
}
This implementation:
- Properly handles value removal (fixes the deleteTag bug)
- Adds support for single selection mode
- Adds null checks
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
export const watchValue = | |
({ api, props, vm, state }) => | |
(newValue, oldValue) => { | |
if (props.multiple) { | |
let initialNodes = [] | |
if (Array.isArray(newValue)) { | |
newValue.forEach((value) => { | |
const option = api.getPluginOption(value) | |
initialNodes = initialNodes.concat(option) | |
}) | |
} | |
const selected = initialNodes.map((node) => { | |
return { | |
...node, | |
currentLabel: node[props.textField], | |
value: node[props.valueField], | |
isTree: true | |
} | |
}) | |
vm.$refs.baseSelectRef.updateSelectedData(selected) | |
vm.$refs.treeRef.setCheckedKeys(newValue) | |
} | |
} | |
export const watchValue = | |
({ api, props, vm, state }) => | |
(newValue, oldValue) => { | |
// Handle value removal | |
if (!newValue || (Array.isArray(newValue) && newValue.length === 0)) { | |
vm.$refs.baseSelectRef.updateSelectedData([]) | |
vm.$refs.treeRef.setCheckedKeys([]) | |
return | |
} | |
if (props.multiple) { | |
let initialNodes = [] | |
if (Array.isArray(newValue)) { | |
newValue.forEach((value) => { | |
const option = api.getPluginOption(value) | |
initialNodes = initialNodes.concat(option) | |
}) | |
} | |
const selected = initialNodes.map((node) => { | |
return { | |
...node, | |
currentLabel: node[props.textField], | |
value: node[props.valueField], | |
isTree: true | |
} | |
}) | |
vm.$refs.baseSelectRef.updateSelectedData(selected) | |
} else { | |
const data = api.getPluginOption(newValue)[0] | |
if (!data) { | |
vm.$refs.baseSelectRef.updateSelectedData(null) | |
return | |
} | |
vm.$refs.baseSelectRef.updateSelectedData({ | |
...data, | |
currentLabel: data[props.textField], | |
value: data[props.valueField], | |
state: { | |
currentLabel: data[props.textField] | |
} | |
}) | |
} | |
} |
PR
PR Checklist
Please check if your PR fulfills the following requirements:
PR Type
What kind of change does this PR introduce?
What is the current behavior?
Issue Number: N/A
What is the new behavior?
Does this PR introduce a breaking change?
Other information
Summary by CodeRabbit
New Features
watchValue
function for real-time updates in multi-select scenarios.:check-strictly
prop to influence node selection behavior.Bug Fixes
Documentation
modelValue
andselected
parameters in the tree select component.