diff --git a/src/js/component.js b/src/js/component.js index 0593e06969..35ac4acbe3 100644 --- a/src/js/component.js +++ b/src/js/component.js @@ -416,6 +416,37 @@ class Component { return this.childNameIndex_[name]; } + /** + * Returns the descendant `Component` following the givent + * descendant `names`. For instance ['foo', 'bar', 'baz'] would + * try to get 'foo' on the current component, 'bar' on the 'foo' + * component and 'baz' on the 'bar' component and return undefined + * if any of those don't exist. + * + * @param {...string[]|...string} names + * The name of the child `Component` to get. + * + * @return {Component|undefined} + * The descendant `Component` following the given descendant + * `names` or undefined. + */ + getDescendant(...names) { + // flatten array argument into the main array + names = names.reduce((acc, n) => acc.concat(n), []); + + let currentChild = this; + + for (let i = 0; i < names.length; i++) { + currentChild = currentChild.getChild(names[i]); + + if (!currentChild || !currentChild.getChild) { + return; + } + } + + return currentChild; + } + /** * Add a child `Component` inside the current `Component`. * diff --git a/test/unit/component.test.js b/test/unit/component.test.js index 1b8bc0c4b5..fcd4d1d59e 100644 --- a/test/unit/component.test.js +++ b/test/unit/component.test.js @@ -1203,3 +1203,24 @@ QUnit.test('should remove child when the child moves to the other parent', funct parentComponent2.dispose(); childComponent.dispose(); }); + +QUnit.test('getDescendant should work as expected', function(assert) { + const comp = new Component(getFakePlayer(), {name: 'component'}); + const descendant1 = new Component(getFakePlayer(), {name: 'descendant1'}); + const descendant2 = new Component(getFakePlayer(), {name: 'descendant2'}); + const descendant3 = new Component(getFakePlayer(), {name: 'descendant3'}); + + comp.addChild(descendant1); + descendant1.addChild(descendant2); + descendant2.addChild(descendant3); + + assert.equal(comp.getDescendant('descendant1', 'descendant2', 'descendant3'), descendant3, 'can pass as args'); + assert.equal(comp.getDescendant(['descendant1', 'descendant2', 'descendant3']), descendant3, 'can pass as array'); + assert.equal(comp.getDescendant('descendant1'), descendant1, 'can pass as single string'); + assert.equal(comp.getDescendant(), comp, 'no args returns base component'); + assert.notOk(comp.getDescendant('descendant5'), 'undefined descendant returned'); + assert.notOk(comp.getDescendant('descendant1', 'descendant5'), 'undefined descendant returned'); + assert.notOk(comp.getDescendant(['descendant1', 'descendant5']), 'undefined descendant returned'); + + comp.dispose(); +});