From 6427dc5285abf793d7207fca85d318d7dd399248 Mon Sep 17 00:00:00 2001 From: regexowl Date: Fri, 22 Nov 2024 13:54:08 +0100 Subject: [PATCH] Wizard: Add Timezone dropdown and NTP servers input This adds a Timezone select, the select is typeahead and allows to filter through all defaultTimezones. It's also scrollable so it doesn't take up all of the space. The NTP servers are currently in a text input, the server is added by confirming with a space, comma or an Enter, making the server a chip and adding it to the array of NTP servers. --- .../Timezone/components/NtpServersInput.tsx | 89 +++++++++++ .../Timezone/components/TimezoneDropDown.tsx | 149 +++++++++++++++++- .../steps/Timezone/index.tsx | 2 + 3 files changed, 238 insertions(+), 2 deletions(-) create mode 100644 src/Components/CreateImageWizard/steps/Timezone/components/NtpServersInput.tsx diff --git a/src/Components/CreateImageWizard/steps/Timezone/components/NtpServersInput.tsx b/src/Components/CreateImageWizard/steps/Timezone/components/NtpServersInput.tsx new file mode 100644 index 00000000..f3cc9e4c --- /dev/null +++ b/src/Components/CreateImageWizard/steps/Timezone/components/NtpServersInput.tsx @@ -0,0 +1,89 @@ +import React, { useState } from 'react'; + +import { + Button, + Chip, + ChipGroup, + FormGroup, + HelperText, + HelperTextItem, + TextInputGroup, + TextInputGroupMain, + TextInputGroupUtilities, +} from '@patternfly/react-core'; +import { TimesIcon } from '@patternfly/react-icons'; + +import { useAppDispatch, useAppSelector } from '../../../../../store/hooks'; +import { + addNtpServer, + clearNtpServers, + removeNtpServer, + selectNtpServers, +} from '../../../../../store/wizardSlice'; + +const NtpServersInput = () => { + const dispatch = useAppDispatch(); + const ntpServers = useAppSelector(selectNtpServers); + const [inputValue, setInputValue] = useState(''); + + const onTextInputChange = ( + _event: React.FormEvent, + value: string + ) => { + setInputValue(value); + }; + + const handleKeyDown = (e: React.KeyboardEvent, value: string) => { + if (e.key === ' ' || e.key === 'Enter' || e.key === ',') { + e.preventDefault(); + if (ntpServers?.includes(value)) { + // TO DO error + } else { + dispatch(addNtpServer(value)); + setInputValue(''); + } + } + }; + + return ( + + + handleKeyDown(e, inputValue)} + > + + {ntpServers?.map((server) => ( + dispatch(removeNtpServer(server))} + > + {server} + + ))} + + + {ntpServers && ntpServers.length > 0 && ( + + + + )} + + + + Confirm the NTP server by pressing space, comma or enter. + + + + ); +}; + +export default NtpServersInput; diff --git a/src/Components/CreateImageWizard/steps/Timezone/components/TimezoneDropDown.tsx b/src/Components/CreateImageWizard/steps/Timezone/components/TimezoneDropDown.tsx index a9f61d43..c67f9810 100644 --- a/src/Components/CreateImageWizard/steps/Timezone/components/TimezoneDropDown.tsx +++ b/src/Components/CreateImageWizard/steps/Timezone/components/TimezoneDropDown.tsx @@ -1,7 +1,152 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; + +import { + Select, + SelectOption, + SelectList, + MenuToggle, + MenuToggleElement, + TextInputGroup, + TextInputGroupMain, + TextInputGroupUtilities, + Button, + FormGroup, +} from '@patternfly/react-core'; +import TimesIcon from '@patternfly/react-icons/dist/esm/icons/times-icon'; + +import { useAppDispatch, useAppSelector } from '../../../../../store/hooks'; +import { + changeTimezone, + selectTimezone, +} from '../../../../../store/wizardSlice'; + +const defaultTimezones = Intl.supportedValuesOf('timeZone'); const TimezoneDropDown = () => { - return <>; + const timezone = useAppSelector(selectTimezone); + const dispatch = useAppDispatch(); + + const [isOpen, setIsOpen] = useState(false); + const [inputValue, setInputValue] = useState(''); + const [filterValue, setFilterValue] = useState(''); + const [selectOptions, setSelectOptions] = + useState(defaultTimezones); + + useEffect(() => { + let filteredTimezones = defaultTimezones; + + if (filterValue) { + filteredTimezones = defaultTimezones.filter((timezone: string) => + String(timezone).toLowerCase().includes(filterValue.toLowerCase()) + ); + if (!filteredTimezones.length) { + filteredTimezones = [`No results found for "${filterValue}"`]; + } + if (!isOpen) { + setIsOpen(true); + } + } + setSelectOptions(filteredTimezones); + + // This useEffect hook should run *only* on when the filter value changes. + // eslint's exhaustive-deps rule does not support this use. + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [filterValue]); + + const onToggle = (isOpen: boolean) => { + setIsOpen(!isOpen); + }; + + const onInputClick = () => { + if (!isOpen) { + setIsOpen(true); + } else if (!inputValue) { + setIsOpen(false); + } + }; + + const onSelect = (_event: React.MouseEvent, value: string) => { + if (value && !value.includes('No results')) { + setInputValue(value); + setFilterValue(''); + dispatch(changeTimezone(value)); + setIsOpen(false); + } + }; + + const onTextInputChange = (_event: React.FormEvent, value: string) => { + setInputValue(value); + setFilterValue(value); + + if (value !== timezone) { + dispatch(changeTimezone('')); + } + }; + + const onToggleClick = () => { + setIsOpen(!isOpen); + }; + + const onClearButtonClick = () => { + setInputValue(''); + setFilterValue(''); + dispatch(changeTimezone('')); + }; + + const toggle = (toggleRef: React.Ref) => ( + + + + + {timezone && ( + + + + )} + + + ); + + return ( + + + + ); }; export default TimezoneDropDown; diff --git a/src/Components/CreateImageWizard/steps/Timezone/index.tsx b/src/Components/CreateImageWizard/steps/Timezone/index.tsx index 1d7dc96b..48bf857f 100644 --- a/src/Components/CreateImageWizard/steps/Timezone/index.tsx +++ b/src/Components/CreateImageWizard/steps/Timezone/index.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { Text, Form, Title } from '@patternfly/react-core'; +import NtpServersInput from './components/NtpServersInput'; import TimezoneDropDown from './components/TimezoneDropDown'; const TimezoneStep = () => { @@ -12,6 +13,7 @@ const TimezoneStep = () => { Select a timezone for your image. + ); };