Simulating a select event with Enzyme - enzyme

I am a little confused because this seems to be such a trivial and easy thing, yet there is hardly any proper documentation in Enzyme on how to do this.
I want to do one of the simplest things in the world. I just want to simulate a select event in a drop down select (HTML select/combo) by selecting an option called "dogs".
When I have this option selected would like to see that the option is "selected", and I would also like to see that the select value is set to "dogs".
What am I doing wrong here?
import React from 'react';
import {shallow, mount} from "enzyme"
test(' change ', () => {
let Compoment =
<select className="sel" name="selectComp" required="">
<option value="empty">Please make your choice ...</option>
<option value="cats">feline</option>
<option value="dogs">canine</option>
</select>;
const wrapper = mount(Compoment);
let sel = wrapper.find(".sel").at(0)
sel.simulate('change', { target: { name: 'selectComp', value: "dogs" } });
expect(wrapper.find(".sel").at(0).getElement().value).toBe("dogs")
expect(wrapper.find(".sel").at(0).find("option").at(2).props().selected).toBe(true)
})

React syncs prop=>DOM property only one way.
If you set selected prop on <option> element React would write it into DOM selected property(but you actually don't use selected prop at all).
You browser(or JSDOM in case of Jest tests) also changes selected property for relevant <option> element upon user selection.
So that's why prop does not reflect change.
You can use getDOMNode to access underlying DOM node:
expect(wrapper.find(".sel").at(0).find("option").at(2).getDOMNode().selected).toBe(true)
but know what? you don't need that. This would just test browser highlights right option upon user selection. It's just a wasted time to check if browser and/or React do their job responsibly.

Related

Charts on Admin-on-rest

I need to do a webpage to finish my uni, and im about 90% done with it, however, im struggling with getting custom Iterators and Charts to work.
Im trying to use Recharts, but i have no idea on how to make it gather the info i want (I need it to get the number of users registered and the number of vaccines they took), but all i see on the documentation is ready results, im not sure if ChartJS will make it any better, and even if it does, im confused on how custom iterators work since i took my "Analytics" one from another StackOverflow question, however that's the least of my worries, since this seems to be simplier than getting the ACTUAL charts to work.
Help would be appreciated since i have 2 weeks to deliver it and im stuck on that, and its CRUCIAL i get that to work.
Oh yes, im also very bad at Javascript so every info is welcome.
EDIT:Forgot to say i've looked around but lots of them are for simplier and static stuff instead of database counts or stuff like that.
Welcome to SO.
1) Recharts wants an array that it needs to iterate over and display. So what I usually do is make the Chart Component a child of a List component.
2) I use the Filter on the List page to select different chart components when the user selects different options from the dropdown (in case your analytics page needs to show different charts from the same page)
^^this is a bit tricky for peeps new to JS but is quite straightforward if you want to get into it.
3) For you i think the best bet will be that you make different List components and resources for each page and just display different charts on their own page.
export const AnalyticsList = (props) => {
return (
<List title="Analytics" {...props} perPage={20} sort={{ field: 'id', order: 'ASC' }}>
<Analytics />
</List>
)
}
here is how the Analytics Component is
import React from 'react';
import {BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, Legend} from 'recharts';
export const Analytics = (props) => {
return (
<BarChart width={800} height={400} data={Object.values(props.data)} margin={{top: 5, right: 30, left: 20, bottom: 5}}>
<XAxis dataKey="name"/>
<YAxis dataKey="charLimit" />
<CartesianGrid strokeDasharray="3 3"/>
<Tooltip/>
<Legend />
<Bar dataKey="charLimit" fill="#82ca9d" />
</BarChart>
);
}
Hope this helps.

How to preselect an option in an embedded HTML form?

I try to preselect an option of a select in an embedded HTML form in Camunda Tasklist, but always the first option is preselected.
I followed Binding to a Process Variable:
Binding to a Process Variable
A select box can be bound to a process variable using the cam-variable-name directive:
<select cam-variable-name="foo" cam-variable-type="String">
<option>bar</option>
<option>zar</option>
</select>
Research
I read also CAM-3173:
select box doesn't show the correct option
If I set the value of variable by a select box on the start form, the next task form didn't show the option that has been choosen in the start form. It uses the same select box.
but I use Camunda 7.9 and the problem is fixed since version 7.2.3.
HTML
<form>
<select cam-variable-name="variable" cam-variable-type="String">
<option value="option1">option1</option>
<option value="option2">option2</option>
</select>
</form>
Result
option1 is preselected. I checked the process variable before entering the user task and it contains option2.
What did I wrong? If the bug still exists, is there any work-around?
I found a work-around, see Camunda Reference:
Implementing Custom Fields
The following is a small usage example which combines some of the features explained so far. It uses custom JavaScript to implement a custom interaction with a form field which does not use any cam-variable-* directives.
It shows how custom scripting can be used for
declaring a variable to be fetched from the backend,
writing the variable’s value to a form field,
reading the value upon submit.
[...]
The above example uses jQuery for interacting with the HTML controls. If you use AngularJS, you can also populate the $scope in the variables-fetched callback and read the values from the $scope in the submit callback:
My changed HTML:
<form>
<script cam-script type="text/form-script">
camForm.on('form-loaded', function() {
camForm.variableManager.fetchVariable('variable');
});
camForm.on('variables-fetched', function() {
$scope.variable = camForm.variableManager.variable('variable');
});
camForm.on('submit', function() {
camForm.variableManager.variableValue('variable', $scope.variable);
});
</script>
<select data-ng-model="variable">
<option value="option1">option1</option>
<option value="option2">option2</option>
</select>
</form>

How to render Ember component on same line

This Ember template code...
<div class="detail bedrooms">
<span>Number of bedrooms:</span> {{rental.bedrooms}}
</div>
<div class="detail temperature">
<span>Current Temperature:</span> {{city-temperature location=rental.city}}
</div>
...results in this rendering...
How could one get the "25C" text to render on the same line as "Current Temperature" in the same way that "San Francisco" is on the same line as "Location"?
I have tried getting the city-temperature component to return only text, but (secondary question here) is it even possible for a component to return only text if the text is from a remote ajax request since the promise seems to need a DOM element to append to?
This Ember app is available here on GitHub. It's a modified version of the official Ember.js tutorial app with this "Current Temperature" detail added.
The problem is that; city-temperature is a component; and by default ember components is assigned div as their tags. Due to this; the content of city-temperature starts at a new line.
What can you do? Play with css and make sure div tag of city-temperature does not start at a new line; but instead floating right within the owner div. The second option is making city-temperature component tagless; so that it does not start at a new line. To achieve that; you can just declare:
tagName: ''
within city-temperature.js. You can see the following twiddle to see the second option I mentioned.
After reading your comments; I have forked your repository and made minor modifications to achieve what you want. You can check my repository and my corresponding commit to see what I changed.
Basically; I really did not like the idea that weather service is returning a DOM element. I have changed it to return an instance of ember promise (I mean Ember.RSVP.Promise). Within city-temperature.js I just set a retrieved weather value from weather service and set it to the component instead of making DOM modification. Finally; I modified city-temperature.hbs to render weatherValue and added a simple css item in order to prevent weather-container div to insert a new line break.
I am not sure; whether you will like my solution or not; but I guess you will retrieve the visual appearance you want. You can always rely on promises for async tasks; you do not need to create DOM elements and pass them around to components and append them to somewhere within DOM tree of the corresponding component. I believe one of the reasons we are making use of a framework like Ember is to prevent such DOM modifications. Best regards.
An ember component adds a div by default. If you don't want to add this div tag, you need to set tagName to an empty string.
That's called tagless component.
Further, for your case, you can give a tagName='span' so you will not need a span in your template.
export default Ember.Component.extend({
tagName: 'span',
classNames: ['weather-container'],
weather: Ember.inject.service(),
//rest of your code...
});

Ember acceptance test for chosen-select

Chosen is working great for me but I cannot figure out how to get my acceptance test to send in the value on the form submit.
Handlebars template:
<form>
{{#ember-chosen value=country}}
{{#each countries as |country|}}
<option value={{country}}>{{country}}</option>
{{/each}}
{{/ember-chosen}}
{{#link-to 'countries.show' (query-params country=country) tagName='button' type='submit' replace=true class="submit__button"}}Search{{/link-to}}
</form>
Acceptance test:
test 'Searching for a country', (assert) ->
visit '/search'
find('.chosen-select').val('USA')
find('.chosen-select').trigger('chosen:updated')
click('.submit__button')
andThen ->
assert.equal find(".chosen-select :selected").text(), "USA"
This fails because on submit country is not even passed in as a query param, but only in the test.
The same thing happens if I do it in the console. If I open my console and do:
$('.chosen-select').val('USA')
$('.chosen-select').trigger('chosen:updated')
then the selection updates before my eyes! But then clicking submit produces the same results as the test, that is, the country is not passed into the query-params.
Clicking "USA" from the dropdown however, then clicking submit, works perfectly.
UPDATE
Chad Carbert's response below gave me the answer. Here are the specific results:
$('.chosen-select').val('USA')
$('.chosen-select').trigger('chosen:updated')
updates the DOM only. Not the data-bindings with Ember.
$('.chosen-select').trigger('change', {'selected': 'USA'})
updates the data-bindings (which is what I really need), not the DOM.
Since this is for my acceptance tests only and I don't need to see the DOM change, my test now looks like this:
test 'Searching for a country', (assert) ->
visit '/search'
find('.chosen-select').trigger('change', {'selected': 'USA'})
click('.submit__button')
andThen ->
assert.equal find(".chosen-select :selected").text(), "USA"
Which works like a charm! If you wanted to though, there would be no harm in doing all three:
test 'Searching for a country', (assert) ->
visit '/search'
find('.chosen-select').val('USA')
find('.chosen-select').trigger('chosen:updated')
find('.chosen-select').trigger('change', {'selected': 'USA'})
click('.submit__button')
andThen ->
assert.equal find(".chosen-select :selected").text(), "USA"
Thank you Chad!
It looks like this might be an issue with the way that this component wraps the jQuery library. Looking at the documentation for chosen:
Updating Chosen Dynamically
If you need to update the options in your select field and want Chosen to pick up the changes, you'll need to trigger the "chosen:updated" event on the field. Chosen will re-build itself based on the updated content.
via chosen docs
However, it doesn't look like the ember component is wrapping that event in any way. It also looks like the chosen:update is updating the chosen UI implementation, but not the data binding within Ember. This would be in a case where you've after the fact added options to the select, and want these reflected ('rebuilt') on the screen.
Looking at the code behind this component and how it's wrapping the chosen library, it appears that this component is only capturing the change event. So try doing a trigger on a change event and that should have things update within ember, and its data binding.
Or try triggering the change the way it's mentioned on the Chosen docs:
Form Field Change**
When working with form fields, you often want to perform some behavior after a value has been selected or deselected. Whenever a user selects a field in Chosen, it triggers a "change" event* on the original form field. That let's you do something like this:
$("#form_field").chosen().change( … );
If that doesn't work, if you're able to provide a codepen I will take a look closer, thanks!

Setting selected entry using helper.options?

Im am using Play 2.0.4 and
helper.options(myitems)
in my template (inside of a helper.select)
In this case, how can I define the default selected entry, which shall be one entry out of myitems? Thanks for any hint!
A little bit more about my case:
Imagine a news archive, showing all news titles. This news archive uses pagination, pagination uses GET to pass the next/previous page number.
The play framework however will only correctly select the currently selected "select" item (here: news category) when a POST request was used - while pagination uses GET!
Intended behaviour: While a filter is applied / a specific news category is selected, this shall always be visible to the user by preselecting the currently selected news category in the "select" form.
A "screenshot" for illustration:
So, anyone having a good idea on how to cope with this problem? Any way to tell Play manually which entry from the "select" form it shall select? '_default always adds a new entry instead of selecting one out of the given options ): Would be great, if one wouldn't have to build the complete "select" form manually.
Try passing '_default option to select helper:
#import views.html.helper._
#select(form("email"), options(List("first", "third")), '_default -> "second")
It seems, unfortunately, the only way to figure it out is to look up the source.
Update:
Specifying _default property doesn't set selected attribute on option tag. It looks like the only way to preselect entry is to pass prefilled form to the template. For example, suppose you have following form:
case class RegInfo(email: String, color: String)
private val registrationForm = Form(
mapping(
"email" → email,
"color" → nonEmptyText(minLength = 5, maxLength = 32)
)(RegInfo.apply)(RegInfo.unapply)
)
Then in the action prefill form before passing to the view:
def create = Action {
Ok(html.create(registrationForm.fill(RegInfo("user#qwe.com", "blue"))))
}
Then in template use helper:
#select(form("color"), options(List("red", "green", "blue")))
And value will be preselected.
Ended up with the pragmatic approach:
<select id="myfield" name="myfield" >
<option class="blank" value="">-- All items --</option>
#for((key, value) <- MyModel.options) {
#if(key == GETValuePassedToTemplate) {
<option value="#key" selected>#value</option>
} else {
<option value="#key">#value</option>
}
}
</select>
Still wondering if there is a better option / way to do it.
Actually, there is a nicer solution to it. If you call the template having the form partially bound you will achieve your goal. Here's the code for your controller:
Ok(views.html.myForm(myForm.bind(
Map("fieldName1" -> "value1",
"fieldName2" -> "value2"))))
Make sure you map fieldnames to the values of the options you want pre-selected.
Still wondering if there is a better option / way to do it.
Well, if your not hell-bent on using play to solve this particular problem, you could always solve it using JavaScript and jQuery:
$(function () {
$('#yourSelect_id').val(5);
}
Where your select options each has values and the one option you whish to pre select has value 5.