Skip to content
This repository has been archived by the owner on Dec 30, 2022. It is now read-only.

Commit

Permalink
feat(RangeInput): add widget
Browse files Browse the repository at this point in the history
  • Loading branch information
samouss committed Oct 20, 2017
1 parent a92b3f4 commit cbc1e1b
Show file tree
Hide file tree
Showing 6 changed files with 1,552 additions and 1 deletion.
3 changes: 2 additions & 1 deletion src/__tests__/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,11 @@ test('Should register all components when installed', () => {
expect(component).toBeCalledWith('ais-search-box', expect.any(Object));
expect(component).toBeCalledWith('ais-clear', expect.any(Object));
expect(component).toBeCalledWith('ais-rating', expect.any(Object));
expect(component).toBeCalledWith('ais-range-input', expect.any(Object));
expect(component).toBeCalledWith('ais-no-results', expect.any(Object));
expect(component).toBeCalledWith('ais-refinement-list', expect.any(Object));
expect(component).toBeCalledWith('ais-price-range', expect.any(Object));
expect(component).toBeCalledWith('ais-powered-by', expect.any(Object));

expect(component).toHaveBeenCalledTimes(18);
expect(component).toHaveBeenCalledTimes(19);
});
268 changes: 268 additions & 0 deletions src/components/RangeInput.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,268 @@
<template>
<div :class="bem()">
<slot name="header" />

<form @submit.prevent="onSubmit(refinement)">
<input
type="number"
:class="bem('input', 'from')"
:min="range.min"
:max="range.max"
:step="step"
:placeholder="rangeForRendering.min"
:value="refinementForRendering.min"
@input="refinement.min = $event.target.value"
/>

<slot name="separator">
to
</slot>

<input
type="number"
:class="bem('input', 'to')"
:min="range.min"
:max="range.max"
:step="step"
:placeholder="rangeForRendering.max"
:value="refinementForRendering.max"
@input="refinement.max = $event.target.value"
/>
<button slot="submit">
ok
</button>
</form>

<slot name="footer" />
</div>
</template>

<script>
import algoliaComponent from '../component';
import { FACET_OR } from '../store';
export default {
mixins: [algoliaComponent],
props: {
attributeName: {
type: String,
required: true,
},
min: {
type: Number,
},
max: {
type: Number,
},
defaultRefinement: {
type: Object,
default() {
return {};
},
},
precision: {
type: Number,
default: 0,
validator(value) {
return value >= 0;
},
},
},
data() {
return {
blockClassName: 'ais-range-input',
};
},
created() {
const { min: minValue, max: maxValue } = this.defaultRefinement;
let min;
if (minValue !== undefined) {
min = minValue;
} else if (this.min !== undefined) {
min = this.min;
}
let max;
if (maxValue !== undefined) {
max = maxValue;
} else if (this.max !== undefined) {
max = this.max;
}
this.searchStore.stop();
this.searchStore.addFacet(this.attributeName, FACET_OR);
if (min !== undefined) {
this.searchStore.addNumericRefinement(this.attributeName, '>=', min);
}
if (max !== undefined) {
this.searchStore.addNumericRefinement(this.attributeName, '<=', max);
}
this.searchStore.start();
this.searchStore.refresh();
},
destroyed() {
this.searchStore.removeFacet(this.attributeName);
},
computed: {
step({ precision }) {
return 1 / Math.pow(10, precision);
},
refinement({ attributeName, searchStore }) {
const { numericValue: min } =
searchStore.activeRefinements.find(
r =>
r.attributeName === attributeName &&
r.type === 'numeric' &&
r.operator === '>='
) || {};
const { numericValue: max } =
searchStore.activeRefinements.find(
r =>
r.attributeName === attributeName &&
r.type === 'numeric' &&
r.operator === '<='
) || {};
return {
min,
max,
};
},
range({
attributeName,
precision,
searchStore,
min: minRange,
max: maxRange,
}) {
const { min: minStat, max: maxStat } = searchStore.getFacetStats(
attributeName
);
const pow = Math.pow(10, precision);
let min;
if (minRange !== undefined) {
min = minRange;
} else if (minStat !== undefined) {
min = minStat;
} else {
min = -Infinity;
}
let max;
if (maxRange !== undefined) {
max = maxRange;
} else if (maxStat !== undefined) {
max = maxStat;
} else {
max = Infinity;
}
return {
min: min !== -Infinity ? Math.floor(min * pow) / pow : min,
max: max !== Infinity ? Math.ceil(max * pow) / pow : max,
};
},
rangeForRendering({ range }) {
const { min, max } = range;
const isMinInfinity = min === -Infinity;
const isMaxInfinity = max === Infinity;
return {
min: !isMinInfinity && !isMaxInfinity ? min : '',
max: !isMinInfinity && !isMaxInfinity ? max : '',
};
},
refinementForRendering({ refinement, range }) {
const { min: minValue, max: maxValue } = refinement;
const { min: minRange, max: maxRange } = range;
return {
min: minValue !== undefined && minValue !== minRange ? minValue : '',
max: maxValue !== undefined && maxValue !== maxRange ? maxValue : '',
};
},
},
methods: {
nextValueForRefinment(hasBound, isReset, range, value) {
let next;
if (!hasBound && range === value) {
next = undefined;
} else if (hasBound && isReset) {
next = range;
} else {
next = value;
}
return next;
},
onSubmit({ min: minNext = '', max: maxNext = '' }) {
const { min: minRange, max: maxRange } = this.range;
const hasMinBound = this.min !== undefined;
const hasMaxBound = this.max !== undefined;
const isMinReset = minNext === '';
const isMaxReset = maxNext === '';
const minNextAsNumber = !isMinReset ? parseFloat(minNext) : undefined;
const maxNextAsNumber = !isMaxReset ? parseFloat(maxNext) : undefined;
const newMinNext = this.nextValueForRefinment(
hasMinBound,
isMinReset,
minRange,
minNextAsNumber
);
const newMaxNext = this.nextValueForRefinment(
hasMaxBound,
isMaxReset,
maxRange,
maxNextAsNumber
);
this.searchStore.stop();
this.searchStore.removeNumericRefinement(this.attributeName, '>=');
if (newMinNext !== undefined) {
this.searchStore.addNumericRefinement(
this.attributeName,
'>=',
newMinNext
);
}
this.searchStore.removeNumericRefinement(this.attributeName, '<=');
if (newMaxNext !== undefined) {
this.searchStore.addNumericRefinement(
this.attributeName,
'<=',
newMaxNext
);
}
this.searchStore.start();
this.searchStore.refresh();
},
},
};
</script>
109 changes: 109 additions & 0 deletions src/components/__tests__/__snapshots__/range-input.js.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`RangeInput render default 1`] = `
<div class="ais-range-input">
<form>
<input type="number"
min="-Infinity"
max="Infinity"
step="1"
placeholder
class="ais-range-input__input ais-range-input__input--from"
>
to
<input type="number"
min="-Infinity"
max="Infinity"
step="1"
placeholder
class="ais-range-input__input ais-range-input__input--to"
>
<button slot="submit">
ok
</button>
</form>
</div>
`;
exports[`RangeInput render with max 1`] = `
<div class="ais-range-input">
<form>
<input type="number"
min="-Infinity"
max="500"
step="1"
placeholder
class="ais-range-input__input ais-range-input__input--from"
>
to
<input type="number"
min="-Infinity"
max="500"
step="1"
placeholder
class="ais-range-input__input ais-range-input__input--to"
>
<button slot="submit">
ok
</button>
</form>
</div>
`;
exports[`RangeInput render with min 1`] = `
<div class="ais-range-input">
<form>
<input type="number"
min="10"
max="Infinity"
step="1"
placeholder
class="ais-range-input__input ais-range-input__input--from"
>
to
<input type="number"
min="10"
max="Infinity"
step="1"
placeholder
class="ais-range-input__input ais-range-input__input--to"
>
<button slot="submit">
ok
</button>
</form>
</div>
`;
exports[`RangeInput render with precision 1`] = `
<div class="ais-range-input">
<form>
<input type="number"
min="-Infinity"
max="Infinity"
step="0.01"
placeholder
class="ais-range-input__input ais-range-input__input--from"
>
to
<input type="number"
min="-Infinity"
max="Infinity"
step="0.01"
placeholder
class="ais-range-input__input ais-range-input__input--to"
>
<button slot="submit">
ok
</button>
</form>
</div>
`;
Loading

0 comments on commit cbc1e1b

Please sign in to comment.