Skip to content
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

bugfix: converts value to be a string instead of a generic value #28257

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "prerelease",
"comment": "bugfix: makes value property on TreeItem less generic to simplify internals",
"packageName": "@fluentui/react-tree",
"email": "[email protected]",
"dependentChangeType": "patch"
}
93 changes: 32 additions & 61 deletions packages/react-components/react-tree/etc/react-tree.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,44 +27,43 @@ import { ProviderProps } from 'react';
import * as React_2 from 'react';
import type { Slot } from '@fluentui/react-utilities';
import type { SlotClassNames } from '@fluentui/react-utilities';
import { SlotRenderFunction } from '@fluentui/react-utilities';

// @public
export const flattenTree_unstable: <Props extends TreeItemProps<unknown>>(items: NestedTreeItem<Props>[]) => FlattenedTreeItem<Props>[];
export const flattenTree_unstable: <Props extends TreeItemProps>(items: NestedTreeItem<Props>[]) => FlattenedTreeItem<Props>[];

// @public
export type FlatTree<Props extends FlatTreeItemProps<unknown> = FlatTreeItemProps> = {
getTreeProps(): FlatTreeProps<Props['value']>;
navigate(data: TreeNavigationData_unstable<Props['value']>): void;
getNextNavigableItem(visibleItems: FlatTreeItem<Props>[], data: TreeNavigationData_unstable<Props['value']>): FlatTreeItem<Props> | undefined;
export type FlatTree<Props extends FlatTreeItemProps = FlatTreeItemProps> = {
getTreeProps(): FlatTreeProps;
navigate(data: TreeNavigationData_unstable): void;
getNextNavigableItem(visibleItems: FlatTreeItem<Props>[], data: TreeNavigationData_unstable): FlatTreeItem<Props> | undefined;
getElementFromItem(item: FlatTreeItem<Props>): HTMLElement | null;
items(): IterableIterator<FlatTreeItem<Props>>;
};

// @public
export type FlatTreeItem<Props extends FlatTreeItemProps<unknown> = FlatTreeItemProps> = {
export type FlatTreeItem<Props extends FlatTreeItemProps = FlatTreeItemProps> = {
index: number;
level: number;
childrenSize: number;
value: Props['value'];
parentValue: Props['parentValue'];
ref: React_2.RefObject<HTMLDivElement>;
getTreeItemProps(): Required<Pick<Props, 'value' | 'aria-setsize' | 'aria-level' | 'aria-posinset' | 'itemType'>> & Omit<Props, 'parentValue'>;
value: TreeItemValue;
parentValue: TreeItemValue | undefined;
getTreeItemProps(): Required<Pick<Props, 'value' | 'aria-setsize' | 'aria-level' | 'aria-posinset' | 'itemType'>> & Omit<Props, 'parentId'>;
};

// @public (undocumented)
export type FlatTreeItemProps<Value = string> = Omit<TreeItemProps<Value>, 'itemType'> & Partial<Pick<TreeItemProps<Value>, 'itemType'>> & {
value: Value;
parentValue?: Value;
export type FlatTreeItemProps = Omit<TreeItemProps, 'itemType'> & Partial<Pick<TreeItemProps, 'itemType'>> & {
value: TreeItemValue;
parentValue?: TreeItemValue;
};

// @public (undocumented)
export type FlatTreeProps<Value = string> = Required<Pick<TreeProps<Value>, 'openItems' | 'onOpenChange' | 'onNavigation_unstable'>> & {
export type FlatTreeProps = Required<Pick<TreeProps, 'openItems' | 'onOpenChange' | 'onNavigation_unstable'>> & {
ref: React_2.Ref<HTMLDivElement>;
openItems: ImmutableSet<Value>;
openItems: ImmutableSet<string>;
};

// @public (undocumented)
export type NestedTreeItem<Props extends TreeItemProps<unknown>> = Omit<Props, 'subtree' | 'itemType'> & {
export type NestedTreeItem<Props extends TreeItemProps> = Omit<Props, 'subtree' | 'itemType'> & {
subtree?: NestedTreeItem<Props>[];
};

Expand All @@ -84,22 +83,7 @@ export const renderTreeItemLayout_unstable: (state: TreeItemLayoutState) => JSX.
export const renderTreeItemPersonaLayout_unstable: (state: TreeItemPersonaLayoutState, contextValues: TreeItemPersonaLayoutContextValues) => JSX.Element;

// @public
export const Tree: React_2.ForwardRefExoticComponent<Omit<TreeSlots, "root"> & Omit<{
as?: "div" | undefined;
} & Pick<React_2.DetailedHTMLProps<React_2.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof React_2.HTMLAttributes<HTMLDivElement>> & {
ref?: ((instance: HTMLDivElement | null) => void) | React_2.RefObject<HTMLDivElement> | null | undefined;
} & {
children?: React_2.ReactNode | SlotRenderFunction<Pick<React_2.DetailedHTMLProps<React_2.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof React_2.HTMLAttributes<HTMLDivElement>> & {
ref?: ((instance: HTMLDivElement | null) => void) | React_2.RefObject<HTMLDivElement> | null | undefined;
}>;
}, "ref"> & {
appearance?: "transparent" | "subtle" | "subtle-alpha" | undefined;
size?: "small" | "medium" | undefined;
openItems?: Iterable<string> | undefined;
defaultOpenItems?: Iterable<string> | undefined;
onOpenChange?(event: React_2.KeyboardEvent<HTMLElement> | React_2.MouseEvent<HTMLElement, MouseEvent>, data: TreeOpenChangeData<string>): void;
onNavigation_unstable?(event: React_2.KeyboardEvent<HTMLElement> | React_2.MouseEvent<HTMLElement, MouseEvent>, data: TreeNavigationData_unstable<string>): void;
} & React_2.RefAttributes<HTMLDivElement>> & (<Value = string>(props: TreeProps<Value>) => JSX.Element);
export const Tree: ForwardRefComponent<TreeProps>;

// @public (undocumented)
export const treeClassNames: SlotClassNames<TreeSlots>;
Expand All @@ -110,24 +94,11 @@ export type TreeContextValue = {
appearance: 'subtle' | 'subtle-alpha' | 'transparent';
size: 'small' | 'medium';
openItems: ImmutableSet<unknown>;
requestTreeResponse(request: TreeItemRequest<unknown>): void;
requestTreeResponse(request: TreeItemRequest): void;
};

// @public
export const TreeItem: React_2.ForwardRefExoticComponent<Omit<Partial<TreeItemSlots>, "root"> & Omit<{
as?: "div" | undefined;
} & Pick<React_2.DetailedHTMLProps<React_2.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof React_2.HTMLAttributes<HTMLDivElement>> & {
ref?: ((instance: HTMLDivElement | null) => void) | React_2.RefObject<HTMLDivElement> | null | undefined;
} & {
children?: React_2.ReactNode | SlotRenderFunction<Pick<React_2.DetailedHTMLProps<React_2.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof React_2.HTMLAttributes<HTMLDivElement>> & {
ref?: ((instance: HTMLDivElement | null) => void) | React_2.RefObject<HTMLDivElement> | null | undefined;
}>;
} & {
style?: TreeItemCSSProperties | undefined;
}, "ref"> & {
value?: string | undefined;
itemType: TreeItemType;
} & React_2.RefAttributes<HTMLDivElement>> & (<Value = string>(props: TreeItemProps<Value>) => JSX.Element);
export const TreeItem: ForwardRefComponent<TreeItemProps>;

// @public
export const TreeItemAside: ForwardRefComponent<TreeItemAsideProps>;
Expand Down Expand Up @@ -204,9 +175,9 @@ export type TreeItemPersonaLayoutState = ComponentState<TreeItemPersonaLayoutSlo
};

// @public
export type TreeItemProps<Value = string> = ComponentProps<Partial<TreeItemSlots>> & {
value?: Value;
export type TreeItemProps = ComponentProps<Partial<TreeItemSlots>> & {
itemType: TreeItemType;
value?: TreeItemValue;
};

// @public (undocumented)
Expand All @@ -226,9 +197,9 @@ export type TreeItemState = ComponentState<TreeItemSlots> & TreeItemContextValue
};

// @public (undocumented)
export type TreeNavigationData_unstable<Value = string> = {
value: Value;
export type TreeNavigationData_unstable = {
target: HTMLElement;
value: string;
} & ({
event: React_2.MouseEvent<HTMLElement>;
type: 'Click';
Expand Down Expand Up @@ -259,9 +230,9 @@ export type TreeNavigationData_unstable<Value = string> = {
export type TreeNavigationEvent_unstable = TreeNavigationData_unstable['event'];

// @public (undocumented)
export type TreeOpenChangeData<Value = string> = {
export type TreeOpenChangeData = {
open: boolean;
value: Value;
value: string;
} & ({
event: React_2.MouseEvent<HTMLElement>;
target: HTMLElement;
Expand All @@ -288,13 +259,13 @@ export type TreeOpenChangeData<Value = string> = {
export type TreeOpenChangeEvent = TreeOpenChangeData['event'];

// @public (undocumented)
export type TreeProps<Value = string> = ComponentProps<TreeSlots> & {
export type TreeProps = ComponentProps<TreeSlots> & {
appearance?: 'subtle' | 'subtle-alpha' | 'transparent';
size?: 'small' | 'medium';
openItems?: Iterable<Value>;
defaultOpenItems?: Iterable<Value>;
onOpenChange?(event: TreeOpenChangeEvent, data: TreeOpenChangeData<Value>): void;
onNavigation_unstable?(event: TreeNavigationEvent_unstable, data: TreeNavigationData_unstable<Value>): void;
openItems?: Iterable<string>;
defaultOpenItems?: Iterable<string>;
onOpenChange?(event: TreeOpenChangeEvent, data: TreeOpenChangeData): void;
onNavigation_unstable?(event: TreeNavigationEvent_unstable, data: TreeNavigationData_unstable): void;
};

// @public (undocumented)
Expand All @@ -311,7 +282,7 @@ export type TreeState = ComponentState<TreeSlots> & TreeContextValue & {
};

// @public
export function useFlatTree_unstable<Props extends FlatTreeItemProps<unknown> = FlatTreeItemProps>(flatTreeItemProps: Props[], options?: FlatTreeOptions<Props>): FlatTree<Props>;
export function useFlatTree_unstable<Props extends FlatTreeItemProps = FlatTreeItemProps>(flatTreeItemProps: Props[], options?: FlatTreeOptions): FlatTree<Props>;

// @public
export const useTree_unstable: (props: TreeProps, ref: React_2.Ref<HTMLElement>) => TreeState;
Expand All @@ -323,7 +294,7 @@ export const useTreeContext_unstable: <T>(selector: ContextSelector<TreeContextV
export function useTreeContextValues_unstable(state: TreeState): TreeContextValues;

// @public
export function useTreeItem_unstable<Value = string>(props: TreeItemProps<Value>, ref: React_2.Ref<HTMLDivElement>): TreeItemState;
export function useTreeItem_unstable(props: TreeItemProps, ref: React_2.Ref<HTMLDivElement>): TreeItemState;

// @public
export const useTreeItemAside_unstable: (props: TreeItemAsideProps, ref: React_2.Ref<HTMLElement>) => TreeItemAsideState;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ import { useTreeContextValues_unstable } from './useTreeContextValues';
* an item representing a folder can be expanded to reveal the contents of the folder,
* which may be files, folders, or both.
*/
export const Tree = React.forwardRef((props, ref) => {
export const Tree: ForwardRefComponent<TreeProps> = React.forwardRef((props, ref) => {
const state = useTree_unstable(props, ref);
useTreeStyles_unstable(state);
const contextValues = useTreeContextValues_unstable(state);
return renderTree_unstable(state, contextValues);
}) as ForwardRefComponent<TreeProps> & (<Value = string>(props: TreeProps<Value>) => JSX.Element);
});

Tree.displayName = 'Tree';
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export type TreeSlots = {
};

// eslint-disable-next-line @typescript-eslint/naming-convention
export type TreeNavigationData_unstable<Value = string> = { value: Value; target: HTMLElement } & (
export type TreeNavigationData_unstable = { target: HTMLElement; value: string } & (
| { event: React.MouseEvent<HTMLElement>; type: 'Click' }
| { event: React.KeyboardEvent<HTMLElement>; type: 'TypeAhead' }
| { event: React.KeyboardEvent<HTMLElement>; type: typeof ArrowRight }
Expand All @@ -22,7 +22,7 @@ export type TreeNavigationData_unstable<Value = string> = { value: Value; target
// eslint-disable-next-line @typescript-eslint/naming-convention
export type TreeNavigationEvent_unstable = TreeNavigationData_unstable['event'];

export type TreeOpenChangeData<Value = string> = { open: boolean; value: Value } & (
export type TreeOpenChangeData = { open: boolean; value: string } & (
| {
event: React.MouseEvent<HTMLElement>;
target: HTMLElement;
Expand Down Expand Up @@ -56,7 +56,7 @@ export type TreeContextValues = {
tree: TreeContextValue;
};

export type TreeProps<Value = string> = ComponentProps<TreeSlots> & {
export type TreeProps = ComponentProps<TreeSlots> & {
/**
* A tree item can have various appearances:
* - 'subtle' (default): The default tree item styles.
Expand All @@ -75,13 +75,13 @@ export type TreeProps<Value = string> = ComponentProps<TreeSlots> & {
* Controls the state of the open tree items.
* These property is ignored for subtrees.
*/
openItems?: Iterable<Value>;
openItems?: Iterable<string>;
/**
* This refers to a list of ids of opened tree items.
* Default value for the uncontrolled state of open tree items.
* These property is ignored for subtrees.
*/
defaultOpenItems?: Iterable<Value>;
defaultOpenItems?: Iterable<string>;
/**
* Callback fired when the component changes value from open state.
* These property is ignored for subtrees.
Expand All @@ -90,7 +90,7 @@ export type TreeProps<Value = string> = ComponentProps<TreeSlots> & {
* @param data - A data object with relevant information,
* such as open value and type of interaction that created the event.
*/
onOpenChange?(event: TreeOpenChangeEvent, data: TreeOpenChangeData<Value>): void;
onOpenChange?(event: TreeOpenChangeEvent, data: TreeOpenChangeData): void;

/**
* Callback fired when navigation happens inside the component.
Expand All @@ -102,7 +102,7 @@ export type TreeProps<Value = string> = ComponentProps<TreeSlots> & {
* @param data - A data object with relevant information,
*/
// eslint-disable-next-line @typescript-eslint/naming-convention
onNavigation_unstable?(event: TreeNavigationEvent_unstable, data: TreeNavigationData_unstable<Value>): void;
onNavigation_unstable?(event: TreeNavigationEvent_unstable, data: TreeNavigationData_unstable): void;
};

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,23 @@ import { TreeItemRequest } from '../../contexts/index';
* @param props - props from this instance of Tree
* @param ref - reference to root HTMLElement of Tree
*/
export function useRootTree<Value = string>(props: TreeProps<Value>, ref: React.Ref<HTMLElement>): TreeState {
export function useRootTree(props: TreeProps, ref: React.Ref<HTMLElement>): TreeState {
warnIfNoProperPropsRootTree(props);

const { appearance = 'subtle', size = 'medium' } = props;

const [openItems, updateOpenItems] = useOpenItemsState(props);
const [navigate, navigationRef] = useNestedTreeNavigation();

const requestOpenChange = (data: TreeOpenChangeData<Value>) => {
const requestOpenChange = (data: TreeOpenChangeData) => {
props.onOpenChange?.(data.event, data);
if (data.event.isDefaultPrevented()) {
return;
}
return updateOpenItems(data);
};

const requestNavigation = (data: TreeNavigationData_unstable<Value>) => {
const requestNavigation = (data: TreeNavigationData_unstable) => {
props.onNavigation_unstable?.(data.event, data);
if (data.event.isDefaultPrevented()) {
return;
Expand All @@ -44,7 +44,7 @@ export function useRootTree<Value = string>(props: TreeProps<Value>, ref: React.
value,
itemType,
type,
}: Extract<TreeItemRequest<Value>, { type: 'Click' | 'ExpandIconClick' }>) => {
}: Extract<TreeItemRequest, { type: 'Click' | 'ExpandIconClick' }>) => {
ReactDOM.unstable_batchedUpdates(() => {
requestOpenChange({
event,
Expand All @@ -59,10 +59,10 @@ export function useRootTree<Value = string>(props: TreeProps<Value>, ref: React.

const handleTreeItemKeyDown = ({
event,
value,
type,
value,
itemType,
}: Exclude<TreeItemRequest<Value>, { type: 'Click' | 'ExpandIconClick' }>) => {
}: Exclude<TreeItemRequest, { type: 'Click' | 'ExpandIconClick' }>) => {
const open = openItems.has(value);
switch (type) {
case treeDataTypes.ArrowRight:
Expand Down Expand Up @@ -107,14 +107,14 @@ export function useRootTree<Value = string>(props: TreeProps<Value>, ref: React.
}
};

const requestTreeResponse = useEventCallback((request: TreeItemRequest<Value>) => {
const requestTreeResponse = useEventCallback((request: TreeItemRequest) => {
switch (request.event.type) {
case 'click':
// casting is required here as we're narrowing down the event to only click events
return handleTreeItemClick(request as Extract<TreeItemRequest<Value>, { type: 'Click' | 'ExpandIconClick' }>);
return handleTreeItemClick(request as Extract<TreeItemRequest, { type: 'Click' | 'ExpandIconClick' }>);
case 'keydown':
// casting is required here as we're narrowing down the event to only keyboard events
return handleTreeItemKeyDown(request as Exclude<TreeItemRequest<Value>, { type: 'Click' | 'ExpandIconClick' }>);
return handleTreeItemKeyDown(request as Exclude<TreeItemRequest, { type: 'Click' | 'ExpandIconClick' }>);
}
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ import { useTreeItemContextValues_unstable } from './useTreeItemContextValues';
* When a TreeItem has nested child subtree, an expand/collapse control is displayed,
* allowing the user to show or hide the children.
*/
export const TreeItem = React.forwardRef((props, ref) => {
export const TreeItem: ForwardRefComponent<TreeItemProps> = React.forwardRef((props, ref) => {
const state = useTreeItem_unstable(props, ref);

useTreeItemStyles_unstable(state);
const contextValues = useTreeItemContextValues_unstable(state);
return renderTreeItem_unstable(state, contextValues);
}) as ForwardRefComponent<TreeItemProps> & (<Value = string>(props: TreeItemProps<Value>) => JSX.Element);
});

TreeItem.displayName = 'TreeItem';
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,16 @@ export type TreeItemSlots = {
root: Slot<ExtractSlotProps<Slot<'div'> & { style?: TreeItemCSSProperties }>>;
};

export type TreeItemValue = string | number;

export type TreeItemContextValues = { treeItem: TreeItemContextValue };

/**
* TreeItem Props
*/
export type TreeItemProps<Value = string> = ComponentProps<Partial<TreeItemSlots>> & {
value?: Value;
export type TreeItemProps = ComponentProps<Partial<TreeItemSlots>> & {
itemType: TreeItemType;
value?: TreeItemValue;
};

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ exports[`TreeItem renders a default state 1`] = `
<div
aria-level="0"
class="fui-TreeItem"
id="fui-TreeItem-7"
data-fui-tree-item-value="fuiTreeItemValue-7"
role="treeitem"
tabindex="-1"
>
Expand Down
Loading