Repository: bokeh/bokeh

Issue:
Have Grid that respects bounds of axis
In `examples/glyph/iris.py` the bounds parameter of LinearAxis is demonstrated.

Grids are also added to the plot.  On zoom out, the axis does not gain any extra tick marks, but the grid lines do continue.

![iris_grid_bounds](https://cloud.githubusercontent.com/assets/1796208/8504175/eeaab8ec-21cc-11e5-89f6-e0aa23d15564.png)



Discussion:
This is already configurable via `Grid.bounds`: 
```
from bokeh.plotting import figure, show, output_file
from bokeh.sampledata.iris import flowers

colormap = {'setosa': 'red', 'versicolor': 'green', 'virginica': 'blue'}
colors = [colormap[x] for x in flowers['species']]

p = figure(title = "Iris Morphology")
p.xaxis.axis_label = 'Petal Length'
p.yaxis.axis_label = 'Petal Width'
p.xaxis.bounds = (3, 5)
p.xgrid.bounds = (3, 5)

p.circle(flowers["petal_length"], flowers["petal_width"],
         color=colors, fill_alpha=0.2, size=10)

output_file("iris.html", title="iris.py example")

show(p)
```
<img width="626" alt="screen shot 2018-02-19 at 12 47 28" src="https://user-images.githubusercontent.com/1078448/36396432-108a65a6-1573-11e8-8b2d-cd1d81644544.png">

It's possible this could be better documented. I think it would be a mistake to have something explicitly set/copy this from axis, say, but perhaps `'auto'` could mean take a cue from axis. I think this issue should also fix the spillover in minor ticks as well. 

Resolving patch:
diff --git a/bokeh/models/grids.py b/bokeh/models/grids.py
--- a/bokeh/models/grids.py
+++ b/bokeh/models/grids.py
@@ -21,8 +21,9 @@ class Grid(GuideRenderer):
     """)
 
     bounds = Either(Auto, Tuple(Float, Float), help="""
-    Bounds for the rendered grid lines. If unset, the grid
-    lines will span the entire plot in the given dimension.
+    Bounds for the rendered grid lines. By default, a grid will look for a
+    corresponding axis to ask for bounds. If one cannot be found, the grid
+    will span the entire visible range.
     """)
 
     # Note: we must allow the possibility of setting both


Test patch:
diff --git a/bokehjs/test/models/grids/grid.coffee b/bokehjs/test/models/grids/grid.coffee
new file mode 100644
--- /dev/null
+++ b/bokehjs/test/models/grids/grid.coffee
@@ -0,0 +1,86 @@
+{expect} = require "chai"
+utils = require "../../utils"
+sinon = require 'sinon'
+
+{Axis} = utils.require("models/axes/axis")
+{BasicTicker} = utils.require("models/tickers/basic_ticker")
+{BasicTickFormatter} = utils.require("models/formatters/basic_tick_formatter")
+{Grid} = utils.require("models/grids/grid")
+{Plot} = utils.require("models/plots/plot")
+{Range1d} = utils.require("models/ranges/range1d")
+{Document} = utils.require "document"
+
+describe "Grid", ->
+
+  it "use axis computed bounds when range names and dimension match, and bounds='auto'", ->
+    doc = new Document()
+    p = new Plot({
+      x_range: new Range1d({start: 0, end: 10})
+      y_range: new Range1d({start: 0, end: 10})
+    })
+    doc.add_root(p)
+    ticker = new BasicTicker()
+    formatter = new BasicTickFormatter()
+    axis = new Axis({
+      ticker: ticker
+      formatter: formatter
+      plot: p
+      bounds: [2, 8]
+    })
+    axis.attach_document(p.document)
+    p.add_layout(axis, 'below')
+    grid = new Grid({
+      ticker: ticker
+      plot: p
+    })
+
+    expect(grid.computed_bounds()).to.be.deep.equal [2, 8]
+
+it "use axis computed bounds when dimensions doesn't match, and bounds='auto'", ->
+    doc = new Document()
+    p = new Plot({
+      x_range: new Range1d({start: 0, end: 10})
+      y_range: new Range1d({start: 0, end: 10})
+    })
+    doc.add_root(p)
+    ticker = new BasicTicker()
+    formatter = new BasicTickFormatter()
+    axis = new Axis({
+      ticker: ticker
+      formatter: formatter
+      plot: p
+      bounds: [2, 8]
+    })
+    axis.attach_document(p.document)
+    p.add_layout(axis, 'left')
+    grid = new Grid({
+      ticker: ticker
+      plot: p
+    })
+
+    expect(grid.computed_bounds()).to.be.deep.equal [0, 10]
+
+it "use user bounds when set'", ->
+    doc = new Document()
+    p = new Plot({
+      x_range: new Range1d({start: 0, end: 10})
+      y_range: new Range1d({start: 0, end: 10})
+    })
+    doc.add_root(p)
+    ticker = new BasicTicker()
+    formatter = new BasicTickFormatter()
+    axis = new Axis({
+      ticker: ticker
+      formatter: formatter
+      plot: p
+      bounds: [2, 8]
+    })
+    axis.attach_document(p.document)
+    p.add_layout(axis, 'below')
+    grid = new Grid({
+      ticker: ticker
+      plot: p
+      bounds: [1, 9]
+    })
+
+    expect(grid.computed_bounds()).to.be.deep.equal [1, 9]
diff --git a/bokehjs/test/models/grids/index.coffee b/bokehjs/test/models/grids/index.coffee
new file mode 100644
--- /dev/null
+++ b/bokehjs/test/models/grids/index.coffee
@@ -0,0 +1 @@
+require "./grid"
diff --git a/bokehjs/test/models/index.coffee b/bokehjs/test/models/index.coffee
--- a/bokehjs/test/models/index.coffee
+++ b/bokehjs/test/models/index.coffee
@@ -7,6 +7,7 @@ require "./filters"
 require "./formatters"
 require "./glyphs"
 require "./graphs"
+require "./grids"
 require "./layouts"
 require "./mappers"
 require "./plots"
diff --git a/bokehjs/test/models/tickers/continuous_ticker.ts b/bokehjs/test/models/tickers/continuous_ticker.ts
--- a/bokehjs/test/models/tickers/continuous_ticker.ts
+++ b/bokehjs/test/models/tickers/continuous_ticker.ts
@@ -10,16 +10,16 @@ describe("ContinuousTicker Model", () => {
     }
   }
 
-  it("should have five major and ten minor ticks", () => {
+  it("should have five major and minor ticks only inside bounds", () => {
     const ticker = new MyTicker({num_minor_ticks: 2, desired_num_ticks: 5})
 
     // `range` and `cross_loc` aren't used by the AdaptiveTicker, so are passed as null args
     const ticks = ticker.get_ticks(-200, 200, null, null, {})
     expect(ticks.major).to.be.deep.equal([-200, -100, 0, 100, 200])
-    expect(ticks.minor).to.be.deep.equal([-250, -200, -150, -100, -50, 0, 50, 100, 150, 200, 250])
+    expect(ticks.minor).to.be.deep.equal([-200, -150, -100, -50, 0, 50, 100, 150, 200])
   })
 
-  it("should have five major and five minor ticks", () => {
+  it("should have five major and matching minor ticks", () => {
     const ticker = new MyTicker({num_minor_ticks: 1, desired_num_ticks: 5})
 
     // `range` and `cross_loc` aren't used by the AdaptiveTicker, so are passed as null args