Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added tooltip UX to School variables(abbrev, desc, regex) #59

Closed
wants to merge 8 commits into from
Closed
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
1 change: 1 addition & 0 deletions frontend/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import SchoolIndexPage from "main/pages/SchoolIndexPage";
import CoursesCreatePage from "main/pages/CoursesCreatePage";
import CourseIndexPage from "main/pages/CourseIndexPage";


import { hasRole, useCurrentUser } from "main/utils/currentUser";
import NotFoundPage from "main/pages/NotFoundPage";

Expand Down
12 changes: 4 additions & 8 deletions frontend/src/fixtures/schoolsFixtures.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,26 @@ const schoolsFixtures = {
"abbrev": "ucsb",
"name": "UC Santa Barbara",
"termRegex": "[WSMF]\\d\\d",
"termDescription": "Enter quarter, e.g. F23, W24, S24, M24",
"termError": "Quarter must be entered in the correct format"
"termDescription": "Enter quarter, e.g. F23, W24, S24, M24"
},
threeSchools: [
{
"abbrev": "ucsb",
"name": "UC Santa Barbara",
"termRegex": "[WSMF]\\d\\d",
"termDescription": "Enter quarter, e.g. F23, W24, S24, M24",
"termError": "Quarter must be entered in the correct format"
"termDescription": "Enter quarter, e.g. F23, W24, S24, M24"
},
{
"abbrev": "umn",
"name": "University of Minnesota",
"termRegex": "[WSMF]\\d\\d",
"termDescription": "Enter quarter, e.g. F23, W24, S24, M24",
"termError": "Quarter must be entered in the correct format"
"termDescription": "Enter quarter, e.g. F23, W24, S24, M24"
},
{
"abbrev": "ucsd",
"name": "UC San Diego",
"termRegex": "[WSMF]\\d\\d",
"termDescription": "Enter quarter, e.g. F23, W24, S24, M24",
"termError": "Quarter must be entered in the correct format"
"termDescription": "Enter quarter, e.g. F23, W24, S24, M24"
}
]
};
Expand Down
1 change: 1 addition & 0 deletions frontend/src/main/components/Courses/CoursesTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import React from "react";
{
Header: 'id',
accessor: 'id',
Cell: ({ value }) => <a data-testid={`linkToCoursesPage-${value}`} href={`/courses/${value}`}>{value}</a>,
},
{
Header: 'Name',
Expand Down
38 changes: 19 additions & 19 deletions frontend/src/main/components/School/SchoolForm.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Button, Form, Row, Col } from 'react-bootstrap';
import { Button, Form, Row, Col, OverlayTrigger, Tooltip } from 'react-bootstrap';
import { useForm } from 'react-hook-form'
import { useNavigate } from 'react-router-dom'

Expand Down Expand Up @@ -26,13 +26,19 @@ function SchoolForm({ initialContents, submitAction, buttonLabel = "Create" }) {
<Col>
<Form.Group className="mb-3" >
<Form.Label htmlFor="abbrev">Abbreviation</Form.Label>
<OverlayTrigger
placement="top"
overlay={<Tooltip>Please enter an abbreviation for school name in lowercase. EX: UCSB</Tooltip>}
delay='5'
>
<Form.Control
data-testid="SchoolForm-abbrev"
id="abbrev"
type="text"
isInvalid={Boolean(errors.abbrev)}
{...register("abbrev", { required: true, pattern: abbrev_regex })}
/>
</OverlayTrigger>
<Form.Control.Feedback type="invalid">
{errors.abbrev?.type === 'required' && 'Abbreviation is required. '}
{errors.abbrev?.type === 'pattern' && 'Abbreviation must be lowercase letters (_ and . allowed), e.g. ucsb'}
Expand All @@ -57,13 +63,19 @@ function SchoolForm({ initialContents, submitAction, buttonLabel = "Create" }) {
<Col>
<Form.Group className="mb-3" >
<Form.Label htmlFor="termRegex">Term Regex</Form.Label>
<OverlayTrigger
placement="top"
overlay={<Tooltip>Please enter a regular expression for the format of terms (semesters or quarters) at the school. EX: [WSMF]\d\d for UCSB</Tooltip>}
delay='5'
>
<Form.Control
data-testid="SchoolForm-termRegex"
id="termRegex"
type="text"
isInvalid={Boolean(errors.termRegex)}
{...register("termRegex", { required: true })}
/>
</OverlayTrigger>
<Form.Control.Feedback type="invalid">
{errors.termRegex && 'Term Regex is required. '}
</Form.Control.Feedback>
Expand All @@ -75,38 +87,26 @@ function SchoolForm({ initialContents, submitAction, buttonLabel = "Create" }) {
<Col>
<Form.Group className="mb-3" >
<Form.Label htmlFor="termDescription">Term Description</Form.Label>
<OverlayTrigger
placement="top"
overlay={<Tooltip>Please enter a term description. EX: Quarter, Semester or Session</Tooltip>}
delay='5'
>
<Form.Control
data-testid="SchoolForm-termDescription"
id="termDescription"
type="text"
isInvalid={Boolean(errors.termDescription)}
{...register("termDescription", { required: true})}
/>
</OverlayTrigger>
<Form.Control.Feedback type="invalid">
{errors.termDescription && 'Term Description is required.'}
</Form.Control.Feedback>
</Form.Group>
</Col>
</Row>

<Row>
<Col>
<Form.Group className="mb-3" >
<Form.Label htmlFor="termError">Term Error</Form.Label>
<Form.Control
data-testid="SchoolForm-termError"
id="termError"
type="text"
isInvalid={Boolean(errors.termError)}
{...register("termError", { required: true})}
/>
<Form.Control.Feedback type="invalid">
{errors.termError && 'Term Error is required.'}
</Form.Control.Feedback>
</Form.Group>
</Col>
</Row>

<Row>
<Col>
<Button
Expand Down
4 changes: 0 additions & 4 deletions frontend/src/main/components/School/SchoolTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,6 @@ import React from "react";
Header: 'TermDescription',
accessor: 'termDescription',
},
{
Header: 'TermError',
accessor: 'termError',
},
];

if (hasRole(currentUser, "ROLE_ADMIN")) {
Expand Down
32 changes: 32 additions & 0 deletions frontend/src/main/pages/CoursesShowPage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from 'react'
import { useParams } from "react-router-dom";
import { useBackend } from 'main/utils/useBackend';
import BasicLayout from "main/layouts/BasicLayout/BasicLayout";
import CoursesTable from 'main/components/Courses/CoursesTable';
import { useCurrentUser} from 'main/utils/currentUser';

export default function CourseShowPage() {

let { id } = useParams();
const { data: currentUser } = useCurrentUser();

const { data: courses, error: _error, status: _status } =
useBackend(
// Stryker disable next-line all : don't test internal caching of React Query
[`/api/courses?id=${id}`],
// Stryker disable next-line all : GET is the default
{ method: "GET", url: "/api/courses/get", params: { id }},
// Stryker disable next-line all : GET is the default
[]
);


return (
<BasicLayout>
<div className="pt-2">
<h1>Course {id} Info</h1>
<CoursesTable courses={[courses]} currentUser={currentUser} />
</div>
</BasicLayout>
)
}
29 changes: 29 additions & 0 deletions frontend/src/stories/pages/CourseShowPage.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React from 'react';
import { apiCurrentUserFixtures } from "fixtures/currentUserFixtures";
import { systemInfoFixtures } from "fixtures/systemInfoFixtures";
import { coursesFixtures } from "fixtures/coursesFixtures";
import { rest } from "msw";

import CoursesShowPage from "main/pages/CoursesShowPage";

export default {
title: 'pages/CourseShowPage',
component: CoursesShowPage
};

const Template = () => <CoursesShowPage storybook={true}/>;

export const Default = Template.bind({});
Default.parameters = {
msw: [
rest.get('/api/currentUser', (_req, res, ctx) => {
return res( ctx.json(apiCurrentUserFixtures.adminUser));
}),
rest.get('/api/systemInfo', (_req, res, ctx) => {
return res(ctx.json(systemInfoFixtures.showingNeither));
}),
rest.get('/api/courses/get', (_req, res, ctx) => {
return res(ctx.json(coursesFixtures.oneCourse));
}),
],
}
18 changes: 18 additions & 0 deletions frontend/src/tests/components/Courses/CoursesTable.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -294,4 +294,22 @@ describe("UserTable tests", () => {
expect(totalCoursesElement).toBeInTheDocument();
});

test("ID is a hyperlink", async () => {

const currentUser = currentUserFixtures.adminUser;

render(
<QueryClientProvider client={queryClient}>
<MemoryRouter>
<CoursesTable courses={coursesFixtures.threeCourses} currentUser={currentUser} />
</MemoryRouter>
</QueryClientProvider>

);

const idLink = screen.getByTestId(`linkToCoursesPage-1`);
expect(idLink).toBeInTheDocument();
expect(idLink.getAttribute('href')).toBe('/courses/1');
});

});
7 changes: 3 additions & 4 deletions frontend/src/tests/components/School/SchoolForm.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ describe("SchoolForm tests", () => {
await screen.findByTestId(/SchoolForm-abbrev/);
expect(screen.getByText(/Abbreviation/)).toBeInTheDocument();
expect(screen.getByTestId(/SchoolForm-abbrev/)).toHaveValue("ucsb");
expect(screen.getByTestId(/SchoolForm-name/)).toHaveValue("UC Santa Barbara");
expect(screen.getByTestId(/SchoolForm-termRegex/)).toHaveValue("[WSMF]\\d\\d");
expect(screen.getByTestId(/SchoolForm-termDescription/)).toHaveValue("Enter quarter, e.g. F23, W24, S24, M24");
});

test("Correct Error messsages on bad input", async () => {
Expand Down Expand Up @@ -64,7 +67,6 @@ describe("SchoolForm tests", () => {
expect(screen.getByText(/Name is required./)).toBeInTheDocument();
expect(screen.getByText(/Term Regex is required./)).toBeInTheDocument();
expect(screen.getByText(/Term Description is required./)).toBeInTheDocument();
expect(screen.getByText(/Term Error is required./)).toBeInTheDocument();
});

test("No Error messsages on good input", async () => {
Expand All @@ -80,14 +82,12 @@ describe("SchoolForm tests", () => {
const nameField = screen.getByTestId("SchoolForm-name");
const termRegexField = screen.getByTestId("SchoolForm-termRegex");
const termDescriptionField = screen.getByTestId("SchoolForm-termDescription");
const termErrorField = screen.getByTestId("SchoolForm-termError");
const submitButton = screen.getByTestId("SchoolForm-submit");

fireEvent.change(abbrevField, { target: { value: 'ucsb' } });
fireEvent.change(nameField, { target: { value: 'UC Santa Barbara' } });
fireEvent.change(termRegexField, { target: { value: '[WSMF]\\d\\d' } });
fireEvent.change(termDescriptionField, { target: { value: 'Enter quarter, e.g. F23, W24, S24, M24' } });
fireEvent.change(termErrorField, { target: { value: 'Quarter must be entered in the correct format' } });
fireEvent.click(submitButton);

await screen.findByTestId(/SchoolForm-abbrev/);
Expand All @@ -96,7 +96,6 @@ describe("SchoolForm tests", () => {
expect(screen.queryByText(/Name is required./)).not.toBeInTheDocument();
expect(screen.queryByText(/Term Regex is required./)).not.toBeInTheDocument();
expect(screen.queryByText(/Term Description is required./)).not.toBeInTheDocument();
expect(screen.queryByText(/Term Error is required./)).not.toBeInTheDocument();
});

test("that navigate(-1) is called when Cancel is clicked", async () => {
Expand Down
12 changes: 6 additions & 6 deletions frontend/src/tests/components/School/SchoolTable.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ describe("UserTable tests", () => {

);

const expectedHeaders = ["Abbrev", "Name", "TermRegex", "TermDescription", "TermError"];
const expectedFields = ["abbrev", "name", "termRegex", "termDescription", "termError"];
const expectedHeaders = ["Abbrev", "Name", "TermRegex", "TermDescription"];
const expectedFields = ["abbrev", "name", "termRegex", "termDescription"];
const testId = "SchoolTable";

expectedHeaders.forEach((headerText) => {
Expand Down Expand Up @@ -60,8 +60,8 @@ describe("UserTable tests", () => {
// arrange
const currentUser = currentUserFixtures.adminUser;

const expectedHeaders = ["Abbrev", "Name", "TermRegex", "TermDescription", "TermError"];
const expectedFields = ["abbrev", "name", "termRegex", "termDescription", "termError"];
const expectedHeaders = ["Abbrev", "Name", "TermRegex", "TermDescription"];
const expectedFields = ["abbrev", "name", "termRegex", "termDescription"];
const testId = "SchoolTable";

// act
Expand Down Expand Up @@ -99,8 +99,8 @@ describe("UserTable tests", () => {

);

const expectedHeaders = ["Abbrev", "Name", "TermRegex", "TermDescription", "TermError"];
const expectedFields = ["abbrev", "name", "termRegex", "termDescription", "termError"];
const expectedHeaders = ["Abbrev", "Name", "TermRegex", "TermDescription"];
const expectedFields = ["abbrev", "name", "termRegex", "termDescription"];
const testId = "SchoolTable";

expectedHeaders.forEach((headerText) => {
Expand Down
Loading
Loading