feat: add 'back/front end' in curriculum (#42596)

* chore: rename APIs and Microservices to include "Backend" (#42515)

* fix typo

* fix typo

* undo change

* Corrected grammar mistake

Corrected a grammar mistake by removing a comma.

* change APIs and Microservices cert title

* update title

* Change APIs and Microservices certi title

* Update translations.json

* update title

* feat(curriculum): rename apis and microservices cert

* rename folder structure

* rename certificate

* rename learn Markdown

* apis-and-microservices -> back-end-development-and-apis

* update backend meta

* update i18n langs and cypress test

Co-authored-by: Shaun Hamilton <shauhami020@gmail.com>

* fix: add development to front-end libraries (#42512)

* fix: added-the-word-Development-to-front-end-libraries

* fix/added-the-word-Development-to-front-end-libraries

* fix/added-word-development-to-front-end-libraries-in-other-related-files

* fix/added-the-word-Development-to-front-end-and-all-related-files

* fix/removed-typos-from-last-commit-in-index.md

* fix/reverted-changes-that-i-made-to-dependecies

* fix/removed xvfg

* fix/reverted changes that i made to package.json

* remove unwanted changes

* front-end-development-libraries changes

* rename backend certSlug and README

* update i18n folder names and keys

* test: add legacy path redirect tests

This uses serve.json from the client-config repo, since we currently use
that in production

* fix: create public dir before moving serve.json

* fix: add missing script

* refactor: collect redirect tests

* test: convert to cy.location for stricter tests

* rename certificate folder to 00-certificates

* change crowdin config to recognise new certificates location

* allow translations to be used

Co-authored-by: Nicholas Carrigan (he/him) <nhcarrigan@gmail.com>

* add forwards slashes to path redirects

* fix cypress path tests again

* plese cypress

* fix: test different challenge

Okay so I literally have no idea why this one particular challenge
fails in Cypress Firefox ONLY. Tom and I paired and spun a full build
instance and confirmed in Firefox the page loads and redirects as
expected. Changing to another bootstrap challenge passes Cypress firefox
locally. Absolutely boggled by this.

AAAAAAAAAAAAAAA

* fix: separate the test

Okay apparently the test does not work unless we separate it into
a different `it` statement.

>:( >:( >:( >:(

Co-authored-by: Sujal Gupta <55016909+heysujal@users.noreply.github.com>
Co-authored-by: Noor Fakhry <65724923+NoorFakhry@users.noreply.github.com>
Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com>
Co-authored-by: Nicholas Carrigan (he/him) <nhcarrigan@gmail.com>
This commit is contained in:
Shaun Hamilton
2021-08-14 03:57:13 +01:00
committed by GitHub
parent 4df2a0c542
commit c2a11ad00d
1215 changed files with 790 additions and 449 deletions

View File

@ -0,0 +1,90 @@
---
id: bad87fee1348bd9aec908849
title: Agregar elementos dentro de tus recursos de Bootstrap
challengeType: 0
forumTopicId: 16636
dashedName: add-elements-within-your-bootstrap-wells
---
# --description--
Ahora estamos a varios elementos `div` de profundidad en cada columna de nuestras filas. Esto es todo lo que necesitamos. Ahora podemos agregar nuestros elementos `button`.
Anida tres elementos `button` dentro de cada uno de los elementos `div` que posean una clase `well`.
# --hints--
Tres elementos `button` deberían ser insertados dentro de cada uno de los elementos `div` con la clase `well`.
```js
assert(
$('div.well:eq(0)').children('button').length === 3 &&
$('div.well:eq(1)').children('button').length === 3
);
```
Debería haber un total de 6 elementos `button`.
```js
assert($('button') && $('button').length > 5);
```
Todos los elementos `button` deben tener etiqueta de cierre.
```js
assert(
code.match(/<\/button>/g) &&
code.match(/<button/g) &&
code.match(/<\/button>/g).length === code.match(/<button/g).length
);
```
# --seed--
## --seed-contents--
```html
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<div class="well">
</div>
</div>
<div class="col-xs-6">
<div class="well">
</div>
</div>
</div>
</div>
```
# --solutions--
```html
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<div class="well">
<button></button>
<button></button>
<button></button>
</div>
</div>
<div class="col-xs-6">
<div class="well">
<button></button>
<button></button>
<button></button>
</div>
</div>
</div>
</div>
```

View File

@ -0,0 +1,178 @@
---
id: bad87fee1348bd9aedc08845
title: Agrega íconos Font Awesome a todos nuestros botones
challengeType: 0
forumTopicId: 16637
required:
-
link: 'https://use.fontawesome.com/releases/v5.8.1/css/all.css'
raw: true
dashedName: add-font-awesome-icons-to-all-of-our-buttons
---
# --description--
Font Awesome es una conveniente librería de íconos. Éstos pueden ser fuentes de la web o gráficos vectoriales. Los iconos son tratados como fuentes. Puedes especificar su tamaño usando píxeles y ellos asumirán el tamaño de fuente de su elemento HTML padre.
# --instructions--
Utiliza Font Awesome para agregar un icono `info-circle` a su botón info y un icono `trash` al botón borrar.
** Nota: **El elemento `span` es una alternativa aceptable al elemento `i` para las direcciones a continuación.
# --hints--
Deberías añadir un elemento `<i class="fas fa-info-circle"></i>` dentro de tu elemento `info` botón info.
```js
assert(
$('.btn-info > i').is('.fas.fa-info-circle') ||
$('.btn-info > span').is('.fas.fa-info-circle')
);
```
Deberías añadir un elemento `<i class="fas fa-trash"></i>` dentro de tu elemento botón `delete`.
```js
assert(
$('.btn-danger > i').is('.fas.fa-trash') ||
$('.btn-danger > span').is('.fas.fa-trash')
);
```
Cada uno de tus elementos `i` debe tener una etiqueta de cierre y `<i class="fas fa-thumbs-up"></i>` tiene que estar en el botón `like`.
```js
assert(
code.match(/<\/i>|<\/span/g) &&
code.match(/<\/i|<\/span>/g).length > 2 &&
($('.btn-primary > i').is('.fas.fa-thumbs-up') ||
$('.btn-primary > span').is('.fas.fa-thumbs-up'))
);
```
# --seed--
## --seed-contents--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
h2 {
font-family: Lobster, Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
</style>
<div class="container-fluid">
<div class="row">
<div class="col-xs-8">
<h2 class="text-primary text-center">CatPhotoApp</h2>
</div>
<div class="col-xs-4">
<a href="#"><img class="img-responsive thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
</div>
</div>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<div class="row">
<div class="col-xs-4">
<button class="btn btn-block btn-primary"><i class="fas fa-thumbs-up"></i> Like</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-info">Info</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-danger">Delete</button>
</div>
</div>
<p>Things cats <span class="text-danger">love:</span></p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```
# --solutions--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
h2 {
font-family: Lobster, Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
</style>
<div class="container-fluid">
<div class="row">
<div class="col-xs-8">
<h2 class="text-primary text-center">CatPhotoApp</h2>
</div>
<div class="col-xs-4">
<a href="#"><img class="img-responsive thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
</div>
</div>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<div class="row">
<div class="col-xs-4">
<button class="btn btn-block btn-primary"><i class="fas fa-thumbs-up"></i> Like</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-info"><i class="fas fa-info-circle"></i> Info</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-danger"><i class="fas fa-trash"></i> Delete</button>
</div>
</div>
<p>Things cats <span class="text-danger">love:</span></p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```

View File

@ -0,0 +1,195 @@
---
id: bad87fee1348bd9aedd08845
title: Agrega íconos Font Awesome a nuestros botones
challengeType: 0
forumTopicId: 16638
required:
-
link: 'https://use.fontawesome.com/releases/v5.8.1/css/all.css'
raw: true
dashedName: add-font-awesome-icons-to-our-buttons
---
# --description--
Font Awesome es una librería de iconos muy conveniente. Estos iconos pueden ser fuentes web o gráficos vectoriales. Estos iconos son tratados como fuentes. Puedes especificar su tamaño usando píxeles, y ellos asumirán el tamaño de fuente de su elemento HTML padre.
Puedes incluir Font Awesome en cualquier aplicación agregando el siguiente código al principio de tu HTML:
```html
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.1/css/all.css" integrity="sha384-50oBUHEmvpQ+1lW4y57PTFmhCaXp0ML5d60M1M7uH2+nqUivzIebhndOJK28anvf" crossorigin="anonymous">
```
En este caso, ya lo hemos añadido por ti en esta página detrás de escena.
El elemento `i` fue originalmente usado para crear elementos itálicos, pero ahora es comúnmente usado para iconos. Puedes añadir las clases de Font Awesome al elemento `i` para convertirlo en un icono, por ejemplo:
```html
<i class="fas fa-info-circle"></i>
```
Ten en cuenta que el elemento `span` también es aceptable para usar con íconos.
# --instructions--
Usa Font Awesome para añadir un icono `thumbs-up` a tu botón de "Like", dandole un elemento `i` con las clases `fas` y `fa-thumbs-up`. Asegúrate de dejar el texto `Like` junto al icono.
# --hints--
Debes añadir un elemento `i` con las clases `fas` y `fa-thumbs-up`.
```js
assert($('i').is('.fas.fa-thumbs-up') || $('span').is('.fas.fa-thumbs-up'));
```
Tu ícono `fa-thumbs-up` debe estar ubicado dentro del botón "Like".
```js
assert(
($('i.fa-thumbs-up').parent().text().match(/Like/gi) &&
$('.btn-primary > i').is('.fas.fa-thumbs-up')) ||
($('span.fa-thumbs-up').parent().text().match(/Like/gi) &&
$('.btn-primary > span').is('.fas.fa-thumbs-up'))
);
```
Tu elemento `i` debe estar anidado dentro de tu elemento `button`.
```js
assert(
$('button').children('i').length > 0 ||
$('button').children('span').length > 0
);
```
Tu ícono debe tener una etiqueta de cierre.
```js
assert(code.match(/<\/i>|<\/span>/g));
```
# --seed--
## --seed-contents--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
h2 {
font-family: Lobster, Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
</style>
<div class="container-fluid">
<div class="row">
<div class="col-xs-8">
<h2 class="text-primary text-center">CatPhotoApp</h2>
</div>
<div class="col-xs-4">
<a href="#"><img class="img-responsive thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
</div>
</div>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<div class="row">
<div class="col-xs-4">
<button class="btn btn-block btn-primary">Like</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-info">Info</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-danger">Delete</button>
</div>
</div>
<p>Things cats <span class="text-danger">love:</span></p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```
# --solutions--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
h2 {
font-family: Lobster, Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
</style>
<div class="container-fluid">
<div class="row">
<div class="col-xs-8">
<h2 class="text-primary text-center">CatPhotoApp</h2>
</div>
<div class="col-xs-4">
<a href="#"><img class="img-responsive thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
</div>
</div>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<div class="row">
<div class="col-xs-4">
<button class="btn btn-block btn-primary"><i class="fas fa-thumbs-up"></i> Like</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-info">Info</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-danger">Delete</button>
</div>
</div>
<p>Things cats <span class="text-danger">love:</span></p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```

View File

@ -0,0 +1,93 @@
---
id: bad87fee1348bd9aec908853
title: Añade atributos de identificación a elementos de Bootstrap
challengeType: 0
forumTopicId: 16639
dashedName: add-id-attributes-to-bootstrap-elements
---
# --description--
Recuerda que además de los atributos de clase, puedes darle a cada elemento un atributo `id`.
Cada "id" debe ser único en un elemento específico y utilizarse una sola vez por página.
Vamos a dar un "id" único a cada uno de nuestros elementos `div` de la clase `well`.
Recuerda que puedes dar un "id" a un elemento de esta manera:
```html
<div class="well" id="center-well">
```
Agrega el "id" `left-well` al elemento de la izquierda de la clase "well". Agrega el "id" `right-well` al elemento de la derecha de la clase "well".
# --hints--
El elemento de la izquierda de la clase `well` deberá tener el "id" `left-well`.
```js
assert(
$('.col-xs-6').children('#left-well') &&
$('.col-xs-6').children('#left-well').length > 0
);
```
El elemento de la derecha de la clase `well` deberá tener el "id" `right-well`.
```js
assert(
$('.col-xs-6').children('#right-well') &&
$('.col-xs-6').children('#right-well').length > 0
);
```
# --seed--
## --seed-contents--
```html
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<div class="well">
<button class="btn btn-default target"></button>
<button class="btn btn-default target"></button>
<button class="btn btn-default target"></button>
</div>
</div>
<div class="col-xs-6">
<div class="well">
<button class="btn btn-default target"></button>
<button class="btn btn-default target"></button>
<button class="btn btn-default target"></button>
</div>
</div>
</div>
</div>
```
# --solutions--
```html
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<div class="well" id="left-well">
<button class="btn btn-default target"></button>
<button class="btn btn-default target"></button>
<button class="btn btn-default target"></button>
</div>
</div>
<div class="col-xs-6">
<div class="well" id="right-well">
<button class="btn btn-default target"></button>
<button class="btn btn-default target"></button>
<button class="btn btn-default target"></button>
</div>
</div>
</div>
</div>
```

View File

@ -0,0 +1,77 @@
---
id: bad87fee1348bd9aec908850
title: Aplica el estilo predeterminado de botón de estilo de Bootstrap
challengeType: 0
forumTopicId: 16657
dashedName: apply-the-default-bootstrap-button-style
---
# --description--
Bootstrap tiene otra clase de botón llamada `btn-default`.
Aplica las clases `btn` y `btn-default` a cada uno de tus elementos `button`.
# --hints--
Debes aplicar la clase `btn` a cada uno de tus elementos `button`.
```js
assert($('.btn').length > 5);
```
Debes aplicar la clase `btn-default` a cada uno de tus elementos `button`.
```js
assert($('.btn-default').length > 5);
```
# --seed--
## --seed-contents--
```html
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<div class="well">
<button></button>
<button></button>
<button></button>
</div>
</div>
<div class="col-xs-6">
<div class="well">
<button></button>
<button></button>
<button></button>
</div>
</div>
</div>
</div>
```
# --solutions--
```html
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<div class="well">
<button class="btn btn-default"></button>
<button class="btn btn-default"></button>
<button class="btn btn-default"></button>
</div>
</div>
<div class="col-xs-6">
<div class="well">
<button class="btn btn-default"></button>
<button class="btn btn-default"></button>
<button class="btn btn-default"></button>
</div>
</div>
</div>
</div>
```

View File

@ -0,0 +1,175 @@
---
id: bad87fee1348cd8acef08813
title: Llama a acciones opcionales con btn-info
challengeType: 0
forumTopicId: 16770
dashedName: call-out-optional-actions-with-btn-info
---
# --description--
Bootstrap viene con diferentes colores predefinidos para botones. La clase `btn-info` se utiliza para llamar la atención sobre las acciones opcionales que el usuario puede realizar.
Crea un nuevo botón de Bootstrap debajo de tu botón `Like` con el texto `Info` y añádele las clases de Bootstrap `btn-info` y `btn-block`.
Ten en cuenta que estos botones todavía necesitan las clases `btn` y `btn-block`.
# --hints--
Debes crear un nuevo elemento `button` con el texto `Info`.
```js
assert(new RegExp('info', 'gi').test($('button').text()));
```
Ambos botones deben tener las clases `btn` y `btn-block`.
```js
assert($('button.btn-block.btn').length > 1);
```
Tu nuevo botón debe tener la clase `btn-info`.
```js
assert($('button').hasClass('btn-info'));
```
Todos los elementos `button` deben tener etiquetas de cierre.
```js
assert(
code.match(/<\/button>/g) &&
code.match(/<button/g) &&
code.match(/<\/button>/g).length === code.match(/<button/g).length
);
```
# --seed--
## --seed-contents--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
.red-text {
color: red;
}
h2 {
font-family: Lobster, Monospace;
}
p {
font-size: 16px;
font-family: Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
.smaller-image {
width: 100px;
}
</style>
<div class="container-fluid">
<h2 class="red-text text-center">CatPhotoApp</h2>
<p>Click here for <a href="#">cat photos</a>.</p>
<a href="#"><img class="smaller-image thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<button class="btn btn-block btn-primary">Like</button>
<p>Things cats love:</p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```
# --solutions--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
.red-text {
color: red;
}
h2 {
font-family: Lobster, Monospace;
}
p {
font-size: 16px;
font-family: Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
.smaller-image {
width: 100px;
}
</style>
<div class="container-fluid">
<h2 class="red-text text-center">CatPhotoApp</h2>
<p>Click here for <a href="#">cat photos</a>.</p>
<a href="#"><img class="smaller-image thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<button class="btn btn-block btn-primary">Like</button>
<button class="btn btn-block btn-info">Info</button>
<p>Things cats love:</p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```

View File

@ -0,0 +1,157 @@
---
id: bad87fee1348bd8acde08812
title: Centrar texto con Bootstrap
challengeType: 0
forumTopicId: 16771
dashedName: center-text-with-bootstrap
---
# --description--
Ahora que estamos usando Bootstrap, podemos centrar el encabezado para que luzca mejor. Todo lo que necesitamos hacer es añadir la clase `text-center` a nuestro elemento `h2`.
Recuerda que puedes añadir varias clases al mismo elemento separando cada una de ellas con un espacio:
```html
<h2 class="red-text text-center">your text</h2>
```
# --hints--
Tu elemento `h2` debe estar centrado al aplicar la clase `text-center`
```js
assert($('h2').hasClass('text-center'));
```
Tu elemento `h2` debe conservar la clase `red-text`
```js
assert($('h2').hasClass('red-text'));
```
# --seed--
## --seed-contents--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
.red-text {
color: red;
}
h2 {
font-family: Lobster, Monospace;
}
p {
font-size: 16px;
font-family: Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
.smaller-image {
width: 100px;
}
</style>
<div class="container-fluid">
<h2 class="red-text">CatPhotoApp</h2>
<p>Click here for <a href="#">cat photos</a>.</p>
<a href="#"><img class="smaller-image thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<p>Things cats love:</p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```
# --solutions--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
.red-text {
color: red;
}
h2 {
font-family: Lobster, Monospace;
}
p {
font-size: 16px;
font-family: Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
.smaller-image {
width: 100px;
}
</style>
<div class="container-fluid">
<h2 class="red-text text-center">CatPhotoApp</h2>
<p>Click here for <a href="#">cat photos</a>.</p>
<a href="#"><img class="smaller-image thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<p>Things cats love:</p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```

View File

@ -0,0 +1,185 @@
---
id: bad87fee1348cd8acef08812
title: Crear un botón Bootstrap como elemento de bloque
challengeType: 0
forumTopicId: 16810
dashedName: create-a-block-element-bootstrap-button
---
# --description--
Normalmente, tus elementos `button` con las clases `btn` y `btn-default` son tan anchos como el texto que contienen. Por ejemplo:
```html
<button class="btn btn-default">Submit</button>
```
Este botón será tan ancho como la palabra `Submit`.
<button class='btn btn-default'>Enviar</button>
Al hacer los elementos de bloque con la clase `btn-block`, tu botón ocupará el ancho horizontal de la página y cualquier elemento que lo siga se posicionará en una "nueva línea" debajo del bloque.
```html
<button class="btn btn-default btn-block">Submit</button>
```
Este botón tomará el 100% del ancho disponible.
<button class='btn btn-default btn-block'>Enviar</button>
Ten en cuenta que estos botones aún necesitan la clase `btn`.
Añade la clase `btn-block` a tu botón Bootstrap.
# --hints--
Tu botón debe contener las clases `btn` y `btn-default`.
```js
assert($('button').hasClass('btn') && $('button').hasClass('btn-default'));
```
Tu botón debe contener la clase `btn-block`.
```js
assert($('button').hasClass('btn-block'));
```
Todos tu elementos `button` deben tener etiquetas de cierre.
```js
assert(
code.match(/<\/button>/g) &&
code.match(/<button/g) &&
code.match(/<\/button>/g).length === code.match(/<button/g).length
);
```
# --seed--
## --seed-contents--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
.red-text {
color: red;
}
h2 {
font-family: Lobster, Monospace;
}
p {
font-size: 16px;
font-family: Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
.smaller-image {
width: 100px;
}
</style>
<div class="container-fluid">
<h2 class="red-text text-center">CatPhotoApp</h2>
<p>Click here for <a href="#">cat photos</a>.</p>
<a href="#"><img class="smaller-image thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<button class="btn btn-default">Like</button>
<p>Things cats love:</p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```
# --solutions--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
.red-text {
color: red;
}
h2 {
font-family: Lobster, Monospace;
}
p {
font-size: 16px;
font-family: Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
.smaller-image {
width: 100px;
}
</style>
<div class="container-fluid">
<h2 class="red-text text-center">CatPhotoApp</h2>
<p>Click here for <a href="#">cat photos</a>.</p>
<a href="#"><img class="smaller-image thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<button class="btn btn-block btn-default">Like</button>
<p>Things cats love:</p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```

View File

@ -0,0 +1,176 @@
---
id: bad87fee1348cd8acdf08812
title: Crea un botón de Bootstrap
challengeType: 0
forumTopicId: 16811
dashedName: create-a-bootstrap-button
---
# --description--
Bootstrap tiene sus propios estilos para los elementos `button`, que se ven mucho mejor que el HTML plano.
Crea un nuevo elemento `button` debajo de la foto grande de tu gatito. Dale las clases `btn` y `btn-default`, así como el texto de `Like`.
# --hints--
Debes crear un nuevo elemento `button` con el texto `Like`.
```js
assert(
new RegExp('like', 'gi').test($('button').text()) &&
$('img.img-responsive + button.btn').length > 0
);
```
Tu nuevo botón debería tener dos clases: `btn` y `btn-default`.
```js
assert($('button').hasClass('btn') && $('button').hasClass('btn-default'));
```
Todos los elementos `button` deben tener etiquetas de cierre.
```js
assert(
code.match(/<\/button>/g) &&
code.match(/<button/g) &&
code.match(/<\/button>/g).length === code.match(/<button/g).length
);
```
# --seed--
## --seed-contents--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
.red-text {
color: red;
}
h2 {
font-family: Lobster, Monospace;
}
p {
font-size: 16px;
font-family: Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
.smaller-image {
width: 100px;
}
</style>
<div class="container-fluid">
<h2 class="red-text text-center">CatPhotoApp</h2>
<p>Click here for <a href="#">cat photos</a>.</p>
<a href="#"><img class="smaller-image thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<p>Things cats love:</p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```
# --solutions--
```html
<html>
<head>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
.red-text {
color: red;
}
h2 {
font-family: Lobster, Monospace;
}
p {
font-size: 16px;
font-family: Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
.smaller-image {
width: 100px;
}
</style>
</head>
<body>
<div class="container-fluid">
<h2 class="red-text text-center">CatPhotoApp</h2>
<p>Click here for <a href="#">cat photos</a>.</p>
<a href="#"><img class="smaller-image thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<!-- ADD Bootstrap Styled Button -->
<button class="btn btn-default">Like</button>
<p>Things cats love:</p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
</html>
```

View File

@ -0,0 +1,67 @@
---
id: bad87fee1348bd9aec908846
title: Crear un encabezado de Bootstrap
challengeType: 0
forumTopicId: 16812
dashedName: create-a-bootstrap-headline
---
# --description--
Ahora construyamos algo desde cero para practicar nuestras habilidades de HTML, CSS y Bootstrap.
Construiremos una zona de juegos de jQuery, que pronto utilizaremos en nuestros desafíos de jQuery.
Para empezar, crea un elemento `h3`, con el texto `jQuery Playground`.
Colorea tu elemento `h3` con la clase `text-primary` y céntralo con la clase `text-center` de Bootstrap.
# --hints--
Debes agregar un elemento `h3` a tu página.
```js
assert($('h3') && $('h3').length > 0);
```
Tu elemento `h3` debe tener una etiqueta de cierre.
```js
assert(
code.match(/<\/h3>/g) &&
code.match(/<h3/g) &&
code.match(/<\/h3>/g).length === code.match(/<h3/g).length
);
```
Tu elemento `h3` debe estar coloreado al aplicar la clase `text-primary`
```js
assert($('h3').hasClass('text-primary'));
```
Tu elemento `h3` debe estar centrado al aplicar la clase `text-center`
```js
assert($('h3').hasClass('text-center'));
```
Tu elemento `h3` debe tener el texto `jQuery Playground`.
```js
assert.isTrue(/jquery(\s)+playground/gi.test($('h3').text()));
```
# --seed--
## --seed-contents--
```html
```
# --solutions--
```html
<h3 class="text-primary text-center">jQuery Playground</h3>
```

View File

@ -0,0 +1,68 @@
---
id: bad87fee1348bd9bec908846
title: Crea una fila Bootstrap
challengeType: 0
forumTopicId: 16813
dashedName: create-a-bootstrap-row
---
# --description--
Ahora crearemos una fila Bootstrap para nuestros elementos en línea.
Crea un elemento `div` debajo de la etiqueta `h3`, con la clase `row`.
# --hints--
Debes agregar un elemento `div` debajo de tu elemento `h3`.
```js
assert(
$('div').length > 1 &&
$('div.row h3.text-primary').length == 0 &&
$('div.row + h3.text-primary').length == 0 &&
$('h3.text-primary + div.row').length > 0
);
```
Tu elemento `div` debe tener la clase `row`
```js
assert($('div').hasClass('row'));
```
Tu `row div` debe estar anidado dentro de `container-fluid div`
```js
assert($('div.container-fluid div.row').length > 0);
```
Tu elemento `div` debe tener una etiqueta de cierre.
```js
assert(
code.match(/<\/div>/g) &&
code.match(/<div/g) &&
code.match(/<\/div>/g).length === code.match(/<div/g).length
);
```
# --seed--
## --seed-contents--
```html
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
</div>
```
# --solutions--
```html
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row"></div>
</div>
```

View File

@ -0,0 +1,71 @@
---
id: bad87fee1348bd9aec908852
title: Crear una clase para apuntar con selectores jQuery
challengeType: 0
forumTopicId: 16815
dashedName: create-a-class-to-target-with-jquery-selectors
---
# --description--
No todas las clases necesitan tener CSS correspondientes. A veces creamos clases sólo con el propósito de seleccionar aquellos elementos más fácilmente utilizando jQuery.
Dale a cada uno de tus elementos `button` la clase `target`.
# --hints--
Debes aplicar la clase `target` a cada uno de tus elementos `button`.
```js
assert($('.target').length > 5);
```
# --seed--
## --seed-contents--
```html
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<div class="well">
<button class="btn btn-default"></button>
<button class="btn btn-default"></button>
<button class="btn btn-default"></button>
</div>
</div>
<div class="col-xs-6">
<div class="well">
<button class="btn btn-default"></button>
<button class="btn btn-default"></button>
<button class="btn btn-default"></button>
</div>
</div>
</div>
</div>
```
# --solutions--
```html
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<div class="well">
<button class="target btn btn-default"></button>
<button class="target btn btn-default"></button>
<button class="target btn btn-default"></button>
</div>
</div>
<div class="col-xs-6">
<div class="well">
<button class="target btn btn-default"></button>
<button class="target btn btn-default"></button>
<button class="target btn btn-default"></button>
</div>
</div>
</div>
</div>
```

View File

@ -0,0 +1,177 @@
---
id: bad87fee1348bd9aede08845
title: Crea un título personalizado
challengeType: 0
forumTopicId: 16816
dashedName: create-a-custom-heading
---
# --description--
Crearemos un título simple para nuestra aplicación de fotos de gatos colocando el título y la imagen relajante del gato en la misma fila.
Recuerda, Bootstrap usa un sistema de cuadrícula responsivo, lo que facilita la colocación de elementos en filas y especifica el ancho relativo de cada elemento. La mayoría de las clases de Bootstrap se pueden aplicar a un elemento `div`.
Anida tu primera imagen y tu elemento `h2` dentro de un solo elemento `<div class="row">`. Anida tu elemento `h2` dentro de un `<div class="col-xs-8">` y tu imagen en un `<div class="col-xs-4">` de modo que estén en la misma línea.
¿Observas cómo la imagen ahora tiene el tamaño adecuado para ajustarse al texto?
# --hints--
Tu elemento `h2` y el elemento superior `img` deben estar anidados juntos dentro de un elemento `div` con la clase `row`.
```js
assert($('div.row:has(h2)').length > 0 && $('div.row:has(img)').length > 0);
```
Tu elemento superior `img` debe estar anidado dentro de un `div` con la clase `col-xs-4`.
```js
assert(
$('div.col-xs-4:has(img)').length > 0 &&
$('div.col-xs-4:has(div)').length === 0
);
```
Tu elemento `h2` debe estar anidado dentro de un `div` con la clase `col-xs-8`.
```js
assert(
$('div.col-xs-8:has(h2)').length > 0 &&
$('div.col-xs-8:has(div)').length === 0
);
```
Todos tus elementos `div` deben tener etiquetas de cierre.
```js
assert(
code.match(/<\/div>/g) &&
code.match(/<div/g) &&
code.match(/<\/div>/g).length === code.match(/<div/g).length
);
```
# --seed--
## --seed-contents--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
h2 {
font-family: Lobster, Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
</style>
<div class="container-fluid">
<h2 class="text-primary text-center">CatPhotoApp</h2>
<a href="#"><img class="img-responsive thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<div class="row">
<div class="col-xs-4">
<button class="btn btn-block btn-primary">Like</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-info">Info</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-danger">Delete</button>
</div>
</div>
<p>Things cats <span class="text-danger">love:</span></p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```
# --solutions--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
h2 {
font-family: Lobster, Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
</style>
<div class="container-fluid">
<div class="row">
<div class="col-xs-8">
<h2 class="text-primary text-center">CatPhotoApp</h2>
</div>
<div class="col-xs-4">
<a href="#"><img class="img-responsive thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
</div>
</div>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<div class="row">
<div class="col-xs-4">
<button class="btn btn-block btn-primary">Like</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-info">Info</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-danger">Delete</button>
</div>
</div>
<p>Things cats <span class="text-danger">love:</span></p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```

View File

@ -0,0 +1,71 @@
---
id: bad87fee1348bd9aec908848
title: Crea Bootstrap wells
challengeType: 0
forumTopicId: 16825
dashedName: create-bootstrap-wells
---
# --description--
Bootstrap tiene una clase llamada `well` (pozo) que puede crear una ilusión visual de profundidad en las columnas.
Incrusta un elemento `div` con la clase `well` en cada uno de los elementos `col-xs-6` y `div`.
# --hints--
Debes añadir un elemento `div` con la clase `well` dentro de cada uno de los elementos `div` con la clase `col-xs-6`
```js
assert($('div.col-xs-6').not(':has(>div.well)').length < 1);
```
Ambos elementos `div` con la clase `col-xs-6` deben estar incrustados en el elemento `div` con la clase `row`.
```js
assert($('div.row > div.col-xs-6').length > 1);
```
Todos los elementos `div` deben tener etiquetas de cierre.
```js
assert(
code.match(/<\/div>/g) &&
code.match(/<div/g) &&
code.match(/<\/div>/g).length === code.match(/<div/g).length
);
```
# --seed--
## --seed-contents--
```html
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
</div>
<div class="col-xs-6">
</div>
</div>
</div>
```
# --solutions--
```html
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<div class="well"></div>
</div>
<div class="col-xs-6">
<div class="well"></div>
</div>
</div>
</div>
```

View File

@ -0,0 +1,188 @@
---
id: bad87fee1347bd9aedf08845
title: Deja de lado el CSS personalizado por Bootstrap
challengeType: 0
forumTopicId: 17565
dashedName: ditch-custom-css-for-bootstrap
---
# --description--
Podemos limpiar nuestro código y hacer que nuestra aplicación de fotos de gatos parezca más convencional, usando los estilos incorporados de Bootstrap, en lugar de los estilos personalizados que hemos creado antes.
No te preocupes tendremos mucho tiempo para personalizar nuestro CSS más adelante.
Elimina las declaraciones de CSS `.red-text`, `p` y `.smaller-image` de tu elemento `style` para que la únicas declaraciones que queden en tu elemento `style` sean `h2` y `thick-green-border`.
A continuación elimina el elemento `p` que contiene un enlace roto. Después elimina la clase `red-text` de tu elemento `h2` y reemplázala con la clase `text-primary` de Bootstrap.
Finalmente elimina la clase `smaller-image` de tu primer elemento `img` y reemplázala con la clase `img-responsive`.
# --hints--
Tu elemento `h2` ya no debería incluir la clase `red-text`.
```js
assert(!$('h2').hasClass('red-text'));
```
Tu elemento `h2` ahora debe incluir la clase `text-primary`.
```js
assert($('h2').hasClass('text-primary'));
```
Tus elementos p (párrafo) ya no deben usar la fuente `Monospace`.
```js
assert(
!$('p')
.css('font-family')
.match(/monospace/i)
);
```
La clase `smaller-image` debe ser eliminada de tu imagen superior.
```js
assert(!$('img').hasClass('smaller-image'));
```
Debes añadir la clase `img-responsive` a tu imagen superior.
```js
assert($('.img-responsive').length > 1);
```
# --seed--
## --seed-contents--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
.red-text {
color: red;
}
h2 {
font-family: Lobster, Monospace;
}
p {
font-size: 16px;
font-family: Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
.smaller-image {
width: 100px;
}
</style>
<div class="container-fluid">
<h2 class="red-text text-center">CatPhotoApp</h2>
<p>Click here for <a href="#">cat photos</a>.</p>
<a href="#"><img class="smaller-image thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<div class="row">
<div class="col-xs-4">
<button class="btn btn-block btn-primary">Like</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-info">Info</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-danger">Delete</button>
</div>
</div>
<p>Things cats love:</p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```
# --solutions--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
h2 {
font-family: Lobster, Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
</style>
<div class="container-fluid">
<h2 class="text-primary text-center">CatPhotoApp</h2>
<a href="#"><img class="img-responsive thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<div class="row">
<div class="col-xs-4">
<button class="btn btn-block btn-primary">Like</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-info">Info</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-danger">Delete</button>
</div>
</div>
<p>Things cats love:</p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```

View File

@ -0,0 +1,125 @@
---
id: bad87fee1348bd9aec908855
title: Dale a cada elemento un id único
challengeType: 0
forumTopicId: 18191
dashedName: give-each-element-a-unique-id
---
# --description--
También vamos a querer poder usar jQuery para apuntar a cada botón por su identificación única.
Da a cada uno de tus botones un id único, empezando con `target1` y terminando con `target6`.
Asegúrate de que `target1` a `target3` estén en `#left-well`, y `target4` a `target6` están en `#right-well`.
# --hints--
Un elemento `button` debe tener el id `target1`.
```js
assert(
$('#left-well').children('#target1') &&
$('#left-well').children('#target1').length > 0
);
```
Un elemento `button` debe tener el id `target2`.
```js
assert(
$('#left-well').children('#target2') &&
$('#left-well').children('#target2').length > 0
);
```
Un elemento `button` debe tener el id `target3`.
```js
assert(
$('#left-well').children('#target3') &&
$('#left-well').children('#target3').length > 0
);
```
Un elemento `button` debe tener el id `target4`.
```js
assert(
$('#right-well').children('#target4') &&
$('#right-well').children('#target4').length > 0
);
```
Un elemento `button` debe tener el id `target5`.
```js
assert(
$('#right-well').children('#target5') &&
$('#right-well').children('#target5').length > 0
);
```
Un elemento `button` debe tener el id `target6`.
```js
assert(
$('#right-well').children('#target6') &&
$('#right-well').children('#target6').length > 0
);
```
# --seed--
## --seed-contents--
```html
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target"></button>
<button class="btn btn-default target"></button>
<button class="btn btn-default target"></button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target"></button>
<button class="btn btn-default target"></button>
<button class="btn btn-default target"></button>
</div>
</div>
</div>
</div>
```
# --solutions--
```html
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1"></button>
<button class="btn btn-default target" id="target2"></button>
<button class="btn btn-default target" id="target3"></button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4"></button>
<button class="btn btn-default target" id="target5"></button>
<button class="btn btn-default target" id="target6"></button>
</div>
</div>
</div>
</div>
```

View File

@ -0,0 +1,53 @@
---
id: bad87fee1348bd9aec908746
title: Aloja nuestra página dentro de un container-fluid de Bootstrap div
challengeType: 0
forumTopicId: 18198
dashedName: house-our-page-within-a-bootstrap-container-fluid-div
---
# --description--
Ahora asegurémonos de que todo el contenido de tu página sea compatible con dispositivos móviles.
Anidemos tu elemento `h3` dentro de un elemento `div` con la clase `container-fluid`.
# --hints--
Tu elemento `div` debe tener la clase `container-fluid`.
```js
assert($('div').hasClass('container-fluid'));
```
Cada uno de tus elementos `div` deben tener etiquetas de cierre.
```js
assert(
code.match(/<\/div>/g) &&
code.match(/<div/g) &&
code.match(/<\/div>/g).length === code.match(/<div/g).length
);
```
Tu elemento `h3` debe estar anidado dentro de un elemento `div`.
```js
assert($('div').children('h3').length > 0);
```
# --seed--
## --seed-contents--
```html
<h3 class="text-primary text-center">jQuery Playground</h3>
```
# --solutions--
```html
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
</div>
```

View File

@ -0,0 +1,105 @@
---
id: bad87fee1348bd9aec908856
title: Etiquetas Buttons Bootstrap
challengeType: 0
forumTopicId: 18222
dashedName: label-bootstrap-buttons
---
# --description--
Al igual que hemos etiquetado nuestros "wells", queremos etiquetar nuestros botones.
Da a cada uno de tus elementos `button` el texto que corresponde a su id.
# --hints--
Tu elemento `button` con el id `target1` debe tener el texto `#target1`.
```js
assert(new RegExp('#target1', 'gi').test($('#target1').text()));
```
Tu elemento `button` con el id `target2` debe tener el texto `#target2`.
```js
assert(new RegExp('#target2', 'gi').test($('#target2').text()));
```
Tu elemento `button` con el id `target3` debe tener el texto `#target3`.
```js
assert(new RegExp('#target3', 'gi').test($('#target3').text()));
```
Tu elemento `button` con el id `target4` debe tener el texto `#target4`.
```js
assert(new RegExp('#target4', 'gi').test($('#target4').text()));
```
Tu elemento `button` con el id `target5` debe tener el texto `#target5`.
```js
assert(new RegExp('#target5', 'gi').test($('#target5').text()));
```
Tu elemento `button` con el id `target6` debe tener el texto `#target6`.
```js
assert(new RegExp('#target6', 'gi').test($('#target6').text()));
```
# --seed--
## --seed-contents--
```html
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1"></button>
<button class="btn btn-default target" id="target2"></button>
<button class="btn btn-default target" id="target3"></button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4"></button>
<button class="btn btn-default target" id="target5"></button>
<button class="btn btn-default target" id="target6"></button>
</div>
</div>
</div>
</div>
```
# --solutions--
```html
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```

View File

@ -0,0 +1,101 @@
---
id: bad87fee1348bd9aec908854
title: Etiqueta Wells de Boostrap
challengeType: 0
forumTopicId: 18223
dashedName: label-bootstrap-wells
---
# --description--
Para mayor claridad, etiquetemos ambos `well` con sus ids.
Sobre tu "left-well", dentro del elemento `col-xs-6` `div`, agrega el elemento `h4` con el texto `#left-well`.
Sobre tu "right-well", dentro del elemento `col-xs-6` `div`, agrega el elemento `h4` con el texto `#right-well`.
# --hints--
Debes añadir un elemento `h4` para cada uno de sus elementos `<div class="col-xs-6">`.
```js
assert(
$('.col-xs-6').children('h4') && $('.col-xs-6').children('h4').length > 1
);
```
Un elemento `h4` debe contener el texto `#left-well`.
```js
assert(new RegExp('#left-well', 'gi').test($('h4').text()));
```
Un elemento `h4` debe contener el texto `#right-well`.
```js
assert(new RegExp('#right-well', 'gi').test($('h4').text()));
```
Todos los elementos `h4` deben contener etiquetas de cierre.
```js
assert(
code.match(/<\/h4>/g) &&
code.match(/<h4/g) &&
code.match(/<\/h4>/g).length === code.match(/<h4/g).length
);
```
# --seed--
## --seed-contents--
```html
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<div class="well" id="left-well">
<button class="btn btn-default target"></button>
<button class="btn btn-default target"></button>
<button class="btn btn-default target"></button>
</div>
</div>
<div class="col-xs-6">
<div class="well" id="right-well">
<button class="btn btn-default target"></button>
<button class="btn btn-default target"></button>
<button class="btn btn-default target"></button>
</div>
</div>
</div>
</div>
```
# --solutions--
```html
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target"></button>
<button class="btn btn-default target"></button>
<button class="btn btn-default target"></button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target"></button>
<button class="btn btn-default target"></button>
<button class="btn btn-default target"></button>
</div>
</div>
</div>
</div>
```

View File

@ -0,0 +1,215 @@
---
id: bad87fee1348bd9aec908845
title: Alinee elementos de formulario de forma responsiva con Bootstrap
challengeType: 0
forumTopicId: 18225
required:
-
link: >-
https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.2.0/css/font-awesome.css
raw: true
dashedName: line-up-form-elements-responsively-with-bootstrap
---
# --description--
Ahora coloquemos tu formulario `input` y tu `button`de envío en la misma línea. Lo haremos de la misma forma que lo hemos hecho anteriormente: usando un elemento `div` con la clase `row`, y otros elementos `div` dentro de el usando la clase `col-xs-*`.
Anida tanto el campo de texto `input` del formulario como el botón de envío `button` dentro de un `div` con la clase `row`. Incrusta tu campo de texto `input` del formulario dentro de un div con la clase `col-xs-7`. Incrusta tu botón de envió `button` del formulario dentro de un `div`con la clase `col-xs-5`.
Este es el ultimo desafío que haremos para nuestra aplicación de fotos de gatos por ahora. ¡Esperamos que hayas disfrutado aprendiendo Font Awesome, Bootstrap y diseño responsivo!
# --hints--
Tu botón de envío del formulario y la entrada de texto deben anidarse en un div con la clase `row`.
```js
assert(
$('div.row:has(input[type="text"])').length > 0 &&
$('div.row:has(button[type="submit"])').length > 0
);
```
Tu entrada de texto del formulario debe estar anidada en un div con la clase `col-xs-7`.
```js
assert($('div.col-xs-7:has(input[type="text"])').length > 0);
```
Tu botón de envío del formulario debe estar anidado en un div con la clase `col-xs-5`.
```js
assert($('div.col-xs-5:has(button[type="submit"])').length > 0);
```
Todos sus elementos `div` deben tener etiquetas de cierre.
```js
assert(
code.match(/<\/div>/g) &&
code.match(/<div/g) &&
code.match(/<\/div>/g).length === code.match(/<div/g).length
);
```
# --seed--
## --seed-contents--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
h2 {
font-family: Lobster, Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
</style>
<div class="container-fluid">
<div class="row">
<div class="col-xs-8">
<h2 class="text-primary text-center">CatPhotoApp</h2>
</div>
<div class="col-xs-4">
<a href="#"><img class="img-responsive thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
</div>
</div>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<div class="row">
<div class="col-xs-4">
<button class="btn btn-block btn-primary"><i class="fa fa-thumbs-up"></i> Like</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-info"><i class="fa fa-info-circle"></i> Info</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-danger"><i class="fa fa-trash"></i> Delete</button>
</div>
</div>
<p>Things cats <span class="text-danger">love:</span></p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<div class="row">
<div class="col-xs-6">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
</div>
<div class="col-xs-6">
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
</div>
</div>
<div class="row">
<div class="col-xs-4">
<label><input type="checkbox" name="personality"> Loving</label>
</div>
<div class="col-xs-4">
<label><input type="checkbox" name="personality"> Lazy</label>
</div>
<div class="col-xs-4">
<label><input type="checkbox" name="personality"> Crazy</label>
</div>
</div>
<input type="text" class="form-control" placeholder="cat photo URL" required>
<button type="submit" class="btn btn-primary"><i class="fa fa-paper-plane"></i> Submit</button>
</form>
</div>
```
# --solutions--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
h2 {
font-family: Lobster, Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
</style>
<div class="container-fluid">
<div class="row">
<div class="col-xs-8">
<h2 class="text-primary text-center">CatPhotoApp</h2>
</div>
<div class="col-xs-4">
<a href="#"><img class="img-responsive thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
</div>
</div>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<div class="row">
<div class="col-xs-4">
<button class="btn btn-block btn-primary"><i class="fa fa-thumbs-up"></i> Like</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-info"><i class="fa fa-info-circle"></i> Info</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-danger"><i class="fa fa-trash"></i> Delete</button>
</div>
</div>
<p>Things cats <span class="text-danger">love:</span></p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<div class="row">
<div class="col-xs-6">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
</div>
<div class="col-xs-6">
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
</div>
</div>
<div class="row">
<div class="col-xs-4">
<label><input type="checkbox" name="personality"> Loving</label>
</div>
<div class="col-xs-4">
<label><input type="checkbox" name="personality"> Lazy</label>
</div>
<div class="col-xs-4">
<label><input type="checkbox" name="personality"> Crazy</label>
</div>
</div>
<div class="row">
<div class="col-xs-7">
<input type="text" class="form-control" placeholder="cat photo URL" required>
</div>
<div class="col-xs-5">
<button type="submit" class="btn btn-primary"><i class="fa fa-paper-plane"></i> Submit</button>
</div>
</div>
</form>
</div>
```

View File

@ -0,0 +1,176 @@
---
id: bad87fee1348bd9acde08812
title: Hacer que las imágenes se adapten a dispositivos móviles
challengeType: 0
forumTopicId: 18232
dashedName: make-images-mobile-responsive
---
# --description--
Primero, agregue una nueva imagen debajo de la existente. Establezca su atributo `src` en `https://bit.ly/fcc-running-cats`.
Sería genial si esta imagen pudiera tener exactamente el ancho de la pantalla de nuestro teléfono.
Afortunadamente, con Bootstrap, todo lo que tenemos que hacer es agregar la clase `img-responsive` a tus imágenes. Haga esto y la imagen debe ajustarse perfectamente al ancho de su página.
# --hints--
Debe tener un total de dos imágenes.
```js
assert($('img').length === 2);
```
Tu nueva imagen debería estar debajo de la anterior y tener la clase `img-responsive`.
```js
assert($('img:eq(1)').hasClass('img-responsive'));
```
Tu nueva imagen no debería tener la clase `smaller-image`.
```js
assert(!$('img:eq(1)').hasClass('smaller-image'));
```
Tu nueva imagen debe tener el atributo `src` en `https://bit.ly/fcc-running-cats`.
```js
assert($('img:eq(1)').attr('src') === 'https://bit.ly/fcc-running-cats');
```
Tu nuevo elemento `img` debe tener una etiqueta de cierre.
```js
assert(
code.match(/<img/g) &&
code.match(/<img[^<]*>/g).length === 2 &&
code.match(/<img/g).length === 2
);
```
# --seed--
## --seed-contents--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
.red-text {
color: red;
}
h2 {
font-family: Lobster, Monospace;
}
p {
font-size: 16px;
font-family: Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
.smaller-image {
width: 100px;
}
</style>
<div class="container-fluid">
<h2 class="red-text">CatPhotoApp</h2>
<p>Click here for <a href="#">cat photos</a>.</p>
<a href="#"><img class="smaller-image thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
<p>Things cats love:</p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```
# --solutions--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
.red-text {
color: red;
}
h2 {
font-family: Lobster, Monospace;
}
p {
font-size: 16px;
font-family: Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
.smaller-image {
width: 100px;
}
</style>
<div class="container-fluid">
<h2 class="red-text">CatPhotoApp</h2>
<p>Click here for <a href="#">cat photos</a>.</p>
<a href="#"><img class="smaller-image thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive">
<p>Things cats love:</p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```

View File

@ -0,0 +1,192 @@
---
id: bad87fee1348bd9aeda08845
title: Casillas de verificación con estilo responsivo
challengeType: 0
forumTopicId: 18269
required:
-
link: >-
https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.2.0/css/font-awesome.css
raw: true
dashedName: responsively-style-checkboxes
---
# --description--
Dado que las clases `col-xs-*` de Bootstrap son aplicables a todos los elementos `form`, ¡también puedes usarlas en tus casillas de verificación! De esta manera, las casillas de verificación se distribuirán uniformemente por toda la página, independientemente de la amplitud de la resolución de la pantalla.
# --instructions--
Anida las tres casillas de verificación en un elemento `<div class="row">`. Luego incrusta cada uno de ellos en un elemento `<div class="col-xs-4">`.
# --hints--
Todas las casillas de verificación deben anidarse dentro de un `div` con la clase `row`.
```js
assert($('div.row:has(input[type="checkbox"])').length > 0);
```
Cada casilla de verificación debe inscrustarse dentro de un `div` con la clase `row`.
```js
assert($('div.col-xs-4:has(input[type="checkbox"])').length > 2);
```
Todos los elementos `div` deben tener etiquetas de cierre.
```js
assert(
code.match(/<\/div>/g) &&
code.match(/<div/g) &&
code.match(/<\/div>/g).length === code.match(/<div/g).length
);
```
# --seed--
## --seed-contents--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
h2 {
font-family: Lobster, Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
</style>
<div class="container-fluid">
<div class="row">
<div class="col-xs-8">
<h2 class="text-primary text-center">CatPhotoApp</h2>
</div>
<div class="col-xs-4">
<a href="#"><img class="img-responsive thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
</div>
</div>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<div class="row">
<div class="col-xs-4">
<button class="btn btn-block btn-primary"><i class="fa fa-thumbs-up"></i> Like</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-info"><i class="fa fa-info-circle"></i> Info</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-danger"><i class="fa fa-trash"></i> Delete</button>
</div>
</div>
<p>Things cats <span class="text-danger">love:</span></p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<div class="row">
<div class="col-xs-6">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
</div>
<div class="col-xs-6">
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
</div>
</div>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```
# --solutions--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
h2 {
font-family: Lobster, Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
</style>
<div class="container-fluid">
<div class="row">
<div class="col-xs-8">
<h2 class="text-primary text-center">CatPhotoApp</h2>
</div>
<div class="col-xs-4">
<a href="#"><img class="img-responsive thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
</div>
</div>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<div class="row">
<div class="col-xs-4">
<button class="btn btn-block btn-primary"><i class="fa fa-thumbs-up"></i> Like</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-info"><i class="fa fa-info-circle"></i> Info</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-danger"><i class="fa fa-trash"></i> Delete</button>
</div>
</div>
<p>Things cats <span class="text-danger">love:</span></p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<div class="row">
<div class="col-xs-6">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
</div>
<div class="col-xs-6">
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
</div>
</div>
<div class="row">
<div class="col-xs-4">
<label><input type="checkbox" name="personality"> Loving</label>
</div>
<div class="col-xs-4">
<label><input type="checkbox" name="personality"> Lazy</label>
</div>
<div class="col-xs-4">
<label><input type="checkbox" name="personality"> Crazy</label>
</div>
</div>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```

View File

@ -0,0 +1,176 @@
---
id: bad87fee1348bd9aedb08845
title: Botones radio con estilo responsivo
challengeType: 0
forumTopicId: 18270
required:
-
link: >-
https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.2.0/css/font-awesome.css
raw: true
dashedName: responsively-style-radio-buttons
---
# --description--
¡También puedes usar las clases `col-xs-*` de Bootstrap en los elementos `form`! De esta manera, nuestros botones de radio se distribuirán uniformemente por toda la página, independientemente del ancho de la resolución de la pantalla.
Anida ambos botones de radio dentro de un elemento `<div class="row">`. Luego anida cada uno de ellos en un elemento `<div class="col-xs-6">`.
**Nota:** Como recordatorio, los botones de radio son `entradas`, elementos de tipo `radio`.
# --hints--
Todas las casillas de radio deben anidarse dentro de un `div` con la clase `row`.
```js
assert($('div.row:has(input[type="radio"])').length > 0);
```
Cada uno de tus botones de radio debe inscrustarse dentro un `div` con la clase `col-xs-6`.
```js
assert($('div.col-xs-6:has(input[type="radio"])').length > 1);
```
Todos sus elementos `div` deben tener etiquetas de cierre.
```js
assert(
code.match(/<\/div>/g) &&
code.match(/<div/g) &&
code.match(/<\/div>/g).length === code.match(/<div/g).length
);
```
# --seed--
## --seed-contents--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
h2 {
font-family: Lobster, Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
</style>
<div class="container-fluid">
<div class="row">
<div class="col-xs-8">
<h2 class="text-primary text-center">CatPhotoApp</h2>
</div>
<div class="col-xs-4">
<a href="#"><img class="img-responsive thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
</div>
</div>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<div class="row">
<div class="col-xs-4">
<button class="btn btn-block btn-primary"><i class="fa fa-thumbs-up"></i> Like</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-info"><i class="fa fa-info-circle"></i> Info</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-danger"><i class="fa fa-trash"></i> Delete</button>
</div>
</div>
<p>Things cats <span class="text-danger">love:</span></p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```
# --solutions--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
h2 {
font-family: Lobster, Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
</style>
<div class="container-fluid">
<div class="row">
<div class="col-xs-8">
<h2 class="text-primary text-center">CatPhotoApp</h2>
</div>
<div class="col-xs-4">
<a href="#"><img class="img-responsive thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
</div>
</div>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<div class="row">
<div class="col-xs-4">
<button class="btn btn-block btn-primary"><i class="fa fa-thumbs-up"></i> Like</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-info"><i class="fa fa-info-circle"></i> Info</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-danger"><i class="fa fa-trash"></i> Delete</button>
</div>
</div>
<p>Things cats <span class="text-danger">love:</span></p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<div class="row">
<div class="col-xs-6">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
</div>
<div class="col-xs-6">
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
</div>
</div>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```

View File

@ -0,0 +1,57 @@
---
id: bad87fee1348bd9aec908847
title: Divide tu fila de Bootstrap
challengeType: 0
forumTopicId: 18306
dashedName: split-your-bootstrap-row
---
# --description--
Ahora que tenemos una fila de Bootstrap, vamos a dividirla en dos columnas para albergar nuestros elementos.
Crea dos elementos `div` dentro de tu fila, ambos con la clase `col-xs-6`.
# --hints--
Dos elementos `div class="col-xs-6"` deben estar anidados dentro de tu elemento `div class="row"`.
```js
assert($('div.row > div.col-xs-6').length > 1);
```
Todos tus elementos `div` deben tener etiquetas de cierre.
```js
assert(
code.match(/<\/div>/g) &&
code.match(/<div/g) &&
code.match(/<\/div>/g).length === code.match(/<div/g).length
);
```
# --seed--
## --seed-contents--
```html
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
</div>
</div>
```
# --solutions--
```html
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6"></div>
<div class="col-xs-6"></div>
</div>
</div>
```

View File

@ -0,0 +1,202 @@
---
id: bad87fee1348bd9aed908845
title: Estilo de entradas de texto como controles de formulario
challengeType: 0
forumTopicId: 18312
required:
-
link: >-
https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.2.0/css/font-awesome.css
raw: true
dashedName: style-text-inputs-as-form-controls
---
# --description--
Puedes añadir el icono `fa-paper-plane` de Font Awesome, agregando `<i class="fa fa-paper-plane"></i>` dentro del elemento `button` de tipo `submit`.
Da al input(campo de entrada de texto) de tu formulario una clase `form-control`. Da el botón de envío(submit) de tu formulario las clases `btn btn-primary`. También da a este botón el icono de Font Awesome con `fa-paper-plane`.
Todo el texto en los elementos `<input>`, `<textarea>`, y `<select>` con la clase `.form-control` deben tener un ancho del 100%.
# --hints--
El botón de envío submit en tu formulario debe tener las clases `btn btn-primary`.
```js
assert($('button[type="submit"]').hasClass('btn btn-primary'));
```
Debes añadir `<i class="fa fa-paper-plane"></i>` dentro de tu elemento button.
```js
assert($('button[type="submit"]:has(i.fa.fa-paper-plane)').length > 0);
```
El texto en `input` en tu formulario debe tener la clase `form-control`.
```js
assert($('input[type="text"]').hasClass('form-control'));
```
Cada uno de tus elementos `i` debe tener una etiqueta de cierre.
```js
assert(code.match(/<\/i>/g) && code.match(/<\/i/g).length > 3);
```
# --seed--
## --seed-contents--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
h2 {
font-family: Lobster, Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
</style>
<div class="container-fluid">
<div class="row">
<div class="col-xs-8">
<h2 class="text-primary text-center">CatPhotoApp</h2>
</div>
<div class="col-xs-4">
<a href="#"><img class="img-responsive thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
</div>
</div>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<div class="row">
<div class="col-xs-4">
<button class="btn btn-block btn-primary"><i class="fa fa-thumbs-up"></i> Like</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-info"><i class="fa fa-info-circle"></i> Info</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-danger"><i class="fa fa-trash"></i> Delete</button>
</div>
</div>
<p>Things cats <span class="text-danger">love:</span></p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<div class="row">
<div class="col-xs-6">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
</div>
<div class="col-xs-6">
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
</div>
</div>
<div class="row">
<div class="col-xs-4">
<label><input type="checkbox" name="personality"> Loving</label>
</div>
<div class="col-xs-4">
<label><input type="checkbox" name="personality"> Lazy</label>
</div>
<div class="col-xs-4">
<label><input type="checkbox" name="personality"> Crazy</label>
</div>
</div>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```
# --solutions--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
h2 {
font-family: Lobster, Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
</style>
<div class="container-fluid">
<div class="row">
<div class="col-xs-8">
<h2 class="text-primary text-center">CatPhotoApp</h2>
</div>
<div class="col-xs-4">
<a href="#"><img class="img-responsive thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
</div>
</div>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<div class="row">
<div class="col-xs-4">
<button class="btn btn-block btn-primary"><i class="fa fa-thumbs-up"></i> Like</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-info"><i class="fa fa-info-circle"></i> Info</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-danger"><i class="fa fa-trash"></i> Delete</button>
</div>
</div>
<p>Things cats <span class="text-danger">love:</span></p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<div class="row">
<div class="col-xs-6">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
</div>
<div class="col-xs-6">
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
</div>
</div>
<div class="row">
<div class="col-xs-4">
<label><input type="checkbox" name="personality"> Loving</label>
</div>
<div class="col-xs-4">
<label><input type="checkbox" name="personality"> Lazy</label>
</div>
<div class="col-xs-4">
<label><input type="checkbox" name="personality"> Crazy</label>
</div>
</div>
<input type="text" class="form-control" placeholder="cat photo URL" required>
<button type="submit" class="btn btn-primary"><i class="fa fa-paper-plane"></i>Submit</button>
</form>
</div>
```

View File

@ -0,0 +1,167 @@
---
id: bad87fee1348cd8acef08811
title: Prueba el arcoíris de colores del botón de Bootstrap
challengeType: 0
forumTopicId: 18323
dashedName: taste-the-bootstrap-button-color-rainbow
---
# --description--
La clase `btn-primary` es el color principal que usarás en tu aplicación. Es útil para resaltar las acciones que quieres que tome tu usuario.
Reemplaza la clase de Bootstrap `btn-default` con `btn-primary` en tu botón.
Ten en cuenta que este botón todavía necesitará las clases `btn` y `btn-block`.
# --hints--
Tu botón debe tener la clase `btn-primary`.
```js
assert($('button').hasClass('btn-primary'));
```
Tu botón debe tener las clases `btn` y `btn-block`.
```js
assert($('button').hasClass('btn-block') && $('button').hasClass('btn'));
```
Todos tus elementos de tipo `button` deben tener etiquetas de cierre.
```js
assert(
code.match(/<\/button>/g) &&
code.match(/<button/g) &&
code.match(/<\/button>/g).length === code.match(/<button/g).length
);
```
# --seed--
## --seed-contents--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
.red-text {
color: red;
}
h2 {
font-family: Lobster, Monospace;
}
p {
font-size: 16px;
font-family: Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
.smaller-image {
width: 100px;
}
</style>
<div class="container-fluid">
<h2 class="red-text text-center">CatPhotoApp</h2>
<p>Click here for <a href="#">cat photos</a>.</p>
<a href="#"><img class="smaller-image thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<button class="btn btn-default btn-block">Like</button>
<p>Things cats love:</p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```
# --solutions--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
.red-text {
color: red;
}
h2 {
font-family: Lobster, Monospace;
}
p {
font-size: 16px;
font-family: Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
.smaller-image {
width: 100px;
}
</style>
<div class="container-fluid">
<h2 class="red-text text-center">CatPhotoApp</h2>
<p>Click here for <a href="#">cat photos</a>.</p>
<a href="#"><img class="smaller-image thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<button class="btn btn-primary btn-block">Like</button>
<p>Things cats love:</p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```

View File

@ -0,0 +1,185 @@
---
id: bad87fee1348bd9aedf08845
title: Usa un span para apuntar a elementos en línea
challengeType: 0
forumTopicId: 18370
dashedName: use-a-span-to-target-inline-elements
---
# --description--
Puedes usar spans para crear elementos en línea. ¿Recuerdas cuando usamos la clase `btn-block` para hacer que el botón ocupe toda la fila completa?
<button class='btn' style='background-color: rgb(0, 100, 0); color: rgb(255, 255, 255);'>botón normal</button>
<button class='btn btn-block' style='background-color: rgb(0, 100, 0); color: rgb(255, 255, 255);'>botón btn-block</button>
Esto ilustra la diferencia entre un elemento "en línea" y un elemento de "bloque".
Usando el elemento en línea `span`, puedes alinear varios elementos, e incluso estilizar diferentes partes de la misma línea de manera diferente.
Usando un elemento `span`, anida la palabra `love` dentro del elemento `p` que actualmente contiene el texto `Things cats love`. Luego asigna al `span` la clase `text-danger` para hacer al texto rojo.
Así es como harías esto para el elemento `p` que tiene el texto `Top 3 things cats hate`:
```html
<p>Top 3 things cats <span class="text-danger">hate:</span></p>
```
# --hints--
Tu elemento `span` debe estar dentro de tu elemento `p`.
```js
assert($('p span') && $('p span').length > 0);
```
Tu elemento `span` solo debe tener el texto `love`.
```js
assert(
$('p span') &&
$('p span').text().match(/love/i) &&
!$('p span')
.text()
.match(/Things cats/i)
);
```
Tu elemento `span` debe tener la clase `text-danger`.
```js
assert($('span').hasClass('text-danger'));
```
Tu elemento `span` debe tener una etiqueta de cierre.
```js
assert(
code.match(/<\/span>/g) &&
code.match(/<span/g) &&
code.match(/<\/span>/g).length === code.match(/<span/g).length
);
```
# --seed--
## --seed-contents--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
h2 {
font-family: Lobster, Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
</style>
<div class="container-fluid">
<h2 class="text-primary text-center">CatPhotoApp</h2>
<a href="#"><img class="img-responsive thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<div class="row">
<div class="col-xs-4">
<button class="btn btn-block btn-primary">Like</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-info">Info</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-danger">Delete</button>
</div>
</div>
<p>Things cats love:</p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```
# --solutions--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
h2 {
font-family: Lobster, Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
</style>
<div class="container-fluid">
<h2 class="text-primary text-center">CatPhotoApp</h2>
<a href="#"><img class="img-responsive thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<div class="row">
<div class="col-xs-4">
<button class="btn btn-block btn-primary">Like</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-info">Info</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-danger">Delete</button>
</div>
</div>
<p>Things cats <span class="text-danger">love</span>:</p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```

View File

@ -0,0 +1,98 @@
---
id: bad87fee1348bd9aec908857
title: Use los comentarios para aclarar el código
challengeType: 0
forumTopicId: 18347
dashedName: use-comments-to-clarify-code
---
# --description--
Cuando comencemos a utilizar jQuery, modificaremos los elementos HTML sin necesidad de cambiarlos en HTML.
Asegurémonos de que todos sepan que en realidad no deben modificar nada de este código directamente.
Recuerda que puedes comenzar un comentario con `<!--` y terminar con `-->`
Añade un comentario en la parte superior de tu HTML que diga `Code below this line should not be changed`
# --hints--
En la parte superior de tu HTML debe comenzar un comentario con `<!--`.
```js
assert(code.match(/^\s*<!--/));
```
El comentario debe contener el texto `Code below this line should not be changed`.
```js
assert(code.match(/<!--(?!(>|->|.*-->.*this line))\s*.*this line.*\s*-->/gi));
```
Debe cerrar el comentario con `-->`.
```js
assert(code.match(/-->.*\n+.+/g));
```
Debe tener el mismo número de aperturas y cierres de comentario.
```js
assert(code.match(/<!--/g).length === code.match(/-->/g).length);
```
# --seed--
## --seed-contents--
```html
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```
# --solutions--
```html
<!-- Code below this line should not be changed -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```

View File

@ -0,0 +1,170 @@
---
id: bad87fee1348bd9acde08712
title: Utiliza diseño responsivo con contenedores de fluido Bootstrap
challengeType: 0
forumTopicId: 18362
dashedName: use-responsive-design-with-bootstrap-fluid-containers
---
# --description--
En la sección de HTML5 y CSS de freeCodeCamp, creamos una aplicación de fotos de gatos. Ahora vamos de vuelta a la aplicación. Esta vez, lo estilizaremos usando el popular framework CSS responsivo de Bootstrap.
Bootstap averiguará la anchura de tu pantalla y responderá redimensionando los elementos HTML, de aquí el nombre <dfn>diseño responsivo</dfn>.
Con diseño responsivo, no hay necesidad de diseñar una versión móvil de tu sitio web. Se verá bien en dispositivos con pantallas de cualquier anchura.
Puedes añadir Bootstrap a cualquier aplicación al agregar el siguiente código encima de tu HTML:
```html
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"/>
```
En este caso, ya lo añadimos a esta página. Ten en cuenta que para cerrar la etiqueta `link` puedes utilizar `>` o `/>`.
Para comenzar, debemos anidar todo nuestro HTML (menos la etiqueta `link` y el elemento `style`) en un elemento `div` con la clase `container-fluid`.
# --hints--
Tu elemento `div` debe tener la clase `container-fluid`.
```js
assert($('div').hasClass('container-fluid'));
```
Tu elemento `div` debe tener una etiqueta de cierre.
```js
assert(
code.match(/<\/div>/g) &&
code.match(/<div/g) &&
code.match(/<\/div>/g).length === code.match(/<div/g).length
);
```
Todos los elementos HTML después de la etiqueta de cierre `style` deben anidarse en `.container-fluid`.
```js
assert($('.container-fluid').children().length >= 8 && !$('.container-fluid').has("style").length && !$('.container-fluid').has("link").length);
```
# --seed--
## --seed-contents--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
.red-text {
color: red;
}
h2 {
font-family: Lobster, Monospace;
}
p {
font-size: 16px;
font-family: Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
.smaller-image {
width: 100px;
}
</style>
<h2 class="red-text">CatPhotoApp</h2>
<p>Click here for <a href="#">cat photos</a>.</p>
<a href="#"><img class="smaller-image thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
<p>Things cats love:</p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
```
# --solutions--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
.red-text {
color: red;
}
h2 {
font-family: Lobster, Monospace;
}
p {
font-size: 16px;
font-family: Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
.smaller-image {
width: 100px;
}
</style>
<div class="container-fluid">
<h2 class="red-text">CatPhotoApp</h2>
<p>Click here for <a href="#">cat photos</a>.</p>
<a href="#"><img class="smaller-image thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
<p>Things cats love:</p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```

View File

@ -0,0 +1,196 @@
---
id: bad88fee1348ce8acef08815
title: Utiliza la cuadrilla (grid) Bootstrap para poner elemento de lado a lado
challengeType: 0
forumTopicId: 18371
dashedName: use-the-bootstrap-grid-to-put-elements-side-by-side
---
# --description--
Bootstrap utiliza un sistema responsivo de cuadrilla de 12 columnas, el cual hace que sea fácil poner elementos en dos filas y especificar la anchura relativa de cada elemento. La mayoría de las clases de Bootstrap pueden ser aplicadas a un elemento `div`.
Bootstrap tiene diferentes atributos de anchura de columna que utiliza con respecto a la anchura de la pantalla del usuario. Por ejemplo, los teléfonos tienen pantallas más angostas y las computadoras portátiles tienen pantallas más anchas.
Ten como ejemplo la clase de Bootstrap `col-md-*`. Aquí, `md` significa mediano y `*` es un número que especifica cuántas columnas de ancho deben tener los elementos. En este caso, se especifica el ancho de la columna de un elemento en una pantalla de tamaño mediano, como un portátil.
En la aplicación de fotos de gatitos que estamos creando, utilizaremos `col-xs-*`, donde `xs` significa extra pequeño (como una pantalla de teléfono celular extra pequeña) y `*` es el número de columnas que especifica cuántas columnas de ancho debe tener un elemento.
Pon los botones `Like`, `Info` y `Delete` de lado a lado anidando los tres dentro de un elemento `<div class="row">`, luego cada uno de ellos dentro de un elemento `<div class="col-xs-4">`.
La clase `row` se aplica a un `div` y los mismos botones pueden anidarse dentro de ella.
# --hints--
Todos los botones deben estar anidados dentro del mismo elemento `div` con la clase `row`.
```js
assert($('div.row:has(button)').length > 0);
```
Cada uno de los botones de Bootstap debe anidarse dentro de su propio elemento `div` con la clase `col-xs-4`.
```js
assert($('div.col-xs-4:has(button)').length > 2);
```
Cada uno de los elementos `button` debe tener una etiqueta de cierre.
```js
assert(
code.match(/<\/button>/g) &&
code.match(/<button/g) &&
code.match(/<\/button>/g).length === code.match(/<button/g).length
);
```
Cada uno de loselementos `div` debe tener una etiqueta de cierre.
```js
assert(
code.match(/<\/div>/g) &&
code.match(/<div/g) &&
code.match(/<\/div>/g).length === code.match(/<div/g).length
);
```
# --seed--
## --seed-contents--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
.red-text {
color: red;
}
h2 {
font-family: Lobster, Monospace;
}
p {
font-size: 16px;
font-family: Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
.smaller-image {
width: 100px;
}
</style>
<div class="container-fluid">
<h2 class="red-text text-center">CatPhotoApp</h2>
<p>Click here for <a href="#">cat photos</a>.</p>
<a href="#"><img class="smaller-image thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<button class="btn btn-block btn-primary">Like</button>
<button class="btn btn-block btn-info">Info</button>
<button class="btn btn-block btn-danger">Delete</button>
<p>Things cats love:</p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```
# --solutions--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
.red-text {
color: red;
}
h2 {
font-family: Lobster, Monospace;
}
p {
font-size: 16px;
font-family: Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
.smaller-image {
width: 100px;
}
</style>
<div class="container-fluid">
<h2 class="red-text text-center">CatPhotoApp</h2>
<p>Click here for <a href="#">cat photos</a>.</p>
<a href="#"><img class="smaller-image thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<div class="row">
<div class="col-xs-4">
<button class="btn btn-block btn-primary">Like</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-info">Info</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-danger">Delete</button>
</div>
</div>
<p>Things cats love:</p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```

View File

@ -0,0 +1,176 @@
---
id: bad87fee1348ce8acef08814
title: Advierte a tus usuarios de una acción peligrosa con btn-danger
challengeType: 0
forumTopicId: 18375
dashedName: warn-your-users-of-a-dangerous-action-with-btn-danger
---
# --description--
Bootstrap viene con diferentes colores predefinidos para botones. La clase `btn-danger` es el color del botón que usarás para notificar a los usuarios que el botón realiza una acción destructiva, como eliminar una foto de gato.
Crea un botón con el texto `Delete` y dale la clase `btn-danger`.
Ten en cuenta que estos botones todavía necesitan las clases `btn` y `btn-block`.
# --hints--
Debes crear un nuevo elemento `button` con el texto `Delete`.
```js
assert(new RegExp('Delete', 'gi').test($('button').text()));
```
Todos tus botones de Bootstrap deben tener clases `btn` y `btn-block`.
```js
assert($('button.btn-block.btn').length > 2);
```
Tu nuevo botón debe tener la clase `btn-danger`.
```js
assert($('button').hasClass('btn-danger'));
```
Todos los elementos `button` deben tener etiquetas de cierre.
```js
assert(
code.match(/<\/button>/g) &&
code.match(/<button/g) &&
code.match(/<\/button>/g).length === code.match(/<button/g).length
);
```
# --seed--
## --seed-contents--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
.red-text {
color: red;
}
h2 {
font-family: Lobster, Monospace;
}
p {
font-size: 16px;
font-family: Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
.smaller-image {
width: 100px;
}
</style>
<div class="container-fluid">
<h2 class="red-text text-center">CatPhotoApp</h2>
<p>Click here for <a href="#">cat photos</a>.</p>
<a href="#"><img class="smaller-image thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<button class="btn btn-block btn-primary">Like</button>
<button class="btn btn-block btn-info">Info</button>
<p>Things cats love:</p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```
# --solutions--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
.red-text {
color: red;
}
h2 {
font-family: Lobster, Monospace;
}
p {
font-size: 16px;
font-family: Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
.smaller-image {
width: 100px;
}
</style>
<div class="container-fluid">
<h2 class="red-text text-center">CatPhotoApp</h2>
<p>Click here for <a href="#">cat photos</a>.</p>
<a href="#"><img class="smaller-image thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<button class="btn btn-block btn-primary">Like</button>
<button class="btn btn-block btn-info">Info</button>
<button class="btn btn-block btn-danger">Delete</button>
<p>Things cats love:</p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```

View File

@ -0,0 +1,81 @@
---
id: bd7158d8c442eddfaeb5bd0f
title: Construye un reloj 25 + 5
challengeType: 3
forumTopicId: 301373
dashedName: build-a-25--5-clock
---
# --description--
**Objetivo:** Construye una aplicación funcional [CodePen.io](https://codepen.io) similar a esta: <https://codepen.io/freeCodeCamp/full/XpKrrW>.
Completa las siguientes [historias de usuario](https://en.wikipedia.org/wiki/User_story) y consigue pasar todas las pruebas. Dale tu propio estilo personal.
Puedes utilizar una combinación de HTML, JavaScript, CSS, Bootstrap, SASS, React, Redux y jQuery para completar este proyecto. Deberías utilizar un framework frontend (como React por ejemplo) porque esta sección trata sobre el aprendizaje de frameworks frontend. No se recomienda utilizar tecnologías adicionales a las indicadas anteriormente, su utilización corre bajo su propio riesgo. Estamos considerando apoyar otros frameworks frontend como Angular y Vue, pero actualmente no están soportados. Aceptaremos e intentaremos solucionar todos los informes de incidencias que utilicen el conjunto de tecnologías sugeridas para este proyecto. ¡Feliz programación!
**Historia de usuario #1:** Puedo ver un elemento con `id="break-label"` que contiene una cadena (por ejemplo: "Break Length").
**Historia de usuario #2:** Puedo ver un elemento con `id="session-label"` que contiene una cadena (p.ej "Session Length").
**Historia de usuario #3:** Puedo ver dos elementos cliqueables con los siguientes IDs: `id="break-decrement"` y `id="session-decrement"`.
**Historia de usuario #4:** Puedo ver dos elemento cliqueables con los siguientes IDs: `id="break-increment"` y `id="session-increment"`.
**Historia de usuario #5:** Puedo ver un elemento con el correspondiente `id="break-length"` que por defecto (al cargarse) muestra el valor 5.
**Historia de usuario #6:** Puedo ver un elemento con el correspondiente `id="session-length"`, que por defecto muestra el valor 25.
**Historia de usuario #7:** Puedo ver un elemento con el correspondiente `id="timer-label"`, que contiene una cadena indicando si la sesión esta inicializada (p.ej. "Session").
**Historia de usuario #8:** Puedo ver un elemento con el correspondiente `id="time-left"`. NOTA: En pausa o en ejecución, el valor en este campo debe mostrarse siempre en formato `mm:ss` (es decir: 25:00).
**User Story #9:** Puedo ver un elemento cliqueable con el correspondiente `id="start_stop"`.
**User Story #10:** Puedo ver un elemento cliqueable con el correspondiente `id="reset"`.
**Historia de usuario #11:** Cuando hago clic en el elemento con el id: `reset`, cualquier temporizador en ejecución debe detenerse. El valor en el `id="break-length"` debe regresar a `5`, el valor en el `id="session-length"` debe regresar a 25 y el elemento con `id="time-left"` debe reiniciarse a su estado predeterminado.
**Historia de usuario #12:** Cuando hago clic en el elemento con id: `break-decrement`, el valor en `id="break-length"` se reduce en 1, y puedo ver el valor actualizado.
**Historia de usuario #13:** Cuando hago clic en el elemento con id: `break-increment`, el valor en `id="break-length"` se incrementa en 1 y puedo ver el valor actualizado.
**Historia de usuario #14:** Cuando hago clic en el elemento con id: `session-decrement`, el valor en `id="session-length"` se reduce en 1 y puedo ver el valor actualizado.
**Historia de usuario #15:** Cuando hago clic en el elemento con id: `session-increment`, el valor en `id="session-length"` se incrementa en 1 y puedo ver el valor actualizado.
**Historia de usuario #16:** No debo ser capaz de establecer una duración de la sesión o pausa &lt;= 0.
**Historia de usuario #17:** No debería poder establecer una duración de la sesión o pausa > 60.
**Historia de usuario #18:** Cuando hago clic por primera vez en el elemento con el correspondiente `id="start_stop"`, el temporizador debe comenzar a correr desde el valor mostrado actualmente en `id="session-length"`, incluso si el valor se incrementó o se redujo respecto al valor original 25.
**Historia de usuario #19:** Si el temporizador se está ejecutando, el elemento con el id: `time-left` debería mostrar el tiempo restante en formato `mm:ss` (reduciendo 1 y actualizando el valor mostrado cada 1000ms).
**Historia de usuario #20:** Si el temporizador se está ejecutando y hago clic en el elemento `id="start_stop"`, la cuenta atrás debería pausarse.
**Historia de usuario #21:** Si el temporizador está pausado y hago clic en el elemento `id="start_stop"`, la cuenta atrás debe reanudarse desde el punto en el que fue pausada.
**Historia de usuario #22:** Cuando la cuenta atrás de la sesión llega a cero (NOTA: temporizador DEBE llegar a 00:00) y una nueva cuenta atrás comienza, el elemento cuyo id es `timer-label` debería mostrar una cadena en la que se indica que el descanso (break) ha comenzado.
**Historia de usuario #23:** Cuando la cuenta atrás de la sesión llega a cero (NOTA: temporizador DEBE llegar a 00:00), una nueva cuenta atrás comienza para el periodo de descanso (break) y se inicia desde el valor mostrado actualmente por el elemento `id="break-length"`.
**Historia de usuario #24:** Cuando la cuenta atrás en el periodo de descanso (break) llega a cero (NOTA: temporizador DEBE llegar a 00:00) y una nueva cuenta atrás debe comenzar, el elemento cuyo id es `timer-label` debe mostrar una cadena en la que se indica que la sesión ha comenzado.
**Historia de usuario #25:** Cuando la cuenta atrás en el periodo de descanso (break) llega a cero (NOTA: temporizador DEBE llegar a 00:00), una nueva cuenta atrás debe comenzar para la sesión, la cual se inicia desde el valor mostrado actualmente por el elemento `id="session-length"`.
**Historia de usuario #26:** Cuando una cuenta atrás llega a cero (NOTA: temporizador DEBE llegar a 00:00), debe reproducirse un sonido que indica que el tiempo se ha agotado. Se debe utilizar una etiqueta HTML5 `audio` y tener un `id="beep"`.
**Historia de usuario #27:** El elemento de audio `id="beep"` debe ser de 1 segundo o mayor.
**Historia de usuario #28:** El elemento de audio con id: `beep` dejará de reproducirse y se reiniciará al hacer clic en el elemento con id: `reset`.
Puedes construir tu proyecto con <a href='https://codepen.io/pen?template=MJjpwO' target='_blank' rel='nofollow'>usando esta plantilla CodePen</a> y haciendo clic en `Save` para crear tu propio Pen. O puedes utilizar este enlace CDN para ejecutar las pruebas en cualquier entorno que desees: `https://cdn.freecodecamp.org/testable-projects-fcc/v1/bundle.js`
Una vez que hayas terminado, envía la URL de tu proyecto con todos los tests pasados.
# --solutions--
```js
// solution required
```

View File

@ -0,0 +1,39 @@
---
id: 587d7dbc367417b2b2512bae
title: Crea una batería electrónica
challengeType: 3
forumTopicId: 301370
dashedName: build-a-drum-machine
---
# --description--
**Objetivo:** Construye una aplicación en [CodePen.io](https://codepen.io) que funcionalmente sea similar a esta: <https://codepen.io/freeCodeCamp/full/MJyNMd>.
Completa las siguientes [historias de usuario](https://en.wikipedia.org/wiki/User_story) y consigue que se aprueben todas las pruebas. Dale tu estilo personal.
Puede utilizar cualquier mezcla de HTML, JavaScript, CSS, Bootstrap, SASS, React, Redux, y jQuery para completar este proyecto. Debe usar un framework de frontend (como React por ejemplo). Porque esta sección trata sobre el aprendizaje de frameworks frontend. No se recomienda utilizar tecnologías adicionales a las indicadas anteriormente, su utilización, corre bajo tu propio riesgo. Estamos buscando dar soporte a otros frameworks de frontend como Angular y Vue, pero actualmente no están soportados. Aceptaremos e intentaremos solucionar todos los informes de incidencias que utilicen el conjunto de tecnologías sugeridas para este proyecto. ¡Feliz programación!
**Historia de usuario #1:** Debe ser capaz de ver un contenedor externo con un correspondiente `id="drum-machine"` que contiene todos los demás elementos.
**Historia de usuario #2:** Dentro de `#drum-machine` Puedo ver un elemento con un `id="display"` correspondiente.
**Historia de usuario #3:** Dentro de `#drum-machine` Puedo ver 9 elementos del panel de tambores clickables, cada una con un nombre de clase de `drum-pad`, un identificador único que describe el clip de audio que el pad de tambor será configurado para activar, y un texto interno que corresponde a una de las siguientes teclas del teclado: `Q`, `W`, `E`, `A`, `S`, `D`, `Z`, `X`, `C`. Los pads de la batería DEBEN estar en este orden.
**Historia de usuario #4:** Dentro de cada `.drum-pad`, debe haber un elemento HTML5 `audio` que tenga un atributo `src` apuntando a un clip de audio, un nombre de clase `clip`, y un id correspondiente al texto interno de su padre `.drum-pad` (e.g. `id="Q"`, `id="W"`, `id="E"` etc.).
**Historia de usuario #5:** Cuando hago clic en `.drum-pad` elemento, el clip de audio contenido en su elemento hijo `audio` debe ser activado.
**Historia de usuario #6:** Cuando presione la tecla de activación asociada con cada `.drum-pad`, el clip de audio contenido en su elemento hijo `audio` debe ser activado (p. ej. pulsando la tecla `Q` debería activar el tambor que contiene la cadena `Q`, pulsando la tecla `W` debería activar la tecla Tambor que contiene la cadena `W`, etc.).
**Historia de usuario #7:** Cuando es un `.drum-pad` es activado, una cadena que describe el clip de audio asociado se muestra como el texto interior del elemento `#display` (cada cadena debe ser única).
Puedes construir tu proyecto usando <a href='https://codepen.io/pen?template=MJjpwO' target='_blank' rel='nofollow'>esta plantilla CodePen</a> y haciendo clic en `Save` para crear tu propio pen. O puedes utilizar este enlace CDN para ejecutar las pruebas en cualquier entorno que desees: `https://cdn.freecodecamp.org/testable-projects-fcc/v1/bundle.js`
Una vez que hayas terminado, envía la URL de tu proyecto funcional con todas las pruebas aprobadas.
# --solutions--
```js
// solution required
```

View File

@ -0,0 +1,62 @@
---
id: bd7158d8c442eddfaeb5bd17
title: Crea una calculadora con JavaScript
challengeType: 3
forumTopicId: 301371
dashedName: build-a-javascript-calculator
---
# --description--
**Objetivo:** Construye una aplicación en [CodePen.io](https://codepen.io) que funcionalmente sea similar a esta: <https://codepen.io/freeCodeCamp/full/wgGVVX>.
Completa las siguientes [historias de usuario](https://en.wikipedia.org/wiki/User_story) y consigue que se aprueben todas las pruebas. Dale tu propio estilo personal.
Puede utilizar cualquier mezcla de HTML, JavaScript, CSS, Bootstrap, SASS, React, Redux y jQuery para completar este proyecto. Debe usar un framework frontend (como React por ejemplo), ya que esta sección trata sobre el aprendizaje de frameworks frontend. No se recomienda utilizar tecnologías adicionales a las indicadas anteriormente, su uso corre bajo tu propio riesgo. Estamos buscando dar soporte a otros frameworks de frontend como Angular y Vue, pero actualmente no están soportados. Aceptaremos e intentaremos arreglar todos los informes de incidencias que utilicen la pila de tecnología sugerida para este proyecto. ¡Feliz codificación!
**Historia de usuario #1:** Mi calculadora debe contener un elemento cliqueable que contenga un `=` (signo igual) con un correspondiente `id="equals"`.
**Historia de usuario #2:** Mi calculadora debe contener 10 elementos que contienen un número cada uno de 0-9 con los siguientes IDs correspondientes: `id="zero"`, `id="one"`, `id="two"`, `id="three"`, `id="four"`, `id="five"`, `id="six"`, `id="seven"`, `id="eight"`, and `id="nine"`.
**Historia de usuario #3:** Mi calculadora debe contener 4 elementos que contengan uno de los 4 operadores matemáticos primarios con los siguientes identificadores correspondientes: `id="add"` `id="subtract"`, `id="multiply"`, `id="divide"`.
**Historia de usuario #4:** Mi calculadora debe contener un elemento cliqueable que contenga un `.` (punto decimal) símbolo con el correspondiente `id="decimal"`.
**Historia de usuario #5:** Mi calculadora debe contener un elemento cliqueable con un `id="clear"`.
**Historia de usuario #6:** Mi calculadora debe contener un elemento para mostrar valores con un correspondiente `id="display"`.
**Historia de usuario #7:** En cualquier momento, pulsando el botón `clear` elimina los valores de entrada y salida. Luego devuelve la calculadora a su estado inicial; debe mostrar 0 en el elemento con el id de `display`.
**Historia de usuario #8:** Al introducir números, Debe ser capaz de ver mi entrada en el elemento con el id de `display`.
**Historia de usuario #9:** En cualquier orden, debe poder añadir, restar, multiplicar y dividir una cadena de números de cualquier longitud. Además cuando presione `=`, el resultado correcto debe mostrarse en el elemento con el id de `display`.
**Historia de usuario #10:** Al introducir números, mi calculadora no debe permitir que un número comience con varios ceros.
**Historia de usuario #11:** Cuando se hace clic en el elemento decimal, un `.` debe añadirse al valor mostrado actualmente; dos `.` en un número no debe ser aceptado.
**Historia de usuario #12:** Debe ser capaz de realizar cualquier operación (`+`, `-`, `*`, `/`) en números que contienen puntos decimales.
**Historia de usuario #13:** Si se introducen 2 o más operadores consecutivamente, la operación realizada debe ser el último operador introducido (excluyendo el signo negativo (`-`). Por ejemplo, si `5 + * 7 =` es introducido, el resultado debe ser `35` (p.e. `5 * 7`); si `5 * - 5 =` es introducido, el resultado debe ser `-25` (p.e `5 * (-5)`).
**Historia de usuario #14:** Presionando un operador inmediatamente después de `=` debe comenzar un nuevo cálculo que opere sobre el resultado de la evaluación anterior.
**Historia de usuario #15:** Mi calculadora debe tener varios decimales de precisión a la hora de redondear (tenga en cuenta que no hay un estándar exacto, pero debe ser capaz de manejar cálculos como `2 / 7` con una precisión razonable de al menos 4 decimales.
**Nota sobre la lógica de la calculadora:** Debe tener en cuenta que hay dos escuelas principales de pensamiento sobre la lógica de entrada de las calculadoras: <dfn>lógica de ejecución inmediata</dfn> y <dfn>lógica de la fórmula</dfn>. Nuestro ejemplo utiliza lógica de la fórmula. Observa el orden de precedencia de la operación, la ejecución inmediata no lo hace. Ambas son aceptables, pero ten en cuenta que dependiendo de cuál elijas, tu calculadora puede producir resultados diferentes a los nuestros para ciertas ecuaciones (ver ejemplo a continuación). Mientras sus matemáticas puedan ser verificadas por otra calculadora de producción, por favor no considere esto un error.
**EXAMPLE:** `3 + 5 x 6 - 2 / 4 =`
- **Lógica de ejecución inmediata:** `11.5`
- **Fórmula/Expresión lógica:** `32.5`
Puedes construir tu proyecto con <a href='https://codepen.io/pen?template=MJjpwO' target='_blank' rel='nofollow'>usando esta plantilla de CodePen</a> y haciendo clic en `Save` para crear tu propio borrador. O puedes utilizar este enlace CDN(Red de distribución de contenidos) para ejecutar las pruebas en cualquier entorno que desees: `https://cdn.freecodecamp.org/testable-projects-fcc/v1/bundle.js`
Una vez que hayas terminado, envía la URL de tu proyecto funcional con todos los tests aprobados.
# --solutions--
```js
// solution required
```

View File

@ -0,0 +1,39 @@
---
id: bd7157d8c242eddfaeb5bd13
title: Crea un previsualizador de Markdown
challengeType: 3
forumTopicId: 301372
dashedName: build-a-markdown-previewer
---
# --description--
**Objetivo:** Construye una aplicación en [CodePen.io](https://codepen.io) que funcionalmente sea similar a esta: <https://codepen.io/freeCodeCamp/full/GrZVVO>.
Completa las siguientes [historias de usuario](https://en.wikipedia.org/wiki/User_story) y consigue que se aprueben todas las pruebas. Dale tu estilo personal.
Puede utilizar cualquier mezcla de HTML, JavaScript, CSS, Bootstrap, SASS, React, Redux, y jQuery para completar este proyecto. Debe usar un framework frontend (como React por ejemplo), ya que esta sección trata sobre el aprendizaje de frameworks frontend. No se recomienda utilizar tecnologías adicionales a las indicadas anteriormente, su uso corre bajo tu propio riesgo. Estamos buscando dar soporte a otros frameworks de frontend como Angular y Vue, pero actualmente no están soportados. Aceptaremos e intentaremos arreglar todos los informes de incidencias que utilicen la pila de tecnología sugerida para este proyecto. ¡Feliz codificación!
**User Story #1:** Debe ver un elemento `textarea` con el correspondiente `id="editor"`.
**User Story #2:** Debe ver un elemento con el correspondiente `id="preview"`.
**User Story #3** Cuando introduzco texto dentro del elemento `#editor`, el elemento `#preview` se actualiza mientras escribo, para mostrar el contenido del textarea.
**Historia de usuario #4:** Cuando introduzco markdown con sabor a GitHub en el elemento `#editor`, el texto es renderizado como HTML en el elemento `#preview` mientras escribo (PISTA: No necesitas analizar markdown tú mismo - puedes importar la biblioteca marcada <https://cdnjs.com/libraries/marked>).
**Historia de usuario #5:** Cuando mi previsualizador de markdown se carga, el texto por defecto en el campo `#editor` debe contener un markdown válido que represente al menos uno de los siguientes elementos: un encabezado (tamaño H1), un subencabezado (tamaño H2), un enlace, código en línea, un bloque de código, un elemento de lista, una cita de bloques(blockquote), una imagen y texto en negrita.
**User Story #6:** Cuando mi previsualizador de markdown se carga, el markdown predeterminado en el campo `#editor` debe ser renderizado como HTML en el elemento `#preview`.
**Bonus opcional (No necesitas hacer esto para pasar esta prueba):** Mi previsualizador de markdown interpreta las devoluciones de carro y las renderiza como elementos `br` (salto de línea).
Puedes construir tu proyecto con <a href='https://codepen.io/pen?template=MJjpwO' target='_blank' rel='nofollow'>usando esta plantilla CodePen</a> y haciendo clic en `Save` para crear tu propio entorno. O puedes usar este enlace CDN(Red de distribución de contenidos) para ejecutar las pruebas en cualquier entorno de tu preferencia: `https://cdn.freecodecamp.org/testable-projects-fcc/v1/bundle.js`
Una vez que hayas terminado, envía la URL de tu proyecto funcional con todas las pruebas aprobadas.
# --solutions--
```js
// solution required
```

View File

@ -0,0 +1,49 @@
---
id: bd7158d8c442eddfaeb5bd13
title: Construye una máquina de citas al azar
challengeType: 3
forumTopicId: 301374
dashedName: build-a-random-quote-machine
---
# --description--
**Objetivo:** Construye una aplicación en [CodePen.io](https://codepen.io) que funcione de manera similar a esta: <https://codepen.io/freeCodeCamp/full/qRZeGZ>.
Rellena las siguientes [historias de usuario](https://en.wikipedia.org/wiki/User_story) y consigue que todas las pruebas pasen. Dale tu propio estilo personal.
Puedes usar cualquier mezcla de HTML, Javascript, CSS, Bootstrap, SASS, React, Redux, y jQuery para completar este proyecto. Puedes usar un framework de frontend (como React por ejemplo) porque esta sección trata sobre el aprendizaje de frameworks de frontend. No se recomienda utilizar tecnologías adicionales que no estén enlistadas, su utilización corre bajo su propio riesgo. Estamos buscando apoyar otros frameworks de frontend como Angular y Vue, pero actualmente no están soportados. Aceptaremos e intentaremos arreglar todos los informes de incidencias que utilicen el stack de tecnologías sugeridas para este proyecto. ¡Feliz día programando!
**Historia de usuario #1:** Puedo ver un elemento contenedor con un archivo `id="quote-box"`.
**Historia de usuario #2:** Dentro de `#quote-box`, puedo ver un elemento con su correspondiente `id="text"`.
**Historia de usuario #3:** Dentro de `#quote-box`, puedo ver un elemento con su correspondiente `id="author"`.
**Historia de usuario #4:** Dentro de `#quote-box`, puedo ver un elemento en el que se puede hacer clic con su correspondiente `id="new-quote"`.
**Historia de usuario #5:** Dentro de `#quote-box`, puedo ver un elemento clickeable `a` con su correspondiente `id="tweet-quote"`.
**Historia de usuario #6:** En el primer cargado, mi máquina de cotizaciones muestra una cita aleatoria en el elemento con `id="text"`.
**Historia de usuario #7:**En el primer cargado, mi máquina de cotizaciones muestra al autor de la cita aleatoria en el elemento con `id="author"`.
**Historia de usuario #8:** Cuando se hace clic en el botón `#new-quote`, mi máquina de cotizaciones debería buscar una nueva cita y mostrarla en el elemento `#text`.
**Historia de usuario #9:** Mi máquina de cotizaciones debería buscar al autor de la nueva cita cuando se hace click en el botón `#new-quote` y mostrarlo en el elemento `#author`.
**Historia de usuario #10:** Puedo tuitear la cita actual haciendo clic en `#tweet-quote` del elemento `a`. Este elemento `a` debe incluir la ruta `"twitter.com/intent/tweet"` en su atributo `href` para tuitear la cita actual.
**Historia de usuario #11:**El elemento contenedor `#quote-box` debe estar centrado horizontalmente. Ejecuta las pruebas con el nivel de zoom del navegador al 100% y la página maximizada.
Puedes crear tu proyecto <a href='https://codepen.io/pen?template=MJjpwO' target='_blank' rel='nofollow'>utilizando la plantilla de CodePen</a> y haciendo clic en `Save` para crear tu propio entorno. O puedes utilizar este enlace CDN para ejecutar cualquier prueba en cualquier entorno que desees: `https://cdn.freecodecamp.org/testable-projects-fcc/v1/bundle.js`
Una vez que hayas terminado, envía la URL a tu proyecto de trabajo con todas sus pruebas verificadas.
**Nota:** Twitter no permite que se carguen enlaces en un iframe. Intenta usar el atributo `target="_blank"` o `target="_top"` en el elemento `#tweet-quote` si tu tweet no se carga. `target="_top"` reemplazará la pestaña actual, así que asegúrate de que tu trabajo esté guardado.
# --solutions--
```js
// solution required
```

View File

@ -0,0 +1,131 @@
---
id: 564944c91be2204b269d51e3
title: Cambia el texto dentro de un elemento usando jQuery
challengeType: 6
forumTopicId: 16773
dashedName: change-text-inside-an-element-using-jquery
---
# --description--
Usando jQuery, puedes cambiar el texto entre las etiquetas de inicio y fin de un elemento. Puedes incluso cambiar el código HTML.
jQuery tiene una función llamada `.html()` que te permite añadir etiquetas HTML y texto dentro de un elemento. Cualquier contenido anterior dentro del elemento será completamente reemplazado con el contenido que proporciones usando esta función.
Así es como se reescribiría y enfatizaría el texto de nuestro título:
```js
$("h3").html("<em>jQuery Playground</em>");
```
jQuery también tiene una función similar llamada `.text()` que solo altera el texto sin añadir etiquetas. En otras palabras, esta función no evaluará las etiquetas HTML que se le pasen. En cambio, lo tomará como el texto con el que quieres reemplazar el contenido existente.
Cambia el botón con id `target4`, enfatizando su texto.
[ Ve a nuestro artículo sobre &lt;em>](https://www.freecodecamp.org/news/html-elements-explained-what-are-html-tags/#em-element) para aprender la diferencia entre `<i>` y `<em>` y sus usos.
Ten en cuenta que, aunque la etiqueta `<i>` se ha utilizado tradicionalmente para enfatizar texto, se ha adoptado su uso desde entonces como etiqueta para íconos. La etiqueta `<em>` es ahora ampliamente aceptada como la etiqueta de énfasis. Cualquiera de las dos servirá para este desafío.
# --hints--
Debes enfatizar el texto en el botón `target4` añadiendo etiquetas HTML.
```js
assert.isTrue(
/<em>|<i>\s*#target4\s*<\/em>|<\/i>/gi.test($('#target4').html())
);
```
De lo contrario, el texto debe permanecer inalterado.
```js
assert($('#target4') && $('#target4').text().trim() === '#target4');
```
No debes modificar ningún otro texto.
```js
assert.isFalse(/<em>|<i>/gi.test($('h3').html()));
```
Debes usar `.html()` y no `.text()`.
```js
assert(code.match(/\.html\(/g));
```
Debes seleccionar `button id="target4"` con jQuery.
```js
assert(code.match(/\$\(\s*?(\"|\')#target4(\"|\')\s*?\)\.html\(/));
```
# --seed--
## --seed-contents--
```html
<script>
$(document).ready(function() {
$("#target1").css("color", "red");
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```
# --solutions--
```html
<script>
$(document).ready(function() {
$("#target1").css("color", "red");
$("#target4").html('<em>#target4</em>');
});
</script>
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```

View File

@ -0,0 +1,119 @@
---
id: bad87fee1348bd9aed908826
title: Cambia el CSS de un elemento usando jQuery
challengeType: 6
forumTopicId: 16776
required:
-
link: 'https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.2.0/animate.css'
dashedName: change-the-css-of-an-element-using-jquery
---
# --description--
Podemos cambiar también el CSS de un elemento HTML directamente con jQuery.
jQuery tiene una función llamada `.css()` que te permite cambiar el CSS de un elemento.
Así es como cambiaríamos su color a azul:
```js
$("#target1").css("color", "blue");
```
Esto es ligeramente diferente de una declaración CSS normal, porque la propiedad CSS y su valor están entre comillas y separados con coma en lugar de con dos puntos.
Elimina tus selectores de jQuery, dejando `document ready function` vacía.
Selecciona `target1` y cambia su color a rojo.
# --hints--
Tu elemento `target1` debe tener texto rojo.
```js
assert($('#target1').css('color') === 'rgb(255, 0, 0)');
```
Debes usar solo jQuery para añadir estas clases al elemento.
```js
assert(!code.match(/class.*animated/g));
```
# --seed--
## --seed-contents--
```html
<script>
$(document).ready(function() {
$("button").addClass("animated bounce");
$(".well").addClass("animated shake");
$("#target3").addClass("animated fadeOut");
$("button").removeClass("btn-default");
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```
# --solutions--
```html
<script>
$(document).ready(function() {
$("button").addClass("animated bounce");
$(".well").addClass("animated shake");
$("#target3").addClass("animated fadeOut");
$("button").removeClass("btn-default");
$("#target1").css("color", "red");
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```

View File

@ -0,0 +1,121 @@
---
id: bad87fee1348bd9aed508826
title: Clona un elemento usando jQuery
challengeType: 6
forumTopicId: 16780
dashedName: clone-an-element-using-jquery
---
# --description--
Además de mover elementos, también puedes copiarlos de un lugar a otro.
jQuery tiene una función llamada `clone()` que hace una copia de un elemento.
Por ejemplo, si quisiéramos copìar `target2` de nuestro `left-well` a nuestro `right-well`, usaríamos:
```js
$("#target2").clone().appendTo("#right-well");
```
¿Notaste que esto implica usar dos funciones jQuery juntas? Esto es llamado <dfn>function chaining</dfn> (encadenamiento de funciones) y es una forma conveniente de hacer cosas con jQuery.
Clona tu elemento `target5` y añadelo a tu `left-well`.
# --hints--
Tu elemento `target5` debe estar dentro de `right-well`.
```js
assert($('#right-well').children('#target5').length > 0);
```
Una copia de tu elemento `target5` también debe estar dentro de `left-well`.
```js
assert($('#left-well').children('#target5').length > 0);
```
Sólo debes usar jQuery para mover estos elementos.
```js
assert(!code.match(/class.*animated/g));
```
# --seed--
## --seed-contents--
```html
<script>
$(document).ready(function() {
$("#target1").css("color", "red");
$("#target1").prop("disabled", true);
$("#target4").remove();
$("#target2").appendTo("#right-well");
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```
# --solutions--
```html
<script>
$(document).ready(function() {
$("#target1").css("color", "red");
$("#target1").prop("disabled", true);
$("#target4").remove();
$("#target2").appendTo("#right-well");
$("#target5").clone().appendTo("#left-well");
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```

View File

@ -0,0 +1,125 @@
---
id: bad87fee1348bd9aeda08726
title: Elimina tus funciones jQuery
challengeType: 6
forumTopicId: 17561
required:
-
link: 'https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.2.0/animate.css'
dashedName: delete-your-jquery-functions
---
# --description--
Estas animaciones fueron geniales al principio, pero ahora distraen un poco.
Elimina las tres funciones de jQuery dentro de tu `document ready function`, pero deja intacta tu función `document ready function` en sí.
# --hints--
Las tres funciones de jQuery deben ser eliminadas de la función `document ready function`.
```js
assert(code.match(/\{\s*\}\);/g));
```
Debes dejar intacto tu elemento `script`.
```js
assert(code.match(/<script>/g));
```
Debes dejar tu `$(document).ready(function() {` al principio de tu elemento `script`.
```js
assert(code.match(/\$\(document\)\.ready\(function\(\)\s?\{/g));
```
Debes dejar intacto el cierre `})` de la función `document.ready`.
```js
assert(code.match(/.*\s*\}\);/g));
```
Debes dejar intacta la etiqueta de cierre del elemento `script`.
```js
assert(
code.match(/<\/script>/g) &&
code.match(/<script/g) &&
code.match(/<\/script>/g).length === code.match(/<script/g).length
);
```
# --seed--
## --seed-contents--
```html
<script>
$(document).ready(function() {
$("button").addClass("animated bounce");
$(".well").addClass("animated shake");
$("#target3").addClass("animated fadeOut");
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```
# --solutions--
```html
<script>
$(document).ready(function() {
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```

View File

@ -0,0 +1,120 @@
---
id: bad87fee1348bd9aed808826
title: Deshabilita un elemento usando jQuery
challengeType: 6
forumTopicId: 17563
dashedName: disable-an-element-using-jquery
---
# --description--
También puedes cambiar las demás propiedades (además de las propiedades CSS) de los elementos HTML con jQuery. Por ejemplo, puedes deshabilitar botones.
Cuando deshabilitas un botón, se mostrará en gris y no se podrá hacer clic sobre este.
jQuery tiene una función llamada `.prop()` que te permite ajustar las propiedades de los elementos.
Así es como deshabilitarías todos los botones:
```js
$("button").prop("disabled", true);
```
Deshabilita solo el botón `target1`.
# --hints--
Tu botón `target1` debe ser deshabilitado.
```js
assert(
$('#target1') &&
$('#target1').prop('disabled') &&
code.match(/["']disabled["'],( true|true)/g)
);
```
Ningún otro botón debe estar deshabilitado.
```js
assert($('#target2') && !$('#target2').prop('disabled'));
```
Solo debes usar jQuery para añadir estas clases al elemento.
```js
assert(!code.match(/disabled[^<]*>/g));
```
# --seed--
## --seed-contents--
```html
<script>
$(document).ready(function() {
$("#target1").css("color", "red");
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```
# --solutions--
```html
<script>
$(document).ready(function() {
$("#target1").css("color", "red");
$("#target1").prop("disabled", true);
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```

View File

@ -0,0 +1,118 @@
---
id: bad87fee1348bd9acdd08826
title: Aprende como funciona la etiqueta script y la función "document ready"
challengeType: 6
forumTopicId: 18224
dashedName: learn-how-script-tags-and-document-ready-work
---
# --description--
Ahora estamos listos para aprender jQuery, la herramienta de JavaScript más popular de todos los tiempos.
Antes de empezar a usar jQuery, necesitamos añadir algunas cosas a nuestro HTML.
Primero, añade un elemento `script` en la parte superior de tu página. Asegúrate de cerrarlo en la siguiente línea.
Tu navegador ejecutará cualquier JavaScript dentro de un elemento `script`, incluyendo jQuery.
Dentro de tu elemento `script`, añade este código: `$(document).ready(function() {` a tu `script`. Luego ciérralo en la siguiente línea (todavía dentro de tu elemento `script`) con: `});`
Aprenderemos más sobre `functions` más tarde. Lo importante a saber es que el código que pongas dentro de esta `function` (función) se ejecutará tan pronto como tu navegador haya cargado tu página.
Esto es importante porque sin tu `document ready function`, tu código podría ejecutarse antes de que se procese tu HTML, lo que causaría errores.
# --hints--
Debes crear un elemento `script` asegurándote de que sea válido y tenga una etiqueta de cierre.
```js
assert(
code.match(/<\/script\s*>/g) &&
code.match(
/<script(\sasync|\sdefer)*(\s(charset|src|type)\s*=\s*["\"]+[^"\"]*["\"]+)*(\sasync|\sdefer)*\s*>/g
) &&
code.match(/<\/script\s*>/g).length ===
code.match(
/<script(\sasync|\sdefer)*(\s(charset|src|type)\s*=\s*["\"]+[^"\"]*["\"]+)*(\sasync|\sdefer)*\s*>/g
).length
);
```
Debes añadir `$(document).ready(function() {` al principio de tu elemento `script`.
```js
assert(
code.match(
/\$\s*?\(\s*?document\s*?\)\.ready\s*?\(\s*?function\s*?\(\s*?\)\s*?\{/g
)
);
```
Debes cerrar tu función `$(document).ready(function() {` con `});`
```js
assert(code.match(/\n*?\s*?\}\s*?\);/g));
```
# --seed--
## --seed-contents--
```html
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```
# --solutions--
```html
<script>
$(document).ready(function() {
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```

View File

@ -0,0 +1,109 @@
---
id: bad87fee1348bd9aed708826
title: Remueve un elemento usando jQuery
challengeType: 6
forumTopicId: 18262
dashedName: remove-an-element-using-jquery
---
# --description--
Ahora vamos a eliminar un elemento HTML de su página utilizando jQuery.
jQuery tiene una función llamada `.remove()` que eliminará completamente un elemento HTML
Remueve el elemento `#target4` de la página utilizando la función `.remove()`.
# --hints--
Debes utilizar jQuery para remover tu elemento `target4` de tu página.
```js
assert(
$('#target4').length === 0 && code.match(/\$\(["']#target4["']\).remove\(\)/g)
);
```
Sólo debes utilizar jQuery para remover este elemento.
```js
assert(
code.match(/id="target4/g) &&
!code.match(/<!--.*id="target4".*-->/g) &&
$('#right-well').length > 0
);
```
# --seed--
## --seed-contents--
```html
<script>
$(document).ready(function() {
$("#target1").css("color", "red");
$("#target1").prop("disabled", true);
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```
# --solutions--
```html
<script>
$(document).ready(function() {
$("#target1").css("color", "red");
$("#target1").prop("disabled", true);
$("#target4").remove();
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```

View File

@ -0,0 +1,122 @@
---
id: bad87fee1348bd9aed918626
title: Elimina clases de un elemento con jQuery
challengeType: 6
forumTopicId: 18264
required:
-
link: 'https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.2.0/animate.css'
dashedName: remove-classes-from-an-element-with-jquery
---
# --description--
De la misma manera en que puedes agregar clases a un elemento con la función `addClass()` de jQuery, puedes eliminarlos con la función `removeClass()` de jQuery.
Así es como lo harías para un botón específico:
```js
$("#target2").removeClass("btn-default");
```
Eliminemos la clase `btn-default` de todos nuestros elementos `button`.
# --hints--
La clase `btn-default` debe ser eliminada de todos los elementos `button`.
```js
assert($('.btn-default').length === 0);
```
Sólo debes usar jQuery para eliminar esta clase del elemento.
```js
assert(code.match(/btn btn-default/g));
```
Solo debes eliminar la clase `btn-default`.
```js
assert(
code.match(
/\.[\v\s]*removeClass[\s\v]*\([\s\v]*('|")\s*btn-default\s*('|")[\s\v]*\)/gm
)
);
```
# --seed--
## --seed-contents--
```html
<script>
$(document).ready(function() {
$("button").addClass("animated bounce");
$(".well").addClass("animated shake");
$("#target3").addClass("animated fadeOut");
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```
# --solutions--
```html
<script>
$(document).ready(function() {
$("button").addClass("animated bounce");
$(".well").addClass("animated shake");
$("#target3").addClass("animated fadeOut");
$("button").removeClass("btn-default");
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```

View File

@ -0,0 +1,144 @@
---
id: bad87fee1348bd9aed108826
title: Apunta a un hijo específico de un elemento usando jQuery
challengeType: 6
forumTopicId: 18315
required:
-
link: 'https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.2.0/animate.css'
dashedName: target-a-specific-child-of-an-element-using-jquery
---
# --description--
Has visto por qué los atributos id son muy convenientes para apuntar con los selectores de jQuery. Pero no siempre tendrás tan buenos id's con los que trabajar.
Afortunadamente, jQuery tiene algunos otros trucos para apuntar a los elementos correctos.
jQuery usa selectores de CSS para apuntar elementos. El selector `target:nth-child(n)` de CSS, te permite seleccionar todos los enésimos elementos con el tipo de elemento o la clase apuntada.
Así es como le darías al tercer elemento en cada pozo la clase de rebote:
```js
$(".target:nth-child(3)").addClass("animated bounce");
```
Has que el segundo niño en cada uno de sus elementos de pozo rebote. Debes seleccionar los hijos de los elementos con la clase `target`.
# --hints--
El segundo elemento en tus elementos `target` deben rebotar.
```js
assert(
$('.target:nth-child(2)').hasClass('animated') &&
$('.target:nth-child(2)').hasClass('bounce')
);
```
Solo deben elegirse dos elementos.
```js
assert($('.animated.bounce').length === 2);
```
Debes usar el selector `:nth-child()` para modificar estos elementos.
```js
assert(code.match(/\:nth-child\(/g));
```
Solo debes usar jQuery para añadir estas clases al elemento.
```js
assert(
code.match(/\$\(".target:nth-child\(2\)"\)/g) ||
code.match(/\$\('.target:nth-child\(2\)'\)/g) ||
code.match(/\$\(".target"\).filter\(":nth-child\(2\)"\)/g) ||
code.match(/\$\('.target'\).filter\(':nth-child\(2\)'\)/g)
);
```
# --seed--
## --seed-contents--
```html
<script>
$(document).ready(function() {
$("#target1").css("color", "red");
$("#target1").prop("disabled", true);
$("#target4").remove();
$("#target2").appendTo("#right-well");
$("#target5").clone().appendTo("#left-well");
$("#target1").parent().css("background-color", "red");
$("#right-well").children().css("color", "orange");
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```
# --solutions--
```html
<script>
$(document).ready(function() {
$("#target1").css("color", "red");
$("#target1").prop("disabled", true);
$("#target4").remove();
$("#target2").appendTo("#right-well");
$("#target5").clone().appendTo("#left-well");
$("#target1").parent().css("background-color", "red");
$("#right-well").children().css("color", "orange");
$(".target:nth-child(2)").addClass("animated bounce");
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```

View File

@ -0,0 +1,113 @@
---
id: bad87fee1348bd9aedc08826
title: Apunta a elementos por clase usando jQuery
challengeType: 6
forumTopicId: 18316
required:
-
link: 'https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.2.0/animate.css'
dashedName: target-elements-by-class-using-jquery
---
# --description--
¿Ves como hicimos que todos tus elementos `button` reboten? Los seleccionamos con `$("button")`, luego agregamos algo de clases CSS a ellos con `.addClass("animated bounce");`.
Acabas de usar la función `.addClass()` de jQuery, que te permite añadir clases a los elementos.
Primero, apuntemos tus elementos `div` con la clase `well` usando el selector `$(".well")`.
Ten en cuenta que, al igual que con las declaraciones CSS, escribes un `.` antes del nombre de la clase.
Luego usa la función `.addClass()` de jQuery para añadir las clases `animated` y `shake`.
Por ejemplo, puedes hacer que todos los elementos con la clase `text-primary` se agiten agregando lo siguiente a tu función `document ready function`:
```js
$(".text-primary").addClass("animated shake");
```
# --hints--
Debes usar la función jQuery `addClass()` para dar las clases `animated` y `shake` a todos tus elementos con la clase `well`.
```js
assert($('.well').hasClass('animated') && $('.well').hasClass('shake'));
```
Solo debes usar jQuery para añadir estas clases al elemento.
```js
assert(!code.match(/class\.\*animated/g));
```
# --seed--
## --seed-contents--
```html
<script>
$(document).ready(function() {
$("button").addClass("animated bounce");
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```
# --solutions--
```html
<script>
$(document).ready(function() {
$("button").addClass("animated bounce");
$(".well").addClass("animated shake");
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```

View File

@ -0,0 +1,123 @@
---
id: bad87fee1348bd9aeda08826
title: Apunta a elementos por clase usando jQuery
challengeType: 6
forumTopicId: 18317
required:
-
link: 'https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.2.0/animate.css'
dashedName: target-elements-by-id-using-jquery
---
# --description--
También puedes apuntar a elementos por su atributo id.
Primero apunta a tu elemento `button` con el id `target3` usando el selector `$("#target3")`.
Ten en cuenta que, al igual que con las declaraciones CSS, escribes un `#` antes del nombre del id.
Luego usa la función `.addClass()` de jQuery para añadir las clases `animated` y `fadeOut`.
Así es como harás que el elemento `button` con el id `target6` se desvanezca:
```js
$("#target6").addClass("animated fadeOut");
```
# --hints--
Debes seleccionar el elemento `button` con el `id` de `target3` y utilizar la función jQuery `addClass()` para darle la clase `animated`.
```js
assert($('#target3').hasClass('animated'));
```
Debes seleccionar el elemento con el id `target3` y utilizar la función jQuery `addClass()` para darle la clase `fadeOut`.
```js
assert(
($('#target3').hasClass('fadeOut') || $('#target3').hasClass('fadeout')) &&
code.match(/\$\(\s*.#target3.\s*\)/g)
);
```
Solo debes usar jQuery para agregar estas clases al elemento.
```js
assert(!code.match(/class.*animated/g));
```
# --seed--
## --seed-contents--
```html
<script>
$(document).ready(function() {
$("button").addClass("animated bounce");
$(".well").addClass("animated shake");
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```
# --solutions--
```html
<script>
$(document).ready(function() {
$("button").addClass("animated bounce");
$(".well").addClass("animated shake");
$("#target3").addClass("animated fadeOut");
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```

View File

@ -0,0 +1,139 @@
---
id: bad87fee1348bd9aed008826
title: Apunta a elementos pares utilizando jQuery
challengeType: 6
forumTopicId: 18318
required:
-
link: 'https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.2.0/animate.css'
dashedName: target-even-elements-using-jquery
---
# --description--
También puedes apuntar a elementos basado en sus posiciones, usando los selectores `:odd` o `:even`.
Ten en cuenta que jQuery es indexado desde el cero, lo que significa que el primer elemento de una selección tiene la posición 0. Esto puede ser un poco confuso, ya que contra-intuitivamente, `:odd` selecciona el segundo elemento (posición 1), cuarto elemento (posición 3), y así sucesivamente.
Así es como apuntas a todos los elementos impares con la clase `target` y les das clases:
```js
$(".target:odd").addClass("animated shake");
```
Intenta seleccionar todos los elementos pares `target` y darle las clases de `animated` y `shake`. Recuerda que **even** se refiere a la posición de los elementos con un sistema de indexado desde cero en mente.
# --hints--
Todos los elementos `target` que jQuery considera que son pares deberían agitarse.
```js
assert(
$('.target:even').hasClass('animated') && $('.target:even').hasClass('shake')
);
```
Debes usar el selector `:even` para modificar estos elementos.
```js
assert(code.match(/\:even/g));
```
Solo debes usar jQuery para añadir estas clases al elemento.
```js
assert(
code.match(/\$\(".target:even"\)/g) ||
code.match(/\$\('.target:even'\)/g) ||
code.match(/\$\(".target"\).filter\(":even"\)/g) ||
code.match(/\$\('.target'\).filter\(':even'\)/g)
);
```
# --seed--
## --seed-contents--
```html
<script>
$(document).ready(function() {
$("#target1").css("color", "red");
$("#target1").prop("disabled", true);
$("#target4").remove();
$("#target2").appendTo("#right-well");
$("#target5").clone().appendTo("#left-well");
$("#target1").parent().css("background-color", "red");
$("#right-well").children().css("color", "orange");
$("#left-well").children().css("color", "green");
$(".target:nth-child(2)").addClass("animated bounce");
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```
# --solutions--
```html
<script>
$(document).ready(function() {
$("#target1").css("color", "red");
$("#target1").prop("disabled", true);
$("#target4").remove();
$("#target2").appendTo("#right-well");
$("#target5").clone().appendTo("#left-well");
$("#target1").parent().css("background-color", "red");
$("#right-well").children().css("color", "orange");
$("#left-well").children().css("color", "green");
$(".target:nth-child(2)").addClass("animated bounce");
$(".target:even").addClass("animated shake");
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```

View File

@ -0,0 +1,118 @@
---
id: bad87fee1348bd9bedc08826
title: Apunta a elementos HTML con selectores usando jQuery
challengeType: 6
forumTopicId: 18319
required:
-
link: 'https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.2.0/animate.css'
dashedName: target-html-elements-with-selectors-using-jquery
---
# --description--
Ahora tenemos una función `document ready`.
Ahora vamos a escribir nuestra primera declaración de jQuery. Todas las funciones de jQuery comienzan con un `$`, usualmente conocido como un operador de signo de dólares.
jQuery a menudo selecciona un elemento HTML con un <dfn>selector</dfn>, y luego le hace algo a ese elemento.
Por ejemplo, hagamos que todos tus elementos `button` reboten. Simplemente añade este código dentro de tu función de documento listo (document ready):
```js
$("button").addClass("animated bounce");
```
Ten en cuenta que ya hemos incluido tanto la librería jQuery como la librería Animate.css en segundo plano para que puedas usarlos en el editor. Así que estás usando jQuery para aplicar la clase Animate.css `bounce` a tus elementos `button`.
# --hints--
Debes usar la función de jQuery `addClass()` para dar las clases `animated` y `bounce` a tus elementos `button`.
```js
assert($('button').hasClass('animated') && $('button').hasClass('bounce'));
```
Solo debes usar jQuery para añadir estas clases al elemento.
```js
assert(!code.match(/class.*animated/g));
```
Tu código de jQuery debería estar dentro de la función `$(document).ready();`.
```js
assert(
code.replace(/\s/g, '').match(/\$\(document\)\.ready\(function\(\)\{\$/g)
);
```
# --seed--
## --seed-contents--
```html
<script>
$(document).ready(function() {
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```
# --solutions--
```html
<script>
$(document).ready(function() {
$("button").addClass("animated bounce");
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```

View File

@ -0,0 +1,125 @@
---
id: bad87fee1348bd9aed208826
title: Apunta al hijo de un elemento usando jQuery
challengeType: 6
forumTopicId: 18320
dashedName: target-the-children-of-an-element-using-jquery
---
# --description--
Cuando los elementos HTML se colocan un nivel por debajo de otro, se denominan <dfn>hijos</dfn> de ese elemento. Por ejemplo, los elementos botón en este desafío con el texto `#target1`, `#target2`, y `#target3` son todos hijos del elemento `<div class="well" id="left-well">`.
jQuery tiene una función llamada `children()` que te permite acceder a los hijos del elemento que hayas seleccionado.
Aquí hay un ejemplo de cómo usarías la función `children()` para dar a los hijos de tu elemento `left-well` el color `blue`:
```js
$("#left-well").children().css("color", "blue")
```
# --instructions--
Dale a todos los hijos de tu elemento `right-well` el color naranja.
# --hints--
Todos los hijos de `#right-well` deben tener texto naranja.
```js
assert($('#right-well').children().css('color') === 'rgb(255, 165, 0)');
```
Debes usar la función `children()` para modificar estos elementos.
```js
assert(code.match(/\.children\(\)\.css/g));
```
Solo debes usar jQuery para agregar estas clases al elemento.
```js
assert(code.match(/<div class="well" id="right-well">/g));
```
# --seed--
## --seed-contents--
```html
<script>
$(document).ready(function() {
$("#target1").css("color", "red");
$("#target1").prop("disabled", true);
$("#target4").remove();
$("#target2").appendTo("#right-well");
$("#target5").clone().appendTo("#left-well");
$("#target1").parent().css("background-color", "red");
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```
# --solutions--
```html
<script>
$(document).ready(function() {
$("#target1").css("color", "red");
$("#target1").prop("disabled", true);
$("#target4").remove();
$("#target2").appendTo("#right-well");
$("#target5").clone().appendTo("#left-well");
$("#target1").parent().css("background-color", "red");
$("#right-well").children().css("color", "orange");
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```

View File

@ -0,0 +1,140 @@
---
id: bad87fee1348bd9aed308826
title: Apunta al padre de un elemento usando jQuery
challengeType: 6
forumTopicId: 18321
dashedName: target-the-parent-of-an-element-using-jquery
---
# --description--
Cada elemento HTML tiene un elemento `parent` (padre) del cual `inherits` (hereda) propiedades.
Por ejemplo, tu elemento `jQuery Playground` `h3` tiene el elemento padre de `<div class="container-fluid">`, que en sí mismo tiene el padre `body`.
jQuery tiene una función llamada `parent()` que te permite acceder al padre del elemento que hayas seleccionado.
Aquí hay un ejemplo de cómo usarías la función `parent()` si quieres dar al elemento padre del elemento `left-well` un color de fondo azul:
```js
$("#left-well").parent().css("background-color", "blue")
```
Dale al padre del elemento `#target1` un color de fondo rojo.
# --hints--
Tu elemento `left-well` debe tener un fondo rojo.
```js
assert(
$('#left-well').css('background-color') === 'red' ||
$('#left-well').css('background-color') === 'rgb(255, 0, 0)' ||
$('#left-well').css('background-color').toLowerCase() === '#ff0000' ||
$('#left-well').css('background-color').toLowerCase() === '#f00'
);
```
Debes usar la función `.parent()` para modificar este elemento.
```js
assert(code.match(/\.parent\s*\(\s*\)\s*\.css/g));
```
El método `.parent()` debe ser llamado en el elemento `#target1`.
```js
assert(
code.match(/\$\s*?\(\s*?(?:'|")\s*?#target1\s*?(?:'|")\s*?\)\s*?\.parent/gi)
);
```
Solo debes usar jQuery para añadir estas clases al elemento.
```js
assert(code.match(/<div class="well" id="left-well">/g));
```
# --seed--
## --seed-contents--
```html
<script>
$(document).ready(function() {
$("#target1").css("color", "red");
$("#target1").prop("disabled", true);
$("#target4").remove();
$("#target2").appendTo("#right-well");
$("#target5").clone().appendTo("#left-well");
});
</script>
<!-- Only change code above this line -->
<body>
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
</body>
```
# --solutions--
```html
<script>
$(document).ready(function() {
$("#target1").css("color", "red");
$("#target1").prop("disabled", true);
$("#target4").remove();
$("#target2").appendTo("#right-well");
$("#target5").clone().appendTo("#left-well");
$("#target1").parent().css("background-color", "red");
});
</script>
<!-- Only change code above this line -->
<body>
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
</body>
```

View File

@ -0,0 +1,143 @@
---
id: bad87fee1348bd9aed908626
title: Apunta al mismo elemento con múltiples selectores jQuery
challengeType: 6
forumTopicId: 18322
required:
-
link: 'https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.2.0/animate.css'
dashedName: target-the-same-element-with-multiple-jquery-selectors
---
# --description--
Ahora conoces tres maneras de seleccionar elementos: por tipo: `$("button")`, por clase: `$(".btn")`, y por id `$("#target1")`.
Aunque es posible añadir múltiples clases en una sola llamada `.addClass()`, vamos a añadirlos al mismo elemento en *tres maneras diferentes*.
Usando `.addClass()`, añade solo una clase a la vez al mismo elemento, de tres maneras diferentes:
Añade la clase `animated` a todos los elementos con el tipo `button`.
Añade la clase `shake` a todos los botones con la clase `.btn`.
Añade la clase `btn-primary` al botón con id `#target1`.
**Nota:** Solo debes apuntar a un elemento y añadir solo una clase a la vez. En total, tus tres selectores individuales terminarán agregando las tres clases `shake`, `animated`, y `btn-primary` a `#target1`.
# --hints--
Tu código debe usar el selector `$("button")`.
```js
assert(code.match(/\$\s*?\(\s*?(?:'|")\s*?button\s*?(?:'|")/gi));
```
Tu código debe usar el selector `$(".btn")`.
```js
assert(code.match(/\$\s*?\(\s*?(?:'|")\s*?\.btn\s*?(?:'|")/gi));
```
Tu código debe usar el selector `$("#target1")`.
```js
assert(code.match(/\$\s*?\(\s*?(?:'|")\s*?#target1\s*?(?:'|")/gi));
```
Solo debes añadir una clase con cada uno de tus tres selectores.
```js
assert(
code.match(/addClass/g) &&
code.match(/addClass\s*?\(\s*?('|")\s*?[\w-]+\s*?\1\s*?\)/g).length > 2
);
```
Tu elemento `#target1` debe tener las clases `animated`, `shake` y `btn-primary`.
```js
assert(
$('#target1').hasClass('animated') &&
$('#target1').hasClass('shake') &&
$('#target1').hasClass('btn-primary')
);
```
Solo debes usar jQuery para agregar estas clases al elemento.
```js
assert(!code.match(/class.*animated/g));
```
# --seed--
## --seed-contents--
```html
<script>
$(document).ready(function() {
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```
# --solutions--
```html
<script>
$(document).ready(function() {
$("button").addClass("animated");
$(".btn").addClass("shake");
$("#target1").addClass("btn-primary");
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```

View File

@ -0,0 +1,117 @@
---
id: bad87fee1348bd9aed608826
title: Usa appendTo para mover elementos con jQuery
challengeType: 6
forumTopicId: 18340
dashedName: use-appendto-to-move-elements-with-jquery
---
# --description--
Ahora intentemos mover elementos de un `div` a otro.
jQuery tiene una función llamada `appendTo()` que te permite seleccionar elementos HTML y añadirlos a otro elemento.
Por ejemplo, si quisiéramos mover `target4` desde nuestro "right well" a nuestro "left well", usaríamos:
```js
$("#target4").appendTo("#left-well");
```
Mueve tu elemento `target2` desde tu `left-well` a tu `right-well`.
# --hints--
Tu elemento `target2` debe estar dentro de `left-well`.
```js
assert($('#left-well').children('#target2').length === 0);
```
Tu elemento `target2` debe estar dentro de `right-well`.
```js
assert($('#right-well').children('#target2').length > 0);
```
Sólo debes usar jQuery para mover estos elementos.
```js
assert(!code.match(/class.*animated/g));
```
# --seed--
## --seed-contents--
```html
<script>
$(document).ready(function() {
$("#target1").css("color", "red");
$("#target1").prop("disabled", true);
$("#target4").remove();
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```
# --solutions--
```html
<script>
$(document).ready(function() {
$("#target1").css("color", "red");
$("#target1").prop("disabled", true);
$("#target4").remove();
$("#target2").appendTo("#right-well");
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```

View File

@ -0,0 +1,118 @@
---
id: bad87fee1348bd9aecb08826
title: Usa jQuery para modificar la página completa
challengeType: 6
forumTopicId: 18361
required:
-
link: 'https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.2.0/animate.css'
dashedName: use-jquery-to-modify-the-entire-page
---
# --description--
Hemos terminado de jugar con nuestra área de juego de jQuery. ¡Vamos a derribarlo!
jQuery también puede seleccionar al elemento `body`.
Así es como haríamos que el cuerpo entero se desvaneciera: `$("body").addClass("animated fadeOut");`
Pero hagamos algo más dramático. Añade las clases `animated` y `hinge` a tu elemento `body`.
# --hints--
Debes añadir las clases `animated` y `hinge` a tu elemento `body`.
```js
assert($('body').hasClass('animated') && $('body').hasClass('hinge'));
```
# --seed--
## --seed-contents--
```html
<script>
$(document).ready(function() {
$("#target1").css("color", "red");
$("#target1").prop("disabled", true);
$("#target4").remove();
$("#target2").appendTo("#right-well");
$("#target5").clone().appendTo("#left-well");
$("#target1").parent().css("background-color", "red");
$("#right-well").children().css("color", "orange");
$("#left-well").children().css("color", "green");
$(".target:nth-child(2)").addClass("animated bounce");
$(".target:even").addClass("animated shake");
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```
# --solutions--
```html
<script>
$(document).ready(function() {
$("#target1").css("color", "red");
$("#target1").prop("disabled", true);
$("#target4").remove();
$("#target2").appendTo("#right-well");
$("#target5").clone().appendTo("#left-well");
$("#target1").parent().css("background-color", "red");
$("#right-well").children().css("color", "orange");
$("#left-well").children().css("color", "green");
$(".target:nth-child(2)").addClass("animated bounce");
$(".target:even").addClass("animated shake");
$("body").addClass("animated hinge");
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```

View File

@ -0,0 +1,156 @@
---
id: 5a24c314108439a4d4036147
title: Conecta Redux a React
challengeType: 6
forumTopicId: 301426
dashedName: connect-redux-to-react
---
# --description--
Ahora que has escrito tanto la función `mapStateToProps()` como la función `mapDispatchToProps()`, puedes usarlos para asignar `state` y `dispatch` a las `props` de uno de tus componentes React. El método `connect` de React Redux puede manejar esta tarea. Este método toma dos argumentos opcionales, `mapStateToProps()` y `mapDispatchToProps()`. Son opcionales porque puedes tener un componente que solo necesita acceso al `state` pero no necesita enviar ninguna acción, o viceversa.
Para utilizar este método, pasa las funciones como argumentos, y llama inmediatamente al resultado con tu componente. Esta sintaxis es un poco inusual y se ve así:
```js
connect(mapStateToProps, mapDispatchToProps)(MyComponent)
```
**Nota:** Si deseas omitir uno de los argumentos del método `connect`, pasa `null` en su lugar.
# --instructions--
El editor de código tiene las funciones `mapStateToProps()` y `mapDispatchToProps()` y un nuevo componente React llamado `Presentational`. Conecta este componente a Redux con el método `connect` del objeto global `ReactRedux`, y llámalo inmediatamente en el componente `Presentational`. Asigna el resultado a una nueva `const` llamada `ConnectedComponent` que representa el componente conectado. Eso es todo, ¡ahora estás conectado a Redux! Intenta cambiar cualquiera de los argumentos de `connect` a `null` y observa los resultados de la prueba.
# --hints--
El componente `Presentational` debe renderizarse.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
return mockedComponent.find('Presentational').length === 1;
})()
);
```
El componente `Presentational` debe recibir una prop `messages` a través de `connect`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
const props = mockedComponent.find('Presentational').props();
return props.messages === '__INITIAL__STATE__';
})()
);
```
El componente `Presentational` debe recibir una prop `submitNewMessage` a través de `connect`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
const props = mockedComponent.find('Presentational').props();
return typeof props.submitNewMessage === 'function';
})()
);
```
# --seed--
## --after-user-code--
```jsx
const store = Redux.createStore(
(state = '__INITIAL__STATE__', action) => state
);
class AppWrapper extends React.Component {
render() {
return (
<ReactRedux.Provider store = {store}>
<ConnectedComponent/>
</ReactRedux.Provider>
);
}
};
ReactDOM.render(<AppWrapper />, document.getElementById('root'))
```
## --seed-contents--
```jsx
const addMessage = (message) => {
return {
type: 'ADD',
message: message
}
};
const mapStateToProps = (state) => {
return {
messages: state
}
};
const mapDispatchToProps = (dispatch) => {
return {
submitNewMessage: (message) => {
dispatch(addMessage(message));
}
}
};
class Presentational extends React.Component {
constructor(props) {
super(props);
}
render() {
return <h3>This is a Presentational Component</h3>
}
};
const connect = ReactRedux.connect;
// Change code below this line
```
# --solutions--
```jsx
const addMessage = (message) => {
return {
type: 'ADD',
message: message
}
};
const mapStateToProps = (state) => {
return {
messages: state
}
};
const mapDispatchToProps = (dispatch) => {
return {
submitNewMessage: (message) => {
dispatch(addMessage(message));
}
}
};
class Presentational extends React.Component {
constructor(props) {
super(props);
}
render() {
return <h3>This is a Presentational Component</h3>
}
};
const connect = ReactRedux.connect;
// Change code below this line
const ConnectedComponent = connect(mapStateToProps, mapDispatchToProps)(Presentational);
```

View File

@ -0,0 +1,300 @@
---
id: 5a24c314108439a4d4036148
title: Conecta Redux a la aplicación de mensajes
challengeType: 6
forumTopicId: 301427
dashedName: connect-redux-to-the-messages-app
---
# --description--
Ahora que entiendes cómo usar `connect` para conectar React a Redux, puedes aplicar lo que has aprendido a tu componente React que maneja mensajes.
En la última lección, el componente que conectaste a Redux se llamó `Presentational`, y esto no fue arbitrario. Este término *generalmente* se refiere a componentes React que no están directamente conectados a Redux. Ellos simplemente son responsables de la presentación de la interfaz de usuario y lo hacen en función de las props que reciben. Por el contrario, los contenedores componentes están conectados a Redux. Estos son típicamente responsables de enviar acciones al store y a menudo pasan el estado del store a componentes secundarios como props.
# --instructions--
El editor tiene todo el código que has escrito hasta ahora en esta sección. El único cambio es que el componente React se renombra a `Presentational`. Crea un nuevo componente en una constante llamada `Container` que usa `connect` para conectar el componente `Presentational` a Redux. Luego, en el `AppWrapper`, renderiza el componente `Provider` de React Redux. Pasa a `Provider` el `store` Redux como una prop y renderiza `Container` como un hijo. Una vez que todo esté configurado, verás que los mensajes de la aplicación son visualizados nuevamente en la página.
# --hints--
El `AppWrapper` debe renderizarse.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
return mockedComponent.find('AppWrapper').length === 1;
})()
);
```
El componente `Presentational` debe renderizarse.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
return mockedComponent.find('Presentational').length === 1;
})()
);
```
El componente `Presentational` debe renderizar los siguientes elementos: un `h2`, `input`, `button`, y `ul`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
const PresentationalComponent = mockedComponent.find('Presentational');
return (
PresentationalComponent.find('div').length === 1 &&
PresentationalComponent.find('h2').length === 1 &&
PresentationalComponent.find('button').length === 1 &&
PresentationalComponent.find('ul').length === 1
);
})()
);
```
El componente `Presentational` debe recibir `messages` desde el store Redux como una prop.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
const PresentationalComponent = mockedComponent.find('Presentational');
const props = PresentationalComponent.props();
return Array.isArray(props.messages);
})()
);
```
El componente `Presentational` debe recibir la acción `submitMessage` como una prop.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
const PresentationalComponent = mockedComponent.find('Presentational');
const props = PresentationalComponent.props();
return typeof props.submitNewMessage === 'function';
})()
);
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<AppWrapper />, document.getElementById('root'))
```
## --seed-contents--
```jsx
// Redux:
const ADD = 'ADD';
const addMessage = (message) => {
return {
type: ADD,
message: message
}
};
const messageReducer = (state = [], action) => {
switch (action.type) {
case ADD:
return [
...state,
action.message
];
default:
return state;
}
};
const store = Redux.createStore(messageReducer);
// React:
class Presentational extends React.Component {
constructor(props) {
super(props);
this.state = {
input: '',
messages: []
}
this.handleChange = this.handleChange.bind(this);
this.submitMessage = this.submitMessage.bind(this);
}
handleChange(event) {
this.setState({
input: event.target.value
});
}
submitMessage() {
this.setState((state) => {
const currentMessage = state.input;
return {
input: '',
messages: state.messages.concat(currentMessage)
};
});
}
render() {
return (
<div>
<h2>Type in a new Message:</h2>
<input
value={this.state.input}
onChange={this.handleChange}/><br/>
<button onClick={this.submitMessage}>Submit</button>
<ul>
{this.state.messages.map( (message, idx) => {
return (
<li key={idx}>{message}</li>
)
})
}
</ul>
</div>
);
}
};
// React-Redux:
const mapStateToProps = (state) => {
return { messages: state }
};
const mapDispatchToProps = (dispatch) => {
return {
submitNewMessage: (newMessage) => {
dispatch(addMessage(newMessage))
}
}
};
const Provider = ReactRedux.Provider;
const connect = ReactRedux.connect;
// Define the Container component here:
class AppWrapper extends React.Component {
constructor(props) {
super(props);
}
render() {
// Complete the return statement:
return (null);
}
};
```
# --solutions--
```jsx
// Redux:
const ADD = 'ADD';
const addMessage = (message) => {
return {
type: ADD,
message: message
}
};
const messageReducer = (state = [], action) => {
switch (action.type) {
case ADD:
return [
...state,
action.message
];
default:
return state;
}
};
const store = Redux.createStore(messageReducer);
// React:
class Presentational extends React.Component {
constructor(props) {
super(props);
this.state = {
input: '',
messages: []
}
this.handleChange = this.handleChange.bind(this);
this.submitMessage = this.submitMessage.bind(this);
}
handleChange(event) {
this.setState({
input: event.target.value
});
}
submitMessage() {
this.setState((state) => {
const currentMessage = state.input;
return {
input: '',
messages: state.messages.concat(currentMessage)
};
});
}
render() {
return (
<div>
<h2>Type in a new Message:</h2>
<input
value={this.state.input}
onChange={this.handleChange}/><br/>
<button onClick={this.submitMessage}>Submit</button>
<ul>
{this.state.messages.map( (message, idx) => {
return (
<li key={idx}>{message}</li>
)
})
}
</ul>
</div>
);
}
};
// React-Redux:
const mapStateToProps = (state) => {
return { messages: state }
};
const mapDispatchToProps = (dispatch) => {
return {
submitNewMessage: (newMessage) => {
dispatch(addMessage(newMessage))
}
}
};
const Provider = ReactRedux.Provider;
const connect = ReactRedux.connect;
const Container = connect(mapStateToProps, mapDispatchToProps)(Presentational);
class AppWrapper extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<Provider store={store}>
<Container/>
</Provider>
);
}
};
```

View File

@ -0,0 +1,397 @@
---
id: 5a24c314108439a4d4036149
title: Extrae el estado local en Redux
challengeType: 6
forumTopicId: 301428
dashedName: extract-local-state-into-redux
---
# --description--
¡Ya casi terminas! Recuerda que escribiste todo el código Redux para que Redux pudiera controlar la gestión del estado de tu aplicación de mensajes React. Ahora que Redux está conectado, necesitas extraer la gestión del estado de `Presentational` y añadirlo a Redux. Actualmente, tienes Redux conectado, pero estás manejando el estado localmente dentro del componente `Presentational`.
# --instructions--
En el componente `Presentational`, primero elimina la propiedad `messages` del local `state`. Estos mensajes serán gestionados por Redux. A continuación, modifica el método `submitMessage()` para que `submitNewMessage()` trabaje desde `this.props` y pase la entrada del mensaje actual desde el `state` local como un argumento. Ya que eliminaste `messages` desde el estado local, elimina también la propiedad `messages` de la llamada a `this.setState()`. Finalmente, modifica el método `render()` para que asigne los mensajes recibidos desde `props` en lugar de `state`.
Una vez realizados estos cambios, la aplicación seguirá funcionando igual, salvo que Redux gestiona el estado. Este ejemplo también ilustra cómo un componente puede tener un `state` local: tu componente aún registra la entrada del usuario localmente en su propio `state`. Puedes ver cómo Redux proporciona un framework útil de gestión de estados sobre React. Alcanzaste el mismo resultado usando solo el estado local de React al principio, y esto es generalmente posible con aplicaciones simples. Sin embargo, cuanto más complejas y grandes se vuelve tus aplicaciones, más lo hará la gestión del estado, y esto es el problema que Redux resuelve.
# --hints--
`AppWrapper` debe renderizarse.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
return mockedComponent.find('AppWrapper').length === 1;
})()
);
```
El componente `Presentational` debe renderizarse.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
return mockedComponent.find('Presentational').length === 1;
})()
);
```
El componente `Presentational` debe renderizar los siguientes elementos: `h2`, `input`, `button` y `ul`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
const PresentationalComponent = mockedComponent.find('Presentational');
return (
PresentationalComponent.find('div').length === 1 &&
PresentationalComponent.find('h2').length === 1 &&
PresentationalComponent.find('button').length === 1 &&
PresentationalComponent.find('ul').length === 1
);
})()
);
```
El componente `Presentational` debe recibir `messages` desde el almacenamiento de Redux como una propiedad.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
const PresentationalComponent = mockedComponent.find('Presentational');
const props = PresentationalComponent.props();
return Array.isArray(props.messages);
})()
);
```
El componente `Presentational` debe recibir `submitMessage` como una propiedad.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
const PresentationalComponent = mockedComponent.find('Presentational');
const props = PresentationalComponent.props();
return typeof props.submitNewMessage === 'function';
})()
);
```
El estado del componente `Presentational` debe contener una propiedad, `input`, que está inicializada a una cadena vacía.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
const PresentationalState = mockedComponent
.find('Presentational')
.instance().state;
return (
typeof PresentationalState.input === 'string' &&
Object.keys(PresentationalState).length === 1
);
})()
);
```
Escribir el elemento `input` debe actualizar el estado del componente `Presentational`.
```js
async () => {
const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
const testValue = '__MOCK__INPUT__';
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 100));
const causeChange = (c, v) =>
c.find('input').simulate('change', { target: { value: v } });
let initialInput = mockedComponent.find('Presentational').find('input');
const changed = () => {
causeChange(mockedComponent, testValue);
return waitForIt(() => mockedComponent);
};
const updated = await changed();
const updatedInput = updated.find('Presentational').find('input');
assert(
initialInput.props().value === '' &&
updatedInput.props().value === '__MOCK__INPUT__'
);
};
```
`submitMessage` del componente `Presentational` debe actualizar el almacenamiento de Redux y vaciar la entrada en el estado local.
```js
async () => {
const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 100));
let beforeProps = mockedComponent.find('Presentational').props();
const testValue = '__TEST__EVENT__INPUT__';
const causeChange = (c, v) =>
c.find('input').simulate('change', { target: { value: v } });
const changed = () => {
causeChange(mockedComponent, testValue);
return waitForIt(() => mockedComponent);
};
const clickButton = () => {
mockedComponent.find('button').simulate('click');
return waitForIt(() => mockedComponent);
};
const afterChange = await changed();
const afterChangeInput = afterChange.find('input').props().value;
const afterClick = await clickButton();
const afterProps = mockedComponent.find('Presentational').props();
assert(
beforeProps.messages.length === 0 &&
afterChangeInput === testValue &&
afterProps.messages.pop() === testValue &&
afterClick.find('input').props().value === ''
);
};
```
El componente `Presentational` debe renderizar `messages` desde el almacenamiento de Redux.
```js
async () => {
const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 100));
let beforeProps = mockedComponent.find('Presentational').props();
const testValue = '__TEST__EVENT__INPUT__';
const causeChange = (c, v) =>
c.find('input').simulate('change', { target: { value: v } });
const changed = () => {
causeChange(mockedComponent, testValue);
return waitForIt(() => mockedComponent);
};
const clickButton = () => {
mockedComponent.find('button').simulate('click');
return waitForIt(() => mockedComponent);
};
const afterChange = await changed();
const afterChangeInput = afterChange.find('input').props().value;
const afterClick = await clickButton();
const afterProps = mockedComponent.find('Presentational').props();
assert(
beforeProps.messages.length === 0 &&
afterChangeInput === testValue &&
afterProps.messages.pop() === testValue &&
afterClick.find('input').props().value === '' &&
afterClick.find('ul').childAt(0).text() === testValue
);
};
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<AppWrapper />, document.getElementById('root'))
```
## --seed-contents--
```jsx
// Redux:
const ADD = 'ADD';
const addMessage = (message) => {
return {
type: ADD,
message: message
}
};
const messageReducer = (state = [], action) => {
switch (action.type) {
case ADD:
return [
...state,
action.message
];
default:
return state;
}
};
const store = Redux.createStore(messageReducer);
// React:
const Provider = ReactRedux.Provider;
const connect = ReactRedux.connect;
// Change code below this line
class Presentational extends React.Component {
constructor(props) {
super(props);
this.state = {
input: '',
messages: []
}
this.handleChange = this.handleChange.bind(this);
this.submitMessage = this.submitMessage.bind(this);
}
handleChange(event) {
this.setState({
input: event.target.value
});
}
submitMessage() {
this.setState((state) => ({
input: '',
messages: state.messages.concat(state.input)
}));
}
render() {
return (
<div>
<h2>Type in a new Message:</h2>
<input
value={this.state.input}
onChange={this.handleChange}/><br/>
<button onClick={this.submitMessage}>Submit</button>
<ul>
{this.state.messages.map( (message, idx) => {
return (
<li key={idx}>{message}</li>
)
})
}
</ul>
</div>
);
}
};
// Change code above this line
const mapStateToProps = (state) => {
return {messages: state}
};
const mapDispatchToProps = (dispatch) => {
return {
submitNewMessage: (message) => {
dispatch(addMessage(message))
}
}
};
const Container = connect(mapStateToProps, mapDispatchToProps)(Presentational);
class AppWrapper extends React.Component {
render() {
return (
<Provider store={store}>
<Container/>
</Provider>
);
}
};
```
# --solutions--
```jsx
// Redux:
const ADD = 'ADD';
const addMessage = (message) => {
return {
type: ADD,
message: message
}
};
const messageReducer = (state = [], action) => {
switch (action.type) {
case ADD:
return [
...state,
action.message
];
default:
return state;
}
};
const store = Redux.createStore(messageReducer);
// React:
const Provider = ReactRedux.Provider;
const connect = ReactRedux.connect;
// Change code below this line
class Presentational extends React.Component {
constructor(props) {
super(props);
this.state = {
input: ''
}
this.handleChange = this.handleChange.bind(this);
this.submitMessage = this.submitMessage.bind(this);
}
handleChange(event) {
this.setState({
input: event.target.value
});
}
submitMessage() {
this.props.submitNewMessage(this.state.input);
this.setState({
input: ''
});
}
render() {
return (
<div>
<h2>Type in a new Message:</h2>
<input
value={this.state.input}
onChange={this.handleChange}/><br/>
<button onClick={this.submitMessage}>Submit</button>
<ul>
{this.props.messages.map( (message, idx) => {
return (
<li key={idx}>{message}</li>
)
})
}
</ul>
</div>
);
}
};
// Change code above this line
const mapStateToProps = (state) => {
return {messages: state}
};
const mapDispatchToProps = (dispatch) => {
return {
submitNewMessage: (message) => {
dispatch(addMessage(message))
}
}
};
const Container = connect(mapStateToProps, mapDispatchToProps)(Presentational);
class AppWrapper extends React.Component {
render() {
return (
<Provider store={store}>
<Container/>
</Provider>
);
}
};
```

View File

@ -0,0 +1,115 @@
---
id: 5a24c314108439a4d4036143
title: Extrae la lógica de estado a Redux
challengeType: 6
forumTopicId: 301429
dashedName: extract-state-logic-to-redux
---
# --description--
Ahora que has terminado el componente React, necesitas mover la lógica que está realizando localmente en su `state` hacia Redux. Este es el primer paso para conectar la aplicación simple de React a Redux. La única funcionalidad que tiene tu aplicación es añadir nuevos mensajes del usuario a una lista desordenada. El ejemplo es simple para demostrar cómo React y Redux trabajan juntos.
# --instructions--
En primer lugar, define un tipo de acción `ADD` y asígnalo a una const `ADD`. A continuación, define un creador de acciones `addMessage()` que crea la acción para añadir un mensaje. Tendrás que pasar un `message` a este creador de acciones e incluir el mensaje en la `action` devuelta.
Luego crea un reductor llamado `messageReducer()` que maneja el estado de los mensajes. El estado inicial debe ser igual a un arreglo vacío. Este reductor debe añadir un mensaje al arreglo de mensajes mantenido en el estado, o devolver el estado actual. Finalmente, crea tu almacén Redux y pásale el reductor.
# --hints--
La const `ADD` debe existir y mantener un valor igual a la cadena `ADD`
```js
assert(ADD === 'ADD');
```
El creador de acción `addMessage` debe devolver un objeto con `type` igual a `ADD` y `message` igual al mensaje que se pasa.
```js
assert(
(function () {
const addAction = addMessage('__TEST__MESSAGE__');
return addAction.type === ADD && addAction.message === '__TEST__MESSAGE__';
})()
);
```
`messageReducer` debe ser una función.
```js
assert(typeof messageReducer === 'function');
```
El almacén debe existir y tener un estado inicial establecido a un arreglo vacío.
```js
assert(
(function () {
const initialState = store.getState();
return typeof store === 'object' && initialState.length === 0;
})()
);
```
El envío de `addMessage` contra el almacén debe añadir un nuevo mensaje de forma inmutable al arreglo de mensajes mantenido en el estado.
```js
assert(
(function () {
const initialState = store.getState();
const isFrozen = DeepFreeze(initialState);
store.dispatch(addMessage('__A__TEST__MESSAGE'));
const addState = store.getState();
return isFrozen && addState[0] === '__A__TEST__MESSAGE';
})()
);
```
El `messageReducer` debe devolver el estado actual si se llama con cualquier otra acción.
```js
assert(
(function () {
const addState = store.getState();
store.dispatch({ type: 'FAKE_ACTION' });
const testState = store.getState();
return addState === testState;
})()
);
```
# --seed--
## --seed-contents--
```jsx
// Define ADD, addMessage(), messageReducer(), and store here:
```
# --solutions--
```jsx
const ADD = 'ADD';
const addMessage = (message) => {
return {
type: ADD,
message
}
};
const messageReducer = (state = [], action) => {
switch (action.type) {
case ADD:
return [
...state,
action.message
];
default:
return state;
}
};
const store = Redux.createStore(messageReducer);
```

View File

@ -0,0 +1,102 @@
---
id: 5a24c314108439a4d4036141
title: Introducción a React Redux
challengeType: 6
forumTopicId: 301430
dashedName: getting-started-with-react-redux
---
# --description--
Esta serie de desafíos presenta cómo utilizar Redux con React. En primer lugar, repasamos algunos de los principios clave de cada tecnología. React es una librería de vistas a la que le proporcionas datos y luego renderiza la vista de forma eficiente y predecible. Redux es un framework de gestión de estados que puedes utilizar para simplificar la gestión del estado de tu aplicación. Por lo general, en una aplicación React Redux, se crea un único almacén Redux que gestiona el estado de toda la aplicación. Tus componentes de React se suscriben sólo a las piezas de datos del almacén que son relevantes para su función. Luego, se envían acciones directamente desde los componentes de React, que luego activan las actualizaciones del almacén.
Aunque los componentes de React pueden gestionar su propio estado localmente, cuando se tiene una aplicación compleja, generalmente es mejor mantener el estado de la aplicación en una sola ubicación con Redux. Hay excepciones cuando los componentes individuales pueden tener un estado local específico sólo para ellos. Por último, debido a que Redux no está diseñado para trabajar con React de fábrica, es necesario utilizar el paquete `react-redux`. Proporciona una forma para pasar Redux `state` y `dispatch` a tus componentes React como `props`.
A lo largo de los siguientes desafíos, primero crearás un simple componente React que te permita introducir nuevos mensajes de texto. Estos se añaden a un arreglo que se muestra en la vista. Esto debería ser un buen repaso de lo aprendido en las lecciones de React. A continuación, crearás un almacén Redux y acciones que gestionen el estado del arreglo de mensajes. Por último, utilizarás `react-redux` para conectar el almacén Redux con tu componente, extrayendo así el estado local en el almacén Redux.
# --instructions--
Comienza con un componente `DisplayMessages`. Añade un constructor a este componente e inicialízalo con un estado que tenga dos propiedades: `input`, que se establece como una cadena vacía, y `messages`, que se establece como un arreglo vacío.
# --hints--
El componente `DisplayMessages` debe mostrar un elemento `div` vacío.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(DisplayMessages));
return mockedComponent.find('div').text() === '';
})()
);
```
El constructor `DisplayMessages` debe ser llamado correctamente con `super`, pasando `props`.
```js
(getUserInput) =>
assert(
(function () {
const noWhiteSpace = __helpers.removeWhiteSpace(getUserInput('index'));
return (
noWhiteSpace.includes('constructor(props)') &&
noWhiteSpace.includes('super(props')
);
})()
);
```
El componente `DisplayMessages` debe tener un estado inicial igual a `{input: "", messages: []}`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(DisplayMessages));
const initialState = mockedComponent.state();
return (
typeof initialState === 'object' &&
initialState.input === '' &&
Array.isArray(initialState.messages) &&
initialState.messages.length === 0
);
})()
);
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<DisplayMessages />, document.getElementById('root'))
```
## --seed-contents--
```jsx
class DisplayMessages extends React.Component {
// Change code below this line
// Change code above this line
render() {
return <div />
}
};
```
# --solutions--
```jsx
class DisplayMessages extends React.Component {
constructor(props) {
super(props);
this.state = {
input: '',
messages: []
}
}
render() {
return <div/>
}
};
```

View File

@ -0,0 +1,260 @@
---
id: 5a24c314108439a4d4036142
title: Gestiona el estado localmente primero
challengeType: 6
forumTopicId: 301431
dashedName: manage-state-locally-first
---
# --description--
Aquí terminarás de crear el componente `DisplayMessages`.
# --instructions--
En primer lugar, en el método `render()`, haz que el componente renderice un elemento `input`, un elemento `button` y un elemento `ul`. Cuando el elemento `input` cambia, debe activar un método `handleChange()`. Además, el elemento `input` debe renderizar el valor de `input` que está en el estado del componente. El elemento `button` debe activar un método `submitMessage()` cuando se hace clic en él.
En segundo lugar, escribe estos dos métodos. El método `handleChange()` debe actualizar el `input` con lo que el usuario está escribiendo. El método `submitMessage()` debe concatenar el mensaje actual (almacenado en `input`) al arreglo `messages` del estado local, y borrar el valor de `input`.
Por último, utiliza el `ul` para asignar el arreglo de `messages` y renderizarlo en la pantalla como una lista de elementos `li`.
# --hints--
El componente `DisplayMessages` debe inicializarse con un estado igual a `{ input: "", messages: [] }`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(DisplayMessages));
const initialState = mockedComponent.state();
return (
typeof initialState === 'object' &&
initialState.input === '' &&
initialState.messages.length === 0
);
})()
);
```
El componente `DisplayMessages` debe renderizar un `div` que contenga un elemento `h2`, un elemento `button`, un elemento `ul` y elementos `li` como hijos.
```js
async () => {
const mockedComponent = Enzyme.mount(React.createElement(DisplayMessages));
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 100));
const state = () => {
mockedComponent.setState({ messages: ['__TEST__MESSAGE'] });
return waitForIt(() => mockedComponent);
};
const updated = await state();
assert(
updated.find('div').length === 1 &&
updated.find('h2').length === 1 &&
updated.find('button').length === 1 &&
updated.find('ul').length === 1 &&
updated.find('li').length > 0
);
};
```
`.map` debe usarse en el arreglo `messages`.
```js
assert(code.match(/this\.state\.messages\.map/g));
```
El elemento `input` debe renderizar el valor de `input` en estado local.
```js
async () => {
const mockedComponent = Enzyme.mount(React.createElement(DisplayMessages));
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 100));
const causeChange = (c, v) =>
c.find('input').simulate('change', { target: { value: v } });
const testValue = '__TEST__EVENT__INPUT';
const changed = () => {
causeChange(mockedComponent, testValue);
return waitForIt(() => mockedComponent);
};
const updated = await changed();
assert(updated.find('input').props().value === testValue);
};
```
La llamada al método `handleChange` debe actualizar el valor de `input` en estado a la entrada actual.
```js
async () => {
const mockedComponent = Enzyme.mount(React.createElement(DisplayMessages));
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 100));
const causeChange = (c, v) =>
c.find('input').simulate('change', { target: { value: v } });
const initialState = mockedComponent.state();
const testMessage = '__TEST__EVENT__MESSAGE__';
const changed = () => {
causeChange(mockedComponent, testMessage);
return waitForIt(() => mockedComponent);
};
const afterInput = await changed();
assert(
initialState.input === '' &&
afterInput.state().input === '__TEST__EVENT__MESSAGE__'
);
};
```
Al hacer clic en el botón `Add message` se debe llamar al método `submitMessage` que debe añadir el `input` actual al arreglo `messages` del estado.
```js
async () => {
const mockedComponent = Enzyme.mount(React.createElement(DisplayMessages));
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 100));
const causeChange = (c, v) =>
c.find('input').simulate('change', { target: { value: v } });
const initialState = mockedComponent.state();
const testMessage_1 = '__FIRST__MESSAGE__';
const firstChange = () => {
causeChange(mockedComponent, testMessage_1);
return waitForIt(() => mockedComponent);
};
const firstResult = await firstChange();
const firstSubmit = () => {
mockedComponent.find('button').simulate('click');
return waitForIt(() => mockedComponent);
};
const afterSubmit_1 = await firstSubmit();
const submitState_1 = afterSubmit_1.state();
const testMessage_2 = '__SECOND__MESSAGE__';
const secondChange = () => {
causeChange(mockedComponent, testMessage_2);
return waitForIt(() => mockedComponent);
};
const secondResult = await secondChange();
const secondSubmit = () => {
mockedComponent.find('button').simulate('click');
return waitForIt(() => mockedComponent);
};
const afterSubmit_2 = await secondSubmit();
const submitState_2 = afterSubmit_2.state();
assert(
initialState.messages.length === 0 &&
submitState_1.messages.length === 1 &&
submitState_2.messages.length === 2 &&
submitState_2.messages[1] === testMessage_2
);
};
```
El método `submitMessage` debe borrar la entrada actual.
```js
async () => {
const mockedComponent = Enzyme.mount(React.createElement(DisplayMessages));
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 100));
const causeChange = (c, v) =>
c.find('input').simulate('change', { target: { value: v } });
const initialState = mockedComponent.state();
const testMessage = '__FIRST__MESSAGE__';
const firstChange = () => {
causeChange(mockedComponent, testMessage);
return waitForIt(() => mockedComponent);
};
const firstResult = await firstChange();
const firstState = firstResult.state();
const firstSubmit = () => {
mockedComponent.find('button').simulate('click');
return waitForIt(() => mockedComponent);
};
const afterSubmit = await firstSubmit();
const submitState = afterSubmit.state();
assert(firstState.input === testMessage && submitState.input === '');
};
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<DisplayMessages />, document.getElementById('root'))
```
## --seed-contents--
```jsx
class DisplayMessages extends React.Component {
constructor(props) {
super(props);
this.state = {
input: '',
messages: []
}
}
// Add handleChange() and submitMessage() methods here
render() {
return (
<div>
<h2>Type in a new Message:</h2>
{ /* Render an input, button, and ul below this line */ }
{ /* Change code above this line */ }
</div>
);
}
};
```
# --solutions--
```jsx
class DisplayMessages extends React.Component {
constructor(props) {
super(props);
this.state = {
input: '',
messages: []
}
this.handleChange = this.handleChange.bind(this);
this.submitMessage = this.submitMessage.bind(this);
}
handleChange(event) {
this.setState({
input: event.target.value
});
}
submitMessage() {
this.setState((state) => {
const currentMessage = state.input;
return {
input: '',
messages: state.messages.concat(currentMessage)
};
});
}
render() {
return (
<div>
<h2>Type in a new Message:</h2>
<input
value={this.state.input}
onChange={this.handleChange}/><br/>
<button onClick={this.submitMessage}>Submit</button>
<ul>
{this.state.messages.map( (message, idx) => {
return (
<li key={idx}>{message}</li>
)
})
}
</ul>
</div>
);
}
};
```

View File

@ -0,0 +1,107 @@
---
id: 5a24c314108439a4d4036146
title: Asigna el envío a props
challengeType: 6
forumTopicId: 301432
dashedName: map-dispatch-to-props
---
# --description--
La función `mapDispatchToProps()` se utiliza para proporcionar creadores de acción específicos a tus componentes React para que puedan enviar acciones contra el almacén Redux. Su estructura es similar a la función `mapStateToProps()` que escribiste en el último desafío. Devuelve un objeto que asigna acciones de envío a nombres de propiedades, que se convierten en `props` del componente. Sin embargo, en lugar de devolver una pieza de `state`, cada propiedad devuelve una función que llama a `dispatch` con un creador de acciones y cualquier dato relevante de la acción. Tienes acceso a este `dispatch` porque se pasa a `mapDispatchToProps()` como parámetro cuando defines la función, igual que pasaste `state` a `mapStateToProps()`. Tras bambalinas, React Redux utiliza `store.dispatch()` para realizar estos envíos con `mapDispatchToProps()`. Esto es similar a cómo se utiliza `store.subscribe()` para los componentes que se asignan a `state`.
Por ejemplo, tienes un creador de acción `loginUser()` que toma un `username` como carga útil de acción. El objeto devuelto por `mapDispatchToProps()` para este creador de acción se vería algo como:
```jsx
{
submitLoginUser: function(username) {
dispatch(loginUser(username));
}
}
```
# --instructions--
El editor de código proporciona un creador de acción llamado `addMessage()`. Escribe la función `mapDispatchToProps()` que toma `dispatch` como argumento y devuelve un objeto. El objeto debe tener una propiedad `submitNewMessage` establecida en la función de envío, que toma un parámetro para el nuevo mensaje a añadir cuando envía `addMessage()`.
# --hints--
`addMessage` debe devolver un objeto con las claves `type` y `message`.
```js
assert(
(function () {
const addMessageTest = addMessage();
return (
addMessageTest.hasOwnProperty('type') &&
addMessageTest.hasOwnProperty('message')
);
})()
);
```
`mapDispatchToProps` debe ser una función.
```js
assert(typeof mapDispatchToProps === 'function');
```
`mapDispatchToProps` debe devolver un objeto.
```js
assert(typeof mapDispatchToProps() === 'object');
```
El envío de `addMessage` con `submitNewMessage` desde `mapDispatchToProps` debe devolver un mensaje a la función de envío.
```js
assert(
(function () {
let testAction;
const dispatch = (fn) => {
testAction = fn;
};
let dispatchFn = mapDispatchToProps(dispatch);
dispatchFn.submitNewMessage('__TEST__MESSAGE__');
return (
testAction.type === 'ADD' && testAction.message === '__TEST__MESSAGE__'
);
})()
);
```
# --seed--
## --seed-contents--
```jsx
const addMessage = (message) => {
return {
type: 'ADD',
message: message
}
};
// Change code below this line
```
# --solutions--
```jsx
const addMessage = (message) => {
return {
type: 'ADD',
message: message
}
};
// Change code below this line
const mapDispatchToProps = (dispatch) => {
return {
submitNewMessage: function(message) {
dispatch(addMessage(message));
}
}
};
```

View File

@ -0,0 +1,69 @@
---
id: 5a24c314108439a4d4036145
title: Asigna el estado a props
challengeType: 6
forumTopicId: 301433
dashedName: map-state-to-props
---
# --description--
El componente `Provider` te permite proporcionar `state` y `dispatch` a tus componentes React, pero debes especificar exactamente qué estado y acciones quieres. De esta manera, te aseguras de que cada componente sólo tiene acceso al estado que necesita. Esto se consigue creando dos funciones: `mapStateToProps()` y `mapDispatchToProps()`.
En estas funciones, declaras a qué partes del estado quieres tener acceso y qué creadores de acción necesitas poder enviar. Una vez que estas funciones están en su lugar, verás cómo usar el método React Redux `connect` para conectarlos a tus componentes en otro desafío.
**Nota:** Tras bambalinas, React Redux utiliza el método `store.subscribe()` para implementar `mapStateToProps()`.
# --instructions--
Crea una función `mapStateToProps()`. Esta función debe tomar `state` como argumento, y luego devolver un objeto que asigna ese estado a nombres de propiedades específicas. Estas propiedades serán accesibles a tu componente a través de `props`. Dado que este ejemplo mantiene todo el estado de la aplicación en un solo arreglo, puedes pasar todo ese estado a tu componente. Crea una propiedad `messages` en el objeto que se devuelve, y establécela como `state`.
# --hints--
La const `state` debe ser un arreglo vacío.
```js
assert(Array.isArray(state) && state.length === 0);
```
`mapStateToProps` debe ser una función.
```js
assert(typeof mapStateToProps === 'function');
```
`mapStateToProps` debe devolver un objeto.
```js
assert(typeof mapStateToProps() === 'object');
```
Pasar un arreglo como estado a `mapStateToProps` debe devolver este arreglo asignado a una clave de `messages`.
```js
assert(mapStateToProps(['messages']).messages.pop() === 'messages');
```
# --seed--
## --seed-contents--
```jsx
const state = [];
// Change code below this line
```
# --solutions--
```jsx
const state = [];
// Change code below this line
const mapStateToProps = (state) => {
return {
messages: state
}
};
```

View File

@ -0,0 +1,69 @@
---
id: 5a24c314108439a4d403614a
title: Avanzando desde aquí
challengeType: 6
forumTopicId: 301434
dashedName: moving-forward-from-here
---
# --description--
¡Felicidades! Has terminado las lecciones sobre React y Redux. Hay un último punto que vale la pena señalar antes de seguir adelante. Por lo general, no escribirás aplicaciones React en un editor de código como este. Este desafío te da una idea de cómo es la sintaxis si trabajas con npm y un sistema de archivos en tu propia máquina. El código debería ser similar, excepto por el uso de las sentencias `import` (éstas traen todas las dependencias que se te han proporcionado en los desafíos). La sección "Gestión de paquetes con npm" cubre npm con más detalle.
Por último, escribir código React y Redux generalmente requiere cierta configuración. Esto puede complicarse rápidamente. Si te interesa experimentar en tu propia máquina, <a href="https://github.com/facebookincubator/create-react-app" target="_blank" rel="nofollow">Create React App</a> viene configurado y listo para funcionar.
Alternativamente, puedes habilitar Babel como un preprocesador de JavaScript en CodePen, añadir React y ReactDOM como recursos externos de JavaScript, y trabajar allí también.
# --instructions--
Imprime el mensaje `'Now I know React and Redux!'` en la consola.
# --hints--
El mensaje `Now I know React and Redux!` debe imprimirse en la consola.
```js
(getUserInput) =>
assert(
/console\s*\.\s*log\s*\(\s*('|"|`)Now I know React and Redux!\1\s*\)/.test(
getUserInput('index')
)
);
```
# --seed--
## --seed-contents--
```jsx
/*
import React from 'react'
import ReactDOM from 'react-dom'
import { Provider, connect } from 'react-redux'
import { createStore, combineReducers, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import rootReducer from './redux/reducers'
import App from './components/App'
const store = createStore(
rootReducer,
applyMiddleware(thunk)
);
ReactDOM.render(
<Provider store={store}>
<App/>
</Provider>,
document.getElementById('root')
);
*/
// Only change code below this line
```
# --solutions--
```jsx
console.log('Now I know React and Redux!');
```

View File

@ -0,0 +1,263 @@
---
id: 5a24c314108439a4d4036144
title: Usa "Provider" para conectar Redux a React
challengeType: 6
forumTopicId: 301435
dashedName: use-provider-to-connect-redux-to-react
---
# --description--
En el último desafío, creaste un almacén Redux para manejar el arreglo de mensajes y creaste una acción para añadir nuevos mensajes. El siguiente paso es proporcionar a React acceso al almacén Redux y a las acciones que necesita para enviar las actualizaciones. React Redux proporciona su paquete `react-redux` para ayudar a realizar estas tareas.
React Redux ofrece si API con dos características clave: `Provider` y `connect`. Otro desafío cubre `connect`. El `Provider` es un componente envolvente de React Redux que envuelve tu aplicación React. Esta envoltura te permite acceder a las funciones Redux `store` y `dispatch` a través de tu árbol de componentes. `Provider` toma dos props, el almacén Redux y los componentes hijos de tu aplicación. La definición del `Provider` para un componente de la App podría ser así:
```jsx
<Provider store={store}>
<App/>
</Provider>
```
# --instructions--
El editor de código ahora muestra todo tu código Redux y React de los últimos desafíos. Incluye el almacén Redux, las acciones y el componente `DisplayMessages`. La única pieza nueva es el componente `AppWrapper` de la parte inferior. Usa este componente de nivel superior para renderizar el `Provider` de `ReactRedux`, y pasa el almacén Redux como prop. Luego, renderiza el componente `DisplayMessages` como hijo. Una vez que hayas terminado, deberías ver tu componente React renderizado en la página.
**Nota:** React Redux está disponible como una variable global aquí, por lo que puedes acceder al "Provider" con notación de puntos. El código del editor aprovecha esto y lo establece en una constante `Provider` para que lo uses en el método de renderizado `AppWrapper`.
# --hints--
El `AppWrapper` debe renderizarse.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
return mockedComponent.find('AppWrapper').length === 1;
})()
);
```
El componente envolvente `Provider` debe tener una prop de `store` pasada, igual al almacén de Redux.
```js
(getUserInput) =>
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
return __helpers
.removeWhiteSpace(getUserInput('index'))
.includes('<Providerstore={store}>');
})()
);
```
`DisplayMessages` debe renderizarse como hijo de `AppWrapper`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
return (
mockedComponent.find('AppWrapper').find('DisplayMessages').length === 1
);
})()
);
```
El componente `DisplayMessages` debe renderizar un elemento `h2`, `input`, `button` y `ul`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
return (
mockedComponent.find('div').length === 1 &&
mockedComponent.find('h2').length === 1 &&
mockedComponent.find('button').length === 1 &&
mockedComponent.find('ul').length === 1
);
})()
);
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<AppWrapper />, document.getElementById('root'))
```
## --seed-contents--
```jsx
// Redux:
const ADD = 'ADD';
const addMessage = (message) => {
return {
type: ADD,
message
}
};
const messageReducer = (state = [], action) => {
switch (action.type) {
case ADD:
return [
...state,
action.message
];
default:
return state;
}
};
const store = Redux.createStore(messageReducer);
// React:
class DisplayMessages extends React.Component {
constructor(props) {
super(props);
this.state = {
input: '',
messages: []
}
this.handleChange = this.handleChange.bind(this);
this.submitMessage = this.submitMessage.bind(this);
}
handleChange(event) {
this.setState({
input: event.target.value
});
}
submitMessage() {
this.setState((state) => {
const currentMessage = state.input;
return {
input: '',
messages: state.messages.concat(currentMessage)
};
});
}
render() {
return (
<div>
<h2>Type in a new Message:</h2>
<input
value={this.state.input}
onChange={this.handleChange}/><br/>
<button onClick={this.submitMessage}>Submit</button>
<ul>
{this.state.messages.map( (message, idx) => {
return (
<li key={idx}>{message}</li>
)
})
}
</ul>
</div>
);
}
};
const Provider = ReactRedux.Provider;
class AppWrapper extends React.Component {
// Render the Provider below this line
// Change code above this line
};
```
# --solutions--
```jsx
// Redux:
const ADD = 'ADD';
const addMessage = (message) => {
return {
type: ADD,
message
}
};
const messageReducer = (state = [], action) => {
switch (action.type) {
case ADD:
return [
...state,
action.message
];
default:
return state;
}
};
const store = Redux.createStore(messageReducer);
// React:
class DisplayMessages extends React.Component {
constructor(props) {
super(props);
this.state = {
input: '',
messages: []
}
this.handleChange = this.handleChange.bind(this);
this.submitMessage = this.submitMessage.bind(this);
}
handleChange(event) {
this.setState({
input: event.target.value
});
}
submitMessage() {
this.setState((state) => {
const currentMessage = state.input;
return {
input: '',
messages: state.messages.concat(currentMessage)
};
});
}
render() {
return (
<div>
<h2>Type in a new Message:</h2>
<input
value={this.state.input}
onChange={this.handleChange}/><br/>
<button onClick={this.submitMessage}>Submit</button>
<ul>
{this.state.messages.map( (message, idx) => {
return (
<li key={idx}>{message}</li>
)
})
}
</ul>
</div>
);
}
};
const Provider = ReactRedux.Provider;
class AppWrapper extends React.Component {
// Change code below this line
render() {
return (
<Provider store = {store}>
<DisplayMessages/>
</Provider>
);
}
// Change code above this line
};
```

View File

@ -0,0 +1,165 @@
---
id: 5a24c314108439a4d403616e
title: Accede a propiedades "props" usando this.props
challengeType: 6
forumTopicId: 301375
dashedName: access-props-using-this-props
---
# --description--
Los últimos desafíos cubrieron las formas básicas de pasar propiedades a un componente hijo. Pero, ¿qué pasa si el componente hijo al que se le pasa una propiedad es un componente de clase ES6, en lugar de un componente funcional sin estado? Los componentes de clase ES6 usan una convención un poco diferente para acceder a las propiedades.
Cada vez que se hace referencia a un componente de clase en sí mismo, se utiliza la palabra clave `this`. Para acceder a las propiedades dentro de un componente de clase, se antepone al código que se utiliza para acceder a él con `this`. Por ejemplo, si un componente de clase de ES6 tiene una propiedad llamada `data`, se escribirá `{this.props.data}` en JSX.
# --instructions--
Renderiza una instancia del componente `ReturnTempPassword` en el componente padre `ResetPassword`. Aquí, dale a `ReturnTempPassword` una propiedad de `tempPassword` y asígnale un valor de una cadena que tenga al menos 8 caracteres. Dentro del componente hijo, `ReturnTempPassword`, accede a la propiedad `tempPassword` dentro de las etiquetas `strong` para asegurarte que el usuario vea una contraseña temporal.
# --hints--
El componente `ResetPassword` debe devolver un único elemento `div`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(ResetPassword));
return mockedComponent.children().type() === 'div';
})()
);
```
El cuarto componente hijo de `ResetPassword` debe ser el componente `ReturnTempPassword`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(ResetPassword));
return (
mockedComponent.children().childAt(3).name() === 'ReturnTempPassword'
);
})()
);
```
El componente `ReturnTempPassword` debe tener una propiedad llamada `tempPassword`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(ResetPassword));
return mockedComponent.find('ReturnTempPassword').props().tempPassword;
})()
);
```
La propiedad `tempPassword` de `ReturnTempPassword` debe ser igual a una cadena de al menos 8 caracteres.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(ResetPassword));
const temp = mockedComponent.find('ReturnTempPassword').props()
.tempPassword;
return typeof temp === 'string' && temp.length >= 8;
})()
);
```
El componente `ReturnTempPassword` debe mostrar la contraseña creada en la propiedad `tempPassword` dentro de las etiquetas `strong`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(ResetPassword));
return (
mockedComponent.find('strong').text() ===
mockedComponent.find('ReturnTempPassword').props().tempPassword
);
})()
);
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<ResetPassword />, document.getElementById('root'))
```
## --seed-contents--
```jsx
class ReturnTempPassword extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
{ /* Change code below this line */ }
<p>Your temporary password is: <strong></strong></p>
{ /* Change code above this line */ }
</div>
);
}
};
class ResetPassword extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h2>Reset Password</h2>
<h3>We've generated a new temporary password for you.</h3>
<h3>Please reset this password from your account settings ASAP.</h3>
{ /* Change code below this line */ }
{ /* Change code above this line */ }
</div>
);
}
};
```
# --solutions--
```jsx
class ReturnTempPassword extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<p>Your temporary password is: <strong>{this.props.tempPassword}</strong></p>
</div>
);
}
};
class ResetPassword extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h2>Reset Password</h2>
<h3>We've generated a new temporary password for you.</h3>
<h3>Please reset this password from your account settings ASAP.</h3>
{ /* Change code below this line */ }
<ReturnTempPassword tempPassword="serrPbqrPnzc" />
{ /* Change code above this line */ }
</div>
);
}
};
```

View File

@ -0,0 +1,82 @@
---
id: 5a24bbe0dba28a8d3cbd4c5e
title: Agrega comentarios en JSX
challengeType: 6
forumTopicId: 301376
dashedName: add-comments-in-jsx
---
# --description--
JSX es una sintaxis que se compila en JavaScript válido. A veces, para facilitar la lectura, es necesario añadir comentarios al código. Como la mayoría de los lenguajes de programación, JSX tiene su propia manera de hacerlo.
Para agregar comentarios dentro de JSX, se utiliza la sintaxis `{/* */}` para envolver el texto del comentario.
# --instructions--
El editor de código tiene un elemento JSX similar al que creaste en el último desafío. Agrega un comentario en algún lugar dentro del elemento `div`, sin modificar los elementos `h1` o `p`.
# --hints--
La constante `JSX` debe devolver un elemento `div`.
```js
assert(JSX.type === 'div');
```
El elemento `div` debe contener una etiqueta `h1` como primer elemento.
```js
assert(JSX.props.children[0].type === 'h1');
```
El elemento `div` debe contener una etiqueta `p` como segundo elemento.
```js
assert(JSX.props.children[1].type === 'p');
```
Los elementos `h1` y `p` existentes no deben ser modificados.
```js
assert(
JSX.props.children[0].props.children === 'This is a block of JSX' &&
JSX.props.children[1].props.children === "Here's a subtitle"
);
```
El elemento`JSX` debe usar una sintaxis de comentario válida.
```js
assert(/<div>[\s\S]*{\s*\/\*[\s\S]*\*\/\s*}[\s\S]*<\/div>/.test(code));
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(JSX, document.getElementById('root'))
```
## --seed-contents--
```jsx
const JSX = (
<div>
<h1>This is a block of JSX</h1>
<p>Here's a subtitle</p>
</div>
);
```
# --solutions--
```jsx
const JSX = (
<div>
<h1>This is a block of JSX</h1>
{ /* this is a JSX comment */ }
<p>Here's a subtitle</p>
</div>);
```

View File

@ -0,0 +1,179 @@
---
id: 5a24c314108439a4d403617e
title: Agrega detector de eventos (Event Listeners)
challengeType: 6
forumTopicId: 301377
dashedName: add-event-listeners
---
# --description--
El método `componentDidMount()` es también el mejor lugar para adjuntar cualquier detector de eventos que necesites agregar para una funcionalidad específica. React proporciona un sistema de eventos sintético que envuelve el sistema de eventos nativo presente en los navegadores. Esto significa que el sistema de eventos sintético se comporta exactamente igual independientemente del navegador del usuario, incluso si los eventos nativos se comportan diferentes entre diferentes navegadores.
Ya has estado usando algunos de estos controladores de eventos sintéticos como `onClick()`. El sistema de eventos sintéticos de React es excelente para usar en la mayoría de las interacciones que administrarás en elementos DOM. Sin embargo, si quieres adjuntar un controlador de eventos al documento o objetos de la ventana, debes hacerlo directamente.
# --instructions--
Agrega un detector de eventos en el método `componentDidMount()` para los eventos `keydown` y haz que estos eventos ejecuten el callback `handleKeyPress()`. Puedes usar `document.addEventListener()` el cual toma el evento (en comillas) como primer argumento y el callback como segundo argumento.
Posteriormente, en `componentWillUnmount()`, remueve este mismo detector de eventos. Puedes pasar los mismos argumentos al `document.removeEventListener()`. Es buena práctica usar este método del ciclo de vida para hacer cualquier limpieza en un componente de React antes de que estos sean desmontados y destruidos. Removiendo los detectores de eventos es un ejemplo de una limpieza de este tipo.
# --hints--
`MyComponent` debe mostrar un elemento `div` el cual envuelva una etiqueta `h1`.
```js
assert(
(() => {
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
return mockedComponent.find('div').children().find('h1').length === 1;
})()
);
```
Un detector `keydown` debe ser agregado al documento en `componentDidMount`.
```js
assert(
(() => {
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
const didMountString = mockedComponent
.instance()
.componentDidMount.toString();
return new RegExp(
'document.addEventListener(.|\n|\r)+keydown(.|\n|\r)+this.handleKeyPress'
).test(didMountString);
})()
);
```
Un detector `keydown` debe ser removido del documento en `componentWillUnmount`.
```js
assert(
(() => {
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
const willUnmountString = mockedComponent
.instance()
.componentWillUnmount.toString();
return new RegExp(
'document.removeEventListener(.|\n|\r)+keydown(.|\n|\r)+this.handleKeyPress'
).test(willUnmountString);
})()
);
```
Una vez que el componente se ha montado, pulsando `enter` debe actualizar el estado y renderizar la etiqueta `h1`.
```js
async () => {
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
const beforeState = mockedComponent.state('message');
const beforeText = mockedComponent.find('h1').text();
const pressEnterKey = () => {
mockedComponent.instance().handleKeyPress({ keyCode: 13 });
return waitForIt(() => {
mockedComponent.update();
return {
state: mockedComponent.state('message'),
text: mockedComponent.find('h1').text()
};
});
};
const afterKeyPress = await pressEnterKey();
assert(
beforeState !== afterKeyPress.state && beforeText !== afterKeyPress.text
);
};
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<MyComponent />, document.getElementById('root'))
```
## --seed-contents--
```jsx
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
message: ''
};
this.handleEnter = this.handleEnter.bind(this);
this.handleKeyPress = this.handleKeyPress.bind(this);
}
// Change code below this line
componentDidMount() {
}
componentWillUnmount() {
}
// Change code above this line
handleEnter() {
this.setState((state) => ({
message: state.message + 'You pressed the enter key! '
}));
}
handleKeyPress(event) {
if (event.keyCode === 13) {
this.handleEnter();
}
}
render() {
return (
<div>
<h1>{this.state.message}</h1>
</div>
);
}
};
```
# --solutions--
```jsx
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
message: ''
};
this.handleKeyPress = this.handleKeyPress.bind(this);
this.handleEnter = this.handleEnter.bind(this); }
componentDidMount() {
// Change code below this line
document.addEventListener('keydown', this.handleKeyPress);
// Change code above this line
}
componentWillUnmount() {
// Change code below this line
document.removeEventListener('keydown', this.handleKeyPress);
// Change code above this line
}
handleEnter() {
this.setState((state) => ({
message: state.message + 'You pressed the enter key! '
}));
}
handleKeyPress(event) {
if (event.keyCode === 13) {
this.handleEnter();
}
}
render() {
return (
<div>
<h1>{this.state.message}</h1>
</div>
);
}
};
```

View File

@ -0,0 +1,112 @@
---
id: 5a24c314108439a4d4036182
title: Agrega inline styles en React
challengeType: 6
forumTopicId: 301378
dashedName: add-inline-styles-in-react
---
# --description--
Es posible que hayas notado en el último desafío que había otras diferencias de sintaxis con respecto a los "inline styles" de HTML, además del atributo `style` establecido en un objeto de JavaScript. En primer lugar, los nombres de ciertas propiedades de estilo CSS utilizan camel case. Por ejemplo, el último desafío establece el tamaño de la fuente con `fontSize` en lugar de `font-size`. Palabras que incluyen guion como `font-size` son sintaxis inválidas para propiedades de objetos de JavaScript, por lo que React utiliza camel case. Como regla, cualquier propiedad de estilo que usa guion se escribe usando camel case en JSX.
Todas las unidades de longitud del valor de la propiedad (como `height`, `width`, y `fontSize`) se supone que están en `px` a menos que se especifique lo contrario. Si quieres utilizar `em`, por ejemplo, debes envolver el valor y las unidades entre comillas, como `{fontSize: "4em"}`. Aparte de los valores de longitud que por defecto son `px`, todos los demás valores de las propiedades deben estar envueltos entre comillas.
# --instructions--
Si tienes una gran cantidad de estilos, puedes asignar un `object` de estilos a una constante para mantener tu código organizado. Declara tu constante "styles" como una variable global al principio del archivo. Inicializa la constante `styles` y asígnale un `object` con tres propiedades de estilo y sus valores. Dale al `div` un color `purple`, un tamaño de fuente de `40` y un borde `2px solid purple`. Luego asigna al atributo `style` la constante `styles`.
# --hints--
La variable `styles` debe ser un `object` con tres propiedades.
```js
assert(Object.keys(styles).length === 3);
```
La variable `styles` debe contener una propiedad `color` con el valor de `purple`.
```js
assert(styles.color === 'purple');
```
La variable `styles` debe contener una propiedad `fontSize` con el valor de `40`.
```js
assert(styles.fontSize === 40);
```
La variable `styles` debe contener una propiedad `border` con el valor de `2px solid purple`.
```js
assert(styles.border === '2px solid purple');
```
El componente debe renderizar un elemento `div`.
```js
assert(
(function () {
const mockedComponent = Enzyme.shallow(React.createElement(Colorful));
return mockedComponent.type() === 'div';
})()
);
```
El elemento `div` debe tener sus estilos definidos en el objeto `styles`.
```js
assert(
(function () {
const mockedComponent = Enzyme.shallow(React.createElement(Colorful));
return (
mockedComponent.props().style.color === 'purple' &&
mockedComponent.props().style.fontSize === 40 &&
mockedComponent.props().style.border === '2px solid purple'
);
})()
);
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<Colorful />, document.getElementById('root'))
```
## --seed-contents--
```jsx
// Change code above this line
class Colorful extends React.Component {
render() {
// Change code below this line
return (
<div style={{color: "yellow", fontSize: 24}}>Style Me!</div>
);
// Change code above this line
}
};
```
# --solutions--
```jsx
const styles = {
color: "purple",
fontSize: 40,
border: "2px solid purple"
};
// Change code above this line
class Colorful extends React.Component {
render() {
// Change code below this line
return (
<div style={styles}>Style Me!</div>
);
// Change code above this line
}
};
```

View File

@ -0,0 +1,136 @@
---
id: 5a24c314108439a4d4036174
title: Vincula 'this' a un método de clase
challengeType: 6
forumTopicId: 301379
dashedName: bind-this-to-a-class-method
---
# --description--
Además de configurar y actualizar `state`, también puedes definir métodos para tu componente de clase. Un método de clase normalmente necesita usar la palabra clave `this` para que pueda acceder a propiedades de la clase (como `state` y `props`) dentro del ámbito del método. Hay algunas maneras de permitir que los métodos de tu clase accedan a `this`.
Una forma común es enlazar explícitamente `this` en el constructor para que `this` se vincule a los métodos de clase cuando el componente es inicializado. Habrás notado que el último desafío utilizó `this.handleClick = this.handleClick.bind(this)` para su método `handleClick` en el constructor. Luego, cuando llamas a una función como `this.setState()` dentro de su método de clase, `this` se refiere a la clase y no será `undefined`.
**Nota:** La palabra clave `this` es uno de los aspectos más confusos de JavaScript pero juega un papel importante en React. Aunque su comportamiento aquí es totalmente normal, estas lecciones no son el lugar para una revisión a profundidad de `this`, así que por favor, ¡consulta otras lecciones si lo anterior es confuso!
# --instructions--
El editor de código tiene un componente con un `state` que mantiene un seguimiento del texto. También tiene un método que le permite establecer el texto a `You clicked!`. Sin embargo, el método no funciona porque está utilizando la palabra clave `this` que no está definida. Arréglalo explícitamente ligando `this` al método `handleClick()` en el constructor del componente.
A continuación, añade un click handler al elemento `button` en el método render. Debes activar el método `handleClick()` cuando el botón recibe un evento de clic. Recuerda que el método que pasas al `onClick` handler necesita llaves porque debe ser interpretado directamente como JavaScript.
Una vez que completes los pasos anteriores debes poder hacer clic en el botón y ver `You clicked!`.
# --hints--
`MyComponent` debe devolver un elemento `div` que envuelve dos elementos, un botón y un elemento `h1`, en ese orden.
```js
assert(
Enzyme.mount(React.createElement(MyComponent)).find('div').length === 1 &&
Enzyme.mount(React.createElement(MyComponent))
.find('div')
.childAt(0)
.type() === 'button' &&
Enzyme.mount(React.createElement(MyComponent))
.find('div')
.childAt(1)
.type() === 'h1'
);
```
El estado de `MyComponent` debe inicializarse con el par de clave valor `{ text: "Hello" }`.
```js
assert(
Enzyme.mount(React.createElement(MyComponent)).state('text') === 'Hello'
);
```
Al hacer clic en el botón `button` se debe ejecutar el método `handleClick` y establecer el estado `text` en `You clicked!`.
```js
async () => {
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
const first = () => {
mockedComponent.setState({ text: 'Hello' });
return waitForIt(() => mockedComponent.state('text'));
};
const second = () => {
mockedComponent.find('button').simulate('click');
return waitForIt(() => mockedComponent.state('text'));
};
const firstValue = await first();
const secondValue = await second();
assert(firstValue === 'Hello' && secondValue === 'You clicked!');
};
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<MyComponent />, document.getElementById('root'))
```
## --seed-contents--
```jsx
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
text: "Hello"
};
// Change code below this line
// Change code above this line
}
handleClick() {
this.setState({
text: "You clicked!"
});
}
render() {
return (
<div>
{ /* Change code below this line */ }
<button>Click Me</button>
{ /* Change code above this line */ }
<h1>{this.state.text}</h1>
</div>
);
}
};
```
# --solutions--
```jsx
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
text: "Hello"
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState({
text: "You clicked!"
});
}
render() {
return (
<div>
<button onClick = {this.handleClick}>Click Me</button>
<h1>{this.state.text}</h1>
</div>
);
}
};
```

View File

@ -0,0 +1,176 @@
---
id: 5a24c314108439a4d4036189
title: Cambia el CSS inline condicionalmente según el estado del componente
challengeType: 6
forumTopicId: 301380
dashedName: change-inline-css-conditionally-based-on-component-state
---
# --description--
Hasta ahora has visto varias aplicaciones de renderizado condicional y el uso de inline styles. Aquí va un ejemplo más que combina los dos temas. También puedes renderizar CSS condicionalmente según el estado de un componente de React. Para hacer esto, tienes que verificar una condición, y si esa condición se cumple, modificas el objeto de estilos que está asignado a los elementos JSX del método render.
Este paradigma es importante entenderlo porque es un cambio dramático del enfoque más tradicional de aplicar estilos modificando elementos del DOM directamente (muy común con jQuery, por ejemplo). Con ese enfoque, debes hacer un seguimiento de cuándo cambian los elementos y también hacer una manipulación real directa. Puede resultar difícil hacer un seguimiento de los cambios, lo que podría hacer que tu interfaz de usuario sea impredecible. Cuando configuras un objeto de estilo en función de una condición, estás describiendo cómo debería verse la interfaz de usuario en función del estado de la aplicación. Existe un flujo claro de información que sólo se mueve en una dirección. Este es el método preferido para escribir aplicaciones con React.
# --instructions--
El editor de código tiene un simple componente de entrada controlado, con un estilo de borde. Quieres aplicar un estilo rojo a este borde si el usuario escribe más de 15 caracteres de texto en la casilla de entrada. Agrega una condición para verificarlo y, si la condición es válida, establece el estilo del borde de la casilla de entrada como `3px solid red`. Puedes probarlo introduciendo texto en la casilla de entrada.
# --hints--
El componente `GateKeeper` debe renderizar un elemento `div`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(GateKeeper));
return mockedComponent.find('div').length === 1;
})()
);
```
El componente `GateKeeper` debe inicializarse con una clave de estado `input` establecida con una cadena vacía.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(GateKeeper));
return mockedComponent.state().input === '';
})()
);
```
El componente `GateKeeper` debe renderizar una etiqueta `h3` y una etiqueta `input`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(GateKeeper));
return (
mockedComponent.find('h3').length === 1 &&
mockedComponent.find('input').length === 1
);
})()
);
```
La etiqueta `input`, inicialmente debe tener un estilo `1px solid black` para la propiedad `border`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(GateKeeper));
return (
mockedComponent.find('input').props().style.border === '1px solid black'
);
})()
);
```
La etiqueta `input` debe tener un estilo de borde de `3px solid red` si el valor de entrada en el estado tiene más de 15 caracteres.
```js
async () => {
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 100));
const mockedComponent = Enzyme.mount(React.createElement(GateKeeper));
const simulateChange = (el, value) =>
el.simulate('change', { target: { value } });
let initialStyle = mockedComponent.find('input').props().style.border;
const state_1 = () => {
mockedComponent.setState({ input: 'this is 15 char' });
return waitForIt(() => mockedComponent.find('input').props().style.border);
};
const state_2 = () => {
mockedComponent.setState({
input: 'A very long string longer than 15 characters.'
});
return waitForIt(() => mockedComponent.find('input').props().style.border);
};
const style_1 = await state_1();
const style_2 = await state_2();
assert(
initialStyle === '1px solid black' &&
style_1 === '1px solid black' &&
style_2 === '3px solid red'
);
};
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<GateKeeper />, document.getElementById('root'))
```
## --seed-contents--
```jsx
class GateKeeper extends React.Component {
constructor(props) {
super(props);
this.state = {
input: ''
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({ input: event.target.value })
}
render() {
let inputStyle = {
border: '1px solid black'
};
// Change code below this line
// Change code above this line
return (
<div>
<h3>Don't Type Too Much:</h3>
<input
type="text"
style={inputStyle}
value={this.state.input}
onChange={this.handleChange} />
</div>
);
}
};
```
# --solutions--
```jsx
class GateKeeper extends React.Component {
constructor(props) {
super(props);
this.state = {
input: ''
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({ input: event.target.value })
}
render() {
let inputStyle = {
border: '1px solid black'
};
if (this.state.input.length > 15) {
inputStyle.border = '3px solid red';
};
return (
<div>
<h3>Don't Type Too Much:</h3>
<input
type="text"
style={inputStyle}
value={this.state.input}
onChange={this.handleChange} />
</div>
);
}
};
```

View File

@ -0,0 +1,199 @@
---
id: 5a24c314108439a4d4036166
title: Compón componentes de React
challengeType: 6
forumTopicId: 301381
dashedName: compose-react-components
---
# --description--
A medida que los desafíos continúan utilizando composiciones más complejas con componentes de React y JSX, hay un una cosa importante a tener en cuenta. Renderizar componentes de clase de estilo ES6 dentro de otros componentes es igual que renderizar los componentes simples que usaste en los últimos desafíos. Puedes renderizar elementos JSX, componentes funcionales sin estado y componentes de clase ES6, dentro de otros componentes.
# --instructions--
En el editor de código, el componente `TypesOfFood` ya está incluyendo (renderizando) un componente llamado `Vegetables`. Además, también está el componente `Fruits` del último desafío.
Anida dos componentes dentro de `Fruits` - primero `NonCitrus` y luego `Citrus`. Ambos componentes se te proporcionan en segundo plano. A continuación, anida el componente de clase `Fruits` en el componente `TypesOfFood`, debajo del encabezado (header) `h1` y encima de `Vegetables`. El resultado debe ser una serie de componentes anidados, que utiliza dos tipos de componentes diferentes.
# --hints--
El componente `TypesOfFood` debe devolver un solo elemento `div`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(TypesOfFood));
return mockedComponent.children().type() === 'div';
})()
);
```
El componente `TypesOfFood` debe devolver el componente `Fruits`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(TypesOfFood));
return mockedComponent.children().childAt(1).name() === 'Fruits';
})()
);
```
El componente `Fruits` debe devolver el componente `NonCitrus` y el componente `Citrus`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(TypesOfFood));
return (
mockedComponent.find('Fruits').children().find('NonCitrus').length ===
1 &&
mockedComponent.find('Fruits').children().find('Citrus').length === 1
);
})()
);
```
El componente `TypesOfFood` debe devolver el componente `Vegetables` debajo del componente `Fruits`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(TypesOfFood));
return mockedComponent.children().childAt(2).name() === 'Vegetables';
})()
);
```
# --seed--
## --before-user-code--
```jsx
class NonCitrus extends React.Component {
render() {
return (
<div>
<h4>Non-Citrus:</h4>
<ul>
<li>Apples</li>
<li>Blueberries</li>
<li>Strawberries</li>
<li>Bananas</li>
</ul>
</div>
);
}
};
class Citrus extends React.Component {
render() {
return (
<div>
<h4>Citrus:</h4>
<ul>
<li>Lemon</li>
<li>Lime</li>
<li>Orange</li>
<li>Grapefruit</li>
</ul>
</div>
);
}
};
class Vegetables extends React.Component {
render() {
return (
<div>
<h2>Vegetables:</h2>
<ul>
<li>Brussel Sprouts</li>
<li>Broccoli</li>
<li>Squash</li>
</ul>
</div>
);
}
};
```
## --after-user-code--
```jsx
ReactDOM.render(<TypesOfFood />, document.getElementById('root'))
```
## --seed-contents--
```jsx
class Fruits extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h2>Fruits:</h2>
{ /* Change code below this line */ }
{ /* Change code above this line */ }
</div>
);
}
};
class TypesOfFood extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h1>Types of Food:</h1>
{ /* Change code below this line */ }
{ /* Change code above this line */ }
<Vegetables />
</div>
);
}
};
```
# --solutions--
```jsx
class Fruits extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h2>Fruits:</h2>
{ /* Change code below this line */ }
<NonCitrus />
<Citrus />
{ /* Change code above this line */ }
</div>
)
}
}
class TypesOfFood extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h1>Types of Food:</h1>
{ /* Change code below this line */ }
<Fruits />
{ /* Change code above this line */ }
<Vegetables />
</div>
);
}
};
```

View File

@ -0,0 +1,110 @@
---
id: 5a24bbe0dba28a8d3cbd4c5d
title: Crea un elemento JSX complejo
challengeType: 6
forumTopicId: 301382
dashedName: create-a-complex-jsx-element
---
# --description--
El último desafío fue un ejemplo sencillo de JSX, pero JSX también puede representar HTML más complejo.
Una cosa importante que debes saber sobre JSX anidado es que debe devolver un solo elemento.
Este elemento principal contendría a todos los demás niveles de elementos anidados.
Por ejemplo, varios elementos JSX escritos al mismo nivel sin elemento contenedor principal no se transpilarán.
Aquí va un ejemplo:
**JSX válido:**
```jsx
<div>
<p>Paragraph One</p>
<p>Paragraph Two</p>
<p>Paragraph Three</p>
</div>
```
**JSX inválido:**
```jsx
<p>Paragraph One</p>
<p>Paragraph Two</p>
<p>Paragraph Three</p>
```
# --instructions--
Define una nueva constante `JSX` que renderice un `div` que contenga los siguientes elementos en orden:
Un `h1`, un `p` y una lista desordenada que contiene tres elementos `li`. Puedes incluir cualquier texto que desees dentro de cada elemento.
**Nota: **Al renderizar varios elementos de esta forma, puedes envolverlos todos entre paréntesis, pero no es estrictamente necesario. Observa también que este desafío usa una etiqueta `div` para envolver a todos los elementos hijos dentro de un solo elemento principal. Si eliminas el `div`, JSX ya no se podrá transpilar. Ten esto en cuenta, ya que también será así cuando devuelvas elementos JSX en los componentes de React.
# --hints--
La constante `JSX` debe devolver un elemento `div`.
```js
assert(JSX.type === 'div');
```
El `div` debe contener una etiqueta `h1` como primer elemento.
```js
assert(JSX.props.children[0].type === 'h1');
```
El `div` debe contener una etiqueta `p` como segundo elemento.
```js
assert(JSX.props.children[1].type === 'p');
```
El `div` debe contener una etiqueta `ul` como tercer elemento.
```js
assert(JSX.props.children[2].type === 'ul');
```
El `ul` debe contener tres elementos `li`.
```js
assert(
JSX.props.children
.filter((ele) => ele.type === 'ul')[0]
.props.children.filter((ele) => ele.type === 'li').length === 3
);
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(JSX, document.getElementById('root'))
```
## --seed-contents--
```jsx
```
# --solutions--
```jsx
const JSX = (
<div>
<h1>Hello JSX!</h1>
<p>Some info</p>
<ul>
<li>An item</li>
<li>Another item</li>
<li>A third item</li>
</ul>
</div>);
```

View File

@ -0,0 +1,134 @@
---
id: 5a24c314108439a4d4036164
title: Crear un componente con composición
challengeType: 6
forumTopicId: 301383
dashedName: create-a-component-with-composition
---
# --description--
Ahora veremos cómo podemos componer múltiples componentes de React juntos. Imagina que estás construyendo una aplicación y has creado tres componentes: un `Navbar`, `Dashboard` y `Footer`.
Para componer estos componentes juntos, se podría crear un componente `App` *parent* que renderiza cada uno de estos tres componentes como *children*. Para renderizar un componente como hijo en un componente React, se incluye el nombre del componente escrito como una etiqueta HTML personalizada en el JSX. Por ejemplo, en el método `render` se puede escribir:
```jsx
return (
<App>
<Navbar />
<Dashboard />
<Footer />
</App>
)
```
Cuando React encuentra una etiqueta HTML personalizada que hace referencia a otro componente (un nombre de componente envuelto en `< />` como en este ejemplo), renderiza el marcado de ese componente en la ubicación de la etiqueta. Esto debería ilustrar la relación padre/hijo entre el componente `App` y `Navbar`, `Dashboard`, y `Footer`.
# --instructions--
En el editor de código, hay un componente funcional simple llamado `ChildComponent` y un componente de clase llamado `ParentComponent`. Compón los dos juntos renderizando el `ChildComponent` dentro del `ParentComponent`. Asegúrate de cerrar la etiqueta `ChildComponent` con una barra diagonal.
**Nota:** `ChildComponent` se define con una función de flecha ES6 porque es una práctica muy común al usar React. Sin embargo, has de saber que se trata de una función simple. Si no estás familiarizado con la sintaxis de la función flecha, consulta la sección de JavaScript.
# --hints--
El componente React debe devolver un único elemento `div`.
```js
assert(
(function () {
var shallowRender = Enzyme.shallow(React.createElement(ParentComponent));
return shallowRender.type() === 'div';
})()
);
```
El componente debe devolver dos elementos anidados.
```js
assert(
(function () {
var shallowRender = Enzyme.shallow(React.createElement(ParentComponent));
return shallowRender.children().length === 2;
})()
);
```
El componente debe devolver el `ChildComponent` como un segundo hijo.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(ParentComponent));
return (
mockedComponent.find('ParentComponent').find('ChildComponent').length ===
1
);
})()
);
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<ParentComponent />, document.getElementById('root'))
```
## --seed-contents--
```jsx
const ChildComponent = () => {
return (
<div>
<p>I am the child</p>
</div>
);
};
class ParentComponent extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h1>I am the parent</h1>
{ /* Change code below this line */ }
{ /* Change code above this line */ }
</div>
);
}
};
```
# --solutions--
```jsx
const ChildComponent = () => {
return (
<div>
<p>I am the child</p>
</div>
);
};
class ParentComponent extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h1>I am the parent</h1>
{ /* Change code below this line */ }
<ChildComponent />
{ /* Change code above this line */ }
</div>
);
}
};
```

View File

@ -0,0 +1,220 @@
---
id: 5a24c314108439a4d4036179
title: Crea un formulario controlado
challengeType: 6
forumTopicId: 301384
dashedName: create-a-controlled-form
---
# --description--
El último desafío mostró que React puede controlar el estado interno de ciertos elementos como `input` y `textarea`, lo que los hace componentes controlados. Esto también se aplica a otros elementos del formulario, incluyendo el elemento regular HTML `form`.
# --instructions--
El componente `MyForm` está configurado con un `form` vacío, con un manejador de envío. El manejador de envío será llamado cuando se envíe el formulario.
Hemos añadido un botón que envía el formulario. Puedes ver que tiene el `type` establecido en `submit` indicando que es el botón que controla el formulario. Añade el elemento `input` en el formulario `form` y establece sus atributos `value` y `onChange()` como el último desafío. A continuación, debes completar el método `handleSubmit` para que establezca la propiedad de estado del componente `submit` al valor de entrada actual en el `state` local.
**Nota:** También debes llamar a `event.preventDefault()` en el controlador de envío, para evitar el comportamiento predeterminado de envío de formulario que actualizará la página web. Para la comodidad de los campistas, el comportamiento predeterminado se ha desactivado aquí para evitar que las actualizaciones restablezcan el código de desafío.
Por último, crea una etiqueta `h1` después del `form` que renderiza el valor de `submit` del `state` del componente. A continuación, puedes escribir en el formulario y hacer clic en el botón (o pulsar intro), y deberías ver tu entrada renderizada en la página.
# --hints--
`MyForm` debe retornar un elemento `div` que contiene un `form` y una etiqueta `h1`. El formulario debe incluir un `input` y un `button`.
```js
assert(
(() => {
const mockedComponent = Enzyme.mount(React.createElement(MyForm));
return (
mockedComponent.find('div').children().find('form').length === 1 &&
mockedComponent.find('div').children().find('h1').length === 1 &&
mockedComponent.find('form').children().find('input').length === 1 &&
mockedComponent.find('form').children().find('button').length === 1
);
})()
);
```
El estado de `MyForm` debe inicializar con propiedades `input` y `submit`, ambos establecidos a cadenas vacías.
```js
assert(
Enzyme.mount(React.createElement(MyForm)).state('input') === '' &&
Enzyme.mount(React.createElement(MyForm)).state('submit') === ''
);
```
Escribir en el elemento `input` debe actualizar la propiedad `input` del estado del componente.
```js
(() => {
const mockedComponent = Enzyme.mount(React.createElement(MyForm));
const _1 = () => {
mockedComponent.setState({ input: '' });
return mockedComponent.state('input');
};
const _2 = () => {
mockedComponent
.find('input')
.simulate('change', { target: { value: 'TestInput' } });
return {
state: mockedComponent.state('input'),
inputVal: mockedComponent.find('input').props().value
};
};
const before = _1();
const after = _2();
assert(
before === '' &&
after.state === 'TestInput' &&
after.inputVal === 'TestInput'
);
})();
```
El envío del formulario debe ejecutar `handleSubmit`, el cual debe establecer la propiedad `submit` en estado igual a la entrada actual.
```js
(() => {
const mockedComponent = Enzyme.mount(React.createElement(MyForm));
const _1 = () => {
mockedComponent.setState({ input: '' });
mockedComponent.setState({ submit: '' });
mockedComponent
.find('input')
.simulate('change', { target: { value: 'SubmitInput' } });
return mockedComponent.state('submit');
};
const _2 = () => {
mockedComponent.find('form').simulate('submit');
return mockedComponent.state('submit');
};
const before = _1();
const after = _2();
assert(before === '' && after === 'SubmitInput');
})();
```
`handleSubmit` debe llamar a `event.preventDefault`
```js
assert(
__helpers.isCalledWithNoArgs(
'event.preventDefault',
MyForm.prototype.handleSubmit.toString()
)
);
```
El encabezado `h1` debe renderizar el valor del campo `submit` del estado del componente.
```js
(() => {
const mockedComponent = Enzyme.mount(React.createElement(MyForm));
const _1 = () => {
mockedComponent.setState({ input: '' });
mockedComponent.setState({ submit: '' });
mockedComponent
.find('input')
.simulate('change', { target: { value: 'TestInput' } });
return mockedComponent.find('h1').text();
};
const _2 = () => {
mockedComponent.find('form').simulate('submit');
return mockedComponent.find('h1').text();
};
const before = _1();
const after = _2();
assert(before === '' && after === 'TestInput');
})();
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<MyForm />, document.getElementById('root'));
```
## --seed-contents--
```jsx
class MyForm extends React.Component {
constructor(props) {
super(props);
this.state = {
input: '',
submit: ''
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({
input: event.target.value
});
}
handleSubmit(event) {
// Change code below this line
// Change code above this line
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
{/* Change code below this line */}
{/* Change code above this line */}
<button type='submit'>Submit!</button>
</form>
{/* Change code below this line */}
{/* Change code above this line */}
</div>
);
}
}
```
# --solutions--
```jsx
class MyForm extends React.Component {
constructor(props) {
super(props);
this.state = {
input: '',
submit: ''
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({
input: event.target.value
});
}
handleSubmit(event) {
event.preventDefault();
this.setState(state => ({
submit: state.input
}));
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<input value={this.state.input} onChange={this.handleChange} />
<button type='submit'>Submit!</button>
</form>
<h1>{this.state.submit}</h1>
</div>
);
}
}
```

View File

@ -0,0 +1,150 @@
---
id: 5a24c314108439a4d4036178
title: Crea una entrada controlada
challengeType: 6
forumTopicId: 301385
dashedName: create-a-controlled-input
---
# --description--
Tu aplicación puede tener interacciones más complejas entre `state` y la interfaz de usuario renderizada. Por ejemplo, elementos de control de formulario para la entrada de texto, tales como `input` y `textarea`, mantienen su propio estado en el DOM como los tipos de usuario. Con React, puedes mover este estado mutable hacia el `state` de un componente de React. La entrada del usuario se convierte en parte del `state` de la aplicación, así que React controla el valor de ese campo de entrada. Por lo general, si tienes componentes de React con campos de entrada en los que el usuario puede escribir, será un formulario de entrada controlado.
# --instructions--
El editor de código tiene el esqueleto de un componente llamado `ControlledInput` para crear un elemento `input` controlado. El `state` del componente ya está inicializado con una propiedad `input` que contiene una cadena vacía. Este valor representa el texto que un usuario escribe en el campo `input`.
Primero, crea un método llamado `handleChange()` que tiene un parámetro llamado `event`. Cuando el método es llamado, este recibe un objeto `event` que contiene una cadena de texto del elemento `input`. Puedes acceder a esta cadena con `event.target.value` dentro del método. Actualiza la propiedad `input` del `state` del componente con esta nueva cadena.
En el método `render`, crea el elemento `input` encima de la etiqueta `h4`. Añade un atributo `value` que es igual a la propiedad `input` del `state` del componente. Luego añade un manejador de eventos `onChange()` establecido al método `handleChange()`.
Cuando escribes en el cuadro de entrada, ese texto es procesado por el método `handleChange()`, establecido como la propiedad `input` en el `state` local, y renderizado como el valor en el cuadro `input` de la página. El `state` del componente es la única fuente de verdad con respecto a los datos de entrada.
Por último, pero no menos importante, no olvides añadir los enlaces necesarios en el constructor.
# --hints--
`ControlledInput` debe devolver un elemento `div` que contiene un `input` y una etiqueta `p`.
```js
assert(
Enzyme.mount(React.createElement(ControlledInput))
.find('div')
.children()
.find('input').length === 1 &&
Enzyme.mount(React.createElement(ControlledInput))
.find('div')
.children()
.find('p').length === 1
);
```
El estado de `ControlledInput` debe inicializarse con una propiedad `input` establecida a una cadena vacía.
```js
assert.strictEqual(
Enzyme.mount(React.createElement(ControlledInput)).state('input'),
''
);
```
Escribir en el elemento de entrada debe actualizar el estado y el valor de la entrada, y el elemento `p` debe renderizar este estado a medida que escribas.
```js
async () => {
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
const mockedComponent = Enzyme.mount(React.createElement(ControlledInput));
const _1 = () => {
mockedComponent.setState({ input: '' });
return waitForIt(() => mockedComponent.state('input'));
};
const _2 = () => {
mockedComponent
.find('input')
.simulate('change', { target: { value: 'TestInput' } });
return waitForIt(() => ({
state: mockedComponent.state('input'),
text: mockedComponent.find('p').text(),
inputVal: mockedComponent.find('input').props().value
}));
};
const before = await _1();
const after = await _2();
assert(
before === '' &&
after.state === 'TestInput' &&
after.text === 'TestInput' &&
after.inputVal === 'TestInput'
);
};
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<ControlledInput />, document.getElementById('root'))
```
## --seed-contents--
```jsx
class ControlledInput extends React.Component {
constructor(props) {
super(props);
this.state = {
input: ''
};
// Change code below this line
// Change code above this line
}
// Change code below this line
// Change code above this line
render() {
return (
<div>
{ /* Change code below this line */}
{ /* Change code above this line */}
<h4>Controlled Input:</h4>
<p>{this.state.input}</p>
</div>
);
}
};
```
# --solutions--
```jsx
class ControlledInput extends React.Component {
constructor(props) {
super(props);
this.state = {
input: ''
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({
input: event.target.value
});
}
render() {
return (
<div>
<input
value={this.state.input}
onChange={this.handleChange} />
<h4>Controlled Input:</h4>
<p>{this.state.input}</p>
</div>
);
}
};
```

View File

@ -0,0 +1,102 @@
---
id: 5a24c314108439a4d4036163
title: Crea un componente de React
challengeType: 6
forumTopicId: 301386
dashedName: create-a-react-component
---
# --description--
La otra forma de definir un componente React es con la sintaxis de clase de ES6 `class`. En el siguiente ejemplo, `Kitten` hereda de `React.Component`:
```jsx
class Kitten extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<h1>Hi</h1>
);
}
}
```
Esto crea una clase de ES6 `Kitten` que hereda de la clase `React.Component`. Así que la clase `Kitten` ahora tiene acceso a muchas características útiles de React, como el estado local y el ciclo de vida de los "hooks". No te preocupes si aún no estás familiarizado con estos términos, ya que se tratarán con más detalle en los desafíos posteriores. También ten en cuenta que la clase `Kitten` tiene un `constructor` definido dentro de ella que llama a `super()`. Utiliza `super()` para llamar al constructor de la clase padre, en este caso `React.Component`. El constructor es un método especial utilizado durante la inicialización de objetos que se crean con la palabra clave `class`. Es una mejor práctica llamar al `constructor` de un componente con `super`, y pasar sus propiedades `props` a ambos. Esto asegura que el componente esté inicializado correctamente. Por ahora, sepan que es estándar que se incluya este código. Pronto verás otros usos para el constructor, así como las `props`.
# --instructions--
`MyComponent` está definido en el editor de código usando la sintaxis de clase. Termina de escribir el método `render` para que devuelva un elemento `div` que contiene un `h1` con el texto `Hello React!`.
# --hints--
El componente React debe devolver un único elemento `div`.
```js
assert(Enzyme.shallow(React.createElement(MyComponent)).type() === 'div');
```
El `div` retornado debe renderizar un encabezado `h1` dentro de él.
```js
assert(
/<div><h1>.*<\/h1><\/div>/.test(
Enzyme.shallow(React.createElement(MyComponent)).html()
)
);
```
El encabezado `h1` debe contener la cadena `Hello React!`.
```js
assert(
Enzyme.shallow(React.createElement(MyComponent)).html() ===
'<div><h1>Hello React!</h1></div>'
);
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<MyComponent />, document.getElementById('root'))
```
## --seed-contents--
```jsx
class MyComponent extends React.Component {
constructor(props) {
super(props);
}
render() {
// Change code below this line
// Change code above this line
}
};
```
# --solutions--
```jsx
class MyComponent extends React.Component {
constructor(props) {
super(props);
}
render() {
// Change code below this line
return (
<div>
<h1>Hello React!</h1>
</div>
);
// Change code above this line
}
};
```

View File

@ -0,0 +1,57 @@
---
id: 587d7dbc367417b2b2512bb1
title: Crea un elemento JSX simple
challengeType: 6
forumTopicId: 301390
dashedName: create-a-simple-jsx-element
---
# --description--
React es una librería de vistas de código abierto creada y mantenida por Facebook. Es una gran herramienta para hacer la interfaz de usuario (UI) de aplicaciones web modernas.
React usa una extensión de sintaxis de JavaScript llamada JSX que te permite escribir HTML directamente dentro de JavaScript. Esto tiene muchos beneficios. Te permite utilizar el poder programático completo de JavaScript dentro de HTML, y ayuda a mantener tu código legible. En su mayor parte, JSX es similar al HTML que ya has aprendido. Sin embargo, hay algunas diferencias clave que se abordarán a lo largo de estos desafíos.
Por ejemplo, dado que JSX es una extensión sintáctica de JavaScript, se puede escribir JavaScript directamente dentro de JSX. Para hacer esto, simplemente incluye el código que deseas que sea tratado como JavaScript entre llaves: `{ 'this is treated as JavaScript code' }`. Ten esto en cuenta, ya que se utiliza en varios desafíos futuros.
Sin embargo, debido a que JSX no es JavaScript válido, el código JSX debe ser compilado en JavaScript. El transpilador Babel es una herramienta muy popular para este proceso. Para tu comodidad, ya se ha añadido tras bambalinas para estos desafíos. Si escribes JSX no válido sintácticamente, verás que la primera prueba de estos desafíos falla.
Vale la pena señalar que por debajo los desafíos están llamando `ReactDOM.render(JSX, document.getElementById('root'))`. Esta llamada de función es la que coloca tu JSX en la representación ligera del DOM de React. React entonces utiliza capturas instantáneas de su propio DOM para optimizar actualizando sólo partes específicas del DOM.
# --instructions--
El código actual utiliza JSX para asignar un elemento `div` a la constante `JSX`. Reemplaza el `div` por un elemento `h1` y añade el texto `Hello JSX!` dentro de él.
# --hints--
La constante `JSX` debe devolver un elemento `h1`.
```js
assert(JSX.type === 'h1');
```
La etiqueta `h1` debe incluir el texto `Hello JSX!`
```js
assert(Enzyme.shallow(JSX).contains('Hello JSX!'));
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(JSX, document.getElementById('root'))
```
## --seed-contents--
```jsx
const JSX = <div></div>;
```
# --solutions--
```jsx
const JSX = <h1>Hello JSX!</h1>;
```

View File

@ -0,0 +1,134 @@
---
id: 5a24c314108439a4d4036170
title: Crea un componente de estado
challengeType: 6
forumTopicId: 301391
dashedName: create-a-stateful-component
---
# --description--
Uno de los temas más importantes en React es `state`. El estado consiste en cualquier dato que tu aplicación necesite conocer y que pueda cambiar con el tiempo. Quieres que tus aplicaciones respondan a los cambios de estado y presenten una interfaz de usuario actualizada cuando sea necesario. React ofrece una buena solución para el manejo de estado de aplicaciones web modernas.
Creas un estado en un componente de React declarando una propiedad `state` en la clase del componente en su `constructor`. Esto inicializa el componente con `state` cuando se crea. La propiedad `state` debe establecerse en un `object` de JavaScript. Declararlo se ve así:
```jsx
this.state = {
}
```
Tienes acceso al objeto `state` a lo largo de la vida de tu componente. Puedes actualizarlo, renderizarlo en tu interfaz de usuario y pasarlo como propiedades a componentes hijos. El objeto `state` puede ser tan complejo o simple como lo necesites. Ten en cuenta que debes crear un componente de clase heredando `React.Component` para crear un `state` como este.
# --instructions--
Hay un componente en el editor de código que está intentando renderizar una propiedad `name` desde su `state`. Sin embargo, no hay ningún `state` definido. Inicializa el componente con `state` en el `constructor` y asigna tu nombre a una propiedad de `name`.
# --hints--
`StatefulComponent` debe existir y renderizar.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(
React.createElement(StatefulComponent)
);
return mockedComponent.find('StatefulComponent').length === 1;
})()
);
```
`StatefulComponent` debe renderizar un `div` y un elemento `h1`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(
React.createElement(StatefulComponent)
);
return (
mockedComponent.find('div').length === 1 &&
mockedComponent.find('h1').length === 1
);
})()
);
```
El estado de `StatefulComponent` debe inicializarse con una propiedad `name` establecida a una cadena.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(
React.createElement(StatefulComponent)
);
const initialState = mockedComponent.state();
return (
typeof initialState === 'object' && typeof initialState.name === 'string'
);
})()
);
```
La propiedad `name` en el estado de `StatefulComponent` debe renderizarse en el elemento `h1`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(
React.createElement(StatefulComponent)
);
const initialState = mockedComponent.state();
return mockedComponent.find('h1').text() === initialState.name;
})()
);
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<StatefulComponent />, document.getElementById('root'))
```
## --seed-contents--
```jsx
class StatefulComponent extends React.Component {
constructor(props) {
super(props);
// Only change code below this line
// Only change code above this line
}
render() {
return (
<div>
<h1>{this.state.name}</h1>
</div>
);
}
};
```
# --solutions--
```jsx
class StatefulComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
name: 'freeCodeCamp!'
}
}
render() {
return (
<div>
<h1>{this.state.name}</h1>
</div>
);
}
};
```

View File

@ -0,0 +1,102 @@
---
id: 5a24c314108439a4d4036162
title: Crear un componente funcional sin estado
challengeType: 6
forumTopicId: 301392
dashedName: create-a-stateless-functional-component
---
# --description--
Los componentes son el núcleo de React. Todo en React es un componente y aquí aprenderás a crear uno.
Hay dos maneras de crear un componente React. La primera forma es utilizar una función JavaScript. Definir un componente de esta manera crea un *componente funcional sin estado*. El concepto de estado en una solicitud se abordará en retos posteriores. Por ahora, piensa en un componente sin estado como uno que puede recibir datos y renderizarlos, pero no administra o rastrea los cambios en esos datos. (Cubriremos la segunda manera de crear un componente React en el siguiente desafío.)
Para crear un componente con una función, simplemente escribe una función JavaScript que devuelva ya sea JSX o `null`. Una cosa importante a tener en cuenta es que React requiere que tu nombre de función comience con una letra mayúscula. Aquí hay un ejemplo de un componente funcional sin estado que asigna una clase HTML en JSX:
```jsx
const DemoComponent = function() {
return (
<div className='customClass' />
);
};
```
Después de ser transpilado, el `<div>` tendrá una clase CSS de `customClass`.
Debido a que un componente JSX representa HTML, podrías poner varios componentes juntos para crear una página HTML más compleja. Esta es una de las ventajas clave de la arquitectura de componentes que React proporciona. Te permite componer tu interfaz de usuario de muchos componentes separados y aislados. Esto hace más fácil construir y mantener complejas interfaces de usuario.
# --instructions--
El editor de código tiene una función llamada `MyComponent`. Completa esta función para que retorne un único elemento `div` que contiene alguna cadena de texto.
**Nota:** El texto se considera hijo del elemento `div`, por lo que no podrás utilizar una etiqueta de autocierre.
# --hints--
`MyComponent` debe retornar JSX.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
return mockedComponent.length === 1;
})()
);
```
`MyComponent` debe renderizar un elemento `div`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
return mockedComponent.children().type() === 'div';
})()
);
```
El elemento `div` debe contener una cadena de texto.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
return mockedComponent.find('div').text() !== '';
})()
);
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<MyComponent />, document.getElementById('root'))
```
## --seed-contents--
```jsx
const MyComponent = function() {
// Change code below this line
// Change code above this line
}
```
# --solutions--
```jsx
const MyComponent = function() {
// Change code below this line
return (
<div>
Demo Solution
</div>
);
// Change code above this line
}
```

View File

@ -0,0 +1,62 @@
---
id: 5a24c314108439a4d4036160
title: Define una clase HTML en JSX
challengeType: 6
forumTopicId: 301393
dashedName: define-an-html-class-in-jsx
---
# --description--
Ahora que te sientes cómodo escribiendo JSX, te preguntarás cuanto difiere de HTML.
Hasta ahora, puede parecer que HTML y JSX son exactamente iguales.
Una diferencia clave en JSX es que ya no puedes usar la palabra `class` para definir clases HTML. Esto es debido a que `class` es una palabra reservada en JavaScript. En su lugar, JSX utiliza `className`.
De hecho, la convención de nomenclatura para todos los atributos HTML y referencias a eventos en JSX se convierte a camelCase. Por ejemplo, un evento de clic en JSX es `onClick`, en lugar de `onclick`. Del mismo modo, `onchange` se convierte en `onChange`. Si bien se trata de una diferencia sutil, es importante tenerlo en cuenta de ahora en adelante.
# --instructions--
Aplica una clase `myDiv` al `div` proporcionado en el código JSX.
# --hints--
La constante `JSX` debe devolver un elemento `div`.
```js
assert.strictEqual(JSX.type, 'div');
```
`div` debe tener una clase `myDiv`.
```js
assert.strictEqual(JSX.props.className, 'myDiv');
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(JSX, document.getElementById('root'))
```
## --seed-contents--
```jsx
const JSX = (
<div>
<h1>Add a class to this div</h1>
</div>
);
```
# --solutions--
```jsx
const JSX = (
<div className = 'myDiv'>
<h1>Add a class to this div</h1>
</div>);
```

View File

@ -0,0 +1,147 @@
---
id: 5a24c314108439a4d403618b
title: Proporciona a los elementos hermanos un atributo de clave única
challengeType: 6
forumTopicId: 301394
dashedName: give-sibling-elements-a-unique-key-attribute
---
# --description--
El último desafío mostró cómo el método `map` es usado para representar dinámicamente un número de elementos según la entrada del usuario. Sin embargo, faltaba una pieza importante de ese ejemplo. Cuando creas un arreglo de elementos, cada uno necesita un atributo `key` establecido en un valor único. React usa estas claves para realizar un seguimiento de los elementos que se agregan, cambian o eliminan. Esto ayuda a que el proceso de re-renderización sea más eficiente cuando la lista se modifica de alguna manera.
**Nota:** Las claves solo necesitan ser únicas entre elementos hermanos, no es necesario que sean únicas globalmente en tu aplicación.
# --instructions--
El editor de código tiene un arreglo con algunos frameworks frontend y un componente funcional sin estado llamado `Frameworks()`. `Frameworks()` necesita mapear el arreglo a una lista desordenada, como en el último desafío. Finaliza la escritura del callback `map` para devolver un elemento `li` por cada framework en el arreglo `frontEndFrameworks`. Esta vez, debes asegurarte de dar a cada elemento `li` un atributo `key`, establecido a un valor único. Los elementos `li` también deben contener texto de `frontEndFrameworks`.
Normalmente, deseas hacer que la clave sea algo que identifique de manera única el elemento que se está procesando. Como último recurso se puede utilizar el índice del arreglo, pero normalmente se debe intentar usar una identificación única.
# --hints--
El componente `Frameworks` debe de existir y renderizar a la página.
```js
assert(
Enzyme.mount(React.createElement(Frameworks)).find('Frameworks').length === 1
);
```
`Frameworks` debe renderizar un elemento `h1`.
```js
assert(Enzyme.mount(React.createElement(Frameworks)).find('h1').length === 1);
```
`Frameworks` debe renderizar un elemento `ul`.
```js
assert(Enzyme.mount(React.createElement(Frameworks)).find('ul').length === 1);
```
La etiqueta `ul` debe renderizar 6 elementos hijos `li`.
```js
assert(
Enzyme.mount(React.createElement(Frameworks)).find('ul').children().length ===
6 &&
Enzyme.mount(React.createElement(Frameworks))
.find('ul')
.childAt(0)
.name() === 'li' &&
Enzyme.mount(React.createElement(Frameworks)).find('li').length === 6
);
```
Cada elemento de la lista de elementos debe tener un atributo `key` único.
```js
assert(
(() => {
const ul = Enzyme.mount(React.createElement(Frameworks)).find('ul');
const keys = new Set([
ul.childAt(0).key(),
ul.childAt(1).key(),
ul.childAt(2).key(),
ul.childAt(3).key(),
ul.childAt(4).key(),
ul.childAt(5).key()
]);
return keys.size === 6;
})()
);
```
Cada elemento de la lista de elementos debe contener un texto de `frontEndFrameworks`.
```js
assert(
(() => {
const li = Enzyme.mount(React.createElement(Frameworks))
.find('ul')
.children();
return [...Array(5)].every((_, i) =>
frontEndFrameworks.includes(li.at(i).text())
);
})()
);
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<Frameworks />, document.getElementById('root'))
```
## --seed-contents--
```jsx
const frontEndFrameworks = [
'React',
'Angular',
'Ember',
'Knockout',
'Backbone',
'Vue'
];
function Frameworks() {
const renderFrameworks = null; // Change this line
return (
<div>
<h1>Popular Front End JavaScript Frameworks</h1>
<ul>
{renderFrameworks}
</ul>
</div>
);
};
```
# --solutions--
```jsx
const frontEndFrameworks = [
'React',
'Angular',
'Ember',
'Knockout',
'Backbone',
'Vue'
];
function Frameworks() {
const renderFrameworks = frontEndFrameworks.map((fw, i) => <li key={i}>{fw}</li>);
return (
<div>
<h1>Popular Front End JavaScript Frameworks</h1>
<ul>
{renderFrameworks}
</ul>
</div>
);
};
```

View File

@ -0,0 +1,104 @@
---
id: 5a24c314108439a4d4036181
title: Introducción a los estilos en línea
challengeType: 6
forumTopicId: 301395
dashedName: introducing-inline-styles
---
# --description--
Hay otros conceptos complejos que añaden poderosas capacidades a tu código de React. Pero tal vez te estés preguntando sobre el problema más sencillo de cómo dar estilo a esos elementos JSX que creas en React. Probablemente sepas que no será exactamente lo mismo que trabajar con HTML debido a [la manera en que aplicas clases a los elementos JSX](/learn/front-end-development-libraries/react/define-an-html-class-in-jsx).
Si importas estilos desde una hoja de estilos, esto no es muy diferente. Aplica una clase a tu elemento JSX usando el atributo `className`, y aplica estilos a la clase en tu hoja de estilos. Otra opción es aplicar estilos en línea, los cuales son muy comunes en el desarrollo de ReactJS.
Los estilos en línea se aplican a los elementos JSX de forma similar a como se hace en HTML, pero con algunas diferencias en JSX. Aquí hay un ejemplo de un estilo en línea en HTML:
```jsx
<div style="color: yellow; font-size: 16px">Mellow Yellow</div>
```
Los elementos JSX usan el atributo `style`, pero debido a la forma en que JSX es transpilado, no puede establecer el valor a un `string`. Es su lugar, lo establece igual a un `object` de JavaScript. Aquí un ejemplo:
```jsx
<div style={{color: "yellow", fontSize: 16}}>Mellow Yellow</div>
```
¿Notas cómo ponemos en camelCase la propiedad `fontSize`? Esto es porque React no aceptará claves kebab-case en el objeto de estilo. React aplicará el nombre correcto de la propiedad por nosotros en el HTML.
# --instructions--
Agrega un atributo `style` al `div` en el editor de código para darle al texto un color rojo y un tamaño de fuente de `72px`.
Ten en cuenta que puedes establecer opcionalmente el tamaño de la fuente para que sea un número, omitiendo las unidades `px`, o escribirlo como `72px`.
# --hints--
El componente debe renderizar un elemento `div`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(Colorful));
return mockedComponent.children().type() === 'div';
})()
);
```
El elemento `div` debe tener un color de `red`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(Colorful));
return mockedComponent.children().props().style.color === 'red';
})()
);
```
El elemento `div` debe tener un tamaño de fuente de `72px`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(Colorful));
return (
mockedComponent.children().props().style.fontSize === 72 ||
mockedComponent.children().props().style.fontSize === '72' ||
mockedComponent.children().props().style.fontSize === '72px'
);
})()
);
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<Colorful />, document.getElementById('root'))
```
## --seed-contents--
```jsx
class Colorful extends React.Component {
render() {
return (
<div>Big Red</div>
);
}
};
```
# --solutions--
```jsx
class Colorful extends React.Component {
render() {
return (
<div style={{color: "red", fontSize: 72}}>Big Red</div>
);
}
};
```

View File

@ -0,0 +1,75 @@
---
id: 5a24c314108439a4d4036161
title: Aprende sobre las etiquetas JSX auto-cerradas
challengeType: 6
forumTopicId: 301396
dashedName: learn-about-self-closing-jsx-tags
---
# --description--
Hasta ahora, has visto cómo JSX difiere de HTML de una manera clave con el uso de `className` vs. `class` para definir clases HTML.
Otra forma importante en la que JSX difiere de HTML está en la idea de la etiqueta de auto-cierre.
En HTML, casi todas las etiquetas tienen una etiqueta de apertura y cierre: `<div></div>`; la etiqueta de cierre siempre tiene una barra inclinada antes del nombre de la etiqueta que está cerrando. Sin embargo, hay instancias especiales en HTML llamadas “etiquetas auto-cerradas”, o etiquetas que no requieren una etiqueta de apertura y cierre antes de que otra etiqueta pueda comenzar.
Por ejemplo, la etiqueta salto de línea puede escribirse como `<br>` o como `<br />`, pero nunca debería escribirse como `<br></br>`, ya que no contiene ningún contenido.
En JSX, las reglas son un poco diferentes. Cualquier elemento JSX se puede escribir con una etiqueta de auto-cierre, y cada elemento debe ser cerrado. La etiqueta de salto de línea, por ejemplo, siempre debe escribirse como `<br />` para ser un JSX válido que puede ser transpilado. Por otra parte, un `<div>` puede escribirse como `<div />` o `<div></div>`. La diferencia es que en la primera versión de sintaxis no hay forma de incluir nada en la `<div />`. Verás en desafíos posteriores que esta sintaxis es útil al renderizar componentes de React.
# --instructions--
Corrige los errores en el editor de código para que sea JSX válido y se transpile exitosamente. Asegúrate de no cambiar nada del contenido: sólo tienes que cerrar las etiquetas donde se necesite.
# --hints--
La constante `JSX` debe devolver un elemento `div`.
```js
assert.strictEqual(JSX.type, 'div');
```
El `div` debe contener una etiqueta `br`.
```js
assert(Enzyme.shallow(JSX).find('br').length === 1);
```
El `div` debe contener una etiqueta `hr`.
```js
assert(Enzyme.shallow(JSX).find('hr').length === 1);
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(JSX, document.getElementById('root'))
```
## --seed-contents--
```jsx
const JSX = (
<div>
<h2>Welcome to React!</h2> <br >
<p>Be sure to close all tags!</p>
<hr >
</div>
);
```
# --solutions--
```jsx
const JSX = (
<div>
<h2>Welcome to React!</h2> <br />
<p>Be sure to close all tags!</p>
<hr />
</div>
);
```

View File

@ -0,0 +1,187 @@
---
id: 5a24c314108439a4d4036180
title: Optimiza re-renderizadores con shouldComponentUpdate
challengeType: 6
forumTopicId: 301398
dashedName: optimize-re-renders-with-shouldcomponentupdate
---
# --description--
Hasta ahora, si cualquier componente recibe un nuevo `state` o un nuevo `props`, se vuelve a renderizar a sí mismo y a todos sus hijos. Normalmente, esto está bien. Pero React proporciona un método de ciclo de vida al que puedes llamar cuando los componentes hijos reciben nuevos `state` o `props`, y declarar específicamente si los componentes deben actualizarse o no. El método es `shouldComponentUpdate()`y toma `nextProps` y `nextState` como parámetros.
Este método es una forma útil de optimizar el rendimiento. Por ejemplo, el comportamiento predeterminado es que el componente re-renderiza cuando recibe nuevos `props`, incluso si los `props` no han cambiado. Puedes usar `shouldComponentUpdate()` para evitar esto comparando los `props`. El método debe devolver un valor `boolean` que le diga a React si actualizar o no el componente. Puedes comparar los "props" actuales (`this.props`) a los siguientes "props" (`nextProps`) para determinar si necesita actualizar o no, y devuelve `true` o `false` en consecuencia.
# --instructions--
El método `shouldComponentUpdate()` se añade en un componente llamado `OnlyEvens`. Actualmente, este método devuelve `true`, así que `OnlyEvens` re-renderizar cada vez que recibe nuevos `props`. Modifica el método para que `OnlyEvens` se actualice sólo si el `value` de sus nuevos accesorios es par. Haz clic en el botón `Add` y observa el orden de los eventos en la consola de tu navegador mientras se activan los "hooks" del ciclo de vida.
# --hints--
El componente `Controller` debe renderizar el componente `OnlyEvens` como un componente hijo.
```js
assert(
(() => {
const mockedComponent = Enzyme.mount(React.createElement(Controller));
return (
mockedComponent.find('Controller').length === 1 &&
mockedComponent.find('OnlyEvens').length === 1
);
})()
);
```
El método `shouldComponentUpdate` debe definirse en el componente `OnlyEvens`.
```js
assert(
(() => {
const child = React.createElement(OnlyEvens)
.type.prototype.shouldComponentUpdate.toString()
.replace(/s/g, '');
return child !== 'undefined';
})()
);
```
El componente `OnlyEvens` debe devolver una etiqueta `h1` que renderiza el valor de `this.props.value`.
```js
(() => {
const mockedComponent = Enzyme.mount(React.createElement(Controller));
const first = () => {
mockedComponent.setState({ value: 1000 });
return mockedComponent.find('h1').html();
};
const second = () => {
mockedComponent.setState({ value: 10 });
return mockedComponent.find('h1').html();
};
const firstValue = first();
const secondValue = second();
assert(firstValue === '<h1>1000</h1>' && secondValue === '<h1>10</h1>');
})();
```
`OnlyEvens` debe re-renderizar sólo cuando `nextProps.value` sea par.
```js
(() => {
const mockedComponent = Enzyme.mount(React.createElement(Controller));
const first = () => {
mockedComponent.setState({ value: 8 });
return mockedComponent.find('h1').text();
};
const second = () => {
mockedComponent.setState({ value: 7 });
return mockedComponent.find('h1').text();
};
const third = () => {
mockedComponent.setState({ value: 42 });
return mockedComponent.find('h1').text();
};
const firstValue = first();
const secondValue = second();
const thirdValue = third();
assert(firstValue === '8' && secondValue === '8' && thirdValue === '42');
})();
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<Controller />, document.getElementById('root'));
```
## --seed-contents--
```jsx
class OnlyEvens extends React.Component {
constructor(props) {
super(props);
}
shouldComponentUpdate(nextProps, nextState) {
console.log('Should I update?');
// Change code below this line
return true;
// Change code above this line
}
componentDidUpdate() {
console.log('Component re-rendered.');
}
render() {
return <h1>{this.props.value}</h1>;
}
}
class Controller extends React.Component {
constructor(props) {
super(props);
this.state = {
value: 0
};
this.addValue = this.addValue.bind(this);
}
addValue() {
this.setState(state => ({
value: state.value + 1
}));
}
render() {
return (
<div>
<button onClick={this.addValue}>Add</button>
<OnlyEvens value={this.state.value} />
</div>
);
}
}
```
# --solutions--
```jsx
class OnlyEvens extends React.Component {
constructor(props) {
super(props);
}
shouldComponentUpdate(nextProps, nextState) {
console.log('Should I update?');
// Change code below this line
return nextProps.value % 2 === 0;
// Change code above this line
}
componentDidUpdate() {
console.log('Component re-rendered.');
}
render() {
return <h1>{this.props.value}</h1>;
}
}
class Controller extends React.Component {
constructor(props) {
super(props);
this.state = {
value: 0
};
this.addValue = this.addValue.bind(this);
}
addValue() {
this.setState(state => ({
value: state.value + 1
}));
}
render() {
return (
<div>
<button onClick={this.addValue}>Add</button>
<OnlyEvens value={this.state.value} />
</div>
);
}
}
```

View File

@ -0,0 +1,112 @@
---
id: 5a24c314108439a4d403616c
title: Reemplaza las "props" predeterminadas
challengeType: 6
forumTopicId: 301399
dashedName: override-default-props
---
# --description--
La capacidad de establecer valores por defecto para las props es una característica útil en React. La manera de reemplazar las props predeterminadas es establecer explícitamente los valores de las props para un componente.
# --instructions--
El componente `ShoppingCart` ahora renderiza un componente hijo `Items`. Este componente `Items` tiene una prop predeterminada `quantity` establecida al entero `0`. Reemplaza la prop predeterminada pasando un valor de `10` para `quantity`.
**Nota:** Recuerda que la sintaxis para agregar una prop a un componente se parece a la sintaxis para agregar atributos en un elemento HTML. Sin embargo, dado que el valor de `quantity` es un entero, no irá entre comillas pero debe estar envuelto entre llaves. Por ejemplo, `{100}`. Esta sintaxis le dice a JSX que interprete el valor dentro de las llaves directamente como JavaScript.
# --hints--
El componente `ShoppingCart` debe renderizarse.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(ShoppingCart));
return mockedComponent.find('ShoppingCart').length === 1;
})()
);
```
El componente `Items` debe renderizarse.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(ShoppingCart));
return mockedComponent.find('Items').length === 1;
})()
);
```
El componente `Items` debe tener una prop de `{ quantity: 10 }` heredada desde el componente `ShoppingCart`.
```js
(getUserInput) =>
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(ShoppingCart));
return (
mockedComponent.find('Items').props().quantity == 10 &&
getUserInput('index')
.replace(/ /g, '')
.includes('<Itemsquantity={10}/>')
);
})()
);
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<ShoppingCart />, document.getElementById('root'))
```
## --seed-contents--
```jsx
const Items = (props) => {
return <h1>Current Quantity of Items in Cart: {props.quantity}</h1>
}
Items.defaultProps = {
quantity: 0
}
class ShoppingCart extends React.Component {
constructor(props) {
super(props);
}
render() {
{ /* Change code below this line */ }
return <Items />
{ /* Change code above this line */ }
}
};
```
# --solutions--
```jsx
const Items = (props) => {
return <h1>Current Quantity of Items in Cart: {props.quantity}</h1>
}
Items.defaultProps = {
quantity: 0
}
class ShoppingCart extends React.Component {
constructor(props) {
super(props);
}
render() {
{ /* Change code below this line */ }
return <Items quantity = {10} />
{ /* Change code above this line */ }
}
};
```

View File

@ -0,0 +1,217 @@
---
id: 5a24c314108439a4d403617b
title: Pasa un callback como "props"
challengeType: 6
forumTopicId: 301400
dashedName: pass-a-callback-as-props
---
# --description--
Puedes pasar `state` como "props" a los componentes hijos, pero no estás limitado a pasar datos. También puedes pasar funciones manejadoras o cualquier método que se defina en un componente React a un componente hijo. Así es como tú permites que los componentes hijos interactúen con sus componentes padres. Pasas métodos a un hijo igual que un "prop" normal. Se le asigna un nombre y tienes acceso a ese nombre de método en `this.props` en el componente hijo.
# --instructions--
Hay tres componentes descritos en el editor de código. El componente `MyApp` es el padre que renderizará los componentes hijos `GetInput` y `RenderInput`. Añade el componente `GetInput` al método de renderizar en `MyApp`, luego pásale un "prop" llamado `input` asignado a `inputValue` desde el estado `state` de `MyApp`. También crea un "prop" llamado `handleChange` y pasa el controlador de entrada `handleChange` a este.
A continuación, añade `RenderInput` al método de renderizar en `MyApp`, luego crea un "prop" llamado `input` y pasa él `inputValue` desde el estado `state` a este. Una vez que hayas terminado podrás escribir en el campo `input` en el componente `GetInput`, que luego llama al método manejador en su padre a través de "props". Esto actualiza la entrada en el `state` del padre, que se pasa como "props" a ambos hijos. Observa cómo fluyen los datos entre los componentes y cómo la única fuente de verdad sigue siendo el `state` del componente padre. Es cierto que este ejemplo es un poco inventado, pero debe servir para ilustrar cómo los datos y los callbacks pueden ser pasados entre componentes React.
# --hints--
El componente `MyApp` debe renderizarse.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(MyApp));
return mockedComponent.find('MyApp').length === 1;
})()
);
```
El componente `GetInput` debe renderizarse.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(MyApp));
return mockedComponent.find('GetInput').length === 1;
})()
);
```
El componente `RenderInput` debe renderizarse.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(MyApp));
return mockedComponent.find('RenderInput').length === 1;
})()
);
```
El componente `GetInput` debe recibir la propiedad de estado `MyApp` `inputValue` como "props" y contener un elemento `input` que modifica el estado de `MyApp`.
```js
async () => {
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
const mockedComponent = Enzyme.mount(React.createElement(MyApp));
const state_1 = () => {
mockedComponent.setState({ inputValue: '' });
return waitForIt(() => mockedComponent.state());
};
const state_2 = () => {
mockedComponent
.find('input')
.simulate('change', { target: { value: 'TestInput' } });
return waitForIt(() => mockedComponent.state());
};
const updated_1 = await state_1();
const updated_2 = await state_2();
assert(updated_1.inputValue === '' && updated_2.inputValue === 'TestInput');
};
```
El componente `RenderInput` debe recibir la propiedad de estado `MyApp` `inputValue` como "props".
```js
async () => {
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
const mockedComponent = Enzyme.mount(React.createElement(MyApp));
const state_1 = () => {
mockedComponent.setState({ inputValue: 'TestName' });
return waitForIt(() => mockedComponent);
};
const updated_1 = await state_1();
assert(updated_1.find('p').text().includes('TestName'));
};
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<MyApp />, document.getElementById('root'))
```
## --seed-contents--
```jsx
class MyApp extends React.Component {
constructor(props) {
super(props);
this.state = {
inputValue: ''
}
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({
inputValue: event.target.value
});
}
render() {
return (
<div>
{ /* Change code below this line */ }
{ /* Change code above this line */ }
</div>
);
}
};
class GetInput extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h3>Get Input:</h3>
<input
value={this.props.input}
onChange={this.props.handleChange}/>
</div>
);
}
};
class RenderInput extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h3>Input Render:</h3>
<p>{this.props.input}</p>
</div>
);
}
};
```
# --solutions--
```jsx
class MyApp extends React.Component {
constructor(props) {
super(props);
this.state = {
inputValue: ''
}
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({
inputValue: event.target.value
});
}
render() {
return (
<div>
<GetInput
input={this.state.inputValue}
handleChange={this.handleChange}/>
<RenderInput
input={this.state.inputValue}/>
</div>
);
}
};
class GetInput extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h3>Get Input:</h3>
<input
value={this.props.input}
onChange={this.props.handleChange}/>
</div>
);
}
};
class RenderInput extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h3>Input Render:</h3>
<p>{this.props.input}</p>
</div>
);
}
};
```

View File

@ -0,0 +1,186 @@
---
id: 5a24c314108439a4d403616a
title: Pasa un arreglo como props
challengeType: 6
forumTopicId: 301401
dashedName: pass-an-array-as-props
---
# --description--
El último desafío demostró cómo pasar información desde un componente padre a un componente hijo como `props` o propiedades. Este desafío busca demostrar cómo se pueden pasar arreglos como `props`. Para pasar un arreglo a un elemento JSX, debe ser tratado como JavaScript y envolverlo entre llaves.
```jsx
<ParentComponent>
<ChildComponent colors={["green", "blue", "red"]} />
</ParentComponent>
```
El componente hijo entonces tiene acceso a la propiedad del arreglo `colors`. Los métodos de arreglo, como `join()` pueden ser usados al acceder a la propiedad. `const ChildComponent = (props) => <p>{props.colors.join(', ')}</p>` Esto unirá a todos elementos `colors` del arreglo en una cadena separada por comas y se convertirá en: `<p>green, blue, red</p>` Más tarde, aprenderemos sobre otros métodos comunes para representar arreglos de datos en React.
# --instructions--
Están los componentes `List` y `ToDo` en el editor de código. Al renderizar cada `List` del componente `ToDo`, pasa una propiedad `tasks` asignada a un arreglo de tareas pendientes, por ejemplo `["walk dog", "workout"]`. Luego, accede a este arreglo de `tasks` en el componente `List`, mostrando su valor dentro del elemento `p`. Usa `join(", ")` para mostrar el arreglo `props.tasks` en el elemento `p` como una lista separada por comas. La lista de hoy debe tener al menos 2 tareas y la de mañana debe tener al menos 3 tareas.
# --hints--
El componente `ToDo` debe devolver un solo elemento `div`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(ToDo));
return mockedComponent.children().first().type() === 'div';
})()
);
```
El tercer hijo del componente `ToDo` debe ser una instancia del componente `List`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(ToDo));
return mockedComponent.children().first().childAt(2).name() === 'List';
})()
);
```
El quinto hijo del componente `ToDo` debe ser una instancia del componente `List`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(ToDo));
return mockedComponent.children().first().childAt(4).name() === 'List';
})()
);
```
Ambas instancias del componente `List` deben tener una propiedad llamada `tasks` y `tasks` debe ser de tipo arreglo.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(ToDo));
return (
Array.isArray(mockedComponent.find('List').get(0).props.tasks) &&
Array.isArray(mockedComponent.find('List').get(1).props.tasks)
);
})()
);
```
El primer componente `List` que representa las tareas de hoy debe tener 2 o más elementos.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(ToDo));
return mockedComponent.find('List').get(0).props.tasks.length >= 2;
})()
);
```
El segundo componente `List` que representa las tareas para mañana debe tener 3 o más elementos.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(ToDo));
return mockedComponent.find('List').get(1).props.tasks.length >= 3;
})()
);
```
El componente `List` debe renderizar el valor del prop `tasks` en la etiqueta `p`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(ToDo));
return (
mockedComponent
.find('p')
.get(0)
.props.children.replace(/\s*,\s*/g, ',') ===
mockedComponent
.find('List')
.get(0)
.props.tasks.join(',')
.replace(/\s*,\s*/g, ',') &&
mockedComponent
.find('p')
.get(1)
.props.children.replace(/\s*,\s*/g, ',') ===
mockedComponent
.find('List')
.get(1)
.props.tasks.join(',')
.replace(/\s*,\s*/g, ',')
);
})()
);
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<ToDo />, document.getElementById('root'))
```
## --seed-contents--
```jsx
const List = (props) => {
{ /* Change code below this line */ }
return <p>{}</p>
{ /* Change code above this line */ }
};
class ToDo extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h1>To Do Lists</h1>
<h2>Today</h2>
{ /* Change code below this line */ }
<List/>
<h2>Tomorrow</h2>
<List/>
{ /* Change code above this line */ }
</div>
);
}
};
```
# --solutions--
```jsx
const List= (props) => {
return <p>{props.tasks.join(', ')}</p>
};
class ToDo extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h1>To Do Lists</h1>
<h2>Today</h2>
<List tasks={['study', 'exercise']} />
<h2>Tomorrow</h2>
<List tasks={['call Sam', 'grocery shopping', 'order tickets']} />
</div>
);
}
};
```

View File

@ -0,0 +1,164 @@
---
id: 5a24c314108439a4d4036169
title: Pasa props a un componente funcional sin estado
challengeType: 6
forumTopicId: 301402
dashedName: pass-props-to-a-stateless-functional-component
---
# --description--
Los desafíos anteriores cubrieron varios casos de creación y composición de elementos de JSX, componentes funcionales y componentes de clase estilo ES6 en React. Con estos cimientos, ha llegado la hora de observar otro patrón de uso muy común en React: **props**. En React, se pueden pasar props, o propiedades a componentes hijos. Digamos, que tienes un componente `App` que devuelve un componente hijo llamado `Welcome`, el cual es un componente funcional sin estado. Puedes pasarle una propiedad llamada `user` a `Welcome` escribiendo:
```jsx
<App>
<Welcome user='Mark' />
</App>
```
Puedes utilizar **atributos personalizados de HTML** creados por ti y soportados por React para ser pasados por props a tu componente. En este caso, la propiedad creada `user` es pasada como atributo al componente `Welcome`. Dado que `Welcome` es un componente funcional sin estado, tiene acceso a este valor de la siguiente manera:
```jsx
const Welcome = (props) => <h1>Hello, {props.user}!</h1>
```
Este valor es llamado `props` por convención y, cuando se trata de componentes funcionales sin estado, se lo considera como un argumento pasado a una función que retorna JSX. Puedes acceder el valor del argumento en el cuerpo de la función. En los componentes de clase, verás que esto es un poco diferente.
# --instructions--
Hay componentes `Calendar` y `CurrentDate` en el editor de código. Al prensentar `CurrentDate` desde el componente `Calendar`, pasa una propiedad de `date` asignada a la fecha actual desde el objeto `Date` de JavaScript. Luego, accede a este `prop` dentro del componente `CurrentDate`, mostrando su valor dentro de las etiquetas `p`. Tenga en cuenta que los valores `prop` se evalúen como JavaScript, deben estar encerrados dentro de corchetes, por ejemplo `date={Date()}`.
# --hints--
El componente `Calendar` debe retornar un único elemento `div`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(Calendar));
return mockedComponent.children().type() === 'div';
})()
);
```
El segundo componente hijo del componente `Calendar` debe ser el componente `CurrentDate`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(Calendar));
return mockedComponent.children().childAt(1).name() === 'CurrentDate';
})()
);
```
El componente `CurrentDate` debe tener una propiedad llamada `date`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(Calendar));
return mockedComponent.children().childAt(1).props().date;
})()
);
```
La propiedad `date` del componente `CurrentDate` debe contener una cadena de texto.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(Calendar));
const prop = mockedComponent.children().childAt(1).props().date;
return typeof prop === 'string' && prop.length > 0;
})()
);
```
La propiedad `date` debe ser generada invocando el método `Date()`
```js
assert(/<CurrentDatedate={Date\(\)}\/>/.test(__helpers.removeWhiteSpace(code)));
```
El componente `CurrentDate` debe mostrar el valor del prop `date` dentro de la etiqueta `p`.
```js
let date = 'dummy date';
assert(
(function () {
const mockedComponent = Enzyme.mount(
React.createElement(CurrentDate, { date })
);
return mockedComponent.find('p').html().includes(date);
})()
);
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<Calendar />, document.getElementById('root'))
```
## --seed-contents--
```jsx
const CurrentDate = (props) => {
return (
<div>
{ /* Change code below this line */ }
<p>The current date is: </p>
{ /* Change code above this line */ }
</div>
);
};
class Calendar extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h3>What date is it?</h3>
{ /* Change code below this line */ }
<CurrentDate />
{ /* Change code above this line */ }
</div>
);
}
};
```
# --solutions--
```jsx
const CurrentDate = (props) => {
return (
<div>
{ /* Change code below this line */ }
<p>The current date is: {props.date}</p>
{ /* Change code above this line */ }
</div>
);
};
class Calendar extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h3>What date is it?</h3>
{ /* Change code below this line */ }
<CurrentDate date={Date()} />
{ /* Change code above this line */ }
</div>
);
}
};
```

View File

@ -0,0 +1,145 @@
---
id: 5a24c314108439a4d403617a
title: Pasa el estado como "props" a componentes hijos
challengeType: 6
forumTopicId: 301403
dashedName: pass-state-as-props-to-child-components
---
# --description--
Has visto varios ejemplos de como pasar props a elementos JSX hijos y a componentes React hijos en desafíos anteriores. Te preguntarás de dónde vienen esos props. Un patrón común es tener un componente con estado que contenga el `state` importante para tu aplicación, que luego renderiza los componentes hijos. Quieres que estos componentes tengan acceso a algunas partes de ese `state`, el cual se pasa como props.
Por ejemplo, tal vez tengas un componente `App` que renderiza una `Navbar`, entre otros componentes. En tu `App`, tienes un `state` que contiene mucha información del usuario, pero la `Navbar` sólo necesita acceder al nombre de usuario para poder mostrarlo. Pasas esa parte del `state` al componente `Navbar` como prop.
Este patrón ilustra algunos paradigmas importantes en React. El primero es *unidirectional data flow*. El componente de estado fluye en una sola dirección descendiendo en el árbol de componentes de tu aplicación, desde el componente padre hasta los componentes hijos. Los componentes hijos únicamente reciben los datos del componente de estado que ellos necesitan. La segunda es que las aplicaciones con estado pueden ser divididas en solo algunos, o tal vez un solo, componente con estado. El resto de tus componentes simplemente reciben el estado del padre como props, y renderizan la interfaz de usuario a partir de ese estado. Esto comienza a crear una separación donde la administración de estado es manejada en una parte del código y la renderización de la interfaz de usuario en otra. Este principio de separar la lógica de estado de la lógica de la interfaz de usuario es uno de los principios clave de React. Cuando se utiliza correctamente, hace que el diseño de aplicaciones complejas y de estado sea mucho más fácil de gestionar.
# --instructions--
El componente `MyApp` es de estado y renderiza un componente `Navbar` como un componente hijo. Pasa la propiedad `name` en su `state` al componente hijo, luego muestra el `name` en la etiqueta `h1` que es parte del método de renderizado de `Navbar`. `name` debe aparecer luego del texto `Hello, my name is:`.
# --hints--
El componente `MyApp` debe renderizare con un componente `Navbar` dentro.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(MyApp));
return (
mockedComponent.find('MyApp').length === 1 &&
mockedComponent.find('Navbar').length === 1
);
})()
);
```
El componente `Navbar` debe recibir la propiedad de estado `name` de `MyApp` como props.
```js
async () => {
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
const mockedComponent = Enzyme.mount(React.createElement(MyApp));
const setState = () => {
mockedComponent.setState({ name: 'TestName' });
return waitForIt(() => mockedComponent.find('Navbar').props());
};
const navProps = await setState();
assert(navProps.name === 'TestName');
};
```
El elemento `h1` en `Navbar` debe renderizar el prop `name`.
```js
async () => {
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
const mockedComponent = Enzyme.mount(React.createElement(MyApp));
const navH1Before = mockedComponent.find('Navbar').find('h1').text();
const setState = () => {
mockedComponent.setState({ name: 'TestName' });
return waitForIt(() => mockedComponent.find('Navbar').find('h1').text());
};
const navH1After = await setState();
assert(new RegExp('TestName').test(navH1After) && navH1After !== navH1Before);
};
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<MyApp />, document.getElementById('root'))
```
## --seed-contents--
```jsx
class MyApp extends React.Component {
constructor(props) {
super(props);
this.state = {
name: 'CamperBot'
}
}
render() {
return (
<div>
{/* Change code below this line */}
<Navbar />
{/* Change code above this line */}
</div>
);
}
};
class Navbar extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
{/* Change code below this line */}
<h1>Hello, my name is: </h1>
{/* Change code above this line */}
</div>
);
}
};
```
# --solutions--
```jsx
class MyApp extends React.Component {
constructor(props) {
super(props);
this.state = {
name: 'CamperBot'
}
}
render() {
return (
<div>
<Navbar name={this.state.name}/>
</div>
);
}
};
class Navbar extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h1>Hello, my name is: {this.props.name}</h1>
</div>
);
}
};
```

View File

@ -0,0 +1,159 @@
---
id: 5a24c314108439a4d4036167
title: Renderiza un componente de clase al DOM
challengeType: 6
forumTopicId: 301404
dashedName: render-a-class-component-to-the-dom
---
# --description--
Puede que recuerdes haber usado la API ReactDOM en un desafío anterior para renderizar elementos JSX al DOM. El proceso para renderizar los componentes de React será muy similar. Los últimos desafíos se centraron en los componentes y la composición, por lo que el renderizado se ha realizado tras bambalinas. Sin embargo, ninguna parte de código de React que escribas se procesará en el DOM sin realizar una llamada a la API de ReactDOM.
Aquí va un recordatorio de la sintaxis: `ReactDOM.render(componentToRender, targetNode)`. El primer argumento es el componente de React que deseas renderizar. El segundo argumento es el nodo del DOM en el que deseas renderizar ese componente.
Los componentes de React se pasan a `ReactDOM.render()` de manera un poco diferente a como se hace con los elementos JSX. Para los elementos JSX, pasas el nombre del elemento que deseas representar. Sin embargo, para los componentes de React, debes usar la misma sintaxis que si estuvieras renderizando un componente anidado, por ejemplo, `ReactDOM.render(<ComponentToRender />, targetNode)`. Se utiliza esta sintaxis tanto para los componentes de clase ES6 como para los componentes funcionales.
# --instructions--
Los componentes `Fruits` y `Vegetables` se definen por ti tras bambalinas. Renderiza ambos componentes como hijos del componente `TypesOfFood`, y luego renderiza `TypesOfFood` al DOM. Hay un `div` con `id='challenge-node'` disponible para que lo uses.
# --hints--
El componente `TypesOfFood` debe devolver un solo elemento `div`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(TypesOfFood));
return mockedComponent.children().type() === 'div';
})()
);
```
El componente `TypesOfFood` debe renderizar el componente `Fruits` después del elemento `h1`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(TypesOfFood));
return mockedComponent.children().childAt(1).name() === 'Fruits';
})()
);
```
El componente `TypesOfFood` debe renderizar el componente `Vegetables` después de `Fruits`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(TypesOfFood));
return mockedComponent.children().childAt(2).name() === 'Vegetables';
})()
);
```
El componente `TypesOfFood` debe renderizarse al DOM dentro del `div` con el id `challenge-node`.
```js
assert(
(function () {
const html = document.getElementById('challenge-node').childNodes[0]
.innerHTML;
return (
html.includes(
'<div><h2>Fruits:</h2><h4>Non-Citrus:</h4><ul><li>Apples</li><li>Blueberries</li><li>Strawberries</li><li>Bananas</li></ul><h4>Citrus:</h4><ul><li>Lemon</li><li>Lime</li><li>Orange</li><li>Grapefruit</li></ul></div>'
) &&
html.includes(
'<div><h2>Vegetables:</h2><ul><li>Brussel Sprouts</li><li>Broccoli</li><li>Squash</li></ul></div>'
)
);
})()
);
```
# --seed--
## --before-user-code--
```jsx
const Fruits = () => {
return (
<div>
<h2>Fruits:</h2>
<h4>Non-Citrus:</h4>
<ul>
<li>Apples</li>
<li>Blueberries</li>
<li>Strawberries</li>
<li>Bananas</li>
</ul>
<h4>Citrus:</h4>
<ul>
<li>Lemon</li>
<li>Lime</li>
<li>Orange</li>
<li>Grapefruit</li>
</ul>
</div>
);
};
const Vegetables = () => {
return (
<div>
<h2>Vegetables:</h2>
<ul>
<li>Brussel Sprouts</li>
<li>Broccoli</li>
<li>Squash</li>
</ul>
</div>
);
};
```
## --seed-contents--
```jsx
class TypesOfFood extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h1>Types of Food:</h1>
{/* Change code below this line */}
{/* Change code above this line */}
</div>
);
}
};
// Change code below this line
```
# --solutions--
```jsx
class TypesOfFood extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h1>Types of Food:</h1>
{/* Change code below this line */}
<Fruits />
<Vegetables />
{/* Change code above this line */}
</div>
);
}
};
// Change code below this line
ReactDOM.render(<TypesOfFood />, document.getElementById('challenge-node'));
```

View File

@ -0,0 +1,303 @@
---
id: 5a24c314108439a4d4036188
title: Renderiza condicionalmente a partir de "props"
challengeType: 6
forumTopicId: 301405
dashedName: render-conditionally-from-props
---
# --description--
Hasta ahora, has visto cómo utilizar `if/else`, `&&`, y el operador ternario (`condition ? expressionIfTrue : expressionIfFalse`) para tomar decisiones condicionales sobre qué renderizar y cuándo. Sin embargo, queda un tema importante por discutir que te permite combinar cualquier o todos estos conceptos con otra poderosa característica de React: los props. El uso de props para renderizar condicionalmente el código es muy común entre los desarrolladores de React, es decir, utilizan el valor de una prop dada para automáticamente tomar decisiones sobre qué renderizar.
En este desafío, configurarás un componente hijo para tomar decisiones de renderizado basadas en props. También usarás el operador ternario, pero puedes ver cómo varios de los otros conceptos que se cubrieron en los últimos desafíos podrían ser igual de útiles en este contexto.
# --instructions--
El editor de código tiene dos componentes que están parcialmente definidos para ti: un padre llamado `GameOfChance`, y un hijo llamado `Results`. Se utilizan para crear un juego sencillo en el que el usuario presiona un botón para ver si gana o pierde.
Primero, necesitarás una expresión simple que devuelva al azar un valor diferente cada vez que se ejecute. Puedes usar `Math.random()`. Este método devuelve un valor entre `0` (inclusivo) y `1` (exclusivo) cada vez que se llama. Así que para las probabilidades de 50/50, usa `Math.random() >= .5` en tu expresión. Estadísticamente hablando, esta expresión devolverá `true` 50% de las veces, y `false` el otro 50%. En el método de renderizado, reemplaza `null` con la expresión anterior para completar la declaración de variables.
Ahora tienes una expresión que puedes usar para tomar una decisión aleatoria en el código. A continuación, debes implementar esto. Renderiza el componente `Results` como hijo de `GameOfChance`, y pásalo a `expression` como un prop llamado `fiftyFifty`. En el componente `Results`, escribe una expresión ternaria para renderizar el elemento `h1` con el texto `You Win!` o `You Lose!` basado en el prop `fiftyFifty` que está siendo pasado desde `GameOfChance`. Finalmente, asegúrate de que el método `handleClick()` está contando correctamente cada turno para que el usuario sepa cuántas veces ha jugado. Esto también sirve para que el usuario sepa que el componente se ha actualizado en caso de que gane o pierda dos veces seguidas.
# --hints--
El componente `GameOfChance` debe existir y renderizarse en la página.
```js
assert.strictEqual(
Enzyme.mount(React.createElement(GameOfChance)).find('GameOfChance').length,
1
);
```
`GameOfChance` debe devolver un solo elemento `button`.
```js
assert.strictEqual(
Enzyme.mount(React.createElement(GameOfChance)).find('button').length,
1
);
```
`GameOfChance` debe devolver una sola instancia del componente `Results`, que tiene un prop llamado `fiftyFifty`.
```js
assert(
Enzyme.mount(React.createElement(GameOfChance)).find('Results').length ===
1 &&
Enzyme.mount(React.createElement(GameOfChance))
.find('Results')
.props()
.hasOwnProperty('fiftyFifty') === true
);
```
El estado de `GameOfChance` debe inicializarse con la propiedad `counter` establecida en valor `1`.
```js
assert.strictEqual(
Enzyme.mount(React.createElement(GameOfChance)).state().counter,
1
);
```
Cuando el componente `GameOfChance` es renderizado por primera vez en el DOM, un elemento `p` debe ser devuelto con el texto interno `Turn: 1`.
```js
assert.strictEqual(
Enzyme.mount(React.createElement(GameOfChance)).find('p').text(),
'Turn: 1'
);
```
Cada vez que se hace clic en el botón, el contador debe incrementarse por un valor de 1, y un único elemento `p` debe ser renderizado al DOM que contiene el texto `Turn: N`, donde `N` es el valor del contador.
```js
(() => {
const comp = Enzyme.mount(React.createElement(GameOfChance));
const simulate = () => {
comp.find('button').simulate('click');
};
const result = () => ({
count: comp.state('counter'),
text: comp.find('p').text()
});
const _1 = () => {
simulate();
return result();
};
const _2 = () => {
simulate();
return result();
};
const _3 = () => {
simulate();
return result();
};
const _4 = () => {
simulate();
return result();
};
const _5 = () => {
simulate();
return result();
};
const _1_val = _1();
const _2_val = _2();
const _3_val = _3();
const _4_val = _4();
const _5_val = _5();
assert(
_1_val.count === 2 &&
_1_val.text === 'Turn: 2' &&
_2_val.count === 3 &&
_2_val.text === 'Turn: 3' &&
_3_val.count === 4 &&
_3_val.text === 'Turn: 4' &&
_4_val.count === 5 &&
_4_val.text === 'Turn: 5' &&
_5_val.count === 6 &&
_5_val.text === 'Turn: 6'
);
})();
```
Cuando el componente `GameOfChance` se monta por primera vez en el DOM y cada vez que se hace clic en el botón, un solo elemento `h1` debe ser devuelto, el cual renderiza aleatoriamente `You Win!` o `You Lose!`.
```js
(() => {
const comp = Enzyme.mount(React.createElement(GameOfChance));
const simulate = () => {
comp.find('button').simulate('click');
};
const result = () => ({
h1: comp.find('h1').length,
text: comp.find('h1').text()
});
const _1 = result();
const _2 = () => {
simulate();
return result();
};
const _3 = () => {
simulate();
return result();
};
const _4 = () => {
simulate();
return result();
};
const _5 = () => {
simulate();
return result();
};
const _6 = () => {
simulate();
return result();
};
const _7 = () => {
simulate();
return result();
};
const _8 = () => {
simulate();
return result();
};
const _9 = () => {
simulate();
return result();
};
const _10 = () => {
simulate();
return result();
};
const _2_val = _2();
const _3_val = _3();
const _4_val = _4();
const _5_val = _5();
const _6_val = _6();
const _7_val = _7();
const _8_val = _8();
const _9_val = _9();
const _10_val = _10();
const __text = new Set([
_1.text,
_2_val.text,
_3_val.text,
_4_val.text,
_5_val.text,
_6_val.text,
_7_val.text,
_8_val.text,
_9_val.text,
_10_val.text
]);
const __h1 = new Set([
_1.h1,
_2_val.h1,
_3_val.h1,
_4_val.h1,
_5_val.h1,
_6_val.h1,
_7_val.h1,
_8_val.h1,
_9_val.h1,
_10_val.h1
]);
assert(__text.size === 2 && __h1.size === 1);
})();
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<GameOfChance />, document.getElementById('root'));
```
## --seed-contents--
```jsx
class Results extends React.Component {
constructor(props) {
super(props);
}
render() {
{/* Change code below this line */}
return <h1></h1>;
{/* Change code above this line */}
}
}
class GameOfChance extends React.Component {
constructor(props) {
super(props);
this.state = {
counter: 1
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(prevState => {
// Complete the return statement:
return {
counter: prevState
}
});
}
render() {
const expression = null; // Change this line
return (
<div>
<button onClick={this.handleClick}>Play Again</button>
{/* Change code below this line */}
{/* Change code above this line */}
<p>{'Turn: ' + this.state.counter}</p>
</div>
);
}
}
```
# --solutions--
```jsx
class Results extends React.Component {
constructor(props) {
super(props);
}
render() {
return <h1>{this.props.fiftyFifty ? 'You Win!' : 'You Lose!'}</h1>;
}
}
class GameOfChance extends React.Component {
constructor(props) {
super(props);
this.state = {
counter: 1
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(prevState => {
return {
counter: prevState.counter + 1
}
});
}
render() {
const expression = Math.random() >= 0.5;
return (
<div>
<button onClick={this.handleClick}>Play Again</button>
<Results fiftyFifty={expression} />
<p>{'Turn: ' + this.state.counter}</p>
</div>
);
}
}
```

View File

@ -0,0 +1,75 @@
---
id: 5a24bbe0dba28a8d3cbd4c5f
title: Renderiza elementos HTML al DOM
challengeType: 6
forumTopicId: 301406
dashedName: render-html-elements-to-the-dom
---
# --description--
Hasta ahora, has aprendido que JSX es una herramienta conveniente para escribir HTML legible dentro de JavaScript. Con React, podemos renderizar este JSX directamente al DOM HTML usando la API de renderizado de React conocida como ReactDOM.
ReactDOM ofrece un método simple para renderizar elementos React al DOM que se ve así: `ReactDOM.render(componentToRender, targetNode)`, donde el primer argumento es el elemento o componente React que deseas renderizar, y el segundo argumento es el nodo DOM al que se quiere renderizar el componente.
Como era de esperarse, `ReactDOM.render()` debe llamarse después de las declaraciones de los elementos JSX, al igual que hay que declarar las variables antes de usarlas.
# --instructions--
El editor de código tiene un componente JSX simple. Usa el método `ReactDOM.render()` para renderizar este componente a la página. Puedes pasar elementos JSX definidos directamente como el primer argumento y utilizar `document.getElementById()` para seleccionar el nodo DOM al que renderizar. Hay un `div` con `id='challenge-node'` disponible para que lo uses. Asegúrate de no cambiar la constante `JSX`.
# --hints--
La constante `JSX` debe devolver un elemento `div`.
```js
assert(JSX.type === 'div');
```
El `div` debe contener una etiqueta `h1` como primer elemento.
```js
assert(JSX.props.children[0].type === 'h1');
```
El elemento `div` debe contener una etiqueta `p` como segundo elemento.
```js
assert(JSX.props.children[1].type === 'p');
```
El elemento JSX proporcionado debe renderizarse al nodo DOM con id `challenge-node`.
```js
assert(
document.getElementById('challenge-node').childNodes[0].innerHTML ===
'<h1>Hello World</h1><p>Lets render this to the DOM</p>'
);
```
# --seed--
## --seed-contents--
```jsx
const JSX = (
<div>
<h1>Hello World</h1>
<p>Lets render this to the DOM</p>
</div>
);
// Change code below this line
```
# --solutions--
```jsx
const JSX = (
<div>
<h1>Hello World</h1>
<p>Lets render this to the DOM</p>
</div>
);
// Change code below this line
ReactDOM.render(JSX, document.getElementById('challenge-node'));
```

View File

@ -0,0 +1,76 @@
---
id: 5a24c314108439a4d403618d
title: Renderiza React en el servidor con renderToString
challengeType: 6
forumTopicId: 301407
dashedName: render-react-on-the-server-with-rendertostring
---
# --description--
Hasta ahora, has estado renderizando componentes React en el cliente. Normalmente, esto es lo que siempre harás. Sin embargo, hay algunos casos de uso donde tiene sentido renderizar un componente React en el servidor. Dado que React es una biblioteca de vistas de JavaScript y se puede ejecutar JavaScript en el servidor con Node, esto es posible. De hecho, React proporciona un método `renderToString()` que puedes usar para este propósito.
Hay dos razones clave por las que el renderizado en el servidor puede ser usado en una aplicación del mundo real. Primero, sin hacer esto, tus aplicaciones de React consistirían en un archivo HTML relativamente vacío y un gran paquete de JavaScript cuando se carga inicialmente en el navegador. Esto puede no ser ideal para motores de búsqueda que intentan indexar el contenido de tus páginas para que la gente pueda encontrarte. Si renderizas el código HTML inicial en el servidor y lo envía al cliente, la carga de la página inicial contiene todo el código de la página que los motores de búsqueda pueden rastrear. Segundo, esto crea una experiencia de carga de página inicial más rápida porque el HTML renderizado es más pequeño que el código JavaScript de toda la aplicación. React aún podrá reconocer tu aplicación y administrarla después de la carga inicial.
# --instructions--
El método `renderToString()` se proporciona en `ReactDOMServer`, el cual está disponible aquí como un objeto global. El método toma un argumento que es un elemento React. Usa esto para renderizar `App` a una cadena.
# --hints--
El componente `App` debe renderizar a una cadena usando `ReactDOMServer.renderToString`.
```js
(getUserInput) =>
assert(
getUserInput('index')
.replace(/ /g, '')
.includes('ReactDOMServer.renderToString(<App/>)') &&
Enzyme.mount(React.createElement(App)).children().name() === 'div'
);
```
# --seed--
## --before-user-code--
```jsx
var ReactDOMServer = { renderToString(x) { return null; } };
```
## --after-user-code--
```jsx
ReactDOM.render(<App />, document.getElementById('root'))
```
## --seed-contents--
```jsx
class App extends React.Component {
constructor(props) {
super(props);
}
render() {
return <div/>
}
};
// Change code below this line
```
# --solutions--
```jsx
class App extends React.Component {
constructor(props) {
super(props);
}
render() {
return <div/>
}
};
// Change code below this line
ReactDOMServer.renderToString(<App/>);
```

View File

@ -0,0 +1,119 @@
---
id: 5a24c314108439a4d4036172
title: Otra manera de renderizar el estado en la interfaz de usuario
challengeType: 6
forumTopicId: 301408
dashedName: render-state-in-the-user-interface-another-way
---
# --description--
Hay otra manera de acceder al `state` de un componente. En el método `render()`, antes de la sentencia `return`, se puede escribir JavaScript directamente. Por ejemplo, puedes declarar funciones, acceder a datos de `state` o `props`, realizar cálculos sobre estos datos, etc. Luego, puedes asignar cualquier dato a las variables, a las que tienes acceso en la sentencia `return`.
# --instructions--
En el método de renderización de `MyComponent`, define una `const` llamada `name` y ponlo igual al valor del nombre en el `state` del componente. Debido a que puedes escribir JavaScript directamente en esta parte del código, no tienes que incluir esta referencia entre llaves.
A continuación, en la sentencia return, renderiza este valor en una etiqueta `h1` usando la variable `name`. Recuerda, necesitas usar la sintaxis JSX (llaves para JavaScript) en la sentencia return.
# --hints--
`MyComponent` debe tener una clave `name` con valor `freeCodeCamp` almacenado en su estado.
```js
assert(
Enzyme.mount(React.createElement(MyComponent)).state('name') ===
'freeCodeCamp'
);
```
`MyComponent` debe renderizar un encabezado `h1` incluido en un solo `div`.
```js
assert(
/<div><h1>.*<\/h1><\/div>/.test(
Enzyme.mount(React.createElement(MyComponent)).html()
)
);
```
La etiqueta `h1` renderizada debe incluir una referencia a `{name}`.
```js
(getUserInput) =>
assert(/<h1>\n*\s*\{\s*name\s*\}\s*\n*<\/h1>/.test(getUserInput('index')));
```
El encabezado `h1` renderizado debe contener texto renderizado desde el state del componente.
```js
async () => {
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
const first = () => {
mockedComponent.setState({ name: 'TestName' });
return waitForIt(() => mockedComponent.html());
};
const firstValue = await first();
assert(firstValue === '<div><h1>TestName</h1></div>');
};
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<MyComponent />, document.getElementById('root'))
```
## --seed-contents--
```jsx
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
name: 'freeCodeCamp'
}
}
render() {
// Change code below this line
// Change code above this line
return (
<div>
{ /* Change code below this line */ }
{ /* Change code above this line */ }
</div>
);
}
};
```
# --solutions--
```jsx
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
name: 'freeCodeCamp'
}
}
render() {
// Change code below this line
const name = this.state.name;
// Change code above this line
return (
<div>
{ /* Change code below this line */ }
<h1>{name}</h1>
{ /* Change code above this line */ }
</div>
);
}
};
```

View File

@ -0,0 +1,113 @@
---
id: 5a24c314108439a4d4036171
title: Renderizar estado en la interfaz de usuario
challengeType: 6
forumTopicId: 301409
dashedName: render-state-in-the-user-interface
---
# --description--
Una vez que se define el estado inicial de un componente, se puede mostrar cualquier parte del mismo en la interfaz de usuario que se renderiza. Si un componente tiene estado, siempre tendrá acceso a los datos en `state` en su método `render()`. Puedes acceder a los datos con `this.state`.
Si quieres acceder a un valor de estado dentro del `return` del método de renderización, tienes que encerrar el valor entre llaves.
`state` es una de las características más poderosas de los componentes de React. Esto te permite realizar un seguimiento de los datos importantes en tu aplicación y generar una interfaz de usuario en respuesta a los cambios en estos datos. Si tus datos cambian, tu interfaz de usuario cambiará. React usa lo que se llama un DOM virtual, para realizar un seguimiento de los cambios detrás de escena. Cuando se actualizan los datos de estado, activa un re-renderizado de los componentes usando esos datos - incluyendo componentes hijos que recibieron los datos como un prop. React actualiza el DOM actual, pero solo cuando sea necesario. Esto significa que no tienes que preocuparte por cambiar el DOM. Tú simplemente declara cómo debe verse la UI.
Ten en cuenta que si creas un componente con estado, ningún otro componente es consciente de su `state`. Su `state` está completamente encapsulado, o local a ese componente, a menos que pases datos de estado a un componente hijo como `props`. Esta noción de `state` encapsulado, es muy importante porque te permite escribir cierta lógica, luego tener esa lógica contenida y aislada en un lugar de tu código.
# --instructions--
En el editor de código, `MyComponent` ya tiene estado. Define una etiqueta `h1` en el método de renderizado del componente que renderiza el valor del `name` desde el estado del componente.
**Note:** El `h1` solo debe renderizar el valor de `state` y nada más. En JSX, cualquier código que escribas con llaves `{ }` será tratado como JavaScript. Así que para acceder al valor desde él `state` solo hay que encerrar la referencia entre llaves.
# --hints--
`MyComponent` debe tener un `name` clave con valor `freeCodeCamp` almacenado en su estado.
```js
assert(
Enzyme.mount(React.createElement(MyComponent)).state('name') ===
'freeCodeCamp'
);
```
`MyComponent` debe renderizar un encabezado `h1` incluido en un solo `div`.
```js
assert(
/<div><h1>.*<\/h1><\/div>/.test(
Enzyme.mount(React.createElement(MyComponent)).html()
)
);
```
El encabezado renderizado `h1` sólo debe contener texto renderizado del estado del componente.
```js
async () => {
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
const first = () => {
mockedComponent.setState({ name: 'TestName' });
return waitForIt(() => mockedComponent.html());
};
const firstValue = await first();
const getValue = firstValue.replace(/\s/g, '');
assert(getValue === '<div><h1>TestName</h1></div>');
};
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<MyComponent />, document.getElementById('root'))
```
## --seed-contents--
```jsx
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
name: 'freeCodeCamp'
}
}
render() {
return (
<div>
{ /* Change code below this line */ }
{ /* Change code above this line */ }
</div>
);
}
};
```
# --solutions--
```jsx
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
name: 'freeCodeCamp'
}
}
render() {
return (
<div>
{ /* Change code below this line */ }
<h1>{this.state.name}</h1>
{ /* Change code above this line */ }
</div>
);
}
};
```

View File

@ -0,0 +1,155 @@
---
id: 5a24c314108439a4d4036184
title: Renderizar con una condición If-Else
challengeType: 6
forumTopicId: 301410
dashedName: render-with-an-if-else-condition
---
# --description--
Otra aplicación de uso de JavaScript para controlar su vista renderizada es vincular los elementos que son renderizados a una condición. Cuando la condición es verdadera (true), se renderiza una vista. Cuando es falso (false), es una vista diferente. Puedes hacer esto con una sentencia estándar `if/else` en el método `render()` de un componente React.
# --instructions--
MyComponent contiene un `boolean` en su estado que rastrea si deseas mostrar algún elemento en la interfaz de usuario o no. Él `button` alterna el estado de este valor. Actualmente, renderiza la misma UI cada vez. Reescribir el método `render()` con una sentencia `if/else` de modo que si `display` es `true`, devuelvas el marcado actual. De lo contrario, devuelve el marcado sin el elemento `h1`.
**Note:** Debes escribir un `if/else` para pasar las pruebas. El uso del operador ternario no pasará aquí.
# --hints--
`MyComponent` debe existir y renderizar.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
return mockedComponent.find('MyComponent').length === 1;
})()
);
```
Cuando `display` se establece en `true`, un `div`, `button`, y `h1` deben renderizarse.
```js
async () => {
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
const state_1 = () => {
mockedComponent.setState({ display: true });
return waitForIt(() => mockedComponent);
};
const updated = await state_1();
assert(
mockedComponent.find('div').length === 1 &&
mockedComponent.find('div').children().length === 2 &&
mockedComponent.find('button').length === 1 &&
mockedComponent.find('h1').length === 1
);
};
```
Cuando `display` se establece en `false`, solo un `div` y un `button` deben renderizarse.
```js
async () => {
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
const state_1 = () => {
mockedComponent.setState({ display: false });
return waitForIt(() => mockedComponent);
};
const updated = await state_1();
assert(
mockedComponent.find('div').length === 1 &&
mockedComponent.find('div').children().length === 1 &&
mockedComponent.find('button').length === 1 &&
mockedComponent.find('h1').length === 0
);
};
```
El método renderizado debe usar una sentencia `if/else` para comprobar la condición de `this.state.display`.
```js
(getUserInput) =>
assert(
getUserInput('index').includes('if') &&
getUserInput('index').includes('else')
);
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<MyComponent />, document.getElementById('root'))
```
## --seed-contents--
```jsx
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
display: true
}
this.toggleDisplay = this.toggleDisplay.bind(this);
}
toggleDisplay() {
this.setState((state) => ({
display: !state.display
}));
}
render() {
// Change code below this line
return (
<div>
<button onClick={this.toggleDisplay}>Toggle Display</button>
<h1>Displayed!</h1>
</div>
);
}
};
```
# --solutions--
```jsx
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
display: true
}
this.toggleDisplay = this.toggleDisplay.bind(this);
}
toggleDisplay() {
this.setState((state) => ({
display: !state.display
}));
}
render() {
// Change code below this line
if (this.state.display) {
return (
<div>
<button onClick={this.toggleDisplay}>Toggle Display</button>
<h1>Displayed!</h1>
</div>
);
} else {
return (
<div>
<button onClick={this.toggleDisplay}>Toggle Display</button>
</div>
);
}
}
};
```

View File

@ -0,0 +1,145 @@
---
id: 5a24c314108439a4d403616f
title: Revisa el uso de props con componentes funcionales sin estado
challengeType: 6
forumTopicId: 301411
dashedName: review-using-props-with-stateless-functional-components
---
# --description--
Excepto por el último desafío, has estado pasando props a componentes funcionales sin estado. Estos componentes actúan como funciones puras. Aceptan props como entrada y devuelven la misma vista cada vez que se les pasa el mismo props. Tal vez te estes preguntando que es estado, y el próximo desafío lo cubrirá con más detalle. Antes de eso, aquí hay una revisión de la terminología de los componentes.
Un *componente funcional sin estado*, es cualquier función que escribas que acepte props y devuelva JSX. Un *componente sin estado*, por otra parte, es una clase que extiende `React.Component`, pero no usa el estado interno (que será cubierto en el siguiente desafío). Finalmente, un *componente con estado* es un componente de clase que mantiene su propio estado interno. Puedes ver componentes con estado referidos simplemente como componentes de React.
Un patrón común es intentar minimizar el estado y crear componentes funcionales sin estado siempre que sea posible. Esto ayuda a contener tu administración de estado a un área específica de tu aplicación. A su vez, esto mejora el desarrollo y el mantenimiento de tu aplicación, haciendo más fácil seguir cómo los cambios en el estado afectan su comportamiento.
# --instructions--
El editor de código tiene un componente `CampSite` que renderiza un componente `Camper` como un componente hijo. Define el componente `Camper` y asigna los props predeterminados de `{ name: 'CamperBot' }`. Dentro del componente `Camper`, renderiza el código que quieras, pero asegúrate de tener un elemento `p` que incluya sólo el valor `name` que se pasa como un `prop`. Por último, define `propTypes` en el componente `Camper` para requerir que `name` sea proporcionado como un prop y verifique que sea de tipo `string`.
# --hints--
El `CampSite` debe renderizarse.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(CampSite));
return mockedComponent.find('CampSite').length === 1;
})()
);
```
El componente `Camper` debe renderizarse.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(CampSite));
return mockedComponent.find('Camper').length === 1;
})()
);
```
El componente `Camper` debe incluir props predeterminados que asignen la cadena `CamperBot` a la clave `name`.
```js
assert(
/Camper.defaultProps={name:(['"`])CamperBot\1,?}/.test(
__helpers.removeWhiteSpace(code)
)
);
```
El componente `Camper` debe incluir tipos de prop que requieran que el prop `name` sea del tipo `string`.
```js
assert(
/Camper.propTypes={name:PropTypes.string.isRequired,?}/.test(
__helpers.removeWhiteSpace(code)
)
);
```
El componente `Camper` debe contener un elemento `p` que contenga únicamente el texto del prop `name`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(CampSite));
return (
mockedComponent.find('p').text() ===
mockedComponent.find('Camper').props().name
);
})()
);
```
# --seed--
## --before-user-code--
```jsx
var PropTypes = {
string: { isRequired: true }
};
```
## --after-user-code--
```jsx
ReactDOM.render(<CampSite />, document.getElementById('root'))
```
## --seed-contents--
```jsx
class CampSite extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<Camper/>
</div>
);
}
};
// Change code below this line
```
# --solutions--
```jsx
class CampSite extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<Camper/>
</div>
);
}
};
// Change code below this line
const Camper = (props) => {
return (
<div>
<p>{props.name}</p>
</div>
);
};
Camper.propTypes = {
name: PropTypes.string.isRequired
};
Camper.defaultProps = {
name: 'CamperBot'
};
```

View File

@ -0,0 +1,143 @@
---
id: 5a24c314108439a4d4036173
title: Define el estado con this.setState
challengeType: 6
forumTopicId: 301412
dashedName: set-state-with-this-setstate
---
# --description--
Los desafíos anteriores cubrieron el componente `state` y cómo inicializar el state en el `constructor`. También hay una forma de cambiar el `state` del componente. React proporciona un método para actualizar el componente `state` llamado `setState`. El método `setState` dentro de tu clase de componente se llama así: `this.setState()`, pasando un objeto con pares clave-valor. Las claves son tus propiedades de estado y los valores son datos de estado actualizados. Por ejemplo, si estuviéramos almacenando un `username` en estado y quisiéramos actualizarlo, se vería así:
```jsx
this.setState({
username: 'Lewis'
});
```
React espera que nunca modifiques `state` directamente. En su lugar, siempre usa `this.setState()` cuando ocurran cambios de estado. Además, ten en cuenta que React puede agrupar múltiples actualizaciones de estado con el fin de mejorar el rendimiento. Esto significa que las actualizaciones de estado a través del método `setState` pueden ser asíncronas. Existe una sintaxis alternativa para el método `setState` que proporciona una forma de evitar ese problema. Esto es raramente necesario, ¡pero es bueno tenerlo en cuenta! Por favor, consulte la [documentación de React](https://facebook.github.io/react/docs/state-and-lifecycle.html) para más información.
# --instructions--
Hay un elemento `button` en el editor de código que tiene un controlador `onClick()`. Este controlador es activado cuando el `button` recibe un evento clic en el navegador, y ejecuta el método `handleClick` definido en `MyComponent`. Dentro del método `handleClick`, actualiza el componente `state` usando `this.setState()`. Establece la propiedad `name` en el `state` para igualar la cadena `React Rocks!`.
Haz clic en el botón y observa la actualización de estado renderizada. No te preocupes si no entiendes completamente cómo funciona el código del controlador de clics hasta ahora. Será cubierto en los siguientes desafíos.
# --hints--
El estado de `MyComponent` debe inicializarse con el par de valores clave `{ name: Initial State }`.
```js
assert(
Enzyme.mount(React.createElement(MyComponent)).state('name') ===
'Initial State'
);
```
`MyComponent` debe renderizar un encabezado `h1`.
```js
assert(Enzyme.mount(React.createElement(MyComponent)).find('h1').length === 1);
```
El encabezado `h1` renderizado debe contener texto renderizado desde el estado del componente.
```js
async () => {
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
const first = () => {
mockedComponent.setState({ name: 'TestName' });
return waitForIt(() => mockedComponent.html());
};
const firstValue = await first();
assert(/<h1>TestName<\/h1>/.test(firstValue));
};
```
Llamar al método `handleClick` en `MyComponent` debe establecer la propiedad de nombre en estado igual a `React Rocks!`.
```js
async () => {
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
const first = () => {
mockedComponent.setState({ name: 'Before' });
return waitForIt(() => mockedComponent.state('name'));
};
const second = () => {
mockedComponent.instance().handleClick();
return waitForIt(() => mockedComponent.state('name'));
};
const firstValue = await first();
const secondValue = await second();
assert(firstValue === 'Before' && secondValue === 'React Rocks!');
};
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<MyComponent />, document.getElementById('root'))
```
## --seed-contents--
```jsx
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
name: 'Initial State'
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
// Change code below this line
// Change code above this line
}
render() {
return (
<div>
<button onClick={this.handleClick}>Click Me</button>
<h1>{this.state.name}</h1>
</div>
);
}
};
```
# --solutions--
```jsx
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
name: 'Initial State'
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
// Change code below this line
this.setState({
name: 'React Rocks!'
});
// Change code above this line
}
render() {
return (
<div>
<button onClick = {this.handleClick}>Click Me</button>
<h1>{this.state.name}</h1>
</div>
);
}
};
```

View File

@ -0,0 +1,146 @@
---
id: 5a24c314108439a4d4036185
title: Usa && para una condicional más concisa
challengeType: 6
forumTopicId: 301413
dashedName: use--for-a-more-concise-conditional
---
# --description--
Las sentencias `if/else` funcionaron en el último desafío, pero hay una manera más concisa de lograr el mismo resultado. Imagina que estás rastreando varias condiciones en un componente y deseas que diferentes elementos se rendericen dependiendo de cada una de estas condiciones. Si escribes un montón de sentencias `else if` para devolver UIs ligeramente diferentes, puedes repetir código que deja espacio para el error. En su lugar, puedes usar el operador lógico `&&` para realizar lógica condicional de una manera más concisa. Esto es posible porque quieres comprobar si una condición es `true`, y si es así, devuelve algún código. Aquí hay un ejemplo:
```jsx
{condition && <p>markup</p>}
```
Si la `condition` es `true`, el código será devuelto. Si la condición es `false`, la operación devolverá inmediatamente `false` después de evaluar la `condition` y no devolverá nada. Puedes incluir estas sentencias directamente en tu JSX y encadenar varias condiciones juntas escribiendo `&&` después de cada uno. Esto te permite manejar una lógica condicional más compleja en tu método `render()` sin repetir un montón de código.
# --instructions--
Resuelve el ejemplo anterior de nuevo, de este modo el `h1` solo renderiza si `display` es `true`, pero usa el operador lógico `&&` en lugar de una sentencia `if/else`.
# --hints--
`MyComponent` debe existir y renderizar.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
return mockedComponent.find('MyComponent').length;
})()
);
```
Cuando `display` se establece en `true`, un `div`, `button`, y `h1` debe renderizarse.
```js
async () => {
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
const state_1 = () => {
mockedComponent.setState({ display: true });
return waitForIt(() => mockedComponent);
};
const updated = await state_1();
assert(
updated.find('div').length === 1 &&
updated.find('div').children().length === 2 &&
updated.find('button').length === 1 &&
updated.find('h1').length === 1
);
};
```
Cuando `display` se establece en `false`, solo un `div` y un `button` debe renderizarse.
```js
async () => {
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
const state_1 = () => {
mockedComponent.setState({ display: false });
return waitForIt(() => mockedComponent);
};
const updated = await state_1();
assert(
updated.find('div').length === 1 &&
updated.find('div').children().length === 1 &&
updated.find('button').length === 1 &&
updated.find('h1').length === 0
);
};
```
El método renderizador debe usar el operador lógico `&&` para comprobar la condición de `this.state.display`.
```js
(getUserInput) => assert(getUserInput('index').includes('&&'));
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<MyComponent />, document.getElementById('root'))
```
## --seed-contents--
```jsx
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
display: true
}
this.toggleDisplay = this.toggleDisplay.bind(this);
}
toggleDisplay() {
this.setState(state => ({
display: !state.display
}));
}
render() {
// Change code below this line
return (
<div>
<button onClick={this.toggleDisplay}>Toggle Display</button>
<h1>Displayed!</h1>
</div>
);
}
};
```
# --solutions--
```jsx
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
display: true
}
this.toggleDisplay = this.toggleDisplay.bind(this);
}
toggleDisplay() {
this.setState(state => ({
display: !state.display
}));
}
render() {
// Change code below this line
return (
<div>
<button onClick={this.toggleDisplay}>Toggle Display</button>
{this.state.display && <h1>Displayed!</h1>}
</div>
);
}
};
```

View File

@ -0,0 +1,280 @@
---
id: 5a24c314108439a4d4036187
title: Utiliza una expresión ternaria para el renderizado condicional
challengeType: 6
forumTopicId: 301414
dashedName: use-a-ternary-expression-for-conditional-rendering
---
# --description--
Antes de pasar a técnicas de renderizado dinámico, hay una última forma de usar condicionales de JavaScript incorporados para representar lo que quieres: el <dfn>operador ternario</dfn>. El operador ternario a menudo es utilizado como un acceso directo para las sentencias `if/else` en JavaScript. No son tan robustas como las sentencias tradicionales `if/else`, pero son muy populares entre los desarrolladores de React. Una de las razones de esto es debido a cómo se compila JSX, las sentencias `if/else` no se pueden insertar directamente en el código JSX. Puede que hayas notado esto hace un par de desafíos, cuando se requirió una sentencia `if/else`, siempre estaba *fuera* de la sentencia `return`. Las expresiones ternarias pueden ser una excelente alternativa si deseas implementar lógica condicional dentro de tu JSX. Recuerda que un operador ternario tiene tres partes, pero puedes combinar varias expresiones ternarias juntas. Aquí está la sintaxis básica:
```jsx
condition ? expressionIfTrue : expressionIfFalse;
```
# --instructions--
El editor de código tiene tres constantes definidas dentro del método `render()` del componente `CheckUserAge`. Estas se llaman `buttonOne`, `buttonTwo` y `buttonThree`. A cada una de estas se le asigna una expresión JSX simple que representa un elemento de botón. Primero, inicializa el estado de `CheckUserAge` con `input` y `userAge` ambos configurados a valores de una cadena vacía.
Una vez que el componente está renderizando información a la página, los usuarios deberían tener una forma de interactuar con ella. Dentro de la declaración `return` del componente, configura una expresión ternaria que implementa la siguiente lógica: cuando la página carga por primera vez, renderiza el botón de envío, `buttonOne`, a la página. Luego, cuando un usuario ingrese su edad y haga clic en el botón, renderiza un botón diferente basado en la edad. Si un usuario introduce un número menor que `18`, renderiza `buttonThree`. Si un usuario introduce un número mayor o igual a `18`, renderiza `buttonTwo`.
# --hints--
El componente `CheckUserAge` debe renderizarse con un solo elemento `input` y un solo elemento `button`.
```js
assert(
Enzyme.mount(React.createElement(CheckUserAge)).find('div').find('input')
.length === 1 &&
Enzyme.mount(React.createElement(CheckUserAge)).find('div').find('button')
.length === 1
);
```
El estado del componente `CheckUserAge` debe inicializarse con una propiedad de `userAge` y una propiedad de `input`, ambos establecidos a un valor de una cadena vacía.
```js
assert(
Enzyme.mount(React.createElement(CheckUserAge)).state().input === '' &&
Enzyme.mount(React.createElement(CheckUserAge)).state().userAge === ''
);
```
Cuando el componente `CheckUserAge` es renderizado por primera vez en el DOM, el texto interno del `button` debe decir "Submit".
```js
assert(
Enzyme.mount(React.createElement(CheckUserAge)).find('button').text() ===
'Submit'
);
```
Cuando se introduce un número menor de 18 en el elemento `input` y se hace clic en el `button`, el texto interno del `button` debe decir `You Shall Not Pass`.
```js
(() => {
const mockedComponent = Enzyme.mount(React.createElement(CheckUserAge));
const initialButton = mockedComponent.find('button').text();
const enter3AndClickButton = () => {
mockedComponent
.find('input')
.simulate('change', { target: { value: '3' } });
mockedComponent.find('button').simulate('click');
mockedComponent.update();
return mockedComponent.find('button').text();
};
const enter17AndClickButton = () => {
mockedComponent
.find('input')
.simulate('change', { target: { value: '17' } });
mockedComponent.find('button').simulate('click');
mockedComponent.update();
return mockedComponent.find('button').text();
};
const userAge3 = enter3AndClickButton();
const userAge17 = enter17AndClickButton();
assert(
initialButton === 'Submit' &&
userAge3 === 'You Shall Not Pass' &&
userAge17 === 'You Shall Not Pass'
);
})();
```
Cuando se introduce un número mayor o igual a 18 en el elemento `input` y se hace clic en el `button` el texto interno del `button` debe decir `You May Enter`.
```js
(() => {
const mockedComponent = Enzyme.mount(React.createElement(CheckUserAge));
const initialButton = mockedComponent.find('button').text();
const enter18AndClickButton = () => {
mockedComponent
.find('input')
.simulate('change', { target: { value: '18' } });
mockedComponent.find('button').simulate('click');
mockedComponent.update();
return mockedComponent.find('button').text();
};
const enter35AndClickButton = () => {
mockedComponent
.find('input')
.simulate('change', { target: { value: '35' } });
mockedComponent.find('button').simulate('click');
mockedComponent.update();
return mockedComponent.find('button').text();
};
const userAge18 = enter18AndClickButton();
const userAge35 = enter35AndClickButton();
assert(
initialButton === 'Submit' &&
userAge18 === 'You May Enter' &&
userAge35 === 'You May Enter'
);
})();
```
Una vez que un número ha sido enviado, y el valor del `input` se cambia una vez más, el `button` debe decir `Submit`.
```js
(() => {
const mockedComponent = Enzyme.mount(React.createElement(CheckUserAge));
const enter18AndClickButton = () => {
mockedComponent
.find('input')
.simulate('change', { target: { value: '18' } });
mockedComponent.find('button').simulate('click');
mockedComponent.update();
return mockedComponent.find('button').text();
};
const changeInputDontClickButton = () => {
mockedComponent
.find('input')
.simulate('change', { target: { value: '5' } });
mockedComponent.update();
return mockedComponent.find('button').text();
};
const enter10AndClickButton = () => {
mockedComponent
.find('input')
.simulate('change', { target: { value: '10' } });
mockedComponent.find('button').simulate('click');
mockedComponent.update();
return mockedComponent.find('button').text();
};
const userAge18 = enter18AndClickButton();
const changeInput1 = changeInputDontClickButton();
const userAge10 = enter10AndClickButton();
const changeInput2 = changeInputDontClickButton();
assert(
userAge18 === 'You May Enter' &&
changeInput1 === 'Submit' &&
userAge10 === 'You Shall Not Pass' &&
changeInput2 === 'Submit'
);
})();
```
Tu código no debe contener ninguna sentencia `if/else`.
```js
assert(
new RegExp(/(\s|;)if(\s|\()/).test(
Enzyme.mount(React.createElement(CheckUserAge)).instance().render.toString()
) === false
);
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<CheckUserAge />, document.getElementById('root'));
```
## --seed-contents--
```jsx
const inputStyle = {
width: 235,
margin: 5
};
class CheckUserAge extends React.Component {
constructor(props) {
super(props);
// Change code below this line
// Change code above this line
this.submit = this.submit.bind(this);
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
this.setState({
input: e.target.value,
userAge: ''
});
}
submit() {
this.setState(state => ({
userAge: state.input
}));
}
render() {
const buttonOne = <button onClick={this.submit}>Submit</button>;
const buttonTwo = <button>You May Enter</button>;
const buttonThree = <button>You Shall Not Pass</button>;
return (
<div>
<h3>Enter Your Age to Continue</h3>
<input
style={inputStyle}
type='number'
value={this.state.input}
onChange={this.handleChange}
/>
<br />
{/* Change code below this line */}
{/* Change code above this line */}
</div>
);
}
}
```
# --solutions--
```jsx
const inputStyle = {
width: 235,
margin: 5
};
class CheckUserAge extends React.Component {
constructor(props) {
super(props);
this.state = {
userAge: '',
input: ''
};
this.submit = this.submit.bind(this);
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
this.setState({
input: e.target.value,
userAge: ''
});
}
submit() {
this.setState(state => ({
userAge: state.input
}));
}
render() {
const buttonOne = <button onClick={this.submit}>Submit</button>;
const buttonTwo = <button>You May Enter</button>;
const buttonThree = <button>You Shall Not Pass</button>;
return (
<div>
<h3>Enter Your Age to Continue</h3>
<input
style={inputStyle}
type='number'
value={this.state.input}
onChange={this.handleChange}
/>
<br />
{this.state.userAge === ''
? buttonOne
: this.state.userAge >= 18
? buttonTwo
: buttonThree}
</div>
);
}
}
```

Some files were not shown because too many files have changed in this diff Show More