Skip to content

Commit 190c642

Browse files
committed
feat(ToastProvider): new props to control position of toast
1 parent 5365c83 commit 190c642

File tree

3 files changed

+89
-4
lines changed

3 files changed

+89
-4
lines changed

src/components/ToastProvider/ToastProvider.stories.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,13 @@ export const Basic = () => (
5252
'ToastProvider',
5353
) as CommonToastProps['message']) || undefined
5454
}
55+
position={select(
56+
'position',
57+
['top-left', 'top', 'top-right', 'bottom-left', 'bottom', 'bottom-right'],
58+
'top-right',
59+
'ToastProvider',
60+
)}
61+
customPadding={boolean('has customPadding', false, 'ToastProvider') ? 100 : undefined}
5562
>
5663
<DemoButtons />
5764
</ToastProvider>

src/components/ToastProvider/ToastProvider.tsx

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,22 @@ export interface ToastProviderProps {
1616
* they are cleared manually.
1717
*/
1818
noTimeout?: CommonToastProps['message'] | CommonToastProps['message'][];
19+
/**
20+
* Specify the location where the toasts will appear from.
21+
* @default "top-right"
22+
*/
23+
position?: 'top-left' | 'top' | 'top-right' | 'bottom-left' | 'bottom' | 'bottom-right';
24+
/**
25+
* If provided, will position the toast this number of pixels away from the
26+
* edge of the screen. This only applies on the y axis, you will have to use
27+
* `className` to add any other styles.
28+
*/
29+
customPadding?: number;
30+
/**
31+
* Custom classname for the div that wraps the toast. This can be used to add
32+
* custom padding or override the animations.
33+
*/
34+
className?: string;
1935
}
2036

2137
interface ToastQueueItem extends AddToast {
@@ -24,7 +40,14 @@ interface ToastQueueItem extends AddToast {
2440

2541
let toastIdSequence = 1;
2642

27-
export const ToastProvider: FC<ToastProviderProps> = ({ defaultTimeout, noTimeout, children }) => {
43+
export const ToastProvider: FC<ToastProviderProps> = ({
44+
defaultTimeout,
45+
noTimeout,
46+
position = 'top-right',
47+
customPadding,
48+
className,
49+
children,
50+
}) => {
2851
const [toasts, setToasts] = useState<ToastQueueItem[]>([]);
2952
const [closing, setClosing] = useState(false);
3053

@@ -103,13 +126,29 @@ export const ToastProvider: FC<ToastProviderProps> = ({ defaultTimeout, noTimeou
103126
[add, remove, toasts],
104127
);
105128

129+
const padding = useMemo(() => {
130+
if (customPadding === undefined) return undefined;
131+
const isTop = ['top-left', 'top', 'top-right'].includes(position);
132+
return customPadding !== undefined
133+
? {
134+
paddingTop: isTop ? customPadding : undefined,
135+
paddingBottom: isTop ? undefined : customPadding,
136+
}
137+
: undefined;
138+
}, [customPadding, position]);
139+
106140
const toastProviderClass = classnames('ui__toastProvider', { 'ui__toastProvider--closing': closing });
141+
const toastClass = classnames(
142+
'ui__toastProvider__toast',
143+
`ui__toastProvider__toast--position-${position}`,
144+
className,
145+
);
107146

108147
return (
109148
<ToastContext.Provider value={value}>
110149
{toastId && (
111150
<Overlay>
112-
<div className="ui__toastProvider__toast" key={toastId}>
151+
<div className={toastClass} key={toastId} style={padding}>
113152
<Toast
114153
{...toastProps}
115154
role={toastProps.message === 'error' ? 'alert' : 'status'}
Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,61 @@
1+
%toastProvider-bottom {
2+
position: absolute;
3+
bottom: 0;
4+
left: 0;
5+
right: 0;
6+
transform: translateY(100%);
7+
}
8+
19
.ui__toastProvider {
210
opacity: 1;
311
@include opacity-transition;
12+
position: relative;
413

514
&--closing {
615
opacity: 0;
716
}
817

918
&__toast {
1019
display: flex;
11-
justify-content: flex-end;
1220
padding: $padding;
1321
pointer-events: none !important;
1422

1523
> * {
1624
pointer-events: all;
1725
}
1826

19-
transform: translateY(-100%);
2027
@include slide-in;
28+
29+
&--position {
30+
&-top-left {
31+
transform: translateY(-100%);
32+
justify-content: flex-start;
33+
}
34+
35+
&-top {
36+
transform: translateY(-100%);
37+
justify-content: center;
38+
}
39+
40+
&-top-right {
41+
transform: translateY(-100%);
42+
justify-content: flex-end;
43+
}
44+
45+
&-bottom-left {
46+
@extend %toastProvider-bottom;
47+
justify-content: flex-start;
48+
}
49+
50+
&-bottom {
51+
@extend %toastProvider-bottom;
52+
justify-content: center;
53+
}
54+
55+
&-bottom-right {
56+
@extend %toastProvider-bottom;
57+
justify-content: flex-end;
58+
}
59+
}
2160
}
2261
}

0 commit comments

Comments
 (0)