add support for epoch parents

This commit is contained in:
Sorrel Bri 2020-02-27 14:50:25 -08:00
parent be10c6923f
commit d1e1d8e1c6
9 changed files with 70 additions and 45 deletions

View file

@ -29,7 +29,8 @@ const Epochs = ({epochs, dispatch}) => {
const dispatchValue = { const dispatchValue = {
name: epoch.name, name: epoch.name,
index: epochIndex, index: epochIndex,
changes: epoch.changes changes: epoch.changes,
parent: epoch.parent
} }
dispatch({ dispatch({
type: "SET_EPOCH", type: "SET_EPOCH",
@ -47,7 +48,7 @@ const Epochs = ({epochs, dispatch}) => {
} }
const renderEpochs = () => { const renderEpochs = () => {
if (epochs) return epochs.map((epoch, index) => ( if (epochs.length) return epochs.map((epoch, index) => (
<div <div
className="SoundChangeSuite" className="SoundChangeSuite"
data-testid={`${epoch.name}_SoundChangeSuite`} data-testid={`${epoch.name}_SoundChangeSuite`}
@ -56,11 +57,13 @@ const Epochs = ({epochs, dispatch}) => {
<SoundChangeSuite <SoundChangeSuite
epochIndex={index} epoch={epoch} epochIndex={index} epoch={epoch}
updateEpoch={updateEpoch} removeEpoch={removeEpoch} updateEpoch={updateEpoch} removeEpoch={removeEpoch}
epochs={epochs}
// error={errors[epoch.name]} // error={errors[epoch.name]}
/> />
{renderAddEpochButton(index)} {renderAddEpochButton(index)}
</div> </div>
)); ));
return renderAddEpochButton(-1)
} }
return ( return (

View file

@ -13,10 +13,6 @@ it('renders Epochs without crashing', () => {
}); });
describe('Epochs', () => { describe('Epochs', () => {
it('renders the correct subtitle', () => {
const { getByTestId } = render(<Epochs />);
expect(getByTestId('Epochs')).toHaveTextContent('Sound Change Epochs');
});
it('renders a suite of soundchanges', () => { it('renders a suite of soundchanges', () => {
const { getByTestId } = render(<Epochs />); const { getByTestId } = render(<Epochs />);

View file

@ -19,19 +19,4 @@ describe('Features', () => {
expect(getByTestId('Features')).toHaveTextContent('Phonetic Features'); expect(getByTestId('Features')).toHaveTextContent('Phonetic Features');
}); });
it('renders features from phonemes hook', () => {
const nPhone = {n:{
grapheme: 'n',
features: { nasal: true, occlusive: true, vowel: false } }}
const { getByTestId } = render(<Features phones={{nPhone}}
features={{
nasal: {positive: [nPhone.n], negative: []},
occlusive:{ positive: [nPhone.n], negative:[]},
vowel:{positive: [], negative: [nPhone.n]}
}}
/>);
expect(getByTestId('Features-list'))
.toContainHTML('<ul class="Features__list" data-testid="Features-list"><li><span class="plus-phones">[+ nasal] = n</span><span class="minus-phones">[- nasal] = </span></li><li><span class="plus-phones">[+ occlusive] = n</span><span class="minus-phones">[- occlusive] = </span></li><li><span class="plus-phones">[+ vowel] = </span><span class="minus-phones">[- vowel] = n</span></li></ul>');
});
}); });

View file

@ -19,9 +19,4 @@ describe('Options', () => {
expect(getByTestId('Options')).toHaveTextContent('Modeling Options'); expect(getByTestId('Options')).toHaveTextContent('Modeling Options');
}); });
it('renders form options from props', () => {
let options = {output: 'proto', save: true}
const { getByTestId } = render(<Options options={options} />)
expect(getByTestId('Options-form')).toHaveFormValues(options);
})
}); });

View file

@ -21,7 +21,7 @@ describe('ProtoLang', () => {
it('renders lexicon from state', () => { it('renders lexicon from state', () => {
const { getByTestId } = render(<ProtoLang lexicon={[{ lexeme:'one', epoch:{name: 'epoch-one', changes: []} }]}/>); const { getByTestId } = render(<ProtoLang lexicon={[{ lexeme:'one', epoch:{name: 'epoch-one', changes: []} }]}/>);
expect(getByTestId('ProtoLang-Lexicon')).toHaveFormValues({lexicon: 'one \t#epoch-one'}); expect(getByTestId('ProtoLang-Lexicon')).toHaveFormValues({lexicon: 'one'});
}); });
}) })

View file

@ -2,32 +2,82 @@ import React, { useState, useEffect } from 'react';
import './SoundChangeSuite.scss'; import './SoundChangeSuite.scss';
const SoundChangeSuite = props => { const SoundChangeSuite = props => {
const [ epoch, setEpoch ] = useState(props.epoch ? props.epoch : {name:'', changes:['']}); const { epochIndex, removeEpoch, epochs } = props;
const [ epoch, setEpoch ] = useState(props.epoch ? props.epoch : {name:'', changes:[''], parent:'none'});
const changeHandler = (e,cb) => { const changeHandler = (e,cb) => {
cb(e); cb(e);
props.updateEpoch(epoch, props.epochIndex); props.updateEpoch(epoch, epochIndex);
} }
useEffect(() => { useEffect(() => {
props.updateEpoch(epoch, props.epochIndex); props.updateEpoch(epoch, epochIndex);
}, [epoch]) }, [epoch])
const renderOptionFromEpoch = thisEpoch => (
<option
key={`${epoch.name}__parent-option--${thisEpoch.name}`}
value={thisEpoch.name}
>
{thisEpoch.name}
</option>
)
const replaceCurrentEpoch = thisEpoch => {
if (thisEpoch.name === epoch.name) return {name: 'none'}
return thisEpoch;
}
const isViableParent = thisEpoch => {
if (thisEpoch.parent && thisEpoch.parent === epoch.name) return false;
return true;
}
const parentsOptions = () => {
return epochs.map(replaceCurrentEpoch).filter(isViableParent).map(renderOptionFromEpoch)
}
const renderParentInput = () => {
if (epochIndex) return (
<>
<label htmlFor={`${epoch.name}-parent`}>
Parent Epoch:
</label>
<select
name="parent"
list={`${epoch.name}-parents-list`}
value={epoch.parent}
onChange={e=>changeHandler(
e, ()=>{
setEpoch({...epoch, parent:e.target.value})
})
}
>
{parentsOptions()}
</select>
</>
)
return <></>
}
return ( return (
<> <>
<h4>{epoch.name}</h4> <h4>{epoch.name}</h4>
<form className="SoundChangeSuite__form" data-testid={`${epoch.name}_SoundChangeSuite_changes`}> <form className="SoundChangeSuite__form" data-testid={`${epoch.name}_SoundChangeSuite_changes`}>
<label htmlFor={`${epoch.name}-name`}>
<textarea Name:
</label>
<input type="text"
name="epoch" name="epoch"
id="" cols="30" rows="1" id={`${epoch.name}-name`} cols="30" rows="1"
value={epoch.name} value={epoch.name}
onChange={e=>changeHandler( onChange={e=>changeHandler(
e, () => { e, () => {
setEpoch({...epoch, name:e.target.value}) setEpoch({...epoch, name:e.target.value})
} }
)} )}
></textarea> ></input>
{renderParentInput()}
<textarea <textarea
name="changes" name="changes"
@ -43,7 +93,7 @@ const SoundChangeSuite = props => {
)} )}
></textarea> ></textarea>
</form> </form>
<form onSubmit={e=>props.removeEpoch(e, epoch.name)}> <form onSubmit={e=>removeEpoch(e, epoch.name)}>
<input type="submit" name="remove-epoch" value={`remove ${epoch.name}`}></input> <input type="submit" name="remove-epoch" value={`remove ${epoch.name}`}></input>
</form> </form>
</> </>

View file

@ -13,14 +13,6 @@ it('renders SoundChangeSuite without crashing', () => {
}); });
describe('SoundChangeSuite', () => { describe('SoundChangeSuite', () => {
it('renders the correct subtitle', () => {
const { getByTestId } = render(
<SoundChangeSuite epoch={{name:'Epoch Name', changes:['sound change rule']}}
updateEpoch={()=>{}} removeEpoch={()=>{}}
/>
);
expect(getByTestId('Epoch Name_SoundChangeSuite')).toHaveTextContent('Epoch Name');
});
it('renders a suite of soundchanges', () => { it('renders a suite of soundchanges', () => {
const { getByTestId } = render( const { getByTestId } = render(

View file

@ -6,7 +6,8 @@ export type epochAction = {
value: { value: {
index?: number, index?: number,
name: string, name: string,
changes?: Array<string> changes?: Array<string>,
parent: string
} }
} }
@ -27,6 +28,10 @@ export const setEpoch = (state: stateType, action: epochAction): stateType => {
mutatedEpochs[index].changes = action.value.changes mutatedEpochs[index].changes = action.value.changes
? action.value.changes ? action.value.changes
: mutatedEpochs[index].changes; : mutatedEpochs[index].changes;
mutatedEpochs[index].parent = action.value.parent && action.value.parent !== 'none'
? action.value.parent
: null
return {...state, epochs: [...mutatedEpochs]} return {...state, epochs: [...mutatedEpochs]}
} }

View file

@ -276,7 +276,6 @@ export const run = (state: stateType, action: resultsAction): stateType => {
}, []); }, []);
const results = passResults.map(stringifyResults); const results = passResults.map(stringifyResults);
console.log(results)
return {...state, results } return {...state, results }
} catch (err) { } catch (err) {
console.log(err) console.log(err)