diff --git a/UNRELEASED.md b/UNRELEASED.md index 0bf87a94a50..e765dcb3bc3 100644 --- a/UNRELEASED.md +++ b/UNRELEASED.md @@ -11,6 +11,7 @@ Use [the changelog guidelines](https://git.io/polaris-changelog-guidelines) to f ### Bug fixes +- Fixed a bug in `Banner` where loading state wasn't getting passed to `primaryAction` ([#4338](https://github.com/Shopify/polaris-react/pull/4338)) - Fixed a bug `TextField` where Safari would render the incorrect text color ([#4344](https://github.com/Shopify/polaris-react/pull/4344)) ### Documentation diff --git a/src/components/Banner/Banner.scss b/src/components/Banner/Banner.scss index e71372395b1..eb7b6ffb38b 100644 --- a/src/components/Banner/Banner.scss +++ b/src/components/Banner/Banner.scss @@ -4,6 +4,7 @@ $accent-height: 3px; $ribbon-flex-basis: rem(32px); $secondary-action-vertical-padding: 0.5 * (control-height() - line-height(body)); $secondary-action-horizontal-padding: 1.5 * spacing(tight); +$spinner-size: rem(20px); @mixin banner-variants($in-page) { --p-banner-background: var(--p-background); @@ -278,3 +279,17 @@ $secondary-action-horizontal-padding: 1.5 * spacing(tight); @include focus-ring($style: 'focused'); } } + +.loading { + position: relative; + color: transparent; + pointer-events: none; +} + +.Spinner { + position: absolute; + top: 50%; + left: 50%; + margin-top: -($spinner-size / 2); + margin-left: -($spinner-size / 2); +} diff --git a/src/components/Banner/Banner.tsx b/src/components/Banner/Banner.tsx index 502a0794a97..7d85b875878 100644 --- a/src/components/Banner/Banner.tsx +++ b/src/components/Banner/Banner.tsx @@ -16,12 +16,14 @@ import { import {classNames, variationName} from '../../utilities/css'; import {BannerContext} from '../../utilities/banner-context'; import {useUniqueId} from '../../utilities/unique-id'; +import {useI18n} from '../../utilities/i18n'; import type {Action, DisableableAction, LoadableAction} from '../../types'; import {Button} from '../Button'; import {Heading} from '../Heading'; import {ButtonGroup} from '../ButtonGroup'; import {UnstyledButton, unstyledButtonFrom} from '../UnstyledButton'; import {UnstyledLink} from '../UnstyledLink'; +import {Spinner} from '../Spinner'; import {Icon, IconProps} from '../Icon'; import {WithinContentContext} from '../../utilities/within-content-context'; @@ -63,6 +65,7 @@ export const Banner = forwardRef(function Banner( ) { const withinContentContainer = useContext(WithinContentContext); const id = useUniqueId('Banner'); + const i18n = useI18n(); const { wrapperRef, handleKeyUp, @@ -92,11 +95,31 @@ export const Banner = forwardRef(function Banner( ); } + const spinnerMarkup = action?.loading ? ( + + ) : null; + const primaryActionMarkup = action ? (
- {unstyledButtonFrom(action, { - className: styles.Button, - })} + {action.loading + ? spinnerMarkup + : unstyledButtonFrom(action, { + className: styles.Button, + })}
) : null; diff --git a/src/components/Banner/tests/Banner.test.tsx b/src/components/Banner/tests/Banner.test.tsx index 596fc66e446..18246e175aa 100644 --- a/src/components/Banner/tests/Banner.test.tsx +++ b/src/components/Banner/tests/Banner.test.tsx @@ -8,7 +8,14 @@ import { } from '@shopify/polaris-icons'; import {mountWithApp} from 'test-utilities'; import {BannerContext} from 'utilities/banner-context'; -import {Button, Icon, UnstyledButton, UnstyledLink, Heading} from 'components'; +import { + Button, + Heading, + Icon, + Spinner, + UnstyledButton, + UnstyledLink, +} from 'components'; import {WithinContentContext} from '../../../utilities/within-content-context'; import {Banner, BannerHandles} from '../Banner'; @@ -101,6 +108,41 @@ describe('', () => { 'Primary action', ); }); + + it('renders a Spinner when loading', () => { + const bannerWithAction = mountWithApp( + + Hello World + , + ); + + expect(bannerWithAction).toContainReactComponent(Spinner); + }); + + it('renders a disabled button when loading', () => { + const bannerWithAction = mountWithApp( + + Hello World + , + ); + + expect(bannerWithAction).toContainReactComponent('button', { + disabled: true, + 'aria-busy': true, + }); + }); }); describe('secondaryAction', () => {