Skip to content

Commit 7b017f3

Browse files
authored
Merge pull request #797 from plotly/adjust-panels-sort
sortMenu adjustments
2 parents bab49ea + 711084c commit 7b017f3

File tree

4 files changed

+93
-29
lines changed

4 files changed

+93
-29
lines changed

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "react-chart-editor",
33
"description": "plotly.js chart editor react component UI",
4-
"version": "0.33.3",
4+
"version": "0.33.4",
55
"author": "Plotly, Inc.",
66
"bugs": {
77
"url": "https://github.com/plotly/react-chart-editor/issues"
@@ -125,4 +125,4 @@
125125
"watch": "babel src --watch --out-dir lib --source-maps | node-sass -w src/styles/main.scss lib/react-chart-editor.css",
126126
"watch-test": "jest --watch"
127127
}
128-
}
128+
}

src/components/PanelMenuWrapper.js

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,9 @@ class PanelsWithSidebar extends Component {
5151
const sections = [];
5252
const groupLookup = {};
5353
let groupIndex;
54-
const panels = React.Children.toArray(children);
54+
const childrenArray = sortMenu(React.Children.toArray(children), menuPanelOrder);
5555

56-
if (menuPanelOrder) {
57-
sortMenu(panels, menuPanelOrder);
58-
}
59-
60-
panels.forEach(child => {
56+
childrenArray.forEach(child => {
6157
if (!child) {
6258
return;
6359
}

src/lib/__tests__/sortMenu-test.js

Lines changed: 43 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
import sortMenu from '../sortMenu';
22

33
describe('sortMenu', () => {
4-
it('modifies original array to follow the group, then name order provided', () => {
4+
it('returns a new array of sorted children', () => {
55
const initialArray = [
66
{props: {group: 'DEV', name: 'Inspector'}},
77
{props: {group: 'DEV', name: 'JSON'}},
88
];
99
const orderProp = [{group: 'DEV', name: 'JSON'}, {group: 'DEV', name: 'Inspector'}];
10+
const newArray = sortMenu(initialArray, orderProp);
1011

11-
sortMenu(initialArray, orderProp);
1212
expect(initialArray).toEqual([
13+
{props: {group: 'DEV', name: 'Inspector'}},
14+
{props: {group: 'DEV', name: 'JSON'}},
15+
]);
16+
expect(newArray).toEqual([
1317
{props: {group: 'DEV', name: 'JSON'}},
1418
{props: {group: 'DEV', name: 'Inspector'}},
1519
]);
@@ -32,9 +36,9 @@ describe('sortMenu', () => {
3236
{group: 'Style', name: 'Color Bars'},
3337
{group: 'Style', name: 'Annotation'},
3438
];
39+
const newArray = sortMenu(initialArray, orderProp);
3540

36-
sortMenu(initialArray, orderProp);
37-
expect(initialArray).toEqual([
41+
expect(newArray).toEqual([
3842
{props: {group: 'DEV', name: 'JSON'}},
3943
{props: {group: 'DEV', name: 'Inspector'}},
4044
{props: {group: 'Structure', name: 'Subplots'}},
@@ -59,9 +63,9 @@ describe('sortMenu', () => {
5963
{group: 'Style', name: 'Color Bars'},
6064
{group: 'Style', name: 'Annotation'},
6165
];
66+
const newArray = sortMenu(initialArray, orderProp);
6267

63-
sortMenu(initialArray, orderProp);
64-
expect(initialArray).toEqual([
68+
expect(newArray).toEqual([
6569
{props: {group: 'Structure', name: 'Subplots'}},
6670
{props: {group: 'Structure', name: 'Create'}},
6771
{props: {group: 'Style', name: 'Color Bars'}},
@@ -79,9 +83,9 @@ describe('sortMenu', () => {
7983
{props: {group: 'Structure', name: 'Create'}},
8084
];
8185
const orderProp = [{group: 'Style', name: 'Traces'}];
86+
const newArray = sortMenu(initialArray, orderProp);
8287

83-
sortMenu(initialArray, orderProp);
84-
expect(initialArray).toEqual([
88+
expect(newArray).toEqual([
8589
{props: {group: 'Style', name: 'Traces'}},
8690
{props: {group: 'Style', name: 'Axes'}},
8791
{props: {group: 'Style', name: 'General'}},
@@ -96,16 +100,15 @@ describe('sortMenu', () => {
96100
{props: {group: 'Style', name: 'Color Bars'}},
97101
{props: {group: 'Style', name: 'Annotation'}},
98102
];
99-
100103
const orderProp = [
101104
{group: 'Non Existent', name: 'Subplots'},
102105
{group: 'Structure', name: 'Create'},
103106
{group: 'Style', name: 'Color Bars'},
104107
{group: 'Style', name: 'Annotation'},
105108
];
109+
const newArray = sortMenu(initialArray, orderProp);
106110

107-
sortMenu(initialArray, orderProp);
108-
expect(initialArray).toEqual([
111+
expect(newArray).toEqual([
109112
{props: {group: 'Structure', name: 'Create'}},
110113
{props: {group: 'Structure', name: 'Subplots'}},
111114
{props: {group: 'Style', name: 'Color Bars'}},
@@ -120,15 +123,14 @@ describe('sortMenu', () => {
120123
{props: {group: 'Style', name: 'Color Bars'}},
121124
{props: {group: 'Style', name: 'Annotation'}},
122125
];
123-
124126
const orderProp = [
125127
{group: 'Structure', name: 'Non Existent'},
126128
{group: 'Style', name: 'Color Bars'},
127129
{group: 'Style', name: 'Annotation'},
128130
];
131+
const newArray = sortMenu(initialArray, orderProp);
129132

130-
sortMenu(initialArray, orderProp);
131-
expect(initialArray).toEqual([
133+
expect(newArray).toEqual([
132134
{props: {group: 'Style', name: 'Color Bars'}},
133135
{props: {group: 'Style', name: 'Annotation'}},
134136
{props: {group: 'Structure', name: 'Create'}},
@@ -143,19 +145,43 @@ describe('sortMenu', () => {
143145
{props: {group: 'Style', name: 'Color Bars'}},
144146
{props: {group: 'Style', name: 'Annotation'}},
145147
];
146-
147148
const orderProp = [
148149
{group: 'Structure', name: 'Annotation'},
149150
{group: 'Style', name: 'Color Bars'},
150151
{group: 'Style', name: 'Annotation'},
151152
];
153+
const newArray = sortMenu(initialArray, orderProp);
152154

153-
sortMenu(initialArray, orderProp);
154-
expect(initialArray).toEqual([
155+
expect(newArray).toEqual([
155156
{props: {group: 'Style', name: 'Color Bars'}},
156157
{props: {group: 'Style', name: 'Annotation'}},
157158
{props: {group: 'Structure', name: 'Create'}},
158159
{props: {group: 'Structure', name: 'Subplots'}},
159160
]);
160161
});
162+
163+
it('does not sort children with no group or name props', () => {
164+
const initialArray = [
165+
{props: {type: 'Logo'}},
166+
{props: {group: 'A', name: 'A'}},
167+
{props: {group: 'Structure', name: 'Subplots'}},
168+
{props: {group: 'Structure', name: 'Create'}},
169+
{props: {type: 'ButtonA'}},
170+
{props: {type: 'ButtonB'}},
171+
];
172+
const orderProp = [
173+
{group: 'Structure', name: 'Create'},
174+
{group: 'Structure', name: 'Subplots'},
175+
];
176+
const newArray = sortMenu(initialArray, orderProp);
177+
178+
expect(newArray).toEqual([
179+
{props: {type: 'Logo'}},
180+
{props: {group: 'Structure', name: 'Create'}},
181+
{props: {group: 'Structure', name: 'Subplots'}},
182+
{props: {group: 'A', name: 'A'}},
183+
{props: {type: 'ButtonA'}},
184+
{props: {type: 'ButtonB'}},
185+
]);
186+
});
161187
});

src/lib/sortMenu.js

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,39 @@ function sortAlphabetically(a, b) {
88
return sortByGroup || sortByName;
99
}
1010

11-
export default function sortMenu(panels, order) {
12-
// validates order, if a desired panel matches no panel in the panels array,
13-
// it is excluded from ordering considerations
11+
export default function sortMenu(children, order) {
12+
// Break out early if no order is provided
13+
if (!order) {
14+
return children;
15+
}
1416

17+
// PART 1: only sorting panels (i.e. child with a group and name prop)
18+
// and not other elements (like Buttons, or Logo)
19+
let panelsStartIndex = null;
20+
let panelsEndIndex = null;
21+
for (let i = 0; i < children.length; i++) {
22+
if (children[i].props.group && children[i].props.name && !panelsStartIndex) {
23+
panelsStartIndex = i;
24+
break;
25+
}
26+
}
27+
for (let i = panelsStartIndex; i < children.length; i++) {
28+
if (!children[i].props.group && !children[i].props.name && !panelsEndIndex) {
29+
panelsEndIndex = i - 1;
30+
break;
31+
} else if (i === children.length - 1) {
32+
panelsEndIndex = i;
33+
}
34+
}
35+
36+
const prePanelsChildren = panelsStartIndex === 0 ? [] : children.slice(0, panelsStartIndex);
37+
const panels =
38+
panelsStartIndex !== panelsEndIndex ? children.slice(panelsStartIndex, panelsEndIndex + 1) : [];
39+
const postPanelsChildren =
40+
panelsEndIndex === children.length ? [] : children.slice(panelsEndIndex + 1);
41+
42+
// PART 2: validate order prop, if a desired panel specified in order, matches no actual panel rendered
43+
// in the panels array, it is excluded from ordering considerations
1544
// eslint-disable-next-line
1645
order = order.filter(desiredPanel =>
1746
panels.some(
@@ -22,8 +51,8 @@ export default function sortMenu(panels, order) {
2251
);
2352

2453
const desiredGroupOrder = order.map(panel => panel.group).filter(getUniqueValues);
25-
const desiredNameOrder = order.map(panel => panel.name).filter(getUniqueValues);
2654

55+
// PART 3: Sort panels
2756
panels.sort((a, b) => {
2857
const panelAHasGroupCustomOrder = desiredGroupOrder.includes(a.props.group);
2958
const panelBHasGroupCustomOrder = desiredGroupOrder.includes(b.props.group);
@@ -57,6 +86,16 @@ export default function sortMenu(panels, order) {
5786
}
5887

5988
if (indexOfGroupA === indexOfGroupB) {
89+
// Since Subpanels names can be reused in different groups
90+
// we compute desired order here to get the desired index right.
91+
// We are filtering on unique values, just in case, even if we don't
92+
// have to because within a given group we'd assume that there will be
93+
// no 2 subpanels named the same.
94+
const desiredNameOrder = order
95+
.filter(panel => panel.group === a.props.group)
96+
.map(panel => panel.name)
97+
.filter(getUniqueValues);
98+
6099
const panelAHasNameCustomOrder = desiredNameOrder.includes(a.props.name);
61100
const panelBHasNameCustomOrder = desiredNameOrder.includes(b.props.name);
62101

@@ -79,4 +118,7 @@ export default function sortMenu(panels, order) {
79118
}
80119
return 0;
81120
});
121+
122+
// PART 4: Return all children
123+
return prePanelsChildren.concat(panels).concat(postPanelsChildren);
82124
}

0 commit comments

Comments
 (0)