Skip to content

Range slider does not support a minimum gap of zero #1341

@AndyButland

Description

@AndyButland

Which exact UI Library version are you using? For example: 1.0.1 - don't just write v1.

1.17.2

Bug summary

When minGap is explicitly set to 0 on uui-range-slider, the component ignores it and falls back to using step as the minimum gap between handles. This prevents both handles from selecting the same value (e.g. a range of "6 to 6"), which is a valid use case for range sliders representing filters like age ranges or price ranges.

Specifics

This currently blocks the Umbraco CMS slider from supporting zero-gap ranges. See Umbraco-CMS#22067 and Umbraco-CMS#22078.

Steps to reproduce

<uui-range-slider label="Test" min="0" max="10" step="1" .minGap=${0}></uui-range-slider>

Try to drag both handles to the same value — the component enforces a minimum gap of 1 (the step value).

Bug details and resolution

If helpful, this is what Claude tells me is the root cause and approach to resolve:

Root cause

The component uses JavaScript truthiness checks to determine whether minGap was configured. Since 0 is falsy, it's indistinguishable from undefined (not set).

Affected locations in range-slider.element.ts:

1. _runGapChecks() — clears _minGap when it's less than step, but the guard this._minGap && already skips 0:

if (this._minGap && this._minGap < this._step) {
    this._minGap = undefined;

2. setValueLow() / setValueHigh() — falls back to step gap when minGap is falsy:

const clampMax = this.minGap
    ? this._highInputValue - this.minGap
    : this._highInputValue - this.step;

3. #transferValueToInternalValues() — same pattern, two occurrences:

this._minGap ? high - this._minGap : high - this._step

4. _runPropertiesChecks() — logs an error when both values are equal, even when that's intentionally allowed:

if (this._highInputValue === this._lowInputValue) {
    console.error(`...Low-end and high-end value should never be equal...`);
}

Suggested fix

Replace truthiness checks with !== undefined so that 0 is treated as an explicit value:

_runGapChecks():

// Before:
if (this._minGap && this._minGap < this._step) {
// After:
if (this._minGap !== undefined && this._minGap > 0 && this._minGap < this._step) {

setValueLow():

// Before:
const clampMax = this.minGap
    ? this._highInputValue - this.minGap
    : this._highInputValue - this.step;
// After:
const clampMax = this.minGap !== undefined
    ? this._highInputValue - this.minGap
    : this._highInputValue - this.step;

setValueHigh():

// Before:
const clampMin = this.minGap
    ? this._lowInputValue + this.minGap
    : this._lowInputValue + this.step;
// After:
const clampMin = this.minGap !== undefined
    ? this._lowInputValue + this.minGap
    : this._lowInputValue + this.step;

#transferValueToInternalValues() (two occurrences):

// Before:
this._minGap ? high - this._minGap : high - this._step
// After:
this._minGap !== undefined ? high - this._minGap : high - this._step
// Before:
this._minGap
    ? this._lowInputValue + this._minGap
    : this._lowInputValue + this._step
// After:
this._minGap !== undefined
    ? this._lowInputValue + this._minGap
    : this._lowInputValue + this._step

_runPropertiesChecks():

// Before:
if (this._highInputValue === this._lowInputValue) {
// After:
if (this._highInputValue === this._lowInputValue && this._minGap !== 0) {

Expected result / actual result

Setting minGap=0 should explicitly allow both handles to overlap, selecting equal low and high values.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingneeds triageThis issue has not been tested by a team member yet

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions