How to get second attribute level with Selenium's xpath - xpath

I'm trying to check in Selenium if an element has a green border or not.
i can use //div[#id="target"]/#style to get the parsed style string applied... and use string search to search for the border... i have to work around some brosers showing the shorthand (border: solid 3px green) and other showing the expanded (border-style: solid; border-color: green....) ...but how could i do that more cleanly?
ideally something like: //div[#id="target"]/#style.borderColor
I also would like to avoid using the contains selector because the syntax is awful. But if it's the only way, so be it. of course.

XPath doesn't know anything about CSS styles, it doesn't understand the attributes and takes them just as simple strings. It would be weird if a XML query language understood CSS styles, right?
Besides, the getAttribute() uses a construct similar to XPath, but not XPath! It also selects the attribute via # sign, right. But consider this locator: "id=target#style". It would work, but it's definitely no XPath.
It would also return the explicitly set style attribute, not it's internal computed value, so it's useless unless there actually is a style attribute on the element. If there is style attribute defined, go for it.
Selenium itself can't give you the right answer (WebDriver has getCssValue(), but there's no counterpart for it in Selenium RC, afaik).
Therefore, the only way to get the computed style (the final value computed from all sources) of an element in Selenium RC is via JavaScript.
The script that has been working for me for ages (after a slight edit) is this (from quirksmode.org):
function getStyle(id, stylePropJs, stylePropCss) {
var x = document.getElementById(id);
if (x.currentStyle)
var y = x.currentStyle[stylePropJs];
else if (window.getComputedStyle)
var y = document.defaultView.getComputedStyle(x,null).getPropertyValue(stylePropCss);
return y;
}
Note that IE needs a JavaScript name of the property, but every other browser uses CSS names. Also, you can't use the shorthand property and you definitely must use the expanded one. Therefore, in your case:
String script = "var x = document.getElementById('target');" +
"if (x.currentStyle) var y = x.currentStyle['borderTopColor'];" +
"else if (window.getComputedStyle) var y = document.defaultView.getComputedStyle(x,null).getPropertyValue('border-top-color');" +
"y;";
String color = selenium.getEval(script);
This returns the color of the top border (yep, you'll need to call it four times in order to get the whole border) of the target element. But guess what! Every browser returns the color in a different format. Fortunately, at least in Java, there's a Color class that can easily reformat it:
String color = Color.fromString(color).asHex();
Now that you have a hex encoded color stored, you can trivially make check whether it's green or not.

Related

Plotly.js, show tooltips outside of chart container

I need to implement a plotly.js chart on a page with a very restricted width. As a result, a tooltip is partially cut. Is it possible to cause tooltip not to be limited by plotly.js container size?
My code example at codepen: https://codepen.io/anatoly314/pen/gOavXzZ?editors=1111
//my single trace defined as following but it's better to see example at codepen
const yValue1 = [1000];
const trace1 = {
x: [1],
y: yValue1,
name: `Model 1`,
text: yValue1.map(value => Math.abs(value)),
type: 'bar',
textposition: 'outside'
};
It is, by design, not possible for any part of the chart to overflow its container.
I would say it is wrong to say that by design this is not possible! It is a bit hacky, but when you add the following lines, it shows the label outside of svg:
svg.main-svg,svg.main-svg *
{
overflow:visible !important;
}
The answer given by rokdd works. However the css selector should be more specific, otherwise it's natural that you will introduce subtle bugs (particularly if you need to scroll the content where the plotly chart is contained).
If we look at the DOM tree constructed by Plotly, we find that the tooltips are created inside the <g class="hoverlayer"></g> element (which is a direct child of one of the three <svg class="main-svg"></svg>). So that parent (that svg.main-svg element) is only one that needs to affected.
The ideal css selector in this case would be the :has selector. However it's still not supported (as of 2022): https://css-tricks.com/the-css-has-selector/
So the next simplest thing is to use a little bit of javascript right after we call Plotly.newPlot:
// get the correct svg element
var mainSvgEl = document.querySelector('#positive g.hoverlayer').parentElement;
mainSvgEl.style['overflow'] = 'visible';
Or in a more generic way (works for any chart):
Array.from(document.querySelectorAll('g.hoverlayer')).forEach(hoverEl => {
let mainSvgEl = hoverEl.parentElement;
mainSvgEl.style['overflow'] = 'visible';
});

Building umlet custom elements

I am trying to do a collaboration diagram in umlet, but there isn't a palette element to do so. So I thought I'd create one. Easier said than done! I am able to define the class element of the diagram, but am unable to define the arrows that define the flow of the collaboration.
I've tried adding a drawline statement to the code window, but it is undefined in umlet custom element implementation.
//Modify the code below to define the element's behavior.
//
//Example: Change the line
// y += printCenter(textline,y);
//to
// y += 2*printCenter(textline,y);
//and observe the element preview.
int y=textHeight();
drawRectangle(0,0,100,35);
drawRectangle(0,150,100,35);
//drawline(10,35,10,115);
for(String textline : textlines) {
y += printCenter(textline,y);
}
The expected result should be two class elements and two arrows denoting the flow of the collaboration.
I've gotten this far, but I am unable to save it to the custom elements palette.
Welllll, my bad! It turns out that you can use the regular palette elements to construct the diagram and then save it to umlet's palettes directory.

SASS HEX to RGB without 'rgb' prefix

The Question:
Is there a SASS function/technique that transforms a HEX value to a simple RGB string.
Simple here meaning just a string without it being enclosed in rgb() ?
E.g: #D50000 --> "213,0,0"
Why I need this:
I'm using Material Design Lite as my UI 'framework'. More specifically I'm using the SASS version so I can tweak the color variables according to my app's style-guide.
For some reason the color variables in _variables.scss of MDL take this format for color definitions:
$color-primary: "0,0,0" !default; // supposed to be black
which is really, really odd. I expected, at most, something along the lines of
$color-primary: rgba(0,0,0,1) !default;
My color variables are stored in another file called _globals.scss in which I store my variables in regular HEX format so I can easily reuse them in other places:
$brand-primary: #FA3166;
$brand-primary-dark: #E02C59;
I don't want to define 2 times my colours (1 HEX & 1 MDL-compatible RGB string), hence the reason I need to transform HEX to RGB-string.
#nicholas-kyriakides's answer works perfectly fine, but here is a more concise function using Sass interpolation.
#function hexToRGBString($hexColor) {
#return "#{red($hexColor)},#{green($hexColor)},#{blue($hexColor)}";
}
You can pass in either a hex either explicity or from rgb() or rgba() with opacity as 1.
For example:
$color-white: hexToRGBString(#fff) => "255,255,255"
$color-white: hexToRGBString(rgb(255,255,255)) => "255,255,255"
$color-white: hexToRGBString(rgba(#fff,1)) => "255,255,255"
I've hacked around it with a SASS function:
#function hexToString($hexColor) {
// 0.999999 val in alpha actually compiles to 1.0
$rgbaVal: inspect(rgba($hexColor,0.9999999));
// slice substring between 'rgba(' and '1.0)'
#return str-slice($rgbaVal, 6, str-length($rgbaVal)-6);
}
Usage:
$brand-primary: #333;
$color-primary: hexToString($brand-primary);
I think the MDL team intended to have a different way to customise the palette and I'm missing it, so if someone knows a better way to customise MDL's palette I'm open to suggestions. Either way this solves the original question.

Need pauses - timing not working on iterating through text strings w/ d3/svg (piling up on each other)

I am trying to use d3 to animate text using an svg text with d3 transitions. I have it working as desired for a single string.
I want to iterate through strings from an array of json objects.
I can do this as well.
All the painting and transitions work great. Problem is, they all happen at once, and appear piled up on each other, and all animate all at once.
I have tried putting them in a setTimeout() to get them to appear sequentially.
Still does not work.
for ( i in haikuStr ) {
if( i !=0 ){
//Make it wait if an appropriate time it is not the first one
setTimeout( function() {
showText();
}, 11000 * i );
} else {
//if i=0, don't make folks wait
showText();
}
}
The showText() function is the full create container -> finish transitions.
I use 11000 * i to ensure that >2 iterations have 11 additional seconds per i.
I have spent quite a bit of time reading and trying to figure out how to get the loop to pause before cycling through to paint the next line.
Any thoughts or ideas would be appreciated.
The un-timed example is here, if you wish to see the text jumble up:
http://www.mysalmagundi.com/js/svg-d3-no-timing.html
Have you read Thinking with Joins? Or some of the other introductory D3 tutorials, such as those by Scott Murray? Or Three Little Circles, or Working with Selections? I ask because your showText function is misusing data joins; it creates text elements for every element in the global haikuStr array:
var text = haikuContainer.selectAll("text")
.data(haikuStr)
.html(String)
.enter().append("text");
And all your text elements are overlapping because you set them to have the same y-attribute:
var thisHaiku = text
.attr("x", -800)
.attr("y", 120)
(Also, that selection.html call is a no-op because the update selection is guaranteed to be empty, since you just created haikuContainer it is guaranteed to not have any descendant text elements. And thisHaiku is the same value as the var text, because when method chaining selection.attr and similar methods return the current selection; so there’s no reason to create a separate var. Also, you shouldn’t use a for-in loop to iterate over arrays.)
If you wait 11 seconds, you’ll see the second SVG appear, but because of your data join as described above, it has the same overlapping text content.
If you just want to show a single piece of text, then pass that string to your showText function (e.g., showText("hello")). Then, since you’re just creating individual elements, just selection.append them rather than using a data-join. You only need the data-join when you’re creating (or updating or removing) a variable number of elements based on data; in this case it looks like you’re trying to just create a single element.

Sub-selection based on function

I have a selection of elements that I'm trying to filter down based on a particular style value (I want just the ones with opacity=1). I'm looking at the documentation for selection.filter along with selection.select and selection.selectAll as well but I'm confused about the correct usage with a function argument.
"select" indicates that it selects the first matching element (as expected) but then the example in the filter documentation shows it being used with a function to select the "odd" elements while maintaining the index.
"selectAll" indicates that you can return an array of elements, but that the function argument is invoked one-by-one in the usual way for each element in the original selection. I'm having difficulty imagining a use case for this.
I guess what I'm wondering is whether there are any tutorials or examples around that discuss the correct usage of these functions?
Thanks,
scott
If you want to reduce a selection to a subset of selected elements, use filter. If you want to select descendent elements, use select or selectAll.
Most often, filter is used to filter elements based on data or index. However, you can access the selected element as this within the filter function. Thus, if you have some elements selected, and you want to reduce that selection to only those elements with an opacity of 1, you can say:
var opaque = selection.filter(function() {
return this.style.opacity == 1;
});
To be safe, you might prefer to look at the computed style rather than the element's style properties. This way, if the opacity is inherited from a stylesheet, you'll get the correct value; otherwise, when a style is inherited this.style.opacity will be the empty string.
var opaque = selection.filter(function() {
return window.getComputedStyle(this, null).getPropertyValue("opacity") == 1;
});
Or equivalently, select the node and use selection.style:
var opaque = selection.filter(function() {
return d3.select(this).style("opacity") == 1;
});
You might find it easier if you filter by data or by class, instead of by computed style property. For example, if you set a class on your nodes, you can filter a selection by class instead:
var opaque = selection.filter(".opaque");

Resources