Skip to content

Commit

Permalink
Merge branch 'master' of github.com:OpenSRP/web
Browse files Browse the repository at this point in the history
  • Loading branch information
bennsimon committed Apr 22, 2021
2 parents 8e9f56e + 25aad0c commit 9af2a5a
Show file tree
Hide file tree
Showing 5 changed files with 178 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,46 @@ import { SelectProps } from 'antd/lib/select';
import { OptionData } from 'rc-select/lib/interface/';

type RawValueType = string | number | (string | number)[];
export type GetOptions<T> = (data: T[]) => OptionData[];
export type GetSelectedFullData<T> = (
data: T[],
getOptions: GetOptions<T>,
value: SelectProps<RawValueType>['value']
) => T[];

/**
* default method to get the fullData object once use selects an option in the select dropdown,
* once the user selects, you only get the id of the selected object, this function will be called
* to get the full object.
*
* @param data - the full data objects
* @param getOptions - function used to get the options tos how on the dropdown
* @param value - selected value (an array for multi select otherwise a string)
*/
export function getSelectedFullData<T>(
data: T[],
getOptions: GetOptions<T>,
value: SelectProps<RawValueType>['value']
) {
const selected = data.filter((dt) => {
const option = getOptions([dt])[0];
return (Array.isArray(value) && value.includes(option.value)) || value === option.value;
});
return selected;
}

/** props for custom select component */
export interface CustomSelectProps<T = Dictionary> extends SelectProps<RawValueType> {
loadData: (stateSetter: Dispatch<SetStateAction<T[]>>) => Promise<void>;
getOptions: (data: T[]) => OptionData[];
getOptions: GetOptions<T>;
fullDataCallback?: (data: T[]) => void;
getSelectedFullData: GetSelectedFullData<T>;
}

const defaultServiceTypeProps = {
loadData: () => Promise.resolve(),
getOptions: () => [],
getSelectedFullData,
};

/** custom select, gets options from the api
Expand All @@ -27,7 +56,14 @@ const defaultServiceTypeProps = {
function CustomSelect<T>(props: CustomSelectProps<T>) {
const [loading, setLoading] = useState(true);
const [data, setData] = useState<T[]>([]);
const { loadData, getOptions, value, fullDataCallback, ...restProps } = props;
const {
loadData,
getOptions,
value,
fullDataCallback,
getSelectedFullData,
...restProps
} = props;

useEffect(() => {
loadData(setData)
Expand All @@ -39,12 +75,9 @@ function CustomSelect<T>(props: CustomSelectProps<T>) {
}, []);

useEffect(() => {
const selected = data.filter((dt) => {
const option = getOptions([dt])[0];
return (Array.isArray(value) && value.includes(option.value)) || value === option.value;
});
const selected = getSelectedFullData(data, getOptions, value);
fullDataCallback?.(selected);
}, [data, fullDataCallback, getOptions, value]);
}, [data, fullDataCallback, getOptions, getSelectedFullData, value]);

const selectOptions = getOptions(data);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
defaultFormField,
generateLocationUnit,
getLocationTagOptions,
getSelectedLocTagObj,
getServiceTypeOptions,
handleGeoFieldsChangeFactory,
LocationFormFields,
Expand Down Expand Up @@ -357,6 +358,7 @@ const LocationForm = (props: LocationFormProps) => {
}}
getOptions={getLocationTagOptions}
fullDataCallback={setLocationTags}
getSelectedFullData={getSelectedLocTagObj}
/>
</FormItem>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,34 @@ export const generatedLocation4 = {
geometry: { type: 'Point', coordinates: [19.56, 34.56] },
};

export const generatedLocation4Dot1 = {
properties: {
geographicLevel: 1,
parentId: '03176924-6b3c-4b74-bccd-32afcceebabd',
name: 'MENABE',
name_en: 'MENABE',
status: 'Active',
version: 0,
},
id: '38a0a19b-f91e-4044-a8db-a4b62490bf27',
syncStatus: 'Synced',
type: 'Feature',
locationTags: [{ id: 2, active: true, name: 'Region', description: 'Region Location Tag' }],
geometry: {
type: 'Polygon',
coordinates: [
[
[17.4298095703125, 29.897805610155874],
[17.215576171875, 29.750070930806785],
[17.4957275390625, 29.3965337391284],
[17.9901123046875, 29.54000879252545],
[18.006591796874996, 29.79298413547051],
[17.4298095703125, 29.897805610155874],
],
],
},
};

export const expectedFormFields = {
externalId: '',
extraFields: [],
Expand Down Expand Up @@ -323,6 +351,27 @@ export const locationTags = [
{ id: 12, active: true, name: 'Test', description: '' },
];

export const duplicateLocationTags = [
{ id: 1, active: true, name: 'Country', description: 'Country Location Tag' },
{ id: 3, active: true, name: 'District', description: 'District Location Tag' },
{ id: 4, active: true, name: 'Commune', description: 'Commune Location Tag' },
{ id: 5, active: true, name: 'Service Point', description: 'Service Point' },
{ id: 6, active: false, name: 'Location name', description: '' },
{ id: 1, active: true, name: 'Country', description: 'Country Location Tag' },
{ id: 3, active: true, name: 'District', description: 'District Location Tag' },
{ id: 4, active: true, name: 'Commune', description: 'Commune Location Tag' },
{ id: 5, active: true, name: 'Service Point', description: 'Service Point' },
{ id: 6, active: false, name: 'Location name', description: '' },
{ id: 1, active: true, name: 'Country', description: 'Country Location Tag' },
{ id: 3, active: true, name: 'District', description: 'District Location Tag' },
{ id: 4, active: true, name: 'Commune', description: 'Commune Location Tag' },
{ id: 5, active: true, name: 'Service Point', description: 'Service Point' },
{ id: 6, active: false, name: 'Location name', description: '' },
{ id: 2, active: true, name: 'Region', description: 'Region Location Tag' },
{ id: 2, active: true, name: 'Region', description: 'Region Location Tag' },
{ id: 2, active: true, name: 'Region', description: 'Region Location Tag' },
];

export const locationSettings = [
{
key: 'sample_key',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@ import { authenticateUser } from '@onaio/session-reducer';
import { Form } from 'antd';
import {
createdLocation1,
duplicateLocationTags,
fetchCalls1,
generatedLocation2,
generatedLocation4,
generatedLocation4Dot1,
location2,
location4,
locationSettings,
Expand Down Expand Up @@ -683,4 +685,64 @@ describe('LocationForm', () => {
]);
wrapper.unmount();
});

it('#595 Duplicate location Tags failing upload', async () => {
const container = document.createElement('div');
document.body.appendChild(container);

fetch
.once(JSON.stringify([location2]))
.once(JSON.stringify(serviceTypeSettings))
.once(JSON.stringify(duplicateLocationTags))
.once(JSON.stringify(locationSettings))
.once(JSON.stringify(rawOpenSRPHierarchy1));

const initialValues = getLocationFormFields(location4);

const locationFormProps = {
initialValues,
};

const wrapper = mount(
<Router history={history}>
<LocationForm {...locationFormProps} />
</Router>,

{ attachTo: container }
);

await act(async () => {
await new Promise((resolve) => setImmediate(resolve));
wrapper.update();
});

fetch.mockReset();

wrapper.find('form').simulate('submit');

await act(async () => {
await new Promise((resolve) => setImmediate(resolve));
wrapper.update();
});

/** payload does not contain duplicate entries in locationTags field*/
expect(generatedLocation4Dot1.locationTags).toHaveLength(1);
expect(fetch.mock.calls).toEqual([
[
'https://opensrp-stage.smartregister.org/opensrp/rest/location?is_jurisdiction=true',
{
'Cache-Control': 'no-cache',
Pragma: 'no-cache',
body: JSON.stringify(generatedLocation4Dot1),
headers: {
accept: 'application/json',
authorization: 'Bearer sometoken',
'content-type': 'application/json;charset=UTF-8',
},
method: 'PUT',
},
],
]);
wrapper.unmount();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import { v4 } from 'uuid';
import { Geometry, Point } from 'geojson';
import lang, { Lang } from '../../lang';
import { FormInstance } from 'antd/lib/form/hooks/useForm';
import { GetSelectedFullData } from './CustomSelect';
import { uniqBy } from 'lodash';

export enum FormInstances {
CORE = 'core',
Expand Down Expand Up @@ -359,6 +361,29 @@ export const getLocationTagOptions = (tags: LocationUnitTag[]) => {
});
};

/**
* method to get the full Location tag object once user selects an option in the select dropdown,
* once the user selects, you only get the id of the selected object, this function will be called
* to get the full location Tag.
*
* @param data - the full data objects
* @param getOptions - function used to get the options tos how on the dropdown
* @param value - selected value (an array for multi select otherwise a string)
*/
export const getSelectedLocTagObj: GetSelectedFullData<LocationUnitTag> = (
data,
getOptions,
value
) => {
// #595 - remove duplicate data(those that have the same id)
const uniqData = uniqBy(data, (obj) => obj.id);
const selected = uniqData.filter((dt) => {
const option = getOptions([dt])[0];
return (Array.isArray(value) && value.includes(option.value)) || value === option.value;
});
return selected;
};

/**
* generates tree select options
*
Expand Down

0 comments on commit 9af2a5a

Please sign in to comment.