Skip to content
This repository has been archived by the owner on Oct 8, 2019. It is now read-only.

[WIP] Stackchart - mobile alternative to treemap #547

Open
wants to merge 26 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
c8b2fbf
stackchart v1
antstalepresh Apr 14, 2019
0f2894c
stylechange
antstalepresh Apr 15, 2019
785af5a
scroll selection & sticky header
antstalepresh Apr 16, 2019
26067ca
fix font size, box height
antstalepresh Apr 18, 2019
ef172c2
fix unsticking when chart scrolls out of view
antstalepresh Apr 18, 2019
c65c5f2
make items not selected at initial stage
antstalepresh Apr 18, 2019
edcccfe
clipRect
antstalepresh Apr 18, 2019
d14fe57
implement lv1 chart
antstalepresh Apr 19, 2019
717f8c0
fix default selection + implment consolidated Lv1 chart
antstalepresh Apr 19, 2019
3652add
early unstick issue on level 1 chart and level2 chart not showing issue
antstalepresh Apr 23, 2019
cbf86f0
level2 stick top style fix
antstalepresh Apr 23, 2019
d339073
scrolling out of view
antstalepresh Apr 23, 2019
ccc5657
bottom margin fix for navigation
antstalepresh Apr 23, 2019
8237df9
fix behavior when scrolling out of view
antstalepresh Apr 23, 2019
71f4e71
Remove changes to global SectionHeading component
schalkventer May 10, 2019
dcf4359
Adds localised StickyHeader sub-component instead of changing global …
schalkventer May 10, 2019
946d921
Wrap scroll listener in class in order to help with mounting and unmo…
schalkventer May 10, 2019
dbfd6f2
Removes presentation logic from templating
schalkventer May 10, 2019
69d1563
Formalise schema for StackChart component
schalkventer May 10, 2019
b7b6378
Create helper functions and removes other redundant helper functions
schalkventer May 10, 2019
3d6d50c
Bring Storybook documentation in line with OpenUp React starter
schalkventer May 10, 2019
64dff6d
Update StackChart presentation logic
schalkventer May 10, 2019
8399390
Refactors StackChart state logic to not crash low-end devices
schalkventer May 10, 2019
c4b6048
Breaks Minimap into it's own component instead of coupling with Stack…
schalkventer May 10, 2019
a086421
Moves sticky header logic into parent presentation instead of couplin…
schalkventer May 10, 2019
c1d997e
Updates NationalTreemap view to correspond to refactor
schalkventer May 10, 2019
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: 7 additions & 3 deletions packages/webapp/src/components/ChartSection/Markup.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import trimValues from '../../helpers/trimValues';
import Icon from '@material-ui/icons/ArrowForward';
import SectionHeading from '../SectionHeading';
import CssBaseline from '@material-ui/core/CssBaseline';
import StickyHeading from './StickyHeading';
import Minimap from '../Minimap';

import {
DetailsWrapper,
Expand Down Expand Up @@ -70,18 +72,20 @@ const Markup = (props) => {
phases,
anchor,
title,
isConsolidatedChart
isConsolidatedChart,
minimapRender,
} = props;

return (
<React.Fragment>
<CssBaseline />
{!!minimapRender && !!selected && <StickyHeading {...{ selected, verb, minimapRender } } />}
<SectionHeading title={title} share={anchor} years={years} phases={phases} />
{!!selected && callDetails(selected, verb, subject, isConsolidatedChart)}
{callChart(chart, onSelectedChange)}
<FooterWrapper>
<FooterContainer>
{footer && <FooterDetails>{footer}</FooterDetails>}
{footer && <FooterDetails component="div">{footer}</FooterDetails>}
</FooterContainer>
</FooterWrapper>
</React.Fragment>
Expand Down
113 changes: 113 additions & 0 deletions packages/webapp/src/components/ChartSection/StickyHeading/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import React, { createPortal } from 'react';
import styled from 'styled-components';
import { darken } from 'polished';
import { ArrowForward } from '@material-ui/icons';
import { Button } from '@material-ui/core';
import trimValue from '../../../helpers/trimValues';

const Wrapper = styled.div`
width: 100%;
position: fixed;
z-index: 99999;
top: 0;
left: 0;
`

const Content = styled.div`
display: flex;
width: 100%;
align-items: center;
background: white;
box-shadow: rgba(0, 0, 0, 0.2) 0px 1px 8px 0px, rgba(0, 0, 0, 0.14) 0px 3px 4px 0px, rgba(0, 0, 0, 0.12) 0px 3px 3px -2px;
border-bottom: 2px solid white;
`

const Icon = styled(ArrowForward)`
&& {
height: 15px;
width: 15px;
}
`;

const Info = styled.div`
width: 100%;
padding: 17px;
min-width: 0;
`;

const ButtonWrapper = styled.a`
text-decoration: none;
padding-right: 17px;
display: block;
`;

const ButtonStyle = styled(Button)`
&& {
background-color: ${({ color }) => color};
text-transform: none;
box-shadow: none;
min-width: 0;
font-weight: 700;
line-height: 120%;
text-align: center;
color: #000;
display: flex;
justify-content: space-between;
align-items: center;

font-size: 12px;
padding: 9px;

&:hover {
background-color: ${({ color }) => darken(0.1, color)};
}
}
`;

const TextExploreButton = styled.div`
padding-right: 12px;
white-space: nowrap;

@media screen and (min-width: 450px) {
padding-right: 24px;
}
`;

const InnerButton = ({ url, color, verb }) => (
<ButtonStyle disabled={!url} {...{color}}>
<TextExploreButton>{verb}</TextExploreButton>
<Icon />
</ButtonStyle>
);

const Title = styled.div`
font-size: 12px;
font-family: Roboto, sans-serif;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
`;

const Amount = styled.div`
font-size: 24px;
font-weight: bold;
font-family: Roboto, sans-serif;
`;


const MobileSectionHeading = ({ selected: { url, color, name, value, id }, verb, minimapRender }) => (
<Wrapper>
<Content>
<Info>
<Title>{name}</Title>
<Amount>R{trimValue(value)}</Amount>
</Info>
<ButtonWrapper href={url}>
<InnerButton {...{ url, color, verb }} />
</ButtonWrapper>
</Content>
<div>{!!minimapRender && minimapRender(id)}</div>
</Wrapper>
);

export default MobileSectionHeading;
164 changes: 164 additions & 0 deletions packages/webapp/src/components/ChartSection/StickyHeading/styled.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
import {
Typography,
Select,
} from '@material-ui/core';

import styled from 'styled-components';

const Wrapper = styled.div`
display: flex;
justify-content: center;
align-items: center;
margin-right: 16px;
margin-left: 16px;
border-bottom: 2px solid red;
`;

const BudgetContainer = styled.div`
border-bottom: 1px solid #000;
margin-bottom: 20px;
max-width: 1200px;
padding-bottom: 16px;
width: 100%;
display: flex;
flex-direction: column;

@media screen and (min-width: 950px) {
flex-direction: row;
justify-content: space-between;
align-items: center;
margin-bottom: 24px;
padding-bottom: 24px;
}
`;

const BudgetHeadingAndShareIcon = styled.div`
display: flex;
justify-content: space-between;
align-items: right;
`;

const BudgetHeading = styled(Typography)`
&& {
font-weight: 700;
font-size: 18px;
line-height: 120%;
color: #000;
text-transform: Capitalize;
text-align: left;

@media screen and (min-width: 950px) {
white-space: nowrap;
padding-right: 22px;
font-size: 32px;
}
}
`;

const FormContainer = styled.div`
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 12px;

@media screen and (min-width: 375px) {
width: 100%;
flex-wrap: wrap;
}

@media screen and (min-width: 950px) {
width: auto;
flex-wrap: nowrap;
margin-top: 0;
}
`;

const BudgetPhase = styled.div`
@media screen and (min-width: 375px) {
width: 60%;
}

@media screen and (min-width: 950px) {
margin-right: 24px;
width: auto;
}
`;

const SelectStyledPhase = styled(Select)`
&& {
background: #d8d8d8;
border-radius: 3px;
padding: 8px 12px 8px 16px;
font-size: 14px;
line-height: 120%;
color: #000;

& .selectMenu {
padding-right: 32px;

@media screen and (min-width: 950px) {
padding-right: 56px;
}
}

& .disabled {
color: rgba(0, 0, 0, 0.26);
}

& .icon {
color: rgba(0, 0, 0, 0.26);
}

@media screen and (min-width: 375px) {
width: 100%;
}

@media screen and (min-width: 950px) {
font-size: 20px;
padding: 10px 16px;
width: auto;
}
}
`;

const SelectStyled = styled(SelectStyledPhase)`
&& {
@media screen and (min-width: 375px) {
width: 35%;
}

@media screen and (min-width: 950px) {
font-size: 20px;
padding: 10px 16px;
width: auto;
}
}
`;

const SpeedDialContainer = styled.div`
margin-right: 8px;
`;

export {
Wrapper,
BudgetContainer,
BudgetHeadingAndShareIcon,
BudgetHeading,
FormContainer,
BudgetPhase,
SelectStyled,
SelectStyledPhase,
SpeedDialContainer,
}

export default {
Wrapper,
BudgetContainer,
BudgetHeadingAndShareIcon,
BudgetHeading,
FormContainer,
BudgetPhase,
SelectStyled,
SelectStyledPhase,
SpeedDialContainer,
}
5 changes: 2 additions & 3 deletions packages/webapp/src/components/ChartSection/index.jsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import React, { Component } from 'react';
import Markup from './Markup';



class ChartSection extends Component {
constructor(props) {
super(props);
Expand All @@ -21,7 +19,8 @@ class ChartSection extends Component {
const { initialSelected } = this.props;

if (event === null) {
return this.setState({ selected: initialSelected });
this.setState({ selected: initialSelected });
return null;
}

this.setState({ selected: event });
Expand Down
21 changes: 20 additions & 1 deletion packages/webapp/src/components/ChartSection/index.story.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import React from 'react';
import Minimap from '../Minimap';
import { mockProps as mockMinimapProps } from '../Minimap/schema';
import { generateItems as mockChartItems } from '../StackChart/schema';
import { storiesOf } from '@storybook/react';

import ChartSection from './';
Expand Down Expand Up @@ -54,6 +57,8 @@ const Chart = ({ onSelectedChange }) => (
</ul>
);

const renderMinimap = () => <Minimap {...mockMinimapProps(false)} reverse />

const national = () => (
<ChartSection
{...{ data, initialSelected }}
Expand All @@ -80,8 +85,22 @@ const provincial = () => (
/>
);


const sticky = () => (
<ChartSection
{...{ initialSelected }}
data={mockChartItems()}
chart={(onSelectedChange) => <Chart {...{ onSelectedChange }} />}
verb='Explore'
subject='this department'
footer='Budget data from 1 April 2018 - 31 March 2019'
phases={phases}
years={years}
title='Provincial Budget Summary'
minimapRender={renderMinimap}
/>
);

storiesOf('component.ChartSection', module)
.add('National', national)
.add('Provincial', provincial)
.add('Sticky', sticky);
22 changes: 22 additions & 0 deletions packages/webapp/src/components/Minimap/Item.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React from 'react';
import styled from 'styled-components';

const Block = styled.div`
position: relative;
width: ${({ width }) => width}%;
height: 100%;
transition: transform 0.3s;
transform-origin: 0 ${({ reverse }) => reverse ? '100%' : '0'};
z-index: ${({ active }) => active ? '99999' : '1'};
border: 2px solid transparent;
transform: scaleY(${({ active }) => active ? '1.66' : '1'});
background: ${({ color, active }) => active ? 'black' : color};
`;

const Item = ({ convertHeightFn, selected, id, amount, color, reverse }) => {
const width = convertHeightFn(amount);
const active = selected === id;
return <Block {...{ width, color, active, reverse }} />;
}

export default Item;
Loading