diff --git a/.babelrc.js b/.babelrc.js
index a183a8c4..f32e0bda 100755
--- a/.babelrc.js
+++ b/.babelrc.js
@@ -1,25 +1,18 @@
-const env = process.env.NODE_ENV;
-
-if (env === 'rollup') {
- module.exports = {
- presets: [
- [
- 'env',
- {
- modules: false,
- },
- ],
- 'stage-2',
- 'react',
+module.exports = {
+ presets: [
+ [
+ '@babel/preset-env',
+ {
+ modules: false,
+ },
],
- plugins: ['external-helpers'],
- };
-}
-
-if (env === 'test') {
- module.exports = {
- comments: false,
- plugins: ['transform-es2015-modules-commonjs'],
- presets: ['react', 'stage-2'],
- };
-}
+ '@babel/preset-react',
+ ],
+ env: {
+ test: {
+ plugins: [
+ '@babel/plugin-transform-modules-commonjs',
+ ],
+ }
+ }
+};
diff --git a/.eslintrc b/.eslintrc
deleted file mode 100755
index 8ad9fa72..00000000
--- a/.eslintrc
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "extends": ["airbnb", "prettier", "prettier/react"],
- "env": {
- "browser": true,
- "jest": true
- },
- "rules": {
- "react/jsx-filename-extension": 0,
- "react/prefer-stateless-function": 0,
- "react/no-did-mount-set-state": 0,
- "react/sort-comp": 0
- }
-}
diff --git a/.eslintrc.js b/.eslintrc.js
new file mode 100644
index 00000000..4f37152a
--- /dev/null
+++ b/.eslintrc.js
@@ -0,0 +1,19 @@
+module.exports = {
+ extends: [
+ 'eslint-config-airbnb',
+ 'prettier',
+ 'prettier/react'
+ ],
+ parser: 'babel-eslint',
+ env: {
+ browser: true,
+ jest: true
+ },
+ rules: {
+ 'react/destructuring-assignment': 0,
+ 'react/jsx-filename-extension': 0,
+ 'react/prefer-stateless-function': 0,
+ 'react/no-did-mount-set-state': 0,
+ 'react/sort-comp': 0
+ },
+}
diff --git a/.gitignore b/.gitignore
index c29a1638..6e375190 100755
--- a/.gitignore
+++ b/.gitignore
@@ -14,6 +14,7 @@ coverage
*.DS_Store
# Build directories (Will be preserved by npm)
+.cache
dist
build
style.css
diff --git a/package.json b/package.json
index e2cc5e45..413a7ac4 100644
--- a/package.json
+++ b/package.json
@@ -3,6 +3,7 @@
"version": "2.3.0",
"description": "Drag-and-drop sortable component for nested data and hierarchies",
"scripts": {
+ "postinstall": "cd node_modules/react-dnd-scrollzone && npm i --ignore-scripts && npm run build",
"prebuild": "npm run lint",
"build": "npm run clean && NODE_ENV=rollup rollup -c",
"build:storybook": "npm run clean:storybook && build-storybook -o build",
@@ -19,11 +20,6 @@
},
"main": "dist/index.cjs.js",
"module": "dist/index.esm.js",
- "babel": {
- "presets": [
- "./.babelrc.js"
- ]
- },
"files": [
"dist",
"style.css"
@@ -64,9 +60,9 @@
"dependencies": {
"lodash.isequal": "^4.5.0",
"prop-types": "^15.6.1",
- "react-dnd": "3.0.2",
- "react-dnd-html5-backend": "3.0.2",
- "react-dnd-scrollzone": "^4.0.0",
+ "react-dnd": "^6.0.0",
+ "react-dnd-html5-backend": "^6.0.0",
+ "react-dnd-scrollzone": "github:dolezel/react-dnd-scrollzone.git#41203ef00c991f744aa87e897d7d518d32f6a994",
"react-lifecycles-compat": "^3.0.4",
"react-virtualized": "^9.19.1"
},
@@ -75,62 +71,58 @@
"react-dom": "^15.3.0 || ^16.0.0"
},
"devDependencies": {
+ "@babel/cli": "^7.1.5",
+ "@babel/core": "^7.1.6",
+ "@babel/plugin-transform-modules-commonjs": "^7.1.0",
+ "@babel/preset-env": "^7.1.6",
+ "@babel/preset-react": "^7.0.0",
"@storybook/addon-options": "^4.0.0-alpha.4",
"@storybook/addon-storyshots": "^4.0.0-alpha.4",
"@storybook/react": "^4.0.0-alpha.4",
- "autoprefixer": "^8.5.2",
- "babel-cli": "^6.26.0",
- "babel-core": "^6.26.3",
- "babel-eslint": "^8.2.3",
- "babel-jest": "^23.0.1",
- "babel-loader": "^7.1.4",
- "babel-plugin-external-helpers": "^6.22.0",
- "babel-plugin-transform-es2015-modules-commonjs": "^6.26.2",
- "babel-plugin-transform-object-rest-spread": "^6.26.0",
- "babel-plugin-transform-runtime": "^6.23.0",
- "babel-polyfill": "^6.26.0",
- "babel-preset-env": "^1.7.0",
- "babel-preset-react": "^6.24.1",
- "babel-preset-stage-2": "^6.24.1",
+ "autoprefixer": "^9.3.1",
+ "babel-core": "^7.0.0-bridge.0",
+ "babel-eslint": "^10.0.1",
+ "babel-jest": "^23.6.0",
+ "babel-loader": "^8.0.4",
"codesandbox": "^1.2.10",
"coveralls": "^3.0.1",
"cross-env": "^5.1.6",
- "css-loader": "^0.28.11",
+ "css-loader": "^1.0.1",
"enzyme": "^3.3.0",
"enzyme-adapter-react-16": "^1.1.1",
- "eslint": "^4.19.1",
- "eslint-config-airbnb": "^16.1.0",
- "eslint-config-prettier": "^2.9.0",
+ "eslint": "^5.9.0",
+ "eslint-config-airbnb": "^17.1.0",
+ "eslint-config-prettier": "^3.3.0",
"eslint-plugin-import": "^2.12.0",
"eslint-plugin-jsx-a11y": "^6.0.3",
"eslint-plugin-react": "^7.8.2",
"extract-text-webpack-plugin": "^4.0.0-beta.0",
- "file-loader": "^1.1.11",
- "gh-pages": "^1.1.0",
+ "file-loader": "^2.0.0",
+ "gh-pages": "^2.0.1",
"html-webpack-plugin": "^3.2.0",
"jest": "^23.1.0",
- "jest-enzyme": "^6.0.1",
+ "jest-enzyme": "^7.0.1",
"json-loader": "^0.5.7",
- "postcss-loader": "^2.1.5",
+ "postcss-loader": "^3.0.0",
"prettier": "^1.13.3",
"react": "^16.4.0",
"react-addons-shallow-compare": "^15.6.2",
- "react-dnd-test-backend": "3.0.2",
- "react-dnd-touch-backend": "0.4.0",
+ "react-dnd-test-backend": "6.0.0",
+ "react-dnd-touch-backend": "0.6.0",
"react-dom": "^16.4.0",
"react-hot-loader": "^4.3.0",
"react-sortable-tree-theme-file-explorer": "^1.1.2",
"react-test-renderer": "^16.4.0",
"rimraf": "^2.6.2",
- "rollup": "^0.59.4",
- "rollup-plugin-babel": "^3.0.4",
+ "rollup": "^0.67.1",
+ "rollup-plugin-babel": "^4.0.3",
"rollup-plugin-commonjs": "^9.1.3",
"rollup-plugin-node-resolve": "^3.3.0",
"rollup-plugin-postcss": "^1.6.2",
"standard-version": "^4.4.0",
- "style-loader": "^0.21.0",
+ "style-loader": "^0.23.1",
"webpack": "^4.10.2",
- "webpack-cli": "^2.1.4",
+ "webpack-cli": "^3.1.2",
"webpack-dev-server": "^3.1.4",
"webpack-node-externals": "^1.7.2"
},
diff --git a/rollup.config.js b/rollup.config.js
index c1c72c74..6d02f531 100644
--- a/rollup.config.js
+++ b/rollup.config.js
@@ -23,6 +23,7 @@ export default {
'react',
'react-dom',
'react-dnd',
+ 'react-dnd/lib/DragDropContext',
'prop-types',
'react-dnd-html5-backend',
'react-dnd-scrollzone',
diff --git a/src/node-renderer-default.js b/src/node-renderer-default.js
index 7de81bd0..61b84513 100644
--- a/src/node-renderer-default.js
+++ b/src/node-renderer-default.js
@@ -96,16 +96,12 @@ class NodeRendererDefault extends Component {
}
/>
- {node.expanded &&
- !isDragging && (
-
- )}
+ {node.expanded && !isDragging && (
+
+ )}
)}
diff --git a/src/react-sortable-tree.js b/src/react-sortable-tree.js
index 6a6b10c9..d5729773 100644
--- a/src/react-sortable-tree.js
+++ b/src/react-sortable-tree.js
@@ -3,9 +3,11 @@ import PropTypes from 'prop-types';
import { AutoSizer, List } from 'react-virtualized';
import isEqual from 'lodash.isequal';
import withScrolling, {
+ createScrollingComponent,
createVerticalStrength,
createHorizontalStrength,
} from 'react-dnd-scrollzone';
+import { Consumer as DragDropContextConsumer } from 'react-dnd/lib/DragDropContext'
import { polyfill } from 'react-lifecycles-compat';
import 'react-virtualized/styles.css';
import TreeNode from './tree-node';
@@ -96,7 +98,7 @@ class ReactSortableTree extends Component {
// Prepare scroll-on-drag options for this list
if (isVirtualized) {
- this.scrollZoneVirtualList = withScrolling(List);
+ this.scrollZoneVirtualList = (createScrollingComponent || withScrolling)(List);
this.vStrength = createVerticalStrength(slideRegionSize);
this.hStrength = createHorizontalStrength(slideRegionSize);
}
@@ -142,7 +144,7 @@ class ReactSortableTree extends Component {
// Hook into react-dnd state changes to detect when the drag ends
// TODO: This is very brittle, so it needs to be replaced if react-dnd
// offers a more official way to detect when a drag ends
- this.clearMonitorSubscription = this.context.dragDropManager
+ this.clearMonitorSubscription = this.props.dragDropManager
.getMonitor()
.subscribeToStateChange(this.handleDndMonitorChange);
}
@@ -218,7 +220,7 @@ class ReactSortableTree extends Component {
}
handleDndMonitorChange() {
- const monitor = this.context.dragDropManager.getMonitor();
+ const monitor = this.props.dragDropManager.getMonitor();
// If the drag ends and the tree is still in a mid-drag state,
// it means that the drag was canceled or the dragSource dropped
// elsewhere, and we should reset the state of this tree
@@ -376,8 +378,6 @@ class ReactSortableTree extends Component {
depth: draggedDepth,
minimumTreeIndex: draggedMinimumTreeIndex,
}) {
- const { instanceProps } = this.state;
-
// Ignore this hover if it is at the same position as the last hover
if (
this.state.draggedDepth === draggedDepth &&
@@ -386,37 +386,38 @@ class ReactSortableTree extends Component {
return;
}
- // Fall back to the tree data if something is being dragged in from
- // an external element
- const draggingTreeData =
- this.state.draggingTreeData || instanceProps.treeData;
+ this.setState(({ draggingTreeData, instanceProps }) => {
+ // Fall back to the tree data if something is being dragged in from
+ // an external element
+ const newDraggingTreeData = draggingTreeData || instanceProps.treeData;
- const addedResult = memoizedInsertNode({
- treeData: draggingTreeData,
- newNode: draggedNode,
- depth: draggedDepth,
- minimumTreeIndex: draggedMinimumTreeIndex,
- expandParent: true,
- getNodeKey: this.props.getNodeKey,
- });
+ const addedResult = memoizedInsertNode({
+ treeData: newDraggingTreeData,
+ newNode: draggedNode,
+ depth: draggedDepth,
+ minimumTreeIndex: draggedMinimumTreeIndex,
+ expandParent: true,
+ getNodeKey: this.props.getNodeKey,
+ });
- const rows = this.getRows(addedResult.treeData);
- const expandedParentPath = rows[addedResult.treeIndex].path;
+ const rows = this.getRows(addedResult.treeData);
+ const expandedParentPath = rows[addedResult.treeIndex].path;
- this.setState({
- draggedNode,
- draggedDepth,
- draggedMinimumTreeIndex,
- draggingTreeData: changeNodeAtPath({
- treeData: draggingTreeData,
- path: expandedParentPath.slice(0, -1),
- newNode: ({ node }) => ({ ...node, expanded: true }),
- getNodeKey: this.props.getNodeKey,
- }),
- // reset the scroll focus so it doesn't jump back
- // to a search result while dragging
- searchFocusTreeIndex: null,
- dragging: true,
+ return {
+ draggedNode,
+ draggedDepth,
+ draggedMinimumTreeIndex,
+ draggingTreeData: changeNodeAtPath({
+ treeData: newDraggingTreeData,
+ path: expandedParentPath.slice(0, -1),
+ newNode: ({ node }) => ({ ...node, expanded: true }),
+ getNodeKey: this.props.getNodeKey,
+ }),
+ // reset the scroll focus so it doesn't jump back
+ // to a search result while dragging
+ searchFocusTreeIndex: null,
+ dragging: true,
+ };
});
}
@@ -604,6 +605,7 @@ class ReactSortableTree extends Component {
render() {
const {
+ dragDropManager,
style,
className,
innerStyle,
@@ -684,6 +686,7 @@ class ReactSortableTree extends Component {
{({ height, width }) => (
+
+ {({ dragDropManager }) => (
+ dragDropManager === undefined
+ ? null
+ :
+ )}
+
+
// Export the tree component without the react-dnd DragDropContext,
// for when component is used with other components using react-dnd.
// see: https://github.com/gaearon/react-dnd/issues/186
-export { ReactSortableTree as SortableTreeWithoutDndContext };
+export { SortableTreeWithoutDndContext };
-export default DndManager.wrapRoot(ReactSortableTree);
+export default DndManager.wrapRoot(SortableTreeWithoutDndContext);
diff --git a/src/react-sortable-tree.test.js b/src/react-sortable-tree.test.js
index 70986845..5dd6908c 100644
--- a/src/react-sortable-tree.test.js
+++ b/src/react-sortable-tree.test.js
@@ -24,79 +24,90 @@ describe('', () => {
});
it('should render nodes for flat data', () => {
- const wrapper = mount( {}} />);
+ let wrapper
// No nodes
+ wrapper = mount( {}} />);
expect(wrapper.find(TreeNode).length).toEqual(0);
// Single node
- wrapper.setProps({
- treeData: [{}],
- });
+ wrapper = mount( {}} />);
expect(wrapper.find(TreeNode).length).toEqual(1);
// Two nodes
- wrapper.setProps({
- treeData: [{}, {}],
- });
+ wrapper = mount( {}} />);
expect(wrapper.find(TreeNode).length).toEqual(2);
});
it('should render nodes for nested, expanded data', () => {
- const wrapper = mount(
+ let wrapper
+
+ // Single Nested
+ wrapper = mount(
{}}
/>
);
-
- // Single Nested
expect(wrapper.find(TreeNode).length).toEqual(2);
// Double Nested
- wrapper.setProps({
- treeData: [
- { expanded: true, children: [{ expanded: true, children: [{}] }] },
- ],
- });
+ wrapper = mount(
+ {}}
+ />
+ );
expect(wrapper.find(TreeNode).length).toEqual(3);
// 2x Double Nested Siblings
- wrapper.setProps({
- treeData: [
- { expanded: true, children: [{ expanded: true, children: [{}] }] },
- { expanded: true, children: [{ expanded: true, children: [{}] }] },
- ],
- });
+ wrapper = mount(
+ {}}
+ />
+ );
expect(wrapper.find(TreeNode).length).toEqual(6);
});
it('should render nodes for nested, collapsed data', () => {
- const wrapper = mount(
+ let wrapper
+
+ // Single Nested
+ wrapper = mount(
{}}
/>
);
-
- // Single Nested
expect(wrapper.find(TreeNode).length).toEqual(1);
// Double Nested
- wrapper.setProps({
- treeData: [
- { expanded: false, children: [{ expanded: false, children: [{}] }] },
- ],
- });
+ wrapper = mount(
+ {}}
+ />
+ );
expect(wrapper.find(TreeNode).length).toEqual(1);
// 2x Double Nested Siblings, top level of first expanded
- wrapper.setProps({
- treeData: [
- { expanded: true, children: [{ expanded: false, children: [{}] }] },
- { expanded: false, children: [{ expanded: false, children: [{}] }] },
- ],
- });
+ wrapper = mount(
+ {}}
+ />
+ );
expect(wrapper.find(TreeNode).length).toEqual(3);
});
@@ -296,7 +307,7 @@ describe('', () => {
/>
);
- const tree = wrapper.find(SortableTreeWithoutDndContext).instance();
+ const tree = wrapper.find('ReactSortableTree').instance();
expect(tree.state.searchMatches).toEqual([
{ node: { title: 'b' }, path: [0, 1], treeIndex: 1 },
{ node: { title: 'be' }, path: [2, 3], treeIndex: 3 },
@@ -428,7 +439,7 @@ describe('', () => {
// Retrieve our DnD-wrapped node component type
const wrappedNodeType = wrapper
- .find(SortableTreeWithoutDndContext)
+ .find('ReactSortableTree')
.instance().nodeContentRenderer;
// And get the first such component
diff --git a/src/utils/default-handlers.js b/src/utils/default-handlers.js
index 98c62004..e23d7ecb 100644
--- a/src/utils/default-handlers.js
+++ b/src/utils/default-handlers.js
@@ -34,7 +34,8 @@ function stringSearch(key, searchQuery, node, path, treeIndex) {
return (
String(node[key]({ node, path, treeIndex })).indexOf(searchQuery) > -1
);
- } else if (typeof node[key] === 'object') {
+ }
+ if (typeof node[key] === 'object') {
// Search within text inside react elements
return getReactElementText(node[key]).indexOf(searchQuery) > -1;
}
diff --git a/src/utils/dnd-manager.js b/src/utils/dnd-manager.js
index 469143fc..64d74337 100644
--- a/src/utils/dnd-manager.js
+++ b/src/utils/dnd-manager.js
@@ -63,16 +63,15 @@ export default class DndManager {
const rowAbove = dropTargetProps.getPrevRow();
if (rowAbove) {
let { path } = rowAbove;
- const aboveNodeCannotHaveChildren = !this.treeRef.canNodeHaveChildren(rowAbove.node);
+ const aboveNodeCannotHaveChildren = !this.treeRef.canNodeHaveChildren(
+ rowAbove.node
+ );
if (aboveNodeCannotHaveChildren) {
path = path.slice(0, path.length - 1);
}
// Limit the length of the path to the deepest possible
- dropTargetDepth = Math.min(
- path.length,
- dropTargetProps.path.length
- );
+ dropTargetDepth = Math.min(path.length, dropTargetProps.path.length);
}
let blocksOffset;
diff --git a/src/utils/tree-data-utils.js b/src/utils/tree-data-utils.js
index 79e2d481..836c902b 100644
--- a/src/utils/tree-data-utils.js
+++ b/src/utils/tree-data-utils.js
@@ -432,7 +432,8 @@ export function changeNodeAtPath({
return typeof newNode === 'function'
? newNode({ node, treeIndex: currentTreeIndex })
: newNode;
- } else if (!node.children) {
+ }
+ if (!node.children) {
// If this node is part of the path, but has no children, return the unchanged node
throw new Error('Path referenced children of node with no children.');
}
diff --git a/src/utils/tree-data-utils.test.js b/src/utils/tree-data-utils.test.js
index e88e49e0..448331cf 100644
--- a/src/utils/tree-data-utils.test.js
+++ b/src/utils/tree-data-utils.test.js
@@ -1602,7 +1602,8 @@ describe('walk', () => {
if (node.key === 2) {
// Cut walk short with false
return false;
- } else if (node.key === 3) {
+ }
+ if (node.key === 3) {
throw new Error('walk not terminated by false');
}
diff --git a/stories/__snapshots__/storyshots.test.js.snap b/stories/__snapshots__/storyshots.test.js.snap
index c92296c9..bb29966a 100644
--- a/stories/__snapshots__/storyshots.test.js.snap
+++ b/stories/__snapshots__/storyshots.test.js.snap
@@ -2829,7 +2829,7 @@ exports[`Storyshots Basics Add and remove nodes programmatically 1`] = `