diff --git a/integration/specs/Tree/tree-1.spec.js b/integration/specs/Tree/tree-1.spec.js
new file mode 100644
index 000000000..0d578e4fa
--- /dev/null
+++ b/integration/specs/Tree/tree-1.spec.js
@@ -0,0 +1,64 @@
+const PageTree = require('../../../src/components/Tree/pageObject');
+const { TAB_KEY, ENTER_KEY, SPACE_KEY } = require('../../constants');
+
+const TREE = '#tree-component-1';
+
+describe('Tree basic', () => {
+ beforeAll(() => {
+ browser.url('/#!/Tree/1');
+ });
+ beforeEach(() => {
+ browser.refresh();
+ const component = $(TREE);
+ component.waitForExist();
+ });
+
+ it('should expand the node when it is collapse and its button icon is clicked', () => {
+ const tree = new PageTree(TREE);
+ const node = tree.getNode(3);
+ node.click();
+ expect(node.isExpanded()).toBe(true);
+ });
+ it('should collapse the node when it is expand and its button icon is clicked', () => {
+ const tree = new PageTree(TREE);
+ const node = tree.getNode(2);
+ node.click();
+ expect(node.isExpanded()).toBe(false);
+ });
+ it('should move focus to the next button icon when the first button icon is focused and press tab', () => {
+ const tree = new PageTree(TREE);
+ const firstNode = tree.getNode(2);
+ const secondNode = tree.getNode(3);
+ firstNode.click();
+ browser.keys(TAB_KEY);
+ expect(secondNode.hasFocus()).toBe(true);
+ });
+ it('should expand the node when its button icon is focused, press enter and the node was initially expanded', () => {
+ const tree = new PageTree(TREE);
+ const node = tree.getNode(2);
+ node.click();
+ browser.keys(ENTER_KEY);
+ expect(node.isExpanded()).toBe(true);
+ });
+ it('should collapse the node when its button icon is focused, press enter and the node was initially collapse', () => {
+ const tree = new PageTree(TREE);
+ const node = tree.getNode(3);
+ node.click();
+ browser.keys(ENTER_KEY);
+ expect(node.isExpanded()).toBe(false);
+ });
+ it('should expand the node when its button icon is focused, press space and the node was initially expanded', () => {
+ const tree = new PageTree(TREE);
+ const node = tree.getNode(2);
+ node.click();
+ browser.keys(SPACE_KEY);
+ expect(node.isExpanded()).toBe(true);
+ });
+ it('should collapse the node when its button icon is focused, press space and the node was initially collapse', () => {
+ const tree = new PageTree(TREE);
+ const node = tree.getNode(3);
+ node.click();
+ browser.keys(SPACE_KEY);
+ expect(node.isExpanded()).toBe(false);
+ });
+});
diff --git a/src/components/Tree/__test__/child.spec.js b/src/components/Tree/__test__/child.spec.js
new file mode 100644
index 000000000..7b486200c
--- /dev/null
+++ b/src/components/Tree/__test__/child.spec.js
@@ -0,0 +1,24 @@
+import React from 'react';
+import { mount } from 'enzyme';
+import Child from './../child';
+
+describe('', () => {
+ it('should render the PrimitiveCheckbox component when isChecked prop has the right value', () => {
+ [true, false, 'indeterminate'].forEach(value => {
+ const component = mount();
+ expect(component.find('PrimitiveCheckbox').exists()).toBe(true);
+ });
+ });
+ it('should not render the PrimitiveCheckbox component when isChecked prop has the wrong value', () => {
+ ['indeterminates', 'one', 'six'].forEach(value => {
+ const component = mount();
+ expect(component.find('PrimitiveCheckbox').exists()).toBe(false);
+ });
+ });
+ it('should render the TreeChildren component when children prop is not undefined', () => {
+ const children = [{ label: 'Tree Item' }, { label: 'Tree Item' }];
+ // eslint-disable-next-line react/no-children-prop
+ const component = mount();
+ expect(component.find('TreeChildren').exists()).toBe(true);
+ });
+});
diff --git a/src/components/Tree/__test__/expandCollapseButton.spec.js b/src/components/Tree/__test__/expandCollapseButton.spec.js
new file mode 100644
index 000000000..e0a063dba
--- /dev/null
+++ b/src/components/Tree/__test__/expandCollapseButton.spec.js
@@ -0,0 +1,40 @@
+import React from 'react';
+import { mount } from 'enzyme';
+import ExpandCollapseButton from './../expandCollapseButton';
+
+describe('', () => {
+ it('should return the Spinner component when isLoading prop is true', () => {
+ const component = mount();
+ expect(component.find('Spinner').exists()).toBe(true);
+ });
+ it('should return the ButtonIcon component when hasChildren prop is true', () => {
+ const component = mount();
+ expect(component.find('ButtonIcon').exists()).toBe(true);
+ });
+ it('should set the right icon when isExpanded prop is true', () => {
+ const component = mount();
+ expect(
+ component
+ .find('ButtonIcon')
+ .find('DownArrow')
+ .exists(),
+ ).toBe(true);
+ });
+ it('should set the right icon when isExpanded prop is false', () => {
+ const component = mount();
+ expect(
+ component
+ .find('ButtonIcon')
+ .find('RightArrow')
+ .exists(),
+ ).toBe(true);
+ });
+ it('should fire onclick callback when ButtonIcon is clicked', () => {
+ const onClickMock = jest.fn();
+ const component = mount(
+ ,
+ );
+ component.find('ButtonIcon').simulate('click');
+ expect(onClickMock).toHaveBeenCalledTimes(1);
+ });
+});
diff --git a/src/components/Tree/__test__/tree.spec.js b/src/components/Tree/__test__/tree.spec.js
new file mode 100644
index 000000000..734ca6756
--- /dev/null
+++ b/src/components/Tree/__test__/tree.spec.js
@@ -0,0 +1,67 @@
+import React from 'react';
+import { mount } from 'enzyme';
+import Tree from '../index';
+
+const data = [
+ { label: 'Tree Item', isChecked: false },
+ { label: 'Tree Item', isChecked: false },
+ {
+ label: 'Tree Branch',
+ isLoading: false,
+ isExpanded: true,
+ isChecked: false,
+ children: [
+ { label: 'Tree Item', isChecked: false },
+ {
+ label: 'Tree Branch',
+ isLoading: false,
+ isExpanded: true,
+ isChecked: false,
+ children: [{ label: 'Tree Item', isChecked: false }],
+ },
+ ],
+ },
+ {
+ label: 'Tree Branch',
+ isExpanded: true,
+ isChecked: false,
+ children: [
+ { label: 'Tree Item', isChecked: false },
+ {
+ label: 'Tree Branch',
+ isLoading: false,
+ isExpanded: true,
+ isChecked: false,
+ children: [{ label: 'Tree Item', isChecked: false }],
+ },
+ ],
+ },
+];
+
+describe('', () => {
+ it('should call onExpandCollapse with the right parameters when the button is clicked', () => {
+ const nodePath = [2, 1];
+ const onExpandCollapsekMock = jest.fn();
+ const component = mount();
+ component
+ .find('ButtonIcon')
+ .at(1)
+ .simulate('click');
+ expect(onExpandCollapsekMock).toHaveBeenCalledWith({ nodePath });
+ });
+ it('should call onSelect with the right parameters when the node is selected', () => {
+ const nodePath = [2];
+ const onSelectMock = jest.fn();
+ const component = mount();
+ component
+ .find('PrimitiveCheckbox')
+ .at(2)
+ .find('input')
+ .simulate('change');
+ expect(onSelectMock).toHaveBeenCalledWith({ nodePath });
+ });
+ it('should render the correct number of children', () => {
+ const component = mount();
+ expect(component.find('Child').length).toBe(10);
+ });
+});
diff --git a/src/components/Tree/child.js b/src/components/Tree/child.js
index fa8c2422a..350f040f0 100644
--- a/src/components/Tree/child.js
+++ b/src/components/Tree/child.js
@@ -26,8 +26,8 @@ export default function Child(props) {
const hasCheckbox = typeof isChecked === 'boolean' || isChecked === 'indeterminate';
const hasIcon = !!icon;
return (
-
-
+
+
{
+ it('should return the right node when nodePath has only one element', () => {
+ const nodePath = [2];
+ const expectedNode = {
+ label: 'Tree Branch',
+ isExpanded: true,
+ children: [
+ { label: 'Tree Item' },
+ {
+ label: 'Tree Branch',
+ isLoading: false,
+ children: [{ label: 'Tree Item' }],
+ },
+ ],
+ };
+ expect(getNode(tree, nodePath)).toStrictEqual(expectedNode);
+ });
+ it('should return the right node when nodePath has more than one element', () => {
+ const nodePath = [2, 1];
+ const expectedNode = {
+ label: 'Tree Branch',
+ isLoading: false,
+ children: [{ label: 'Tree Item' }],
+ };
+ expect(getNode(tree, nodePath)).toStrictEqual(expectedNode);
+ });
+});
diff --git a/src/components/Tree/index.d.ts b/src/components/Tree/index.d.ts
index ba8954e61..b291c5848 100644
--- a/src/components/Tree/index.d.ts
+++ b/src/components/Tree/index.d.ts
@@ -9,8 +9,10 @@ interface DataItem {
label?: ReactNode;
icon?: ReactNode;
isExpanded?: boolean;
+ isLoading?: boolean;
isChecked?: boolean | 'indeterminate';
children?: DataItem[];
+ id?: string;
}
export interface TreeProps extends BaseProps {
diff --git a/src/components/Tree/index.js b/src/components/Tree/index.js
index 456d54ec7..9990b306d 100644
--- a/src/components/Tree/index.js
+++ b/src/components/Tree/index.js
@@ -9,10 +9,10 @@ import getNode from './helpers/getNode';
* @category Layout
*/
export default function Tree(props) {
- const { data, onExpandCollapse, onSelect, className, style } = props;
+ const { data, onExpandCollapse, onSelect, className, style, id } = props;
return (
-
+
{},
className: undefined,
style: undefined,
+ id: undefined,
};
/**
diff --git a/src/components/Tree/pageObject/index.js b/src/components/Tree/pageObject/index.js
new file mode 100644
index 000000000..37d77700e
--- /dev/null
+++ b/src/components/Tree/pageObject/index.js
@@ -0,0 +1,33 @@
+const PageNodeItem = require('./node');
+
+/**
+ * Tree page object class.
+ * @class
+ */
+class PageTree {
+ /**
+ * Create a new Tree page object.
+ * @constructor
+ * @param {string} rootElement - The selector of the Tree root element.
+ */
+ constructor(rootElement) {
+ this.rootElement = rootElement;
+ }
+
+ /**
+ * Returns a new Node page object of the element in item position.
+ * @method
+ * @param {number} itemPosition - The base 0 index of the Node.
+ */
+ getNode(itemPosition) {
+ const items = $(this.rootElement).$$('[data-id="node-element-li"]');
+ if (items[itemPosition]) {
+ return new PageNodeItem(
+ `${this.rootElement} [data-id="node-element-li"]:nth-child(${itemPosition + 1})`,
+ );
+ }
+ return null;
+ }
+}
+
+module.exports = PageTree;
diff --git a/src/components/Tree/pageObject/node.js b/src/components/Tree/pageObject/node.js
new file mode 100644
index 000000000..9984b7990
--- /dev/null
+++ b/src/components/Tree/pageObject/node.js
@@ -0,0 +1,60 @@
+/**
+ * Node page object class.
+ * @class
+ */
+class PageNodeItem {
+ /**
+ * Create a new Node page object.
+ * @constructor
+ * @param {string} rootElement - The selector of the Node root element.
+ */
+ constructor(rootElement) {
+ this.rootElement = rootElement;
+ }
+
+ /**
+ * Clicks the button icon element.
+ * @method
+ */
+ click() {
+ $(this.rootElement)
+ .$('[data-id="node-element"] > button')
+ .click();
+ }
+
+ /**
+ * Returns true when the button icon has focus.
+ * @method
+ * @returns {bool}
+ */
+ hasFocus() {
+ return $(this.rootElement)
+ .$('[data-id="node-element"] > button')
+ .isFocused();
+ }
+
+ /**
+ * Returns true when the node is expanded, false otherwise.
+ * @method
+ * @returns {bool}
+ */
+ isExpanded() {
+ return $(this.rootElement)
+ .$('[data-id="node-element-li"]')
+ .isDisplayed();
+ }
+
+ /**
+ * Returns the label of the node.
+ * @method
+ * @returns {string}
+ */
+ getLabel() {
+ return $(this.rootElement)
+ .$('[data-id="node-element"]')
+ .$('h1')
+ .getText();
+ }
+}
+
+module.exports = PageNodeItem;
diff --git a/src/components/Tree/readme.md b/src/components/Tree/readme.md
index fbe6e9027..1c3966ba6 100644
--- a/src/components/Tree/readme.md
+++ b/src/components/Tree/readme.md
@@ -36,6 +36,7 @@
setState({ data: state.data });
}