Skip to content

Commit b633f06

Browse files
mreid21mellis481
andauthored
fix for broken inference on metadata prop (#193)
* rfc-fix for broken inference on metadata prop * updated fix to use type assertion instead of custom forward ref. * fixed missing generics on node * fixed missing generics, added default constraints for all generics --------- Co-authored-by: Mike Ellis <[email protected]>
1 parent 98ce705 commit b633f06

File tree

4 files changed

+36
-30
lines changed

4 files changed

+36
-30
lines changed

src/TreeView/index.tsx

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import {
3434
isBranchNotSelectedAndHasOnlySelectedChild,
3535
getOnSelectTreeAction,
3636
getBranchNodesToExpand,
37+
IFlatMetadata,
3738
} from "./utils";
3839
import { Node } from "./node";
3940
import {
@@ -502,9 +503,9 @@ export interface ITreeViewOnLoadDataProps {
502503
treeState: ITreeViewState;
503504
}
504505

505-
export interface ITreeViewProps {
506+
export interface ITreeViewProps<M extends IFlatMetadata = IFlatMetadata> {
506507
/** Tree data*/
507-
data: INode[];
508+
data: INode<M>[];
508509
/** Function called when a node changes its selected state */
509510
onSelect?: (props: ITreeViewOnSelectProps) => void;
510511
/** Function called when a single node is manually selected/unselected. */
@@ -517,7 +518,7 @@ export interface ITreeViewProps {
517518
/** className to add to the outermost ul */
518519
className?: string;
519520
/** Render prop for the node */
520-
nodeRenderer: (props: INodeRendererProps) => React.ReactNode;
521+
nodeRenderer: (props: INodeRendererProps<M>) => React.ReactNode;
521522
/** Indicates what action will be performed on a node which informs the correct aria-* properties to use on the node (aria-checked if using checkboxes, aria-selected if not). */
522523
nodeAction?: NodeAction;
523524
/** Array with the ids of the default expanded nodes */
@@ -553,8 +554,8 @@ export interface ITreeViewProps {
553554
focusedId?: NodeId;
554555
}
555556

556-
const TreeView = React.forwardRef<HTMLUListElement, ITreeViewProps>(
557-
function TreeView(
557+
const TreeView = React.forwardRef(
558+
function TreeView<M extends IFlatMetadata = IFlatMetadata>(
558559
{
559560
data,
560561
selectedIds,
@@ -579,8 +580,8 @@ const TreeView = React.forwardRef<HTMLUListElement, ITreeViewProps>(
579580
focusedId,
580581
onBlur,
581582
...other
582-
},
583-
ref
583+
}: ITreeViewProps<M>,
584+
ref: React.ForwardedRef<HTMLUListElement>
584585
) {
585586
validateTreeViewData(data);
586587
const nodeRefs = useRef({});
@@ -648,7 +649,7 @@ const TreeView = React.forwardRef<HTMLUListElement, ITreeViewProps>(
648649
<Node
649650
key={`${x}-${typeof x}`}
650651
data={data}
651-
element={getTreeNode(data, x)}
652+
element={getTreeNode(data, x) as INode<M>}
652653
setsize={getTreeParent(data).children.length}
653654
posinset={index + 1}
654655
level={1}
@@ -669,9 +670,12 @@ const TreeView = React.forwardRef<HTMLUListElement, ITreeViewProps>(
669670
/>
670671
))}
671672
</ul>
672-
);
673-
}
674-
);
673+
)
674+
})
675+
676+
677+
678+
675679

676680
const handleKeyDown = ({
677681
data,
@@ -975,6 +979,7 @@ const handleKeyDown = ({
975979
}
976980
};
977981

982+
978983
TreeView.propTypes = {
979984
/** Tree data*/
980985
data: PropTypes.array.isRequired,

src/TreeView/node.tsx

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,14 @@ import {
2222
noop,
2323
propagatedIds,
2424
getOnSelectTreeAction,
25+
IFlatMetadata,
2526
} from "./utils";
2627
import { baseClassNames, clickActions, treeTypes } from "./constants";
2728

28-
export interface INodeProps {
29-
element: INode;
29+
export interface INodeProps<M extends IFlatMetadata = IFlatMetadata> {
30+
element: INode<M>;
3031
dispatch: React.Dispatch<TreeViewAction>;
31-
data: INode[];
32+
data: INode<M>[];
3233
nodeAction: NodeAction;
3334
selectedIds: Set<NodeId>;
3435
tabbableId: NodeId;
@@ -40,7 +41,7 @@ export interface INodeProps {
4041
nodeRefs: INodeRefs;
4142
leafRefs: INodeRefs;
4243
baseClassNames: typeof baseClassNames;
43-
nodeRenderer: (props: INodeRendererProps) => React.ReactNode;
44+
nodeRenderer: (props: INodeRendererProps<M>) => React.ReactNode;
4445
setsize: number;
4546
posinset: number;
4647
level: number;
@@ -53,8 +54,8 @@ export interface INodeProps {
5354
propagateSelectUpwards: boolean;
5455
}
5556

56-
export interface INodeGroupProps
57-
extends Omit<INodeProps, "setsize" | "posinset"> {
57+
export interface INodeGroupProps<M extends IFlatMetadata = IFlatMetadata>
58+
extends Omit<INodeProps<M>, "setsize" | "posinset"> {
5859
getClasses: (className: string) => string;
5960
/** don't send this. The NodeGroup render function, determines it for you */
6061
setsize?: undefined;
@@ -65,15 +66,15 @@ export interface INodeGroupProps
6566
/**
6667
* It's convenient to pass props down to the child, but we don't want to pass everything since it would create incorrect values for setsize and posinset
6768
*/
68-
const removeIrrelevantGroupProps = (
69-
nodeProps: INodeProps
70-
): Omit<INodeGroupProps, "getClasses"> => {
69+
const removeIrrelevantGroupProps = <M extends IFlatMetadata = IFlatMetadata,>(
70+
nodeProps: INodeProps<M>
71+
): Omit<INodeGroupProps<M>, "getClasses"> => {
7172
// eslint-disable-next-line @typescript-eslint/no-unused-vars
7273
const { setsize, posinset, ...rest } = nodeProps;
7374
return rest;
7475
};
7576

76-
export const Node = (props: INodeProps) => {
77+
export const Node = <M extends IFlatMetadata = IFlatMetadata>(props: INodeProps<M>) => {
7778
const {
7879
element,
7980
dispatch,
@@ -311,15 +312,15 @@ export const Node = (props: INodeProps) => {
311312
);
312313
};
313314

314-
export const NodeGroup = ({
315+
export const NodeGroup = <M extends IFlatMetadata = IFlatMetadata>({
315316
data,
316317
element,
317318
expandedIds,
318319
getClasses,
319320
baseClassNames,
320321
level,
321322
...rest
322-
}: INodeGroupProps) => (
323+
}: INodeGroupProps<M>) => (
323324
<ul role="group" className={getClasses(baseClassNames.nodeGroup)}>
324325
{expandedIds.has(element.id) &&
325326
element.children.length > 0 &&

src/TreeView/types.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,9 @@ export type EventCallback = <T, E>(
3333
metadata?: M;
3434
}
3535

36-
export interface INodeRendererProps {
36+
export interface INodeRendererProps<M extends IFlatMetadata = IFlatMetadata> {
3737
/** The object that represents the rendered node */
38-
element: INode;
38+
element: INode<M>;
3939
/** A function which gives back the props to pass to the node */
4040
getNodeProps: (args?: {
4141
onClick?: EventCallback;

src/TreeView/utils.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -307,15 +307,15 @@ export type IFlatMetadata = Record<
307307
string | number | boolean | undefined | null
308308
>;
309309

310-
interface ITreeNode<M extends IFlatMetadata> {
310+
interface ITreeNode<M extends IFlatMetadata = IFlatMetadata> {
311311
id?: NodeId;
312312
name: string;
313313
isBranch?: boolean;
314314
children?: ITreeNode<M>[];
315315
metadata?: M;
316316
}
317317

318-
export const flattenTree = <M extends IFlatMetadata>(
318+
export const flattenTree = <M extends IFlatMetadata = IFlatMetadata>(
319319
tree: ITreeNode<M>
320320
): INode<M>[] => {
321321
let internalCount = 0;
@@ -515,8 +515,8 @@ export const getOnSelectTreeAction = (
515515
return treeTypes.toggleSelect;
516516
};
517517

518-
export const getTreeParent = (data: INode[]): INode => {
519-
const parentNode: INode | undefined = data.find(
518+
export const getTreeParent = <M extends IFlatMetadata = IFlatMetadata>(data: INode<M>[]): INode<M> => {
519+
const parentNode: INode<M> | undefined = data.find(
520520
(node) => node.parent === null
521521
);
522522

@@ -527,7 +527,7 @@ export const getTreeParent = (data: INode[]): INode => {
527527
return parentNode;
528528
};
529529

530-
export const getTreeNode = (data: INode[], id: NodeId): INode => {
530+
export const getTreeNode = <M extends IFlatMetadata = IFlatMetadata>(data: INode<M>[], id: NodeId): INode<M> => {
531531
const treeNode = data.find((node) => node.id === id);
532532

533533
if (treeNode == null) {

0 commit comments

Comments
 (0)