diff --git a/pages/course/tooltip/tooltip-component.tsx b/pages/course/tooltip/tooltip-component.tsx
new file mode 100644
index 00000000..1624c271
--- /dev/null
+++ b/pages/course/tooltip/tooltip-component.tsx
@@ -0,0 +1,225 @@
+import React from 'react';
+import TitleAndDescription from '@/component/TitleAndDescription';
+import { LayoutCourse } from '@/component/LayoutCourse';
+import { lessonList } from '@/util/lessonList';
+import { CodeSandbox } from '@/component/CodeSandbox';
+import Link from 'next/link';
+import { Badge } from '@/component/UI/badge';
+import { ExerciseAccordion } from '@/component/ExerciseAccordion';
+import {
+ Exercise,
+ ExerciseDoubleSandbox,
+} from '@/component/ExerciseDoubleSandbox';
+import { Caption } from '@/component/UI/Caption';
+import { Sidenote } from '@/component/SideNote';
+import { ScatterplotBasicDemo } from '@/viz/ScatterplotBasic/ScatterplotBasicDemo';
+import { ScatterplotClimateCrisisDemo } from '@/viz/ScatterplotClimateCrisis/ScatterplotClimateCrisisDemo';
+import { ChartOrSandbox } from '@/component/ChartOrSandbox';
+import { TakeHome } from '@/component/TakeHome';
+import { CodeBlock } from '@/component/UI/CodeBlock';
+import { Graph2 } from '@/viz/exercise/TooltipFirstSolution/Graph';
+import { Graph8 } from '@/viz/exercise/TooltipAddContentSolution/Graph';
+
+const previousURL = '/course/tooltip/introduction';
+const currentURL = '/course/tooltip/tooltip-component';
+const nextURL = '/course/tooltip/display-on-hover';
+const seoDescription = '';
+
+export default function Home() {
+ const currentLesson = lessonList.find((l) => l.link === currentURL);
+
+ if (!currentLesson) {
+ return null;
+ }
+
+ return (
+ l.link === nextURL)}
+ previousTocItem={lessonList.find((l) => l.link === previousURL)}
+ >
+
+
+ Let's see how to create a Tooltip
component that can
+ be re-used in your codebase when a tooltip is required.
+
+ >
+ }
+ />
+ Minimal tooltip example
+
+ 💾 InteractionData
: where the tooltip info is stored.
+
+
+ Our tooltip component is going to expect one property only: an object of
+ type InteractionData.
+
+
+ This object stores everything we need to build a tooltip. At the very
+ least we need some positions (xPos and yPos) and a title to display
+ (name here)
+
+
+
+ But we could put many other things! A color for the border, some values
+ to display, a link to an image.. Anything really.
+
+ 🦴 Tooltip component skeleton
+
+ The Tooltip component uses some props of type TooltipProps, which is
+ basically just an interactionData object.
+
+
+ If this object is undefined (user is not hovering anything), then we do
+ not return anything;
+
+ {
+
+ if (!interactionData) {
+ return null;
+ }
+
+ ... Do something with interactionData otherwise
+};
+
+ `.trim()}
+ />
+ 🍔 The meat
+
+ Now, we just need to return something based on the{' '}
+ interactionData
information.
+
+
+ Do not forget to use xPos
and yPos
to put the
+ tooltip at the right position!
+
+
+ {name}
+
+);
+
+ `.trim()}
+ />
+
+ {/* -
+ -
+ -
+ -
+ -
+ -
+ -
+ -
+ */}
+
+
+
+ Exercises
+ Your first tooltip!,
+ content: ,
+ },
+ {
+ title: Pass more information ,
+ content: ,
+ },
+ ]}
+ />
+
+ );
+}
+
+const exercices: Exercise[] = [
+ {
+ whyItMatters: (
+ <>
+
+ Let's start with a very simple tooltip. We'll see how to improve it
+ later on.
+
+ >
+ ),
+ toDo: (
+
+ The Sandbox has a circle rendered in SVG.
+ Create an absolute positionned div on top of the SVG area
+
+ Create a Tooltip
component in a Tooltip.tsx
{' '}
+ file.
+
+
+ In this div, render a small div exactly where the circle is with one
+ word in it: hello
+
+
+ ),
+ practiceSandbox: 'exercise/TooltipFirstPractice',
+ solutionSandbox: 'exercise/TooltipFirstSolution',
+ },
+ {
+ whyItMatters: (
+ <>
+
+ Keep in mind that you can pass absolutely any information to the
+ tooltip component to display it.
+
+
+ Passing styling information like the marker color to make the tooltip
+ fit the style is a very good practice.
+
+ >
+ ),
+ toDo: (
+
+
+ Pass 3 more information to the Tooltip
component:{' '}
+ xValue
, yValue
and color (use{' '}
+ green
)
+
+
+ Use the color
to change the tooltip border color
+
+
+ Display xValue
and yValue
in the tooltip
+ body.
+
+
+ ),
+ practiceSandbox: 'exercise/TooltipAddContentPractice',
+ solutionSandbox: 'exercise/TooltipAddContentSolution',
+ },
+];
diff --git a/viz/exercise/TooltipAddContentPractice/Graph.tsx b/viz/exercise/TooltipAddContentPractice/Graph.tsx
new file mode 100644
index 00000000..d8e6d0de
--- /dev/null
+++ b/viz/exercise/TooltipAddContentPractice/Graph.tsx
@@ -0,0 +1,31 @@
+import { Tooltip } from './Tooltip';
+
+const width = 500;
+const height = 300;
+
+export const Graph = () => {
+ return (
+
+ {/* SVG layer */}
+
+
+
+
+ {/* Tooltip Layer */}
+
+
+
+
+ );
+};
diff --git a/viz/exercise/TooltipAddContentPractice/Tooltip.tsx b/viz/exercise/TooltipAddContentPractice/Tooltip.tsx
new file mode 100644
index 00000000..d5f00e02
--- /dev/null
+++ b/viz/exercise/TooltipAddContentPractice/Tooltip.tsx
@@ -0,0 +1,31 @@
+import styles from './tooltip.module.css';
+
+type InteractionData = {
+ xPos: number;
+ yPos: number;
+ name: string;
+};
+
+type TooltipProps = {
+ interactionData: InteractionData | undefined;
+};
+
+export const Tooltip = ({ interactionData }: TooltipProps) => {
+ if (!interactionData) {
+ return null;
+ }
+
+ const { xPos, yPos, name } = interactionData;
+
+ return (
+
+ {name}
+
+ );
+};
diff --git a/viz/exercise/TooltipAddContentPractice/index.js b/viz/exercise/TooltipAddContentPractice/index.js
new file mode 100644
index 00000000..fa564d27
--- /dev/null
+++ b/viz/exercise/TooltipAddContentPractice/index.js
@@ -0,0 +1,6 @@
+// File used to render something in codesandbox only
+import ReactDOM from 'react-dom';
+import { Graph } from './Graph';
+
+const rootElement = document.getElementById('root');
+ReactDOM.render( , rootElement);
diff --git a/viz/exercise/TooltipAddContentPractice/package.json b/viz/exercise/TooltipAddContentPractice/package.json
new file mode 100644
index 00000000..ac08117a
--- /dev/null
+++ b/viz/exercise/TooltipAddContentPractice/package.json
@@ -0,0 +1,28 @@
+{
+ "name": "pie-chart-basic",
+ "version": "1.0.0",
+ "description": "",
+ "keywords": [],
+ "main": "index.js",
+ "dependencies": {
+ "react": "17.0.2",
+ "react-dom": "17.0.2",
+ "react-scripts": "4.0.0"
+ },
+ "devDependencies": {
+ "@babel/runtime": "7.13.8",
+ "typescript": "4.1.3"
+ },
+ "scripts": {
+ "start": "react-scripts start",
+ "build": "react-scripts build",
+ "test": "react-scripts test --env=jsdom",
+ "eject": "react-scripts eject"
+ },
+ "browserslist": [
+ ">0.2%",
+ "not dead",
+ "not ie <= 11",
+ "not op_mini all"
+ ]
+}
diff --git a/viz/exercise/TooltipAddContentPractice/tooltip.module.css b/viz/exercise/TooltipAddContentPractice/tooltip.module.css
new file mode 100644
index 00000000..76691eeb
--- /dev/null
+++ b/viz/exercise/TooltipAddContentPractice/tooltip.module.css
@@ -0,0 +1,9 @@
+.tooltip {
+ position: absolute;
+ background-color: black;
+ color: white;
+ border-radius: 4px;
+ padding: 4px;
+ max-width: 100px;
+ font-size: 14px;
+}
diff --git a/viz/exercise/TooltipAddContentSolution/Graph.tsx b/viz/exercise/TooltipAddContentSolution/Graph.tsx
new file mode 100644
index 00000000..04ec3e69
--- /dev/null
+++ b/viz/exercise/TooltipAddContentSolution/Graph.tsx
@@ -0,0 +1,38 @@
+import { Tooltip } from './Tooltip';
+
+const width = 500;
+const height = 300;
+
+export const Graph8 = () => {
+ return (
+
+ {/* SVG layer */}
+
+
+
+
+ {/* Tooltip Layer */}
+
+
+
+
+ );
+};
diff --git a/viz/exercise/TooltipAddContentSolution/Tooltip.tsx b/viz/exercise/TooltipAddContentSolution/Tooltip.tsx
new file mode 100644
index 00000000..a170cf3d
--- /dev/null
+++ b/viz/exercise/TooltipAddContentSolution/Tooltip.tsx
@@ -0,0 +1,37 @@
+import styles from './tooltip.module.css';
+
+type InteractionData = {
+ xPos: number;
+ yPos: number;
+ name: string;
+ xValue: number;
+ yValue: number;
+ color: string;
+};
+
+type TooltipProps = {
+ interactionData: InteractionData | undefined;
+};
+
+export const Tooltip = ({ interactionData }: TooltipProps) => {
+ if (!interactionData) {
+ return null;
+ }
+
+ const { xPos, yPos, name, xValue, yValue, color } = interactionData;
+
+ return (
+
+
{name}
+
{'x: ' + xValue}
+
{'y: ' + yValue}
+
+ );
+};
diff --git a/viz/exercise/TooltipAddContentSolution/index.js b/viz/exercise/TooltipAddContentSolution/index.js
new file mode 100644
index 00000000..fa564d27
--- /dev/null
+++ b/viz/exercise/TooltipAddContentSolution/index.js
@@ -0,0 +1,6 @@
+// File used to render something in codesandbox only
+import ReactDOM from 'react-dom';
+import { Graph } from './Graph';
+
+const rootElement = document.getElementById('root');
+ReactDOM.render( , rootElement);
diff --git a/viz/exercise/TooltipAddContentSolution/package.json b/viz/exercise/TooltipAddContentSolution/package.json
new file mode 100644
index 00000000..ac08117a
--- /dev/null
+++ b/viz/exercise/TooltipAddContentSolution/package.json
@@ -0,0 +1,28 @@
+{
+ "name": "pie-chart-basic",
+ "version": "1.0.0",
+ "description": "",
+ "keywords": [],
+ "main": "index.js",
+ "dependencies": {
+ "react": "17.0.2",
+ "react-dom": "17.0.2",
+ "react-scripts": "4.0.0"
+ },
+ "devDependencies": {
+ "@babel/runtime": "7.13.8",
+ "typescript": "4.1.3"
+ },
+ "scripts": {
+ "start": "react-scripts start",
+ "build": "react-scripts build",
+ "test": "react-scripts test --env=jsdom",
+ "eject": "react-scripts eject"
+ },
+ "browserslist": [
+ ">0.2%",
+ "not dead",
+ "not ie <= 11",
+ "not op_mini all"
+ ]
+}
diff --git a/viz/exercise/TooltipAddContentSolution/tooltip.module.css b/viz/exercise/TooltipAddContentSolution/tooltip.module.css
new file mode 100644
index 00000000..5dba63cc
--- /dev/null
+++ b/viz/exercise/TooltipAddContentSolution/tooltip.module.css
@@ -0,0 +1,11 @@
+.tooltip {
+ position: absolute;
+ background-color: black;
+ color: white;
+ border: solid;
+ border-width: 3px;
+ border-radius: 4px;
+ padding: 4px;
+ max-width: 100px;
+ font-size: 14px;
+}
diff --git a/viz/exercise/TooltipFirstPractice/Graph.tsx b/viz/exercise/TooltipFirstPractice/Graph.tsx
new file mode 100644
index 00000000..9427d8ac
--- /dev/null
+++ b/viz/exercise/TooltipFirstPractice/Graph.tsx
@@ -0,0 +1,17 @@
+// import { Tooltip } from './Tooltip';
+
+const width = 500;
+const height = 300;
+
+export const Graph = () => {
+ return (
+
+ {/* SVG layer */}
+
+
+
+
+ {/* Tooltip Layer: TODO */}
+
+ );
+};
diff --git a/viz/exercise/TooltipFirstPractice/index.js b/viz/exercise/TooltipFirstPractice/index.js
new file mode 100644
index 00000000..fa564d27
--- /dev/null
+++ b/viz/exercise/TooltipFirstPractice/index.js
@@ -0,0 +1,6 @@
+// File used to render something in codesandbox only
+import ReactDOM from 'react-dom';
+import { Graph } from './Graph';
+
+const rootElement = document.getElementById('root');
+ReactDOM.render( , rootElement);
diff --git a/viz/exercise/TooltipFirstPractice/package.json b/viz/exercise/TooltipFirstPractice/package.json
new file mode 100644
index 00000000..ac08117a
--- /dev/null
+++ b/viz/exercise/TooltipFirstPractice/package.json
@@ -0,0 +1,28 @@
+{
+ "name": "pie-chart-basic",
+ "version": "1.0.0",
+ "description": "",
+ "keywords": [],
+ "main": "index.js",
+ "dependencies": {
+ "react": "17.0.2",
+ "react-dom": "17.0.2",
+ "react-scripts": "4.0.0"
+ },
+ "devDependencies": {
+ "@babel/runtime": "7.13.8",
+ "typescript": "4.1.3"
+ },
+ "scripts": {
+ "start": "react-scripts start",
+ "build": "react-scripts build",
+ "test": "react-scripts test --env=jsdom",
+ "eject": "react-scripts eject"
+ },
+ "browserslist": [
+ ">0.2%",
+ "not dead",
+ "not ie <= 11",
+ "not op_mini all"
+ ]
+}
diff --git a/viz/exercise/TooltipFirstSolution/Graph.tsx b/viz/exercise/TooltipFirstSolution/Graph.tsx
new file mode 100644
index 00000000..d8e6d0de
--- /dev/null
+++ b/viz/exercise/TooltipFirstSolution/Graph.tsx
@@ -0,0 +1,31 @@
+import { Tooltip } from './Tooltip';
+
+const width = 500;
+const height = 300;
+
+export const Graph = () => {
+ return (
+
+ {/* SVG layer */}
+
+
+
+
+ {/* Tooltip Layer */}
+
+
+
+
+ );
+};
diff --git a/viz/exercise/TooltipFirstSolution/Tooltip.tsx b/viz/exercise/TooltipFirstSolution/Tooltip.tsx
new file mode 100644
index 00000000..d5f00e02
--- /dev/null
+++ b/viz/exercise/TooltipFirstSolution/Tooltip.tsx
@@ -0,0 +1,31 @@
+import styles from './tooltip.module.css';
+
+type InteractionData = {
+ xPos: number;
+ yPos: number;
+ name: string;
+};
+
+type TooltipProps = {
+ interactionData: InteractionData | undefined;
+};
+
+export const Tooltip = ({ interactionData }: TooltipProps) => {
+ if (!interactionData) {
+ return null;
+ }
+
+ const { xPos, yPos, name } = interactionData;
+
+ return (
+
+ {name}
+
+ );
+};
diff --git a/viz/exercise/TooltipFirstSolution/index.js b/viz/exercise/TooltipFirstSolution/index.js
new file mode 100644
index 00000000..fa564d27
--- /dev/null
+++ b/viz/exercise/TooltipFirstSolution/index.js
@@ -0,0 +1,6 @@
+// File used to render something in codesandbox only
+import ReactDOM from 'react-dom';
+import { Graph } from './Graph';
+
+const rootElement = document.getElementById('root');
+ReactDOM.render( , rootElement);
diff --git a/viz/exercise/TooltipFirstSolution/package.json b/viz/exercise/TooltipFirstSolution/package.json
new file mode 100644
index 00000000..ac08117a
--- /dev/null
+++ b/viz/exercise/TooltipFirstSolution/package.json
@@ -0,0 +1,28 @@
+{
+ "name": "pie-chart-basic",
+ "version": "1.0.0",
+ "description": "",
+ "keywords": [],
+ "main": "index.js",
+ "dependencies": {
+ "react": "17.0.2",
+ "react-dom": "17.0.2",
+ "react-scripts": "4.0.0"
+ },
+ "devDependencies": {
+ "@babel/runtime": "7.13.8",
+ "typescript": "4.1.3"
+ },
+ "scripts": {
+ "start": "react-scripts start",
+ "build": "react-scripts build",
+ "test": "react-scripts test --env=jsdom",
+ "eject": "react-scripts eject"
+ },
+ "browserslist": [
+ ">0.2%",
+ "not dead",
+ "not ie <= 11",
+ "not op_mini all"
+ ]
+}
diff --git a/viz/exercise/TooltipFirstSolution/tooltip.module.css b/viz/exercise/TooltipFirstSolution/tooltip.module.css
new file mode 100644
index 00000000..76691eeb
--- /dev/null
+++ b/viz/exercise/TooltipFirstSolution/tooltip.module.css
@@ -0,0 +1,9 @@
+.tooltip {
+ position: absolute;
+ background-color: black;
+ color: white;
+ border-radius: 4px;
+ padding: 4px;
+ max-width: 100px;
+ font-size: 14px;
+}