RangeFacet
RangeFacet renders a histogram, slider, and optional default/custom ranges for a numeric year range.
It reads buckets from the results aggregations and stores the selected range in the query filters.
Usage
<RangeFacet
title="Publication Year"
agg={{ aggName: "years" }}
rangeSeparator=".."
// optional
defaultRanges={[
{ label: "Last 1 year", type: "years", value: 1 },
{ label: "Last 5 years", type: "years", value: 5 },
{ label: "Last 6 months", type: "months", value: 6 },
]}
enableCustomRange
// customize custom-range labels and placeholders
customDatesLabel="Pick dates"
dateRangeToLabel="–"
datePlaceholders={{ YYYY: "Year", MM: "Mo", DD: "Day" }}
/>
Aggregation format
The component expects the aggregation result under results.aggregations[aggName].buckets
with a numeric key (or key_as_string) and doc_count:
{
"years": {
"buckets": [
{ "key": 2021, "doc_count": 12 },
{ "key": 2022, "doc_count": 7 }
]
}
}
Filters format
The selected filter is stored as [ "<aggName>", "<from><rangeSeparator><to>" ].
<from> and <to> can be YYYY or full ISO dates (YYYY-MM-DD) when custom ranges
include months/days.
Props
title
StringThe title rendered in the card header.
agg
ObjectAn object that describes the aggregation to read from
results:- aggName
String: the aggregation name to look for inresults
- aggName
rangeSeparator
StringThe separator used when building the filter value (for example
..or--).defaultRanges
ArrayoptionalDefault ranges rendered as checkboxes. Each entry should have:
- label
String - type
String:"years"or"months" - value
Number: number of years/months back
- label
enableCustomRange
BooleanoptionalWhen
true, enables the custom date range checkbox with optional month/day inputs.customDatesLabel
StringoptionalLabel for the button that expands the custom date inputs (year/month/day). Default:
"Custom Dates".dateRangeToLabel
StringoptionalText shown between the "from" and "to" date inputs. Default:
"to".datePlaceholders
ObjectoptionalPlaceholder text for the date input fields. Only keys you provide are used; others keep their defaults. Keys:
YYYY,MM,DD. Example:{ YYYY: "Year", MM: "Mo", DD: "Day" }. Default placeholders when not set:"YYYY","MM","DD".histogramHeight
NumberoptionalHeight in pixels for the histogram area.
overridableId
StringoptionalOptional string to define a specific overridable id.
Usage when overriding
Overriding full component
Use this when you want to replace the whole RangeFacet logic and UI.
Brief flow (matches the default behavior):
- Read buckets from
currentResultsState.data.aggregations[agg.aggName].buckets. - Compute
min/maxyears from bucket keys. - Compute the histogram data with aggregation keys and doc counts.
- Store the selected range in
currentQueryState.filtersas:[agg.aggName,${from}${rangeSeparator}${to}]. - Render your own UI (histogram/slider/etc).
Example with only histogram and custom filter facet:
class MyRangeFacet extends React.Component {
constructor(props) {
super(props);
const { min, max } = this.getMinMax();
this.state = { range: [min, max], activeFilter: null };
}
applyRange = (from, to) => {
// Apply the filter
};
onBarClick = (year) => {
this.applyRange(year, year);
};
onClear = () => {
// Clear the filter
};
getBuckets = () => {
const { currentResultsState, agg } = this.props;
const resultsAggregations = currentResultsState?.data?.aggregations;
return resultsAggregations?.[agg.aggName]?.buckets ?? [];
};
render() {
const { title, agg, rangeSeparator, currentQueryState, histogramHeight } =
this.props;
const { min, max } = this.getMinMax();
const hasActiveFilter;
// Extract the buckets in aggregations
const buckets = getBuckets();
// Active filter range: [from(year), to(year)]
const { range } = this.state
const containerCmp = (
<>
<RangeHistogram
data={
// Extract key and doc_count from agg into a map, then fill every year with the count.
getHistogramData(buckets, min, max)
}
range={range}
height={histogramHeight}
onBarClick={this.onBarClick}
/>
<RangeCustomFilter
min={min}
max={max}
value={range}
rangeSeparator={rangeSeparator}
activeMode={RANGE_MODES.CUSTOM}
activeFilter={this.state.activeFilter}
onApply={(r, s) => this.applyRange(r[0], r[1])}
onClear={this.onClear}
/>
</>
);
return (
<RangeFacetElement
title={title}
containerCmp={containerCmp}
hasActiveFilter={hasActiveFilter}
onClear={this.onClear}
/>
);
}
}
const overriddenComponents = {
RangeFacet: MyRangeFacet,
};
Overriding the element
Wraps the default content and lets you customize the header or layout while keeping the built-in histogram/slider/custom filters.
const MyRangeFacetElement = ({ title, containerCmp, hasActiveFilter, onClear }) => {
return (
<section>
<header>
<strong>{title}</strong>
{hasActiveFilter && (
<button type="button" onClick={onClear}>
Clear
</button>
)}
</header>
{containerCmp}
</section>
);
};
const overriddenComponents = {
"RangeFacet.element": MyRangeFacetElement,
};
Overriding the histogram
Replace the histogram visualization. Use data and range to show counts and
highlight the selected years. Call onBarClick(year) to update the range.
const MyRangeHistogram = ({ data, range, onBarClick }) => {
return (
<ul>
{data.map((item) => {
...
})}
</ul>
);
};
const overriddenComponents = {
"RangeFacet.Histogram.element": MyRangeHistogram,
};
Tooltip override (histogram)
The histogram tooltip is its own overridable element. The tooltip value has the
shape { year, count, x, y } where x/y are viewport coordinates used to position
the tooltip.
const MyHistogramTooltip = ({ tooltip }) => {
if (!tooltip) return null;
return (
<div>
{tooltip.year}: {tooltip.count}
</div>
);
};
const overriddenComponents = {
"RangeFacet.Histogram.Tooltip.element": MyHistogramTooltip,
};
Overriding the slider
Replace the range slider. Call onChange([min, max]) when the selected range changes.
const MyRangeSlider = ({ min, max, value, onChange }) => {
...
};
const overriddenComponents = {
"RangeFacet.Slider.element": MyRangeSlider,
};
Overriding the default filters
Replace the default ranges list. Call onToggle(range, checked) to select or clear
a range option.
const MyDefaultFilters = ({ ranges, activeLabel, onToggle }) => {
...
};
const overriddenComponents = {
"RangeFacet.DefaultFilters.element": MyDefaultFilters,
};
Overriding the custom filter
Replace the custom range checkbox and inputs wrapper. Use checked/expanded to
control layout and render dateError when validation fails.
const MyCustomFilter = ({ checked, expanded, dateError, activeFilter, children }) => {
...
};
const overriddenComponents = {
"RangeFacet.CustomFilter.element": MyCustomFilter,
};
Overriding the date range inputs (for custom filter)
Replace the date input layout for the custom filter. Use format to decide which
parts to render and values to populate them.
const MyDateRangeInputs = ({ format, values, disabled, children }) => {
...
};
const overriddenComponents = {
"RangeFacet.DateInputs.Layout": MyDateRangeInputs,
};
format controls the input structure:
YYYYrenders a single row with year-only inputs.YYYY-MMandYYYY-MM-DDrender stacked rows for from/to values.
RangeFacet parameters
Component that wraps the histogram, slider, and optional filters.
title
StringThe title to render.
containerCmp
React componentThe rendered content (histogram, slider, and filters).
hasActiveFilter
Booleantrueif a range filter is active,falseotherwise.onClear
FunctionFunction to call to clear the active range filter.
Callback methods used by overrides
onClear
FunctionClears the active range filter and resets to the full available range.
onBarClick
FunctionAccepts a
yearand updates the range to that single year.onChange
FunctionAccepts
[from, to]to update the selected range (used by slider overrides).onToggle
FunctionAccepts
(range, checked)to select a default range or clear it.
Utilities
Helpers from src/lib/components/RangeFacet/utils.js used by RangeFacet and its sub-components.
- extractBuckets(resultsAggregations, aggName): read aggregation buckets safely.
- getKey(bucket): returns
key_as_stringwhen present, otherwisekey. - getHistogramData(buckets, min, max): build continuous year data with counts.
- resolveDefaultRange(range, min, max, rangeSeparator): compute the year range and optional ISO date range for a default option.
- normalizeFilterValue(filterValue, rangeSeparator, minYear, maxYear): sanitize
and normalize a filter string like
YYYY..YYYYor ISO dates. - parseFilterYears(filterValue, rangeSeparator): parse years from a filter string (supports ISO dates).
- findDefaultLabel(defaultRanges, filterValue, min, max, rangeSeparator): match an active filter to a default range label.
- buildDateRange({ fromYear, fromMonth, fromDay, toYear, toMonth, toDay, rangeSeparator }):
build a
YYYY..YYYYor ISOYYYY-MM-DD..YYYY-MM-DDstring. - RANGE_MODES: enum with
DEFAULTandCUSTOMused for optional default and custom filters.