How to update Scroll values while using Bunit automation scripts - unit-testing

We have a requirement to update the scrollLeft value of the a element in blazor platform. We have tried to update the scroll Left property through databinding using the below code snippet. But its not working. So it is a mandatory to use the JS code to update the scrollLeft property of the parent element.
#page "/Scroll"
#using Microsoft.JSInterop;
#inject IJSRuntime JSRuntime;
<input type="button" #onclick="#OnScroll" value="scroll" />
<input type="button" #onclick="#OnScrollJs" value="scroll-Js" />
<div id="parentDiv" style="width:500px;height:300px;overflow:auto" scrollLeft="#ScrollLeft" scrollRight="#ScrollRight">
<div id="childDiv" style="width:1500px;height:1500px"></div>
</div>
#code {
double ScrollLeft = 0;
double ScrollRight = 0;
private void OnScroll()
{
ScrollLeft = 200;
ScrollRight = 200;
}
private async void OnScrollJs()
{
await JSRuntime.InvokeVoidAsync("onChangeScrollValues", "parentDiv", 200, 200);
}
}
JS code was shown in below
window.onChangeScrollValues = function (id, left, top) {
var element = document.getElementById(id);
element.scrollLeft = left; element.scrollTop = top;
}
From the above code when we use the JS code snippet to update the DOM elements means, it does not suitable for Bunit testing. So in this scenario how I able to set the Scroll values in Bunit scripts ?

bUnit doesn't run JavaScript, instead you can write a test that verifies that the onChangeScrollValues is correctly called when scroll-Js button is clicked.
Something like this should suffice:
// arrange
using var ctx = new TestContext();
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
var cut = ctx.RenderComponent<MyComponent>(); // replace MyComponent with the name of the component.
// act
cut.Find("input[value=scroll-Js]").Click();
// assert
cut.JSInterop.VerifyInvoke("onChangeScrollValues");

Related

React Native Render HTML bullet list is not centered with text

I'm using react-native-render-html library to display data from Django Rest Framework stored in RichTextUploadingField from django-ckeditor. As basic styling is working properly I have a problem with the <ul> tag - text is not centered with circle/square/number as shown in the image. I tried using renderersProps like:
<RenderHTML renderersProps={{ ul: { markerBoxStyle:{ paddingRight: 3, top: 5 }, markerTextStyle: { color: "red" } } }} />
But it works for specific text size. If I change the text size in style in Django admin, the element and text aren't centered again. Is there any option on how to fix it or what causes the problem?
You could add a custom CSS class to unordered list items only. Then apply your styles to that CSS class.
// this function goes through every list item in <ul> only and adds your custom class ('ul-li' in my case)
const onElement = (el) => {
const { children, name } = el;
if (name === 'ul' && children && children.length) {
children.forEach(listItem => listItem.attribs = {class: 'ul-li'})
}
}
// define your styles for your custom class
const classesStyles = {
"ul-li": {
lineHeight: '21px', // for me adjusting lineHeight did the job
}
}
// in your JSX
<RenderHtml
source={source}
contentWidth={width}
tagsStyles={tagsStyles}
... // other props you may have
domVisitors={{onElement}} // first alter your html
classesStyles={classesStyles} // then apply styles
/>
Sources:
classesstyles
onElement

Knockout not sending updated value

I have a knockout page where I am formatting the input with regex. It makes the input field to a MM/dd/yyyy format. So if a user inputs "1111" it will change the input vbox to show "01/01/2011" or for "01111" it will show "01/01/2011".
The problem I am facing is that my observable only returning the keystroke entered by user and not the fully formatted item. For example , if user is entering "1111" I get back "1111" instead of the "01/01/2011"
Here is the Html segment
<input id="inpEventDt" placeholder="MM/DD/YYYY" class="input-small" data-date-blur="true" data-regex="^((\d{0,2})|(\d{1,2}/?\d{0,2})|(\d{1,2}/?\d{1,2}/?\d{0,4}))$"
type="text" data-bind="textInput: dateofevent"/>
And this is how I have the knockout binding
var ViewModel = function (eventdt ) {
var self = this;
self.dateofevent = ko.observable(eventdt);
}
viewModel = new ViewModel("");
ko.applyBindings(viewModel);
Trying to figure out what I am doing wrong.
I would not try to format the text input while the user is typing, because it makes a hard to understand user interface and non intuitive typing experience.
In addition, it's more complicated, because while typing, the input is likely invalid.
Try instead to format your input on some event (blur for example), while validating it on keystroke:
var viewModel = function() {
var self = this;
var regex = /^(\d{1,2})\/(\d{1,2})\/(\d{4})$/;
this.isValid = ko.observable(false);
this.date = ko.observable("");
this.format = function() {
self.validate(self.date());
// TODO: something else
}
this.validate = function(newVal) {
var matches = newVal.match(regex);
if (!matches || matches.length != 4) {
self.isValid(false);
} else {
self.isValid(true);
}
};
this.date.subscribe(function(newVal) {
self.validate(newVal);
});
this.style = ko.computed(function() {
return self.isValid() ? "valid" : "invalid";
}, this);
};
var vm = new viewModel();
ko.applyBindings(vm);
.invalid {
border: 1px solid red;
}
.valid {
border: 1px solid green;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<input id="inpEventDt" placeholder="MM/DD/YYYY" class="input-small" data-date-blur="true" type="text" data-bind="textInput: date, event: { blur: format }, css: style" />
<div data-bind="visible: isValid">OK</div>
You should try using a read/write computed for this. Check out the example 3 in the knockout documentation for computed observables.
Also, here is a jsfiddle using moment.js to help with date formatting.
var ViewModel = function (eventdt ) {
var self = this;
self.dateofevent = ko.observable(eventdt);
self.formattedDate = ko.pureComputed({
read: function () {
return moment(self.dateofevent()).format("MM/DD/YYYY");
},
write: function (value) {
self.dateofevent(moment(value).toDate()); // Write to underlying storage
}
});
}
viewModel = new ViewModel(new Date("03/25/2015"));
ko.applyBindings(viewModel);

How to use images inside a select component in Ionic 2

I am trying to put an image inside a Select component in Ionic 2 :
I have put the image source files inside the www/img folder in my Ionic 2 project.
However, using a simple img-tag does not display any image using this code:
<ion-list>
<ion-item>
<ion-label>Gaming</ion-label>
<ion-select [(ngModel)]="gaming">
<ion-option value="nes">
NES
<img src="img/myImage.png">
</ion-option>
</ion-select>
</ion-item>
</ion-list>
Does anyone have any idea?
The ion-select component doesn't allow direct customization to itself and anything that you add to ion-select and ion-option which is not as per the ionic documentation, will be ignore in the generated output.
You cannot add class or style to the component.
One way to do this is to put the ion-select in a parent element like div or ion-row etc. with class and apply the CSS rules using .parentclass childElement selector.
To show the images in option check the function below:
prepareImageSelector() {
setTimeout(() => {
let buttonElements = document.querySelectorAll('div.alert-radio-group button');
if (!buttonElements.length) {
this.prepareImageSelector();
} else {
for (let index = 0; index < buttonElements.length; index++) {
let buttonElement = buttonElements[index];
let optionLabelElement = buttonElement.querySelector('.alert-radio-label');
let image = optionLabelElement.innerHTML.trim();
buttonElement.classList.add('imageselect', 'image_' + image);
if (image == this.image) {
buttonElement.classList.add('imageselected');
}
}
}
}, 100);
}
I have implemented color and image selector using ion-select. Please refer https://github.com/ketanyekale/ionic-color-and-image-selector
You can also check the answer at Ionic select input of colours
I achieved it like this.
<ion-select (click)="loadFlags()" formControlName="pays" value="select-country">
<ion-select-option value="select-country" >select your country </ion-select-option>
<ion-select-option *ngFor="let country of countries" value="{{country.name}}">{{country.name}}</ion-select-option>
</ion-select>
And this is my .ts file
loadFlags() {
setTimeout(function(){
let radios=document.getElementsByClassName('alert-radio-label');
for (let index = 1; index < radios.length; index++) {
let element = radios[index];
element.innerHTML=element.innerHTML.concat('<img class="country-image"
style="width: 30px;height:16px;" src="'+countries[index-1].flag+'" />');
}
}, 1000);
}
Timeout is to let the system create alert componens. My Json is an array with elements like {name:"Cameroon",flag:"https://restcountries.eu/data/cmr.svg"}
On your basis, I have worked out a more concise solution!
The method 'prepareImageSelector' is used as the Click event of the control。
Thank you!
image: string = 'English';
prepareImageSelector() {
setTimeout(() => {
let buttonElements = document.querySelectorAll('div.alert-radio-group button');
if (!buttonElements.length) {
this.prepareImageSelector();
} else {
for (let index = 0; index < buttonElements.length; index++) {
let buttonElement = buttonElements[index];
let optionLabelElement = buttonElement.querySelector('.alert-radio-label');
let image = optionLabelElement.innerHTML.trim();
if (image == this.image) {
optionLabelElement.innerHTML += '<img src="assets/img/English.png" style="width:20px;height:20px;float:right;"/>';
}
}
}
}, 100);
}
There are already some great answers, and I honestly think Select with some images should have been part of the core Ionic library, but here we are.
I made the following adaptations for my answer:
Implemented with Ionic Vue (Ionic 6, so it's still an issue)
Flags for the countries are nested in a country JSON obtained from an API call.
Implemented for Action Sheet instead of Alert as the other answers
Flags appear on the left instead of the right
First, the HTML:
<IonSelect #click="loadFlags()" placeholder="Country" :interface-options="customAlertOptions" interface="action-sheet" cancelText="Cancel" style="width: 100%" v-model="selected_country">
<template v-for="(country, index) in countries" :key="index">
<div class="ion-text-center">
<IonSelectOption :value="country.code">
<IonLabel>{{ country.name }}</IonLabel>
</IonSelectOption>
</div>
</template>
</IonSelect>
And then the Javascript:
loadFlags() {
let that = this;
setTimeout(function() {
let radios = document.getElementsByClassName('action-sheet-button-inner');
for (let i = 0; i < that.countries.length; i++) {
let element = radios[i];
element.innerHTML = `<div style="display: flex; position: relative; width: 100%"><img style="width: 40px;height:40px;" src="${that.countries[i].flag}"/>`
+ `<span style="font-size:16px; position: absolute; top:10px; left: 70px;">${element.innerHTML}</span></div>`
}
}, 10)
}
Even though it's a little crude, the innerHTML in the loadFlags function, with some HTML and CSS, can be used to achieve almost anything.

Why does this CSS transition event not fire when two classes are added?

CSS
.horizontalTranslate {
-webkit-transition: 3s;
}
.secondClass {
background: red;
}
HTML
<div class='box'></div>
JS
var box = document.querySelector('.box');
box.addEventListener('webkitTransitionEnd', function(evt) {
alert('Finished transition!');
}, false);
function transitionBox() {
// this works
box.className = 'horizontalTranslate';
// this does not work
// box.className = 'secondClass horizontalTranslate';
}
setTimeout(transitionBox, 100);
Why does the transition event not fire when two classes are added rather than one? I've also tried chaining my CSS selector, a la .secondClass.horizontalTranslate { ... }.
The reason is that box.className = 'horizontalTranslate'; is actually removing styling, causing the CSS transition to actually happen. But when I set box.className = 'secondClass horizontalTranslate';, the styling of the DOM node is not changing and no event is fired.
Another way to write transitionBox is this:
function transitionBox() {
box.classList.add('horizontalTranslate');
box.classList.add('blue');
}
If blue changes the styling of box, this works too.

Highslide: Pause HTML Slideshow when Gallery Expands

I created a HTML Gallery that expands an image every window.
http://mmvirtualtours.com/0002/photos.php
When I run the gallery as a slideshow I would like it to pause when the contained image expands.
I am trying the following code:
hs.Expander.prototype.onAfterExpand = function() {
if (this.contentType == 'html') {
this.slideshow.pause();
As it is it will pause after every transition in the slideshow. What do I need to change the contentType to and or is there a better way to do this?
Thanks!
Remove this:
hs.Expander.prototype.onAfterExpand = function() {
if (this.contentType == 'gallery') {
this.slideshow.pause();
}
};
Add this:
// Stop slideshow when viewing large image
function stopSlideshowAndExpand(element, config) {
var exp = hs.getExpander(element);
if (exp.slideshow) {
exp.slideshow.pause();
}
return hs.expand(element, config);
}
Change the onclick for the large image in the highslide-maincontent div:
<a class='highslide' href='media/01.jpg' onclick="return stopSlideshowAndExpand(this, largeGalleryOptions1 )">
<img src='media/s01.jpg' alt='' / border="0">
</a>