-
Notifications
You must be signed in to change notification settings - Fork 0
feat: search in collapsed nodes #23
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
Conversation
|
Preview is ready. |
|
Visual Tests Report is ready. |
036f12f to
14bacf0
Compare
14bacf0 to
637478c
Compare
|
|
||
| await page.getByTestId('qa:structuredyson:search').locator('input').fill('Attr'); | ||
| // Wait for search to complete | ||
| await page.waitForTimeout(300); |
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.
Isn't it better to wait for some search results like a highlighted text or N matches?
| await page.getByTestId('qa:structuredyson:search').locator('input').fill('attr'); | ||
|
|
||
| // Wait for search to complete | ||
| await page.waitForTimeout(300); |
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.
!
| await page.getByTestId('qa:structuredyson:search:next').click(); | ||
|
|
||
| // Wait for expansion and navigation | ||
| await page.waitForTimeout(300); |
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.
!
| const count = allMatchPaths.filter((matchPath) => { | ||
| // Match if path is exactly the item path (match at the node itself) | ||
| // or starts with prefix (match inside the node) | ||
| return matchPath === item.path || matchPath.startsWith(prefix); |
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.
There is no need to compare the strings twice:
const sameStart = matchPath.startsWith(item.path);
if (item.path.length === matchPath.length ) {
// exact match
return true;
}
if (matchPath[item.path.length] === '/') {
// match inside the node
return true;
}
return false;| if (collapsedState[checkPath]) { | ||
| return checkPath; | ||
| } | ||
| } |
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.
It is better to avoid unnecessary split/join calls:
let nextSlash: number = -1;
while(true) {
nextSlash = matchPath.nextIndex('/', nextSlash + 1)
if (nextSlash === -1) {
break;
}
const checkPath = matchPath.slice(0, nextSlash);
if (collapsedState[checkPath]) {
return checkPath;
}
}| } | ||
| // Calculate next index in total matches | ||
| let nextTotalIndex = matchIndex + diff; | ||
| nextTotalIndex = ((nextTotalIndex % totalMatches) + totalMatches) % totalMatches; |
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.
Isn't it the same for:
const nextTotalIndex = (matchIndex + diff) % totalMatches;?
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.
Cannot simplify ((nextTotalIndex % totalMatches) + totalMatches) % totalMatches because:
- onPrevMatch() calls onNextMatch(null, -1) for backward navigation
- This makes nextTotalIndex = matchIndex + (-1) potentially negative (e.g., when matchIndex = 0)
- JavaScript's % operator returns negative results: (-1) % 5 = -1 (incorrect index)
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.
const nextTotalIndex = (totalMatches + matchIndex + diff) % totalMatches;| this.searchRef.current?.focus(); | ||
| // Target is hidden - expand the collapsed parent | ||
| const newCollapsedState = {...collapsedState}; | ||
| delete newCollapsedState[collapsedParent]; |
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.
As I understand it expands only one level for each call of this.onNextMatch. Can we expand all parents here before call retry this.onNextMatch(...)?
| paths.push(...collectMapMatches(value.$value, filter, settings, isJson, valuePath)); | ||
| } else if (value.$type === 'list' && value.$value) { | ||
| paths.push(...collectListMatches(value.$value, filter, settings, isJson, valuePath)); | ||
| } |
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.
It looks like it is better to addelse {...} block here for primitive types:
} else {
const valueMatch = rowSearchInfo(value, filter, settings);
if (vauleMatch && currentPath) {
paths.push(currentPath);
}
}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.
It looks like you have forgotten about it, haven't you?
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.
sorry! fixed!
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.
We don't need checkPrimitiveMatch function anymore.
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.
Additionally it does unnecessary check
src/utils/flattenUnipika.ts
Outdated
| paths.push(...collectAttributeMatches(value, filter, settings, isJson, currentPath)); | ||
|
|
||
| // Get value path with JSON $ prefix if needed | ||
| const valuePath = getJsonValuePath(value, isJson, currentPath); |
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.
We need to calculate the value only for processing structures.
| /** | ||
| * Collect all paths that contain search matches (including in collapsed nodes) | ||
| */ | ||
| function collectAllMatchPaths( |
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.
It is more efficient to pass paths as an additional parameter to avoid unnecessary memory allocations for internal arrays, like:
function collectAllMatchPaths(
dstPaths: Array<string>,
...
) {
...
return dstPaths;
}| this.tableRef.current?.scrollToIndex(matchedRows[visibleMatchCount]); | ||
| this.searchRef.current?.focus(); | ||
| nextSlash++; | ||
| } |
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.
Is it possible to avoid copy-paste from findCollapsedParent here, like:
const expandCollapsedPath = (path: string) {
if (effectiveCollapsedState === collapsedState) {
effectiveCollapsedState = {...collapsedState};
}
delete effectiveCollapsedState[collapsedParent];
}
let collapsedParent: string | undefined;
while(collapsedParent = this.findCollapsedParent(..., effectiveCollapsedState)) {
expandPath(collapsedParent);
}?
| } | ||
| // Check the full path as well | ||
| if (collapsedState[targetMatchPath]) { | ||
| if (!hasCollapsedParents) { |
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.
if (collapsedPath) {
expandPath(targetMatchPath)
}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.
refactored!
| } | ||
|
|
||
| // If we expanded any parents, recalculate state and retry | ||
| if (hasCollapsedParents) { |
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.
if (collapsedState !== effectiveCollapsedState) {
...| this.setState({matchIndex: nextTotalIndex}); | ||
| this.tableRef.current?.scrollToIndex(matchedRows[visibleMatchCount]); | ||
| this.searchRef.current?.focus(); | ||
| } |
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.
Line ranges 310-326 and 334-346 looks like a copy paste. Isn't it better to extract it to a local helper, like:
const navigateTo = ({matchIndex, matchedRows, collapsedState}) => {
...
}Is there any blockers for such approach?
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.
no blockers!
| paths.push(...collectMapMatches(value.$value, filter, settings, isJson, valuePath)); | ||
| } else if (value.$type === 'list' && value.$value) { | ||
| paths.push(...collectListMatches(value.$value, filter, settings, isJson, valuePath)); | ||
| } |
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.
It looks like you have forgotten about it, haven't you?
| } | ||
| }); | ||
| return; | ||
| } |
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.
Shouldn't we add else here for next call?
I see there is return in the end of if block
No description provided.