This is an automated email from the ASF dual-hosted git repository.
rusackas pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git
The following commit(s) were added to refs/heads/master by this push:
new 4ff9aac1fa feat(sqllab): giving the query history pane a facelift
(#31316)
4ff9aac1fa is described below
commit 4ff9aac1fae2d88fd7667317c706256d3cfcb4f4
Author: Maxime Beauchemin <[email protected]>
AuthorDate: Thu Dec 12 15:30:50 2024 -0800
feat(sqllab): giving the query history pane a facelift (#31316)
---
superset-frontend/src/SqlLab/actions/sqlLab.js | 4 +-
.../src/SqlLab/components/QueryTable/index.tsx | 96 ++++++++++++++++------
.../src/components/Icons/Icons.stories.tsx | 13 ++-
.../src/components/Label/Label.stories.tsx | 9 +-
superset-frontend/src/components/Label/index.tsx | 54 +++++++-----
5 files changed, 121 insertions(+), 55 deletions(-)
diff --git a/superset-frontend/src/SqlLab/actions/sqlLab.js
b/superset-frontend/src/SqlLab/actions/sqlLab.js
index 568dbb9b1f..4fd7708338 100644
--- a/superset-frontend/src/SqlLab/actions/sqlLab.js
+++ b/superset-frontend/src/SqlLab/actions/sqlLab.js
@@ -421,9 +421,7 @@ export function postStopQuery(query) {
})
.then(() => dispatch(stopQuery(query)))
.then(() => dispatch(addSuccessToast(t('Query was stopped.'))))
- .catch(() =>
- dispatch(addDangerToast(t('Failed at stopping query. %s', query.id))),
- );
+ .catch(() => dispatch(addDangerToast(t('Failed to stop query.'))));
};
}
diff --git a/superset-frontend/src/SqlLab/components/QueryTable/index.tsx
b/superset-frontend/src/SqlLab/components/QueryTable/index.tsx
index 916c04aece..43cc5da7b0 100644
--- a/superset-frontend/src/SqlLab/components/QueryTable/index.tsx
+++ b/superset-frontend/src/SqlLab/components/QueryTable/index.tsx
@@ -16,11 +16,10 @@
* specific language governing permissions and limitations
* under the License.
*/
-import { useMemo } from 'react';
+import { useMemo, ReactNode } from 'react';
import moment from 'moment';
import Card from 'src/components/Card';
import ProgressBar from 'src/components/ProgressBar';
-import Label from 'src/components/Label';
import { t, useTheme, QueryResponse } from '@superset-ui/core';
import { useDispatch, useSelector } from 'react-redux';
@@ -35,6 +34,7 @@ import TableView from 'src/components/TableView';
import Button from 'src/components/Button';
import { fDuration } from 'src/utils/dates';
import Icons from 'src/components/Icons';
+import Label from 'src/components/Label';
import { Tooltip } from 'src/components/Tooltip';
import { SqlLabRootState } from 'src/SqlLab/types';
import ModalTrigger from 'src/components/ModalTrigger';
@@ -44,11 +44,16 @@ import HighlightedSql from '../HighlightedSql';
import { StaticPosition, verticalAlign, StyledTooltip } from './styles';
interface QueryTableQuery
- extends Omit<QueryResponse, 'state' | 'sql' | 'progress' | 'results'> {
+ extends Omit<
+ QueryResponse,
+ 'state' | 'sql' | 'progress' | 'results' | 'duration' | 'started'
+ > {
state?: Record<string, any>;
sql?: Record<string, any>;
progress?: Record<string, any>;
results?: Record<string, any>;
+ duration?: ReactNode;
+ started?: ReactNode;
}
interface QueryTableProps {
@@ -125,55 +130,95 @@ const QueryTable = ({
const statusAttributes = {
success: {
config: {
- icon: <Icons.Check iconColor={theme.colors.success.base} />,
+ icon: (
+ <Icons.CheckOutlined
+ iconColor={theme.colors.success.base}
+ iconSize="m"
+ />
+ ),
+ // icon: <Icons.Edit iconSize="xl" />,
label: t('Success'),
},
},
failed: {
config: {
- icon: <Icons.XSmall iconColor={theme.colors.error.base} />,
+ icon: (
+ <Icons.CloseOutlined
+ iconColor={theme.colors.error.base}
+ iconSize="m"
+ />
+ ),
label: t('Failed'),
},
},
stopped: {
config: {
- icon: <Icons.XSmall iconColor={theme.colors.error.base} />,
+ icon: (
+ <Icons.CloseOutlined
+ iconColor={theme.colors.error.base}
+ iconSize="m"
+ />
+ ),
label: t('Failed'),
},
},
running: {
config: {
- icon: <Icons.Running iconColor={theme.colors.primary.base} />,
+ icon: (
+ <Icons.LoadingOutlined
+ iconColor={theme.colors.primary.base}
+ iconSize="m"
+ />
+ ),
label: t('Running'),
},
},
fetching: {
config: {
- icon: <Icons.Queued iconColor={theme.colors.primary.base} />,
+ icon: (
+ <Icons.LoadingOutlined
+ iconColor={theme.colors.primary.base}
+ iconSize="m"
+ />
+ ),
label: t('Fetching'),
},
},
timed_out: {
config: {
- icon: <Icons.Offline iconColor={theme.colors.grayscale.light1} />,
+ icon: (
+ <Icons.Clock iconColor={theme.colors.error.base} iconSize="m" />
+ ),
label: t('Offline'),
},
},
scheduled: {
config: {
- icon: <Icons.Queued iconColor={theme.colors.grayscale.base} />,
+ icon: (
+ <Icons.LoadingOutlined
+ iconColor={theme.colors.warning.base}
+ iconSize="m"
+ />
+ ),
label: t('Scheduled'),
},
},
pending: {
config: {
- icon: <Icons.Queued iconColor={theme.colors.grayscale.base} />,
+ icon: (
+ <Icons.LoadingOutlined
+ iconColor={theme.colors.warning.base}
+ iconSize="m"
+ />
+ ),
label: t('Scheduled'),
},
},
error: {
config: {
- icon: <Icons.Error iconColor={theme.colors.error.base} />,
+ icon: (
+ <Icons.Error iconColor={theme.colors.error.base} iconSize="m" />
+ ),
label: t('Unknown Status'),
},
},
@@ -187,16 +232,10 @@ const QueryTable = ({
const status = statusAttributes[state] || statusAttributes.error;
if (q.endDttm) {
- q.duration = fDuration(q.startDttm, q.endDttm);
+ q.duration = (
+ <Label monospace>{fDuration(q.startDttm, q.endDttm)}</Label>
+ );
}
- const time = moment(q.startDttm).format().split('T');
- q.time = (
- <div>
- <span>
- {time[0]} <br /> {time[1]}
- </span>
- </div>
- );
q.user = (
<Button
buttonSize="small"
@@ -215,7 +254,9 @@ const QueryTable = ({
{q.db}
</Button>
);
- q.started = moment(q.startDttm).format('L HH:mm:ss');
+ q.started = (
+ <Label monospace>{moment(q.startDttm).format('L HH:mm:ss')}</Label>
+ );
q.querylink = (
<Button
buttonSize="small"
@@ -241,9 +282,9 @@ const QueryTable = ({
<ModalTrigger
className="ResultsModal"
triggerNode={
- <Label type="info" className="pointer">
+ <Button buttonSize="xsmall" buttonStyle="tertiary">
{t('View')}
- </Label>
+ </Button>
}
modalTitle={t('Data preview')}
beforeOpen={() => openAsyncResults(query, displayLimit)}
@@ -275,9 +316,7 @@ const QueryTable = ({
<ProgressBar percent={parseInt(progress.toFixed(0), 10)} striped />
);
q.state = (
- <Tooltip title={status.config.label} placement="bottom">
- <span>{status.config.icon}</span>
- </Tooltip>
+ <Tooltip title={status.config.label}>{status.config.icon}</Tooltip>
);
q.actions = (
<div>
@@ -287,6 +326,7 @@ const QueryTable = ({
'Overwrite text in the editor with a query on this table',
)}
placement="top"
+ className="pointer"
>
<Icons.Edit iconSize="xl" />
</StyledTooltip>
@@ -294,6 +334,7 @@ const QueryTable = ({
onClick={() => openQueryInNewTab(query)}
tooltip={t('Run query in a new tab')}
placement="top"
+ className="pointer"
>
<Icons.PlusCircleOutlined iconSize="xl" css={verticalAlign} />
</StyledTooltip>
@@ -301,6 +342,7 @@ const QueryTable = ({
<StyledTooltip
tooltip={t('Remove query from log')}
onClick={() => dispatch(removeQuery(query))}
+ className="pointer"
>
<Icons.Trash iconSize="xl" />
</StyledTooltip>
diff --git a/superset-frontend/src/components/Icons/Icons.stories.tsx
b/superset-frontend/src/components/Icons/Icons.stories.tsx
index db7410ca62..5a21daf5fb 100644
--- a/superset-frontend/src/components/Icons/Icons.stories.tsx
+++ b/superset-frontend/src/components/Icons/Icons.stories.tsx
@@ -44,10 +44,19 @@ const IconBlock = styled.div`
flex-direction: column;
align-items: center;
padding: ${({ theme }) => theme.gridUnit * 2}px;
+
+ span {
+ margin-top: ${({ theme }) =>
+ 2 * theme.gridUnit}px; // Add spacing between icon and name
+ font-size: ${({ theme }) =>
+ theme.typography.sizes.m}; // Optional: adjust font size for elegance
+ color: ${({ theme }) =>
+ theme.colors.grayscale.base}; // Optional: subtle color for the name
+ }
`;
export const InteractiveIcons = ({
- showNames,
+ showNames = true,
...rest
}: IconType & { showNames: boolean }) => (
<IconSet>
@@ -56,7 +65,7 @@ export const InteractiveIcons = ({
return (
<IconBlock key={k}>
<IconComponent {...rest} />
- {showNames && k}
+ {showNames && <span>{k}</span>}
</IconBlock>
);
})}
diff --git a/superset-frontend/src/components/Label/Label.stories.tsx
b/superset-frontend/src/components/Label/Label.stories.tsx
index d502e160c5..5c46182465 100644
--- a/superset-frontend/src/components/Label/Label.stories.tsx
+++ b/superset-frontend/src/components/Label/Label.stories.tsx
@@ -55,9 +55,13 @@ export const LabelGallery = () => (
);
export const InteractiveLabel = (args: any) => {
- const { hasOnClick, label, ...rest } = args;
+ const { hasOnClick, label, monospace, ...rest } = args;
return (
- <Label onClick={hasOnClick ? action('clicked') : undefined} {...rest}>
+ <Label
+ onClick={hasOnClick ? action('clicked') : undefined}
+ monospace={monospace}
+ {...rest}
+ >
{label}
</Label>
);
@@ -66,4 +70,5 @@ export const InteractiveLabel = (args: any) => {
InteractiveLabel.args = {
hasOnClick: true,
label: 'Example',
+ monospace: true,
};
diff --git a/superset-frontend/src/components/Label/index.tsx
b/superset-frontend/src/components/Label/index.tsx
index 30f3ca6aed..d904a445be 100644
--- a/superset-frontend/src/components/Label/index.tsx
+++ b/superset-frontend/src/components/Label/index.tsx
@@ -46,12 +46,20 @@ export interface LabelProps extends
HTMLAttributes<HTMLSpanElement> {
style?: CSSProperties;
children?: ReactNode;
role?: string;
+ monospace?: boolean;
}
export default function Label(props: LabelProps) {
const theme = useTheme();
const { colors, transitionTiming } = theme;
- const { type = 'default', onClick, children, ...rest } = props;
+ const {
+ type = 'default',
+ monospace = false,
+ style,
+ onClick,
+ children,
+ ...rest
+ } = props;
const {
alert,
primary,
@@ -89,37 +97,41 @@ export default function Label(props: LabelProps) {
} else {
baseColor = primary;
}
-
backgroundColor = baseColor.base;
backgroundColorHover = onClick ? baseColor.dark1 : baseColor.base;
borderColor = onClick ? baseColor.dark1 : 'transparent';
borderColorHover = onClick ? baseColor.dark2 : 'transparent';
}
+ const css = {
+ transition: `background-color ${transitionTiming}s`,
+ whiteSpace: 'nowrap',
+ cursor: onClick ? 'pointer' : 'default',
+ overflow: 'hidden',
+ textOverflow: 'ellipsis',
+ backgroundColor,
+ borderColor,
+ borderRadius: 21,
+ padding: '0.35em 0.8em',
+ lineHeight: 1,
+ color,
+ maxWidth: '100%',
+ '&:hover': {
+ backgroundColor: backgroundColorHover,
+ borderColor: borderColorHover,
+ opacity: 1,
+ },
+ };
+ if (monospace) {
+ css['font-family'] = theme.typography.families.monospace;
+ }
return (
<Tag
onClick={onClick}
role={onClick ? 'button' : undefined}
+ style={style}
{...rest}
- css={{
- transition: `background-color ${transitionTiming}s`,
- whiteSpace: 'nowrap',
- cursor: onClick ? 'pointer' : 'default',
- overflow: 'hidden',
- textOverflow: 'ellipsis',
- backgroundColor,
- borderColor,
- borderRadius: 21,
- padding: '0.35em 0.8em',
- lineHeight: 1,
- color,
- maxWidth: '100%',
- '&:hover': {
- backgroundColor: backgroundColorHover,
- borderColor: borderColorHover,
- opacity: 1,
- },
- }}
+ css={css}
>
{children}
</Tag>