Skip to content

Commit 35c9051

Browse files
committed
fix rerender/redraw when feedback alert is removed
1 parent 4009c68 commit 35c9051

9 files changed

+78
-103
lines changed

packages/bygger/src/components/MessageAlerts.tsx

+6-5
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import { Close } from '@navikt/ds-icons';
22
import { Alert, BodyShort, Button } from '@navikt/ds-react';
33
import { makeStyles, navCssVariables } from '@navikt/skjemadigitalisering-shared-components';
4-
import { Message } from '../hooks/useMessageQueue';
4+
import { Message } from '../context/notifications/messageQueueReducer';
55

66
interface AlertProps {
77
message: Message;
8+
clearMessage?: () => void;
89
}
910

1011
const useStyles = makeStyles({
@@ -36,7 +37,7 @@ export const SuccessAlert = ({ message }: AlertProps) => {
3637
);
3738
};
3839

39-
export const ErrorAlert = ({ message }: AlertProps) => {
40+
export const ErrorAlert = ({ message, clearMessage }: AlertProps) => {
4041
const styles = useStyles();
4142
return (
4243
<Alert variant="error" className={styles.alert}>
@@ -45,7 +46,7 @@ export const ErrorAlert = ({ message }: AlertProps) => {
4546
<Button
4647
variant="tertiary"
4748
icon={<Close aria-hidden />}
48-
onClick={() => message.clear()}
49+
onClick={clearMessage}
4950
type="button"
5051
aria-label="Lukk feilmelding"
5152
/>
@@ -54,7 +55,7 @@ export const ErrorAlert = ({ message }: AlertProps) => {
5455
);
5556
};
5657

57-
export const WarningAlert = ({ message }: AlertProps) => {
58+
export const WarningAlert = ({ message, clearMessage }: AlertProps) => {
5859
const styles = useStyles();
5960
return (
6061
<Alert variant="warning" className={styles.alert}>
@@ -63,7 +64,7 @@ export const WarningAlert = ({ message }: AlertProps) => {
6364
<Button
6465
variant="tertiary"
6566
icon={<Close aria-hidden />}
66-
onClick={() => message.clear()}
67+
onClick={clearMessage}
6768
type="button"
6869
aria-label="Lukk advarsel"
6970
/>

packages/bygger/src/components/Navbar/components/NotificationDropdown.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ const NotificationDropdown = () => {
2424
<InternalHeader.Button as={Dropdown.Toggle} aria-label="Notifikasjoner">
2525
<InformationFilled color={navCssVariables.navWarning} fontSize="1.5rem" role="presentation" />
2626
</InternalHeader.Button>
27-
<Dropdown.Menu className={`${styles.notificationsMenu}`} onClose={() => clearAll()}>
27+
<Dropdown.Menu className={`${styles.notificationsMenu}`} onClose={clearAll}>
2828
<Dropdown.Menu.List>
2929
{messages.map(({ title, message, type, id, created }) => (
3030
<Alert key={id} variant={type} className={styles.messagePanel}>

packages/bygger/src/components/UserFeedback.tsx

+7-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { makeStyles } from '@navikt/skjemadigitalisering-shared-components';
22
import { useFeedbackMessages } from '../context/notifications/FeedbackContext';
3-
import { Message } from '../hooks/useMessageQueue';
3+
import { Message } from '../context/notifications/messageQueueReducer';
44
import { ErrorAlert, SuccessAlert, WarningAlert } from './MessageAlerts';
55

66
const useStyles = makeStyles({
@@ -15,24 +15,25 @@ const useStyles = makeStyles({
1515
});
1616

1717
const UserFeedback = () => {
18-
const feedbackMessages = useFeedbackMessages();
18+
const { messages, clearMessage } = useFeedbackMessages();
1919
const style = useStyles();
2020

2121
const renderUserFeedback = (message: Message) => {
22+
const removeMessage = () => clearMessage(message.id);
2223
switch (message.type) {
2324
case 'success':
24-
setTimeout(() => message.clear(), 5000);
25+
setTimeout(removeMessage, 5000);
2526
return <SuccessAlert key={message.id} message={message} />;
2627
case 'warning':
27-
return <WarningAlert key={message.id} message={message} />;
28+
return <WarningAlert key={message.id} message={message} clearMessage={removeMessage} />;
2829
case 'error':
29-
return <ErrorAlert key={message.id} message={message} />;
30+
return <ErrorAlert key={message.id} message={message} clearMessage={removeMessage} />;
3031
}
3132
};
3233

3334
return (
3435
<aside className={style.alertstripe} aria-live="polite">
35-
{feedbackMessages.map(renderUserFeedback)}
36+
{messages.map(renderUserFeedback)}
3637
</aside>
3738
);
3839
};

packages/bygger/src/context/notifications/FeebackContext.test.tsx

+4-4
Original file line numberDiff line numberDiff line change
@@ -55,17 +55,17 @@ describe('FeedbackContext', () => {
5555
});
5656

5757
it('returns emitted messages in reverse order of when it was added', () => {
58-
const messages = getLatest(onMessagesUpdated);
58+
const { messages } = getLatest(onMessagesUpdated);
5959
expect(messages[0].message).toBe('Warning message');
6060
expect(messages[1].message).toBe('Error message');
6161
expect(messages[2].message).toBe('Success message');
6262
});
6363

6464
it('returns an updated list when a message is removed', () => {
65-
const messages = getLatest(onMessagesUpdated);
65+
const { messages, clearMessage } = getLatest(onMessagesUpdated);
6666
expect(messages).toHaveLength(3);
67-
act(() => messages[1].clear());
68-
const updatedMessages = getLatest(onMessagesUpdated);
67+
act(() => clearMessage(messages[1].id));
68+
const { messages: updatedMessages } = getLatest(onMessagesUpdated);
6969
expect(updatedMessages).toHaveLength(2);
7070
});
7171
});

packages/bygger/src/context/notifications/FeedbackContext.tsx

+24-14
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
1-
import React, { createContext, useContext, useEffect } from 'react';
2-
import useMessageQueue, { Message } from '../../hooks/useMessageQueue';
1+
import React, { createContext, useContext, useEffect, useMemo, useReducer } from 'react';
2+
import messageQueueReducer, { Message } from './messageQueueReducer';
3+
4+
type FeedbackMessageContextValue = {
5+
messages: Message[];
6+
clearMessage: (id: string) => void;
7+
};
38

49
const defaultEmit = (_message: string) => {};
510

@@ -8,31 +13,36 @@ export const FeedbackEmitContext = createContext({
813
error: defaultEmit,
914
warning: defaultEmit,
1015
});
11-
const FeedbackMessageContext = createContext<Message[]>([]);
16+
const FeedbackMessageContext = createContext<FeedbackMessageContextValue>({ messages: [], clearMessage: () => {} });
1217

1318
const FeedbackProvider = ({ children }: { children: React.ReactElement }) => {
14-
const [messages, messageQueue] = useMessageQueue();
19+
const [messages, dispatch] = useReducer(messageQueueReducer, []);
1520

1621
useEffect(() => {
1722
const callback = (error: PromiseRejectionEvent) => {
1823
if (error?.reason?.message) {
19-
messageQueue.push({ message: error.reason.message, type: 'error' });
24+
dispatch({ type: 'ADD_MESSAGE', payload: { message: error.reason.message, type: 'error' } });
2025
}
2126
};
2227
window.addEventListener('unhandledrejection', callback);
2328
return () => window.removeEventListener('unhandledrejection', callback);
24-
}, [messageQueue]);
29+
}, []);
30+
31+
const emit = useMemo(
32+
() => ({
33+
success: (message: string) => dispatch({ type: 'ADD_MESSAGE', payload: { message, type: 'success' } }),
34+
warning: (message: string) => dispatch({ type: 'ADD_MESSAGE', payload: { message, type: 'warning' } }),
35+
error: (message: string) => dispatch({ type: 'ADD_MESSAGE', payload: { message, type: 'error' } }),
36+
}),
37+
[],
38+
);
2539

26-
const emit = {
27-
success: (message: string) => messageQueue.push({ message, type: 'success' }),
28-
warning: (message: string) => messageQueue.push({ message, type: 'warning' }),
29-
error: (message: string) => messageQueue.push({ message, type: 'error' }),
30-
};
40+
const clearMessage = (id: string) => dispatch({ type: 'REMOVE_MESSAGE', payload: { id } });
3141

3242
return (
33-
<FeedbackEmitContext.Provider value={emit}>
34-
<FeedbackMessageContext.Provider value={messages}>{children}</FeedbackMessageContext.Provider>
35-
</FeedbackEmitContext.Provider>
43+
<FeedbackMessageContext.Provider value={{ messages, clearMessage }}>
44+
<FeedbackEmitContext.Provider value={emit}>{children}</FeedbackEmitContext.Provider>
45+
</FeedbackMessageContext.Provider>
3646
);
3747
};
3848

packages/bygger/src/context/notifications/NotificationsContext.tsx

+9-7
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { useAppConfig } from '@navikt/skjemadigitalisering-shared-components';
22
import Pusher from 'pusher-js';
3-
import React, { createContext, useContext, useEffect, useMemo } from 'react';
4-
import useMessageQueue, { Message } from '../../hooks/useMessageQueue';
3+
import React, { createContext, useContext, useEffect, useMemo, useReducer } from 'react';
4+
import messageQueueReducer, { Message } from './messageQueueReducer';
55

66
export const CHANNEL = 'fyllut-deployment';
77
export const EVENT = { success: 'success', failure: 'failure' };
@@ -22,29 +22,31 @@ const createPusher = (config) => {
2222
};
2323

2424
const PusherNotificationsProvider = ({ children }: { children: React.ReactElement }) => {
25-
const [messages, messageQueue] = useMessageQueue();
25+
const [messages, dispatch] = useReducer(messageQueueReducer, []);
2626
const { config } = useAppConfig();
2727
const pusher = useMemo(() => createPusher(config), [config]);
2828

2929
useEffect(() => {
3030
const fyllutDeploymentChannel = pusher.subscribe(CHANNEL);
3131
fyllutDeploymentChannel.bind(EVENT.success, ({ title, message }) =>
32-
messageQueue.push({ title, message, type: 'success' }),
32+
dispatch({ type: 'ADD_MESSAGE', payload: { title, message, type: 'success' } }),
3333
);
3434
fyllutDeploymentChannel.bind(EVENT.failure, ({ title, message }) =>
35-
messageQueue.push({ title, message, type: 'error' }),
35+
dispatch({ type: 'ADD_MESSAGE', payload: { title, message, type: 'error' } }),
3636
);
3737
return () => {
3838
fyllutDeploymentChannel.unbind(EVENT.success);
3939
fyllutDeploymentChannel.unbind(EVENT.failure);
4040
pusher.unsubscribe(CHANNEL);
4141
pusher.disconnect();
4242
};
43-
}, [pusher, messageQueue]);
43+
}, [pusher]);
44+
45+
const clearAll = () => dispatch({ type: 'CLEAR_ALL' });
4446

4547
const value = {
4648
messages,
47-
clearAll: () => messageQueue.clearAll(),
49+
clearAll,
4850
};
4951

5052
return <PusherNotificationContext.Provider value={value}>{children}</PusherNotificationContext.Provider>;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { v4 as uuidv4 } from 'uuid';
2+
3+
type MessageType = 'success' | 'warning' | 'error';
4+
type MessageIn = { title?: string; message: string; type: MessageType };
5+
export type Message = MessageIn & { id: string; created: string };
6+
7+
type Action =
8+
| { type: 'ADD_MESSAGE'; payload: MessageIn }
9+
| { type: 'REMOVE_MESSAGE'; payload: { id: string } }
10+
| { type: 'CLEAR_ALL' };
11+
12+
const getInitValues = (): Pick<Message, 'id' | 'created'> => ({ id: uuidv4(), created: new Date().toLocaleString() });
13+
14+
const messageReducer = (state: Message[], action: Action): Message[] => {
15+
switch (action.type) {
16+
case 'ADD_MESSAGE':
17+
return [{ ...getInitValues(), ...action.payload }, ...state];
18+
case 'REMOVE_MESSAGE':
19+
return state.filter((message) => message.id !== action.payload.id);
20+
case 'CLEAR_ALL':
21+
return [];
22+
default:
23+
return state;
24+
}
25+
};
26+
27+
export default messageReducer;

packages/bygger/src/hooks/useMessageQueue.test.ts

-32
This file was deleted.

packages/bygger/src/hooks/useMessageQueue.ts

-34
This file was deleted.

0 commit comments

Comments
 (0)