Construir una pantalla
Nos hemos concentrado en crear interfaces de usuario desde "abajo hacia arriba"; empezando con los componentes individuales y añadiendo complejidad gradualmente. Esto nos ha permitido desarrollar cada componente de forma aislada, determinar los datos que necesita y jugar con ellos en Storybook. ¡Todo sin necesidad de utilizar un servidor o, siquiera, construir una sola pantalla!
En este capítulo aumentaremos la sofisticación al combinar los componentes que hemos construido en una pantalla y desarrollar esa pantalla dentro de Storybook.
Componentes "contenedores"
Como nuestra aplicación es muy simple, la pantalla que construiremos es bastante trivial, simplemente envolviendo el componente TaskList
(que proporciona sus propios datos a través de Svelte Store) en alguna maqueta y sacando un campo error
de el store (asumamos que pondremos ese campo si tenemos algún problema para conectarnos a nuestro servidor). Ahora crearemos InboxScreen.svelte
dentro de la carpeta components
:
<script>
import TaskList from './TaskList.svelte';
export let error = false;
</script>
<div>
{#if error}
<div class="page lists-show">
<div class="wrapper-message">
<span class="icon-face-sad" />
<div class="title-message">Oh no!</div>
<div class="subtitle-message">Something went wrong</div>
</div>
</div>
{:else}
<div class="page lists-show">
<nav>
<h1 class="title-page">
<span class="title-wrapper">Taskbox</span>
</h1>
</nav>
<TaskList />
</div>
{/if}
</div>
Necesitamos actualizar nuestro store (en src/store.js
) para incluir nuestro nuevo campo error
, transformándolo en:
import { writable } from 'svelte/store';
const TaskBox = () => {
// creates a new writable store populated with some initial data
const { subscribe, update } = writable([
{ id: '1', title: 'Something', state: 'TASK_INBOX' },
{ id: '2', title: 'Something more', state: 'TASK_INBOX' },
{ id: '3', title: 'Something else', state: 'TASK_INBOX' },
{ id: '4', title: 'Something again', state: 'TASK_INBOX' },
]);
return {
subscribe,
// method to archive a task, think of a action with redux or Vuex
archiveTask: (id) =>
update((tasks) =>
tasks.map((task) => (task.id === id ? { ...task, state: 'TASK_ARCHIVED' } : task))
),
// method to archive a task, think of a action with redux or Vuex
pinTask: (id) =>
update((tasks) =>
tasks.map((task) => (task.id === id ? { ...task, state: 'TASK_PINNED' } : task))
),
};
};
export const taskStore = TaskBox();
// store to handle the app state
const appState = () => {
const { subscribe, update } = writable(false);
return {
subscribe,
error: () => update((error) => !error),
};
};
export const AppStore = appState();
También cambiamos nuestro componente App
para que incluya InboxScreen
(en una aplicación real esto sería manejado por el enrutador pero podemos obviarlo):
<script>
import { AppStore } from './store';
import InboxScreen from './components/InboxScreen.svelte';
</script>
<InboxScreen error={$AppStore} />
Sin embargo, al intentar mostrar nuestro componente "contenedor" dentro de Storybook las cosas se ponen interesantes.
Como vimos anteriormente, el componente TaskList
es un contenedor que renderiza el componente de presentación PureTaskList
. Por definición, los componentes contenedores no pueden renderizarse de manera aislada; esperan que se les pase algún contexto o servicio.
Al colocar la "Lista de tareas" TaskList
en Storybook, pudimos esquivar este problema simplemente renderizando la PureTaskList
y evadiendo el contenedor. Haremos algo similar y renderizaremos la PureInboxScreen
en Storybook también.
Entonces, cuando configuramos nuestras historias en InboxScreen.stories.js
:
import InboxScreen from './InboxScreen.svelte';
export default {
title: 'PureInboxScreen',
Component: InboxScreen,
};
export const standard = () => ({
Component: InboxScreen,
});
export const error = () => ({
Component: InboxScreen,
props: {
error: true,
},
});
Vemos que aunque la historia de error
y standard
funciona bien. (También encontrarás problemas similares cuando intentes probar la PureInboxScreen
con un test unitario si no se proporcionan datos como lo hicimos conTaskList
).
Recorrer los estados en Storybook hace que sea fácil comprobar que lo hemos hecho correctamente:
Desarrollo basado en componentes
Empezamos desde abajo con Task
, luego progresamos a TaskList
, ahora estamos aquí con una interfaz de usuario de pantalla completa. Nuestra InboxScreen
contiene un componente de contenedor anidado e incluye historias de acompañamiento.
El desarrollo basado en componentes te permite expandir gradualmente la complejidad a medida que asciendes en la jerarquía de componentes. Entre los beneficios están un proceso de desarrollo más enfocado y una mayor cobertura de todas las posibles mutaciones de la interfaz de usuario. En resumen, la CDD te ayuda a construir interfaces de usuario de mayor calidad y complejidad.
Aún no hemos terminado, el trabajo no termina cuando se construye la interfaz de usuario. También tenemos que asegurarnos de que siga siendo duradero a lo largo del tiempo.