mirror of
https://github.com/brianlovin/security-checklist.git
synced 2026-03-11 08:55:31 +00:00
Add progress indicator on header
This commit is contained in:
parent
5a35f675a6
commit
1d99bc4739
5 changed files with 101 additions and 11 deletions
|
|
@ -1,16 +1,24 @@
|
|||
// @flow
|
||||
import * as React from 'react';
|
||||
import Link from 'next/link';
|
||||
import { Container, ButtonRowContainer, Label } from './style';
|
||||
import {
|
||||
Container,
|
||||
ButtonRowContainer,
|
||||
Label,
|
||||
Progression,
|
||||
ProgressBar } from './style';
|
||||
import { PrimaryButton, GhostButton } from '../Button';
|
||||
import Logo from './Logo';
|
||||
|
||||
type Props = {
|
||||
showHeaderShadow: boolean,
|
||||
displayProgress: boolean,
|
||||
totalItemsCount: number,
|
||||
currentCount: number,
|
||||
};
|
||||
|
||||
export default function Header(props: Props) {
|
||||
const { showHeaderShadow } = props;
|
||||
const { showHeaderShadow, totalItemsCount, currentCount, displayProgress } = props;
|
||||
|
||||
return (
|
||||
<Container showHeaderShadow={showHeaderShadow} data-cy="header">
|
||||
|
|
@ -21,6 +29,11 @@ export default function Header(props: Props) {
|
|||
</a>
|
||||
</Link>
|
||||
|
||||
<Progression isHidden={!displayProgress}>
|
||||
{currentCount} of {totalItemsCount} completed
|
||||
<ProgressBar />
|
||||
</Progression>
|
||||
|
||||
<ButtonRowContainer>
|
||||
<Link href="/about">
|
||||
<a>
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ import { theme } from '../theme';
|
|||
|
||||
export const Container = styled.div`
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-template-areas: 'logo actions';
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
grid-template-areas: 'logo progression actions';
|
||||
padding: 16px;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
|
|
@ -25,7 +25,7 @@ export const Container = styled.div`
|
|||
@media (max-width: 968px) {
|
||||
padding: 8px 16px;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-template-areas: 'logo actions';
|
||||
grid-template-areas: "logo actions" "progression progression";
|
||||
}
|
||||
`;
|
||||
|
||||
|
|
@ -36,6 +36,34 @@ export const Logo = styled.h1`
|
|||
color: ${theme.text.default};
|
||||
`;
|
||||
|
||||
export const Progression = styled.div`
|
||||
grid-area: progression;
|
||||
text-align: center;
|
||||
|
||||
display: ${props =>
|
||||
props.isHidden ? 'none' : 'block'};
|
||||
`;
|
||||
|
||||
export const ProgressBar = styled.div`
|
||||
height: 8px;
|
||||
margin: 6px 0;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
border-radius: 4px;
|
||||
background-image: linear-gradient(to left, #a913de, #6ac9ff);
|
||||
|
||||
&:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
right: 0;
|
||||
width: 100%;
|
||||
background: ${theme.border.default};
|
||||
max-width: var(--progress);
|
||||
transition: max-width ${theme.animations.default};
|
||||
}
|
||||
`;
|
||||
|
||||
export const ButtonRowContainer = styled.div`
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
|
|
|
|||
|
|
@ -17,18 +17,25 @@ import {
|
|||
ScrollToTop,
|
||||
} from './style';
|
||||
import * as gtag from '../../lib/gtag';
|
||||
import { getLocalStorageLength } from '../../lib/localStorage';
|
||||
import data from '../../config/data';
|
||||
|
||||
export { SectionHeading, Heading, Subheading };
|
||||
|
||||
type Props = {
|
||||
children: Node,
|
||||
displayProgress: boolean,
|
||||
};
|
||||
|
||||
const totalItemsCount = Object.keys(data).length;
|
||||
|
||||
export default function Page(props: Props) {
|
||||
const { children } = props;
|
||||
const { children, displayProgress } = props;
|
||||
const [lastTrackedPageview, setLastTrackedPageview] = useState(null);
|
||||
const [showHeaderShadow, setHeaderShadow] = useState(false);
|
||||
const [scrollToTopVisible, setScrollToTopVisible] = useState(false);
|
||||
const [progress, setProgress] = useState(0);
|
||||
const [currentCount, setCurrentCount] = useState(0);
|
||||
|
||||
function handleScroll() {
|
||||
const headerShadowState = window && window.pageYOffset > 0;
|
||||
|
|
@ -39,6 +46,14 @@ export default function Page(props: Props) {
|
|||
|
||||
const throttledScroll = throttle(300, handleScroll);
|
||||
|
||||
function updateProgress() {
|
||||
console.log("called")
|
||||
const checkedItemsCount = getLocalStorageLength();
|
||||
const progressPercentage = checkedItemsCount * 100 / totalItemsCount;
|
||||
setProgress(progressPercentage);
|
||||
setCurrentCount(checkedItemsCount);
|
||||
};
|
||||
|
||||
const scrollToTop = () => {
|
||||
if (window) {
|
||||
window.scrollTo(0, 0);
|
||||
|
|
@ -46,6 +61,8 @@ export default function Page(props: Props) {
|
|||
};
|
||||
|
||||
useEffect(() => {
|
||||
updateProgress();
|
||||
|
||||
if (window) {
|
||||
window.addEventListener('scroll', throttledScroll);
|
||||
}
|
||||
|
|
@ -56,7 +73,18 @@ export default function Page(props: Props) {
|
|||
setLastTrackedPageview(null);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
}, [progress]);
|
||||
|
||||
useEffect(() => {
|
||||
if (window && displayProgress) {
|
||||
window.addEventListener('storage:updated', updateProgress );
|
||||
}
|
||||
return () => {
|
||||
if (window && displayProgress) {
|
||||
window.removeEventListener('storage:updated', updateProgress );
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (document) {
|
||||
|
|
@ -71,7 +99,18 @@ export default function Page(props: Props) {
|
|||
return (
|
||||
<ThemeProvider theme={theme}>
|
||||
<Container>
|
||||
<Header showHeaderShadow={showHeaderShadow} />
|
||||
<style>{`
|
||||
:root {
|
||||
--progress: ${100 - progress}%;
|
||||
}
|
||||
`}</style>
|
||||
|
||||
<Header
|
||||
showHeaderShadow={showHeaderShadow}
|
||||
displayProgress={displayProgress}
|
||||
totalItemsCount={totalItemsCount}
|
||||
currentCount={currentCount}
|
||||
/>
|
||||
<InnerContainer>{children}</InnerContainer>
|
||||
<Footer />
|
||||
<ScrollToTop isVisible={scrollToTopVisible} onClick={scrollToTop}>
|
||||
|
|
|
|||
|
|
@ -10,7 +10,8 @@ export const storeItem = (key, value) => {
|
|||
if (!localStorage) return;
|
||||
|
||||
try {
|
||||
return localStorage.setItem(key, JSON.stringify(value));
|
||||
localStorage.setItem(key, JSON.stringify(value));
|
||||
window.dispatchEvent(new Event('storage:updated'));
|
||||
} catch (err) {}
|
||||
};
|
||||
|
||||
|
|
@ -18,6 +19,15 @@ export const removeItemFromStorage = key => {
|
|||
if (!localStorage) return;
|
||||
|
||||
try {
|
||||
return localStorage.removeItem(key);
|
||||
localStorage.removeItem(key);
|
||||
window.dispatchEvent(new Event('storage:updated'));
|
||||
} catch (err) {}
|
||||
};
|
||||
|
||||
export const getLocalStorageLength = () => {
|
||||
if (!localStorage) return;
|
||||
|
||||
try {
|
||||
return localStorage.length;
|
||||
} catch (err) {}
|
||||
}
|
||||
|
|
@ -19,7 +19,7 @@ class Index extends React.Component<{}> {
|
|||
|
||||
render() {
|
||||
return (
|
||||
<Page>
|
||||
<Page displayProgress={true}>
|
||||
<SectionHeading>
|
||||
<Heading>Be safe on the internet.</Heading>
|
||||
<Subheading>
|
||||
|
|
|
|||
Loading…
Reference in a new issue