Skip to content

Commit

Permalink
fix: Show Single & Multi-select fields in Report View
Browse files Browse the repository at this point in the history
  • Loading branch information
okdistribute committed Sep 17, 2020
1 parent 6fe01ce commit fba9e50
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 4 deletions.
7 changes: 3 additions & 4 deletions src/renderer/components/MapFilter/ReportView/PDFReport.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,10 @@ import {
} from '@react-pdf/renderer'
import type { Observation } from 'mapeo-schema'

import { FormattedFieldProp, FormattedFieldValue } from '../internal/FormattedData'
import FormattedLocation from '../internal/FormattedLocation'
import { isEmptyValue } from '../utils/helpers'
import { get } from '../utils/get_set'
import FormattedFieldname from '../internal/FormattedFieldname'
import FormattedValue from '../internal/FormattedValue'
import type { ImageMediaItem } from '../ObservationDialog'
import type {
Field,
Expand Down Expand Up @@ -201,10 +200,10 @@ function Details ({view}: {view: ObservationView}) {
{nonEmptyFields.map(field => (
<View key={field.id} style={styles.field} wrap={false}>
<Text style={styles.fieldLabel}>
<FormattedFieldname field={field} component={Text} />
<FormattedFieldProp field={field} propName="label" />
</Text>
<Text style={styles.fieldValue}>
<FormattedValue field={field} value={get(view.tags, field.key)} />
<FormattedFieldValue field={field} value={get(view.tags, field.key)} />
</Text>
</View>
))}
Expand Down
102 changes: 102 additions & 0 deletions src/renderer/components/MapFilter/internal/FormattedData.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// @flow
import React from "react";
import { defineMessages, useIntl } from "react-intl";
import type { Field } from "mapeo-schema";

import { convertSelectOptionsToLabeled } from "../utils/fields";


const m = defineMessages({
noAnswer: {
// Keep original id to avoid re-translation
id: "screens.Observation.ObservationView.noAnswer",
defaultMessage: "No answer",
description:
"Placeholder text for fields on an observation which are not answered",
}
})

// Render the translated value of a translatable Field property (one of
// `label`, `placeholder` or `helperText`). `label` will always render
// something: if it is undefined or an empty string, then it will use the field
// key as the label. `placeholder` and `helperText` will render to null if they
// are not defined.
export const FormattedFieldProp = ({
field,
propName,
}: {
field: Field,
propName: "label" | "placeholder" | "helperText",
}) => {
const { formatMessage: t } = useIntl();
const fieldKey = Array.isArray(field.key) ? field.key[0] : field.key;
const value = field[propName]
? t({
id: `fields.${field.id}.${propName}`,
defaultMessage: field[propName],
})
: // Never show a blank label, fall back to field.key, otherwise return null
propName === "label"
? fieldKey
: undefined;
if (!value) return null;
return <>{value}</>;
};

// Render a field value as a string. If the value is an array, convert to string
// and join with `, `. If the field is a select_one or select_multiple field,
// then use `field.option.label` to display the value, if a label is defined.
// Translate the field value if a translation is defined.
//
// TODO: Consider an API like
// https://formatjs.io/docs/react-intl/components#formatteddateparts to enable
// formatting of individual items in an array value.
export const FormattedFieldValue = ({
value,
field,
}: {|
value: any,
field: Field,
|}) => {
const { formatMessage: t } = useIntl();
// Select multiple answers are an array, so we join them with commas
const formattedValue = (Array.isArray(value) ? value : [value])
// Filter any undefined values or empty strings (an empty string can come
// from a user deleting an answer) TODO: Values that are just spaces
.filter(value => typeof value !== "undefined" && value !== "")
.map(value =>
t({
id: `fields.${field.id}.options.${JSON.stringify(value)}`,
defaultMessage: getValueLabel(value, field),
}).trim()
)
.join(", ");
// This will return a noAnswer string if formattedValue is undefined or an
// empty string
return <>{formattedValue || t(m.noAnswer)}</>;
};

// TODO: Better hangling of boolean and null values (we don't create these
// anywhere yet)
function getValueLabel(
value: null | boolean | number | string,
field: Field
): string {
if (field.type === "select_one" || field.type === "select_multiple") {
// Look up label from field options. This is not necessary for presets
// created with mapeo-settings-builder@^3.1.0, which will have these options
// in the translation file, but is needed for older versions of presets
const options = convertSelectOptionsToLabeled(field.options);
const matchingOption = options.find(option => option.value === value);
if (matchingOption) return matchingOption.label;
}
if (value === null) {
return "NULL";
} else if (typeof value === "boolean") {
return value ? "TRUE" : "FALSE";
} else if (typeof value === "number") {
return String(value);
} else {
return value;
}
}
29 changes: 29 additions & 0 deletions src/renderer/components/MapFilter/utils/fields.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// @flow
export function getProp(tags: any, fieldKey: Key, defaultValue: any) {
// TODO: support deeply nested tags.
const shallowKey = Array.isArray(fieldKey) ? fieldKey[0] : fieldKey;
const tagValue = tags[shallowKey];
return typeof tagValue === "undefined" ? defaultValue : tagValue;
}

/**
* Convert a select option which could either be a string or an object with
* label and value props, to an object with label and value props TODO: Show
* meaningful translated values for null and boolean, but these types are not
* used widely in presets yet
*/
export function convertSelectOptionsToLabeled(
options: SelectOptions
): LabeledSelectOption[] {
return options.map(option => {
if (option === null) {
return { label: "NULL", value: option };
} else if (typeof option === "boolean") {
return { label: option ? "TRUE" : "FALSE", value: option };
} else if (typeof option === "string" || typeof option === "number") {
return { label: option + "", value: option };
} else {
return option;
}
});
}

0 comments on commit fba9e50

Please sign in to comment.