Merge pull request #1243 from umap-project/filters-refacto

Refactor "advanced filters" (and rename to facets)
This commit is contained in:
Yohan Boniface 2023-08-15 16:22:55 +02:00 committed by GitHub
commit 0f2198b4ff
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 165 additions and 173 deletions

View file

@ -481,7 +481,7 @@ i.info {
.umap-datalayer-container, .umap-datalayer-container,
.umap-layer-properties-container, .umap-layer-properties-container,
.umap-browse-data, .umap-browse-data,
.umap-filter-data, .umap-facet-search,
.umap-browse-datalayers { .umap-browse-datalayers {
padding: 0 10px; padding: 0 10px;
} }

View file

@ -753,11 +753,7 @@ L.U.Map.include({
const build = () => { const build = () => {
ul.innerHTML = '' ul.innerHTML = ''
datalayer.eachFeature((feature) => { datalayer.eachFeature((feature) => {
if ( if (filterValue && !feature.matchFilter(filterValue, filterKeys)) return
(filterValue && !feature.matchFilter(filterValue, filterKeys)) ||
feature.properties.isVisible === false
)
return
ul.appendChild(addFeature(feature)) ul.appendChild(addFeature(feature))
}) })
} }
@ -788,148 +784,72 @@ L.U.Map.include({
L.bind(appendAll, this)() L.bind(appendAll, this)()
L.DomEvent.on(filter, 'input', appendAll, this) L.DomEvent.on(filter, 'input', appendAll, this)
L.DomEvent.on(filter, 'input', resetLayers, this) L.DomEvent.on(filter, 'input', resetLayers, this)
const link = L.DomUtil.create('li', '')
L.DomUtil.create('i', 'umap-icon-16 umap-caption', link) this.ui.openPanel({
const label = L.DomUtil.create('span', '', link) data: { html: browserContainer },
label.textContent = label.title = L._('About') actions: [this._aboutLink()],
L.DomEvent.on(link, 'click', this.displayCaption, this) })
this.ui.openPanel({ data: { html: browserContainer }, actions: [link] })
}, },
_openFilter: function () { _openFacet: function () {
const filterContainer = L.DomUtil.create('div', 'umap-filter-data'), const container = L.DomUtil.create('div', 'umap-facet-search'),
title = L.DomUtil.add( title = L.DomUtil.add('h3', 'umap-filter-title', container, L._('Facet search')),
'h3', keys = Object.keys(this.getFacetKeys())
'umap-filter-title',
filterContainer,
this.options.name
),
propertiesContainer = L.DomUtil.create(
'div',
'umap-filter-properties',
filterContainer
),
advancedFilterKeys = this.getAdvancedFilterKeys()
const advancedFiltersFull = {} const knownValues = {}
let filtersAlreadyLoaded = true
if (!this.getMap().options.advancedFilters) { keys.forEach((key) => {
this.getMap().options.advancedFilters = {} knownValues[key] = []
filtersAlreadyLoaded = false if (!this.facets[key]) this.facets[key] = []
}
advancedFilterKeys.forEach((property) => {
advancedFiltersFull[property] = []
if (!filtersAlreadyLoaded || !this.getMap().options.advancedFilters[property]) {
this.getMap().options.advancedFilters[property] = []
}
}) })
this.eachBrowsableDataLayer((datalayer) => { this.eachBrowsableDataLayer((datalayer) => {
datalayer.eachFeature((feature) => { datalayer.eachFeature((feature) => {
advancedFilterKeys.forEach((property) => { keys.forEach((key) => {
if (feature.properties[property]) { let value = feature.properties[key]
if (!advancedFiltersFull[property].includes(feature.properties[property])) { if (typeof value !== 'undefined' && !knownValues[key].includes(value)) {
advancedFiltersFull[property].push(feature.properties[property]) knownValues[key].push(value)
}
} }
}) })
}) })
}) })
const addPropertyValue = function (property, value) {
const property_li = L.DomUtil.create('li', ''),
filter_check = L.DomUtil.create('input', '', property_li),
filter_label = L.DomUtil.create('label', '', property_li)
filter_check.type = 'checkbox'
filter_check.id = `checkbox_${property}_${value}`
filter_check.checked =
this.getMap().options.advancedFilters[property] &&
this.getMap().options.advancedFilters[property].includes(value)
filter_check.setAttribute('data-property', property)
filter_check.setAttribute('data-value', value)
filter_label.htmlFor = `checkbox_${property}_${value}`
filter_label.innerHTML = value
L.DomEvent.on(
filter_check,
'change',
function (e) {
const property = e.srcElement.dataset.property
const value = e.srcElement.dataset.value
if (e.srcElement.checked) {
this.getMap().options.advancedFilters[property].push(value)
} else {
this.getMap().options.advancedFilters[property].splice(
this.getMap().options.advancedFilters[property].indexOf(value),
1
)
}
L.bind(filterFeatures, this)()
},
this
)
return property_li
}
const addProperty = function (property) {
const container = L.DomUtil.create(
'div',
'property-container',
propertiesContainer
),
headline = L.DomUtil.add('h5', '', container, property)
const ul = L.DomUtil.create('ul', '', container)
const orderedValues = advancedFiltersFull[property]
orderedValues.sort()
orderedValues.forEach((value) => {
ul.appendChild(L.bind(addPropertyValue, this)(property, value))
})
}
const filterFeatures = function () { const filterFeatures = function () {
let noResults = true let found = false
this.eachBrowsableDataLayer((datalayer) => { this.eachBrowsableDataLayer((datalayer) => {
datalayer.eachFeature(function (feature) { datalayer.resetLayer(true)
feature.properties.isVisible = true if (datalayer.hasDataVisible()) found = true
for (const [property, values] of Object.entries(
this.map.options.advancedFilters
)) {
if (values.length > 0) {
if (
!feature.properties[property] ||
!values.includes(feature.properties[property])
) {
feature.properties.isVisible = false
}
}
}
if (feature.properties.isVisible) {
noResults = false
if (!this.isLoaded()) this.fetchData()
this.map.addLayer(feature)
this.fire('show')
} else {
this.map.removeLayer(feature)
this.fire('hide')
}
}) })
}) // TODO: display a results counter in the panel instead.
if (noResults) { if (!found)
this.help.show('advancedFiltersNoResults') this.ui.alert({ content: L._('No results for these facets'), level: 'info' })
} else {
this.help.hide()
}
} }
propertiesContainer.innerHTML = '' const fields = keys.map((current) => [
advancedFilterKeys.forEach((property) => { `facets.${current}`,
L.bind(addProperty, this)(property) {
handler: 'FacetSearch',
choices: knownValues[current],
label: this.getFacetKeys()[current],
},
])
const builder = new L.U.FormBuilder(this, fields, {
makeDirty: false,
callback: filterFeatures,
callbackContext: this,
}) })
container.appendChild(builder.build())
this.ui.openPanel({ data: { html: container }, actions: [this._aboutLink()] })
},
_aboutLink: function () {
const link = L.DomUtil.create('li', '') const link = L.DomUtil.create('li', '')
L.DomUtil.create('i', 'umap-icon-16 umap-caption', link) L.DomUtil.create('i', 'umap-icon-16 umap-caption', link)
const label = L.DomUtil.create('span', '', link) const label = L.DomUtil.create('span', '', link)
label.textContent = label.title = L._('About') label.textContent = label.title = L._('About')
L.DomEvent.on(link, 'click', this.displayCaption, this) L.DomEvent.on(link, 'click', this.displayCaption, this)
this.ui.openPanel({ data: { html: filterContainer }, actions: [link] }) return link
}, },
displayCaption: function () { displayCaption: function () {
@ -1011,11 +931,11 @@ L.U.Map.include({
labelBrowser.textContent = labelBrowser.title = L._('Browse data') labelBrowser.textContent = labelBrowser.title = L._('Browse data')
L.DomEvent.on(browser, 'click', this.openBrowser, this) L.DomEvent.on(browser, 'click', this.openBrowser, this)
const actions = [browser] const actions = [browser]
if (this.options.advancedFilterKey) { if (this.options.facetKey) {
const filter = L.DomUtil.create('li', '') const filter = L.DomUtil.create('li', '')
L.DomUtil.create('i', 'umap-icon-16 umap-add', filter) L.DomUtil.create('i', 'umap-icon-16 umap-add', filter)
const labelFilter = L.DomUtil.create('span', '', filter) const labelFilter = L.DomUtil.create('span', '', filter)
labelFilter.textContent = labelFilter.title = L._('Select data') labelFilter.textContent = labelFilter.title = L._('Facet search')
L.DomEvent.on(filter, 'click', this.openFilter, this) L.DomEvent.on(filter, 'click', this.openFilter, this)
actions.push(filter) actions.push(filter)
} }

View file

@ -568,10 +568,9 @@ L.U.Help = L.Class.extend({
), ),
slugKey: L._('The name of the property to use as feature unique identifier.'), slugKey: L._('The name of the property to use as feature unique identifier.'),
filterKey: L._('Comma separated list of properties to use when filtering features'), filterKey: L._('Comma separated list of properties to use when filtering features'),
advancedFilterKey: L._( facetKey: L._(
'Comma separated list of properties to use for checkbox filtering' 'Comma separated list of properties to use for facet search (eg.: mykey,otherkey). To control label, add it after a | (eg.: mykey|My Key,otherkey|Other Key)'
), ),
advancedFiltersNoResults: L._('No results for these filters'),
interactive: L._('If false, the polygon will act as a part of the underlying map.'), interactive: L._('If false, the polygon will act as a part of the underlying map.'),
outlink: L._('Define link to open in a new window on polygon click.'), outlink: L._('Define link to open in a new window on polygon click.'),
dynamicRemoteData: L._('Fetch data each time map view changes.'), dynamicRemoteData: L._('Fetch data each time map view changes.'),

View file

@ -87,7 +87,12 @@ L.U.FeatureMixin = {
edit: function (e) { edit: function (e) {
if (!this.map.editEnabled || this.isReadOnly()) return if (!this.map.editEnabled || this.isReadOnly()) return
const container = L.DomUtil.create('div', 'umap-feature-container') const container = L.DomUtil.create('div', 'umap-feature-container')
L.DomUtil.add('h3', `umap-feature-properties ${this.getClassName()}`, container, L._('Feature properties')) L.DomUtil.add(
'h3',
`umap-feature-properties ${this.getClassName()}`,
container,
L._('Feature properties')
)
let builder = new L.U.FormBuilder(this, ['datalayer'], { let builder = new L.U.FormBuilder(this, ['datalayer'], {
callback: function () { callback: function () {
@ -469,6 +474,17 @@ L.U.FeatureMixin = {
return false return false
}, },
matchFacets: function () {
const facets = this.map.facets
for (const [property, expected] of Object.entries(facets)) {
if (expected.length) {
let value = this.properties[property]
if (!value || !expected.includes(value)) return false
}
}
return true
},
onVertexRawClick: function (e) { onVertexRawClick: function (e) {
new L.Toolbar.Popup(e.latlng, { new L.Toolbar.Popup(e.latlng, {
className: 'leaflet-inplace-toolbar', className: 'leaflet-inplace-toolbar',

View file

@ -431,7 +431,7 @@ L.FormBuilder.OnLoadPanel = L.FormBuilder.Select.extend({
['none', L._('None')], ['none', L._('None')],
['caption', L._('Caption')], ['caption', L._('Caption')],
['databrowser', L._('Data browser')], ['databrowser', L._('Data browser')],
['datafilters', L._('Data filters')], ['facet', L._('Facet search')],
], ],
}) })
@ -684,6 +684,37 @@ L.FormBuilder.Switch = L.FormBuilder.CheckBox.extend({
}, },
}) })
L.FormBuilder.FacetSearch = L.FormBuilder.Element.extend({
build: function () {
this.container = L.DomUtil.create('div', 'umap-facet', this.parentNode)
this.ul = L.DomUtil.create('ul', '', this.container)
const choices = this.options.choices
choices.sort()
choices.forEach((value) => this.buildLi(value))
},
buildLabel: function () {
this.label = L.DomUtil.add('h5', '', this.parentNode, this.options.label);
},
buildLi: function (value) {
const property_li = L.DomUtil.create('li', '', this.ul),
input = L.DomUtil.create('input', '', property_li),
label = L.DomUtil.create('label', '', property_li)
input.type = 'checkbox'
input.id = `checkbox_${this.name}_${value}`
input.checked = this.get().includes(value)
input.dataset.value = value
label.htmlFor = `checkbox_${this.name}_${value}`
label.innerHTML = value
L.DomEvent.on(input, 'change', (e) => this.sync())
},
toJS: function () {
return [...this.ul.querySelectorAll('input:checked')].map(i => i.dataset.value)
},
})
L.FormBuilder.MultiChoice = L.FormBuilder.Element.extend({ L.FormBuilder.MultiChoice = L.FormBuilder.Element.extend({
default: 'null', default: 'null',
className: 'umap-multiplechoice', className: 'umap-multiplechoice',
@ -1125,7 +1156,7 @@ L.U.FormBuilder = L.FormBuilder.extend({
setter: function (field, value) { setter: function (field, value) {
L.FormBuilder.prototype.setter.call(this, field, value) L.FormBuilder.prototype.setter.call(this, field, value)
this.obj.isDirty = true if (this.options.makeDirty !== false) this.obj.isDirty = true
}, },
finish: function () { finish: function () {

View file

@ -151,12 +151,14 @@ L.U.Map.include({
this.options.slideshow.active === undefined this.options.slideshow.active === undefined
) )
this.options.slideshow.active = true this.options.slideshow.active = true
if (this.options.advancedFilterKey) this.options.facetKey = this.options.advancedFilterKey
// Global storage for retrieving datalayers and features // Global storage for retrieving datalayers and features
this.datalayers = {} this.datalayers = {}
this.datalayers_index = [] this.datalayers_index = []
this.dirty_datalayers = [] this.dirty_datalayers = []
this.features_index = {} this.features_index = {}
this.facets = {}
if (this.options.hash) this.addHash() if (this.options.hash) this.addHash()
this.initControls() this.initControls()
@ -250,7 +252,7 @@ L.U.Map.include({
if (L.Util.queryString('share')) this.renderShareBox() if (L.Util.queryString('share')) this.renderShareBox()
else if (this.options.onLoadPanel === 'databrowser') this.openBrowser() else if (this.options.onLoadPanel === 'databrowser') this.openBrowser()
else if (this.options.onLoadPanel === 'caption') this.displayCaption() else if (this.options.onLoadPanel === 'caption') this.displayCaption()
else if (this.options.onLoadPanel === 'datafilters') this.openFilter() else if (this.options.onLoadPanel === 'facet' || this.options.onLoadPanel === 'datafilters') this.openFacet()
}) })
this.onceDataLoaded(function () { this.onceDataLoaded(function () {
const slug = L.Util.queryString('feature') const slug = L.Util.queryString('feature')
@ -953,9 +955,9 @@ L.U.Map.include({
}) })
}, },
openFilter: function () { openFacet: function () {
this.onceDatalayersLoaded(function () { this.onceDatalayersLoaded(function () {
this._openFilter() this._openFacet()
}) })
}, },
@ -1067,7 +1069,7 @@ L.U.Map.include({
'sortKey', 'sortKey',
'labelKey', 'labelKey',
'filterKey', 'filterKey',
'advancedFilterKey', 'facetKey',
'slugKey', 'slugKey',
'showLabel', 'showLabel',
'labelDirection', 'labelDirection',
@ -1394,13 +1396,12 @@ L.U.Map.include({
}, },
], ],
[ [
'options.advancedFilterKey', 'options.facetKey',
{ {
handler: 'Input', handler: 'Input',
helpEntries: 'advancedFilterKey', helpEntries: 'facetKey',
placeholder: L._('Example: key1,key2,key3'), placeholder: L._('Example: key1,key2,key3'),
label: L._('Advanced filter keys'), label: L._('Facet keys')
inheritable: true,
}, },
], ],
[ [
@ -1785,7 +1786,7 @@ L.U.Map.include({
this.openBrowser, this.openBrowser,
this this
) )
if (this.options.advancedFilterKey) { if (this.options.facetKey) {
const filter = L.DomUtil.add( const filter = L.DomUtil.add(
'a', 'a',
'umap-open-filter-link', 'umap-open-filter-link',
@ -1796,7 +1797,7 @@ L.U.Map.include({
L.DomEvent.on(filter, 'click', L.DomEvent.stop).on( L.DomEvent.on(filter, 'click', L.DomEvent.stop).on(
filter, filter,
'click', 'click',
this.openFilter, this.openFacet,
this this
) )
} }
@ -2014,10 +2015,10 @@ L.U.Map.include({
text: L._('Browse data'), text: L._('Browse data'),
callback: this.openBrowser, callback: this.openBrowser,
}) })
if (this.options.advancedFilterKey) { if (this.options.facetKey) {
items.push({ items.push({
text: L._('Select data'), text: L._('Facet search'),
callback: this.openFilter, callback: this.openFacet,
}) })
} }
items.push( items.push(
@ -2102,8 +2103,12 @@ L.U.Map.include({
return (this.options.filterKey || this.options.sortKey || 'name').split(',') return (this.options.filterKey || this.options.sortKey || 'name').split(',')
}, },
getAdvancedFilterKeys: function () { getFacetKeys: function () {
return (this.options.advancedFilterKey || '').split(',') return (this.options.facetKey || '').split(',').reduce((acc, curr) => {
const els = curr.split("|")
acc[els[0]] = els[1] || els[0]
return acc
}, {})
}, },
getLayersBounds: function () { getLayersBounds: function () {

View file

@ -10,6 +10,10 @@ L.U.Layer = {
}, },
postUpdate: function () {}, postUpdate: function () {},
hasDataVisible: function () {
return !!Object.keys(this._layers).length
},
} }
L.U.Layer.Default = L.FeatureGroup.extend({ L.U.Layer.Default = L.FeatureGroup.extend({
@ -53,6 +57,17 @@ L.U.Layer.Cluster = L.MarkerClusterGroup.extend({
} }
L.MarkerClusterGroup.prototype.initialize.call(this, options) L.MarkerClusterGroup.prototype.initialize.call(this, options)
this._markerCluster = L.U.MarkerCluster this._markerCluster = L.U.MarkerCluster
this._layers = []
},
addLayer: function (layer) {
this._layers.push(layer)
return L.MarkerClusterGroup.prototype.addLayer.call(this, layer)
},
removeLayer: function (layer) {
this._layers.splice(this._layers.indexOf(layer), 1)
return L.MarkerClusterGroup.prototype.removeLayer.call(this, layer)
}, },
getEditableOptions: function () { getEditableOptions: function () {
@ -285,6 +300,10 @@ L.U.DataLayer = L.Evented.extend({
this.parentPane.appendChild(this.pane) this.parentPane.appendChild(this.pane)
}, },
hasDataVisible: function () {
return this.layer.hasDataVisible()
},
resetLayer: function (force) { resetLayer: function (force) {
if (this.layer && this.options.type === this.layer._type && !force) return if (this.layer && this.options.type === this.layer._type && !force) return
const visible = this.isVisible() const visible = this.isVisible()
@ -293,12 +312,7 @@ L.U.DataLayer = L.Evented.extend({
if (visible) this.map.removeLayer(this.layer) if (visible) this.map.removeLayer(this.layer)
const Class = L.U.Layer[this.options.type] || L.U.Layer.Default const Class = L.U.Layer[this.options.type] || L.U.Layer.Default
this.layer = new Class(this) this.layer = new Class(this)
const filterKeys = this.map.getFilterKeys(), this.eachLayer((feature) => this.showFeature(feature))
filter = this.map.options.filter
this.eachLayer(function (layer) {
if (filter && !layer.matchFilter(filter, filterKeys)) return
this.layer.addLayer(layer)
})
if (visible) this.map.addLayer(this.layer) if (visible) this.map.addLayer(this.layer)
this.propagateRemote() this.propagateRemote()
}, },
@ -478,15 +492,23 @@ L.U.DataLayer = L.Evented.extend({
return this.options.type === 'Cluster' return this.options.type === 'Cluster'
}, },
showFeature: function (feature) {
const filterKeys = this.map.getFilterKeys(),
filter = this.map.options.filter
if (filter && !feature.matchFilter(filter, filterKeys)) return
if (!feature.matchFacets()) return
this.layer.addLayer(feature)
},
addLayer: function (feature) { addLayer: function (feature) {
const id = L.stamp(feature) const id = L.stamp(feature)
feature.connectToDataLayer(this) feature.connectToDataLayer(this)
this._index.push(id) this._index.push(id)
this._layers[id] = feature this._layers[id] = feature
this.layer.addLayer(feature)
this.indexProperties(feature) this.indexProperties(feature)
if (this.hasDataLoaded()) this.fire('datachanged')
this.map.features_index[feature.getSlug()] = feature this.map.features_index[feature.getSlug()] = feature
this.showFeature(feature)
if (this.hasDataLoaded()) this.fire('datachanged')
}, },
removeLayer: function (feature) { removeLayer: function (feature) {

View file

@ -800,17 +800,16 @@ a.add-datalayer:hover,
/* Features browser panel */ /* Features browser panel */
/* ********************************* */ /* ********************************* */
.umap-facet-search .formbox,
.umap-browse-features > div { .umap-browse-features > div {
border: 1px solid #d3d3d3; border: 1px solid #d3d3d3;
margin-bottom: 14px; margin-bottom: 14px;
border-radius: 2px; border-radius: 2px;
} }
.umap-browse-features h5, .umap-filter-data h5 { .umap-browse-features h5, .umap-facet-search h5 {
margin-bottom: 0; margin-bottom: 0;
overflow: hidden; overflow: hidden;
padding-left: 5px; padding-left: 5px;
}
.umap-browse-features h5 {
height: 30px; height: 30px;
line-height: 30px; line-height: 30px;
background-color: #eeeee0; background-color: #eeeee0;
@ -819,14 +818,13 @@ a.add-datalayer:hover,
.umap-browse-features h5 span { .umap-browse-features h5 span {
margin-left: 10px; margin-left: 10px;
} }
.umap-browse-features li { .umap-browse-features li, .umap-facet-search li {
padding: 2px 0; padding: 2px 0;
}
.umap-browse-features li, .umap-filter-data li {
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
} }
.umap-facet-search li:nth-child(even),
.umap-browse-features li:nth-child(even) { .umap-browse-features li:nth-child(even) {
background-color: #f8f8f3; background-color: #f8f8f3;
} }
@ -855,9 +853,6 @@ a.add-datalayer:hover,
.umap-browse-features .polygon .feature-color { .umap-browse-features .polygon .feature-color {
background-position: -32px -16px; background-position: -32px -16px;
} }
.umap-filter-data .property-container:not(:first-child) {
margin-top: 14px;
}
.show-on-edit { .show-on-edit {
display: none!important; display: none!important;
} }

View file

@ -403,22 +403,22 @@ describe('L.U.DataLayer', function () {
assert.ok(qs('path[fill="DarkGoldenRod"]')) assert.ok(qs('path[fill="DarkGoldenRod"]'))
}) })
}) })
describe('#advanced-filters()', function () { describe('#facet-search()', function () {
before(function () { before(function () {
this.server.respondWith( this.server.respondWith(
/\/datalayer\/63\/\?.*/, /\/datalayer\/63\/\?.*/,
JSON.stringify(RESPONSES.datalayer63_GET) JSON.stringify(RESPONSES.datalayer63_GET)
) )
this.map.options.advancedFilterKey = 'name' this.map.options.facetKey = 'name'
this.map.createDataLayer(RESPONSES.datalayer63_GET._umap_options) this.map.createDataLayer(RESPONSES.datalayer63_GET._umap_options)
this.server.respond() this.server.respond()
}) })
it('should show non browsable layer', function () { it('should not impact non browsable layer', function () {
assert.ok(qs('path[fill="SteelBlue"]')) assert.ok(qs('path[fill="SteelBlue"]'))
}) })
it('should allow advanced filter', function () { it('should allow advanced filter', function () {
this.map.openFilter() this.map.openFacet()
assert.ok(qs('div.umap-filter-properties')) assert.ok(qs('div.umap-facet-search'))
// This one if from the normal datalayer // This one if from the normal datalayer
// it's name is "test", so it should be hidden // it's name is "test", so it should be hidden
// by the filter // by the filter
@ -430,8 +430,13 @@ describe('L.U.DataLayer', function () {
assert.ok(qs('path[fill="SteelBlue"]')) assert.ok(qs('path[fill="SteelBlue"]'))
happen.click(qs('input[data-value="name poly"]')) // Undo happen.click(qs('input[data-value="name poly"]')) // Undo
}) })
it('should allow to control facet label', function () {
this.map.options.facetKey = 'name|Nom'
this.map.openFacet()
assert.ok(qs('div.umap-facet-search h5'))
assert.equal(qs('div.umap-facet-search h5').textContent, 'Nom')
})
}) })
describe('#zoomEnd', function () { describe('#zoomEnd', function () {
it('should honour the fromZoom option', function () { it('should honour the fromZoom option', function () {
this.map.setZoom(6, {animate: false}) this.map.setZoom(6, {animate: false})
@ -453,5 +458,4 @@ describe('L.U.DataLayer', function () {
assert.ok(qs('path[fill="none"]')) assert.ok(qs('path[fill="none"]'))
}) })
}) })
}) })