Skip to content

Commit

Permalink
fix: show status div when table results changes
Browse files Browse the repository at this point in the history
  • Loading branch information
jorilindell committed Apr 26, 2024
1 parent 7b506e4 commit cc89c5b
Show file tree
Hide file tree
Showing 10 changed files with 189 additions and 23 deletions.
7 changes: 7 additions & 0 deletions public/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,13 @@
"text": "Viewing this content requires strong identification. Log out and try another login method.",
"title": "Strong identification is required"
},
"table": {
"accessibility": {
"noResultsFound": "Your search returned no results.",
"resultsFoundText": "{{count}} search result found",
"resultsFoundText_other": "{{count}} search results found"
}
},
"titleServerErrorSummary": "Form contains following errors",
"total": "In total",
"validation": {
Expand Down
7 changes: 7 additions & 0 deletions public/locales/fi/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,13 @@
"text": "Tämän sisällön näkeminen edellyttää vahvaa tunnistautumista. Kirjaudu ulos ja kokeile toista kirjautumistapaa.",
"title": "Vahva tunnistautuminen vaaditaan"
},
"table": {
"accessibility": {
"noResultsFound": "Hakusi ei tuottanut yhtään tuloksia.",
"resultsFoundText": "{{count}} hakutulos löytynyt",
"resultsFoundText_other": "{{count}} hakutulosta löytynyt"
}
},
"titleServerErrorSummary": "Lomakkeella on seuraavat virheet",
"total": "Yhteensä",
"validation": {
Expand Down
7 changes: 7 additions & 0 deletions public/locales/sv/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,13 @@
"text": "Att se detta innehåll kräver stark identifiering. Logga ut och prova en annan inloggningsmetod.",
"title": "Stark identifiering krävs"
},
"table": {
"accessibility": {
"noResultsFound": "Din sökning gav inga resultat.",
"resultsFoundText": "{{count}} sökresultat hittades",
"resultsFoundText_other": "{{count}} sökresultat hittades"
}
},
"titleServerErrorSummary": "Formuläret innehåller följande fel",
"total": "Totalt",
"validation": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import uniqueId from 'lodash/uniqueId';
import React, {
createContext,
FC,
PropsWithChildren,
useCallback,
useMemo,
useState,
} from 'react';

import styles from './accessibilityNotification.module.scss';

export type SetAccessibilityTextFn = (text: string) => void;

export type AccessibilityNotificationProps = { id: string; text: string };

export type AccessibilityNotificationContextProps = {
setAccessibilityText: SetAccessibilityTextFn;
};

export const AccessibilityNotificationContext = createContext<
AccessibilityNotificationContextProps | undefined
>(undefined);

export const AccessibilityNotificationProvider: FC<PropsWithChildren> = ({
children,
}) => {
const [notifications, setNotifications] = useState<
AccessibilityNotificationProps[]
>([]);

const removeNotification = (notificationId: string) => {
setNotifications((items) =>
items.filter(({ id }) => id !== notificationId)
);
};

const updateNotificationText = (text: string, notificationId: string) =>
setNotifications((items) =>
items.map((notification) =>
notification.id === notificationId
? { ...notification, text }
: notification
)
);

const setAccessibilityText = useCallback((text: string) => {
const notificationId = uniqueId('accessibility-notification-');

setNotifications((items) => [...items, { id: notificationId, text: '' }]);
// Change notification text after 100ms to force screen reader
// to read the notification
setTimeout(() => {
updateNotificationText(text, notificationId);
}, 100);

// Clear notification area after 1000ms
setTimeout(() => {
removeNotification(notificationId);
}, 1000);
}, []);

const value = useMemo(
() => ({
setAccessibilityText,
}),
[setAccessibilityText]
);

return (
<AccessibilityNotificationContext.Provider value={value}>
{notifications.map(({ text, id }) => (
<output key={id} className={styles.accessibilityNotification}>
{text}
</output>
))}

{children}
</AccessibilityNotificationContext.Provider>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@import '../../../styles/mixins.scss';

.accessibilityNotification {
@include hidden-from-screen;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/* eslint @typescript-eslint/explicit-function-return-type: 0 */
import { useContext } from 'react';

import {
AccessibilityNotificationContext,
AccessibilityNotificationContextProps,
} from '../AccessibilityNotificationContext';

export const useAccessibilityNotificationContext =
(): AccessibilityNotificationContextProps => {
const context = useContext<
AccessibilityNotificationContextProps | undefined
>(AccessibilityNotificationContext);

/* istanbul ignore next */
if (!context) {
throw new Error(
// eslint-disable-next-line max-len
'AccessibilityNotificationContext context is undefined, please verify you are calling useAccessibilityNotificationContext() as child of a <AccessibilityNotificationProvider> component.'
);
}

return context;
};
25 changes: 25 additions & 0 deletions src/common/components/searchStatus/SearchStatus.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/* eslint-disable max-len */
import { useTranslation } from 'next-i18next';
import { FC, useEffect } from 'react';

import { useAccessibilityNotificationContext } from '../accessibilityNotificationContext/hooks/useAccessibilityNotificationContext';

type Props = { count: number; loading: boolean };

const SearchStatus: FC<Props> = ({ count, loading }) => {
const { t } = useTranslation('common');
const { setAccessibilityText } = useAccessibilityNotificationContext();

useEffect(() => {
if (!loading) {
setAccessibilityText(
count
? t('common:table.accessibility.resultsFoundText', { count })
: t('common:table.accessibility.noResultsFound')
);
}
}, [count, loading, setAccessibilityText, t]);
return null;
};

export default SearchStatus;
4 changes: 3 additions & 1 deletion src/domain/attendanceList/attendeeList/AttendeeList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import React, { useState } from 'react';

import Checkbox from '../../../common/components/checkbox/Checkbox';
import { useNotificationsContext } from '../../../common/components/notificationsContext/hooks/useNotificationsContext';
import SearchStatus from '../../../common/components/searchStatus/SearchStatus';
import useHandleError from '../../../hooks/useHandleError';
import { ExtendedSession } from '../../../types';
import skipFalsyType from '../../../utils/skipFalsyType';
Expand Down Expand Up @@ -106,9 +107,10 @@ const AttendeeList: React.FC<Props> = ({ registration }) => {

return (
<>
<SearchStatus count={filteredAttendees.length} loading={false} />
<SearchRow
countText={t('attendanceList:count', {
count: attendees.length,
count: filteredAttendees.length,
})}
onSearchSubmit={
/* istanbul ignore next */
Expand Down
28 changes: 16 additions & 12 deletions src/pages/_app.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable max-len */
/* eslint-disable @typescript-eslint/ban-ts-comment */
import 'hds-core/lib/base.min.css';
import '../styles/main.scss';
Expand All @@ -14,6 +15,7 @@ import { SessionProvider } from 'next-auth/react';
import { appWithTranslation, SSRConfig } from 'next-i18next';
import React, { useEffect } from 'react';

import { AccessibilityNotificationProvider } from '../common/components/accessibilityNotificationContext/AccessibilityNotificationContext';
import { NotificationsProvider } from '../common/components/notificationsContext/NotificationsContext';
import CookieConsent from '../domain/app/cookieConsent/CookieConsent';
import PageLayout from '../domain/app/layout/pageLayout/PageLayout';
Expand Down Expand Up @@ -47,18 +49,20 @@ const MyApp = ({
}, []);

return (
<NotificationsProvider>
<SessionProvider session={session} refetchInterval={30}>
<QueryClientProvider client={queryClient}>
<HydrationBoundary state={pageProps.dehydratedState}>
<CookieConsent />
<PageLayout {...pageProps}>
<Component {...pageProps} />
</PageLayout>
</HydrationBoundary>
</QueryClientProvider>
</SessionProvider>
</NotificationsProvider>
<AccessibilityNotificationProvider>
<NotificationsProvider>
<SessionProvider session={session} refetchInterval={30}>
<QueryClientProvider client={queryClient}>
<HydrationBoundary state={pageProps.dehydratedState}>
<CookieConsent />
<PageLayout {...pageProps}>
<Component {...pageProps} />
</PageLayout>
</HydrationBoundary>
</QueryClientProvider>
</SessionProvider>
</NotificationsProvider>
</AccessibilityNotificationProvider>
);
};

Expand Down
24 changes: 14 additions & 10 deletions src/utils/testUtils.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable max-len */
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/ban-ts-comment */
/* eslint-disable no-console */
Expand All @@ -19,6 +20,7 @@ import { SessionProvider } from 'next-auth/react';
import React, { useMemo } from 'react';
import wait from 'waait';

import { AccessibilityNotificationProvider } from '../common/components/accessibilityNotificationContext/AccessibilityNotificationContext';
import { testId } from '../common/components/loadingSpinner/LoadingSpinner';
import { NotificationsProvider } from '../common/components/notificationsContext/NotificationsContext';
import { registration } from '../domain/registration/__mocks__/registration';
Expand Down Expand Up @@ -61,16 +63,18 @@ const customRender: CustomRender = (
);

return (
<NotificationsProvider>
<RouterContext.Provider value={value}>
<SessionProvider session={session}>
{/* @ts-ignore */}
<QueryClientProvider client={queryClient}>
{children as React.ReactElement}
</QueryClientProvider>
</SessionProvider>
</RouterContext.Provider>
</NotificationsProvider>
<AccessibilityNotificationProvider>
<NotificationsProvider>
<RouterContext.Provider value={value}>
<SessionProvider session={session}>
{/* @ts-ignore */}
<QueryClientProvider client={queryClient}>
{children as React.ReactElement}
</QueryClientProvider>
</SessionProvider>
</RouterContext.Provider>
</NotificationsProvider>
</AccessibilityNotificationProvider>
);
};

Expand Down

0 comments on commit cc89c5b

Please sign in to comment.