Skip to content

Commit

Permalink
M2-6326: Removed auto-default value for TimeRange item (#777)
Browse files Browse the repository at this point in the history
* M2-6326: Removed auto-default value for TimeRange item

* M2-6326: Refactor, added isValidAnswer function to AnswerValidator util, added tests
  • Loading branch information
vmkhitaryanscn authored May 21, 2024
1 parent 109889d commit 09c009d
Show file tree
Hide file tree
Showing 9 changed files with 215 additions and 34 deletions.
4 changes: 2 additions & 2 deletions src/features/pass-survey/lib/types/payload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -405,8 +405,8 @@ export type AudioResponse = MediaFile;
export type AudioPlayerResponse = boolean;

export type TimeRangeResponse = {
startTime: HourMinute;
endTime: HourMinute;
startTime: HourMinute | null;
endTime: HourMinute | null;
};

export type RadioResponse = RadioOption;
Expand Down
23 changes: 22 additions & 1 deletion src/features/pass-survey/model/AnswerValidator.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { Item } from '@app/shared/ui';
import { ConditionalLogicModel } from '@entities/conditional-logic';

import { Answers, PipelineItem, RadioResponse } from '../lib';
import {
Answers,
PipelineItem,
RadioResponse,
TimeRangeResponse,
} from '../lib';

type AnswerValidatorArgs = {
items: PipelineItem[];
Expand All @@ -19,6 +24,7 @@ export interface IAnswerValidator {
isLessThen(value: number): boolean;
includesOption(optionValue: string): boolean;
notIncludesOption(optionValue: string): boolean;
isValidAnswer(): boolean;
}

function AnswerValidator(params?: AnswerValidatorArgs): IAnswerValidator {
Expand Down Expand Up @@ -89,6 +95,21 @@ function AnswerValidator(params?: AnswerValidatorArgs): IAnswerValidator {

return ConditionalLogicModel.doesNotIncludeValue(answer, optionValue);
},
isValidAnswer() {
switch (currentPipelineItem?.type) {
case 'TimeRange': {
const answer = currentAnswer?.answer as TimeRangeResponse;

if (answer) {
return !!answer?.startTime && !!answer?.endTime;
}

return true;
}
default:
return true;
}
},
};
}

Expand Down
9 changes: 6 additions & 3 deletions src/features/pass-survey/model/hooks/useActivityStepper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const ConditionalLogicItems: ActivityItemType[] = [
];

function useActivityStepper(state: ActivityState | undefined) {
const answerValidator = AnswerValidator(state);
const step = state?.step ?? 0;
const items = state?.items ?? [];
const answers = state?.answers ?? {};
Expand All @@ -36,12 +37,16 @@ function useActivityStepper(state: ActivityState | undefined) {

const canSkip =
!!currentPipelineItem?.isSkippable && !hasAnswer && !isSplashStep;

const canMoveNext =
isTutorialStep ||
isMessageStep ||
isAbTestStep ||
currentPipelineItem?.isSkippable ||
(hasAnswer && (!additionalAnswerRequired || hasAdditionalAnswer));
(hasAnswer &&
(!additionalAnswerRequired || hasAdditionalAnswer) &&
answerValidator.isValidAnswer());

const canMoveBack = currentPipelineItem?.isAbleToMoveBack;
const canReset =
currentPipelineItem?.canBeReset && (hasAnswer || hasAdditionalAnswer);
Expand All @@ -53,8 +58,6 @@ function useActivityStepper(state: ActivityState | undefined) {
currentPipelineItem!?.type,
);

const answerValidator = AnswerValidator(state);

function isValid() {
const valid = answerValidator.isCorrect();

Expand Down
133 changes: 132 additions & 1 deletion src/features/pass-survey/model/tests/AnswerValidator.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ConditionType } from '@app/entities/activity';

import { getEmptySliderItem } from './testHelpers';
import { getEmptySliderItem, getTimeRangeItem } from './testHelpers';
import { Answers } from '../../lib';
import AnswerValidator, { IAnswerValidator } from '../AnswerValidator';

Expand Down Expand Up @@ -208,4 +208,135 @@ describe('AnswerValidator tests', () => {

expect(result).toEqual(false);
});

it('Should return true when validationOptions is not present', () => {
const sliderItem1 = getEmptySliderItem('mock-slider-name-1');

const items = [sliderItem1];

const answer1 = { answer: '1' };

const answers: Answers = {
'0': answer1,
};

const validator = AnswerValidator({
items: items as any,
answers: answers as any,
step: 1,
});

const result = validator.isCorrect();

expect(result).toEqual(true);
});

it('Should return true if slider item has (any) answer ', () => {
const sliderItem = getEmptySliderItem('mock-slider-name-2');

const items = [sliderItem];

const answer = { answer: '2' };

const answers: Answers = {
'0': answer,
};

const validator = AnswerValidator({
items: items as any,
answers: answers as any,
step: 0,
});

const result = validator.isValidAnswer();

expect(result).toEqual(true);
});

it('Should return true when timeRange answer has both startTime and endTime set ', () => {
const timeRangeItem = getTimeRangeItem();

const items = [timeRangeItem];

const answer = {
answer: {
startTime: {
hours: 1,
minutes: 2,
},
endTime: {
hours: 1,
minutes: 2,
},
},
};

const answers: Answers = {
'0': answer,
};

const validator = AnswerValidator({
items: items as any,
answers: answers as any,
step: 0,
});

const result = validator.isValidAnswer();

expect(result).toEqual(true);
});

it('Should return false when timeRange answer does not have both startTime and endTime set', () => {
const timeRangeItem = getTimeRangeItem();

const items = [timeRangeItem];

const answer = {
answer: {
startTime: {
hours: 1,
minutes: 2,
},
endTime: null,
},
};

const answers: Answers = {
'0': answer,
};

const validator = AnswerValidator({
items: items as any,
answers: answers as any,
step: 0,
});

const result = validator.isValidAnswer();

expect(result).toEqual(false);
});

it('Should return true when timeRange item answer is null', () => {
const timeRangeItem = getTimeRangeItem();

const items = [timeRangeItem];

const answer = {
answer: null,
};

const answers: Answers = {
'0': answer,
};

const validator = AnswerValidator({
items: items as any,
answers: answers as any,
step: 0,
});

const result = validator.isValidAnswer();

expect(result).toEqual(true);
});
});
13 changes: 13 additions & 0 deletions src/features/pass-survey/model/tests/testHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
StackedRadioResponse,
StackedSliderPipelineItem,
TextInputPipelineItem,
TimeRangePipelineItem,
TutorialPipelineItem,
} from '../../lib';

Expand Down Expand Up @@ -531,3 +532,15 @@ export const getStackedSliderItem = (): StackedSliderPipelineItem => {
};
return result;
};

export const getTimeRangeItem = (): TimeRangePipelineItem => {
const result: TimeRangePipelineItem = {
id: 'mock-timerange-id',
name: 'mock-timerange-name',
timer: null,
payload: null,
type: 'TimeRange',
};

return result;
};
4 changes: 2 additions & 2 deletions src/shared/api/services/answerService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,8 @@ export type AbTestAnswerDto = {
};

export type TimeRangeAnswerDto = {
from: { hour: number; minute: number };
to: { hour: number; minute: number };
from: { hour: number; minute: number } | null;
to: { hour: number; minute: number } | null;
};

export type TimeAnswerDto = HourMinute;
Expand Down
21 changes: 12 additions & 9 deletions src/shared/ui/survey/TimeRangeItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,13 @@ import {
getMidnightDateInMs,
getMsFromHours,
getMsFromMinutes,
getNow,
TIME_PICKER_FORMAT_PLACEHOLDER,
} from '@app/shared/lib';
import { YStack, DateTimePicker, AlarmIcon, BedIcon } from '@shared/ui';

type TimeRangeValue = {
endTime: HourMinute;
startTime: HourMinute;
endTime: HourMinute | null;
startTime: HourMinute | null;
};

type Props = {
Expand All @@ -37,26 +36,30 @@ const TimeRangeItem: FC<Props> = ({ value, onChange }) => {
});

const startTimeAsDate = useMemo(
() => (value?.startTime ? transformToDate(value.startTime) : getNow()),
() => (value?.startTime ? transformToDate(value.startTime) : null),
[value],
);

const endTimeAsDate = useMemo(
() => (value?.endTime ? transformToDate(value.endTime) : getNow()),
() => (value?.endTime ? transformToDate(value.endTime) : null),
[value],
);

const onChangeStartTime = (time: Date) =>
const onChangeStartTime = (time: Date) => {
onChange({
endTime: transformToHourMinute(endTimeAsDate),
endTime: endTimeAsDate ? transformToHourMinute(endTimeAsDate) : null,
startTime: transformToHourMinute(time),
});
};

const onChangeEndTime = (time: Date) =>
const onChangeEndTime = (time: Date) => {
onChange({
startTime: transformToHourMinute(startTimeAsDate),
startTime: startTimeAsDate
? transformToHourMinute(startTimeAsDate)
: null,
endTime: transformToHourMinute(time),
});
};

return (
<YStack>
Expand Down
4 changes: 2 additions & 2 deletions src/shared/ui/survey/tests/TimeRangeItem.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ describe('Test TimeRangeItem', () => {

const endDateValue = endDatePicker.props.value;

expect(startDateValue).toBe(mockNowDate);
expect(startDateValue).toBe(null);

expect(endDateValue).toBe(mockNowDate);
expect(endDateValue).toBe(null);
});

it('Should render start/end date pickers if initial value specified', () => {
Expand Down
38 changes: 24 additions & 14 deletions src/widgets/survey/model/mappers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import {
AbLogLineDto,
AbLogPointDto,
AnswerAlertsDto,
TimeRangeAnswerDto,
} from '@shared/api';
import {
HourMinute,
Expand All @@ -58,8 +59,8 @@ import { canItemHaveAnswer } from './operations';
type Answer = PipelineItemAnswer['value'];

type TimeRange = {
endTime: HourMinute;
startTime: HourMinute;
endTime: HourMinute | null;
startTime: HourMinute | null;
};

type StackedRadioAnswerValue = Array<Array<Item>>;
Expand Down Expand Up @@ -214,19 +215,28 @@ function convertToTimeRangeAnswer(answer: Answer): AnswerDto {
const timeRangeItem = answer.answer as TimeRange;
const { startTime, endTime } = timeRangeItem ?? {};

const timeRangeValue = {
value: {
from: null,
to: null,
} as TimeRangeAnswerDto,
};

if (startTime) {
timeRangeValue.value.from = {
hour: startTime.hours,
minute: startTime.minutes,
};
}
if (endTime) {
timeRangeValue.value.to = {
hour: endTime?.hours,
minute: endTime?.minutes,
};
}

return {
...(timeRangeItem && {
value: {
from: {
hour: startTime.hours,
minute: startTime.minutes,
},
to: {
hour: endTime.hours,
minute: endTime.minutes,
},
},
}),
...timeRangeValue,
...(answer.additionalAnswer && {
text: answer.additionalAnswer,
}),
Expand Down

0 comments on commit 09c009d

Please sign in to comment.