Skip to content

Commit

Permalink
refactor(resin): Move iscurrent and fileid to BaseManager (#642)
Browse files Browse the repository at this point in the history
  • Loading branch information
Conrad Chan authored Dec 2, 2020
1 parent 8fc39c5 commit 907e14b
Show file tree
Hide file tree
Showing 17 changed files with 154 additions and 71 deletions.
17 changes: 13 additions & 4 deletions src/common/BaseManager.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import * as ReactDOM from 'react-dom';
import { IntlShape } from 'react-intl';
import { Store } from 'redux';
import { applyResinTags } from '../utils/resin';

export type ResinTags = Record<string, unknown>;

export type Options = {
location?: number;
referenceEl: HTMLElement;
resinTags?: ResinTags;
};

export type Props = {
Expand All @@ -24,9 +28,12 @@ export default class BaseManager implements Manager {

reactEl: HTMLElement;

constructor({ location = 1, referenceEl }: Options) {
constructor({ location = 1, referenceEl, resinTags = {} }: Options) {
this.location = location;
this.reactEl = this.insert(referenceEl);
this.reactEl = this.insert(referenceEl, {
...resinTags,
feature: 'annotations',
});

this.decorate();
}
Expand All @@ -45,15 +52,17 @@ export default class BaseManager implements Manager {
return parentEl.contains(this.reactEl);
}

insert(referenceEl: HTMLElement): HTMLElement {
insert(referenceEl: HTMLElement, resinTags: ResinTags = {}): HTMLElement {
// Find the nearest applicable reference and document elements
const documentEl = referenceEl.ownerDocument || document;
const parentEl = referenceEl.parentNode || documentEl;

// Construct a layer element where we can inject a root React component
const rootLayerEl = documentEl.createElement('div');
rootLayerEl.classList.add('ba-Layer');
rootLayerEl.setAttribute('data-resin-feature', 'annotations');

// Apply any resin tags
applyResinTags(rootLayerEl, resinTags);

// Insert the new layer element immediately after the reference element
return parentEl.insertBefore(rootLayerEl, referenceEl.nextSibling);
Expand Down
41 changes: 41 additions & 0 deletions src/common/__tests__/BaseManager-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import BaseManager, { Options, Props } from '../BaseManager';

class TestBaseManager extends BaseManager {
// eslint-disable-next-line @typescript-eslint/no-empty-function
decorate(): void {}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
render(props: Props): void {} // eslint-disable-line @typescript-eslint/no-empty-function
}

describe('BaseManager', () => {
const rootEl = document.createElement('div');
const getOptions = (options: Partial<Options> = {}): Options => ({
referenceEl: rootEl.querySelector('.reference') as HTMLElement,
...options,
});
const getWrapper = (options?: Partial<Options>): TestBaseManager => new TestBaseManager(getOptions(options));

beforeEach(() => {
rootEl.classList.add('root');
rootEl.innerHTML = '<div class="reference" />'; // referenceEl
});

describe('constructor()', () => {
test('should add rootLayerEl', () => {
const wrapper = getWrapper();
expect(wrapper.reactEl.classList.contains('ba-Layer')).toBe(true);
expect(wrapper.reactEl.getAttribute('data-resin-feature')).toBe('annotations');
});

test('should add rootLayerEl with custom resin tags', () => {
const resinTags = {
foo: 'bar',
};
const wrapper = getWrapper({ resinTags });
expect(wrapper.reactEl.classList.contains('ba-Layer')).toBe(true);
expect(wrapper.reactEl.getAttribute('data-resin-feature')).toBe('annotations');
expect(wrapper.reactEl.getAttribute('data-resin-foo')).toBe('bar');
});
});
});
6 changes: 0 additions & 6 deletions src/components/ItemList/ItemRow.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import * as React from 'react';
import * as ReactRedux from 'react-redux';
import classNames from 'classnames';
import { Collaborator } from '../../@types';
import { getFileId, getIsCurrentFileVersion } from '../../store';
import './ItemRow.scss';

export type Props = {
Expand All @@ -18,8 +16,6 @@ export type ItemRowRef = HTMLLIElement;

const ItemRow = (props: Props, ref: React.Ref<ItemRowRef>): JSX.Element | null => {
const { className, isActive, item: collaborator, onClick, onMouseDown, onMouseEnter } = props;
const fileId = ReactRedux.useSelector(getFileId);
const isCurrentFileVersion = ReactRedux.useSelector(getIsCurrentFileVersion);

if (!collaborator || !collaborator.item || !collaborator.item.name) {
return null;
Expand All @@ -33,8 +29,6 @@ const ItemRow = (props: Props, ref: React.Ref<ItemRowRef>): JSX.Element | null =
ref={ref}
aria-selected={isActive}
className={classNames(className, 'ba-ItemRow', { 'is-active': isActive })}
data-resin-fileid={fileId}
data-resin-iscurrent={isCurrentFileVersion}
data-resin-target="atMention"
data-testid="ba-ItemRow"
onClick={onClick}
Expand Down
14 changes: 0 additions & 14 deletions src/components/ItemList/__tests__/ItemRow-test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import * as React from 'react';
import * as ReactRedux from 'react-redux';
import { shallow, ShallowWrapper } from 'enzyme';
import { UserMini } from '../../../@types';
import ItemRow, { Props } from '../ItemRow';
Expand All @@ -16,12 +15,6 @@ describe('ItemRow', () => {

const getWrapper = (props = {}): ShallowWrapper => shallow(<ItemRow {...defaults} {...props} />);

let reduxSpy: jest.SpyInstance;

beforeEach(() => {
reduxSpy = jest.spyOn(ReactRedux, 'useSelector').mockImplementation(() => true);
});

describe('render()', () => {
test('should not render anything if no item name', () => {
const wrapper = getWrapper({ item: {} });
Expand All @@ -43,16 +36,9 @@ describe('ItemRow', () => {
});

test('should add resin tags', () => {
// mock fileId
reduxSpy.mockReturnValueOnce('0');
// mock isCurrentFileVersion
reduxSpy.mockReturnValueOnce(true);

const wrapper = getWrapper();

expect(wrapper.props()).toMatchObject({
'data-resin-fileid': '0',
'data-resin-iscurrent': true,
'data-resin-target': 'atMention',
});
});
Expand Down
6 changes: 1 addition & 5 deletions src/components/ReplyForm/ReplyForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export type Props = ReplyFormProps &
Pick<FormikProps<FormValues>, 'errors' | 'setFieldValue' | 'values'>;

const ReplyForm = (props: Props): JSX.Element => {
const { errors, fileId, isCurrentFileVersion, isPending, onCancel, onChange, setFieldValue, values } = props;
const { errors, isPending, onCancel, onChange, setFieldValue, values } = props;

const formRef = React.useRef<HTMLFormElement>(null);
const intl = useIntl();
Expand Down Expand Up @@ -83,8 +83,6 @@ const ReplyForm = (props: Props): JSX.Element => {
</div>
<div className="ba-Popup-footer">
<ReplyButton
data-resin-fileid={fileId}
data-resin-iscurrent={isCurrentFileVersion}
data-resin-target="cancel"
data-testid="ba-Popup-cancel"
isDisabled={isPending}
Expand All @@ -94,8 +92,6 @@ const ReplyForm = (props: Props): JSX.Element => {
<FormattedMessage {...messages.buttonCancel} />
</ReplyButton>
<ReplyButton
data-resin-fileid={fileId}
data-resin-iscurrent={isCurrentFileVersion}
data-resin-target="post"
data-testid="ba-Popup-submit"
isDisabled={hasErrors || isPending}
Expand Down
6 changes: 1 addition & 5 deletions src/components/ReplyForm/ReplyFormContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,10 @@ import { FormikBag, withFormik } from 'formik';
import { connect } from 'react-redux';
import ReplyForm, { ReplyFormProps } from './ReplyForm';
import withMentionDecorator from '../ReplyField/withMentionDecorator';
import { AppState, getCreatorCursor, getFileId, getIsCurrentFileVersion } from '../../store';
import { AppState, getCreatorCursor } from '../../store';

export type PropsFromState = {
cursorPosition: number;
fileId: string | null;
isCurrentFileVersion: boolean;
};

type Props = ReplyFormProps & PropsFromState;
Expand All @@ -27,8 +25,6 @@ const MAX_LENGTH = 10000;

export const mapStateToProps = (state: AppState): PropsFromState => ({
cursorPosition: getCreatorCursor(state),
fileId: getFileId(state),
isCurrentFileVersion: getIsCurrentFileVersion(state),
});

export const mapPropsToErrors = (): FormErrors => ({ editorState: 'initial' });
Expand Down
9 changes: 0 additions & 9 deletions src/components/ReplyForm/__tests__/ReplyForm-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ describe('ReplyForm', () => {
const defaults: Props = {
cursorPosition: 0,
errors: {},
fileId: '0',
isCurrentFileVersion: true,
isPending: false,
onCancel: jest.fn(),
onChange: jest.fn(),
Expand Down Expand Up @@ -61,17 +59,10 @@ describe('ReplyForm', () => {
const cancelButton = wrapper.find('[data-testid="ba-Popup-cancel"]');
const postButton = wrapper.find('[data-testid="ba-Popup-submit"]');

const resinTags = {
'data-resin-fileid': defaults.fileId,
'data-resin-iscurrent': defaults.isCurrentFileVersion,
};

expect(cancelButton.props()).toMatchObject({
...resinTags,
'data-resin-target': 'cancel',
});
expect(postButton.props()).toMatchObject({
...resinTags,
'data-resin-target': 'post',
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ jest.mock('../../../store');
describe('ReplyFormContainer', () => {
const defaults = {
cursorPosition: 0,
fileId: '0',
isCurrentFileVersion: true,
isPending: false,
onCancel: jest.fn(),
onChange: jest.fn(),
Expand All @@ -19,8 +17,6 @@ describe('ReplyFormContainer', () => {
test('should set props', () => {
expect(mapStateToProps({} as AppState)).toEqual({
cursorPosition: 1,
fileId: '0',
isCurrentFileVersion: true,
});
});
});
Expand Down
15 changes: 9 additions & 6 deletions src/document/DocumentAnnotator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { Event } from '../@types';
import { getAnnotation } from '../store/annotations';
import { getSelection } from './docUtil';
import { Manager } from '../common/BaseManager';
import { Mode } from '../store';
import { getFileId, getIsCurrentFileVersion, Mode } from '../store';
import { scrollToLocation } from '../utils/scroll';
import './DocumentAnnotator.scss';

Expand Down Expand Up @@ -54,9 +54,12 @@ export default class DocumentAnnotator extends BaseAnnotator {
}

getPageManagers(pageEl: HTMLElement): Set<Manager> {
const fileId = getFileId(this.store.getState());
const isCurrentFileVersion = getIsCurrentFileVersion(this.store.getState());
const pageNumber = this.getPageNumber(pageEl);
const pageReferenceEl = this.getPageReference(pageEl);
const managers = this.managers.get(pageNumber) || new Set();
const resinTags = { fileid: fileId, iscurrent: isCurrentFileVersion };

// Destroy any managers that were attached to page elements that no longer exist
managers.forEach(manager => {
Expand All @@ -68,10 +71,10 @@ export default class DocumentAnnotator extends BaseAnnotator {

// Lazily instantiate managers as pages are added or re-rendered
if (managers.size === 0) {
managers.add(new PopupManager({ location: pageNumber, referenceEl: pageReferenceEl }));
managers.add(new PopupManager({ location: pageNumber, referenceEl: pageReferenceEl, resinTags }));

if (this.isFeatureEnabled('drawing')) {
managers.add(new DrawingManager({ location: pageNumber, referenceEl: pageReferenceEl }));
managers.add(new DrawingManager({ location: pageNumber, referenceEl: pageReferenceEl, resinTags }));
}

const textLayer = pageEl.querySelector('.textLayer') as HTMLElement;
Expand All @@ -87,15 +90,15 @@ export default class DocumentAnnotator extends BaseAnnotator {
);
}

managers.add(new HighlightManager({ location: pageNumber, referenceEl: pageReferenceEl }));
managers.add(new HighlightManager({ location: pageNumber, referenceEl: pageReferenceEl, resinTags }));

managers.add(new RegionManager({ location: pageNumber, referenceEl: pageReferenceEl }));
managers.add(new RegionManager({ location: pageNumber, referenceEl: pageReferenceEl, resinTags }));

const canvasLayerEl = pageEl.querySelector<HTMLElement>('.canvasWrapper');
const referenceEl =
this.isFeatureEnabled('discoverability') && canvasLayerEl ? canvasLayerEl : pageReferenceEl;

managers.add(new RegionCreationManager({ location: pageNumber, referenceEl }));
managers.add(new RegionCreationManager({ location: pageNumber, referenceEl, resinTags }));
}

return managers;
Expand Down
4 changes: 1 addition & 3 deletions src/drawing/DrawingAnnotations.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,11 @@ import './DrawingAnnotations.scss';
export type Props = {
activeAnnotationId: string | null;
annotations: AnnotationDrawing[];
isCurrentFileVersion: boolean;
setActiveAnnotationId: (annotationId: string | null) => void;
};

const DrawingAnnotations = (props: Props): JSX.Element => {
const { activeAnnotationId, annotations, isCurrentFileVersion, setActiveAnnotationId } = props;
const { activeAnnotationId, annotations, setActiveAnnotationId } = props;

const handleAnnotationActive = (annotationId: string | null): void => {
setActiveAnnotationId(annotationId);
Expand All @@ -22,7 +21,6 @@ const DrawingAnnotations = (props: Props): JSX.Element => {
activeId={activeAnnotationId}
annotations={annotations}
className="ba-DrawingAnnotations-list"
data-resin-iscurrent={isCurrentFileVersion}
onSelect={handleAnnotationActive}
/>
);
Expand Down
1 change: 0 additions & 1 deletion src/drawing/__tests__/DrawingAnnotations-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ describe('DrawingAnnotations', () => {
const getDefaults = (): Props => ({
activeAnnotationId: null,
annotations: [],
isCurrentFileVersion: true,
setActiveAnnotationId: jest.fn(),
});
const getWrapper = (props = {}): ShallowWrapper => shallow(<DrawingAnnotations {...getDefaults()} {...props} />);
Expand Down
4 changes: 0 additions & 4 deletions src/highlight/HighlightTarget.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import React from 'react';
import * as ReactRedux from 'react-redux';
import * as uuid from 'uuid';
import classNames from 'classnames';
import noop from 'lodash/noop';
import { getIsCurrentFileVersion } from '../store';
import { MOUSE_PRIMARY } from '../constants';
import { Rect } from '../@types/model';
import './HighlightTarget.scss';
Expand All @@ -23,7 +21,6 @@ export type HighlightTargetRef = HTMLAnchorElement;
const HighlightTarget = (props: Props, ref: React.Ref<HighlightTargetRef>): JSX.Element => {
const { annotationId, className, isActive, onHover = noop, onMount = noop, onSelect = noop, shapes } = props;
const uuidRef = React.useRef<string>(uuid.v4());
const isCurrentFileVersion = ReactRedux.useSelector(getIsCurrentFileVersion);

const handleClick = (event: React.MouseEvent<HighlightTargetRef>): void => {
// These are needed to prevent the anchor link from being followed and updating the url location
Expand Down Expand Up @@ -78,7 +75,6 @@ const HighlightTarget = (props: Props, ref: React.Ref<HighlightTargetRef>): JSX.
ref={ref}
className={classNames('ba-HighlightTarget', className, { 'is-active': isActive })}
data-ba-reference-id={uuidRef.current}
data-resin-iscurrent={isCurrentFileVersion}
data-resin-itemid={annotationId}
data-resin-target="highlightText"
data-testid={`ba-AnnotationTarget-${annotationId}`}
Expand Down
14 changes: 9 additions & 5 deletions src/image/ImageAnnotator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import PopupManager from '../popup/PopupManager';
import { centerDrawing, DrawingManager, isDrawing } from '../drawing';
import { centerRegion, isRegion, RegionCreationManager, RegionManager } from '../region';
import { CreatorStatus, getCreatorStatus } from '../store/creator';
import { getAnnotation, getRotation } from '../store';
import { getAnnotation, getFileId, getIsCurrentFileVersion, getRotation } from '../store';
import { getRotatedPosition } from '../utils/rotate';
import { Manager } from '../common/BaseManager';
import { scrollToLocation } from '../utils/scroll';
Expand Down Expand Up @@ -42,6 +42,10 @@ export default class ImageAnnotator extends BaseAnnotator {
}

getManagers(parentEl: HTMLElement, referenceEl: HTMLElement): Set<Manager> {
const fileId = getFileId(this.store.getState());
const isCurrentFileVersion = getIsCurrentFileVersion(this.store.getState());
const resinTags = { fileid: fileId, iscurrent: isCurrentFileVersion };

this.managers.forEach(manager => {
if (!manager.exists(parentEl)) {
manager.destroy();
Expand All @@ -50,12 +54,12 @@ export default class ImageAnnotator extends BaseAnnotator {
});

if (this.managers.size === 0) {
this.managers.add(new PopupManager({ referenceEl }));
this.managers.add(new PopupManager({ referenceEl, resinTags }));
if (this.isFeatureEnabled('drawing')) {
this.managers.add(new DrawingManager({ referenceEl }));
this.managers.add(new DrawingManager({ referenceEl, resinTags }));
}
this.managers.add(new RegionManager({ referenceEl }));
this.managers.add(new RegionCreationManager({ referenceEl }));
this.managers.add(new RegionManager({ referenceEl, resinTags }));
this.managers.add(new RegionCreationManager({ referenceEl, resinTags }));
}

return this.managers;
Expand Down
Loading

0 comments on commit 907e14b

Please sign in to comment.