From 9a270cf8f93b7ca12933e2049e78e7b9e9441020 Mon Sep 17 00:00:00 2001 From: Mingze Xiao Date: Wed, 18 Nov 2020 12:12:11 -0800 Subject: [PATCH] feat(drawing): Add client ids --- src/@types/model.ts | 2 + src/drawing/DrawingPathGroup.tsx | 5 +-- src/drawing/DrawingTarget.tsx | 3 +- src/drawing/__mocks__/drawingData.ts | 8 ++++ .../annotations/__tests__/reducer-test.ts | 2 +- src/store/annotations/__tests__/util-test.ts | 41 +++++++++++++++++++ src/store/annotations/reducer.ts | 3 +- src/store/annotations/util.ts | 31 ++++++++++++++ 8 files changed, 88 insertions(+), 7 deletions(-) create mode 100644 src/store/annotations/__tests__/util-test.ts create mode 100644 src/store/annotations/util.ts diff --git a/src/@types/model.ts b/src/@types/model.ts index 9926cd4f2..c72178755 100644 --- a/src/@types/model.ts +++ b/src/@types/model.ts @@ -42,9 +42,11 @@ export interface Page { } export interface Path { + id?: string; points: Array; } export interface PathGroup { + id?: string; paths: Array; stroke: Stroke; } diff --git a/src/drawing/DrawingPathGroup.tsx b/src/drawing/DrawingPathGroup.tsx index 431aa9cc5..29c46dd97 100644 --- a/src/drawing/DrawingPathGroup.tsx +++ b/src/drawing/DrawingPathGroup.tsx @@ -1,5 +1,4 @@ import * as React from 'react'; -import * as uuid from 'uuid'; import classNames from 'classnames'; import { DrawingSVGRef } from './DrawingSVG'; import { isVectorEffectSupported } from './drawingUtil'; @@ -71,10 +70,10 @@ export const DrawingPathGroup = ({ stroke={color} strokeWidth={strokeWidth} > - {paths.map(({ points }) => { + {paths.map(({ id, points }) => { const pathCommands = getPathCommands(points); return ( - + ): J {...shape} /> {pathGroups.map(pathGroup => ( - + ))} ); diff --git a/src/drawing/__mocks__/drawingData.ts b/src/drawing/__mocks__/drawingData.ts index b17c8248a..4faa42e4f 100644 --- a/src/drawing/__mocks__/drawingData.ts +++ b/src/drawing/__mocks__/drawingData.ts @@ -5,8 +5,10 @@ export const annotations = [ location: { type: 'page' as const, value: 1 }, path_groups: [ { + id: '1_1', paths: [ { + id: '1_1_1', points: [ { x: 10, y: 10 }, { x: 11, y: 11 }, @@ -20,8 +22,10 @@ export const annotations = [ }, }, { + id: '1_2', paths: [ { + id: '1_2_1', points: [ { x: 20, y: 20 }, { x: 21, y: 21 }, @@ -44,8 +48,10 @@ export const annotations = [ location: { type: 'page' as const, value: 2 }, path_groups: [ { + id: '2_1', paths: [ { + id: '2_1_1', points: [ { x: 20, y: 20 }, { x: 21, y: 21 }, @@ -59,8 +65,10 @@ export const annotations = [ }, }, { + id: '2_2', paths: [ { + id: '2_2_1', points: [ { x: 40, y: 40 }, { x: 41, y: 41 }, diff --git a/src/store/annotations/__tests__/reducer-test.ts b/src/store/annotations/__tests__/reducer-test.ts index 470dc079e..fbb35f744 100644 --- a/src/store/annotations/__tests__/reducer-test.ts +++ b/src/store/annotations/__tests__/reducer-test.ts @@ -32,7 +32,7 @@ describe('store/annotations/reducer', () => { describe('createAnnotationAction', () => { test('should set state when fulfilled', () => { - const annotation = { id: 'anno_1', type: 'annotation' }; + const annotation = { id: 'anno_1', target: { type: 'region' }, type: 'annotation' }; const payload = { entries: [annotation], limit: 1000, next_marker: null } as APICollection; const newState = reducer(state, fetchAnnotationsAction.fulfilled(payload, 'test', undefined)); diff --git a/src/store/annotations/__tests__/util-test.ts b/src/store/annotations/__tests__/util-test.ts new file mode 100644 index 000000000..20b0fc610 --- /dev/null +++ b/src/store/annotations/__tests__/util-test.ts @@ -0,0 +1,41 @@ +import { addClientIds } from '../util'; +import { Annotation, PathGroup, TargetDrawing } from '../../../@types'; + +describe('store/annotations/util', () => { + describe('addClientIds()', () => { + test('should do nothing if type is not drawing', () => { + const annotation = { + id: 'anno_1', + target: { type: 'region' }, + type: 'annotation', + } as Annotation; + + expect(addClientIds(annotation)).toBe(annotation); + }); + + test('should add ids to path groups and paths', () => { + const annotation = { + id: 'anno_2', + target: { + type: 'drawing', + path_groups: [ + { + paths: [{ points: [] }], + stroke: { color: '#000', size: 1 }, + }, + ] as Array, + }, + type: 'annotation', + } as Annotation; + + const annotationWithIds = addClientIds(annotation); + + const { + target: { path_groups: pathGroups }, + } = annotationWithIds as { target: TargetDrawing }; + + expect(pathGroups[0].id).not.toBeUndefined(); + expect(pathGroups[0].paths[0].id).not.toBeUndefined(); + }); + }); +}); diff --git a/src/store/annotations/reducer.ts b/src/store/annotations/reducer.ts index 9beae3e54..4d772d13c 100644 --- a/src/store/annotations/reducer.ts +++ b/src/store/annotations/reducer.ts @@ -1,4 +1,5 @@ import { createReducer, combineReducers } from '@reduxjs/toolkit'; +import { addClientIds } from './util'; import { AnnotationsState } from './types'; import { createAnnotationAction, @@ -37,7 +38,7 @@ const annotationsById = createReducer({}, builder => }) .addCase(fetchAnnotationsAction.fulfilled, (state, { payload }) => { payload.entries.forEach(annotation => { - state[annotation.id] = annotation; + state[annotation.id] = addClientIds(annotation); }); }), ); diff --git a/src/store/annotations/util.ts b/src/store/annotations/util.ts new file mode 100644 index 000000000..fb2d13de3 --- /dev/null +++ b/src/store/annotations/util.ts @@ -0,0 +1,31 @@ +import * as uuid from 'uuid'; +import { Annotation } from '../../@types'; + +export function addClientIds(annotation: Annotation): Annotation { + if (annotation.target.type !== 'drawing') { + return annotation; + } + + const { target } = annotation; + const { path_groups: pathGroups } = target; + + return { + ...annotation, + target: { + ...annotation.target, + // eslint-disable-next-line @typescript-eslint/camelcase + path_groups: pathGroups.map(pathGroup => { + const { paths } = pathGroup; + + return { + ...pathGroup, + id: uuid.v4(), + paths: paths.map(path => ({ + ...path, + id: uuid.v4(), + })), + }; + }), + }, + }; +}