Fix AwakeningSelect component

It was completely broken for weapons. We managed to fix the problem by refactoring how data is sent to SelectWithInput but I fear the root error is still there.

We also cleaned the component up a bit.
This commit is contained in:
Justin Edmund 2023-01-20 19:29:07 -08:00
parent 39940abf9f
commit 6023f9ab6a
5 changed files with 60 additions and 87 deletions

View file

@ -1,103 +1,75 @@
import React, { useEffect, useState } from 'react' import React, { useEffect, useState } from 'react'
import { useRouter } from 'next/router' import cloneDeep from 'lodash.clonedeep'
import { useTranslation } from 'next-i18next'
import SelectWithInput from '~components/SelectWithInput' import SelectWithInput from '~components/SelectWithInput'
import SelectItem from '~components/SelectItem'
import classNames from 'classnames'
import { weaponAwakening, characterAwakening } from '~data/awakening' import { weaponAwakening, characterAwakening } from '~data/awakening'
import type { Awakening } from '~data/awakening'
import './index.scss' import './index.scss'
interface Props { interface Props {
object: 'character' | 'weapon' object: 'character' | 'weapon'
awakeningType?: number type?: number
awakeningLevel?: number level?: number
onOpenChange?: (open: boolean) => void onOpenChange?: (open: boolean) => void
sendValidity: (isValid: boolean) => void sendValidity: (isValid: boolean) => void
sendValues: (type: number, level: number) => void sendValues: (type: number, level: number) => void
} }
const AwakeningSelect = (props: Props) => { const AwakeningSelect = (props: Props) => {
const router = useRouter() // Data states
const locale =
router.locale && ['en', 'ja'].includes(router.locale) ? router.locale : 'en'
const { t } = useTranslation('common')
const [open, setOpen] = useState(false)
// States
const [awakeningType, setAwakeningType] = useState( const [awakeningType, setAwakeningType] = useState(
props.object === 'weapon' ? -1 : 1 props.object === 'weapon' ? 0 : 1
) )
const [awakeningLevel, setAwakeningLevel] = useState(1) const [awakeningLevel, setAwakeningLevel] = useState(1)
const [maxValue, setMaxValue] = useState(1)
const [error, setError] = useState('')
// Data // Data
const dataSet = () => { const chooseDataset = () => {
if (props.object === 'character') return characterAwakening let list: ItemSkill[] = []
else {
const weaponDataSet = weaponAwakening
weaponDataSet.unshift({ switch (props.object) {
id: 0, case 'character':
name: { list = characterAwakening
en: 'No awakening', break
ja: '覚醒なし', case 'weapon':
}, // WARNING: Clonedeep is masking a deeper error
slug: 'no-awakening', // which is running this method every time this component is rerendered
minValue: 0, // causing multiple "No awakening" items to be added
maxValue: 0, const awakening = cloneDeep(weaponAwakening)
fractional: false, awakening.unshift({
}) id: 0,
name: {
return weaponDataSet en: 'No awakening',
ja: '覚醒なし',
},
slug: 'no-awakening',
minValue: 0,
maxValue: 0,
fractional: false,
})
list = awakening
break
} }
return list
} }
// Classes
const inputClasses = classNames({
Bound: true,
Hidden: awakeningType === -1,
})
const errorClasses = classNames({
errors: true,
visible: error !== '',
})
// Set max value based on object type
useEffect(() => {
if (props.object === 'character') setMaxValue(9)
else if (props.object === 'weapon') setMaxValue(15)
}, [props.object])
// Set default awakening and level based on object type // Set default awakening and level based on object type
useEffect(() => { useEffect(() => {
let defaultAwakening = 1 const defaultAwakening = props.object === 'weapon' ? 0 : 1
if (props.object === 'weapon') defaultAwakening = -1 const type = props.type != undefined ? props.type : defaultAwakening
setAwakeningType( setAwakeningType(type)
props.awakeningType != undefined ? props.awakeningType : defaultAwakening setAwakeningLevel(props.level ? props.level : 1)
) }, [props.object, props.type, props.level])
setAwakeningLevel(props.awakeningLevel ? props.awakeningLevel : 1)
}, [props.object, props.awakeningType, props.awakeningLevel])
// Send validity of form when awakening level changes // Send validity of form when awakening level changes
useEffect(() => { useEffect(() => {
props.sendValidity(awakeningLevel > 0 && error === '') props.sendValidity(awakeningLevel > 0)
}, [props.sendValidity, awakeningLevel, error]) }, [props.sendValidity, awakeningLevel])
// Classes // Classes
function changeOpen() { function changeOpen(open: boolean) {
setOpen(!open) if (props.onOpenChange) props.onOpenChange(open)
if (props.onOpenChange) props.onOpenChange(!open)
} }
function handleValueChange(type: number, level: number) { function handleValueChange(type: number, level: number) {
@ -110,7 +82,7 @@ const AwakeningSelect = (props: Props) => {
<div className="Awakening"> <div className="Awakening">
<SelectWithInput <SelectWithInput
object={`${props.object}_awakening`} object={`${props.object}_awakening`}
dataSet={dataSet()} dataSet={chooseDataset()}
selectValue={awakeningType} selectValue={awakeningType}
inputValue={awakeningLevel} inputValue={awakeningLevel}
onOpenChange={changeOpen} onOpenChange={changeOpen}

View file

@ -254,8 +254,8 @@ const CharacterModal = ({
<h3>{t('modals.characters.subtitles.awakening')}</h3> <h3>{t('modals.characters.subtitles.awakening')}</h3>
<AwakeningSelect <AwakeningSelect
object="character" object="character"
awakeningType={awakeningType} type={awakeningType}
awakeningLevel={awakeningLevel} level={awakeningLevel}
sendValidity={receiveValidity} sendValidity={receiveValidity}
sendValues={receiveAwakeningValues} sendValues={receiveAwakeningValues}
/> />

View file

@ -72,8 +72,11 @@ const SelectWithInput = ({
// Set default values from props // Set default values from props
useEffect(() => { useEffect(() => {
setCurrentItemSkill(dataSet.find((sk) => sk.id === selectValue)) const found = dataSet.find((sk) => sk.id === selectValue)
setFieldInputValue(inputValue) if (found) {
setCurrentItemSkill(found)
setFieldInputValue(inputValue)
}
}, [selectValue, inputValue]) }, [selectValue, inputValue])
// Methods: UI state management // Methods: UI state management
@ -98,14 +101,6 @@ const SelectWithInput = ({
) )
}) })
if (object === 'weapon_awakening') {
options?.unshift(
<SelectItem key={-1} value={-1}>
{t(`${object}.no_type`)}
</SelectItem>
)
}
return options return options
} }
@ -113,12 +108,18 @@ const SelectWithInput = ({
function handleSelectChange(rawValue: string) { function handleSelectChange(rawValue: string) {
const value = parseInt(rawValue) const value = parseInt(rawValue)
const skill = dataSet.find((sk) => sk.id === value) const skill = dataSet.find((sk) => sk.id === value)
setCurrentItemSkill(skill)
if (skill) {
setCurrentItemSkill(skill)
sendValues(skill.id, fieldInputValue)
}
} }
function handleInputChange(event: React.ChangeEvent<HTMLInputElement>) { function handleInputChange(event: React.ChangeEvent<HTMLInputElement>) {
const value = parseFloat(event.target.value) const value = parseFloat(event.target.value)
if (handleInputError(value)) setFieldInputValue(value) if (handleInputError(value)) setFieldInputValue(value)
if (currentItemSkill) sendValues(currentItemSkill.id, value)
} }
// Methods: Handle error // Methods: Handle error
@ -169,7 +170,7 @@ const SelectWithInput = ({
open={open} open={open}
disabled={selectDisabled} disabled={selectDisabled}
onValueChange={handleSelectChange} onValueChange={handleSelectChange}
onOpenChange={() => changeOpen()} onOpenChange={changeOpen}
onClose={onClose} onClose={onClose}
triggerClass="modal" triggerClass="modal"
> >

View file

@ -64,7 +64,7 @@ const WeaponModal = ({ gridWeapon, children }: Props) => {
const [element, setElement] = useState(-1) const [element, setElement] = useState(-1)
const [awakeningType, setAwakeningType] = useState(-1) const [awakeningType, setAwakeningType] = useState(0)
const [awakeningLevel, setAwakeningLevel] = useState(1) const [awakeningLevel, setAwakeningLevel] = useState(1)
const [primaryAxModifier, setPrimaryAxModifier] = useState(-1) const [primaryAxModifier, setPrimaryAxModifier] = useState(-1)
@ -298,8 +298,8 @@ const WeaponModal = ({ gridWeapon, children }: Props) => {
<h3>{t('modals.weapon.subtitles.awakening')}</h3> <h3>{t('modals.weapon.subtitles.awakening')}</h3>
<AwakeningSelect <AwakeningSelect
object="weapon" object="weapon"
awakeningType={gridWeapon.awakening?.type} type={gridWeapon.awakening?.type}
awakeningLevel={gridWeapon.awakening?.level} level={gridWeapon.awakening?.level}
onOpenChange={receiveAwakeningOpen} onOpenChange={receiveAwakeningOpen}
sendValidity={receiveValidity} sendValidity={receiveValidity}
sendValues={receiveAwakeningValues} sendValues={receiveAwakeningValues}

View file

@ -84,7 +84,7 @@ const WeaponUnit = (props: Props) => {
props.gridWeapon && props.gridWeapon &&
props.gridWeapon.object.awakening && props.gridWeapon.object.awakening &&
props.gridWeapon.awakening && props.gridWeapon.awakening &&
props.gridWeapon.awakening.type >= 0 && props.gridWeapon.awakening.type > 0 &&
props.gridWeapon.awakening.type != null props.gridWeapon.awakening.type != null
) { ) {
const awakening = weaponAwakening.find( const awakening = weaponAwakening.find(