Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .prettierrc
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"requirePragma": false,
"semi": true,
"singleQuote": false,
"tabWidth": 2,
"tabWidth": 4,
"trailingComma": "es5",
"useTabs": false,
"vueIndentScriptAndStyle": false
Expand Down
68 changes: 33 additions & 35 deletions Sprint-3/todo-list/README.md
Original file line number Diff line number Diff line change
@@ -1,28 +1,33 @@
# ToDo List App

The files in this folder implements a ToDo List App that allows a user to
- Create a new ToDo task
- Delete a ToDo task
- Display all ToDo tasks
- Changing the "completed" status of a ToDo task

- Create a new ToDo task
- Delete a ToDo task
- Display all ToDo tasks
- Changing the "completed" status of a ToDo task

Each ToDo task has two properties:
- `task`: A string describing the task
- `completed`: a Boolean value that indicates whether the task is completed or not

- `task`: A string describing the task
- `completed`: a Boolean value that indicates whether the task is completed or not

## Installation

Run the following command in this directory to install all required dependencies:
Run the following command in this directory to install all required dependencies:

```
npm install
```

> This will install
- `jest` - for running unix test
- `http-server` - for serving `index.html` over HTTP


- `jest` - for running unix test
- `http-server` - for serving `index.html` over HTTP

**Note:** If you are using a Windows CLI, replace `package.json` by `package.json-windows`.

## Running the App
## Running the App

Since the app uses **ES modules**, the HTML file **must be loaded via HTTP/HTTPS** rather than
directly from the file system.
Expand All @@ -33,40 +38,40 @@ Two possible ways to serve `index.html` over HTTP:

#### Option 1: `http-server`

1. Run
```
npm run serve
```
> Here, `serve` is a shortcut defined in `package.json` for running `http-server`.


2. Open one of the URLs shown in the terminal (e.g., `http://127.0.0.1:8080`).
1. Run

```
npm run serve
```

#### Option 2: Open `index.html` with Live Server in VSCode.
> Here, `serve` is a shortcut defined in `package.json` for running `http-server`.

2. Open one of the URLs shown in the terminal (e.g., `http://127.0.0.1:8080`).

#### Option 2: Open `index.html` with Live Server in VSCode.

## Understanding how the code is organized as ES modules

- [What is ES Modules?](00-what_is_ES_modules.md)
- [How to use ES modules with Node.js and Jest?](01-using_esm_with_nodejs_and_jest.md)
- [A guide to modularize a web app](02-guide_to_modularize_code.md)
- [What is ES Modules?](00-what_is_ES_modules.md)
- [How to use ES modules with Node.js and Jest?](01-using_esm_with_nodejs_and_jest.md)
- [A guide to modularize a web app](02-guide_to_modularize_code.md)

---

## Exercise Instructions

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

### Main Feature: Mass delete of completed ToDos

Add a button that deletes all completed tasks at once.

Steps:

1. In `index.html`, add a "Delete completed tasks" button.

2. In `todos.mjs`, implement a function `deleteCompleted(todoList)` that removes all completed
2. In `todos.mjs`, implement a function `deleteCompleted(todoList)` that removes all completed
ToDos from the given list.

3. In `todos.test.mjs`, write a Jest test that verifies `deleteCompleted()` works correctly.
Expand All @@ -76,19 +81,12 @@ Steps:

### Stretch 1: Add deadlines for ToDos

Allow users to set and view deadlines for their tasks.
- When creating a ToDo, let the user select a deadline using an HTML **datepicker** input.
- If no date is selected, the ToDo has **no deadline**.
- When rendering a ToDo in the list, display the deadline only if it exists.
Allow users to set and view deadlines for their tasks. - When creating a ToDo, let the user select a deadline using an HTML **datepicker** input. - If no date is selected, the ToDo has **no deadline**. - When rendering a ToDo in the list, display the deadline only if it exists.

### Stretch 2: Extra Challenge – Show time remaining

Instead of showing the deadline as a date, display how many days are left until the
deadline (relative to today).
- Decide how overdue ToDos should be handled and then implement your chosen solution.
Instead of showing the deadline as a date, display how many days are left until the
deadline (relative to today). - Decide how overdue ToDos should be handled and then implement your chosen solution.

👉 Hint: You can use the [JavaScript Date API](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date)
to calculate the difference.



78 changes: 49 additions & 29 deletions Sprint-3/todo-list/index.html
Original file line number Diff line number Diff line change
@@ -1,40 +1,60 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>ToDo List</title>
<link rel="stylesheet" href="style.css" />
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>ToDo List</title>
<link rel="stylesheet" href="style.css" />
<link
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"
rel="stylesheet"
/>

<script type="module" src="script.mjs"></script>
</head>
<body>
<div class="todo-container">
<h1>My ToDo List</h1>
<script type="module" src="script.mjs"></script>
</head>
<body>
<div class="todo-container">
<h1>My ToDo List</h1>

<div class="todo-input">
<input type="text" id="new-task-input" placeholder="Enter a new task..." />
<button id="add-task-btn">Add</button>
</div>
<div class="todo-input">
<input
type="text"
id="new-task-input"
placeholder="Enter a new task..."
/>
<button id="add-task-btn">Add</button>
</div>

<ul id="todo-list" class="todo-list">
</ul>
<ul id="todo-list" class="todo-list"></ul>

<!--
<div class="delete-completed">
<button id="delete-completed">Delete completed tasks</button>
</div>

<!--
This is a template for the To-do list item.
It can simplify the creation of list item node in JS script.
-->
<template id="todo-item-template">
<li class="todo-item"> <!-- include class "completed" if the task completed state is true -->
<span class="description">Task description</span>
<div class="actions">
<button class="complete-btn"><span class="fa-solid fa-check" aria-hidden="true"></span></button>
<button class="delete-btn"><span class="fa-solid fa-trash" aria-hidden="true"></span></button>
<template id="todo-item-template">
<li class="todo-item">
<!-- include class "completed" if the task completed state is true -->
<span class="description">Task description</span>
<div class="actions">
<button class="complete-btn">
<span
class="fa-solid fa-check"
aria-hidden="true"
></span>
</button>
<button class="delete-btn">
<span
class="fa-solid fa-trash"
aria-hidden="true"
></span>
</button>
</div>
</li>
</template>
</div>
</li>
</template>

</div>
</body>
</body>
</html>
6 changes: 3 additions & 3 deletions Sprint-3/todo-list/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"description": "You must update this package",
"type": "module",
"scripts": {
"serve": "http-server",
"test": "NODE_OPTIONS=--experimental-vm-modules jest"
"serve": "http-server",
"test": "set NODE_OPTIONS=--experimental-vm-modules && jest"
},
"repository": {
"type": "git",
Expand All @@ -17,7 +17,7 @@
},
"homepage": "https://github.com/CodeYourFuture/CYF-Coursework-Template#readme",
"devDependencies": {
"http-server": "^14.1.1",
"http-server": "^14.1.1",
"jest": "^30.0.4"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"description": "You must update this package",
"type": "module",
"scripts": {
"serve": "http-server",
"test": "set NODE_OPTIONS=--experimental-vm-modules && jest"
"serve": "http-server",
"test": "NODE_OPTIONS=--experimental-vm-modules jest"
},
"repository": {
"type": "git",
Expand All @@ -17,7 +17,7 @@
},
"homepage": "https://github.com/CodeYourFuture/CYF-Coursework-Template#readme",
"devDependencies": {
"http-server": "^14.1.1",
"http-server": "^14.1.1",
"jest": "^30.0.4"
}
}
86 changes: 46 additions & 40 deletions Sprint-3/todo-list/script.mjs
Original file line number Diff line number Diff line change
@@ -1,32 +1,33 @@
// Store everything imported from './todos.mjs' module as properties of an object named Todos
// Store everything imported from './todos.mjs' module as properties of an object named Todos
import * as Todos from "./todos.mjs";

// To store the todo tasks
const todos = [];

// Set up tasks to be performed once on page load
window.addEventListener("load", () => {
document.getElementById("add-task-btn").addEventListener("click", addNewTodo);
document
.getElementById("add-task-btn")
.addEventListener("click", addNewTodo);

// Populate sample data
Todos.addTask(todos, "Wash the dishes", false);
Todos.addTask(todos, "Do the shopping", true);
// Populate sample data
Todos.addTask(todos, "Wash the dishes", false);
Todos.addTask(todos, "Do the shopping", true);

render();
render();
});


// A callback that reads the task description from an input field and
// A callback that reads the task description from an input field and
// append a new task to the todo list.
function addNewTodo() {
const taskInput = document.getElementById("new-task-input");
const task = taskInput.value.trim();
if (task) {
Todos.addTask(todos, task, false);
render();
}

taskInput.value = "";
const taskInput = document.getElementById("new-task-input");
const task = taskInput.value.trim();
if (task) {
Todos.addTask(todos, task, false);
render();
}

taskInput.value = "";
}

// Note:
Expand All @@ -37,40 +38,45 @@ const todoListEl = document.getElementById("todo-list");

// Render the whole todo list
function render() {
todoListEl.innerHTML = "";
todoListEl.innerHTML = "";

todos.forEach((todo, index) => {
const todoListItem = createListItem(todo, index);
todoListEl.append(todoListItem);
});
todos.forEach((todo, index) => {
const todoListItem = createListItem(todo, index);
todoListEl.append(todoListItem);
});
}


// Note:
// - First child of #todo-item-template is a <li> element.
// We will create each ToDo list item as a clone of this node.
// - This variable is declared here to be close to the only function that uses it.
const todoListItemTemplate =
document.getElementById("todo-item-template").content.firstElementChild;
const todoListItemTemplate =
document.getElementById("todo-item-template").content.firstElementChild;

// Create a <li> element for the given todo task
function createListItem(todo, index) {
const li = todoListItemTemplate.cloneNode(true); // true => Do a deep copy of the node
const li = todoListItemTemplate.cloneNode(true); // true => Do a deep copy of the node

li.querySelector(".description").textContent = todo.task;
if (todo.completed) {
li.classList.add("completed");
}
li.querySelector(".description").textContent = todo.task;
if (todo.completed) {
li.classList.add("completed");
}

li.querySelector('.complete-btn').addEventListener("click", () => {
Todos.toggleCompletedOnTask(todos, index);
render();
});

li.querySelector('.delete-btn').addEventListener("click", () => {
Todos.deleteTask(todos, index);
render();
});
li.querySelector(".complete-btn").addEventListener("click", () => {
Todos.toggleCompletedOnTask(todos, index);
render();
});

li.querySelector(".delete-btn").addEventListener("click", () => {
Todos.deleteTask(todos, index);
render();
});

return li;
}

return li;
}
// Connected the delete-completed button
document.getElementById("delete-completed").addEventListener("click", () => {
Todos.deleteCompleted(todos);
render();
});
Loading