From b189743ca07fc42311b3a5111111a93b13cfba7f Mon Sep 17 00:00:00 2001 From: Ihor Yeromin Date: Wed, 23 Oct 2024 09:24:06 +0200 Subject: [PATCH] Actions: Improve drag and drop experience (#94263) * feat(actions): improve dnd ux --------- Co-authored-by: Kristina Durivage --- .../DataLinksInlineEditor.tsx | 69 +++++++++---------- .../DataLinksListItem.tsx | 50 ++++++-------- .../features/actions/ActionsInlineEditor.tsx | 68 +++++++++--------- .../app/features/actions/ActionsListItem.tsx | 38 +++++----- public/locales/en-US/grafana.json | 3 - public/locales/pseudo-LOCALE/grafana.json | 3 - 6 files changed, 103 insertions(+), 128 deletions(-) diff --git a/packages/grafana-ui/src/components/DataLinks/DataLinksInlineEditor/DataLinksInlineEditor.tsx b/packages/grafana-ui/src/components/DataLinks/DataLinksInlineEditor/DataLinksInlineEditor.tsx index d7adcd96133..a431e241479 100644 --- a/packages/grafana-ui/src/components/DataLinks/DataLinksInlineEditor/DataLinksInlineEditor.tsx +++ b/packages/grafana-ui/src/components/DataLinks/DataLinksInlineEditor/DataLinksInlineEditor.tsx @@ -1,7 +1,7 @@ import { css } from '@emotion/css'; import { DragDropContext, Droppable, DropResult } from '@hello-pangea/dnd'; import { cloneDeep } from 'lodash'; -import { ReactNode, useEffect, useState } from 'react'; +import { useEffect, useState } from 'react'; import { DataFrame, DataLink, GrafanaTheme2, VariableSuggestion } from '@grafana/data'; @@ -91,49 +91,40 @@ export const DataLinksInlineEditor = ({ onChange(update); }; - const renderFirstLink = (linkJSX: ReactNode, key: string) => { - if (showOneClick) { - return ( -
+ return ( +
+ {/* one-link placeholder */} + {showOneClick && linksSafe.length > 0 && ( +
One-click link - {linkJSX}
- ); - } - return linkJSX; - }; + )} - return ( - <> {(provided) => ( -
+
0 ? '28px' : '0px' }} + > {linksSafe.map((link, idx) => { const key = `${link.title}/${idx}`; - - const linkJSX = ( -
- setEditIndex(idx)} - onRemove={() => onDataLinkRemove(idx)} - data={data} - itemKey={key} - /> -
+ return ( + setEditIndex(idx)} + onRemove={() => onDataLinkRemove(idx)} + data={data} + itemKey={key} + /> ); - - if (idx === 0) { - return renderFirstLink(linkJSX, key); - } - - return linkJSX; })} {provided.placeholder}
@@ -164,22 +155,27 @@ export const DataLinksInlineEditor = ({ - +
); }; const getDataLinksInlineEditorStyles = (theme: GrafanaTheme2) => ({ + container: css({ + position: 'relative', + }), wrapper: css({ marginBottom: theme.spacing(2), display: 'flex', flexDirection: 'column', }), oneClickOverlay: css({ - height: 'auto', border: `2px dashed ${theme.colors.text.link}`, fontSize: 10, color: theme.colors.text.primary, marginBottom: theme.spacing(1), + position: 'absolute', + width: '100%', + height: '92px', }), oneClickSpan: css({ padding: 10, @@ -187,9 +183,6 @@ const getDataLinksInlineEditorStyles = (theme: GrafanaTheme2) => ({ marginBottom: -10, display: 'inline-block', }), - itemWrapper: css({ - padding: '4px 8px 8px 8px', - }), button: css({ marginLeft: theme.spacing(1), }), diff --git a/packages/grafana-ui/src/components/DataLinks/DataLinksInlineEditor/DataLinksListItem.tsx b/packages/grafana-ui/src/components/DataLinks/DataLinksInlineEditor/DataLinksListItem.tsx index b245c7500a9..939a4ec15b7 100644 --- a/packages/grafana-ui/src/components/DataLinks/DataLinksInlineEditor/DataLinksListItem.tsx +++ b/packages/grafana-ui/src/components/DataLinks/DataLinksInlineEditor/DataLinksListItem.tsx @@ -5,10 +5,9 @@ import { DataFrame, DataLink, GrafanaTheme2 } from '@grafana/data'; import { useStyles2 } from '../../../themes'; import { isCompactUrl } from '../../../utils'; -import { Trans } from '../../../utils/i18n'; -import { FieldValidationMessage } from '../../Forms/FieldValidationMessage'; import { Icon } from '../../Icon/Icon'; import { IconButton } from '../../IconButton/IconButton'; +import { Tooltip } from '../../Tooltip/Tooltip'; export interface DataLinksListItemProps { index: number; @@ -33,40 +32,33 @@ export const DataLinksListItem = ({ link, onEdit, onRemove, index, itemKey }: Da return ( {(provided) => ( - <> -
-
-
- {hasTitle ? title : 'Data link title not provided'} -
+
+
+
+ {hasTitle ? title : 'Data link title not provided'} +
+
{hasUrl ? url : 'Data link url not provided'}
- {isCompactExploreUrl && ( - - - Explore data link may not work in the future. Please edit. - - - )} -
-
- - -
- -
+ +
+
+ + +
+
- +
)} ); @@ -79,7 +71,6 @@ const getDataLinkListItemStyles = (theme: GrafanaTheme2) => { flexGrow: 1, alignItems: 'center', justifyContent: 'space-between', - width: '100%', padding: '5px 0 5px 10px', borderRadius: theme.shape.radius.default, background: theme.colors.background.secondary, @@ -112,6 +103,7 @@ const getDataLinkListItemStyles = (theme: GrafanaTheme2) => { }), dragRow: css({ position: 'relative', + margin: '8px', }), icons: css({ display: 'flex', diff --git a/public/app/features/actions/ActionsInlineEditor.tsx b/public/app/features/actions/ActionsInlineEditor.tsx index 04a59f0714e..356fb4a2410 100644 --- a/public/app/features/actions/ActionsInlineEditor.tsx +++ b/public/app/features/actions/ActionsInlineEditor.tsx @@ -1,7 +1,7 @@ import { css } from '@emotion/css'; import { DragDropContext, Droppable, DropResult } from '@hello-pangea/dnd'; import { cloneDeep } from 'lodash'; -import { ReactNode, useEffect, useState } from 'react'; +import { useEffect, useState } from 'react'; import { Action, DataFrame, GrafanaTheme2, defaultActionConfig, VariableSuggestion } from '@grafana/data'; import { Button } from '@grafana/ui/src/components/Button'; @@ -90,46 +90,39 @@ export const ActionsInlineEditor = ({ onChange(update); }; - const renderFirstAction = (actionsJSX: ReactNode, key: string) => { - if (showOneClick) { - return ( -
- One-click action {actionsJSX} -
- ); - } - return actionsJSX; - }; - return ( - <> +
+ {/* one-link placeholder */} + {showOneClick && actionsSafe.length > 0 && ( +
+ One-click link +
+ )} + {(provided) => ( -
+
0 ? '28px' : '0px' }} + > {actionsSafe.map((action, idx) => { const key = `${action.title}/${idx}`; - const actionsJSX = ( -
- setEditIndex(idx)} - onRemove={() => onActionRemove(idx)} - data={data} - itemKey={key} - /> -
+ return ( + setEditIndex(idx)} + onRemove={() => onActionRemove(idx)} + data={data} + itemKey={key} + /> ); - - if (idx === 0) { - return renderFirstAction(actionsJSX, key); - } - - return actionsJSX; })} {provided.placeholder}
@@ -160,22 +153,27 @@ export const ActionsInlineEditor = ({ - +
); }; const getActionsInlineEditorStyle = (theme: GrafanaTheme2) => ({ + container: css({ + position: 'relative', + }), wrapper: css({ marginBottom: theme.spacing(2), display: 'flex', flexDirection: 'column', }), oneClickOverlay: css({ - height: 'auto', border: `2px dashed ${theme.colors.text.link}`, fontSize: 10, color: theme.colors.text.primary, marginBottom: theme.spacing(1), + position: 'absolute', + width: '100%', + height: '89px', }), oneClickSpan: css({ padding: 10, diff --git a/public/app/features/actions/ActionsListItem.tsx b/public/app/features/actions/ActionsListItem.tsx index a8288d70df7..6dd9cebf58f 100644 --- a/public/app/features/actions/ActionsListItem.tsx +++ b/public/app/features/actions/ActionsListItem.tsx @@ -26,27 +26,25 @@ export const ActionListItem = ({ action, onEdit, onRemove, index, itemKey }: Act return ( {(provided) => ( - <> -
-
-
- {hasTitle ? title : 'Action title not provided'} -
-
-
- - -
- -
+
+
+
+ {hasTitle ? title : 'Action title not provided'}
- +
+ + +
+ +
+
+
)} ); @@ -59,7 +57,6 @@ const getActionListItemStyles = (theme: GrafanaTheme2) => { flexGrow: 1, alignItems: 'center', justifyContent: 'space-between', - width: '100%', padding: '5px 0 5px 10px', borderRadius: theme.shape.radius.default, background: theme.colors.background.secondary, @@ -100,6 +97,7 @@ const getActionListItemStyles = (theme: GrafanaTheme2) => { }), dragRow: css({ position: 'relative', + margin: '8px', }), icons: css({ display: 'flex', diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json index 858f7357e31..46c91cab42a 100644 --- a/public/locales/en-US/grafana.json +++ b/public/locales/en-US/grafana.json @@ -1084,9 +1084,6 @@ "add-link": "Add link", "one-click-link": "One-click link" }, - "data-links-list-item": { - "compact-explore-disclaimer": "Explore data link may not work in the future. Please edit." - }, "data-source-http-settings": { "access-help": "Help <1>", "access-help-details": "Access mode controls how requests to the data source will be handled.<1> <1>Server should be the preferred way if nothing else is stated.", diff --git a/public/locales/pseudo-LOCALE/grafana.json b/public/locales/pseudo-LOCALE/grafana.json index 31f557cdd18..df37041278f 100644 --- a/public/locales/pseudo-LOCALE/grafana.json +++ b/public/locales/pseudo-LOCALE/grafana.json @@ -1084,9 +1084,6 @@ "add-link": "Åđđ ľįʼnĸ", "one-click-link": "Øʼnę-čľįčĸ ľįʼnĸ" }, - "data-links-list-item": { - "compact-explore-disclaimer": "Ēχpľőřę đäŧä ľįʼnĸ mäy ʼnőŧ ŵőřĸ įʼn ŧĥę ƒūŧūřę. Pľęäşę ęđįŧ." - }, "data-source-http-settings": { "access-help": "Ħęľp <1>", "access-help-details": "Åččęşş mőđę čőʼnŧřőľş ĥőŵ řęqūęşŧş ŧő ŧĥę đäŧä şőūřčę ŵįľľ þę ĥäʼnđľęđ.<1> <1>Ŝęřvęř şĥőūľđ þę ŧĥę přęƒęřřęđ ŵäy įƒ ʼnőŧĥįʼnģ ęľşę įş şŧäŧęđ.",