Creation of a Formula Blot (Hierarchical Tags) - quill

Creation of a complex formula blot, such as the one below, is successful using an Embed, but it works only in a single instance. All other instances are truncated. Attempts to create it as an Inline or Block resulted in errors.
I am developing a graphic formula editor, with a dialog and toolbars of its own, and it is successfully getting the delta from an existing blot. Unfortunately, saving more than one formula fails, and replacing an existing formula fails. When saving more than one formula, only the top level spans are saved for all but the first.
It isn't clear from the documentation if the tag hierarchy can be used as an Inline or Block element.
As a side note, I am building a complete Quill npm module for Angular 7+ apps that includes a number of other quill modules as well, translated to Typescript. The npm module will support WCAG compliance with large buttons, etc. that can be toggled.
Example tag hierarchy to be used as a blot:
<span class="pl3-quill-formula-blot" data-id="pl3qfml_1560288135645" data-editors="[{"id":1560288133767,"parentId":null,"depth":0,"position":0,"fElement":{"name":"cube root","text":"∛","type":"O","shape":"R","latex":null,"values":["x"],"nValues":1}},{"id":1560288133768,"parentId":0,"depth":0,"position":1,"fElement":{"name":"-EMPTY-","text":"","type":"S","shape":"C","latex":null,"latexCb":null,"values":[""],"nValues":1}}]">
<span contenteditable="false">
<span class="katex">
<span class="katex-mathml"><math><semantics><mrow><mroot><mi>x</mi><mn>3</mn></mroot></mrow><annotation encoding="application/x-tex">\sqrt[3] {x} </annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height: 1.04em; vertical-align: -0.23972em;"></span><span class="mord sqrt"><span class="root"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height: 0.658556em;"><span class="" style="top: -2.83634em;"><span class="pstrut" style="height: 2.5em;"></span><span class="sizing reset-size6 size1 mtight"><span class="mord mtight"><span class="mord mtight">3</span></span></span></span></span></span></span></span><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height: 0.80028em;"><span class="svg-align" style="top: -3em;"><span class="pstrut" style="height: 3em;"></span><span class="mord" style="padding-left: 0.833em;"><span class="mord mathdefault">x</span></span></span><span class="" style="top: -2.76028em;"><span class="pstrut" style="height: 3em;"></span><span class="hide-tail" style="min-width: 0.853em; height: 1.08em;"><svg width="400em" height="1.08em" viewBox="0 0 400000 1080" preserveAspectRatio="xMinYMin slice"><path d="M95,702c-2.7,0,-7.17,-2.7,-13.5,-8c-5.8,-5.3,-9.5,
-10,-9.5,-14c0,-2,0.3,-3.3,1,-4c1.3,-2.7,23.83,-20.7,67.5,-54c44.2,-33.3,65.8,
-50.3,66.5,-51c1.3,-1.3,3,-2,5,-2c4.7,0,8.7,3.3,12,10s173,378,173,378c0.7,0,
35.3,-71,104,-213c68.7,-142,137.5,-285,206.5,-429c69,-144,104.5,-217.7,106.5,
-221c5.3,-9.3,12,-14,20,-14H400000v40H845.2724s-225.272,467,-225.272,467
s-235,486,-235,486c-2.7,4.7,-9,7,-19,7c-6,0,-10,-1,-12,-3s-194,-422,-194,-422
s-65,47,-65,47z M834 80H400000v40H845z"></path></svg></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height: 0.23972em;"><span class=""></span></span></span></span></span></span></span>
</span>
</span>
</span>
Blot Code:
import {Pl3QuillFormulaResult} from './pl3-quill-formula.component';
const Quill = require('quill');
const Embed = Quill.import('blots/embed');
class Pl3QuillFormulaBlotComponent extends Embed {
static blotName = 'pl3-quill-formula';
static className = 'pl3-quill-formula-blot';
static tagName = 'span';
constructor(domNode: Element, value: any) {
super(domNode);
}
static create(result: Pl3QuillFormulaResult) {
const node = super.create(result.id);
node.setAttribute('data-id', result.id);
node.setAttribute('data-editors', result.editorsAsString);
node.appendChild(result.domElement);
return node;
}
/**
* Quill uses this to return a Delta with the attributes in the return.
*/
static formats(domNode: Element) {
return {
editors: domNode.getAttribute('data-editors'),
id: domNode.getAttribute('data-id')
};
}
static value(domNode: HTMLElement) {
return {
id: domNode.dataset.id,
editors: domNode.dataset.editors
};
}
public remove() {
if (this.prev == null && this.next == null) {
this.parent.remove();
}
else {
super.remove();
}
}
}
Quill.register('formats/pl3-quill-formula', Pl3QuillFormulaBlotComponent);
This is the call to insert the formula that is not working for subsequent instances, although it works for the first formula:
if (result) {
const range = this.quill.getSelection(true);
this.quill.insertEmbed(range.start, 'pl3-quill-formula', result, Quill.sources.API);
this.quill.setSelection(range.length + 1);
}
Formula Editor UI Snapshot
The expected results are to be able to insert the formula within the editor, inline, and to be able to replace it when the formula is updated.

Related

Customizing Day Cell content in FullCalendar

I am using fullcalendar with react. I am trying to customize the dayGrid view. According to the Content Injection docs for react I can use custom content for the rendering of both the date and the header cells. The dayCellContent "hook" states that:
Generated content is inserted inside the inner-most wrapper of the day cell. It does not replace the cell.
I've provided an implementation for the dayCellContent and noticed that my content gets injected into the following structure:
<td class="fc-daygrid-day fc-day fc-day-wed fc-day-past rot_time-off_day-cell" data-date="2021-04-07">
<div class="fc-daygrid-day-frame fc-scrollgrid-sync-inner">
<div class="fc-daygrid-day-top">
<a class="fc-daygrid-day-number">
...custom content goes here
</a>
</div>
<div class="fc-daygrid-day-events"></div>
<div class="fc-daygrid-day-bg"></div>
</div>
</td>
Now, the problem is that this structure lets you insert content ONLY in the upper right corner of the date cell due to the positioning of the element. Furthermore, it is in an anchor element.
Example:
function renderDayCell(dayCellContent: DayCellContentArg) {
return (
<div>
{dayCellContent.dayNumberText}
</div>
);
}
Is there a clean way to customize the whole content of the cell somehow? I've seen a couple of sites using fullcalendar that have their content inserted directly into the td. Not sure if this is version dependent or they're using the alternative JS approach based on domNodes or html. I am using version 5.6.0 of fullcalendar.
I had the same requirement although not using React. I solved it using a manual manipulation of the DOM elements as suggested above. I have used jQuery for the select and manipulation. It is posted here in case anyone would like to see an example of how this can be achieved using DOM manipulation.
I implemented dayCellContent to make the day-cell DOM element easily identifiable by wrapping it in a span, with a unique id attribute based on the day of year number:
dayCellContent: function(info, create) {
const element = create('span', { id: "fc-day-span-"+info.date.getDayOfYear() }, info.dayNumberText);
return element;
},
This dayCellContent implementation makes no visible difference to the calendar but makes it easier to identify the elements to be modified in the DOM.
I then implemented dayCellDidMount to do the DOM manipulation by finding the appropriate cells and selecting their parent’s parent:
dayCellDidMount: function(info) {
let element = "<div style='position: absolute; left: 4px; top: 4px;'><a href='https://www.w3schools.com/'>TEST-"+info.dayNumberText+"</a></div>";
$('#fc-day-span-'+info.date.getDayOfYear()).parent().parent().prepend(element);
},
In this case I have just put a link to w3c in the top left of the cell with test text which also includes the day number. It results in cells that look like this:
Clearly the CSS could be improved and should be moved out to the CSS definitions but it illustrates the point.
Warning: This approach makes assumptions about the DOM structure that FullCalendar generates. The generated HTML may change in future versions of the product which could invalidate it. If you go this way then be careful when doing a FullCalendar update.
Note that the getDayOfYear function is from the ext-all.js library. Any way of uniquely identifying the day will work.
ngAfterViewInit(){
// Your CSS as text
var styles =.fc td, .fc th { vertical-align: top; padding: 0; height: 100px; } a{ color:#3d1cba; }
let styleSheet = document.createElement("style");
styleSheet.innerText = styles;
document.head.appendChild(styleSheet);
let arrTD = document.querySelectorAll('td.fc-timeline-slot');
let arrTR= document.querySelectorAll('td.fc-timeline-lane.fc-resource');
let arrInject= document.querySelectorAll('td.fc-timeline-lane.fc-resource>div.fc-timeline-lane-frame');
console.log(arrTR);
let k=-1;
arrTR.forEach(eachTR => {
let i=1;
let str = '';
k++;
let data_resource_id= eachTR.getAttribute('data-resource-id');
console.log(data_resource_id);
arrTD.forEach(eachTD => {
let k=100*(i-1);
i=i+1;
let data_date= eachTD.getAttribute('data-date');
console.log(data_date);
let data_resource_id= eachTR.getAttribute('data-resource-id');
console.log(data_resource_id);
str = str + '<span data-date="'+data_date+'" data-resource-id="'+data_resource_id+'" class="plus_icon" style="position:relative;top: 0px; left: '+k+'px !important;width:500px;height:500px;z-index:3;-moz-border-radius:100px;border:1px solid #ddd;-moz-box-shadow: 0px 0px 8px #fff;">+</span>';
});
arrInject[k].innerHTML=str;
});
let elementList = this.elRef.nativeElement.querySelectorAll('span.plus_icon');
for(let i=0;i<elementList.length;i++){
elementList[i].addEventListener('click', this.plusClick.bind(this));
}
}

Strange behaviour of angular bootstrap collapse

I faced with strange behaviour of uib-collapse.
Let's assume I have a list of elements and i want each of them to be collapsed. Also i want to refresh its content periodically depend on something.
For example: i have some items and each of them have description which consists of some sections. I can pick item and description sections should be populated with item's description content. The problem is that each time i refresh its content, some sections are collapsing (despite the fact i set uib-collapse to false)
My controller:
var i = 0;
$scope.sections = [0,1,2];
$scope.next = function(nextOffset) {
i+=nextOffset;
$scope.sections = [i, i+1, i+2]
}
My template:
<button ng-click="next(1)" style="margin-bottom: 10px">Next item</button>
<button ng-click="next(2)" style="margin-bottom: 10px">Next next item</button>
<button ng-click="next(3)" style="margin-bottom: 10px">Next next next item</button>
<div ng-repeat="section in sections">
<div uib-collapse="false">
<div class="well well-lg">{{ section }}</div>
</div>
</div>
So when i click first button, only one section does transition. When i click second, 2 section do transition and click to third button leads to all section transition.
See plunkr
Any ideas?
UPD: if $scope.sections is array of object, not of primitives, then all sections have transition in each of 3 cases. It is so ugly...
You are not refreshing the existing content, you are adding new arrays each time, which will make ng-repeat remove the old DOM elements and insert new ones.
If you try with track by $index you will see the difference:
<div ng-repeat="section in primitiveSections track by $index">
Demo: http://plnkr.co/edit/hTsVBrRLa8nWXhaqfhVK?p=preview
Note that track by $index might not be the solution you want in your real application, I just used it for demonstration purposes.
What you probably need is to just modify the existing objects in the array.
For example:
$scope.nextObject = function(nextOffset) {
j += nextOffset;
$scope.objectSections.forEach(function (o, i) {
o.content = j + i;
});
};
Demo: http://plnkr.co/edit/STxy1lAUGnyxmKL7jYJH?p=preview
Update
From the collapse source code:
scope.$watch(attrs.uibCollapse, function(shouldCollapse) {
if (shouldCollapse) {
collapse();
} else {
expand();
}
});
When a new item is added the watch listener will execute, shouldCollapse will always be false in your case so it will execute the expand function.
The expand function will always perform the animation:
function expand() {
element.removeClass('collapse')
.addClass('collapsing')
.attr('aria-expanded', true)
.attr('aria-hidden', false);
if ($animateCss) {
$animateCss(element, {
addClass: 'in',
easing: 'ease',
to: {
height: element[0].scrollHeight + 'px'
}
}).start().finally(expandDone);
} else {
$animate.addClass(element, 'in', {
to: {
height: element[0].scrollHeight + 'px'
}
}).then(expandDone);
}
}
If this is the intended behavior or not I don't know, but this is the reason why it happens.
this is a comment on the original ui-bootstrap library: (and the new uib prefixed directive doesn't comply this comment.)
// IMPORTANT: The height must be set before adding "collapsing" class.
Otherwise, the browser attempts to animate from height 0 (in
collapsing class) to the given height here.
use the deprecated "collapse" directive instead of new "uib-collapse" until it gets fixed.

How to handle tree like structure in angularjs?

I am trying to implement a hierarchical structure in the form of a tree in a complex angular.js web app. I am using nested ng-repeats to render the structure but I am getting significant performance related issues in IE 10 and minor performance issues in chrome. Data that will be used will contain as many as 5,000 entries at the final level.
Based on my research I think following could be the reasons behind it:
Large number of watcher elements.
To tackle this I have already implemented one time data binding and number of watchers is not that high.
Browser repaint time:
ng-repeat adds the elements to the DOM one by one. This could result in overloading of browser engine to render complex HTML multiple number of times causing large amount of lags.
To tackle this I have applied lazy loading sort of technique by rendering the child only when one node is collapsed. Still I am experiencing an observable delay in rendering of nodes where the number of nodes to be rendered is large.
CSS classes:
I tried implementing the tree structure by stripping off all the classes from node elements. This resulted in significant improvement but removing classes is not really an option. Also if I give inline style to the elements then it also results in better performance.
Performance issues of Angular material:
Angular material is the integral part of my web app. After looking into the issues submitted by angular material users for large amount of ng-repeats to which fixes have already been rolled out. But upgrading to latest version didn't help either.
Please refer to this image for the table design. Template used for the creation of tree is as follows:
<li ng-repeat="item in ::item.childLevelDetails" >
<div >
<a ng-click="toggle(this)" class="icon icon-stream-add-2">
<span></span>
</a>
<div class="unitTextDiv">{{::item.title}}</div>
</div>
<ol ng-include= "'tree_node'">
</li>
Request you to suggest any possible solutions to this problem.
angular-ui-grid can be considered as one option. It uses virtualization and renders only the rows that are visible. So, it performs well with huge number of rows too. It comes with a great documentation and examples http://ui-grid.info/docs/#/tutorial
Refer to the grouping example to make sure that this helps for your use case http://ui-grid.info/docs/#/tutorial/209_grouping
Time to use stick with this potion and finalize according to black magic!
You can try this recursive sample I did for you.
Using ng-if for showing/hidding element will reduce the amount of watches.
Here find the Fiddler
var myApp = angular.module('myApp',[]);
myApp.controller('MyCtrl', ['$scope','$timeout', 'getWatchCount' , function ($scope ,$timeout, getWatchCount ){
$scope.tree = [
{title:'Element level 1',
elements: [
{ title: 'Element level 1.1'},
{ title: 'Element level 1.2',
elements: [
{ title: 'Element level 1.2.2'},
{ title: 'Element level 1.2.2'},
]}
]},
{title:'Element level 2'}
]
//NEXT CODE ONLY USED FOR COUNTING WATCHES//
$scope.countWatches = function(){
$scope.numberOfWatches = getWatchCount();
}
$timeout(function(){$scope.countWatches()} , 0 );
// I return the count of watchers on the current page.
function getWatchCount() {
// Keep track of the total number of watch bindings on the page.
var total = 0;
// There are cases in which two different ng-scope markers will actually be referencing
// the same scope, such as with transclusion into an existing scope (ie, cloning a node
// and then linking it with an existing scope, not a new one). As such, we need to make
// sure that we don't double-count scopes.
var scopeIds = {};
// AngularJS denotes new scopes in the HTML markup by appending the classes "ng-scope"
// and "ng-isolate-scope" to appropriate elements. As such, rather than attempting to
// navigate the hierarchical Scope tree, we can simply query the DOM for the individual
// scopes. Then, we can pluck the watcher-count from each scope.
// --
// NOTE: Ordinarily, it would be a HUGE SIN for an AngularJS service to access the DOM
// (Document Object Model). But, in this case, we're not really building a true AngularJS
// service, so we can break the rules a bit.
angular.forEach(
document.querySelectorAll( ".ng-scope , .ng-isolate-scope" ),
countWatchersInNode
);
return( total );
// ---
// PRIVATE METHODS.
// ---
// I count the $watchers in to the scopes (regular and isolate) associated with the given
// element node, and add the count to the running total.
function countWatchersInNode( node ) {
// Get the current, wrapped element.
var element = angular.element( node );
// It seems that in earlier versions of AngularJS, the separation between the regular
// scope and the isolate scope where not as strong. The element was flagged as having
// an isolate scope (using the ng-isolate-scope class); but, there was no .isolateScope()
// method before AngularJS 1.2. As such, in earlier versions of AngularJS, we have to
// fall back to using the .scope() method for both regular and isolate scopes.
if ( element.hasClass( "ng-isolate-scope" ) && element.isolateScope ) {
countWatchersInScope( element.isolateScope() );
}
// This class denotes a non-isolate scope in later versions of AngularJS; but,
// possibly an isolate-scope in earlier versions of AngularJS (1.0.8).
if ( element.hasClass( "ng-scope" ) ) {
countWatchersInScope( element.scope() );
}
}
// I count the $$watchers in the given scope and add the count to the running total.
function countWatchersInScope( scope ) {
// Make sure we're not double-counting this scope.
if ( scopeIds.hasOwnProperty( scope.$id ) ) {
return;
}
scopeIds[ scope.$id ] = true;
// The $$watchers value starts out as NULL until the first watcher is bound. As such,
// the $$watchers collection may not exist yet on this scope.
if ( scope.$$watchers ) {
total += scope.$$watchers.length;
}
}
}
}]);
myApp.factory(
"getWatchCount",
function() {
// I return the count of watchers on the current page.
function getWatchCount() {
var total = 0;
// AngularJS denotes new scopes in the HTML markup by appending the
// class "ng-scope" to appropriate elements. As such, rather than
// attempting to navigate the hierarchical Scope tree, we can simply
// query the DOM for the individual scopes. Then, we can pluck the
// watcher-count from each scope.
// --
// NOTE: Ordinarily, it would be a HUGE SIN for an AngularJS service
// to access the DOM (Document Object Model). But, in this case,
// we're not really building a true AngularJS service, so we can
// break the rules a bit.
angular.element( ".ng-scope" ).each(
function ngScopeIterator() {
// Get the scope associated with this element node.
var scope = $( this ).scope();
// The $$watchers value starts out as NULL.
total += scope.$$watchers
? scope.$$watchers.length
: 0
;
}
);
return( total );
}
// For convenience, let's serialize the above method and convert it to
// a bookmarklet that can easily be run on ANY AngularJS page.
getWatchCount.bookmarklet = (
"javascript:alert('Watchers:'+(" +
getWatchCount.toString()
.replace( /\/\/.*/g, " " )
.replace( /\s+/g, " " ) +
")());void(0);"
);
return( getWatchCount );
}
);
ul{
list-style-type: none;
}
li{
font-size:13px;
}
.arrow{
width: 0;
height: 0;
border-top: 7px solid transparent;
border-bottom: 7px solid transparent;
border-right: 7px solid transparent;
cursor: pointer;
margin-left: 5px;
border-left: 7px solid #000;
display: inline-block;
transition:all 0.3s;
}
.arrow.expand {
transform: rotate(45deg);
transform-origin: 20% 50%;
margin-top: 0;
}
.arrow.none {
border-left: 7px solid #ccc;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="MyCtrl" >
<p>
<strong>Watch Count:</strong> {{ numberOfWatches }}
</p>
<script type="text/ng-template" id="elementTree">
<li>
<div class="arrow"
ng-class="{expand:element.isOpen,none:!element.elements}"
ng-click="$apply(element.isOpen = !element.isOpen) ; countWatches()">
</div>
{{element.title}}
</li>
<div ng-if="element.isOpen">
<ul
ng-repeat="element in element.elements"
ng-include="'elementTree'">
</ul
</div>
</script>
<ul ng-repeat="element in tree"
ng-include="'elementTree'">
</ul>
</div>

Running header with nested generated content

This is a question about Paged Media.
I want to mix a string-set with running elements. It works in PrinceXML but not in PDFReactor. My question, is this possible in PDFReactor?
HTML
<p class="head">
<span class="first">Repeatable title</span>
<span class="divider">|</span>
<span class="last"></span>
</p>
CSS
p.head {
position: running(heading);
font-size: 8pt;
margin: 0;
padding: 0;
}
#page {
size: A4;
#top-left {
content: element(heading);
}
}
So far everything is peachy. But when I try to define a string-set from a H1 and try to write this into span.last this isn't working.
h1 {
string-set: doctitle content();
}
p.head span.last {
content: string(doctitle);
}
This is possible with PDFreactor as well. Just the syntax is a bit different. PDFreactor does not support the content() function for the string-set property with Named Strings. Instead it uses the self value which works like content() or content(text) (see http://www.pdfreactor.com/product/doc_html/index.html#NamedStrings )
There is a second issue. You are setting the content property on the span element itself. Usually in CSS, creating generated content with the content property is actually only allowed for page margin boxes and pseudo elements such as ::before and ::after. This is also how browsers support it. Not sure why this works in Prince.
So basically you just have to make 2 minor adjustments to your style sheet to make this work in PDFreactor:
h1 {
string-set: doctitle self;
}
p.head span.last::before {
content: string(doctitle);
}

how to handle combo boxes of ExtJS in selenium webdriver

Hi i have a ExtJS based UI. I have come to know that in ExtJS the combo box is not a real combo box but a combination of input text field, image of drop down box and a list. Now i am able to identify the control but i am stuck at selecting the value from the list. In the HTML source i see that the list is appearing as a seperate div and gets attached at the end of the source when we click on the drop down. find below the HTML source of the drop down control.
{
<div id="ext-gen678" class="x-form-field-wrap x-form-btn-plugin-wrap" style="width: 556px;">
<div id="ext-gen676" class="x-form-field-wrap x-form-field-trigger-wrap x-trigger-wrap-focus" style="width: 521px;">
<input id="ext-gen677" type="hidden" name="GHVOg:#concat#~inputFld~ISGP_UNIV:ft_t_isgp.prnt_iss_grp_oid:0" value="">
<input id="GHVOg:Mixh8:0" class="x-form-text x-form-field gs_dropDown_input gs_req x-form-invalid x-form-focus" type="text" autocomplete="off" size="24" style="width: 504px;">
<img id="trigger-GHVOg:Mixh8:0" class="x-form-trigger x-form-arrow-trigger" alt="" src="../../ext/resources/images/default/s.gif">
}
find below the HTML source of the drop down list:
<div id="ext-gen726" class="x-layer x-combo-list x-resizable-pinned" style="position: absolute; z-index: 12007; visibility: visible; left: 294px; top: 370px; width: 554px; height: 123px; font-size: 11px;">
<div id="ext-gen727" class="x-combo-list-inner" style="width: 554px; margin-bottom: 8px; height: 114px;">
<div class="x-combo-list-item"></div>
<div class="x-combo-list-item">12h Universe</div>
<div class="x-combo-list-item">1h Universe</div>
<div class="x-combo-list-item">24h Universe</div>
<div class="x-combo-list-item">2h Universe</div>
<div class="x-combo-list-item x-combo-selected">4h Universe</div>
Now i have problem selecting the value from the list as the div element of the list is not attached to the control.
Also please refer the screen shot, where i have multiple similar controls [Named "Add Security to Universe"]
In the screen shot you can see multiple drop downs [Add security to Universe] highlighted and all the drop downs have same value appearing in the list. so how can i identify these values from the drop down list.
I was wondering how ExtJS maintains mapping of the drop down div elements with the combo Box widget so that i could use the same logic for identifying the list. Can anyone tell me how can i go about doing this thing in selenium webdriver?
Did you notice that there will be only one visible x-combo-list on the page? (Let me know if you can open up two combo lists at the same time)
Therefore you only need to care about the visible one x-combo-list.
Css selector: .x-combo-list[style*='visibility: visible;'] .x-combo-list-item
Xpath: //*[contains(#class, 'x-combo-list') and contains(#style, 'visibility: visible;')]//*[contains(#class, 'x-combo-list-item')]
// untested java code, just for the logic
public void clickComboItem(WebElement input, String target) {
input.click(); // click input to pop up the combo list
List<WebElement> comboItems = driver.findElements(By.cssSelector(".x-combo-list[style*='visibility: visible;'] .x-combo-list-item"));
for (int i = 0; i <= comboItems.size(); i++) {
WebElement item = comboItems.get(i);
if (item.getText().eqauls(target)) {
item.click();
break;
}
}
}
// compilable C# version
public void ClickComboItem(IWebElement input, string target) {
input.Click();
IList<IWebElement> comboItems = driver.findElements(By.CssSelector(".x-combo-list[style*='visibility: visible;'] .x-combo-list-item"));
comboItems.First(item => item.Text.Trim() == target).Click();
}
What i can suggest is :
you catch all your inputs like :
List<WebElement> inputList = driver.findElements(By.cssSelector("input cssSelector")); // you must complete this cssSelector
WebElement input = inputList.get(0); // get the 1st input
input.click(); //click on the first input and the option list appears.
you catch all "options" like :
List<WebElement> optionList = driver.findElements(By.cssSelector(".x-combo-list-item")); // get all options
WebElement option = optionList.get(1);
option.click();
input.sendKeys(option.getText()); //getText() get the html inner value
This is just an example in Java and you can actually use a loop foreach if you want to automat this populate for all your inputs.
I use JavaScriptExecutor, my SelectRandomOption looks like this:
public void SelectRandomOption()
{
String randomOptionIndex = "Math.floor(Math.random()*Ext.getCmp('" + ExtJSIdOfComboBox + "').getStore().getCount()-1)";
String randomOptionValue = "Ext.getCmp('" + ExtJSIdOfComboBox + "').getStore().getAt(" + randomOptionIndex + ").getData()['model']";
String jsScript = "Ext.getCmp('" + ExtJSIdOfComboBox + "').setValue(" + randomOptionValue + ");";
js.ExecuteScript(jsScript);
}
I basically used the marked answer above however it needed a bit of adapting for Ext Js 4.1.
It's essentially the same approach but you need to look for a visible div marked with class "x-boundlist"
I used xpath and used queries that looked something like this:
.//div[#class[contains(.,'x-boundlist')]]
and then retrieve and click on an li matching your desired entry:
.//li[normalize-space(text())='combobox entry text']
I've put normalize-space in there as xpath seems to have real problems if you dont trim strings. That methods does a left + right trim and also removes duplicate spaces eg
' blah blah ' would change to 'blah blah'.

Resources