From fcd8a6896412e69b761ee5e6baa86f052bf31798 Mon Sep 17 00:00:00 2001 From: PRIYANKAR GUPTA Date: Tue, 5 Mar 2019 00:07:04 +0530 Subject: [PATCH 01/13] Changhes for adding exclusive flag --- .../Types/TreeExclusiveExample.shorthand.tsx | 54 +++++++++++++++++++ .../examples/components/Tree/Types/index.tsx | 5 ++ packages/react/src/components/Tree/Tree.tsx | 19 ++++++- .../react/src/components/Tree/TreeItem.tsx | 22 +++++--- .../react/src/components/Tree/TreeTitle.tsx | 4 +- 5 files changed, 93 insertions(+), 11 deletions(-) create mode 100644 docs/src/examples/components/Tree/Types/TreeExclusiveExample.shorthand.tsx diff --git a/docs/src/examples/components/Tree/Types/TreeExclusiveExample.shorthand.tsx b/docs/src/examples/components/Tree/Types/TreeExclusiveExample.shorthand.tsx new file mode 100644 index 000000000..009a37852 --- /dev/null +++ b/docs/src/examples/components/Tree/Types/TreeExclusiveExample.shorthand.tsx @@ -0,0 +1,54 @@ +import * as React from 'react' +import { Icon, Tree } from '@stardust-ui/react' + +const items = [ + { + key: '1', + title: 'one', + items: [ + { + key: '2', + title: 'one one', + items: [ + { + key: '3', + title: 'one one one', + }, + ], + }, + { + key: '6', + title: 'one two', + items: [ + { + key: '7', + title: 'one two one', + }, + ], + }, + ], + }, + { + key: '4', + title: 'two', + items: [ + { + key: '5', + title: 'two one', + }, + ], + }, +] + +const titleRenderer = (Component, { content, open, hasSubtree, ...restProps }) => ( + + {hasSubtree && } + {content} + +) + +const TreeExclusiveExample = () => ( + +) + +export default TreeExclusiveExample diff --git a/docs/src/examples/components/Tree/Types/index.tsx b/docs/src/examples/components/Tree/Types/index.tsx index e6ba4e852..672d4d8a7 100644 --- a/docs/src/examples/components/Tree/Types/index.tsx +++ b/docs/src/examples/components/Tree/Types/index.tsx @@ -14,6 +14,11 @@ const Types = () => ( description="A Tree with customized title rendering." examplePath="components/Tree/Types/TreeTitleCustomizationExample" /> + ) diff --git a/packages/react/src/components/Tree/Tree.tsx b/packages/react/src/components/Tree/Tree.tsx index a607dd0f9..f6f27dc34 100644 --- a/packages/react/src/components/Tree/Tree.tsx +++ b/packages/react/src/components/Tree/Tree.tsx @@ -50,23 +50,38 @@ class Tree extends UIComponent> { ...commonPropTypes.createCommon({ content: false, }), + exclusive: PropTypes.bool, items: customPropTypes.collectionShorthand, renderItemTitle: PropTypes.func, rtlAttributes: PropTypes.func, } + state = { + selectedIndex: -1, + } + public static defaultProps = { as: 'ul', accessibility: defaultBehavior, } + clickHandler = (e, index) => { + this.setState({ + selectedIndex: index, + }) + } + renderContent() { - const { items, renderItemTitle } = this.props + const { items, renderItemTitle, exclusive } = this.props - return _.map(items, item => + return _.map(items, (item, index) => TreeItem.create(item, { defaultProps: { + index, + exclusive, renderItemTitle, + open: index === this.state.selectedIndex, + onClick: this.clickHandler, }, }), ) diff --git a/packages/react/src/components/Tree/TreeItem.tsx b/packages/react/src/components/Tree/TreeItem.tsx index 695d4d3a3..a161180b7 100644 --- a/packages/react/src/components/Tree/TreeItem.tsx +++ b/packages/react/src/components/Tree/TreeItem.tsx @@ -7,7 +7,7 @@ import TreeTitle from './TreeTitle' import { defaultBehavior } from '../../lib/accessibility' import { Accessibility } from '../../lib/accessibility/types' import { - AutoControlledComponent, + UIComponent, childrenExist, customPropTypes, createShorthandFactory, @@ -52,24 +52,25 @@ export interface TreeItemState { open?: boolean } -class TreeItem extends AutoControlledComponent, TreeItemState> { +class TreeItem extends UIComponent, TreeItemState> { static create: Function static className = 'ui-tree__item' static displayName = 'TreeItem' - static autoControlledProps = ['open'] - static propTypes = { ...commonPropTypes.createCommon({ content: false, }), defaultOpen: PropTypes.bool, items: customPropTypes.collectionShorthand, + index: PropTypes.number, + exclusive: PropTypes.bool, open: PropTypes.bool, renderItemTitle: PropTypes.func, treeItemRtlAttributes: PropTypes.func, + onClick: PropTypes.func, title: customPropTypes.itemShorthand, } @@ -78,19 +79,24 @@ class TreeItem extends AutoControlledComponent, TreeIt accessibility: defaultBehavior, } + public state = { + open: false, + } + handleTitleOverrides = predefinedProps => ({ onClick: (e, titleProps) => { e.preventDefault() - this.trySetState({ + this.setState({ open: !this.state.open, }) _.invoke(predefinedProps, 'onClick', e, titleProps) + _.invoke(this.props, 'onClick', e, this.props.index, titleProps) }, }) renderContent() { const { items, title, renderItemTitle } = this.props - const { open } = this.state + const open = this.props.exclusive ? this.props.open : this.state.open const hasSubtree = !!(items && items.length) @@ -104,7 +110,9 @@ class TreeItem extends AutoControlledComponent, TreeIt render: renderItemTitle, overrideProps: this.handleTitleOverrides, })} - {hasSubtree && open && } + {hasSubtree && open && ( + + )} ) } diff --git a/packages/react/src/components/Tree/TreeTitle.tsx b/packages/react/src/components/Tree/TreeTitle.tsx index eace9f9f0..4911ff740 100644 --- a/packages/react/src/components/Tree/TreeTitle.tsx +++ b/packages/react/src/components/Tree/TreeTitle.tsx @@ -61,11 +61,11 @@ class TreeTitle extends UIComponent> { return ( {childrenExist(children) ? children : content} From 0102ff1640e9e481fa31c02edd30e1faace3c234 Mon Sep 17 00:00:00 2001 From: PRIYANKAR GUPTA Date: Tue, 5 Mar 2019 23:40:09 +0530 Subject: [PATCH 02/13] Addreasing comments --- packages/react/src/components/Tree/Tree.tsx | 23 ++++++++++++------- .../react/src/components/Tree/TreeItem.tsx | 18 +++++++++++---- 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/packages/react/src/components/Tree/Tree.tsx b/packages/react/src/components/Tree/Tree.tsx index f6f27dc34..543d203c2 100644 --- a/packages/react/src/components/Tree/Tree.tsx +++ b/packages/react/src/components/Tree/Tree.tsx @@ -23,6 +23,9 @@ export interface TreeProps extends UIComponentProps, ChildrenComponentProps { */ accessibility?: Accessibility + /** Only allow one subtree to be open at a time. */ + exclusive?: boolean + /** Shorthand array of props for Tree. */ items: ShorthandValue[] @@ -57,7 +60,7 @@ class Tree extends UIComponent> { } state = { - selectedIndex: -1, + clickedIndex: -1, } public static defaultProps = { @@ -65,11 +68,15 @@ class Tree extends UIComponent> { accessibility: defaultBehavior, } - clickHandler = (e, index) => { - this.setState({ - selectedIndex: index, - }) - } + handleTreeItemOverrides = predefinedProps => ({ + onOpenChanged: (e, treeItemProps) => { + const { index } = treeItemProps + this.setState({ + clickedIndex: index, + }) + _.invoke(predefinedProps, 'onOpenChanged', e, treeItemProps) + }, + }) renderContent() { const { items, renderItemTitle, exclusive } = this.props @@ -80,9 +87,9 @@ class Tree extends UIComponent> { index, exclusive, renderItemTitle, - open: index === this.state.selectedIndex, - onClick: this.clickHandler, + open: exclusive ? index === this.state.clickedIndex : false, }, + overrideProps: this.handleTreeItemOverrides, }), ) } diff --git a/packages/react/src/components/Tree/TreeItem.tsx b/packages/react/src/components/Tree/TreeItem.tsx index a161180b7..70a0b3598 100644 --- a/packages/react/src/components/Tree/TreeItem.tsx +++ b/packages/react/src/components/Tree/TreeItem.tsx @@ -16,7 +16,12 @@ import { ChildrenComponentProps, rtlTextContainer, } from '../../lib' -import { ReactProps, ShorthandRenderFunction, ShorthandValue } from '../../types' +import { + ComponentEventHandler, + ReactProps, + ShorthandRenderFunction, + ShorthandValue, +} from '../../types' export interface TreeItemProps extends UIComponentProps, ChildrenComponentProps { /** @@ -25,12 +30,18 @@ export interface TreeItemProps extends UIComponentProps, ChildrenComponentProps */ accessibility?: Accessibility + /** Only allow one subtree to be open at a time. */ + exclusive?: boolean + /** Initial open value. */ defaultOpen?: boolean /** Array of props for sub tree. */ items?: ShorthandValue[] + /** Callback for toggling the current tree item as open element in the menu. */ + onOpenChanged?: ComponentEventHandler + /** Whether or not the subtree of the item is in the open state. */ open?: boolean @@ -67,10 +78,10 @@ class TreeItem extends UIComponent, TreeItemState> { items: customPropTypes.collectionShorthand, index: PropTypes.number, exclusive: PropTypes.bool, + onOpenChanged: PropTypes.func, open: PropTypes.bool, renderItemTitle: PropTypes.func, treeItemRtlAttributes: PropTypes.func, - onClick: PropTypes.func, title: customPropTypes.itemShorthand, } @@ -89,15 +100,14 @@ class TreeItem extends UIComponent, TreeItemState> { this.setState({ open: !this.state.open, }) + _.invoke(this.props, 'onOpenChanged', e, this.props) _.invoke(predefinedProps, 'onClick', e, titleProps) - _.invoke(this.props, 'onClick', e, this.props.index, titleProps) }, }) renderContent() { const { items, title, renderItemTitle } = this.props const open = this.props.exclusive ? this.props.open : this.state.open - const hasSubtree = !!(items && items.length) return ( From 728e753f6653cb61feea38e3cbd43c4c1e313c00 Mon Sep 17 00:00:00 2001 From: PRIYANKAR GUPTA Date: Tue, 5 Mar 2019 23:46:13 +0530 Subject: [PATCH 03/13] Addressing comments --- packages/react/src/components/Tree/TreeTitle.tsx | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/packages/react/src/components/Tree/TreeTitle.tsx b/packages/react/src/components/Tree/TreeTitle.tsx index 4911ff740..eac0cfdf6 100644 --- a/packages/react/src/components/Tree/TreeTitle.tsx +++ b/packages/react/src/components/Tree/TreeTitle.tsx @@ -14,7 +14,7 @@ import { } from '../../lib' import { treeTitleBehavior } from '../../lib/accessibility' import { Accessibility } from '../../lib/accessibility/types' -import { ReactProps } from '../../types' +import { ComponentEventHandler, ReactProps } from '../../types' export interface TreeTitleProps extends UIComponentProps, @@ -26,6 +26,14 @@ export interface TreeTitleProps */ accessibility?: Accessibility + /** + * Called on click. + * + * @param {SyntheticEvent} event - React's original SyntheticEvent. + * @param {object} data - All props. + */ + onClick?: ComponentEventHandler + /** Whether or not the subtree of the item is in the open state. */ open?: boolean @@ -42,6 +50,7 @@ class TreeTitle extends UIComponent> { static propTypes = { ...commonPropTypes.createCommon(), + onClick: PropTypes.func, open: PropTypes.bool, hasSubtree: PropTypes.bool, } @@ -61,10 +70,10 @@ class TreeTitle extends UIComponent> { return ( {childrenExist(children) ? children : content} From 9bdee10af6810dc01f847c19c61af8d9e714827e Mon Sep 17 00:00:00 2001 From: PRIYANKAR GUPTA Date: Wed, 6 Mar 2019 20:44:35 +0530 Subject: [PATCH 04/13] Resolving comments --- .../Types/TreeExclusiveExample.shorthand.tsx | 4 +-- packages/react/src/components/Tree/Tree.tsx | 12 +++---- .../react/src/components/Tree/TreeItem.tsx | 36 ++++++++++++------- .../react/src/components/Tree/TreeTitle.tsx | 2 +- 4 files changed, 32 insertions(+), 22 deletions(-) diff --git a/docs/src/examples/components/Tree/Types/TreeExclusiveExample.shorthand.tsx b/docs/src/examples/components/Tree/Types/TreeExclusiveExample.shorthand.tsx index 009a37852..8edd835cd 100644 --- a/docs/src/examples/components/Tree/Types/TreeExclusiveExample.shorthand.tsx +++ b/docs/src/examples/components/Tree/Types/TreeExclusiveExample.shorthand.tsx @@ -47,8 +47,6 @@ const titleRenderer = (Component, { content, open, hasSubtree, ...restProps }) = ) -const TreeExclusiveExample = () => ( - -) +const TreeExclusiveExample = () => export default TreeExclusiveExample diff --git a/packages/react/src/components/Tree/Tree.tsx b/packages/react/src/components/Tree/Tree.tsx index 543d203c2..34135b94a 100644 --- a/packages/react/src/components/Tree/Tree.tsx +++ b/packages/react/src/components/Tree/Tree.tsx @@ -2,7 +2,7 @@ import * as _ from 'lodash' import * as PropTypes from 'prop-types' import * as React from 'react' -import TreeItem from './TreeItem' +import TreeItem, { TreeItemProps } from './TreeItem' import { UIComponent, childrenExist, @@ -60,7 +60,7 @@ class Tree extends UIComponent> { } state = { - clickedIndex: -1, + activeIndex: -1, } public static defaultProps = { @@ -69,12 +69,12 @@ class Tree extends UIComponent> { } handleTreeItemOverrides = predefinedProps => ({ - onOpenChanged: (e, treeItemProps) => { + onOpenChange: (e: React.SyntheticEvent, treeItemProps: TreeItemProps) => { const { index } = treeItemProps this.setState({ - clickedIndex: index, + activeIndex: index, }) - _.invoke(predefinedProps, 'onOpenChanged', e, treeItemProps) + _.invoke(predefinedProps, 'onOpenChange', e, treeItemProps) }, }) @@ -87,7 +87,7 @@ class Tree extends UIComponent> { index, exclusive, renderItemTitle, - open: exclusive ? index === this.state.clickedIndex : false, + open: exclusive ? index === this.state.activeIndex : false, }, overrideProps: this.handleTreeItemOverrides, }), diff --git a/packages/react/src/components/Tree/TreeItem.tsx b/packages/react/src/components/Tree/TreeItem.tsx index 70a0b3598..c0aefa92f 100644 --- a/packages/react/src/components/Tree/TreeItem.tsx +++ b/packages/react/src/components/Tree/TreeItem.tsx @@ -7,7 +7,7 @@ import TreeTitle from './TreeTitle' import { defaultBehavior } from '../../lib/accessibility' import { Accessibility } from '../../lib/accessibility/types' import { - UIComponent, + AutoControlledComponent, childrenExist, customPropTypes, createShorthandFactory, @@ -36,11 +36,14 @@ export interface TreeItemProps extends UIComponentProps, ChildrenComponentProps /** Initial open value. */ defaultOpen?: boolean + /** The index of the item among its sibbling */ + index: number + /** Array of props for sub tree. */ items?: ShorthandValue[] /** Callback for toggling the current tree item as open element in the menu. */ - onOpenChanged?: ComponentEventHandler + onOpenChange?: ComponentEventHandler /** Whether or not the subtree of the item is in the open state. */ open?: boolean @@ -63,13 +66,15 @@ export interface TreeItemState { open?: boolean } -class TreeItem extends UIComponent, TreeItemState> { +class TreeItem extends AutoControlledComponent, TreeItemState> { static create: Function static className = 'ui-tree__item' static displayName = 'TreeItem' + static autoControlledProps = ['open'] + static propTypes = { ...commonPropTypes.createCommon({ content: false, @@ -78,7 +83,7 @@ class TreeItem extends UIComponent, TreeItemState> { items: customPropTypes.collectionShorthand, index: PropTypes.number, exclusive: PropTypes.bool, - onOpenChanged: PropTypes.func, + onOpenChange: PropTypes.func, open: PropTypes.bool, renderItemTitle: PropTypes.func, treeItemRtlAttributes: PropTypes.func, @@ -90,24 +95,27 @@ class TreeItem extends UIComponent, TreeItemState> { accessibility: defaultBehavior, } - public state = { - open: false, + getInitialAutoControlledState({ open }) { + return { open: !!open } } - handleTitleOverrides = predefinedProps => ({ + handleTitleOverrides = (predefinedProps: TreeItemProps) => ({ onClick: (e, titleProps) => { e.preventDefault() - this.setState({ + this.trySetState({ open: !this.state.open, }) - _.invoke(this.props, 'onOpenChanged', e, this.props) + + if (this.props.exclusive) { + _.invoke(this.props, 'onOpenChange', e, this.props) + } _.invoke(predefinedProps, 'onClick', e, titleProps) }, }) renderContent() { - const { items, title, renderItemTitle } = this.props - const open = this.props.exclusive ? this.props.open : this.state.open + const { items, title, renderItemTitle, exclusive } = this.props + const open = exclusive ? this.props.open : this.state.open const hasSubtree = !!(items && items.length) return ( @@ -121,7 +129,11 @@ class TreeItem extends UIComponent, TreeItemState> { overrideProps: this.handleTitleOverrides, })} {hasSubtree && open && ( - + )} ) diff --git a/packages/react/src/components/Tree/TreeTitle.tsx b/packages/react/src/components/Tree/TreeTitle.tsx index eac0cfdf6..753b15ae6 100644 --- a/packages/react/src/components/Tree/TreeTitle.tsx +++ b/packages/react/src/components/Tree/TreeTitle.tsx @@ -71,10 +71,10 @@ class TreeTitle extends UIComponent> { return ( {childrenExist(children) ? children : content} From 522080719071fd8b9aa4c8390f4005c720f63686 Mon Sep 17 00:00:00 2001 From: PRIYANKAR GUPTA Date: Wed, 6 Mar 2019 20:48:30 +0530 Subject: [PATCH 05/13] Fix for change after autocontrolled revert --- packages/react/src/components/Tree/Tree.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react/src/components/Tree/Tree.tsx b/packages/react/src/components/Tree/Tree.tsx index 34135b94a..9a8dd7e81 100644 --- a/packages/react/src/components/Tree/Tree.tsx +++ b/packages/react/src/components/Tree/Tree.tsx @@ -87,7 +87,7 @@ class Tree extends UIComponent> { index, exclusive, renderItemTitle, - open: exclusive ? index === this.state.activeIndex : false, + open: exclusive ? index === this.state.activeIndex : undefined, }, overrideProps: this.handleTreeItemOverrides, }), From 1f68c2f412ecf65e49845683f15a5a116e4b0ed0 Mon Sep 17 00:00:00 2001 From: PRIYANKAR GUPTA Date: Thu, 7 Mar 2019 14:19:04 +0530 Subject: [PATCH 06/13] Moving state management to the Tree component --- packages/react/src/components/Tree/Tree.tsx | 47 +++++++++++++++---- .../react/src/components/Tree/TreeItem.tsx | 23 ++------- 2 files changed, 42 insertions(+), 28 deletions(-) diff --git a/packages/react/src/components/Tree/Tree.tsx b/packages/react/src/components/Tree/Tree.tsx index 9a8dd7e81..6327feaec 100644 --- a/packages/react/src/components/Tree/Tree.tsx +++ b/packages/react/src/components/Tree/Tree.tsx @@ -4,7 +4,7 @@ import * as React from 'react' import TreeItem, { TreeItemProps } from './TreeItem' import { - UIComponent, + AutoControlledComponent, childrenExist, commonPropTypes, customPropTypes, @@ -17,12 +17,18 @@ import { Accessibility } from '../../lib/accessibility/types' import { defaultBehavior } from '../../lib/accessibility' export interface TreeProps extends UIComponentProps, ChildrenComponentProps { + /** Index of the currently active subtree. */ + activeIndex?: number[] | number + /** * Accessibility behavior if overridden by the user. * @default defaultBehavior */ accessibility?: Accessibility + /** Initial activeIndex value. */ + defaultActiveIndex?: number[] | number + /** Only allow one subtree to be open at a time. */ exclusive?: boolean @@ -42,7 +48,7 @@ export interface TreeProps extends UIComponentProps, ChildrenComponentProps { /** * Allows users to display data organised in tree-hierarchy. */ -class Tree extends UIComponent> { +class Tree extends AutoControlledComponent, any> { static create: Function static className = 'ui-tree' @@ -53,26 +59,47 @@ class Tree extends UIComponent> { ...commonPropTypes.createCommon({ content: false, }), + activeIndex: customPropTypes.every([ + customPropTypes.disallow(['children']), + PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.number), PropTypes.number]), + ]), + defaultActiveIndex: customPropTypes.every([ + customPropTypes.disallow(['children']), + PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.number), PropTypes.number]), + ]), exclusive: PropTypes.bool, items: customPropTypes.collectionShorthand, renderItemTitle: PropTypes.func, rtlAttributes: PropTypes.func, } - state = { - activeIndex: -1, - } - public static defaultProps = { as: 'ul', accessibility: defaultBehavior, } + static autoControlledProps = ['activeIndex'] + + getInitialAutoControlledState({ defaultActiveIndex, exclusive }) { + return { + activeIndex: defaultActiveIndex === undefined ? (exclusive ? -1 : []) : defaultActiveIndex, + } + } + + computeNewIndex = index => { + const { activeIndex } = this.state + const { exclusive } = this.props + + if (exclusive) return index === activeIndex ? -1 : index + // check to see if index is in array, and remove it, if not then add it + return _.includes(activeIndex, index) ? _.without(activeIndex, index) : [...activeIndex, index] + } + handleTreeItemOverrides = predefinedProps => ({ onOpenChange: (e: React.SyntheticEvent, treeItemProps: TreeItemProps) => { const { index } = treeItemProps - this.setState({ - activeIndex: index, + this.trySetState({ + activeIndex: this.computeNewIndex(index), }) _.invoke(predefinedProps, 'onOpenChange', e, treeItemProps) }, @@ -87,7 +114,9 @@ class Tree extends UIComponent> { index, exclusive, renderItemTitle, - open: exclusive ? index === this.state.activeIndex : undefined, + open: exclusive + ? index === this.state.activeIndex + : _.includes(this.state.activeIndex, index), }, overrideProps: this.handleTreeItemOverrides, }), diff --git a/packages/react/src/components/Tree/TreeItem.tsx b/packages/react/src/components/Tree/TreeItem.tsx index 46a1bdc0e..8e850058e 100644 --- a/packages/react/src/components/Tree/TreeItem.tsx +++ b/packages/react/src/components/Tree/TreeItem.tsx @@ -7,7 +7,7 @@ import TreeTitle from './TreeTitle' import { defaultBehavior } from '../../lib/accessibility' import { Accessibility } from '../../lib/accessibility/types' import { - AutoControlledComponent, + UIComponent, childrenExist, customPropTypes, createShorthandFactory, @@ -62,11 +62,7 @@ export interface TreeItemProps extends UIComponentProps, ChildrenComponentProps title?: ShorthandValue } -export interface TreeItemState { - open?: boolean -} - -class TreeItem extends AutoControlledComponent, TreeItemState> { +class TreeItem extends UIComponent> { static create: Function static className = 'ui-tree__item' @@ -95,27 +91,16 @@ class TreeItem extends AutoControlledComponent, TreeIt accessibility: defaultBehavior, } - getInitialAutoControlledState({ open }) { - return { open: !!open } - } - handleTitleOverrides = (predefinedProps: TreeItemProps) => ({ onClick: (e, titleProps) => { e.preventDefault() - this.trySetState({ - open: !this.state.open, - }) - - if (this.props.exclusive) { - _.invoke(this.props, 'onOpenChange', e, this.props) - } + _.invoke(this.props, 'onOpenChange', e, this.props) _.invoke(predefinedProps, 'onClick', e, titleProps) }, }) renderContent() { - const { items, title, renderItemTitle, exclusive } = this.props - const open = exclusive ? this.props.open : this.state.open + const { items, title, renderItemTitle, open } = this.props const hasSubtree = !!(items && items.length) return ( From c4915fb3621bb98f4a58ae8f25817baf85344206 Mon Sep 17 00:00:00 2001 From: PRIYANKAR GUPTA Date: Thu, 7 Mar 2019 15:04:52 +0530 Subject: [PATCH 07/13] Addressing comments --- .../examples/components/Tree/Types/index.tsx | 2 +- packages/react/src/components/Tree/Tree.tsx | 32 ++++++++++++------- .../react/src/components/Tree/TreeItem.tsx | 22 ++++++------- 3 files changed, 32 insertions(+), 24 deletions(-) diff --git a/docs/src/examples/components/Tree/Types/index.tsx b/docs/src/examples/components/Tree/Types/index.tsx index 672d4d8a7..c6b19a374 100644 --- a/docs/src/examples/components/Tree/Types/index.tsx +++ b/docs/src/examples/components/Tree/Types/index.tsx @@ -15,7 +15,7 @@ const Types = () => ( examplePath="components/Tree/Types/TreeTitleCustomizationExample" /> diff --git a/packages/react/src/components/Tree/Tree.tsx b/packages/react/src/components/Tree/Tree.tsx index 6327feaec..c0c42aab9 100644 --- a/packages/react/src/components/Tree/Tree.tsx +++ b/packages/react/src/components/Tree/Tree.tsx @@ -7,6 +7,7 @@ import { AutoControlledComponent, childrenExist, commonPropTypes, + createShorthandFactory, customPropTypes, UIComponentProps, ChildrenComponentProps, @@ -45,10 +46,14 @@ export interface TreeProps extends UIComponentProps, ChildrenComponentProps { renderItemTitle?: ShorthandRenderFunction } +export interface TreeState { + activeIndex: number[] | number +} + /** * Allows users to display data organised in tree-hierarchy. */ -class Tree extends AutoControlledComponent, any> { +class Tree extends AutoControlledComponent, TreeState> { static create: Function static className = 'ui-tree' @@ -80,43 +85,44 @@ class Tree extends AutoControlledComponent, any> { static autoControlledProps = ['activeIndex'] - getInitialAutoControlledState({ defaultActiveIndex, exclusive }) { + getInitialAutoControlledState({ defaultActiveIndex, exclusive }): TreeState { return { activeIndex: defaultActiveIndex === undefined ? (exclusive ? -1 : []) : defaultActiveIndex, } } - computeNewIndex = index => { + computeNewIndex = (index: number) => { const { activeIndex } = this.state const { exclusive } = this.props - if (exclusive) return index === activeIndex ? -1 : index + if (exclusive) return index // check to see if index is in array, and remove it, if not then add it - return _.includes(activeIndex, index) ? _.without(activeIndex, index) : [...activeIndex, index] + return _.includes(activeIndex as number[], index) + ? _.without(activeIndex as number[], index) + : [...(activeIndex as number[]), index] } - handleTreeItemOverrides = predefinedProps => ({ - onOpenChange: (e: React.SyntheticEvent, treeItemProps: TreeItemProps) => { + handleTreeItemOverrides = (predefinedProps: TreeItemProps) => ({ + onTitleClick: (e: React.SyntheticEvent, treeItemProps: TreeItemProps) => { const { index } = treeItemProps this.trySetState({ activeIndex: this.computeNewIndex(index), }) - _.invoke(predefinedProps, 'onOpenChange', e, treeItemProps) + _.invoke(predefinedProps, 'onTitleClick', e, treeItemProps) }, }) renderContent() { const { items, renderItemTitle, exclusive } = this.props + const { activeIndex } = this.state - return _.map(items, (item, index) => + return _.map(items, (item: ShorthandValue, index: number) => TreeItem.create(item, { defaultProps: { index, exclusive, renderItemTitle, - open: exclusive - ? index === this.state.activeIndex - : _.includes(this.state.activeIndex, index), + open: exclusive ? index === activeIndex : _.includes(activeIndex as number[], index), }, overrideProps: this.handleTreeItemOverrides, }), @@ -139,4 +145,6 @@ class Tree extends AutoControlledComponent, any> { } } +Tree.create = createShorthandFactory({ Component: Tree, mappedArrayProp: 'items' }) + export default Tree diff --git a/packages/react/src/components/Tree/TreeItem.tsx b/packages/react/src/components/Tree/TreeItem.tsx index 8e850058e..788530747 100644 --- a/packages/react/src/components/Tree/TreeItem.tsx +++ b/packages/react/src/components/Tree/TreeItem.tsx @@ -43,7 +43,7 @@ export interface TreeItemProps extends UIComponentProps, ChildrenComponentProps items?: ShorthandValue[] /** Callback for toggling the current tree item as open element in the menu. */ - onOpenChange?: ComponentEventHandler + onTitleClick?: ComponentEventHandler /** Whether or not the subtree of the item is in the open state. */ open?: boolean @@ -79,7 +79,7 @@ class TreeItem extends UIComponent> { items: customPropTypes.collectionShorthand, index: PropTypes.number, exclusive: PropTypes.bool, - onOpenChange: PropTypes.func, + onTitleClick: PropTypes.func, open: PropTypes.bool, renderItemTitle: PropTypes.func, treeItemRtlAttributes: PropTypes.func, @@ -94,13 +94,13 @@ class TreeItem extends UIComponent> { handleTitleOverrides = (predefinedProps: TreeItemProps) => ({ onClick: (e, titleProps) => { e.preventDefault() - _.invoke(this.props, 'onOpenChange', e, this.props) + _.invoke(this.props, 'onTitleClick', e, this.props) _.invoke(predefinedProps, 'onClick', e, titleProps) }, }) renderContent() { - const { items, title, renderItemTitle, open } = this.props + const { items, title, renderItemTitle, open, exclusive } = this.props const hasSubtree = !!(items && items.length) return ( @@ -113,13 +113,13 @@ class TreeItem extends UIComponent> { render: renderItemTitle, overrideProps: this.handleTitleOverrides, })} - {hasSubtree && open && ( - - )} + {open && + Tree.create(items, { + defaultProps: { + exclusive, + renderItemTitle, + }, + })} ) } From a76edcfd7434b46b7d843422509f39876476e80b Mon Sep 17 00:00:00 2001 From: PRIYANKAR GUPTA Date: Thu, 7 Mar 2019 15:29:19 +0530 Subject: [PATCH 08/13] Addressing comments --- packages/react/src/components/Tree/TreeItem.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react/src/components/Tree/TreeItem.tsx b/packages/react/src/components/Tree/TreeItem.tsx index 788530747..8bb483092 100644 --- a/packages/react/src/components/Tree/TreeItem.tsx +++ b/packages/react/src/components/Tree/TreeItem.tsx @@ -42,7 +42,7 @@ export interface TreeItemProps extends UIComponentProps, ChildrenComponentProps /** Array of props for sub tree. */ items?: ShorthandValue[] - /** Callback for toggling the current tree item as open element in the menu. */ + /** Called when a tree title is clicked. */ onTitleClick?: ComponentEventHandler /** Whether or not the subtree of the item is in the open state. */ From 57aba87dcc1876e4b1d838bf255235ae94e33543 Mon Sep 17 00:00:00 2001 From: PRIYANKAR GUPTA Date: Thu, 7 Mar 2019 15:48:14 +0530 Subject: [PATCH 09/13] Updating changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d8c6e315..8ed8d97ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,9 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Features - Add `inline` prop in the `Popup` for rendering the content next to the trigger element @mnajdova ([#1017](https://github.com/stardust-ui/react/pull/1017)) +- Add `exclusive` prop in the `Tree` for expanding one tree item at a time +@priyankar205 ([#1018](https://github.com/stardust-ui/react/pull/1018)) + ## [v0.23.0](https://github.com/stardust-ui/react/tree/v0.23.0) (2019-03-06) [Compare changes](https://github.com/stardust-ui/react/compare/v0.22.1...v0.23.0) From c8c6495a0140cafccbbe67b4237c15008260412a Mon Sep 17 00:00:00 2001 From: PRIYANKAR GUPTA Date: Thu, 7 Mar 2019 17:40:52 +0530 Subject: [PATCH 10/13] Addressing comments --- CHANGELOG.md | 1 - packages/react/src/components/Tree/Tree.tsx | 5 +---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ed8d97ac..76125a129 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,6 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Features - Add `inline` prop in the `Popup` for rendering the content next to the trigger element @mnajdova ([#1017](https://github.com/stardust-ui/react/pull/1017)) - - Add `exclusive` prop in the `Tree` for expanding one tree item at a time @priyankar205 ([#1018](https://github.com/stardust-ui/react/pull/1018)) diff --git a/packages/react/src/components/Tree/Tree.tsx b/packages/react/src/components/Tree/Tree.tsx index c0c42aab9..fe07b4fbb 100644 --- a/packages/react/src/components/Tree/Tree.tsx +++ b/packages/react/src/components/Tree/Tree.tsx @@ -104,10 +104,7 @@ class Tree extends AutoControlledComponent, TreeState> { handleTreeItemOverrides = (predefinedProps: TreeItemProps) => ({ onTitleClick: (e: React.SyntheticEvent, treeItemProps: TreeItemProps) => { - const { index } = treeItemProps - this.trySetState({ - activeIndex: this.computeNewIndex(index), - }) + this.trySetState({ activeIndex: this.computeNewIndex(treeItemProps.index) }) _.invoke(predefinedProps, 'onTitleClick', e, treeItemProps) }, }) From 7367cc8e291531e79b6f7da6931be039bdb2a025 Mon Sep 17 00:00:00 2001 From: PRIYANKAR GUPTA Date: Thu, 7 Mar 2019 18:11:56 +0530 Subject: [PATCH 11/13] Addressing comments --- packages/react/src/components/Tree/TreeItem.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react/src/components/Tree/TreeItem.tsx b/packages/react/src/components/Tree/TreeItem.tsx index 8bb483092..222206cbb 100644 --- a/packages/react/src/components/Tree/TreeItem.tsx +++ b/packages/react/src/components/Tree/TreeItem.tsx @@ -3,7 +3,7 @@ import * as PropTypes from 'prop-types' import * as React from 'react' import Tree from './Tree' -import TreeTitle from './TreeTitle' +import TreeTitle, { TreeTitleProps } from './TreeTitle' import { defaultBehavior } from '../../lib/accessibility' import { Accessibility } from '../../lib/accessibility/types' import { @@ -91,7 +91,7 @@ class TreeItem extends UIComponent> { accessibility: defaultBehavior, } - handleTitleOverrides = (predefinedProps: TreeItemProps) => ({ + handleTitleOverrides = (predefinedProps: TreeTitleProps) => ({ onClick: (e, titleProps) => { e.preventDefault() _.invoke(this.props, 'onTitleClick', e, this.props) From 1c44abaafd284a98e54ccd6e012b2c911ba81f3e Mon Sep 17 00:00:00 2001 From: PRIYANKAR GUPTA Date: Thu, 7 Mar 2019 21:30:43 +0530 Subject: [PATCH 12/13] Changes for accepting defaultActiveIndex as number --- packages/react/src/components/Tree/Tree.tsx | 22 ++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/packages/react/src/components/Tree/Tree.tsx b/packages/react/src/components/Tree/Tree.tsx index fe07b4fbb..219927d29 100644 --- a/packages/react/src/components/Tree/Tree.tsx +++ b/packages/react/src/components/Tree/Tree.tsx @@ -85,21 +85,26 @@ class Tree extends AutoControlledComponent, TreeState> { static autoControlledProps = ['activeIndex'] - getInitialAutoControlledState({ defaultActiveIndex, exclusive }): TreeState { + getInitialAutoControlledState({ exclusive }): TreeState { return { - activeIndex: defaultActiveIndex === undefined ? (exclusive ? -1 : []) : defaultActiveIndex, + activeIndex: exclusive ? -1 : [], } } - computeNewIndex = (index: number) => { + getActiveIndexAsArray() { const { activeIndex } = this.state + return _.isArray(activeIndex) ? activeIndex : [activeIndex] + } + + computeNewIndex = (index: number) => { const { exclusive } = this.props if (exclusive) return index + const activeIndexAsArray = this.getActiveIndexAsArray() // check to see if index is in array, and remove it, if not then add it - return _.includes(activeIndex as number[], index) - ? _.without(activeIndex as number[], index) - : [...(activeIndex as number[]), index] + return _.includes(activeIndexAsArray as number[], index) + ? _.without(activeIndexAsArray as number[], index) + : [...(activeIndexAsArray as number[]), index] } handleTreeItemOverrides = (predefinedProps: TreeItemProps) => ({ @@ -112,6 +117,7 @@ class Tree extends AutoControlledComponent, TreeState> { renderContent() { const { items, renderItemTitle, exclusive } = this.props const { activeIndex } = this.state + const activeIndexAsArray = this.getActiveIndexAsArray() return _.map(items, (item: ShorthandValue, index: number) => TreeItem.create(item, { @@ -119,7 +125,9 @@ class Tree extends AutoControlledComponent, TreeState> { index, exclusive, renderItemTitle, - open: exclusive ? index === activeIndex : _.includes(activeIndex as number[], index), + open: exclusive + ? index === activeIndex + : _.includes(activeIndexAsArray as number[], index), }, overrideProps: this.handleTreeItemOverrides, }), From 9d0ebfc3b549f76ce85c1f08d54360b6dc240ad4 Mon Sep 17 00:00:00 2001 From: PRIYANKAR GUPTA Date: Mon, 11 Mar 2019 14:58:28 +0530 Subject: [PATCH 13/13] Addressing comments --- packages/react/src/components/Tree/Tree.tsx | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/packages/react/src/components/Tree/Tree.tsx b/packages/react/src/components/Tree/Tree.tsx index 219927d29..caf0667e5 100644 --- a/packages/react/src/components/Tree/Tree.tsx +++ b/packages/react/src/components/Tree/Tree.tsx @@ -91,7 +91,7 @@ class Tree extends AutoControlledComponent, TreeState> { } } - getActiveIndexAsArray() { + getActiveIndexes(): number[] { const { activeIndex } = this.state return _.isArray(activeIndex) ? activeIndex : [activeIndex] } @@ -100,11 +100,11 @@ class Tree extends AutoControlledComponent, TreeState> { const { exclusive } = this.props if (exclusive) return index - const activeIndexAsArray = this.getActiveIndexAsArray() + const activeIndexes = this.getActiveIndexes() // check to see if index is in array, and remove it, if not then add it - return _.includes(activeIndexAsArray as number[], index) - ? _.without(activeIndexAsArray as number[], index) - : [...(activeIndexAsArray as number[]), index] + return _.includes(activeIndexes, index) + ? _.without(activeIndexes, index) + : [...activeIndexes, index] } handleTreeItemOverrides = (predefinedProps: TreeItemProps) => ({ @@ -117,7 +117,7 @@ class Tree extends AutoControlledComponent, TreeState> { renderContent() { const { items, renderItemTitle, exclusive } = this.props const { activeIndex } = this.state - const activeIndexAsArray = this.getActiveIndexAsArray() + const activeIndexes = this.getActiveIndexes() return _.map(items, (item: ShorthandValue, index: number) => TreeItem.create(item, { @@ -125,9 +125,7 @@ class Tree extends AutoControlledComponent, TreeState> { index, exclusive, renderItemTitle, - open: exclusive - ? index === activeIndex - : _.includes(activeIndexAsArray as number[], index), + open: exclusive ? index === activeIndex : _.includes(activeIndexes, index), }, overrideProps: this.handleTreeItemOverrides, }),