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

[CARBON-865] CustomDragLayer component for enabling Drag & Drop ghost row #1439

Merged
merged 10 commits into from Aug 29, 2017
46 changes: 45 additions & 1 deletion CHANGELOG.md
Expand Up @@ -4,12 +4,56 @@

* `ConfigurableItems` now accepts an `onReset` prop to be passed in.

### npm
## npm

* Carbon now require `npm` version 5 for installing dependencies.
* To upgrade your version of npm, run `npm install npm@latest`.
* Then, before running `npm install` in your project folder, run `npm verify cache` to update your cache.

## Draggable ghost row

The `DraggableContext` component now includes a `CustomDragLayer` to enable a ghost row when dragging.

In order to enable this you need to define the `draggableNode` prop on the `<WithDrag>` component. For example:

```
class DraggableItems extends React.Component {
render() {
return (
<DraggableContext onDrag={ onItemMoved }>
<ol>
{
items.map((item, index) => {
return (
<WithDrop key={ index } index={ index }>
<DraggableItem />
</WithDrop>
);
});
}
</ol>
</DraggableContext>
);
}
}

...

class DraggableItem extends React.Component {
render() {
return (
<li ref={ (node) => { this._listItem = node; } } >
<WithDrag draggableNode={ () => { return this._listItem; } }>
<span>{ item.content }</span>
</WithDrag>
</li>
);
}
}
```

Note that the `draggableNode` is passed as a function because the ref `_listItem` is undefined until the component is mounted.

# 1.4.4

* `Date`: Fixes missing background color on validation errors.
Expand Down
@@ -1,9 +1,10 @@
import React from 'react';
import { shallow } from 'enzyme';
import { shallow, mount } from 'enzyme';
import ConfigurableItemRow from './configurable-item-row';
import Checkbox from './../../checkbox';
import Icon from './../../icon';
import { WithDrag, WithDrop } from './../../drag-and-drop';
import DraggableContext from './../../drag-and-drop/draggable-context';
import { rootTagTest } from './../../../utils/helpers/tags/tags-specs';

describe('ConfigurableItemRow', () => {
Expand Down Expand Up @@ -140,13 +141,20 @@ describe('ConfigurableItemRow', () => {

describe('icon', () => {
beforeEach(() => {
wrapper = shallow(
<ConfigurableItemRow name='Foo' />
wrapper = mount(
<DraggableContext onDrag={() => {}}>
<ConfigurableItemRow name='Foo' />
</DraggableContext>
);
});

it('renders a drag vertical icon', () => {
expect(wrapper.find(Icon).props().type).toEqual('drag_vertical')
it('renders a drag vertical icon wrapped in WithDrag', () => {
wrapper.update(); // this is required because the _draggableNode ref is initially undefined.
const withDrag = wrapper.find(WithDrag);
const row = wrapper.find(ConfigurableItemRow);
expect(withDrag.length).toEqual(1);
expect(withDrag.find(Icon).props().type).toEqual('drag_vertical');
expect(withDrag.props().draggableNode()).toEqual(row.node._listItem)
});
});

Expand All @@ -157,8 +165,10 @@ describe('ConfigurableItemRow', () => {
);
});

it('renders an <li>', () => {
expect(wrapper.find('li').length).toEqual(1)
it('renders an <li> wrapped in WithDrop', () => {
const withDrop = wrapper.find(WithDrop);
expect(withDrop.length).toEqual(1);
expect(withDrop.find('li').length).toEqual(1);
});
})
});
});
Expand Up @@ -73,15 +73,21 @@ class ConfigurableItemRow extends React.Component {
);
}

iconHTML() {
return (
<div>
<Icon
className='configurable-item-row__icon'
type='drag_vertical'
/>
</div>
);
}

icon() {
return (
<WithDrag>
<div>
<Icon
className='configurable-item-row__icon'
type='drag_vertical'
/>
</div>
<WithDrag draggableNode={ () => { return this._listItem; } } >
{this.iconHTML()}
</WithDrag>
);
}
Expand Down Expand Up @@ -119,16 +125,25 @@ class ConfigurableItemRow extends React.Component {
return typeof (dragAndDropActiveIndex) === 'number';
}

render() {
listItemHTML = () => {
const { rowIndex, enabled, locked, name, onChange } = this.props;
return (
<WithDrop index={ rowIndex } { ...tagComponent('configurable-item-row', this.props) }>
<li className={ this.classes(this.context.dragAndDropActiveIndex, rowIndex) }>
<div className='configurable-item-row__content-wrapper'>
{ this.icon() }
{ this.checkbox(enabled, locked, name, onChange) }
</div>
</li>
<li
className={ this.classes(this.context.dragAndDropActiveIndex, rowIndex) }
ref={ (node) => { this._listItem = node; } }
>
<div className='configurable-item-row__content-wrapper'>
{ this.icon() }
{ this.checkbox(enabled, locked, name, onChange) }
</div>
</li>
);
}

render() {
return (
<WithDrop index={ this.props.rowIndex } { ...tagComponent('configurable-item-row', this.props) }>
{ this.listItemHTML() }
</WithDrop>
);
}
Expand Down
Expand Up @@ -13,6 +13,12 @@
cursor: -webkit-grabbing;
}

.configurable-item-row--dragged {
.configurable-item-row__content-wrapper {
visibility: hidden;
}
}

.configurable-item-row__content-wrapper {
align-items: center;
display: flex;
Expand All @@ -22,3 +28,23 @@
.configurable-item-row__icon {
cursor: move;
}

.custom-drag-layer {
.configurable-item-row {
background-color: $grey-light;
border: none;
cursor: grabbing;
cursor: -moz-grabbing;
cursor: -webkit-grabbing;

.configurable-item-row__icon {
cursor: grabbing;
cursor: -moz-grabbing;
cursor: -webkit-grabbing;
}

.configurable-item-row__content-wrapper {
visibility: visible;
}
}
}
@@ -0,0 +1,14 @@
import CustomDragLayer from './';
import Definition from './../../../../demo/utils/definition';

const definition = new Definition('custom-drag-layer', CustomDragLayer, {
props: ['className'],
propTypes: {
className: 'String'
},
propDescriptions: {
className: 'Custom classes to apply to the component'
}
});

export default definition;