diff --git a/__tests__/components/Transfer/Transfer.spec.tsx b/__tests__/components/Transfer/Transfer.spec.tsx new file mode 100644 index 0000000..6275cfb --- /dev/null +++ b/__tests__/components/Transfer/Transfer.spec.tsx @@ -0,0 +1,168 @@ +import React from 'react'; +import { Transfer } from '../../../src/components/Transfer'; + +import '@testing-library/jest-dom'; +import { act, fireEvent, render } from '@testing-library/react'; + +const CreateTransfer = () => { + const [values, update] = React.useState(['Banner', 'Stark', 'Steve']); + + return ( + e} + uniqueIdentifier={(e) => e} + /> + ); +}; + +describe('Transfer', () => { + it('Should render a transfer', () => { + render(); + + const transfer = document.querySelector('.sha-el-transfer'); + expect(transfer).toHaveStyle(` + min-width: 500px; + `); + + const listColumn = transfer.querySelector('.list-column'); + const listHeader = listColumn.querySelector('span'); + expect(listHeader.innerHTML).toBe('4 item(s)'); + const lists = listColumn.querySelectorAll('li'); + expect(lists.length).toBe(4); + + const buttonColumn = transfer.querySelector('.button-column'); + + const toRightButton = buttonColumn.querySelectorAll('button')[0]; + expect(toRightButton.querySelector('svg').innerHTML).toContain('M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z'); // Path for MdKeyboardArrowRight + + const toLeftButton = buttonColumn.querySelectorAll('button')[1]; + expect(toLeftButton.querySelector('svg').innerHTML).toContain('M15.41 16.09l-4.58-4.59 4.58-4.59L14 5.5l-6 6 6 6z'); // Path for MdKeyboardArrowLeft + + const valueColumn = transfer.querySelector('.value-column'); + const valueHeader = valueColumn.querySelector('span'); + expect(valueHeader.innerHTML).toBe('3 item(s)'); + const values = valueColumn.querySelectorAll('li'); + expect(values.length).toBe(3); + }); + + it('Should transfer items to right', () => { + render(); + + let listColumn = document.querySelector('.list-column'); + let lists = listColumn.querySelectorAll('li'); + act(() => { + fireEvent.click(lists[1]); + }); + + const buttonColumn = document.querySelector('.button-column'); + const toRightButton = buttonColumn.querySelectorAll('button')[0]; + act(() => { + fireEvent.click(toRightButton); + }); + + listColumn = document.querySelector('.list-column'); + lists = listColumn.querySelectorAll('li'); + expect(lists.length).toBe(3); + + const valueColumn = document.querySelector('.value-column'); + const values = valueColumn.querySelectorAll('li'); + expect(values.length).toBe(4); + }); + + it('Should transfer multiple items to right', () => { + render(); + + let listColumn = document.querySelector('.list-column'); + let lists = listColumn.querySelectorAll('li'); + act(() => { + fireEvent.click(lists[0]); + fireEvent.click(lists[1]); + }); + + const buttonColumn = document.querySelector('.button-column'); + const toRightButton = buttonColumn.querySelectorAll('button')[0]; + act(() => { + fireEvent.click(toRightButton); + }); + + listColumn = document.querySelector('.list-column'); + lists = listColumn.querySelectorAll('li'); + expect(lists.length).toBe(2); + + const valueColumn = document.querySelector('.value-column'); + const values = valueColumn.querySelectorAll('li'); + expect(values.length).toBe(5); + }); + + it('Should trasfer items to left', () => { + render(); + + let valueColumn = document.querySelector('.value-column'); + let values = valueColumn.querySelectorAll('li'); + act(() => { + fireEvent.click(values[0]); + }); + + const buttonColumn = document.querySelector('.button-column'); + const toLeftButton = buttonColumn.querySelectorAll('button')[1]; + act(() => { + fireEvent.click(toLeftButton); + }); + + valueColumn = document.querySelector('.value-column'); + values = valueColumn.querySelectorAll('li'); + expect(values.length).toBe(2); + + const listColumn = document.querySelector('.list-column'); + const lists = listColumn.querySelectorAll('li'); + expect(lists.length).toBe(5); + }); + + it('Should transfer multiple items to left', () => { + render(); + + let valueColumn = document.querySelector('.value-column'); + let values = valueColumn.querySelectorAll('li'); + act(() => { + fireEvent.click(values[0]); + fireEvent.click(values[1]); + }); + + const buttonColumn = document.querySelector('.button-column'); + const toLeftButton = buttonColumn.querySelectorAll('button')[1]; + act(() => { + fireEvent.click(toLeftButton); + }); + + valueColumn = document.querySelector('.value-column'); + values = valueColumn.querySelectorAll('li'); + expect(values.length).toBe(1); + + const listColumn = document.querySelector('.list-column'); + const lists = listColumn.querySelectorAll('li'); + expect(lists.length).toBe(6); + }); + + it('Should select and unselect an item', () => { + render(); + + let listColumn = document.querySelector('.list-column'); + let lists = listColumn.querySelectorAll('li'); + act(() => { + fireEvent.click(lists[1]); + }); + + listColumn = document.querySelector('.list-column'); + lists = listColumn.querySelectorAll('li'); + act(() => { + fireEvent.click(lists[1]); + }); + + const buttonColumn = document.querySelector('.button-column'); + const toRightButton = buttonColumn.querySelectorAll('button')[0]; + expect(toRightButton.disabled).toBeTruthy(); + }); +}); diff --git a/src/components/Transfer/Transfer.stories.tsx b/src/components/Transfer/Transfer.stories.tsx index 7065b25..3231162 100644 --- a/src/components/Transfer/Transfer.stories.tsx +++ b/src/components/Transfer/Transfer.stories.tsx @@ -17,12 +17,12 @@ export const Basic: Story> = (args) => { return ( ['Bruce', 'Clark', 'Arthur', 'Diana', 'Banner', 'Stark', 'Steve']} + {...args} + data={['Bruce', 'Clark', 'Arthur', 'Diana', 'Banner', 'Stark', 'Steve']} values={values} onChange={update} listDisplayProp={(e) => e} uniqueIdentifier={(e) => e} - {...args} /> ); }; diff --git a/src/components/Transfer/Transfer.tsx b/src/components/Transfer/Transfer.tsx index 7855f55..68a0b55 100644 --- a/src/components/Transfer/Transfer.tsx +++ b/src/components/Transfer/Transfer.tsx @@ -1,143 +1,75 @@ -import * as React from 'react'; +import React, { useState, useEffect } from 'react'; import { Row, Col } from '../Grid'; import { List, ListItem } from '../List'; import { Button } from '../Button'; import { CardHeader, Card } from '../Card'; import { MdKeyboardArrowRight, MdKeyboardArrowLeft } from 'react-icons/md'; -import { Skeleton } from '../Loading'; import { SurfaceProps } from '../../typings/surface'; export interface TransferProps extends SurfaceProps { listDisplayProp: (arg: T) => React.ReactNode; uniqueIdentifier: (arg: T) => string; - data?: (search: string) => Promise | T[]; - displayValue?: (value: T) => string; - searchValue?: (value: T) => string; - onChange?: (values: T[]) => void; - values?: T[]; -} - -interface State { - search: string; data: T[]; - selectedLeft: T[]; - selectedRight: T[]; - open: boolean; - loading: boolean; + onChange: (values: T[]) => void; + values: T[]; } -export class Transfer extends React.Component, State> { - constructor(props: TransferProps) { - super(props); - - this.state = { - search: '', - data: [], - open: false, - loading: false, - selectedRight: [], - selectedLeft: [], - }; - } - - static defaultProps = { - searchValue: (e) => String(e), - elevation: 0, - }; - - componentDidMount() { - this.fetchData(); - } - - fetchData = async () => { - const { data } = this.props; - const { search } = this.state; +export function Transfer(props: TransferProps) { + const { listDisplayProp, uniqueIdentifier, onChange, values } = props; - let items = data(search); + const [data, updateData] = useState([]); + const [selectedLeft, updateSelectedLeft] = useState([]); + const [selectedRight, updateSelectedRight] = useState([]); - if (!Array.isArray(items)) { - this.setState({ loading: true }); - items = await items; - this.setState({ loading: false }); - } - - this.setState({ data: items }); - }; - - isItemMoved = (current: T) => { - const { uniqueIdentifier, values } = this.props; - - if (!values) { - return false; - } + useEffect(() => { + updateData(props.data); + }); + const isItemMoved = (current: T) => { return !!values.find((v) => uniqueIdentifier(v) === uniqueIdentifier(current)); }; - isItemSelected = (current: T, selections: T[]) => { - const { uniqueIdentifier } = this.props; - - if (!selections) { - return false; - } - + const isItemSelected = (current: T, selections: T[]) => { return !!selections.find((v) => uniqueIdentifier(v) === uniqueIdentifier(current)); }; - makeSelection = (current: T, key: 'selectedRight' | 'selectedLeft') => { - const { uniqueIdentifier } = this.props; - - let selections = this.state[key]; - - if (this.isItemSelected(current, selections)) { + const makeSelection = (current: T, key: 'selectedRight' | 'selectedLeft') => { + let selections: T[] = key === 'selectedLeft' ? selectedLeft : selectedRight; + if (isItemSelected(current, selections)) { selections = selections.filter((v) => uniqueIdentifier(v) !== uniqueIdentifier(current)); } else { selections.push(current); } - const state = { ...this.state, [key]: selections }; - this.setState(state); + key === 'selectedLeft' ? updateSelectedLeft([...selections]) : updateSelectedRight([...selections]); }; - displayList = () => { - const { data, search, loading, selectedLeft } = this.state; - const { listDisplayProp, uniqueIdentifier, searchValue } = this.props; - + const displayList = () => { return ( - ( - - {data - .filter((v) => searchValue(v).toLowerCase().includes(search)) - .map( - (v) => - !this.isItemMoved(v) && ( - this.makeSelection(v, 'selectedLeft')} - > - {listDisplayProp(v)} - - ), - )} - + + {data.map( + (v) => + !isItemMoved(v) && ( + makeSelection(v, 'selectedLeft')} + > + {listDisplayProp(v)} + + ), )} - /> + ); }; - displayValue = () => { - const { selectedRight } = this.state; - const { listDisplayProp, uniqueIdentifier, values } = this.props; - + const displayValue = () => { return ( - + {values.map((v) => ( this.makeSelection(v, 'selectedRight')} + selected={isItemSelected(v, selectedRight)} + onClick={() => makeSelection(v, 'selectedRight')} > {listDisplayProp(v)} @@ -146,53 +78,58 @@ export class Transfer extends React.Component, State> { ); }; - transfer = (to: 'right' | 'left') => { - const { onChange, values, uniqueIdentifier } = this.props; - const { selectedLeft, selectedRight } = this.state; + const transfer = (to: 'right' | 'left') => { if (to === 'right') { onChange([...values, ...selectedLeft]); - this.setState({ selectedLeft: [] }); + updateSelectedLeft([]); return; } - onChange(values.filter((v) => !selectedRight.find((sr) => uniqueIdentifier(sr) === uniqueIdentifier(v)))); - this.setState({ selectedRight: [] }); + updateSelectedRight([]); }; - render() { - const { selectedLeft, selectedRight, data } = this.state; - const { padding = { xs: 10, sm: 15, md: 24 }, margin, border, elevation } = this.props; - return ( - - - - - {this.displayList()} - - -