Nzall
Nzall

Reputation: 3555

Dijit/form/Select inside Dijit/TitlePane part of custom widget opens dropdown in the wrong location

I have the following widget HTML.

<div class="expandableSearch">
    <div data-dojo-type="dijit/TitlePane" data-dojo-props="title:'${prefixTitle}', open:false" id="${containedWidgetId}titleNodePane">
        <div id="container"
        class="${baseClass}Container"
        data-dojo-attach-point="containerNode"></div>
    </div>
</div>

with the following Javascript behind it:

/**
 * Javascript for ExpandableSearchComponent
 */
define([ "dojo/_base/declare", "dijit/_WidgetBase", "dijit/_TemplatedMixin",
        "dojo/text!./templates/ExpandableSearchComponent.html",
        "dijit/TitlePane", "dijit/_WidgetsInTemplateMixin", "dijit/registry",
        "dojo/on", "dojo/aspect", "dojo/_base/lang", "dojo/dom",
        "dojo/dom-attr", "js/utils/CommonUtils", "dijit/focus" ], function(declare,
        _WidgetBase, _TemplatedMixin, template, TitlePane,
        _WidgetsInTemplateMixin, registry, on, aspect, lang, dom, domAttr,
        CommonUtils, focusUtil) {

    return declare([ _WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin ], {
        templateString : template,
        prefixTitle : "",
        containedWidgetId : "",
        defaultValue : "",

        startup : function() {
            this.inherited(arguments);
            var containedWidgetId = this.containedWidgetId;
            var containedWidget = registry.byId(this.containedWidgetId);
            if (typeof containedWidget === "undefined") {
                containedWidget = dom.byId(this.containedWidgetId);
            }

            var titlePane = registry.byId(this.containedWidgetId
                    + "titleNodePane");
            this.own(on(titlePane, "Show", function() {

                if (typeof containedWidget.loadAndOpenDropDown === "function") {
                    containedWidget._aroundNode = containedWidget.focusNode;
                    containedWidget.loadAndOpenDropDown();
                }
                focusUtil.focus(dom.byId(containedWidgetId));
            }));
            this.own(on(titlePane, "Hide", function() {
                if (typeof containedWidget.closeDropDown === "function") {
                    containedWidget.closeDropDown();
                }

            }));

            this.own(on(containedWidget, "change", function(event) {
                var newVal = "";
                if (typeof containedWidget.attr == "function"
                        && containedWidget.attr("displayedValue") !== null) {
                    newVal = containedWidget.attr("displayedValue");
                }
                if (typeof event === "object") {
                    newVal = containedWidget.value;
                }
                var newTitle = this.prefixTitle + newVal;
                if (newTitle.length > 35) {
                    newTitle = newTitle.substring(0, 32) + "...";
                }
                titlePane.set("title", newTitle);
                if (titlePane.open) {
                    titlePane.toggle();
                }
            }.bind(this)));

        },
        reset : function() {
            var containedWidget = registry.byId(this.containedWidgetId);
            if (typeof containedWidget === "undefined") {
                containedWidget = dom.byId(this.containedWidgetId);
            }
            if (typeof containedWidget.set === "function"
                    && containedWidget.attr("displayedValue") !== null) {
                containedWidget.set("value", this.defaultValue);
            }
            if (typeof containedWidget.set === "function"
                    && containedWidget.attr("displayedValue") === null) {
                containedWidget.set("value", this.defaultValue);
            }
            if (typeof containedWidget.onChange !== "function") {
                domAttr.set(containedWidget, "value", this.defaultValue);
                if ("createEvent" in document) {
                    var evt = document.createEvent("HTMLEvents");
                    evt.initEvent("change", false, true);
                    containedWidget.dispatchEvent(evt);
                } else {
                    containedWidget.fireEvent("onchange");
                }
            }
        }
    });

});

Essentially, this combination of HTML and Javascript is just a dijit/TitlePane with some extra javascript to

  1. Automatically set the title of the pane;
  2. Automaticaly open and close the underlying element if it has a dropdown;
  3. Automatically set the focus to the contained element.

Used like this:

<div data-dojo-type="js/widgets/ExpandableSearchComponent" id="machineSearchView.operatingSystemExpandableSearch"
    data-dojo-props="prefixTitle: '<s:property value="%{getText('label.machine.operatingSystem')}"/>: ', containedWidgetId: 'machineSearchView.operatingSystem', defaultValue: '-1'">
    <div data-dojo-type="dojo/store/Memory"
        data-dojo-id="operatingSystemStore"
        data-dojo-props="<s:property value='%{getOperatingSystemTypeCodeJsonString()}'/>"></div>
    <s:set name="operatingSystem" value="machineSearchView.operatingSystem"
        scope="request"></s:set>
    <div data-dojo-type="dijit/form/Select" class="readOnlySelect"
        data-dojo-props="store:operatingSystemStore, labelAttr: 'label', sortByLabel: false, value:'${operatingSystem}'"
        name="machineSearchView.operatingSystem" id="machineSearchView.operatingSystem"></div>
</div>

When I open it, it looks like this:

enter image description here

As you can see, the dijit/form/Select opens the SelectMenu on the TitlePane component instead of on the Select div itself. you can JUST about see the Select peek out to the side of the SelectMenu.

As an example of how I'd like it to look:

enter image description here

The containedWidget._aroundNode = containedWidget.focusNode;was an attempt to make it work based on https://davidwalsh.name/dijit-dropdowns, but it didn't help. I'm not sure what else I can try. I've already tried to go on debugging binges deep inside the Dojo components, but to no avail?

How do I fix this?

Upvotes: 0

Views: 226

Answers (1)

pgianna
pgianna

Reputation: 725

TitlePane plays an animation when it opens. Opening the select drop down on the "Show" event is too early, the TitlePane container is not ready yet. You need to wait for the animation to finish. The internal opening animation object in TitlePane is _wipeIn and you can add an aspect after its onEnd() function.

require([
  "dijit/TitlePane",
  "dijit/form/Select",
  "dojo/data/ObjectStore",
  "dojo/store/Memory",
  "dojo/on",
  "dojo/aspect",
  "dojo/domReady!"
], function(TitlePane, Select, ObjectStore, Memory, on, aspect) {

  var store = new Memory({
    data: [
      { id: "foo", label: "Foo" },
      { id: "bar", label: "Bar" }
    ]
  });

  var os = new ObjectStore({ objectStore: store });

  var s = new Select({store: os});
  s.startup();
  
  var tp = new TitlePane({title:"I'm a TitlePane", content: s, open: false});
  tp.placeAt("content");
  tp.startup();
  
  /*
  // This does not work:
  on(tp, "Show", function() {
    s.loadAndOpenDropDown();
  });
  */
  
  // This works:
  aspect.after(tp._wipeIn, "onEnd", function() {
      s.loadAndOpenDropDown();
  });
  
  on(tp, "Hide", function() {
      s.closeDropDown();
  });
});
<script src="//ajax.googleapis.com/ajax/libs/dojo/1.10.4/dojo/dojo.js"></script>
<link rel="stylesheet" type="text/css" href="http://ajax.googleapis.com/ajax/libs/dojo/1.10.4/dojo/resources/dojo.css"> 
<link rel="stylesheet" type="text/css" href="http://ajax.googleapis.com/ajax/libs/dojo/1.10.4/dijit/themes/claro/claro.css">

<div class="claro">
  <div id="content"></div>
</div>

Upvotes: 1

Related Questions