Skip to content

Commit

Permalink
refactored subthought and thought test for flat-render
Browse files Browse the repository at this point in the history
use react-use-measure instead of custom hook

cancel debounced height callback function on unmount

set cursor offset
  • Loading branch information
shresthabijay committed Sep 2, 2020
1 parent 11158d9 commit 91197e8
Show file tree
Hide file tree
Showing 9 changed files with 130 additions and 107 deletions.
8 changes: 8 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"react-split-pane": "^0.1.89",
"react-spring": "^9.0.0-rc.3",
"react-transition-group": "^4.3.0",
"react-use-measure": "^2.0.1",
"redux": "^4.0.5",
"redux-devtools-extension": "^2.13.8",
"redux-multi": "^0.1.12",
Expand Down
12 changes: 10 additions & 2 deletions src/components/FlatTreeRenderer.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
/* eslint-disable */
import React, { useCallback, useEffect, useRef, useState } from 'react'
import _ from 'lodash'
import { connect } from 'react-redux'
import { Interpolation, SpringValue, useSpring, useTransition } from 'react-spring'
import useMeasure from '../hooks/useMeasure.js'
import useMeasure from 'react-use-measure'
import { store } from '../store.js'

// util
Expand Down Expand Up @@ -124,7 +125,8 @@ const TreeNode = ({
}: TreeNode) => {

// @ts-ignore
const [measureBind, { height: measuredHeight }] = useMeasure()
const [measureBind, bounds] = useMeasure()
const { height: measuredHeight } = bounds
const viewHeight: number = measuredHeight
const viewHeightRef = useRef<number>(viewHeight)
const wrapperDivRef = useRef<HTMLDivElement>(null)
Expand Down Expand Up @@ -222,6 +224,8 @@ const TreeNode = ({
)
}

TreeNode.displayName = 'TreeNode'

/**
* Calculate x offset.
*/
Expand Down Expand Up @@ -292,6 +296,10 @@ const TreeAnimation = ({
}
}

React.useEffect(() => {
return () => debouncedHeightUpdate && debouncedHeightUpdate.cancel()
}, [])

const transitions = useTransition(
flatArray,
{
Expand Down
9 changes: 6 additions & 3 deletions src/components/ThoughtNew.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ const mapStateToProps = (state: State, props: ThoughtProps) => {
thoughtsRanked,
viewInfo,
depth,
thoughtsResolved
thoughtsResolved,
} = props.nodeItem

const { childrenForced } = props
Expand Down Expand Up @@ -361,7 +361,7 @@ const ThoughtWrapper = ({ measureBind, wrapperDivRef, innerDivStyle, wrapperStyl
return (
<animated.div style={wrapperStyle} ref={wrapperDivRef}>
<animated.div style={innerDivStyle}>
<div {...measureBind}>
<div ref={measureBind}>
<animated.div
className={classNames({
node: true,
Expand All @@ -378,6 +378,8 @@ const ThoughtWrapper = ({ measureBind, wrapperDivRef, innerDivStyle, wrapperStyl
)
}

ThoughtWrapper.displayName = 'ThoughtWrapper'

/** A thought container with bullet, thought annotation, thought, and subthoughts.
*
@param allowSingleContext Pass through to Subthoughts since the SearchSubthoughts component does not have direct access to the Subthoughts of the Subthoughts of the search. Default: false.
Expand All @@ -388,6 +390,7 @@ const ThoughtContainer = ({
isEditing,
thoughtsRankedLive,
isCursorParent,
cursorOffset,
nodeItem
}: ThoughtContainerProps & ThoughtProps) => {
const { thoughtsResolved, thoughtsRanked, contextChain, expanded, isCursor, viewInfo, childrenLength, hasChildren, parentKey } = nodeItem
Expand Down Expand Up @@ -464,7 +467,7 @@ const ThoughtContainer = ({
: isThoughtDivider ? <DividerNew thoughtsRanked={thoughtsRanked} isActive={isCursor} parentKey={parentKey!}/>
: <Editable
contextChain={contextChain}
cursorOffset={0}
cursorOffset={cursorOffset}
disabled={!isDocumentEditable()}
isEditing={isEditing}
rank={rank}
Expand Down
132 changes: 60 additions & 72 deletions src/components/__tests__/Subthoughts.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
import { store } from '../../store'
import createTestApp, { cleanupTestApp } from '../../test-helpers/createTestApp'
import { RANKED_ROOT } from '../../constants'
import { equalArrays, pathToContext } from '../../util'
import { pathToContext } from '../../util'
import { importText } from '../../action-creators'
import Editable from '../Editable'
import Thought from '../Thought'
import Subthoughts from '../Subthoughts'

/** A filterWhere predicate that returns true for Thought or Subthought nodes that match the given context. */
const whereContext = context => node => equalArrays(pathToContext(node.props().thoughtsRanked), context)
import { findNodeByContext, findNodeByResolvedContext, findSubthoughtsByKey } from '../../test-helpers/flatRenderHelper'

// const debugThoughtWrapper = wrapper => wrapper.map(node => ({
// name: node.name(),
Expand All @@ -33,25 +29,29 @@ afterEach(async () => {
it('normal view', async () => {

// import thoughts
await store.dispatch(importText(RANKED_ROOT, `- a
- b
- c`))
await store.dispatch(importText(RANKED_ROOT, `
- a
- b
- c`))

// set the cursor to expand the subthoughts
store.dispatch({ type: 'setCursor', thoughtsRanked: [{ value: 'a', rank: 0 }] })

// update DOM
wrapper.update()

// select elements
const subthoughtsWrapper = wrapper.find('.children .children')
const thoughtsWrapper = subthoughtsWrapper.find(Thought)
// select parent node
const parentNode = findNodeByContext(wrapper, ['a']).at(0)

// find subthoughts by parent's node key.
const subthoughtNodes = findSubthoughtsByKey(wrapper, parentNode.props().item.key)

// assert
expect(thoughtsWrapper).toHaveLength(2)
expect(pathToContext(thoughtsWrapper.first().props().thoughtsRanked))
expect(subthoughtNodes).toHaveLength(2)

expect(pathToContext(subthoughtNodes.first().props().item.thoughtsRanked))
.toMatchObject(['a', 'b'])
expect(pathToContext(thoughtsWrapper.at(1).props().thoughtsRanked))
expect(pathToContext(subthoughtNodes.at(1).props().item.thoughtsRanked))
.toMatchObject(['a', 'c'])

})
Expand All @@ -75,22 +75,28 @@ describe('context view', () => {
wrapper.update()

// assert context view container
const subthoughtsWrapper = wrapper
.find(Subthoughts)
.filterWhere(whereContext(['a', 'm']))
.first() // have to select first node, as second node is empty-children with contextChain (?)
const parentNode = findNodeByContext(wrapper, ['a', 'm']).at(0) // have to select first node, as second node is empty-children with contextChain (?)

// assert contexts
const contextsWrapper = subthoughtsWrapper.find(Thought)
expect(contextsWrapper).toHaveLength(2)
expect(contextsWrapper.at(0).props())
const subthoughtNodes = findSubthoughtsByKey(wrapper, parentNode.props().item.key)
expect(subthoughtNodes).toHaveLength(2)

expect(subthoughtNodes.at(0).props().item)
.toMatchObject({
showContexts: true,
viewInfo: {
context: {
showContextsParent: true
}
},
thoughtsRanked: [{ value: 'a' }, { value: 'm' }],
})
expect(contextsWrapper.at(1).props())
expect(subthoughtNodes.at(1).props().item)
.toMatchObject({
showContexts: true,
viewInfo: {
context: {
showContextsParent: true
}
},
thoughtsRanked: [{ value: 'b' }, { value: 'm' }],
})

Expand All @@ -99,12 +105,13 @@ describe('context view', () => {
it('render context children of contexts that have different lexeme instances', async () => {

// import thoughts
await store.dispatch(importText(RANKED_ROOT, `- a
- one
- x
- b
- ones
- y`))
await store.dispatch(importText(RANKED_ROOT, `
- a
- one
- x
- b
- one
- y`))

// enable Context View on /a/one
store.dispatch({ type: 'setCursor', thoughtsRanked: [{ value: 'a', rank: 0 }, { value: 'one', rank: 1 }] })
Expand All @@ -113,58 +120,39 @@ describe('context view', () => {
// update DOM
wrapper.update()

/** Select /a/one Subthoughts component. Call function after re-render to use new DOM. */
const subthoughtsAOne = () => wrapper
.find(Subthoughts)
.filterWhere(whereContext(['a', 'one']))
const subthoughtsAOne1 = subthoughtsAOne()
expect(subthoughtsAOne1).toHaveLength(2)
// select /a/one node.
const nodeAOne1 = findNodeByContext(wrapper, ['a', 'one'])
expect(nodeAOne1).toHaveLength(2)

// check if the context first subthought has been rendered
const nodeAOneA = findNodeByResolvedContext(wrapper, ['a', 'one', 'a'])
expect(nodeAOneA).toHaveLength(1)

// select first context (a)
// select editable of node a/one~/a to get it to expand
// use childAt to get passed Connected HOC
const editableAOneA = subthoughtsAOne1.find(Editable).at(0).childAt(0)
const editableAOneA = nodeAOneA.find(Editable).at(0).childAt(0)
expect(editableAOneA).toHaveLength(1)

// focus on a/one~/a to get it to expand
editableAOneA.simulate('focus')
wrapper.update()

// select a/one~/a Subthoughts component
const subthoughtsAOneA = subthoughtsAOne()
.find(Subthoughts)
.filterWhere(whereContext(['a', 'one']))
.filterWhere(node => node.props().contextChain.length > 0)
expect(subthoughtsAOneA).toHaveLength(1)

// assert that child of context is rendered
const thoughtsAOneA = subthoughtsAOneA.find(Thought)
expect(thoughtsAOneA).toHaveLength(1)
expect(thoughtsAOneA.first().props())
.toMatchObject({
thoughtsRanked: [{ value: 'a' }, { value: 'one' }, { value: 'x' }],
})
/// assert that child of a/one~/a is rendered
const nodeAOneAX = findNodeByResolvedContext(wrapper, ['a', 'one', 'a', 'x'])
expect(nodeAOneAX).toHaveLength(1)

// check if the context first subthought has been rendered
const nodeAOneB = findNodeByResolvedContext(wrapper, ['a', 'one', 'b'])
expect(nodeAOneB).toHaveLength(1)

// select second context (b)
// select editable of node a/one~/a to get it to expand
// focus on a/one~/b to get it to expand
const editableAOneB = subthoughtsAOne1.find(Editable).at(1).childAt(0)
const editableAOneB = nodeAOneB.find(Editable).at(0).childAt(0)
expect(editableAOneB).toHaveLength(1)
editableAOneB.simulate('focus')
wrapper.update()

// select a/one~/b Subthoughts component
const subthoughtsAOneB = subthoughtsAOne()
.find(Subthoughts)
.filterWhere(whereContext(['b', 'ones']))
.filterWhere(node => node.props().contextChain.length > 0)
expect(subthoughtsAOneB).toHaveLength(1)

// assert that child of context is rendered
const thoughtsAOneB = subthoughtsAOneB.find(Thought)
expect(thoughtsAOneB).toHaveLength(1)
expect(thoughtsAOneB.first().props())
.toMatchObject({
thoughtsRanked: [{ value: 'b' }, { value: 'ones' }, { value: 'y' }],
})
})
/// assert that child of a/one~/a is rendered
const nodeAOneAY = findNodeByResolvedContext(wrapper, ['a', 'one', 'b', 'y'])
expect(nodeAOneAY).toHaveLength(1)

})
})
22 changes: 12 additions & 10 deletions src/components/__tests__/Thought.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,17 @@ afterEach(async () => {

it('create, navigate, and edit thoughts', async () => {

// create thought
// // create thought
windowEvent('keydown', { key: 'Enter' })
wrapper.update()
const editable = wrapper.find('div.editable')
const editable = wrapper.find('.node div.editable')
await editable.simulate('change', { target: { value: 'a' } })

// create subthought
windowEvent('keydown', { key: 'Enter', ctrlKey: true })
wrapper.update()
const editableSubthought = wrapper.find('.children .children div.editable')
const editableSubthought = wrapper.find('.node div.editable').at(1)

await editableSubthought.simulate('change', { target: { value: 'a1' } })

// cursor back
Expand All @@ -45,10 +46,12 @@ it('create, navigate, and edit thoughts', async () => {

// DOM
wrapper.update()
const editableSubthoughts2 = wrapper.find('.children .children div.editable')
expect(editableSubthoughts2).toHaveLength(2)
expect(editableSubthoughts2.at(0).text()).toBe('')
expect(editableSubthoughts2.at(1).text()).toBe('a1')
const editableSubthoughts2 = wrapper.find('.node div.editable')
expect(editableSubthoughts2).toHaveLength(3)

expect(editableSubthoughts2.at(0).text()).toBe('a')
expect(editableSubthoughts2.at(1).text()).toBe('')
expect(editableSubthoughts2.at(2).text()).toBe('a1')

})

Expand All @@ -68,13 +71,13 @@ it('caret is set on new subthought', async () => {
// create thought
windowEvent('keydown', { key: 'Enter' })
wrapper.update()
const editable = wrapper.find('div.editable')
const editable = wrapper.find('.node div.editable')
await editable.simulate('change', { target: { value: 'a' } })

// create subthought
windowEvent('keydown', { key: 'Enter', ctrlKey: true })
wrapper.update()
const editableSubthought = wrapper.find('.children .children div.editable')
const editableSubthought = wrapper.find('.node div.editable').at(1)
await editableSubthought.simulate('change', { target: { value: 'a1' } })
act(jest.runOnlyPendingTimers)

Expand All @@ -85,5 +88,4 @@ it('caret is set on new subthought', async () => {
const { focusNode, focusOffset } = window.getSelection() || {}
expect(focusNode.textContent).toEqual('a1')
expect(focusOffset).toEqual(0)

})
20 changes: 0 additions & 20 deletions src/hooks/useMeasure.js

This file was deleted.

Loading

0 comments on commit 91197e8

Please sign in to comment.