Skip to content
This repository has been archived by the owner on Feb 23, 2022. It is now read-only.

Lakehead-CEM-2596 Draggable Row #412

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
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
24 changes: 24 additions & 0 deletions src/Containers/DraggableRow/DraggableRow.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React from 'react';
import { Story, Meta } from '@storybook/react';
import { DraggableRow, DraggableRowProps } from '../../index';
import { createStoryTitle } from '../../Constants';

export default {
title: createStoryTitle('Draggable Row'),
component: DraggableRow,
args: {
draggable: true,
templatePrefills: {
TEMPLATE_ITEM: {
caption: 'This is a template caption.'
},
TEMPLATE_ITEM2: {
caption: 'This is another template caption.'
},
},
},
} as Meta;

export const Basic: Story<DraggableRowProps> = (args) => (
<DraggableRow {...args} />
);
25 changes: 25 additions & 0 deletions src/Containers/DraggableRow/DraggableRow.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React from 'react';
import { Template } from './Template';
import { ITemplatePrefill } from './DraggableRowTypes';

export interface DraggableRowProps extends React.HTMLAttributes<HTMLDivElement> {
/*Template of a draggable element that can be reordered*/
templatePrefills: ITemplatePrefill[];
draggable?: boolean;
}

export const DraggableRow: React.FC<DraggableRowProps> = ({
templatePrefills,
draggable = true,
...props
}): React.ReactElement => {

return (
<div {...props}>
<Template
templatePrefills={templatePrefills}
draggable={draggable}
/>
</div>
);
};
31 changes: 31 additions & 0 deletions src/Containers/DraggableRow/DraggableRowTypes.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
export interface ITemplatePrefill {
caption: string;
}

const NO_OF_ITEMS_DELETED = 1;
const REMOVE_NO_ITEMS = 0;

/**
* Reorders the draggable elements in a list
* @param {any} list - list of objects to reorder: should add a typing sequence
* @param {number} startIndex - index of where the element originates from
* @param {number} endIndex - index of where the element will be placed
*/
export function reorder(
list: ITemplatePrefill[],
startIndex: number,
endIndex: number,
): ITemplatePrefill[];

export function reorder(
list: string[][],
startIndex: number,
endIndex: number,
): string[][];

// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export function reorder(list: any, startIndex: number, endIndex: number): any {
const [removed] = list.splice(startIndex, NO_OF_ITEMS_DELETED);
list.splice(endIndex, REMOVE_NO_ITEMS, removed);
return list;
}
144 changes: 144 additions & 0 deletions src/Containers/DraggableRow/Template.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import React, { useState } from 'react';
import styled from 'styled-components';
import { ReOrderDotsVertical } from '@styled-icons/fluentui-system-filled/ReOrderDotsVertical';
import {
DragDropContext,
Droppable,
DroppableProvided,
Draggable,
DraggableProvided,
DraggableStateSnapshot,
DropResult,
} from 'react-beautiful-dnd';
import { ITemplatePrefill, reorder } from './DraggableRowTypes';

export interface TemplateProps extends React.HTMLAttributes<HTMLDivElement> {
/*Managable template that holds information for each element of the draggable row (by default a caption)*/
templatePrefills: ITemplatePrefill[];
/*Boolean that toggles if items are draggable or not*/
draggable: boolean;
}

export const Template: React.FC<TemplateProps> = ({
templatePrefills,
draggable,
...props
}): React.ReactElement => {
const [items, setItems] = useState<ITemplatePrefill[]>(
Object.values(templatePrefills),
);

/**
* Handles the draggable elements when dragged - required function
* @param {DropResult} result - react-beautiful-dnd object that gives access to source and destination ids
*/
const onDrag = (result: DropResult): void => {
const { source, destination } = result;

if (!destination) return;

const reorderedList = reorder(items, source.index, destination.index);
setItems(reorderedList);
};



const renderDraggableComponent = (drag:boolean) =>
Object.values(items).map((templatePrefill, index) => {
const {caption} = templatePrefill;
return (
<Draggable
key={caption}
draggableId={caption}
index={index}>

{(
providedDraggable: DraggableProvided,
snapshotDraggable: DraggableStateSnapshot,
) => {
const {innerRef, draggableProps, dragHandleProps} = providedDraggable;
return (
<DraggableWrapper
ref={innerRef}
style={draggableProps.style}
isDragging={snapshotDraggable.isDragging}
{...draggableProps}>
<Header>
{
drag
? <div {...dragHandleProps}> <Icon as={ReOrderDotsVertical} /> </div>
: <div> <Icon as={ReOrderDotsVertical} /> </div>
}
{caption}
</Header>
</DraggableWrapper>
)}
}
</Draggable>
)
});

return (
<ListWrapper>
<ElementWrapper {...props}>
<DragDropContext onDragEnd={onDrag}>
<Droppable droppableId="templateDroppable">
{(
provided: DroppableProvided,
) => {
const {innerRef, droppableProps, placeholder} = provided
return (
<div ref={innerRef} {...droppableProps}>
{renderDraggableComponent(draggable)}
{placeholder}
</div>
)}
}
</Droppable>
</DragDropContext>
</ElementWrapper>
</ListWrapper>
);
};

const ListWrapper = styled.div`
width: 100%;
margin: 10px;
`;

const ElementWrapper = styled.div`
width: 100%;
font-weight: bold;
${({ theme }): string => `
font-size: ${theme.font.size.small};
background-color: ${theme.colors.background};
`};
`;

interface DraggableWrapperProps {
isDragging?: boolean;
}

const DraggableWrapper = styled.div<DraggableWrapperProps>`
border-radius: 5px;
${({ theme, isDragging }): string => `
background-color: ${
isDragging ? theme.colors.border : theme.colors.background
};
`};
`;

const Header = styled.div`
border-radius: 5px;
display: flex;
${({ theme }): string => `
font-size: ${theme.font.size.small};
`};
`;

const Icon = styled.svg`
height: 20px;
padding-right: 5px;
padding-bottom: 1px;
`;

2 changes: 2 additions & 0 deletions src/Containers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ export * from './HighlightedText/HighlightedText';
export * from './NotificationBubble/NotificationBubble';
export * from './TagGroup/TagGroup';
export * from './Analytics/Analytics';
export * from './DraggableRow/DraggableRow';
export * from './OrderTracker/OrderTracker';
export * from './TableHeaderCell/TableHeaderCell';
export * from './CustomerProfile/CustomerProfile';
Expand All @@ -91,5 +92,6 @@ export * from './CRMRow/CRMRow';
export * from './ProfileModal/ProfileModal';
export * from './CRMTable/CRMTable';
export * from './LimitedTimeBanner/LimitedTimeBanner';
export * from './InfoHeader/InfoHeader';
export * from './ReachIndicator/ReachIndicator';