Skip to content

Commit

Permalink
feat(react-scheduler): allow appointment resizing (#1932)
Browse files Browse the repository at this point in the history
  • Loading branch information
MaximKudriavtsev authored Apr 5, 2019
1 parent 7fdf293 commit 785f8a1
Show file tree
Hide file tree
Showing 31 changed files with 1,114 additions and 151 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,16 @@ namespace Appointments {
type AppointmentContentProps = Appointments_2.AppointmentContentProps;
}

// @public (undocumented)
namespace Appointments {
type SplitIndicatorProps = Appointments_2.SplitIndicatorProps;
}

// @public (undocumented)
namespace Appointments {
type ContainerProps = Appointments_2.ContainerProps;
}

// @public
declare const Appointments: React.ComponentType<AppointmentsProps> & {
Appointment: React.ComponentType<Appointments_2.AppointmentProps & { // (undocumented)
Expand All @@ -145,13 +155,23 @@ declare const Appointments: React.ComponentType<AppointmentsProps> & {
AppointmentContent: React.ComponentType<Appointments_2.AppointmentContentProps & { // (undocumented)
className?: string; // (undocumented)
style?: React.CSSProperties; // (undocumented)
[x: string]: any }>;
SplitIndicator: React.ComponentType<Appointments_2.SplitIndicatorProps & { // (undocumented)
className?: string; // (undocumented)
style?: React.CSSProperties; // (undocumented)
[x: string]: any }>;
Container: React.ComponentType<Appointments_2.ContainerProps & { // (undocumented)
className?: string; // (undocumented)
style?: React.CSSProperties; // (undocumented)
[x: string]: any }>;
};

// @public (undocumented)
interface AppointmentsProps {
appointmentComponent?: React.ComponentType<Appointments_2.AppointmentProps>;
appointmentContentComponent?: React.ComponentType<Appointments_2.AppointmentContentProps>;
containerComponent?: React.ComponentType<Appointments_2.ContainerProps>;
splitIndicatorComponent?: React.ComponentType<Appointments_2.SplitIndicatorProps>;
}

// @public (undocumented)
Expand Down Expand Up @@ -387,6 +407,11 @@ namespace DragDropProvider {
type SourceAppointmentProps = DragDropProvider_2.SourceAppointmentProps;
}

// @public (undocumented)
namespace DragDropProvider {
type ResizeProps = DragDropProvider_2.ResizeProps;
}

// @public (undocumented)
namespace DragDropProvider {
type ContainerProps = DragDropProvider_2.ContainerProps;
Expand All @@ -401,6 +426,10 @@ declare const DragDropProvider: React.ComponentType<DragDropProviderProps> & {
SourceAppointment: React.ComponentType<DragDropProvider_2.SourceAppointmentProps & { // (undocumented)
className?: string; // (undocumented)
style?: React.CSSProperties; // (undocumented)
[x: string]: any }>;
Resize: React.ComponentType<DragDropProvider_2.ResizeProps & { // (undocumented)
className?: string; // (undocumented)
style?: React.CSSProperties; // (undocumented)
[x: string]: any }>;
Container: React.ComponentType<DragDropProvider_2.ContainerProps & { // (undocumented)
className?: string; // (undocumented)
Expand All @@ -411,8 +440,10 @@ declare const DragDropProvider: React.ComponentType<DragDropProviderProps> & {
// @public (undocumented)
interface DragDropProviderProps {
allowDrag?: (appointmentData: AppointmentModel) => boolean;
allowResize?: (appointmentData: AppointmentModel) => boolean;
containerComponent?: React.ComponentType<DragDropProvider_2.ContainerProps>;
draftAppointmentComponent?: React.ComponentType<DragDropProvider_2.DraftAppointmentProps>;
resizeComponent?: React.ComponentType<DragDropProvider_2.ResizeProps>;
sourceAppointmentComponent?: React.ComponentType<DragDropProvider_2.SourceAppointmentProps>;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@ import { withComponents } from '@devexpress/dx-react-core';
import { Appointments as AppointmentsBase } from '@devexpress/dx-react-scheduler';
import { AppointmentContent } from '../templates/appointment/appointment-content';
import { Appointment } from '../templates/appointment/appointment';
import { SplitIndicator } from '../templates/appointment/split-indicator';
import { AppointmentContainer as Container } from '../templates/appointment/appointment-container';

export const Appointments = withComponents({
Appointment,
AppointmentContent,
Container,
SplitIndicator,
})(AppointmentsBase);
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import { withComponents } from '@devexpress/dx-react-core';
import { DragDropProvider as DragDropProviderBase } from '@devexpress/dx-react-scheduler';
import { DraftAppointment, SourceAppointment } from '../templates/drag-drop/appointments';
import { Container } from '../templates/drag-drop/container';
import { Resize } from '../templates/drag-drop/resize';

export const DragDropProvider = withComponents(
{ DraftAppointment, SourceAppointment, Container },
)(DragDropProviderBase);
export const DragDropProvider = withComponents({
DraftAppointment, SourceAppointment, Container, Resize,
})(DragDropProviderBase);
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import * as React from 'react';
import * as PropTypes from 'prop-types';

export const AppointmentContainer = ({ style, children, ...restProps }) => (
<div
style={style}
{...restProps}
>
{children}
</div>
);

AppointmentContainer.propTypes = {
children: PropTypes.node.isRequired,
style: PropTypes.object.isRequired,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import * as React from 'react';
import { shallow } from 'enzyme';
import { AppointmentContainer } from './appointment-container';

describe('Appointments', () => {
const defaultProps = {
style: {},
};
describe('AppointmentContainer', () => {
it('should pass rest props to the root element', () => {
const tree = shallow((
<AppointmentContainer {...defaultProps} className="custom-class">
<div />
</AppointmentContainer>
));

expect(tree.find('.custom-class'))
.toBeTruthy();
});
it('should render children inside', () => {
const tree = shallow((
<AppointmentContainer {...defaultProps} data={{ a: 1 }}>
<div className="child" />
</AppointmentContainer>
));

expect(tree.find('.child').exists())
.toBeTruthy();
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import { withStyles } from '@material-ui/core/styles';

const styles = ({ palette, typography, spacing }) => ({
appointment: {
position: 'absolute',
height: '100%',
width: '100%',
overflow: 'hidden',
boxSizing: 'border-box',
borderRight: '1px solid transparent',
Expand All @@ -28,7 +31,6 @@ const styles = ({ palette, typography, spacing }) => ({

const AppointmentBase = ({
classes, className,
style,
children,
data,
onClick: handleClick,
Expand All @@ -49,7 +51,6 @@ const AppointmentBase = ({
[classes.appointment]: true,
[classes.clickableAppointment]: clickable,
}, className)}
style={style}
{...onClick}
{...restProps}
>
Expand All @@ -61,7 +62,6 @@ const AppointmentBase = ({
AppointmentBase.propTypes = {
classes: PropTypes.object.isRequired,
children: PropTypes.node.isRequired,
style: PropTypes.object.isRequired,
className: PropTypes.string,
data: PropTypes.object,
onClick: PropTypes.func,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import * as React from 'react';
import * as PropTypes from 'prop-types';
import classNames from 'classnames';
import { withStyles } from '@material-ui/core/styles';
import {
VERTICAL_TYPE, HORIZONTAL_TYPE,
POSITION_START, POSITION_END,
} from '@devexpress/dx-scheduler-core';

const verticalStyles = {
width: '100%',
height: '10px',
};

const horizontalStyles = {
top: 0,
width: '10px',
height: '100%',
};

const styles = {
slice: {
position: 'absolute',
zIndex: 50,
},
verticalStart: {
...verticalStyles,
top: '-10px',
boxShadow: '0 10px 15px rgba(0,0,0,0.2)',
},
verticalEnd: {
...verticalStyles,
bottom: '-10px',
boxShadow: '0 -10px 15px rgba(0,0,0,0.2)',
},
horizontalStart: {
...horizontalStyles,
left: '-10px',
boxShadow: '10px 0 15px rgba(0,0,0,0.2)',
},
horizontalEnd: {
...horizontalStyles,
right: '-10px',
boxShadow: '-10px 0 15px rgba(0,0,0,0.2)',
},
};

const SplitIndicatorBase = ({
position, appointmentType, classes, className, ...restProps
}) => {
const vertical = appointmentType === VERTICAL_TYPE;
const start = position === POSITION_START;
return (
<div
className={classNames({
[classes.slice]: true,
[classes.verticalStart]: vertical && start,
[classes.verticalEnd]: vertical && !start,
[classes.horizontalStart]: !vertical && start,
[classes.horizontalEnd]: !vertical && !start,
}, className)}
{...restProps}
/>
);
};

SplitIndicatorBase.propTypes = {
classes: PropTypes.object.isRequired,
appointmentType: PropTypes.oneOf([VERTICAL_TYPE, HORIZONTAL_TYPE]).isRequired,
position: PropTypes.oneOf([POSITION_START, POSITION_END]).isRequired,
className: PropTypes.string,
};

SplitIndicatorBase.defaultProps = {
className: undefined,
};

export const SplitIndicator = withStyles(styles, { name: 'AppointmentsContainer' })(SplitIndicatorBase);
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import * as React from 'react';
import { createShallow, getClasses } from '@material-ui/core/test-utils';
import { SplitIndicator } from './split-indicator';

describe('Appointment', () => {
const defaultProps = {
position: 'start',
appointmentType: 'vertical',
};
describe('SplitIndicator', () => {
let shallow;
let classes;
beforeAll(() => {
classes = getClasses(<SplitIndicator {...defaultProps} />);
shallow = createShallow({ dive: true });
});
it('should pass rest props to the root element', () => {
const tree = shallow((
<SplitIndicator {...defaultProps} a={{ a: 1 }} />
));

expect(tree.props().a)
.toMatchObject({ a: 1 });
});
it('should pass className', () => {
const tree = shallow((
<SplitIndicator {...defaultProps} className="custom-class" />
));

expect(tree.is('.custom-class'))
.toBeTruthy();
expect(tree.is(`.${classes.slice}`))
.toBeTruthy();
expect(tree.is(`.${classes.verticalStart}`))
.toBeTruthy();
});
it('should manage classes by props', () => {
let tree = shallow((
<SplitIndicator position="start" appointmentType="horizontal" />
));

expect(tree.is(`.${classes.horizontalStart}`))
.toBeTruthy();

tree = shallow((
<SplitIndicator position="end" appointmentType="horizontal" />
));

expect(tree.is(`.${classes.horizontalEnd}`))
.toBeTruthy();

tree = shallow((
<SplitIndicator position="end" appointmentType="vertical" />
));

expect(tree.is(`.${classes.verticalEnd}`))
.toBeTruthy();
});
});
});
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import * as React from 'react';
import * as PropTypes from 'prop-types';
import classNames from 'classnames';
import { POSITION_START, POSITION_END } from '@devexpress/dx-scheduler-core';
import { withStyles } from '@material-ui/core/styles';
import { AppointmentContent } from '../appointment/appointment-content';
import { Appointment } from '../appointment/appointment';
import { SplitIndicator } from '../appointment/split-indicator';

const draftStyles = theme => ({
appointment: {
Expand All @@ -21,25 +23,28 @@ const sourceStyles = {
};

const DraftAppointmentBase = ({
classes, className, style,
data, type, ...restProps
classes, className, data,
type, fromPrev, toNext, ...restProps
}) => (
<Appointment
className={classNames(classes.appointment, className)}
style={style}
type={type}
{...restProps}
>
{fromPrev && <SplitIndicator position={POSITION_START} appointmentType={type} />}
<AppointmentContent
data={data}
type={type}
/>
{toNext && <SplitIndicator position={POSITION_END} appointmentType={type} />}
</Appointment>
);

DraftAppointmentBase.propTypes = {
classes: PropTypes.object.isRequired,
style: PropTypes.object.isRequired,
data: PropTypes.object.isRequired,
fromPrev: PropTypes.bool.isRequired,
toNext: PropTypes.bool.isRequired,
className: PropTypes.string,
type: PropTypes.string,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import { DraftAppointment, SourceAppointment } from './appointments';

describe('DragDrop', () => {
const defaultProps = {
style: {},
data: {},
type: 'horizontal',
fromPrev: false,
toNext: false,
};
describe('DraftAppointment', () => {
let shallow;
Expand Down
Loading

0 comments on commit 785f8a1

Please sign in to comment.