Groups keyboard shortcuts have been updated
Dismiss
See shortcuts

Blockly Custom Input Field With Searchable Text Dropdown

62 views
Skip to first unread message

Sabin Adhikari

unread,
Jan 6, 2025, 11:43:42 AMJan 6
to Blockly
Hi all,
I am trying to implement a text input text with custom dropdown div that can be searched. Everything is working fine except for the dropdown which is getting rendered mostly 300-400 pixels above the actual text block.
I tried debugging everything in the code, referenced the blockly example code for custom fields but no luck. 

The issue is most probably somewhere in - showPositionedByField()
which sends a reference to current CustomField.
Has anyone encountered this before? If so, how did you resolve it?

Attached is the screenshot of the issue. Also, note this issue seems to be also
and also happening with the context menu dropdowns in blockly workspace itself.
Below is a snippet of the implementation:

export class FieldFilter extends Blockly.FieldTextInput{
INITWORDS: any;
WORDS: any;
imageElement_ = null
sourceBlock_ : any
isBeingEdited_ = false
fieldName = ""


constructor(text, options, opt_validate?){
super(text, opt_validate)
this.fieldName = text
this.INITWORDS = options;
this.WORDS = this.INITWORDS;
this.setSpellcheck(false);
this.SERIALIZABLE = true
}


loadState(state) {
if (typeof state === 'string') {
this.fieldName = state
} else {
let words = state['WORDS']
this.WORDS = words
this.INITWORDS = words
if (this.fieldName !== '') {
this.setEditorValue_(this.fieldName);
}
}
}
// editor_
showEditor_(): void {
super.showEditor_();
// Build the DOM.
const editor = this.dropdownCreate_();
Blockly.DropDownDiv.getContentDiv().appendChild(editor);

Blockly.DropDownDiv.setColour(
this.sourceBlock_.style.colourPrimary,
this.sourceBlock_.style.colourTertiary,
);


Blockly.DropDownDiv.showPositionedByField(
this,
this.dropdownDispose_.bind(this)
);


this.boundEvents_.push(
Blockly.browserEvents.bind(this.imageElement_, 'click', this, this.hide_),
);
this.boundEvents_.push(
Blockly.browserEvents.bind(
this.imageElement_,
'mousemove',
this,
this.onMouseMove,
),
);

}

//create dropdown for options
dropdownCreate_() {
this.imageElement_ = document.createElement('div');
this.imageElement_.id = 'field_idFilter';
this.WORDS = this.INITWORDS ?? [];
this.imageElement_.style = this.getDropdownInnerStyle(this.WORDS.length)
this.imageElement_.innerHTML = this.generateDropDownHtml()
return this.imageElement_;
}

onMouseMove(e) {

const bBox = this.imageElement_.getBoundingClientRect();
const dy = e.clientY - bBox.top;
const note = Blockly.utils.math.clamp(Math.round(13.5 - dy / 7.5), 0, 12);
this.imageElement_.style.backgroundPosition = -note * 37 + 'px 0';
this.setEditorValue_(note);
}

Any help is appreciated!
Thanks team.

Screenshot 2025-01-06 at 4.53.02 pm.png
Screenshot 2025-01-06 at 4.51.43 pm.png

Christopher Allen

unread,
Jan 9, 2025, 6:52:07 AMJan 9
to blo...@googlegroups.com
Hi Sabin,

I'm not as familiar with the DOM manipulation for custom fields as some of my colleagues are, so we may eventually need to prevail upon one of them to have a look at this, but in the meantime I have given your post a good read and have some hopefully-helpful suggestions.

The first thing that caught my eye was this:

Also, note this issue seems to be also
and also happening with the context menu dropdowns in blockly workspace itself.

Are you saying that you are seeing an issue with the placement of the dropdown div for some of the built-in field types?  If so, that sounds like a bug and we'd definitely appreciate you filling a bug report with steps to reproduce.

I am trying to implement a text input text with custom dropdown div that can be searched. Everything is working fine except for the dropdown which is getting rendered mostly 300-400 pixels above the actual text block.

I'm not sure I understand what you mean by "custom dropdown div that can be searched", but "text input… with dropdown" makes it sounds like you might want your field editor to use both DropdownDiv and WidgetDiv.  I see you've based your code on FieldTextInput, which seems perfectly reasonable (it uses WidgetDiv), but I note that FieldDropdown already has working DropdownDiv code (that, at least for me, does correctly position the dropdown); have you tried either inheriting from that class, or cribbing the dropdown code from it?

I tried debugging everything in the code, referenced the blockly example code for custom fields but no luck. 

The issue is most probably somewhere in - showPositionedByField()which sends a reference to current CustomField.

I had a little trouble understanding your code—it didn't help that it had lost its indentation when you pasted it into your message—but this section:

  // editor_
  showEditor_(): void {
    super.showEditor_();
    // Build the DOM.
    const editor = this.dropdownCreate_();
    Blockly.DropDownDiv.getContentDiv().appendChild(editor);

    Blockly.DropDownDiv.setColour(
      this.sourceBlock_.style.colourPrimary,
      this.sourceBlock_.style.colourTertiary,
    );

    Blockly.DropDownDiv.showPositionedByField(
      this,
      this.dropdownDispose_.bind(this)
    );
    // ...
  }

… seems reasonable enough and I don't see anything wrong with the call to .showPositionedByField().

I did wonder if there might be an issue here, however:
 
  //create dropdown for options
  dropdownCreate_() {
    this.imageElement_ = document.createElement('div');
    this.imageElement_.id = 'field_idFilter';
    this.WORDS = this.INITWORDS ?? [];
    this.imageElement_.style = this.getDropdownInnerStyle(this.WORDS.length)
    this.imageElement_.innerHTML = this.generateDropDownHtml()
    return this.imageElement_;
  }

My two thoughts about this were:
  • Is it possible that the HTML being generated by generated by .generateDropDownHtml() is somehow including either some absolute positioning or some large margins/offsets that are messing things up?
  • Tangentially: the use of .innterHTML here is pretty risky; you'd best be absolutely sure that there is no way that .generateDropDownHtml() can ever produce unexpected (possibly executable) output, because this is a classic XSS vector.
Beyond that, however, your screenshot with the mispositioned context menu suggests there is something else going on, not directly related to the code you shared with us, that is causing the issue.  I think it would likely be very instructive if you were to try to create a minimal reproduction for that problem, because I suspect it will turn up the cause of the problem you have having with the field (or it will turn up a bug in Blockly itself).


Best wishes,

Christopher

Sabin Adhikari

unread,
Jan 9, 2025, 6:05:24 PMJan 9
to Blockly
Thanks Chris,

Appreciate the concern on XSS, however, the innerHTML is properly sanitized, static in nature and is only generated on client side in the same session. 
So, I believe it should not be an issue.

Regarding, the positioning, I will try to recreate this without Custom Dropdown and send the reproduction steps through.
Thank again for your guidelines.

Regards,
Sabin

Christopher Allen

unread,
Jan 10, 2025, 4:20:33 AMJan 10
to blo...@googlegroups.com
Hi Sabin,

Appreciate the concern on XSS, however, the innerHTML is properly sanitized, static in nature and is only generated on client side in the same session. 

Great.  It's certainly a powerful and useful feature, just one that—like a chainsaw—needs to be handled with care.

Regarding, the positioning, I will try to recreate this without Custom Dropdown and send the reproduction steps through.

That would be most appreciated.
 
Thank again for your guidelines.

You are most welcome!


Christopher

Sabin Adhikari

unread,
Jan 20, 2025, 10:54:12 AMJan 20
to Blockly
Hi Christopher,
Is there a way to test blockly v.9.3.3 as this is the version that seems to be causing issues in our app.
The blockly demo site seems to only have the latest version - https://blockly-demo.appspot.com/static/demos/blockfactory/index.html.
Appreciate your help.

Regards,
Sabin Adhikari

Mark Friedman

unread,
Jan 22, 2025, 5:45:47 PMJan 22
to blo...@googlegroups.com
Sabin,

  If by "test" Blockly v9.3.3, you mean to run a version of the Blockly Playground that uses that version of Blockly then all you need to do is clone/fork the Blockly Git repository (here) to your local machine, run checkout the rc/v9.3.3 branch and run "npm i && npm start".

  Hope this helps.

-Mark

P.S. If you're not familiar with Git then you can also download the v9.3.3 source code from here.  You'll still need to install npm and run the npm commands listed above.


--
You received this message because you are subscribed to the Google Groups "Blockly" group.
To unsubscribe from this group and stop receiving emails from it, send an email to blockly+u...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/blockly/398d7e66-6958-414a-93fd-c6fc8e018f0en%40googlegroups.com.
Reply all
Reply to author
Forward
0 new messages