Skip to content

Commit 5fd6602

Browse files
author
cjyuan
committed
Update instructions and improve code
1 parent bef4d1c commit 5fd6602

File tree

5 files changed

+91
-59
lines changed

5 files changed

+91
-59
lines changed

Sprint-3/todo-list/01-using_esm_with_nodejs_and_jest.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44

55
Node.js supports both CommonJS and ESM, with CommonJS being the default module system.
66

7-
To use ESM, we can add `"type": "module"` to `package.json`, or we can name the JS script files with `.mjs` file extension.
7+
To use ESM, we can add `"type": "module"` to `package.json`, or we can name the JavaScript files
8+
with `.mjs` file extension.
89

910
**Important**:
1011
- Avoid mixing CommonJS and ESM in the same project unless you know what you're doing.
@@ -37,11 +38,13 @@ One way to execute Jest test script that uses ESM is to
3738
npm test -- <test_script_filename>
3839
```
3940

40-
**Note**: The `--` is optional if you do not have arguments to be forwarded to the underlying jest command.
41+
**Note**: The `--` is optional if you do not have arguments to be forwarded to the underlying
42+
`jest` command.
4143

4244

4345
## `jsdom`
4446

45-
[**`jsdom`**](https://github.com/jsdom/jsdom), a pure-JavaScript implementation of DOM for use with Node.js, **does not yet support** `<script type="module">` tags in HTML.
47+
[**`jsdom`**](https://github.com/jsdom/jsdom), a pure-JavaScript implementation of DOM for
48+
use with Node.js, **does not yet support** `<script type="module">` tags in HTML.
4649

4750
Testing an ESM-based application with `jsdom` requires additional configuration and third-party tooling.

Sprint-3/todo-list/02-guide_to_modularize_code.md

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Why modularize code?
22

3-
Modularizing code means breaking a program into smaller, self-contained pieces (modules), each responsible for a specific functionality. This approach offers several key advantages:
3+
Modularizing code means breaking a program into smaller, self-contained pieces (modules),
4+
each responsible for a specific functionality. This approach offers several key advantages:
45

56
- Reusability – You can use the same code in different parts of a project or in other projects.
67
- Maintainability – It's easier to fix bugs or update features when code is organized into smaller parts.
@@ -12,9 +13,19 @@ Modularizing code means breaking a program into smaller, self-contained pieces (
1213

1314
## How to break a program into smaller modules?
1415

15-
This is a relatively big topic and you will learn more about how to modularize a web app in the SDC course.
16+
This is a relatively big topic and you will learn more about how to modularize a web app in the
17+
SDC course.
1618

17-
For starters, we recommend focusing on breaking a web app into the **non-UI part** and the **UI part**.
19+
For starters, we recommend focusing on breaking a web app into the **non-UI part** and
20+
the **UI part**. Some of the advantages of doing so include:
21+
22+
1. Easier Testing
23+
- Non-UI code can be tested independently with unit tests.
24+
- You don't need browser environments or DOM simulations (e.g., `jsdom`) for testing logic.
25+
26+
2. Easier Collaboration
27+
- UI developers and business-logic developers can work more independently.
28+
- Clearer separation of concerns makes the codebase easier to maintain over time.
1829

1930
### The Non-UI Part of a Web App
2031

Sprint-3/todo-list/README.md

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,25 +24,39 @@ To view the website, open `index.html` with Live Server in VS Code.
2424

2525
## Instructions
2626

27-
In this exercise, your objective is to implement additional features.
27+
In this exercise, your objective is to extend the ToDo app by implementing new features.
28+
Start with the main feature and then try the stretch goals if you have extra time.
2829

29-
#### Feature 1: Mass delete of completed ToDos
30+
### Main Feature: Mass delete of completed ToDos
3031

31-
Add a "Delete completed tasks" button that, when clicked, will delete all the completed ToDos. You should
32-
- In `todos.mjs`, implement a function to delete all completed tasks in the given ToDo List
33-
- In `todos.test.mjs`, implement a test to check your function.
34-
- In `script.js`, call the function to delete all completed tasks whenever the user clicks a "Delete All" button.
32+
Add a button that deletes all completed tasks at once.
3533

36-
#### Stretch 1: Set deadlines for ToDos
34+
Steps:
35+
1. In `index.html`, add a "Delete completed tasks" button.
3736

38-
We want users to be able to set, and see, deadlines for their ToDos.
37+
2. In `todos.mjs`, implement a function `deleteCompleted(todoList)` that removes all completed
38+
ToDos from the given list.
3939

40-
When creating a ToDo we want the user to be able to use a datepicker input to specify a deadline for the Todo.
41-
If no date is selected, the ToDo is considered having no deadline.
40+
3. In `todos.test.mjs`, write a Jest test that verifies `deleteCompleted()` works correctly.
41+
42+
4. In `script.js`, call `deleteCompleted()` whenever the new button is clicked.
43+
- ⚠️ You should not need to modify the `render()` function.
44+
45+
### Stretch 1: Add deadlines for ToDos
46+
47+
Allow users to set and view deadlines for their tasks.
48+
- When creating a ToDo, let the user select a deadline using an HTML **datepicker** input.
49+
- If no date is selected, the ToDo has **no deadline**.
50+
- When rendering a ToDo in the list, display the deadline only if it exists.
51+
52+
### Stretch 2: Extra Challenge – Show time remaining
53+
54+
Instead of showing the deadline as a date, display how many days are left until the
55+
deadline (relative to today).
56+
- Decide how overdue ToDos should be handled and then implement your chosen solution.
57+
58+
👉 Hint: You can use the [JavaScript Date API](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date)
59+
to calculate the difference.
4260

43-
When displaying a ToDo in the list, display the deadline only if the ToDo has one.
4461

45-
#### Stretch 2: EXTRA CHALLENGE
4662

47-
Instead of displaying the date on the ToDo, implement a countdown of days left until the deadline. You can use the Javascript Date reference to accomplish this:
48-
https://www.w3schools.com/jsref/jsref_obj_date.asp

Sprint-3/todo-list/script.mjs

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,10 @@
1-
// Note: Todos will be an object containing all the named exports from
2-
// the "./todos.mjs" module.
1+
// Store everything imported from './todos.mjs' module as properties of an object named Todos
32
import * as Todos from "./todos.mjs";
43

54
// To store the todo tasks
65
let todos = [];
76

8-
// First child of #todo-item-template is a <li> element.
9-
// We will create each ToDo list item as a clone of this node.
10-
const todoListItemTemplate =
11-
document.getElementById("todo-item-template").content.firstElementChild;
12-
13-
const todoListEl = document.getElementById("todo-list");
14-
15-
7+
// Set up tasks to be performed once on page load
168
window.addEventListener("load", () => {
179
document.getElementById("add-task-btn").addEventListener("click", addNewTodo);
1810

@@ -37,6 +29,12 @@ function addNewTodo() {
3729
taskInput.value = "";
3830
}
3931

32+
// Note:
33+
// - Store the reference to the <ul> element with id "todo-list" here
34+
// to avoid querying the DOM repeatedly inside render().
35+
// - This variable is declared here to be close to the only function that uses it.
36+
const todoListEl = document.getElementById("todo-list");
37+
4038
// Render the whole todo list
4139
function render() {
4240
todoListEl.innerHTML = "";
@@ -47,6 +45,14 @@ function render() {
4745
});
4846
}
4947

48+
49+
// Note:
50+
// - First child of #todo-item-template is a <li> element.
51+
// We will create each ToDo list item as a clone of this node.
52+
// - This variable is declared here to be close to the only function that uses it.
53+
const todoListItemTemplate =
54+
document.getElementById("todo-item-template").content.firstElementChild;
55+
5056
// Create a <li> element for the given todo task
5157
function createListItem(todo, index) {
5258
const li = todoListItemTemplate.cloneNode(true); // true => Do a deep copy of the node

Sprint-3/todo-list/todos.test.mjs

Lines changed: 27 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
1-
// The tests is designed to demonstrates we can test the functions
1+
// The tests is prepared to demonstrate we can test the functions
22
// in a module independently.
33

44
// Command to execute this script:
55
// npm test todos.test.mjs
66

7-
87
// Import all the exported members through an object
98
import * as Todos from "./todos.mjs";
109

11-
// Return a mock ToDo List data
12-
// Tests in this file expect exactly 4 elements in the mocked ToDo List
10+
// Return a mock ToDo List data with exactly 4 elements.
1311
function createMockTodos() {
1412
return [
1513
{ task: "Task 1 description", completed: true },
@@ -19,26 +17,26 @@ function createMockTodos() {
1917
];
2018
}
2119

20+
// A mock task to simulate user input
2221
const theTask = { task: "The Task", completed: false };
2322

24-
2523
describe("addTask()", () => {
26-
test("Add a task to an empty array", () => {
24+
test("Add a task to an empty ToDo list", () => {
2725
let todos = [];
2826
Todos.addTask(todos, theTask.task, theTask.completed);
2927
expect(todos).toHaveLength(1);
3028
expect(todos[0]).toEqual(theTask);
3129
});
3230

33-
test("Should append a task to end of an array", () => {
31+
test("Should append a new task to the end of a ToDo list", () => {
3432

3533
const todos = createMockTodos();
36-
const lengthBeforeAddingTask = todos.length;
34+
const lengthBeforeAddition = todos.length;
3735
Todos.addTask(todos, theTask.task, theTask.completed);
38-
// Array should have one more task
39-
expect(todos).toHaveLength(lengthBeforeAddingTask + 1);
36+
// todos should now have one more task
37+
expect(todos).toHaveLength(lengthBeforeAddition + 1);
4038

41-
// New task should be appended to the array
39+
// New task should be appended to the todos
4240
expect(todos[todos.length - 1]).toEqual(theTask);
4341
});
4442
});
@@ -47,24 +45,24 @@ describe("deleteTask()", () => {
4745

4846
test("Delete the first task", () => {
4947
const todos = createMockTodos();
50-
const todosBeforeDeletion = createMockTodos();
51-
const lengthBeforeAddingTask = todos.length;
48+
const todosBeforeDeletion = [...todos];
49+
const lengthBeforeDeletion = todos.length;
5250
Todos.deleteTask(todos, 0);
5351

54-
expect(todos).toHaveLength(lengthBeforeAddingTask-1);
52+
expect(todos).toHaveLength(lengthBeforeDeletion - 1);
5553

5654
expect(todos[0]).toEqual(todosBeforeDeletion[1]);
5755
expect(todos[1]).toEqual(todosBeforeDeletion[2]);
5856
expect(todos[2]).toEqual(todosBeforeDeletion[3]);
5957
});
6058

61-
test("Delete a middle task", () => {
59+
test("Delete the second task (a middle task)", () => {
6260
const todos = createMockTodos();
63-
const todosBeforeDeletion = createMockTodos();
64-
const lengthBeforeAddingTask = todos.length;
61+
const todosBeforeDeletion = [...todos];
62+
const lengthBeforeDeletion = todos.length;
6563
Todos.deleteTask(todos, 1);
6664

67-
expect(todos).toHaveLength(lengthBeforeAddingTask-1);
65+
expect(todos).toHaveLength(lengthBeforeDeletion - 1);
6866

6967
expect(todos[0]).toEqual(todosBeforeDeletion[0]);
7068
expect(todos[1]).toEqual(todosBeforeDeletion[2]);
@@ -73,11 +71,11 @@ describe("deleteTask()", () => {
7371

7472
test("Delete the last task", () => {
7573
const todos = createMockTodos();
76-
const todosBeforeDeletion = createMockTodos();
77-
const lengthBeforeAddingTask = todos.length;
78-
Todos.deleteTask(todos, todos.length-1);
74+
const todosBeforeDeletion = [...todos];
75+
const lengthBeforeDeletion = todos.length;
76+
Todos.deleteTask(todos, todos.length - 1);
7977

80-
expect(todos).toHaveLength(lengthBeforeAddingTask-1);
78+
expect(todos).toHaveLength(lengthBeforeDeletion - 1);
8179

8280
expect(todos[0]).toEqual(todosBeforeDeletion[0]);
8381
expect(todos[1]).toEqual(todosBeforeDeletion[1]);
@@ -86,7 +84,7 @@ describe("deleteTask()", () => {
8684

8785
test("Delete a non-existing task", () => {
8886
const todos = createMockTodos();
89-
const todosBeforeDeletion = createMockTodos();
87+
const todosBeforeDeletion = [...todos];
9088
Todos.deleteTask(todos, 10);
9189
expect(todos).toEqual(todosBeforeDeletion);
9290

@@ -100,18 +98,18 @@ describe("toggleCompletedOnTask()", () => {
10098
test("Expect the 'completed' property to toggle on an existing task", () => {
10199
const todos = createMockTodos();
102100
const taskIdx = 1;
103-
const completedBeforeToggle = todos[taskIdx].completed;
101+
const completedStateBeforeToggle = todos[taskIdx].completed;
104102
Todos.toggleCompletedOnTask(todos, taskIdx);
105-
expect(todos[taskIdx].completed).toEqual(!completedBeforeToggle);
103+
expect(todos[taskIdx].completed).toEqual(!completedStateBeforeToggle);
106104

107105
// Toggle again
108106
Todos.toggleCompletedOnTask(todos, taskIdx);
109-
expect(todos[taskIdx].completed).toEqual(completedBeforeToggle);
107+
expect(todos[taskIdx].completed).toEqual(completedStateBeforeToggle);
110108
});
111109

112-
test("Expect toggling on an existing task does not affect other tasks", () => {
110+
test("Expect toggling on a task does not affect other tasks", () => {
113111
const todos = createMockTodos();
114-
const todosBeforeToggle = createMockTodos();
112+
const todosBeforeToggle = [...todos];
115113
Todos.toggleCompletedOnTask(todos, 1);
116114

117115
expect(todos[0]).toEqual(todosBeforeToggle[0]);
@@ -122,7 +120,7 @@ describe("toggleCompletedOnTask()", () => {
122120

123121
test("Expect no change when toggling on a non-existing task", () => {
124122
const todos = createMockTodos();
125-
const todosBeforeToggle = createMockTodos();
123+
const todosBeforeToggle = [...todos];
126124

127125
Todos.toggleCompletedOnTask(todos, 10);
128126
expect(todos).toEqual(todosBeforeToggle);

0 commit comments

Comments
 (0)