Skip to content

Commit

Permalink
chore: cleaned up options fields and util
Browse files Browse the repository at this point in the history
  • Loading branch information
Skaiir committed Nov 23, 2023
1 parent d96f21b commit c031664
Show file tree
Hide file tree
Showing 12 changed files with 204 additions and 208 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useContext, useRef } from 'preact/hooks';
import useValuesAsync, { LOAD_STATES } from '../../hooks/useValuesAsync';
import { useContext, useEffect, useRef } from 'preact/hooks';
import useOptionsAsync, { LOAD_STATES } from '../../hooks/useOptionsAsync';
import classNames from 'classnames';
import { FormContext } from '../../context';

Expand All @@ -9,7 +9,7 @@ import Label from '../Label';

import { sanitizeMultiSelectValue } from '../util/sanitizerUtil';

import { createEmptyOptions } from '../util/valuesUtil';
import { createEmptyOptions } from '../util/optionsUtil';

import {
formFieldClasses,
Expand All @@ -27,7 +27,7 @@ export default function Checklist(props) {
onFocus,
field,
readonly,
value = [],
value: values = [],
} = props;

const {
Expand All @@ -43,7 +43,7 @@ export default function Checklist(props) {

const toggleCheckbox = (v) => {

let newValue = [ ...value ];
let newValue = [ ...values ];

if (!newValue.includes(v)) {
newValue.push(v);
Expand Down Expand Up @@ -76,9 +76,9 @@ export default function Checklist(props) {
};

const {
state: loadState,
values: options
} = useValuesAsync(field);
loadState,
options
} = useOptionsAsync(field);

const { formId } = useContext(FormContext);
const errorMessageId = errors.length === 0 ? undefined : `${prefixId(id, formId)}-error-message`;
Expand All @@ -95,11 +95,11 @@ export default function Checklist(props) {
key={ `${id}-${index}` }
label={ v.label }
class={ classNames({
'fjs-checked': value.includes(v.value)
'fjs-checked': values.includes(v.value)
}) }
required={ false }>
<input
checked={ value.includes(v.value) }
checked={ values.includes(v.value) }
class="fjs-input"
disabled={ disabled }
readOnly={ readonly }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useContext, useRef } from 'preact/hooks';
import useValuesAsync, { LOAD_STATES } from '../../hooks/useValuesAsync';
import useOptionsAsync, { LOAD_STATES } from '../../hooks/useOptionsAsync';
import classNames from 'classnames';
import { FormContext } from '../../context';

Expand All @@ -9,7 +9,7 @@ import Label from '../Label';

import { sanitizeSingleSelectValue } from '../util/sanitizerUtil';

import { createEmptyOptions } from '../util/valuesUtil';
import { createEmptyOptions } from '../util/optionsUtil';

import {
formFieldClasses,
Expand Down Expand Up @@ -65,9 +65,9 @@ export default function Radio(props) {
};

const {
state: loadState,
values: options
} = useValuesAsync(field);
loadState,
options
} = useOptionsAsync(field);

const { formId } = useContext(FormContext);
const errorMessageId = errors.length === 0 ? undefined : `${prefixId(id, formId)}-error-message`;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import Label from '../Label';

import { sanitizeSingleSelectValue } from '../util/sanitizerUtil';

import { createEmptyOptions } from '../util/valuesUtil';
import { createEmptyOptions } from '../util/optionsUtil';

import {
formFieldClasses,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useContext, useEffect, useMemo, useRef, useState } from 'preact/hooks';
import { useContext, useMemo, useRef, useState } from 'preact/hooks';

import useValuesAsync, { LOAD_STATES } from '../../hooks/useValuesAsync';
import useOptionsAsync, { LOAD_STATES } from '../../hooks/useOptionsAsync';
import { useService } from '../../hooks';

import { FormContext } from '../../context';
Expand All @@ -15,7 +15,8 @@ import Label from '../Label';
import SkipLink from './parts/SkipLink';

import { sanitizeMultiSelectValue } from '../util/sanitizerUtil';
import { createEmptyOptions } from '../util/valuesUtil';

import { createEmptyOptions } from '../util/optionsUtil';

import {
formFieldClasses,
Expand Down Expand Up @@ -47,35 +48,30 @@ export default function Taglist(props) {
const { formId } = useContext(FormContext);
const errorMessageId = errors.length === 0 ? undefined : `${prefixId(id, formId)}-error-message`;
const [ filter, setFilter ] = useState('');
const [ filteredOptions, setFilteredOptions ] = useState([]);
const [ isDropdownExpanded, setIsDropdownExpanded ] = useState(false);
const [ hasOptionsLeft, setHasOptionsLeft ] = useState(true);
const [ isEscapeClosed, setIsEscapeClose ] = useState(false);
const focusScopeRef = useRef();
const inputRef = useRef();
const eventBus = useService('eventBus');

const {
state: loadState,
values: options
} = useValuesAsync(field);
loadState,
options
} = useOptionsAsync(field);

// We cache a map of option values to their index so that we don't need to search the whole options array every time to correlate the label
const valueToOptionMap = useMemo(() => Object.assign({}, ...options.map((o, x) => ({ [o.value]: options[x] }))), [ options ]);

const hasOptionsLeft = useMemo(() => options.length > values.length, [ options.length, values.length ]);

// Usage of stringify is necessary here because we want this effect to only trigger when there is a value change to the array
useEffect(() => {
if (loadState === LOAD_STATES.LOADED) {
setFilteredOptions(options.filter((o) => o.label && o.value && (o.label.toLowerCase().includes(filter.toLowerCase())) && !values.includes(o.value)));
}
else {
setFilteredOptions([]);
const filteredOptions = useMemo(() => {
if (loadState !== LOAD_STATES.LOADED) {
return [];
}
}, [ filter, JSON.stringify(values), options, loadState ]);
return options.filter((o) => o.label && o.value && (o.label.toLowerCase().includes(filter.toLowerCase())) && !values.includes(o.value));
}, [ filter, options, JSON.stringify(values), loadState ]);

useEffect(() => {
setHasOptionsLeft(options.length > values.length);
}, [ options.length, values.length ]);

const selectValue = (value) => {
if (filter) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'preact/hooks';
import useValuesAsync, { LOAD_STATES } from '../../../hooks/useValuesAsync';
import useOptionsAsync, { LOAD_STATES } from '../../../hooks/useOptionsAsync';
import { useService } from '../../../hooks';

import { FormContext } from '../../../context';
Expand Down Expand Up @@ -37,9 +37,9 @@ export default function SearchableSelect(props) {
const eventBus = useService('eventBus');

const {
state: loadState,
values: options
} = useValuesAsync(field);
loadState,
options
} = useOptionsAsync(field);

// We cache a map of option values to their index so that we don't need to search the whole options array every time to correlate the label
const valueToOptionMap = useMemo(() => Object.assign({}, ...options.map((o, x) => ({ [o.value]: options[x] }))), [ options ]);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useCallback, useContext, useMemo, useRef, useState } from 'preact/hooks';
import useValuesAsync, { LOAD_STATES } from '../../../hooks/useValuesAsync';
import useOptionsAsync, { LOAD_STATES } from '../../../hooks/useOptionsAsync';

import { FormContext } from '../../../context';

Expand Down Expand Up @@ -34,9 +34,9 @@ export default function SimpleSelect(props) {
const inputRef = useRef();

const {
state: loadState,
values: options
} = useValuesAsync(field);
loadState,
options
} = useOptionsAsync(field);

// We cache a map of option values to their index so that we don't need to search the whole options array every time to correlate the label
const valueToOptionMap = useMemo(() => Object.assign({}, ...options.map((o, x) => ({ [o.value]: options[x] }))), [ options ]);
Expand Down
69 changes: 69 additions & 0 deletions packages/form-js-viewer/src/render/components/util/optionsUtil.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { get } from 'min-dash';

// parses the options data from the provided form field and form data
export function getOptionsData(formField, formData) {
const { valuesKey: optionsKey, values: staticOptions } = formField;
return optionsKey ? get(formData, [ optionsKey ]) : staticOptions;
}

// transforms the provided options into a normalized format, trimming invalid options
export function normalizeOptionsData(optionsData) {
return optionsData.filter(_isOptionSomething).map(v => _normalizeOptionsData(v)).filter(v => v);
}

function _normalizeOptionsData(optionData) {

if (_isAllowedOption(optionData)) {

// if a primitive is provided, use it as label and value
return { value: optionData, label: `${ optionData }` };
}

if (typeof (optionData) === 'object') {
if (!optionData.label && _isAllowedOption(optionData.value)) {

// if no label is provided, use the value as label
return { value: optionData.value, label: `${ optionData.value }` };
}

if (_isOptionSomething(optionData.value) && _isAllowedOption(optionData.label)) {

// if both value and label are provided, use them as is, in this scenario, the value may also be an object
return optionData;
}
}

return null;
}

function _isAllowedOption(option) {
return _isReadableType(option) && _isOptionSomething(option);
}

function _isReadableType(option) {
return [ 'number', 'string', 'boolean' ].includes(typeof(option));
}

function _isOptionSomething(option) {
return option || option === 0 || option === false;
}

export function createEmptyOptions(options = {}) {

const defaults = {};

// provide default options if valuesKey and valuesExpression are not set
if (!options.valuesKey && !options.valuesExpression) {
defaults.values = [
{
label: 'Value',
value: 'value'
}
];
}

return {
...defaults,
...options
};
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { DATETIME_SUBTYPES } from '../../../util/constants/DatetimeConstants';
import { isDateInputInformationMatching, isDateTimeInputInformationSufficient, isInvalidDateString, parseIsoTime } from './dateTimeUtil';
import { getValuesData, normalizeValuesData } from './valuesUtil';
import { getOptionsData, normalizeOptionsData } from './optionsUtil';

export function sanitizeDateTimePickerValue(options) {
const {
Expand All @@ -27,7 +27,7 @@ export function sanitizeSingleSelectValue(options) {
} = options;

try {
const validValues = normalizeValuesData(getValuesData(formField, data)).map(v => v.value);
const validValues = normalizeOptionsData(getOptionsData(formField, data)).map(v => v.value);
return validValues.includes(value) ? value : null;
} catch (error) {

Expand All @@ -45,7 +45,7 @@ export function sanitizeMultiSelectValue(options) {
} = options;

try {
const validValues = normalizeValuesData(getValuesData(formField, data)).map(v => v.value);
const validValues = normalizeOptionsData(getOptionsData(formField, data)).map(v => v.value);
return value.filter(v => validValues.includes(v));
} catch (error) {

Expand Down
69 changes: 0 additions & 69 deletions packages/form-js-viewer/src/render/components/util/valuesUtil.js

This file was deleted.

Loading

0 comments on commit c031664

Please sign in to comment.