Skip to content

Commit

Permalink
[Feature] #4415 Activities Overview for MMIS (#4445)
Browse files Browse the repository at this point in the history
* WIP activities overview for MMIS

* create overview wrapper component

* scope StandardsAndConditions to HITECH type

* use Controller component for MMIS fields

* split NameAndFundingSourceForm into two components

* remove unused NameAndFundingSourceReview component and associated directory

* udpate ActivityRoutes snapshot test since StandardsAndConditions was moved into the Overview component

* resolving test warnings, add test

* cleanup, add tests

* cypress testing

* move test location after rebase

* test update after display text change

* capitalization changes for display text

* adding comment for extra clarity

* removing useMemos
  • Loading branch information
amyd11 committed Jan 18, 2023
1 parent f97cc98 commit 11796ab
Show file tree
Hide file tree
Showing 26 changed files with 1,021 additions and 589 deletions.
2 changes: 1 addition & 1 deletion api/seeds/development/03-mmis.js
Expand Up @@ -125,7 +125,7 @@ const mmis = {
activityId: '152a1e2b',
name: 'Activity 1',
activityOverview: {
activitySnapshot: 'This is an snapshot',
activitySnapshot: 'This is a snapshot',
problemStatement: 'This is a problem statement',
proposedSolution: 'This is a proposed solution'
},
Expand Down
6 changes: 3 additions & 3 deletions api/util/adminCheck.test.js
Expand Up @@ -2159,7 +2159,7 @@ const expectedErrorsMockMmisApdRandom = [
{
section: 'Activity 1 Activity Overview',
link: '/apd/632a0fbc5665670a34b3bbd7/activity/0/overview',
fieldDescription: 'Provide an Activity Snapshot'
fieldDescription: 'Provide an Activity snapshot'
},
{
section: 'Activity 1 Conditions for Enhanced Funding',
Expand Down Expand Up @@ -2617,12 +2617,12 @@ tap.test('MMIS apd document admin check', async mmisValidationTests => {
{
section: 'Activity 1 Activity Overview',
link: '/apd/632a0fbc5665670a34b3bbd7/activity/0/overview',
fieldDescription: 'Provide an Activity Name'
fieldDescription: 'Provide an Activity name'
},
{
section: 'Activity 1 Activity Overview',
link: '/apd/632a0fbc5665670a34b3bbd7/activity/0/overview',
fieldDescription: 'Provide a Problem Statement'
fieldDescription: 'Provide a Problem statement'
},
{
section: 'Activity 1 Conditions for Enhanced Funding',
Expand Down
19 changes: 10 additions & 9 deletions common/schemas/activityOverview.js
Expand Up @@ -32,6 +32,7 @@ export const standardsAndConditionsSchema = Joi.object({
})
});

// No Standards and Conditions (SC)
export const hitechActivityOverviewNoSCSchema = Joi.object({
summary: activitySummarySchema,
description: activityDescriptionSchema,
Expand All @@ -46,21 +47,21 @@ export const hitechActivityOverviewSchema = Joi.object({
});

export const activitySnapshotSchema = Joi.string().required().messages({
'string.base': 'Provide an Activity Snapshot',
'string.empty': 'Provide an Activity Snapshot',
'string.required': 'Provide an Activity Snapshot'
'string.base': 'Provide an Activity snapshot',
'string.empty': 'Provide an Activity snapshot',
'string.required': 'Provide an Activity snapshot'
});

export const problemStatementSchema = Joi.string().required().messages({
'string.base': 'Provide a Problem Statement',
'string.empty': 'Provide a Problem Statement',
'string.required': 'Provide a Problem Statement'
'string.base': 'Provide a Problem statement',
'string.empty': 'Provide a Problem statement',
'string.required': 'Provide a Problem statement'
});

export const proposedSolutionSchema = Joi.string().required().messages({
'string.base': 'Provide a Proposed Solution',
'string.empty': 'Provide a Proposed Solution',
'string.required': 'Provide a Proposed Solution'
'string.base': 'Provide a Proposed solution',
'string.empty': 'Provide a Proposed solution',
'string.required': 'Provide a Proposed solution'
});

export const mmisActivityOverviewSchema = Joi.object({
Expand Down
4 changes: 2 additions & 2 deletions common/schemas/nameAndFundingSource.js
@@ -1,8 +1,8 @@
import Joi from 'joi';

export const activityNameSchema = Joi.string().required().messages({
'string.base': 'Provide an Activity Name',
'string.empty': 'Provide an Activity Name'
'string.base': 'Provide an Activity name',
'string.empty': 'Provide an Activity name'
});

export const activityFundingSourceSchema = Joi.string().required().messages({
Expand Down
120 changes: 118 additions & 2 deletions e2e/cypress/integration/05-mmis/01-mmis-basics.cy.js
Expand Up @@ -8,14 +8,13 @@ import ActivityPage from '../../page-objects/activity-page';
/* eslint-disable prefer-arrow-callback */

describe('MMIS Basics', { tags: ['@apd', '@default', '@mmis'] }, () => {
let activityPage;
let apdUrl;
let apdId;
const years = [];
let activityPage;

before(() => {
activityPage = new ActivityPage();

cy.useStateStaff();
cy.updateFeatureFlags({ enableMmis: true, adminCheckFlag: true });
cy.reload();
Expand Down Expand Up @@ -159,6 +158,123 @@ describe('MMIS Basics', { tags: ['@apd', '@default', '@mmis'] }, () => {
});
});
describe('MMIS Pages', () => {
it('tests Activity Overview page', () => {
cy.goToActivityDashboard();

// Create new Activity
cy.contains('Add Activity').click();
cy.contains('Activity 1').should('exist');

// Check Activity Overview
cy.goToActivityOverview(0);
cy.url().should('contain', '/activity/0/overview');
cy.findAllByRole('heading', { name: /Activity Overview/i }).should(
'exist'
);

// Defaults to empty field values
cy.contains('Activity name').should('exist');
activityPage.checkTinyMCE('activity-name-field', '');
cy.contains('Activity snapshot').should('exist');
activityPage.checkTinyMCE('activity-snapshot-field', '');
cy.contains('Problem statement').should('exist');
activityPage.checkTinyMCE('activity-problem-statement-field', '');
cy.contains('Proposed solution').should('exist');
activityPage.checkTinyMCE('activity-proposed-solution-field', '');

// Check validation errors
cy.turnOnAdminCheck();

// Validation errors within admin check list
cy.get('[class="eapd-admin-check-list"]').within(list => {
cy.get(list).contains('Activity 1 Activity Overview').should('exist');
cy.get(list).contains('Provide an Activity name').should('exist');
cy.get(list).contains('Provide an Activity snapshot').should('exist');
cy.get(list).contains('Provide a Problem statement').should('exist');
cy.get(list).contains('Provide a Proposed solution').should('exist');
});

// Verifies Activity Overview exists in the admin check list and navigates to that page
cy.checkAdminCheckHyperlinks(
'Activity 1 Activity Overview',
'Activity Overview',
3
);

// Validation errors on the Activity Overview page
cy.contains('Provide an Activity name').should('exist');
cy.get('[data-cy="validationError"]')
.contains('Provide an Activity snapshot')
.should('exist');
cy.get('[data-cy="validationError"]')
.contains('Provide a Problem statement')
.should('exist');
cy.get('[data-cy="validationError"]')
.contains('Provide a Proposed solution')
.should('exist');

cy.collapseAdminCheck();

// Fill out fields and check that each validation error is cleared
cy.get(`[data-cy="activity-name"]`).click().type('The Coolest Activity');
cy.waitForSave();
cy.contains('Provide an Activity name').should('not.exist');

cy.setTinyMceContent(
'activity-snapshot-field',
'This is an activity snapshot.'
);
cy.waitForSave();
cy.contains('Provide an Activity snapshot').should('not.exist');

cy.setTinyMceContent(
'activity-problem-statement-field',
'This is a problem statement.'
);
cy.waitForSave();
cy.contains('Provide a Problem statement').should('not.exist');

cy.setTinyMceContent(
'activity-proposed-solution-field',
'This is a proposed solution.'
);
cy.waitForSave();
cy.contains('Provide a Proposed solution').should('not.exist');

// Check validation errors are gone from admin check
cy.expandAdminCheck();
cy.get('[class="eapd-admin-check-list"]').within(list => {
cy.get(list)
.contains('Activity 1 Activity Overview')
.should('not.exist');
});
cy.turnOffAdminCheck();

// Verify that fields save
// Navigates away from page and back to check persistence of entered data
cy.goToApdOverview();
cy.wait(2000);
cy.goToActivityOverview(0);

cy.contains('Activity name').should('exist');
activityPage.checkTinyMCE('activity-name-field', 'The Coolest Activity');
cy.contains('Activity snapshot').should('exist');
activityPage.checkTinyMCE(
'activity-snapshot-field',
'<p>This is an activity snapshot.</p>'
);
cy.contains('Problem statement').should('exist');
activityPage.checkTinyMCE(
'activity-problem-statement-field',
'<p>This is a problem statement.</p>'
);
cy.contains('Proposed solution').should('exist');
activityPage.checkTinyMCE(
'activity-proposed-solution-field',
'<p>This is a proposed solution.</p>'
);
});

it('tests the Security Planning page', () => {
cy.turnOnAdminCheck();
cy.checkAdminCheckHyperlinks('Security Planning', 'Security Planning', 2);
Expand Down
18 changes: 18 additions & 0 deletions e2e/cypress/support/commands.js
Expand Up @@ -653,6 +653,24 @@ Cypress.Commands.add('turnOnAdminCheck', () => {
});
});

Cypress.Commands.add('turnOffAdminCheck', () => {
cy.findByRole('button', { name: /Stop Administrative Check/i }).click({
force: true
});
});

Cypress.Commands.add('collapseAdminCheck', () => {
cy.findByRole('button', { name: /Collapse/i }).click({
force: true
});
});

Cypress.Commands.add('expandAdminCheck', () => {
cy.findByRole('button', { name: /Expand/i }).click({
force: true
});
});

Cypress.Commands.add('checkAdminCheckHyperlinks', (link, heading, level) => {
cy.get('[class="eapd-admin-check-list"]').within(() => {
cy.contains(link).click();
Expand Down
20 changes: 20 additions & 0 deletions web/src/i18n/locales/en/activities/comprehensiveOverview.yaml
@@ -0,0 +1,20 @@
title: Comprehensive Activity Overview

problemStatementInput:
label: Problem statement
helpText: >-
Example: Explain what problem you are trying to solve with this particular
activity.
Example: Highlight how this problem has been addressed in the past.
detail: >-
Provide a comprehensive explanation of the problem the state is faced with.
Make sure to expain its significance and scope.
proposedSolutionInput:
label: Proposed solution
detail: >-
Explain your proposed solution and how it addresses the state’s problem.
Additionally, justifiy how enhanced funding will support the proposed
solution.
5 changes: 3 additions & 2 deletions web/src/i18n/locales/en/activities/index.js
@@ -1,6 +1,6 @@
import base from './activities.yaml';

import alternativesAndRisks from './alternativesAndRisks.yaml';
import base from './activities.yaml';
import comprehensiveOverview from './comprehensiveOverview.yaml';
import contractorResources from './contractorResources.yaml';
import conditions from './conditions.yaml';
import costAllocate from './costAllocate.yaml';
Expand All @@ -16,6 +16,7 @@ import statePersonnel from './statePersonnel.yaml';
export default {
...base,
alternativesAndRisks,
comprehensiveOverview,
contractorResources,
conditions,
costAllocate,
Expand Down
39 changes: 26 additions & 13 deletions web/src/i18n/locales/en/activities/overview.yaml
Expand Up @@ -9,26 +9,39 @@ activityDescriptionInput:
label: Include as much detail as is necessary to explain the activity.
hie:
hint: >-
Detail linkage to meaningful use, including value proposition to providers,
and a sustainability plan describing how the activity will be sustained after
HIE DDI funding is exhausted.
Detail linkage to meaningful use, including value proposition to
providers, and a sustainability plan describing how the activity will be
sustained after HIE DDI funding is exhausted.
helpText: >-
Diagrams and/or descriptions of other HIE linkages can be included, if relevant.
Diagrams and/or descriptions of other HIE linkages can be included, if
relevant.
activityAlternativesInput:
label: Statement of alternative considerations and supporting justification
helpText: >-
Example: Explain and justify the decision to build a custom software solution instead of using a COTS or SaaS option.
Example: Explain and justify the decision to build a custom software
solution instead of using a COTS or SaaS option.
Example: Explain and justify the decision to conduct a contract modification instead of doing a fully competitive procurement.
Example: Explain and justify the decision to conduct a contract modification
instead of doing a fully competitive procurement.
detail: >-
Provide a brief description of each alternative considered for the implementation of this activity.
The description should include considerations for costs/benefits/risks for each option. Indicate
which option was finally selected and why the final selection was made. As applicable,
include how the selected approach supports the economic and efficient management of the Medicaid program.
Provide a brief description of each alternative considered for the
implementation of this activity. The description should include
considerations for costs/benefits/risks for each option. Indicate which
option was finally selected and why the final selection was made. As
applicable, include how the selected approach supports the economic and
efficient management of the Medicaid program.
If this is a previously approved activity, only use this section to discuss alternative considerations made
for substantive changes / decisions made during the implementation of the project.
Do NOT include the alternative analysis conducted when justifying the original activity.
If this is a previously approved activity, only use this section to discuss
alternative considerations made for substantive changes / decisions made
during the implementation of the project. Do NOT include the alternative
analysis conducted when justifying the original activity.
activitySnapshotInput:
label: Activity snapshot
detail: >-
Provide a brief and high-level snapshot of the activity. You can speak on
the purpose of your activity, its benefits, and any additional information
that would give a reviewer a quick understanding of your activity.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 0 additions & 2 deletions web/src/pages/apd/activities/activityRoutesList.js
Expand Up @@ -9,7 +9,6 @@ import Costs from './state-costs/Costs';
import Milestones from './schedule-and-milestones/Milestones';
import Overview from './overview/Overview';
import Outcomes from './oms/Outcomes';
import StandardsAndConditions from './overview/StandardsAndConditions';
import Schedule from './schedule-and-milestones/Schedule';
import { Section } from '../../../components/Section';

Expand Down Expand Up @@ -116,7 +115,6 @@ const routes = activityIndex => [
children: (
<Section>
<Overview activityIndex={activityIndex} />
<StandardsAndConditions activityIndex={activityIndex} />
</Section>
),
isPublic: false,
Expand Down

0 comments on commit 11796ab

Please sign in to comment.