diff --git a/apps/dashboard/app/javascript/packs/batchConnect.js b/apps/dashboard/app/javascript/packs/batchConnect.js index 39af19b220..0b8281da2e 100644 --- a/apps/dashboard/app/javascript/packs/batchConnect.js +++ b/apps/dashboard/app/javascript/packs/batchConnect.js @@ -16,13 +16,13 @@ const optionForHandlerCache = {}; // simples array of string ids for elements that have a handler const minMaxHandlerCache = []; const setHandlerCache = []; +const hideHandlerCache = []; -// Lookup table for setting min & max values. -// TODO: changeme to minMaxDirectiveLookup -const lookup = {}; - -// Lookup table for setting values of other elements -const setDirectiveLookup = {}; +// Lookup tables for setting min & max values +// for different directives. +const minMaxLookup = {}; +const setValueLookup = {}; +const hideLookup = {}; function bcElement(name) { return `${bcPrefix}_${name.toLowerCase()}`; @@ -117,12 +117,14 @@ function makeChangeHandlers(){ if(keys.length !== 0) { keys.forEach((key) => { if(key.startsWith('optionFor')) { - let token = key.replace('optionFor',''); + let token = key.replace(/^optionFor/,''); addOptionForHandler(idFromToken(token), element['id']); } else if(key.startsWith('max') || key.startsWith('min')) { addMinMaxForHandler(element['id'], opt.value, key, data[key]); } else if(key.startsWith('set')) { addSetHandler(element['id'], opt.value, key, data[key]); + } else if(key.startsWith('hide')) { + addHideHandler(element['id'], opt.value, key, data[key]); } }); } @@ -131,6 +133,26 @@ function makeChangeHandlers(){ }); }; +function addHideHandler(optionId, option, key, configValue) { + const changeId = idFromToken(key.replace(/^hide/,'')); + + if(hideLookup[optionId] === undefined) hideLookup[optionId] = new Table(changeId, undefined); + const table = hideLookup[optionId]; + table.put(option, undefined, configValue); + + if(!hideHandlerCache.includes(optionId)) { + const changeElement = $(`#${optionId}`); + + changeElement.on('change', (event) => { + updateVisibility(event, changeId); + }); + + hideHandlerCache.push(optionId); + } + + updateVisibility({ target: document.querySelector(`#${optionId}`) }, changeId); +} + /** * * @param {*} optionId batch_connect_session_context_node_type @@ -158,8 +180,8 @@ function addMinMaxForHandler(optionId, option, key, configValue) { const secondDimId = configObj['predicateId']; const secondDimValue = configObj['predicateValue']; - if(lookup[id] === undefined) lookup[id] = new Table(optionId, secondDimId); - const table = lookup[id]; + if(minMaxLookup[id] === undefined) minMaxLookup[id] = new Table(optionId, secondDimId); + const table = minMaxLookup[id]; table.put(option, secondDimValue, {[minOrMax(key)] : configValue }); let cacheKey = `${optionId}_${secondDimId}`; @@ -207,8 +229,8 @@ function addSetHandler(optionId, option, key, configValue) { // id is account. optionId is classroom let cacheKey = `${id}_${optionId}` - if(setDirectiveLookup[cacheKey] === undefined) setDirectiveLookup[cacheKey] = new Table(optionId, undefined); - const table = setDirectiveLookup[cacheKey]; + if(setValueLookup[cacheKey] === undefined) setValueLookup[cacheKey] = new Table(optionId, undefined); + const table = setValueLookup[cacheKey]; table.put(option, undefined, configValue); if(!setHandlerCache.includes(cacheKey)) { @@ -227,7 +249,7 @@ function addSetHandler(optionId, option, key, configValue) { function setValue(event, changeId) { const chosenVal = event.target.value; const cacheKey = `${changeId}_${event.target['id']}` - const table = setDirectiveLookup[cacheKey]; + const table = setValueLookup[cacheKey]; if (table === undefined) return; const changeVal = table.get(chosenVal, undefined); @@ -310,12 +332,32 @@ class Table { } } +/** + * Update the visibility of `changeId` based on the + * event and what's in the hideLookup table. + */ +function updateVisibility(event, changeId) { + const val = event.target.value; + const id = event.target['id']; + const changeElement = $(`#${changeId}`).parent(); + + if (changeElement.size() <= 0) return; + + // safe to access directly? + const hide = hideLookup[id].get(val, undefined); + if(hide === undefined) { + changeElement.show(); + }else if(hide === true) { + changeElement.hide(); + } +} + function toggleMinMax(event, changeId, otherId) { let x = undefined, y = undefined; // in the example of cluster & node_type, either element can trigger a change // so let's figure out the axis' based on the change element's id. - if(event.target['id'] == lookup[changeId].x) { + if(event.target['id'] == minMaxLookup[changeId].x) { x = snakeCaseWords(event.target.value); y = snakeCaseWords($(`#${otherId}`).val()); } else { @@ -324,7 +366,7 @@ function toggleMinMax(event, changeId, otherId) { } const changeElement = $(`#${changeId}`); - const mm = lookup[changeId].get(x, y); + const mm = minMaxLookup[changeId].get(x, y); const prev = { min: changeElement.attr('min'), max: changeElement.attr('max'), @@ -459,7 +501,6 @@ function minOrMax(key) { } } - /** * Turn a MountainCase token into a form element id * diff --git a/apps/dashboard/test/fixtures/sys_with_gateway_apps/bc_jupyter/form.yml b/apps/dashboard/test/fixtures/sys_with_gateway_apps/bc_jupyter/form.yml index 43c3d78eb2..4a8e90b803 100644 --- a/apps/dashboard/test/fixtures/sys_with_gateway_apps/bc_jupyter/form.yml +++ b/apps/dashboard/test/fixtures/sys_with_gateway_apps/bc_jupyter/form.yml @@ -18,6 +18,7 @@ attributes: options: - [ "any", + data-hide-cuda-version: true, data-minnn-bc-not-found-for-cluster-mistype: 30, data-max-bc-not-found-for-cluster-mistype: 30, data-maximum-bc-not-found-for-cluster-mistype: 30 @@ -35,18 +36,21 @@ attributes: - [ "hugemem", data-option-for-cluster-oakley: false, + data-hide-cuda-version: true, data-max-bc-num-slots-for-cluster-owens: 42, - data-min-bc-num-slots-for-cluster-owens: 42 + data-min-bc-num-slots-for-cluster-owens: 42, ] - [ "advanced", data-option-for-cluster-oakley: false, + data-hide-cuda-version: true, data-max-bc-num-slots-for-cluster-oakley: 9001 ] # this node type is the same for both clusters, so there's no 'for-cluster-...' clause # TODO: this currently breaks tests - [ "same", + data-hide-cuda-version: true, data-min-bc-num-slots: 100, data-max-bc-num-slots: 200 ] @@ -81,6 +85,12 @@ attributes: "4.0.nightly", data-set-hidden-change-thing: 'python4nightly', ] + cuda_version: + widget: select + options: + - "4" + - "7" + - "38" form: - bc_num_hours - bc_num_slots @@ -88,4 +98,5 @@ form: - bc_account - bc_email_on_started - python_version + - cuda_version - hidden_change_thing diff --git a/apps/dashboard/test/system/batch_connect_test.rb b/apps/dashboard/test/system/batch_connect_test.rb index 1a8fb52f0e..4884b66bd6 100644 --- a/apps/dashboard/test/system/batch_connect_test.rb +++ b/apps/dashboard/test/system/batch_connect_test.rb @@ -15,13 +15,13 @@ def setup select('oakley', from: bc_ele_id('cluster')) # FIXME: no idea why .visible? doesn't work here. Selenium/chrome native still shows element as visible? - assert_equal 'display: none;', find_option_style('node_type', 'advanced') - assert_equal 'display: none;', find_option_style('node_type', 'hugemem') + assert_equal('display: none;', find_option_style('node_type', 'advanced')) + assert_equal('display: none;', find_option_style('node_type', 'hugemem')) # select owens and now they're available select('owens', from: bc_ele_id('cluster')) - assert_equal '', find_option_style('node_type', 'advanced') - assert_equal '', find_option_style('node_type', 'hugemem') + assert_equal('', find_option_style('node_type', 'advanced')) + assert_equal('', find_option_style('node_type', 'hugemem')) end test 'node type choice changes python versions' do @@ -239,4 +239,20 @@ def setup select('4.0.nightly', from: bc_ele_id('python_version')) assert_equal 'python4nightly', find_value('hidden_change_thing', visible: false) end + + test 'hiding cuda version' do + visit new_batch_connect_session_context_url('sys/bc_jupyter') + + # default is any, so we can't see cuda_version + assert_equal 'any', find_value('node_type') + assert !find("##{bc_ele_id('cuda_version')}", visible: false).visible? + + # select gpu and you can + select('gpu', from: bc_ele_id('node_type')) + assert find("##{bc_ele_id('cuda_version')}").visible? + + # toggle back to 'same' and it's gone + select('same', from: bc_ele_id('node_type')) + assert !find("##{bc_ele_id('cuda_version')}", visible: false).visible? + end end