add support for epoch parents
This commit is contained in:
parent
be10c6923f
commit
d1e1d8e1c6
9 changed files with 70 additions and 45 deletions
|
@ -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 (
|
||||||
|
|
|
@ -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 />);
|
||||||
|
|
|
@ -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>');
|
|
||||||
});
|
|
||||||
});
|
});
|
|
@ -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);
|
|
||||||
})
|
|
||||||
});
|
});
|
|
@ -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'});
|
||||||
});
|
});
|
||||||
|
|
||||||
})
|
})
|
|
@ -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>
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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]}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in a new issue