Skip to content

Commit

Permalink
[core] fix Toaster type backcompat, better OverlayToaster docs (#6165)
Browse files Browse the repository at this point in the history
  • Loading branch information
adidahiya committed May 17, 2023
1 parent 86dc351 commit b9d7d5f
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 10 deletions.
32 changes: 27 additions & 5 deletions packages/core/src/components/toast/toast.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,38 @@ You can also apply the same visual intent styles to `Toast`s that you can to [`B

@### OverlayToaster

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
The __OverlayToaster__ component (previously named __Toaster__) 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
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 __OverlayToaster__:

1. `OverlayToaster.create(props)` static method returns a new `ToasterInstance` instance. Use the instance method `toaster.show()` to manipulate this instance. __(recommended)__
1. `<OverlayToaster><Toast />...</OverlayToaster>`: Render a `<OverlayToaster>` element with React `children`.
1. `<OverlayToaster ref={(ref: ToasterInstance) => ref.show({ ...toast })} />`: Render a `<OverlayToaster>` element and use the `ref` prop to access its instance methods.
1. __Recommended__: use the `OverlayToaster.create()` static method to access a new `ToasterInstance`:
```ts
const myToaster: ToasterInstance = OverlayToaster.create({ position: "bottom" });
myToaster.show({ ...toastOptions });
```
2. Render an `<OverlayToaster>` with `<Toast>` children:
```ts
render(
<OverlayToaster>
<Toast {...toastOptions} />
</OverlayToaster>,
targetElement,
);
```
3. Use a ref callback or object to access toaster instance methods.
- Example with ref callback:
```ts
render(<OverlayToaster ref={(ref: ToasterInstance | null) => ref?.show({ ...toastOptions })} />, targetElement);
```
- Example with ref object (note that React type constraints require us to use the more specific `OverlayToaster` type):
```ts
const myToaster = React.createRef<OverlayToaster>();
render(<OverlayToaster ref={myToaster} />, targetElement);
myToaster.current?.show({ ...toastOptions });
```

<div class="@ns-callout @ns-intent-primary @ns-icon-info-sign">
<h5 class="@ns-heading">Working with multiple toasters</h5>
Expand Down
6 changes: 2 additions & 4 deletions packages/core/src/components/toast/toaster.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ export class OverlayToaster
const toaster = ReactDOM.render<OverlayToasterProps>(
<OverlayToaster {...props} usePortal={false} />,
containerElement,
) as OverlayToaster;
) as OverlayToaster as ToasterInstance;
if (toaster == null) {
throw new Error(TOASTER_CREATE_NULL);
}
Expand Down Expand Up @@ -276,8 +276,6 @@ export class OverlayToaster
export const Toaster = OverlayToaster;
/** @deprecated use the new, more specific type `ToasterInstance` instead (forwards-compatible with v5) */
// eslint-disable-next-line @typescript-eslint/no-redeclare
export type Toaster = ToasterInstance;
// eslint-disable-next-line deprecation/deprecation
Toaster.displayName = `${DISPLAYNAME_PREFIX}.Toaster`;
export type Toaster = OverlayToaster;
/** @deprecated use `OverlayToasterProps` instead */
export type IToasterProps = OverlayToasterProps;
39 changes: 38 additions & 1 deletion packages/core/test/toast/toasterTests.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { spy } from "sinon";

import { expectPropValidationError } from "@blueprintjs/test-commons";

import { Classes, OverlayToaster, ToasterInstance } from "../../src";
import { Classes, OverlayToaster, Toaster, ToasterInstance } from "../../src";
import { TOASTER_CREATE_NULL, TOASTER_MAX_TOASTS_INVALID } from "../../src/common/errors";

describe("OverlayToaster", () => {
Expand Down Expand Up @@ -187,4 +187,41 @@ describe("OverlayToaster", () => {
}
mount(React.createElement(LifecycleToaster));
});

it("ref callback is assignable to ToasterInstance", () => {
const handleToasterRef = (_toaster: ToasterInstance | null) => {
/* no-op */
};
const refSpy = spy(handleToasterRef);
mount(<OverlayToaster ref={refSpy} />);
assert.isTrue(refSpy.calledOnce);
});

it("ref object is assignable to OverlayToaster", () => {
const toasterInstance = React.createRef<ToasterInstance>();
const overlayToaster = React.createRef<OverlayToaster>();
// @ts-expect-error
const invalidToaster = <OverlayToaster ref={toasterInstance} />;
mount(<OverlayToaster ref={overlayToaster} />);
assert.isDefined(overlayToaster.current);
});

// this type compatibility test can be removed in Blueprint v5.0
it("<Toaster> ref callback is backwards-compatible with (deprecated) Toaster type", () => {
// N.B. without `export type Toaster = ...`, the following `Toaster` reference will be invalid
const deprecatedToasterRef = React.createRef<Toaster>();
// N.B. `Toaster` type needs to be identical to the `OverlayToaster` type for this ref to type check properly
mount(<Toaster ref={deprecatedToasterRef} />);
});

// this type compatibility test can be removed in Blueprint v5.0
it("<Toaster> ref callback is compatible with ToasterInstance type", () => {
mount(
<Toaster
ref={(_instance: ToasterInstance | null) => {
/* no-op */
}}
/>,
);
});
});

1 comment on commit b9d7d5f

@adidahiya
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[core] fix Toaster type backcompat, better OverlayToaster docs (#6165)

Build artifact links for this commit: documentation | landing | table | demo

This is an automated comment from the deploy-preview CircleCI job.

Please sign in to comment.