Skip to content

Commit

Permalink
Merge pull request #2596 from OneCommunityGlobal/niketha_project_reload
Browse files Browse the repository at this point in the history
Niketha Anand- Rotate indefinitely after adding project
  • Loading branch information
one-community authored Sep 24, 2024
2 parents 806d20a + f6f1c4b commit a4c8ef9
Show file tree
Hide file tree
Showing 3 changed files with 156 additions and 130 deletions.
145 changes: 84 additions & 61 deletions src/components/Projects/AddProject/AddProject.jsx
Original file line number Diff line number Diff line change
@@ -1,64 +1,87 @@
/*********************************************************************************
* Component: postProject
* Author: Henry Ng - 01/17/20
* This component is used to add more project into the database
********************************************************************************/
import React, { useState } from 'react';
import React, { useState } from 'react';

const AddProject = props => {
const [showAddButton, setShowAddButton] = useState(false);
const [newName, setNewName] = useState('');
const [newCategory, setNewCategory] = useState('Unspecified');

const changeNewName = newName => {
if (newName.length !== 0) {
setShowAddButton(true);
} else {
const AddProject = props => {
const [showAddButton, setShowAddButton] = useState(false);
const [newName, setNewName] = useState('');
const [newCategory, setNewCategory] = useState('Unspecified');
const [loading, setLoading] = useState(false);

const changeNewName = name => {
if (name.length !== 0) {
setShowAddButton(true);
} else {
setShowAddButton(false);
}
setNewName(name);
};

const handleAddProject = () => {
setLoading(true); // Start loading
Promise.resolve(props.onAddNewProject(newName, newCategory))
.then(() => {
// Reset fields after the project is added
setNewName('');
setNewCategory('Unspecified');
setShowAddButton(false);
}
setNewName(newName);
};

return (
<div className="input-group" id="new_project">
<div className="input-group-prepend">
<span className="input-group-text">Add new project</span>
</div>
<input
type="text"
className="form-control"
aria-label="New Project"
placeholder="Project Name (required) type to add."
onChange={e => changeNewName(e.target.value)}
/>
<div className="input-group-append">
<select onChange={e => setNewCategory(e.target.value)}>
<option default value="Unspecified">
Select Category
</option>
<option value="Food">Food</option>
<option value="Energy">Energy</option>
<option value="Housing">Housing</option>
<option value="Education">Education</option>
<option value="Society">Society</option>
<option value="Economics">Economics</option>
<option value="Stewardship">Stewardship</option>
<option value="Other">Other</option>
</select>
</div>
<div className="input-group-append">
{showAddButton ? (
<button
className="btn btn-outline-primary"
type="button"
onClick={() => props.onAddNewProject(newName, newCategory)}
>
<i className="fa fa-plus" aria-hidden="true"></i>
</button>
) : null}
</div>
</div>
);
};
})
.finally(() => {
setLoading(false); // Stop loading
});
};

return (
<div className="input-group" id="new_project">
<div className="input-group-prepend">
<span className="input-group-text">Add new project</span>
</div>
<input
type="text"
className="form-control"
aria-label="New Project"
placeholder="Project Name (required) type to add."
value={newName}
onChange={e => changeNewName(e.target.value)}
disabled={loading}
/>
<div className="input-group-append">
<select
value={newCategory}
onChange={e => setNewCategory(e.target.value)}
disabled={loading}
>
<option default value="Unspecified">
Select Category
</option>
<option value="Food">Food</option>
<option value="Energy">Energy</option>
<option value="Housing">Housing</option>
<option value="Education">Education</option>
<option value="Society">Society</option>
<option value="Economics">Economics</option>
<option value="Stewardship">Stewardship</option>
<option value="Other">Other</option>
</select>
</div>
<div className="input-group-append">
{showAddButton && (
<button
className="btn btn-outline-primary"
type="button"
onClick={handleAddProject}
disabled={loading}
>
{loading ? (
<i className="fa fa-spinner fa-spin" aria-hidden="true"></i>
) : (
<i className="fa fa-plus" aria-hidden="true"></i>
)}
</button>
)}
</div>
</div>
);
};

export default AddProject;


export default AddProject;
86 changes: 41 additions & 45 deletions src/components/Projects/AddProject/AddProject.test.jsx
Original file line number Diff line number Diff line change
@@ -1,94 +1,90 @@
import AddProject from './AddProject';
import { render, screen, fireEvent } from '@testing-library/react';
import { render, screen, fireEvent, act } from '@testing-library/react';
import userEvent from '@testing-library/user-event';

//helper function
// Helper function
const typeIntoInput = ({ input }) => {
const inputField = screen.getByRole('textbox');

if (input) {
userEvent.type(inputField, input)
userEvent.type(inputField, input);
}

return {
inputField
}
}
inputField,
};
};

describe("AddProject component structure", () => {
describe('AddProject component structure', () => {
beforeEach(() => {
render(<AddProject />);
});

beforeEach(() => {
render(<AddProject />)
})

test("it renders correctly", () => {
expect(screen.getByText("Add new project")).toBeInTheDocument();
})
test('it renders correctly', () => {
expect(screen.getByText('Add new project')).toBeInTheDocument();
});

test("input field should initially be empty", () => {
test('input field should initially be empty', () => {
expect(screen.getByRole('textbox').value).toBe('');
})
});

test("select element should initially have a value of unspecified", () => {
test('select element should initially have a value of unspecified', () => {
expect(screen.getByRole('combobox').value).toBe('Unspecified');
})
});

test("button should not be in the document when the input field is empty", () => {
test('button should not be in the document when the input field is empty', () => {
expect(screen.queryByRole('button')).toBeNull();
})
});

test("user should be able to type in the input field", () => {
test('user should be able to type in the input field', () => {
const { inputField } = typeIntoInput({ input: 'New Project Name' });
expect(inputField.value).toBe('New Project Name');
})
});

test("user should be able to select a category", () => {
test('user should be able to select a category', () => {
userEvent.selectOptions(screen.getByRole('combobox'), 'Food');
expect(screen.getByRole('option', { name: 'Food' }).selected).toBe(true);
})
});

test("button should appear when user types in the input field", () => {
test('button should appear when user types in the input field', () => {
typeIntoInput({ input: '123' });
expect(screen.queryByRole('button')).not.toBeNull();
})
})
});
});

describe('AddProject component state handlers', () => {

//mock the onAddNewProject function

const mockAddNewProject = jest.fn();
// Mock the onAddNewProject function
const mockAddNewProject = jest.fn().mockResolvedValueOnce();

beforeEach(() => {
render(<AddProject onAddNewProject={mockAddNewProject} />)
render(<AddProject onAddNewProject={mockAddNewProject} />);
});

test("Input change handler updates state correctly", () => {
test('Input change handler updates state correctly', () => {
const inputField = screen.getByRole('textbox');

fireEvent.change(inputField, { target: { value: 'New Project' } });

expect(inputField.value).toBe('New Project');
})
});

test("Select change handler updates state correctly", () => {
test('Select change handler updates state correctly', () => {
const selectElement = screen.getByRole('combobox');
fireEvent.change(selectElement, { target: { value: 'Food' } });
expect(selectElement.value).toBe('Food');
})
});

test('Button click handler calls onAddNewProject with correct arguments', () => {
test('Button click handler calls onAddNewProject with correct arguments', async () => {
const inputField = screen.getByRole('textbox');
const selectElement = screen.getByRole('combobox');

fireEvent.change(inputField, { target: { value: 'New Project' } });
fireEvent.change(selectElement, { target: { value: 'Food' } });

const buttonElement = screen.queryByRole('button');

fireEvent.click(buttonElement);
const buttonElement = screen.getByRole('button');

expect(mockAddNewProject).toHaveBeenLastCalledWith('New Project', 'Food');
})
})
await act(async () => {
fireEvent.click(buttonElement);
});

expect(mockAddNewProject).toHaveBeenCalledWith('New Project', 'Food');
});
});
55 changes: 31 additions & 24 deletions src/components/Projects/Projects.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
modifyProject,
clearError,
} from '../../actions/projects';
import {getProjectsByUsersName} from '../../actions/userProfile';
import { getProjectsByUsersName } from '../../actions/userProfile';
import { getPopupById } from '../../actions/popupEditorAction';
import Overview from './Overview';
import AddProject from './AddProject';
Expand Down Expand Up @@ -119,6 +119,7 @@ const Projects = function(props) {

const postProject = async (name, category) => {
await props.postNewProject(name, category);
refreshProjects(); // Refresh project list after adding a project
};

const generateProjectList = (categorySelectedForSort, showStatus, sortedByName) => {
Expand Down Expand Up @@ -157,21 +158,25 @@ const Projects = function(props) {
setAllProjects(projectList);
}

const refreshProjects = async () => {
await props.fetchAllProjects();
};

useEffect(() => {
props.fetchAllProjects();
}, []);

useEffect(() => {
generateProjectList(categorySelectedForSort, showStatus, sortedByName);
if (status !== 200) {
setModalData({
showModal: true,
modalMessage: error,
modalTitle: 'ERROR',
hasConfirmBtn: false,
hasInactiveBtn: false,
});
}
generateProjectList(categorySelectedForSort, showStatus, sortedByName);
if (status !== 200) {
setModalData({
showModal: true,
modalMessage: error,
modalTitle: 'ERROR',
hasConfirmBtn: false,
hasInactiveBtn: false,
});
}
}, [categorySelectedForSort, showStatus, sortedByName, props.state.allProjects, props.state.theme.darkMode]);

useEffect(() => {
Expand All @@ -183,7 +188,7 @@ const Projects = function(props) {
projects.some(p => p === project.key)
);
setProjectList(newProjectList);
}else{
} else {
setProjectList(allProjects);
}
} else {
Expand Down Expand Up @@ -214,23 +219,25 @@ const Projects = function(props) {
<Overview numberOfProjects={numberOfProjects} numberOfActive={numberOfActive} />
</div>

{canPostProject ? <AddProject onAddNewProject={postProject} /> : null}
{canPostProject ? <AddProject onAddNewProject={postProject} refreshProjects={refreshProjects} /> : null}

<SearchProjectByPerson onSearch={handleSearchName}/>
<SearchProjectByPerson onSearch={handleSearchName} />

<table className="table table-bordered table-responsive-sm">
<thead>
<ProjectTableHeader
onChange={onChangeCategory}
selectedValue={categorySelectedForSort}
showStatus={showStatus}
selectStatus={onSelectStatus}
sorted={sortedByName}
handleSort = {handleSort}
darkMode={darkMode}
/>
<ProjectTableHeader
onChange={onChangeCategory}
selectedValue={categorySelectedForSort}
showStatus={showStatus}
selectStatus={onSelectStatus}
sorted={sortedByName}
handleSort={handleSort}
darkMode={darkMode}
/>
</thead>
<tbody className={darkMode ? 'bg-yinmn-blue dark-mode' : ''}>{projectList}</tbody>
<tbody className={darkMode ? 'bg-yinmn-blue dark-mode' : ''}>
{projectList}
</tbody>
</table>
</div>

Expand Down

0 comments on commit a4c8ef9

Please sign in to comment.