浏览代码

Slider: add range support (#2751)

杨奕 8 年之前
父节点
当前提交
450cf81ded
共有 5 个文件被更改,包括 537 次插入159 次删除
  1. 33 2
      examples/docs/en-US/slider.md
  2. 33 2
      examples/docs/zh-CN/slider.md
  3. 156 0
      packages/slider/src/button.vue
  4. 164 131
      packages/slider/src/main.vue
  5. 151 24
      test/unit/specs/slider.spec.js

+ 33 - 2
examples/docs/en-US/slider.md

@@ -7,7 +7,8 @@
         value3: 42,
         value4: 0,
         value5: 0,
-        value6: 0
+        value6: 0,
+        value7: [4, 8]
       };
     }
   }
@@ -119,6 +120,35 @@ Set value via a input box.
 ```
 :::
 
+### Range selection
+
+Selecting a range of values is supported.
+
+:::demo Setting the `range` attribute activates range mode, where the binding value is an array made up of two boundary values.
+```html
+<template>
+  <div class="block">
+    <el-slider
+      v-model="value7"
+      range
+      show-stops
+      :max="10">
+    </el-slider>
+  </div>
+</template>
+
+<script>
+  export default {
+    data() {
+      return {
+        value7: [4, 8]
+      }
+    }
+  }
+</script>
+```
+:::
+
 ## Attributes
 | Attribute      | Description          | Type      | Accepted Values       | Default  |
 |---------- |-------------- |---------- |--------------------------------  |-------- |
@@ -126,9 +156,10 @@ Set value via a input box.
 | max | maximum value | number | — | 100 |
 | disabled | whether Slider is disabled | boolean | — | false |
 | step | step size | number | — | 1 |
-| show-input | whether to display an input box | boolean | — | false |
+| show-input | whether to display an input box, works when `range` is false | boolean | — | false |
 | show-input-controls | whether to display control buttons when `show-input` is true | boolean | — | true |
 | show-stops | whether to display breakpoints | boolean | — | false |
+| range | whether to select a range | boolean | — | false |
 
 ## Events
 | Event Name | Description | Parameters |

+ 33 - 2
examples/docs/zh-CN/slider.md

@@ -7,7 +7,8 @@
         value3: 42,
         value4: 0,
         value5: 0,
-        value6: 0
+        value6: 0,
+        value7: [4, 8]
       };
     }
   }
@@ -143,6 +144,35 @@
 ```
 :::
 
+### 范围选择
+
+支持选择某一数值范围
+
+:::demo 设置`range`即可开启范围选择,此时绑定值是一个数组,其元素分别为最小边界值和最大边界值
+```html
+<template>
+  <div class="block">
+    <el-slider
+      v-model="value7"
+      range
+      show-stops
+      :max="10">
+    </el-slider>
+  </div>
+</template>
+
+<script>
+  export default {
+    data() {
+      return {
+        value7: [4, 8]
+      }
+    }
+  }
+</script>
+```
+:::
+
 ### Attributes
 | 参数      | 说明          | 类型      | 可选值                           | 默认值  |
 |---------- |-------------- |---------- |--------------------------------  |-------- |
@@ -150,9 +180,10 @@
 | max | 最大值 | number | — | 100 |
 | disabled | 是否禁用 | boolean | — | false |
 | step | 步长 | number | — | 1 |
-| show-input | 是否显示输入框 | boolean | — | false |
+| show-input | 是否显示输入框,仅在非范围选择时有效 | boolean | — | false |
 | show-input-controls | 在显示输入框的情况下,是否显示输入框的控制按钮 | boolean | — | true|
 | show-stops | 是否显示间断点 | boolean | — | false |
+| range | 是否为范围选择 | boolean | — | false |
 
 ### Events
 | 事件名称      | 说明    | 回调参数      |

+ 156 - 0
packages/slider/src/button.vue

@@ -0,0 +1,156 @@
+<template>
+  <div
+    class="el-slider__button-wrapper"
+    @mouseenter="handleMouseEnter"
+    @mouseleave="handleMouseLeave"
+    @mousedown="onButtonDown"
+    :class="{ 'hover': hovering, 'dragging': dragging }"
+    :style="{ left: currentPosition }"
+    ref="button">
+    <el-tooltip placement="top" ref="tooltip">
+      <span slot="content">{{ value }}</span>
+      <div class="el-slider__button" :class="{ 'hover': hovering, 'dragging': dragging }"></div>
+    </el-tooltip>
+  </div>
+</template>
+
+<script>
+  import ElTooltip from 'element-ui/packages/tooltip';
+
+  export default {
+    name: 'ElSliderButton',
+
+    components: {
+      ElTooltip
+    },
+
+    props: {
+      value: {
+        type: Number,
+        default: 0
+      }
+    },
+
+    data() {
+      return {
+        hovering: false,
+        dragging: false,
+        startX: 0,
+        currentX: 0,
+        startPosition: 0,
+        newPosition: null,
+        oldValue: this.value
+      };
+    },
+
+    computed: {
+      disabled() {
+        return this.$parent.disabled;
+      },
+
+      max() {
+        return this.$parent.max;
+      },
+
+      min() {
+        return this.$parent.min;
+      },
+
+      step() {
+        return this.$parent.step;
+      },
+
+      precision() {
+        return this.$parent.precision;
+      },
+
+      currentPosition() {
+        return `${ (this.value - this.min) / (this.max - this.min) * 100 }%`;
+      }
+    },
+
+    watch: {
+      dragging(val) {
+        this.$parent.dragging = val;
+      }
+    },
+
+    methods: {
+      showTooltip() {
+        this.$refs.tooltip && (this.$refs.tooltip.showPopper = true);
+      },
+
+      hideTooltip() {
+        this.$refs.tooltip && (this.$refs.tooltip.showPopper = false);
+      },
+
+      handleMouseEnter() {
+        this.hovering = true;
+        this.showTooltip();
+      },
+  
+      handleMouseLeave() {
+        this.hovering = false;
+        this.hideTooltip();
+      },
+
+      onButtonDown(event) {
+        if (this.disabled) return;
+        this.onDragStart(event);
+        window.addEventListener('mousemove', this.onDragging);
+        window.addEventListener('mouseup', this.onDragEnd);
+        window.addEventListener('contextmenu', this.onDragEnd);
+      },
+
+      onDragStart(event) {
+        this.dragging = true;
+        this.startX = event.clientX;
+        this.startPosition = parseInt(this.currentPosition, 10);
+      },
+
+      onDragging(event) {
+        if (this.dragging) {
+          this.showTooltip();
+          this.currentX = event.clientX;
+          const diff = (this.currentX - this.startX) / this.$parent.$sliderWidth * 100;
+          this.newPosition = this.startPosition + diff;
+          this.setPosition(this.newPosition);
+        }
+      },
+
+      onDragEnd() {
+        if (this.dragging) {
+          /*
+           * 防止在 mouseup 后立即触发 click,导致滑块有几率产生一小段位移
+           * 不使用 preventDefault 是因为 mouseup 和 click 没有注册在同一个 DOM 上
+           */
+          setTimeout(() => {
+            this.dragging = false;
+            this.hideTooltip();
+            this.setPosition(this.newPosition);
+          }, 0);
+          window.removeEventListener('mousemove', this.onDragging);
+          window.removeEventListener('mouseup', this.onDragEnd);
+          window.removeEventListener('contextmenu', this.onDragEnd);
+        }
+      },
+
+      setPosition(newPosition) {
+        if (newPosition < 0) {
+          newPosition = 0;
+        } else if (newPosition > 100) {
+          newPosition = 100;
+        }
+        const lengthPerStep = 100 / ((this.max - this.min) / this.step);
+        const steps = Math.round(newPosition / lengthPerStep);
+        let value = steps * lengthPerStep * (this.max - this.min) * 0.01 + this.min;
+        value = parseFloat(value.toFixed(this.precision));
+        this.$emit('input', value);
+        this.$refs.tooltip && this.$refs.tooltip.updatePopper();
+        if (!this.dragging && this.value !== this.oldValue) {
+          this.oldValue = this.value;
+        }
+      }
+    }
+  };
+</script>

+ 164 - 131
packages/slider/src/main.vue

@@ -2,9 +2,8 @@
   <div class="el-slider">
     <el-input-number
       v-model="inputValue"
-      v-if="showInput"
+      v-if="showInput && !range"
       class="el-slider__input"
-      @keyup.native="onInputChange"
       ref="input"
       :step="step"
       :disabled="disabled"
@@ -15,29 +14,37 @@
     </el-input-number>
     <div class="el-slider__runway"
       :class="{ 'show-input': showInput, 'disabled': disabled }"
-      @click="onSliderClick" ref="slider">
-      <div class="el-slider__bar" :style="{ width: currentPosition }"></div>
+      @click="onSliderClick"
+      ref="slider">
       <div
-        class="el-slider__button-wrapper"
-        @mouseenter="handleMouseEnter"
-        @mouseleave="handleMouseLeave"
-        @mousedown="onButtonDown"
-        :class="{ 'hover': hovering, 'dragging': dragging }"
-        :style="{left: currentPosition}"
-        ref="button">
-        <el-tooltip placement="top" ref="tooltip">
-          <span slot="content">{{ value }}</span>
-          <div class="el-slider__button" :class="{ 'hover': hovering, 'dragging': dragging }"></div>
-        </el-tooltip>
+        class="el-slider__bar"
+        :style="{
+          width: barWidth,
+          left: barLeft
+        }">
+      </div>
+      <slider-button
+        v-model="firstValue"
+        ref="button1">
+      </slider-button>
+      <slider-button
+        v-model="secondValue"
+        ref="button2"
+        v-if="range">
+      </slider-button>
+      <div
+        class="el-slider__stop"
+        v-for="item in stops"
+        :style="{ 'left': item + '%' }"
+        v-if="showStops">
       </div>
-      <div class="el-slider__stop" v-for="item in stops" :style="{ 'left': item + '%' }" v-if="showStops"></div>
     </div>
   </div>
 </template>
 
 <script type="text/babel">
   import ElInputNumber from 'element-ui/packages/input-number';
-  import ElTooltip from 'element-ui/packages/tooltip';
+  import SliderButton from './button.vue';
   import { getStyle } from 'element-ui/src/utils/dom';
 
   export default {
@@ -56,12 +63,8 @@
         type: Number,
         default: 1
       },
-      defaultValue: {
-        type: Number,
-        default: 0
-      },
       value: {
-        type: Number,
+        type: [Number, Array],
         default: 0
       },
       showInput: {
@@ -79,142 +82,136 @@
       disabled: {
         type: Boolean,
         default: false
+      },
+      range: {
+        type: Boolean,
+        default: false
       }
     },
 
     components: {
       ElInputNumber,
-      ElTooltip
+      SliderButton
     },
 
     data() {
       return {
+        firstValue: null,
+        secondValue: null,
+        oldValue: null,
         precision: 0,
         inputValue: null,
-        timeout: null,
-        hovering: false,
-        dragging: false,
-        startX: 0,
-        currentX: 0,
-        startPos: 0,
-        newPos: null,
-        oldValue: this.value,
-        currentPosition: (this.value - this.min) / (this.max - this.min) * 100 + '%'
+        dragging: false
       };
     },
 
     watch: {
       inputValue(val) {
-        this.$emit('input', Number(val));
+        this.firstValue = val;
       },
 
-      value(val) {
-        this.$nextTick(() => {
-          this.updatePopper();
-        });
-        if (typeof val !== 'number' || isNaN(val) || val < this.min) {
-          this.$emit('input', this.min);
-          return;
-        }
-        if (val > this.max) {
-          this.$emit('input', this.max);
+      value(val, oldVal) {
+        if (this.dragging ||
+          Array.isArray(val) &&
+          Array.isArray(oldVal) &&
+          val.every((item, index) => item === oldVal[index])) {
           return;
         }
-        this.inputValue = val;
-        this.setPosition((val - this.min) * 100 / (this.max - this.min));
-      }
-    },
-
-    methods: {
-      handleMouseEnter() {
-        this.hovering = true;
-        this.$refs.tooltip.showPopper = true;
-      },
-
-      handleMouseLeave() {
-        this.hovering = false;
-        this.$refs.tooltip.showPopper = false;
+        this.setValues();
       },
 
-      updatePopper() {
-        this.$refs.tooltip.updatePopper();
+      dragging(val) {
+        if (!val) {
+          this.setValues();
+        }
       },
 
-      setPosition(newPos) {
-        if (newPos < 0) {
-          newPos = 0;
-        } else if (newPos > 100) {
-          newPos = 100;
+      firstValue(val) {
+        if (this.range) {
+          this.$emit('input', [this.minValue, this.maxValue]);
+        } else {
+          this.inputValue = val;
+          this.$emit('input', val);
         }
+      },
 
-        const lengthPerStep = 100 / ((this.max - this.min) / this.step);
-        const steps = Math.round(newPos / lengthPerStep);
-        let value = steps * lengthPerStep * (this.max - this.min) * 0.01 + this.min;
-        value = parseFloat(value.toFixed(this.precision));
-        this.$emit('input', value);
-        this.currentPosition = (this.value - this.min) / (this.max - this.min) * 100 + '%';
-        if (!this.dragging) {
-          if (this.value !== this.oldValue) {
-            this.$emit('change', this.value);
-            this.oldValue = this.value;
-          }
+      secondValue() {
+        if (this.range) {
+          this.$emit('input', [this.minValue, this.maxValue]);
         }
       },
 
-      onSliderClick(event) {
-        if (this.disabled || this.dragging) return;
-        const sliderOffsetLeft = this.$refs.slider.getBoundingClientRect().left;
-        this.setPosition((event.clientX - sliderOffsetLeft) / this.$sliderWidth * 100);
+      min() {
+        this.setValues();
       },
 
-      onInputChange() {
-        if (this.value === '') {
-          return;
-        }
-        if (!isNaN(this.value)) {
-          this.setPosition((this.value - this.min) * 100 / (this.max - this.min));
-        }
-      },
+      max() {
+        this.setValues();
+      }
+    },
 
-      onDragStart(event) {
-        this.dragging = true;
-        this.startX = event.clientX;
-        this.startPos = parseInt(this.currentPosition, 10);
+    methods: {
+      valueChanged() {
+        if (this.range) {
+          return ![this.minValue, this.maxValue]
+            .every((item, index) => item === this.oldValue[index]);
+        } else {
+          return this.value !== this.oldValue;
+        }
       },
-
-      onDragging(event) {
-        if (this.dragging) {
-          this.$refs.tooltip.showPopper = true;
-          this.currentX = event.clientX;
-          const diff = (this.currentX - this.startX) / this.$sliderWidth * 100;
-          this.newPos = this.startPos + diff;
-          this.setPosition(this.newPos);
+      setValues() {
+        const val = this.value;
+        if (this.range && Array.isArray(val)) {
+          if (val[1] < this.min) {
+            this.$emit('input', [this.min, this.min]);
+          } else if (val[0] > this.max) {
+            this.$emit('input', [this.max, this.max]);
+          } else if (val[0] < this.min) {
+            this.$emit('input', [this.min, val[1]]);
+          } else if (val[1] > this.max) {
+            this.$emit('input', [val[0], this.max]);
+          } else {
+            this.firstValue = val[0];
+            this.secondValue = val[1];
+            if (this.valueChanged()) {
+              this.$emit('change', [this.minValue, this.maxValue]);
+              this.oldValue = val.slice();
+            }
+          }
+        } else if (!this.range && typeof val === 'number' && !isNaN(val)) {
+          if (val < this.min) {
+            this.$emit('input', this.min);
+          } else if (val > this.max) {
+            this.$emit('input', this.max);
+          } else {
+            this.firstValue = val;
+            if (this.valueChanged()) {
+              this.$emit('change', val);
+              this.oldValue = val;
+            }
+          }
         }
       },
 
-      onDragEnd() {
-        if (this.dragging) {
-          /*
-           * 防止在 mouseup 后立即触发 click,导致滑块有几率产生一小段位移
-           * 不使用 preventDefault 是因为 mouseup 和 click 没有注册在同一个 DOM 上
-           */
-          setTimeout(() => {
-            this.dragging = false;
-            this.$refs.tooltip.showPopper = false;
-            this.setPosition(this.newPos);
-          }, 0);
-          window.removeEventListener('mousemove', this.onDragging);
-          window.removeEventListener('mouseup', this.onDragEnd);
-          window.removeEventListener('contextmenu', this.onDragEnd);
+      setPosition(percent) {
+        const targetValue = this.min + percent * (this.max - this.min) / 100;
+        if (!this.range) {
+          this.$refs.button1.setPosition(percent);
+          return;
         }
+        let button;
+        if (Math.abs(this.minValue - targetValue) < Math.abs(this.maxValue - targetValue)) {
+          button = this.firstValue < this.secondValue ? 'button1' : 'button2';
+        } else {
+          button = this.firstValue > this.secondValue ? 'button1' : 'button2';
+        }
+        this.$refs[button].setPosition(percent);
       },
 
-      onButtonDown(event) {
-        if (this.disabled) return;
-        this.onDragStart(event);
-        window.addEventListener('mousemove', this.onDragging);
-        window.addEventListener('mouseup', this.onDragEnd);
-        window.addEventListener('contextmenu', this.onDragEnd);
+      onSliderClick(event) {
+        if (this.disabled || this.dragging) return;
+        const sliderOffsetLeft = this.$refs.slider.getBoundingClientRect().left;
+        this.setPosition((event.clientX - sliderOffsetLeft) / this.$sliderWidth * 100);
       }
     },
 
@@ -224,31 +221,67 @@
       },
 
       stops() {
-        const stopCount = (this.max - this.value) / this.step;
-        const currentLeft = parseFloat(this.currentPosition);
+        const stopCount = (this.max - this.min) / this.step;
         const stepWidth = 100 * this.step / (this.max - this.min);
         const result = [];
         for (let i = 1; i < stopCount; i++) {
-          result.push(currentLeft + i * stepWidth);
+          result.push(i * stepWidth);
         }
-        return result;
+        if (this.range) {
+          return result.filter(step => {
+            return step < 100 * (this.minValue - this.min) / (this.max - this.min) ||
+              step > 100 * (this.maxValue - this.min) / (this.max - this.min);
+          });
+        } else {
+          return result.filter(step => step > 100 * (this.firstValue - this.min) / (this.max - this.min));
+        }
+      },
+
+      minValue() {
+        return Math.min(this.firstValue, this.secondValue);
+      },
+
+      maxValue() {
+        return Math.max(this.firstValue, this.secondValue);
+      },
+
+      barWidth() {
+        return this.range
+          ? `${ 100 * (this.maxValue - this.minValue) / (this.max - this.min) }%`
+          : `${ 100 * (this.firstValue - this.min) / (this.max - this.min) }%`;
+      },
+
+      barLeft() {
+        return this.range
+          ? `${ 100 * (this.minValue - this.min) / (this.max - this.min) }%`
+          : '0%';
       }
     },
 
-    created() {
-      if (typeof this.value !== 'number' ||
-        isNaN(this.value) ||
-        this.value < this.min) {
-        this.$emit('input', this.min);
-      } else if (this.value > this.max) {
-        this.$emit('input', this.max);
+    mounted() {
+      if (this.range) {
+        if (Array.isArray(this.value)) {
+          this.firstValue = Math.max(this.min, this.value[0]);
+          this.secondValue = Math.min(this.max, this.value[1]);
+        } else {
+          this.firstValue = this.min;
+          this.secondValue = this.max;
+        }
+        this.oldValue = [this.firstValue, this.secondValue];
+      } else {
+        if (typeof this.value !== 'number' || isNaN(this.value)) {
+          this.firstValue = this.min;
+        } else {
+          this.firstValue = Math.min(this.max, Math.max(this.min, this.value));
+        }
+        this.oldValue = this.firstValue;
       }
       let precisions = [this.min, this.max, this.step].map(item => {
         let decimal = ('' + item).split('.')[1];
         return decimal ? decimal.length : 0;
       });
       this.precision = Math.max.apply(null, precisions);
-      this.inputValue = this.inputValue || this.value;
+      this.inputValue = this.inputValue || this.firstValue;
     }
   };
 </script>

+ 151 - 24
test/unit/specs/slider.spec.js

@@ -37,7 +37,7 @@ describe('Slider', () => {
           done();
         });
       });
-    }, 100);
+    }, 10);
   });
 
   it('show tooltip', () => {
@@ -55,7 +55,7 @@ describe('Slider', () => {
         };
       }
     }, true);
-    const slider = vm.$children[0];
+    const slider = vm.$children[0].$children[0];
     slider.handleMouseEnter();
     expect(slider.$refs.tooltip.showPopper).to.true;
     slider.handleMouseLeave();
@@ -76,14 +76,14 @@ describe('Slider', () => {
         };
       }
     }, true);
-    const slider = vm.$children[0];
+    const slider = vm.$children[0].$children[0];
+    slider.onButtonDown({ clientX: 0 });
+    slider.onDragging({ clientX: 100 });
+    slider.onDragEnd();
     setTimeout(() => {
-      slider.onButtonDown({ clientX: 0 });
-      slider.onDragging({ clientX: 100 });
-      slider.onDragEnd();
       expect(vm.value > 0).to.true;
       done();
-    }, 150);
+    }, 10);
   });
 
   it('step', done => {
@@ -100,14 +100,14 @@ describe('Slider', () => {
         };
       }
     }, true);
-    const slider = vm.$children[0];
+    const slider = vm.$children[0].$children[0];
+    slider.onButtonDown({ clientX: 0 });
+    slider.onDragging({ clientX: 100 });
+    slider.onDragEnd();
     setTimeout(() => {
-      slider.onButtonDown({ clientX: 0 });
-      slider.onDragging({ clientX: 100 });
-      slider.onDragEnd();
       expect(vm.value > 0.4 && vm.value < 0.6).to.true;
       done();
-    }, 150);
+    }, 10);
   });
 
   it('click', done => {
@@ -130,8 +130,8 @@ describe('Slider', () => {
       setTimeout(() => {
         expect(vm.value > 0).to.true;
         done();
-      }, 150);
-    }, 150);
+      }, 10);
+    }, 10);
   });
 
   it('disabled', done => {
@@ -148,15 +148,14 @@ describe('Slider', () => {
         };
       }
     }, true);
-    const slider = vm.$children[0];
+    const slider = vm.$children[0].$children[0];
+    slider.onButtonDown({ clientX: 0 });
+    slider.onDragging({ clientX: 100 });
+    slider.onDragEnd();
     setTimeout(() => {
-      slider.onButtonDown({ clientX: 0 });
-      slider.onDragging({ clientX: 100 });
-      slider.onDragEnd();
-      slider.onSliderClick({ clientX: 200 });
       expect(vm.value).to.equal(0);
       done();
-    }, 100);
+    }, 10);
   });
 
   it('show input', done => {
@@ -180,17 +179,145 @@ describe('Slider', () => {
       setTimeout(() => {
         expect(vm.value).to.equal(40);
         done();
-      }, 150);
-    }, 150);
+      }, 10);
+    }, 10);
   });
 
-  it('show stops', done => {
+  it('show stops', () => {
     vm = createTest(Slider, {
       showStops: true,
       step: 10
     }, true);
     const stops = vm.$el.querySelectorAll('.el-slider__stop');
     expect(stops.length).to.equal(9);
-    done();
+  });
+
+  describe('range', () => {
+    it('basic ranged slider', () => {
+      vm = createVue({
+        template: `
+        <div>
+          <el-slider v-model="value" range></el-slider>
+        </div>
+      `,
+
+        data() {
+          return {
+            value: [10, 20]
+          };
+        }
+      }, true);
+      const buttons = vm.$children[0].$children;
+      expect(buttons.length).to.equal(2);
+    });
+
+    it('should not exceed min and max', done => {
+      vm = createVue({
+        template: `
+        <div>
+          <el-slider v-model="value" range :min="50">
+          </el-slider>
+        </div>
+      `,
+
+        data() {
+          return {
+            value: [50, 60]
+          };
+        }
+      }, true);
+      setTimeout(() => {
+        vm.value = [40, 60];
+        setTimeout(() => {
+          expect(vm.value).to.deep.equal([50, 60]);
+          vm.value = [50, 120];
+          setTimeout(() => {
+            expect(vm.value).to.deep.equal([50, 100]);
+            done();
+          }, 10);
+        }, 10);
+      }, 10);
+    });
+
+    it('click', done => {
+      vm = createVue({
+        template: `
+        <div style="width: 200px;">
+          <el-slider range v-model="value"></el-slider>
+        </div>
+      `,
+
+        data() {
+          return {
+            value: [0, 100]
+          };
+        }
+      }, true);
+      const slider = vm.$children[0];
+      setTimeout(() => {
+        slider.onSliderClick({ clientX: 100 });
+        setTimeout(() => {
+          expect(vm.value[0] > 0).to.true;
+          expect(vm.value[1]).to.equal(100);
+          done();
+        }, 10);
+      }, 10);
+    });
+
+    it('responsive to dynamic min and max', done => {
+      vm = createVue({
+        template: `
+        <div>
+          <el-slider v-model="value" range :min="min" :max="max">
+          </el-slider>
+        </div>
+      `,
+
+        data() {
+          return {
+            min: 0,
+            max: 100,
+            value: [50, 80]
+          };
+        }
+      }, true);
+      setTimeout(() => {
+        vm.min = 60;
+        setTimeout(() => {
+          expect(vm.value).to.deep.equal([60, 80]);
+          vm.min = 30;
+          vm.max = 40;
+          setTimeout(() => {
+            expect(vm.value).to.deep.equal([40, 40]);
+            done();
+          }, 10);
+        }, 10);
+      }, 10);
+    });
+
+    it('show stops', done => {
+      vm = createVue({
+        template: `
+        <div>
+          <el-slider
+            v-model="value"
+            range
+            :step="10"
+            show-stops></el-slider>
+        </div>
+      `,
+
+        data() {
+          return {
+            value: [30, 60]
+          };
+        }
+      }, true);
+      setTimeout(() => {
+        const stops = vm.$el.querySelectorAll('.el-slider__stop');
+        expect(stops.length).to.equal(5);
+        done();
+      }, 10);
+    });
   });
 });