Skip to content

Commit 9a3e3ec

Browse files
committed
feat(ui-tree-browser): add animation prop to TreeBrowser
INSTUI-4683
1 parent 327fe69 commit 9a3e3ec

File tree

8 files changed

+128
-41
lines changed

8 files changed

+128
-41
lines changed
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* The MIT License (MIT)
3+
*
4+
* Copyright (c) 2015 - present Instructure, Inc.
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all
14+
* copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
* SOFTWARE.
23+
*/
24+
25+
import { createContext } from 'react'
26+
27+
type TreeBrowserContextType = {
28+
/**
29+
* Whether to enable animation for the TreeBrowser
30+
*/
31+
animation?: boolean
32+
}
33+
34+
/**
35+
* ---
36+
* category: utilities/react
37+
* ---
38+
* React context created by the `TreeBrowser` component to hold its data which are
39+
* read by its children.
40+
*
41+
* @module
42+
*/
43+
const TreeBrowserContext = createContext<TreeBrowserContextType>({
44+
animation: false
45+
})
46+
47+
export default TreeBrowserContext
48+
export { TreeBrowserContext }
49+
export type { TreeBrowserContextType }

packages/ui-tree-browser/src/TreeBrowser/TreeButton/index.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
* SOFTWARE.
2323
*/
2424

25-
import { Component } from 'react'
25+
import { Component, ContextType } from 'react'
2626

2727
import { testable } from '@instructure/ui-testable'
2828
import { Img } from '@instructure/ui-img'
@@ -33,6 +33,7 @@ import generateStyles from './styles'
3333
import generateComponentTheme from './theme'
3434
import type { TreeBrowserButtonProps } from './props'
3535
import { allowedProps, propTypes } from './props'
36+
import TreeBrowserContext from '../TreeBrowserContext'
3637

3738
// Todo: merge TreeButton and TreeNode: TreeButton should be a special type of TreeNode
3839

@@ -50,6 +51,9 @@ class TreeButton extends Component<TreeBrowserButtonProps> {
5051
static allowedProps = allowedProps
5152
static propTypes = propTypes
5253

54+
static contextType = TreeBrowserContext
55+
declare context: ContextType<typeof TreeBrowserContext>
56+
5357
static defaultProps = {
5458
type: 'treeButton',
5559
size: 'medium',
@@ -62,11 +66,11 @@ class TreeButton extends Component<TreeBrowserButtonProps> {
6266
ref: Element | null = null
6367

6468
componentDidMount() {
65-
this.props.makeStyles?.()
69+
this.props.makeStyles?.({ animation: this.context?.animation })
6670
}
6771

6872
componentDidUpdate() {
69-
this.props.makeStyles?.()
73+
this.props.makeStyles?.({ animation: this.context?.animation })
7074
}
7175

7276
defaultContentRenderer(props: TreeBrowserButtonProps) {

packages/ui-tree-browser/src/TreeBrowser/TreeButton/styles.ts

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,13 @@ const transform = keyframes`
4545
* Generates the style object from the theme and provided additional information
4646
* @param {Object} componentTheme The theme variable object.
4747
* @param {Object} props the props of the component, the style is applied to
48+
* @param {Object} state the state of the component, the style is applied to
4849
* @return {Object} The final style object, which will be used in the component
4950
*/
5051
const generateStyles = (
5152
componentTheme: TreeBrowserButtonTheme,
52-
props: TreeBrowserButtonProps
53+
props: TreeBrowserButtonProps,
54+
state: { animation?: boolean }
5355
): TreeBrowserButtonStyle => {
5456
const { size, variant, selected, focused, level } = props
5557

@@ -226,14 +228,18 @@ const generateStyles = (
226228
borderRadius: componentTheme.borderRadius,
227229
position: 'relative',
228230
zIndex: 1,
229-
transform: 'translate3d(-2%, 0, 0)',
230-
opacity: 0.01,
231-
transformOrigin: 'left center',
232-
animationName: transform,
233-
animationDuration: '0.2s',
234-
animationFillMode: 'forwards',
235-
animationTimingFunction: 'ease-out',
236-
animationDelay: '0.2s',
231+
...(state.animation
232+
? {
233+
transform: 'translate3d(-2%, 0, 0)',
234+
opacity: 0.01,
235+
transformOrigin: 'left center',
236+
animationName: transform,
237+
animationDuration: '0.2s',
238+
animationFillMode: 'forwards',
239+
animationTimingFunction: 'ease-out',
240+
animationDelay: '0.2s'
241+
}
242+
: {}),
237243
outline: '0',
238244
padding: '0',
239245
...(variant === 'folderTree' &&
@@ -278,9 +284,10 @@ const generateStyles = (
278284
? componentTheme.selectedBackgroundColor
279285
: componentTheme.hoverBackgroundColor,
280286

281-
'[class$=-treeButton__textName], [class$=-treeButton__textDescriptor], [class$=-treeButton__icon], [class$=-treeButton__node]': {
282-
color: componentTheme.hoverTextColor
283-
}
287+
'[class$=-treeButton__textName], [class$=-treeButton__textDescriptor], [class$=-treeButton__icon], [class$=-treeButton__node]':
288+
{
289+
color: componentTheme.hoverTextColor
290+
}
284291
},
285292
...(selected && {
286293
backgroundColor: componentTheme.selectedBackgroundColor

packages/ui-tree-browser/src/TreeBrowser/TreeCollection/index.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
* SOFTWARE.
2323
*/
2424

25-
import { Component, ReactElement, SyntheticEvent } from 'react'
25+
import { Component, ContextType, ReactElement, SyntheticEvent } from 'react'
2626

2727
import { testable } from '@instructure/ui-testable'
2828
import { withStyle } from '@instructure/emotion'
@@ -36,6 +36,7 @@ import type { TreeBrowserCollectionProps, TreeCollectionState } from './props'
3636
import type { CompareObject } from '../props'
3737
import { allowedProps, propTypes } from './props'
3838
import { CollectionItem, CollectionProps, CollectionData } from '../props'
39+
import TreeBrowserContext from '../TreeBrowserContext'
3940

4041
type AriaSelectedType = { 'aria-selected'?: boolean }
4142

@@ -56,6 +57,9 @@ class TreeCollection extends Component<
5657
static allowedProps = allowedProps
5758
static propTypes = propTypes
5859

60+
static contextType = TreeBrowserContext
61+
declare context: ContextType<typeof TreeBrowserContext>
62+
5963
static defaultProps = {
6064
collections: [],
6165
items: [],
@@ -76,10 +80,10 @@ class TreeCollection extends Component<
7680
}
7781

7882
componentDidMount() {
79-
this.props.makeStyles?.()
83+
this.props.makeStyles?.({ animation: this.context?.animation })
8084
}
8185
componentDidUpdate() {
82-
this.props.makeStyles?.()
86+
this.props.makeStyles?.({ animation: this.context?.animation })
8387
}
8488

8589
get itemsLevel() {

packages/ui-tree-browser/src/TreeBrowser/TreeCollection/styles.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ const list = keyframes`
4848

4949
const generateStyles = (
5050
componentTheme: TreeBrowserCollectionTheme,
51-
props: TreeBrowserCollectionProps
51+
props: TreeBrowserCollectionProps,
52+
state: { animation?: boolean }
5253
): TreeBrowserCollectionStyle => {
5354
const { size, variant } = props
5455
const sizeMap = {
@@ -115,12 +116,16 @@ const generateStyles = (
115116
bottom: '1.1875rem',
116117
insetInlineStart: '0',
117118
insetInlineEnd: 'auto',
118-
transform: 'scaleY(0.01)',
119-
transformOrigin: 'center top',
120-
animationName: list,
121-
animationDuration: '0.2s',
122-
animationFillMode: 'forwards',
123-
animationTimingFunction: 'ease-out',
119+
...(state.animation
120+
? {
121+
transform: 'scaleY(0.01)',
122+
transformOrigin: 'center top',
123+
animationName: list,
124+
animationDuration: '0.2s',
125+
animationFillMode: 'forwards',
126+
animationTimingFunction: 'ease-out'
127+
}
128+
: {}),
124129
pointerEvents: 'none',
125130
width: componentTheme.borderWidth,
126131
background: componentTheme.borderColor

packages/ui-tree-browser/src/TreeBrowser/TreeNode/index.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
* SOFTWARE.
2323
*/
2424

25-
import { Component } from 'react'
25+
import { Component, ContextType } from 'react'
2626

2727
import { Img } from '@instructure/ui-img'
2828
import { callRenderProp } from '@instructure/ui-react-utils'
@@ -33,6 +33,7 @@ import generateStyles from '../TreeButton/styles'
3333
import generateComponentTheme from '../TreeButton/theme'
3434
import type { TreeBrowserNodeProps } from './props'
3535
import { allowedProps, propTypes } from './props'
36+
import TreeBrowserContext from '../TreeBrowserContext'
3637

3738
// Todo: merge TreeButton and TreeNode: TreeButton should be a special type of TreeNode
3839

@@ -52,6 +53,9 @@ class TreeNode extends Component<TreeBrowserNodeProps> {
5253
static allowedProps = allowedProps
5354
static propTypes = propTypes
5455

56+
static contextType = TreeBrowserContext
57+
declare context: ContextType<typeof TreeBrowserContext>
58+
5559
static defaultProps = {
5660
size: 'medium',
5761
variant: 'folderTree',
@@ -62,11 +66,11 @@ class TreeNode extends Component<TreeBrowserNodeProps> {
6266
ref: Element | null = null
6367

6468
componentDidMount() {
65-
this.props.makeStyles?.()
69+
this.props.makeStyles?.({ animation: this.context?.animation })
6670
}
6771

6872
componentDidUpdate() {
69-
this.props.makeStyles?.()
73+
this.props.makeStyles?.({ animation: this.context?.animation })
7074
}
7175

7276
handleRef = (el: HTMLDivElement) => {

packages/ui-tree-browser/src/TreeBrowser/index.tsx

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ import type {
4545
TreeBrowserState
4646
} from './props'
4747
import { allowedProps, propTypes } from './props'
48+
import TreeBrowserContext from './TreeBrowserContext'
4849

4950
/**
5051
---
@@ -72,7 +73,8 @@ class TreeBrowser extends Component<TreeBrowserProps, TreeBrowserState> {
7273
selectionType: 'none',
7374
sortOrder: function () {
7475
return 0
75-
}
76+
},
77+
animation: true
7678
}
7779

7880
static Node = TreeNode
@@ -364,18 +366,24 @@ class TreeBrowser extends Component<TreeBrowserProps, TreeBrowserState> {
364366
const { styles } = this.props
365367

366368
return (
367-
<ul
368-
css={styles?.treeBrowser}
369-
tabIndex={0}
370-
role="tree"
371-
onKeyDown={this.handleKeyDown}
372-
ref={(el) => {
373-
this.ref = el
369+
<TreeBrowserContext.Provider
370+
value={{
371+
animation: this.props.animation
374372
}}
375-
aria-label={this.props.treeLabel}
376373
>
377-
{this.renderRoot()}
378-
</ul>
374+
<ul
375+
css={styles?.treeBrowser}
376+
tabIndex={0}
377+
role="tree"
378+
onKeyDown={this.handleKeyDown}
379+
ref={(el) => {
380+
this.ref = el
381+
}}
382+
aria-label={this.props.treeLabel}
383+
>
384+
{this.renderRoot()}
385+
</ul>
386+
</TreeBrowserContext.Provider>
379387
)
380388
}
381389
}

packages/ui-tree-browser/src/TreeBrowser/props.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,10 @@ type TreeBrowserOwnProps = {
8383
* An optional compare function to specify order of the collections and the items
8484
*/
8585
sortOrder?: (obj1: any, obj2: any) => number
86+
/**
87+
* Whether to enable animation for the TreeBrowser
88+
*/
89+
animation?: boolean
8690
} & TreeBrowserBaseProps
8791

8892
// props shared between TreeBrowser, TreeCollection
@@ -204,7 +208,8 @@ const propTypes: PropValidators<PropKeys> = {
204208
onItemClick: PropTypes.func,
205209
treeLabel: PropTypes.string,
206210
renderContent: PropTypes.func,
207-
sortOrder: PropTypes.func
211+
sortOrder: PropTypes.func,
212+
animation: PropTypes.bool
208213
}
209214

210215
const allowedProps: AllowedPropKeys = [
@@ -227,7 +232,8 @@ const allowedProps: AllowedPropKeys = [
227232
'onItemClick',
228233
'treeLabel',
229234
'renderContent',
230-
'sortOrder'
235+
'sortOrder',
236+
'animation'
231237
]
232238

233239
type TreeBrowserState = {

0 commit comments

Comments
 (0)