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

VACMS - 19549 19800 conditional render services select, and loading indicator until CCP services loaded #33784

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
673714c
get specialties
eselkin Dec 26, 2024
048e1a7
naming
eselkin Dec 26, 2024
bc8c8ea
naming - moved
eselkin Dec 26, 2024
db2219b
naming - moved
eselkin Dec 26, 2024
211a77a
Merge branch 'VACMS-19548-orientation-of-map-controls-fl' into VACMS-…
eselkin Dec 26, 2024
4dbdd26
benefits services removal
eselkin Dec 27, 2024
c88802c
fix some tests
eselkin Dec 27, 2024
ce4af78
test that may or may not pass?
eselkin Dec 28, 2024
9c4395e
fix widths on va-button
eselkin Dec 30, 2024
d8ac30f
merge - really overwrite
eselkin Jan 2, 2025
66439ad
Merge branch 'VACMS-19548-orientation-of-map-controls-fl' into VACMS-…
eselkin Jan 2, 2025
75a376f
Merge branch 'VACMS-19548-orientation-of-map-controls-fl' into VACMS-…
eselkin Jan 2, 2025
a7faa7e
Merge branch 'VACMS-19548-orientation-of-map-controls-fl' into VACMS-…
eselkin Jan 3, 2025
d5a753b
Merge branch 'VACMS-19548-orientation-of-map-controls-fl' into VACMS-…
eselkin Jan 6, 2025
3c4ecc3
Merge branch 'VACMS-19549-conditional-render-services-select-fl' of g…
eselkin Jan 6, 2025
b27f770
Merge branch 'VACMS-19548-orientation-of-map-controls-fl' into VACMS-…
eselkin Jan 7, 2025
ebf36af
Merge branch 'VACMS-19548-orientation-of-map-controls-fl' into VACMS-…
eselkin Jan 7, 2025
5828dff
Merge branch 'VACMS-19548-orientation-of-map-controls-fl' into VACMS-…
eselkin Jan 7, 2025
708b463
Merge branch 'VACMS-19548-orientation-of-map-controls-fl' into VACMS-…
eselkin Jan 9, 2025
c81854e
Merge branch 'VACMS-19548-orientation-of-map-controls-fl' into VACMS-…
eselkin Jan 10, 2025
c23b706
Merge branch 'VACMS-19548-orientation-of-map-controls-fl' into VACMS-…
eselkin Jan 13, 2025
4588eb3
Merge branch 'VACMS-19548-orientation-of-map-controls-fl' into VACMS-…
eselkin Jan 14, 2025
5a730aa
Vacms 19801 - error message if ppms services get fails (fixed branch)…
eselkin Jan 16, 2025
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
7 changes: 6 additions & 1 deletion src/applications/facility-locator/api/LocatorApi.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,12 @@ class LocatorApi {
fetch(url, api.settings)
.then(res => res.json())
.then(
data => resolve(data.data.map(specialty => specialty.attributes)),
data => {
if (data.errors?.length) {
return reject(data.errors[0]);
}
return resolve(data.data.map(specialty => specialty.attributes));
},
error => reject(error),
);
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import React, { useEffect, useRef } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { setFocus } from '../utils/helpers';

function PpmsServiceError({ currentQuery }) {
const shownAlert = useRef(null);
useEffect(
() => {
if (
shownAlert.current &&
currentQuery?.fetchSvcsError &&
!shownAlert.current.hasAttribute('tabindex')
) {
setTimeout(() => {
// We need the timeout because the alert is rendered after the error is set
// and we need to wait for the alert to be rendered before setting focus
// Also, the required field (facilityType) steals focus immediately no matter how it is set
setFocus(shownAlert.current);
}, 50);
}
},
[shownAlert, currentQuery],
);
if (currentQuery?.fetchSvcsError) {
return (
<va-alert
status="error"
class="vads-u-margin-bottom--4"
id="fetch-ppms-services-error"
ref={shownAlert}
>
<h2 slot="headline">We’ve run into a problem</h2>
<p className="vads-u-margin-y--0">
Community provider searches aren’t working right now. Try again later.
</p>
</va-alert>
);
}
return null;
}

PpmsServiceError.propTypes = {
currentQuery: PropTypes.object,
};

const mapStateToProps = state => {
return {
currentQuery: state.searchQuery,
};
};

export default connect(mapStateToProps)(PpmsServiceError);
25 changes: 13 additions & 12 deletions src/applications/facility-locator/components/SearchControls.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
} from '@department-of-veterans-affairs/component-library/dist/react-bindings';
import {
healthServices,
benefitsServices,
urgentCareServices,
facilityTypesOptions,
emergencyCareServices,
Expand All @@ -18,6 +17,7 @@
import ServiceTypeAhead from './ServiceTypeAhead';
import { setFocus } from '../utils/helpers';
import { SearchControlsTypes } from '../types';
import ServicesLoadingOrShow from './ServicesLoadingOrShow';

const SearchControls = props => {
const {
Expand Down Expand Up @@ -56,6 +56,8 @@
onChange({
facilityType: e.target.value,
serviceType: null,
// Since the facility type may cause an error (PPMS), reset it if the type is changed
fetchSvcsError: null,
});
};

Expand Down Expand Up @@ -167,7 +169,7 @@
<span aria-live="assertive">Finding your location...</span>
</div>
) : (
<button

Check warning on line 172 in src/applications/facility-locator/components/SearchControls.jsx

View workflow job for this annotation

GitHub Actions / Linting (Files Changed)

src/applications/facility-locator/components/SearchControls.jsx:172:13:The <va-button> Web Component should be used instead of the button HTML element.
onClick={handleGeolocationButtonClick}
type="button"
className="use-my-location-link"
Expand Down Expand Up @@ -195,7 +197,7 @@
value={searchString}
/>
{searchString?.length > 0 && (
<button

Check warning on line 200 in src/applications/facility-locator/components/SearchControls.jsx

View workflow job for this annotation

GitHub Actions / Linting (Files Changed)

src/applications/facility-locator/components/SearchControls.jsx:200:13:The <va-button> Web Component should be used instead of the button HTML element.
aria-label="Clear your zip code or city, state"
type="button"
id="clear-input"
Expand Down Expand Up @@ -277,21 +279,20 @@
case LocationType.EMERGENCY_CARE:
services = emergencyCareServices;
break;
case LocationType.BENEFITS:
services = benefitsServices;
break;
case LocationType.CC_PROVIDER:
return (
<div id="service-typeahead-container" className="typeahead">
<ServiceTypeAhead
handleServiceTypeChange={handleServiceTypeChange}
initialSelectedServiceType={serviceType}
showError={showError}
/>
</div>
<ServicesLoadingOrShow serviceType="ppms_services">
<div id="service-typeahead-container" className="typeahead">
<ServiceTypeAhead
handleServiceTypeChange={handleServiceTypeChange}
initialSelectedServiceType={serviceType}
showError={showError}
/>
</div>
</ServicesLoadingOrShow>
);
default:
services = {};
return null;
}

// Create option elements for each VA service type.
Expand Down
30 changes: 20 additions & 10 deletions src/applications/facility-locator/components/ServiceTypeAhead.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import Downshift from 'downshift';
import classNames from 'classnames';
import { getProviderSpecialties } from '../actions';
import MessagePromptDiv from './MessagePromptDiv';

const MIN_SEARCH_CHARS = 2;
Expand All @@ -23,13 +22,21 @@ class ServiceTypeAhead extends Component {
this.getServices();
}

componentDidUpdate(prevProps) {
if (
!prevProps.currentQuery.specialties &&
this.state.services.length === 0
) {
this.getServices();
}
}

getServices = async () => {
const services = await this.props.getProviderSpecialties();
this.setState({
services,
services: this.props.currentQuery?.fetchSvcsRawData || [],
defaultSelectedItem:
this.props.initialSelectedServiceType &&
services.find(
this.props.currentQuery?.fetchSvcsRawData?.find(
({ specialtyCode }) =>
specialtyCode === this.props.initialSelectedServiceType,
),
Expand Down Expand Up @@ -67,7 +74,7 @@ class ServiceTypeAhead extends Component {
};

matchingServices = inputValue => {
if (inputValue) {
if (inputValue && this.state.services?.length) {
return this.state.services.filter(specialty =>
this.shouldShow(inputValue, specialty),
);
Expand Down Expand Up @@ -134,7 +141,10 @@ class ServiceTypeAhead extends Component {
};

render() {
const { defaultSelectedItem } = this.state;
const { defaultSelectedItem, services } = this.state;
if (!services) {
return null;
}
const { showError, currentQuery, handleServiceTypeChange } = this.props;
return (
<Downshift
Expand Down Expand Up @@ -214,14 +224,14 @@ class ServiceTypeAhead extends Component {
}

ServiceTypeAhead.propTypes = {
getProviderSpecialties: PropTypes.func.isRequired,
initialSelectedServiceType: PropTypes.string,
handleServiceTypeChange: PropTypes.func.isRequired,
onBlur: PropTypes.func,
currentQuery: PropTypes.object,
initialSelectedServiceType: PropTypes.string,
showError: PropTypes.bool,
onBlur: PropTypes.func,
};

const mapDispatch = { getProviderSpecialties };
const mapDispatch = {};

const mapStateToProps = state => {
return {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import React, { useEffect, useState } from 'react';
import { VaLoadingIndicator } from '@department-of-veterans-affairs/component-library/dist/react-bindings';
import { getProviderSpecialties } from '../actions';

function ServicesLoadingOrShow({
children,
serviceType = '',
currentQuery,
...dispatchProps
}) {
const [loadingMessage, setLoadingMessage] = useState('Loading services');
useEffect(
() => {
let ignore = false;
if (ignore) return;
switch (serviceType) {
case 'ppms_services':
setLoadingMessage('Loading community provider services');
dispatchProps.getProviderSpecialties();
break;
case 'vamc_services':
setLoadingMessage('Loading VAMC services');
break;
default:
break;
}
// eslint-disable-next-line consistent-return

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ESLint disabled here

return () => {
ignore = true;
};
},
// eslint-disable-next-line react-hooks/exhaustive-deps

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ESLint disabled here

[serviceType],
);
if (currentQuery.fetchSvcsInProgress) {
return (
<VaLoadingIndicator
id="service-type-loading"
message={loadingMessage}
className="vads-u-margin-bottom--2"
/>
);
}
return children;
}

ServicesLoadingOrShow.propTypes = {
children: PropTypes.node,
currentQuery: PropTypes.object,
serviceType: PropTypes.string,
};

const mapDispatch = { getProviderSpecialties };

const mapStateToProps = state => {
return {
currentQuery: state.searchQuery,
};
};

export default connect(
mapStateToProps,
mapDispatch,
)(ServicesLoadingOrShow);
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
import Alert from '../components/Alert';
import ControlResultsHolder from '../components/ControlResultsHolder';
import EmergencyCareAlert from '../components/EmergencyCareAlert';
import PpmsServiceError from '../components/PpmsServiceError';

let lastZoom = 3;

Expand Down Expand Up @@ -447,12 +448,14 @@
description="We’re sorry. Searches for non-VA facilities such as community providers and urgent care are currently unavailable. We’re working to fix this. Please check back soon."
/>
)}
{/* a HOC that either returns a div or fragment */}
{/* Listens for the query error. If present it displays */}
<PpmsServiceError />
<div
className={
isSmallDesktop ? 'desktop-vertical-and-map-holder' : undefined
}
>
{/* a HOC that either returns a div or fragment */}
<ControlResultsHolder isSmallDesktop={isSmallDesktop}>
<SearchControls
geolocateUser={props.geolocateUser}
Expand Down Expand Up @@ -656,7 +659,7 @@
}
return () => {
if (mapboxContainerRef.current) {
resizeObserver.unobserve(mapboxContainerRef.current);

Check warning on line 662 in src/applications/facility-locator/containers/FacilitiesMap.jsx

View workflow job for this annotation

GitHub Actions / Linting (Files Changed)

src/applications/facility-locator/containers/FacilitiesMap.jsx:662:55:The ref value 'mapboxContainerRef.current' will likely have changed by the time this effect cleanup function runs. If this ref points to a node rendered by React, copy 'mapboxContainerRef.current' to a variable inside the effect, and use that variable in the cleanup function.
}
};
},
Expand Down Expand Up @@ -692,7 +695,7 @@
() => {
searchCurrentArea();
},
[props.currentQuery.searchArea],

Check warning on line 698 in src/applications/facility-locator/containers/FacilitiesMap.jsx

View workflow job for this annotation

GitHub Actions / Linting (Files Changed)

src/applications/facility-locator/containers/FacilitiesMap.jsx:698:5:React Hook useEffect has a missing dependency: 'searchCurrentArea'. Either include it or remove the dependency array.
);

useEffect(() => {
Expand All @@ -705,7 +708,7 @@
() => {
handleSearchOnQueryChange();
},
[props.currentQuery.id],

Check warning on line 711 in src/applications/facility-locator/containers/FacilitiesMap.jsx

View workflow job for this annotation

GitHub Actions / Linting (Files Changed)

src/applications/facility-locator/containers/FacilitiesMap.jsx:711:5:React Hook useEffect has a missing dependency: 'handleSearchOnQueryChange'. Either include it or remove the dependency array.
);

useEffect(
Expand All @@ -713,7 +716,7 @@
if (!map) return;
renderMarkers(props.results);
},
[props.results, map],

Check warning on line 719 in src/applications/facility-locator/containers/FacilitiesMap.jsx

View workflow job for this annotation

GitHub Actions / Linting (Files Changed)

src/applications/facility-locator/containers/FacilitiesMap.jsx:719:5:React Hook useEffect has a missing dependency: 'renderMarkers'. Either include it or remove the dependency array.
);

useEffect(
Expand All @@ -729,7 +732,7 @@
() => {
handleMapOnNoResultsFound();
},
[props.currentQuery.searchCoords, props.results],

Check warning on line 735 in src/applications/facility-locator/containers/FacilitiesMap.jsx

View workflow job for this annotation

GitHub Actions / Linting (Files Changed)

src/applications/facility-locator/containers/FacilitiesMap.jsx:735:5:React Hook useEffect has a missing dependency: 'handleMapOnNoResultsFound'. Either include it or remove the dependency array.
);

useEffect(
Expand Down
11 changes: 8 additions & 3 deletions src/applications/facility-locator/reducers/searchQuery.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,14 +95,17 @@ export const SearchQueryReducer = (state = INITIAL_STATE, action) => {
case FETCH_SPECIALTIES:
return {
...state,
error: false,
fetchSvcsError: null,
fetchSvcsInProgress: true,
specialties: {},
fetchSvcsRawData: [],
};
case FETCH_SPECIALTIES_DONE:
return {
...state,
error: false,
fetchSvcsError: null,
fetchSvcsInProgress: false,
fetchSvcsRawData: action.data,
specialties: action.data
? action.data.reduce((acc, cur) => {
acc[cur.specialtyCode] = cur.name;
Expand All @@ -113,8 +116,10 @@ export const SearchQueryReducer = (state = INITIAL_STATE, action) => {
case FETCH_SPECIALTIES_FAILED:
return {
...state,
error: true,
fetchSvcsInProgress: false,
fetchSvcsError: action.error || true,
facilityType: '', // resets facility type to the Choose a facility
isValid: true,
};
case SEARCH_FAILED:
return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ $facility-locator-shadow: rgba(0, 0, 0, 0.5);

#search-results-title {
width: calc(100% - 8px);
margin-left: 4px;
margin: 4px;
}

.map-and-message-container {
Expand Down Expand Up @@ -329,7 +329,6 @@ $facility-locator-shadow: rgba(0, 0, 0, 0.5);

#facility-search {
margin-top: 24 px;

@media screen and (min-width: $small-desktop-screen) {
width: 100%;
max-width: none;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
cy.axeCheck();
});

it('shows error message in location field on invalid search', () => {

Check warning on line 24 in src/applications/facility-locator/tests/e2e/errorMessages.cypress.spec.js

View workflow job for this annotation

GitHub Actions / Linting (Files Changed)

src/applications/facility-locator/tests/e2e/errorMessages.cypress.spec.js:24:3:Cypress E2E tests must include at least one axeCheck call. Documentation for adding checks and understanding errors can be found here: https://depo-platform-documentation.scrollhelp.site/developer-docs/A11y-Testing.1935409178.html
cy.get('#facility-search').click({ waitForAnimations: true });
cy.get('.usa-input-error-message').contains(
'Please fill in a zip code or city, state.',
Expand All @@ -29,7 +29,7 @@
cy.get('#street-city-state-zip').should('be.focused');
});

it('shows error message on leaving location field empty', () => {

Check warning on line 32 in src/applications/facility-locator/tests/e2e/errorMessages.cypress.spec.js

View workflow job for this annotation

GitHub Actions / Linting (Files Changed)

src/applications/facility-locator/tests/e2e/errorMessages.cypress.spec.js:32:3:Cypress E2E tests must include at least one axeCheck call. Documentation for adding checks and understanding errors can be found here: https://depo-platform-documentation.scrollhelp.site/developer-docs/A11y-Testing.1935409178.html
cy.get('#street-city-state-zip').focus();
cy.get('#facility-type-dropdown')
.shadow()
Expand All @@ -42,7 +42,7 @@
cy.get('.usa-input-error-message').should('not.exist');
});

it('shows error message when leaving facility type field empty', () => {

Check warning on line 45 in src/applications/facility-locator/tests/e2e/errorMessages.cypress.spec.js

View workflow job for this annotation

GitHub Actions / Linting (Files Changed)

src/applications/facility-locator/tests/e2e/errorMessages.cypress.spec.js:45:3:Cypress E2E tests must include at least one axeCheck call. Documentation for adding checks and understanding errors can be found here: https://depo-platform-documentation.scrollhelp.site/developer-docs/A11y-Testing.1935409178.html
cy.get('#street-city-state-zip').type('Austin, TX');
cy.get('#facility-type-dropdown')
.shadow()
Expand Down Expand Up @@ -130,4 +130,17 @@
);
cy.get('#service-type-ahead-input').should('be.empty');
});
it('shows error message for Community Providers when 500 error is returned', () => {
cy.intercept('GET', '/facilities_api/v2/ccp/specialties', {
statusCode: 500,
error: 'Internal Server Error',
}).as('mockServices');
cy.get('#street-city-state-zip').type('Austin, TX');
cy.get('#facility-type-dropdown')
.shadow()
.find('select')
.select('Community providers (in VA’s network)');
cy.get('#fetch-ppms-services-error').should('exist');
cy.get('#fetch-ppms-services-error').focused();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -63,29 +63,23 @@ Cypress.Commands.add('verifyOptions', () => {
.shadow()
.find('select')
.select('Vet Centers');
cy.get('.service-type-dropdown-container')
.find('select')
.should('not.have', 'disabled');

cy.get('#facility-type-dropdown')
.shadow()
.find('select')
.select('VA cemeteries');
cy.get('.service-type-dropdown-container')
.find('select')
.should('not.have', 'disabled');

cy.get('#facility-type-dropdown')
.shadow()
.find('select')
.select('VA benefits');
cy.get('.service-type-dropdown-container') // remember to remove when we allow selection again for VA Benefits
.find('select')
.should('have.attr', 'disabled');

// CCP care have services available
cy.get('#facility-type-dropdown')
.shadow()
.find('select')
.select('Community providers (in VA’s network)');
cy.get('#service-type-loading').should('exist');
cy.get('#service-typeahead').should('not.have.attr', 'disabled');

// CCP pharmacies dont have services available
Expand Down
Loading
Loading