Skip to content

Commit

Permalink
Merge pull request #758 from complexdatacollective/feature/min-max-na…
Browse files Browse the repository at this point in the history
…megenerators

Feature/min max namegenerators
  • Loading branch information
jthrilly committed Mar 22, 2022
2 parents 76079f9 + 6128628 commit 50a64a1
Show file tree
Hide file tree
Showing 14 changed files with 404 additions and 36 deletions.
2 changes: 1 addition & 1 deletion network-canvas
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
@@ -1,6 +1,6 @@
{
"name": "network-canvas-architect",
"version": "6.3.1",
"version": "6.4.0",
"productName": "Network Canvas Architect",
"description": "A tool for building Network Canvas interviews.",
"author": "Complex Data Collective <info@networkcanvas.com>",
Expand Down
2 changes: 1 addition & 1 deletion public/package.json
@@ -1,6 +1,6 @@
{
"name": "network-canvas-architect",
"version": "6.3.1",
"version": "6.4.0",
"productName": "Network Canvas Architect",
"description": "A tool for building Network Canvas interviews.",
"author": "Complex Data Collective",
Expand Down
2 changes: 1 addition & 1 deletion src/__mocks__/config.js
@@ -1,4 +1,4 @@
/* eslint-env jest */
/* eslint-disable import/prefer-default-export */

export const APP_SCHEMA_VERSION = 2;
export const APP_SCHEMA_VERSION = 7;
4 changes: 2 additions & 2 deletions src/components/Form/Fields/VariablePicker/VariablePill.js
Expand Up @@ -12,7 +12,7 @@ import { get } from 'lodash';
import cx from 'classnames';
import { useDispatch, useSelector } from 'react-redux';
import { actionCreators as codebookActions } from '@modules/protocol/codebook';
import { requiredWithMessage, uniqueByList, allowedVariableName } from '@app/utils/validations';
import { required as requiredValidation, uniqueByList, allowedVariableName } from '@app/utils/validations';
import TextInput from '@codaco/ui/lib/components/Fields/Text';
import { makeGetVariableFromUUID, getVariablesForSubject } from '../../../../selectors/codebook';
import { getColorForType, getIconForType } from '../../../../config/variables';
Expand Down Expand Up @@ -108,7 +108,7 @@ const EditableVariablePill = ({ uuid }) => {
const { target: { value } } = event;
setNewName(value);

const required = requiredWithMessage('You must enter a variable name', true)(value);
const required = requiredValidation(true, 'You must enter a variable name', true)(value);
const unique = uniqueByList(existingVariableNames)(value);
const allowed = allowedVariableName()(value);

Expand Down
4 changes: 2 additions & 2 deletions src/components/Form/ValidatedField.js
Expand Up @@ -7,12 +7,12 @@ const ValidatedField = ({
validation,
...rest
}) => {
const validate = useValidate(validation);
const validations = useValidate(validation);

return (
<Field
{...rest} // eslint-disable-line react/jsx-props-no-spreading
validate={validate}
validate={validations}
/>
);
};
Expand Down
4 changes: 4 additions & 0 deletions src/components/StageEditor/Interfaces.js
Expand Up @@ -18,6 +18,7 @@ import {
NarrativePresets,
NodePanels,
NodeType,
MinMaxAlterLimits,
OrdinalBinPrompts,
QuickAdd,
SearchOptionsForExternalData,
Expand Down Expand Up @@ -101,6 +102,7 @@ const NameGenerator = {
NameGeneratorPrompts,
NodePanels,
SkipLogic,
MinMaxAlterLimits,
InterviewScript,
],
documentation: 'https://documentation.networkcanvas.com/interface-documentation/name-generator-using-forms/',
Expand All @@ -116,6 +118,7 @@ const NameGeneratorRoster = {
SearchOptionsForExternalData,
NameGeneratorRosterPrompts,
SkipLogic,
MinMaxAlterLimits,
InterviewScript,
],
documentation: 'https://documentation.networkcanvas.com/interface-documentation/name-generator-roster/',
Expand All @@ -129,6 +132,7 @@ const NameGeneratorQuickAdd = {
NameGeneratorPrompts,
NodePanels,
SkipLogic,
MinMaxAlterLimits,
InterviewScript,
],
name: 'Name Generator (quick add)',
Expand Down
Expand Up @@ -69,7 +69,7 @@ export const testPromptFields = (PromptFieldsComponent, name = '') => {
mockStore = getStore(initialState);
});

// TODO This seems to test the wrong part of codebook
// FIXME This seems to test the wrong part of codebook

describe(name, () => {
describe('PromptFields', () => {
Expand Down
144 changes: 144 additions & 0 deletions src/components/sections/MinMaxAlterLimits.js
@@ -0,0 +1,144 @@
import React, { useCallback, useMemo } from 'react';
import { get, isNull, isUndefined } from 'lodash';
import { Section } from '@components/EditorLayout';
import {
change, FormSection, formValueSelector,
} from 'redux-form';
import { actionCreators as dialogActions } from '@modules/dialogs';
import { useDispatch, useSelector } from 'react-redux';
import { Number } from '@codaco/ui/lib/components/Fields';
import { ValidatedField } from '../Form';
import IssueAnchor from '../IssueAnchor';
import Tip from '../Tip';

const maxValidation = (value, allValues) => {
const minValue = get(allValues, 'behaviours.minNodes', null);

if (isUndefined(minValue) || isNull(minValue) || !value) {
return undefined;
}

return (
value >= minValue ? undefined : 'Maximum number of alters must be greater than or equal to the minimum number'
);
};

const minValidation = (value, allValues) => {
const maxValue = get(allValues, 'behaviours.maxNodes');

if (isUndefined(maxValue) || isNull(maxValue) || !value) {
return undefined;
}

return (
value <= maxValue ? undefined : 'Minimum number of alters must be less than or equal to the maximum number'
);
};

const MinMaxAlterLimits = () => {
const getFormValue = formValueSelector('edit-stage');
const currentMinValue = useSelector((state) => getFormValue(state, 'behaviours.minNodes'));
const currentMaxValue = useSelector((state) => getFormValue(state, 'behaviours.maxNodes'));
const hasMultiplePrompts = useSelector((state) => {
const prompts = getFormValue(state, 'prompts');
return !!prompts && prompts.length > 1;
});

const dispatch = useDispatch();
const openDialog = useCallback(
(dialog) => dispatch(dialogActions.openDialog(dialog)),
[dispatch],
);

const handleToggleChange = useCallback(
async (newState) => {
if ((isUndefined(currentMinValue) && isUndefined(currentMaxValue)) || newState === true) {
return true;
}

const confirm = await openDialog({
type: 'Warning',
title: 'This will clear your values',
message: 'This will clear the minimum and maximum alter values. Do you want to continue?',
confirmLabel: 'Clear values',
});

if (confirm) {
dispatch(change('edit-stage', 'behaviours.minNodes', null));
dispatch(change('edit-stage', 'behaviours.maxNodes', null));
return true;
}

return false;
},
[dispatch, openDialog, currentMinValue, currentMaxValue],
);

const startExpanded = useMemo(() => !isUndefined(currentMinValue)
|| !isUndefined(currentMaxValue), []);

return (
<Section
title="Set minimum or maximum alter numbers"
summary={(
<>
<p>
This feature allows you to specify a minimum or maximum number of alters that can
be named on this stage. Please note that these limits apply to the
{' '}
<strong>
stage as a whole
</strong>
, regardless of the number of
prompts you have created.
</p>
</>
)}
toggleable
startExpanded={startExpanded}
handleToggleChange={handleToggleChange}
>
{ hasMultiplePrompts && (
<Tip type="warning">
<p>
You have multiple prompts configured on this stage. Remember that the limits you
specify here apply to the
{' '}
<strong>
stage as a whole
</strong>
. Consider splitting your prompts up into multiple stages, or ensure you take extra
care in the phrasing of your prompts so that you communicate the alter limits
to your participants.
</p>
</Tip>
)}
<FormSection name="behaviours">
<IssueAnchor fieldName="behaviours.minNodes" description="Minimum alters" />
<ValidatedField
name="minNodes"
label="Minimum Number of Alters. (0 = no minimum)"
component={Number}
placeholder="0"
validation={{
lessThanMax: minValidation,
positiveNumber: true,
}}
/>
<IssueAnchor fieldName="behaviours.maxNodes" description="Maximum alters" />
<ValidatedField
name="maxNodes"
label="Maximum Number of Alters. _(Leave empty for no maximum)_"
component={Number}
placeholder="Infinity"
validation={{
greaterThanMin: maxValidation,
minValue: 1,
}}
/>
</FormSection>
</Section>
);
};

export default MinMaxAlterLimits;
1 change: 1 addition & 0 deletions src/components/sections/index.js
Expand Up @@ -16,6 +16,7 @@ export { default as NarrativeBehaviours } from './NarrativeBehaviours';
export { default as NarrativePresets } from './NarrativePresets';
export { default as NodePanels } from './NodePanels';
export { default as NodeType } from './NodeType';
export { default as MinMaxAlterLimits } from './MinMaxAlterLimits';
export { default as OrdinalBinPrompts } from './OrdinalBinPrompts';
export { default as QuickAdd } from './QuickAdd';
export { default as SearchOptionsForExternalData } from './SearchOptionsForExternalData';
Expand Down
2 changes: 1 addition & 1 deletion src/protocol-validation

0 comments on commit 50a64a1

Please sign in to comment.