From f46c07189c849d727f567c85e985695e75acc4cf Mon Sep 17 00:00:00 2001 From: Mike Perrotti Date: Wed, 20 Dec 2023 16:07:59 -0500 Subject: [PATCH] Add `animated` prop to ProgressBar (#4061) * adds animated prop to ProgressBar * updates component docs * adds changeset --- .changeset/serious-drinks-work.md | 7 +++++ src/ProgressBar/ProgressBar.docs.json | 5 ++++ .../ProgressBar.features.stories.tsx | 2 ++ src/ProgressBar/ProgressBar.tsx | 26 ++++++++++++++-- .../__snapshots__/ProgressBar.test.tsx.snap | 30 +++++++++++++++++++ 5 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 .changeset/serious-drinks-work.md diff --git a/.changeset/serious-drinks-work.md b/.changeset/serious-drinks-work.md new file mode 100644 index 00000000000..f10a071bc79 --- /dev/null +++ b/.changeset/serious-drinks-work.md @@ -0,0 +1,7 @@ +--- +'@primer/react': minor +--- + +Adds a new prop, `animated`, to the ProgressBar component. This allows the "filled" segment(s) to animate and indicate some process is still in progress. + + diff --git a/src/ProgressBar/ProgressBar.docs.json b/src/ProgressBar/ProgressBar.docs.json index 6534d5a08ed..77b5fb3f109 100644 --- a/src/ProgressBar/ProgressBar.docs.json +++ b/src/ProgressBar/ProgressBar.docs.json @@ -5,6 +5,11 @@ "a11yReviewed": false, "stories": [], "props": [ + { + "name": "animated", + "type": "boolean", + "description": "Whether the filled in area(s) of the progress bar will be animated or not" + }, { "name": "progress", "type": "number", diff --git a/src/ProgressBar/ProgressBar.features.stories.tsx b/src/ProgressBar/ProgressBar.features.stories.tsx index 5c1e860a26a..5af0a689c19 100644 --- a/src/ProgressBar/ProgressBar.features.stories.tsx +++ b/src/ProgressBar/ProgressBar.features.stories.tsx @@ -25,3 +25,5 @@ export const MultipleItems = () => ( ) + +export const Animated = () => diff --git a/src/ProgressBar/ProgressBar.tsx b/src/ProgressBar/ProgressBar.tsx index bc7fb26a5be..d6723c0d769 100644 --- a/src/ProgressBar/ProgressBar.tsx +++ b/src/ProgressBar/ProgressBar.tsx @@ -1,5 +1,5 @@ import React, {forwardRef} from 'react' -import styled from 'styled-components' +import styled, {keyframes} from 'styled-components' import {width, WidthProps} from 'styled-system' import {get} from '../constants' import sx, {SxProp} from '../sx' @@ -7,9 +7,25 @@ import {warning} from '../utils/warning' type ProgressProp = {progress?: string | number} +const shimmer = keyframes` + from { mask-position: 200%; } + to { mask-position: 0%; } +` + export const Item = styled.span` width: ${props => (props.progress ? `${props.progress}%` : 0)}; background-color: ${get('colors.success.emphasis')}; + + @media (prefers-reduced-motion: no-preference) { + &[data-animated='true'] { + mask-image: linear-gradient(75deg, #000 30%, rgba(0, 0, 0, 0.65) 80%); + mask-size: 200%; + animation: ${shimmer}; + animation-duration: 1s; + animation-iteration-count: infinite; + } + } + ${sx}; ` @@ -24,6 +40,7 @@ const sizeMap = { type StyledProgressContainerProps = { inline?: boolean barSize?: keyof typeof sizeMap + animated?: boolean } & WidthProps & SxProp @@ -42,7 +59,10 @@ export type ProgressBarProps = React.HTMLAttributes & {bg?: str ProgressProp export const ProgressBar = forwardRef( - ({progress, bg = 'success.emphasis', barSize = 'default', children, ...rest}: ProgressBarProps, forwardRef) => { + ( + {animated, progress, bg = 'success.emphasis', barSize = 'default', children, ...rest}: ProgressBarProps, + forwardRef, + ) => { if (children && progress) { throw new Error('You should pass `progress` or children, not both.') } @@ -65,7 +85,7 @@ export const ProgressBar = forwardRef( return ( - {children ?? } + {children ?? } ) }, diff --git a/src/__tests__/__snapshots__/ProgressBar.test.tsx.snap b/src/__tests__/__snapshots__/ProgressBar.test.tsx.snap index dad58b54e9e..7e516f82971 100644 --- a/src/__tests__/__snapshots__/ProgressBar.test.tsx.snap +++ b/src/__tests__/__snapshots__/ProgressBar.test.tsx.snap @@ -18,6 +18,21 @@ exports[`ProgressBar renders consistently 1`] = ` height: 8px; } +@media (prefers-reduced-motion:no-preference) { + .c1[data-animated='true'] { + -webkit-mask-image: linear-gradient(75deg,#000 30%,rgba(0,0,0,0.65) 80%); + mask-image: linear-gradient(75deg,#000 30%,rgba(0,0,0,0.65) 80%); + -webkit-mask-size: 200%; + mask-size: 200%; + -webkit-animation: jiNfbM; + animation: jiNfbM; + -webkit-animation-duration: 1s; + animation-duration: 1s; + -webkit-animation-iteration-count: infinite; + animation-iteration-count: infinite; + } +} +