Skip to content

Commit 80386fa

Browse files
Merge pull request #798 from neo4j-labs/develop
2.4.3 Release
2 parents 25b225d + f700e57 commit 80386fa

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+1834
-198
lines changed

.github/workflows/master-deployment.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ jobs:
7979
context: .
8080
file: ./Dockerfile
8181
push: true
82-
tags: ${{ secrets.DOCKER_HUB_LABS_USERNAME }}/neodash:latest,${{ secrets.DOCKER_HUB_LABS_USERNAME }}/neodash:2.4.2
82+
tags: ${{ secrets.DOCKER_HUB_LABS_USERNAME }}/neodash:latest,${{ secrets.DOCKER_HUB_LABS_USERNAME }}/neodash:2.4.3
8383
build-docker-legacy:
8484
needs: build-test
8585
runs-on: neodash-runners
@@ -103,7 +103,7 @@ jobs:
103103
context: .
104104
file: ./Dockerfile
105105
push: true
106-
tags: ${{ secrets.DOCKER_HUB_USERNAME }}/neodash:latest,${{ secrets.DOCKER_HUB_USERNAME }}/neodash:2.4.2
106+
tags: ${{ secrets.DOCKER_HUB_USERNAME }}/neodash:latest,${{ secrets.DOCKER_HUB_USERNAME }}/neodash:2.4.3
107107
deploy-gallery:
108108
runs-on: neodash-runners
109109
strategy:

.gitignore

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ target
1515
/coverage
1616
/.nyc_output
1717
cypress/videos
18-
18+
cypress/screenshots
1919
# production
2020
/build
2121
/dist

Dockerfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,4 @@ USER nginx
4343
EXPOSE $NGINX_PORT
4444

4545
HEALTHCHECK cmd curl --fail "http://localhost:$NGINX_PORT" || exit 1
46-
LABEL version="2.4.2"
46+
LABEL version="2.4.3"

changelog.md

+29
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,32 @@
1+
## NeoDash 2.4.3
2+
This release contains several improvements and additions to multi-dashboard management, as well as a bug fixes and a variety of quality-of-life improvements:
3+
4+
Dashboard management and access control:
5+
- Added a UI for handling dashboard access using RBAC, as well as a new extension to simply access control.
6+
- Added button to sidebar to refresh the list of dashboards saved in the database.
7+
- Improved handling and detection of draft dashboards in the dashboard sidebar.
8+
9+
Other improvements:
10+
- Changed CSV export functionality for tables to use UTF-8 format.
11+
- Various improvements / fixes to the documentation to include new images, and up-to-date functionality.
12+
- Added logic for handling refresh tokens when connected to NeoDash via SSO.
13+
- Incorporated tooltips for bar charts with and without custom labels.
14+
15+
Bug fixes and testing:
16+
- Implemented bug fixes on type casting for numeric parameter selectors.
17+
- Fixed issue with report actions not functioning properly on node click events.
18+
- Extended test suite with Cypress tests for advanced settings in the bar chart.
19+
20+
Thanks to all the contributors for this release:
21+
- [OskarDamkjaer](https://github.com/OskarDamkjaer)
22+
- [alfredorubin96](https://github.com/alfredorubin96),
23+
- [AleSim94](https://github.com/AleSim94),
24+
- [BennuFire](https://github.com/BennuFire),
25+
- [jacobbleakley-neo4j](https://github.com/jacobbleakley-neo4j),
26+
- [josepmonclus](https://github.com/josepmonclus)
27+
- [nielsdejong](https://github.com/nielsdejong)
28+
29+
130
## NeoDash 2.4.2
231
This is a release with a large amount of quality of life improvements, as well as some new features:
332

cypress/e2e/bar_chart.cy.js

+316
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,316 @@
1+
import { barChartCypherQuery } from '../fixtures/cypher_queries';
2+
3+
const WAITING_TIME = 20000;
4+
// Ignore warnings that may appear when using the Cypress dev server
5+
Cypress.on('uncaught:exception', (err, runnable) => {
6+
console.log(err, runnable);
7+
return false;
8+
});
9+
10+
describe('Testing bar chart', () => {
11+
beforeEach('open neodash', () => {
12+
cy.viewport(1920, 1080);
13+
cy.visit('/', {
14+
onBeforeLoad(win) {
15+
win.localStorage.clear();
16+
},
17+
});
18+
19+
cy.get('#form-dialog-title', { timeout: 20000 }).should('contain', 'NeoDash - Neo4j Dashboard Builder').click();
20+
21+
cy.get('#form-dialog-title').then(($div) => {
22+
const text = $div.text();
23+
if (text == 'NeoDash - Neo4j Dashboard Builder') {
24+
cy.wait(500);
25+
// Create new dashboard
26+
cy.contains('New Dashboard').click();
27+
}
28+
});
29+
30+
cy.get('#form-dialog-title', { timeout: 20000 }).should('contain', 'Connect to Neo4j');
31+
32+
cy.get('#url').clear().type('localhost');
33+
cy.get('#dbusername').clear().type('neo4j');
34+
cy.get('#dbpassword').type('test1234');
35+
cy.get('button').contains('Connect').click();
36+
cy.wait(100);
37+
38+
//Opens the div containing all report cards
39+
cy.get('.react-grid-layout:eq(0)').within(() => {
40+
//Finds the 2nd card
41+
cy.get('.MuiGrid-root')
42+
.eq(1)
43+
.within(() => {
44+
//Clicks the 2nd button (opens settings)
45+
cy.get('button').eq(1).click();
46+
});
47+
});
48+
cy.get('.react-grid-layout:eq(0)').within(() => {
49+
//Finds the 2nd card
50+
cy.get('.MuiGrid-root')
51+
.eq(1)
52+
.within(() => {
53+
//Opens the drop down
54+
cy.getDataTest('type-dropdown').click();
55+
});
56+
});
57+
// Selects the Bar option
58+
cy.get('[id^="react-select-5-option"]')
59+
.contains(/Bar Chart/i)
60+
.should('be.visible')
61+
.click({ force: true });
62+
cy.get('.react-grid-layout .MuiGrid-root:eq(1) #type input[name="Type"]').should('have.value', 'Bar Chart');
63+
64+
// Creates basic bar chart
65+
cy.get('.react-grid-layout')
66+
.first()
67+
.within(() => {
68+
//Finds the 2nd card
69+
cy.get('.MuiGrid-root')
70+
.eq(1)
71+
.within(() => {
72+
//Removes text in cypher editor and types new query
73+
cy.get('.ndl-cypher-editor div[role="textbox"]')
74+
.should('be.visible')
75+
.click()
76+
.clear()
77+
.type(barChartCypherQuery);
78+
79+
cy.wait(400);
80+
cy.get('button[aria-label="run"]').click();
81+
});
82+
});
83+
84+
cy.wait(500);
85+
});
86+
87+
it.skip('Checking Colour Picker settings', () => {
88+
//Opens advanced settings
89+
cy.get('.react-grid-layout')
90+
.first()
91+
.within(() => {
92+
//Finds the 2nd card
93+
cy.get('.MuiGrid-root')
94+
.eq(1)
95+
.within(() => {
96+
// Access advanced settings
97+
cy.get('button').eq(1).click();
98+
cy.get('[role="switch"]').click();
99+
cy.wait(200);
100+
// Changing setting for colour picker
101+
cy.get('[data-testid="colorpicker-input"]').find('input').click().type('{selectall}').type('red');
102+
cy.get('button[aria-label="run"]').click();
103+
// Checking that colour picker was applied correctly
104+
cy.get('.card-view').should('have.css', 'background-color', 'rgb(255, 0, 0)');
105+
cy.wait(200);
106+
// Changing colour back to white
107+
cy.get('button').eq(1).click();
108+
cy.get('[data-testid="colorpicker-input"]').find('input').click().type('{selectall}').type('white');
109+
cy.get('button[aria-label="run"]').click();
110+
// Checking colour has been set back to white
111+
cy.wait(200);
112+
cy.get('.card-view').should('have.css', 'background-color', 'rgb(255, 255, 255)');
113+
});
114+
});
115+
});
116+
117+
it.skip('Checking Selector Description', () => {
118+
//Opens first 2nd card
119+
cy.get('.react-grid-layout:eq(0) .MuiGrid-root:eq(1)').within(() => {
120+
// Access advanced settings
121+
cy.get('button').eq(1).click();
122+
cy.get('[role="switch"]').click();
123+
cy.wait(200);
124+
// Changing Selector Description to 'Test'
125+
cy.get('.ndl-textarea').contains('span', 'Selector Description').click().type('Test');
126+
cy.get('button[aria-label="run"]').click();
127+
// Pressing Selector Description button
128+
cy.get('button[aria-label="details"]').click();
129+
});
130+
// Checking that Selector Description is behaving as expected
131+
cy.get('.MuiDialog-paper').should('be.visible').and('contain.text', 'Test');
132+
cy.wait(1000);
133+
134+
// Click elsewhere on the page to close dialog box
135+
cy.get('div[role="dialog"]').parent().click(-100, -100, { force: true });
136+
});
137+
138+
it.skip('Checking full screen bar chart setting', () => {
139+
//Opens first 2nd card
140+
cy.get('.react-grid-layout:eq(0) .MuiGrid-root:eq(1)').within(() => {
141+
// Opening settings
142+
cy.get('button').eq(1).click();
143+
// Activating advanced settings
144+
cy.get('[role="switch"]').click();
145+
cy.wait(200);
146+
// Finding fullscreen setting and changing it to 'on'
147+
cy.get('.ndl-dropdown')
148+
.contains('label', 'Fullscreen enabled')
149+
.scrollIntoView()
150+
.should('be.visible')
151+
.click()
152+
.type('on{enter}');
153+
// Pressing run to return to card view
154+
cy.get('button[aria-label="run"]').click();
155+
cy.get('button[aria-label="maximize"]').click();
156+
});
157+
// Modal outside of scope of card
158+
// Checking existence of full-screen modal
159+
cy.get('.dialog-xxl').should('be.visible');
160+
// Action to close full-screen modal
161+
cy.get('button[aria-label="un-maximize"]').click();
162+
// Checking that fullscreen has un-maximized
163+
// Check that the div is no longer in the DOM
164+
cy.get('div[data-focus-lock-disabled="false"]').should('not.exist');
165+
});
166+
167+
it.skip('Checking "Autorun Query" works as intended', () => {
168+
// Custom command to open advanced settings
169+
cy.advancedSettings(() => {
170+
// Finding 'Auto-run query setting and changing it to 'off'
171+
cy.get('.ndl-dropdown')
172+
.contains('label', 'Auto-run query')
173+
.scrollIntoView()
174+
.should('be.visible')
175+
.click()
176+
.type('off{enter}');
177+
cy.wait(200);
178+
cy.get('button[aria-label="run"]').click();
179+
cy.get('.ndl-cypher-editor').should('be.visible');
180+
cy.get('g').should('not.exist');
181+
cy.wait(100);
182+
cy.get('.MuiCardContent-root').find('button[aria-label="run"]').filter(':visible').click();
183+
cy.get('g').should('exist');
184+
});
185+
});
186+
187+
it.skip('Checking Legend integration works as intended', () => {
188+
cy.advancedSettings(() => {
189+
// Checking that legend appears
190+
cy.setDropdownValue('Show Legend', 'on');
191+
cy.wait(100);
192+
cy.get('button[aria-label="run"]').click();
193+
cy.wait(100);
194+
//Checking that legend matches value specified: in the case - 'count'
195+
cy.get('svg g g text').last().contains(/count/i);
196+
});
197+
cy.advancedSettings(() => {
198+
// Activating advanced settings
199+
cy.get('[role="switch"]').click();
200+
// Checking that legend disappears
201+
cy.setDropdownValue('Show Legend', 'off');
202+
cy.wait(100);
203+
cy.get('button[aria-label="run"]').click();
204+
cy.wait(100);
205+
cy.get('svg g g text').last().contains(/count/i).should('not.exist');
206+
});
207+
});
208+
209+
it.skip('Checking the stacked grouping function works as intended', () => {
210+
cy.advancedSettings(() => {
211+
cy.get('.ndl-cypher-editor div[role="textbox"]')
212+
.should('be.visible')
213+
.click()
214+
.clear()
215+
.type(
216+
'MATCH (p:Person)-[:DIRECTED]->(n:Movie) RETURN n.released AS released, p.name AS Director, count(n.title) AS count LIMIT 5'
217+
);
218+
cy.setDropdownValue('Grouping', 'on');
219+
cy.wait(100);
220+
cy.get('button[aria-label="run"]').click();
221+
cy.get('.ndl-dropdown:contains("Group")').find('svg').parent().click().type('Director{enter}');
222+
// Checking that the groups are stacked
223+
cy.get('.MuiCardContent-root')
224+
.find('g')
225+
.children('g')
226+
.eq(3) // Get the fourth g element (index starts from 0)
227+
.invoke('attr', 'transform')
228+
.then((transformValue) => {
229+
// Captures the first number in the tranlsate attribute using the parenthisis to capture the first digit and put it in the second value of the resulting array
230+
// if transformValue is translate(100,200), then transformValue.match(/translate\((\d+),\d+\)/) will produce an array like ["translate(100,200)", "100"],
231+
const match = transformValue.match(/translate\((\d+),\d+\)/);
232+
if (match?.[1]) {
233+
const xValue = match[1];
234+
console.log('xValue: ', xValue);
235+
236+
// Now find sibling g elements with the same x transform value
237+
cy.get('.MuiCardContent-root')
238+
.find('g')
239+
.children('g')
240+
.filter((index, element) => {
241+
const siblingTransform = Cypress.$(element).attr('transform');
242+
return siblingTransform?.includes(`translate(${xValue},`);
243+
})
244+
.should('have.length', 3); // Check that there's at least one element
245+
} else {
246+
throw new Error('Transform attribute not found or invalid format');
247+
}
248+
});
249+
});
250+
cy.get('.ndl-dropdown:contains("Group")').find('svg').parent().click().type('(none){enter}');
251+
// Checking that the stacked grouped elements do not exist
252+
cy.get('.MuiCardContent-root')
253+
.find('g')
254+
.children('g')
255+
.eq(3) // Get the fourth g element (index starts from 0)
256+
.invoke('attr', 'transform')
257+
.then((transformValue) => {
258+
// Captures the first number in the tranlsate attribute using the parenthisis to capture the first digit and put it in the second value of the resulting array
259+
// if transformValue is translate(100,200), then transformValue.match(/translate\((\d+),\d+\)/) will produce an array like ["translate(100,200)", "100"],
260+
const match = transformValue.match(/translate\((\d+),\d+\)/);
261+
if (match?.[1]) {
262+
const xValue = match[1];
263+
console.log('xValue: ', xValue);
264+
265+
// Now find sibling g elements with the same x transform value
266+
cy.get('.MuiCardContent-root')
267+
.find('g')
268+
.children('g')
269+
.filter((index, element) => {
270+
const siblingTransform = Cypress.$(element).attr('transform');
271+
return siblingTransform?.includes(`translate(${xValue},`);
272+
})
273+
.should('have.length', 1); // Check that there are no matching elements
274+
} else {
275+
throw new Error('Transform attribute not found or invalid format');
276+
}
277+
});
278+
});
279+
280+
// How to properly test this?
281+
it.skip('Testing grouped grouping mode', () => {
282+
cy.advancedSettings(() => {
283+
cy.get('.ndl-cypher-editor div[role="textbox"]')
284+
.should('be.visible')
285+
.click()
286+
.clear()
287+
.type(
288+
'MATCH (p:Person)-[:DIRECTED]->(n:Movie) RETURN n.released AS released, p.name AS Director, count(n.title) AS count LIMIT 5'
289+
);
290+
cy.setDropdownValue('Grouping', 'on');
291+
cy.setDropdownValue('Group Mode', 'grouped');
292+
cy.wait(400);
293+
cy.get('button[aria-label="run"]').click();
294+
cy.get('.ndl-dropdown:contains("Group")').find('svg').parent().click().type('Director{enter}');
295+
});
296+
});
297+
298+
it.skip('Testing "Show Value on Bars"', () => {
299+
cy.advancedSettings(() => {
300+
cy.setDropdownValue('Show Values On Bars', 'on');
301+
cy.get('button[aria-label="run"]').click();
302+
cy.get('.MuiCardContent-root')
303+
.find('div svg > g > g > text')
304+
.should('have.length', 5)
305+
.then((textElements) => {
306+
cy.log('Number of text elements:', textElements.length);
307+
});
308+
});
309+
cy.wait(100);
310+
cy.openSettings(() => {
311+
cy.setDropdownValue('Show Values On Bars', 'off');
312+
cy.get('button[aria-label="run"]').click();
313+
cy.get('.MuiCardContent-root').find('div svg > g > g > text').should('not.exist');
314+
});
315+
});
316+
});

0 commit comments

Comments
 (0)