Skip to content

Commit

Permalink
Feature: styles per event and disableDrag/Press per event (#254)
Browse files Browse the repository at this point in the history
* Add styles per event

* Add tests for WV visual-customizations

* Add disable drag, press, longpress per event

* Rename test file

* Add fields to EventPropType
  • Loading branch information
pdpino authored Aug 31, 2022
1 parent b10c015 commit 17fc66e
Show file tree
Hide file tree
Showing 6 changed files with 281 additions and 143 deletions.
19 changes: 18 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,15 +155,32 @@ See [CHANGELOG.md](docs/CHANGELOG.md) for details.
### Event Item
```js
{
// Basic fields:
id: 1,
description: 'Event',
startDate: new Date(2021, 3, 15, 12, 0),
endDate: new Date(2021, 3, 15, 12, 30),
color: 'blue',
// ... more properties if needed,

// Special fields for extra features, details below. e.g.:
style: { borderColor: 'red' },

// ... your custom fields if needed,
}
```

#### Special fields

There are some fields in the `EventItem` that provide extra customizations for each event.

| Extra `EventItem` fields | Type | Default | Description |
| ------------------------ | -------- | ------- | ---------------------------------------- |
| `style` | `Object` | `null` | Provide extra styling for the container. |
| `disableDrag` | `bool` | `false` | Disables drag-and-drop interaction. |
| `disablePress` | `bool` | `false` | Disables onPress interaction. |
| `disableLongPress` | `bool` | `false` | Disables onLongPress interaction. |


### Methods

* **`goToDate(date, animated = true)`**: navigate to a custom date.
Expand Down
12 changes: 10 additions & 2 deletions example/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,15 @@ const generateDates = (hours, minutes) => {
const makeBuilder = () => {
let index = 0;

return (start, duration, color) => {
return (start, duration, color, more = {}) => {
index += 1;
return {
id: index,
description: `Event ${index}`,
startDate: generateDates(start),
endDate: generateDates(start + duration),
color,
...(more || {}),
};
};
};
Expand All @@ -54,7 +55,14 @@ const sampleEvents = [

// Next week
buildEvent(24 * 7, 2, 'magenta'),
buildEvent(24 * 7 - 48, 3, 'lightblue'),
buildEvent(24 * 7 - 48, 3, 'lightblue', {
style: {
borderWidth: 5,
},
disableDrag: true,
disablePress: true,
disableLongPress: true,
}),
buildEvent(24 * 7 + 6, 6, 'brown'),

// Two more weeks
Expand Down
12 changes: 9 additions & 3 deletions src/Event/Event.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ const Event = ({
editEventConfig,
}) => {
const isEditing = !!onEdit && editingEventId === event.id;
const isDragEnabled = !!onDrag && editingEventId == null;
const isDragEnabled =
!!onDrag && editingEventId == null && !event.disableDrag;

// Wrappers are needed due to RN-reanimated runOnJS behavior. See docs:
// https://docs.swmansion.com/react-native-reanimated/docs/api/miscellaneous/runOnJS
Expand Down Expand Up @@ -172,7 +173,7 @@ const Event = ({
});

const longPressGesture = Gesture.LongPress()
.enabled(!!onLongPress)
.enabled(!!onLongPress && !event.disableLongPress)
.maxDistance(20)
.onTouchesDown(() => {
isLongPressing.value = true;
Expand All @@ -187,7 +188,7 @@ const Event = ({
});

const pressGesture = Gesture.Tap()
.enabled(!!onPress)
.enabled(!!onPress && !event.disablePress)
.withTestId(`pressGesture-${event.id}`)
.onTouchesDown(() => {
isPressing.value = true;
Expand Down Expand Up @@ -282,6 +283,7 @@ const Event = ({
backgroundColor: event.color || DEFAULT_COLOR,
},
containerStyle,
event.style,
animatedStyles,
]}
>
Expand Down Expand Up @@ -316,6 +318,10 @@ export const eventPropType = PropTypes.shape({
description: PropTypes.string,
startDate: PropTypes.instanceOf(Date).isRequired,
endDate: PropTypes.instanceOf(Date).isRequired,
style: PropTypes.object,
disableDrag: PropTypes.bool,
disablePress: PropTypes.bool,
disableLongPress: PropTypes.bool,
});

Event.propTypes = {
Expand Down
139 changes: 139 additions & 0 deletions src/__tests__/WeekView/basic.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import React from 'react';
import { render } from '@testing-library/react-native';
import WeekView from '../../WeekView/WeekView';

/**
* NOTE: In most cases, when providing events to <WeekView />, select
* carefully some props so the events _are visible in the screen_.
* For example:
* - numberOfDays
* - selectedDate (usually, at least the same day the target event's startDate)
* - hoursInDisplay
* - startHour (usually, some hours before the target event)
*/
describe('Basic render functionality', () => {
describe('with no events', () => {
it('renders an empty grid', () => {
const { getByHintText, queryAllByHintText } = render(
<WeekView
events={[]}
numberOfDays={3}
selectedDate={new Date(2020, 3, 24)}
/>,
);
expect(getByHintText('Grid with horizontal scroll')).toBeDefined();
expect(queryAllByHintText(/Show event \d+/)).toBeArrayOfSize(0);
});
});

const eventsWithoutOverlap = [
{
id: 1,
startDate: new Date(2021, 4, 2, 12, 0),
endDate: new Date(2021, 4, 2, 13, 0),
color: 'blue',
},
{
id: 2,
startDate: new Date(2021, 4, 2, 13, 0),
endDate: new Date(2021, 4, 2, 13, 45),
color: 'lightblue',
},
{
id: 7,
startDate: new Date(2021, 4, 3, 11, 14, 56),
endDate: new Date(2021, 4, 3, 11, 30, 21),
color: 'purple',
},
{
id: 19,
startDate: new Date(2021, 4, 4, 13, 32),
endDate: new Date(2021, 4, 4, 14, 35),
color: 'yellow',
},
{
id: 52,
startDate: new Date(2021, 4, 5, 18, 23),
endDate: new Date(2021, 4, 5, 19, 57),
color: 'pink',
},
].map((evt) => ({ ...evt, description: `event ${evt.id}` }));

const propsForNoOverlap = {
numberOfDays: 5,
selectedDate: new Date(2021, 4, 1),
hoursInDisplay: 10,
startHour: 11,
};

const eventsWithOverlap = [
{
id: 9,
startDate: new Date(2020, 7, 24, 17),
endDate: new Date(2020, 7, 24, 18),
color: 'purple',
},
{
id: 43,
startDate: new Date(2020, 7, 24, 17),
endDate: new Date(2020, 7, 24, 18),
color: 'red',
},
{
id: 11,
startDate: new Date(2020, 7, 25, 12, 15),
endDate: new Date(2020, 7, 25, 14, 30),
color: 'pink',
},
{
id: 2,
startDate: new Date(2020, 7, 25, 13, 0),
endDate: new Date(2020, 7, 25, 13, 45),
color: 'yellow',
},
{
id: 3,
startDate: new Date(2020, 7, 24, 17),
endDate: new Date(2020, 7, 24, 18),
color: 'lightblue',
},
{
id: 1,
startDate: new Date(2020, 7, 26, 13),
endDate: new Date(2020, 7, 26, 20),
color: 'lightblue',
},
].map((evt) => ({ ...evt, description: `event ${evt.id}` }));

const propsForOverlap = {
numberOfDays: 3,
selectedDate: new Date(2020, 7, 24),
hoursInDisplay: 12,
startHour: 12,
};

describe.each([
['non-overlapping', eventsWithoutOverlap, propsForNoOverlap],
['overlapped', eventsWithOverlap, propsForOverlap],
])('with %s events', (_, events, props) => {
it('shows all events', () => {
const { queryAllByText } = render(
/* eslint-disable-next-line react/jsx-props-no-spreading */
<WeekView events={events} {...props} />,
);
expect(queryAllByText(/event \d+/)).toBeArrayOfSize(events.length);
});

it('shows each event with its color', () => {
const { getByHintText } = render(
/* eslint-disable-next-line react/jsx-props-no-spreading */
<WeekView events={events} {...props} />,
);
events.forEach((evt) => {
const renderedEvent = getByHintText(`Show event ${evt.id}`);
const expectedColor = evt.color;
expect(renderedEvent).toHaveStyle({ backgroundColor: expectedColor });
});
});
});
});
104 changes: 104 additions & 0 deletions src/__tests__/WeekView/customizations.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import React from 'react';
import { render } from '@testing-library/react-native';
import { Text } from 'react-native';
import WeekView from '../../WeekView/WeekView';

describe('Visual customizations', () => {
describe('custom EventComponent', () => {
const exampleEvents = [
{
id: 1,
startDate: new Date(2021, 4, 2, 12, 0),
endDate: new Date(2021, 4, 2, 13, 0),
},
{
id: 2,
startDate: new Date(2021, 4, 2, 13, 0),
endDate: new Date(2021, 4, 2, 13, 45),
},
{
id: 7,
startDate: new Date(2021, 4, 3, 11, 14, 56),
endDate: new Date(2021, 4, 3, 11, 30, 21),
},
{
id: 19,
startDate: new Date(2021, 4, 4, 13, 32),
endDate: new Date(2021, 4, 4, 14, 35),
},
{
id: 52,
startDate: new Date(2021, 4, 5, 18, 23),
endDate: new Date(2021, 4, 5, 19, 57),
},
].map((evt) => ({
...evt,
description: 'event by default',
color: 'green',
}));

it('renders the custom component instead of the default', () => {
const MyEventComponent = ({ event }) => (
<Text>{`custom ${event.id}`}</Text>
);

const { queryAllByText } = render(
<WeekView
events={exampleEvents}
numberOfDays={5}
selectedDate={new Date(2021, 4, 1)}
hoursInDisplay={10}
startHour={11}
EventComponent={MyEventComponent}
/>,
);
expect(queryAllByText('event by default')).toBeArrayOfSize(0);
expect(queryAllByText(/custom \d+/)).toBeArrayOfSize(
exampleEvents.length,
);
});
});

describe('using event.style', () => {
const exampleEvents = [
{
id: 1,
startDate: new Date(2021, 4, 2, 12, 0),
endDate: new Date(2021, 4, 2, 13, 0),
style: { borderRadius: 2, borderWidth: 1, borderColor: 'blue' },
},
{
id: 7,
startDate: new Date(2021, 4, 3, 11, 14, 56),
endDate: new Date(2021, 4, 3, 11, 30, 21),
style: { backgroundColor: 'red' },
},
{
id: 19,
startDate: new Date(2021, 4, 4, 13, 32),
endDate: new Date(2021, 4, 4, 14, 35),
style: { backgroundColor: 'green' },
},
].map((evt) => ({
...evt,
description: `event ${evt.id}`,
color: 'green',
}));

it('renders the event with extra style', () => {
const { getByHintText } = render(
<WeekView
events={exampleEvents}
numberOfDays={5}
selectedDate={new Date(2021, 4, 1)}
hoursInDisplay={10}
startHour={11}
/>,
);
exampleEvents.forEach((evt) => {
const renderedEvent = getByHintText(`Show event ${evt.id}`);
expect(renderedEvent).toHaveStyle(evt.style);
});
});
});
});
Loading

0 comments on commit 17fc66e

Please sign in to comment.