dsifford/academic-bloggers-toolkit

View on GitHub
src/js/gutenberg/components/reference-form-manual/index.tsx

Summary

Maintainability
B
5 hrs
Test Coverage
import { Notice } from '@wordpress/components';
import { useEffect, useState } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
 
import Autocite from 'gutenberg/components/autocite';
import DataFields from 'gutenberg/components/data-fields';
import PeopleFields from 'gutenberg/components/people-fields';
import { DataContext, PeopleContext } from 'gutenberg/context';
import { isCslPersonKey } from 'utils/constants';
import fields from 'utils/fieldmaps';
import { typedKeys } from 'utils/types';
 
import styles from './style.scss';
 
interface Props {
id: string;
data?: CSL.Data;
withAutocite?: boolean;
onSubmit(data: CSL.Data): void;
}
 
type Person = { kind: CSL.PersonFieldKey } & CSL.Person;
 
Function `ReferenceFormManual` has 129 lines of code (exceeds 25 allowed). Consider refactoring.
Function `ReferenceFormManual` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.
function ReferenceFormManual(props: Props) {
const [errorMessage, setErrorMessage] = useState('');
const [data, setData] = useState<CSL.Data>({
id: '',
type: 'webpage',
});
const [people, setPeople] = useState<Person[]>([
{
kind: 'author',
family: '',
given: '',
},
]);
 
useEffect(() => {
if (props.data) {
const [d, p] = consumeData(props.data);
setData(d);
setPeople(p);
}
}, [props.data]);
 
const isBookType = ['book', 'chapter'].includes(data.type);
const isWebpageType = data.type === 'webpage';
 
return (
<PeopleContext.Provider
value={{
people,
add() {
setPeople([
...people,
{ kind: 'author', family: '', given: '' },
]);
},
remove() {
setPeople(people.slice(0, people.length - 1));
},
update(idx, person) {
setPeople([
...people.slice(0, idx),
person,
...people.slice(idx + 1),
]);
},
}}
>
<DataContext.Provider
value={{
data,
update(key, val) {
setData({ ...data, [key]: val });
},
}}
>
<form
className={styles.form}
id={props.id}
onSubmit={e => {
e.preventDefault();
props.onSubmit(
people.reduce<CSL.Data>(
(obj, { kind, ...person }) => {
const existingField = obj[kind];
if (existingField) {
return {
...obj,
[kind]: [...existingField, person],
};
}
return {
...obj,
[kind]: [person],
};
},
data,
),
);
}}
>
{errorMessage && (
<Notice
status="error"
onRemove={() => setErrorMessage('')}
>
{errorMessage}
</Notice>
)}
<label className={styles.field}>
{__('Citation type', 'academic-bloggers-toolkit')}
<select
style={{ maxWidth: 'initial' }}
value={data.type}
onChange={e =>
setData({
id: '',
type: e.currentTarget.value as CSL.ItemType,
})
}
>
{[...Object.entries(fields)].map(
([value, { title: label }]) => (
<option key={value} value={value}>
{label}
</option>
),
)}
</select>
</label>
{props.withAutocite && (
<Autocite
inputProps={{
pattern: isBookType
? '(?:[\\dxX]-?){10}|(?:[\\dxX]-?){13}'
: undefined,
placeholder: isBookType
? __('ISBN', 'academic-bloggers-toolkit')
: isWebpageType
? __('URL', 'academic-bloggers-toolkit')
: undefined,
type: isWebpageType ? 'url' : undefined,
}}
kind={data.type}
onError={setErrorMessage}
onSubmit={consumeData}
/>
)}
<PeopleFields fields={fields[data.type].people} />
<DataFields fieldmap={fields[data.type]} />
</form>
</DataContext.Provider>
</PeopleContext.Provider>
);
}
 
function consumeData(input: CSL.Data): [CSL.Data, Person[]] {
const keys = typedKeys(input);
const peopleFields: Person[] = keys
.filter(key => isCslPersonKey(key))
.map(key => {
const field = input[key] as CSL.Person[];
return field.map(person => ({
...person,
kind: key as CSL.PersonFieldKey,
}));
})
.reduce((arr, values) => [...arr, ...values], []);
const dataFields = keys
.filter(key => !isCslPersonKey(key))
.reduce<CSL.Data>((obj, key) => ({ ...obj, [key]: input[key] }), {
id: '',
type: 'article',
});
return [dataFields, peopleFields];
}
 
export default ReferenceFormManual;