Skip to content

Commit

Permalink
move collect method selection to separate component and simplify its …
Browse files Browse the repository at this point in the history
…looks
  • Loading branch information
magicznyleszek committed Aug 14, 2024
1 parent f088b17 commit 8d96fc4
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 59 deletions.
3 changes: 3 additions & 0 deletions jsapp/js/components/common/koboDropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ interface KoboDropdownProps {
'data-cy'?: string;
/** Alternative way of getting the opened status of the menu. */
onMenuVisibilityChange?: (isOpened: boolean) => void;
/** Additional class names. */
className?: string;
}

interface KoboDropdownState {
Expand Down Expand Up @@ -244,6 +246,7 @@ export default class KoboDropdown extends React.Component<
{...additionalWrapperAttributes}
role='combobox'
aria-required={this.props.isRequired}
className={this.props.className}
>
<bem.KoboDropdown__trigger
onClick={this.onTriggerClick.bind(this)}
Expand Down
5 changes: 3 additions & 2 deletions jsapp/js/components/common/koboSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export type KoboSelectType = 'blue' | 'gray' | 'outline';

export interface KoboSelectOption {
icon?: IconName;
label: string;
label: React.ReactNode;
/** Needs to be unique! */
value: string;
}
Expand Down Expand Up @@ -81,6 +81,7 @@ interface KoboSelectProps {
'data-cy'?: string;
placeholder?: string;
error?: string;
className?: string;
}

interface KoboSelectState {
Expand Down Expand Up @@ -341,7 +342,7 @@ class KoboSelect extends React.Component<KoboSelectProps, KoboSelectState> {
}

return (
<bem.KoboSelect m={modifiers}>
<bem.KoboSelect m={modifiers} className={this.props.className}>
{this.props.label &&
<bem.KoboSelect__label htmlFor={this.props.name}>
{this.props.label}{' '}
Expand Down
36 changes: 8 additions & 28 deletions jsapp/js/components/formLanding.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import sessionStore from 'js/stores/session';
import PopoverMenu from 'js/popoverMenu';
import LoadingSpinner from 'js/components/common/loadingSpinner';
import InlineMessage from 'js/components/common/inlineMessage';
import CollectMethodSelector from 'js/project/collectMethodSelector.component';
import mixins from '../mixins';
import {actions} from '../actions';
import DocumentTitle from 'react-document-title';
Expand Down Expand Up @@ -376,32 +377,10 @@ class FormLanding extends React.Component {
<bem.FormView__cell m='box'>
<bem.FormView__cell m={['columns', 'padding', 'collect-header']}>
<bem.FormView__cell>
<PopoverMenu
type='collectData-menu'
triggerLabel={(
<Button
size='m'
type='primary'
label={COLLECTION_METHODS[chosenMethod].label}
endIcon='angle-down'
isFullWidth
/>
)}
>
{deployment__links_list.map((c) => {
return (
<bem.PopoverMenu__link
m={['collect-row']}
key={`c-${c.key}`}
data-method={c.key}
onClick={this.setCollectMethod}
>
<div className='collect-data-label'>{c.label}</div>
<div className='collect-data-desc'>{c.desc}</div>
</bem.PopoverMenu__link>
);
})}
</PopoverMenu>
<CollectMethodSelector
onChange={(newMethod) => {this.setCollectMethod(newMethod);}}
selectedMethod={chosenMethod}
/>
</bem.FormView__cell>

<bem.FormView__cell className='collect-header-actions'>
Expand Down Expand Up @@ -553,9 +532,10 @@ class FormLanding extends React.Component {
);
}

setCollectMethod(evt) {
this.setState({selectedCollectMethod: evt.currentTarget.dataset.method});
setCollectMethod(newMethod) {
this.setState({selectedCollectMethod: newMethod});
}

goToProjectsList() {
this.props.router.navigate(ROUTES.FORMS);
}
Expand Down
66 changes: 37 additions & 29 deletions jsapp/js/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -487,55 +487,63 @@ export const MAX_DISPLAYED_STRING_LENGTH = Object.freeze({
connect_projects: 30,
});

export const COLLECTION_METHODS = Object.freeze({
export enum CollectionMethodName {
offline_url = 'offline_url',
url = 'url',
single_url = 'single_url',
single_once_url = 'single_once_url',
iframe_url = 'iframe_url',
preview_url = 'preview_url',
android = 'android',
}

interface CollectionMethodDefinition {
id: CollectionMethodName;
label: string;
desc: string;
/** This is being used with android application Kobo Collect option */
url?: string;
}

type CollectionMethods = {
[P in CollectionMethodName]: CollectionMethodDefinition;
};

export const COLLECTION_METHODS: CollectionMethods = Object.freeze({
offline_url: {
id: 'offline_url',
id: CollectionMethodName.offline_url,
label: t('Online-Offline (multiple submission)'),
desc: t(
'This allows online and offline submissions and is the best option for collecting data in the field.'
),
desc: t('This allows online and offline submissions and is the best option for collecting data in the field.'),
},
url: {
id: 'url',
id: CollectionMethodName.url,
label: t('Online-Only (multiple submissions)'),
desc: t(
'This is the best option when entering many records at once on a computer, e.g. for transcribing paper records.'
),
desc: t('This is the best option when entering many records at once on a computer, e.g. for transcribing paper records.'),
},
single_url: {
id: 'single_url',
id: CollectionMethodName.single_url,
label: t('Online-Only (single submission)'),
desc: t(
'This allows a single submission, and can be paired with the "return_url" parameter to redirect the user to a URL of your choice after the form has been submitted.'
),
desc: t('This allows a single submission, and can be paired with the "return_url" parameter to redirect the user to a URL of your choice after the form has been submitted.'),
},
single_once_url: {
id: 'single_once_url',
id: CollectionMethodName.single_once_url,
label: t('Online-only (once per respondent)'),
desc: t(
'This allows your web form to only be submitted once per user, using basic protection to prevent the same user (on the same browser & device) from submitting more than once.'
),
desc: t('This allows your web form to only be submitted once per user, using basic protection to prevent the same user (on the same browser & device) from submitting more than once.'),
},
iframe_url: {
id: 'iframe_url',
id: CollectionMethodName.iframe_url,
label: t('Embeddable web form code'),
desc: t(
'Use this html5 code snippet to integrate your form on your own website using smaller margins.'
),
desc: t('Use this html5 code snippet to integrate your form on your own website using smaller margins.'),
},
preview_url: {
id: 'preview_url',
id: CollectionMethodName.preview_url,
label: t('View only'),
desc: t(
'Use this version for testing, getting feedback. Does not allow submitting data.'
),
desc: t('Use this version for testing, getting feedback. Does not allow submitting data.'),
},
android: {
id: 'android',
id: CollectionMethodName.android,
label: t('Android application'),
desc: t(
'Use this option to collect data in the field with your Android device.'
),
desc: t('Use this option to collect data in the field with your Android device.'),
url: 'https://play.google.com/store/apps/details?id=org.koboc.collect.android&hl=en',
},
});
Expand Down
47 changes: 47 additions & 0 deletions jsapp/js/project/collectMethodSelector.component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Libraries
import React from 'react';

// Partial components
import KoboSelect from 'js/components/common/koboSelect';

// Constants
import {CollectionMethodName, COLLECTION_METHODS} from 'js/constants';

// Styles
import styles from './collectMethodSelector.module.scss';

interface CollectMethodSelectorProps {
onChange: (method: CollectionMethodName) => void;
selectedMethod: CollectionMethodName;
}

export default function CollectMethodSelector(props: CollectMethodSelectorProps) {
const methodsList: Array<{
value: CollectionMethodName;
label: string;
}> = [];
for (const [, methodDef] of Object.entries(COLLECTION_METHODS)) {
methodsList.push({
value: methodDef.id,
label: methodDef.label,
});
}

return (
<KoboSelect
name='collect-method-selector'
type='outline'
size='m'
placement={'down-left'}
isClearable={false}
options={methodsList}
selectedOption={props.selectedMethod}
onChange={(newMethod) => {
if (newMethod !== null) {
props.onChange(newMethod as CollectionMethodName);
}
}}
className={styles.collectMethodSelector}
/>
);
}
3 changes: 3 additions & 0 deletions jsapp/js/project/collectMethodSelector.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.collectMethodSelector {
min-width: 300px;
}

0 comments on commit 8d96fc4

Please sign in to comment.