How to access child element from parent using react testing library? - react-testing-library

i have dom like so
<label value="false" data-testid ="source" class="StyledSwitch">
<input type="checkbox" name="source" >
<i class="ball-container"></i>
<span class="label" data-enabled="On" data-disabled="Off"></span>
</label>
how can i access input element using label with data-tesid source
i have tried something like below,
const element = screen.getByTestId('source').firstChild();
but this gives error "object is possibly null" cannot invoke an object which is possibly null. how to fix this
could someone help me with this. thanks.

You could use a querySelector to select the input, like this:
const element = screen.getByTestId('source').querySelector('input');
if(element) {
/// Do something...
);

const element = getByTestId('source').firstChild;

I would recommend using a querySelector:
const element = getByTestID('').firstchild;

Related

salesforce lightning this.template.querySelector not working

<template>
<div class="container-wrapper">
<div if:false={loggedIn} class="slds-m-around_medium">
<span>Login to Salesforce App</span>
<lightning-input name='username' label="Username"></lightning-input>
<lightning-input type="password" name='password' label="Password"></lightning-input>
<br/>
<lightning-button variant="brand" label="Login" title="Login" onclick={login}></lightning-button>
</div>
</div>
</template>
login() {
console.log('login attempt');
console.log(this.template);
var Username =this.template.querySelector('input[name="username"]').value;
var Password =this.template.querySelector('input[name="password"]').value;
console.log(Password);
console.log(Username );
}
values are not getting fetch in username, password variables.
this.template.querySelector('input[name="username"]').value is not working.
I have also tried onchange event approach on lightning-input elements , in that case event.target was undefined ? I am stuck not able to read user input.
app screenshot
There is no property 'name' on lightning-input like on standard HTML input. You should use 'data-id' instead.
HTML:
<lightning-input data-id='username' label="Username"></lightning-input>
JS:
let username = this.template.querySelector('lightning-input[data-id=username]');
The <template> element is not a common element. It holds its (inactive) DOM content inside a Document Fragment that you can access through the content property.
Therefore you should try:
var Username = this.template.content.querySelector('input[name="username"]').value;
var Password = this.template.content.querySelector('input[name="password"]').value;
In Salesforce when you are accessing a lightning input using the query selector first you have to search for lightning-input tag not the input tag.
Secondly when you add a name attribute to the lightning-input that attribute is transfered to the input tag which is created when the lightning component is rendered. But you can not access the input tag (my assumption is that it's in the Shadow DOM). Therefore to search for the lighting-input you must use a class name to identify the lightning component.
<lightning-input type="number" class="optionEditQuantityVal" value={Quantity} variant="label-hidden" step="1" max-length="1"></lightning-input>
let input = this.template.querySelectorAll('lightning-input.optionEditQuantityVal')

How to edit an item which is created using a single form on Angular2?

I am new to angular2 & I have a form which can add more item to a page (item with name & decription). This works really well, I can keep on adding new item to my list.
However, each of this item has its own edit & delete. How can I edit and delete each of the item using that only 1 form?
<form #formExperiencesRef="ngForm">
<label for="name">name</label>
<input id="name" type="text" name="fruit [(ngModel)]="formData.name">
<label for="description">description</label>
<input id="description" type="text" name="fruit [(ngModel)]="formData.description">
<button (click)="onSubmit(formExperiencesRef.value)"></button>
</form>
This single form is what I use to keep on adding new item. And now I find it hard to edit the item that I created using this. Can someone help me?
Often I would advise to go with a reactive form for all it's benefits, but if your form is this simple a template driven approach can be sufficient.
First of all I see problem in your form. Your name attributes are the same for both fields, this will mean that they are evaluated as one and the same. I would actually name them as for how your formData object looks like, and then just push the form value as is to the array. I'll just use one way binding here for the sake of the editing of item. Also pass the form object in submit.
How we can edit can be done numerous ways. Here we'll utilize the index of your list (assumingly it's an array).
<form #formExperiencesRef="ngForm" (ngSubmit)="onSubmit(formExperiencesRef.value)">
<input name="name" [ngModel]="formData.name">
<input name="description" [ngModel]="formData.description">
<button type="submit">Submit</button>
</form>
Your list:
<div *ngFor="let item of items; let i = index">
{{item.name}} <button (click)="edit(item, i)">Edit</button>
</div>
And in the TS, we can use #ViewChild to reference our form, which I am using to reset the form:
#ViewChild('formExperiencesRef') formExperiencesRef: NgForm;
and your methods for editing and saving a new item:
formData = {};
items = [];
index = null; // used to store current index value of item (if exists)
edit(item, i) {
this.index = i;
this.formData = item;
}
onSubmit(val) {
// check if index exists, if not it's a new item
if(this.index == null) {
this.items.push(val)
} else {
this.items[this.index] = val;
}
// reset index & form
this.index = null;
this.formExperiencesRef.reset();
}
DEMO: http://plnkr.co/edit/ksHp10WwaDg4AQjwDf2d?p=preview
For the future, I really suggest you check out reactive forms, you have tighter control over your form, handle validations easier and a big,big advantage to me is especially if you are dealing with nested components. Reactive forms can be confusing in the beginning, but it's worth it! :)

How do I get required message to toggle in AngularJs?

I am trying to set a required field based on a condition. Here is my code.
<div id= 'outer-form' ng-form='main' validate-on-submit='#page-errors' on-invalid='DisplayErrors();' on-valid="SubmitPage();'>
...other div's
<div ng-repeat='myItem in aFunction.myItemArray' ng-form='sub'>
<div ng-show= 'myType == "Shoes"'>
#Html.TextBox('Shoes', null, new { ng_model = 'Shoes', ng_required = 'myType == "Shoes"' })
<span ng-show="sub.Shoes.$error.Text">Shoes required</span>
</div>
<div ng-show= 'myType == "Socks"'>
#Html.TextBox('Socks', null, new { ng_model = 'Socks', ng_required = 'myType == "Socks"' })
<span ng-show="sub.Socks.$error.Text">Socks required</span>
</div>
<button class="clientContinue" id="step-submit-btn">CONTINUE</button>
</div>
I found this which does not work for me. I am stuck so any help/suggestions will be appreciated. Thanks!!
I don't know what kind of template engine are you using on your exemplo, but if it supports adding attributes depending on a boolean your code looks right.
Speaking about Angular, looks like you'll need a custom validator (type validator) where you can pass the type as a HTML attribute. Inside the validator you'll be able to control the required flag very easily.
You can found more information about custom validators in the following link: http://docs.angularjs.org/guide/forms

Dynamically adding form elements with jQuery and Zend_Form

I have a form in which people shall be able to add the same portion of elements with a plus-button, so that something like this is produced:
<div id="person-1" class="person">
<input type="text" name="name-1" id="name-1" />
<input type="text" name="age-1" id="age-1" />
</div>
<!-- as of here, it's JS created -->
<div id="person-2" class="person">
<input type="text" name="name-2" id="name-2" />
<input type="text" name="age-2" id="age-2" />
</div>
<div id="person-3" class="person">
<input type="text" name="name-3" id="name-3" />
<input type="text" name="age-3" id="age-3" />
</div>
I already managed to write jquery-code that allows me to add the same elements once again with a new id (name-1, age-1, name-2, age-2, name-3, age-3, …).
Of course, Zend_Form does not know about name-2 and name-3, so it just drops them when the form contains an error and is displayed again. Neither can I access the value of name-2 with $form->getValue('name-2'). I have to go over raw $this->getRequest()->getPost().
Is there a better method I can use to combine Zend_Form and javascript-based added form elements (of same type like an hardcoded element).
Caveat: In the real problem, it’s select and not input. Found out this could make a difference (with ->setIsArray(true)), but using select would blow up the example code.
What you could do is create a subform container inside your main form and add an X amount of subforms to that container.
For example:
class My_Form extends Zend_Form
{
private $_numPersons = 1;
public function setNumPersons($numPersons)
{
$this->_numPersons = (int) $numPersons;
}
public function init()
{
$container = new Zend_Form_SubForm();
$this->addSubForm($container, 'persons');
for($index = 0; $index < $this->_numPersons; $index++) {
$personForm = new My_PersonForm();
$container->addSubForm($personForm, $index+1);
}
}
}
When rendered, the input fields will have names like persons[1][name]. Note the $index+1, Zend_Form does not allow a form to be named '0'.
Ofcourse, you should only use this method if the amount of person subforms is limited.
Another strategy would be to override the isValid method and use a single My_PersonForm form to validate all the person data.
Sidenote; the above code will only work when you define the numPersons as part of the options set, when creating the form instance. E.g.;
$form = new My_Form(array('numPersons' => 10));

Submit the value of a <p> element when an html form is submitted

I have this code: <p class = "foo">Text</p>
And I also have a form: <form action = "XXX.php" method = post></form>
However, how can I get the value of the <p> when I submit it, as the <p> element can be changed.
So what I mean is to be able to post the value of the <p> when the user submits the form, and to be able to access it from that php file with: $_POST['foo'];
Thanks, I have tried to be as clear as possible.
You have to use Javascript for that
A jQuery function that will work
$("form").submit(function(){
var value = $("p").html();
// If foo already exists
if( $("[name=foo]").length > 0 )
{
$("[name=foo]").val(value);
}
else
{
var input = $("<input />", { name : "foo",
value : value ,
type : "hidden" });
$(this).append(input);
}
});
Use
<input type="hidden" value="something" name="something" id="something" />
and when you change inner html of <p> change the value of hidden input.
I think your best bet is to make it an input with readonly enabled, and style to to look like a <p>. It's better then trying to add it to the POST parameters with JavaScript.
Here's a quick example. I bet it could still be improved with a few extra CSS quirks, experiment a bit.
The easiest thing to do is set the value of a hidden form field when you change the contents of your <p>.
Alternatively, you can get its contents and post with JavaScript.
For text you need to use input field:
<input type="text"/>
Form fields should must have an id:
<input type="text" id="pewpew" class="foo"/>
I would go with:
<input type="text" id="pewpew" class="foo" value="default text goes here"/>
OR
Go with different workarounds, like setting form's hidden elements on the fly, etc.
You can create hidden field on the fly and set its value on form submit. Like this:
<form id="form" action="/somewhere" method="post">
<p>Some text</p>
<input type="submit" value="Submit"/>
</form>
<script type="text/javascript">
var form = document.getElementById('form');
form.onsubmit = function()
{
var p = this.getElementsByTagName('p')[0];
if (!document.getElementById('pval'))
{
var pinput = document.createElement('input');
pinput.setAttribute('type', 'hidden');
pinput.setAttribute('id', 'pval');
pinput.setAttribute('name', 'p');
this.appendChild(pinput);
}
document.getElementById('pval').value = p.innerHTML;
return true;
}
</script>
Works, i've tested.