Skip to content

Commit

Permalink
bugfix: makes value serializable
Browse files Browse the repository at this point in the history
  • Loading branch information
bsunderhus committed Jun 22, 2023
1 parent 3a08754 commit b0037f3
Show file tree
Hide file tree
Showing 23 changed files with 188 additions and 209 deletions.
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

0 comments on commit b0037f3

Please sign in to comment.