Skip to content

Commit 6306e37

Browse files
committed
Merge pull request #510 from plotly/relayout-axis-category
Fix relayout axis category
2 parents 5aef0e7 + c6075cb commit 6306e37

File tree

5 files changed

+94
-25
lines changed

5 files changed

+94
-25
lines changed

src/plot_api/plot_api.js

+6
Original file line numberDiff line numberDiff line change
@@ -2211,6 +2211,12 @@ Plotly.relayout = function relayout(gd, astr, val) {
22112211
else if(/[xy]axis[0-9]*?$/.test(pleaf) && !Object.keys(vi || {}).length) {
22122212
docalc = true;
22132213
}
2214+
else if(/[xy]axis[0-9]*\.categoryorder$/.test(pleafPlus)) {
2215+
docalc = true;
2216+
}
2217+
else if(/[xy]axis[0-9]*\.categoryarray/.test(pleafPlus)) {
2218+
docalc = true;
2219+
}
22142220

22152221
if(pleafPlus.indexOf('rangeslider') !== -1) {
22162222
docalc = true;

src/plots/cartesian/axis_defaults.js

+7-6
Original file line numberDiff line numberDiff line change
@@ -74,10 +74,6 @@ module.exports = function handleAxisDefaults(containerIn, containerOut, coerce,
7474
}
7575
}
7676

77-
containerOut._initialCategories = axType === 'category' ?
78-
orderedCategories(letter, containerIn.categoryorder, containerIn.categoryarray, options.data) :
79-
[];
80-
8177
setConvert(containerOut);
8278

8379
var dfltColor = coerce('color');
@@ -142,13 +138,18 @@ module.exports = function handleAxisDefaults(containerIn, containerOut, coerce,
142138
delete containerOut.zerolinewidth;
143139
}
144140

141+
// fill in categories
142+
containerOut._initialCategories = axType === 'category' ?
143+
orderedCategories(letter, containerOut.categoryorder, containerOut.categoryarray, options.data) :
144+
[];
145+
145146
return containerOut;
146147
};
147148

148149
function setAutoType(ax, data) {
149150
// new logic: let people specify any type they want,
150151
// only autotype if type is '-'
151-
if(ax.type!=='-') return;
152+
if(ax.type !== '-') return;
152153

153154
var id = ax._id,
154155
axLetter = id.charAt(0);
@@ -163,7 +164,7 @@ function setAutoType(ax, data) {
163164
// should always default to a linear axis
164165
if(d0.type==='histogram' &&
165166
axLetter === {v: 'y', h: 'x'}[d0.orientation || 'v']) {
166-
ax.type='linear';
167+
ax.type = 'linear';
167168
return;
168169
}
169170

src/plots/cartesian/category_order_defaults.js

+12-19
Original file line numberDiff line numberDiff line change
@@ -8,32 +8,25 @@
88

99
'use strict';
1010

11-
var layoutAttributes = require('./layout_attributes');
1211

1312
module.exports = function handleCategoryOrderDefaults(containerIn, containerOut, coerce) {
13+
if(containerOut.type !== 'category') return;
1414

15-
if(containerIn.type !== 'category') return;
15+
var arrayIn = containerIn.categoryarray,
16+
orderDefault;
1617

17-
var validCategories = layoutAttributes.categoryorder.values;
18+
var isValidArray = (Array.isArray(arrayIn) && arrayIn.length > 0);
1819

19-
var propercategoryarray = Array.isArray(containerIn.categoryarray) && containerIn.categoryarray.length > 0;
20+
// override default 'categoryorder' value when non-empty array is supplied
21+
if(isValidArray) orderDefault = 'array';
2022

21-
if(validCategories.indexOf(containerIn.categoryorder) === -1 && propercategoryarray) {
23+
var order = coerce('categoryorder', orderDefault);
2224

23-
// when unspecified or invalid, use the default, unless categoryarray implies 'array'
24-
coerce('categoryorder', 'array'); // promote to 'array'
25-
26-
} else if(containerIn.categoryorder === 'array' && !propercategoryarray) {
27-
28-
// when mode is 'array' but no list is given, revert to default
29-
30-
containerIn.categoryorder = 'trace'; // revert to default
31-
coerce('categoryorder');
32-
33-
} else {
34-
35-
// otherwise use the supplied mode, or the default one if unsupplied or invalid
36-
coerce('categoryorder');
25+
// coerce 'categoryarray' only in array order case
26+
if(order === 'array') coerce('categoryarray');
3727

28+
// cannot set 'categoryorder' to 'array' with an invalid 'categoryarray'
29+
if(!isValidArray && order === 'array') {
30+
containerOut.categoryorder = 'trace';
3831
}
3932
};

test/jasmine/tests/axes_test.js

+15
Original file line numberDiff line numberDiff line change
@@ -399,22 +399,27 @@ describe('Test axes', function() {
399399
it('should set categoryorder to default if categoryorder and categoryarray are not supplied', function() {
400400
PlotlyInternal.plot(gd, [{x: ['c','a','e','b','d'], y: [15,11,12,13,14]}], {xaxis: {type: 'category'}});
401401
expect(gd._fullLayout.xaxis.categoryorder).toBe('trace');
402+
expect(gd._fullLayout.xaxis.categorarray).toBe(undefined);
402403
});
403404

404405
it('should set categoryorder to default even if type is not set to category explicitly', function() {
405406
PlotlyInternal.plot(gd, [{x: ['c','a','e','b','d'], y: [15,11,12,13,14]}]);
406407
expect(gd._fullLayout.xaxis.categoryorder).toBe('trace');
408+
expect(gd._fullLayout.xaxis.categorarray).toBe(undefined);
407409
});
408410

409411
it('should NOT set categoryorder to default if type is not category', function() {
410412
PlotlyInternal.plot(gd, [{x: ['c','a','e','b','d'], y: [15,11,12,13,14]}]);
411413
expect(gd._fullLayout.yaxis.categoryorder).toBe(undefined);
414+
expect(gd._fullLayout.xaxis.categorarray).toBe(undefined);
412415
});
413416

414417
it('should set categoryorder to default if type is overridden to be category', function() {
415418
PlotlyInternal.plot(gd, [{x: [1,2,3,4,5], y: [15,11,12,13,14]}], {yaxis: {type: 'category'}});
416419
expect(gd._fullLayout.xaxis.categoryorder).toBe(undefined);
420+
expect(gd._fullLayout.yaxis.categorarray).toBe(undefined);
417421
expect(gd._fullLayout.yaxis.categoryorder).toBe('trace');
422+
expect(gd._fullLayout.yaxis.categorarray).toBe(undefined);
418423
});
419424

420425
});
@@ -426,20 +431,23 @@ describe('Test axes', function() {
426431
xaxis: {type: 'category', categoryorder: 'array', categoryarray: ['b','a','d','e','c']}
427432
});
428433
expect(gd._fullLayout.xaxis.categoryorder).toBe('array');
434+
expect(gd._fullLayout.xaxis.categoryarray).toEqual(['b','a','d','e','c']);
429435
});
430436

431437
it('should switch categoryorder on "array" if it is not supplied but categoryarray is supplied', function() {
432438
PlotlyInternal.plot(gd, [{x: ['c','a','e','b','d'], y: [15,11,12,13,14]}], {
433439
xaxis: {type: 'category', categoryarray: ['b','a','d','e','c']}
434440
});
435441
expect(gd._fullLayout.xaxis.categoryorder).toBe('array');
442+
expect(gd._fullLayout.xaxis.categoryarray).toEqual(['b','a','d','e','c']);
436443
});
437444

438445
it('should revert categoryorder to "trace" if "array" is supplied but there is no list', function() {
439446
PlotlyInternal.plot(gd, [{x: ['c','a','e','b','d'], y: [15,11,12,13,14]}], {
440447
xaxis: {type: 'category', categoryorder: 'array'}
441448
});
442449
expect(gd._fullLayout.xaxis.categoryorder).toBe('trace');
450+
expect(gd._fullLayout.xaxis.categorarray).toBe(undefined);
443451
});
444452

445453
});
@@ -451,13 +459,15 @@ describe('Test axes', function() {
451459
xaxis: {type: 'category', categoryorder: 'array', categoryarray: []}
452460
});
453461
expect(gd._fullLayout.xaxis.categoryorder).toBe('trace');
462+
expect(gd._fullLayout.xaxis.categoryarray).toEqual([]);
454463
});
455464

456465
it('should not switch categoryorder on "array" if categoryarray is supplied but empty', function() {
457466
PlotlyInternal.plot(gd, [{x: ['c','a','e','b','d'], y: [15,11,12,13,14]}], {
458467
xaxis: {type: 'category', categoryarray: []}
459468
});
460469
expect(gd._fullLayout.xaxis.categoryorder).toBe('trace');
470+
expect(gd._fullLayout.xaxis.categoryarray).toEqual(undefined);
461471
});
462472
});
463473

@@ -468,20 +478,23 @@ describe('Test axes', function() {
468478
xaxis: {type: 'category', categoryorder: 'trace', categoryarray: ['b','a','d','e','c']}
469479
});
470480
expect(gd._fullLayout.xaxis.categoryorder).toBe('trace');
481+
expect(gd._fullLayout.xaxis.categoryarray).toBe(undefined);
471482
});
472483

473484
it('should use specified categoryorder if it is supplied even if categoryarray exists', function() {
474485
PlotlyInternal.plot(gd, [{x: ['c','a','e','b','d'], y: [15,11,12,13,14]}], {
475486
xaxis: {type: 'category', categoryorder: 'category ascending', categoryarray: ['b','a','d','e','c']}
476487
});
477488
expect(gd._fullLayout.xaxis.categoryorder).toBe('category ascending');
489+
expect(gd._fullLayout.xaxis.categoryarray).toBe(undefined);
478490
});
479491

480492
it('should use specified categoryorder if it is supplied even if categoryarray exists', function() {
481493
PlotlyInternal.plot(gd, [{x: ['c','a','e','b','d'], y: [15,11,12,13,14]}], {
482494
xaxis: {type: 'category', categoryorder: 'category descending', categoryarray: ['b','a','d','e','c']}
483495
});
484496
expect(gd._fullLayout.xaxis.categoryorder).toBe('category descending');
497+
expect(gd._fullLayout.xaxis.categoryarray).toBe(undefined);
485498
});
486499

487500
});
@@ -493,13 +506,15 @@ describe('Test axes', function() {
493506
xaxis: {type: 'category', categoryorder: 'invalid value'}
494507
});
495508
expect(gd._fullLayout.xaxis.categoryorder).toBe('trace');
509+
expect(gd._fullLayout.xaxis.categoryarray).toBe(undefined);
496510
});
497511

498512
it('should switch categoryorder to "array" if mode is supplied but invalid and list is supplied', function() {
499513
PlotlyInternal.plot(gd, [{x: ['c','a','e','b','d'], y: [15,11,12,13,14]}], {
500514
xaxis: {type: 'category', categoryorder: 'invalid value', categoryarray: ['b','a','d','e','c']}
501515
});
502516
expect(gd._fullLayout.xaxis.categoryorder).toBe('array');
517+
expect(gd._fullLayout.xaxis.categoryarray).toEqual(['b','a','d','e','c']);
503518
});
504519

505520
});

test/jasmine/tests/cartesian_test.js

+54
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,57 @@ describe('zoom box element', function() {
4949
.toEqual(0);
5050
});
5151
});
52+
53+
describe('relayout', function() {
54+
55+
describe('axis category attributes', function() {
56+
var mock = require('@mocks/basic_bar.json');
57+
58+
var gd, mockCopy;
59+
60+
beforeEach(function() {
61+
mockCopy = Lib.extendDeep({}, mock);
62+
gd = createGraphDiv();
63+
});
64+
65+
afterEach(destroyGraphDiv);
66+
67+
it('should response to \'categoryarray\' and \'categoryorder\' updates', function(done) {
68+
function assertCategories(list) {
69+
d3.selectAll('g.xtick').each(function(_, i) {
70+
var tick = d3.select(this).select('text');
71+
expect(tick.html()).toEqual(list[i]);
72+
});
73+
}
74+
75+
Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(function() {
76+
assertCategories(['giraffes', 'orangutans', 'monkeys']);
77+
78+
return Plotly.relayout(gd, 'xaxis.categoryorder', 'category descending');
79+
}).then(function() {
80+
var list = ['orangutans', 'monkeys', 'giraffes'];
81+
82+
expect(gd._fullLayout.xaxis._initialCategories).toEqual(list);
83+
assertCategories(list);
84+
85+
return Plotly.relayout(gd, 'xaxis.categoryorder', null);
86+
}).then(function() {
87+
assertCategories(['giraffes', 'orangutans', 'monkeys']);
88+
89+
return Plotly.relayout(gd, {
90+
'xaxis.categoryarray': ['monkeys', 'giraffes', 'orangutans']
91+
});
92+
}).then(function() {
93+
var list = ['monkeys', 'giraffes', 'orangutans'];
94+
95+
expect(gd.layout.xaxis.categoryarray).toEqual(list);
96+
expect(gd._fullLayout.xaxis.categoryarray).toEqual(list);
97+
expect(gd._fullLayout.xaxis._initialCategories).toEqual(list);
98+
assertCategories(list);
99+
100+
done();
101+
});
102+
});
103+
});
104+
105+
});

0 commit comments

Comments
 (0)