diff options
author | 2024-07-03 23:00:43 +0100 | |
---|---|---|
committer | 2024-07-03 23:00:43 +0100 | |
commit | 169da3d820d311f2da83d14d964907560304a346 (patch) | |
tree | e30e76e3ba7f97aa4387d8a62f502c82a4e72fa1 | |
parent | Update success colour (diff) |
Convert TimeZone to functional component to fix lazy loading bug
-rw-r--r-- | src/components/InputTypes/TimeZone.tsx | 157 |
1 files changed, 87 insertions, 70 deletions
diff --git a/src/components/InputTypes/TimeZone.tsx b/src/components/InputTypes/TimeZone.tsx index 7b65961..ce205c7 100644 --- a/src/components/InputTypes/TimeZone.tsx +++ b/src/components/InputTypes/TimeZone.tsx @@ -1,7 +1,7 @@ /** @jsx jsx */ /** @jsxFrag React.Fragment */ import { jsx, css } from "@emotion/react"; -import React from "react"; +import React, { MutableRefObject, useEffect, useRef } from "react"; import { hiddenInput, invalidStyles } from "../../commonStyles"; import RenderedQuestion from "../Question"; @@ -216,110 +216,127 @@ const getTZ = () => { return recognisedZone ? guessedTimeZoneOffset : false; }; -class TimeZone extends React.Component<TimeZoneProps> { - selected_option: React.RefObject<HTMLDivElement> | null = null; +const TimeZone: React.FC<TimeZoneProps> = ({ question, valid, onBlurHandler }) => { + const containerRef = useRef<HTMLDivElement>(null); + const selectedOptionRef = useRef<HTMLDivElement>(null); - handler(selected_option: React.RefObject<HTMLDivElement>, event: React.ChangeEvent<HTMLInputElement>): void { - const option_container = event.target.parentElement; - if (!option_container || !option_container.parentElement || !selected_option.current) { + const handler = (selectedOption: MutableRefObject<HTMLDivElement | null>, event: React.ChangeEvent<HTMLInputElement>) => { + const optionContainer = event.target.parentElement; + if (!optionContainer || !optionContainer.parentElement || !selectedOption.current) { return; } - if (!this.props.question?.current) { + if (!question?.current) { throw new Error("Missing ref for select question."); } // Update stored value - this.props.question.current.setState({ value: option_container.textContent }); + question.current.setState({ value: optionContainer.textContent }); // Close the menu - selected_option.current.focus(); - selected_option.current.blur(); - selected_option.current.textContent = option_container.textContent; - } + selectedOption.current.focus(); + selectedOption.current.blur(); + selectedOption.current.textContent = optionContainer.textContent; + }; + + const handleClick = (container: MutableRefObject<HTMLDivElement | null>, selectedOption: MutableRefObject<HTMLDivElement | null>, event: React.MouseEvent<HTMLDivElement> | React.KeyboardEvent<HTMLDivElement>) => { + if (!container.current || !selectedOption.current) { + return; + } - handle_click( - container: React.RefObject<HTMLDivElement>, - selected_option: React.RefObject<HTMLDivElement>, - event: React.MouseEvent<HTMLDivElement> | React.KeyboardEvent<HTMLDivElement> - ): void { - if (!container.current || !selected_option.current || (event.type === "keydown" && (event as React.KeyboardEvent).code !== "Space")) { + // if it is a keyboard event and it is not the space key, return + if (event.type === "keydown" && (event as React.KeyboardEvent).code !== "Space") { return; } // Check if menu is open if (container.current.contains(document.activeElement)) { // Close menu - selected_option.current.focus(); - selected_option.current.blur(); + selectedOption.current.focus(); + selectedOption.current.blur(); event.preventDefault(); } - } + }; - focusOption(): void { - if (!this.props.question?.current) { + const focusOption = () => { + if (!question?.current) { throw new Error("Missing ref for select question."); } - if (!this.props.question.current.realState.value) { - this.props.question.current.setState({ value: "temporary" }); - this.props.onBlurHandler(); - this.props.question.current.setState({ value: null }); + if (!question.current.realState.value) { + question.current.setState({ value: "temporary" }); + onBlurHandler(); + question.current.setState({ value: null }); } - } + }; - componentDidMount() { + useEffect(() => { const tz = getTZ(); if (tz) { - if (!this.props.question.current) { + if (!question.current) { console.warn("No ref to question component in TimeZone."); + + setTimeout(() => { + console.log(question); + }, 5000); } else { - this.props.question.current.setState({ value: tz }); + question.current.setState({ value: tz }); } } - } - - render(): JSX.Element { - const container_ref: React.RefObject<HTMLDivElement> = React.createRef(); - const selected_option_ref: React.RefObject<HTMLDivElement> = React.createRef(); - - this.selected_option = selected_option_ref; - - const handle_click = (event: React.MouseEvent<HTMLDivElement> | React.KeyboardEvent<HTMLDivElement>) => this.handle_click(container_ref, selected_option_ref, event); - - const tz = getTZ(); - - const FOUND_COPY = "We have tried to guess your timezone based on your system settings. If this is incorrect, please select the correct timezone from the list below."; - const NOT_FOUND_COPY = "We could not automatically detect your timezone. Please select it from the list below."; - - return ( - <> - <div css={copyStyles}> - <p>{tz ? FOUND_COPY : NOT_FOUND_COPY}</p> - - <p>Timezones are displayed as offsets from UTC. For example, UTC+1 is one hour ahead of UTC, and UTC-5 is five hours behind UTC.</p> - </div> - <div css={[containerStyles, arrowStyles, optionContainerStyles, invalidStyles]} onFocus={this.focusOption.bind(this)} ref={container_ref} onBlur={this.props.onBlurHandler}> - <div css={mainWindowStyles} className={!this.props.valid ? "invalid-box selected_container" : "selected_container"}> - <span className="arrow" /> - <div tabIndex={0} className="selected_option" ref={selected_option_ref} onMouseDown={handle_click} onKeyDown={handle_click}>{tz ? tz : "..."}</div> + }, [question]); + + const tz = getTZ(); + + const FOUND_COPY = "We have tried to guess your timezone based on your system settings. If this is incorrect, please select the correct timezone from the list below."; + const NOT_FOUND_COPY = "We could not automatically detect your timezone. Please select it from the list below."; + + return ( + <> + <div css={copyStyles}> + <p>{tz ? FOUND_COPY : NOT_FOUND_COPY}</p> + + <p>Timezones are displayed as offsets from UTC. For example, UTC+1 is one hour ahead of UTC, and UTC-5 is five hours behind UTC.</p> + </div> + <div + css={[containerStyles, arrowStyles, optionContainerStyles, invalidStyles]} + onFocus={focusOption} + ref={containerRef} + onBlur={onBlurHandler} + > + <div + css={mainWindowStyles} + className={!valid ? "invalid-box selected_container" : "selected_container"} + > + <span className="arrow" /> + <div + tabIndex={0} + className="selected_option" + ref={selectedOptionRef} + onMouseDown={(event) => handleClick(containerRef, selectedOptionRef, event)} + onKeyDown={(event) => handleClick(containerRef, selectedOptionRef, event)} + > + {tz ? tz : "..."} </div> + </div> - <div className="option_container" tabIndex={-1}> - <div className="scrollbar-container"> - {TIMEZONE_OFFSETS.map((option, index) => ( - <div key={index} css={optionStyles}> - <input type="checkbox" css={[hiddenInput, inputStyles]} onChange={event => this.handler.call(this, selected_option_ref, event)} /> - <div>{option}</div> - </div> - ))} - </div> + <div className="option_container" tabIndex={-1}> + <div className="scrollbar-container"> + {TIMEZONE_OFFSETS.map((option, index) => ( + <div key={index} css={optionStyles}> + <input + type="checkbox" + css={[hiddenInput, inputStyles]} + onChange={(event) => handler(selectedOptionRef, event)} + /> + <div>{option}</div> + </div> + ))} </div> </div> - </> - ); - } -} + </div> + </> + ); +}; export default TimeZone; |