@reltio/pivoting 1.4.1584 → 1.4.1586-mui5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.ts +1 -0
- package/package.json +39 -24
- package/public/bundle.js +205 -0
- package/public/bundle.js.LICENSE.txt +59 -0
- package/public/package.json +25 -0
- package/scripts/build/index.js +20 -0
- package/src/components/PivotingLayout/PivotingLayout.tsx +34 -0
- package/src/components/PivotingLayout/__tests__/PivotingLayout.specs.tsx +31 -0
- package/src/components/PivotingLayout/styles.ts +30 -0
- package/src/components/PivotingLayoutItem/PivotingLayoutItem.tsx +46 -0
- package/src/components/PivotingLayoutItem/__tests__/PivotingLayoutItem.specs.tsx +58 -0
- package/src/components/PivotingProfileBand/PivotingProfileBand.tsx +46 -0
- package/src/components/PivotingProfileBand/__tests__/PivotingProfileBand.specs.tsx +25 -0
- package/src/components/PivotingProfileBand/styles.ts +67 -0
- package/src/index.ts +3 -0
- package/src/perspective/PivotingPerspective.tsx +78 -0
- package/src/perspective/__tests__/PivotingPerspective.specs.tsx +112 -0
- package/src/perspective/index.tsx +52 -0
- package/src/perspective/styles.ts +16 -0
- package/src/types/PivotingPerspectiveConfig.ts +5 -0
- package/src/types/index.ts +1 -0
- package/tsconfig.json +4 -0
- package/webpack.config.js +10 -0
- package/bundle.js +0 -2
- package/bundle.js.LICENSE.txt +0 -22
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/*
|
|
2
|
+
object-assign
|
|
3
|
+
(c) Sindre Sorhus
|
|
4
|
+
@license MIT
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/*! decimal.js-light v2.5.1 https://github.com/MikeMcl/decimal.js-light/LICENCE */
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @license React
|
|
11
|
+
* react-is.production.min.js
|
|
12
|
+
*
|
|
13
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
14
|
+
*
|
|
15
|
+
* This source code is licensed under the MIT license found in the
|
|
16
|
+
* LICENSE file in the root directory of this source tree.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @mui/material v5.13.4
|
|
21
|
+
*
|
|
22
|
+
* @license MIT
|
|
23
|
+
* This source code is licensed under the MIT license found in the
|
|
24
|
+
* LICENSE file in the root directory of this source tree.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @mui/styled-engine v5.13.2
|
|
29
|
+
*
|
|
30
|
+
* @license MIT
|
|
31
|
+
* This source code is licensed under the MIT license found in the
|
|
32
|
+
* LICENSE file in the root directory of this source tree.
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* A better abstraction over CSS.
|
|
37
|
+
*
|
|
38
|
+
* @copyright Oleg Isonen (Slobodskoi) / Isonen 2014-present
|
|
39
|
+
* @website https://github.com/cssinjs/jss
|
|
40
|
+
* @license MIT
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
/** @license React v16.13.1
|
|
44
|
+
* react-is.production.min.js
|
|
45
|
+
*
|
|
46
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
47
|
+
*
|
|
48
|
+
* This source code is licensed under the MIT license found in the
|
|
49
|
+
* LICENSE file in the root directory of this source tree.
|
|
50
|
+
*/
|
|
51
|
+
|
|
52
|
+
/** @license React v17.0.2
|
|
53
|
+
* react-jsx-runtime.production.min.js
|
|
54
|
+
*
|
|
55
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
56
|
+
*
|
|
57
|
+
* This source code is licensed under the MIT license found in the
|
|
58
|
+
* LICENSE file in the root directory of this source tree.
|
|
59
|
+
*/
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@reltio/pivoting",
|
|
3
|
+
"version": "1.4.1586-mui5",
|
|
4
|
+
"license": "SEE LICENSE IN LICENSE FILE",
|
|
5
|
+
"main": "bundle.js",
|
|
6
|
+
"dependencies": {
|
|
7
|
+
"@date-io/moment": "^1.3.5",
|
|
8
|
+
"@reltio/components": "^1.4.1586-mui5",
|
|
9
|
+
"@reltio/dashboard": "^1.4.1586-mui5",
|
|
10
|
+
"@reltio/mdm-module": "^1.4.1586-mui5",
|
|
11
|
+
"@reltio/mdm-sdk": "^1.4.1586-mui5",
|
|
12
|
+
"classnames": "^2.2.5",
|
|
13
|
+
"memoize-one": "^5.1.0",
|
|
14
|
+
"object-hash": "^2.1.1",
|
|
15
|
+
"prop-types": "^15.6.2",
|
|
16
|
+
"ramda": "^0.28.0",
|
|
17
|
+
"react-redux": "^7.2.3",
|
|
18
|
+
"react-resize-detector": "^4.2.0",
|
|
19
|
+
"redux": "^4.1.2",
|
|
20
|
+
"ui-i18n": "bitbucket:reltio-ondemand/ui-i18n#v1.4.0"
|
|
21
|
+
},
|
|
22
|
+
"publishConfig": {
|
|
23
|
+
"access": "public"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
|
|
4
|
+
const outputDir = path.resolve(process.cwd(), 'public');
|
|
5
|
+
const generatePackageJson = () => {
|
|
6
|
+
const packageJson = require('../../package.json');
|
|
7
|
+
const result = {
|
|
8
|
+
name: packageJson.name,
|
|
9
|
+
version: packageJson.version,
|
|
10
|
+
license: 'SEE LICENSE IN LICENSE FILE',
|
|
11
|
+
main: 'bundle.js',
|
|
12
|
+
dependencies: packageJson.dependencies,
|
|
13
|
+
peerDependencies: packageJson.peerDependencies,
|
|
14
|
+
publishConfig: packageJson.publishConfig
|
|
15
|
+
};
|
|
16
|
+
const source = JSON.stringify(result, null, 2);
|
|
17
|
+
fs.writeFileSync(path.join(outputDir, 'package.json'), source);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
generatePackageJson();
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import React, {memo} from 'react';
|
|
2
|
+
import {ReltioGridLayoutItem} from '@reltio/mdm-sdk';
|
|
3
|
+
import {ReltioGridLayout} from '@reltio/components';
|
|
4
|
+
import {PivotingView} from '../../types';
|
|
5
|
+
import {PivotingLayoutItem} from '../PivotingLayoutItem/PivotingLayoutItem';
|
|
6
|
+
import {useStyles} from './styles';
|
|
7
|
+
|
|
8
|
+
type Props = {
|
|
9
|
+
views: PivotingView[];
|
|
10
|
+
layout: ReltioGridLayoutItem[];
|
|
11
|
+
onLayoutChanged: (layout: ReltioGridLayoutItem[]) => void;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const PivotingLayout = ({views, layout, onLayoutChanged}: Props) => {
|
|
15
|
+
const styles = useStyles();
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<div className={styles.pageWrapper}>
|
|
19
|
+
<div className={styles.content}>
|
|
20
|
+
<div className={styles.mainPanel}>
|
|
21
|
+
<ReltioGridLayout
|
|
22
|
+
LayoutItem={PivotingLayoutItem}
|
|
23
|
+
views={views}
|
|
24
|
+
layout={layout}
|
|
25
|
+
onLayoutChanged={onLayoutChanged}
|
|
26
|
+
draggableHandle=".viewDraggableHandle"
|
|
27
|
+
/>
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export default memo(PivotingLayout);
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import {shallow} from 'enzyme';
|
|
3
|
+
import {act} from 'react-dom/test-utils';
|
|
4
|
+
import PivotingLayout from '../PivotingLayout';
|
|
5
|
+
import {ReltioGridLayout} from '@reltio/components';
|
|
6
|
+
|
|
7
|
+
describe('PivotingLayout tests', () => {
|
|
8
|
+
const onLayoutChangedSpy = jest.fn();
|
|
9
|
+
const defaultProps = {
|
|
10
|
+
views: [
|
|
11
|
+
{config: {id: 'view1', title: 'title1', component: 'Component'}},
|
|
12
|
+
{config: {id: 'view2', title: 'title2', component: 'Component'}},
|
|
13
|
+
{config: {id: 'view3', title: 'title3', component: 'Component'}}
|
|
14
|
+
],
|
|
15
|
+
layout: [
|
|
16
|
+
{id: 'view1', height: 1, x: 0, y: 0, width: 1},
|
|
17
|
+
{id: 'view2', height: 1, x: 0, y: 1, width: 1}
|
|
18
|
+
],
|
|
19
|
+
onLayoutChanged: onLayoutChangedSpy
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
it('should render PivotingLayout', () => {
|
|
23
|
+
const wrapper = shallow(<PivotingLayout {...defaultProps} />);
|
|
24
|
+
act(() => {
|
|
25
|
+
wrapper.update();
|
|
26
|
+
});
|
|
27
|
+
const grid = wrapper.find(ReltioGridLayout);
|
|
28
|
+
expect(grid.length).toBe(1);
|
|
29
|
+
expect(grid.prop('views')).toEqual(defaultProps.views);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import {makeStyles} from '@mui/styles';
|
|
2
|
+
|
|
3
|
+
const configWidth = 490;
|
|
4
|
+
|
|
5
|
+
export const useStyles = makeStyles({
|
|
6
|
+
pageWrapper: {
|
|
7
|
+
height: '100%',
|
|
8
|
+
overflow: 'hidden',
|
|
9
|
+
display: 'flex',
|
|
10
|
+
flexDirection: 'column'
|
|
11
|
+
},
|
|
12
|
+
content: {
|
|
13
|
+
display: 'flex',
|
|
14
|
+
overflow: 'hidden',
|
|
15
|
+
height: '100%'
|
|
16
|
+
},
|
|
17
|
+
rightPanel: {
|
|
18
|
+
overflowY: 'auto',
|
|
19
|
+
backgroundColor: '#FFF',
|
|
20
|
+
borderLeft: 'solid 1px rgba(0, 0, 0, 0.12)',
|
|
21
|
+
width: `${configWidth}px`,
|
|
22
|
+
minWidth: `${configWidth}px`,
|
|
23
|
+
maxWidth: `${configWidth}px`
|
|
24
|
+
},
|
|
25
|
+
mainPanel: {
|
|
26
|
+
flex: '1 1 auto',
|
|
27
|
+
overflow: 'auto',
|
|
28
|
+
backgroundColor: '#F9F9F9'
|
|
29
|
+
}
|
|
30
|
+
});
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import {ViewsFactory} from '@reltio/dashboard';
|
|
3
|
+
import {ReactGridLayoutItem} from '@reltio/components';
|
|
4
|
+
import {PivotingView} from '../../types';
|
|
5
|
+
|
|
6
|
+
type Props = {
|
|
7
|
+
className?: string;
|
|
8
|
+
isFullscreen: boolean;
|
|
9
|
+
style: React.CSSProperties;
|
|
10
|
+
onToggleFullscreen: (id: string) => void;
|
|
11
|
+
onRemove: (id: string) => void;
|
|
12
|
+
views: PivotingView[];
|
|
13
|
+
layoutItemConfig: ReactGridLayoutItem;
|
|
14
|
+
children: React.ReactNode;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const PivotingLayoutItem = ({
|
|
18
|
+
layoutItemConfig,
|
|
19
|
+
views,
|
|
20
|
+
onToggleFullscreen,
|
|
21
|
+
isFullscreen,
|
|
22
|
+
className,
|
|
23
|
+
onRemove,
|
|
24
|
+
children: ResizeComponent,
|
|
25
|
+
style,
|
|
26
|
+
...props
|
|
27
|
+
}: Props) => {
|
|
28
|
+
const view = views.find(({config}) => layoutItemConfig.i === config.id);
|
|
29
|
+
if (!view) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const {config} = view;
|
|
34
|
+
return (
|
|
35
|
+
<div style={style} className={className} {...props}>
|
|
36
|
+
{ViewsFactory.getComponent({
|
|
37
|
+
type: config.component,
|
|
38
|
+
config,
|
|
39
|
+
onRemove,
|
|
40
|
+
onToggleFullscreen,
|
|
41
|
+
isFullscreen
|
|
42
|
+
})}
|
|
43
|
+
{ResizeComponent}
|
|
44
|
+
</div>
|
|
45
|
+
);
|
|
46
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import {shallow} from 'enzyme';
|
|
3
|
+
|
|
4
|
+
import {PivotingLayoutItem} from '../PivotingLayoutItem';
|
|
5
|
+
import {ViewsFactory} from '@reltio/dashboard';
|
|
6
|
+
|
|
7
|
+
jest.mock('@reltio/dashboard', () => ({
|
|
8
|
+
...jest.requireActual<Record<string, unknown>>('@reltio/dashboard'),
|
|
9
|
+
ViewsFactory: {getComponent: jest.fn().mockImplementation(() => <div className="component">Component</div>)}
|
|
10
|
+
}));
|
|
11
|
+
|
|
12
|
+
describe('PivotingLayoutItem tests', () => {
|
|
13
|
+
const onToggleFullscreenMock = jest.fn();
|
|
14
|
+
const onRemoveMock = jest.fn();
|
|
15
|
+
const views = [
|
|
16
|
+
{
|
|
17
|
+
config: {
|
|
18
|
+
id: 'test1',
|
|
19
|
+
title: 'test',
|
|
20
|
+
component: 'Component',
|
|
21
|
+
config: 'config'
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
];
|
|
25
|
+
const layoutItemConfig = {
|
|
26
|
+
i: 'test1',
|
|
27
|
+
x: 0,
|
|
28
|
+
y: 0,
|
|
29
|
+
w: 100,
|
|
30
|
+
h: 100
|
|
31
|
+
};
|
|
32
|
+
const props = {
|
|
33
|
+
views,
|
|
34
|
+
layoutItemConfig,
|
|
35
|
+
isFullscreen: false,
|
|
36
|
+
style: {},
|
|
37
|
+
onRemove: onRemoveMock,
|
|
38
|
+
onToggleFullscreen: onToggleFullscreenMock
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
it('should create layout item based on config', () => {
|
|
42
|
+
const wrapper = shallow(
|
|
43
|
+
<PivotingLayoutItem {...props}>
|
|
44
|
+
<div className="child">testChild</div>
|
|
45
|
+
</PivotingLayoutItem>
|
|
46
|
+
);
|
|
47
|
+
expect(wrapper.find('DashboardFacet'));
|
|
48
|
+
expect(ViewsFactory.getComponent).toBeCalledWith({
|
|
49
|
+
config: views[0].config,
|
|
50
|
+
isFullscreen: false,
|
|
51
|
+
onToggleFullscreen: onToggleFullscreenMock,
|
|
52
|
+
onRemove: onRemoveMock,
|
|
53
|
+
type: views[0].config.component
|
|
54
|
+
});
|
|
55
|
+
expect(wrapper.find('.component').text()).toBe('Component');
|
|
56
|
+
expect(wrapper.find('.child').text()).toBe('testChild');
|
|
57
|
+
});
|
|
58
|
+
});
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import Avatar from '@mui/material/Avatar';
|
|
2
|
+
import Typography from '@mui/material/Typography';
|
|
3
|
+
import {AttributeType, getLabel} from '@reltio/mdm-sdk';
|
|
4
|
+
import classnames from 'classnames';
|
|
5
|
+
import {isNil} from 'ramda';
|
|
6
|
+
import React from 'react';
|
|
7
|
+
import useStyles from './styles';
|
|
8
|
+
|
|
9
|
+
type Props = {
|
|
10
|
+
label: string;
|
|
11
|
+
attributeType: AttributeType;
|
|
12
|
+
className?: string;
|
|
13
|
+
classes?: Record<string, string>;
|
|
14
|
+
children?: React.ReactNode;
|
|
15
|
+
};
|
|
16
|
+
const PivotingProfileBand = ({label, attributeType, className, classes, children}: Props) => {
|
|
17
|
+
const styles = useStyles({classes});
|
|
18
|
+
|
|
19
|
+
if (isNil(attributeType)) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
return (
|
|
23
|
+
<div className={classnames(styles.profileBandWrapper, className)}>
|
|
24
|
+
<div className={styles.profileBand}>
|
|
25
|
+
<Avatar className={classnames(styles.profileIcon, styles.imageProfileIcon)} />
|
|
26
|
+
<div className={styles.profileInfo}>
|
|
27
|
+
<div>
|
|
28
|
+
<Typography variant="h6" className={styles.label}>
|
|
29
|
+
{getLabel(label)}
|
|
30
|
+
</Typography>
|
|
31
|
+
</div>
|
|
32
|
+
<div className={styles.specialInfo}>
|
|
33
|
+
<div className={styles.attributeType}>
|
|
34
|
+
<Typography className={classnames(styles.badge, className)} component="div">
|
|
35
|
+
{getLabel(attributeType.label)}
|
|
36
|
+
</Typography>
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
40
|
+
{children && <div>{children}</div>}
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export default PivotingProfileBand;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import {shallow} from 'enzyme';
|
|
3
|
+
import PivotingProfileBand from '../PivotingProfileBand';
|
|
4
|
+
import Avatar from '@mui/material/Avatar';
|
|
5
|
+
|
|
6
|
+
describe('Pivoting profile band tests', () => {
|
|
7
|
+
const attributeType = {
|
|
8
|
+
name: 'FirstName',
|
|
9
|
+
uri: 'configuration/entityTypes/HCP/attributes/FirstName',
|
|
10
|
+
type: 'String'
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
it('should render main parts', () => {
|
|
14
|
+
const label = 'testLabel';
|
|
15
|
+
const component = shallow(
|
|
16
|
+
<PivotingProfileBand attributeType={attributeType} label={label}>
|
|
17
|
+
<div className="content">Content</div>
|
|
18
|
+
</PivotingProfileBand>
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
expect(component.find(Avatar)).toHaveLength(1);
|
|
22
|
+
expect(component.find('.label').text()).toBe(label);
|
|
23
|
+
expect(component.find('.content').text()).toBe('Content');
|
|
24
|
+
});
|
|
25
|
+
});
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import {makeStyles} from '@mui/styles';
|
|
2
|
+
|
|
3
|
+
const useStyles = makeStyles((theme) => ({
|
|
4
|
+
profileBandWrapper: {
|
|
5
|
+
// especially for IE (overflow fix)
|
|
6
|
+
flexShrink: 0
|
|
7
|
+
},
|
|
8
|
+
profileBand: {
|
|
9
|
+
display: 'flex',
|
|
10
|
+
padding: '16px 12px 16px 16px',
|
|
11
|
+
alignItems: 'flex-start',
|
|
12
|
+
backgroundColor: '#fff',
|
|
13
|
+
boxShadow: 'inset 0 -1px 0 0 rgba(0,114,206,0.06), inset 0 1px 0 0 rgba(0,114,206,0.06);',
|
|
14
|
+
|
|
15
|
+
'&:after': {
|
|
16
|
+
content: '""',
|
|
17
|
+
minHeight: 'inherit',
|
|
18
|
+
fontSize: 0
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
profileIcon: {
|
|
22
|
+
marginRight: '12px'
|
|
23
|
+
},
|
|
24
|
+
imageProfileIcon: {
|
|
25
|
+
width: '48px',
|
|
26
|
+
height: '48px'
|
|
27
|
+
},
|
|
28
|
+
profileInfo: {
|
|
29
|
+
display: 'flex',
|
|
30
|
+
flexDirection: 'column',
|
|
31
|
+
flex: '1 1 50%'
|
|
32
|
+
},
|
|
33
|
+
label: {
|
|
34
|
+
lineHeight: '1.2',
|
|
35
|
+
letterSpacing: '0.25px',
|
|
36
|
+
color: theme.palette.text.primary,
|
|
37
|
+
wordBreak: 'break-word',
|
|
38
|
+
|
|
39
|
+
'&+ $specialInfo': {
|
|
40
|
+
marginTop: '4px'
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
specialInfo: {
|
|
44
|
+
display: 'flex',
|
|
45
|
+
justifyContent: 'space-between',
|
|
46
|
+
flexWrap: 'wrap',
|
|
47
|
+
width: '100%',
|
|
48
|
+
marginTop: '8px'
|
|
49
|
+
},
|
|
50
|
+
attributeType: {
|
|
51
|
+
display: 'flex',
|
|
52
|
+
flexWrap: 'wrap'
|
|
53
|
+
},
|
|
54
|
+
badge: {
|
|
55
|
+
padding: '2px 8px 3px',
|
|
56
|
+
borderRadius: '2px',
|
|
57
|
+
fontSize: '13px',
|
|
58
|
+
fontWeight: 500,
|
|
59
|
+
lineHeight: '15px',
|
|
60
|
+
display: 'inline-flex',
|
|
61
|
+
marginRight: '16px',
|
|
62
|
+
backgroundColor: '#0071ce',
|
|
63
|
+
color: '#f5f5f5'
|
|
64
|
+
}
|
|
65
|
+
}));
|
|
66
|
+
|
|
67
|
+
export default useStyles;
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import React, {memo, useCallback, useEffect, useMemo, useState} from 'react';
|
|
2
|
+
import {
|
|
3
|
+
AttributePresentations,
|
|
4
|
+
convertPivotingValueToSearchFilters,
|
|
5
|
+
getEntityUriFromAttributeUri,
|
|
6
|
+
PivotingPerspectiveConfig,
|
|
7
|
+
findAttributeTypeByUri,
|
|
8
|
+
getPivotingLabel,
|
|
9
|
+
isNested,
|
|
10
|
+
Metadata,
|
|
11
|
+
PivotingValue,
|
|
12
|
+
PrimitiveValue,
|
|
13
|
+
removeUnsupportedLayoutItems,
|
|
14
|
+
ReltioGridLayoutItem
|
|
15
|
+
} from '@reltio/mdm-sdk';
|
|
16
|
+
import {ReloadFacetProvider, SearchFiltersContext} from '@reltio/components';
|
|
17
|
+
import PivotingLayout from '../components/PivotingLayout/PivotingLayout';
|
|
18
|
+
import {useStyles} from './styles';
|
|
19
|
+
import PivotingProfileBand from '../components/PivotingProfileBand/PivotingProfileBand';
|
|
20
|
+
import {useSelector} from 'react-redux';
|
|
21
|
+
import mdmModule from '@reltio/mdm-module';
|
|
22
|
+
|
|
23
|
+
type Props = {
|
|
24
|
+
config: PivotingPerspectiveConfig;
|
|
25
|
+
attributeTypeUri: string;
|
|
26
|
+
value: PivotingValue[];
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const PivotingPerspective = ({config, attributeTypeUri, value}: Props) => {
|
|
30
|
+
const styles = useStyles();
|
|
31
|
+
const [label, setLabel] = useState('');
|
|
32
|
+
const metadata: Metadata = useSelector(mdmModule.selectors.getMetadata);
|
|
33
|
+
const attributePresentations: AttributePresentations = useSelector(mdmModule.selectors.getAttributePresentations);
|
|
34
|
+
const attributeType = findAttributeTypeByUri(metadata, attributeTypeUri);
|
|
35
|
+
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
getPivotingLabel({attributeType, attributePresentations, value}).then((label) => setLabel(label));
|
|
38
|
+
}, [attributeType, attributePresentations, value]);
|
|
39
|
+
const getFirstValue = (value: PivotingValue[]) => Object.values(value[0])[0];
|
|
40
|
+
const entityTypeUri = getEntityUriFromAttributeUri(attributeTypeUri);
|
|
41
|
+
|
|
42
|
+
const filters = convertPivotingValueToSearchFilters({
|
|
43
|
+
value: isNested(attributeType) ? value : (getFirstValue(value) as PrimitiveValue),
|
|
44
|
+
attributeType,
|
|
45
|
+
entityType: entityTypeUri
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const pivotingViews = useMemo(
|
|
49
|
+
() =>
|
|
50
|
+
config.views.map((view) => ({
|
|
51
|
+
config: view
|
|
52
|
+
})),
|
|
53
|
+
[config]
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
const [layout, setLayout] = useState<ReltioGridLayoutItem[]>();
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
const newLayout = removeUnsupportedLayoutItems(config.layout, config.views);
|
|
59
|
+
setLayout(newLayout);
|
|
60
|
+
}, [config]);
|
|
61
|
+
|
|
62
|
+
const handleLayoutChange = useCallback((layout: ReltioGridLayoutItem[]) => setLayout(layout), []);
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<div className={styles.perspectiveView}>
|
|
66
|
+
<SearchFiltersContext.Provider value={filters}>
|
|
67
|
+
<PivotingProfileBand label={label} attributeType={attributeType} />
|
|
68
|
+
<ReloadFacetProvider>
|
|
69
|
+
{layout && (
|
|
70
|
+
<PivotingLayout layout={layout} onLayoutChanged={handleLayoutChange} views={pivotingViews} />
|
|
71
|
+
)}
|
|
72
|
+
</ReloadFacetProvider>
|
|
73
|
+
</SearchFiltersContext.Provider>
|
|
74
|
+
</div>
|
|
75
|
+
);
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export default memo(PivotingPerspective);
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import {act} from 'react-dom/test-utils';
|
|
3
|
+
import {mount, shallow} from 'enzyme';
|
|
4
|
+
import PivotingLayout from '../../components/PivotingLayout/PivotingLayout';
|
|
5
|
+
import * as reactRedux from 'react-redux';
|
|
6
|
+
import * as mdmSdk from '@reltio/mdm-sdk';
|
|
7
|
+
import mdm from '@reltio/mdm-module';
|
|
8
|
+
import {EmptyStub} from '@reltio/components';
|
|
9
|
+
import {SearchFiltersContext} from '@reltio/components';
|
|
10
|
+
import PivotingPerspective from '../PivotingPerspective';
|
|
11
|
+
|
|
12
|
+
jest.mock('@reltio/mdm-sdk', () => ({
|
|
13
|
+
...jest.requireActual<Record<string, unknown>>('@reltio/mdm-sdk'),
|
|
14
|
+
getPivotingLabel: jest.fn()
|
|
15
|
+
}));
|
|
16
|
+
describe('PivotingPerspective tests', () => {
|
|
17
|
+
const getPivotingLabelSpy = jest.spyOn(mdmSdk, 'getPivotingLabel');
|
|
18
|
+
|
|
19
|
+
const views = [
|
|
20
|
+
{
|
|
21
|
+
component: 'DashboardFacet',
|
|
22
|
+
attributeUri: 'configuration/entityTypes/HCP/attributes/CountryCode',
|
|
23
|
+
chartType: 'bar',
|
|
24
|
+
id: 'MyDashboard',
|
|
25
|
+
title: 'Country codes'
|
|
26
|
+
}
|
|
27
|
+
];
|
|
28
|
+
const attrType = {
|
|
29
|
+
name: 'CountryCode',
|
|
30
|
+
lookupCode: 'COUNTRY_CD',
|
|
31
|
+
uri: 'configuration/entityTypes/Organization/attributes/CountryCode',
|
|
32
|
+
type: 'String'
|
|
33
|
+
};
|
|
34
|
+
const props = {
|
|
35
|
+
config: {
|
|
36
|
+
id: 'com.reltio.plugins.PivotingPerspective',
|
|
37
|
+
layout: [{minHeight: 10, x: 2, width: 2, y: 0, id: 'MyDashboard', height: 10}],
|
|
38
|
+
views,
|
|
39
|
+
margin: undefined
|
|
40
|
+
},
|
|
41
|
+
attributeTypeUri: 'configuration/entityTypes/HCP/attributes/CountryCode',
|
|
42
|
+
value: [{CountryCode: 'BE'}]
|
|
43
|
+
};
|
|
44
|
+
const metadata = {
|
|
45
|
+
entityTypes: [{uri: 'configuration/entityTypes/HCP', name: 'HCP', attributes: [attrType]}]
|
|
46
|
+
};
|
|
47
|
+
let resolveLabel;
|
|
48
|
+
beforeAll(() => {
|
|
49
|
+
jest.spyOn(PivotingLayout, 'type').mockImplementation(EmptyStub);
|
|
50
|
+
jest.spyOn(reactRedux, 'useSelector').mockImplementation((selector) => selector({}));
|
|
51
|
+
jest.spyOn(mdm.selectors, 'getMetadata').mockReturnValue(metadata);
|
|
52
|
+
jest.spyOn(mdm.selectors, 'getAttributePresentations').mockReturnValue({});
|
|
53
|
+
getPivotingLabelSpy.mockImplementation(
|
|
54
|
+
() =>
|
|
55
|
+
new Promise((resolve) => {
|
|
56
|
+
resolveLabel = (value) => resolve(value);
|
|
57
|
+
})
|
|
58
|
+
);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
beforeEach(() => {
|
|
62
|
+
jest.clearAllMocks();
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('should render initial state correctly', () => {
|
|
66
|
+
const wrapper = mount(<PivotingPerspective {...props} />);
|
|
67
|
+
expect(wrapper.find('PivotingProfileBand')).toHaveLength(1);
|
|
68
|
+
expect(wrapper.find('PivotingLayout')).toHaveLength(0);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('should pass correct props to profile band', async () => {
|
|
72
|
+
const wrapper = mount(<PivotingPerspective {...props} />);
|
|
73
|
+
await act(async () => {
|
|
74
|
+
resolveLabel('testLabel');
|
|
75
|
+
});
|
|
76
|
+
wrapper.update();
|
|
77
|
+
const profileBand = wrapper.find('PivotingProfileBand');
|
|
78
|
+
expect(profileBand).toHaveLength(1);
|
|
79
|
+
expect(profileBand.prop('label')).toEqual('testLabel');
|
|
80
|
+
expect(profileBand.prop('attributeType')).toEqual(attrType);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('should pass correct props to context', async () => {
|
|
84
|
+
const wrapper = shallow(<PivotingPerspective {...props} />);
|
|
85
|
+
wrapper.update();
|
|
86
|
+
const contextProvider = wrapper.find(SearchFiltersContext.Provider);
|
|
87
|
+
expect(contextProvider.prop('value')).toEqual([
|
|
88
|
+
{fieldName: 'type', filter: 'equals', values: ['configuration/entityTypes/HCP']},
|
|
89
|
+
{fieldName: 'attributes.CountryCode', filter: 'equals', values: ['BE']}
|
|
90
|
+
]);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('should render dashboard layout correctly', async () => {
|
|
94
|
+
const wrapper = mount(<PivotingPerspective {...props} />);
|
|
95
|
+
wrapper.update();
|
|
96
|
+
expect(wrapper.find(PivotingLayout).props()).toMatchObject({
|
|
97
|
+
views: views.map((view) => ({config: view})),
|
|
98
|
+
layout: props.config.layout
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('should apply layout on change', async () => {
|
|
103
|
+
const wrapper = mount(<PivotingPerspective {...props} />);
|
|
104
|
+
wrapper.update();
|
|
105
|
+
const newLayout = [{minHeight: 10, x: 0, width: 2, y: 2, id: 'MyDashboard', height: 10}];
|
|
106
|
+
act(() => {
|
|
107
|
+
wrapper.find(PivotingLayout).prop('onLayoutChanged')(newLayout);
|
|
108
|
+
});
|
|
109
|
+
wrapper.update();
|
|
110
|
+
expect(wrapper.find(PivotingLayout).prop('layout')).toEqual(newLayout);
|
|
111
|
+
});
|
|
112
|
+
});
|