From fb9c5243ab90b6ad6745f38eec34383de7aea84f Mon Sep 17 00:00:00 2001 From: Mark Chiu Date: Sat, 12 Nov 2022 11:37:15 -0500 Subject: [PATCH 1/6] Add Demographics page (Closes #53) (#73) Changes: (outside of Demographics page) * colour of the text (might be changed later on) because pure white strains the eye (contrast is too high). * font size + line height * SectionHeader word break fix of the title * `isMobile` breakpoint is changed to 900 https://demographics-page-csc-class-profile-staging-snedadah.k8s.csclub.cloud/demographics/ Co-authored-by: e26chiu Reviewed-on: https://git.csclub.uwaterloo.ca/www/cs-2022-class-profile/pulls/73 Reviewed-by: Shahan Nedadahandeh --- components/ComponentWrapper.module.css | 6 + components/SectionHeader.module.css | 1 + data/demographics.ts | 348 +++++++++++++++++++++++++ pages/demographics.tsx | 199 ++++++++++++++ pages/samplePage.module.css | 22 ++ utils/isMobile.ts | 2 +- 6 files changed, 577 insertions(+), 1 deletion(-) create mode 100644 data/demographics.ts create mode 100644 pages/demographics.tsx diff --git a/components/ComponentWrapper.module.css b/components/ComponentWrapper.module.css index 8cbdc3b..ded0e80 100644 --- a/components/ComponentWrapper.module.css +++ b/components/ComponentWrapper.module.css @@ -65,4 +65,10 @@ .internalWrapper { padding: calc(20rem / 16); +} + +.internalWrapper p { + font-size: calc(24rem / 16); + opacity: .85; + line-height: 1.25; } \ No newline at end of file diff --git a/components/SectionHeader.module.css b/components/SectionHeader.module.css index a9faa52..8723cfb 100644 --- a/components/SectionHeader.module.css +++ b/components/SectionHeader.module.css @@ -10,6 +10,7 @@ color: var(--primary-accent-light); font-size: calc(70rem / 16); margin: calc(40rem / 16) auto; + word-break: break-all; } .subTitle { diff --git a/data/demographics.ts b/data/demographics.ts new file mode 100644 index 0000000..2c35a22 --- /dev/null +++ b/data/demographics.ts @@ -0,0 +1,348 @@ +export const D1 = [ + { + category: "CS", + value: 88, + }, + { + category: "CS/BBA", + value: 12, + }, + { + category: "CFM", + value: 5, + }, +]; + +export const D2 = [ + { + category: "Man", + value: 72, + }, + { + category: "Woman", + value: 29, + }, + { + category: "Gender non-conforming", + value: 4, + }, +]; + +export const D3 = [ + { + category: "She/Her/Her", + value: 31, + }, + { + category: "He/Him/His", + value: 73, + }, + { + category: "They/Them/Their", + value: 3, + }, +]; + +export const D4 = [ + { + category: "Black", + value: 2, + }, + { + category: "Latin", + value: 1, + }, + { + category: "East Asian", + value: 68, + }, + { + category: "Middle Eastern", + value: 2, + }, + { + category: "South Asian", + value: 13, + }, + { + category: "Southeast Asian", + value: 2, + }, + { + category: "White", + value: 18, + }, + { + category: "Prefer not to say", + value: 1, + }, +]; + +export const D5 = [ + { + category: "89", + value: 1, + }, + { + category: "90", + value: 6, + }, + { + category: "91", + value: 1, + }, + { + category: "92", + value: 5, + }, + { + category: "93", + value: 4, + }, + { + category: "94", + value: 10, + }, + { + category: "95", + value: 15, + }, + { + category: "96", + value: 22, + }, + { + category: "97", + value: 21, + }, + { + category: "98", + value: 10, + }, + { + category: "99", + value: 8, + }, +]; + +export const D6 = [ + { + category: "Asexual", + value: 6, + }, + { + category: "Bisexual", + value: 12, + }, + { + category: "Gay", + value: 3, + }, + { + category: "Heterosexual", + value: 81, + }, + { + category: "Queer", + value: 3, + }, + { + category: "Pansexual", + value: 3, + }, + { + category: "Questioning", + value: 2, + }, + { + category: "Prefer not to say", + value: 2, + }, +]; + +export const D7 = [ + { + text: "Ontario (Other)", + value: 15, + }, + { + text: "British Columbia", + value: 11, + }, + { + text: "Kitchener / Waterloo", + value: 5, + }, + { + text: "Alberta", + value: 3, + }, + { + text: "Quebec", + value: 3, + }, + { + text: "USA", + value: 2, + }, + { + text: "Hong Kong", + value: 2, + }, + { + text: "Manitoba", + value: 2, + }, + { + text: "India", + value: 1, + }, + { + text: "GTA / Toronto", + value: 1, + }, + { + text: "United Arab Emirates", + value: 1, + }, + { + text: "Indonesia", + value: 1, + }, + { + text: "Saskatchewan", + value: 1, + }, +]; + +export const D8 = [ + { + category: "High School Diploma", + value: 2, + }, + { + category: "College Diploma", + value: 6, + }, + { + category: "Bachelor's Degree", + value: 43, + }, + { + category: "Master's Degree", + value: 33, + }, + { + category: "Doctoral Degree", + value: 17, + }, + { + category: "Not Applicable", + value: 1, + }, + { + category: "Prefer not to say", + value: 2, + }, +]; + +export const D9 = [ + { + category: "0-50", + value: 9, + }, + { + category: "51-100", + value: 28, + }, + { + category: "101-150", + value: 22, + }, + { + category: "151-200", + value: 23, + }, + { + category: "201-250", + value: 7, + }, + { + category: "251-300", + value: 4, + }, + { + category: "301+", + value: 5, + }, + { + category: "Prefer not to say", + value: 6, + }, +]; + +export const D10 = [ + { + category: "0", + value: 74, + }, + { + category: "1", + value: 17, + }, + { + category: "2", + value: 5, + }, + { + category: "3", + value: 2, + }, + { + category: "4", + value: 4, + }, + { + category: "5", + value: 2, + }, +]; + +export const D11 = [ + { + category: "No religious affiliation", + value: 79, + }, + { + category: "Christianity", + value: 16, + }, + { + category: "Atheist", + value: 1, + }, + { + category: "Judaism", + value: 3, + }, + { + category: "Buddhism", + value: 2, + }, + { + category: "Islam", + value: 1, + }, + { + category: "Hinduism", + value: 5, + }, + { + category: "Jainism", + value: 1, + }, + { + category: "Prefer not to say", + value: 2, + }, +]; diff --git a/pages/demographics.tsx b/pages/demographics.tsx new file mode 100644 index 0000000..7999f88 --- /dev/null +++ b/pages/demographics.tsx @@ -0,0 +1,199 @@ +import { + D1, + D2, + D3, + D4, + D5, + D6, + D7, + D8, + D9, + D10, + D11, +} from "data/demographics"; +import { pageRoutes } from "data/routes"; +import React from "react"; +import { useWindowDimensions } from "utils/getWindowDimensions"; +import { useIsMobile } from "utils/isMobile"; + +import { BarGraphHorizontal, BarGraphVertical } from "@/components/BarGraph"; +import { BottomNav } from "@/components/BottomNav"; +import { ComponentWrapper } from "@/components/ComponentWrapper"; +import { Header } from "@/components/Header"; +import { PieChart } from "@/components/PieChart"; +import { SectionHeader } from "@/components/SectionHeader"; +import { WordCloud } from "@/components/WordCloud"; + +import styles from "./samplePage.module.css"; + +export default function Demographics() { + const { width } = useWindowDimensions(); + const isMobile = useIsMobile(); + + const defaultGraphWidth = isMobile ? width / 1.25 : width / 2; + const defaultPieChartWidth = isMobile ? width / 1.25 : width / 3; + const defaultGraphHeight = 500; + + const defaultBarGraphMargin = { top: 20, bottom: 80, left: 60, right: 20 }; + + const defaultLabelSize = 24; + const defaultLabelWidth = 100; + + return ( +
+
+ + + +
+ +
+
+ + +
+ +
+
+ + +
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ ); +} diff --git a/pages/samplePage.module.css b/pages/samplePage.module.css index b50d07b..f3761c2 100644 --- a/pages/samplePage.module.css +++ b/pages/samplePage.module.css @@ -2,4 +2,26 @@ display: flex; flex-direction: column; justify-content: center; +} + +.graphContainer { + padding: 0 calc(70rem / 16); +} + +@media screen and (max-width: 1200px) { + .graphContainer { + padding: 0 calc(40rem / 16); + } +} + +@media screen and (max-width: 1100px) { + .graphContainer { + padding: 0 calc(20rem / 16); + } +} + +@media screen and (max-width: 900px) { + .graphContainer { + padding: 0; + } } \ No newline at end of file diff --git a/utils/isMobile.ts b/utils/isMobile.ts index 4022df1..a8fbc43 100644 --- a/utils/isMobile.ts +++ b/utils/isMobile.ts @@ -1,3 +1,3 @@ import { useWindowDimensions } from "./getWindowDimensions"; -export const useIsMobile = () => useWindowDimensions().width <= 768; +export const useIsMobile = () => useWindowDimensions().width <= 900; From aa25b8451b1142e7497422ebb061fea5d8906394 Mon Sep 17 00:00:00 2001 From: Mark Chiu Date: Sun, 13 Nov 2022 18:36:34 -0500 Subject: [PATCH 2/6] Fix Mobile Vertical Bar Graph Labels (Closes #50) (#83) * Add alternating label functionality to vertical bar graphs when the width of the graph reaches its `minWidth` * Add `minWidth` prop to horizontal and vertical bar graphs, by default, set to 500 (make a PR to contain these default prop values) * Add overflow scroll on mobile for horizontal and vertical bar graphs https://fix-mobile-bug-labels-csc-class-profile-stag-snedadah.k8s.csclub.cloud/ Co-authored-by: e26chiu Co-authored-by: shahanneda Reviewed-on: https://git.csclub.uwaterloo.ca/www/cs-2022-class-profile/pulls/83 Reviewed-by: Shahan Nedadahandeh --- components/BarGraph.tsx | 38 +++++++++++++++++++------- components/ComponentWrapper.module.css | 9 +++++- components/ComponentWrapper.tsx | 6 +++- pages/_app.css | 5 ++++ pages/demographics.tsx | 3 +- 5 files changed, 48 insertions(+), 13 deletions(-) diff --git a/components/BarGraph.tsx b/components/BarGraph.tsx index b4b0a03..6884ab3 100644 --- a/components/BarGraph.tsx +++ b/components/BarGraph.tsx @@ -43,6 +43,16 @@ interface BarGraphProps { valueAxisLabelSize?: number; /** Controls the distance between the value axis label and the value axis. */ valueAxisLabelOffset?: number; + /** Minimum width of the graph. */ + minWidth?: number; + /** Breakpoint width of graph where alernating labels are displayed. Only for Vertical graphs */ + widthAlternatingLabel?: number; + /** Space added to the bottom of the graph to show overflowing labels. Only for Vertical graphs */ + alternatingLabelSpace?: number; + /** Default position of labels in x-axis, in px. */ + defaultLabelDy?: string; + /** Position of lower labels in x-axis, in px. Only for Vertical graphs */ + lowerLabelDy?: string; } interface BarGraphData { @@ -54,11 +64,11 @@ const DEFAULT_LABEL_SIZE = 16; export function BarGraphHorizontal(props: BarGraphProps) { const { - width, height, margin, data, className, + minWidth = 500, categoryTickLabelSize = DEFAULT_LABEL_SIZE, valueTickLabelSize = DEFAULT_LABEL_SIZE, hoverLabelSize, @@ -68,8 +78,9 @@ export function BarGraphHorizontal(props: BarGraphProps) { valueAxisLabel, valueAxisLabelSize = DEFAULT_LABEL_SIZE, valueAxisLabelOffset = 0, + defaultLabelDy = "0", } = props; - + const width = props.width < minWidth ? minWidth : props.width; // Ensuring graph's width >= minWidth const barPadding = 0.4; const categoryMax = height - margin.top - margin.bottom; @@ -163,8 +174,6 @@ export function BarGraphHorizontal(props: BarGraphProps) { return { ...leftTickLabelProps(), className: styles.tickLabel, - dx: "-0.5rem", - dy: "0.25rem", fontSize: `${categoryTickLabelSize / 16}rem`, }; }} @@ -186,7 +195,7 @@ export function BarGraphHorizontal(props: BarGraphProps) { return { ...bottomTickLabelProps(), className: styles.tickLabel, - dy: "0.25rem", + dy: defaultLabelDy, fontSize: `${valueTickLabelSize / 16}rem`, }; }} @@ -203,11 +212,11 @@ export function BarGraphHorizontal(props: BarGraphProps) { export function BarGraphVertical(props: BarGraphProps) { const { - width, height, margin, data, className, + minWidth = 500, categoryTickLabelSize = DEFAULT_LABEL_SIZE, valueTickLabelSize = DEFAULT_LABEL_SIZE, hoverLabelSize, @@ -217,12 +226,20 @@ export function BarGraphVertical(props: BarGraphProps) { valueAxisLabel, valueAxisLabelSize = DEFAULT_LABEL_SIZE, valueAxisLabelOffset = 0, + widthAlternatingLabel = 600, + alternatingLabelSpace = 80, + defaultLabelDy = `0px`, + lowerLabelDy = `30px`, } = props; - + const width = props.width < minWidth ? minWidth : props.width; // Ensuring graph's width >= minWidth const barPadding = 0.4; + const alternatingLabel = width <= widthAlternatingLabel; + const final_margin_bottom = alternatingLabel + ? margin.bottom + alternatingLabelSpace + : margin.bottom; const categoryMax = width - margin.left - margin.right; - const valueMax = height - margin.top - margin.bottom; + const valueMax = height - margin.top - final_margin_bottom; const getCategory = (d: BarGraphData) => d.category; const getValue = (d: BarGraphData) => d.value; @@ -308,11 +325,12 @@ export function BarGraphVertical(props: BarGraphProps) { left={margin.left} hideAxisLine hideTicks - tickLabelProps={() => { + tickLabelProps={(value, index) => { + const alternatingDy = index % 2 == 0 ? defaultLabelDy : lowerLabelDy; return { ...bottomTickLabelProps(), className: styles.tickLabel, - dy: "-0.25rem", + dy: alternatingLabel ? alternatingDy : defaultLabelDy, fontSize: `${categoryTickLabelSize / 16}rem`, width: categoryScale.bandwidth(), verticalAnchor: "start", diff --git a/components/ComponentWrapper.module.css b/components/ComponentWrapper.module.css index ded0e80..25f8ae3 100644 --- a/components/ComponentWrapper.module.css +++ b/components/ComponentWrapper.module.css @@ -45,7 +45,10 @@ .wrapperNoBodyText { flex-direction: column; - align-items: center; +} + +.wrapperNoBodyText .internalWrapper { + text-align: center; } @media screen and (max-width: 900px) { @@ -61,6 +64,10 @@ .wrapperCenter { padding: 0; } + + .horizontalScrollOnMobile { + overflow: scroll; + } } .internalWrapper { diff --git a/components/ComponentWrapper.tsx b/components/ComponentWrapper.tsx index 400b0c5..1d98bee 100644 --- a/components/ComponentWrapper.tsx +++ b/components/ComponentWrapper.tsx @@ -37,7 +37,11 @@ export function ComponentWrapper({

{heading}

{bodyText ?

{bodyText}

: null} -
{children}
+
+ {children} +
); } diff --git a/pages/_app.css b/pages/_app.css index 94a87f7..d1bf82b 100644 --- a/pages/_app.css +++ b/pages/_app.css @@ -2,6 +2,10 @@ html { scroll-behavior: smooth; } +html, body { + overflow-x: hidden; +} + body { /* Theme colours */ --pink: #EF839D; @@ -68,6 +72,7 @@ body { color: var(--primary-text); font-family: "Inconsolata", monospace; margin: 0; + position: relative; /* Font styling for body */ font-size: calc(18rem / 16); diff --git a/pages/demographics.tsx b/pages/demographics.tsx index 7999f88..4d5e5a8 100644 --- a/pages/demographics.tsx +++ b/pages/demographics.tsx @@ -124,6 +124,7 @@ export default function Demographics() { width={defaultGraphWidth} height={defaultGraphHeight} margin={defaultBarGraphMargin} + widthAlternatingLabel={700} /> @@ -189,7 +190,7 @@ export default function Demographics() { data={D11} width={defaultGraphWidth} height={defaultGraphHeight} - margin={{ ...defaultBarGraphMargin, ...{ left: 200 } }} + margin={{ ...defaultBarGraphMargin, ...{ left: 220 } }} /> From d39a1f82741a006c6f79da340d0d59bb58921d07 Mon Sep 17 00:00:00 2001 From: Shahan Nedadahandeh Date: Sun, 13 Nov 2022 23:11:29 -0500 Subject: [PATCH 3/6] Add media query to section header and update sample page (#85) https://update-smpl-page-csc-class-profile-staging-snedadah.k8s.csclub.cloud/demographics/ Co-authored-by: shahanneda Reviewed-on: https://git.csclub.uwaterloo.ca/www/cs-2022-class-profile/pulls/85 Reviewed-by: Mark Chiu --- components/SectionHeader.module.css | 12 +++++++++++ pages/samplePage.tsx | 32 ++++++++++++++++++----------- 2 files changed, 32 insertions(+), 12 deletions(-) diff --git a/components/SectionHeader.module.css b/components/SectionHeader.module.css index 8723cfb..6da437d 100644 --- a/components/SectionHeader.module.css +++ b/components/SectionHeader.module.css @@ -17,4 +17,16 @@ color: var(--primary-accent-lighter); font-size: calc(26rem / 16); margin: auto; +} + +@media screen and (max-width: 900px) { + .title { + font-size: calc(50rem / 16); + margin: calc(20rem / 16) auto; + } + + .subTitle { + font-size: calc(30rem / 16); + margin: auto calc(15rem / 16); + } } \ No newline at end of file diff --git a/pages/samplePage.tsx b/pages/samplePage.tsx index a1be48d..70b8f94 100644 --- a/pages/samplePage.tsx +++ b/pages/samplePage.tsx @@ -22,7 +22,18 @@ export default function SamplePage() { const defaultGraphHeight = 500; // Make vars for common configs such as common margins - const defaultBarGraphMargin = { top: 20, bottom: 40, left: 150, right: 20 }; + const defaultVerticalBarGraphMargin = { + top: 20, + bottom: 80, + left: 60, + right: 20, + }; + const defaultHorizontalBarGraphMargin = { + top: 20, + bottom: 80, + left: 120, + right: 20, + }; return (
@@ -39,7 +50,7 @@ export default function SamplePage() { data={mockCategoricalData} width={defaultGraphWidth} height={defaultGraphHeight} - margin={defaultBarGraphMargin} + margin={defaultVerticalBarGraphMargin} /> @@ -59,17 +70,14 @@ export default function SamplePage() { /> - + @@ -84,7 +92,7 @@ export default function SamplePage() { data={mockCategoricalData} width={defaultGraphWidth} height={defaultGraphHeight} - margin={defaultBarGraphMargin} + margin={defaultHorizontalBarGraphMargin} /> @@ -94,7 +102,7 @@ export default function SamplePage() { data={mockCategoricalData} width={defaultGraphWidth} height={defaultGraphHeight} - margin={defaultBarGraphMargin} + margin={defaultHorizontalBarGraphMargin} /> @@ -157,7 +165,7 @@ export default function SamplePage() { data={mockCategoricalData} width={defaultGraphWidth} height={defaultGraphHeight} - margin={defaultBarGraphMargin} + margin={defaultHorizontalBarGraphMargin} /> @@ -171,7 +179,7 @@ export default function SamplePage() { data={mockCategoricalData} width={defaultGraphWidth} height={defaultGraphHeight} - margin={defaultBarGraphMargin} + margin={defaultHorizontalBarGraphMargin} /> From e061850575209d79e9168bc1d80744f2e6c1e3d3 Mon Sep 17 00:00:00 2001 From: Shahan Nedadahandeh Date: Sun, 13 Nov 2022 23:20:57 -0500 Subject: [PATCH 4/6] Fixed data and tooltip issues with wordcloud (#84) Closes #80 Closes #78 Added finer control for word cloud min/max font sizes. Changed defaults to be a bit more sensible, and also adjusted the values for the demographics page word cloud to display all data (main culprit was "wordPadding" was too big) Added a console.error for when not all the words are being display to aid in development for when we're adjusting the min/max font sizes. Fixed word cloud tooltip not being correct on centered word clouds. Added a min width to the word cloud, aniticipating changes from #85 https://fix-wordcloud-data-csc-class-profile-staging-snedadah.k8s.csclub.cloud/samplePage/ https://fix-wordcloud-data-csc-class-profile-staging-snedadah.k8s.csclub.cloud/demographics/ Co-authored-by: shahanneda Reviewed-on: https://git.csclub.uwaterloo.ca/www/cs-2022-class-profile/pulls/84 Reviewed-by: Mark Chiu --- components/WordCloud.tsx | 103 ++++++++++++++++++++++++++++----------- pages/demographics.tsx | 5 +- utils/inDevEnviroment.ts | 3 ++ 3 files changed, 82 insertions(+), 29 deletions(-) create mode 100644 utils/inDevEnviroment.ts diff --git a/components/WordCloud.tsx b/components/WordCloud.tsx index ca295e5..a0e087c 100644 --- a/components/WordCloud.tsx +++ b/components/WordCloud.tsx @@ -6,6 +6,8 @@ import { TooltipWithBounds, useTooltip, withTooltip } from "@visx/tooltip"; import { Wordcloud as VisxWordcloud } from "@visx/wordcloud"; import React from "react"; import { Color } from "utils/Color"; +import { inDevEnvironment } from "utils/inDevEnviroment"; +import { useIsMobile } from "utils/isMobile"; import styles from "./WordCloud.module.css"; @@ -13,16 +15,22 @@ interface WordCloudProps { data: Array; /** Width of the graph, in px */ width?: number; + /** The minimum width of the graph */ + minWidth?: number; /** Height of the graph, in px */ height?: number; /** Minimum padding between words, in px */ wordPadding?: number; /** Weight of the font of the words */ fontWeight?: number; - /** The desired font size of the smallest word, in px.*/ - minFontSize?: number; - /** The desired font size of the largest word, in px. */ - maxFontSize?: number; + /** The desired font size of the smallest word on desktop, in px.*/ + desktopMinFontSize?: number; + /** The desired font size of the smallest word on mobile, in px.*/ + mobileMinFontSize?: number; + /** The desired font size of the largest word on desktop, in px. */ + desktopMaxFontSize?: number; + /** The desired font size of the largest word on mobile, in px. */ + mobileMaxFontSize?: number; /** A random seed in the range [0, 1) used for placing the words, change this value to get an alternate placement of words */ randomSeed?: number; /** Type of spiral used for rendering the words, either rectangular or archimedean */ @@ -46,11 +54,14 @@ export const WordCloud = withTooltip( height, wordPadding, fontWeight, - minFontSize, - maxFontSize, + desktopMinFontSize, + mobileMinFontSize, + desktopMaxFontSize, + mobileMaxFontSize, randomSeed, spiral, className, + minWidth, }: WordCloudProps) => { const { tooltipData, @@ -69,8 +80,10 @@ export const WordCloud = withTooltip( data={data} wordPadding={wordPadding} fontWeight={fontWeight} - minFontSize={minFontSize} - maxFontSize={maxFontSize} + desktopMinFontSize={desktopMinFontSize} + mobileMinFontSize={mobileMinFontSize} + desktopMaxFontSize={desktopMaxFontSize} + mobileMaxFontSize={mobileMaxFontSize} showTooltip={(data, left, top) => { showTooltip({ tooltipData: data, @@ -83,6 +96,8 @@ export const WordCloud = withTooltip( tooltipTop={tooltipTop} randomSeed={randomSeed} spiral={spiral} + isMobile={useIsMobile()} + minWidth={minWidth} /> {tooltipOpen && tooltipData ? ( @@ -114,27 +129,38 @@ type WordCloudWordsProps = Omit & { // tooltipLeft and tooltipTop are used for preventing unnessary renders tooltipLeft?: number; tooltipTop?: number; + isMobile: boolean; // passing in isMobile as a prop so we can rerender if this changes }; const WordCloudWords: React.FC = ({ data, width = 1000, + minWidth = 500, height = 500, - wordPadding = 30, - fontWeight = 500, - minFontSize = 20, - maxFontSize = 150, + wordPadding = 20, + fontWeight = 400, + desktopMinFontSize = 15, + desktopMaxFontSize = 100, + mobileMinFontSize = 15, + mobileMaxFontSize = 60, randomSeed = 0.5, spiral = "rectangular", showTooltip, hideTooltip, + isMobile, }) => { + width = width < minWidth ? minWidth : width; + + const minFontSize = isMobile ? mobileMinFontSize : desktopMinFontSize; + const maxFontSize = isMobile ? mobileMaxFontSize : desktopMaxFontSize; + + const maxVal = Math.max(...data.map((w) => w.value)); + const minVal = Math.min(...data.map((w) => w.value)); + const fontScale = scaleLog({ - domain: [ - Math.min(...data.map((w) => w.value)), - Math.max(...data.map((w) => w.value)), - ], + domain: [minVal, maxVal], range: [minFontSize, maxFontSize], }); + const fontSizeSetter = (datum: WordData) => fontScale(datum.value); const fixedValueGenerator = () => randomSeed; return ( @@ -149,8 +175,19 @@ const WordCloudWords: React.FC = ({ rotate={0} random={fixedValueGenerator} > - {(cloudWords) => - cloudWords.map((word, index) => { + {(cloudWords) => { + if ( + inDevEnvironment && + cloudWords.length != 0 && // since on initial load the length is 0, but thats not an error + cloudWords.length != data.length + ) { + console.error( + `Not all words rendered for wordcloud! (${ + data.length - cloudWords.length + } words missing) Please try adjusting the min/max font size, the random seed, and the wordPadding` + ); + } + return cloudWords.map((word, index) => { return ( = ({ textAnchor="middle" onMouseMove={ ((e: React.MouseEvent) => { - const eventSvgCoords = localPoint( - // ownerSVGElement is given by visx docs but not recognized by typescript - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - e.target.ownerSVGElement as Element, - e - ) as Point; + // ownerSVGElement is given by visx docs but not recognized by typescript + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + const eventElement = e.target.ownerSVGElement as Element; + const eventSvgCoords = localPoint(eventElement, e) as Point; + const rootSVGLeft = + eventElement.parentElement?.parentElement?.getBoundingClientRect() + .left ?? 0; + const parentDivLeft = + eventElement.parentElement?.parentElement?.parentElement?.getBoundingClientRect() + .left ?? 0; + + // visx localPoint does not account for the horizontal shift due to centering of the parent element, + // so manually add any shift from that + const alignmentOffset = rootSVGLeft - parentDivLeft; if (word.text) { showTooltip( { text: word.text, value: data[index].value }, eventSvgCoords.x - - word.text.length * TOOLTIP_HORIZONTAL_SHIFT_SCALER, + word.text.length * TOOLTIP_HORIZONTAL_SHIFT_SCALER + + alignmentOffset, eventSvgCoords.y ); } @@ -186,8 +232,8 @@ const WordCloudWords: React.FC = ({ {word.text} ); - }) - } + }); + }} ); }; @@ -199,6 +245,7 @@ const shouldNotRerender = ( if ( // if width changes, rerender, else don't rerender for a tooltip change prevProps.width === nextProps.width && + prevProps.isMobile === nextProps.isMobile && (prevProps.tooltipLeft !== nextProps.tooltipLeft || prevProps.tooltipTop !== nextProps.tooltipTop || nextProps.tooltipLeft === undefined || diff --git a/pages/demographics.tsx b/pages/demographics.tsx index 4d5e5a8..cece66f 100644 --- a/pages/demographics.tsx +++ b/pages/demographics.tsx @@ -16,7 +16,7 @@ import React from "react"; import { useWindowDimensions } from "utils/getWindowDimensions"; import { useIsMobile } from "utils/isMobile"; -import { BarGraphHorizontal, BarGraphVertical } from "@/components/BarGraph"; +import { BarGraphVertical, BarGraphHorizontal } from "@/components/BarGraph"; import { BottomNav } from "@/components/BottomNav"; import { ComponentWrapper } from "@/components/ComponentWrapper"; import { Header } from "@/components/Header"; @@ -137,6 +137,9 @@ export default function Demographics() { data={D7} width={isMobile ? width / 1.5 : 800} height={defaultGraphHeight} + wordPadding={7} + desktopMaxFontSize={75} + mobileMaxFontSize={48} /> diff --git a/utils/inDevEnviroment.ts b/utils/inDevEnviroment.ts new file mode 100644 index 0000000..872ea50 --- /dev/null +++ b/utils/inDevEnviroment.ts @@ -0,0 +1,3 @@ +const inDevEnvironment = process && process.env.NODE_ENV === "development"; + +export { inDevEnvironment }; From 97aa2261f29ed0d24937178ca27eb4f8c276a72b Mon Sep 17 00:00:00 2001 From: Shahan Nedadahandeh Date: Wed, 16 Nov 2022 21:35:55 -0500 Subject: [PATCH 5/6] Created TooltipWrapper (#88) Co-authored-by: shahanneda Reviewed-on: https://git.csclub.uwaterloo.ca/www/cs-2022-class-profile/pulls/88 Reviewed-by: Mark Chiu --- components/Boxplot.module.css | 32 +-- components/Boxplot.tsx | 26 +- components/LineGraph.module.css | 14 - components/LineGraph.tsx | 385 +++++++++++++------------- components/StackedBarGraph.module.css | 17 -- components/StackedBarGraph.tsx | 20 +- components/TooltipWrapper.module.css | 31 +++ components/TooltipWrapper.tsx | 35 +++ components/WordCloud.module.css | 15 - components/WordCloud.tsx | 14 +- pages/samplePage.tsx | 52 ++-- 11 files changed, 316 insertions(+), 325 deletions(-) create mode 100644 components/TooltipWrapper.module.css create mode 100644 components/TooltipWrapper.tsx diff --git a/components/Boxplot.module.css b/components/Boxplot.module.css index f776cf6..66dda09 100644 --- a/components/Boxplot.module.css +++ b/components/Boxplot.module.css @@ -5,34 +5,4 @@ .boxplot:hover { fill: var(--primary-accent); filter: drop-shadow(0 0 calc(4rem / 16) var(--primary-accent)); -} - -.tooltip { - font-family: "Inconsolata", monospace; - top: 0; - left: 0; - position: absolute; - background-color: var(--label); - color: var(--primary-background); - pointer-events: none; - padding: calc(10rem / 16); - border-radius: calc(10rem / 16); -} - -.tooltip .category { - margin: calc(10rem / 16) 0 0 0; - font-size: calc(16rem / 16); - font-weight: 700; -} - -.tooltip .toolTipData { - margin-top: calc(5rem / 16); - margin-bottom: calc(10rem / 16); - font-size: calc(16rem / 16); -} - -.tooltip .toolTipData p { - margin: 0; - padding: 0; - font-size: calc(16rem / 16); -} +} \ No newline at end of file diff --git a/components/Boxplot.tsx b/components/Boxplot.tsx index 924bc65..f413b2b 100644 --- a/components/Boxplot.tsx +++ b/components/Boxplot.tsx @@ -6,11 +6,13 @@ import { Point } from "@visx/point"; import { scaleBand, scaleLinear } from "@visx/scale"; import { Line } from "@visx/shape"; import { BoxPlot as VisxBoxPlot } from "@visx/stats"; -import { withTooltip, Tooltip } from "@visx/tooltip"; +import { withTooltip } from "@visx/tooltip"; import { WithTooltipProvidedProps } from "@visx/tooltip/lib/enhancers/withTooltip"; import React from "react"; import { Color } from "utils/Color"; +import { TooltipWrapper } from "./TooltipWrapper"; + import styles from "./Boxplot.module.css"; const DEFAULT_LABEL_SIZE = 16; @@ -339,21 +341,17 @@ export const BoxPlot = withTooltip( {tooltipOpen && tooltipData && ( - -

{tooltipData.category}

-
-

max: {tooltipData.max}

-

third quartile: {tooltipData.thirdQuartile}

-

median: {tooltipData.median}

-

first quartile: {tooltipData.firstQuartile}

-

min: {tooltipData.min}

-
-
+

max: {tooltipData.max}

+

third quartile: {tooltipData.thirdQuartile}

+

median: {tooltipData.median}

+

first quartile: {tooltipData.firstQuartile}

+

min: {tooltipData.min}

+ )}
); diff --git a/components/LineGraph.module.css b/components/LineGraph.module.css index bb8cdfe..8fedff0 100644 --- a/components/LineGraph.module.css +++ b/components/LineGraph.module.css @@ -8,20 +8,6 @@ filter: drop-shadow(0 0 calc(4rem / 16) var(--primary-accent)); } -.tooltip { - font-family: "Inconsolata", monospace; - font-weight: bold; - top: 0; - left: 0; - position: absolute; - background-color: var(--label); - color: var(--primary-background); - box-shadow: 0px calc(1rem / 16) calc(2rem / 16) var(--card-background); - pointer-events: none; - padding: calc(10rem / 16); - font-size: calc(18rem / 16); - border-radius: calc(10rem / 16); -} .wrapper { display: flex; diff --git a/components/LineGraph.tsx b/components/LineGraph.tsx index 7e98241..878a948 100644 --- a/components/LineGraph.tsx +++ b/components/LineGraph.tsx @@ -8,10 +8,12 @@ import { LegendOrdinal } from "@visx/legend"; import { Point } from "@visx/point"; import { scaleBand, scaleLinear, scaleOrdinal } from "@visx/scale"; import { LinePath } from "@visx/shape"; -import { useTooltip, useTooltipInPortal } from "@visx/tooltip"; +import { withTooltip } from "@visx/tooltip"; import React from "react"; import { Color } from "utils/Color"; +import { TooltipWrapper } from "./TooltipWrapper"; + import styles from "./LineGraph.module.css"; interface LineData { @@ -85,8 +87,11 @@ const DEFAULT_LEGEND_GAP = 16; // TODO: Address unused props in this file /* eslint-disable unused-imports/no-unused-vars*/ -export function LineGraph(props: LineGraphProps) { - const { + +type TooltipData = string; + +export const LineGraph = withTooltip( + ({ width, height, margin, @@ -101,211 +106,199 @@ export function LineGraph(props: LineGraphProps) { yAxisLabel, yAxisLabelSize = DEFAULT_LABEL_SIZE, yAxisLabelOffset = 0, - legendProps, - } = props; - - const { - position: legendPosition = "right", - itemLabelSize: legendLabelSize = DEFAULT_LABEL_SIZE, - itemGap: legendItemGap = DEFAULT_LEGEND_GAP, - margin: legendMargin = {}, - } = legendProps ?? {}; - - const xLength = data.xValues.length; - - data.lines.forEach((line) => { - if (line.yValues.length != xLength) { - throw new Error("Invalid data with wrong length."); - } - }); - - const { - tooltipData, + tooltipOpen, tooltipLeft, tooltipTop, - tooltipOpen, - showTooltip, + tooltipData, hideTooltip, - } = useTooltip(); + showTooltip, + legendProps, + }) => { + const { + position: legendPosition = "right", + itemLabelSize: legendLabelSize = DEFAULT_LABEL_SIZE, + itemGap: legendItemGap = DEFAULT_LEGEND_GAP, + margin: legendMargin = {}, + } = legendProps ?? {}; - const { containerRef, TooltipInPortal } = useTooltipInPortal({ - // use TooltipWithBounds - detectBounds: true, - // when tooltip containers are scrolled, this will correctly update the Tooltip position - scroll: true, - }); + const xLength = data.xValues.length; - const yMax = height - margin.top - margin.bottom; - const xMax = width - margin.left - margin.right; - - const actualData = data.lines.map((line) => { - return line.yValues.map((val, idx) => { - return { x: data.xValues[idx], y: val }; + data.lines.forEach((line) => { + if (line.yValues.length != xLength) { + throw new Error("Invalid data with wrong length."); + } }); - }); - const yMaxValue = Math.max( - ...data.lines.map((line) => { - return Math.max(...line.yValues); - }) - ); + const yMax = height - margin.top - margin.bottom; + const xMax = width - margin.left - margin.right; - // data accessors - const getX = (d: PointData) => d.x; - const getY = (d: PointData) => d.y; + const actualData = data.lines.map((line) => { + return line.yValues.map((val, idx) => { + return { x: data.xValues[idx], y: val }; + }); + }); - // scales - const xScale = scaleBand({ - range: [0, xMax], - domain: data.xValues, - }); + const yMaxValue = Math.max( + ...data.lines.map((line) => { + return Math.max(...line.yValues); + }) + ); - const yScale = scaleLinear({ - range: [0, yMax], - nice: true, - domain: [yMaxValue, 0], - }); + // data accessors + const getX = (d: PointData) => d.x; + const getY = (d: PointData) => d.y; - const keys = data.lines.map((line) => line.label); + // scales + const xScale = scaleBand({ + range: [0, xMax], + domain: data.xValues, + }); - const legendScale = scaleOrdinal({ - domain: keys, - range: [Color.primaryAccent, Color.secondaryAccent], - }); + const yScale = scaleLinear({ + range: [0, yMax], + nice: true, + domain: [yMaxValue, 0], + }); - return ( -
- - - - - { - return { - ...bottomTickLabelProps(), - className: styles.tickLabel, - dy: "-0.25rem", - fontSize: `${xTickLabelSize / 16}rem`, - width: xScale.bandwidth(), - }; - }} - /> - { - return { - ...leftTickLabelProps(), - className: styles.tickLabel, - dx: "1.25rem", - dy: "0.25rem", - fontSize: `${yTickLabelSize / 16}rem`, - }; - }} - /> - - {actualData.map((lineData, i) => { - const isEven = i % 2 === 0; - return ( - - { - const eventSvgCoords = localPoint( - // ownerSVGElement is given by visx docs but not recognized by typescript - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - e.target.ownerSVGElement as Element, - e - ) as Point; - showTooltip({ - tooltipData: data.lines[i].label, - tooltipTop: eventSvgCoords.y, - tooltipLeft: eventSvgCoords.x, - }); - }} - onMouseOut={hideTooltip} - data={lineData} - className={styles.line} - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - x={(d) => xScale(getX(d))!} - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - y={(d) => yScale(getY(d))!} - stroke={ - isEven ? Color.primaryAccent : Color.secondaryAccent - } - strokeWidth={4} - strokeOpacity={2} - /> - - ); - })} - - - - line.label); + + const legendScale = scaleOrdinal({ + domain: keys, + range: [Color.primaryAccent, Color.secondaryAccent], + }); + + return ( +
+ style={{ + flexDirection: legendPosition === "right" ? "row" : "column-reverse", + }} + > + + + + + { + return { + ...bottomTickLabelProps(), + className: styles.tickLabel, + dy: "-0.25rem", + fontSize: `${xTickLabelSize / 16}rem`, + width: xScale.bandwidth(), + }; + }} + /> + { + return { + ...leftTickLabelProps(), + className: styles.tickLabel, + dx: "1.25rem", + dy: "0.25rem", + fontSize: `${yTickLabelSize / 16}rem`, + }; + }} + /> + + {actualData.map((lineData, i) => { + const isEven = i % 2 === 0; + return ( + + { + const eventSvgCoords = localPoint( + // ownerSVGElement is given by visx docs but not recognized by typescript + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + e.target.ownerSVGElement as Element, + e + ) as Point; + showTooltip({ + tooltipData: data.lines[i].label, + tooltipTop: eventSvgCoords.y, + tooltipLeft: eventSvgCoords.x, + }); + }} + onMouseOut={hideTooltip} + data={lineData} + className={styles.line} + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + x={(d) => xScale(getX(d))!} + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + y={(d) => yScale(getY(d))!} + stroke={ + isEven ? Color.primaryAccent : Color.secondaryAccent + } + strokeWidth={4} + strokeOpacity={2} + /> + + ); + })} + + + + - {tooltipOpen && ( - - <>{tooltipData} - - )} -
- ); -} + {tooltipOpen && ( + + )} +
+ ); + } +); diff --git a/components/StackedBarGraph.module.css b/components/StackedBarGraph.module.css index ffe660f..4c25e0e 100644 --- a/components/StackedBarGraph.module.css +++ b/components/StackedBarGraph.module.css @@ -13,23 +13,6 @@ top: 0; } -.toolTip { - font-family: "Inconsolata", monospace; - top: 0; - left: 0; - position: absolute; - background-color: var(--label); - color: var(--primary-background); - pointer-events: none; - border-radius: calc(10rem / 16); - padding: calc(10rem / 16); -} - -.toolTip p { - margin: 0 calc(5rem / 16); - font-size: calc(16rem / 16); -} - .key { font-weight: bold; } \ No newline at end of file diff --git a/components/StackedBarGraph.tsx b/components/StackedBarGraph.tsx index 7c195e0..9b0bd44 100644 --- a/components/StackedBarGraph.tsx +++ b/components/StackedBarGraph.tsx @@ -7,11 +7,13 @@ import { Point } from "@visx/point"; import { scaleBand, scaleLinear, scaleOrdinal } from "@visx/scale"; import { BarStack, BarStackHorizontal, Line } from "@visx/shape"; import { SeriesPoint } from "@visx/shape/lib/types"; -import { withTooltip, Tooltip } from "@visx/tooltip"; +import { withTooltip } from "@visx/tooltip"; import { WithTooltipProvidedProps } from "@visx/tooltip/lib/enhancers/withTooltip"; import React from "react"; import { Color } from "utils/Color"; +import { TooltipWrapper } from "./TooltipWrapper"; + import styles from "./StackedBarGraph.module.css"; interface StackedBarData { @@ -248,16 +250,14 @@ export const StackedBarGraphVertical = withTooltip< {tooltipOpen && tooltipData ? ( - -

{tooltipData.key}

{tooltipData.bar.data[tooltipData.key]}

{getCategory(tooltipData.bar.data)}

-
+ ) : null} ); @@ -438,16 +438,14 @@ export const StackedBarGraphHorizontal = withTooltip< {tooltipOpen && tooltipData ? ( - -

{tooltipData.key}

{tooltipData.bar.data[tooltipData.key]}

{getCategory(tooltipData.bar.data)}

-
+ ) : null} ); diff --git a/components/TooltipWrapper.module.css b/components/TooltipWrapper.module.css new file mode 100644 index 0000000..c6c0ed5 --- /dev/null +++ b/components/TooltipWrapper.module.css @@ -0,0 +1,31 @@ +.tooltip { + font-family: "Inconsolata", monospace; + top: 0; + left: 0; + position: absolute; + background-color: var(--label); + pointer-events: none; + padding: calc(10rem / 16); + border-radius: calc(10rem / 16); + font-size: calc(18rem / 16); +} + +.header { + color: var(--primary-background); + margin: 0; + font-size: calc(16rem / 16); + font-weight: 700; +} + +.body { + color: var(--primary-background); + margin-top: calc(5rem / 16); + font-size: calc(16rem / 16); +} + +.body p { + color: var(--primary-background); + margin: 0; + padding: 0; + font-size: calc(16rem / 16) !important; +} \ No newline at end of file diff --git a/components/TooltipWrapper.tsx b/components/TooltipWrapper.tsx new file mode 100644 index 0000000..d260852 --- /dev/null +++ b/components/TooltipWrapper.tsx @@ -0,0 +1,35 @@ +import { Tooltip } from "@visx/tooltip"; +import React from "react"; + +import styles from "./TooltipWrapper.module.css"; + +type TooltipWrapperProps = { + top?: number; + left?: number; + className?: string; + header?: string; + children?: React.ReactNode; +}; + +const TooltipWrapper = ({ + top, + left, + className, + header, + children, +}: TooltipWrapperProps) => { + return ( + + {header ? {header} : null} + {children ?
{children}
: null} +
+ ); +}; + +export { TooltipWrapper }; diff --git a/components/WordCloud.module.css b/components/WordCloud.module.css index 728094a..a6059ae 100644 --- a/components/WordCloud.module.css +++ b/components/WordCloud.module.css @@ -2,19 +2,4 @@ text-shadow: var(--primary-accent) 0 0 calc(20rem / 16); text-anchor: "middle"; cursor: default; -} - -.tooltip { - font-family: "Inconsolata", monospace; - font-weight: bold; - top: 0; - left: 0; - position: absolute; - background-color: var(--label); - color: var(--primary-background); - box-shadow: 0px calc(1rem / 16) calc(2rem / 16) var(--card-background); - pointer-events: none; - padding: calc(10rem / 16); - font-size: calc(18rem / 16); - border-radius: calc(10rem / 16); } \ No newline at end of file diff --git a/components/WordCloud.tsx b/components/WordCloud.tsx index a0e087c..fb09684 100644 --- a/components/WordCloud.tsx +++ b/components/WordCloud.tsx @@ -2,13 +2,15 @@ import { localPoint } from "@visx/event"; import { Point } from "@visx/point"; import { scaleLog } from "@visx/scale"; import { Text } from "@visx/text"; -import { TooltipWithBounds, useTooltip, withTooltip } from "@visx/tooltip"; +import { useTooltip, withTooltip } from "@visx/tooltip"; import { Wordcloud as VisxWordcloud } from "@visx/wordcloud"; import React from "react"; import { Color } from "utils/Color"; import { inDevEnvironment } from "utils/inDevEnviroment"; import { useIsMobile } from "utils/isMobile"; +import { TooltipWrapper } from "./TooltipWrapper"; + import styles from "./WordCloud.module.css"; interface WordCloudProps { @@ -101,17 +103,13 @@ export const WordCloud = withTooltip( /> {tooltipOpen && tooltipData ? ( - - {tooltipData.text} ({tooltipData.value}) - + header={`${tooltipData.text} (${tooltipData.value})`} + > ) : null} ); diff --git a/pages/samplePage.tsx b/pages/samplePage.tsx index 70b8f94..715e1fa 100644 --- a/pages/samplePage.tsx +++ b/pages/samplePage.tsx @@ -1,4 +1,9 @@ -import { mockCategoricalData, moreMockCategoricalData } from "data/mocks"; +import { + mockBoxPlotData, + mockCategoricalData, + mockLineData, + moreMockCategoricalData, +} from "data/mocks"; import { pageRoutes } from "data/routes"; import React from "react"; import { useWindowDimensions } from "utils/getWindowDimensions"; @@ -6,8 +11,10 @@ import { useIsMobile } from "utils/isMobile"; import { BarGraphVertical, BarGraphHorizontal } from "@/components/BarGraph"; import { BottomNav } from "@/components/BottomNav"; +import { BoxPlot } from "@/components/Boxplot"; import { ComponentWrapper } from "@/components/ComponentWrapper"; import { Header } from "@/components/Header"; +import { LineGraph } from "@/components/LineGraph"; import { SectionHeader } from "@/components/SectionHeader"; import { WordCloud } from "@/components/WordCloud"; @@ -53,7 +60,6 @@ export default function SamplePage() { margin={defaultVerticalBarGraphMargin} /> - - - - - - ({ @@ -142,7 +143,6 @@ export default function SamplePage() { height={defaultGraphHeight} /> - ({ @@ -153,7 +153,6 @@ export default function SamplePage() { height={defaultGraphHeight} /> - - - - ({ - text: word.key, - value: word.value, - }))} - width={defaultGraphWidth} - height={defaultGraphHeight} + + + + - Date: Wed, 16 Nov 2022 22:00:56 -0500 Subject: [PATCH 6/6] Refactor default prop values into util file (Closes #82) (#87) This PR creates a new util file `DefaultProp` that provides default prop values for graph/chart components. More values could be added in the future as the pages are developed. staging: (shahan: sorry, due to a unlucky naming this branch name doesnt work with staging ) Co-authored-by: e26chiu Reviewed-on: https://git.csclub.uwaterloo.ca/www/cs-2022-class-profile/pulls/87 Reviewed-by: Shahan Nedadahandeh --- pages/demographics.tsx | 84 +++++++++++------------------------------- utils/defaultProps.ts | 58 +++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 62 deletions(-) create mode 100644 utils/defaultProps.ts diff --git a/pages/demographics.tsx b/pages/demographics.tsx index cece66f..b365e08 100644 --- a/pages/demographics.tsx +++ b/pages/demographics.tsx @@ -13,6 +13,13 @@ import { } from "data/demographics"; import { pageRoutes } from "data/routes"; import React from "react"; +import { + barGraphProps, + DefaultProp, + pieChartProps, + barGraphMargin, + barGraphWidth, +} from "utils/defaultProps"; import { useWindowDimensions } from "utils/getWindowDimensions"; import { useIsMobile } from "utils/isMobile"; @@ -27,18 +34,9 @@ import { WordCloud } from "@/components/WordCloud"; import styles from "./samplePage.module.css"; export default function Demographics() { - const { width } = useWindowDimensions(); + const pageWidth = useWindowDimensions().width; const isMobile = useIsMobile(); - const defaultGraphWidth = isMobile ? width / 1.25 : width / 2; - const defaultPieChartWidth = isMobile ? width / 1.25 : width / 3; - const defaultGraphHeight = 500; - - const defaultBarGraphMargin = { top: 20, bottom: 80, left: 60, right: 20 }; - - const defaultLabelSize = 24; - const defaultLabelWidth = 100; - return (
@@ -52,11 +50,7 @@ export default function Demographics() { bodyText="There are a total of 106 respondents of the CS Class Profile. Interestingly, there are a huge number of students that are just in CS, partially due to the overwhelming number of people in CS as seen in the total demographics." >
- +
@@ -67,23 +61,13 @@ export default function Demographics() { noBackground >
- +
- +
@@ -93,24 +77,14 @@ export default function Demographics() { align="left" noBackground > - + - + @@ -135,8 +107,8 @@ export default function Demographics() { > - + @@ -176,12 +141,7 @@ export default function Demographics() { align="left" noBackground > - + diff --git a/utils/defaultProps.ts b/utils/defaultProps.ts new file mode 100644 index 0000000..70dd302 --- /dev/null +++ b/utils/defaultProps.ts @@ -0,0 +1,58 @@ +// Only static number props are included for this list +const propNames = ["graphHeight", "labelSize", "labelWidth"] as const; + +// This type is needed for smart autocomplete +type PropName = typeof propNames[number]; + +const mobileBarGraphFactor = 1.25; +const desktopBarGraphFactor = 2; + +const mobilePieChartFactor = 1.25; +const desktopPieChartFactor = 3; + +const graphWidth = ( + isMobile: boolean, + pageWidth: number, + isPieChart: boolean +): number => { + const mobileFactor = isPieChart ? mobilePieChartFactor : mobileBarGraphFactor; + const desktopFactor = isPieChart + ? desktopPieChartFactor + : desktopBarGraphFactor; + return isMobile ? pageWidth / mobileFactor : pageWidth / desktopFactor; +}; + +export const barGraphWidth = (isMobile: boolean, width: number) => + graphWidth(isMobile, width, false); + +export const pieChartWidth = (isMobile: boolean, width: number) => + graphWidth(isMobile, width, true); + +export const barGraphMargin = { + top: 20, + bottom: 80, + left: 60, + right: 20, +}; + +export const DefaultProp: { [key in PropName]: number } = { + graphHeight: 500, + labelSize: 24, + labelWidth: 100, +}; + +export const barGraphProps = (isMobile: boolean, pageWidth: number) => { + return { + width: barGraphWidth(isMobile, pageWidth), + height: DefaultProp.graphHeight, + margin: barGraphMargin, + }; +}; + +export const pieChartProps = (isMobile: boolean, pageWidth: number) => { + return { + width: pieChartWidth(isMobile, pageWidth), + labelWidth: DefaultProp.labelWidth, + labelTextSize: DefaultProp.labelSize, + }; +};