Skip to content

Commit

Permalink
Allow Snackbar to be persistent (#426)
Browse files Browse the repository at this point in the history
* allow snakbar to be persistent

* change useSnackMessage hook to include closeSnackbar function
* allow message and header to be null

---------

Signed-off-by: jamal-khey <myjamal89@gmail.com>
  • Loading branch information
jamal-khey committed May 15, 2024
1 parent acc785d commit d975c09
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 73 deletions.
50 changes: 47 additions & 3 deletions demo/src/app.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,13 @@ const getMuiTheme = (theme) => {
}
};

const style = {
button: {
float: 'left',
margin: '5px',
},
};

/**
* @param {import('@mui/material/styles').Theme} theme Theme from ThemeProvider
*/
Expand Down Expand Up @@ -194,7 +201,7 @@ function SnackErrorButton() {
<Button
variant="contained"
color="error"
style={{ float: 'left', margin: '5px' }}
style={style.button}
onClick={() => {
snackError({
messageTxt: 'Snack error message',
Expand All @@ -213,7 +220,7 @@ function SnackWarningButton() {
<Button
variant="contained"
color="warning"
style={{ float: 'left', margin: '5px' }}
style={style.button}
onClick={() => {
snackWarning({
messageTxt: 'Snack warning message',
Expand All @@ -232,7 +239,7 @@ function SnackInfoButton() {
<Button
variant="contained"
color="info"
style={{ float: 'left', margin: '5px' }}
style={style.button}
onClick={() => {
snackInfo({
messageTxt: 'Snack info message',
Expand All @@ -245,6 +252,41 @@ function SnackInfoButton() {
);
}

function PermanentSnackButton() {
const { snackInfo, closeSnackbar } = useSnackMessage();
const [snackKey, setSnackKey] = useState(undefined);
return (
<>
<Button
variant="contained"
color="info"
style={style.button}
onClick={() => {
const key = snackInfo({
messageTxt: 'Permanent Snack info message',
headerTxt: 'Header',
persist: true,
});
setSnackKey(key);
}}
>
permanent snack
</Button>
<Button
variant="contained"
color="info"
style={style.button}
onClick={() => {
closeSnackbar(snackKey);
setSnackKey(undefined);
}}
>
close snack
</Button>
</>
);
}

const validateUser = (user) => {
// change to false to simulate user unauthorized access
return new Promise((resolve) =>
Expand Down Expand Up @@ -539,9 +581,11 @@ const AppContent = ({ language, onLanguageClick }) => {
{testIcons()}
<hr />

<PermanentSnackButton />
<SnackErrorButton />
<SnackWarningButton />
<SnackInfoButton />

<Button
variant="contained"
style={{
Expand Down
130 changes: 60 additions & 70 deletions src/hooks/useSnackMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,17 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

import React, { useCallback } from 'react';
import { BaseVariant, EnqueueSnackbar, useSnackbar } from 'notistack';
import { MutableRefObject, useCallback } from 'react';
import {
BaseVariant,
OptionsObject,
closeSnackbar,
useSnackbar,
} from 'notistack';
import { useIntlRef } from './useIntlRef';
import { IntlShape } from 'react-intl';

interface SnackInputs {
interface SnackInputs extends Omit<OptionsObject, 'variant' | 'style'> {
messageTxt?: string;
messageId?: string;
messageValues?: { [key: string]: string };
Expand All @@ -23,85 +28,64 @@ export interface UseSnackMessageReturn {
snackError: (snackInputs: SnackInputs) => void;
snackWarning: (snackInputs: SnackInputs) => void;
snackInfo: (snackInputs: SnackInputs) => void;
closeSnackbar: typeof closeSnackbar;
}

export function useSnackMessage(): UseSnackMessageReturn {
const intlRef = useIntlRef();
const { enqueueSnackbar } = useSnackbar();
const { enqueueSnackbar, closeSnackbar } = useSnackbar();

const enqueue = useCallback(
(snackInputs: SnackInputs, variant: BaseVariant) => {
const message = makeMessage(intlRef, snackInputs);
if (message === null) {
return;
}
return enqueueSnackbar(message, {
...snackInputs,
variant: variant,
style: { whiteSpace: 'pre-line' },
});
},
[enqueueSnackbar, intlRef]
);

/*
There is two kind of messages : the message itself (bottom of snackbar), and the header (top of snackbar).
As inputs, you can give either a text message, or an ID with optional values (for translation with intl).
snackInputs: {
messageTxt,
messageId,
messageValues,
headerTxt,
headerId,
headerValues,
snackInputs: {
messageTxt,
messageId,
messageValues,
headerTxt,
headerId,
headerValues,
key?, // optional key to close the snackbar
persist
}
*/
const snackError = useCallback(
(snackInputs: SnackInputs) =>
makeSnackbar(snackInputs, intlRef, enqueueSnackbar, 'error', true),
[enqueueSnackbar, intlRef]
(snackInputs: SnackInputs) => enqueue(snackInputs, 'error'),
[enqueue]
);

/* see snackError */
const snackWarning = useCallback(
(snackInputs: SnackInputs) =>
makeSnackbar(
snackInputs,
intlRef,
enqueueSnackbar,
'warning',
true
),
[enqueueSnackbar, intlRef]
(snackInputs: SnackInputs) => enqueue(snackInputs, 'warning'),
[enqueue]
);

/* see snackError */
const snackInfo = useCallback(
(snackInputs: SnackInputs) =>
makeSnackbar(snackInputs, intlRef, enqueueSnackbar, 'info', false),
[enqueueSnackbar, intlRef]
(snackInputs: SnackInputs) => enqueue(snackInputs, 'info'),
[enqueue]
);

return { snackError, snackInfo, snackWarning };
}

function makeSnackbar(
snackInputs: SnackInputs,
intlRef: React.MutableRefObject<IntlShape>,
enqueueSnackbar: EnqueueSnackbar,
level: BaseVariant,
persistent: boolean
) {
const message = checkAndTranslateIfNecessary(
intlRef,
snackInputs.messageTxt,
snackInputs.messageId,
snackInputs.messageValues
);
const header = checkAndTranslateIfNecessary(
intlRef,
snackInputs.headerTxt,
snackInputs.headerId,
snackInputs.headerValues
);
if (message !== null && header !== null) {
displayMessageWithSnackbar(
message,
header,
enqueueSnackbar,
level,
persistent
);
}
return { snackError, snackInfo, snackWarning, closeSnackbar };
}

function checkAndTranslateIfNecessary(
intlRef: React.MutableRefObject<IntlShape>,
intlRef: MutableRefObject<IntlShape>,
txt?: string,
id?: string,
values?: any
Expand All @@ -126,13 +110,23 @@ function checkInputs(txt?: string, id?: string, values?: any) {
}
}

function displayMessageWithSnackbar(
message: string,
header: string,
enqueueSnackbar: EnqueueSnackbar,
level: BaseVariant,
persistent: boolean
) {
function makeMessage(
intlRef: MutableRefObject<IntlShape>,
snackInputs: SnackInputs
): string | null {
const message = checkAndTranslateIfNecessary(
intlRef,
snackInputs.messageTxt,
snackInputs.messageId,
snackInputs.messageValues
);
const header = checkAndTranslateIfNecessary(
intlRef,
snackInputs.headerTxt,
snackInputs.headerId,
snackInputs.headerValues
);

let fullMessage = '';
if (header) {
fullMessage += header;
Expand All @@ -143,9 +137,5 @@ function displayMessageWithSnackbar(
}
fullMessage += message;
}
enqueueSnackbar(fullMessage, {
variant: level,
persist: persistent,
style: { whiteSpace: 'pre-line' },
});
return fullMessage;
}

0 comments on commit d975c09

Please sign in to comment.