Strange behavior with web fragment when using the option "Run code and display dialog"

Kamran Ansari August 19, 2021

I’m trying to implement a feature through ScriptRunner that involves extending the Jira “issue details” screen using a web fragment (a button), which in turn shows a dialog box with a form. For the purpose of demonstrating this issue, I have kept the implementation to a minimum (the form only contains a couple of Radio Buttons and a Submit button).

 To summarize:

  1. I have a new button (web fragment) on the issue details screen that is tied to a REST endpoint that I implemented in ScriptRunner
  2. The REST endpoint returns the dialog to be shown with two radio buttons and a Submit button. Upon hitting the Submit button, a JavaScript dialog pops-up displaying the number of radio buttons it found on the form (this should always be 2).

 image.png

 

image.png

However, when I execute the following sequence, my JavaScript somehow thinks bumps up the radio button count by 2 upon every single invocation of this sequence.

  1. Click on the button “Show my Little Form” to bring up the form (works as expected)
  2. Use the radio buttons to select from the two options (works as expected)
  3. Click on the Submit button (works as expected)
  4. You should see the JavaScript pop-up with the message “Number of radio buttons in the group: 2” (works as expected)
  5. Press ESC to exit out of the dialog (works as expected)
  6. Click on the button “Show my Little Form” again (this is where the problem starts)
  7. Try to select between the two options. You will notice that the second radio button is not selectable (problem #1)
  8. Now click on the Submit button. You will notice that the number of pop-ups is now 4 in the message (problem #2 – this should have been 2 and not 4)
  9. Repeat steps (5) through (8) and see the number go up by 2 every time.

 Interestingly, if you refresh the web page after exiting the dialog, the dialog shows the correct number again, and both radio buttons are selectable.

 image.png

 

import com.onresolve.scriptrunner.runner.rest.common.CustomEndpointDelegate
import groovy.transform.BaseScript
import javax.ws.rs.core.MediaType
import javax.ws.rs.core.MultivaluedMap
import javax.ws.rs.core.Response

@BaseScript CustomEndpointDelegate delegate

showForm_Debug { MultivaluedMap queryParams ->
def header =
"""
<!-- Dialog header -->
<header class="aui-dialog2-header">
<h2 class="aui-dialog2-header-main">Test Form</h2>
<a class="aui-dialog2-header-close">
<span class="aui-icon aui-icon-small aui-iconfont-close-dialog">Close</span>
</a>
</header>
"""

def formContent =
"""
<form class="aui" name="crb_main_form">
<div class="field-group" id="cal_fieldgroup_id1">
<label for="cal_fieldgroup_id1">Phase Found</label>
<div class="radio">
<input class="radio" type="radio" checked="checked" name="radiobuttons_phase_found" id="rbStaging">
<label for="rbStaging">Pre-Production</label>
</div>

<div class="radio">
<input class="radio" type="radio" name="radiobuttons_phase_found" id="rbProduction">
<label for="rbProduction">Production</label>
</div>
</div>
</form>
"""

def footer =
"""
<!-- Dialog footer -->
<footer class="aui-dialog2-footer">
<div class="aui-dialog2-footer-actions">
<form id="my-custom-sr-dialog-form" class="aui" action="javascript&colon;getSelectedPhase()">
<button id="submit-button" class="aui-button aui-button-primary">Submit</button>
</form>
</div>
</footer>
"""

def scripts =
"""
<script>
function getSelectedPhase()
{
var radios = document.getElementsByName('radiobuttons_phase_found');
var msg = "Number of radio buttons in the group: " + radios.length
alert(msg);
}
</script>
"""

def dialog =
"""
<section id="static-dialog" class="aui-dialog2 aui-dialog2-large" role="dialog">
${header}
${formContent}
${footer}
${scripts}
</section>

"""
Response.ok().type(MediaType.TEXT_HTML).entity(dialog.toString()).build()
}

I'm at a loss to understand this behavior, and I'm hoping someone from this community could shed some light and hopefully point to what I'm doing wrong here and/or offer suggestions on how to fix this.

Many thanks,

Kamran

1 answer

1 accepted

Suggest an answer

Log in or Sign up to answer
0 votes
Answer accepted
Peter-Dave Sheehan
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
August 19, 2021

Hi Kamran

I've encountered this.

It turns out that every time you click the button, your dialog html is embedded into the DOM and when you close it, the html is still in there.

Every time you click the button, you get another copy in the DOM.

You can avoid this by adding the data-aui-remove-on-hide attribute to the section tag

<section id="static-dialog" class="aui-dialog2 aui-dialog2-large" role="dialog" data-aui-remove-on-hide="true">

I would encourage you to explore using the MarkupBuilder to create your dialog and saving your javascript in a separate file (which you can maintain in an IDE that is good for javascript)

Here is how I would implement your form:


def writer = new StringWriter()
def dialog = new MarkupBuilder(writer)
dialog.section(role: 'dialog', id: 'static-dialog', class: 'aui-layer aui-dialog2 aui-dialog2-large', 'aria-hidden': true, 'data-aui-remove-on-hide': true) {
header(class: 'aui-dialog2-header') {
h2(class: 'aui-dialog2-header-main') { dialog.mkp.yield("QAD Log Work Settings") }
}
div(class: 'aui-dialog2-content') {
form(
class: 'aui', name:'crb_main_form'){
fieldset(class: 'group',id:'cal_fieldgroup_id1'){
legend { span { dialog.mkp.yield('Phase Found') } }
div(class: 'radio') {
input(class: 'radio', type: 'radio', id: 'rbStaging', name: 'radiobuttons_phase_found', value: 'rbStaging')
label(for: 'rbStaging') { dialog.mkp.yield "Pre-Production" }
}
div(class: 'radio') {
input(class: 'radio', type: 'radio', id: 'rbProduction', name: 'radiobuttons_phase_found', value: 'rbProduction',)
label(for: 'rbProduction') { dialog.mkp.yield "Production" }
}
}
}
}
foooter(class: 'aui-dialog2-footer') {
div(class: 'aui-dialog2-footer-actions') {
button(id: 'cancel-button', class: 'aui-button aui-button-link cancel', onclick: 'closeDialog()') { dialog.mkp.yield "Cancel" }
button(id: 'submit-button', class: 'aui-button aui-button-primary', type: 'submit', accesskey: 's', value: 'Submit', onclick: 'getSelectedPhase()') { dialog.mkp.yield "Submit" }
}
}
script(type: 'text/javascript') {
def scriptFile = new File("$jiraHome/scripts/path/to/your/javascript/file.js")
dialog.mkp.yieldUnescaped(scriptFile.getText('UTF-8'))
}
}

Response.ok().type(MediaType.TEXT_HTML).entity(writer.toString()).build()

If you haven't already found this...  I find it invaluable: https://aui.atlassian.com/aui/7.9/docs/dialog2.html

Kamran Ansari August 23, 2021

Thank you so much, you're awesome! :) This took care of both problems I described in my post. I also appreciate the explanation of what's going on with the DOM every time the button is clicked - it makes a lot of sense now.

 I'm still very new to ScriptRunner and Groovy, and I sincerely appreciate your suggestions about using MarkupBuilder and an IDE for development. Wondering if you'd have a recommendation for any good IDE out there (I use Windows).

Thanks again!

Kamran

PS: I've always wondered what name you went by if I had to address you by name (Peter or Peter-Dave?) :) 

Peter-Dave Sheehan
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
August 23, 2021

Glad I could help.

I use Intelij community edition as my IDE. There are some instructions on the adaptavist site on how to configure.

When you start with their sample project (even if you have no plans to create a plugin or anything like that) the IDE will download all the relevant dependencies so that you get the full autocomplete experience.

I don't use a local instance of Jira or use any debug configuration. I just have WINSCP set up to copy my local files to my DEV server. This way every time I change a line of code, it is immediately picked up by my DEV server and I can test the results there.

I encourage people to call me by both: "Peter-Dave". But I'll answer to either.

TAGS
AUG Leaders

Atlassian Community Events