[Streams] Partitioning page toasts UX improvement (#206571)

## Summary

Just a UX improvement to implement this:
https://github.com/elastic/kibana/pull/206116#issuecomment-2587744663
(https://github.com/elastic/kibana/pull/206116 was merged already).
This commit is contained in:
Kerry Gallagher 2025-01-15 13:07:26 +00:00 committed by GitHub
parent 0c4c8aa163
commit a4c1c2066a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -45,6 +45,7 @@ import {
import { useUnsavedChangesPrompt } from '@kbn/unsaved-changes-prompt';
import { AbortableAsyncState } from '@kbn/observability-utils-browser/hooks/use_abortable_async';
import { DraggableProvided } from '@hello-pangea/dnd';
import { IToasts, Toast } from '@kbn/core/public';
import { toMountPoint } from '@kbn/react-kibana-mount';
import { useKibana } from '../../hooks/use_kibana';
import { useStreamsAppFetch } from '../../hooks/use_streams_app_fetch';
@ -57,10 +58,31 @@ import { PreviewTable } from '../preview_table';
import { StreamDeleteModal } from '../stream_delete_modal';
import { AssetImage } from '../asset_image';
function useRoutingState({ definition }: { definition?: ReadStreamDefinition }) {
const [childUnderEdit, setChildUnderEdit] = React.useState<
{ isNew: boolean; child: StreamChild } | undefined
>();
interface ChildUnderEdit {
isNew: boolean;
child: StreamChild;
}
function useRoutingState({
definition,
toasts,
}: {
definition?: ReadStreamDefinition;
toasts: IToasts;
}) {
const [lastDisplayedToast, setLastDisplayedToast] = React.useState<Toast | undefined>();
const [childUnderEdit, setChildUnderEdit] = React.useState<ChildUnderEdit | undefined>();
const selectChildUnderEdit = useCallback(
(child: ChildUnderEdit | undefined) => {
if (lastDisplayedToast) {
toasts.remove(lastDisplayedToast.id);
}
setChildUnderEdit(child);
},
[lastDisplayedToast, toasts]
);
// Child streams: either represents the child streams as they are, or the new order from drag and drop.
const [childStreams, setChildStreams] = React.useState<
@ -83,6 +105,9 @@ function useRoutingState({ definition }: { definition?: ReadStreamDefinition })
const onChildStreamDragEnd = useCallback(
(event: DropResult) => {
if (lastDisplayedToast) {
toasts.remove(lastDisplayedToast.id);
}
setDraggingChildStream(undefined);
if (typeof event.source.index === 'number' && typeof event.destination?.index === 'number') {
setChildStreams([
@ -90,7 +115,7 @@ function useRoutingState({ definition }: { definition?: ReadStreamDefinition })
]);
}
},
[childStreams]
[childStreams, lastDisplayedToast, toasts]
);
const cancelChanges = useCallback(() => {
@ -104,9 +129,10 @@ function useRoutingState({ definition }: { definition?: ReadStreamDefinition })
const [showDeleteModal, setShowDeleteModal] = React.useState(false);
return {
setLastDisplayedToast,
debouncedChildUnderEdit,
childUnderEdit,
setChildUnderEdit,
selectChildUnderEdit,
saveInProgress,
setSaveInProgress,
showDeleteModal,
@ -129,7 +155,7 @@ export function StreamDetailRouting({
}) {
const { appParams, core } = useKibana();
const theme = useEuiTheme().euiTheme;
const routingAppState = useRoutingState({ definition });
const routingAppState = useRoutingState({ definition, toasts: core.notifications.toasts });
const {
dependencies: {
@ -174,7 +200,7 @@ export function StreamDetailRouting({
{routingAppState.showDeleteModal && routingAppState.childUnderEdit && (
<StreamDeleteModal
closeModal={closeModal}
clearChildUnderEdit={() => routingAppState.setChildUnderEdit(undefined)}
clearChildUnderEdit={() => routingAppState.selectChildUnderEdit(undefined)}
refreshDefinition={refreshDefinition}
id={routingAppState.childUnderEdit.child.name}
availableStreams={availableStreams}
@ -349,7 +375,7 @@ function ControlBar({
}
routingAppState.setSaveInProgress(false);
notifications.toasts.addSuccess({
const toast = notifications.toasts.addSuccess({
title: i18n.translate('xpack.streams.streamDetailRouting.saved', {
defaultMessage: 'Stream saved',
}),
@ -376,16 +402,18 @@ function ControlBar({
core
),
});
routingAppState.setChildUnderEdit(undefined);
routingAppState.setLastDisplayedToast(toast);
routingAppState.selectChildUnderEdit(undefined);
refreshDefinition();
} catch (error) {
routingAppState.setSaveInProgress(false);
notifications.toasts.addError(error, {
const toast = notifications.toasts.addError(error, {
title: i18n.translate('xpack.streams.failedToSave', {
defaultMessage: 'Failed to save',
}),
toastMessage: 'body' in error ? error.body.message : error.message,
});
routingAppState.setLastDisplayedToast(toast);
}
}
@ -640,7 +668,7 @@ function ChildStreamList({
availableStreams,
routingAppState: {
childUnderEdit,
setChildUnderEdit,
selectChildUnderEdit,
childStreams,
onChildStreamDragEnd,
onChildStreamDragStart,
@ -706,13 +734,13 @@ function ChildStreamList({
edit={!childUnderEdit?.isNew && child.name === childUnderEdit?.child.name}
onEditStateChange={() => {
if (child.name === childUnderEdit?.child.name) {
setChildUnderEdit(undefined);
selectChildUnderEdit(undefined);
} else {
setChildUnderEdit({ isNew: false, child });
selectChildUnderEdit({ isNew: false, child });
}
}}
onChildChange={(newChild) => {
setChildUnderEdit({
selectChildUnderEdit({
isNew: false,
child: newChild,
});
@ -733,10 +761,10 @@ function ChildStreamList({
child={childUnderEdit.child}
onChildChange={(newChild) => {
if (!newChild) {
setChildUnderEdit(undefined);
selectChildUnderEdit(undefined);
return;
}
setChildUnderEdit({
selectChildUnderEdit({
isNew: true,
child: newChild,
});
@ -750,7 +778,7 @@ function ChildStreamList({
iconType="plus"
data-test-subj="streamsAppStreamDetailRoutingAddRuleButton"
onClick={() => {
setChildUnderEdit({
selectChildUnderEdit({
isNew: true,
child: {
name: `${definition.name}.child`,
@ -839,7 +867,10 @@ function RoutingStreamEntry({
color="transparent"
paddingSize="s"
{...draggableProvided.dragHandleProps}
aria-label="Drag Handle"
aria-label={i18n.translate(
'xpack.streams.routingStreamEntry.euiPanel.dragHandleLabel',
{ defaultMessage: 'Drag Handle' }
)}
>
<EuiIcon type="grab" />
</EuiPanel>