Closes https://github.com/elastic/kibana/issues/162978
## Summary
This PR removes the `labs:dashboard:dashboardControls` advanced UI
setting. The removal of this setting is **not** a breaking change, as it
hasn't functioned properly for quite some time; it is completely safe to
remove this setting **regardless** of the previous value. Telemetry
tracking for this setting is also no longer required.
### Checklist
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
### For maintainers
- [ ] This was checked for breaking API changes and was [labeled
appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)
---------
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Closes https://github.com/elastic/kibana/issues/140135
Closes https://github.com/elastic/kibana/issues/159724
## Summary
### Changes to range slider
This PR migrates the range slider control to use the `EuiDualRange`
component, which both (a) removes a bunch of range slider code , thus
improving DX, and (b) improves the usability of the control by
simplifying the different states and making the slide animation much
smoother:
| Before | After |
|--------|--------|
| 
| 
|
Note that, due to the fact that migrating to the `EuiDualRange`
component means we no longer control the opening/closing of the popover,
this makes the "selections that extend beyond the current min/max of the
data" scenario slightly more difficult to handle. Specifically, we need
to decide the best time to recalculate the min/max of the slider as the
user's selections change.
The benefit of having control over the popover opening and closing was
that we could calculate the range slider's min/max values **when the
popover opened** and lock them in place so that they stayed static as
the user dragged:
<div align="center"><img width="500px"
src="1bf552d4-e70e-41ce-bf85-ab9ed0262398"
/></div><br/>
However, because the `EuiDualRange` handles the behaviour of the popover
and we therefore do not know when it opens/closes, it is not currently
possible to fully replicate this old behaviour. We have two main
options:
1. Recalculate the min/max of the range slider the user drags, which
gives a "sliding" effect:
<div align="center"><img width="500px"
src="4bb32b22-990e-40e9-a7df-78afca16bd27"
/></div>
2. Debounce this min/max calculation - with this, we avoid the "sliding"
effect shown above, and the min/max values **only get adjusted** when
the pin has been static for a long enough period of time (specifically,
`750ms`) or when the pin is dropped (i.e. the user lets go of the
mouse):
<div align="center"><img width="500px"
src="ac8943e6-4bcc-42dd-8cbb-1cdb627ba9f2"
/></div>
Ultimately, I went with option 2 in this PR. As a future enhancement, we
could replicate the old behaviour by adding some sort of
`onPopoverClosed` or `onPopoverOpened` logic to the `EuiDualRange`
component - however, this would need discussion with the EUI team to
ensure that this is the best way to handle it and not too specific to
our use case. Since EUI changes take awhile to propagate to KIbana, it's
not worth holding up this PR even further.
### Consistency with other control types
To keep things consistent, this PR also switches both the options list
and time slider controls to use the `EuiInputPopover` component and
removes the redundant control titles from the popover header:
| Before | After |
|--------|--------|
|

|

|
|

|

|
|

|

|
### Checklist
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [x] Any UI touched in this PR is usable by keyboard only (learn more
about [keyboard accessibility](https://webaim.org/techniques/keyboard/))
- [x] Any UI touched in this PR does not create any new axe failures
(run axe in browser:
[FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/),
[Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US))
- Technically, it does - but this will be resolved by the
@elastic/eui-team team adding an `aria-label` to the thumb `div`s
(https://github.com/elastic/eui/issues/7035). This is purely for the
sake of axe and/or automated `a11y` testing - it does not actually
impact `a11y` of the range slider, because the components missing an
`aria-label` are never accessible via the keyboard.
- [x] This renders correctly on smaller devices using a responsive
layout. (You can test this [in your
browser](https://www.browserstack.com/guide/responsive-testing-on-local-server))
- [x] This was checked for [cross-browser
compatibility](https://www.elastic.co/support/matrix#matrix_browsers)
### For maintainers
- [ ] This was checked for breaking API changes and was [labeled
appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)
---------
Co-authored-by: Jatin Kathuria <jatin.kathuria@elastic.co>
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
Closes https://github.com/elastic/kibana/issues/162777
flaky test runner
https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/2811
Failure image shows options list is still open. PR updates
optionsListEnsurePopoverIsClosed with logic to prevent 2 possible issues
1) retry added to ensure missed click will attempt to close options list
again
2) check options list is open before clicking. This will resolve issue
where options list is closed and clicking actually opens it again

---------
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
Closes https://github.com/elastic/kibana/issues/162697
## Summary
This PR adds a few tiny UI improvements to the control creation
elements, including...
**Data view picker:**
- Made the `Data view` form title respond to focus as expected
| <div align="center">Before</div> | <div align="center">After</div> |
|--------|--------|
| 
| 
|
- Switched to use `EuiInputPopover` rather than `EuiPopover`
- Removed the redundant popover title
| <div align="center">Before</div> | <div align="center">After</div> |
|--------|--------|
|

| 
|
**Field picker:**
- Made the `Field` form row title respond to focus as expected for all
of the inner form elements
| <div align="center">Before</div> | <div align="center">After</div> |
|--------|--------|
| 
| 
|
- Switched the `FieldTypeFilter` to use `EuiInputPopover` rather than
`EuiPopover`
- Removed the redundant title from the `FieldTypeFilter` popover
| <div align="center">Before</div> | <div align="center">After</div> |
|--------|--------|
|

|

|
- Made changes described in
https://github.com/elastic/eui/issues/6627#issuecomment-1452693611 so
that, when the field type filter is closed (either via `Esc` or through
the natural tab order), the focus returns to the search field
| <div align="center">Before</div> | <div align="center">After</div> |
|--------|--------|
| 
| 
|
- If provided, the initial selected field is now brought to the top of
the list
| <div align="center">Before</div> | <div align="center">After</div> |
|--------|--------|
|

|

|
**Controls display settings:**
- Surrounded the `Minimum width` row with a `div` so that it can receive
the `id` passed down from the `EuiFormRow` and respond to focus as
expected
| <div align="center">Before</div> | <div align="center">After</div> |
|--------|--------|
| 
| 
|
### Checklist
- [x] Any UI touched in this PR is usable by keyboard only (learn more
about [keyboard accessibility](https://webaim.org/techniques/keyboard/))
- [x] Any UI touched in this PR does not create any new axe failures
(run axe in browser:
[FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/),
[Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US))
- [x] This renders correctly on smaller devices using a responsive
layout. (You can test this [in your
browser](https://www.browserstack.com/guide/responsive-testing-on-local-server))
- [x] This was checked for [cross-browser
compatibility](https://www.elastic.co/support/matrix#matrix_browsers)
### For maintainers
- [ ] This was checked for breaking API changes and was [labeled
appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)
---------
Co-authored-by: Jatin Kathuria <jatin.kathuria@elastic.co>
Closes https://github.com/elastic/kibana/issues/157157
Closes https://github.com/elastic/kibana/issues/152921
## Summary
The primary goal of this PR is to introduce an option for wildcard
("contains") searching to the options list control:
6847d0c5-014a-4322-8e59-308bc3ca27aa
### New Flyout Design
However, since this required adding a radio group to the custom options
list settings in the create/edit control flyout, I also made some
changes to the flyout design in order to better accommodate this (we
were previously using `EuiSwitch` components for the
control-type-specific settings, which did not work in this case because
I wanted to be able to add a tooltip to describe each search type):
| Before | After |
|--------|-------|
| 
| 
|
Note in the above GIFs that, since I was using an `EuiRadioGroup` for
the search technique setting, I decided it made more sense + was more
consistent for the "Allow multiple selections in dropdown" to also be
converted to a radio group rather than a switch. The "Ignore timeout for
results" setting is the only one that remained a switch in the new
design:
| Before | After |
|--------|-------|
|

|

|
### `EuiSwitch` with Tooltip Bug
As part of this redesign, I also fixed a very quick bug where, because
the old `SwitchWithTooltip` was defined **inside** the larger
`OptionsListEditorOptions` component, any state update on
`OptionsListEditorOptions` would cause `SwitchWithTooltip` to **also**
be re-rendered - this caused the "slide" animation to be interrupted on
click:
| Before | After |
|--------|-------|
|

|

|
### Title Bug Fix
And, since I was making so many changes to the flyout code as part of
refactoring the code (including the design changes above), I also fixed
a bug with control titles where things weren't getting set properly. To
test this, consider taking the following steps:
1. Create a new options list control, keeping the default title
2. Edit that options list control and change it to a range slider
control by selecting a number field
3. Notice that...
- Before this PR, the title gets completely cleared:
<br>
- After this PR, the default title gets updated to the range slider
field name:
<br>
4. Delete that range slider control and create a new control, keeping
the default title once again (options list or range slider, the type
doesn't matter).
5. Edit the control and change the field to a different field **of the
same type** (i.e. if your control from step 4 was an options list
control, select a field that keeps it as an options list control).
Before saving your changes, notice that the "default title" in the
`Label` input gets updated to the new field title:<br>
<img width="450"
src="0fabe2e3-7f83-4f2a-87e6-33253652972d"/><br>
6. After saving, notice that...
- Before this PR, the title doesn't actually get updated to the new
default title:
<br>
- After this PR, the title gets updated as expected:
<br>
### Flaky Test Runner
[test/functional/apps/dashboard_elements/controls/options_list/options_list_suggestions.ts](https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/2343):
<img
src="f2ed9d65-adcf-47af-bb00-ee11837c406b"/>
### Checklist
- [x] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [x] Any UI touched in this PR is usable by keyboard only (learn more
about [keyboard accessibility](https://webaim.org/techniques/keyboard/))
- [x] Any UI touched in this PR does not create any new axe failures
(run axe in browser:
[FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/),
[Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US))
- [x] This renders correctly on smaller devices using a responsive
layout. (You can test this [in your
browser](https://www.browserstack.com/guide/responsive-testing-on-local-server))
- [x] This was checked for [cross-browser
compatibility](https://www.elastic.co/support/matrix#matrix_browsers)
### For maintainers
- [ ] This was checked for breaking API changes and was [labeled
appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)
---------
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Closes https://github.com/elastic/kibana/issues/140175
Closes https://github.com/elastic/kibana/issues/143580
## Summary
Oh, boy! Get ready for a doozy of a PR, folks! Let's talk about the
three major things that were accomplished here:
### 1) Pagination
Originally, this PR was meant to add traditional pagination to the
options list control. However, after implementing a version of this, it
became apparent that, not only was UI becoming uncomfortably messy, it
also had some UX concerns because we were deviating from the usual
pagination pattern by showing the cardinality rather than the number of
pages:
<p align="center"><img
src="https://user-images.githubusercontent.com/8698078/214687041-f8950d3a-2b29-41d5-b656-c79d9575d744.gif"/></p>
So, instead of traditional pagination, we decided to take a different
approach (which was made possible by
https://github.com/elastic/kibana/pull/148420) - **load more options
when the user scrolls to the bottom!** Here it is in action:
<p align="center"><img
src="https://user-images.githubusercontent.com/8698078/214688854-06c7e8a9-7b8c-4dc0-9846-00ccf5e5f771.gif"/></p>
It is important that the first query remains **fast** - that is why we
still only request the top 10 options when the control first loads. So,
having a "load more" is the best approach that allows users to see more
suggestions while also ensuring that the performance of options lists
(especially with respect to chaining) is not impacted.
Note that it is **not possible** to grab every single value of a field -
the limit is `10,000`. However, since it is impractical that a user
would want to scroll through `10,000` suggestions (and potentially very
slow to fetch), we have instead made the limit of this "show more"
functionality `1,000`. To make this clear, if the field has more than
`1,000` values and the user scrolls all the way to the bottom, they will
get the following message:
<p align="center"><img
src="https://user-images.githubusercontent.com/8698078/214920302-1e3574dc-f2b6-4845-be69-f9ba04177e7f.png"/></p>
### 2) Cardinality
Previously, the cardinality of the options list control was **only**
shown as part of the control placeholder text - this meant that, once
the user entered their search term, they could no longer see the
cardinality of the returned options. This PR changes this functionality
by placing the cardinality in a badge **beside** the search bar - this
value now changes as the user types, so they can very clearly see how
many options match their search:
<p align="center"><img
src="https://user-images.githubusercontent.com/8698078/214689739-9670719c-5878-4e8b-806c-0b5a6f6f907f.gif"/></p>
> **Note**
> After some initial feedback, we have removed both the cardinality and
invalid selections badges in favour of displaying the cardinality below
the search bar, like so:
>
> <p align="center"><img
src="https://user-images.githubusercontent.com/8698078/216473930-e99366a3-86df-4777-a3d8-cf2d41e550fb.gif"/></p>
>
> So, please be aware that the screenshots above are outdated.
### 3) Changes to Queries
This is where things get.... messy! Essentially, our previous queries
were all built with the expectation that the Elasticsearch setting
`search.allow_expensive_queries` was **off** - this meant that they
worked regardless of the value of this setting. However, when trying to
get the cardinality to update based on a search term, it became apparent
that this was not possible if we kept the same assumptions -
specifically, if `search.allow_expensive_queries` is off, there is
absolutely no way for the cardinality of **keyword only fields** to
respond to a search term.
After a whole lot of discussion, we decided that the updating
cardinality was a feature important enough to justify having **two
separate versions** of the queries:
1. **Queries for when `search.allow_expensive_queries` is off**:
These are essentially the same as our old queries - however, since we
can safely assume that this setting is **usually** on (it defaults on,
and there is no UI to easily change it), we opted to simplify them a
bit.
First of all, we used to create a special object for tracking the
parent/child relationship of fields that are mapped as keyword+text -
this was so that, if a user created a control on these fields, we could
support case-insensitive search. We no longer do this - if
`search.allow_expensive_queries` is off and you create a control on a
text+keyword field, the search will be case sensitive. This helps clean
up our code quite a bit.
Second, we are no longer returning **any** cardinality. Since the
cardinality is now displayed as a badge beside the search bar, users
would expect that this value would change as they type - however, since
it's impossible to make this happen for keyword-only fields and to keep
behaviour consistent, we have opted to simply remove this badge when
`search.allow_expensive_queries` is off **regardless** of the field
type. So, there is no longer a need to include the `cardinality` query
when grabbing the suggestions.
Finally, we do not support "load more" when
`search.allow_expensive_queries` is off. While this would theoretically
be possible, because we are no longer grabbing the cardinality, we would
have to always fetch `1,000` results when the user loads more, even if
the true cardinality is much smaller. Again, we are pretty confident
that **more often than not**, the `search.allow_expensive_queries` is
on; therefore, we are choosing to favour developer experience in this
instance because the impact should be quite small.
2. **Queries for when `search.allow_expensive_queries` is on**:
When this setting is on, we now have access to the prefix query, which
greatly simplifies how our queries are handled - now, rather than having
separate queries for keyword-only, keyword+text, and nested fields,
these have all been combined into a single query! And even better -
⭐ now **all** string-based fields support case-insensitive search!
⭐ Yup, that's right - even keyword-only fields 💃
There has been [discussion on the Elasticsearch side
](https://github.com/elastic/elasticsearch/issues/90898) about whether
or not this setting is even **practical**, and so it is possible that,
in the near future, this distinction will no longer be necessary. With
this in mind, I have made these two versions of our queries **completely
separate** from each other - while this introduces some code
duplication, it makes the cleanup that may follow much, much easier.
Well, that was sure fun, hey?
<p align="center"><img
src="https://user-images.githubusercontent.com/8698078/214921985-49058ff0-42f2-4b01-8ae3-0a4d259d1075.gif"/></p>
## How to Test
I've created a quick little Python program to ingest some good testing
data for this PR:
```python
import random
import time
import pandas as pd
from faker import Faker
from elasticsearch import Elasticsearch
SIZE = 10000
ELASTIC_PASSWORD = "changeme"
INDEX_NAME = 'test_large_index'
Faker.seed(time.time())
faker = Faker()
hundredRandomSentences = [faker.sentence(random.randint(5, 35)) for _ in range(100)]
thousandRandomIps = [faker.ipv4() if random.randint(0, 99) < 50 else faker.ipv6() for _ in range(1000)]
client = Elasticsearch(
"http://localhost:9200",
basic_auth=("elastic", ELASTIC_PASSWORD),
)
if(client.indices.exists(index=INDEX_NAME)):
client.indices.delete(index=INDEX_NAME)
client.indices.create(index=INDEX_NAME, mappings={"properties":{"keyword_field":{"type":"keyword"},"id":{"type":"long"},"ip_field":{"type":"ip"},"boolean_field":{"type":"boolean"},"keyword_text_field":{"type":"text","fields":{"keyword":{"type":"keyword"}}},"nested_field":{"type":"nested","properties":{"first":{"type":"text","fields":{"keyword":{"type":"keyword"}}},"last":{"type":"text","fields":{"keyword":{"type":"keyword"}}}}},"long_keyword_text_field":{"type":"text","fields":{"keyword":{"type":"keyword"}}}}})
print('Generating data', end='')
for i in range(SIZE):
name1 = faker.name();
[first_name1, last_name1] = name1.split(' ', 1)
name2 = faker.name();
[first_name2, last_name2] = name2.split(' ', 1)
response = client.create(index=INDEX_NAME, id=i, document={
'keyword_field': faker.country(),
'id': i,
'boolean_field': faker.boolean(),
'ip_field': thousandRandomIps[random.randint(0, 999)],
'keyword_text_field': faker.name(),
'nested_field': [
{ 'first': first_name1, 'last': last_name1},
{ 'first': first_name2, 'last': last_name2}
],
'long_keyword_text_field': hundredRandomSentences[random.randint(0, 99)]
})
print('.', end='')
print(' Done!')
```
However, if you don't have Python up and running, here's a CSV with a
smaller version of this data:
[testNewQueriesData.csv](10538537/testNewQueriesData.csv)
> **Warning**
> When uploading, make sure to update the mappings of the CSV data to
the mappings included as part of the Python script above (which you can
find as part of the `client.indices.create` call). You'll notice,
however, that **none of the CSV documents have a nested field**.
Unfortunately, there doesn't seem to be a way to able to ingest nested
data through uploading a CSV, so the above data does not include one -
in order to test the nested data type, you'd have to add some of your
own documents
>
> Here's a sample nested field document, for your convenience:
> ```json
> {
> "keyword_field": "Russian Federation",
> "id": 0,
> "boolean_field": true,
> "ip_field": "121.149.70.251",
> "keyword_text_field": "Michael Foster",
> "nested_field": [
> {
> "first": "Rachel",
> "last": "Wright"
> },
> {
> "first": "Gary",
> "last": "Reyes"
> }
> ],
> "long_keyword_text_field": "Color hotel indicate appear since well
sure right yet individual easy often test enough left a usually
attention."
> }
> ```
>
### Testing Notes
Because there are now two versions of the queries, thorough testing
should be done for both when `search.allow_expensive_queries` is `true`
and when it is `false` for every single field type that is currently
supported. Use the following call to the cluster settings API to toggle
this value back and forth:
```php
PUT _cluster/settings
{
"transient": {
"search.allow_expensive_queries": <value> // true or false
}
}
```
You should pay super special attention to the behaviour that happens
when toggling this value from `true` to `false` - for example, consider
the following:
1. Ensure `search.allow_expensive_queries` is either `true` or
`undefined`
2. Create and save a dashboard with at least one options list control
3. Navigate to the console and set `search.allow_expensive_queries` to
`false` - **DO NOT REFRESH**
4. Go back to the dashboard
5. Open up the options list control you created in step 2
6. Fetch a new, uncached request, either by scrolling to the bottom and
fetching more (assuming these values aren't already in the cache) or by
performing a search with a string you haven't tried before
7. ⚠️ **The options list control _should_ have a fatal error** ⚠️<br>The
Elasticsearch server knows that `search.allow_expensive_queries` is now
`false` but, because we only fetch this value on the first load on the
client side, it has not yet been updated - this means the options list
service still tries to fetch the suggestions using the expensive version
of the queries despite the fact that Elasticsearch will now reject this
request. The most graceful way to handle this is to simply throw a fatal
error.
8. Refreshing the browser will make things sync up again and you should
now get the expected results when opening the options list control.
### Flaky Test Runner
<a
href="https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/1845"><img
src="https://user-images.githubusercontent.com/8698078/215894267-97f07e59-6660-4117-bda7-18f63cb19af6.png"/></a>
### Checklist
- [x] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [x] Any UI touched in this PR is usable by keyboard only (learn more
about [keyboard accessibility](https://webaim.org/techniques/keyboard/))
- [x] Any UI touched in this PR does not create any new axe failures
(run axe in browser:
[FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/),
[Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US))
> **Note**
> Technically, it actually does - however, it is due to an [EUI
bug](https://github.com/elastic/eui/issues/6565) from adding the group
label to the bottom of the list.
- [x] This renders correctly on smaller devices using a responsive
layout. (You can test this [in your
browser](https://www.browserstack.com/guide/responsive-testing-on-local-server))
- [x] This was checked for [cross-browser
compatibility](https://www.elastic.co/support/matrix#matrix_browsers)
### For maintainers
- [ ] This was checked for breaking API changes and was [labeled
appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)
---------
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Closes https://github.com/elastic/kibana/issues/147293
## Summary
Before this change, the Redux state `explicitInput` was getting out of
sync with the embeddable `explicitInput` in scenarios where the new
`explicitInput` was missing a key that the old `explicitInput` had -
therefore, because they were out of sync, the changes that **should**
have been discarded kept getting injected back into the embeddable
`explicitInput`, which made it impossible to actually discard anything
unless the key existed in both the before and after state.
This PR fixes this by replacing the entire Redux state `explicitInput`
with the embeddable `explicitInput` rather than spreading the new value.
It also fixes a bug with the time slider control where changes to the
embeddable's input were not reflected properly in the control's state,
so nothing could be discarded even after the initial bug was fixed.
#### Further Explanation
When a control is first created, all the optional properties of the
explicit input do not yet exist - for example, when creating an options
list control, the `selections` key does not exist in the `explicitInput`
until a selection is made. Therefore, imagine the following scenario:
1. You create an options list control (where the `selections` key does
not exist) and save the dashboard
2. You make some selections, which causes `unsaved changes` because the
`selections` key now exists and is equal to an array
3. You switch to view mode and choose to discard your changes, thus
(supposedly) removing the `selections` key from the `explicitInput`
object once again
Unfortunately, the Redux embeddable state for each control was **not**
accurately removing the `selections` key as expected - this was because,
when trying to update the `explicitInput` via the old
`updateEmbeddableReduxInput`, the new value was **spread** on top of the
older value rather than replacing it. In a simplified scenario, this
resulted in something like this:
```typescript
const oldExplicitInput = { id: 'test_id', selections: ['test selection'] };
const newExplicitInput = { id: 'test_id' }
const result = { ...oldExplicitInput, ...newExplicitInput };
```
In this code, because `newExplicitInput` does not have the `selections`
key, `result` will equal `{ id: 'test_id', selections: ['test
selection'] }` - this is not the behaviour we want! Instead, we wanted
to replace the entire old `explicitInput` with the new `explicitInput`.
Effectively, that is what this PR does.
Thanks to @ThomThomson for helping out with finding the root cause of
this after I got lost :)
### How to Test
For both options list and range slider controls,
1. Create a control of the desired type
2. Save the dashboard
3. Make some sort of change that causes unsaved changes - for example,
make a selection or, if an options list control, set `exclude` to `true`
4. Switch to view mode, discarding the changes
5. Ensure that the changes made in step 3 are no longer applied ✅
6. Switch back to edit mode
7. Ensure that there are no `unsaved changes` ✅
#### Flaky Test Runner
<a
href="https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/1649"><img
src="https://user-images.githubusercontent.com/8698078/207701101-69cdfada-77c6-4510-b254-1fd1fa13af5c.png"/></a>
### Checklist
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [x] This was checked for [cross-browser
compatibility](https://www.elastic.co/support/matrix#matrix_browsers)
### For maintainers
- [ ] This was checked for breaking API changes and was [labeled
appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)
Closes https://github.com/elastic/kibana/issues/147140
## Summary
After discussion surrounding the "author" versus "analyst" experience
for the controls, we have come to the conclusion that a simpler create
experience is necessary to streamline our current process. This means
removing **all** options that are not absolutely necessary -
specifically, this PR removes the UX for the following "Additional
settings" toggles from the creating/editing flyout:
- Allow selections to be excluded (`hideExclude`)
- Allow exists query (`hideExists`)
- Allow dynamic sorting of suggestions (`hideSort`)
This will leave only two additional settings:
- Allow multiple selections in dropdown
- Ignore timeout for results
While the UX for the removed toggles will be removed, the logic is kept
- this means that, for the Control Group API that solutions will be
using, this functionality could still be exposed to the consumers.
### Migration
Consider a scenario where, in 8.6, a user creates an options list
control with `hideExists` set to `true`- that is, the `Exists` option is
not displayed in the options list suggestions. After upgrading to 8.7,
they realize that they want to turn the `Exists` query back on - but
they can't, because the toggle has been removed so `hideExists` is stuck
as `true`! Their only choice is to remove the control and start over.
In order to avoid this scenario, I added an 8.7 migration that goes
through all existing control group panels and, if a control is an
options list, it removes the `hideExclude` and `hideExists` keys from
the explicit input. This is effectively the same was setting these to
`false` since they are optional.
> **Note**
> Because the `hideExclude` and `hideExists` toggles were added back in
8.6.0 but `hideSort` was only added in 8.7.0 (which has not yet reached
feature freeze), that is why only the `hideExclude` and `hideExists`
keys are removed. There is no need to add a migration for `hideSort`
because there will be no customer impact from removing the "Allow
dynamic sorting of suggestions" toggle.
## How to test
For the sake of convenience, I have exported various dashboard saved
objects from 8.6 (all of which used the demo "Kibana Sample Data
eCommerce" data view) in order to test the migration to 8.7:
- `8.6_OneControlNoMigrations.ndjson`
This dashboard had a single options list control that should require
**no** migration because it was created with the default settings:

- `8.6_OneControlTogglesOff.ndjson`
This dashboard had a single options list control with **every single**
toggle turned off:

- `8.6_TwoOptionsListControls.ndjson`
This dashboard had two options list controls with the following
settings:

The first control also had the `exists` query selected with `exclude`
set to `true`, while the second control had non-default size settings,
and I made some selections and saved them as part of the dashboard.
- `8.6_MultipleControlTypes.ndjson`
This dashboard had four controls: two options list controls with the
following settings, a range slider control, and a time slider control:

These can each be found in the following ZIP file:
> **Warning**
>
[DashboardSavedObjectsForTestingMigration.zip](10180945/DashboardSavedObjectsForTestingMigration.zip)
In order to test this PR, you should (at minimum) download the previous
ZIP, individually import each dashboard into Kibana 8.7, and ensure
that:
1. the resulting JSON looks as expected, with no `hideExists` or
`hideExclude` keys present, and
2. the dashboard loads correctly and each options list control has the
ability to include/exclude, sort, and create an exists query regardless
of the previous state of `hideExists` or `hideExclude`
### Checklist
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [x] This was checked for [cross-browser
compatibility](https://www.elastic.co/support/matrix#matrix_browsers)
### For maintainers
- [ ] This was checked for breaking API changes and was [labeled
appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
https://github.com/elastic/kibana/pull/140739 caused a regression with
timeslider control where `ControlGroupContainer` was not not firing
`updateOutput` on `output.timeslice` changes.
This PR resolves the regression and adds functional tests to ensure
interactions with timeslider properly flow to dashboard saved search
panel
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
* Mock first attempt at UI
* Add `exists` filter functionality
* Make `exists` selection change button
* Overwrite selections instead of disabling them
* Add support for negate to exists
* Add to diffing system
* Add toggle to disable `exists` query
* Clear `exists` selection when toggle is disabled + fix mocks
* Switch to tooltip instead of docs link
* Clean up popover logic
* Fix rendering through memoization
* Auto focus on search when popover opens
* Added Jest unit tests
* Beef up mock and add more Jest unit tests
* Add functional tests
* Split up popover in to smaller components
* Fix unit tests + functional test flakiness
* Fix flakiness a second time + add chaining tests
* Clean up code
* Add `exists` selection to validation
* Fix invalid bug
* Fix failing unit test
* More code clean up
* Add another functional test
* Apply styling changes
* Fix tests
* Fix a11y issues
* Remove validation
* Fix types
* Clean up `a11y` fix
* Fix jest test
* Address feedback
* Fix wording of tooltip
* Add buttons with no functionality
* Added basic negate functionality
* Add `NOT` text when negated
* Clean up
* Add jest and functional tests
* Fix merge conflicts
* Rename `negate` to `exclude`
* Fix `unsaved changes` bug
* Move erase button back to beside search
* Clean up
* Add chaining functional tests
* Fix other unsaved changes bug
* Fix mobile view of popover
* Add option to disable exclude/include toggle
* Prevent unsaved changes bug for options list settings
* Add tooltip to run past timeout setting
* Address review comments
* Rename variable
* Set `exclude` to `false` when footer is hidden
* Timeslider plugin boilerplate
* wire redux store
* add time range bounds to store
* range control
* set value in store
* previous next buttons
* Spacetime timeslider dash (#138553)
* wire redux store
* add time range bounds to store
* range control
* set value in store
* previous next buttons
* push timeslice to Dashboard input
* Wire up timeslice (#138570)
* wire redux store
* add time range bounds to store
* range control
* set value in store
* previous next buttons
* push timeslice to Dashboard input
* cleanup
* Spacetime timeslider dash (#138573)
* wire redux store
* add time range bounds to store
* range control
* set value in store
* previous next buttons
* push timeslice to Dashboard input
* cleanup
* play button
* Notify control group when all panels loaded (#138674)
* get play button working
* play button (#138681)
* wire redux store
* add time range bounds to store
* range control
* set value in store
* previous next buttons
* push timeslice to Dashboard input
* cleanup
* play button
* get play button working
* cleanup
* clean up
* clean up wrap logic
* Spacetime timeslider dash -clean up wrap logic (#138822)
* wire redux store
* add time range bounds to store
* range control
* set value in store
* previous next buttons
* push timeslice to Dashboard input
* cleanup
* play button
* get play button working
* cleanup
* clean up wrap logic
* Add waitForPanelsToLoad$ observable (#138950)
* fix import
* [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix'
* sync range slider width on popover panel resize
* fix styling
* update embeddables to support time slice
* [CI] Auto-commit changed files from 'node scripts/precommit_hook.js --ref HEAD~1..HEAD --fix'
* change file structure
* [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix'
* disable add button when control group contains timeslider
* hide edit button for timeslider control
* [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix'
* force timeslider width to be large
* [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix'
* use timeslice to filter other controls
* [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix'
* skip timeslider control in control chaining
* [CI] Auto-commit changed files from 'node scripts/precommit_hook.js --ref HEAD~1..HEAD --fix'
* cleanup
* fix initial timeRange
* [CI] Auto-commit changed files from 'node scripts/precommit_hook.js --ref HEAD~1..HEAD --fix'
* clear button
* [CI] Auto-commit changed files from 'node scripts/precommit_hook.js --ref HEAD~1..HEAD --fix'
* render prepend
* [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix'
* remove unused code
* Container.getAnyChildOutputChange$
* play observable
* tslint
* tslint
* clean up
* [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix'
* fix lint
* fix import
* fix plugin size and other clean up
* fix 'unsaved changes' issue
* [CI] Auto-commit changed files from 'node scripts/precommit_hook.js --ref HEAD~1..HEAD --fix'
* fix Dragging UI Nit
* fix Label position Above Nit
* [CI] Auto-commit changed files from 'node scripts/precommit_hook.js --ref HEAD~1..HEAD --fix'
* support relative time ranges
* fix issue where clear not propogated to panels
* time slider functional test
* clean up functional test
* [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix'
* tslint
* fix time_slider functional test
* fix time slider not expanding after creating non-expanding control
Co-authored-by: Nick Peihl <nick.peihl@elastic.co>
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Greg Thompson <thompson.glowe@gmail.com>
Co-authored-by: Joe Reuter <johannes.reuter@elastic.co>
* Field first *creation*
* Field first *editing*
* Add support for custom control options
* Add i18n
* Make field picker accept predicate again + clean up imports
* Fix functional tests
* Attempt 1 at case sensitivity
* Works both ways
* [CI] Auto-commit changed files from 'node scripts/precommit_hook.js --ref HEAD~1..HEAD --fix'
* Clean up code
* Use React useMemo to calculate field registry
* Fix functional tests
* Fix default state + control settings label
* Fix functional tests
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
* Decouple control auto sizing and control width
* Add grow control setting to control group settings flyout
* Set min-width style for each control size
* Remove width settings from control group settings
* Add media query to remove min-widths in small viewport
* Fix grow state management
* Fix i18n errors
* Fix control group settings functional tests
* Updated control width label and move tooltip icon
* Use euiBreakpoints for media queries
* Removed tooltip
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
* Replace is half working
* Child embeddable listens for change in type
* Fix control order on replace
* Fix factory & remove duplicated onPanelAdded/Removed
* Comments + clean up code
* Set invalid editor state on type change
* Add functional tests and clean test code
* Comment out time slider tests
* Remove getReplacementPanelState
* Fix promise syntax
* Fix mutation
* Fix flaky test
* Fix conflicts
* Move control type selection in to flyout
* Set default icon type if getIconType undefined
* Fix create control functional tests
* Fix factories for multiple types
* Show only selected type icon when editing
* Add optional tooltip support
* Rename promise variable
* Fix imports
* Fix nits
* Edit tooltip text for options list control
* Add controls button to toolbar
* Add dismiss button
* Add style to toolbar controls button
* Clean up unnecessary isControlsEnabled check
* Make toolbar controls button conditional once callout dismissed
* Move add and edit controls to toolbar dropdown
* Remove icon buttons
* Add each control seperately to toolbar dropdown
* Remove unused code
* Fix close popover on click
* Remove unnecessary dark theme check
* Make closePopover optional for creating controls
* Fix control group strings
* Fix alignment of toolbar popover items
* Functional tests - create controls from new menu button
* Hide controls callout for empty dashboards
* Add tooltips to control types + i18n support.
* Move callout render logic to dashboard viewport
* Add controls callout functional tests
* Fix bundle size by lazy importing controls callout
* Get create control button in callout via passed function
* Fix mobile view of callout
* Add documentation and cleaned code based on Devon's feedback
* Moved the 'add to library' and 'controls' buttons in to extra