Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
915a2d1
Adding animation and hasBackground props to EuiHeaderSectionItemButton
elizabetdev Jan 14, 2021
14d5b51
A few improvements
elizabetdev Jan 14, 2021
cc5c874
Docs props
elizabetdev Jan 15, 2021
4f5c09f
Removing hasBackground prop
elizabetdev Jan 18, 2021
e020d7a
Using JS do hide and show on xs breakpoints
elizabetdev Jan 18, 2021
c6f7bc3
Adding a snippet
elizabetdev Jan 18, 2021
41a54c8
refactor animation prop into a ref method
chandlerprall Jan 29, 2021
e4b21e1
Merge pull request #8 from chandlerprall/notification_header_button_m…
elizabetdev Feb 1, 2021
d5923d3
Snapshots. Trigger animation comments.
elizabetdev Feb 1, 2021
4d3c970
Deleting unused css animation
elizabetdev Feb 1, 2021
8253863
triggerAnimation explanation
elizabetdev Feb 1, 2021
46a3d77
Cleaned up some logic and created a docs example specifically for the…
Feb 8, 2021
f88a657
Merge pull request #11 from cchaos/notification_header_button
elizabetdev Feb 10, 2021
aba5d8e
re-enable the animation test
chandlerprall Feb 10, 2021
d13e053
Merge pull request #12 from chandlerprall/notification_header_button_…
elizabetdev Feb 10, 2021
f3a8851
Update src-docs/src/views/header/header_animate.js
elizabetdev Feb 10, 2021
733ae30
Update src-docs/src/views/header/header_example.js
elizabetdev Feb 10, 2021
4634390
Update src-docs/src/views/header/header_alert.js
elizabetdev Feb 10, 2021
b4eb475
Update src-docs/src/views/header/header_alert.js
elizabetdev Feb 10, 2021
10736bd
Update src-docs/src/views/header/header_animate.js
elizabetdev Feb 10, 2021
4b396ac
Update src-docs/src/views/header/header_example.js
elizabetdev Feb 10, 2021
36c586d
renaming animate methods to euiAnimate
elizabetdev Feb 10, 2021
67d1c48
euiAnimate into header example
elizabetdev Feb 10, 2021
c5b041e
simplifying onClick closeFlyout
elizabetdev Feb 10, 2021
d431074
Update src-docs/src/views/header/header_example.js
elizabetdev Feb 11, 2021
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
305 changes: 192 additions & 113 deletions src-docs/src/views/header/header_alert.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import React, { useState } from 'react';
import {
EuiAvatar,
EuiBadge,
EuiButton,
EuiButtonEmpty,
EuiFlexGroup,
EuiFlexItem,
Expand All @@ -29,42 +30,14 @@ import {
} from '../../../../src/components';
import { htmlIdGenerator } from '../../../../src/services';

export default () => {
const [position, setPosition] = useState('static');

return (
<>
<EuiSwitch
label={'Make header fixed position and put alerts in flyout'}
checked={position === 'fixed'}
onChange={(e) => setPosition(e.target.checked ? 'fixed' : 'static')}
/>
<EuiSpacer />
<EuiHeader position={position}>
<EuiHeaderSection grow={false}>
<EuiHeaderSectionItem border="right">
<EuiHeaderLogo>Elastic</EuiHeaderLogo>
</EuiHeaderSectionItem>
</EuiHeaderSection>

<EuiHeaderSection side="right">
<EuiHeaderSectionItem>
<HeaderUpdates
flyoutOrPopover={position === 'fixed' ? 'flyout' : 'popover'}
/>
</EuiHeaderSectionItem>
<EuiHeaderSectionItem>
<HeaderUserMenu />
</EuiHeaderSectionItem>
</EuiHeaderSection>
</EuiHeader>
</>
);
};

const HeaderUpdates = ({ flyoutOrPopover = 'flyout' }) => {
const HeaderUpdates = ({
showNotification,
setShowNotification,
notificationsNumber,
isAnimating,
}) => {
const [isFlyoutVisible, setIsFlyoutVisible] = useState(false);
const [showBadge, setShowBadge] = useState(true);
const [isPopoverVisible, setIsPopoverVisible] = useState(false);

const alerts = [
{
Expand Down Expand Up @@ -146,88 +119,64 @@ const HeaderUpdates = ({ flyoutOrPopover = 'flyout' }) => {
setIsFlyoutVisible(false);
};

const closePopover = () => {
setIsPopoverVisible(false);
};

const showFlyout = () => {
setShowBadge(false);
setShowNotification(false);
setIsFlyoutVisible(!isFlyoutVisible);
};

const button = (
const showPopover = () => {
setShowNotification(false);
setIsPopoverVisible(!isPopoverVisible);
};

const bellButton = (
<EuiHeaderSectionItemButton
aria-controls="headerNewsFeed"
aria-controls="headerFlyoutNewsFeed"
aria-expanded={isFlyoutVisible}
aria-haspopup="true"
aria-label={`News feed: ${
showBadge ? 'Updates available' : 'No updates'
showNotification ? 'Updates available' : 'No updates'
}`}
onClick={() => showFlyout()}
notification={showBadge}>
<EuiIcon type="cheer" size="m" />
notification={showNotification}
animation={isAnimating}
hasBackground>
<EuiIcon type="bell" />
</EuiHeaderSectionItemButton>
);

let content;
if (flyoutOrPopover === 'flyout') {
content = (
<>
{button}
{isFlyoutVisible && (
<EuiPortal>
<EuiFlyout
onClose={() => closeFlyout()}
size="s"
id="headerNewsFeed"
aria-labelledby="flyoutSmallTitle">
<EuiFlyoutHeader hasBorder>
<EuiTitle size="s">
<h2 id="flyoutSmallTitle">What&apos;s new</h2>
</EuiTitle>
</EuiFlyoutHeader>
<EuiFlyoutBody>
{alerts.map((alert, i) => (
<EuiHeaderAlert
key={`alert-${i}`}
title={alert.title}
action={alert.action}
text={alert.text}
date={alert.date}
badge={alert.badge}
/>
))}
</EuiFlyoutBody>
<EuiFlyoutFooter>
<EuiFlexGroup justifyContent="spaceBetween" alignItems="center">
<EuiFlexItem grow={false}>
<EuiButtonEmpty
iconType="cross"
onClick={() => closeFlyout()}
flush="left">
Close
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiText color="subdued" size="s">
<p>Version 7.0</p>
</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlyoutFooter>
</EuiFlyout>
</EuiPortal>
)}
</>
);
}
const cheerButton = (
<EuiHeaderSectionItemButton
aria-controls="headerPopoverNewsFeed"
aria-expanded={isPopoverVisible}
aria-haspopup="true"
aria-label={`News feed: ${
showNotification ? 'Updates available' : 'No updates'
}`}
onClick={() => showPopover()}
Comment thread
elizabetdev marked this conversation as resolved.
Outdated
notification={showNotification && notificationsNumber}
animation={isAnimating}>
<EuiIcon type="cheer" />
</EuiHeaderSectionItemButton>
);

if (flyoutOrPopover === 'popover') {
content = (
<EuiPopover
button={button}
isOpen={isFlyoutVisible}
closePopover={() => closeFlyout()}
panelPaddingSize="none">
<EuiPopoverTitle paddingSize="s">What&apos;s new</EuiPopoverTitle>
<div style={{ maxHeight: '40vh', overflowY: 'auto', padding: 4 }}>
<EuiSpacer size="s" />
const flyout = (
<EuiPortal>
<EuiFlyout
onClose={() => closeFlyout()}
size="s"
id="headerFlyoutNewsFeed"
aria-labelledby="flyoutSmallTitle">
<EuiFlyoutHeader hasBorder>
<EuiTitle size="s">
<h2 id="flyoutSmallTitle">What&apos;s new</h2>
</EuiTitle>
</EuiFlyoutHeader>
<EuiFlyoutBody>
{alerts.map((alert, i) => (
<EuiHeaderAlert
key={`alert-${i}`}
Expand All @@ -238,17 +187,66 @@ const HeaderUpdates = ({ flyoutOrPopover = 'flyout' }) => {
badge={alert.badge}
/>
))}
</div>
<EuiPopoverFooter paddingSize="s">
<EuiText color="subdued" size="s">
<p>Version 7.0</p>
</EuiText>
</EuiPopoverFooter>
</EuiPopover>
);
}
</EuiFlyoutBody>
<EuiFlyoutFooter>
<EuiFlexGroup justifyContent="spaceBetween" alignItems="center">
<EuiFlexItem grow={false}>
<EuiButtonEmpty
iconType="cross"
onClick={() => closeFlyout()}
flush="left">
Close
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiText color="subdued" size="s">
<p>Version 7.0</p>
</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlyoutFooter>
</EuiFlyout>
</EuiPortal>
);

return content;
const popover = (
<EuiPopover
id="headerPopoverNewsFeed"
ownFocus
repositionOnScroll
button={cheerButton}
isOpen={isPopoverVisible}
closePopover={() => closePopover()}
Comment thread
elizabetdev marked this conversation as resolved.
Outdated
panelPaddingSize="none">
<EuiPopoverTitle paddingSize="s">What&apos;s new</EuiPopoverTitle>
<div style={{ maxHeight: '40vh', overflowY: 'auto', padding: 4 }}>
<EuiSpacer size="s" />
{alerts.map((alert, i) => (
<EuiHeaderAlert
key={`alert-${i}`}
title={alert.title}
action={alert.action}
text={alert.text}
date={alert.date}
badge={alert.badge}
/>
))}
</div>
<EuiPopoverFooter paddingSize="s">
<EuiText color="subdued" size="s">
<p>Version 7.0</p>
</EuiText>
</EuiPopoverFooter>
</EuiPopover>
);

return (
<>
{bellButton}
{popover}
{isFlyoutVisible && flyout}
</>
);
};

const HeaderUserMenu = () => {
Expand Down Expand Up @@ -319,3 +317,84 @@ const HeaderUserMenu = () => {
</EuiPopover>
);
};

export default () => {
const [position, setPosition] = useState('static');
const [showNotification, setShowNotification] = useState(true);
const [isAnimating, setIsAnimating] = useState(false);
const [theme, setTheme] = useState('light');
const [notificationsNumber, setNotificationsNumber] = useState(1);

const animate = () => {
setIsAnimating(false);

setTimeout(() => {
setIsAnimating(true);
}, 100);

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 this is an important piece that consumers will not see. This is the only way they'd be able to reset the animation after each go. I think there's a couple paths:

  1. Make sure to document this somewhere, maybe a prop comment will suffice
  2. Is there any way to reset this within the component itself, maybe the prop is a callback function or something that passes back when the animation is done.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks @cchaos,

  1. I'm not completely sure if this is the best example to set true/false. I'm just trying to avoid a setTimeout of 5s. So the .euiHeaderSectionItemButton__content--isAnimating stays there forever, until the animation is triggered again. It doesn't seem right to have the class there when it's not animating.
  2. Maybe there is.

@chandlerprall can you recommend what you believe is the best way to inject the CSS class .euiHeaderSectionItemButton__content--isAnimating and remove it after the animation is over (5s).

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.

Instead of a prop which needs to toggle off/on to animate, we could add a method to the component's ref,

headerRef = useRef();

<HeaderUpdates
  ref={headerRef}
  showNotification={showNotification}
  setShowNotification={() => setShowNotification()}
  notificationsNumber={notificationsNumber}
/>

// trigger animation when notification count changes
useEffect(
  () => headerRef.animateAlertIcon(),
  [notifications.length]
)

If that sounds better I can put that together. Alternatively, we could use the animationend or transitionend events to trigger a callback to the parent, telling it to reset the animation prop back to false. This would avoid any setTimeout, but still requires extra boilerplate code in the consuming app

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@cchaos what do you think of this approach?

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 that approach works. My only concern would be discoverability. Of course we'd have it hooked up in our docs, but would there be any autocompletion in IDE's that would help surface this option.

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.

PR showing the method-based approach at elizabetdev#8

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks @chandlerprall. I found it a really bit difficult to document/exemplify this solution. So do you think this would work elizabetdev#9?

};

const notify = () => {
if (!showNotification) {
setNotificationsNumber(1);
setShowNotification(true);
} else {
setNotificationsNumber(notificationsNumber + 1);
}
};

return (
<>
<EuiFlexGroup alignItems="center" gutterSize="m">
<EuiFlexItem grow={false}>
<EuiSwitch
label={'Make header fixed position'}
checked={position === 'fixed'}
onChange={(e) => setPosition(e.target.checked ? 'fixed' : 'static')}
/>
</EuiFlexItem>

<EuiFlexItem grow={false}>
<EuiSwitch
label={'Change theme to dark'}
checked={theme === 'dark'}
onChange={(e) => setTheme(e.target.checked ? 'dark' : 'light')}
/>
</EuiFlexItem>

<EuiFlexItem grow={false}>
<EuiButton size="s" onClick={notify}>
Notify
</EuiButton>
</EuiFlexItem>

<EuiFlexItem grow={false}>
<EuiButton size="s" onClick={animate}>
Animate
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>

<EuiSpacer />
<EuiHeader position={position} theme={theme}>
<EuiHeaderSection grow={false}>
<EuiHeaderSectionItem border="right">
<EuiHeaderLogo>Elastic</EuiHeaderLogo>
</EuiHeaderSectionItem>
</EuiHeaderSection>
<EuiHeaderSection side="right">
<EuiHeaderSectionItem>
<HeaderUpdates
showNotification={showNotification}
setShowNotification={() => setShowNotification()}
notificationsNumber={notificationsNumber}
isAnimating={isAnimating}
/>
</EuiHeaderSectionItem>
<EuiHeaderSectionItem>
<HeaderUserMenu isAnimating={isAnimating} />
</EuiHeaderSectionItem>
</EuiHeaderSection>
</EuiHeader>
</>
);
};
Loading