Skip to content

Commit

Permalink
Support repeatable group questions for row and col item controls
Browse files Browse the repository at this point in the history
  • Loading branch information
vesnushka committed Aug 28, 2023
1 parent 2e837b7 commit e03eaee
Show file tree
Hide file tree
Showing 22 changed files with 576 additions and 372 deletions.
Original file line number Diff line number Diff line change
@@ -1,64 +1,56 @@
@import 'src/styles/colors';
@import 'src/styles/fonts';

.groupWrapper {
background-color: #77ee77;
}

.groupLabel {
@include base-text-12-bold;
}

.questLabel {
@include base-text-12-bold;
}

.repeatsGroupItemsContainer {
margin-bottom: 10px;
}

.repeatsGroupItemTitle {
@include base-text-10-bold;
color: lighten($base-dark-color, 60%);
}

.repeatsGroupItemTitle {
}

.repeatsGroupItemHeader {
.form {
display: flex;
flex-direction: row;
justify-content: space-between;
}
flex-direction: column;
gap: 12px;
padding-right: 24px;

.repeatsGroupRemoveItemButton {
text-align: right;
input {
width: 100%;
box-sizing: border-box;
}
}

.repeatsGroupRemoveItemButtonTitle {
@include base-text-10-bold;
color: $danger-color;
.field {
width: 100%;
display: flex;
flex-direction: column;
gap: 4px;
}

.repeatsGroupRemoveItemButtonTitle:hover {
color: darken($danger-color, 20%);
.labelWrapper {
display: flex;
flex-direction: column;
align-items: flex-start;
}

.repeatsGroupItemBody {
}
.label {
font-weight: 600;
font-size: 12px;
line-height: 14px;
margin: 0;
cursor: pointer;

.repeatsGroupAddItemButton {
margin-bottom: 20px;
&._readOnly {
cursor: default;
}
}

.repeatsGroupAddItemButtonTitle {
@include base-text-10-bold;
color: $primary-color;
.helpText {
font-size: 12px;
line-height: 18px;
margin: 0;
}

.repeatsGroupAddItemButtonTitle:hover {
color: darken($primary-color, 20%);
.display {
width: 100%;
}

.questLabel {
.displayLabel {
font-weight: 600;
font-size: 12px;
line-height: 14px;
margin: 0;
}
Original file line number Diff line number Diff line change
@@ -1,24 +1,15 @@
@import "src/styles/colors";
@import 'src/styles/colors';
@import 'src/styles/fonts';

.wrapper {
margin-top: 10px;
.group {
display: flex;
flex-direction: column;
}

.groupLabel {
@include base-text-12-bold;
}

.select {
@include base-text-12-normal;
font-family: Poppins, serif;
padding: 6px 5px 6px;
border: none;
border-bottom: 2px solid $base-surface-color;
}

.select:focus {
border-bottom: 2px solid $primary-color;
flex-direction: row;
align-items: center;
gap: 4px;
cursor: pointer;

input.checkbox {
width: auto;
margin: 0;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,28 @@ import { Field } from 'react-final-form';
import { QuestionItemProps, useQuestionnaireResponseFormContext } from 'sdc-qrf/src';

import s from './BooleanField.module.scss';
import { QuestionLabel } from '../label';

export function QuestionBoolean({ parentPath, questionItem }: QuestionItemProps) {
const qrfContext = useQuestionnaireResponseFormContext();
const { linkId, text, readOnly, hidden } = questionItem;
const { linkId, readOnly, hidden } = questionItem;
const fieldPath = [...parentPath, linkId, 0, 'value', 'boolean'];
const fieldName = fieldPath.join('.');

return (
<Field name={fieldName}>
{({ input }) => (
<label className={s.groupLabel}>
{text}
<input className={s.checkbox} disabled={qrfContext.readOnly || readOnly || hidden} type="checkbox" checked={input.value} onChange={input.onChange} />
</label>
<div className={s.group}>
<input
className={s.checkbox}
disabled={qrfContext.readOnly || readOnly || hidden}
type="checkbox"
checked={input.value}
onChange={input.onChange}
id={fieldName}
/>
<QuestionLabel questionItem={questionItem} htmlFor={fieldName} />
</div>
)}
</Field>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { QuestionItemProps, useQuestionnaireResponseFormContext } from "sdc-qrf/src";
import { getAnswerCode, getAnswerDisplay } from "web/src/utils/questionnaire";
import { QuestionItemProps, useQuestionnaireResponseFormContext } from 'sdc-qrf/src';
import { getAnswerCode, getAnswerDisplay } from 'web/src/utils/questionnaire';

import { QuestionnaireItemAnswerOption } from "shared/src/contrib/aidbox";
import { QuestionnaireItemAnswerOption } from 'shared/src/contrib/aidbox';


import { useAnswerChoice } from "./hook";
import { AsyncSelectField } from "./select";
import { useAnswerChoice } from './hook';
import { AsyncSelectField } from './select';
import { QuestionField } from '../field';
import { QuestionLabel } from '../label';

export function QuestionChoice(props: QuestionItemProps) {
const { questionItem } = props;
Expand All @@ -17,20 +18,29 @@ export function QuestionChoice(props: QuestionItemProps) {
return null;
}

const fieldProps = { validate };

return (
<AsyncSelectField<QuestionnaireItemAnswerOption>
key={`answer-choice-${deps.join('-')}`}
data-testid={`choice-${linkId}`}
name={fieldName}
label={text}
loadOptions={loadOptions}
isMulti={!!repeats}
getOptionLabel={(option) => getAnswerDisplay(option.value)}
getOptionValue={(option) => getAnswerCode(option.value)}
fieldProps={{
validate,
<QuestionField name={fieldName} {...fieldProps}>
{({ input }) => {
return (
<>
<QuestionLabel questionItem={questionItem} htmlFor={fieldName} />
<AsyncSelectField<QuestionnaireItemAnswerOption>
key={`answer-choice-${deps.join('-')}`}
data-testid={`choice-${linkId}`}
id={fieldName}
input={input}
label={text}
loadOptions={loadOptions}
isMulti={!!repeats}
getOptionLabel={(option) => getAnswerDisplay(option.value)}
getOptionValue={(option) => getAnswerCode(option.value)}
readOnly={qrfContext.readOnly || readOnly}
/>
</>
);
}}
readOnly={qrfContext.readOnly || readOnly}
/>
</QuestionField>
);
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import _ from 'lodash';
import { Field } from 'react-final-form';
// eslint-disable-next-line import/named
import { FieldInputProps } from 'react-final-form';
import { ActionMeta, PropsValue } from 'react-select';
import { AsyncSelect } from 'web/src/components/Select';

interface Props<T> {
name: string;
label?: string;
id?: string;
placeholder?: string;
helpText?: string;
fieldProps?: any;
input: FieldInputProps<any, HTMLElement>;
formItemProps?: any;
loadOptions: (searchText: string) => Promise<T[]>;
readOnly?: boolean;
Expand All @@ -22,9 +23,9 @@ interface Props<T> {
export function AsyncSelectField<T>(props: Props<T>) {
const {
readOnly,
name,
input,
id,
placeholder = 'Select...',
fieldProps,
getOptionLabel,
getOptionValue,
onChange,
Expand All @@ -40,30 +41,25 @@ export function AsyncSelectField<T>(props: Props<T>) {
);

return (
<Field name={name} {...fieldProps}>
{({ input }) => {
return (
<AsyncSelect
defaultOptions
isDisabled={readOnly}
loadOptions={debouncedLoadOptions}
placeholder={placeholder}
getOptionLabel={getOptionLabel}
getOptionValue={getOptionValue}
{...input}
onChange={(value, action) => {
input.onChange(value ?? undefined);
<AsyncSelect
defaultOptions
isDisabled={readOnly}
loadOptions={debouncedLoadOptions}
placeholder={placeholder}
getOptionLabel={getOptionLabel}
getOptionValue={getOptionValue}
id={id}
{...input}
onChange={(value, action) => {
input.onChange(value ?? undefined);

if (onChange) {
onChange(value, action);
}
}}
isMulti={isMulti}
styles={{ menuPortal: (base) => ({ ...base, zIndex: 9999 }) }}
menuPortalTarget={document.body}
/>
);
if (onChange) {
onChange(value, action);
}
}}
</Field>
isMulti={isMulti}
styles={{ menuPortal: (base) => ({ ...base, zIndex: 9999 }) }}
menuPortalTarget={document.body}
/>
);
}
Original file line number Diff line number Diff line change
@@ -1,35 +1,38 @@
import moment from 'moment';
import { Field } from 'react-final-form';
import { QuestionItemProps, useQuestionnaireResponseFormContext } from 'sdc-qrf/src';

import { FHIRDateTimeFormat } from 'fhir-react/lib/utils/date';

import { QuestionField } from './field';
import { QuestionLabel } from './label';

export function QuestionDate({ parentPath, questionItem }: QuestionItemProps) {
const qrfContext = useQuestionnaireResponseFormContext();
const { linkId, text, readOnly, hidden } = questionItem;
const { linkId, readOnly, hidden } = questionItem;
const fieldPath = [...parentPath, linkId, 0, 'value', 'date'];
const fieldName = fieldPath.join('.');

return (
<Field name={fieldName}>
<QuestionField name={fieldName}>
{({ input, meta }) => (
<div>
<label>{text}</label>
<>
<QuestionLabel questionItem={questionItem} htmlFor={fieldName} />
<input
type="date"
id={fieldName}
{...input}
readOnly={qrfContext.readOnly || readOnly || hidden}
/>
{meta.touched && meta.error && <span>{meta.error}</span>}
</div>
</>
)}
</Field>
</QuestionField>
);
}

export function QuestionDateTime({ parentPath, questionItem }: QuestionItemProps) {
const qrfContext = useQuestionnaireResponseFormContext();
const { linkId, text, readOnly, hidden } = questionItem;
const { linkId, readOnly, hidden } = questionItem;
const fieldPath = [...parentPath, linkId, 0, 'value', 'dateTime'];
const fieldName = fieldPath.join('.');
const parseValue = (value: string) => {
Expand All @@ -41,18 +44,19 @@ export function QuestionDateTime({ parentPath, questionItem }: QuestionItemProps
: '';
};
return (
<Field name={fieldName} parse={parseValue} format={formatValue}>
<QuestionField name={fieldName} parse={parseValue} format={formatValue}>
{({ input, meta }) => (
<div>
<label>{text}</label>
<>
<QuestionLabel questionItem={questionItem} htmlFor={fieldName} />
<input
type="datetime-local"
id={fieldName}
{...input}
readOnly={qrfContext.readOnly || readOnly || hidden}
/>
{meta.touched && meta.error && <span>{meta.error}</span>}
</div>
</>
)}
</Field>
</QuestionField>
);
}
Loading

0 comments on commit e03eaee

Please sign in to comment.