Building a Project List Component with Filtering and Modal Details in React

In this tutorial, we will build a simple project list component using React and Fluent UI. This component will allow users to:

  1. View a list of projects.
  2. Filter the projects by name.
  3. Click on a project to view its details in a modal dialog.

We’ll use Fluent UI components, including DetailsList for the project list, TextField for filtering, and Modal for displaying project details.

Prerequisites

To follow along, make sure you have:

  • React installed.
  • Fluent UI installed: You can install Fluent UI using npm install @fluentui/react.

Step 1: Setting Up the Project Component

To start, we’ll define a basic component structure. Inside this component, we’ll define the project data structure and initial project data. We’ll also set up state management for modal visibility, project selection, and filtering.

import * as React from 'react';
import { useBoolean } from '@fluentui/react-hooks';
import { Modal, DetailsList, IColumn, TextField } from '@fluentui/react';

// Define the project type
interface Project {
  id: number;
  name: string;
  description: string;
}

// Sample project data
const initialProjects: Project[] = [
  { id: 1, name: "Project A", description: "Description for Project A" },
  { id: 2, name: "Project B", description: "Description for Project B" },
  { id: 3, name: "Project C", description: "Description for Project C" },
];

Here, Project defines the structure of each project. The initialProjects array serves as our sample data for this component.

Step 2: Implementing the ProjectList Component

We’ll now build the main component, ProjectList, which will render the project list, filter input, and modal for displaying details.

  1. State Management: We use useBoolean to manage the modal’s open/close state and useState for selected projects and filter text.
  2. Column Definition: Define columns for DetailsList.
  3. Event Handlers: Set up handlers for filtering and modal actions.
const ProjectList: React.FunctionComponent = () => {
  const [isModalOpen, { setTrue: showModal, setFalse: hideModal }] = useBoolean(false);
  const [selectedProject, setSelectedProject] = React.useState<Project | null>(null);
  const [filterText, setFilterText] = React.useState<string>('');

  // Reference initial projects as constant
  const projects: Project[] = initialProjects;

  // Define columns for the DetailsList
  const columns: IColumn[] = [
    {
      key: 'column1',
      name: 'Project Name',
      fieldName: 'name',
      minWidth: 100,
      maxWidth: 300,
      onRender: (item: Project) => (
        <span
          style={{
            fontWeight: 'bold',
            color: '#0078d4',
            cursor: 'pointer',
            textDecoration: 'underline',
          }}
          onClick={() => handleProjectClick(item)}
          onMouseOver={(e) => (e.currentTarget.style.color = '#005a9e')}
          onMouseOut={(e) => (e.currentTarget.style.color = '#0078d4')}
        >
          {item.name}
        </span>
      ),
    },
  ];

  // Handle project click to show modal
  const handleProjectClick = (project: Project) => {
    setSelectedProject(project);
    showModal();
  };

  // Filter projects based on input text
  const filteredProjects = projects.filter(project =>
    project.name.toLowerCase().includes(filterText.toLowerCase())
  );

In this code:

  • columns define the structure and style for each project entry in the list.
  • handleProjectClick opens the modal when a project name is clicked.

Step 3: Adding a Title Bar and Rendering Components

We’ll add a custom title bar and integrate TextField, DetailsList, and Modal components.

return (
  <div>
    {/* Title Bar */}
    <div style={{ 
        backgroundColor: '#0078d4', 
        color: 'white', 
        padding: '10px', 
        fontWeight: 'bold', 
        textAlign: 'center', 
        marginBottom: '20px' 
    }}>
      Meus Projetos
    </div>

    {/* Filter TextField */}
    <TextField
      label="Filter by Project Name"
      value={filterText}
      onChange={(_, newValue) => setFilterText(newValue || '')}
      styles={{ root: { marginBottom: 20 } }} 
    />

    {/* Project List */}
    <DetailsList
      items={filteredProjects}
      columns={columns}
      setKey="set"
      layoutMode={0}
      selectionMode={0}
    />

    {/* Modal for Project Details */}
    <Modal
      isOpen={isModalOpen}
      onDismiss={hideModal}
      isBlocking={false}
      titleAriaId="modal-title"
      styles={{
        main: {
          backgroundColor: '#f3f2f1',
          width: '60%',
          height: '60%',
          top: '20%',
          left: '20%',
          overflow: 'hidden',
        },
      }}
    >
      {selectedProject && (
        <div>
          <h2 id="modal-title" style={{ color: 'white', backgroundColor: '#0078d4', padding: '10px' }}>
            {selectedProject.name}
          </h2>
          <p>{selectedProject.description}</p>
          <span style={{ cursor: 'pointer', color: '#0078d4' }} onClick={hideModal}>
            Close
          </span>
        </div>
      )}
    </Modal>
  </div>
);
};
export default ProjectList;

In this part:

  • Title Bar: Displays the “Meus Projetos” title at the top, with custom styles for color and spacing.
  • Filter Input: TextField allows users to filter projects by name.
  • Project List: DetailsList renders the project data with column definitions.
  • Project Details Modal: Modal displays detailed information about the selected project.

Full Code

Here’s the full component code:

import * as React from 'react';
import { useBoolean } from '@fluentui/react-hooks';
import { Modal, DetailsList, IColumn, TextField } from '@fluentui/react';

interface Project {
  id: number;
  name: string;
  description: string;
}

const initialProjects: Project[] = [
  { id: 1, name: "Project A", description: "Description for Project A" },
  { id: 2, name: "Project B", description: "Description for Project B" },
  { id: 3, name: "Project C", description: "Description for Project C" },
];

const ProjectList: React.FunctionComponent = () => {
  const [isModalOpen, { setTrue: showModal, setFalse: hideModal }] = useBoolean(false);
  const [selectedProject, setSelectedProject] = React.useState<Project | null>(null);
  const [filterText, setFilterText] = React.useState<string>('');

  const projects: Project[] = initialProjects;

  const columns: IColumn[] = [
    {
      key: 'column1',
      name: 'Project Name',
      fieldName: 'name',
      minWidth: 100,
      maxWidth: 300,
      onRender: (item: Project) => (
        <span
          style={{
            fontWeight: 'bold',
            color: '#0078d4',
            cursor: 'pointer',
            textDecoration: 'underline',
          }}
          onClick={() => handleProjectClick(item)}
          onMouseOver={(e) => (e.currentTarget.style.color = '#005a9e')}
          onMouseOut={(e) => (e.currentTarget.style.color = '#0078d4')}
        >
          {item.name}
        </span>
      ),
    },
  ];

  const handleProjectClick = (project: Project) => {
    setSelectedProject(project);
    showModal();
  };

  const filteredProjects = projects.filter(project =>
    project.name.toLowerCase().includes(filterText.toLowerCase())
  );

  return (
    <div>
      <div style={{ backgroundColor: '#0078d4', color: 'white', padding: '10px', fontWeight: 'bold', textAlign: 'center', marginBottom: '20px' }}>
        Meus Projetos
      </div>

      <TextField
        label="Filter by Project Name"
        value={filterText}
        onChange={(_, newValue) => setFilterText(newValue || '')}
        styles={{ root: { marginBottom: 20 } }} 
      />

      <DetailsList
        items={filteredProjects}
        columns={columns}
        setKey="set"
        layoutMode={0}
        selectionMode={0}
      />

      <Modal
        isOpen={isModalOpen}
        onDismiss={hideModal}
        isBlocking={false}
        titleAriaId="modal-title"
        styles={{
          main: {


            backgroundColor: '#f3f2f1',
            width: '60%',
            height: '60%',
            top: '20%',
            left: '20%',
            overflow: 'hidden',
          },
        }}
      >
        {selectedProject && (
          <div>
            <h2 id="modal-title" style={{ color: 'white', backgroundColor: '#0078d4', padding: '10px' }}>
              {selectedProject.name}
            </h2>
            <p>{selectedProject.description}</p>
            <span style={{ cursor: 'pointer', color: '#0078d4' }} onClick={hideModal}>
              Close
            </span>
          </div>
        )}
      </Modal>
    </div>
  );
};

export default ProjectList;

Conclusion

This ProjectList component provides a user-friendly interface for managing and viewing project data. It demonstrates how to use Fluent UI components and styles effectively to enhance the UI. You can expand this component by adding more project details or additional actions as needed.

Edvaldo Guimrães Filho Avatar

Published by

Categories: