Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(Toaster): fix incompatibility of different Toaster APIs #1987

Open
wants to merge 8 commits into
base: next
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@
"import/no-extraneous-dependencies": "off",
"import/consistent-type-specifier-style": ["error", "prefer-top-level"],
"@typescript-eslint/prefer-ts-expect-error": "error",
"@typescript-eslint/consistent-type-imports": ["error", {"prefer": "type-imports", "fixStyle": "separate-type-imports"}]
"@typescript-eslint/consistent-type-imports": ["error", {"prefer": "type-imports", "fixStyle": "separate-type-imports"}],
"complexity": "off"
},
"overrides": [
{
Expand Down
21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,26 @@
# Changelog

## [6.37.0](https://github.com/gravity-ui/uikit/compare/v6.36.0...v6.37.0) (2024-11-27)


### Features

* **Breadcrunbs:** allow items to be disabled independently ([#1962](https://github.com/gravity-ui/uikit/issues/1962)) ([301e4ab](https://github.com/gravity-ui/uikit/commit/301e4ab365639188e010390d5b19da1df13d75fa))
* **Select:** new label and value resize behaviour ([#1896](https://github.com/gravity-ui/uikit/issues/1896)) ([2be5eb8](https://github.com/gravity-ui/uikit/commit/2be5eb8dc21679154bbb924af5e1e1eefa8a7a58))

## [6.36.0](https://github.com/gravity-ui/uikit/compare/v6.35.2...v6.36.0) (2024-11-25)


### Features

* **PasswordInput:** add component ([#1745](https://github.com/gravity-ui/uikit/issues/1745)) ([2e7f2c7](https://github.com/gravity-ui/uikit/commit/2e7f2c731c8cb2fd08993fc30ffed8b06a5f0ea2))


### Bug Fixes

* **Select:** do not reserve space for clear if empty ([#1956](https://github.com/gravity-ui/uikit/issues/1956)) ([11dd537](https://github.com/gravity-ui/uikit/commit/11dd537feaa230133f8051fd6c370e6e3ec7d54f))
* **Toc:** correctly display content of no items.length and add event forward ([#1939](https://github.com/gravity-ui/uikit/issues/1939)) ([8d456c3](https://github.com/gravity-ui/uikit/commit/8d456c3d77d63674f20ebac82913d8a26c14f997))

## [6.35.2](https://github.com/gravity-ui/uikit/compare/v6.35.1...v6.35.2) (2024-11-14)


Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@gravity-ui/uikit",
"version": "6.35.2",
"version": "6.37.0",
"description": "Gravity UI base styling and components",
"keywords": [
"component",
Expand Down
1 change: 0 additions & 1 deletion src/components/Button/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,6 @@ const isButtonIconComponent = isOfType(ButtonIcon);
const isSpan = isOfType<{className?: string}>('span');
const buttonIconClassRe = RegExp(`^${b('icon')}($|\\s+\\w)`);

// eslint-disable-next-line complexity
function prepareChildren(children: React.ReactNode) {
const items = React.Children.toArray(children);

Expand Down
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
20 changes: 0 additions & 20 deletions src/components/Select/__tests__/Select.visual.test.tsx

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ $block: '.#{variables.$ns}select-clear';
align-items: center;
margin-inline-start: auto;
z-index: 1;
flex-shrink: 0;

&:focus-visible {
border: 1px solid var(--g-color-line-generic-active);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,22 +48,6 @@ $blockButton: '.#{variables.$ns}select-control__button';
}
}

@mixin block_clear_reserved_width() {
// reserving place for clear icon to fix width changing on displaying clear
#{$block}_has-clear:not(#{$block}_has-value)#{$block}_size_s & {
padding-inline-end: calc(#{variables.$s-height} + var(--_--text-right-padding));
}
#{$block}_has-clear:not(#{$block}_has-value)#{$block}_size_m & {
padding-inline-end: calc(#{variables.$m-height} + var(--_--text-right-padding));
}
#{$block}_has-clear:not(#{$block}_has-value)#{$block}_size_l & {
padding-inline-end: calc(#{variables.$l-height} + var(--_--text-right-padding));
}
#{$block}_has-clear:not(#{$block}_has-value)#{$block}_size_xl & {
padding-inline-end: calc(#{variables.$xl-height} + var(--_--text-right-padding));
}
}

@mixin block_clear_reserved_disabled_width() {
// reserving place for clear icon to fix width changing on displaying clear for disabled select
#{$block}_has-clear#{$block}_size_s #{$blockButton}_disabled & {
Expand Down Expand Up @@ -116,8 +100,10 @@ $blockButton: '.#{variables.$ns}select-control__button';
@include mixins.button-reset();
@include mixins.pin($blockButton, ('::before', '::after'), var(--_--border-radius));

display: inline-flex;
display: inline-grid;
grid-template-columns: auto auto;
align-items: center;
justify-content: flex-start;
overflow: hidden;
width: 100%;
height: 100%;
Expand Down Expand Up @@ -213,26 +199,22 @@ $blockButton: '.#{variables.$ns}select-control__button';
@include mixins.text-accent;
@include mixins.overflow-ellipsis();

flex-shrink: 0;
max-width: 50%;
margin-inline-end: 4px;
white-space: nowrap;
}

&__placeholder,
&__option-text {
@include mixins.overflow-ellipsis();
@include block_clear_reserved_disabled_width();

padding-inline-end: var(--_--text-right-padding);
}

&__option-text {
@include block_clear_reserved_disabled_width();
}

&__placeholder {
color: var(--g-color-text-hint);

#{$blockButton}:not(#{$blockButton}_disabled) & {
@include block_clear_reserved_width();
}
}

&__chevron-icon {
Expand Down
111 changes: 19 additions & 92 deletions src/components/Toaster/Provider/ToasterProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,105 +2,32 @@

import React from 'react';

import type {InternalToastProps, ToastProps, ToasterPublicMethods} from '../types';
import {getToastIndex} from '../utilities/getToastIndex';
import {hasToast} from '../utilities/hasToast';
import {removeToast} from '../utilities/removeToast';
import type {ToasterSingleton} from '../ToasterSingleton';
import type {InternalToastProps} from '../types';

import {ToasterContext} from './ToasterContext';
import {ToastsContext} from './ToastsContext';

type Props = React.PropsWithChildren<{}>;
type Props = React.PropsWithChildren<{
toaster: ToasterSingleton;
Copy link
Contributor

Choose a reason for hiding this comment

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

This is definitely breaking change, please change target branch to next

}>;

export const ToasterProvider = React.forwardRef<ToasterPublicMethods, Props>(
function ToasterProvider({children}: Props, ref) {
const [toasts, setToasts] = React.useState<InternalToastProps[]>([]);
export const ToasterProvider = ({toaster, children}: Props) => {
const [toasts, setToasts] = React.useState<InternalToastProps[]>([]);

const add = React.useCallback((toast: ToastProps) => {
const {name} = toast;
React.useEffect(() => {
const unsubscribe = toaster.subscribe(setToasts);

setToasts((toasts) => {
let nextToasts = toasts;
return () => {
unsubscribe();
};
}, [toaster]);

if (hasToast(toasts, name)) {
nextToasts = removeToast(toasts, name);
}

return [
...nextToasts,
{
...toast,
addedAt: Date.now(),
ref: React.createRef<HTMLDivElement>(),
},
];
});
}, []);

const remove = React.useCallback((toastName: ToastProps['name']) => {
setToasts((toasts) => {
return removeToast(toasts, toastName);
});
}, []);

const removeAll = React.useCallback(() => {
setToasts(() => []);
}, []);

const update = React.useCallback(
(toastName: ToastProps['name'], override: Partial<ToastProps>) => {
setToasts((toasts) => {
if (!hasToast(toasts, toastName)) {
return toasts;
}

const index = getToastIndex(toasts, toastName);

return [
...toasts.slice(0, index),
{
...toasts[index],
...override,
},
...toasts.slice(index + 1),
];
});
},
[],
);

const toastsRef = React.useRef<InternalToastProps[]>(toasts);
React.useEffect(() => {
toastsRef.current = toasts;
}, [toasts]);
const has = React.useCallback((toastName: ToastProps['name']) => {
return toastsRef.current ? hasToast(toastsRef.current, toastName) : false;
}, []);

const toasterContext = React.useMemo(() => {
return {
add,
remove,
removeAll,
update,
has,
};
}, [add, remove, removeAll, update, has]);

React.useImperativeHandle(ref, () => ({
add,
remove,
removeAll,
update,
has,
}));

return (
<ToasterContext.Provider value={toasterContext}>
<ToastsContext.Provider value={toasts}>{children}</ToastsContext.Provider>
</ToasterContext.Provider>
);
},
);
return (
<ToasterContext.Provider value={toaster}>
<ToastsContext.Provider value={toasts}>{children}</ToastsContext.Provider>
</ToasterContext.Provider>
);
};

ToasterProvider.displayName = 'ToasterProvider';
31 changes: 5 additions & 26 deletions src/components/Toaster/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@ Component for adjustable notifications.
```jsx
import React from 'react';
import ReactDOMClient from 'react-dom/client';
import {ToasterComponent, ToasterProvider} from '@gravity-ui/uikit';
import {Toaster, ToasterComponent, ToasterProvider} from '@gravity-ui/uikit';

const toaster = new Toaster();

const root = ReactDOMClient.createRoot(document.getElementById('root'));
root.render(
<ToasterProvider>
<ToasterProvider toaster={toaster}>
<App />
<ToasterComponent className="optional additional classes" />
</ToasterProvider>,
Expand Down Expand Up @@ -66,8 +68,6 @@ const FoobarWithToaster = withToaster()(FoobarComponent);
Toaster has singleton, so when it is initialized in different parts of the application, the same instance will be returned.
On initialization, it is possible to transmit a className that will be assigned to dom-element which wrap all toasts.

### React < 18

```js
import {Toaster} from '@gravity-ui/uikit';
const toaster = new Toaster();
Expand All @@ -79,34 +79,13 @@ or
import {toaster} from '@gravity-ui/uikit/toaster-singleton';
```

### React 18

```js
import ReactDOMClient from 'react-dom/client';
import {Toaster} from '@gravity-ui/uikit';
Toaster.injectReactDOMClient(ReactDOMClient);
const toaster = new Toaster();
```

or

```js
import {toaster} from '@gravity-ui/uikit/toaster-singleton-react-18';
```

## Constructor arguments

| Parameter | Type | Default | Description |
| :-------- | :-------- | :---------- | :-------------------------------------------------- |
| className | `string` | `undefined` | Custom class name to add to the component container |
| mobile | `boolean` | `false` | Configuration that manages mobile/desktop views |

## Methods

| Method name | Params | Description |
| :---------------------------- | :----------------- | :-------------------------------------------------------------------------------------------------------------------------------------------- |
| add(toastOptions) | `Object` | Creates a new notification |
| remove(name) | `string` | Manually deletes an existing notification |
| removeAll() | | Deletes all existing notifications |
| update(name, overrideOptions) | `string`, `Object` | Changes already rendered notification content. In `overrideOptions`, the following fields are optional: `title`, `type`, `content`, `actions` |
| has(name) | `string` | Checks fora toast with the given name in the list of displayed toasts |

Expand Down
Loading
Loading