Cover Image: Confused emoji wondering why the calendar arrows show up on the mobile view (left) of my React Native app but are missing on the web view (right).
This week ended up being pretty frontend heavy, so while I didn't get anything done in Python land, I did spend a lot of time with JavaScript and CSS, with some more React Native troubleshooting (a recurring theme so far in these posts) thrown in.
Quick Links
- React Native—The Case of the Missing Arrows in 'React Native Calendars'
- JavaScript—JavaScript30 Project #3: CSS Variables
- JavaScript—JavaScript30 Project #4: Array Cardio (Day 1)
- Responsive Design—Kevin Powell's 'Conquering Responsive Layouts' (Week 1)
React Native—The Case of the Missing Arrows in React Native Calendars
In my last update, I mentioned that I would be working with the 'React Native Calendars' library to build out a calendar tracker for the 'random acts of kindness' app my friend and I are working on. Little did I know that, without even doing anything to modify the default <Calendar>
component, I would already have debugging to do. The forward and back arrows to change months were displayed on the mobile view but not the web view.
I found a stale GitHub issue about the missing arrows and saw that another user had success unearthing the arrows by modifying the arrow's height
and width
within the component's theme
(the working theory being that, because there was no height
or width
explicitly set for the images, they weren't rendering), so I tried this for myself:
import { View } from 'react-native';
import { Calendar } from 'react-native-calendars';
function CalendarView() {
return (
<View>
<Calendar
theme={{
arrowHeight: 50,
arrowWidth: 50
}}
/>
</View>
)
}
The result looked oddly blurry.
Upon inspecting the page to figure out why that might be, I found out that the arrow icons are PNG files. This is what the original image looks like (you may want to get out your magnifying glass!):
To get around this, I took the suggestion of another user in the issue thread to create custom <Arrow>
components, utilizing SVG icons from the React Native Vector Icons library.
import { View } from 'react-native';
import { Calendar } from 'react-native-calendars';
import Icon from 'react-native-vector-icons/MaterialIcons'
function CalendarView() {
const leftArrow = <Icon
name="keyboard-arrow-left"
size={30}
color='#62BEC1'
/>
const rightArrow = <Icon
name="keyboard-arrow-right"
size={30}
color='#62BEC1'
/>
return (
<View>
<Calendar
renderArrow={(direction) => {if (direction == "left") return leftArrow;
if (direction == "right") return rightArrow;
}}
/>
</View>
)
}
Unlike PNG files, SVG files can be resized without losing resolution, which we need here. (Check out this helpful comparison of PNG vs. SVG from Adobe for more info.)
Here's what the calendar looks like now:
That's more like it! Next up with this project, I'll be doing more styling and customizations and working out some logic to keep track of the days users complete an act of kindness.
JavaScript—JavaScript30 Project #3: CSS Variables
The third project in Wes Bos's JavaScript30 course was an intriguing exercise that introduced me to a concept I was as yet unfamiliar with: CSS variables, also known as custom properties.
These handy variables allow you to update multiple properties simultaneously with JavaScript. Unlike the variables in the Sass library that Wes briefly mentioned in the lesson intro, which are defined when a page is compiled, regular CSS variables can dynamically update as a user interacts with the page.
Before learning about CSS variables, here is how I would have written the CSS to match the color of some text with the background behind an image on the same page.
.hl {
color: #ffc600;
}
img {
background: #ffc600;
}
Then, if I wanted to change that CSS based on the value chosen from the color input selector on the page, I would need to update both the text and the background separately with JS. I'd need to select each element that needs to be updated, as well as the input, from the DOM, and attach an event listener to the input to watch for changes to the color selection and trigger a handler function. The handler would update the text color and background separately.
const text = document.querySelector('.hl')
const image = document.querySelector('img')
const input = document.getElementById('base')
input.addEventListener("input", (e) => handleColor(e))
function handleColor(e) {
text.style.color = e.target.value
image.style.background = e.target.value
}
While this isn't so bad with just two elements being updated, it grows in repetitiveness as more elements need to be changed at once.
With CSS variables, we can pull that common value out by defining a variable for it in the root of the document (set to the default value) and then pointing to that variable next to each of the elements sharing it using var(--variable)
.
:root {
--color: #ffc600;
}
.hl {
color: var(--color);
}
img {
padding: 10px;
background: var(--color);
}
Now, instead of using JavaScript to change the value for each element individually, we just need it to update the root value. Then, elements referencing that value will also be updated.
const input = document.getElementById('base')
input.addEventListener("input", (e) => handleColor(e))
function handleColor(e) {
document.body.style.setProperty('--color', e.target.value)
}
Simplifying this code makes it much easier to create one master event handler for updating numerous elements on the page based on various user interactions, as I eventually did for this exercise. The id
in this case refers to the input element ('spacing', 'blur', and 'base'), which, if named similarly to the variables, makes selecting the correct value to update simple with string interpolation.
function handleChange(e) {
id = e.target.id
value = e.target.value
if (id === "base") {
document.body.style.setProperty(`--${id}`, value)
} else {
document.body.style.setProperty(`--${id}`, `${value}px`)
}
}
You can check out the code and finished product for Project #3 on GitHub.
A Note on the 'Input' Event
I used the input
event when setting my event listeners. I was initially confused by Wes's approach of adding separate event listeners for mousemove
and change
events on the page inputs. Looking closely at the MDN reference for "input", though, I realized that the input
event type is a new addition as of March 2023. Because it's new, someone using an older device or browser version may have issues getting the page to work as intended.
JavaScript—JavaScript30 Project #4: Array Cardio (Day 1)
This project was, as promised by the title, a great workout and refresher on the various array methods, including .filter()
, .map()
, .sort()
, and .reduce()
.
These exercises were an excellent opportunity for me to go back and review what gets returned by each of these methods.
-
.filter()
: returns a shallow copy of the original array containing the filtered values. -
.map()
: returns a new array containing the 'mapped' elements of the original array. -
.sort()
: returns the same array, sorted in place. -
.reduce()
: returns the accumulated/aggregated value resulting from iterating through the array (the format of which will vary depending on how you've configured it; one of thereduce
exercises here returned an integer value; the other one an object with the tallied appearances of each 'key' element in the original array).
The only one of these methods I haven't used somewhere before was reduce()
, and at first, I was a little intimidated. However, I could quickly see its benefits once I took the time to slow down and understand how it works.
Here is how I would have written out Question 4 () before learning about .reduce()
:
// Array.prototype.reduce()
// 4. How many years did all the inventors live all together?
function totalLifespan(inventorArray) {
let totalLifespan = 0
for (let inventor of inventorArray) {
totalLifespan += (inventor.passed - inventor.year)
}
return totalLifespan
}
const totalYears = totalLifespan(inventors)
console.log("Question 4:", totalYears)
With .reduce()
, I was able to streamline this code quite a bit! I created a callback function, lifespan
, that would return the running total plus the current inventor's lifespan, passing that down the line until we reach the end of the 'inventors' array.
function lifespan(total, inventor){
return total + (inventor.passed - inventor.year)
}
const totalYears = inventors.reduce(lifespan, 0)
console.log("Question 4:", totalYears)
You could even write that callback inline using an arrow function.
const totalYears = inventors.reduce((total, inventor) => {
return total + (inventor.passed - inventor.year)
}, 0)
console.log("Question 4:", totalYears)
You can check out the code and finished product for Project #4 on GitHub.
Responsive Design—Kevin Powell's 'Conquering Responsive Layouts' (Week 1)
This past week, I started working through Kevin Powell's 'Conquering Responsive Layouts' course. I'm coming in with some basic familiarity with what it means for a website to be responsive. For instance, I know that adaptable formatting and predictable behavior between devices are vital for accessibility. Kevin's YouTube channel has managed to get me out of multiple CSS jams before, so I figured that this (totally free!) course would be enlightening. One week in, I've already learned a lot.
This first week, we reviewed a few foundational concepts for responsive layouts. During the first lesson, Kevin reiterated a point I'd seen him make in another video ('5 simple tips to making responsive layouts the easy way') that I watched last year while taking some initial steps to make my 'Accessible Parks' app more responsive: HTML is, by default, responsive. The decisions we make with CSS rules we add on top of that HTML are what make it unresponsive. There are two big offenders here in particular:
-
Fixed widths: Setting the
width
property in pixels makes it challenging to account for the wide variety of screen sizes that your layout might appear on and sets you up for having to create media queries that still won't be able to perfectly match the behavior you want. Settingwidth
with percentages instead will remove that guesswork.-
max-width
: To account for larger screens where you don't want your layout to stretch too far, you can set themax-width
property using pixels.- This is helpful because, it turns out, people have a hard time reading long, continuous lines of text stretching from one end of a big screen to the other; it makes it harder to keep track of where we are, and we don't want to have to move our heads to keep reading! This article from the Baymard Institute gives a little more background on line lengths and readability: Readability: The Optimal Line Length
-
Heights: It's better to avoid assigning a
height
property and let thewidth
and the content within the element determine the height of the container. That way, you won't run into situations where the text overflows or gets truncated. You can still add more height usingpadding
.
Over the next week, we'll dive into another area I've worked with before and am excited to gain better mastery of: Flexbox!
Looking Ahead
That's a wrap for this week!
Here's what I'll be working on until my next post:
- Week 2 of Kevin Powell's Responsive Design Course
- Working through part of Colt Steele's JavaScript Pro: Mastering Advanced Concepts and Techniques
- Another project from JavaScript30
- More React Native Calendars (styling and customization)
Top comments (0)