diff --git a/pages/course/responsiveness/introduction.tsx b/pages/course/responsiveness/introduction.tsx index c67679f0..263101e8 100644 --- a/pages/course/responsiveness/introduction.tsx +++ b/pages/course/responsiveness/introduction.tsx @@ -7,8 +7,9 @@ import { useDimensions } from '@/hook/use-dimensions'; import { DensityChart } from '@/viz/DensityChartBasic/DensityChart'; import Link from 'next/link'; import { CodeBlock } from '@/component/UI/CodeBlock'; +import { TakeHome } from '@/component/TakeHome'; -const previousURL = '/course/axis/draw-with-d3'; +const previousURL = '/course/axis/project'; const currentURL = '/course/responsiveness/introduction'; const nextURL = '/course/responsiveness/use-dimension-hook'; const seoDescription = ''; @@ -38,8 +39,8 @@ export default function Home() { description={ <>

- Most of the visualization components in this gallery accept{' '} - width and height properties. + To build a graph, you need to know the width and{' '} + height of the SVG area.

However, we often don't know the exact dimensions we need for our @@ -143,14 +144,16 @@ export const DensityPlot = ({ width, height, data }: DensityProps) => {

Let's Code

- Let's assume we have a div that is responsive and takes the full width - of an app. + Let's assume we have a div that is responsive and takes the + full width of an app.

- How can we draw a chart inside this div that also takes up the{' '} - entire space and remains responsive? + How can we draw a chart inside this div that also takes up + the entire space and remains responsive? +

+

+ Let's find out! 🙇

-

Let's find out! 🙇

); } diff --git a/pages/course/responsiveness/use-dimension-hook.tsx b/pages/course/responsiveness/use-dimension-hook.tsx index 87c6bbcb..ad749a7d 100644 --- a/pages/course/responsiveness/use-dimension-hook.tsx +++ b/pages/course/responsiveness/use-dimension-hook.tsx @@ -3,6 +3,7 @@ import TitleAndDescription from '@/component/TitleAndDescription'; import { LayoutCourse } from '@/component/LayoutCourse'; import { lessonList } from '@/util/lessonList'; import { CodeBlock } from '@/component/UI/CodeBlock'; +import { TakeHome } from '@/component/TakeHome'; const previousURL = '/course/responsiveness/introduction'; const currentURL = '/course/responsiveness/use-dimension-hook'; @@ -51,7 +52,10 @@ export default function Home() { .

-

Explain more why a hook is what we need.

+

+ That's exactly what we need! A function that we can reuse in all + our chart components and that manages its internal state. +

🎣 Hook to get the container dimensions

That's how the hook looks like:

@@ -100,8 +104,10 @@ export const useDimensions = (targetRef: React.RefObject) => { window to ensure the dimensions are updated when the window size changes.

-

Why do I have the useLayoutEffect in there?

-

Explain more in depth how it works.

+

+ I do not want to go too much in depth on how it works. More importantly, + let's see how to use it! +

); } diff --git a/pages/course/responsiveness/using-the-hook.tsx b/pages/course/responsiveness/using-the-hook.tsx index ef5389df..c4cddf15 100644 --- a/pages/course/responsiveness/using-the-hook.tsx +++ b/pages/course/responsiveness/using-the-hook.tsx @@ -4,6 +4,13 @@ import { LayoutCourse } from '@/component/LayoutCourse'; import { lessonList } from '@/util/lessonList'; import { CodeBlock } from '@/component/UI/CodeBlock'; import { CodeSandbox } from '@/component/CodeSandbox'; +import Link from 'next/link'; +import { ExerciseAccordion } from '@/component/ExerciseAccordion'; +import { + Exercise, + ExerciseDoubleSandbox, +} from '@/component/ExerciseDoubleSandbox'; +import { Graph9 } from '@/viz/exercise/ResponsiveDivUseDimensionsSolution/Graph'; const previousURL = '/course/responsiveness/use-dimension-hook'; const currentURL = '/course/responsiveness/using-the-hook'; @@ -32,25 +39,33 @@ export default function Home() { description={ <>

- We have a hook that listens for changes in a div's dimensions and - returns those dimensions. -

-

- Now, it's time to use this hook to inject the dimensions into our + Our use-dimensions hook monitors changes in a{' '} + div's dimensions and returns those values. Now, let’s{' '} + use this hook to feed the dimensions directly into our graph!

} /> -

Create a ref

+ {/* - + - + - + - + - + - + - + - */} +

1️⃣ Create a Ref

- Start by creating a ref using the React{' '} + Begin by creating a ref with the React{' '} useRef() function.

- A ref allows you to target and interact with a specific - HTML element in the DOM of your application. + A ref lets you directly target and interact with a specific + HTML element in your app’s DOM. Remember, we covered this concept in the + module on D3.js axes.

+

@@ -59,7 +74,7 @@ export default function Home() {

-

Use the hook

+

2️⃣ Use the hook

Then, pass this newly created chartRef to the hook:

@@ -70,7 +85,8 @@ export default function Home() { properties: height and width. These properties can be used in your chart component. 🔥

-
+ +
Pro tip: Before building a responsive visualization, use{' '} console.log() to check the chartSize object and ensure everything is working correctly. @@ -81,15 +97,44 @@ export default function Home() { // */}

📊 Application

+

You’re all set!

- You’re all set! Just pass the values from chartSize to the - visualization component, and it will become responsive. + Just pass the values from chartSize to the visualization + component, and it will become responsive.

Here is a full example with code:


+ {/* - +- +- +- +- +- +- +- +- */} + +

Exercices

+ Responsive div ↔️, + content: , + }, + { + title: ( + + First use of the use-dimensions hook 🫧 + + ), + content: , + }, + ]} + /> ); } @@ -111,3 +156,71 @@ return( ) `.trim(); + +const exercices: Exercise[] = [ + { + whyItMatters: ( + <> +

CSS knows very well how to handle responsiveness. But do you?

+

+ There are numerous caveats to keep in mind! For instance, a{' '} + div does not have aheight by default. +

+

+ I strongly encourage you to put a bg color to your chart container to + double check it behaves as expected. +

+ + ), + toDo: ( +
    +
  • + Open the Graph component. +
  • +
  • + Make it render a div with a width of 100% and a grey background. +
  • +
  • + Inside this container, render another div with a height of 300px, add + margins of 30px, and set a red background. +
  • +
+ ), + practiceSandbox: 'exercise/ResponsiveDivSolutionPractice', + solutionSandbox: 'exercise/ResponsiveDivSolutionSolution', + }, + { + whyItMatters: ( + <> +

Direct application of this lesson and the previous.

+ + ), + toDo: ( + <> +

+ The red rectangle is responsive. Let's find its{' '} + width and height! +

+
    +
  • + Create a new file named use-dimensions.tsx, and copy + the content of the UseDimensions hook into it. +
  • +
  • + Set up a chartRef and attach it to the red rectangle{' '} + div. +
  • +
  • + Use the hook to retrieve the dimensions and display them within the + red rectangle using p elements. +
  • +
  • + Resize the window to verify that the dimensions update as expected. +
  • +
+ + ), + practiceSandbox: 'exercise/ResponsiveDivUseDimensionsPractice', + solutionSandbox: 'exercise/ResponsiveDivUseDimensionsSolution', + }, +]; diff --git a/util/lessonList.tsx b/util/lessonList.tsx index 7a354454..f6cbc1e7 100644 --- a/util/lessonList.tsx +++ b/util/lessonList.tsx @@ -345,7 +345,7 @@ export const lessonList: Lesson[] = [ ), readTime: 4, link: '/course/responsiveness/use-dimension-hook', - status: 'wip', + status: 'free', moduleId: 'responsiveness', }, { diff --git a/viz/exercise/ResponsiveDivPractice/Graph.tsx b/viz/exercise/ResponsiveDivPractice/Graph.tsx new file mode 100644 index 00000000..5be66780 --- /dev/null +++ b/viz/exercise/ResponsiveDivPractice/Graph.tsx @@ -0,0 +1,7 @@ +export const Graph = () => { + return ( +
+
+
+ ); +}; diff --git a/viz/exercise/ResponsiveDivPractice/index.js b/viz/exercise/ResponsiveDivPractice/index.js new file mode 100644 index 00000000..fa564d27 --- /dev/null +++ b/viz/exercise/ResponsiveDivPractice/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/ResponsiveDivPractice/package.json b/viz/exercise/ResponsiveDivPractice/package.json new file mode 100644 index 00000000..ac08117a --- /dev/null +++ b/viz/exercise/ResponsiveDivPractice/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/ResponsiveDivSolution/Graph.tsx b/viz/exercise/ResponsiveDivSolution/Graph.tsx new file mode 100644 index 00000000..c6442928 --- /dev/null +++ b/viz/exercise/ResponsiveDivSolution/Graph.tsx @@ -0,0 +1,12 @@ +export const Graph = () => { + return ( +
+
+
+ ); +}; diff --git a/viz/exercise/ResponsiveDivSolution/index.js b/viz/exercise/ResponsiveDivSolution/index.js new file mode 100644 index 00000000..fa564d27 --- /dev/null +++ b/viz/exercise/ResponsiveDivSolution/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/ResponsiveDivSolution/package.json b/viz/exercise/ResponsiveDivSolution/package.json new file mode 100644 index 00000000..ac08117a --- /dev/null +++ b/viz/exercise/ResponsiveDivSolution/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/ResponsiveDivUseDimensionsPractice/Graph.tsx b/viz/exercise/ResponsiveDivUseDimensionsPractice/Graph.tsx new file mode 100644 index 00000000..c6442928 --- /dev/null +++ b/viz/exercise/ResponsiveDivUseDimensionsPractice/Graph.tsx @@ -0,0 +1,12 @@ +export const Graph = () => { + return ( +
+
+
+ ); +}; diff --git a/viz/exercise/ResponsiveDivUseDimensionsPractice/index.js b/viz/exercise/ResponsiveDivUseDimensionsPractice/index.js new file mode 100644 index 00000000..fa564d27 --- /dev/null +++ b/viz/exercise/ResponsiveDivUseDimensionsPractice/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/ResponsiveDivUseDimensionsPractice/package.json b/viz/exercise/ResponsiveDivUseDimensionsPractice/package.json new file mode 100644 index 00000000..ac08117a --- /dev/null +++ b/viz/exercise/ResponsiveDivUseDimensionsPractice/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/ResponsiveDivUseDimensionsSolution/Graph.tsx b/viz/exercise/ResponsiveDivUseDimensionsSolution/Graph.tsx new file mode 100644 index 00000000..7f2b38c2 --- /dev/null +++ b/viz/exercise/ResponsiveDivUseDimensionsSolution/Graph.tsx @@ -0,0 +1,24 @@ +import { useRef } from 'react'; +import { useDimensions } from './use-dimensions'; + +export const Graph = () => { + const chartRef = useRef(null); + const chartSize = useDimensions(chartRef); + + return ( +
+
+

Width: {chartSize.width}

+

Height: {chartSize.height}

+
+
+ ); +}; diff --git a/viz/exercise/ResponsiveDivUseDimensionsSolution/index.js b/viz/exercise/ResponsiveDivUseDimensionsSolution/index.js new file mode 100644 index 00000000..fa564d27 --- /dev/null +++ b/viz/exercise/ResponsiveDivUseDimensionsSolution/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/ResponsiveDivUseDimensionsSolution/package.json b/viz/exercise/ResponsiveDivUseDimensionsSolution/package.json new file mode 100644 index 00000000..ac08117a --- /dev/null +++ b/viz/exercise/ResponsiveDivUseDimensionsSolution/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/ResponsiveDivUseDimensionsSolution/use-dimensions.tsx b/viz/exercise/ResponsiveDivUseDimensionsSolution/use-dimensions.tsx new file mode 100644 index 00000000..b83dd0dc --- /dev/null +++ b/viz/exercise/ResponsiveDivUseDimensionsSolution/use-dimensions.tsx @@ -0,0 +1,29 @@ +import { useEffect, useLayoutEffect, useState } from 'react'; + +// Hook to retrieve the dimensions of a div. +// react-graph-gallery.com +export const useDimensions = (targetRef: React.RefObject) => { + const getDimensions = () => { + return { + width: targetRef.current ? targetRef.current.offsetWidth : 0, + height: targetRef.current ? targetRef.current.offsetHeight : 0, + }; + }; + + const [dimensions, setDimensions] = useState(getDimensions); + + const handleResize = () => { + setDimensions(getDimensions()); + }; + + useEffect(() => { + window.addEventListener('resize', handleResize); + return () => window.removeEventListener('resize', handleResize); + }, []); + + useLayoutEffect(() => { + handleResize(); + }, []); + + return dimensions; +};