Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

new(annotations/EditableAnnotation): add canEditSubject canEditLabel #919

Merged
merged 4 commits into from
Nov 23, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 45 additions & 35 deletions packages/visx-annotation/src/components/EditableAnnotation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ export type EditableAnnotationProps = Pick<AnnotationContextType, 'x' | 'y' | 'd
height: number;
/** Annotation children (Subject, Label, Connector) */
children: React.ReactNode;
/** Whether the Label position (dx, dy) is editable. */
canEditLabel?: boolean;
/** Whether the Subject position (x, y) is editable. */
canEditSubject?: boolean;
/** Optional circle props to set on the subject drag handle. */
subjectDragHandleProps?: React.SVGProps<SVGCircleElement>;
/** Optional circle props to set on the label drag handle. */
Expand Down Expand Up @@ -40,18 +44,20 @@ const defaultDragHandleProps = {
};

export default function EditableAnnotation({
x: subjectX = 0,
y: subjectY = 0,
canEditLabel = true,
canEditSubject = true,
children,
dx: labelDx = 0,
dy: labelDy = 0,
children,
width,
height,
subjectDragHandleProps,
labelDragHandleProps,
onDragStart,
onDragMove,
onDragEnd,
onDragMove,
onDragStart,
subjectDragHandleProps,
width,
x: subjectX = 0,
y: subjectY = 0,
}: EditableAnnotationProps) {
// chicken before the egg, we need these to reference drag state
// in drag callbacks which are defined before useDrag() state is available
Expand Down Expand Up @@ -142,20 +148,22 @@ export default function EditableAnnotation({
fill="transparent"
/>
)}
<circle
cx={subjectX}
cy={subjectY}
transform={`translate(${subjectDrag.dx},${subjectDrag.dy})`}
onMouseMove={subjectDrag.dragMove}
onMouseUp={subjectDrag.dragEnd}
onMouseDown={subjectDrag.dragStart}
onTouchStart={subjectDrag.dragStart}
onTouchMove={subjectDrag.dragMove}
onTouchEnd={subjectDrag.dragEnd}
cursor={subjectDrag.isDragging ? 'grabbing' : 'grab'}
{...defaultDragHandleProps}
{...subjectDragHandleProps}
/>
{canEditSubject && (
<circle
cx={subjectX}
cy={subjectY}
transform={`translate(${subjectDrag.dx},${subjectDrag.dy})`}
onMouseMove={subjectDrag.dragMove}
onMouseUp={subjectDrag.dragEnd}
onMouseDown={subjectDrag.dragStart}
onTouchStart={subjectDrag.dragStart}
onTouchMove={subjectDrag.dragMove}
onTouchEnd={subjectDrag.dragEnd}
cursor={subjectDrag.isDragging ? 'grabbing' : 'grab'}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would we want this cursor state to configurable? maybe through subjectDragHandleProps?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep! {...subjectDragHandleProps} is spread below this line so it should override cursor (tho currently it doesn't have access to subjectDrag state, is that what you meant?)

{...defaultDragHandleProps}
{...subjectDragHandleProps}
/>
)}
{labelDrag.isDragging && (
<rect
width={width}
Expand All @@ -165,20 +173,22 @@ export default function EditableAnnotation({
fill="transparent"
/>
)}
<circle
cx={subjectX + subjectDrag.dx + labelDx}
cy={subjectY + subjectDrag.dy + labelDy}
transform={`translate(${labelDrag.dx},${labelDrag.dy})`}
onMouseMove={labelDrag.dragMove}
onMouseUp={labelDrag.dragEnd}
onMouseDown={labelDrag.dragStart}
onTouchStart={labelDrag.dragStart}
onTouchMove={labelDrag.dragMove}
onTouchEnd={labelDrag.dragEnd}
cursor={labelDrag.isDragging ? 'grabbing' : 'grab'}
{...defaultDragHandleProps}
{...labelDragHandleProps}
/>
{canEditLabel && (
<circle
cx={subjectX + subjectDrag.dx + labelDx}
cy={subjectY + subjectDrag.dy + labelDy}
transform={`translate(${labelDrag.dx},${labelDrag.dy})`}
onMouseMove={labelDrag.dragMove}
onMouseUp={labelDrag.dragEnd}
onMouseDown={labelDrag.dragStart}
onTouchStart={labelDrag.dragStart}
onTouchMove={labelDrag.dragMove}
onTouchEnd={labelDrag.dragEnd}
cursor={labelDrag.isDragging ? 'grabbing' : 'grab'}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above

{...defaultDragHandleProps}
{...labelDragHandleProps}
/>
)}
</>
);
}
9 changes: 7 additions & 2 deletions packages/visx-annotation/test/EditableAnnotation.test.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import React from 'react';
import { shallow } from 'enzyme';
import { Annotation, EditableAnnotation } from '../src';
import { EditableAnnotationProps } from '../lib/components/EditableAnnotation';

describe('<EditableAnnotation />', () => {
function setup() {
function setup(props?: Partial<EditableAnnotationProps>) {
return shallow(
<EditableAnnotation width={100} height={100}>
<EditableAnnotation width={100} height={100} {...props}>
<div />
</EditableAnnotation>,
);
Expand All @@ -16,6 +17,10 @@ describe('<EditableAnnotation />', () => {
it('should render two resize handles', () => {
expect(setup().find('circle')).toHaveLength(2);
});
it('should render one resize handle if canEditLabel or canEditSubject are false', () => {
expect(setup({ canEditLabel: false }).find('circle')).toHaveLength(1);
expect(setup({ canEditSubject: false }).find('circle')).toHaveLength(1);
});
it('should render an Annotation', () => {
expect(setup().find(Annotation)).toHaveLength(1);
});
Expand Down
4 changes: 4 additions & 0 deletions packages/visx-demo/src/sandboxes/visx-annotation/Example.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ export default function Example({ width, height, compact = false }: AnnotationPr
approxTooltipHeight,
connectorType,
data,
editLabelPosition,
editSubjectPosition,
getDate,
getStockValue,
horizontalAnchor,
Expand Down Expand Up @@ -51,6 +53,8 @@ export default function Example({ width, height, compact = false }: AnnotationPr
y={annotationPosition.y}
dx={annotationPosition.dx}
dy={annotationPosition.dy}
canEditLabel={editLabelPosition}
canEditSubject={editSubjectPosition}
onDragEnd={({ event, ...nextPosition }) => {
// snap Annotation to the nearest data point
const nearestDatum = findNearestDatum({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ type ProvidedProps = {
approxTooltipHeight: number;
connectorType: 'line' | 'elbow';
data: AppleStock[];
editLabelPosition: boolean;
editSubjectPosition: boolean;
getDate: (d: AppleStock) => number;
getStockValue: (d: AppleStock) => number;
horizontalAnchor?: 'left' | 'middle' | 'right';
Expand Down Expand Up @@ -62,7 +64,8 @@ export default function ExampleControls({
[height],
);

const [editAnnotation, setEditAnnotation] = useState(false);
const [editLabelPosition, setEditLabelPosition] = useState(false);
const [editSubjectPosition, setEditSubjectPosition] = useState(false);
const [title, setTitle] = useState('Title');
const [subtitle, setSubtitle] = useState(
compact ? 'Subtitle' : 'Subtitle with deets and deets and deets and deets',
Expand All @@ -87,11 +90,14 @@ export default function ExampleControls({
return (
<>
{children({
AnnotationComponent: editAnnotation ? EditableAnnotation : Annotation,
AnnotationComponent:
editLabelPosition || editSubjectPosition ? EditableAnnotation : Annotation,
annotationPosition,
approxTooltipHeight,
connectorType,
data,
editLabelPosition,
editSubjectPosition,
getDate,
getStockValue,
horizontalAnchor: horizontalAnchor === 'auto' ? undefined : horizontalAnchor,
Expand Down Expand Up @@ -121,10 +127,19 @@ export default function ExampleControls({
<label>
<input
type="checkbox"
onChange={() => setEditAnnotation(!editAnnotation)}
checked={editAnnotation}
onChange={() => setEditSubjectPosition(!editSubjectPosition)}
checked={editSubjectPosition}
/>
Edit annotation
Edit subject position
</label>
&nbsp;&nbsp;
<label>
<input
type="checkbox"
onChange={() => setEditLabelPosition(!editLabelPosition)}
checked={editLabelPosition}
/>
Edit label position
</label>
</div>
<div>
Expand Down