Skip to content

Commit 81d9bb1

Browse files
authored
feat: allow to inject errors to during PollComposer field updates (#1527)
Allow to inject errors generated outside the PollComposer state middleware chain so that platform native data (browser event data) can be reflected in platform agnostic manager state.
1 parent bf607d3 commit 81d9bb1

File tree

4 files changed

+83
-6
lines changed

4 files changed

+83
-6
lines changed

src/messageComposer/middleware/pollComposer/state.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ export const pollStateChangeValidators: Partial<
3131
max_votes_allowed: ({ data, value }) => {
3232
if (data.enforce_unique_vote && value)
3333
return { max_votes_allowed: 'Enforce unique vote is enabled' };
34+
const numericMatch = value.match(/^[0-9]+$/);
35+
if (!numericMatch && value) {
36+
return { max_votes_allowed: 'Only numbers are allowed' };
37+
}
3438
if (value?.length > 1 && !value.match(VALID_MAX_VOTES_VALUE_REGEX))
3539
return { max_votes_allowed: 'Type a number from 2 to 10' };
3640
return { max_votes_allowed: undefined };
@@ -225,7 +229,7 @@ export const createPollComposerStateMiddleware = ({
225229
forward,
226230
}: MiddlewareHandlerParams<PollComposerStateChangeMiddlewareValue>) => {
227231
if (!state.targetFields) return forward();
228-
const { previousState } = state;
232+
const { previousState, injectedFieldErrors } = state;
229233
const finalValidators = {
230234
...pollStateChangeValidators,
231235
...defaultPollFieldChangeEventValidators,
@@ -247,7 +251,7 @@ export const createPollComposerStateMiddleware = ({
247251
nextState: {
248252
...previousState,
249253
data: { ...previousState.data, ...newData },
250-
errors: { ...previousState.errors, ...newErrors },
254+
errors: { ...previousState.errors, ...newErrors, ...injectedFieldErrors },
251255
},
252256
});
253257
},
@@ -275,7 +279,11 @@ export const createPollComposerStateMiddleware = ({
275279
nextState: {
276280
...previousState,
277281
data: { ...previousState.data, ...newData },
278-
errors: { ...previousState.errors, ...newErrors },
282+
errors: {
283+
...previousState.errors,
284+
...newErrors,
285+
...state.injectedFieldErrors,
286+
},
279287
},
280288
});
281289
},

src/messageComposer/middleware/pollComposer/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ export type PollComposerStateChangeMiddlewareValue = {
6060
? PollComposerOptionUpdate
6161
: PollComposerState['data'][K];
6262
}>;
63+
injectedFieldErrors?: PollComposerFieldErrors;
6364
};
6465

6566
export type PollComposerStateMiddlewareValue =

src/messageComposer/pollComposer.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@ import { StateStore } from '../store';
77
import { VotingVisibility } from '../types';
88
import { generateUUIDv4 } from '../utils';
99
import type { MessageComposer } from './messageComposer';
10-
import type { PollComposerState, UpdateFieldsData } from './middleware/pollComposer';
10+
import type {
11+
PollComposerFieldErrors,
12+
PollComposerState,
13+
UpdateFieldsData,
14+
} from './middleware/pollComposer';
1115

1216
export type PollComposerOptions = {
1317
composer: MessageComposer;
@@ -102,13 +106,23 @@ export class PollComposer {
102106
this.state.next(this.initialState);
103107
};
104108

105-
updateFields = async (data: UpdateFieldsData) => {
109+
/**
110+
* Updates specified fields and generates relevant errors
111+
* @param data
112+
* @param injectedFieldErrors - errors produced externally that will take precedence over the errors generated in the middleware chaing
113+
*/
114+
// FIXME: change method params to a single object with the next major release
115+
updateFields = async (
116+
data: UpdateFieldsData,
117+
injectedFieldErrors?: PollComposerFieldErrors,
118+
) => {
106119
const { state, status } = await this.stateMiddlewareExecutor.execute({
107120
eventName: 'handleFieldChange',
108121
initialValue: {
109122
nextState: { ...this.state.getLatestValue() },
110123
previousState: { ...this.state.getLatestValue() },
111124
targetFields: data,
125+
injectedFieldErrors,
112126
},
113127
});
114128

test/unit/MessageComposer/middleware/pollComposer/state.test.ts

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,9 @@ describe('PollComposerStateMiddleware', () => {
9696
}),
9797
);
9898

99-
expect(result.state.nextState.errors.max_votes_allowed).toBeDefined();
99+
expect(result.state.nextState.errors.max_votes_allowed).toBe(
100+
'Enforce unique vote is enabled',
101+
);
100102
expect(result.state.nextState.data.max_votes_allowed).toBe('5');
101103
expect(result.status).toBeUndefined;
102104
});
@@ -275,6 +277,32 @@ describe('PollComposerStateMiddleware', () => {
275277
expect(result.state.nextState.errors.options).toEqual({ x: 'failed option X' });
276278
expect(result.status).toBeUndefined;
277279
});
280+
281+
it('should override internally generated errors with injected errors', async () => {
282+
const stateMiddleware = setup();
283+
const result = await stateMiddleware.handlers.handleFieldChange(
284+
setupHandlerParams({
285+
nextState: {
286+
...getInitialState(),
287+
data: { ...getInitialState().data, enforce_unique_vote: false },
288+
},
289+
previousState: {
290+
...getInitialState(),
291+
data: { ...getInitialState().data, enforce_unique_vote: false },
292+
},
293+
targetFields: { max_votes_allowed: '5' }, // Valid value (between 2 and 10)
294+
injectedFieldErrors: {
295+
max_votes_allowed: 'Injected error message',
296+
},
297+
}),
298+
);
299+
300+
expect(result.state.nextState.errors.max_votes_allowed).toBe(
301+
'Injected error message',
302+
);
303+
expect(result.state.nextState.data.max_votes_allowed).toBe('5');
304+
expect(result.status).toBeUndefined;
305+
});
278306
});
279307

280308
describe('handleFieldBlur', () => {
@@ -407,4 +435,30 @@ describe('PollComposerStateMiddleware', () => {
407435
expect(result.status).toBeUndefined;
408436
});
409437
});
438+
439+
it('should override internally generated errors with injected errors', async () => {
440+
const stateMiddleware = setup();
441+
const result = await stateMiddleware.handlers.handleFieldBlur(
442+
setupHandlerParams({
443+
nextState: {
444+
...getInitialState(),
445+
data: { ...getInitialState().data, enforce_unique_vote: false },
446+
},
447+
previousState: {
448+
...getInitialState(),
449+
data: { ...getInitialState().data, enforce_unique_vote: false },
450+
},
451+
targetFields: { max_votes_allowed: '5' }, // Valid value (between 2 and 10)
452+
injectedFieldErrors: {
453+
max_votes_allowed: 'Injected error message',
454+
},
455+
}),
456+
);
457+
458+
expect(result.state.nextState.errors.max_votes_allowed).toBe(
459+
'Injected error message',
460+
);
461+
expect(result.state.nextState.data.max_votes_allowed).toBe('5');
462+
expect(result.status).toBeUndefined;
463+
});
410464
});

0 commit comments

Comments
 (0)