diff --git a/packages/core/src/common/errors.ts b/packages/core/src/common/errors.ts index e89cd73f92..1bfa2fb3e5 100644 --- a/packages/core/src/common/errors.ts +++ b/packages/core/src/common/errors.ts @@ -105,9 +105,12 @@ export const SPINNER_WARN_CLASSES_SIZE = ns + ` Classes.SMALL/LARGE ar export const TOASTER_CREATE_NULL = ns + - ` Toaster.create() is not supported inside React lifecycle methods in React 16.` + + ` OverlayToaster.create() is not supported inside React lifecycle methods in React 16.` + ` See usage example on the docs site.`; -export const TOASTER_WARN_INLINE = ns + ` Toaster.create() ignores inline prop as it always creates a new element.`; +export const TOASTER_MAX_TOASTS_INVALID = + ns + ` maxToasts is set to an invalid number, must be greater than 0`; +export const TOASTER_WARN_INLINE = + ns + ` OverlayToaster.create() ignores inline prop as it always creates a new element.`; export const DIALOG_WARN_NO_HEADER_ICON = ns + ` iconName is ignored if title is omitted.`; export const DIALOG_WARN_NO_HEADER_CLOSE_BUTTON = @@ -115,6 +118,3 @@ export const DIALOG_WARN_NO_HEADER_CLOSE_BUTTON = export const DRAWER_ANGLE_POSITIONS_ARE_CASTED = ns + ` all angle positions are casted into pure position (TOP, BOTTOM, LEFT or RIGHT)`; - -export const TOASTER_MAX_TOASTS_INVALID = - ns + ` maxToasts is set to an invalid number, must be greater than 0`; diff --git a/packages/core/src/components/toast/toast.md b/packages/core/src/components/toast/toast.md index 8dee85ea4d..7f56dfac68 100644 --- a/packages/core/src/components/toast/toast.md +++ b/packages/core/src/components/toast/toast.md @@ -21,18 +21,18 @@ You can also apply the same visual intent styles to `Toast`s that you can to [`B @interface ToastProps -@### Toaster +@### OverlayToaster -The `Toaster` React component is a stateful container for a single list of toasts. Internally, it -uses [`Overlay`](#core/components/overlay) to manage children and transitions. It can be vertically +The __OverlayToaster__ component is a stateful container for a single list of toasts. Internally, it +uses the [Overlay](#core/components/overlay) component to manage children and transitions. It can be vertically aligned along the top or bottom edge of its container (new toasts will slide in from that edge) and horizontally aligned along the left edge, center, or right edge of its container. -There are three ways to use the `Toaster` component: +There are three ways to use __OverlayToaster__: -1. `Toaster.create(props)` static method returns a new `ToasterInstance` instance. Use the instance method `toaster.show()` to manipulate this instance. __(recommended)__ -1. `...`: Render a `` element with React `children`. -1. ` ref.show({ ...toast })} />`: Render a `` element and use the `ref` prop to access its instance methods. +1. `OverlayToaster.create(props)` static method returns a new `ToasterInstance` instance. Use the instance method `toaster.show()` to manipulate this instance. __(recommended)__ +1. `...`: Render a `` element with React `children`. +1. ` ref.show({ ...toast })} />`: Render a `` element and use the `ref` prop to access its instance methods.
Working with multiple toasters
@@ -45,7 +45,7 @@ You can have multiple toasters in a single application, but you must ensure that
Toaster focus
-`Toaster` always disables `Overlay`'s `enforceFocus` behavior (meaning that you're not blocked +__OverlayToaster__ always disables Overlay's `enforceFocus` behavior (meaning that you're not blocked from accessing other parts of the application while a toast is active), and by default also disables `autoFocus` (meaning that focus will not switch to a toast when it appears). You can enable `autoFocus` for an individual `Toaster` via a prop, if desired. @@ -53,30 +53,31 @@ enable `autoFocus` for an individual `Toaster` via a prop, if desired.
-@interface IToasterProps +@interface OverlayToasterProps @## Static usage -The `Toaster` component provides the static `create` method that returns a new `Toaster` instance, rendered into an -element attached to ``. A `Toaster` instance -has a collection of methods to show and hide toasts in its given container. +__OverlayToaster__ provides the static `create` method that returns a new `ToasterInstance`, rendered into an +element attached to ``. A toaster instance has a collection of methods to show and hide toasts in its given container. ```ts -Toaster.create(props?: IToasterProps, container = document.body): ToasterInstance +OverlayToaster.create(props?: IToasterProps, container = document.body): ToasterInstance ``` -The `Toaster` will be rendered into a new element appended to the given `container`. +The toaster will be rendered into a new element appended to the given `container`. The `container` determines which element toasts are positioned relative to; the default value of `` allows them to use the entire viewport. Note that the return type is `ToasterInstance`, which is a minimal interface that exposes only the instance -methods detailed below. It can be thought of as `Toaster` minus the `React.Component` methods, -because the `Toaster` should not be treated as a normal React component. +methods detailed below. It can be thought of as `OverlayToaster` minus the `React.Component` methods, +because the `OverlayToaster` should not be treated as a normal React component.
React 16 usage
-`Toaster.create()` will throw an error if invoked inside a component lifecycle method in React 16, as `ReactDOM.render()` will return -`null` resulting in an inaccessible toaster instance. See the second bullet point on the [React 16 release notes](https://reactjs.org/blog/2017/09/26/react-v16.0.html#breaking-changes) for more information. +`OverlayToaster.create()` will throw an error if invoked inside a component lifecycle method in React 16, +as `ReactDOM.render()` will return `null` resulting in an inaccessible toaster instance. See the second bullet +point on the [React 16 release notes](https://reactjs.org/blog/2017/09/26/react-v16.0.html#breaking-changes) +for more information.
@@ -84,17 +85,17 @@ because the `Toaster` should not be treated as a normal React component. @### Example -Your application can contain several `Toaster` instances and easily share them across the codebase as modules. +Your application can contain several `ToasterInstance`s and easily share them across the codebase as modules. The following code samples demonstrate our preferred pattern for intergrating a toaster into a React application: #### `toaster.ts` ```ts -import { Position, Toaster } from "@blueprintjs/core"; +import { OverlayToaster, Position } from "@blueprintjs/core"; /** Singleton toaster instance. Create separate instances for different options. */ -export const AppToaster = Toaster.create({ +export const AppToaster = OverlayToaster.create({ className: "recipe-toaster", position: Position.TOP, }); @@ -122,32 +123,32 @@ export class App extends React.PureComponent { @## React component usage -Render the `` component like any other element and supply `` elements as `children`. You can +Render the `` component like any other element and supply `` elements as `children`. You can optionally attach a `ref` handler to access the instance methods, but we strongly recommend using the -[`Toaster.create` static method](#core/components/toast.static-usage) documented above instead. Note that +[`OverlayToaster.create` static method](#core/components/toast.static-usage) documented above instead. Note that `children` and `ref` can be used together, but `children` will always appear _after_ toasts created with `ref.show()`. ```tsx -import { Button, Position, Toast, Toaster } from "@blueprintjs/core"; +import { Button, OverlayToaster, Position, Toast, ToasterInstance } from "@blueprintjs/core"; import * as React from "react"; class MyComponent extends React.PureComponent { public state = { toasts: [ /* ToastProps[] */ ] } - private toaster: Toaster; + private toaster: ToasterInstance; private refHandlers = { - toaster: (ref: Toaster) => this.toaster = ref, + toaster: (ref: ToasterInstance) => this.toaster = ref, }; public render() { return (
) } diff --git a/packages/core/src/components/toast/toaster.tsx b/packages/core/src/components/toast/toaster.tsx index 14e75b61eb..0ddc83e720 100644 --- a/packages/core/src/components/toast/toaster.tsx +++ b/packages/core/src/components/toast/toaster.tsx @@ -38,7 +38,7 @@ export type ToasterPosition = /** @deprecated use ToasterInstance */ export type IToaster = ToasterInstance; -/** Public API methods available on a `` component instance. */ +/** Public API methods available on a `` component instance. */ export interface ToasterInstance { /** * Shows a new toast to the user, or updates an existing toast corresponding to the provided key (optional). @@ -58,10 +58,11 @@ export interface ToasterInstance { } /** - * Props supported by the `` component. - * These props can be passed as an argument to the static `Toaster.create(props?, container?)` method. + * Props supported by the `OverlayToaster` component. + * + * These props can be passed as an argument to the static `OverlayToaster.create(props?, container?)` method. */ -export interface IToasterProps extends Props { +export interface OverlayToasterProps extends Props { /** * Whether a toast should acquire application focus when it first opens. * This is disabled by default so that toasts do not interrupt the user's flow. @@ -85,7 +86,7 @@ export interface IToasterProps extends Props { * Whether the toaster should be rendered into a new element attached to `document.body`. * If `false`, then positioning will be relative to the parent element. * - * This prop is ignored by `Toaster.create()` as that method always appends a new element + * This prop is ignored by `OverlayToaster.create()` as that method always appends a new element * to the container. * * @default true @@ -114,14 +115,17 @@ export interface IToasterState { } /** - * Toaster component. + * Default Toaster component which renders toasts inside an Overlay. * * @see https://blueprintjs.com/docs/#core/components/toast.toaster */ -export class Toaster extends AbstractPureComponent2 implements ToasterInstance { - public static displayName = `${DISPLAYNAME_PREFIX}.Toaster`; +export class OverlayToaster + extends AbstractPureComponent2 + implements ToasterInstance +{ + public static displayName = `${DISPLAYNAME_PREFIX}.OverlayToaster`; - public static defaultProps: IToasterProps = { + public static defaultProps: OverlayToasterProps = { autoFocus: false, canEscapeKeyClear: true, position: Position.TOP, @@ -132,16 +136,16 @@ export class Toaster extends AbstractPureComponent2( - , + const toaster = ReactDOM.render( + , containerElement, - ) as Toaster; + ) as OverlayToaster; if (toaster == null) { throw new Error(TOASTER_CREATE_NULL); } @@ -218,7 +222,7 @@ export class Toaster extends AbstractPureComponent2 { props: { lazy: false, usePortal: false }, }, OverlayToaster: { - skip: true, + props: { usePortal: false }, + children: React.createElement(Core.Toast, { message: "Toast" }), }, PanelStack: { props: { @@ -100,8 +101,7 @@ describe("Core isomorphic rendering", () => { children: requiredChild, }, Toaster: { - props: { usePortal: false }, - children: React.createElement(Core.Toast, { message: "Toast" }), + skip: true, }, }); }); diff --git a/packages/core/test/toast/toasterTests.tsx b/packages/core/test/toast/toasterTests.tsx index e67d96b315..c3b83bdb78 100644 --- a/packages/core/test/toast/toasterTests.tsx +++ b/packages/core/test/toast/toasterTests.tsx @@ -22,17 +22,17 @@ import { spy } from "sinon"; import { expectPropValidationError } from "@blueprintjs/test-commons"; -import { Classes, Toaster, ToasterInstance } from "../../src"; +import { Classes, OverlayToaster, ToasterInstance } from "../../src"; import { TOASTER_CREATE_NULL, TOASTER_MAX_TOASTS_INVALID } from "../../src/common/errors"; -describe("Toaster", () => { +describe("OverlayToaster", () => { let testsContainerElement: HTMLElement; let toaster: ToasterInstance; before(() => { testsContainerElement = document.createElement("div"); document.documentElement.appendChild(testsContainerElement); - toaster = Toaster.create({}, testsContainerElement); + toaster = OverlayToaster.create({}, testsContainerElement); }); afterEach(() => { @@ -129,7 +129,7 @@ describe("Toaster", () => { it("reusing props object does not produce React errors", () => { const errorSpy = spy(console, "error"); - // if Toaster doesn't clone the props object before injecting key then there will be a + // if OverlayToaster doesn't clone the props object before injecting key then there will be a // React error that both toasts have the same key, because both instances refer to the // same object. const toast = { message: "repeat" }; @@ -139,7 +139,7 @@ describe("Toaster", () => { }); it("does not exceed the maximum toast limit set", () => { - toaster = Toaster.create({ maxToasts: 3 }); + toaster = OverlayToaster.create({ maxToasts: 3 }); toaster.show({ message: "one" }); toaster.show({ message: "two" }); toaster.show({ message: "three" }); @@ -149,7 +149,7 @@ describe("Toaster", () => { describe("validation", () => { it("throws an error when max toast is set to a number less than 1", () => { - expectPropValidationError(Toaster, { maxToasts: 0 }, TOASTER_MAX_TOASTS_INVALID); + expectPropValidationError(OverlayToaster, { maxToasts: 0 }, TOASTER_MAX_TOASTS_INVALID); }); }); @@ -157,7 +157,7 @@ describe("Toaster", () => { before(() => { testsContainerElement = document.createElement("div"); document.documentElement.appendChild(testsContainerElement); - toaster = Toaster.create({ autoFocus: true }, testsContainerElement); + toaster = OverlayToaster.create({ autoFocus: true }, testsContainerElement); }); it("focuses inside toast container", done => { @@ -179,7 +179,7 @@ describe("Toaster", () => { public componentDidMount() { try { - Toaster.create(); + OverlayToaster.create(); } catch (err: any) { assert.equal(err.message, TOASTER_CREATE_NULL); } diff --git a/packages/docs-app/src/examples/core-examples/alertExample.tsx b/packages/docs-app/src/examples/core-examples/alertExample.tsx index 8c2cce5a48..54900148e0 100644 --- a/packages/docs-app/src/examples/core-examples/alertExample.tsx +++ b/packages/docs-app/src/examples/core-examples/alertExample.tsx @@ -16,7 +16,7 @@ import * as React from "react"; -import { Alert, Button, H5, Intent, Switch, Toaster, ToasterInstance } from "@blueprintjs/core"; +import { Alert, Button, H5, Intent, OverlayToaster, Switch, ToasterInstance } from "@blueprintjs/core"; import { Example, ExampleProps, handleBooleanChange } from "@blueprintjs/docs-theme"; import { IBlueprintExampleData } from "../../tags/types"; @@ -106,7 +106,7 @@ export class AlertExample extends React.PureComponent - (this.toaster = ref)} /> + (this.toaster = ref)} /> ); } diff --git a/packages/docs-app/src/examples/core-examples/toastExample.tsx b/packages/docs-app/src/examples/core-examples/toastExample.tsx index 5ae8e047bf..554fb723cb 100644 --- a/packages/docs-app/src/examples/core-examples/toastExample.tsx +++ b/packages/docs-app/src/examples/core-examples/toastExample.tsx @@ -23,13 +23,14 @@ import { H5, HTMLSelect, Intent, - IToasterProps, Label, NumericInput, + OverlayToaster, + OverlayToasterProps, Position, ProgressBar, Switch, - Toaster, + ToasterInstance, ToasterPosition, ToastProps, } from "@blueprintjs/core"; @@ -48,8 +49,8 @@ const POSITIONS = [ Position.BOTTOM_RIGHT, ]; -export class ToastExample extends React.PureComponent, IToasterProps> { - public state: IToasterProps = { +export class ToastExample extends React.PureComponent, OverlayToasterProps> { + public state: OverlayToasterProps = { autoFocus: false, canEscapeKeyClear: true, position: Position.TOP, @@ -126,10 +127,10 @@ export class ToastExample extends React.PureComponent (this.toaster = ref), + toaster: (ref: ToasterInstance) => (this.toaster = ref), }; private progressToastInterval?: number; @@ -147,7 +148,7 @@ export class ToastExample extends React.PureComponent {this.TOAST_BUILDERS.map(this.renderToastDemo, this)}