@dhis2/analytics 25.2.3 → 26.0.0
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/build/cjs/__demo__/FileMenu.stories.js +8 -6
- package/build/cjs/__demo__/Toolbar.stories.js +77 -0
- package/build/cjs/components/FileMenu/FileMenu.js +21 -59
- package/build/cjs/components/FileMenu/__tests__/FileMenu.spec.js +318 -194
- package/build/cjs/components/Options/VisualizationOptions.js +3 -1
- package/build/cjs/components/Toolbar/HoverMenuBar/HoverMenuBar.js +107 -0
- package/build/cjs/components/Toolbar/HoverMenuBar/HoverMenuDropdown.js +66 -0
- package/build/cjs/components/Toolbar/HoverMenuBar/HoverMenuList.js +94 -0
- package/build/cjs/components/Toolbar/HoverMenuBar/HoverMenuListItem.js +99 -0
- package/build/cjs/components/Toolbar/HoverMenuBar/HoverMenuListItem.styles.js +13 -0
- package/build/cjs/components/Toolbar/HoverMenuBar/__tests__/HoverMenuBar.spec.js +219 -0
- package/build/cjs/components/Toolbar/HoverMenuBar/__tests__/HoverMenuDropdown.spec.js +23 -0
- package/build/cjs/components/Toolbar/HoverMenuBar/__tests__/HoverMenuList.spec.js +56 -0
- package/build/cjs/components/Toolbar/HoverMenuBar/__tests__/HoverMenuListItem.spec.js +50 -0
- package/build/cjs/components/Toolbar/HoverMenuBar/index.js +37 -0
- package/build/cjs/components/Toolbar/InterpretationsAndDetailsToggler.js +50 -0
- package/build/cjs/components/Toolbar/MenuButton.styles.js +13 -0
- package/build/cjs/components/Toolbar/Toolbar.js +39 -0
- package/build/cjs/components/Toolbar/ToolbarSidebar.js +45 -0
- package/build/cjs/components/Toolbar/UpdateButton.js +57 -0
- package/build/cjs/components/Toolbar/__tests__/InterpretationsAndDetailsToggler.spec.js +50 -0
- package/build/cjs/components/Toolbar/__tests__/Toolbar.spec.js +24 -0
- package/build/cjs/components/Toolbar/__tests__/ToolbarSidebar.spec.js +30 -0
- package/build/cjs/components/Toolbar/__tests__/UpdateButton.spec.js +44 -0
- package/build/cjs/components/Toolbar/index.js +57 -0
- package/build/cjs/index.js +304 -46
- package/build/cjs/locales/en/translations.json +1 -0
- package/build/es/__demo__/FileMenu.stories.js +7 -6
- package/build/es/__demo__/Toolbar.stories.js +69 -0
- package/build/es/components/FileMenu/FileMenu.js +20 -57
- package/build/es/components/FileMenu/__tests__/FileMenu.spec.js +293 -189
- package/build/es/components/Options/VisualizationOptions.js +3 -1
- package/build/es/components/Toolbar/HoverMenuBar/HoverMenuBar.js +90 -0
- package/build/es/components/Toolbar/HoverMenuBar/HoverMenuDropdown.js +44 -0
- package/build/es/components/Toolbar/HoverMenuBar/HoverMenuList.js +75 -0
- package/build/es/components/Toolbar/HoverMenuBar/HoverMenuListItem.js +78 -0
- package/build/es/components/Toolbar/HoverMenuBar/HoverMenuListItem.styles.js +4 -0
- package/build/es/components/Toolbar/HoverMenuBar/__tests__/HoverMenuBar.spec.js +168 -0
- package/build/es/components/Toolbar/HoverMenuBar/__tests__/HoverMenuDropdown.spec.js +16 -0
- package/build/es/components/Toolbar/HoverMenuBar/__tests__/HoverMenuList.spec.js +49 -0
- package/build/es/components/Toolbar/HoverMenuBar/__tests__/HoverMenuListItem.spec.js +41 -0
- package/build/es/components/Toolbar/HoverMenuBar/index.js +4 -0
- package/build/es/components/Toolbar/InterpretationsAndDetailsToggler.js +33 -0
- package/build/es/components/Toolbar/MenuButton.styles.js +4 -0
- package/build/es/components/Toolbar/Toolbar.js +24 -0
- package/build/es/components/Toolbar/ToolbarSidebar.js +29 -0
- package/build/es/components/Toolbar/UpdateButton.js +38 -0
- package/build/es/components/Toolbar/__tests__/InterpretationsAndDetailsToggler.spec.js +43 -0
- package/build/es/components/Toolbar/__tests__/Toolbar.spec.js +17 -0
- package/build/es/components/Toolbar/__tests__/ToolbarSidebar.spec.js +23 -0
- package/build/es/components/Toolbar/__tests__/UpdateButton.spec.js +37 -0
- package/build/es/components/Toolbar/index.js +5 -0
- package/build/es/index.js +1 -0
- package/build/es/locales/en/translations.json +1 -0
- package/package.json +3 -1
- package/CHANGELOG.md +0 -4072
|
@@ -1,16 +1,19 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { CustomDataProvider } from '@dhis2/app-runtime';
|
|
2
|
+
import { render, fireEvent, screen, getByText } from '@testing-library/react';
|
|
3
|
+
import '@testing-library/jest-dom';
|
|
3
4
|
import React from 'react';
|
|
4
|
-
import {
|
|
5
|
-
import { TranslationDialog } from '../../TranslationDialog/index.js';
|
|
6
|
-
import { DeleteDialog } from '../DeleteDialog.js';
|
|
5
|
+
import { HoverMenuBar } from '../../Toolbar/index.js';
|
|
7
6
|
import { FileMenu } from '../FileMenu.js';
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
jest.mock('../../TranslationDialog/TranslationModal/useTranslationsResults.js', () => ({
|
|
8
|
+
/* This will keep the translation dialog in
|
|
9
|
+
* a loading state, which prevents it from
|
|
10
|
+
* throwing other errors */
|
|
11
|
+
useTranslationsResults: () => ({
|
|
12
|
+
translationsData: undefined,
|
|
13
|
+
fetching: true
|
|
14
|
+
})
|
|
15
|
+
}));
|
|
11
16
|
describe('The FileMenu component ', () => {
|
|
12
|
-
let shallowFileMenu;
|
|
13
|
-
let props;
|
|
14
17
|
const onDelete = jest.fn();
|
|
15
18
|
const onError = jest.fn();
|
|
16
19
|
const onNew = jest.fn();
|
|
@@ -20,206 +23,307 @@ describe('The FileMenu component ', () => {
|
|
|
20
23
|
const onSaveAs = jest.fn();
|
|
21
24
|
const onShare = jest.fn();
|
|
22
25
|
const onTranslate = jest.fn();
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
const baseProps = {
|
|
27
|
+
currentUser: {
|
|
28
|
+
id: 'u1',
|
|
29
|
+
displayName: 'Test user'
|
|
30
|
+
},
|
|
31
|
+
fileType: 'visualization',
|
|
32
|
+
fileObject: undefined,
|
|
33
|
+
onDelete,
|
|
34
|
+
onError,
|
|
35
|
+
onNew,
|
|
36
|
+
onOpen,
|
|
37
|
+
onRename,
|
|
38
|
+
onSave,
|
|
39
|
+
onSaveAs,
|
|
40
|
+
onShare,
|
|
41
|
+
onTranslate
|
|
30
42
|
};
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
shallowFileMenu = undefined;
|
|
34
|
-
props = {
|
|
35
|
-
currentUser: {
|
|
36
|
-
id: 'u1',
|
|
37
|
-
displayName: 'Test user'
|
|
38
|
-
},
|
|
39
|
-
fileType: 'visualization',
|
|
40
|
-
fileObject: undefined,
|
|
41
|
-
onDelete,
|
|
42
|
-
onError,
|
|
43
|
-
onNew,
|
|
44
|
-
onOpen,
|
|
45
|
-
onRename,
|
|
46
|
-
onSave,
|
|
47
|
-
onSaveAs,
|
|
48
|
-
onShare,
|
|
49
|
-
onTranslate
|
|
50
|
-
};
|
|
51
|
-
});
|
|
52
|
-
it('renders a button', () => {
|
|
53
|
-
expect(getFileMenuComponent(props).find('button')).toHaveLength(1);
|
|
54
|
-
});
|
|
55
|
-
it('renders some enabled buttons regardless of the access settings', () => {
|
|
56
|
-
const fileMenuComponent = getFileMenuComponent(props);
|
|
57
|
-
fileMenuComponent.find('button').simulate('click');
|
|
58
|
-
const buttonLabels = ['New', 'Open…'];
|
|
59
|
-
buttonLabels.forEach(buttonLabel => expect(fileMenuComponent.findWhere(n => n.prop('label') === buttonLabel).prop('disabled')).toBe(undefined));
|
|
60
|
-
});
|
|
61
|
-
it('renders some disabled buttons when no fileObject is present', () => {
|
|
62
|
-
const fileMenuComponent = getFileMenuComponent(props);
|
|
63
|
-
fileMenuComponent.find('button').simulate('click');
|
|
64
|
-
const buttonLabels = ['Save as…', 'Rename…', 'Translate…', 'Share…', 'Get link…', 'Delete'];
|
|
65
|
-
buttonLabels.forEach(buttonLabel => expect(fileMenuComponent.findWhere(n => n.prop('label') === buttonLabel).prop('disabled')).toBe(true));
|
|
66
|
-
});
|
|
67
|
-
it('renders some enabled buttons when update access is granted', () => {
|
|
68
|
-
props.fileObject = {
|
|
69
|
-
id: 'test',
|
|
70
|
-
access: {
|
|
71
|
-
delete: false,
|
|
72
|
-
manage: false,
|
|
73
|
-
update: true
|
|
74
|
-
}
|
|
75
|
-
};
|
|
76
|
-
const fileMenuComponent = getFileMenuComponent(props);
|
|
77
|
-
fileMenuComponent.find('button').simulate('click');
|
|
78
|
-
const buttonLabels = ['Save', 'Rename…', 'Translate…'];
|
|
79
|
-
buttonLabels.forEach(buttonLabel => expect(fileMenuComponent.findWhere(n => n.prop('label') === buttonLabel).prop('disabled')).toBe(false));
|
|
80
|
-
});
|
|
81
|
-
it('renders enabled Delete button when delete access is granted', () => {
|
|
82
|
-
props.fileObject = {
|
|
83
|
-
id: 'test',
|
|
84
|
-
access: {
|
|
85
|
-
delete: true,
|
|
86
|
-
manage: false,
|
|
87
|
-
update: false
|
|
88
|
-
}
|
|
89
|
-
};
|
|
90
|
-
const fileMenuComponent = getFileMenuComponent(props);
|
|
91
|
-
fileMenuComponent.find('button').simulate('click');
|
|
92
|
-
expect(fileMenuComponent.findWhere(n => n.prop('label') === 'Delete').prop('disabled')).toBe(false);
|
|
93
|
-
});
|
|
94
|
-
it('renders enabled Share button when manage access is granted', () => {
|
|
95
|
-
props.fileObject = {
|
|
96
|
-
id: 'test',
|
|
97
|
-
access: {
|
|
98
|
-
delete: false,
|
|
99
|
-
manage: true,
|
|
100
|
-
update: false
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
const fileMenuComponent = getFileMenuComponent(props);
|
|
104
|
-
fileMenuComponent.find('button').simulate('click');
|
|
105
|
-
expect(fileMenuComponent.findWhere(n => n.prop('label') === 'Share…').prop('disabled')).toBe(false);
|
|
106
|
-
});
|
|
107
|
-
it('renders the OpenFileDialog component when the Open button is clicked', () => {
|
|
108
|
-
const fileMenuComponent = getFileMenuComponent(props);
|
|
109
|
-
fileMenuComponent.find('button').simulate('click');
|
|
110
|
-
fileMenuComponent.findWhere(n => n.prop('label') === 'Open…').simulate('click');
|
|
111
|
-
expect(fileMenuComponent.find(OpenFileDialog)).toHaveLength(1);
|
|
112
|
-
});
|
|
113
|
-
it('renders the RenameDialog when the Rename button is clicked', () => {
|
|
114
|
-
props.fileObject = {
|
|
43
|
+
const fullAccessProps = {
|
|
44
|
+
fileObject: {
|
|
115
45
|
id: 'test',
|
|
116
46
|
access: {
|
|
117
47
|
delete: true,
|
|
118
48
|
manage: true,
|
|
119
49
|
update: true
|
|
120
|
-
}
|
|
50
|
+
},
|
|
51
|
+
href: 'http://dhis2.org'
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const renderFileMenu = function () {
|
|
56
|
+
let customProps = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
57
|
+
const props = { ...baseProps,
|
|
58
|
+
...customProps
|
|
121
59
|
};
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
60
|
+
const providerData = {
|
|
61
|
+
translations: {
|
|
62
|
+
translations: {}
|
|
63
|
+
},
|
|
64
|
+
sharing: {
|
|
65
|
+
meta: {
|
|
66
|
+
allowPublicAccess: true
|
|
67
|
+
},
|
|
68
|
+
object: {
|
|
69
|
+
userAccesses: [],
|
|
70
|
+
userGroupAccesses: []
|
|
71
|
+
}
|
|
134
72
|
}
|
|
135
73
|
};
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
74
|
+
return render( /*#__PURE__*/React.createElement(CustomDataProvider, {
|
|
75
|
+
data: providerData
|
|
76
|
+
}, /*#__PURE__*/React.createElement(HoverMenuBar, null, /*#__PURE__*/React.createElement(FileMenu, props))));
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const openDropdown = async () => {
|
|
80
|
+
fireEvent.click(screen.getByTestId('dhis2-analytics-hovermenudropdown'));
|
|
81
|
+
expect(await screen.findByTestId('file-menu-container')).toBeVisible();
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const MENU_ITEMS = {
|
|
85
|
+
NEW: {
|
|
86
|
+
testId: 'file-menu-new',
|
|
87
|
+
text: 'New'
|
|
88
|
+
},
|
|
89
|
+
OPEN: {
|
|
90
|
+
testId: 'file-menu-open',
|
|
91
|
+
text: 'Open…'
|
|
92
|
+
},
|
|
93
|
+
SAVE: {
|
|
94
|
+
testId: 'file-menu-save',
|
|
95
|
+
text: 'Save'
|
|
96
|
+
},
|
|
97
|
+
SAVE_AS: {
|
|
98
|
+
testId: 'file-menu-saveas',
|
|
99
|
+
text: 'Save as…'
|
|
100
|
+
},
|
|
101
|
+
RENAME: {
|
|
102
|
+
testId: 'file-menu-rename',
|
|
103
|
+
text: 'Rename…'
|
|
104
|
+
},
|
|
105
|
+
TRANSLATE: {
|
|
106
|
+
testId: 'file-menu-translate',
|
|
107
|
+
text: 'Translate…'
|
|
108
|
+
},
|
|
109
|
+
SHARE: {
|
|
110
|
+
testId: 'file-menu-sharing',
|
|
111
|
+
text: 'Share…'
|
|
112
|
+
},
|
|
113
|
+
GET_LINK: {
|
|
114
|
+
testId: 'file-menu-getlink',
|
|
115
|
+
text: 'Get link…'
|
|
116
|
+
},
|
|
117
|
+
DELETE: {
|
|
118
|
+
testId: 'file-menu-delete',
|
|
119
|
+
text: 'Delete'
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
const assertMenuItemsDisabledState = menuItems => {
|
|
124
|
+
for (const menuTitem of menuItems) {
|
|
125
|
+
const li = screen.getByTestId(menuTitem.testId);
|
|
126
|
+
expect(getByText(li, menuTitem.text)).toBeVisible();
|
|
127
|
+
|
|
128
|
+
if (menuTitem.disabled) {
|
|
129
|
+
expect(li).toHaveClass('disabled');
|
|
130
|
+
} else {
|
|
131
|
+
expect(li).not.toHaveClass('disabled');
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
it('renders a button', () => {
|
|
137
|
+
renderFileMenu();
|
|
138
|
+
expect(screen.getAllByTestId('dhis2-analytics-hovermenudropdown')).toHaveLength(1);
|
|
139
|
+
const button = screen.getByTestId('dhis2-analytics-hovermenudropdown');
|
|
140
|
+
expect(button).toBeVisible();
|
|
141
|
+
expect(button).toHaveTextContent('File');
|
|
140
142
|
});
|
|
141
|
-
it('
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
143
|
+
it('opens when clicking the button', async () => {
|
|
144
|
+
renderFileMenu();
|
|
145
|
+
expect(screen.queryByTestId('file-menu-container')).not.toBeInTheDocument();
|
|
146
|
+
await openDropdown();
|
|
147
|
+
expect(await screen.findByTestId('file-menu-container')).toBeVisible();
|
|
148
|
+
});
|
|
149
|
+
it('renders some enabled buttons regardless of the access settings', async () => {
|
|
150
|
+
renderFileMenu();
|
|
151
|
+
await openDropdown();
|
|
152
|
+
assertMenuItemsDisabledState([{ ...MENU_ITEMS.NEW,
|
|
153
|
+
disabled: false
|
|
154
|
+
}, { ...MENU_ITEMS.OPEN,
|
|
155
|
+
disabled: false
|
|
156
|
+
}]);
|
|
157
|
+
});
|
|
158
|
+
it('renders some disabled buttons when no fileObject is present', async () => {
|
|
159
|
+
renderFileMenu();
|
|
160
|
+
await openDropdown();
|
|
161
|
+
assertMenuItemsDisabledState([{ ...MENU_ITEMS.SAVE_AS,
|
|
162
|
+
disabled: true
|
|
163
|
+
}, { ...MENU_ITEMS.RENAME,
|
|
164
|
+
disabled: true
|
|
165
|
+
}, { ...MENU_ITEMS.TRANSLATE,
|
|
166
|
+
disabled: true
|
|
167
|
+
}, { ...MENU_ITEMS.SHARE,
|
|
168
|
+
disabled: true
|
|
169
|
+
}, { ...MENU_ITEMS.GET_LINK,
|
|
170
|
+
disabled: true
|
|
171
|
+
}, { ...MENU_ITEMS.DELETE,
|
|
172
|
+
disabled: true
|
|
173
|
+
}]);
|
|
174
|
+
});
|
|
175
|
+
it('renders some enabled buttons when update access is granted', async () => {
|
|
176
|
+
const customProps = {
|
|
177
|
+
fileObject: {
|
|
178
|
+
id: 'test',
|
|
179
|
+
access: {
|
|
180
|
+
delete: false,
|
|
181
|
+
manage: false,
|
|
182
|
+
update: true
|
|
183
|
+
}
|
|
148
184
|
}
|
|
149
185
|
};
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
186
|
+
renderFileMenu(customProps);
|
|
187
|
+
await openDropdown();
|
|
188
|
+
assertMenuItemsDisabledState([{ ...MENU_ITEMS.SAVE,
|
|
189
|
+
disabled: false
|
|
190
|
+
}, { ...MENU_ITEMS.RENAME,
|
|
191
|
+
disabled: false
|
|
192
|
+
}, { ...MENU_ITEMS.TRANSLATE,
|
|
193
|
+
disabled: false
|
|
194
|
+
}]);
|
|
154
195
|
});
|
|
155
|
-
it('renders
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
196
|
+
it('renders enabled Delete button when delete access is granted', async () => {
|
|
197
|
+
const customProps = {
|
|
198
|
+
fileObject: {
|
|
199
|
+
id: 'test',
|
|
200
|
+
access: {
|
|
201
|
+
delete: true,
|
|
202
|
+
manage: false,
|
|
203
|
+
update: false
|
|
204
|
+
}
|
|
162
205
|
}
|
|
163
206
|
};
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
207
|
+
renderFileMenu(customProps);
|
|
208
|
+
await openDropdown();
|
|
209
|
+
assertMenuItemsDisabledState([{ ...MENU_ITEMS.DELETE,
|
|
210
|
+
disabled: false
|
|
211
|
+
}]);
|
|
212
|
+
});
|
|
213
|
+
it('renders enabled Share button when manage access is granted', async () => {
|
|
214
|
+
const customProps = {
|
|
215
|
+
fileObject: {
|
|
216
|
+
id: 'test',
|
|
217
|
+
access: {
|
|
218
|
+
delete: false,
|
|
219
|
+
manage: true,
|
|
220
|
+
update: false
|
|
221
|
+
}
|
|
176
222
|
}
|
|
177
223
|
};
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
224
|
+
renderFileMenu(customProps);
|
|
225
|
+
await openDropdown();
|
|
226
|
+
assertMenuItemsDisabledState([{ ...MENU_ITEMS.SHARE,
|
|
227
|
+
disabled: false
|
|
228
|
+
}]);
|
|
182
229
|
});
|
|
183
|
-
it('renders the
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
230
|
+
it('renders the OpenFileDialog component when the Open button is clicked', async () => {
|
|
231
|
+
renderFileMenu();
|
|
232
|
+
await openDropdown();
|
|
233
|
+
fireEvent.click(screen.getByTestId(MENU_ITEMS.OPEN.testId));
|
|
234
|
+
expect(await screen.findByText('Open a visualization', {
|
|
235
|
+
selector: 'h1'
|
|
236
|
+
})).toBeVisible();
|
|
237
|
+
});
|
|
238
|
+
it('renders the RenameDialog when the Rename button is clicked', async () => {
|
|
239
|
+
renderFileMenu(fullAccessProps);
|
|
240
|
+
await openDropdown();
|
|
241
|
+
fireEvent.click(screen.getByTestId(MENU_ITEMS.RENAME.testId));
|
|
242
|
+
expect(await screen.findByText('Rename visualization', {
|
|
243
|
+
selector: 'h1'
|
|
244
|
+
})).toBeVisible();
|
|
245
|
+
});
|
|
246
|
+
it('renders the TranslationDialog when the Translate button is clicked', async () => {
|
|
247
|
+
renderFileMenu(fullAccessProps);
|
|
248
|
+
await openDropdown();
|
|
249
|
+
fireEvent.click(screen.getByTestId(MENU_ITEMS.TRANSLATE.testId));
|
|
250
|
+
expect(await screen.findByText('Translate', {
|
|
251
|
+
exact: false,
|
|
252
|
+
selector: 'h1'
|
|
253
|
+
})).toBeVisible();
|
|
254
|
+
});
|
|
255
|
+
it('renders the SharingDialog when the Share button is clicked', async () => {
|
|
256
|
+
renderFileMenu(fullAccessProps);
|
|
257
|
+
await openDropdown();
|
|
258
|
+
fireEvent.click(screen.getByTestId(MENU_ITEMS.SHARE.testId));
|
|
259
|
+
expect(await screen.findByText('Sharing and access', {
|
|
260
|
+
selector: 'h1'
|
|
261
|
+
})).toBeVisible();
|
|
262
|
+
});
|
|
263
|
+
it('renders the GetLinkDialog when the Get link button is clicked', async () => {
|
|
264
|
+
const url = 'http://localhost/dhis-web-data-visualizer/#/test';
|
|
265
|
+
renderFileMenu(fullAccessProps);
|
|
266
|
+
await openDropdown();
|
|
267
|
+
fireEvent.click(screen.getByTestId(MENU_ITEMS.GET_LINK.testId));
|
|
268
|
+
expect(await screen.findByTestId('dhis2-uicore-modal')).toBeVisible();
|
|
269
|
+
expect(screen.getByRole('link', {
|
|
270
|
+
name: url
|
|
271
|
+
})).toHaveAttribute('href', url);
|
|
272
|
+
});
|
|
273
|
+
it('renders the DeleteDialog when the Delete button is clicked', async () => {
|
|
274
|
+
const customProps = {
|
|
275
|
+
fileObject: {
|
|
276
|
+
id: 'delete-test',
|
|
277
|
+
access: {
|
|
278
|
+
delete: true,
|
|
279
|
+
manage: true,
|
|
280
|
+
update: true
|
|
281
|
+
}
|
|
190
282
|
}
|
|
191
283
|
};
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
expect(
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
284
|
+
renderFileMenu(customProps);
|
|
285
|
+
await openDropdown();
|
|
286
|
+
fireEvent.click(screen.getByTestId(MENU_ITEMS.DELETE.testId));
|
|
287
|
+
expect(await screen.findByText('Delete visualization', {
|
|
288
|
+
selector: 'h1'
|
|
289
|
+
})).toBeVisible();
|
|
290
|
+
});
|
|
291
|
+
it('renders the SaveAsDialog when the Save as… button is clicked', async () => {
|
|
292
|
+
renderFileMenu(fullAccessProps);
|
|
293
|
+
await openDropdown();
|
|
294
|
+
fireEvent.click(screen.getByTestId(MENU_ITEMS.SAVE_AS.testId));
|
|
295
|
+
expect(await screen.findByText('Save visualization as', {
|
|
296
|
+
selector: 'h1'
|
|
297
|
+
})).toBeVisible();
|
|
298
|
+
});
|
|
299
|
+
it('renders the SaveAsDialog when the Save… button is clicked but no fileObject is present', async () => {
|
|
300
|
+
const customProps = {
|
|
301
|
+
fileObject: {
|
|
302
|
+
// NOTE: no `id` field
|
|
303
|
+
access: {
|
|
304
|
+
update: true
|
|
305
|
+
}
|
|
210
306
|
}
|
|
211
307
|
};
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
expect(
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
expect(
|
|
308
|
+
renderFileMenu(customProps);
|
|
309
|
+
await openDropdown();
|
|
310
|
+
fireEvent.click(screen.getByTestId(MENU_ITEMS.SAVE.testId));
|
|
311
|
+
expect(await screen.findByText('Save visualization as', {
|
|
312
|
+
selector: 'h1'
|
|
313
|
+
})).toBeVisible();
|
|
314
|
+
});
|
|
315
|
+
it('calls the onSave callback when the Save button is clicked and a fileObject is present', async () => {
|
|
316
|
+
renderFileMenu(fullAccessProps);
|
|
317
|
+
await openDropdown();
|
|
318
|
+
fireEvent.click(screen.getByTestId(MENU_ITEMS.SAVE.testId));
|
|
319
|
+
expect(screen.queryByText('Open a visualization')).not.toBeVisible();
|
|
320
|
+
expect(onSave).toHaveBeenCalledTimes(1);
|
|
321
|
+
});
|
|
322
|
+
it('calls the onNew callback when the New button is clicked', async () => {
|
|
323
|
+
renderFileMenu();
|
|
324
|
+
await openDropdown();
|
|
325
|
+
fireEvent.click(screen.getByTestId(MENU_ITEMS.NEW.testId));
|
|
326
|
+
expect(screen.queryByText('Open a visualization')).not.toBeVisible();
|
|
327
|
+
expect(onNew).toHaveBeenCalledTimes(1);
|
|
224
328
|
});
|
|
225
329
|
});
|
|
@@ -6,11 +6,12 @@ import { modalContent, tabSection, tabSectionTitle, tabSectionTitleMargin, tabSe
|
|
|
6
6
|
|
|
7
7
|
const VisualizationOptions = _ref => {
|
|
8
8
|
let {
|
|
9
|
+
initiallyActiveTabKey,
|
|
9
10
|
optionsConfig,
|
|
10
11
|
onClose,
|
|
11
12
|
onUpdate
|
|
12
13
|
} = _ref;
|
|
13
|
-
const [activeTabKey, setActiveTabKey] = useState();
|
|
14
|
+
const [activeTabKey, setActiveTabKey] = useState(initiallyActiveTabKey);
|
|
14
15
|
|
|
15
16
|
const generateTabContent = sections => sections.map(_ref2 => {
|
|
16
17
|
let {
|
|
@@ -92,6 +93,7 @@ const VisualizationOptions = _ref => {
|
|
|
92
93
|
|
|
93
94
|
VisualizationOptions.propTypes = {
|
|
94
95
|
optionsConfig: PropTypes.array.isRequired,
|
|
96
|
+
initiallyActiveTabKey: PropTypes.string,
|
|
95
97
|
onClose: PropTypes.func,
|
|
96
98
|
onUpdate: PropTypes.func
|
|
97
99
|
};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import _JSXStyle from "styled-jsx/style";
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import React, { createContext, useCallback, useContext, useEffect, useState } from 'react';
|
|
4
|
+
|
|
5
|
+
const throwErrorIfNotInitialized = () => {
|
|
6
|
+
throw new Error('`HoverMenubarContext` has not been initialised');
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
const HoverMenubarContext = /*#__PURE__*/createContext({
|
|
10
|
+
closeMenu: throwErrorIfNotInitialized,
|
|
11
|
+
onDropDownButtonClick: throwErrorIfNotInitialized,
|
|
12
|
+
onDropDownButtonMouseOver: throwErrorIfNotInitialized,
|
|
13
|
+
setLastHoveredSubMenuEl: throwErrorIfNotInitialized,
|
|
14
|
+
openedDropdownEl: null
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
const useHoverMenubarContext = () => useContext(HoverMenubarContext);
|
|
18
|
+
|
|
19
|
+
const HoverMenuBar = _ref => {
|
|
20
|
+
let {
|
|
21
|
+
children,
|
|
22
|
+
dataTest
|
|
23
|
+
} = _ref;
|
|
24
|
+
const [openedDropdownEl, setOpenedDropdownEl] = useState(null);
|
|
25
|
+
const [lastHoveredSubMenuEl, setLastHoveredSubMenuEl] = useState(null);
|
|
26
|
+
const [isInHoverMode, setIsInHoverMode] = useState(false);
|
|
27
|
+
const closeMenu = useCallback(() => {
|
|
28
|
+
setIsInHoverMode(false);
|
|
29
|
+
setOpenedDropdownEl(null);
|
|
30
|
+
}, []);
|
|
31
|
+
const onDocumentClick = useCallback(event => {
|
|
32
|
+
const isClickOnOpenedSubMenuAnchor = lastHoveredSubMenuEl && (lastHoveredSubMenuEl === event.target || lastHoveredSubMenuEl.contains(event.target));
|
|
33
|
+
|
|
34
|
+
if (!isClickOnOpenedSubMenuAnchor) {
|
|
35
|
+
closeMenu();
|
|
36
|
+
}
|
|
37
|
+
}, [closeMenu, lastHoveredSubMenuEl]);
|
|
38
|
+
const onDropDownButtonClick = useCallback(event => {
|
|
39
|
+
if (!isInHoverMode) {
|
|
40
|
+
setIsInHoverMode(true);
|
|
41
|
+
setOpenedDropdownEl(event.currentTarget);
|
|
42
|
+
} else {
|
|
43
|
+
closeMenu();
|
|
44
|
+
}
|
|
45
|
+
}, [closeMenu, isInHoverMode]);
|
|
46
|
+
const onDropDownButtonMouseOver = useCallback(event => {
|
|
47
|
+
if (isInHoverMode) {
|
|
48
|
+
setOpenedDropdownEl(event.currentTarget);
|
|
49
|
+
}
|
|
50
|
+
}, [isInHoverMode]);
|
|
51
|
+
const closeMenuWithEsc = useCallback(event => {
|
|
52
|
+
if (event.keyCode === 27) {
|
|
53
|
+
closeMenu();
|
|
54
|
+
}
|
|
55
|
+
}, [closeMenu]);
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
if (isInHoverMode) {
|
|
58
|
+
document.addEventListener('click', onDocumentClick, {
|
|
59
|
+
once: true
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return () => {
|
|
64
|
+
document.removeEventListener('click', onDocumentClick);
|
|
65
|
+
};
|
|
66
|
+
}, [onDocumentClick, isInHoverMode]);
|
|
67
|
+
return /*#__PURE__*/React.createElement(HoverMenubarContext.Provider, {
|
|
68
|
+
value: {
|
|
69
|
+
onDropDownButtonClick,
|
|
70
|
+
onDropDownButtonMouseOver,
|
|
71
|
+
openedDropdownEl,
|
|
72
|
+
setLastHoveredSubMenuEl
|
|
73
|
+
}
|
|
74
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
75
|
+
onKeyDown: closeMenuWithEsc,
|
|
76
|
+
"data-test": dataTest,
|
|
77
|
+
className: "jsx-3020154784"
|
|
78
|
+
}, children, /*#__PURE__*/React.createElement(_JSXStyle, {
|
|
79
|
+
id: "3020154784"
|
|
80
|
+
}, [".jsx-3020154784{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-flex:1;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1;}"])));
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
HoverMenuBar.defaultProps = {
|
|
84
|
+
dataTest: 'dhis2-analytics-hovermenubar'
|
|
85
|
+
};
|
|
86
|
+
HoverMenuBar.propTypes = {
|
|
87
|
+
children: PropTypes.node.isRequired,
|
|
88
|
+
dataTest: PropTypes.string
|
|
89
|
+
};
|
|
90
|
+
export { HoverMenuBar, useHoverMenubarContext };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import _JSXStyle from "styled-jsx/style";
|
|
2
|
+
import { Popper } from '@dhis2-ui/popper';
|
|
3
|
+
import { Portal } from '@dhis2-ui/portal';
|
|
4
|
+
import PropTypes from 'prop-types';
|
|
5
|
+
import React, { useRef } from 'react';
|
|
6
|
+
import menuButtonStyles from '../MenuButton.styles.js';
|
|
7
|
+
import { useHoverMenubarContext } from './HoverMenuBar.js';
|
|
8
|
+
export const HoverMenuDropdown = _ref => {
|
|
9
|
+
let {
|
|
10
|
+
children,
|
|
11
|
+
label,
|
|
12
|
+
dataTest,
|
|
13
|
+
disabled
|
|
14
|
+
} = _ref;
|
|
15
|
+
const buttonRef = useRef();
|
|
16
|
+
const {
|
|
17
|
+
onDropDownButtonClick,
|
|
18
|
+
onDropDownButtonMouseOver,
|
|
19
|
+
openedDropdownEl
|
|
20
|
+
} = useHoverMenubarContext();
|
|
21
|
+
const isOpen = openedDropdownEl === buttonRef.current;
|
|
22
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("button", {
|
|
23
|
+
ref: buttonRef,
|
|
24
|
+
onClick: onDropDownButtonClick,
|
|
25
|
+
disabled: disabled,
|
|
26
|
+
onMouseOver: disabled ? undefined : onDropDownButtonMouseOver,
|
|
27
|
+
"data-test": dataTest,
|
|
28
|
+
className: "jsx-".concat(menuButtonStyles.__hash)
|
|
29
|
+
}, label, /*#__PURE__*/React.createElement(_JSXStyle, {
|
|
30
|
+
id: menuButtonStyles.__hash
|
|
31
|
+
}, menuButtonStyles)), isOpen && /*#__PURE__*/React.createElement(Portal, null, /*#__PURE__*/React.createElement(Popper, {
|
|
32
|
+
placement: "bottom-start",
|
|
33
|
+
reference: buttonRef
|
|
34
|
+
}, children)));
|
|
35
|
+
};
|
|
36
|
+
HoverMenuDropdown.defaultProps = {
|
|
37
|
+
dataTest: 'dhis2-analytics-hovermenudropdown'
|
|
38
|
+
};
|
|
39
|
+
HoverMenuDropdown.propTypes = {
|
|
40
|
+
children: PropTypes.node.isRequired,
|
|
41
|
+
label: PropTypes.node.isRequired,
|
|
42
|
+
dataTest: PropTypes.string,
|
|
43
|
+
disabled: PropTypes.bool
|
|
44
|
+
};
|