Skip to content

Commit

Permalink
Adds code to make the broken test pass
Browse files Browse the repository at this point in the history
  • Loading branch information
HazelGrant committed Dec 19, 2024
1 parent 4205ae7 commit 5875584
Show file tree
Hide file tree
Showing 2 changed files with 147 additions and 5 deletions.
142 changes: 141 additions & 1 deletion apps/dashboard/app/javascript/dynamic_forms.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const formTokens = [];
// elements. I.e., {'cluster': [ 'node_type' ] } means that changes to cluster
// trigger changes to node_type
const optionForHandlerCache = {};
const exclusiveOptionForHandlerCache = {};


// simples array of string ids for elements that have a handler
Expand Down Expand Up @@ -110,6 +111,7 @@ function memorizeElements(elements) {
elements.each((_i, ele) => {
formTokens.push(mountainCaseWords(shortId(ele['id'])));
optionForHandlerCache[ele['id']] = [];
exclusiveOptionForHandlerCache[ele['id']] = [];
});
};

Expand Down Expand Up @@ -137,6 +139,9 @@ function makeChangeHandlers(prefix){
if(key.startsWith('optionFor')) {
let token = key.replace(/^optionFor/,'');
addOptionForHandler(idFromToken(token), element['id']);
} else if (key.startsWith('exclusiveOptionFor')) {
let token = key.replace(/^exclusiveOptionFor/, '');
addExclusiveOptionForHandler(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')) {
Expand Down Expand Up @@ -537,7 +542,7 @@ function clamp(currentValue, previous, next) {

function addOptionForHandler(causeId, targetId) {
const changeId = String(causeId || '');

if(changeId.length == 0 || optionForHandlerCache[causeId].includes(targetId)) {
// nothing to do. invalid causeId or we already have a handler between the 2
return;
Expand All @@ -558,6 +563,29 @@ function addOptionForHandler(causeId, targetId) {
}
};

function addExclusiveOptionForHandler(causeId, targetId) {
const changeId = String(causeId || '');

if(changeId.length == 0 || exclusiveOptionForHandlerCache[causeId].includes(targetId)) {
// nothing to do. invalid causeId or we already have a handler between the 2
return;
}

let causeElement = $(`#${causeId}`);

if(targetId && causeElement) {
// cache the fact that there's a new handler here
exclusiveOptionForHandlerCache[causeId].push(targetId);

causeElement.on('change', (event) => {
toggleExclusiveOptionsFor(event, targetId);
});

// fake an event to initialize
toggleExclusiveOptionsFor({ target: document.querySelector(`#${causeId}`) }, targetId);
}
};

function parseCheckedWhen(key) {
const tokens = key.match(/^hide(\w+)When(\w+)$/);

Expand Down Expand Up @@ -709,6 +737,27 @@ function optionForFromToken(str) {
})[0];
}

/**
* Extract the option for out of an exclusive option for directive.
*
* @example
* exclusiveOptionForClusterOakley -> Cluster
*
* @param {*} str
* @returns - the option for string
*/
function exclusiveOptionForFromToken(str) {
return formTokens.map((token) => {
let match = str.match(`^exclusiveOptionFor${token}`);

if (match && match.length >= 1) {
return token;
}
}).filter((id) => {
return id !== undefined;
})[0];
}

/**
* Hide or show options of an element based on which cluster is
* currently selected and the data-option-for-CLUSTER attributes
Expand Down Expand Up @@ -798,6 +847,97 @@ function optionForFromToken(str) {
document.getElementById(elementId).dispatchEvent((new Event('change', { bubbles: true })));
};

/**
* Hide or show options of an element based on which cluster is
* currently selected and the data-exclusive-option-for-CLUSTER attributes
* for each option
*
* @param {string} element_name The name of the element with options to toggle
*/
function toggleExclusiveOptionsFor(_event, elementId) {
const options = [...document.querySelectorAll(`#${elementId} option`)];
let hideSelectedValue = undefined;

options.forEach(option => {
let hide = false;

// even though an event occured - an option may be hidden based on the value of
// something else entirely. We're going to hide this option if _any_ of the
// exlusive-option-for- directives apply.
for (let key of Object.keys(option.dataset)) {
let exclusiveOptionFor = exclusiveOptionForFromToken(key);
let exclusiveOptionForId = idFromToken(key.replace(/^exclusiveOptionFor/,''));

// it's some other directive type, so just keep going and/or not real
if(!key.startsWith('exclusiveOptionFor') || exclusiveOptionForId === undefined) {
continue;
}

let exclusiveOptionForValue = mountainCaseWords(document.getElementById(exclusiveOptionForId).value);

// handle special case where the very first token here is a number.
// browsers expect a prefix of hyphens as if it's the next token.
// if (exclusiveOptionForValue.match(/^\d/)) {
// exclusiveOptionForValue = `-${exclusiveOptionForValue}`;
// }
let exclusiveForCluster = option.dataset[`exclusiveOptionFor${exclusiveOptionFor}${exclusiveOptionForValue}`];

hide = !(exclusiveForCluster === 'true')

if (hide) {
break;
}
};

if(hide) {
if(option.selected) {
option.selected = false;
hideSelectedValue = option.textContent;
}

option.style.display = 'none';
option.disabled = true;
} else {
option.style.display = '';
option.disabled = false;
}
});

// now that we've hidden/shown everything, let's choose what should now
// be the current selected value.
// if you've hidden what _was_ selected.
if(hideSelectedValue !== undefined) {
let others = [...document.querySelectorAll(`#${elementId} option[value='${hideSelectedValue}']`)];
let newSelectedOption = undefined;

// You have hidden what _was_ selected, so try to find a duplicate option that is visible
if(others.length > 1) {
others.forEach(ele => {
if(ele.style.display === '') {
newSelectedOption = ele;
return;
}
});
}

// no duplciates are visible, so just pick the first visible option
if (newSelectedOption === undefined) {
others = document.querySelectorAll(`#${elementId} option`);
others.forEach(ele => {
if(newSelectedOption === undefined && ele.style.display === '') {
newSelectedOption = ele;
}
});
}

if (newSelectedOption !== undefined) {
newSelectedOption.selected = true;
}
}

// now that we're done, propogate this change to data-set or data-hide handlers
document.getElementById(elementId).dispatchEvent((new Event('change', { bubbles: true })));
};

export {
makeChangeHandlers
Expand Down
10 changes: 6 additions & 4 deletions apps/dashboard/test/system/batch_connect_widgets_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -481,7 +481,7 @@ def make_bc_app(dir, form)
end
end

test 'data-option-exlusive-for-*' do
test 'data-option-exlusive-for-' do
Dir.mktmpdir do |dir|
"#{dir}/app".tap { |d| Dir.mkdir(d) }
SysRouter.stubs(:base_path).returns(Pathname.new(dir))
Expand All @@ -504,7 +504,7 @@ def make_bc_app(dir, form)
widget: "select"
options:
- standard
- ['gpu', 'gpu', data-option-exclusive-for-cluster-owens: true]
- ['gpu', 'gpu', data-exclusive-option-for-cluster-owens: true]
HEREDOC

Pathname.new("#{dir}/app/").join('form.yml').write(form)
Expand All @@ -516,13 +516,15 @@ def make_bc_app(dir, form)
select('owens', from: 'batch_connect_session_context_cluster')
options = find_all("#batch_connect_session_context_node_type option")

display_property = { "display" => "block" }

assert_equal "standard", options[0]["innerHTML"]
assert_equal "gpu", options[1]["innerHTML"]
assert_equal display_property, options[1].style('display')

# pitzer is selected, gpu is not visible
select('pitzer', from: 'batch_connect_session_context_cluster')
options = find_all("#batch_connect_session_context_node_type option")

display_property = { "display" => "none" }

assert_equal "standard", options[0]["innerHTML"]
Expand Down

0 comments on commit 5875584

Please sign in to comment.