Skip to content

Commit

Permalink
Merge pull request #458 from IQSS/feat/449-transfer-list-component
Browse files Browse the repository at this point in the history
TransferList component
  • Loading branch information
GPortas authored Aug 19, 2024
2 parents 7bc5d41 + 9e44a99 commit f66b503
Show file tree
Hide file tree
Showing 11 changed files with 1,520 additions and 8 deletions.
69 changes: 67 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/design-system/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline
- **FormTextArea:** extend Props Interface to accept `autoFocus` prop.
- **FormSelect:** extend Props Interface to accept `autoFocus` prop.
- **Stack:** NEW Stack element to manage layouts.
- **TransferList:** NEW TransferList component to transfer items between two list, also sortable.
- **Table:** extend Props Interface to accept `bordered` prop to add or remove borders on all sides of the table and cells. Defaults to true.

# [1.1.0](https://github.com/IQSS/dataverse-frontend/compare/@iqss/[email protected]...@iqss/[email protected]) (2024-03-12)
Expand Down
4 changes: 4 additions & 0 deletions packages/design-system/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@
"test:coverage": "nyc check-coverage"
},
"dependencies": {
"@dnd-kit/core": "6.1.0",
"@dnd-kit/modifiers": "^7.0.0",
"@dnd-kit/sortable": "8.0.0",
"@dnd-kit/utilities": "3.2.2",
"@types/react": "18.0.27",
"bootstrap": "5.2.3",
"react-bootstrap": "2.7.2",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Base colors
$dv-brand-color: #C55B28;
$dv-primary-color: #337AB7;
$dv-brand-color: #c55b28;
$dv-primary-color: #337ab7;
$dv-secondary-color: #e0e0e0;
$dv-success-color: #3c763d;
$dv-danger-color: #a94442;
Expand All @@ -10,7 +10,7 @@ $dv-success-box-color: #e0e0e0;
$dv-danger-box-color: #f2dede;
$dv-warning-box-color: #fcf8e3;
$dv-info-box-color: #d9edf7;
$dv-info-border-color: #428BCA;
$dv-info-border-color: #428bca;

// Text colors
$dv-text-color: #333;
Expand All @@ -22,13 +22,16 @@ $dv-secondary-text-color: $dv-text-color;
$dv-headings-color: #333;

// Link colors
$dv-link-color: #3174AF;
$dv-link-color: #3174af;
$dv-link-hover-color: #23527c;
$dv-tooltip-color: #99bcdb;
$dv-tooltip-hover-color: #337ab7;

// Button colors
$dv-button-border-color: #CCC;
$dv-button-border-color: #ccc;

// Border colors
$dv-border-color: #dee2e6;

:export {
brand: $dv-brand-color;
Expand All @@ -52,4 +55,5 @@ $dv-button-border-color: #CCC;
buttonBorderColor: $dv-button-border-color;
tooltipFillColor: $dv-tooltip-color;
tooltipBorderColor: $dv-tooltip-hover-color;
}
borderColor: $dv-border-color;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { ListGroup } from 'react-bootstrap'
import { DndContext, DragEndEvent } from '@dnd-kit/core'
import { arrayMove, SortableContext } from '@dnd-kit/sortable'
import { restrictToVerticalAxis } from '@dnd-kit/modifiers'
import { type TransferListItem } from './TransferList'
import { ListItem } from './ListItem'
import styles from './TransferList.module.scss'

type ListProps =
| {
items: TransferListItem[]
side: 'left'
checked: TransferListItem[]
onToggle: (item: TransferListItem) => () => void
rightItems?: never
setRight?: never
onChange?: never
}
| {
items: TransferListItem[]
side: 'right'
checked: TransferListItem[]
onToggle: (item: TransferListItem) => () => void
rightItems: TransferListItem[]
setRight: React.Dispatch<React.SetStateAction<TransferListItem[]>>
onChange?: (selected: TransferListItem[]) => void
}

export const ItemsList = ({
items,
side,
checked,
onToggle,
rightItems,
setRight,
onChange
}: ListProps) => {
if (side === 'left') {
return (
<ListGroup className={styles['items-list']} data-testid={`${side}-list-group`}>
{items.map((item: TransferListItem) => (
<ListItem
item={item}
side={side}
checked={checked}
onToggle={onToggle}
key={item.value}
/>
))}
</ListGroup>
)
}

const handleDragEnd = (event: DragEndEvent) => {
const { active, over } = event

if (over && active.id !== over.id) {
const oldIndex = rightItems.findIndex((item) => item.id === active.id)
const newIndex = rightItems.findIndex((item) => item.id === over.id)

const newItems = arrayMove(rightItems, oldIndex, newIndex)

setRight(newItems)

onChange && onChange(newItems)
}
}

return (
<DndContext onDragEnd={handleDragEnd} modifiers={[restrictToVerticalAxis]}>
<SortableContext items={items}>
<ListGroup className={styles['items-list']} data-testid={`${side}-list-group`}>
{items.map((item: TransferListItem) => (
<ListItem
item={item}
side={side}
checked={checked}
onToggle={onToggle}
key={item.value}
/>
))}
</ListGroup>
</SortableContext>
</DndContext>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { useId } from 'react'
import { useSortable } from '@dnd-kit/sortable'
import { CSS } from '@dnd-kit/utilities'
import { ListGroup } from 'react-bootstrap'
import { Form } from '../form/Form'
import { TransferListItem } from './TransferList'
import { Stack } from '../stack/Stack'
import styles from './TransferList.module.scss'

interface ListItemProps {
item: TransferListItem
side: 'left' | 'right'
checked: readonly TransferListItem[]
onToggle: (item: TransferListItem) => () => void
}

export const ListItem = ({ item, side, checked, onToggle }: ListItemProps) => {
const { attributes, listeners, transform, transition, setNodeRef, setActivatorNodeRef } =
useSortable({ id: item.id })

const uniqueID = useId()
const labelId = `transfer-list-item-${item.value}-label-${uniqueID}`

if (side === 'left') {
return (
<ListGroup.Item className={styles['list-item']}>
<Form.Group.Checkbox
label={item.label}
onChange={onToggle(item)}
id={labelId}
checked={checked.indexOf(item) !== -1}
/>
</ListGroup.Item>
)
}

const style = {
transform: CSS.Transform.toString(transform),
transition
}

return (
<ListGroup.Item
ref={setNodeRef}
{...attributes}
role=""
style={style}
className={styles['list-item']}>
<Stack direction="horizontal" gap={1}>
<button
type="button"
ref={setActivatorNodeRef}
{...listeners}
className={styles['drag-handle']}
aria-label="press space to select and keys to drag">
<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg">
<circle cx="9" cy="6" r="1.5" fill="#777" />
<circle cx="15" cy="6" r="1.5" fill="#777" />
<circle cx="9" cy="12" r="1.5" fill="#777" />
<circle cx="15" cy="12" r="1.5" fill="#777" />
<circle cx="9" cy="18" r="1.5" fill="#777" />
<circle cx="15" cy="18" r="1.5" fill="#777" />
</svg>
</button>
<Form.Group.Checkbox
label={item.label}
onChange={onToggle(item)}
id={labelId}
checked={checked.indexOf(item) !== -1}
/>
</Stack>
</ListGroup.Item>
)
}
Loading

0 comments on commit f66b503

Please sign in to comment.