From d2e05f31dc1becf6bdeef5d78bf9b220f66d6331 Mon Sep 17 00:00:00 2001 From: "Eugeniy Aryamnov (DevExpress)" Date: Fri, 8 Nov 2019 17:20:51 +0300 Subject: [PATCH 001/141] Add GroupingState --- packages/dx-react-scheduler/src/index.ts | 1 + .../src/plugins/grouping-state.test.tsx | 70 +++++++++++++++++++ .../src/plugins/grouping-state.tsx | 63 +++++++++++++++++ .../types/grouping/grouping-state.types.ts | 22 ++++++ .../src/types/grouping/index.ts | 1 + .../dx-react-scheduler/src/types/index.ts | 3 +- packages/dx-scheduler-core/src/index.ts | 2 + .../src/plugins/grouping/reducers.test.ts | 29 ++++++++ .../src/plugins/grouping/reducers.ts | 21 ++++++ .../src/types/grouping-state.types.ts | 17 +++++ packages/dx-scheduler-core/src/types/index.ts | 1 + 11 files changed, 229 insertions(+), 1 deletion(-) create mode 100644 packages/dx-react-scheduler/src/plugins/grouping-state.test.tsx create mode 100644 packages/dx-react-scheduler/src/plugins/grouping-state.tsx create mode 100644 packages/dx-react-scheduler/src/types/grouping/grouping-state.types.ts create mode 100644 packages/dx-react-scheduler/src/types/grouping/index.ts create mode 100644 packages/dx-scheduler-core/src/plugins/grouping/reducers.test.ts create mode 100644 packages/dx-scheduler-core/src/plugins/grouping/reducers.ts create mode 100644 packages/dx-scheduler-core/src/types/grouping-state.types.ts diff --git a/packages/dx-react-scheduler/src/index.ts b/packages/dx-react-scheduler/src/index.ts index 64fbee82ba..351ac2fa6e 100644 --- a/packages/dx-react-scheduler/src/index.ts +++ b/packages/dx-react-scheduler/src/index.ts @@ -16,3 +16,4 @@ export * from './plugins/today-button'; export * from './plugins/edit-recurrence-menu'; export * from './plugins/integrated-editing'; export * from './plugins/confirmation-dialog'; +export * from './plugins/grouping-state'; diff --git a/packages/dx-react-scheduler/src/plugins/grouping-state.test.tsx b/packages/dx-react-scheduler/src/plugins/grouping-state.test.tsx new file mode 100644 index 0000000000..be7e713c89 --- /dev/null +++ b/packages/dx-react-scheduler/src/plugins/grouping-state.test.tsx @@ -0,0 +1,70 @@ +import * as React from 'react'; +import { + testStatePluginField, pluginDepsToComponents, getComputedState, setupConsole, +} from '@devexpress/dx-testing'; +import { + toggleExpandedGroups, +} from '@devexpress/dx-scheduler-core'; +import { mount } from 'enzyme'; +import { PluginHost } from '@devexpress/dx-react-core'; +import { GroupingState } from './grouping-state'; + +jest.mock('@devexpress/dx-scheduler-core', () => ({ + ...require.requireActual('@devexpress/dx-scheduler-core'), + toggleExpandedGroups: jest.fn(), +})); + +const defaultDeps = { + getter: {}, +}; + +describe('GroupingState', () => { + let resetConsole; + + beforeAll(() => { + resetConsole = setupConsole(); + }); + afterAll(() => { + resetConsole(); + }); + + beforeEach(() => { + toggleExpandedGroups.mockImplementation(() => {}); + }); + afterEach(() => { + jest.resetAllMocks(); + }); + + testStatePluginField({ + defaultDeps, + Plugin: GroupingState, + propertyName: 'expandedGroups', + values: [ + ['A'], + ['B'], + ['C'], + ], + actions: [{ + actionName: 'toggleGroupExpanded', + reducer: toggleExpandedGroups, + fieldReducer: false, + }], + }); + it('should provide "grouping" getter', () => { + const tree = mount(( + + + {pluginDepsToComponents({})} + + )); + + expect(getComputedState(tree).grouping) + .toEqual([{ + resourceName: 'a', + }]); + }); +}); diff --git a/packages/dx-react-scheduler/src/plugins/grouping-state.tsx b/packages/dx-react-scheduler/src/plugins/grouping-state.tsx new file mode 100644 index 0000000000..e275edd6ca --- /dev/null +++ b/packages/dx-react-scheduler/src/plugins/grouping-state.tsx @@ -0,0 +1,63 @@ +import * as React from 'react'; +import { + Action, Plugin, Getter, StateHelper, ActionFn, createStateHelper, +} from '@devexpress/dx-react-core'; +import { + ToggleGroupPayload, toggleExpandedGroups, +} from '@devexpress/dx-scheduler-core'; +import { GroupingStateProps, GroupingStateState } from '../types'; + +class GroupingStateBase extends React.PureComponent { + static defaultProps = { + defaultExpandedGroups: [], + }; + stateHelper: StateHelper; + toggleGroupExpanded: ActionFn; + + constructor(props) { + super(props); + + this.state = { + grouping: props.grouping, + expandedGroups: props.expandedGroups || props.defaultExpandedGroups, + }; + this.stateHelper = createStateHelper( + this, + { + expandedGroups: () => { + const { onExpandedGroupsChange } = this.props; + return onExpandedGroupsChange; + }, + }, + ); + this.toggleGroupExpanded = this.stateHelper.applyReducer + .bind(this.stateHelper, toggleExpandedGroups); + } + + static getDerivedStateFromProps(nextProps, prevState) { + const { + grouping = prevState.grouping, + expandedGroups = prevState.expandedGroups, + } = nextProps; + + return { grouping, expandedGroups }; + } + + render() { + const { grouping, expandedGroups } = this.state; + + return ( + + + + + + + ); + } +} + +/*** + * A plugin that manages the grouping state. + * */ +export const GroupingState: React.ComponentType = GroupingStateBase; diff --git a/packages/dx-react-scheduler/src/types/grouping/grouping-state.types.ts b/packages/dx-react-scheduler/src/types/grouping/grouping-state.types.ts new file mode 100644 index 0000000000..1dea51f656 --- /dev/null +++ b/packages/dx-react-scheduler/src/types/grouping/grouping-state.types.ts @@ -0,0 +1,22 @@ +import { Grouping, GroupKey, GroupOrientation } from '../index'; + +// tslint:disable-next-line:no-namespace +export interface GroupingStateProps { + /** Specifies resources to group by. */ + grouping: Array; + /** Specifies whether appointments should be grouped by date. */ + groupByDate?: (view: string) => boolean; + groupOrientation?: (view: string) => GroupOrientation; + /** Specifies expanded groups. */ + expandedGroups?: Array; + /** Specifies initially expanded groups in the uncontrolled mode. */ + defaultExpandedGroups?: Array; + /** Handles expanded group changes. */ + onExpandedGroupsChange?: (expandedGroups: Array) => void; +} + +/** @internal */ +export type GroupingStateState = { + grouping: Grouping[]; + expandedGroups: Array; +}; diff --git a/packages/dx-react-scheduler/src/types/grouping/index.ts b/packages/dx-react-scheduler/src/types/grouping/index.ts new file mode 100644 index 0000000000..8388cd2bd8 --- /dev/null +++ b/packages/dx-react-scheduler/src/types/grouping/index.ts @@ -0,0 +1 @@ +export * from './grouping-state.types'; diff --git a/packages/dx-react-scheduler/src/types/index.ts b/packages/dx-react-scheduler/src/types/index.ts index 14ec7ced32..4bbc95cc91 100644 --- a/packages/dx-react-scheduler/src/types/index.ts +++ b/packages/dx-react-scheduler/src/types/index.ts @@ -1,7 +1,7 @@ export { AppointmentModel, AppointmentMeta, ElementRect, ScrollingStrategy, CellElementsMeta, FormatterFn, SchedulerView, - PreCommitChangesFn, ChangeSet, ViewCell, + PreCommitChangesFn, ChangeSet, ViewCell, Grouping, GroupKey, GroupOrientation, } from '../../../dx-scheduler-core/src/index'; /** @internal */ @@ -16,3 +16,4 @@ export * from './drag-drop'; export * from './date-navigator'; export * from './appointments'; export * from './today-button'; +export * from './grouping'; diff --git a/packages/dx-scheduler-core/src/index.ts b/packages/dx-scheduler-core/src/index.ts index 32959ccfa4..05ecced917 100644 --- a/packages/dx-scheduler-core/src/index.ts +++ b/packages/dx-scheduler-core/src/index.ts @@ -45,6 +45,8 @@ export * from './plugins/vertical-rect/helpers'; /** @internal */ export * from './plugins/drag-drop-provider/helpers'; /** @internal */ +export * from './plugins/grouping/reducers'; +/** @internal */ export * from './constants'; /** @internal */ export * from './utils'; diff --git a/packages/dx-scheduler-core/src/plugins/grouping/reducers.test.ts b/packages/dx-scheduler-core/src/plugins/grouping/reducers.test.ts new file mode 100644 index 0000000000..6dfab6068f --- /dev/null +++ b/packages/dx-scheduler-core/src/plugins/grouping/reducers.test.ts @@ -0,0 +1,29 @@ +import { toggleExpandedGroups } from './reducers'; + +describe('GroupingState reducers', () => { + describe('#toggleExpandedGroups', () => { + it('should add an opened group', () => { + const state = { + expandedGroups: ['a', 'b'], + }; + const payload = { groupKey: 'c' }; + + expect(toggleExpandedGroups(state, payload)) + .toEqual({ + expandedGroups: ['a', 'b', 'c'], + }); + }); + + it('should remove a closed group', () => { + const state = { + expandedGroups: ['a', 'b', 'c'], + }; + const payload = { groupKey: 'c' }; + + expect(toggleExpandedGroups(state, payload)) + .toEqual({ + expandedGroups: ['a', 'b'], + }); + }); + }); +}); diff --git a/packages/dx-scheduler-core/src/plugins/grouping/reducers.ts b/packages/dx-scheduler-core/src/plugins/grouping/reducers.ts new file mode 100644 index 0000000000..18d070c3f4 --- /dev/null +++ b/packages/dx-scheduler-core/src/plugins/grouping/reducers.ts @@ -0,0 +1,21 @@ +import { PureReducer, slice } from '@devexpress/dx-core'; +import { + ResourceGroupingState, ToggleGroupPayload, +} from '../../types'; + +export const toggleExpandedGroups: PureReducer = ( + state, { groupKey }, +) => { + const expandedGroups = slice(state.expandedGroups); + const groupKeyIndex = expandedGroups.indexOf(groupKey); + + if (groupKeyIndex > -1) { + expandedGroups.splice(groupKeyIndex, 1); + } else { + expandedGroups.push(groupKey); + } + + return { + expandedGroups, + }; +}; diff --git a/packages/dx-scheduler-core/src/types/grouping-state.types.ts b/packages/dx-scheduler-core/src/types/grouping-state.types.ts new file mode 100644 index 0000000000..ad0feee9b4 --- /dev/null +++ b/packages/dx-scheduler-core/src/types/grouping-state.types.ts @@ -0,0 +1,17 @@ +/** Describes grouping options. */ +export interface Grouping { + /** Specifies the name of the resource by which the data is grouped. */ + resourceName: string; +} +/** Describes a group that can be nested in another one. */ +export type GroupKey = string; +/** Describes group orientation (either Vertical or Horizontal) */ +export type GroupOrientation = 'Vertical' | 'Horizontal'; + +/** @internal */ +export type ResourceGroupingState = { + grouping?: Grouping[], + expandedGroups?: ReadonlyArray, +}; +/** @internal */ +export type ToggleGroupPayload = { groupKey: GroupKey }; diff --git a/packages/dx-scheduler-core/src/types/index.ts b/packages/dx-scheduler-core/src/types/index.ts index 59dd0b0e7d..a764ce6f4e 100644 --- a/packages/dx-scheduler-core/src/types/index.ts +++ b/packages/dx-scheduler-core/src/types/index.ts @@ -11,3 +11,4 @@ export * from './view-state.types'; export * from './utils.types'; export * from './week-view.types'; export * from './drag-drop.types'; +export * from './grouping-state.types'; From 739c977d41a112d8e9f406178e0944ca2f529efa Mon Sep 17 00:00:00 2001 From: "Eugeniy Aryamnov (DevExpress)" Date: Fri, 8 Nov 2019 17:35:38 +0300 Subject: [PATCH 002/141] Add GroupingState docs --- .../docs/reference/grouping-state.md | 44 +++++++++++++++++++ .../src/plugins/grouping-state.tsx | 1 + 2 files changed, 45 insertions(+) create mode 100644 packages/dx-react-scheduler/docs/reference/grouping-state.md diff --git a/packages/dx-react-scheduler/docs/reference/grouping-state.md b/packages/dx-react-scheduler/docs/reference/grouping-state.md new file mode 100644 index 0000000000..9a76cd70c2 --- /dev/null +++ b/packages/dx-react-scheduler/docs/reference/grouping-state.md @@ -0,0 +1,44 @@ +# GroupingState Plugin Reference + +A plugin that manages the grouping state. It lists resources used for grouping and stores information about expanded/collapsed groups. + +## Import + +Use the following statement to import the plugin: + +```js +import { GroupingState } from '@devexpress/dx-react-scheduler'; +``` + +## User Reference + +### Dependencies + +None + +### Properties + +Name | Type | Default | Description +-----|------|---------|------------ +grouping? | Array<[Grouping](#grouping)> | | Specifies resources to group by. +expandedGroups? | Array<[GroupKey](#groupkey)> | | Specifies expanded groups. +defaultExpandedGroups? | Array<[GroupKey](#groupkey)> | [] | Specifies initially expanded groups in the uncontrolled mode. +onExpandedGroupsChange? | (expandedGroups: Array<[GroupKey](#groupkey)>) => void | | Handles expanded group changes. + +## Interfaces + +### Grouping + +Describes grouping options. + +Field | Type | Description +------|------|------------ +resourceId | string | Specifies the name of the column by which the data is grouped. + +### GroupKey + +Type: `string` + +Describes a group that can be nested in another one. + +A string value that consists of values by which rows are grouped, separated by the `|` character. For example, the expanded group 'Male' is described as `Male` and 'Male'/'Audi' as `Male|Audi` and so on. diff --git a/packages/dx-react-scheduler/src/plugins/grouping-state.tsx b/packages/dx-react-scheduler/src/plugins/grouping-state.tsx index e275edd6ca..c89d1587a2 100644 --- a/packages/dx-react-scheduler/src/plugins/grouping-state.tsx +++ b/packages/dx-react-scheduler/src/plugins/grouping-state.tsx @@ -59,5 +59,6 @@ class GroupingStateBase extends React.PureComponent = GroupingStateBase; From f7a4fc6cfcc1b58ead6cdc433282c6a12b32a437 Mon Sep 17 00:00:00 2001 From: "Eugeniy Aryamnov (DevExpress)" Date: Tue, 19 Nov 2019 14:15:24 +0300 Subject: [PATCH 003/141] Add GroupingPanel --- .../src/templates/grouping-panel/layout.jsx | 48 ++++++++++++++++ packages/dx-react-scheduler/src/index.ts | 1 + .../src/plugins/grouping-panel.tsx | 57 +++++++++++++++++++ packages/dx-scheduler-core/src/index.ts | 4 +- .../plugins/grouping-panel/helpers.test.ts | 43 ++++++++++++++ .../src/plugins/grouping-panel/helpers.ts | 39 +++++++++++++ .../reducers.test.ts | 0 .../{grouping => grouping-state}/reducers.ts | 4 +- 8 files changed, 192 insertions(+), 4 deletions(-) create mode 100644 packages/dx-react-scheduler-material-ui/src/templates/grouping-panel/layout.jsx create mode 100644 packages/dx-react-scheduler/src/plugins/grouping-panel.tsx create mode 100644 packages/dx-scheduler-core/src/plugins/grouping-panel/helpers.test.ts create mode 100644 packages/dx-scheduler-core/src/plugins/grouping-panel/helpers.ts rename packages/dx-scheduler-core/src/plugins/{grouping => grouping-state}/reducers.test.ts (100%) rename packages/dx-scheduler-core/src/plugins/{grouping => grouping-state}/reducers.ts (93%) diff --git a/packages/dx-react-scheduler-material-ui/src/templates/grouping-panel/layout.jsx b/packages/dx-react-scheduler-material-ui/src/templates/grouping-panel/layout.jsx new file mode 100644 index 0000000000..41af1ca996 --- /dev/null +++ b/packages/dx-react-scheduler-material-ui/src/templates/grouping-panel/layout.jsx @@ -0,0 +1,48 @@ +import * as React from 'react'; +import * as PropTypes from 'prop-types'; +import classNames from 'clsx'; +import { withStyles } from '@material-ui/core/styles'; + +const styles = {}; + +const LayoutBase = ({ + rowComponent: Row, + cellComponent: Cell, + groups, + classes, + className, + ...restProps +}) => ( +
+ {groups.map((group) => { + return ( + + {group.map(groupItem => { + return + })} + + ) + })} +
+); + +LayoutBase.propTypes = { + rowComponent: PropTypes.oneOfType([PropTypes.func, PropTypes.object]).isRequired, + cellComponent: PropTypes.oneOfType([PropTypes.func, PropTypes.object]).isRequired, + children: PropTypes.node, + className: PropTypes.string, + classes: PropTypes.object.isRequired, + isRecurrence: PropTypes.bool, + groups: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.object)).isRequired, +}; + +LayoutBase.defaultProps = { + className: undefined, + isRecurrence: false, + children: null, +}; + +export const Layout = withStyles(styles)(LayoutBase, { name: 'Layout' }); diff --git a/packages/dx-react-scheduler/src/index.ts b/packages/dx-react-scheduler/src/index.ts index 87b299e84e..499f732ec7 100644 --- a/packages/dx-react-scheduler/src/index.ts +++ b/packages/dx-react-scheduler/src/index.ts @@ -18,3 +18,4 @@ export * from './plugins/integrated-editing'; export * from './plugins/resources'; export * from './plugins/confirmation-dialog'; export * from './plugins/grouping-state'; +export * from './plugins/grouping-panel'; diff --git a/packages/dx-react-scheduler/src/plugins/grouping-panel.tsx b/packages/dx-react-scheduler/src/plugins/grouping-panel.tsx new file mode 100644 index 0000000000..e8e262aea9 --- /dev/null +++ b/packages/dx-react-scheduler/src/plugins/grouping-panel.tsx @@ -0,0 +1,57 @@ +import * as React from 'react'; +import { + Plugin, + Template, + TemplatePlaceholder, + TemplateConnector, + PluginComponents, +} from '@devexpress/dx-react-core'; +import { getGroupingItemsFromResources } from '@devexpress/dx-scheduler-core'; + +const pluginDependencies = [ + { name: 'Resources' }, +]; + +class GroupingPanelBase extends React.PureComponent { + static components: PluginComponents = { + layoutComponent: 'Layout', + rowComponent: 'Row', + cellComponent: 'Cell', + iconComponent: 'Icon', + }; + + static defaultProps = { + grouping: [], + }; + + render() { + const { + containerComponent, + layoutComponent, + rowComponent, + cellComponent, + iconComponent, + } = this.props; + + return ( + + + + ); + } +} + +/** A plugin that renders the Scheduler's grouping panel. */ +export const GroupingPanel: React.ComponentType = GroupingPanelBase; diff --git a/packages/dx-scheduler-core/src/index.ts b/packages/dx-scheduler-core/src/index.ts index 5a43ca97d9..1438ea85a3 100644 --- a/packages/dx-scheduler-core/src/index.ts +++ b/packages/dx-scheduler-core/src/index.ts @@ -49,7 +49,9 @@ export * from './plugins/resources/computeds'; /** @internal */ export * from './plugins/resources/helpers'; /** @internal */ -export * from './plugins/grouping/reducers'; +export * from './plugins/grouping-state/reducers'; +/** @internal */ +export * from './plugins/grouping-panel/helpers'; /** @internal */ export * from './constants'; /** @internal */ diff --git a/packages/dx-scheduler-core/src/plugins/grouping-panel/helpers.test.ts b/packages/dx-scheduler-core/src/plugins/grouping-panel/helpers.test.ts new file mode 100644 index 0000000000..9088cdecc2 --- /dev/null +++ b/packages/dx-scheduler-core/src/plugins/grouping-panel/helpers.test.ts @@ -0,0 +1,43 @@ +import { getGroupingItemsFromResources } from './helpers'; + +describe('GroupingPanel helpers', () => { + describe('#getGroupingItemsFromResources', () => { + const resources = [ + { + fieldName: 'resource1', + title: 'Location', + instances: [ + { id: 'Room 1', text: 'Room 1' }, + { id: 'Room 2', text: 'Room 2' }, + ], + }, + { + fieldName: 'members', + title: 'Members', + instances: [ + { id: 1, text: 'Andrew Glover' }, + { id: 2, text: 'Arnie Schwartz' }, + ], + }, + ]; + + it('should add an opened group', () => { + expect(getGroupingItemsFromResources(state, payload)) + .toEqual({ + expandedGroups: ['a', 'b', 'c'], + }); + }); + + it('should remove a closed group', () => { + const state = { + expandedGroups: ['a', 'b', 'c'], + }; + const payload = { groupKey: 'c' }; + + expect(getGroupingItemsFromResources(state, payload)) + .toEqual({ + expandedGroups: ['a', 'b'], + }); + }); + }); +}); diff --git a/packages/dx-scheduler-core/src/plugins/grouping-panel/helpers.ts b/packages/dx-scheduler-core/src/plugins/grouping-panel/helpers.ts new file mode 100644 index 0000000000..9d1e60eef4 --- /dev/null +++ b/packages/dx-scheduler-core/src/plugins/grouping-panel/helpers.ts @@ -0,0 +1,39 @@ +import { PureComputed } from '@devexpress/dx-core'; +import { Resource, Grouping, ResourceInstance } from '../../types'; + +const filterResourcesByGrouping: PureComputed< + [Array, Array], Array +> = (resources, grouping) => resources.filter( + resource => grouping.find(resourceId => resource.fieldName === resourceId.resourceName), +); + +const sortFilteredResources: PureComputed< + [Array, Array], Array +> = (resources, grouping) => { + return grouping.map(({ resourceName }: Grouping) => { + return resources.find(resource => resource.fieldName === resourceName) as Resource; + }); +}; + +export const getGroupingItemsFromResources: PureComputed< + [Array, Array], Array> +> = (resources, grouping) => { + const filteredResources = filterResourcesByGrouping(resources, grouping); + const sortedResources = sortFilteredResources(filteredResources, grouping); + return sortedResources.reduce(( + acc: Array>, resource: Resource, index: number, + ) => { + if (index === 0) { + return [resource.instances.map( + (instance: ResourceInstance) => instance, + )]; + } + const result = acc[index - 1].reduce((currentResourceNames: Array) => [ + ...currentResourceNames, + ...resource.instances.map( + (instance: ResourceInstance) => instance, + ), + ], []); + return [...acc, result]; + }, []); +}; diff --git a/packages/dx-scheduler-core/src/plugins/grouping/reducers.test.ts b/packages/dx-scheduler-core/src/plugins/grouping-state/reducers.test.ts similarity index 100% rename from packages/dx-scheduler-core/src/plugins/grouping/reducers.test.ts rename to packages/dx-scheduler-core/src/plugins/grouping-state/reducers.test.ts diff --git a/packages/dx-scheduler-core/src/plugins/grouping/reducers.ts b/packages/dx-scheduler-core/src/plugins/grouping-state/reducers.ts similarity index 93% rename from packages/dx-scheduler-core/src/plugins/grouping/reducers.ts rename to packages/dx-scheduler-core/src/plugins/grouping-state/reducers.ts index 18d070c3f4..54aea62818 100644 --- a/packages/dx-scheduler-core/src/plugins/grouping/reducers.ts +++ b/packages/dx-scheduler-core/src/plugins/grouping-state/reducers.ts @@ -15,7 +15,5 @@ export const toggleExpandedGroups: PureReducer Date: Tue, 19 Nov 2019 17:08:32 +0300 Subject: [PATCH 004/141] Show group names --- .../scheduler-resources/material-ui/basic.jsx | 66 ++++++++++--------- .../src/index.js | 1 + .../src/plugins/grouping-panel.jsx | 10 +++ .../src/templates/grouping-panel/cell.jsx | 60 +++++++++++++++++ .../templates/grouping-panel/container.jsx | 10 +++ .../src/templates/grouping-panel/layout.jsx | 13 ++-- packages/dx-react-scheduler/rollup.config.js | 1 + .../src/plugins/grouping-panel.tsx | 28 +++++++- packages/dx-scheduler-core/rollup.config.js | 1 + 9 files changed, 152 insertions(+), 38 deletions(-) create mode 100644 packages/dx-react-scheduler-material-ui/src/plugins/grouping-panel.jsx create mode 100644 packages/dx-react-scheduler-material-ui/src/templates/grouping-panel/cell.jsx create mode 100644 packages/dx-react-scheduler-material-ui/src/templates/grouping-panel/container.jsx diff --git a/packages/dx-react-scheduler-demos/src/demo-sources/scheduler-resources/material-ui/basic.jsx b/packages/dx-react-scheduler-demos/src/demo-sources/scheduler-resources/material-ui/basic.jsx index 73a86ec23e..f8047bd4c6 100644 --- a/packages/dx-react-scheduler-demos/src/demo-sources/scheduler-resources/material-ui/basic.jsx +++ b/packages/dx-react-scheduler-demos/src/demo-sources/scheduler-resources/material-ui/basic.jsx @@ -1,12 +1,13 @@ import * as React from 'react'; import Paper from '@material-ui/core/Paper'; -import { ViewState } from '@devexpress/dx-react-scheduler'; +import { ViewState, GroupingState } from '@devexpress/dx-react-scheduler'; import { Scheduler, Resources, WeekView, Appointments, AppointmentTooltip, + GroupingPanel, } from '@devexpress/dx-react-scheduler-material-ui'; import Select from '@material-ui/core/Select'; import MenuItem from '@material-ui/core/MenuItem'; @@ -17,36 +18,15 @@ const appointments = [{ startDate: new Date(2018, 5, 25, 12, 35), endDate: new Date(2018, 5, 25, 15, 0), id: 0, - members: [1, 3, 5], + members: 1, location: 'Room 1', }, { title: 'Book Flights to San Fran for Sales Trip', startDate: new Date(2018, 5, 26, 12, 35), endDate: new Date(2018, 5, 26, 15, 0), id: 1, - members: [2, 4], + members: 2, location: 'Room 2', -}, { - title: 'Install New Router in Dev Room', - startDate: new Date(2018, 5, 27, 12, 35), - endDate: new Date(2018, 5, 27, 15, 0), - id: 2, - members: [3], - location: 'Room 3', -}, { - title: 'Approve Personal Computer Upgrade Plan', - startDate: new Date(2018, 5, 28, 12, 35), - endDate: new Date(2018, 5, 28, 15, 0), - id: 3, - members: [4, 1], - location: 'Room 4', -}, { - title: 'Final Budget Review', - startDate: new Date(2018, 5, 29, 12, 35), - endDate: new Date(2018, 5, 29, 15, 0), - id: 4, - members: [5, 1, 3], - location: 'Room 5', }]; const styles = theme => ({ @@ -96,21 +76,37 @@ export default class Demo extends React.PureComponent { instances: [ { id: 'Room 1', text: 'Room 1' }, { id: 'Room 2', text: 'Room 2' }, - { id: 'Room 3', text: 'Room 3' }, - { id: 'Room 4', text: 'Room 4' }, - { id: 'Room 5', text: 'Room 5' }, ], }, { fieldName: 'members', title: 'Members', - allowMultiple: true, instances: [ { id: 1, text: 'Andrew Glover' }, { id: 2, text: 'Arnie Schwartz' }, - { id: 3, text: 'John Heart' }, - { id: 4, text: 'Taylor Riley' }, - { id: 5, text: 'Brad Farkus' }, + ], + }, + { + fieldName: 'location1', + instances: [ + { id: 'Room 3', text: 'Room 3' }, + { id: 'Room 4', text: 'Room 4' }, + ], + }, + { + fieldName: 'location2', + title: 'Location', + instances: [ + { id: 'Room 5', text: 'Room 5' }, + { id: 'Room 6', text: 'Room 6' }, + ], + }, + { + fieldName: 'location3', + title: 'Location', + instances: [ + { id: 'Room 7', text: 'Room 7' }, + { id: 'Room 8', text: 'Room 8' }, ], }, ], @@ -141,6 +137,13 @@ export default class Demo extends React.PureComponent { + + diff --git a/packages/dx-react-scheduler-material-ui/src/index.js b/packages/dx-react-scheduler-material-ui/src/index.js index ade8679e4f..6bbdeb3c4a 100644 --- a/packages/dx-react-scheduler-material-ui/src/index.js +++ b/packages/dx-react-scheduler-material-ui/src/index.js @@ -14,3 +14,4 @@ export * from './plugins/today-button'; export * from './plugins/edit-recurrence-menu'; export * from './plugins/confirmation-dialog'; export * from './plugins/resources'; +export * from './plugins/grouping-panel'; diff --git a/packages/dx-react-scheduler-material-ui/src/plugins/grouping-panel.jsx b/packages/dx-react-scheduler-material-ui/src/plugins/grouping-panel.jsx new file mode 100644 index 0000000000..8cdd122b48 --- /dev/null +++ b/packages/dx-react-scheduler-material-ui/src/plugins/grouping-panel.jsx @@ -0,0 +1,10 @@ +import { withComponents } from '@devexpress/dx-react-core'; +import { GroupingPanel as GroupingPanelBase } from '@devexpress/dx-react-scheduler'; +import { Container } from '../templates/grouping-panel/container'; +import { Layout } from '../templates/grouping-panel/layout'; +import { Cell } from '../templates/grouping-panel/cell'; +import { Row } from '../templates/views/common/row'; + +export const GroupingPanel = withComponents({ + Layout, Cell, Row, Container, +})(GroupingPanelBase); diff --git a/packages/dx-react-scheduler-material-ui/src/templates/grouping-panel/cell.jsx b/packages/dx-react-scheduler-material-ui/src/templates/grouping-panel/cell.jsx new file mode 100644 index 0000000000..5c1886c2ca --- /dev/null +++ b/packages/dx-react-scheduler-material-ui/src/templates/grouping-panel/cell.jsx @@ -0,0 +1,60 @@ +import * as React from 'react'; +import * as PropTypes from 'prop-types'; +import classNames from 'clsx'; +import TableCell from '@material-ui/core/TableCell'; +import { withStyles } from '@material-ui/core/styles'; +import { getBorder } from '../utils'; + +const styles = theme => ({ + cell: { + userSelect: 'none', + paddingBottom: 0, + textAlign: 'center', + borderBottom: 'none', + paddingRight: 0, + paddingLeft: 0, + '@media (max-width: 700px)': { + padding: theme.spacing(1), + paddingBottom: 0, + }, + 'table:last-child &': { + borderBottom: getBorder(theme), + }, + '&:only-child': { + textAlign: 'left', + paddingLeft: theme.spacing(2), + }, + paddingTop: theme.spacing(0.5), + }, +}); + +const CellBase = React.memo(({ + classes, + className, + groupingItem, + ...restProps +}) => ( + + {groupingItem.text} + +)); + +CellBase.propTypes = { + classes: PropTypes.object.isRequired, + formatDate: PropTypes.func.isRequired, + startDate: PropTypes.instanceOf(Date).isRequired, + endDate: PropTypes.instanceOf(Date), + className: PropTypes.string, + today: PropTypes.bool, +}; + +CellBase.defaultProps = { + className: undefined, + endDate: undefined, + today: false, +}; + +export const Cell = withStyles(styles, { name: 'Cell' })(CellBase); diff --git a/packages/dx-react-scheduler-material-ui/src/templates/grouping-panel/container.jsx b/packages/dx-react-scheduler-material-ui/src/templates/grouping-panel/container.jsx new file mode 100644 index 0000000000..31e1d6be44 --- /dev/null +++ b/packages/dx-react-scheduler-material-ui/src/templates/grouping-panel/container.jsx @@ -0,0 +1,10 @@ +import { withStyles } from '@material-ui/core/styles'; +import { ContainerBase } from '../common/container'; + +const styles = { + container: { + position: 'relative', + }, +}; + +export const Container = withStyles(styles, { name: 'GroupingPanelContainer' })(ContainerBase); diff --git a/packages/dx-react-scheduler-material-ui/src/templates/grouping-panel/layout.jsx b/packages/dx-react-scheduler-material-ui/src/templates/grouping-panel/layout.jsx index 41af1ca996..72d1468422 100644 --- a/packages/dx-react-scheduler-material-ui/src/templates/grouping-panel/layout.jsx +++ b/packages/dx-react-scheduler-material-ui/src/templates/grouping-panel/layout.jsx @@ -3,7 +3,8 @@ import * as PropTypes from 'prop-types'; import classNames from 'clsx'; import { withStyles } from '@material-ui/core/styles'; -const styles = {}; +const styles = { +}; const LayoutBase = ({ rowComponent: Row, @@ -20,11 +21,15 @@ const LayoutBase = ({ {groups.map((group) => { return ( - {group.map(groupItem => { - return + {group.map((groupingItem) => { + return ( + + ); })} - ) + ); })} ); diff --git a/packages/dx-react-scheduler/rollup.config.js b/packages/dx-react-scheduler/rollup.config.js index 766f675435..3baedcb39d 100644 --- a/packages/dx-react-scheduler/rollup.config.js +++ b/packages/dx-react-scheduler/rollup.config.js @@ -20,6 +20,7 @@ export default { typescriptRollup({ typescript, useTsconfigDeclarationDir: true, + abortOnError: false, }), replace({ "/** @class */": "/*#__PURE__*/", diff --git a/packages/dx-react-scheduler/src/plugins/grouping-panel.tsx b/packages/dx-react-scheduler/src/plugins/grouping-panel.tsx index e8e262aea9..61dc8326d1 100644 --- a/packages/dx-react-scheduler/src/plugins/grouping-panel.tsx +++ b/packages/dx-react-scheduler/src/plugins/grouping-panel.tsx @@ -8,6 +8,8 @@ import { } from '@devexpress/dx-react-core'; import { getGroupingItemsFromResources } from '@devexpress/dx-scheduler-core'; +const GroupingPanelPlaceholder = () => ; + const pluginDependencies = [ { name: 'Resources' }, ]; @@ -18,6 +20,7 @@ class GroupingPanelBase extends React.PureComponent { rowComponent: 'Row', cellComponent: 'Cell', iconComponent: 'Icon', + containerComponent: 'Container', }; static defaultProps = { @@ -26,8 +29,8 @@ class GroupingPanelBase extends React.PureComponent { render() { const { - containerComponent, - layoutComponent, + containerComponent: Container, + layoutComponent: Layout, rowComponent, cellComponent, iconComponent, @@ -39,12 +42,31 @@ class GroupingPanelBase extends React.PureComponent { dependencies={pluginDependencies} > + + diff --git a/packages/dx-scheduler-core/rollup.config.js b/packages/dx-scheduler-core/rollup.config.js index 0cc6d434a4..cbdd7f89d6 100644 --- a/packages/dx-scheduler-core/rollup.config.js +++ b/packages/dx-scheduler-core/rollup.config.js @@ -20,6 +20,7 @@ export default { typescriptRollup({ typescript, useTsconfigDeclarationDir: true, + abortOnError: false, }), replace({ '/** @class */': '/*#__PURE__*/', From 829e0c3985fae26bb38682461cf4a7d37681b758 Mon Sep 17 00:00:00 2001 From: "Eugeniy Aryamnov (DevExpress)" Date: Wed, 20 Nov 2019 14:09:11 +0300 Subject: [PATCH 005/141] Add group info to cells --- package.json | 5 -- .../src/templates/grouping-panel/cell.jsx | 38 ++++++++------- .../src/templates/grouping-panel/layout.jsx | 48 +++++++++++-------- .../views/vertical/time-scale/tick-cell.jsx | 4 +- .../src/plugins/grouping-panel.tsx | 29 +++++++++-- packages/dx-scheduler-core/src/index.ts | 2 + .../src/plugins/grouping-panel/computeds.ts | 48 +++++++++++++++++++ .../src/plugins/grouping-panel/helpers.ts | 4 +- 8 files changed, 128 insertions(+), 50 deletions(-) create mode 100644 packages/dx-scheduler-core/src/plugins/grouping-panel/computeds.ts diff --git a/package.json b/package.json index f222bfe083..a256b0ad82 100644 --- a/package.json +++ b/package.json @@ -65,11 +65,6 @@ "start-demo-servers": "yarn build:generator && lerna run --parallel start-demo-server:prod", "update-deps": "ncu -u && ./node_modules/.bin/lerna exec \"ncu -u\" --ignore @devexpress/dx-vue-*" }, - "husky": { - "hooks": { - "pre-push": "yarn build:ts && yarn update-api" - } - }, "workspaces": [ "packages/*" ], diff --git a/packages/dx-react-scheduler-material-ui/src/templates/grouping-panel/cell.jsx b/packages/dx-react-scheduler-material-ui/src/templates/grouping-panel/cell.jsx index 5c1886c2ca..f0a6f325e7 100644 --- a/packages/dx-react-scheduler-material-ui/src/templates/grouping-panel/cell.jsx +++ b/packages/dx-react-scheduler-material-ui/src/templates/grouping-panel/cell.jsx @@ -2,11 +2,12 @@ import * as React from 'react'; import * as PropTypes from 'prop-types'; import classNames from 'clsx'; import TableCell from '@material-ui/core/TableCell'; -import { withStyles } from '@material-ui/core/styles'; +import { makeStyles } from '@material-ui/core/styles'; import { getBorder } from '../utils'; -const styles = theme => ({ +const useStyles = makeStyles(theme => ({ cell: { + width: ({ width }) => width, userSelect: 'none', paddingBottom: 0, textAlign: 'center', @@ -26,35 +27,40 @@ const styles = theme => ({ }, paddingTop: theme.spacing(0.5), }, -}); +})); -const CellBase = React.memo(({ - classes, +export const Cell = React.memo(({ className, groupingItem, + width, ...restProps -}) => ( - - {groupingItem.text} - -)); +}) => { + const classes = useStyles({ width }); + return ( + + {groupingItem.text} + + ); +}); -CellBase.propTypes = { +Cell.propTypes = { classes: PropTypes.object.isRequired, formatDate: PropTypes.func.isRequired, startDate: PropTypes.instanceOf(Date).isRequired, endDate: PropTypes.instanceOf(Date), className: PropTypes.string, today: PropTypes.bool, + width: PropTypes.string, }; -CellBase.defaultProps = { +Cell.defaultProps = { className: undefined, endDate: undefined, today: false, + width: '0', }; -export const Cell = withStyles(styles, { name: 'Cell' })(CellBase); +// export const Cell = withStyles(styles, { name: 'Cell' })(CellBase); diff --git a/packages/dx-react-scheduler-material-ui/src/templates/grouping-panel/layout.jsx b/packages/dx-react-scheduler-material-ui/src/templates/grouping-panel/layout.jsx index 72d1468422..89b82ca192 100644 --- a/packages/dx-react-scheduler-material-ui/src/templates/grouping-panel/layout.jsx +++ b/packages/dx-react-scheduler-material-ui/src/templates/grouping-panel/layout.jsx @@ -13,26 +13,34 @@ const LayoutBase = ({ classes, className, ...restProps -}) => ( -
- {groups.map((group) => { - return ( - - {group.map((groupingItem) => { - return ( - - ); - })} - - ); - })} -
-); +}) => { + // eslint-disable-next-line prefer-spread + const maxCells = groups[groups.length - 1].length; + return ( +
+ {groups.map((group) => { + const cellWidth = `${100 / group.length}%`; + const colSpan = maxCells / group.length; + return ( + + {group.map((groupingItem) => { + return ( + + ); + })} + + ); + })} +
+ ); +}; LayoutBase.propTypes = { rowComponent: PropTypes.oneOfType([PropTypes.func, PropTypes.object]).isRequired, diff --git a/packages/dx-react-scheduler-material-ui/src/templates/views/vertical/time-scale/tick-cell.jsx b/packages/dx-react-scheduler-material-ui/src/templates/views/vertical/time-scale/tick-cell.jsx index d5f295d162..fdb2d958fd 100644 --- a/packages/dx-react-scheduler-material-ui/src/templates/views/vertical/time-scale/tick-cell.jsx +++ b/packages/dx-react-scheduler-material-ui/src/templates/views/vertical/time-scale/tick-cell.jsx @@ -16,7 +16,7 @@ const styles = theme => ({ }, }); -const TickCellBase = ({ +const TickCellBase = React.memo(({ classes, className, startDate, @@ -27,7 +27,7 @@ const TickCellBase = ({ className={classNames(classes.cell, className)} {...restProps} /> -); +)); TickCellBase.propTypes = { classes: PropTypes.object.isRequired, diff --git a/packages/dx-react-scheduler/src/plugins/grouping-panel.tsx b/packages/dx-react-scheduler/src/plugins/grouping-panel.tsx index 61dc8326d1..e7396e289d 100644 --- a/packages/dx-react-scheduler/src/plugins/grouping-panel.tsx +++ b/packages/dx-react-scheduler/src/plugins/grouping-panel.tsx @@ -5,8 +5,10 @@ import { TemplatePlaceholder, TemplateConnector, PluginComponents, + Getter, } from '@devexpress/dx-react-core'; -import { getGroupingItemsFromResources } from '@devexpress/dx-scheduler-core'; +import { memoize } from '@devexpress/dx-core'; +import { getGroupingItemsFromResources, getGroupedViewCellsData, sortFilteredResources, filterResourcesByGrouping } from '@devexpress/dx-scheduler-core'; const GroupingPanelPlaceholder = () => ; @@ -14,6 +16,22 @@ const pluginDependencies = [ { name: 'Resources' }, ]; +const getViewCellsDataComputed = memoize(({ viewCellsData, groupingItems, sortedResources }) => { + console.log(viewCellsData) + console.log(groupingItems) + const result = getGroupedViewCellsData(viewCellsData, groupingItems, sortedResources); + console.log(result) + return result; +}); + +const getGroupingItemsComputed = memoize(( + { grouping, resources }, +) => getGroupingItemsFromResources(resources, grouping)); + +const getSortedResourcesComputed = memoize(( + { resources, grouping }, +) => sortFilteredResources(filterResourcesByGrouping(resources, grouping), grouping)); + class GroupingPanelBase extends React.PureComponent { static components: PluginComponents = { layoutComponent: 'Layout', @@ -41,6 +59,10 @@ class GroupingPanelBase extends React.PureComponent { name="GroupingPanel" dependencies={pluginDependencies} > + + + +