Skip to content
Draft
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
10 changes: 10 additions & 0 deletions .changeset/sweet-news-search.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
'@kaizen/components': patch
---

Add optional prop to `VideoPlayer` and update `BrandMomentCaptureIntro` to always display the animation toggle.

- Add `hasVisibleAnimationToggle` prop to `VideoPlayer`
- Update VideoPlayer to next `Button` component
- Remove redundant styles from button migration
- Add i18n strings for play/pause labels
8 changes: 8 additions & 0 deletions packages/components/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -188,5 +188,13 @@
"splitButton.dropdownButton.label": {
"description": "Label for a dropdown menu holding additional actions",
"message": "Additional actions"
},
"videoPlayer.pausePlayBtn.pauseLabel": {
"description": "Label for the pausing / stopping an animation",
"message": "Play animation"
},
"videoPlayer.pausePlayBtn.playLabel": {
"description": "Label for the starting / playing an animation",
"message": "Play animation"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,13 @@ export const BrandMomentCaptureIntro = ({
isAnimated,
alt,
enableAspectRatio,
autoplay,
loop,
...otherProps
}: AnimatedSceneProps): JSX.Element => {
const [firstAnimationComplete, setFirstAnimationComplete] = useState<boolean>(false)
const aspectRatio = enableAspectRatio ? 'landscape' : undefined
const shouldAlwaysShowAnimationToggle = autoplay && loop

if (!isAnimated) {
return (
Expand All @@ -35,7 +38,9 @@ export const BrandMomentCaptureIntro = ({
aspectRatio={aspectRatio}
fallback="illustrations/heart/scene/brand-moments-capture-intro-loop"
source="illustrations/heart/scene/brand-moments-capture-intro-loop"
autoplay={firstAnimationComplete ? otherProps.autoplay : false}
autoplay={firstAnimationComplete ? autoplay : false}
loop={loop}
hasVisibleAnimationToggle={shouldAlwaysShowAnimationToggle}
/>
)
}
Expand All @@ -47,7 +52,9 @@ export const BrandMomentCaptureIntro = ({
fallback="illustrations/heart/scene/brand-moments-capture-intro"
source="illustrations/heart/scene/brand-moments-capture-intro"
onEnded={(): void => setFirstAnimationComplete(true)}
autoplay={autoplay}
loop={false}
hasVisibleAnimationToggle={shouldAlwaysShowAnimationToggle}
/>
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,9 @@ Will render the a looped animation. Should be used with `isAnimated` to render t
If false will render the animation paused. This can be re-enabled clicking the pause sign on hover.

<Canvas of={BrandMomentCaptureIntroStories.Autoplay} />

## Example

This is not an exact template, rather an example of the component in use with a looping animation.

<Canvas of={BrandMomentCaptureIntroStories.CaptureExample} />
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import React from 'react'
import { type Meta, type StoryObj } from '@storybook/react'
import { Heading } from '~components/Heading'
import { Link } from '~components/Link'
import { Text } from '~components/Text'
import { Button, Icon } from '~components/__next__'
import { BrandMomentCaptureIntro } from '../index'

const meta = {
Expand All @@ -21,23 +26,62 @@ export const Playground: Story = {
}

export const Animated: Story = {
name: 'Animated Capture illustration on toggle',
args: {
loop: false,
isAnimated: true,
},
}

export const Looped: Story = {
name: 'Animated Capture illustration looped without autoplay',
args: {
isAnimated: true,
loop: true,
},
}

export const Autoplay: Story = {
name: 'Animated Capture illustration looped with autoplay',
args: {
isAnimated: true,
loop: true,
autoplay: false,
autoplay: true,
},
}

// This is an example that provides a closer representation of how the component is used in the product.
export const CaptureExample: Story = {
args: {
isAnimated: true,
loop: true,
autoplay: true,
},
render: (args) => (
<div className="bg-blue-100 flex justify-center gap-24 p-16">
<div className="flex justify-center items-center w-[50%]">
<BrandMomentCaptureIntro {...args} />
</div>
<div className="flex flex-col justify-center w-[50%]">
<Heading classNameOverride="mb-64" variant="display-0">
Survey Title
</Heading>
<Text classNameOverride="mb-32" variant="body">
You have been asked to provide feedback for Demonstration Employee.
</Text>
<Text classNameOverride="mb-32" variant="body">
The setting for this survey control how your responses can be used by Hooli.
</Text>
<Text classNameOverride="mb-32" variant="body">
Your information will be stored and processed in accordance with Culture Amp’s{' '}
<Link href="#">Privacy Policy</Link>. <Link href="#">More on managing information</Link>.
</Text>
<div>
<Button icon={<Icon name={'arrow_forward'} isPresentational />} iconPosition="end">
Take survey
</Button>
</div>
</div>
</div>
),
}
3 changes: 2 additions & 1 deletion packages/components/src/Illustration/Scene/Scene.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export type SceneProps = {
type BaseAnimatedSceneProps = {
isAnimated?: boolean
} & SceneProps &
Pick<VideoPlayerProps, 'autoplay' | 'loop'>
Pick<VideoPlayerProps, 'autoplay' | 'loop' | 'hasVisibleAnimationToggle'>

type AnimatedProps = {
isAnimated: true
Expand All @@ -21,6 +21,7 @@ type NotAnimatedProps = {
isAnimated?: false
autoplay?: never
loop?: never
hasVisibleAnimationToggle?: never
} & BaseAnimatedSceneProps

export type AnimatedSceneProps = AnimatedProps | NotAnimatedProps
Expand Down
10 changes: 10 additions & 0 deletions packages/components/src/Illustration/Scene/_docs/Scene.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,13 @@ import * as SceneStories from './Scene.stories'
<Controls of={SceneStories.Playground} />

<Canvas of={Stickersheets.StickerSheetDefault} />

### Animated scenes

Some Scene illustrations have animated versions. Looped animation should be used sparingly as it can be distracting for users, especially those with motion sensitivity. Consider also the WCAG spec for [non-essential animations](https://www.w3.org/WAI/WCAG22/Understanding/animation-from-interactions.html).

Note that all Scene illustration with `autoplay` and `loop` set to `true` will render the animation toggle.

This can be overridden by setting `hasVisibleAnimationToggle` to `false`, which will render the toggle only on hover or focus. Note that this can have accessibility implications, so use with caution.

<Canvas of={SceneStories.AnimatedScenes} />
80 changes: 78 additions & 2 deletions packages/components/src/Illustration/Scene/_docs/Scene.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
import React from 'react'
import { type Meta, type StoryObj } from '@storybook/react'

import { BrandMomentPositiveOutro } from '../index'
import {
BrandMomentError,
BrandMomentLogin,
BrandMomentPositiveOutro,
EmptyStatesAction,
EmptyStatesInformative,
EmptyStatesNegative,
EmptyStatesNeutral,
EmptyStatesPositive,
} from '../index'

const meta = {
title: 'Components/Illustrations/Scene',
title: 'Components/illustrations/Scene',
component: BrandMomentPositiveOutro,
} satisfies Meta<typeof BrandMomentPositiveOutro>

Expand All @@ -20,3 +30,69 @@ export const Playground: Story = {
},
},
}

export const AnimatedScenes: Story = {
name: 'Animated Scene illustration autoplay and loop',
args: {
isAnimated: true,
loop: true,
autoplay: true,
},
render: (args) => (
<>
<EmptyStatesAction {...args} />
<EmptyStatesInformative {...args} />
<EmptyStatesNegative {...args} />
<EmptyStatesNeutral {...args} />
<EmptyStatesPositive {...args} />
<BrandMomentPositiveOutro {...args} />
<BrandMomentLogin {...args} />
<BrandMomentError {...args} />
</>
),
decorators: [
(Story) => (
<div className="flex flex-col justify-center gap-16 max-w-[400px]">
<Story />
</div>
),
],
}
export const AnimatedScenesWithAutoPlayNoLoop: Story = {
name: 'Animated Scene illustration with autoplay but no loop',
...AnimatedScenes,
args: {
isAnimated: true,
loop: false,
autoplay: true,
},
}

export const AnimatedScenesLoopedWithoutAutoplay: Story = {
name: 'Animated Scene illustration without autoplay but with loop',
...AnimatedScenes,
args: {
isAnimated: true,
loop: true,
autoplay: false,
},
}

export const AnimatedScenesHover: Story = {
...AnimatedScenes,
parameters: {
pseudo: {
hover: ['[data-sb-pseudo-styles="hover"]', '[data-sb-pseudo-styles="hover"] figure'],
},
},
decorators: [
(Story) => (
<div
className="flex flex-col justify-center gap-16 max-w-[400px]"
data-sb-pseudo-styles="hover"
>
<Story />
</div>
),
],
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
@layer kz-components {
.wrapper {
width: 100%;
margin: 0;
}

.figure {
margin: 0;
position: relative;
}

.figure .pausePlayButton {
opacity: 0;
position: absolute;
left: 0;
bottom: 0;
border-width: 1px;
transition: all var(--animation-duration-immediate) ease;

@media (hover: none) and (pointer: coarse) {
background: var(--color-gray-200);
opacity: 1;
}
}

.figure .pausePlayButton:hover,
.figure .pausePlayButton:focus,
.figure:hover .pausePlayButton {
opacity: 1;
background: var(--color-gray-200);
border-color: var(--color-gray-600);
}

.hasVisibleAnimationToggle {
opacity: 1 !important;
}

.aspectRatioWrapper {
margin: 0;
display: flex;
align-items: center;
}

.landscape {
aspect-ratio: 4/3;

@supports not (aspect-ratio: auto) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think we can safely use this without a fallback as it's a baseline widely avaiable feature and it landed 4+ years ago in most browsers

&::before {
float: left;
padding-top: 75%;
content: '';
}

&::after {
display: block;
content: '';
clear: both;
}
}
}

.portrait {
aspect-ratio: 3/4;

@supports not (aspect-ratio: auto) {
&::before {
float: left;
padding-top: 133.33%;
content: '';
}

&::after {
display: block;
content: '';
clear: both;
}
}
}

.square {
aspect-ratio: auto 1/1;

@supports not (aspect-ratio: auto) {
&::before {
float: left;
padding-top: 100%;
content: '';
}

&::after {
display: block;
content: '';
clear: both;
}
}
}
}
Loading