Skip to content

Commit

Permalink
Merge 29bde38 into 383c28f
Browse files Browse the repository at this point in the history
  • Loading branch information
ifirmawan committed Aug 9, 2023
2 parents 383c28f + 29bde38 commit 0445518
Show file tree
Hide file tree
Showing 11 changed files with 177 additions and 78 deletions.
37 changes: 27 additions & 10 deletions app/src/form/fields/TypeCascade.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import React, { useState, useEffect, useMemo } from 'react';
import React, { useState, useEffect, useMemo, useCallback } from 'react';
import { View, Text } from 'react-native';
import { Dropdown } from 'react-native-element-dropdown';
import { FieldLabel } from '../support';
import { styles } from '../styles';
import { FormState, UIState } from '../../store';
import { i18n } from '../../lib';
import { i18n, cascades } from '../../lib';

const TypeCascade = ({
onChange,
Expand All @@ -21,6 +21,7 @@ const TypeCascade = ({
const [dropdownItems, setDropdownItems] = useState([]);
const activeLang = UIState.useState((s) => s.lang);
const trans = i18n.text(activeLang);
const requiredValue = required ? requiredSign : null;

const groupBy = (array, property) => {
const gd = array
Expand Down Expand Up @@ -60,8 +61,8 @@ const TypeCascade = ({
onChange(id, finalValues);
if (finalValues) {
const { options: selectedOptions, value: selectedValue } = dropdownValues.pop();
const findSelected = selectedOptions?.find((o) => o.id === selectedValue) || [];
const cascadeName = findSelected?.name || null;
const findSelected = selectedOptions?.find((o) => o.id === selectedValue);
const cascadeName = findSelected?.name;
FormState.update((s) => {
s.cascades = { ...s.cascades, [id]: cascadeName };
});
Expand All @@ -87,6 +88,27 @@ const TypeCascade = ({
});
}, [dataSource, source, values, id]);

const fetchCascade = useCallback(async () => {
if (source && values?.[id]?.length) {
const cascadeID = values[id].slice(-1)[0];
const { rows } = await cascades.loadDataSource(source, cascadeID);
const { length: rowLength, _array: rowItems } = rows;
const csValue = rowLength ? rowItems[0] : null;
if (csValue) {
FormState.update((s) => {
s.cascades = {
...s.cascades,
[id]: csValue.name,
};
});
}
}
}, [source, values, id]);

useEffect(() => {
fetchCascade();
}, [fetchCascade]);

useEffect(() => {
if (dropdownItems.length === 0 && initialDropdowns.length) {
setDropdownItems(initialDropdowns);
Expand All @@ -95,12 +117,7 @@ const TypeCascade = ({

return (
<View testID="view-type-cascade">
<FieldLabel
keyform={keyform}
name={name}
tooltip={tooltip}
requiredSign={required ? requiredSign : null}
/>
<FieldLabel keyform={keyform} name={name} tooltip={tooltip} requiredSign={requiredValue} />
<Text testID="text-values" style={styles.cascadeValues}>
{values[id]}
</Text>
Expand Down
8 changes: 2 additions & 6 deletions app/src/form/fields/TypeDate.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,10 @@ const TypeDate = ({ onChange, values, keyform, id, name, tooltip, required, requ
};

const datePickerValue = getDate(values?.[id]);
const requiredValue = required ? requiredSign : null;
return (
<View>
<FieldLabel
keyform={keyform}
name={name}
tooltip={tooltip}
requiredSign={required ? requiredSign : null}
/>
<FieldLabel keyform={keyform} name={name} tooltip={tooltip} requiredSign={requiredValue} />
<Field name={id}>
{({ field, meta }) => {
const dateValue = field?.value ? getDate(field.value).toLocaleDateString() : field?.value;
Expand Down
8 changes: 2 additions & 6 deletions app/src/form/fields/TypeGeo.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const TypeGeo = ({ onChange, values, keyform, id, name, tooltip, required, requi

const navigation = useNavigation();
const route = useRoute();
const requiredValue = required ? requiredSign : null;

const handleGetCurrLocation = async (openMap = false) => {
const loadingKey = openMap ? 'map' : 'current';
Expand Down Expand Up @@ -53,12 +54,7 @@ const TypeGeo = ({ onChange, values, keyform, id, name, tooltip, required, requi

return (
<View>
<FieldLabel
keyform={keyform}
name={name}
tooltip={tooltip}
requiredSign={required ? requiredSign : null}
/>
<FieldLabel keyform={keyform} name={name} tooltip={tooltip} requiredSign={requiredValue} />
<View style={styles.inputGeoContainer}>
{errorMsg ? (
<Text testID="text-error">{errorMsg}</Text>
Expand Down
8 changes: 2 additions & 6 deletions app/src/form/fields/TypeInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,14 +78,10 @@ const TypeInput = ({
required,
requiredSign,
}) => {
const requiredValue = required ? requiredSign : null;
return (
<View>
<FieldLabel
keyform={keyform}
name={name}
tooltip={tooltip}
requiredSign={required ? requiredSign : null}
/>
<FieldLabel keyform={keyform} name={name} tooltip={tooltip} requiredSign={requiredValue} />
<Input
inputContainerStyle={styles.inputFieldContainer}
onChangeText={(val) => {
Expand Down
8 changes: 2 additions & 6 deletions app/src/form/fields/TypeMultipleOption.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,11 @@ const TypeMultipleOption = ({
}, [option]);
const activeLang = UIState.useState((s) => s.lang);
const trans = i18n.text(activeLang);
const requiredValue = required ? requiredSign : null;

return (
<View style={styles.multipleOptionContainer}>
<FieldLabel
keyform={keyform}
name={name}
tooltip={tooltip}
requiredSign={required ? requiredSign : null}
/>
<FieldLabel keyform={keyform} name={name} tooltip={tooltip} requiredSign={requiredValue} />
{isCheckBox ? (
option.map((opt, opti) => (
<CheckBox
Expand Down
8 changes: 2 additions & 6 deletions app/src/form/fields/TypeNumber.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,10 @@ const TypeNumber = ({
required,
requiredSign,
}) => {
const requiredValue = required ? requiredSign : null;
return (
<View>
<FieldLabel
keyform={keyform}
name={name}
tooltip={tooltip}
requiredSign={required ? requiredSign : null}
/>
<FieldLabel keyform={keyform} name={name} tooltip={tooltip} requiredSign={requiredValue} />
<Input
inputContainerStyle={styles.inputFieldContainer}
keyboardType="numeric"
Expand Down
8 changes: 2 additions & 6 deletions app/src/form/fields/TypeOption.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,11 @@ const TypeOption = ({
}, [option]);
const activeLang = UIState.useState((s) => s.lang);
const trans = i18n.text(activeLang);
const requiredValue = required ? requiredSign : null;

return (
<View style={styles.optionContainer}>
<FieldLabel
keyform={keyform}
name={name}
tooltip={tooltip}
requiredSign={required ? requiredSign : null}
/>
<FieldLabel keyform={keyform} name={name} tooltip={tooltip} requiredSign={requiredValue} />
{isRadioGroup ? (
option.map((opt, opti) => (
<CheckBox
Expand Down
8 changes: 2 additions & 6 deletions app/src/form/fields/TypeText.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,10 @@ import { styles } from '../styles';
import { Input } from '@rneui/themed';

const TypeText = ({ onChange, values, keyform, id, name, tooltip, required, requiredSign }) => {
const requiredValue = required ? requiredSign : null;
return (
<View>
<FieldLabel
keyform={keyform}
name={name}
tooltip={tooltip}
requiredSign={required ? requiredSign : null}
/>
<FieldLabel keyform={keyform} name={name} tooltip={tooltip} requiredSign={requiredValue} />
<Input
inputContainerStyle={styles.inputFieldContainer}
multiline={true}
Expand Down
116 changes: 116 additions & 0 deletions app/src/form/fields/__test__/TypeCascade.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, { useState } from 'react';
import { render, fireEvent, act, waitFor, renderHook } from '@testing-library/react-native';
import TypeCascade from '../TypeCascade';
import { generateDataPointName } from '../../lib';
import { cascades } from '../../../lib';
import { FormState } from '../../../store';

const dummyLocations = [
Expand All @@ -20,6 +21,29 @@ import { View } from 'react-native';
jest.spyOn(View.prototype, 'measureInWindow').mockImplementation((cb) => {
cb(18, 113, 357, 50);
});
jest.mock('expo-sqlite');
jest.mock('../../../lib', () => ({
cascades: {
loadDataSource: jest.fn(async (source, id) => {
return id
? { rows: { length: 1, _array: [{ id: 112, name: 'KAB. PURBALINGGA', parent: 111 }] } }
: {
rows: {
length: dummyLocations.length,
_array: dummyLocations,
},
};
}),
},
i18n: {
text: jest.fn(() => ({
latitude: 'Latitude',
longitude: 'Longitude',
})),
},

generateDataPointName: jest.fn(),
}));

describe('TypeCascade', () => {
it('Should not show options when the data source is not set.', () => {
Expand Down Expand Up @@ -566,4 +590,96 @@ describe('TypeCascade', () => {
expect(values[fieldID]).toEqual([107, 109]);
});
});

it('should set datapointname when input has data', async () => {
const fieldID = 'location';
const fieldName = 'Location';
const initialValue = [111, 112];
const values = { [fieldID]: initialValue };

const mockedOnChange = jest.fn((fieldName, value) => {
values[fieldName] = value;
});
act(() => {
FormState.update((s) => {
s.currentValues = {
location: initialValue,
};
});
});

const questionSource = { file: 'file.sqlite', parent_id: 111 };
const { getByTestId, getByText, debug } = render(
<TypeCascade
onChange={mockedOnChange}
id={fieldID}
name={fieldName}
values={values}
dataSource={dummyLocations}
source={questionSource}
/>,
);

act(() => {
FormState.update((s) => {
s.cascades = { location: 'KAB. PURBALINGGA' };
});
});

await waitFor(() => {
const { result } = renderHook(() => FormState.useState((s) => s.currentValues));
const form = {
question_group: [
{ name: 'Example', question: [{ type: 'cascade', meta: true, id: 'location' }] },
],
};
const datapoint = generateDataPointName(form, result.current[0], {
location: 'KAB. PURBALINGGA',
});
expect(datapoint.dpName).toBe('KAB. PURBALINGGA');
});
});

it('should generate empty datapointName when selected value not match', async () => {
const fieldID = 'location';
const fieldName = 'Location';
const initialValue = [200];
const values = { [fieldID]: initialValue };

const mockedOnChange = jest.fn((fieldName, value) => {
values[fieldName] = value;
});
act(() => {
FormState.update((s) => {
s.currentValues = {
location: initialValue,
};
});
});

cascades.loadDataSource.mockReturnValue({ rows: { length: 0, _array: [] } });

const questionSource = { file: 'file.sqlite', parent_id: 0 };
const { getByTestId, getByText, debug } = render(
<TypeCascade
onChange={mockedOnChange}
id={fieldID}
name={fieldName}
values={values}
dataSource={dummyLocations}
source={questionSource}
/>,
);

await waitFor(() => {
const { result } = renderHook(() => FormState.useState((s) => s.currentValues));
const form = {
question_group: [
{ name: 'Example', question: [{ type: 'cascade', meta: true, id: 'location' }] },
],
};
const datapoint = generateDataPointName(form, result.current[0], {});
expect(datapoint.dpName).toBe('');
});
});
});
4 changes: 2 additions & 2 deletions app/src/lib/background-task.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,14 +93,14 @@ const syncFormSubmission = async () => {
// get user
const user = await crudUsers.selectUserById({ id: d.user });
const form = await crudForms.selectFormById({ id: d.form });
const geo = d.geo ? d.geo.split('|')?.map((x) => parseFloat(x)) : null;
const geo = d.geo ? d.geo.split('|')?.map((x) => parseFloat(x)) : [];
const syncData = {
formId: form.formId,
name: d.name,
duration: Math.round(d.duration),
submittedAt: d.submittedAt,
submitter: user.name,
geo: geo || [],
geo,
answers: JSON.parse(d.json.replace(/''/g, "'")),
};
console.info('[syncFormSubmision] SyncData:', syncData);
Expand Down
Loading

0 comments on commit 0445518

Please sign in to comment.