Repository: Enterprise-CMCS/macpro-mdct-mcr
Pull request #12102: Render NAAAR standards in a table in export view
Author: login: gmrabian
Labels: name: ready for review
description: Ready for all the reviews!
Created: 2025-04-04T15:34:21Z | Merged: 2025-04-09T16:38:24Z

### Description
<!-- Detailed description of changes and related context -->
I added comments to explain the key code changes. If you review by commit you'll see the core changes in the first commit, and some type updates and tests in the rest.

- Add custom branch to render standards page using modal overlay export page type (it's the closest to what we wanted)
  - The standards page is a `drawer` page type. What do you think of this? Is it ok to render another page type for the export page logically? I thought it would save us lines of code and be more reusable. Perhaps our export types don't perfectly align with our form page types?
- Add NAAAR standards logic to `ModalOverlay` export page
- Add display name to screen reader header type and rename
- Add tests

### Related ticket(s)
<!-- Link to related ticket(s) or issue(s) -->
<!-- Hint: Type CMDCT-<ticket-number> for autolinking -->
CMDCT-4464

---
### How to test
<!-- Step-by-step instructions on how to test, if necessary -->
Open the [deployed environment](https://d6dq6fzvsiy1v.cloudfront.net/)
- Log in as `stateuser@test.com`
- Go to the existing NAAAR titled "standards"
- Go to the PDF view (Review & Submit -> Review PDF)
- View the standards table and verify it matches the [Figma](https://www.figma.com/design/QcoVlfZ2adUBnzcHiI0G0S/MDCT-MCR-NAAAR?node-id=1356-27373&t=3x4VxLNRMgWtuzKX-4)
- Verify the counter indicates the correct number of standards
- Leave this report and open the NAAAR report titled "no standards"
- Go to the PDF view (Review & Submit -> Review PDF)
- Verify the table indicates its empty, and the counter is set to 0 with a custom message

Bonus:
- Turn on VoiceOver and verify the `Pop.` column header reads out "Population" 

### Notes
<!-- Changed dependencies, .env files, configs, etc. -->
<!-- Instructions for local dev, e.g. requires new installs in directories -->
I'm not sure what the best way is to color this text mid-line so I left it all black.
Figma:
![Screenshot 2025-04-04 at 11 27 43 AM](https://github.com/user-attachments/assets/b6811903-e121-410c-8ef3-5f412afd11dc)
Deployed instance:
![Screenshot 2025-04-04 at 11 56 21 AM](https://github.com/user-attachments/assets/c66e5f2b-3f60-403c-9792-7e030d0136ce)

---
### Pre-review checklist
<!-- Complete the following steps before opening for review -->
- [x] I have added [thorough](https://shorturl.at/aejkF) tests, if necessary
- [ ] I have updated relevant documentation, if necessary
- [x] I have performed a self-review of my code
- [x] I have manually tested this PR in the deployed cloud environment

---
### Pre-merge checklist
<!-- Complete the following steps before merging -->

#### Review
- [x] Design: This work has been reviewed and approved by design, if necessary
- [x] Product: This work has been reviewed and approved by product owner, if necessary
---


Diff:
diff --git a/services/app-api/utils/types/other.ts b/services/app-api/utils/types/other.ts
index 9669d083c..5aa20098f 100644
--- a/services/app-api/utils/types/other.ts
+++ b/services/app-api/utils/types/other.ts
@@ -128,8 +128,9 @@ export enum PageTypes {
   REVIEW_SUBMIT = "reviewSubmit",
 }
 
-export interface ScreenReaderOnlyHeaderName {
+export interface ScreenReaderCustomHeaderName {
   hiddenName: string;
+  name?: string;
 }
 
 export interface SortableHeadRow {
@@ -145,7 +146,7 @@ export interface SortableHeadRow {
 
 export interface TableContentShape {
   caption?: string;
-  headRow?: Array<string | ScreenReaderOnlyHeaderName>;
+  headRow?: Array<string | ScreenReaderCustomHeaderName>;
   sortableHeadRow?: SortableHeadRow;
   bodyRows?: string[][];
 }
diff --git a/services/app-api/utils/types/reports.ts b/services/app-api/utils/types/reports.ts
index 1856e530e..59466ed4a 100644
--- a/services/app-api/utils/types/reports.ts
+++ b/services/app-api/utils/types/reports.ts
@@ -6,7 +6,7 @@ import {
   State,
   CompletionData,
   EntityType,
-  ScreenReaderOnlyHeaderName,
+  ScreenReaderCustomHeaderName,
   TableContentShape,
 } from "./index";
 
@@ -96,7 +96,7 @@ export interface EntityDetailsMultiformShape {
   table?: {
     caption: string;
     bodyRows: string[][];
-    headRow: Array<string | ScreenReaderOnlyHeaderName>;
+    headRow: Array<string | ScreenReaderCustomHeaderName>;
   };
   verbiage?: EntityDetailsMultiformVerbiage;
 }
diff --git a/services/ui-src/src/components/export/ExportedModalOverlayReportSection.test.tsx b/services/ui-src/src/components/export/ExportedModalOverlayReportSection.test.tsx
index c4286cb6b..aa8373262 100644
--- a/services/ui-src/src/components/export/ExportedModalOverlayReportSection.test.tsx
+++ b/services/ui-src/src/components/export/ExportedModalOverlayReportSection.test.tsx
@@ -1,16 +1,17 @@
-import { render, screen } from "@testing-library/react";
+import { render, screen, within } from "@testing-library/react";
 // components
 import {
   ExportedModalOverlayReportSection,
   renderModalOverlayTableBody,
 } from "./ExportedModalOverlayReportSection";
 // types
-import { ModalOverlayReportPageShape, ReportType } from "types";
+import { EntityType, ModalOverlayReportPageShape, ReportType } from "types";
 // utils
 import {
   mockMlrReportContext,
   mockMlrReportStore,
   mockModalOverlayReportPageJson,
+  mockNaaarReportStore,
 } from "utils/testing/setupJest";
 import { useStore } from "utils";
 import { testA11y } from "utils/testing/commonTests";
@@ -22,9 +23,6 @@ const mockReportContextOther = Object.assign({}, mockReportContext);
 
 jest.mock("utils/state/useStore");
 const mockedUseStore = useStore as jest.MockedFunction<typeof useStore>;
-mockedUseStore.mockReturnValue({
-  ...mockMlrReportStore,
-});
 
 const exportedModalOverlayReportSectionComponent = (
   <ExportedModalOverlayReportSection
@@ -32,6 +30,15 @@ const exportedModalOverlayReportSectionComponent = (
   />
 );
 
+const exportedNaaarStandardsComponent = (
+  <ExportedModalOverlayReportSection
+    section={{
+      ...(mockModalOverlayReportPageJson as ModalOverlayReportPageShape),
+      entityType: EntityType.STANDARDS,
+    }}
+  />
+);
+
 const mlrTableHeader = Object.values(
   mlrVerbiage.modalOverlayTableHeaders as Record<string, string>
 );
@@ -130,6 +137,12 @@ const exportedModalOverlayReportSectionComponentOther = (
 );
 
 describe("<ExportedModalOverlayReportSection />", () => {
+  beforeEach(() => {
+    jest.clearAllMocks();
+    mockedUseStore.mockReturnValue({
+      ...mockMlrReportStore,
+    });
+  });
   test("ExportedModalOverlayReportSection renders", () => {
     const { getByTestId } = render(exportedModalOverlayReportSectionComponent);
     const section = getByTestId("exportTable");
@@ -137,10 +150,7 @@ describe("<ExportedModalOverlayReportSection />", () => {
   });
 
   describe("Test renderModalOverlayTableBody", () => {
-    beforeEach(() => {
-      jest.clearAllMocks();
-    });
-    test("Should render data correctly", async () => {
+    test("Should render data correctly for mlr", async () => {
       mockReportContext.report.fieldData.program = [mockMlrProgram];
       const { container, findByText } = render(
         exportedModalOverlayReportSectionComponent
@@ -196,6 +206,73 @@ describe("<ExportedModalOverlayReportSection />", () => {
       expect(await findByText(mockMlrProgram.report_miscellaneousNotes));
     });
 
+    test("Should render data correctly for naaar", async () => {
+      mockedUseStore.mockReturnValue(mockNaaarReportStore);
+      render(exportedNaaarStandardsComponent);
+
+      // All table headers are present
+      expect(screen.getAllByRole("columnheader").length).toBe(7);
+
+      // Every entity has a row (+1 for header)
+      const tbody = screen.getAllByRole("rowgroup")[1];
+      const rows = within(tbody).getAllByRole("row");
+      expect(rows.length).toBe(
+        mockNaaarReportStore.report?.fieldData.standards.length
+      );
+
+      // index
+      expect(screen.getByRole("gridcell", { name: "1" })).toBeVisible();
+
+      // provider type
+      expect(
+        screen.getByRole("gridcell", {
+          name: "Primary Care",
+        })
+      ).toBeVisible();
+
+      // standard type
+      expect(
+        screen.getByRole("gridcell", { name: "Appointment wait time" })
+      ).toBeVisible();
+
+      // description
+      expect(
+        screen.getByRole("gridcell", { name: "standard description" })
+      ).toBeVisible();
+
+      // analysis methods, joined with a comma
+      expect(
+        screen.getByRole("gridcell", {
+          name: "Geomapping, Plan Provider Directory Review",
+        })
+      ).toBeVisible();
+
+      // population
+      expect(screen.getByRole("gridcell", { name: "Pediatric" })).toBeVisible();
+
+      // region
+      expect(screen.getByRole("gridcell", { name: "Metro" })).toBeVisible();
+    });
+
+    test("Should render message for naaar with no standards", async () => {
+      const mockEmptyStandardsNaaarStore = {
+        ...mockNaaarReportStore,
+      };
+      mockEmptyStandardsNaaarStore.report!.fieldData.standards = undefined;
+      mockedUseStore.mockReturnValue(mockEmptyStandardsNaaarStore);
+      render(exportedNaaarStandardsComponent);
+
+      // No table renders
+      expect(screen.queryByRole("table")).toBeNull();
+
+      // region
+      expect(
+        screen.getByTestId("exportedModalOverlayReportSection")
+      ).toHaveTextContent(
+        "Standard total count: 0 - No access standards entered"
+      );
+    });
+
     test('Should render "other" explanations if they are filled.', async () => {
       mockReportContextOther.report.fieldData.program = [mockMlrProgramOther];
 
diff --git a/services/ui-src/src/components/export/ExportedModalOverlayReportSection.tsx b/services/ui-src/src/components/export/ExportedModalOverlayReportSection.tsx
index 816ea18b2..f9854d8e1 100644
--- a/services/ui-src/src/components/export/ExportedModalOverlayReportSection.tsx
+++ b/services/ui-src/src/components/export/ExportedModalOverlayReportSection.tsx
@@ -2,12 +2,19 @@
 import { EntityStatusIcon, Table } from "components";
 import { Box, Image, Td, Text, Tr } from "@chakra-ui/react";
 // types
-import { EntityShape, ModalOverlayReportPageShape, ReportType } from "types";
+import {
+  EntityShape,
+  EntityType,
+  ModalOverlayReportPageShape,
+  ReportType,
+} from "types";
+import { NaaarStandardsTableShape } from "components/tables/SortableNaaarStandardsTable";
 // utils
-import { assertExhaustive, getEntityDetailsMLR, useStore } from "utils";
+import { getEntityDetailsMLR, mapNaaarStandardsData, useStore } from "utils";
 // verbiage
 import mcparVerbiage from "verbiage/pages/mcpar/mcpar-export";
 import mlrVerbiage from "verbiage/pages/mlr/mlr-export";
+import naaarVerbiage from "verbiage/pages/naaar/naaar-export";
 // assets
 import unfinishedIcon from "assets/icons/icon_error_circle_bright.png";
 import finishedIcon from "assets/icons/icon_check_circle.png";
@@ -15,12 +22,14 @@ import finishedIcon from "assets/icons/icon_check_circle.png";
 const exportVerbiageMap: { [key in ReportType]: any } = {
   MCPAR: mcparVerbiage,
   MLR: mlrVerbiage,
-  NAAAR: undefined,
+  NAAAR: naaarVerbiage,
 };
 
 export const ExportedModalOverlayReportSection = ({ section }: Props) => {
   const { report } = useStore();
   const entityType = section.entityType;
+  const entities = report?.fieldData?.[entityType];
+  const entityCount = entities?.length;
 
   const verbiage = exportVerbiageMap[report?.reportType as ReportType];
 
@@ -31,24 +40,35 @@ export const ExportedModalOverlayReportSection = ({ section }: Props) => {
   );
 
   return (
-    <Box>
-      <Table
-        sx={sx.root}
-        content={{
-          caption: "Reporting Overview",
-          headRow: headerLabels,
-        }}
-        data-testid="exportTable"
-      >
-        {report?.fieldData[entityType] &&
-          renderModalOverlayTableBody(
-            report?.reportType as ReportType,
-            report?.fieldData[entityType]
+    <Box data-testid="exportedModalOverlayReportSection">
+      {entityType === EntityType.STANDARDS && (
+        <Text sx={sx.standardCount}>
+          Standard total count:{" "}
+          {entityCount > 0
+            ? entityCount
+            : verbiage.emptyEntityMessage[entityType]}
+        </Text>
+      )}
+      {entityType === EntityType.STANDARDS && !entityCount ? null : (
+        <>
+          <Table
+            sx={sx.root}
+            content={{
+              caption: "Reporting Overview",
+              headRow: headerLabels,
+            }}
+            data-testid="exportTable"
+          >
+            {entities &&
+              renderModalOverlayTableBody(
+                report?.reportType as ReportType,
+                entities
+              )}
+          </Table>
+          {(!entities || entityCount === 0) && (
+            <Text sx={sx.emptyState}> No entities found.</Text>
           )}
-      </Table>
-      {(!report?.fieldData[entityType] ||
-        report?.fieldData[entityType].length === 0) && (
-        <Text sx={sx.emptyState}> No entities found.</Text>
+        </>
       )}
     </Box>
   );
@@ -114,13 +134,38 @@ export function renderModalOverlayTableBody(
           </Tr>
         );
       });
-    case ReportType.MCPAR:
     case ReportType.NAAAR:
-      throw new Error(
-        `The modal overlay table headers for report type '${reportType}' have not been implemented.`
-      );
+      // render pattern for NAAAR Standards
+      return entities.map((entity, idx) => {
+        const {
+          provider,
+          standardType,
+          description,
+          analysisMethods,
+          population,
+          region,
+        } = mapNaaarStandardsData<NaaarStandardsTableShape>([entity])[0];
+        return (
+          <Tr key={idx}>
+            <Td>
+              <Text sx={sx.tableIndex}>{idx + 1}</Text>
+            </Td>
+            {[
+              provider,
+              standardType,
+              description,
+              analysisMethods,
+              population,
+              region,
+            ].map((value) => (
+              <Td>
+                <Text>{value}</Text>
+              </Td>
+            ))}
+          </Tr>
+        );
+      });
     default:
-      assertExhaustive(reportType);
       throw new Error(
         `The modal overlay table headers for report type '${reportType}' have not been implemented.`
       );
@@ -183,6 +228,12 @@ const sx = {
       },
     },
   },
+  standardCount: {
+    display: "block",
+    fontSize: "md",
+    fontWeight: "bold",
+    marginTop: "0.5rem",
+  },
   entityList: {
     wordBreak: "break-word",
   },
diff --git a/services/ui-src/src/components/export/ExportedReportWrapper.test.tsx b/services/ui-src/src/components/export/ExportedReportWrapper.test.tsx
index 04f72d0ab..2906016e7 100644
--- a/services/ui-src/src/components/export/ExportedReportWrapper.test.tsx
+++ b/services/ui-src/src/components/export/ExportedReportWrapper.test.tsx
@@ -9,8 +9,16 @@ import {
   mockNestedReportPageJson,
   mockStandardReportPageJson,
   mockMcparReportContext,
+  mockNaaarReportContext,
+  mockNaaarStandardsPageJson,
+  mockNaaarReportStore,
+  mockMcparReportStore,
 } from "utils/testing/setupJest";
 import { testA11y } from "utils/testing/commonTests";
+import { useStore } from "utils";
+
+jest.mock("utils/state/useStore");
+const mockedUseStore = useStore as jest.MockedFunction<typeof useStore>;
 
 const exportedStandardReportWrapperComponent = (
   <ReportContext.Provider value={mockMcparReportContext}>
@@ -42,7 +50,16 @@ const exportedModalDrawerReportWrapperComponent = (
   </ReportContext.Provider>
 );
 
+const standardEntityReportWrapperComponent = (
+  <ReportContext.Provider value={mockNaaarReportContext}>
+    <ExportedReportWrapper section={mockNaaarStandardsPageJson} />
+  </ReportContext.Provider>
+);
+
 describe("<ExportedReportWrapper />", () => {
+  beforeEach(() => {
+    mockedUseStore.mockReturnValue(mockMcparReportStore);
+  });
   test("ExportedStandardReportSection renders", () => {
     render(exportedStandardReportWrapperComponent);
     expect(
@@ -78,6 +95,15 @@ describe("<ExportedReportWrapper />", () => {
     ).toBeInTheDocument();
   });
 
+  // this is a drawer page in the normal report, but we use a different page for the pdf
+  test("Standards Entity renders ModalOverlay page in export", () => {
+    mockedUseStore.mockReturnValue(mockNaaarReportStore);
+    render(standardEntityReportWrapperComponent);
+    expect(
+      screen.getByTestId("exportedModalOverlayReportSection")
+    ).toBeInTheDocument();
+  });
+
   testA11y(exportedStandardReportWrapperComponent);
   testA11y(exportedDrawerReportWrapperComponent);
   testA11y(exportedModalDrawerReportWrapperComponent);
diff --git a/services/ui-src/src/components/export/ExportedReportWrapper.tsx b/services/ui-src/src/components/export/ExportedReportWrapper.tsx
index b9e8c0001..376cb5f3c 100644
--- a/services/ui-src/src/components/export/ExportedReportWrapper.tsx
+++ b/services/ui-src/src/components/export/ExportedReportWrapper.tsx
@@ -9,6 +9,7 @@ import {
 // types
 import {
   DrawerReportPageShape,
+  EntityType,
   ModalDrawerReportPageShape,
   ModalOverlayReportPageShape,
   PageTypes,
@@ -27,6 +28,14 @@ export const ExportedReportWrapper = ({ section }: Props) => {
         </Box>
       );
     case PageTypes.DRAWER:
+      // standards uses the table pattern from the modal overlay component
+      if (section.entityType === EntityType.STANDARDS) {
+        return (
+          <ExportedModalOverlayReportSection
+            section={section as ModalOverlayReportPageShape}
+          />
+        );
+      }
       return (
         <Box data-testid="exportedDrawerReportSection" mt="2rem">
           <ExportedReportFieldTable
diff --git a/services/ui-src/src/components/overlays/EntityDetailsMultiformOverlay.tsx b/services/ui-src/src/components/overlays/EntityDetailsMultiformOverlay.tsx
index 5e144a8ce..6b1e8deab 100644
--- a/services/ui-src/src/components/overlays/EntityDetailsMultiformOverlay.tsx
+++ b/services/ui-src/src/components/overlays/EntityDetailsMultiformOverlay.tsx
@@ -37,7 +37,7 @@ import {
   EntityShape,
   EntityType,
   ReportShape,
-  ScreenReaderOnlyHeaderName,
+  ScreenReaderCustomHeaderName,
 } from "types";
 // utils
 import { translateVerbiage } from "utils";
@@ -315,7 +315,7 @@ export const EntityDetailsMultiformOverlay = ({
       text,
     }: {
       formId: string;
-      header?: string | ScreenReaderOnlyHeaderName;
+      header?: string | ScreenReaderCustomHeaderName;
       text: string;
     }) => {
       const headerName =
diff --git a/services/ui-src/src/components/overlays/PlanComplianceTableOverlay.tsx b/services/ui-src/src/components/overlays/PlanComplianceTableOverlay.tsx
index 3bb1a95e8..acc95156a 100644
--- a/services/ui-src/src/components/overlays/PlanComplianceTableOverlay.tsx
+++ b/services/ui-src/src/components/overlays/PlanComplianceTableOverlay.tsx
@@ -22,8 +22,8 @@ import {
   EntityDetailsTableContentShape,
   EntityShape,
   FormJson,
+  ScreenReaderCustomHeaderName,
   ReportShape,
-  ScreenReaderOnlyHeaderName,
 } from "types";
 import { NaaarStandardsTableShape } from "components/tables/SortableNaaarStandardsTable";
 // utils
@@ -61,7 +61,7 @@ export const PlanComplianceTableOverlay = ({
     const closeEntityDetailsFormOverlay = () => {
       setSelectedStandard(null);
     };
-    let headRow = [] as ScreenReaderOnlyHeaderName[];
+    let headRow = [] as ScreenReaderCustomHeaderName[];
     const bodyRows = [];
     let formJson = structuredClone(form);
 
diff --git a/services/ui-src/src/components/tables/Table.tsx b/services/ui-src/src/components/tables/Table.tsx
index e04a1a60e..edae07cda 100644
--- a/services/ui-src/src/components/tables/Table.tsx
+++ b/services/ui-src/src/components/tables/Table.tsx
@@ -13,7 +13,7 @@ import {
 // types
 import {
   AnyObject,
-  ScreenReaderOnlyHeaderName,
+  ScreenReaderCustomHeaderName,
   TableContentShape,
 } from "types";
 // utils
@@ -43,7 +43,7 @@ export const Table = ({
           <Tr>
             {content.headRow.map(
               (
-                headerCell: string | ScreenReaderOnlyHeaderName,
+                headerCell: string | ScreenReaderCustomHeaderName,
                 index: number
               ) => (
                 <Th
@@ -52,9 +52,12 @@ export const Table = ({
                   sx={{ ...sx.tableHeader, ...sxOverride }}
                 >
                   {typeof headerCell === "object" ? (
-                    <VisuallyHidden>
-                      {sanitizeAndParseHtml(headerCell.hiddenName)}
-                    </VisuallyHidden>
+                    <>
+                      <VisuallyHidden>
+                        {sanitizeAndParseHtml(headerCell.hiddenName)}
+                      </VisuallyHidden>
+                      {headerCell?.name ? headerCell.name : null}
+                    </>
                   ) : (
                     sanitizeAndParseHtml(headerCell)
                   )}
diff --git a/services/ui-src/src/types/other.ts b/services/ui-src/src/types/other.ts
index 8b5e69f3f..5021967be 100644
--- a/services/ui-src/src/types/other.ts
+++ b/services/ui-src/src/types/other.ts
@@ -59,8 +59,9 @@ export enum PageTypes {
 
 export interface InputChangeEvent extends React.ChangeEvent<HTMLInputElement> {}
 
-export interface ScreenReaderOnlyHeaderName {
+export interface ScreenReaderCustomHeaderName {
   hiddenName: string;
+  name?: string;
 }
 
 export interface SortableHeadRow {
@@ -76,7 +77,7 @@ export interface SortableHeadRow {
 
 export interface TableContentShape {
   caption?: string;
-  headRow?: Array<string | ScreenReaderOnlyHeaderName>;
+  headRow?: Array<string | ScreenReaderCustomHeaderName>;
   sortableHeadRow?: SortableHeadRow;
   bodyRows?: string[][];
 }
diff --git a/services/ui-src/src/types/reports.ts b/services/ui-src/src/types/reports.ts
index bf8f9d160..9c3699c89 100644
--- a/services/ui-src/src/types/reports.ts
+++ b/services/ui-src/src/types/reports.ts
@@ -5,7 +5,7 @@ import {
   CustomHtmlElement,
   EntityType,
   FormJson,
-  ScreenReaderOnlyHeaderName,
+  ScreenReaderCustomHeaderName,
   SortableHeadRow,
   State,
   TableContentShape,
@@ -97,7 +97,7 @@ export interface EntityDetailsMultiformShape {
   table?: {
     caption: string;
     bodyRows: string[][];
-    headRow: Array<string | ScreenReaderOnlyHeaderName>;
+    headRow: Array<string | ScreenReaderCustomHeaderName>;
   };
   verbiage?: EntityDetailsMultiformVerbiage;
 }
diff --git a/services/ui-src/src/utils/testing/mockReport.tsx b/services/ui-src/src/utils/testing/mockReport.tsx
index 0304d1da8..aa905dd56 100644
--- a/services/ui-src/src/utils/testing/mockReport.tsx
+++ b/services/ui-src/src/utils/testing/mockReport.tsx
@@ -451,7 +451,7 @@ export const mockNaaarReportFieldData = {
           value: "Metro",
         },
       ],
-      "standard_standardDescription-standard-id": "5",
+      "standard_standardDescription-standard-id": "standard description",
       "standard_analysisMethodsUtilized-standard-id": [
         {
           key: "standard_analysisMethodsUtilized-standard-id-mockUUID1",
diff --git a/services/ui-src/src/verbiage/pages/naaar/naaar-export.ts b/services/ui-src/src/verbiage/pages/naaar/naaar-export.ts
index fabb33f8a..87f0efea1 100644
--- a/services/ui-src/src/verbiage/pages/naaar/naaar-export.ts
+++ b/services/ui-src/src/verbiage/pages/naaar/naaar-export.ts
@@ -22,4 +22,19 @@ export default {
     indicator: "Indicator",
     response: "Response",
   },
+  modalOverlayTableHeaders: {
+    index: "#",
+    provider: "Provider",
+    standardType: "Standard type",
+    standardDescription: "Standard description",
+    analysisMethods: "Analysis methods",
+    population: {
+      name: "Pop.",
+      hiddenName: "Population",
+    },
+    region: "Region",
+  },
+  emptyEntityMessage: {
+    standards: "0 - No access standards entered",
+  },
 };


Reviews:
user: login: gmrabian
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-04T15:40:34Z
user: login: gmrabian
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T14:13:20Z
user: login: bangbay-bluetiger
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T16:33:29Z
user: login: gmrabian
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T17:25:08Z
user: login: bangbay-bluetiger
body: Looks good to me.
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T18:55:28Z
user: login: gmrabian
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-08T13:03:28Z
user: login: bangbay-bluetiger
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-04-08T14:20:23Z
user: login: karla-vm
state: DISMISSED
author_association: COLLABORATOR
submitted_at: 2025-04-09T15:06:52Z
user: login: ntsummers1
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-09T15:11:09Z
user: login: ntsummers1
body: A question and comment to note, but nothing blocking here. Looks great overall!
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-04-09T15:12:27Z
user: login: gmrabian
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-09T15:26:36Z
user: login: britt-mo
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-09T16:06:58Z
user: login: bangbay-bluetiger
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-09T16:09:10Z

Review comments:
diff_hunk: @@ -52,9 +52,12 @@ export const Table = ({
                   sx={{ ...sx.tableHeader, ...sxOverride }}
                 >
                   {typeof headerCell === "object" ? (
-                    <VisuallyHidden>
-                      {sanitizeAndParseHtml(headerCell.hiddenName)}
-                    </VisuallyHidden>
+                    <>
+                      <VisuallyHidden>
+                        {sanitizeAndParseHtml(headerCell.hiddenName)}
+                      </VisuallyHidden>
+                      {headerCell?.name ? headerCell.name : null}
+                    </>
path: services/ui-src/src/components/tables/Table.tsx
user: login: gmrabian
body: I added the `name` attribute so that you could still have an aria label and have the visual name be different.
created_at: 2025-04-04T15:37:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -27,6 +28,16 @@ export const ExportedReportWrapper = ({ section }: Props) => {
         </Box>
       );
     case PageTypes.DRAWER:
+      // standards uses the table pattern from the modal overlay component
+      if (section.entityType === EntityType.STANDARDS) {
+        return (
+          <>
+            <ExportedModalOverlayReportSection
+              section={section as ModalOverlayReportPageShape}
+            />
+          </>
+        );
+      }
path: services/ui-src/src/components/export/ExportedReportWrapper.tsx
user: login: gmrabian
body: Modal Overlay was really close to the pdf layout for the standards data so I thought we should use that, even though it's a drawer page type in the report.
What do you think?
created_at: 2025-04-04T15:38:57Z
author_association: CONTRIBUTOR
diff_hunk: @@ -114,13 +130,37 @@ export function renderModalOverlayTableBody(
           </Tr>
         );
       });
-    case ReportType.MCPAR:
     case ReportType.NAAAR:
-      throw new Error(
-        `The modal overlay table headers for report type '${reportType}' have not been implemented.`
-      );
+      return entities.map((entity, idx) => {
path: services/ui-src/src/components/export/ExportedModalOverlayReportSection.tsx
user: login: gmrabian
body: retrieve and map standard data to table data within row. Great way to reuse `mapNaaarStandardsData`!
created_at: 2025-04-04T15:39:38Z
author_association: CONTRIBUTOR
diff_hunk: @@ -2,25 +2,34 @@
 import { EntityStatusIcon, Table } from "components";
 import { Box, Image, Td, Text, Tr } from "@chakra-ui/react";
 // types
-import { EntityShape, ModalOverlayReportPageShape, ReportType } from "types";
+import {
+  EntityShape,
+  EntityType,
+  ModalOverlayReportPageShape,
+  ReportType,
+} from "types";
+import { NaaarStandardsTableShape } from "components/tables/SortableNaaarStandardsTable";
 // utils
-import { assertExhaustive, getEntityDetailsMLR, useStore } from "utils";
+import { getEntityDetailsMLR, mapNaaarStandardsData, useStore } from "utils";
 // verbiage
 import mcparVerbiage from "verbiage/pages/mcpar/mcpar-export";
 import mlrVerbiage from "verbiage/pages/mlr/mlr-export";
+import naaarVerbiage from "verbiage/pages/naaar/naaar-export";
 // assets
 import unfinishedIcon from "assets/icons/icon_error_circle_bright.png";
 import finishedIcon from "assets/icons/icon_check_circle.png";
 
 const exportVerbiageMap: { [key in ReportType]: any } = {
   MCPAR: mcparVerbiage,
   MLR: mlrVerbiage,
-  NAAAR: undefined,
+  NAAAR: naaarVerbiage,
 };
 
 export const ExportedModalOverlayReportSection = ({ section }: Props) => {
   const { report } = useStore();
   const entityType = section.entityType;
+  const entities = report?.fieldData?.[entityType];
+  const entityCount = entities?.length;
path: services/ui-src/src/components/export/ExportedModalOverlayReportSection.tsx
user: login: gmrabian
body: for simplified reference below
created_at: 2025-04-04T15:40:03Z
author_association: CONTRIBUTOR
diff_hunk: @@ -31,7 +40,15 @@ export const ExportedModalOverlayReportSection = ({ section }: Props) => {
   );
 
   return (
-    <Box>
+    <Box data-testid="exportedModalOverlayReportSection">
+      {entityType === EntityType.STANDARDS && (
+        <Text sx={sx.standardCount}>
+          Standard total count:{" "}
+          {entityCount > 0
+            ? entityCount
+            : verbiage.emptyEntityMessage[entityType]}
+        </Text>
+      )}
path: services/ui-src/src/components/export/ExportedModalOverlayReportSection.tsx
user: login: gmrabian
body: add custom count text, only for standard entity table
created_at: 2025-04-04T15:40:21Z
author_association: CONTRIBUTOR
diff_hunk: @@ -31,24 +40,35 @@ export const ExportedModalOverlayReportSection = ({ section }: Props) => {
   );
 
   return (
-    <Box>
-      <Table
-        sx={sx.root}
-        content={{
-          caption: "Reporting Overview",
-          headRow: headerLabels,
-        }}
-        data-testid="exportTable"
-      >
-        {report?.fieldData[entityType] &&
-          renderModalOverlayTableBody(
-            report?.reportType as ReportType,
-            report?.fieldData[entityType]
+    <Box data-testid="exportedModalOverlayReportSection">
+      {entityType === EntityType.STANDARDS && (
+        <Text sx={sx.standardCount}>
+          Standard total count:{" "}
+          {entityCount > 0
+            ? entityCount
+            : verbiage.emptyEntityMessage[entityType]}
+        </Text>
+      )}
+      {entityType === EntityType.STANDARDS && !entityCount ? null : (
path: services/ui-src/src/components/export/ExportedModalOverlayReportSection.tsx
user: login: gmrabian
body: per design: only render table if there are standards to show
created_at: 2025-04-07T14:13:20Z
author_association: CONTRIBUTOR
diff_hunk: @@ -196,6 +206,61 @@ describe("<ExportedModalOverlayReportSection />", () => {
       expect(await findByText(mockMlrProgram.report_miscellaneousNotes));
     });
 
+    test("Should render data correctly for naaar", async () => {
+      mockedUseStore.mockReturnValue(mockNaaarReportStore);
+      const { container, findByText } = render(exportedNaaarStandardsComponent);
+
+      // All table headers are present
+      expect(container.querySelectorAll("th").length).toBe(7);
path: services/ui-src/src/components/export/ExportedModalOverlayReportSection.test.tsx
user: login: bangbay-bluetiger
body: You can use `screen.getAllByRole("columnheader").length` .
created_at: 2025-04-07T16:04:35Z
author_association: CONTRIBUTOR
diff_hunk: @@ -196,6 +206,61 @@ describe("<ExportedModalOverlayReportSection />", () => {
       expect(await findByText(mockMlrProgram.report_miscellaneousNotes));
     });
 
+    test("Should render data correctly for naaar", async () => {
+      mockedUseStore.mockReturnValue(mockNaaarReportStore);
+      const { container, findByText } = render(exportedNaaarStandardsComponent);
+
+      // All table headers are present
+      expect(container.querySelectorAll("th").length).toBe(7);
+
+      // Every entity has a row (+1 for header)
+      expect(container.querySelectorAll("tr").length).toBe(
path: services/ui-src/src/components/export/ExportedModalOverlayReportSection.test.tsx
user: login: bangbay-bluetiger
body: If we want to count just the body rows, I'd do something like this:
```
const tbody = screen.getAllByRole("rowgroup")[1];
const rows = within(tbody).getAllByRole("row");
expect(rows.length).toBe(mockNaaarReportStore.report?.fieldData.standards.length);
```
created_at: 2025-04-07T16:09:30Z
author_association: CONTRIBUTOR
diff_hunk: @@ -196,6 +206,61 @@ describe("<ExportedModalOverlayReportSection />", () => {
       expect(await findByText(mockMlrProgram.report_miscellaneousNotes));
     });
 
+    test("Should render data correctly for naaar", async () => {
+      mockedUseStore.mockReturnValue(mockNaaarReportStore);
+      const { container, findByText } = render(exportedNaaarStandardsComponent);
+
+      // All table headers are present
+      expect(container.querySelectorAll("th").length).toBe(7);
+
+      // Every entity has a row (+1 for header)
+      expect(container.querySelectorAll("tr").length).toBe(
+        mockNaaarReportStore.report?.fieldData.standards.length + 1
+      );
+
+      // index
+      expect(await findByText("1")).toBeVisible();
path: services/ui-src/src/components/export/ExportedModalOverlayReportSection.test.tsx
user: login: bangbay-bluetiger
body: For these text finds, you could replace with `screen.getByRole("gridcell", { name: textToFind })`.
created_at: 2025-04-07T16:10:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -196,6 +206,61 @@ describe("<ExportedModalOverlayReportSection />", () => {
       expect(await findByText(mockMlrProgram.report_miscellaneousNotes));
     });
 
+    test("Should render data correctly for naaar", async () => {
+      mockedUseStore.mockReturnValue(mockNaaarReportStore);
+      const { container, findByText } = render(exportedNaaarStandardsComponent);
+
+      // All table headers are present
+      expect(container.querySelectorAll("th").length).toBe(7);
+
+      // Every entity has a row (+1 for header)
+      expect(container.querySelectorAll("tr").length).toBe(
+        mockNaaarReportStore.report?.fieldData.standards.length + 1
+      );
+
+      // index
+      expect(await findByText("1")).toBeVisible();
+
+      // provider type
+      expect(
+        await findByText("Mock Provider; mock provider details")
+      ).toBeVisible();
+
+      // standard type
+      expect(await findByText("mock standard type")).toBeVisible();
+
+      // description
+      expect(await findByText("description of standard")).toBeVisible();
+
+      // analysis methods, joined with a comma
+      expect(await findByText("Mock am 1, Mock am 2, Mock am 3")).toBeVisible();
+
+      // population
+      expect(await findByText("Mock population")).toBeVisible();
+
+      // region
+      expect(await findByText("Mock region")).toBeVisible();
+    });
+
+    test("Should render message for naaar with no standards", async () => {
+      const mockEmptyStandardsNaaarStore = {
+        ...mockNaaarReportStore,
+      };
+      mockEmptyStandardsNaaarStore.report!.fieldData.standards = undefined;
+      mockedUseStore.mockReturnValue(mockEmptyStandardsNaaarStore);
+      const { container, findByText } = render(exportedNaaarStandardsComponent);
+
+      // No table renders
+      expect(container.querySelectorAll("table").length).toBe(0);
path: services/ui-src/src/components/export/ExportedModalOverlayReportSection.test.tsx
user: login: bangbay-bluetiger
body: I'd use `expect(screen.queryByRole("table")).toBeNull()`.
created_at: 2025-04-07T16:11:28Z
author_association: CONTRIBUTOR
diff_hunk: @@ -196,6 +206,61 @@ describe("<ExportedModalOverlayReportSection />", () => {
       expect(await findByText(mockMlrProgram.report_miscellaneousNotes));
     });
 
+    test("Should render data correctly for naaar", async () => {
+      mockedUseStore.mockReturnValue(mockNaaarReportStore);
+      const { container, findByText } = render(exportedNaaarStandardsComponent);
+
+      // All table headers are present
+      expect(container.querySelectorAll("th").length).toBe(7);
+
+      // Every entity has a row (+1 for header)
+      expect(container.querySelectorAll("tr").length).toBe(
+        mockNaaarReportStore.report?.fieldData.standards.length + 1
+      );
+
+      // index
+      expect(await findByText("1")).toBeVisible();
+
+      // provider type
+      expect(
+        await findByText("Mock Provider; mock provider details")
+      ).toBeVisible();
+
+      // standard type
+      expect(await findByText("mock standard type")).toBeVisible();
+
+      // description
+      expect(await findByText("description of standard")).toBeVisible();
+
+      // analysis methods, joined with a comma
+      expect(await findByText("Mock am 1, Mock am 2, Mock am 3")).toBeVisible();
+
+      // population
+      expect(await findByText("Mock population")).toBeVisible();
+
+      // region
+      expect(await findByText("Mock region")).toBeVisible();
+    });
+
+    test("Should render message for naaar with no standards", async () => {
+      const mockEmptyStandardsNaaarStore = {
+        ...mockNaaarReportStore,
+      };
+      mockEmptyStandardsNaaarStore.report!.fieldData.standards = undefined;
+      mockedUseStore.mockReturnValue(mockEmptyStandardsNaaarStore);
+      const { container, findByText } = render(exportedNaaarStandardsComponent);
+
+      // No table renders
+      expect(container.querySelectorAll("table").length).toBe(0);
+
+      // region
+      expect(
path: services/ui-src/src/components/export/ExportedModalOverlayReportSection.test.tsx
user: login: bangbay-bluetiger
body: I'd use:
```
expect(screen.getByTestId("exportedModalOverlayReportSection"))
  .toHaveTextContent("Standard total count: 0 - No access standards entered");
```
created_at: 2025-04-07T16:17:12Z
author_association: CONTRIBUTOR
diff_hunk: @@ -52,9 +52,12 @@ export const Table = ({
                   sx={{ ...sx.tableHeader, ...sxOverride }}
                 >
                   {typeof headerCell === "object" ? (
-                    <VisuallyHidden>
-                      {sanitizeAndParseHtml(headerCell.hiddenName)}
-                    </VisuallyHidden>
+                    <>
+                      <VisuallyHidden>
+                        {sanitizeAndParseHtml(headerCell.hiddenName)}
+                      </VisuallyHidden>
+                      {headerCell?.name ? headerCell.name : null}
+                    </>
path: services/ui-src/src/components/tables/Table.tsx
user: login: bangbay-bluetiger
body: It seems like if there's visible text we shouldn't be using `VisuallyHidden`. What if we did something like this instead:
```
{!headerCell.name && (
  <VisuallyHidden>
    {sanitizeAndParseHtml(headerCell.hiddenName)}
  </VisuallyHidden>
)}

{headerCell.name && (
  <Text as="span" aria-label={headerCell.hiddenName}>
    {headerCell.name}
  </Text>
)}

```
created_at: 2025-04-07T16:31:14Z
author_association: CONTRIBUTOR
diff_hunk: @@ -52,9 +52,12 @@ export const Table = ({
                   sx={{ ...sx.tableHeader, ...sxOverride }}
                 >
                   {typeof headerCell === "object" ? (
-                    <VisuallyHidden>
-                      {sanitizeAndParseHtml(headerCell.hiddenName)}
-                    </VisuallyHidden>
+                    <>
+                      <VisuallyHidden>
+                        {sanitizeAndParseHtml(headerCell.hiddenName)}
+                      </VisuallyHidden>
+                      {headerCell?.name ? headerCell.name : null}
+                    </>
path: services/ui-src/src/components/tables/Table.tsx
user: login: gmrabian
body: I tried the suggestion locally and was having trouble with voiceover where it would announce:
"Population group, pop., end of population group"

I think when we have the Text element it changes how the voiceover reads it? I tried both as span and as p.

As a result I left the change as is.
created_at: 2025-04-07T17:25:08Z
author_association: CONTRIBUTOR
diff_hunk: @@ -27,6 +28,16 @@ export const ExportedReportWrapper = ({ section }: Props) => {
         </Box>
       );
     case PageTypes.DRAWER:
+      // standards uses the table pattern from the modal overlay component
+      if (section.entityType === EntityType.STANDARDS) {
+        return (
+          <>
path: services/ui-src/src/components/export/ExportedReportWrapper.tsx
user: login: bangbay-bluetiger
body: Nit: Extra `<>` wrapper not needed.
created_at: 2025-04-07T18:53:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -27,6 +28,16 @@ export const ExportedReportWrapper = ({ section }: Props) => {
         </Box>
       );
     case PageTypes.DRAWER:
+      // standards uses the table pattern from the modal overlay component
+      if (section.entityType === EntityType.STANDARDS) {
+        return (
+          <>
+            <ExportedModalOverlayReportSection
+              section={section as ModalOverlayReportPageShape}
+            />
+          </>
+        );
+      }
path: services/ui-src/src/components/export/ExportedReportWrapper.tsx
user: login: bangbay-bluetiger
body: This seems fine to me.
created_at: 2025-04-07T18:55:09Z
author_association: CONTRIBUTOR
diff_hunk: @@ -27,6 +28,16 @@ export const ExportedReportWrapper = ({ section }: Props) => {
         </Box>
       );
     case PageTypes.DRAWER:
+      // standards uses the table pattern from the modal overlay component
+      if (section.entityType === EntityType.STANDARDS) {
+        return (
+          <>
path: services/ui-src/src/components/export/ExportedReportWrapper.tsx
user: login: gmrabian
body: thank you for catching this!
created_at: 2025-04-08T13:03:28Z
author_association: CONTRIBUTOR
diff_hunk: @@ -114,13 +134,38 @@ export function renderModalOverlayTableBody(
           </Tr>
         );
       });
-    case ReportType.MCPAR:
     case ReportType.NAAAR:
-      throw new Error(
-        `The modal overlay table headers for report type '${reportType}' have not been implemented.`
-      );
+      // render pattern for NAAAR Standards
+      return entities.map((entity, idx) => {
+        const {
+          provider,
+          standardType,
+          description,
+          analysisMethods,
+          population,
+          region,
+        } = mapNaaarStandardsData<NaaarStandardsTableShape>([entity])[0];
path: services/ui-src/src/components/export/ExportedModalOverlayReportSection.tsx
user: login: ntsummers1
body: Is it ever possible that this [0] can not be there? Double checking cause that'll hard error the user out.
created_at: 2025-04-09T15:11:09Z
author_association: CONTRIBUTOR
diff_hunk: @@ -114,13 +134,38 @@ export function renderModalOverlayTableBody(
           </Tr>
         );
       });
-    case ReportType.MCPAR:
     case ReportType.NAAAR:
-      throw new Error(
-        `The modal overlay table headers for report type '${reportType}' have not been implemented.`
-      );
+      // render pattern for NAAAR Standards
+      return entities.map((entity, idx) => {
+        const {
+          provider,
+          standardType,
+          description,
+          analysisMethods,
+          population,
+          region,
+        } = mapNaaarStandardsData<NaaarStandardsTableShape>([entity])[0];
path: services/ui-src/src/components/export/ExportedModalOverlayReportSection.tsx
user: login: gmrabian
body: Hmm good callout 🤔 

since we are within `entities.map()` I believe entity must be defined, and in that case this function will return an array with an object of the relevant info. I also think the entity (Standards) will have to be complete because it's a drawer form, which can't submit without all fields.

I think if `entity` here were undefined or incomplete it might be a problem with the report as whole.
created_at: 2025-04-09T15:26:36Z
author_association: CONTRIBUTOR

PR comments:
user: login: ntsummers1
created_at: 2025-04-09T15:08:15Z
body: ```
Add custom branch to render standards page using modal overlay export page type (it's the
closest to what we wanted)
The standards page is a drawer page type. What do you think of this? Is it ok to render another 
page type for the export page logically? I thought it would save us lines of code and be more 
reusable. Perhaps our export types don't perfectly align with our form page types?
```
Personally I don't like it since its a hard break in the pattern. However, deadline is coming up and I'm not one to stop progress over something like that. But in a world where these forms are getting more and more complex and we're passing and injecting questions, any semblance of consistency and clear separation would help. I'd love to circle back to this in the future when we're in a lul period. Like the ticket could chill for 6 months as a nice to have backlog ticket for a rainy day type thing. As of right now, I'm totally cool with this approach. Save lines, save time, meet deadline, and circle back when the time feels right.

author_association: CONTRIBUTOR
user: login: qlty-cloud-legacy[bot]
created_at: 2025-04-09T15:37:14Z
body: Code Climate has analyzed commit 64376cac and detected **0 issues** on this pull request.

The test coverage on the diff in this pull request is **100.0%** (**90%** is the threshold).

This pull request will bring the total coverage in the repository to **97.1%** (**0.0%** change).

View more on [Code Climate](https://codeclimate.com/repos/644971a20bd7db00dfeaf21a/pull/12102).

author_association: NONE