diff --git a/packages/core/src/components/DismissableBanner/DismissableBanner.stories.tsx b/packages/core/src/components/DismissableBanner/DismissableBanner.stories.tsx index e19495a099..37d982b0f4 100644 --- a/packages/core/src/components/DismissableBanner/DismissableBanner.stories.tsx +++ b/packages/core/src/components/DismissableBanner/DismissableBanner.stories.tsx @@ -17,50 +17,86 @@ import React from 'react'; import DismissableBanner from './DismissableBanner'; import { Link, Typography } from '@material-ui/core'; +import { + ApiProvider, + ApiRegistry, + CreateStorageApiOptions, + ErrorApi, + storageApiRef, + StorageApi, + WebStorage, +} from '@backstage/core'; export default { title: 'DismissableBanner', component: DismissableBanner, }; +let errorApi: ErrorApi; const containerStyle = { width: '70%' }; +const createWebStorage = ( + args?: Partial, +): StorageApi => { + return WebStorage.create({ + errorApi: errorApi, + ...args, + }); +}; + +const apis = ApiRegistry.from([[storageApiRef, createWebStorage()]]); + export const Default = () => (
- + + +
); export const Error = () => (
- + + +
); export const EmojisIncluded = () => (
- + + +
); export const WithLink = () => (
- - This is a dismissable banner with a link:{' '} - - example.com - - - } - variant="info" - /> + + + This is a dismissable banner with a link:{' '} + + example.com + + + } + variant="info" + id="linked_dismissable" + /> +
); diff --git a/packages/core/src/components/DismissableBanner/DismissableBanner.test.js b/packages/core/src/components/DismissableBanner/DismissableBanner.test.js deleted file mode 100644 index 8981acf8af..0000000000 --- a/packages/core/src/components/DismissableBanner/DismissableBanner.test.js +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import React from 'react'; -// import { fireEvent, waitForElementToBeRemoved } from '@testing-library/react'; -import { renderWithEffects, wrapInTestApp } from '@backstage/test-utils'; -// import { createSetting } from 'shared/apis/settings'; -import DismissableBanner from './DismissableBanner'; - -describe('', () => { - it('renders the message and the popover', async () => { - /* - const mockSetting = createSetting({ - id: 'mockSetting', - defaultValue: true, - }); - */ - - const rendered = await renderWithEffects( - wrapInTestApp( - , - ), - ); - rendered.getByText('test message'); - - // fireEvent.click(rendered.getByTitle('Permanently dismiss this message')); - // await waitForElementToBeRemoved(rendered.queryByText('test message')); - }); -}); diff --git a/packages/core/src/components/DismissableBanner/DismissableBanner.test.tsx b/packages/core/src/components/DismissableBanner/DismissableBanner.test.tsx new file mode 100644 index 0000000000..b31641eb8f --- /dev/null +++ b/packages/core/src/components/DismissableBanner/DismissableBanner.test.tsx @@ -0,0 +1,88 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import { fireEvent } from '@testing-library/react'; +import { renderWithEffects, wrapInTestApp } from '@backstage/test-utils'; +import DismissableBanner from './DismissableBanner'; +import { + ApiRegistry, + ApiProvider, + storageApiRef, + CreateStorageApiOptions, + StorageApi, + WebStorage, +} from '@backstage/core'; + +describe('', () => { + let apis: ApiRegistry; + const mockErrorApi = { post: jest.fn(), error$: jest.fn() }; + const createWebStorage = ( + args?: Partial, + ): StorageApi => { + return WebStorage.create({ + errorApi: mockErrorApi, + ...args, + }); + }; + + beforeEach(() => { + apis = ApiRegistry.from([[storageApiRef, createWebStorage()]]); + }); + + it('renders the message and the popover', async () => { + const rendered = await renderWithEffects( + wrapInTestApp( + + + , + ), + ); + const element = await rendered.findByText('test message'); + expect(element).toBeInTheDocument(); + }); + + it('gets placed in local storage on dismiss', async () => { + const rendered = await renderWithEffects( + wrapInTestApp( + + + , + ), + ); + const webstore = apis.get(storageApiRef); + const notifications = webstore?.forBucket('notifications'); + const button = await rendered.findByTitle( + 'Permanently dismiss this message', + ); + fireEvent.click(button); + const dismissedBanners = + notifications?.get('dismissedBanners') ?? []; + expect( + dismissedBanners.includes('catalog_page_welcome_banner'), + ).toBeTruthy(); + }); +}); diff --git a/packages/core/src/components/DismissableBanner/DismissableBanner.tsx b/packages/core/src/components/DismissableBanner/DismissableBanner.tsx index b7a36c8b98..76c27827ec 100644 --- a/packages/core/src/components/DismissableBanner/DismissableBanner.tsx +++ b/packages/core/src/components/DismissableBanner/DismissableBanner.tsx @@ -14,14 +14,15 @@ * limitations under the License. */ -import React, { FC, ReactNode, useState } from 'react'; +import React, { FC, ReactNode, useState, useEffect } from 'react'; +import { useApi, storageApiRef } from '@backstage/core'; +import { useObservable } from 'react-use'; import classNames from 'classnames'; import { makeStyles, Theme } from '@material-ui/core'; import Snackbar from '@material-ui/core/Snackbar'; import SnackbarContent from '@material-ui/core/SnackbarContent'; import IconButton from '@material-ui/core/IconButton'; import Close from '@material-ui/icons/Close'; -// import { useSetting, Setting } from 'shared/apis/settings'; const useStyles = makeStyles((theme: Theme) => ({ root: { @@ -54,23 +55,40 @@ const useStyles = makeStyles((theme: Theme) => ({ type Props = { variant: 'info' | 'error'; - // setting: Setting; message: ReactNode; + id: string; }; -const DismissableBanner: FC = ({ variant, /* setting, */ message }) => { - // const [show, setShown, loading] = useSetting(setting); - const [show, setShown] = useState(true); +const DismissableBanner: FC = ({ variant, message, id }) => { const classes = useStyles(); + const storageApi = useApi(storageApiRef); + const notificationsStore = storageApi.forBucket('notifications'); + const rawDismissedBanners = + notificationsStore.get('dismissedBanners') ?? []; + + const [dismissedBanners, setDismissedBanners] = useState( + new Set(rawDismissedBanners), + ); + + const observedItems = useObservable( + notificationsStore.observe$('dismissedBanners'), + ); + + useEffect(() => { + if (observedItems?.newValue) { + const currentValue = observedItems?.newValue ?? []; + setDismissedBanners(new Set(currentValue)); + } + }, [observedItems?.newValue]); const handleClick = () => { - setShown(false); + notificationsStore.set('dismissedBanners', [...dismissedBanners, id]); }; return ( = { Promise.resolve({ id: 'id', type: 'github', target: 'url' }), }; +const mockWebStorageErrorApi = { post: jest.fn(), error$: jest.fn() }; +const createWebStorage = ( + args?: Partial, +): StorageApi => { + return WebStorage.create({ + errorApi: mockWebStorageErrorApi, + ...args, + }); +}; +const storageApi = createWebStorage(); + describe('CatalogPage', () => { // this test right now causes some red lines in the log output when running tests // related to some theme issues in mui-table @@ -50,6 +69,7 @@ describe('CatalogPage', () => { apis={ApiRegistry.from([ [errorApiRef, errorApi], [catalogApiRef, catalogApi], + [storageApiRef, storageApi], ])} > diff --git a/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx b/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx index da22e94fa8..ac8de59bd9 100644 --- a/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx +++ b/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx @@ -149,6 +149,7 @@ const CatalogPage: FC<{}> = () => { page. } + id="catalog_page_welcome_banner" />