diff --git a/src/HospitalRun.tsx b/src/HospitalRun.tsx index f9bf8567d0..58985c424a 100644 --- a/src/HospitalRun.tsx +++ b/src/HospitalRun.tsx @@ -5,7 +5,7 @@ import { Toaster } from '@hospitalrun/components' import Appointments from 'scheduling/appointments/Appointments' import NewAppointment from 'scheduling/appointments/new/NewAppointment' import ViewAppointment from 'scheduling/appointments/view/ViewAppointment' -import Breadcrumb from 'components/Breadcrumb' +import Breadcrumb from 'components/breadcrumb/Breadcrumb' import Sidebar from './components/Sidebar' import Permissions from './model/Permissions' import Dashboard from './dashboard/Dashboard' diff --git a/src/__tests__/HospitalRun.test.tsx b/src/__tests__/HospitalRun.test.tsx index 9d66e3a2e1..274f6a7867 100644 --- a/src/__tests__/HospitalRun.test.tsx +++ b/src/__tests__/HospitalRun.test.tsx @@ -77,7 +77,7 @@ describe('HospitalRun', () => { store={mockStore({ title: 'test', user: { permissions: [Permissions.ReadPatients] }, - patient, + patient: { patient }, })} > @@ -95,6 +95,7 @@ describe('HospitalRun', () => { store={mockStore({ title: 'test', user: { permissions: [] }, + patient: { patient: {} }, })} > @@ -133,6 +134,7 @@ describe('HospitalRun', () => { title: 'test', user: { permissions: [] }, appointments: { appointments: [] }, + appointment: { appointment: {} }, })} > diff --git a/src/__tests__/components/Breadcrumb.test.tsx b/src/__tests__/components/Breadcrumb.test.tsx deleted file mode 100644 index 3ad64f2d5c..0000000000 --- a/src/__tests__/components/Breadcrumb.test.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import '../../__mocks__/matchMediaMock' -import React from 'react' -import { mount } from 'enzyme' -import { createMemoryHistory } from 'history' -import { Router } from 'react-router' -import Breadcrumb from 'components/Breadcrumb' -import { - Breadcrumb as HrBreadcrumb, - BreadcrumbItem as HrBreadcrumbItem, -} from '@hospitalrun/components' - -describe('Breadcrumb', () => { - let history = createMemoryHistory() - const setup = (location: string) => { - history = createMemoryHistory() - history.push(location) - return mount( - - - , - ) - } - - it('should render the breadcrumb items', () => { - const wrapper = setup('/patients') - const breadcrumbItem = wrapper.find(HrBreadcrumbItem) - - expect(wrapper.find(HrBreadcrumb)).toHaveLength(1) - expect( - breadcrumbItem.matchesElement(patients), - ).toBeTruthy() - }) -}) diff --git a/src/__tests__/components/breadcrumb/Appointmentbreadcrumb.test.tsx b/src/__tests__/components/breadcrumb/Appointmentbreadcrumb.test.tsx new file mode 100644 index 0000000000..93a04fcc98 --- /dev/null +++ b/src/__tests__/components/breadcrumb/Appointmentbreadcrumb.test.tsx @@ -0,0 +1,31 @@ +import '../../../__mocks__/matchMediaMock' +import React from 'react' +import { Router } from 'react-router' +import { Provider } from 'react-redux' +import { mount } from 'enzyme' +import configureMockStore from 'redux-mock-store' +import { createMemoryHistory } from 'history' +import { BreadcrumbItem as HrBreadcrumbItem } from '@hospitalrun/components' +import AppointmentBreadcrumb from 'components/breadcrumb/AppointmentBreadcrumb' + +const mockStore = configureMockStore() + +describe('Breadcrumb', () => { + const history = createMemoryHistory() + history.push('/appointments/1234') + const wrapper = mount( + + + + + , + ) + + it('should render 2 breadcrumb items', () => { + expect(wrapper.find(HrBreadcrumbItem)).toHaveLength(2) + }) +}) diff --git a/src/__tests__/components/breadcrumb/Breadcrumb.test.tsx b/src/__tests__/components/breadcrumb/Breadcrumb.test.tsx new file mode 100644 index 0000000000..9d05a7f604 --- /dev/null +++ b/src/__tests__/components/breadcrumb/Breadcrumb.test.tsx @@ -0,0 +1,61 @@ +import '../../../__mocks__/matchMediaMock' +import React from 'react' +import { Router } from 'react-router' +import { Provider } from 'react-redux' +import { mount } from 'enzyme' +import configureMockStore from 'redux-mock-store' +import { createMemoryHistory } from 'history' +import DefaultBreadcrumb from 'components/breadcrumb/DefaultBreadcrumb' +import PatientBreadcrumb from 'components/breadcrumb/PatientBreadcrumb' +import AppointmentBreadcrumb from 'components/breadcrumb/AppointmentBreadcrumb' +import Breadcrumb from 'components/breadcrumb/Breadcrumb' + +const mockStore = configureMockStore() + +describe('Breadcrumb', () => { + const setup = (location: string) => { + const history = createMemoryHistory() + history.push(location) + return mount( + + + + + , + ) + } + it('should render the patient breadcrumb when /patients/:id is accessed', () => { + const wrapper = setup('/patients/1234') + expect(wrapper.find(PatientBreadcrumb)).toHaveLength(1) + }) + it('should render the appointment breadcrumb when /appointments/:id is accessed', () => { + const wrapper = setup('/appointments/1234') + expect(wrapper.find(AppointmentBreadcrumb)).toHaveLength(1) + }) + + it('should render the default breadcrumb when /patients/new is accessed', () => { + const wrapper = setup('/patients/new') + expect(wrapper.find(DefaultBreadcrumb)).toHaveLength(1) + }) + + it('should render the default breadcrumb when /appointments/new is accessed', () => { + const wrapper = setup('/appointments/new') + expect(wrapper.find(DefaultBreadcrumb)).toHaveLength(1) + }) + + it('should render the default breadcrumb when any other path is accessed', () => { + let wrapper = setup('/appointments') + expect(wrapper.find(DefaultBreadcrumb)).toHaveLength(1) + + wrapper = setup('/patients') + expect(wrapper.find(DefaultBreadcrumb)).toHaveLength(1) + + wrapper = setup('/') + expect(wrapper.find(DefaultBreadcrumb)).toHaveLength(1) + }) +}) diff --git a/src/__tests__/components/breadcrumb/DefaultBreadcrumb.test.tsx b/src/__tests__/components/breadcrumb/DefaultBreadcrumb.test.tsx new file mode 100644 index 0000000000..0632dc3820 --- /dev/null +++ b/src/__tests__/components/breadcrumb/DefaultBreadcrumb.test.tsx @@ -0,0 +1,54 @@ +import '../../../__mocks__/matchMediaMock' +import React from 'react' +import { mount } from 'enzyme' +import { createMemoryHistory } from 'history' +import { Router } from 'react-router' +import DefaultBreadcrumb, { getItems } from 'components/breadcrumb/DefaultBreadcrumb' +import { BreadcrumbItem as HrBreadcrumbItem } from '@hospitalrun/components' + +describe('DefaultBreadcrumb', () => { + describe('getItems', () => { + it('should return valid items for pathname /', () => { + expect(getItems('/')).toEqual([{ url: '/', active: true }]) + }) + + it('should return valid items for pathname /patients', () => { + expect(getItems('/patients')).toEqual([{ url: '/patients', active: true }]) + }) + + it('should return valid items for pathname /appointments', () => { + expect(getItems('/appointments')).toEqual([{ url: '/appointments', active: true }]) + }) + + it('should return valid items for pathname /patients/new', () => { + expect(getItems('/patients/new')).toEqual([ + { url: '/patients', active: false }, + { url: '/patients/new', active: true }, + ]) + }) + + it('should return valid items for pathname /appointments/new', () => { + expect(getItems('/appointments/new')).toEqual([ + { url: '/appointments', active: false }, + { url: '/appointments/new', active: true }, + ]) + }) + }) + + describe('rendering', () => { + const setup = (location: string) => { + const history = createMemoryHistory() + history.push(location) + return mount( + + + , + ) + } + + it('should render one breadcrumb item for the path /', () => { + const wrapper = setup('/') + expect(wrapper.find(HrBreadcrumbItem)).toHaveLength(1) + }) + }) +}) diff --git a/src/__tests__/components/breadcrumb/PatientBreadcrumb.test.tsx b/src/__tests__/components/breadcrumb/PatientBreadcrumb.test.tsx new file mode 100644 index 0000000000..1a33139245 --- /dev/null +++ b/src/__tests__/components/breadcrumb/PatientBreadcrumb.test.tsx @@ -0,0 +1,31 @@ +import '../../../__mocks__/matchMediaMock' +import React from 'react' +import { Router } from 'react-router' +import { Provider } from 'react-redux' +import { mount } from 'enzyme' +import configureMockStore from 'redux-mock-store' +import { createMemoryHistory } from 'history' +import { BreadcrumbItem as HrBreadcrumbItem } from '@hospitalrun/components' +import PatientBreadcrumb from 'components/breadcrumb/PatientBreadcrumb' + +const mockStore = configureMockStore() + +describe('Breadcrumb', () => { + const history = createMemoryHistory() + history.push('/patients/1234') + const wrapper = mount( + + + + + , + ) + + it('should render 2 breadcrumb items', () => { + expect(wrapper.find(HrBreadcrumbItem)).toHaveLength(2) + }) +}) diff --git a/src/components/Breadcrumb.tsx b/src/components/Breadcrumb.tsx deleted file mode 100644 index e0de776d84..0000000000 --- a/src/components/Breadcrumb.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import React from 'react' -import { useLocation, useHistory } from 'react-router' -import { - Breadcrumb as HrBreadcrumb, - BreadcrumbItem as HrBreadcrumbItem, -} from '@hospitalrun/components' - -interface Item { - name: string - url: string -} - -function getItems(pathname: string): Item[] { - if (!pathname || pathname === '/') { - return [{ name: 'dashboard', url: '/' }] - } - - return pathname - .substring(1) - .split('/') - .map((name) => ({ name, url: '/' })) -} - -const Breadcrumb = () => { - const { pathname } = useLocation() - const history = useHistory() - const items = getItems(pathname) - const lastIndex = items.length - 1 - - return ( - - {items.map((item, index) => { - const isLast = index === lastIndex - const onClick = !isLast ? () => history.push(item.url) : undefined - - return ( - - {item.name} - - ) - })} - - ) -} - -export default Breadcrumb diff --git a/src/components/breadcrumb/AppointmentBreadcrumb.tsx b/src/components/breadcrumb/AppointmentBreadcrumb.tsx new file mode 100644 index 0000000000..5c0aa386f3 --- /dev/null +++ b/src/components/breadcrumb/AppointmentBreadcrumb.tsx @@ -0,0 +1,33 @@ +import React from 'react' +import { useHistory } from 'react-router' +import { useSelector } from 'react-redux' +import { useTranslation } from 'react-i18next' +import { + Breadcrumb as HrBreadcrumb, + BreadcrumbItem as HrBreadcrumbItem, +} from '@hospitalrun/components' +import { RootState } from '../../store' + +const AppointmentBreacrumb = () => { + const { t } = useTranslation() + const { appointment } = useSelector((state: RootState) => state.appointment) + const history = useHistory() + let appointmentLabel = '' + + if (appointment.startDateTime && appointment.endDateTime) { + const startDateLabel = new Date(appointment.startDateTime).toLocaleString() + const endDateLabel = new Date(appointment.endDateTime).toLocaleString() + appointmentLabel = `${startDateLabel} - ${endDateLabel}` + } + + return ( + + history.push('/appointments')}> + {t('scheduling.appointments.label')} + + {appointmentLabel} + + ) +} + +export default AppointmentBreacrumb diff --git a/src/components/breadcrumb/Breadcrumb.tsx b/src/components/breadcrumb/Breadcrumb.tsx new file mode 100644 index 0000000000..e2a53d6f77 --- /dev/null +++ b/src/components/breadcrumb/Breadcrumb.tsx @@ -0,0 +1,16 @@ +import React from 'react' +import { Switch, Route } from 'react-router' +import DefaultBreadcrumb from 'components/breadcrumb/DefaultBreadcrumb' +import PatientBreadcrumb from 'components/breadcrumb/PatientBreadcrumb' +import AppointmentBreadcrumb from 'components/breadcrumb/AppointmentBreadcrumb' + +const Breadcrumb = () => ( + + + + + + +) + +export default Breadcrumb diff --git a/src/components/breadcrumb/DefaultBreadcrumb.tsx b/src/components/breadcrumb/DefaultBreadcrumb.tsx new file mode 100644 index 0000000000..f9d1e54d6c --- /dev/null +++ b/src/components/breadcrumb/DefaultBreadcrumb.tsx @@ -0,0 +1,53 @@ +import React from 'react' +import { useLocation, useHistory } from 'react-router' +import { useTranslation } from 'react-i18next' +import { + Breadcrumb as HrBreadcrumb, + BreadcrumbItem as HrBreadcrumbItem, +} from '@hospitalrun/components' + +interface Item { + url: string + active: boolean +} + +const urlToi18nKey: { [url: string]: string } = { + '/': 'dashboard.label', + '/patients': 'patients.label', + '/patients/new': 'patients.newPatient', + '/appointments': 'scheduling.appointments.label', + '/appointments/new': 'scheduling.appointments.new', +} + +export function getItems(pathname: string): Item[] { + let url = '' + const paths = pathname.substring(1).split('/') + + return paths.map((path, index) => { + url += `/${path}` + return { url, active: index === paths.length - 1 } + }) +} + +const DefaultBreadcrumb = () => { + const { t } = useTranslation() + const { pathname } = useLocation() + const history = useHistory() + const items = getItems(pathname) + + return ( + + {items.map((item) => { + const onClick = !item.active ? () => history.push(item.url) : undefined + + return ( + + {t(urlToi18nKey[item.url])} + + ) + })} + + ) +} + +export default DefaultBreadcrumb diff --git a/src/components/breadcrumb/PatientBreadcrumb.tsx b/src/components/breadcrumb/PatientBreadcrumb.tsx new file mode 100644 index 0000000000..32c5c71d98 --- /dev/null +++ b/src/components/breadcrumb/PatientBreadcrumb.tsx @@ -0,0 +1,27 @@ +import React from 'react' +import { useHistory } from 'react-router' +import { useSelector } from 'react-redux' +import { useTranslation } from 'react-i18next' +import { + Breadcrumb as HrBreadcrumb, + BreadcrumbItem as HrBreadcrumbItem, +} from '@hospitalrun/components' +import { getPatientFullName } from 'patients/util/patient-name-util' +import { RootState } from '../../store' + +const PatientBreacrumb = () => { + const { t } = useTranslation() + const { patient } = useSelector((state: RootState) => state.patient) + const history = useHistory() + + return ( + + history.push('/patients')}> + {t('patients.label')} + + {getPatientFullName(patient)} + + ) +} + +export default PatientBreacrumb diff --git a/src/index.css b/src/index.css index 4dba66ac57..9ad2e9496a 100644 --- a/src/index.css +++ b/src/index.css @@ -94,9 +94,4 @@ code { position: relative; padding: .2rem 1rem; background-color: white; - border-bottom: .1rem solid lightgray; -} - -.breadcrumb-item > span, .breadcrumb-item > a { - text-transform: capitalize; }