diff --git a/src/app/components/cms/ChartRowCardContent/ChartRowCardContent.tsx b/src/app/components/cms/ChartRowCardContent/ChartRowCardContent.tsx index b3fb2ac77..22a818cbd 100644 --- a/src/app/components/cms/ChartRowCardContent/ChartRowCardContent.tsx +++ b/src/app/components/cms/ChartRowCardContent/ChartRowCardContent.tsx @@ -53,7 +53,14 @@ export function ChartRowCardContent({ value, isPublic, pageClassification }: Cha className="ukhsa-chart-card flex flex-col gap-6" >
- + diff --git a/src/app/components/ui/ukhsa/ChartRowCardHeader/ChartRowCardHeader.spec.tsx b/src/app/components/ui/ukhsa/ChartRowCardHeader/ChartRowCardHeader.spec.tsx index 497996a16..9c7090c57 100644 --- a/src/app/components/ui/ukhsa/ChartRowCardHeader/ChartRowCardHeader.spec.tsx +++ b/src/app/components/ui/ukhsa/ChartRowCardHeader/ChartRowCardHeader.spec.tsx @@ -14,14 +14,30 @@ const getAreaSelectorMock = jest.mocked(getAreaSelector) describe('ChartRowCardHeader', () => { test('renders correctly with props', async () => { getAreaSelectorMock.mockResolvedValue([]) - render(await ChartRowCardHeader({ id: '1', title: 'Sample Title', description: 'Sample Description' })) + render( + await ChartRowCardHeader({ + id: '1', + title: 'Sample Title', + description: 'Sample Description', + pageClassification: 'official', + authEnabled: false, + }) + ) expect(screen.getByRole('heading', { level: 3, name: 'Sample Title' })).toBeInTheDocument() expect(screen.getByText('Sample Description')).toBeInTheDocument() }) test('displays a location when set', async () => { getAreaSelectorMock.mockResolvedValue([null, 'Test Area']) - render(await ChartRowCardHeader({ id: '1', title: 'Title', description: 'Description' })) + render( + await ChartRowCardHeader({ + id: '1', + title: 'Title', + description: 'Description', + pageClassification: 'official', + authEnabled: false, + }) + ) expect(screen.getByRole('heading', { level: 3, name: 'Title (Test Area)' })).toBeInTheDocument() }) @@ -31,18 +47,22 @@ describe('ChartRowCardHeader', () => { id: '1', title: 'Title', description: 'Description', + pageClassification: 'official', + authEnabled: false, children:
Child Element
, }) ) expect(screen.getByText('Child Element')).toBeInTheDocument() }) - test('has correct structure and HTML elements', async () => { + test('applies correct classes to description and heading', async () => { render( await ChartRowCardHeader({ id: '1', title: 'Title', description: 'Description', + pageClassification: 'official', + authEnabled: false, }) ) expect(screen.getByText('Description')).toHaveClass( diff --git a/src/app/components/ui/ukhsa/ChartRowCardHeader/ChartRowCardHeader.tsx b/src/app/components/ui/ukhsa/ChartRowCardHeader/ChartRowCardHeader.tsx index f7fc782d7..28999d05e 100644 --- a/src/app/components/ui/ukhsa/ChartRowCardHeader/ChartRowCardHeader.tsx +++ b/src/app/components/ui/ukhsa/ChartRowCardHeader/ChartRowCardHeader.tsx @@ -1,21 +1,35 @@ import { ReactNode } from 'react' +import { DataClassification } from '@/api/models/DataClassification' import { getAreaSelector } from '@/app/hooks/getAreaSelector' +import { getDataClassification } from '@/app/utils/table.utils' interface ChartRowCardHeaderProps { children?: ReactNode title: string description?: string id: string + isPublic?: boolean + pageClassification?: DataClassification + authEnabled?: boolean } -export async function ChartRowCardHeader({ children, id, title, description }: Readonly) { +export async function ChartRowCardHeader({ + children, + id, + title, + description, + isPublic, + pageClassification = 'official_sensitive', + authEnabled, +}: Readonly) { const [, areaName] = await getAreaSelector() return (

- {title} {areaName && `(${areaName})`} + {title} {areaName && `(${areaName})`}{' '} + {getDataClassification(isPublic, pageClassification as DataClassification, authEnabled)}

{description ? (

{description}

diff --git a/src/app/components/ui/ukhsa/FilterLinkedCards/SubplotFilterCard.tsx b/src/app/components/ui/ukhsa/FilterLinkedCards/SubplotFilterCard.tsx index ca219405d..84b11e591 100644 --- a/src/app/components/ui/ukhsa/FilterLinkedCards/SubplotFilterCard.tsx +++ b/src/app/components/ui/ukhsa/FilterLinkedCards/SubplotFilterCard.tsx @@ -18,6 +18,7 @@ import SubplotClientChart from '@/app/components/ui/ukhsa/FilterLinkedCards/comp import { SubplotClientTable } from '@/app/components/ui/ukhsa/Table/SubplotClientTable' import { formatDate } from '@/app/utils/date.utils' import { FlattenedGeography, getParentGeography } from '@/app/utils/geography.utils' +import { getDataClassification } from '@/app/utils/table.utils' import { Card } from '../Card/Card' import DropdownTab from '../Tabs/DropdownTab' @@ -59,6 +60,7 @@ const SubplotFilterCard = ({ const title = `${cardData.title_prefix} between ${timePeriods[currentTimePeriodIndex].value.label} (${geographyParent?.name}, ${geography.name})` const id = title const about = cardData.about ? cardData.about : '' + const dataClassification = getDataClassification(isPublic, level ?? 'official_sensitive', authEnabled) return (
@@ -66,7 +68,7 @@ const SubplotFilterCard = ({

- {title} + {title} {dataClassification}

{description}

diff --git a/src/app/components/ui/ukhsa/FilterLinkedCards/TimeseriesFilterCard.tsx b/src/app/components/ui/ukhsa/FilterLinkedCards/TimeseriesFilterCard.tsx index 5db87a060..c208384a4 100644 --- a/src/app/components/ui/ukhsa/FilterLinkedCards/TimeseriesFilterCard.tsx +++ b/src/app/components/ui/ukhsa/FilterLinkedCards/TimeseriesFilterCard.tsx @@ -13,6 +13,7 @@ import { ClientTable } from '@/app/components/ui/ukhsa/Table/ClientTable' import DropdownTab from '@/app/components/ui/ukhsa/Tabs/DropdownTab' import { formatDate } from '@/app/utils/date.utils' import { FlattenedGeography, getParentGeography } from '@/app/utils/geography.utils' +import { getDataClassification } from '@/app/utils/table.utils' import { getMinMaxYears, MinMaxYear } from '@/app/utils/time-period.utils' import { Card } from '../Card/Card' @@ -47,6 +48,7 @@ const TimeseriesFilterCard = ({ const title = `${cardData.title_prefix} between ${minMaxDateRange.minDate} - ${minMaxDateRange.maxDate} (${geographyParent!.name}, ${geography.name})` const id = title const about = cardData.about ? cardData.about : '' + const dataClassification = getDataClassification(isPublic, level ?? 'official_sensitive', authEnabled) return (
@@ -54,7 +56,7 @@ const TimeseriesFilterCard = ({

- {title} + {title} {dataClassification}

{description}

diff --git a/src/app/utils/table.utils.spec.tsx b/src/app/utils/table.utils.spec.tsx index cb94854a5..1b00b87ca 100644 --- a/src/app/utils/table.utils.spec.tsx +++ b/src/app/utils/table.utils.spec.tsx @@ -1,6 +1,6 @@ import { render, screen } from '@/config/test-utils' -import { getColumnHeader } from './table.utils' +import { getColumnHeader, getDataClassification } from './table.utils' const renderHeader = ( chartLabel: string, @@ -90,3 +90,41 @@ describe('getColumnHeader', () => { }) }) }) + +describe('getDataClassification', () => { + test('returns empty string when authEnabled is false', () => { + expect(getDataClassification(false, 'official_sensitive', false)).toBe('') + }) + + test('returns classification when authEnabled is true', () => { + expect(getDataClassification(false, 'official_sensitive', true)).toBe('(OFFICIAL SENSITIVE)') + }) + + test('returns empty string when isPublic is true', () => { + expect(getDataClassification(true, 'official_sensitive', true)).toBe('') + }) + + test('returns empty string when isPublic is undefined', () => { + expect(getDataClassification(undefined, 'official_sensitive', true)).toBe('') + }) + + test('returns uppercase classification wrapped in parentheses for official_sensitive', () => { + expect(getDataClassification(false, 'official_sensitive', true)).toBe('(OFFICIAL SENSITIVE)') + }) + + test('returns uppercase classification wrapped in parentheses for official', () => { + expect(getDataClassification(false, 'official', true)).toBe('(OFFICIAL)') + }) + + test('returns uppercase classification wrapped in parentheses for secret', () => { + expect(getDataClassification(false, 'secret', true)).toBe('(SECRET)') + }) + + test('returns uppercase classification wrapped in parentheses for top_secret', () => { + expect(getDataClassification(false, 'top_secret', true)).toBe('(TOP SECRET)') + }) + + test('returns uppercase classification wrapped in parentheses for protective_marking_not_set', () => { + expect(getDataClassification(false, 'protective_marking_not_set', true)).toBe('(PROTECTIVE MARKING NOT SET)') + }) +}) diff --git a/src/app/utils/table.utils.tsx b/src/app/utils/table.utils.tsx index 1d7a00a10..f10dbed33 100644 --- a/src/app/utils/table.utils.tsx +++ b/src/app/utils/table.utils.tsx @@ -1,6 +1,6 @@ -type Level = 'official' | 'official_sensitive' | 'protective_marking_not_set' | 'secret' | 'top_secret' +import { DataClassification } from '@/api/models/DataClassification' -const levelContent: Record = { +const levelContent: Record = { official: 'Official', official_sensitive: 'Official-Sensitive', protective_marking_not_set: 'Protective marking not set', @@ -8,12 +8,20 @@ const levelContent: Record = { top_secret: 'Top Secret', } +const levelContentCaps: Record = { + official: 'OFFICIAL', + official_sensitive: 'OFFICIAL SENSITIVE', + protective_marking_not_set: 'PROTECTIVE MARKING NOT SET', + secret: 'SECRET', + top_secret: 'TOP SECRET', +} + export const getColumnHeader = ( chartLabel: string, axisTitle: string, fallback: string, isPublic?: boolean, - level: Level = 'official_sensitive', + level: DataClassification = 'official_sensitive', authEnabled?: boolean ) => { const label = chartLabel || axisTitle || fallback @@ -28,3 +36,14 @@ export const getColumnHeader = ( ) } + +export const getDataClassification = ( + isPublic: boolean | undefined, + pageClassification: DataClassification = 'official_sensitive', + authEnabled: boolean | undefined +): string => { + if (authEnabled && isPublic === false) { + return `(${levelContentCaps[pageClassification]})` + } + return '' +}