Compare commits

...

76 Commits

Author SHA1 Message Date
Richard Shuai ff27c3cbc0 Component Switcher (#169) 1 day ago
Darren Lo 75f90bbb87 darren/mental-health-and-personal-recopy (#170) 2 days ago
Shahan Nedadahandeh 8c0b5f8556 Fixed word cloud data issues (#159) 3 days ago
Shahan Nedadahandeh 41066af334 Recopied academics page (#168) 3 days ago
Darren Lo 71f9125bf2 Changed academic average bar graph colors (#160) 4 days ago
Richard Shuai db4d2a268c Copy Home and Demographics + Refactoring (#167) 5 days ago
Annie Sun 5e58f28879 Removed profanity (#161) 5 days ago
Richard Shuai 8f39bb3483 View button font change (#152) 7 days ago
Shahan Nedadahandeh 1a6d031dd6 Fix weird percentage number and add percent symbol (#157) 1 week ago
Shahan Nedadahandeh 09978edd0e Fixed incorrect data (#153) 1 week ago
Tejas Srikanth 6266c48267 Change-Legend-Labels_And_Colors (#155) 1 week ago
Richard Shuai 45264a8ae0 Merge religion (#151) 1 week ago
Shahan Nedadahandeh 229260cba9 Revert "Hid wierd percentage and added percent symbol" 2 weeks ago
Shahan Nedadahandeh d315f2904e Hid wierd percentage and added percent symbol 2 weeks ago
Mark Chiu dd8a33fa41 Fix StackedBar Graph left axis issue (#136) 3 weeks ago
Beihao Zhou 5970f9aebe Fix: Demographic Page Pie Chart Number to Percentage (#137) 1 month ago
Shahan Nedadahandeh 95ab1383ca Change production deploy to only deploy on main branch (#135) 1 month ago
Mark Chiu eb74c8e4a9 Fix StackedBar Graph nitpicks (Closes #51) (#133) 1 month ago
Shahan Nedadahandeh 3558fa0fb9 Fixed tooltip being hidden (#132) 1 month ago
Shahan Nedadahandeh 2202fccb11 Fixed header breaking view link (#131) 1 month ago
Shahan Nedadahandeh 65a5bdbd1c Added animations (#125) 1 month ago
Shahan Nedadahandeh 7c4efad55c Add auto deployment (#130) 1 month ago
Shahan Nedadahandeh 7ef5d8aa61 Fixed graph overflows (#123) 1 month ago
Shahan Nedadahandeh 2dca08ccef Setup deploy site (#126) 1 month ago
Jared He 9a60c6b779 Add front page (Closes #44) (#68) 1 month ago
Mark Chiu 85535414c3 Add Contributors page (Closes #63) (#121) 1 month ago
Shahan Nedadahandeh fbb0ca25ac Style scrollbar (#119) 1 month ago
Shahan Nedadahandeh 743f050ec8 Fix tooltip not being correctly positioned for certain centered graphs (#118) 1 month ago
Shahan Nedadahandeh b8590f7a28 Adjust pie chart (#117) 1 month ago
Mark Chiu dcc318b671 Add Mental Health page (Closes #61) (#114) 1 month ago
Mark Chiu 519f925155 Add Personal Page (Closes #62) (#113) 2 months ago
Mark Chiu 550029feb0 Change Graphs to use tooltips (Closes #97) (#112) 2 months ago
Mark Chiu d77ebe0f8d Add Intimacy and Drugs page (Closes #57) (#103) 2 months ago
Mark Chiu 6779486aac Fix GroupBarGraph component (Closes #104) (#108) 2 months ago
Beihao Zhou 2720728e46 Fix Line Graph Legend Position (Close #100) (#107) 2 months ago
Beihao Zhou 6ba124ee15 Add Miscellaneous Page (Close #60) (#106) 2 months ago
Beihao Zhou 7bcd11e186 Add Friends Page (Close #59) (#105) 2 months ago
Beihao Zhou dbb1a5929f Add Postgrad Page (Close #58) (#99) 2 months ago
Mark Chiu f659393335 Add Academics page (Closes #54) (#94) 2 months ago
Beihao Zhou 3936b7870e Add LifeStyle + Interest Page (Close #56) (#101) 2 months ago
Beihao Zhou 15191bb89e Add Coop Page (Close #55) (#69) 2 months ago
Shahan Nedadahandeh 3dd978c67e Added default word cloud width util (#102) 2 months ago
Beihao Zhou db9f74a42a Enable customized colors for line graph (#92) 2 months ago
Shahan Nedadahandeh ec4f33919c Fixed WordCloud not displaying values correctly (#95) 2 months ago
Mark Chiu 1c0191facc Refactor default prop values into util file (Closes #82) (#87) 3 months ago
Shahan Nedadahandeh 97aa2261f2 Created TooltipWrapper (#88) 3 months ago
Shahan Nedadahandeh e061850575 Fixed data and tooltip issues with wordcloud (#84) 3 months ago
Shahan Nedadahandeh d39a1f8274 Add media query to section header and update sample page (#85) 3 months ago
Mark Chiu aa25b8451b Fix Mobile Vertical Bar Graph Labels (Closes #50) (#83) 3 months ago
Mark Chiu fb9c5243ab Add Demographics page (Closes #53) (#73) 3 months ago
Shahan Nedadahandeh 80530c1c1a Made bottom arrows optional and refactored routes (Closes #72) (#76) 3 months ago
Shahan Nedadahandeh 0fcc367ab6 Make section component body text optional (Closes #70) (#77) 3 months ago
Mark Chiu 7404c60d8f Add SectionHeader component (Closes #74) (#75) 3 months ago
Shahan Nedadahandeh bfa3e9960c Header/Side Menu (#52) 4 months ago
Beihao Zhou fc2bf48706 Add Bottom Navigation (#64) 4 months ago
Beihao Zhou d8867d3c86 Line Graph Component using curve (#47) 4 months ago
Amy Wang 4458dcfa1f Add Grouped Bar Graph (#65) 4 months ago
Mark Chiu 2d34b84cb0 Add Stacked Bar Graph component (Closes #3) (#37) 4 months ago
Jared He 8253f6cbab Add Sections Component (#49) 5 months ago
Jared He e3948c0577 Add Timeline Component (#35) 5 months ago
Jared He fc5600cb20 Add About Component (#48) 5 months ago
Jared He 3cb5780964 Standardize font sizes (#46) 5 months ago
Jared He e2d19a281a Add Textbox Component (#45) 5 months ago
Jared He 9200e5f491 Pie chart component (#19) 5 months ago
Mark Chiu 9526f1b0f5 Add BoxPlot component (Closes #6) (#34) 5 months ago
Amy Wang a2dbcb90c6 Add Quotation Carousel (#36) 5 months ago
Shahan Nedadahandeh 9cd5c158e7 Sample page and graph wrappers (#32) 5 months ago
Shahan Nedadahandeh b586d52f72 Add Wordcloud Component (#27) 6 months ago
Amy Wang 933833d331 Fix monospace fallback (#28) 7 months ago
Amy Wang 6735c52914 Bar Graph Component (#16) 7 months ago
Amy Wang 5ce2efdc31 Fix Inconsolata font (#24) 7 months ago
Shahan Nedadahandeh 854c5d5bae Fix Staging server 404 error (#22) 7 months ago
Shahan Nedadahandeh 126a61fc28 Add colors in JS and Color Palette (#18) 7 months ago
Shahan Nedadahandeh 67aa21fd65 Add CI (#13) 8 months ago
Jared He b8e43bcf64 Change sans-serif fallback to monospace (#20) 8 months ago
Mark Chiu e5acb92e3e Add global colours and styles (Closes #11) (#14) 8 months ago
  1. 84
      .drone.yml
  2. 11
      .eslintrc.js
  3. 10
      .vscode/settings.json
  4. 5
      Dockerfile
  5. 65
      components/About.module.css
  6. 67
      components/About.tsx
  7. 30
      components/BarGraph.module.css
  8. 398
      components/BarGraph.tsx
  9. 15
      components/BlankLink.tsx
  10. 110
      components/BottomNav.module.css
  11. 88
      components/BottomNav.tsx
  12. 9
      components/Boxplot.module.css
  13. 316
      components/Boxplot.tsx
  14. 15
      components/CenterWrapper.module.css
  15. 11
      components/CenterWrapper.tsx
  16. 21
      components/ColorPalette.module.css
  17. 24
      components/ColorPalette.tsx
  18. 59
      components/ComponentSwitcher.module.css
  19. 46
      components/ComponentSwitcher.tsx
  20. 100
      components/ComponentWrapper.module.css
  21. 53
      components/ComponentWrapper.tsx
  22. 30
      components/GroupedBarGraph.module.css
  23. 623
      components/GroupedBarGraph.tsx
  24. 137
      components/Header.module.css
  25. 70
      components/Header.tsx
  26. 25
      components/LineGraph.module.css
  27. 231
      components/LineGraph.tsx
  28. 21
      components/PieChart.module.css
  29. 176
      components/PieChart.tsx
  30. 125
      components/QuotationCarousel.module.css
  31. 136
      components/QuotationCarousel.tsx
  32. 32
      components/SectionHeader.module.css
  33. 17
      components/SectionHeader.tsx
  34. 11
      components/SectionWrapper.module.css
  35. 15
      components/SectionWrapper.tsx
  36. 87
      components/Sections.module.css
  37. 55
      components/Sections.tsx
  38. 23
      components/StackedBarGraph.module.css
  39. 449
      components/StackedBarGraph.tsx
  40. 92
      components/Timeline.module.css
  41. 127
      components/Timeline.tsx
  42. 19
      components/Title.tsx
  43. 31
      components/TooltipWrapper.module.css
  44. 85
      components/TooltipWrapper.tsx
  45. 9
      components/WordCloud.module.css
  46. 247
      components/WordCloud.tsx
  47. 1574
      data/academics.ts
  48. 75
      data/contributors.ts
  49. 609
      data/coop.ts
  50. 344
      data/demographics.ts
  51. 487
      data/friends.ts
  52. 426
      data/intimacy-and-drugs.ts
  53. 725
      data/lifestyle-and-interests.ts
  54. 480
      data/mental-health.ts
  55. 380
      data/miscellaneous.ts
  56. 206
      data/mocks.ts
  57. 334
      data/personal.ts
  58. 243
      data/post-grad.ts
  59. 70
      data/routes.ts
  60. 27
      deploy.sh
  61. 8
      docs/CI.md
  62. 13
      next.config.js
  63. 4566
      package-lock.json
  64. 11
      package.json
  65. 174
      pages/_app.css
  66. 16
      pages/_app.tsx
  67. 521
      pages/academics.tsx
  68. 95
      pages/contributors.tsx
  69. 315
      pages/coop.tsx
  70. 233
      pages/demographics.tsx
  71. 80
      pages/font.css
  72. 238
      pages/friends.tsx
  73. 151
      pages/frontpage.module.css
  74. 87
      pages/index.tsx
  75. 224
      pages/intimacy-and-drugs.tsx
  76. 261
      pages/lifestyle-and-interests.tsx
  77. 341
      pages/mental-health.tsx
  78. 130
      pages/miscellaneous.tsx
  79. 112
      pages/personal.tsx
  80. 15
      pages/playground.module.css
  81. 298
      pages/playground.tsx
  82. 145
      pages/post-grad.tsx
  83. 34
      pages/samplePage.module.css
  84. 313
      pages/samplePage.tsx
  85. BIN
      public/fonts/inconsolata-v30-latin-200.woff
  86. BIN
      public/fonts/inconsolata-v30-latin-200.woff2
  87. BIN
      public/fonts/inconsolata-v30-latin-300.woff
  88. BIN
      public/fonts/inconsolata-v30-latin-300.woff2
  89. BIN
      public/fonts/inconsolata-v30-latin-500.woff
  90. BIN
      public/fonts/inconsolata-v30-latin-500.woff2
  91. BIN
      public/fonts/inconsolata-v30-latin-600.woff
  92. BIN
      public/fonts/inconsolata-v30-latin-600.woff2
  93. BIN
      public/fonts/inconsolata-v30-latin-700.woff
  94. BIN
      public/fonts/inconsolata-v30-latin-700.woff2
  95. BIN
      public/fonts/inconsolata-v30-latin-800.woff
  96. BIN
      public/fonts/inconsolata-v30-latin-800.woff2
  97. BIN
      public/fonts/inconsolata-v30-latin-900.woff
  98. BIN
      public/fonts/inconsolata-v30-latin-900.woff2
  99. BIN
      public/fonts/inconsolata-v30-latin-regular.woff
  100. BIN
      public/fonts/inconsolata-v30-latin-regular.woff2
  101. Some files were not shown because too many files have changed in this diff Show More

@ -0,0 +1,84 @@
---
kind: pipeline
type: docker
name: node16
steps:
- name: view-staging-url
image: node:16
commands:
- echo "staging url will be https://${DRONE_BRANCH//\//-}-csc-class-profile-staging-snedadah.k8s.csclub.cloud"
- name: install-deps
image: node:16
commands:
- npm install
- name: lint
image: node:16
depends_on:
- install-deps
commands:
- npm run lint
- name: build
image: node:16
depends_on:
- install-deps
commands:
- npm run build
- name: export
image: node:16
depends_on:
- build
commands:
- npm run export
- name: publish
image: plugins/docker:latest
depends_on:
- export
settings:
username:
from_secret: HARBOUR_USERNAME
password:
from_secret: HARBOUR_PASSWORD
tags:
- ${DRONE_BRANCH//\//-}
- latest
registry: registry.cloud.csclub.uwaterloo.ca
repo: registry.cloud.csclub.uwaterloo.ca/snedadah/csc-class-profile-staging
- name: deploy-staging
image: node:16
depends_on:
- publish
environment:
STAGING_AUTH_TOKEN:
from_secret: STAGING_AUTH_TOKEN
commands:
- echo "The docker build tag is ${DRONE_BRANCH//\//-}"
- 'curl -H "Authorization: $STAGING_AUTH_TOKEN" https://csclub.uwaterloo.ca/~snedadah/webhooks/cscclassprofilestaging?ref=${DRONE_BRANCH//\//-}'
- name: deploy-production
image: node:16
depends_on:
- export
environment:
SSH_KEY:
from_secret: DEPLOYMENT_SSH_KEY
commands:
- 'echo "$SSH_KEY" > /tmp/ssh_key'
- chmod 600 /tmp/ssh_key
- ssh -4 -i /tmp/ssh_key www@caffeine.csclub.uwaterloo.ca -o StrictHostKeyChecking=no '~/bin/classprofile/deploy-2022.sh'
when:
branch:
- main
trigger:
event:
exclude:
- pull_request #avoid double build on PRs

@ -15,10 +15,15 @@ module.exports = {
"plugin:react/recommended",
"plugin:prettier/recommended",
],
plugins: ["@typescript-eslint", "react", "react-hooks", "prettier"],
plugins: ["@typescript-eslint", "react", "react-hooks", "prettier", "unused-imports"],
rules: {
"prettier/prettier": "error",
"no-unused-vars": "off",
"unused-imports/no-unused-imports": "error",
"unused-imports/no-unused-vars": [
"error",
{ "vars": "all", "varsIgnorePattern": "^_", "args": "after-used", "argsIgnorePattern": "^_" }
],
"prettier/prettier": ["error", { "endOfLine": "auto" }],
"import/first": "error",
"import/order": [
"error",

@ -2,11 +2,13 @@
"typescript.tsdk": "node_modules/typescript/lib",
"eslint.format.enable": true,
"eslint.codeActionsOnSave.mode": "all",
"css.lint.validProperties": ["composes"],
"css.format.spaceAroundSelectorSeparator": true,
"[css]": {
"editor.suggest.insertMode": "replace",
"gitlens.codeLens.scopes": ["document"],
"editor.formatOnSave": true,
"editor.defaultFormatter": "vscode.css-language-features"
"editor.defaultFormatter": "vscode.css-language-features",
},
"[javascript]": {
"editor.formatOnSave": false,
@ -41,7 +43,11 @@
"files.eol": "\n",
"[markdown]": {
"editor.wordWrap": "on",
"editor.quickSuggestions": false,
"editor.quickSuggestions": {
"comments": "off",
"strings": "off",
"other": "off"
},
"editor.tabSize": 4
}
}

@ -0,0 +1,5 @@
FROM nginx
COPY ./out /usr/share/nginx/html
COPY staging-nginx.conf /etc/nginx/conf.d
RUN rm /etc/nginx/conf.d/default.conf

@ -0,0 +1,65 @@
.aboutWrapper {
position: relative;
width: 90%;
}
.about {
display: flex;
flex-direction: row;
padding: calc(45rem / 16);
}
.about h1 {
margin: 0;
}
.about h4 {
margin: 0;
}
.about aside {
flex: 1;
margin-right: calc(40rem / 16);
}
.about aside h1 {
color: var(--secondary-accent)
}
.about aside p {
color: var(--primary-accent-lighter)
}
.about article {
flex: 3;
}
.about article p {
color: var(--primary-text);
}
.angle {
position: absolute;
top: 0;
left: 0;
width: calc(70rem / 16);
height: calc(70rem / 16);
}
.anglePath {
stroke: var(--primary-accent-light)
}
.left.angle {
transform: rotate(180deg);
top: unset;
left: unset;
bottom: 0;
right: 0;
}
@media screen and (max-width: 900px) {
.about {
flex-direction: column;
}
}

@ -0,0 +1,67 @@
import React from "react";
import styles from "./About.module.css";
export function About() {
return (
<div className={styles.aboutWrapper} id="about">
<AngleDecoration isBottom={false} />
<section className={styles.about}>
<aside>
<h1>About the Programs</h1>
<p>Getting an overview of the CS programs in UW</p>
</aside>
<article>
<h4>Computer Science</h4>
<p>
Computer Science (CS) is commonly offered by the Faculty of
Mathematics as a co-op program, with students usually attending 8
school and 6 co-op terms in their degree. However, CS is more
flexible than the other two programs because of the ability to
choose from a wider range and number of electives, to take terms
off, and to change their academic schedules to fit their needs.
</p>
<h4>Computing and Financial Management</h4>
<p>
Computing and Financial Management (CFM) combines the core CS
courses with electives from areas such as accounting, economics, and
financial management. This is a joint offering by the Faculty of
Mathematics and the School of Accounting and Finance. The program is
offered only as a co-op program with 6 co-op terms. The program is
offered only as a co-op program with 6 co-op terms.
</p>
<h4>CS/BBA</h4>
<p>
Joint with Wilfrid Laurier University, the Business Administration
and Computer Science Double Degree (CS/BBA) is an exclusive offering
that allows students to gain experience in CS as well as many
subfields of business. There are 10 school terms and either 4 or 5
co-op terms in the usual schedule, so its a longer degree with more
academic terms than CS or CFM.
</p>
</article>
</section>
<AngleDecoration isBottom={true} />
</div>
);
}
interface AngleDecorationProps {
isBottom: boolean;
}
function AngleDecoration({ isBottom }: AngleDecorationProps) {
return (
<svg
viewBox="0 0 100 100"
className={isBottom ? `${styles.left} ${styles.angle}` : styles.angle}
>
<path
d="M100,2 L2,2 L2,100"
fill="none"
strokeWidth="10"
className={styles.anglePath}
/>
</svg>
);
}

@ -0,0 +1,30 @@
.barBackground {
fill: var(--card-background);
transition: fill 0.5s ease-out;
}
.bar {
fill: var(--primary-accent-light);
transition: fill 0.5s ease-out;
}
.barGroup {
transition: fill 0.5s ease-out;
}
.barGroup:hover .bar {
fill: var(--primary-accent);
filter: drop-shadow(0 0 calc(4rem / 16) var(--primary-accent));
}
.tickLabel {
font-family: "Inconsolata", monospace;
font-weight: 800;
fill: var(--label);
}
.axisLabel {
font-family: "Inconsolata", monospace;
font-weight: 800;
fill: var(--label);
}

@ -0,0 +1,398 @@
import { AxisBottom, AxisLeft } from "@visx/axis";
import { bottomTickLabelProps } from "@visx/axis/lib/axis/AxisBottom";
import { leftTickLabelProps } from "@visx/axis/lib/axis/AxisLeft";
import { GridColumns, GridRows } from "@visx/grid";
import { Group } from "@visx/group";
import { scaleBand, scaleLinear } from "@visx/scale";
import { Bar } from "@visx/shape";
import { withTooltip } from "@visx/tooltip";
import React from "react";
import { Color } from "utils/Color";
import { getTooltipPosition, TooltipWrapper } from "./TooltipWrapper";
import styles from "./BarGraph.module.css";
interface BarGraphProps {
data: BarGraphData[];
/** Width of the entire graph, in pixels. */
width: number;
/** Height of the entire graph, in pixels. */
height: number;
/** Distance between the edge of the graph and the area where the bars are drawn, in pixels. */
margin: {
top: number;
bottom: number;
left: number;
right: number;
};
className?: string;
/** Font size of the category axis tick labels, in pixels. Default is 16px. */
categoryTickLabelSize?: number;
/** Font size of the value axis tick labels, in pixels. Default is 16px. */
valueTickLabelSize?: number;
/** Label text for the category axis. */
categoryAxisLabel?: string;
/** Font size of the label for the cateogry axis, in pixels. */
categoryAxisLabelSize?: number;
/** Controls the distance between the category axis label and the category axis. */
categoryAxisLabelOffset?: number;
/** Label text for the value axis. */
valueAxisLabel?: string;
/** Font size of the label for the value axis, in pixels. */
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 {
category: string;
value: number;
}
const DEFAULT_LABEL_SIZE = 16;
type TooltipData = string;
export const BarGraphHorizontal = withTooltip<BarGraphProps, TooltipData>(
({
width,
height,
margin,
data,
className,
minWidth = 500,
categoryTickLabelSize = DEFAULT_LABEL_SIZE,
valueTickLabelSize = DEFAULT_LABEL_SIZE,
categoryAxisLabel,
categoryAxisLabelSize = DEFAULT_LABEL_SIZE,
categoryAxisLabelOffset = 0,
valueAxisLabel,
valueAxisLabelSize = DEFAULT_LABEL_SIZE,
valueAxisLabelOffset = 0,
defaultLabelDy = "0",
tooltipOpen,
tooltipLeft,
tooltipTop,
tooltipData,
hideTooltip,
showTooltip,
}) => {
width = width < minWidth ? minWidth : width; // Ensuring graph's width >= minWidth
const barPadding = 0.4;
const categoryMax = height - margin.top - margin.bottom;
const valueMax = width - margin.left - margin.right;
const getCategory = (d: BarGraphData) => d.category;
const getValue = (d: BarGraphData) => d.value;
const categoryScale = scaleBand({
range: [0, categoryMax],
domain: data.map(getCategory),
padding: barPadding,
});
const valueScale = scaleLinear({
range: [0, valueMax],
nice: true,
domain: [0, Math.max(...data.map(getValue))],
});
const categoryPoint = (d: BarGraphData) => categoryScale(getCategory(d));
const valuePoint = (d: BarGraphData) => valueScale(getValue(d));
return (
<div>
<svg className={className} width={width} height={height}>
<Group top={margin.top} left={margin.left}>
<Group>
{data.map((d, idx) => {
const barName = `${getCategory(d)}-${idx}`;
const barWidth = categoryScale.bandwidth();
const backgroundBarWidth = barWidth / (1 - barPadding);
return idx % 2 === 0 ? (
<Bar
className={styles.barBackground}
key={`bar-${barName}-background`}
x={0}
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
y={categoryPoint(d)! - (backgroundBarWidth - barWidth) / 2}
width={valueMax}
height={backgroundBarWidth}
/>
) : null;
})}
</Group>
<GridColumns
scale={valueScale}
height={categoryMax}
numTicks={5}
stroke={Color.label}
strokeWidth={4}
strokeDasharray="10"
strokeLinecap="round"
/>
<Group>
{data.map((d, idx) => {
const barName = `${getCategory(d)}-${idx}`;
const barLength = valuePoint(d);
const barWidth = categoryScale.bandwidth();
return (
<Group className={styles.barGroup} key={`bar-${barName}`}>
<Bar
onMouseMove={(e) => {
const tooltipPos = getTooltipPosition(e);
showTooltip({
tooltipData: getValue(d).toString(),
tooltipLeft: tooltipPos.x,
tooltipTop: tooltipPos.y,
});
}}
onMouseOut={hideTooltip}
className={styles.bar}
x={0}
y={categoryPoint(d)}
width={barLength}
height={barWidth}
/>
</Group>
);
})}
</Group>
<AxisLeft
scale={categoryScale}
hideAxisLine
hideTicks
tickLabelProps={() => {
return {
...leftTickLabelProps(),
className: styles.tickLabel,
fontSize: `${categoryTickLabelSize / 16}rem`,
};
}}
label={categoryAxisLabel}
labelClassName={styles.axisLabel}
labelOffset={categoryAxisLabelOffset}
labelProps={{
fontSize: `${categoryAxisLabelSize / 16}rem`,
}}
/>
<AxisBottom
scale={valueScale}
top={categoryMax}
hideAxisLine
hideTicks
numTicks={5}
tickLabelProps={() => {
return {
...bottomTickLabelProps(),
className: styles.tickLabel,
dy: defaultLabelDy,
fontSize: `${valueTickLabelSize / 16}rem`,
};
}}
label={valueAxisLabel}
labelClassName={styles.axisLabel}
labelOffset={valueAxisLabelOffset}
labelProps={{
fontSize: `${valueAxisLabelSize / 16}rem`,
}}
/>
</Group>
</svg>
{tooltipOpen && (
<TooltipWrapper
top={tooltipTop}
left={tooltipLeft}
header={tooltipData as string}
></TooltipWrapper>
)}
</div>
);
}
);
export const BarGraphVertical = withTooltip<BarGraphProps, TooltipData>(
({
width,
height,
margin,
data,
className,
minWidth = 500,
categoryTickLabelSize = DEFAULT_LABEL_SIZE,
valueTickLabelSize = DEFAULT_LABEL_SIZE,
categoryAxisLabel,
categoryAxisLabelSize = DEFAULT_LABEL_SIZE,
categoryAxisLabelOffset = 0,
valueAxisLabel,
valueAxisLabelSize = DEFAULT_LABEL_SIZE,
valueAxisLabelOffset = 0,
widthAlternatingLabel = 600,
alternatingLabelSpace = 80,
defaultLabelDy = `0px`,
lowerLabelDy = `30px`,
tooltipOpen,
tooltipLeft,
tooltipTop,
tooltipData,
hideTooltip,
showTooltip,
}) => {
width = width < minWidth ? minWidth : 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 - final_margin_bottom;
const getCategory = (d: BarGraphData) => d.category;
const getValue = (d: BarGraphData) => d.value;
const categoryScale = scaleBand({
range: [0, categoryMax],
domain: data.map(getCategory),
padding: barPadding,
});
const valueScale = scaleLinear({
range: [valueMax, 0],
nice: true,
domain: [0, Math.max(...data.map(getValue))],
});
const categoryPoint = (d: BarGraphData) => categoryScale(getCategory(d));
const valuePoint = (d: BarGraphData) => valueScale(getValue(d));
return (
<div>
<svg className={className} width={width} height={height}>
<Group top={margin.top} left={margin.left}>
<Group>
{data.map((d, idx) => {
const barName = `${getCategory(d)}-${idx}`;
const barWidth = categoryScale.bandwidth();
const backgroundBarWidth = barWidth / (1 - barPadding);
return idx % 2 === 0 ? (
<Bar
className={styles.barBackground}
key={`bar-${barName}-background`}
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
x={categoryPoint(d)! - (backgroundBarWidth - barWidth) / 2}
y={0}
width={backgroundBarWidth}
height={valueMax}
/>
) : null;
})}
</Group>
<GridRows
scale={valueScale}
width={categoryMax}
numTicks={5}
stroke={Color.label}
strokeWidth={4}
strokeDasharray="10"
strokeLinecap="round"
/>
<Group>
{data.map((d, idx) => {
const barName = `${getCategory(d)}-${idx}`;
const barHeight = valueMax - valuePoint(d);
const barWidth = categoryScale.bandwidth();
return (
<Group className={styles.barGroup} key={`bar-${barName}`}>
<Bar
onMouseMove={(e) => {
const tooltipPos = getTooltipPosition(e);
showTooltip({
tooltipData: getValue(d).toString(),
tooltipLeft: tooltipPos.x,
tooltipTop: tooltipPos.y,
});
}}
onMouseOut={hideTooltip}
className={styles.bar}
x={categoryPoint(d)}
y={valueMax - barHeight}
width={barWidth}
height={barHeight}
/>
</Group>
);
})}
</Group>
<AxisBottom
scale={categoryScale}
top={valueMax}
hideAxisLine
hideTicks
tickLabelProps={(value, index) => {
const alternatingDy =
index % 2 == 0 ? defaultLabelDy : lowerLabelDy;
return {
...bottomTickLabelProps(),
className: styles.tickLabel,
dy: alternatingLabel ? alternatingDy : defaultLabelDy,
fontSize: `${categoryTickLabelSize / 16}rem`,
width: categoryScale.bandwidth(),
verticalAnchor: "start",
};
}}
label={categoryAxisLabel}
labelClassName={styles.axisLabel}
labelOffset={categoryAxisLabelOffset}
labelProps={{
fontSize: `${categoryAxisLabelSize / 16}rem`,
}}
/>
<AxisLeft
scale={valueScale}
hideAxisLine
hideTicks
numTicks={5}
tickLabelProps={() => {
return {
...leftTickLabelProps(),
className: styles.tickLabel,
dx: "-0.5rem",
dy: "0.25rem",
fontSize: `${valueTickLabelSize / 16}rem`,
};
}}
label={valueAxisLabel}
labelClassName={styles.axisLabel}
labelOffset={valueAxisLabelOffset}
labelProps={{
fontSize: `${valueAxisLabelSize / 16}rem`,
}}
/>
</Group>
</svg>
{tooltipOpen && (
<TooltipWrapper
top={tooltipTop}
left={tooltipLeft}
header={tooltipData as string}
></TooltipWrapper>
)}
</div>
);
}
);

@ -0,0 +1,15 @@
import React from "react";
export const BodyLink = ({
targetBlank = true,
children,
href,
}: {
href: string;
targetBlank?: boolean;
children: React.ReactNode;
}) => (
<a href={href} target={targetBlank ? "_blank" : ""} rel="noreferrer">
<b>{children}</b>
</a>
);

@ -0,0 +1,110 @@
.container {
display: flex;
flex-flow: center;
align-items: center;
justify-content: space-between;
margin: calc(40rem / 16) 0;
}
.subBox {
display: inline-block;
}
.leftItem {
text-align: right;
}
.item {
color: var(--primary-text);
font-size: calc(28rem / 16);
position: relative;
margin: calc(24rem / 16);
}
.subBox {
display: flex;
flex-direction: row;
align-items: center;
}
.arrow {
width: calc(250rem / 16);
height: calc(20rem / 16);
display: flex;
align-items: center;
justify-content: center;
}
.item:after {
content: '';
position: absolute;
width: 100%;
transform: scaleX(0);
height: calc(2rem / 16);
bottom: 0;
left: 0;
background-color: var(--primary-accent);
cursor: pointer;
transform-origin: bottom right;
transition: transform 0.25s ease-out;
}
.item:hover:after {
transform: scaleX(1);
transform-origin: bottom left;
}
.linePath {
stroke: var(--primary-text);
}
.arrowPath {
fill: var(--primary-text);
}
.right {
transform: rotate(180deg);
}
@media screen and (max-width: 1000px) {
.subBox {
flex-direction: column;
align-items: flex-start;
}
.subBoxLeft {
flex-direction: column-reverse;
align-items: flex-end;
}
.item {
font-size: calc(20rem / 16);
margin: 0;
margin-bottom: calc(10rem / 16);
}
.arrow {
width: calc(200rem / 16);
}
}
@media screen and (max-width: 500px) {
.container {
justify-content: center;
gap: calc(50rem / 16);
}
.arrow {
width: 100%;
}
}
.containerOnlyRightArrow {
justify-content: flex-end;
}
.containerOnlyLeftArrow {
justify-content: flex-start;
}

@ -0,0 +1,88 @@
import Link from "next/link";
import React from "react";
import styles from "./BottomNav.module.css";
interface PagesInfo {
leftPage?: {
url: string;
name: string;
};
rightPage?: {
url: string;
name: string;
};
}
export function BottomNav(props: PagesInfo) {
const onlyRightArrow = props.rightPage && !props.leftPage;
const onlyLeftArrow = !props.rightPage && props.leftPage;
return (
<div
className={`${styles.container}
${onlyRightArrow ? styles.containerOnlyRightArrow : ""}
${onlyLeftArrow ? styles.containerOnlyLeftArrow : ""}`}
>
{props.leftPage ? (
<div className={styles.subBox + " " + styles.subBoxLeft}>
<Link href={props.leftPage.url}>
<a>
<Arrow />
</a>
</Link>
<Link href={props.leftPage.url}>
<a className={styles.item + " " + styles.leftItem}>
{props.leftPage.name}
</a>
</Link>
</div>
) : null}
{props.rightPage ? (
<div className={styles.subBox}>
<Link href={props.rightPage.url}>
<a className={styles.item}>{props.rightPage.name}</a>
</Link>
<Link href={props.rightPage.url}>
<a>
<Arrow isPointingRight />
</a>
</Link>
</div>
) : null}
</div>
);
}
interface ArrowProps {
isPointingRight?: boolean;
}
function Arrow({ isPointingRight }: ArrowProps) {
return (
<svg className={(isPointingRight ? styles.right : "") + " " + styles.arrow}>
<defs>
<marker
id="arrow"
markerWidth="10"
markerHeight="10"
refX="5"
refY="3"
orient="auto"
markerUnits="strokeWidth"
>
<path d="M0,0 L0,6 L9,3 z" className={styles.arrowPath} />
</marker>
</defs>
<line
x1="250"
y1="10"
x2="100"
y2="10" // half of svg height
strokeWidth="3"
markerEnd="url(#arrow)"
className={styles.linePath}
/>
</svg>
);
}

@ -0,0 +1,9 @@
.boxplot {
fill: var(--primary-accent-light);
transition: fill 0.5s ease-out;
}
.boxplot:hover {
fill: var(--primary-accent);
filter: drop-shadow(0 0 calc(4rem / 16) var(--primary-accent));
}

@ -0,0 +1,316 @@
import { AxisLeft, AxisBottom } from "@visx/axis";
import { GridRows, GridColumns } from "@visx/grid";
import { Group } from "@visx/group";
import { Stats } from "@visx/mock-data/lib/generators/genStats";
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 } from "@visx/tooltip";
import { WithTooltipProvidedProps } from "@visx/tooltip/lib/enhancers/withTooltip";
import React from "react";
import { Color } from "utils/Color";
import { getTooltipPosition, TooltipWrapper } from "./TooltipWrapper";
import styles from "./Boxplot.module.css";
const DEFAULT_LABEL_SIZE = 16;
const TICK_LABEL_FONT_WEIGHT = 800;
interface BoxPlotData {
category: string;
min: number;
median: number;
max: number;
firstQuartile: number;
thirdQuartile: number;
outliers?: number[];
}
type TooltipData = Omit<BoxPlotData, "outliers">;
export type StatsPlotProps = {
data: BoxPlotData[];
/** Width of the entire graph, in pixels, greater than 10. */
width: number;
/** Height of the entire graph, in pixels. */
height: number;