Skip to content

Commit

Permalink
fix(useToaster): fix the container parameter of toaster.push does n…
Browse files Browse the repository at this point in the history
…ot work
  • Loading branch information
simonguo committed Dec 3, 2024
1 parent fbdc928 commit 77b7f95
Show file tree
Hide file tree
Showing 6 changed files with 141 additions and 51 deletions.
4 changes: 4 additions & 0 deletions docs/pages/components/toaster/en-US/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ Toaster display brief, temporary notifications of actions, errors, or other even

<!--{include:`custom.md`}-->

### Custom Container

<!--{include:`custom-container.md`}-->

## Hooks

### useToaster
Expand Down
50 changes: 50 additions & 0 deletions docs/pages/components/toaster/fragments/custom-container.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<!--start-code-->

```js
import React from 'react';
import { useToaster, ButtonToolbar, SelectPicker, Button } from 'rsuite';

const Toast = React.forwardRef((props, ref) => {
const { type, placement, duration, children, onClose, ...rest } = props;

return (
<div
ref={ref}
{...rest}
style={{ padding: 10, background: '#fff', borderRadius: 4, marginTop: 10 }}
>
{children}
<hr />
<button onClick={onClose}>Close</button>
</div>
);
});

const App = () => {
const toaster = useToaster();
const container = React.useRef();

return (
<div ref={container}>
<Button
onClick={() =>
toaster.push(
<Toast>
<h4>Custom Toast</h4>
<p>This is a custom toast with a close button.</p>
</Toast>,
{ container: () => container.current }
)
}
appearance="primary"
>
Push
</Button>
</div>
);
};

ReactDOM.render(<App />, document.getElementById('root'));
```

<!--end-code-->
4 changes: 4 additions & 0 deletions docs/pages/components/toaster/zh-CN/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ Toaster 用于在应用程序中显示简短的、临时的通知,用于表示

<!--{include:`custom.md`}-->

### 自定义容器

<!--{include:`custom-container.md`}-->

## Hooks

### useToaster
Expand Down
55 changes: 33 additions & 22 deletions src/toaster/ToastContainer.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, { useState, useImperativeHandle, useRef, useCallback } from 'react';
import { createPortal } from 'react-dom';
import PropTypes from 'prop-types';
import kebabCase from 'lodash/kebabCase';
import Transition from '../Animation/Transition';
Expand Down Expand Up @@ -56,6 +57,7 @@ export interface ToastContainerProps extends WithAsProps {
interface PushOptions {
duration?: number;
mouseReset?: boolean;
container?: HTMLElement | (() => HTMLElement);
}

export interface ToastContainerInstance {
Expand Down Expand Up @@ -96,12 +98,12 @@ const useMessages = () => {
);

const push = useCallback((message, options?: PushOptions) => {
const { duration, mouseReset = true } = options || {};
const { duration, mouseReset = true, container } = options || {};
const key = guid();

setMessages(prevMessages => [
...prevMessages,
{ key, visible: true, node: message, duration, mouseReset }
{ key, visible: true, node: message, duration, mouseReset, container }
]);

return key;
Expand Down Expand Up @@ -161,28 +163,37 @@ const ToastContainer: ToastContainerComponent = React.forwardRef(
useImperativeHandle(ref, () => ({ root: rootRef.current, push, clear, remove }));

const elements = messages.map(item => {
const { mouseReset, duration, node } = item;
const { mouseReset, duration, node, container } = item;
const toastWithTransition = (
<Transition
in={item.visible}
exitedClassName={rootPrefix('toast-fade-exited')}
exitingClassName={rootPrefix('toast-fade-exiting')}
enteringClassName={rootPrefix('toast-fade-entering')}
enteredClassName={rootPrefix('toast-fade-entered')}
timeout={300}
>
{(transitionProps, ref) => {
const { className: transitionClassName, ...rest } = transitionProps;
return React.cloneElement(node, {
...rest,
ref,
duration,
onClose: createChainedFunction(node.props?.onClose, () => remove(item.key)),
className: merge(rootPrefix('toast'), node.props?.className, transitionClassName)
});
}}
</Transition>
);

return (
<ToastContext.Provider value={{ usedToaster: true, mouseReset, duration }} key={item.key}>
<Transition
in={item.visible}
exitedClassName={rootPrefix('toast-fade-exited')}
exitingClassName={rootPrefix('toast-fade-exiting')}
enteringClassName={rootPrefix('toast-fade-entering')}
enteredClassName={rootPrefix('toast-fade-entered')}
timeout={300}
>
{(transitionProps, ref) => {
const { className: transitionClassName, ...rest } = transitionProps;
return React.cloneElement(node, {
...rest,
ref,
duration,
onClose: createChainedFunction(node.props?.onClose, () => remove(item.key)),
className: merge(rootPrefix('toast'), node.props?.className, transitionClassName)
});
}}
</Transition>
{container
? createPortal(
toastWithTransition,
typeof container === 'function' ? container() : container
)
: toastWithTransition}
</ToastContext.Provider>
);
});
Expand Down
58 changes: 29 additions & 29 deletions src/toaster/styles/index.less
Original file line number Diff line number Diff line change
Expand Up @@ -23,30 +23,6 @@
box-shadow: var(--rs-shadow-overlay);
}

// Animations
.rs-toast-fade-entered,
.rs-toast-fade-exiting {
animation-fill-mode: forwards;
}

.rs-toast-fade-entered {
animation-duration: 0.4s;
animation-timing-function: cubic-bezier(0.99, 0.44, 0.44, 1.35);
}

.rs-toast-fade-exited,
.rs-toast-fade-entering {
opacity: 0;
}

// The same to @keyframe notificationMoveOut 100%.
.rs-toast-fade-exited {
transform-origin: 0 0;
transform: scaleY(0.8);
max-height: 0;
overflow: hidden;
}

&-top-center,
&-bottom-center {
align-items: center;
Expand Down Expand Up @@ -89,10 +65,34 @@
&-bottom-end {
bottom: @toast-spacing;
}
}

.rs-toast-fade-exiting {
animation-duration: 0.3s;
animation-timing-function: cubic-bezier(0.64, 0.65, 0.57, 1.13);
animation-name: notificationMoveOut;
}
// Animations
.rs-toast-fade-entered,
.rs-toast-fade-exiting {
animation-fill-mode: forwards;
}

.rs-toast-fade-entered {
animation-duration: 0.4s;
animation-timing-function: cubic-bezier(0.99, 0.44, 0.44, 1.35);
}

.rs-toast-fade-exited,
.rs-toast-fade-entering {
opacity: 0;
}

// The same to @keyframe notificationMoveOut 100%.
.rs-toast-fade-exited {
transform-origin: 0 0;
transform: scaleY(0.8);
max-height: 0;
overflow: hidden;
}

.rs-toast-fade-exiting {
animation-duration: 0.3s;
animation-timing-function: cubic-bezier(0.64, 0.65, 0.57, 1.13);
animation-name: notificationMoveOut;
}
21 changes: 21 additions & 0 deletions src/useToaster/test/useToasterSpec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -163,4 +163,25 @@ describe('useToaster', () => {

expect(toaster1).to.equal(toaster2);
});

it('Should push a message to a custom container', () => {
const container = React.createRef<HTMLDivElement>();
const App = React.forwardRef<HTMLDivElement, any>((props, ref) => {
const { children, ...rest } = props;
return (
<CustomProvider {...rest} ref={ref}>
<div role="alert" ref={container} />
{children}
</CustomProvider>
);
});

const toaster = renderHook(() => useToaster(), { wrapper: App }).result.current;

act(() => {
toaster.push(<div>message</div>, { container: container.current });
});

expect(container.current).to.have.text('message');
});
});

0 comments on commit 77b7f95

Please sign in to comment.