diff --git a/src/ui/ElementStyler.js b/src/ui/ElementStyler.js index d4ad572..3101a05 100644 --- a/src/ui/ElementStyler.js +++ b/src/ui/ElementStyler.js @@ -20,7 +20,7 @@ * * @needsLove * - Everything! This class exists from when the framework was barely - * more than a few prototypes and has rotted ever since with little else + * more than a few prototypes and has rotted ever since with little else * but workarounds. * @end needsLove */ @@ -82,6 +82,18 @@ module.exports = Class( 'ElementStyler', */ 'private _$context': null, + /** + * jQuery Object + * @type {jQuery} + */ + 'private _jquery': null, + + /** + * HTML Document + * @type {HTMLElement} + */ + 'private _document': null, + _answerStyles: { 'deductible': function( value, _, default_val ) @@ -186,6 +198,8 @@ module.exports = Class( 'ElementStyler', __construct: function( jquery ) { this._$context = jquery; + this._jquery = jquery; + this._document = jquery( 'body' ).context; }, @@ -802,28 +816,34 @@ module.exports = Class( 'ElementStyler', * * This allows for a simple mapping from bucket to UI. * - * @param {string} name element name (question name) - * @param {number=} index index of element to retrieve (bucket index) - * @param {string=} filter filter to apply to widgets - * @param {jQuery=} $context filtering context + * @param {string} name element name (question name) + * @param {number=} index index of element to retrieve (bucket index) + * @param {string=} filter filter to apply to widgets + * @param {HTMLElement} $context filtering context * * @return {jQuery} matches */ - 'public getWidgetByName': function( name, index, filter, $context ) + 'public getWidgetByName': function( name, index, filter, context ) { - $context = $context || this._$context; + context = context || this._$context; + + // Todo: Transitional step to remove jQuery + if ( context instanceof jQuery ) + { + context = context[ 0 ]; + } // find the field; note that we *skip* the index selection if we have // been notified---via a property on the context---that the content // should contain only the index we are looking for - var $results = this._getWidgetByNameQuick( name, index, $context ); + var results = this._getWidgetByNameQuick( name, index, context ); if ( filter ) { - return $results.filter( filter ); + throw new Error( 'Filter deprecated' ); } - return $results; + return this._jquery( results ); }, @@ -835,33 +855,31 @@ module.exports = Class( 'ElementStyler', * performed the check to begin with, so the idea is to find the id for as * many as possible. */ - 'private _getWidgetByNameQuick': function( name, index, $context ) + 'private _getWidgetByNameQuick': function( name, index, context ) { - var hasindex = ( ( index !== undefined ) && !$context.singleIndex ); + var hasindex = ( ( index !== undefined ) && !context.singleIndex ); if ( hasindex ) { - var id = this._getElementId( name, index, $context ); + var id = this._getElementId( name, index ); if ( id ) { - $element = $context.find( '#' + id ); + element = this._document.getElementById( id ); // let's hope for the best - if ( $element.length ) + if ( element !== null ) { - return $element; + return element; } } + + // Fallback to the painfully slow method. + return context.querySelectorAll( '[data-field-name="' + name + '"]' )[ index ]; } - // damnit. Fallback to the painfully slow method. - return $context.find( '[data-field-name="' + name + '"]' + - ( ( hasindex ) - ? ':nth(' + +index + ')' - : '' - ) - ); + // If no index, return first element + return context.querySelector( '[data-field-name="' + name + '"]' ); }, @@ -922,28 +940,32 @@ module.exports = Class( 'ElementStyler', * using NAME as an element id; otherwise, this acts just as * getElementByName. * - * @param {string} name element name (question name) - * @param {number=} index index of element to retrieve (bucket index) - * @param {string=} filter filter to apply to widgets - * @param {jQuery=} $context filtering context + * @param {string} name element name (question name) + * @param {number=} index index of element to retrieve (bucket index) + * @param {string=} filter filter to apply to widgets + * @param {HTMLElement} context filtering context * * @return {jQuery} matches */ 'public getElementByNameLax': function( - name, index, filter, $context + name, index, filter, context ) { - $context = $context || this._$context; + context = context || this._$context; + + // Todo: Transitional step to remove jQuery + if ( context instanceof jQuery ) + { + context = context[ 0 ]; + } if ( !( this.isAField( name ) ) ) { - return $context.find( - '#' + name + ':nth(' + index + ')' - ); + return this._jquery( context.querySelectorAll( '#' + name )[ index ] ); } return this.getElementByName( - name, index, filter, $context + name, index, filter, context ); }, @@ -1030,11 +1052,25 @@ module.exports = Class( 'ElementStyler', }, - 'private _getElementId': function( name, index, $context ) + /** + * Determines the id of an element based on the type + * + * @param {string} name element name + * @param {number} index index of element to retrieve (bucket index) + * + * @return {string} element id + */ + 'private _getElementId': function( name, index ) { switch ( this._getElementType( name ) ) { - case 'radio': return ''; + case 'radio': + return ''; + case 'answer': + return name; + case 'checkbox': + name += '_n'; + break; case 'noyes': // append yes/no depending on whether or not the given index is // even/odd @@ -1042,13 +1078,11 @@ module.exports = Class( 'ElementStyler', ? '_y' : '_n'; - index = index / 2; - - /* fallthrough */ - - default: - return 'q_' + name + '_' + index; + index = Math.floor( index / 2 ); + break; } + + return 'q_' + name + '_' + index; }, diff --git a/test/ui/ElementStylerTest.js b/test/ui/ElementStylerTest.js new file mode 100644 index 0000000..30812a8 --- /dev/null +++ b/test/ui/ElementStylerTest.js @@ -0,0 +1,154 @@ +/** + * Test case for ElementStyler + * + * Copyright (C) 2010-2020 R-T Specialty, LLC. + * + * This file is part of the Liza Data Collection Framework + * + * Liza is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +var Sut = require( '../../' ).ui.ElementStyler, + expect = require( 'chai' ).expect, + sinon = require( 'sinon' ), + Class = require( 'easejs' ).Class; + + +describe( 'ui.ElementStyler', () => +{ + [ + { + name: 'business_city', + css_class: '', + index: '0', + qtypes: { + business_city: { + type: 'text', + } + }, + expected_id: 'q_business_city_0', + }, + + { + name: 'business_city', + css_class: '', + index: '1', + qtypes: { + business_city: { + type: 'text', + } + }, + expected_id: 'q_business_city_1', + }, + + { + name: 'sgo_foo', + css_class: 'checkbox', + index: '0', + qtypes: { + sgo_foo: { + type: 'checkbox', + } + }, + expected_id: 'q_sgo_foo_n_0', + }, + + { + name: 'noyes_foo', + css_class: 'noyes', + index: '0', + qtypes: { + noyes_foo: { + type: 'noyes', + } + }, + expected_id: 'q_noyes_foo_n_0', + }, + + { + name: 'noyes_foo', + css_class: 'noyes', + index: '1', + qtypes: { + noyes_foo: { + type: 'noyes', + } + }, + expected_id: 'q_noyes_foo_y_0', + }, + + { + name: 'noyes_baz', + css_class: 'noyes', + index: '4', + qtypes: { + noyes_baz: { + type: 'noyes', + } + }, + expected_id: 'q_noyes_baz_n_2', + }, + + { + name: 'noyes_baz', + css_class: 'noyes', + index: '5', + qtypes: { + noyes_baz: { + type: 'noyes', + } + }, + expected_id: 'q_noyes_baz_y_2', + }, + ].forEach( ( { name, css_class, qtypes, index, expected_id } ) => + { + it( "determines the correct element id for " + name, () => + { + // Stub document and jQuery calls + $ = sinon.stub(); + jQuery = sinon.stub(); + + const document = { + getElementById: sinon.stub() + }; + + jQuery.withArgs( 'body' ).returns( { context: document } ); + + const sut = Sut( jQuery ); + + sut.setTypeData( qtypes ); + + const node = ''; + + const html = '
' + node + '
'; + + const context = { + html: html, + }; + + document.getElementById.returns( node ); + + sut.getWidgetByName( name, index, null, context ); + + const actual_id = document.getElementById.getCall( 0 ).args[ 0 ]; + + expect( actual_id ) + .to.equal( expected_id ); + } ); + } ); +} );