Implementing Task Movement in a Kanban Board
Introduction to Task Movement in Kanban Boards
Welcome to the fourth lesson of our "Building A Kanban Board" course! In our previous lessons, we've set up our task store using Svelte's Runes API, created the visual components to display our tasks, and implemented a form to add new tasks. Now, we're ready to make our Kanban board truly functional by implementing task movement between columns.
Task movement is a core feature of any Kanban board. In real-world project management, tasks naturally progress through different stages of completion. A task might start in the "To Do" column, move to "In Progress" when someone begins working on it, and finally reach the "Done" column when completed. Without the ability to move tasks between these columns, our Kanban board would be little more than a static display of information.
Lesson Objectives
In this lesson, we'll implement this crucial functionality by:
- Adding a function to our task store that updates a task's status
- Creating a component that displays action buttons for moving tasks
- Enhancing our
TaskCardcomponent to handle status changes - Connecting everything together to create a seamless user experience
By the end of this lesson, you'll be able to click on a task to expand it, see available actions based on its current status, and move it to a different column with a single click. This interaction pattern is intuitive and mirrors how real Kanban boards work, making our application not just visually appealing but also practically useful.
Let's get started by examining how we'll update our task store to support status changes!
Enhancing the Task Store with Status Updates
In our first lesson, we created a task store with an initial state and derived values for filtering tasks by status. In the third lesson, we added the ability to create new tasks. Now, we need to add functionality to update a task's status.
Let's examine the enhanced version of our taskStore.svelte.js file:
Understanding the updateTaskStatus Function
The key addition here is the updateTaskStatus function, which takes two parameters:
id: The unique identifier of the task to updatenewStatus: The new status to assign to the task (e.g., 'todo', 'inprogress', or 'done')
Let's break down how this function works:
-
First, it uses the
findIndexmethod to locate the task with the specified ID in ourtasksarray. This method returns the index of the first element that satisfies the provided testing function, or -1 if no element is found. -
Then, it checks if a task with the given ID was found (i.e., if
taskIndexis not -1). -
If a matching task was found, it updates the status of that task by directly modifying the
statusproperty of the task object at the found index.
Reactivity Benefits for Task Movement
This direct modification of the task's status property works because our tasks array is reactive (created with $state). When we modify a property of an object within this array, Svelte's reactivity system detects the change and automatically updates any parts of the UI that depend on this data.
This is particularly powerful because our derived values (todoTasks, inProgressTasks, and doneTasks) are calculated based on the status of each task. When a task's status changes, these derived values automatically update, which in turn updates the UI to show the task in its new column.
For example, if we move a task from "To Do" to "In Progress", the following happens automatically:
- The task is removed from the
todoTasksarray - The task is added to the
inProgressTasksarray - The UI updates to show the task in the "In Progress" column
All of this happens without us having to manually update multiple arrays or trigger UI refreshes. This is the power of Svelte's reactivity system — we declare our dependencies and let Svelte handle the updates.
Now that we have the functionality to update a task's status, let's create a component that provides a user interface for this functionality.
Building the TaskActions Component
To provide a user-friendly way to move tasks between columns, we'll create a new component called TaskActions.svelte. This component will display buttons for moving a task to different statuses, based on its current status.
Here's the code for our TaskActions.svelte component:
Component Structure and Props
Let's examine this component in detail:
In the script section, we're using the $props() rune to define the props that this component accepts:
status: The current status of the taskonStatusChange: A callback function that will be called when the user clicks a button to change the task's status
The $props() rune is a new feature in Svelte 5 that provides a cleaner way to define component props. It returns an object with the props passed to the component, which we destructure to get the specific props we need.
Conditional Rendering of Action Buttons
In the markup section, we're creating a div that contains buttons for moving the task to different statuses. We're using conditional rendering with {#if} blocks to only show buttons for statuses that are different from the task's current status. This prevents users from moving a task to the status it's already in, which would be redundant.
Each button has an onclick handler that calls the onStatusChange callback with the new status as an argument. This callback will be provided by the parent component (which we'll create next) and will handle the actual status update.
Styling the Action Buttons
In the style section, we're using flexbox to arrange the buttons in a row, with wrapping enabled in case there are too many buttons to fit on one line. We're also styling the buttons to be small and subtle, with a hover effect that changes the background color to provide visual feedback.
This component follows the principle of separation of concerns:
- It's responsible only for displaying the UI for changing a task's status
- It doesn't know how to actually update the status; it just calls a callback provided by the parent
- It doesn't know about the task store or any other parts of the application
This makes the component reusable and maintainable. If we wanted to change how task statuses are updated, we would only need to modify the parent component, not this one.
Now that we have our TaskActions component, let's see how to integrate it into our TaskCard component to provide a complete user interface for task management.
Implementing Expandable TaskCards
Now that we have our TaskActions component, we need to enhance our TaskCard component to display these actions when a user interacts with a task. We'll also add the ability to expand and collapse a task card to show or hide additional details and actions.
Here's the updated code for our TaskCard.svelte component:
Enhanced TaskCard Features
Let's break down the changes we've made to this component:
In the script section, we've added several new features:
- We're importing the
TaskActionscomponent and theupdateTaskStatusfunction from our task store - We've expanded our props to include
idandstatus, which we'll need for task movement - We've added a new reactive variable
isExpandedusing the$staterune, initialized tofalseto track whether the task card is expanded or collapsed - We've added two functions:
toggleExpand: Toggles the value ofisExpandedwhen calledhandleStatusChange: Calls theupdateTaskStatusfunction from our task store with the task's ID and the new status
Interactive UI Elements
In the markup section, we've made several changes:
- We've added a
tabindex="0"attribute to the task carddiv, which makes it focusable with keyboard navigation (improving accessibility) - We've added an
onclickhandler that calls thetoggleExpandfunction when the task card is clicked - We've wrapped the description in a conditional block that only renders it if the description is not empty and the card is expanded
- We've added a conditional block that renders the
TaskActionscomponent if the card is expanded, passing the task's current status and ourhandleStatusChangefunction as props
Visual Feedback through Styling
In the style section, we've added styles for the expanded state and improved the hover and focus styles to provide better visual feedback. The subtle animations help users understand that the cards are interactive and provide a more polished user experience.
This implementation creates an intuitive user interface:
- By default, task cards show only the title, keeping the UI clean and compact
- When a user clicks on a card, it expands to show the description (if any) and action buttons
- The user can then click an action button to move the task to a different status
- The user can collapse the card by clicking it again
This pattern of progressive disclosure (showing more details and actions only when needed) is common in modern UIs and helps prevent information overload.
How Status Changes Flow Through the Application
Now that we have our TaskActions component and our enhanced TaskCard component, let's examine how status changes are handled throughout our application.
The Chain of Events for Task Movement
When a user clicks on a task card and then clicks one of the action buttons, a chain of events occurs:
-
The click on the action button triggers the
onclickhandler in theTaskActionscomponent, which calls theonStatusChangecallback with the new status as an argument. -
This callback is actually the
handleStatusChangefunction from theTaskCardcomponent, which receives the new status and calls theupdateTaskStatusfunction from our task store with the task's ID and the new status. -
The
updateTaskStatusfunction finds the task with the specified ID in ourtasksarray and updates its status. -
Because our
tasksarray is reactive (created with$state), this update triggers a re-evaluation of our derived values (todoTasks,inProgressTasks, anddoneTasks). -
The UI automatically updates to show the task in its new column.
This chain of events demonstrates the power of Svelte's reactivity system and the component-based architecture we've built. Each component has a specific responsibility, and they work together to create a seamless user experience.
A Concrete Example
Let's look at a concrete example to illustrate this process:
Imagine we have a task with ID 2, title "Design components", and status "todo". It's currently displayed in the "To Do" column of our Kanban board.
- The user clicks on the task card, which expands to show the action buttons
- The user clicks the "Move to In Progress" button
- The
onclickhandler in theTaskActionscomponent callsonStatusChange('inprogress') - The
handleStatusChangefunction in theTaskCardcomponent callsupdateTaskStatus(2, 'inprogress') - The
updateTaskStatusfunction finds the task with ID 2 and changes its status to "inprogress" - The
todoTasksderived value is re-evaluated and no longer includes this task - The
inProgressTasksderived value is re-evaluated and now includes this task - The UI updates to show the task in the "In Progress" column
All of this happens automatically, without us having to manually update arrays or trigger UI refreshes. This is the beauty of reactive programming with Svelte — we declare our dependencies and let the framework handle the updates.
Now, let's see how our Board component ties everything together to create a complete Kanban board.
Connecting Components with the Board
Our Board component is responsible for rendering the three columns of our Kanban board and populating them with task cards. Let's examine the updated code for our Board.svelte component:
Board Structure and Component Composition
In this component, we're importing the Column and TaskCard components, as well as the functions to get tasks by status from our task store. We're rendering three Column components, each with a title corresponding to a task status. Inside each column, we're using the {#each} block to iterate over the tasks for that status and render a TaskCard for each one.
We pass all necessary props to each TaskCard, including:
id: Used to identify the task when updating its statustitle: The task's title to displaydescription: The task's description (if any)status: The current status of the task, used by the action buttons
This setup creates a dynamic Kanban board where tasks can be moved between columns by changing their status. The reactivity of Svelte ensures that the UI updates automatically whenever a task's status changes, providing a seamless user experience.
Summary
In this lesson, we've implemented the core functionality that makes a Kanban board truly useful: the ability to move tasks between columns. Here's what we've accomplished:
- Enhanced our task store with the
updateTaskStatus()function that modifies a task's status based on its ID - Created a TaskActions component that displays context-appropriate buttons for moving tasks between columns
- Upgraded our TaskCard component with expandable functionality that reveals task descriptions and action buttons when clicked
- Connected everything together through Svelte's reactive system, ensuring the UI automatically updates when task statuses change
This implementation demonstrates the power of reactive programming with Svelte. By simply updating a task's status property, our derived column values automatically recalculate, and the task visually moves to its new column without any manual DOM manipulation.
The component-based architecture we've built maintains a clear separation of concerns where each component has a specific responsibility, creating a clean and maintainable codebase.
