Skip to content

Commit

Permalink
Chapter 2. Rendering (#4)
Browse files Browse the repository at this point in the history
* feat: Chapter2. Rendering

* chore(02. 렌더링/02): 닉네임 폴더 아래로 파일 이동
  • Loading branch information
Hanna922 authored Jan 19, 2024
1 parent d3594e6 commit 6b7fffe
Show file tree
Hide file tree
Showing 10 changed files with 284 additions and 0 deletions.
19 changes: 19 additions & 0 deletions 02. 렌더링/02/Hanna/getTodos.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
const { faker } = window;

const createElement = () => ({
text: faker.random.words(2),
completed: faker.random.boolean(),
});

const repeat = (elementFactory, number) => {
const array = [];
for (let index = 0; index < number; index++) {
array.push(elementFactory());
}
return array;
};

export default () => {
const howMany = faker.random.number(10);
return repeat(createElement, howMany);
};
44 changes: 44 additions & 0 deletions 02. 렌더링/02/Hanna/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<html>
<head>
<link rel="shortcut icon" href="../favicon.ico" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/todomvc-common@1.0.5/base.css" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/todomvc-app-css@2.1.2/index.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/Faker/3.1.0/faker.js"></script>
<title>Frameworkless Frontend Development: Rendering</title>
</head>

<body>
<section class="todoapp">
<header class="header">
<h1>todos</h1>
<input class="new-todo" placeholder="What needs to be done?" autofocus />
</header>
<section class="main">
<input id="toggle-all" class="toggle-all" type="checkbox" />
<label for="toggle-all">Mark all as complete</label>
<ul class="todo-list"></ul>
</section>
<footer class="footer">
<span class="todo-count">1 Item Left</span>
<ul class="filters">
<li>
<a href="#/">All</a>
</li>
<li>
<a href="#/active">Active</a>
</li>
<li>
<a href="#/completed">Completed</a>
</li>
</ul>
<button class="clear-completed">Clear completed</button>
</footer>
</section>
<footer class="info">
<p>Double-click to edit a todo</p>
<p>Created by <a href="http://twitter.com/thestrazz86">Francesco Strazzullo</a></p>
<p>Thanks to <a href="http://todomvc.com">TodoMVC</a></p>
</footer>
<script type="module" src="index.js"></script>
</body>
</html>
14 changes: 14 additions & 0 deletions 02. 렌더링/02/Hanna/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import getTodos from './getTodos';
import appView from './view/app';

const state = {
todos: getTodos(),
currentFilter: 'All',
};

const main = document.querySelector('.todoapp');

window.requestAnimationFrame(() => {
const newMain = appView(main, state);
main.replaceWith(newMain);
});
17 changes: 17 additions & 0 deletions 02. 렌더링/02/Hanna/view/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import todosView from './todos';
import counterView from './counter';
import filtersView from './filters';

export default (targetElement, state) => {
const element = targetElement.cloneNode(true);

const list = element.querySelector('.todo-list');
const counter = element.querySelector('.todo-count');
const filters = element.querySelector('.filters');

list.replaceWith(todosView(list, state));
counter.replaceWith(counterView(counter, state));
filters.replaceWith(filtersView(filters, state));

return element;
};
18 changes: 18 additions & 0 deletions 02. 렌더링/02/Hanna/view/counter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
const getNotCompleted = (todos) => {
const notCompleted = todos.filter((todo) => !todo.completed);
const { length } = notCompleted;

if (length === 1) {
return '1 Item left';
}

return `${length} Items left`;
};

const counterView = (targetElement, { todos }) => {
const element = targetElement.cloneNode(true);
element.textContent = getNotCompleted(todos);
return element;
};

export default counterView;
47 changes: 47 additions & 0 deletions 02. 렌더링/02/Hanna/view/counter.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import counterView from './counter';

let targetElement;

describe('counterView', () => {
beforeEach(() => {
targetElement = document.createElement('div');
});

test('새로운 DOM 요소는 완료되지 않은 todo의 수를 가지고 있어야 한다.', () => {
const newCounter = counterView(targetElement, {
todos: [
{
text: 'First',
completed: true,
},
{
text: 'Second',
completed: false,
},
{
text: 'Third',
completed: false,
},
],
});

expect(newCounter.textContent).toBe('2 Items left');
});

test('완료하지 않은 todo가 1개일 경우를 고려해야 한다.', () => {
const newCounter = counterView(targetElement, {
todos: [
{
text: 'First',
completed: true,
},
{
text: 'Third',
completed: false,
},
],
});

expect(newCounter.textContent).toBe('1 Item left');
});
});
14 changes: 14 additions & 0 deletions 02. 렌더링/02/Hanna/view/filters.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
const filtersView = (targetElement, { currentFilter }) => {
const element = targetElement.cloneNode(true);
Array.from(element.querySelectorAll('li a')).forEach((v) => {
if (v.textContent === currentFilter) {
v.classList.add('selected');
} else {
v.classList.remove('selected');
}
});

return element;
};

export default filtersView;
32 changes: 32 additions & 0 deletions 02. 렌더링/02/Hanna/view/filters.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import filtersView from './filters';

let targetElement;
const TEMPLATE = `<ul class="filters">
<li>
<a href="#/">All</a>
</li>
<li>
<a href="#/active">Active</a>
</li>
<li>
<a href="#/completed">Completed</a>
</li>
</ul>`;

describe('filtersView', () => {
beforeEach(() => {
const tempElement = document.createElement('div');
tempElement.innerHTML = TEMPLATE;
[targetElement] = tempElement.childNodes;
});

test('"currentFilter"와 동일한 텍스트를 가지는 anchor 태그에 "selected" 클래스를 추가해야 한다.', () => {
const newCounter = filtersView(targetElement, {
currentFilter: 'Active',
});

const selectedItem = newCounter.querySelector('li a.selected');

expect(selectedItem.textContent).toBe('Active');
});
});
21 changes: 21 additions & 0 deletions 02. 렌더링/02/Hanna/view/todos.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const todosView = (targetElement, { todos }) => {
const element = targetElement.cloneNode(true);
element.innerHTML = todos
.map((todo) => {
const { text, completed } = todo;
return `
<li class="${completed ? 'completed' : ''}">
<div class="view">
<input class="toggle" type="checkbox" ${completed ? 'checked' : ''}>
<label>${text}</label>
</div>
<input class="edit" value="${text}">
</li>
`;
})
.join('');

return element;
};

export default todosView;
58 changes: 58 additions & 0 deletions 02. 렌더링/02/Hanna/view/todos.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import todosView from './todos';

let targetElement;

describe('filtersView', () => {
beforeEach(() => {
targetElement = document.createElement('ul');
});

test('모든 todo 요소에 대해서 li 태그를 생성해야 한다.', () => {
const newCounter = todosView(targetElement, {
todos: [
{
text: 'First',
completed: true,
},
{
text: 'Second',
completed: false,
},
{
text: 'Third',
completed: false,
},
],
});

const items = newCounter.querySelectorAll('li');
expect(items.length).toBe(3);
});

test('"todos"에 따라 모든 li 요소에 올바른 속성을 설정해야 한다.', () => {
const newCounter = todosView(targetElement, {
todos: [
{
text: 'First',
completed: true,
},
{
text: 'Second',
completed: false,
},
],
});

const [firstItem, secondItem] = newCounter.querySelectorAll('li');

expect(firstItem.classList.contains('completed')).toBe(true);
expect(firstItem.querySelector('.toggle').checked).toBe(true);
expect(firstItem.querySelector('label').textContent).toBe('First');
expect(firstItem.querySelector('.edit').value).toBe('First');

expect(secondItem.classList.contains('completed')).toBe(false);
expect(secondItem.querySelector('.toggle').checked).toBe(false);
expect(secondItem.querySelector('label').textContent).toBe('Second');
expect(secondItem.querySelector('.edit').value).toBe('Second');
});
});

0 comments on commit 6b7fffe

Please sign in to comment.