mirror of https://github.com/grafana/grafana.git
Scopes: Add filterNode and toggleExpandNode to ScopesSelectorService (#111553)
Add filterNode and toggleExpandNode to ScopesSelectorService
This commit is contained in:
parent
47e6528c74
commit
60fed679c4
|
@ -31,7 +31,7 @@ export function useScopeServicesState() {
|
|||
},
|
||||
};
|
||||
}
|
||||
const { updateNode, selectScope, resetSelection, searchAllNodes, deselectScope, apply } =
|
||||
const { updateNode, filterNode, selectScope, resetSelection, searchAllNodes, deselectScope, apply } =
|
||||
services.scopesSelectorService;
|
||||
const selectorServiceState: ScopesSelectorServiceState | undefined = useObservable(
|
||||
services.scopesSelectorService.stateObservable ?? new Observable(),
|
||||
|
@ -39,6 +39,7 @@ export function useScopeServicesState() {
|
|||
);
|
||||
|
||||
return {
|
||||
filterNode,
|
||||
updateNode,
|
||||
selectScope,
|
||||
resetSelection,
|
||||
|
|
|
@ -65,10 +65,11 @@ export const ScopesSelector = () => {
|
|||
removeAllScopes,
|
||||
closeAndApply,
|
||||
closeAndReset,
|
||||
updateNode,
|
||||
filterNode,
|
||||
selectScope,
|
||||
deselectScope,
|
||||
getRecentScopes,
|
||||
toggleExpandedNode,
|
||||
} = scopesSelectorService;
|
||||
|
||||
const recentScopes = getRecentScopes();
|
||||
|
@ -128,12 +129,13 @@ export const ScopesSelector = () => {
|
|||
<ScopesTree
|
||||
tree={tree}
|
||||
loadingNodeName={loadingNodeName}
|
||||
onNodeUpdate={updateNode}
|
||||
filterNode={filterNode}
|
||||
recentScopes={recentScopes}
|
||||
selectedScopes={selectedScopes}
|
||||
scopeNodes={nodes}
|
||||
selectScope={selectScope}
|
||||
deselectScope={deselectScope}
|
||||
toggleExpandedNode={toggleExpandedNode}
|
||||
onRecentScopesSelect={(scopeIds: string[], parentNodeId?: string) => {
|
||||
scopesSelectorService.changeScopes(scopeIds, parentNodeId);
|
||||
scopesSelectorService.closeAndReset();
|
||||
|
|
|
@ -99,6 +99,61 @@ export class ScopesSelectorService extends ScopesServiceBase<ScopesSelectorServi
|
|||
}
|
||||
};
|
||||
|
||||
// Resets query and toggles expanded state of a node
|
||||
public toggleExpandedNode = async (scopeNodeId: string) => {
|
||||
const path = getPathOfNode(scopeNodeId, this.state.nodes);
|
||||
const nodeToToggle = treeNodeAtPath(this.state.tree!, path);
|
||||
|
||||
if (!nodeToToggle) {
|
||||
throw new Error(`Node ${scopeNodeId} not found in tree`);
|
||||
}
|
||||
|
||||
if (nodeToToggle.scopeNodeId !== '' && !isNodeExpandable(this.state.nodes[nodeToToggle.scopeNodeId])) {
|
||||
throw new Error(`Trying to expand node at id ${scopeNodeId} that is not expandable`);
|
||||
}
|
||||
|
||||
// Collapse if expanded
|
||||
if (nodeToToggle.expanded) {
|
||||
const newTree = modifyTreeNodeAtPath(this.state.tree!, path, (treeNode) => {
|
||||
treeNode.expanded = false;
|
||||
// Resets query when collapsing
|
||||
treeNode.query = '';
|
||||
});
|
||||
this.updateState({ tree: newTree });
|
||||
return;
|
||||
}
|
||||
|
||||
// Expand if collapsed
|
||||
const newTree = modifyTreeNodeAtPath(this.state.tree!, path, (treeNode) => {
|
||||
treeNode.expanded = true;
|
||||
treeNode.query = '';
|
||||
});
|
||||
this.updateState({ tree: newTree });
|
||||
|
||||
await this.loadNodeChildren(path, nodeToToggle);
|
||||
};
|
||||
|
||||
public filterNode = async (scopeNodeId: string, query: string) => {
|
||||
const path = getPathOfNode(scopeNodeId, this.state.nodes);
|
||||
const nodeToFilter = treeNodeAtPath(this.state.tree!, path);
|
||||
|
||||
if (!nodeToFilter) {
|
||||
throw new Error(`Trying to filter node at path or id ${scopeNodeId} not found`);
|
||||
}
|
||||
|
||||
if (nodeToFilter.scopeNodeId !== '' && !isNodeExpandable(this.state.nodes[nodeToFilter.scopeNodeId])) {
|
||||
throw new Error(`Trying to filter node at id ${scopeNodeId} that is not expandable`);
|
||||
}
|
||||
|
||||
const newTree = modifyTreeNodeAtPath(this.state.tree!, path, (treeNode) => {
|
||||
treeNode.expanded = true;
|
||||
treeNode.query = query;
|
||||
});
|
||||
this.updateState({ tree: newTree });
|
||||
|
||||
await this.loadNodeChildren(path, nodeToFilter, query);
|
||||
};
|
||||
|
||||
private expandOrFilterNode = async (scopeNodeId: string, query?: string) => {
|
||||
this.interactionProfiler?.startInteraction('scopeNodeDiscovery');
|
||||
|
||||
|
@ -135,15 +190,16 @@ export class ScopesSelectorService extends ScopesServiceBase<ScopesSelectorServi
|
|||
const path = getPathOfNode(scopeNodeId, this.state.nodes);
|
||||
|
||||
const nodeToCollapse = treeNodeAtPath(this.state.tree!, path);
|
||||
if (nodeToCollapse) {
|
||||
const newTree = modifyTreeNodeAtPath(this.state.tree!, path, (treeNode) => {
|
||||
treeNode.expanded = false;
|
||||
treeNode.query = '';
|
||||
});
|
||||
this.updateState({ tree: newTree });
|
||||
} else {
|
||||
|
||||
if (!nodeToCollapse) {
|
||||
throw new Error(`Trying to collapse node at path or id ${scopeNodeId} not found`);
|
||||
}
|
||||
|
||||
const newTree = modifyTreeNodeAtPath(this.state.tree!, path, (treeNode) => {
|
||||
treeNode.expanded = false;
|
||||
treeNode.query = '';
|
||||
});
|
||||
this.updateState({ tree: newTree });
|
||||
};
|
||||
|
||||
private loadNodeChildren = async (path: string[], treeNode: TreeNode, query?: string) => {
|
||||
|
@ -165,7 +221,7 @@ export class ScopesSelectorService extends ScopesServiceBase<ScopesSelectorServi
|
|||
expanded: false,
|
||||
scopeNodeId: node.metadata.name,
|
||||
// Only set query on tree nodes if parent already has children (filtering vs first expansion). This is used for saerch highlighting.
|
||||
query: '',
|
||||
query: query || '',
|
||||
children: undefined,
|
||||
};
|
||||
}
|
||||
|
@ -249,7 +305,7 @@ export class ScopesSelectorService extends ScopesServiceBase<ScopesSelectorServi
|
|||
this.updateState({ selectedScopes: newSelectedScopes });
|
||||
};
|
||||
|
||||
// TODO: We should split this into two functions: expandNode and filterNode.
|
||||
// TODO: Replace all usage of this function with expandNode and filterNode.
|
||||
// @deprecated
|
||||
public updateNode = async (scopeNodeId: string, expanded: boolean, query: string) => {
|
||||
if (expanded) {
|
||||
|
|
|
@ -18,7 +18,7 @@ export interface ScopesTreeProps {
|
|||
selectedScopes: SelectedScope[];
|
||||
scopeNodes: NodesMap;
|
||||
|
||||
onNodeUpdate: (scopeNodeId: string, expanded: boolean, query: string) => void;
|
||||
filterNode: (scopeNodeId: string, query: string) => void;
|
||||
|
||||
selectScope: (scopeNodeId: string) => void;
|
||||
deselectScope: (scopeNodeId: string) => void;
|
||||
|
@ -26,6 +26,8 @@ export interface ScopesTreeProps {
|
|||
// Recent scopes are only shown at the root node
|
||||
recentScopes?: Scope[][];
|
||||
onRecentScopesSelect?: (scopeIds: string[], parentNodeId?: string) => void;
|
||||
|
||||
toggleExpandedNode: (scopeNodeId: string) => void;
|
||||
}
|
||||
|
||||
export function ScopesTree({
|
||||
|
@ -34,10 +36,11 @@ export function ScopesTree({
|
|||
selectedScopes,
|
||||
recentScopes,
|
||||
onRecentScopesSelect,
|
||||
onNodeUpdate,
|
||||
filterNode,
|
||||
scopeNodes,
|
||||
selectScope,
|
||||
deselectScope,
|
||||
toggleExpandedNode,
|
||||
}: ScopesTreeProps) {
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
|
@ -75,7 +78,7 @@ export function ScopesTree({
|
|||
treeQuery: tree.query,
|
||||
scopeNodes,
|
||||
selectedScopes,
|
||||
onNodeUpdate,
|
||||
toggleExpandedNode,
|
||||
selectScope,
|
||||
deselectScope,
|
||||
});
|
||||
|
@ -91,7 +94,7 @@ export function ScopesTree({
|
|||
<ScopesTreeSearch
|
||||
anyChildExpanded={anyChildExpanded}
|
||||
searchArea={searchArea}
|
||||
onNodeUpdate={onNodeUpdate}
|
||||
filterNode={filterNode}
|
||||
treeNode={tree}
|
||||
aria-controls={`${selectedNodesToShowId} ${childrenArrayId}`}
|
||||
aria-activedescendant={ariaActiveDescendant}
|
||||
|
@ -114,11 +117,12 @@ export function ScopesTree({
|
|||
anyChildExpanded={anyChildExpanded}
|
||||
lastExpandedNode={lastExpandedNode}
|
||||
loadingNodeName={loadingNodeName}
|
||||
onNodeUpdate={onNodeUpdate}
|
||||
filterNode={filterNode}
|
||||
selectedScopes={selectedScopes}
|
||||
scopeNodes={scopeNodes}
|
||||
selectScope={selectScope}
|
||||
deselectScope={deselectScope}
|
||||
toggleExpandedNode={toggleExpandedNode}
|
||||
maxHeight={`${Math.min(5, selectedNodesToShow.length) * 30}px`}
|
||||
highlightedId={highlightedId}
|
||||
id={selectedNodesToShowId}
|
||||
|
@ -136,10 +140,11 @@ export function ScopesTree({
|
|||
anyChildExpanded={anyChildExpanded}
|
||||
lastExpandedNode={lastExpandedNode}
|
||||
loadingNodeName={loadingNodeName}
|
||||
onNodeUpdate={onNodeUpdate}
|
||||
filterNode={filterNode}
|
||||
selectedScopes={selectedScopes}
|
||||
scopeNodes={scopeNodes}
|
||||
selectScope={selectScope}
|
||||
toggleExpandedNode={toggleExpandedNode}
|
||||
deselectScope={deselectScope}
|
||||
maxHeight={'100%'}
|
||||
highlightedId={highlightedId}
|
||||
|
|
|
@ -18,22 +18,24 @@ export interface ScopesTreeItemProps {
|
|||
selectedScopes: SelectedScope[];
|
||||
highlighted: boolean;
|
||||
|
||||
onNodeUpdate: (scopeNodeId: string, expanded: boolean, query: string) => void;
|
||||
filterNode: (scopeNodeId: string, query: string) => void;
|
||||
selectScope: (scopeNodeId: string) => void;
|
||||
deselectScope: (scopeNodeId: string) => void;
|
||||
toggleExpandedNode: (scopeNodeId: string) => void;
|
||||
}
|
||||
|
||||
export function ScopesTreeItem({
|
||||
anyChildExpanded,
|
||||
loadingNodeName,
|
||||
treeNode,
|
||||
onNodeUpdate,
|
||||
filterNode,
|
||||
scopeNodes,
|
||||
selected,
|
||||
selectedScopes,
|
||||
selectScope,
|
||||
deselectScope,
|
||||
highlighted,
|
||||
toggleExpandedNode,
|
||||
}: ScopesTreeItemProps) {
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
|
@ -126,7 +128,7 @@ export function ScopesTreeItem({
|
|||
data-testid={`scopes-tree-${treeNode.scopeNodeId}-expand`}
|
||||
aria-label={treeNode.expanded ? t('scopes.tree.collapse', 'Collapse') : t('scopes.tree.expand', 'Expand')}
|
||||
onClick={() => {
|
||||
onNodeUpdate(treeNode.scopeNodeId, !treeNode.expanded, treeNode.query);
|
||||
toggleExpandedNode(treeNode.scopeNodeId);
|
||||
}}
|
||||
>
|
||||
<Icon name={!treeNode.expanded ? 'angle-right' : 'angle-down'} />
|
||||
|
@ -145,11 +147,12 @@ export function ScopesTreeItem({
|
|||
<ScopesTree
|
||||
tree={treeNode}
|
||||
loadingNodeName={loadingNodeName}
|
||||
onNodeUpdate={onNodeUpdate}
|
||||
filterNode={filterNode}
|
||||
scopeNodes={scopeNodes}
|
||||
selectedScopes={selectedScopes}
|
||||
selectScope={selectScope}
|
||||
deselectScope={deselectScope}
|
||||
toggleExpandedNode={toggleExpandedNode}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
@ -15,11 +15,12 @@ type Props = {
|
|||
maxHeight: string;
|
||||
selectedScopes: SelectedScope[];
|
||||
scopeNodes: NodesMap;
|
||||
onNodeUpdate: (scopeNodeId: string, expanded: boolean, query: string) => void;
|
||||
filterNode: (scopeNodeId: string, query: string) => void;
|
||||
selectScope: (scopeNodeId: string) => void;
|
||||
deselectScope: (scopeNodeId: string) => void;
|
||||
highlightedId: string | undefined;
|
||||
id: string;
|
||||
toggleExpandedNode: (scopeNodeId: string) => void;
|
||||
};
|
||||
|
||||
export function ScopesTreeItemList({
|
||||
|
@ -30,11 +31,12 @@ export function ScopesTreeItemList({
|
|||
selectedScopes,
|
||||
scopeNodes,
|
||||
loadingNodeName,
|
||||
onNodeUpdate,
|
||||
filterNode,
|
||||
selectScope,
|
||||
deselectScope,
|
||||
highlightedId,
|
||||
id,
|
||||
toggleExpandedNode,
|
||||
}: Props) {
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
|
@ -65,10 +67,11 @@ export function ScopesTreeItemList({
|
|||
scopeNodes={scopeNodes}
|
||||
loadingNodeName={loadingNodeName}
|
||||
anyChildExpanded={anyChildExpanded}
|
||||
onNodeUpdate={onNodeUpdate}
|
||||
filterNode={filterNode}
|
||||
selectScope={selectScope}
|
||||
deselectScope={deselectScope}
|
||||
highlighted={childNode.scopeNodeId === highlightedId}
|
||||
toggleExpandedNode={toggleExpandedNode}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
|
|
@ -12,7 +12,7 @@ export interface ScopesTreeSearchProps {
|
|||
anyChildExpanded: boolean;
|
||||
searchArea: string;
|
||||
treeNode: TreeNode;
|
||||
onNodeUpdate: (scopeNodeId: string, expanded: boolean, query: string) => void;
|
||||
filterNode: (scopeNodeId: string, query: string) => void;
|
||||
onFocus: () => void;
|
||||
onBlur: () => void;
|
||||
'aria-controls': string;
|
||||
|
@ -22,7 +22,7 @@ export interface ScopesTreeSearchProps {
|
|||
export function ScopesTreeSearch({
|
||||
anyChildExpanded,
|
||||
treeNode,
|
||||
onNodeUpdate,
|
||||
filterNode,
|
||||
searchArea,
|
||||
onFocus,
|
||||
onBlur,
|
||||
|
@ -45,7 +45,7 @@ export function ScopesTreeSearch({
|
|||
useDebounce(
|
||||
() => {
|
||||
if (inputState.dirty) {
|
||||
onNodeUpdate(treeNode.scopeNodeId, true, inputState.value);
|
||||
filterNode(treeNode.scopeNodeId, inputState.value);
|
||||
}
|
||||
},
|
||||
500,
|
||||
|
|
|
@ -11,7 +11,7 @@ interface UseScopesHighlightingParams {
|
|||
treeQuery: string;
|
||||
scopeNodes: NodesMap;
|
||||
selectedScopes: SelectedScope[];
|
||||
onNodeUpdate: (scopeNodeId: string, expanded: boolean, query: string) => void;
|
||||
toggleExpandedNode: (scopeNodeId: string) => void;
|
||||
selectScope: (scopeNodeId: string) => void;
|
||||
deselectScope: (scopeNodeId: string) => void;
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ export function useScopesHighlighting({
|
|||
treeQuery,
|
||||
scopeNodes,
|
||||
selectedScopes,
|
||||
onNodeUpdate,
|
||||
toggleExpandedNode,
|
||||
selectScope,
|
||||
deselectScope,
|
||||
}: UseScopesHighlightingParams) {
|
||||
|
@ -47,7 +47,7 @@ export function useScopesHighlighting({
|
|||
isNodeExpandable(scopeNodes[nodeId]);
|
||||
|
||||
if (isExpanding || isSelectingAndExpandable) {
|
||||
onNodeUpdate(nodeId, true, treeQuery);
|
||||
toggleExpandedNode(nodeId);
|
||||
setHighlightEnabled(false);
|
||||
return;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue