Merge pull request #29031 from GabDug/fix/dismissable-banner-layout-shift

fix(core-components): Avoid Layout Shift for DismissableBanner when using a storageApi with latency (e.g. user-settings-backend)
This commit is contained in:
Fredrik Adelöw
2025-03-13 15:30:44 +01:00
committed by GitHub
2 changed files with 16 additions and 15 deletions
+7
View File
@@ -0,0 +1,7 @@
---
'@backstage/core-components': patch
---
Avoid Layout Shift for `DismissableBanner` when using a `storageApi` with latency (e.g. `user-settings-backend`)
Properly handle the `unknown` state of the `storageApi`. There's a trade-off: this may lead to some Layout Shift if the banner has not been dismissed, but once it has been dismissed, you won't have any.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import React, { ReactNode, useState, useEffect } from 'react';
import React, { ReactNode, useMemo } from 'react';
import { useApi, storageApiRef } from '@backstage/core-plugin-api';
import useObservable from 'react-use/esm/useObservable';
import classNames from 'classnames';
@@ -102,23 +102,17 @@ export const DismissableBanner = (props: Props) => {
const classes = useStyles();
const storageApi = useApi(storageApiRef);
const notificationsStore = storageApi.forBucket('notifications');
const rawDismissedBanners =
notificationsStore.snapshot<string[]>('dismissedBanners').value ?? [];
const [dismissedBanners, setDismissedBanners] = useState(
new Set(rawDismissedBanners),
);
const observedItems = useObservable(
notificationsStore.observe$<string[]>('dismissedBanners'),
notificationsStore.snapshot<string[]>('dismissedBanners'),
);
useEffect(() => {
if (observedItems?.value) {
const currentValue = observedItems?.value ?? [];
setDismissedBanners(new Set(currentValue));
}
}, [observedItems?.value]);
const dismissedBanners = useMemo(
() => new Set(observedItems.value ?? []),
[observedItems.value],
);
const loadingSettings = observedItems.presence === 'unknown';
const handleClick = () => {
notificationsStore.set('dismissedBanners', [...dismissedBanners, id]);
@@ -131,7 +125,7 @@ export const DismissableBanner = (props: Props) => {
? { vertical: 'bottom', horizontal: 'center' }
: { vertical: 'top', horizontal: 'center' }
}
open={!dismissedBanners.has(id)}
open={!loadingSettings && !dismissedBanners.has(id)}
classes={{
root: classNames(classes.root, !fixed && classes.topPosition),
}}