Skip to content

Conversation

@ahmed-mgd
Copy link
Contributor

Fixes #1446

Comment on lines 511 to 513
} else {
const defaultVal = dim == 'min' ? 1 : 999;
changeElement.attr(dim, defaultVal);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I love the precision here in making just a small enough change, but sadly, I feel like it's not enough.

It seems to me that we should embed the min or max to fall back on. Take this for example, we should fall back to these values if they're defined.

some_element:
  widget: number_field
  min: 2
  max: 4

If we can't fallback to any values, then we just don't. I think the conservative route of just doing nothing is better than choosing 1 or 999. But we can document that these are fallback values when you use data-min or data-max sparingly.

@Bubballoo3 Bubballoo3 moved this from Awaiting Review to Changes Requested in PR Review Pipeline Oct 2, 2025
Comment on lines 49 to 59
:div,
id: [form.object_name, attrib.id, 'wrapper'].join('_'),
class: attrib.hide_by_default? ? 'd-none' : '',
data: data
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like we need should have some sort of helper function as a member function of the smart attribute to generate this hash. That way it can deal with all the edge cases and it encapsulates that logic within that object.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Bubballoo3 FYI - let me know if a member function on the smart attribute parent class is to your liking. I think it makes sense there as it'll likely need all sorts of toggles based on the widget type.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I think that would be good. We should try to keep this as small as possible though

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it were me, I'd likely put it all in a module that you include so that it's all encapsulated in one single file. In that way, it won't be mixed in with other functionality when you try to read and understand it. It's one file that serves one purpose, even if it has many lines. But that's just me.

Copy link
Contributor

@Bubballoo3 Bubballoo3 Oct 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't we only want a module if we need to use it elsewhere in ruby code? I might be misunderstanding; are you talking about just the data hash or would it do more?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't we only want a module if we need to use it elsewhere in ruby code? I might be misunderstanding; are you talking about just the data hash or would it do more?
React

No it wouldn't do more, but my thinking is it may be nice to just to have all this functionality & logic contained in 1 file instead of being spread out amongst all the other functionality and logic in the existing file.

It's not about reuse it's more about separating functionality.

Comment on lines 49 to 59
:div,
id: [form.object_name, attrib.id, 'wrapper'].join('_'),
class: attrib.hide_by_default? ? 'd-none' : '',
data: data
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I think that would be good. We should try to keep this as small as possible though

min_val = attrib.opts[:min]
max_val = attrib.opts[:max]
data[:min_default] = min_val if min_val
data[:max_default] = max_val if max_val
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think storing the min/max here might add a bit more complexity than we need, as it requires us to keep conventions between server-side and client-side code, which is difficult as we can't share any methods between ruby and javascript. We can expand this initial hash whenever necessary ofc, but things like mins and maxes shouldn't need to be collected at this stage since we know the widget will be ok when it renders. IMO it would be much easier to manage if all of this was handled in dynamic_forms.js, and we load the default values into the data hash whenever we bind the min/max-for listener. This also prevents us from expanding the data hash for static widgets (without min/max-for directives), where this info is unnecessary bloat.

Just to justify the existing items under this criteria, widget_type is necessary because we don't have any other way of connecting the complex form items (like resolution_fields) to a meaningful name. The hide_by_default attribute is less necessary admittedly, but ok because we absolutely have to check hide_by_default? when we add the d-none class, and it is simpler to just get it from the attrib object here than detect it from the d-none class later.

Let me know what you guys think of this, obviously the wrapper is new so we could go several ways with what it becomes, and this current approach does reduce complexity in dynamic_forms, which is definitely already complex enough. Overall though I think trying to avoid creating conventions that have to be followed in two places will save us some headache as we use this more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can expand this initial hash whenever necessary ofc, but things like mins and maxes shouldn't need to be collected at this stage since we know the widget will be ok when it renders. IMO it would be much easier to manage if all of this was handled in dynamic_forms.js, and we load the default values into the data hash whenever we bind the min/max-for listener.

I don't think I follow you. It needs to be rendered in the HTML on the server side somewhere at some point. But I could be completely missing what you're trying to say.

Copy link
Contributor

@Bubballoo3 Bubballoo3 Oct 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am saying that unless the data information needs to be rendered server-side for some reason, we should store and retrieve that data exclusively in the javascript, just to keep that storing and retrieval in a single place. Since we can get the min and max information from the html post-rendering, this would be a place where we could just as effectively gather and store min_default and max_default in addMinMaxForHandler and set the element's data in the same way, allowing us to only store data on widgets that we are going to change dynamically later, and keep min/max-for functionality entirely withing dynamic_forms.js.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I think I got you - because mins and maxes will be in the HTML before any of our javascript runs that's when & where we can extract it. I.e., the defaults are there before we modify them when our handlers run. Is that it?

Copy link
Contributor

@Bubballoo3 Bubballoo3 Oct 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah. And even though the resulting data attribute will be the same by the time we start changing the mins/maxes, we should still try and set this at the latest possible moment, since that is the closest to when the data will be used/accessed (same principle as initializing a variable close to where it's used). The most immediate impact is reducing the splash radius of this change to only dynamic_forms.js and not having to touch session_contexts_helper.rb

if (!wrapper) return;

['max', 'min'].forEach((dim) => {
const defaultVal = element.getAttribute(dim);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the structure of this method and its error resistance, but I am not sure why you pass every form element through here when most do not have min/max defined. IMO it makes more sense to call this method in addMinMaxForHandlers so that

  1. Every element you try to store should have a max and min
  2. You only store elements that might be changed in the future

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since addMinMaxForHandlers calls toggleMinMax at the very end, it ends up changing the default values for the "second" element. If/when we iterate to that second element later, we don't store the actual defaults for that element but those mutated values. I'm not sure if there's an easy way to work around this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to use the language from the method to make this a little more clear, I'll refer to the subject (the element with the directive) and the object (element the directive modifies). In this case the object is the only thing we need to store data on, since that is the element that toggleMinMax is going to change. The secondEle variable refers to a second subject, so in the case of data-max-num-cores-for-cluster-oakley: 28 the object is num-cores and the secondEle is cluster. So we assign a second event listener to cluster, but that listener can still only modify num-cores. So as long as you storeDefaults(num-cores) before those change handlers are added, I think it should all work out. If you tried it and it didn't work, or you have a specific example in mind that I am not considering, I'd definitely be open to discussing and getting to the bottom of it.

Copy link
Contributor

@Bubballoo3 Bubballoo3 Nov 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I see what you are talking about now, and the easiest solution is to only collect the min/maxDefault one time for each element. So inside here you just need to check that wrapper.dataset['${dim}Default'] === undefined before wrapper.dataset['${dim}Default'] = n;

@ahmed-mgd
Copy link
Contributor Author

It looks like some test cases are still failing since it expects different min/max values now. I'll look into those.

@Bubballoo3
Copy link
Contributor

Bubballoo3 commented Nov 7, 2025

Just a quick note on the tests, everything with visit new_batch_connect_session_context_url('sys/bc_jupyter') uses the form fixture https://github.com/OSC/ondemand/blob/0b26bcc08c110b1ecfbb8429fc6af772804b3a57/apps/dashboard/test/fixtures/sys_with_gateway_apps/bc_jupyter/form.yml and you can throw that directly into a dev app to manually work through the tests. You may have already figured that out, but I remember being kind of thrown trying to connect the fixtures to the app names as there are several bc_jupyters in the fixtures.

@johrstrom
Copy link
Contributor

the latest update I think is good on the surface - but there seems to be an issue where multiple events are firing and while it does change the min & max correctly when the first event fires - subsequent events overwrite this. Not sure at a glance/off the top of my head to solve for this in a nice way.

@moffatsadeghi
Copy link
Collaborator

the latest update I think is good on the surface - but there seems to be an issue where multiple events are firing and while it does change the min & max correctly when the first event fires - subsequent events overwrite this. Not sure at a glance/off the top of my head to solve for this in a nice way.

@ahmed-mgd we are wrapping up 4.1 development here soon. What do you need from us to help you with this issue? At a quick glance, this PR shows a lengthy, complex discussion.

@ahmed-mgd
Copy link
Contributor Author

@ahmed-mgd we are wrapping up 4.1 development here soon. What do you need from us to help you with this issue? At a quick glance, this PR shows a lengthy, complex discussion.

We resolved most of the problems in this discussion and now I just need to work out a quirk with the toggleMinMax function. I've kinda put this off, but with the code freeze coming up I'll prioritize this ticket since it's dragged on.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: Changes Requested

Development

Successfully merging this pull request may close these issues.

min/max handlers account for last setting

6 participants