[Dashboard] [Controls] Use uiActions for control hover actions (#153065)

Closes https://github.com/elastic/kibana/issues/143585
Closes https://github.com/elastic/kibana/issues/151767
Closes https://github.com/elastic/kibana/issues/152609

## Summary
This PR accomplishes three things, the first of which is moving the
edit/delete control hover actions to use the `uiActions` service - this
is the first step in moving existing panel actions (such as replacing
the panel, opening the panel settings flyout, etc.) to this hover
framework, which is outlined in
[this](https://github.com/elastic/kibana/issues/151233) issue.

While this was the primary goal of this PR, this also made the following
fixes possible:
1. Since I was refactoring the control editor flyout code as part of
this PR, I made it so that changes to the control's width/grow
properties are **only applied** when the changes are **saved** rather
than being automatically applied.

    | Before            | After |
    | ------------- | ------------- |
| ![Mar-14-2023
13-05-36](https://user-images.githubusercontent.com/8698078/225110954-d443f76b-4ac7-476c-b5b7-9af082d187fd.gif)
| ![Mar-14-2023
13-06-41](https://user-images.githubusercontent.com/8698078/225111172-ab9cce7e-7a70-45e4-ab06-5a87c053fb95.gif)
|
  


2. Since the edit control button is no longer a custom component, the
tooltip now responds to focus as expected.

    | Before            | After |
    | ------------- | ------------- |
| ![Mar-14-2023
13-05-36](https://user-images.githubusercontent.com/8698078/225113458-fe8f05fb-d56c-437a-b625-2a336bb4ba29.gif)
| ![Mar-14-2023
13-06-41](https://user-images.githubusercontent.com/8698078/225113313-d8cb7fcc-f611-48d0-83b4-f6fd147ce0ae.gif)
|


### 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 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>
This commit is contained in:
Hannah Mudge 2023-03-21 08:47:40 -06:00 committed by GitHub
parent f731ffbf01
commit b6fb66017b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
37 changed files with 808 additions and 276 deletions

View file

@ -6,10 +6,12 @@
* Side Public License, v 1.
*/
import { pickBy } from 'lodash';
import React, { useState } from 'react';
import {
EuiButton,
EuiButtonEmpty,
EuiButtonGroup,
EuiFlexGroup,
EuiFlexItem,
EuiLoadingContent,
@ -19,8 +21,13 @@ import {
EuiTitle,
} from '@elastic/eui';
import { ViewMode } from '@kbn/embeddable-plugin/public';
import { LazyControlGroupRenderer, ControlGroupContainer } from '@kbn/controls-plugin/public';
import {
LazyControlGroupRenderer,
ControlGroupContainer,
ControlGroupInput,
} from '@kbn/controls-plugin/public';
import { withSuspense } from '@kbn/presentation-util-plugin/public';
import { ACTION_EDIT_CONTROL, ACTION_DELETE_CONTROL } from '@kbn/controls-plugin/public';
const ControlGroupRenderer = withSuspense(LazyControlGroupRenderer);
@ -30,6 +37,27 @@ export const EditExample = () => {
const [isSaving, setIsSaving] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [controlGroup, setControlGroup] = useState<ControlGroupContainer>();
const [toggleIconIdToSelectedMapIcon, setToggleIconIdToSelectedMapIcon] = useState<{
[id: string]: boolean;
}>({});
function onChangeIconsMultiIcons(optionId: string) {
const newToggleIconIdToSelectedMapIcon = {
...toggleIconIdToSelectedMapIcon,
...{
[optionId]: !toggleIconIdToSelectedMapIcon[optionId],
},
};
if (controlGroup) {
const disabledActions: string[] = Object.keys(
pickBy(newToggleIconIdToSelectedMapIcon, (value) => value)
);
controlGroup.updateInput({ disabledActions });
}
setToggleIconIdToSelectedMapIcon(newToggleIconIdToSelectedMapIcon);
}
async function onSave() {
setIsSaving(true);
@ -48,16 +76,20 @@ export const EditExample = () => {
// simulated async load await
await new Promise((resolve) => setTimeout(resolve, 1000));
let input = {};
let input: Partial<ControlGroupInput> = {};
const inputAsString = localStorage.getItem(INPUT_KEY);
if (inputAsString) {
try {
input = JSON.parse(inputAsString);
const disabledActions = input.disabledActions ?? [];
setToggleIconIdToSelectedMapIcon({
[ACTION_EDIT_CONTROL]: disabledActions.includes(ACTION_EDIT_CONTROL),
[ACTION_DELETE_CONTROL]: disabledActions.includes(ACTION_DELETE_CONTROL),
});
} catch (e) {
// ignore parse errors
}
}
setIsLoading(false);
return input;
}
@ -72,7 +104,7 @@ export const EditExample = () => {
</EuiText>
<EuiSpacer size="m" />
<EuiPanel hasBorder={true}>
<EuiFlexGroup gutterSize="s" alignItems="center">
<EuiFlexGroup gutterSize="m" alignItems="center">
<EuiFlexItem grow={false}>
<EuiButtonEmpty
color="primary"
@ -85,11 +117,32 @@ export const EditExample = () => {
Add control
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem grow={true}>
<EuiButtonGroup
legend="Text style"
buttonSize="m"
options={[
{
id: ACTION_EDIT_CONTROL,
label: 'Disable edit action',
value: ACTION_EDIT_CONTROL,
},
{
id: ACTION_DELETE_CONTROL,
label: 'Disable delete action',
value: ACTION_DELETE_CONTROL,
},
]}
idToSelectedMap={toggleIconIdToSelectedMapIcon}
type="multi"
onChange={(id: string) => onChangeIconsMultiIcons(id)}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton
fill
color="primary"
isDisabled={controlGroup === undefined || isSaving}
fill
onClick={onSave}
isLoading={isSaving}
>