Skip to content

Commit

Permalink
Add collapsibility to calculator pages (#1878)
Browse files Browse the repository at this point in the history
* test: Try out one implementation of expandable

* feat: Rudimentary implementation of collapsible

* feat: Improve aesthetics

* feat: Improve aesthetics

* feat: Fix to bottom

* feat: CollapsedPanel

* fix: Improve styling

* feat: Naïve implementation; will refactor

* fix: Beginning of rewrite to use ThreeColumnPage for button

* fix: Refactor CollapsedPanel

* fix: Add panel titles as props

* fix: Improve styling at bottom of page

* feat: Add collapsing to household inputs

* feat: Refactor using padding instead of margin

* feat: Add proper sizing to middle panel of policy editor

* feat: Replace box shadow with top border

* fix: Redo how gradient ends are calced

* fix: Change center title on policy output

* fix: Repair household calculator panel names

* fix: Fix household left panel sizing

* fix: Improve sizing

* chore: Lint

* feat: Add width context to charts

* feat: Refactor BottomCarousel to work with collapse

* fix: Lint

* fix: Move resize observer to ThreePanelPage

* feat: Add resizing to input charts

* fix: Polyfill ResizeObserver

---------

Co-authored-by: PolicyEngine[bot] <hello@policyengine.org>
  • Loading branch information
anth-volk and PolicyEngine[bot] authored Jun 17, 2024
1 parent 6d189f6 commit 90f8a62
Show file tree
Hide file tree
Showing 28 changed files with 443 additions and 117 deletions.
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@
"lint-staged": "^15.2.5",
"node-fetch": "^3.3.2",
"prettier": "^3.2.5",
"resize-observer-polyfill": "^1.5.1",
"typescript": "^5.4.5",
"whatwg-fetch": "^3.6.20"
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { fireEvent, render, screen } from "@testing-library/react";
import BaselineAndReformChart from "pages/household/output/EarningsVariation/BaselineAndReformChart";
import { getValueFromHousehold } from "api/variables.js";
import ResizeObserver from "resize-observer-polyfill";

jest.mock("react-plotly.js", () => jest.fn());
jest.mock("../../../../../api/variables.js", () => ({
Expand All @@ -22,6 +23,8 @@ const metadata = {
},
};

global.ResizeObserver = ResizeObserver;

describe("Test Render Output", () => {
test("Should render toggle", () => {
getValueFromHousehold.mockImplementation(() => {
Expand Down
74 changes: 74 additions & 0 deletions src/controls/CollapseButton.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { DoubleLeftOutlined, DoubleRightOutlined } from "@ant-design/icons";
import { Tooltip } from "antd";
import fixedStyles from "../style";

export default function CollapseButton(props) {
const { onClick, isCollapsed, style, title } = props;

function clickHandler() {
if (onClick instanceof Function) {
onClick();
}
}

return (
<div
style={{
width: "100%",
display: "flex",
justifyContent: "flex-start",
alignItems: "center",
gap: "12px",
// Extra padding at bottom to
// avoid collision with scrollbar
// padding: "10px 0px 0px 20px",
paddingLeft: "20px",
borderStyle: "solid",
borderImage: `linear-gradient(to right,
${fixedStyles.colors.LIGHT_GRAY} 0px,
${fixedStyles.colors.LIGHT_GRAY} 12px,
${fixedStyles.colors.MEDIUM_DARK_GRAY} 12px,
${fixedStyles.colors.MEDIUM_DARK_GRAY} calc(100% - 12px),
${fixedStyles.colors.LIGHT_GRAY} calc(100% - 12px),
${fixedStyles.colors.LIGHT_GRAY} 100%) 100% 0 0 0/3px 0 0 0 stretch`,
backgroundColor: fixedStyles.colors.LIGHT_GRAY,
borderImageWidth: "1px",
borderWidth: "1px",
height: "100%",
...style,
}}
>
<Tooltip
title={`${isCollapsed ? "Maximize" : "Minimize"} panel`}
placement="right"
>
{isCollapsed ? (
<DoubleRightOutlined
onClick={clickHandler}
style={{
fontSize: "12px",
color: fixedStyles.colors.DARK_GRAY,
}}
/>
) : (
<DoubleLeftOutlined
onClick={clickHandler}
style={{
fontSize: "12px",
color: fixedStyles.colors.DARK_GRAY,
}}
/>
)}
</Tooltip>
<p
style={{
margin: "0",
fontSize: "12px",
color: fixedStyles.colors.DARK_GRAY,
}}
>
{title}
</p>
</div>
);
}
84 changes: 49 additions & 35 deletions src/layout/BottomCarousel.jsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,42 @@
import SearchParamNavButton from "../controls/SearchParamNavButton";
import style from "../style";
import useMobile from "./Responsive";
import { COLLAPSE_BUTTON_HEIGHT, PaneWidthContext } from "./ThreeColumnPage";
import { useContext } from "react";

export default function BottomCarousel(props) {
const { selected, options, bottomElements } = props;
const mobile = useMobile();
const currentIndex = options.map((option) => option.name).indexOf(selected);
const previous = options[currentIndex - 1] || {};
const next = options[currentIndex + 1] || {};
const paneWidth = useContext(PaneWidthContext);

// Show the previous to the left, the current in the middle, and the next to the right

return (
<div
style={{
position: "absolute",
position: "fixed",
bottom: mobile ? "25vh" : 0,
display: "flex",
height: "min-content",
left: mobile ? 0 : "50%",
width: mobile ? "100%" : "50%",
minHeight: COLLAPSE_BUTTON_HEIGHT,
right: 0,
width: mobile ? "100%" : paneWidth,
alignItems: "center",
backgroundColor: style.colors.WHITE,
justifyContent: mobile ? "center" : "right",
// borderTop: "1px solid black",
borderTop: `1px solid ${style.colors.MEDIUM_DARK_GRAY}`,
justifyContent: mobile ? "center" : "left",
borderImage: `linear-gradient(to right,
${style.colors.LIGHT_GRAY} 0px,
${style.colors.LIGHT_GRAY} 12px,
${style.colors.MEDIUM_DARK_GRAY} 12px,
${style.colors.MEDIUM_DARK_GRAY} calc(100% - 12px),
${style.colors.LIGHT_GRAY} calc(100% - 12px),
${style.colors.LIGHT_GRAY} 100%) 100% 0 0 0/3px 0 0 0 stretch`,
borderImageWidth: "1px",
borderWidth: "1px",
borderStyle: "solid",
padding: "10px 20px",
gap: "10px",
}}
Expand All @@ -35,40 +47,42 @@ export default function BottomCarousel(props) {
display: "flex",
justifyContent: "flex-end",
alignItems: "flex-start",
color: style.colors.DARK_GRAY,
}}
>
{bottomElements}
</div>
)}
<div
style={{
flex: 1,
display: "flex",
justifyContent: mobile ? "center" : "right",
alignItems: "flex-start",
gap: 20,
}}
>
{mobile && previous.label ? (
<SearchParamNavButton
focus={previous.name}
direction="left"
style={{ width: 50, fontSize: 16 }}
/>
) : (
<div style={{ width: 50 }} />
)}
{}
{mobile && next.label ? (
<SearchParamNavButton
focus={next.name}
direction="right"
style={{ width: 50, fontSize: 16 }}
/>
) : (
<div style={{ width: 60 }} />
)}
</div>
{mobile && (
<div
style={{
flex: 1,
display: "flex",
justifyContent: mobile ? "center" : "right",
alignItems: "flex-start",
gap: 20,
}}
>
{previous.label ? (
<SearchParamNavButton
focus={previous.name}
direction="left"
style={{ width: 50, fontSize: 16 }}
/>
) : (
<div style={{ width: 50 }} />
)}
{mobile && next.label ? (
<SearchParamNavButton
focus={next.name}
direction="right"
style={{ width: 50, fontSize: 16 }}
/>
) : (
<div style={{ width: 60 }} />
)}
</div>
)}
</div>
);
}
32 changes: 32 additions & 0 deletions src/layout/CollapsedPanel.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import style from "../style";

export default function CollapsedPanel(props) {
const { title } = props;

return (
<div
style={{
height: "100%",
width: "100%",
backgroundColor: style.colors.LIGHT_GRAY,
position: "relative",
display: "flex",
flexDirection: "column",
justifyContent: "space-between",
alignItems: "center",
}}
>
<p
style={{
writingMode: "sideways-lr",
margin: "0",
paddingTop: "30px",
fontSize: "12px",
color: style.colors.DARK_GRAY,
}}
>
{title}
</p>
</div>
);
}
54 changes: 43 additions & 11 deletions src/layout/HoverCard.jsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { AnimatePresence, motion } from "framer-motion";
import { createContext, useEffect, useState, memo } from "react";
import { createContext, useEffect, useState, memo, useRef } from "react";
import style from "../style";
import Divider from "./Divider";

export const HoverCardContext = createContext((obj) => obj);

export const ChartWidthContext = createContext((obj) => obj);

export default function HoverCard(props) {
const { children } = props;
const [enabled, setEnabled] = useState(false);
Expand Down Expand Up @@ -64,18 +66,48 @@ export default function HoverCard(props) {

const MemoizedChildren = memo(function MemoizedChildren(props) {
const { children, setContent, setEnabled } = props;

const [chartWidth, setChartWidth] = useState(0);

// This represents the div holding the hovercard item,
// and will be used for dynamic size updating
const containerRef = useRef(null);

useEffect(() => {
if (containerRef.current) {
// If the container div
const observer = new ResizeObserver((entries) => {
setChartWidth(entries[0].contentRect.width);
});

observer.observe(containerRef.current);

return () => {
observer.disconnect();
};
}
});

return (
<HoverCardContext.Provider value={setContent}>
<div
onMouseEnter={() => {
setEnabled(true);
}}
onMouseLeave={() => {
setEnabled(false);
}}
>
{children}
</div>
<ChartWidthContext.Provider value={chartWidth}>
<div
className="test"
onMouseEnter={() => {
setEnabled(true);
}}
onMouseLeave={() => {
setEnabled(false);
}}
ref={containerRef}
style={{
height: "100%",
display: "flex",
}}
>
{children}
</div>
</ChartWidthContext.Provider>
</HoverCardContext.Provider>
);
});
4 changes: 2 additions & 2 deletions src/layout/StackedMenu.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ export default function StackedMenu(props) {
}

return (
<div style={{ height: "80vh" }}>
<div style={{ overflow: "scroll", padding: 20 }}>{result}</div>
<div style={{ overflow: "scroll", padding: 20, height: "100%" }}>
{result}
</div>
);
}
Loading

0 comments on commit 90f8a62

Please sign in to comment.