Skip to content

Commit f9e53f0

Browse files
authored
Merge pull request #3523 from jasongrout/stack
Rename Stacked widget to Stack
2 parents 056f1d1 + 53115b3 commit f9e53f0

File tree

8 files changed

+176
-55
lines changed

8 files changed

+176
-55
lines changed

docs/source/changelog.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ See also the
2727
- Tagsinput widget ([#2591](https://github.com/jupyter-widgets/ipywidgets/pull/2591), [#3272](https://github.com/jupyter-widgets/ipywidgets/pull/3272))
2828
- Drop notebook dependency from widgetsnbextension ([#2590](https://github.com/jupyter-widgets/ipywidgets/pull/2590))
2929
- Cast 'value' in range sliders to a tuple ([#2441](https://github.com/jupyter-widgets/ipywidgets/pull/2441))
30-
- Added a layout widget for 'stacked' layout ([#2376](https://github.com/jupyter-widgets/ipywidgets/pull/2376))
30+
- Added a layout widget for 'stack' layout ([#2376](https://github.com/jupyter-widgets/ipywidgets/pull/2376))
3131
- Play widget: expose playing and repeat ([#2283](https://github.com/jupyter-widgets/ipywidgets/pull/2283), [#1897](https://github.com/jupyter-widgets/ipywidgets/issues/1897))
3232
- Fix debouncing and throttling code ([#3060](https://github.com/jupyter-widgets/ipywidgets/pull/3060))
3333
- Fix regression on spinning icons ([#2685](https://github.com/jupyter-widgets/ipywidgets/pull/2685), [#2477](https://github.com/jupyter-widgets/ipywidgets/issues/2477))

docs/source/examples/Widget List.ipynb

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1388,9 +1388,9 @@
13881388
"cell_type": "markdown",
13891389
"metadata": {},
13901390
"source": [
1391-
"### Stacked\n",
1391+
"### Stack\n",
13921392
"\n",
1393-
"The `Stacked` widget can have multiple children widgets as for `Tab` and `Accordion`, but only shows one at a time depending on the value of ``selected_index``:"
1393+
"The `Stack` widget can have multiple children widgets as for `Tab` and `Accordion`, but only shows one at a time depending on the value of ``selected_index``:"
13941394
]
13951395
},
13961396
{
@@ -1401,8 +1401,8 @@
14011401
"source": [
14021402
"button = widgets.Button(description='Click here')\n",
14031403
"slider = widgets.IntSlider()\n",
1404-
"stacked = widgets.Stacked([button, slider], selected_index=0)\n",
1405-
"stacked # will show only the button"
1404+
"stack = widgets.Stack([button, slider], selected_index=0)\n",
1405+
"stack # will show only the button"
14061406
]
14071407
},
14081408
{
@@ -1419,15 +1419,15 @@
14191419
"outputs": [],
14201420
"source": [
14211421
"dropdown = widgets.Dropdown(options=['button', 'slider'])\n",
1422-
"widgets.jslink((dropdown, 'index'), (stacked, 'selected_index'))\n",
1423-
"widgets.VBox([dropdown, stacked])"
1422+
"widgets.jslink((dropdown, 'index'), (stack, 'selected_index'))\n",
1423+
"widgets.VBox([dropdown, stack])"
14241424
]
14251425
},
14261426
{
14271427
"cell_type": "markdown",
14281428
"metadata": {},
14291429
"source": [
1430-
"### Accordion, Tab, and Stacked use `selected_index`, not value\n",
1430+
"### Accordion, Tab, and Stack use `selected_index`, not value\n",
14311431
"\n",
14321432
"Unlike the rest of the widgets discussed earlier, the container widgets `Accordion` and `Tab` update their `selected_index` attribute when the user changes which accordion or tab is selected. That means that you can both see what the user is doing *and* programmatically set what the user sees by setting the value of `selected_index`.\n",
14331433
"\n",

packages/controls/src/widget_selectioncontainer.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -422,17 +422,17 @@ export class TabView extends DOMWidgetView {
422422
luminoWidget: JupyterLuminoTabPanelWidget;
423423
}
424424

425-
export class StackedModel extends SelectionContainerModel {
425+
export class StackModel extends SelectionContainerModel {
426426
defaults(): Backbone.ObjectHash {
427427
return {
428428
...super.defaults(),
429-
_model_name: 'StackedModel',
430-
_view_name: 'StackedView',
429+
_model_name: 'StackModel',
430+
_view_name: 'StackView',
431431
};
432432
}
433433
}
434434

435-
export class StackedView extends BoxView {
435+
export class StackView extends BoxView {
436436
initialize(parameters: WidgetView.IInitializeParameters): void {
437437
super.initialize(parameters);
438438
this.listenTo(this.model, 'change:selected_index', this.update_children);

packages/schema/jupyterwidgetmodels.latest.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6554,7 +6554,7 @@
65546554
"type": "string"
65556555
},
65566556
{
6557-
"default": "StackedModel",
6557+
"default": "StackModel",
65586558
"help": "",
65596559
"name": "_model_name",
65606560
"type": "string"
@@ -6572,7 +6572,7 @@
65726572
"type": "string"
65736573
},
65746574
{
6575-
"default": "StackedView",
6575+
"default": "StackView",
65766576
"help": "",
65776577
"name": "_view_name",
65786578
"type": "string"
@@ -6634,12 +6634,12 @@
66346634
],
66356635
"model": {
66366636
"module": "@jupyter-widgets/controls",
6637-
"name": "StackedModel",
6637+
"name": "StackModel",
66386638
"version": "2.0.0"
66396639
},
66406640
"view": {
66416641
"module": "@jupyter-widgets/controls",
6642-
"name": "StackedView",
6642+
"name": "StackView",
66436643
"version": "2.0.0"
66446644
}
66456645
},

packages/schema/jupyterwidgetmodels.latest.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1168,17 +1168,17 @@ Attribute | Type | Default | Help
11681168
`description_width` | string | `''` | Width of the description to the side of the control.
11691169
`handle_color` | `null` or string | `null` | Color of the slider handle.
11701170

1171-
### StackedModel (@jupyter-widgets/controls, 2.0.0); StackedView (@jupyter-widgets/controls, 2.0.0)
1171+
### StackModel (@jupyter-widgets/controls, 2.0.0); StackView (@jupyter-widgets/controls, 2.0.0)
11721172

11731173
Attribute | Type | Default | Help
11741174
-----------------|------------------|------------------|----
11751175
`_dom_classes` | array of string | `[]` | CSS classes applied to widget DOM element
11761176
`_model_module` | string | `'@jupyter-widgets/controls'` |
11771177
`_model_module_version` | string | `'2.0.0'` |
1178-
`_model_name` | string | `'StackedModel'` |
1178+
`_model_name` | string | `'StackModel'` |
11791179
`_view_module` | string | `'@jupyter-widgets/controls'` |
11801180
`_view_module_version` | string | `'2.0.0'` |
1181-
`_view_name` | string | `'StackedView'` |
1181+
`_view_name` | string | `'StackView'` |
11821182
`box_style` | string (one of `'success'`, `'info'`, `'warning'`, `'danger'`, `''`) | `''` | Use a predefined styling for the box.
11831183
`children` | array of reference to Widget widget | `[]` | List of widget children
11841184
`layout` | reference to Layout widget | reference to new instance |

python/ipywidgets/ipywidgets/widgets/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
from .widget_time import TimePicker
2020
from .widget_output import Output
2121
from .widget_selection import RadioButtons, ToggleButtons, ToggleButtonsStyle, Dropdown, Select, SelectionSlider, SelectMultiple, SelectionRangeSlider
22-
from .widget_selectioncontainer import Tab, Accordion, Stacked
22+
from .widget_selectioncontainer import Tab, Accordion, Stack
2323
from .widget_string import HTML, HTMLMath, Label, Text, Textarea, Password, Combobox
2424
from .widget_controller import Controller
2525
from .interaction import interact, interactive, fixed, interact_manual, interactive_output

python/ipywidgets/ipywidgets/widgets/tests/test_selectioncontainer.py

Lines changed: 136 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,50 +5,164 @@
55

66
from traitlets import TraitError
77

8-
from ipywidgets.widgets import Accordion, HTML
8+
from ipywidgets.widgets import Accordion, Tab, Stack, HTML
99

10+
class TestTab(TestCase):
11+
12+
def setUp(self):
13+
self.children = [HTML('0'), HTML('1')]
14+
self.widget = Tab
15+
16+
def test_selected_index_none(self):
17+
widget = self.widget(self.children, selected_index=None)
18+
state = widget.get_state()
19+
assert state['selected_index'] == 0
20+
21+
def test_selected_index_default(self):
22+
widget = self.widget(self.children)
23+
state = widget.get_state()
24+
assert state['selected_index'] == 0
25+
26+
def test_selected_index(self):
27+
widget = self.widget(self.children, selected_index=1)
28+
state = widget.get_state()
29+
assert state['selected_index'] == 1
30+
31+
def test_selected_index_out_of_bounds(self):
32+
with self.assertRaises(TraitError):
33+
self.widget(self.children, selected_index=-1)
34+
35+
def test_children_position_argument(self):
36+
self.widget(self.children)
37+
38+
def test_titles(self):
39+
widget = self.widget(self.children, selected_index=None)
40+
assert widget.get_state()['titles'] == ('', '')
41+
assert widget.titles == ('', '')
42+
43+
widget.set_title(1, 'Title 1')
44+
assert widget.get_state()['titles'] == ('', 'Title 1')
45+
assert widget.titles[1] == 'Title 1'
46+
assert widget.get_title(1) == 'Title 1'
47+
48+
# Backwards compatible with 7.x api
49+
widget.set_title(1, None)
50+
assert widget.get_state()['titles'] == ('', '')
51+
assert widget.titles[1] == ''
52+
assert widget.get_title(1) == ''
53+
54+
with self.assertRaises(IndexError):
55+
widget.set_title(2, 'out of bounds')
56+
with self.assertRaises(IndexError):
57+
widget.get_title(2)
58+
59+
widget.children = tuple(widget.children[:1])
60+
assert len(widget.children) == 1
61+
assert widget.titles == ('',)
1062

1163
class TestAccordion(TestCase):
1264

1365
def setUp(self):
1466
self.children = [HTML('0'), HTML('1')]
67+
self.widget = Accordion
68+
69+
def test_selected_index_none(self):
70+
widget = self.widget(self.children, selected_index=None)
71+
state = widget.get_state()
72+
assert state['selected_index'] is None
73+
74+
def test_selected_index_default(self):
75+
widget = self.widget(self.children)
76+
state = widget.get_state()
77+
assert state['selected_index'] is None
78+
79+
def test_selected_index(self):
80+
widget = self.widget(self.children, selected_index=1)
81+
state = widget.get_state()
82+
assert state['selected_index'] == 1
83+
84+
def test_selected_index_out_of_bounds(self):
85+
with self.assertRaises(TraitError):
86+
self.widget(self.children, selected_index=-1)
87+
88+
def test_children_position_argument(self):
89+
self.widget(self.children)
90+
91+
def test_titles(self):
92+
widget = self.widget(self.children, selected_index=None)
93+
assert widget.get_state()['titles'] == ('', '')
94+
assert widget.titles == ('', '')
95+
96+
widget.set_title(1, 'Title 1')
97+
assert widget.get_state()['titles'] == ('', 'Title 1')
98+
assert widget.titles[1] == 'Title 1'
99+
assert widget.get_title(1) == 'Title 1'
100+
101+
# Backwards compatible with 7.x api
102+
widget.set_title(1, None)
103+
assert widget.get_state()['titles'] == ('', '')
104+
assert widget.titles[1] == ''
105+
assert widget.get_title(1) == ''
106+
107+
with self.assertRaises(IndexError):
108+
widget.set_title(2, 'out of bounds')
109+
with self.assertRaises(IndexError):
110+
widget.get_title(2)
111+
112+
widget.children = tuple(widget.children[:1])
113+
assert len(widget.children) == 1
114+
assert widget.titles == ('',)
115+
116+
class TestStack(TestCase):
117+
118+
def setUp(self):
119+
self.children = [HTML('0'), HTML('1')]
120+
self.widget = Stack
15121

16122
def test_selected_index_none(self):
17-
accordion = Accordion(self.children, selected_index=None)
18-
state = accordion.get_state()
123+
widget = self.widget(self.children, selected_index=None)
124+
state = widget.get_state()
125+
assert state['selected_index'] is None
126+
127+
def test_selected_index_default(self):
128+
widget = self.widget(self.children)
129+
state = widget.get_state()
19130
assert state['selected_index'] is None
20131

21132
def test_selected_index(self):
22-
accordion = Accordion(self.children, selected_index=1)
23-
state = accordion.get_state()
133+
widget = self.widget(self.children, selected_index=1)
134+
state = widget.get_state()
24135
assert state['selected_index'] == 1
25136

26137
def test_selected_index_out_of_bounds(self):
27138
with self.assertRaises(TraitError):
28-
Accordion(self.children, selected_index=-1)
139+
self.widget(self.children, selected_index=-1)
29140

141+
def test_children_position_argument(self):
142+
self.widget(self.children)
30143

31144
def test_titles(self):
32-
accordion = Accordion(self.children, selected_index=None)
33-
assert accordion.get_state()['titles'] == ('', '')
34-
assert accordion.titles == ('', '')
145+
widget = self.widget(self.children, selected_index=None)
146+
assert widget.get_state()['titles'] == ('', '')
147+
assert widget.titles == ('', '')
35148

36-
accordion.set_title(1, 'Title 1')
37-
assert accordion.get_state()['titles'] == ('', 'Title 1')
38-
assert accordion.titles[1] == 'Title 1'
39-
assert accordion.get_title(1) == 'Title 1'
149+
widget.set_title(1, 'Title 1')
150+
assert widget.get_state()['titles'] == ('', 'Title 1')
151+
assert widget.titles[1] == 'Title 1'
152+
assert widget.get_title(1) == 'Title 1'
40153

41154
# Backwards compatible with 7.x api
42-
accordion.set_title(1, None)
43-
assert accordion.get_state()['titles'] == ('', '')
44-
assert accordion.titles[1] == ''
45-
assert accordion.get_title(1) == ''
155+
widget.set_title(1, None)
156+
assert widget.get_state()['titles'] == ('', '')
157+
assert widget.titles[1] == ''
158+
assert widget.get_title(1) == ''
46159

47160
with self.assertRaises(IndexError):
48-
accordion.set_title(2, 'out of bounds')
161+
widget.set_title(2, 'out of bounds')
49162
with self.assertRaises(IndexError):
50-
accordion.get_title(2)
163+
widget.get_title(2)
164+
165+
widget.children = tuple(widget.children[:1])
166+
assert len(widget.children) == 1
167+
assert widget.titles == ('',)
51168

52-
accordion.children = tuple(accordion.children[:1])
53-
assert len(accordion.children) == 1
54-
assert accordion.titles == ('',)

python/ipywidgets/ipywidgets/widgets/widget_selectioncontainer.py

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,15 @@ def _validate_titles(self, proposal):
4141

4242
@observe('children')
4343
def _observe_children(self, change):
44-
if self.selected_index is not None and len(change.new) < self.selected_index:
44+
self._reset_selected_index()
45+
self._reset_titles()
46+
47+
def _reset_selected_index(self):
48+
if self.selected_index is not None and len(self.children) < self.selected_index:
4549
self.selected_index = None
46-
if len(self.titles) != len(change.new):
50+
51+
def _reset_titles(self):
52+
if len(self.titles) != len(self.children):
4753
# Run validation function
4854
self.titles = tuple(self.titles)
4955

@@ -85,15 +91,15 @@ class Tab(_SelectionContainer):
8591
_view_name = Unicode('TabView').tag(sync=True)
8692
_model_name = Unicode('TabModel').tag(sync=True)
8793

88-
def __init__(self, **kwargs):
89-
if 'children' in kwargs and 'selected_index' not in kwargs and len(kwargs['children']) > 0:
94+
def __init__(self, children=(), **kwargs):
95+
if len(children) > 0 and 'selected_index' not in kwargs:
9096
kwargs['selected_index'] = 0
91-
super(Tab, self).__init__(**kwargs)
97+
super().__init__(children=children, **kwargs)
9298

93-
@observe('children')
94-
def _observe_children(self, change):
99+
def _reset_selected_index(self):
95100
# if there are no tabs, then none should be selected
96-
if len(change.new) == 0:
101+
num_children = len(self.children)
102+
if num_children == 0:
97103
self.selected_index = None
98104

99105
# if there are tabs, but none is selected, select the first one
@@ -102,12 +108,13 @@ def _observe_children(self, change):
102108

103109
# if there are tabs and a selection, but the selection is no longer
104110
# valid, select the last tab.
105-
elif len(change.new) < self.selected_index:
106-
self.selected_index = len(change.new) - 1
111+
elif num_children < self.selected_index:
112+
self.selected_index = num_children - 1
113+
107114

108115

109116
@register
110-
class Stacked(_SelectionContainer):
117+
class Stack(_SelectionContainer):
111118
"""Displays only the selected child."""
112-
_view_name = Unicode('StackedView').tag(sync=True)
113-
_model_name = Unicode('StackedModel').tag(sync=True)
119+
_view_name = Unicode('StackView').tag(sync=True)
120+
_model_name = Unicode('StackModel').tag(sync=True)

0 commit comments

Comments
 (0)