This is an automated email from the ASF dual-hosted git repository. msyavuz pushed a commit to branch msyavuz/fix/south-pane-visuals in repository https://gitbox.apache.org/repos/asf/superset.git
commit 50e046e2f5452191851d0fa37381f1d8b1f6c941 Author: Mehmet Salih Yavuz <[email protected]> AuthorDate: Fri Oct 10 10:19:13 2025 +0300 fix(SqlLab): South pane visuals --- .../superset-ui-core/src/components/Tabs/Tabs.tsx | 7 +- .../src/SqlLab/components/SqlEditor/index.tsx | 1 + .../src/SqlLab/components/TablePreview/index.tsx | 106 ++++++++++----------- .../src/components/ActionButton/index.tsx | 75 +++++++++++++++ .../src/components/ListView/ActionsBar.tsx | 49 +--------- 5 files changed, 135 insertions(+), 103 deletions(-) diff --git a/superset-frontend/packages/superset-ui-core/src/components/Tabs/Tabs.tsx b/superset-frontend/packages/superset-ui-core/src/components/Tabs/Tabs.tsx index d834cd05f2..edc50623f7 100644 --- a/superset-frontend/packages/superset-ui-core/src/components/Tabs/Tabs.tsx +++ b/superset-frontend/packages/superset-ui-core/src/components/Tabs/Tabs.tsx @@ -21,15 +21,18 @@ import { css, styled, useTheme } from '@superset-ui/core'; // eslint-disable-next-line no-restricted-imports import { Tabs as AntdTabs, TabsProps as AntdTabsProps } from 'antd'; import { Icons } from '@superset-ui/core/components/Icons'; +import type { SerializedStyles } from '@emotion/react'; export interface TabsProps extends AntdTabsProps { allowOverflow?: boolean; + contentStyle?: SerializedStyles; } const StyledTabs = ({ animated = false, allowOverflow = true, tabBarStyle, + contentStyle, ...props }: TabsProps) => { const theme = useTheme(); @@ -46,6 +49,7 @@ const StyledTabs = ({ .ant-tabs-content-holder { overflow: ${allowOverflow ? 'visible' : 'auto'}; + ${contentStyle} } .ant-tabs-tab { flex: 1 1 auto; @@ -85,9 +89,10 @@ const Tabs = Object.assign(StyledTabs, { }); const StyledEditableTabs = styled(StyledTabs)` - ${({ theme }) => ` + ${({ theme, contentStyle }) => ` .ant-tabs-content-holder { background: ${theme.colorBgContainer}; + ${contentStyle} } & > .ant-tabs-nav { diff --git a/superset-frontend/src/SqlLab/components/SqlEditor/index.tsx b/superset-frontend/src/SqlLab/components/SqlEditor/index.tsx index 547f2850e4..4585ed09cd 100644 --- a/superset-frontend/src/SqlLab/components/SqlEditor/index.tsx +++ b/superset-frontend/src/SqlLab/components/SqlEditor/index.tsx @@ -185,6 +185,7 @@ const StyledSqlEditor = styled.div` .queryPane { padding: ${theme.sizeUnit * 2}px; + padding-left: 0px; + .ant-splitter-bar .ant-splitter-bar-dragger { &::before { background: transparent; diff --git a/superset-frontend/src/SqlLab/components/TablePreview/index.tsx b/superset-frontend/src/SqlLab/components/TablePreview/index.tsx index 6973adc19f..d7b0c364c8 100644 --- a/superset-frontend/src/SqlLab/components/TablePreview/index.tsx +++ b/superset-frontend/src/SqlLab/components/TablePreview/index.tsx @@ -25,15 +25,15 @@ import { getExtensionsRegistry, styled, t, + useTheme, } from '@superset-ui/core'; import { SafeMarkdown, Alert, Breadcrumb, - Button, Card, - Dropdown, Skeleton, + Flex, } from '@superset-ui/core/components'; import AutoSizer from 'react-virtualized-auto-sizer'; import { Icons } from '@superset-ui/core/components/Icons'; @@ -47,7 +47,7 @@ import { useTableMetadataQuery, } from 'src/hooks/apiResources'; import { runTablePreviewQuery } from 'src/SqlLab/actions/sqlLab'; -import { Menu } from '@superset-ui/core/components/Menu'; +import { ActionButton } from 'src/components/ActionButton'; import ResultSet from '../ResultSet'; import ShowSQL from '../ShowSQL'; @@ -68,23 +68,6 @@ const TABS_KEYS = { INDEXES: 'indexes', SAMPLE: 'sample', }; -const MENUS = [ - { - key: 'refresh-table', - label: t('Refresh table schema'), - icon: <Icons.SyncOutlined iconSize="s" aria-hidden />, - }, - { - key: 'copy-select-statement', - label: t('Copy SELECT statement'), - icon: <Icons.CopyOutlined iconSize="s" aria-hidden />, - }, - { - key: 'show-create-view-statement', - label: t('Show CREATE VIEW statement'), - icon: <Icons.EyeOutlined iconSize="s" aria-hidden />, - }, -]; const TAB_HEADER_HEIGHT = 80; const PREVIEW_QUERY_LIMIT = 100; @@ -96,6 +79,8 @@ const Title = styled.div` column-gap: ${theme.sizeUnit}px; font-size: ${theme.fontSizeLG}px; font-weight: ${theme.fontWeightStrong}; + padding-top: ${theme.sizeUnit * 2}px; + padding-left: ${theme.sizeUnit * 4}px; `} `; const renderWell = (partitions: TableMetaData['partitions']) => { @@ -133,6 +118,7 @@ const renderWell = (partitions: TableMetaData['partitions']) => { const TablePreview: FC<Props> = ({ dbId, catalog, schema, tableName }) => { const dispatch = useDispatch(); + const theme = useTheme(); const [databaseName, backend, disableDataPreview] = useSelector< SqlLabRootState, string[] @@ -240,16 +226,37 @@ const TablePreview: FC<Props> = ({ dbId, catalog, schema, tableName }) => { ], ); - const dropdownMenu = useMemo(() => { - let menus = [...MENUS]; - if (!tableData.selectStar) { - menus = menus.filter(({ key }) => key !== 'copy-select-statement'); - } - if (!tableData.view) { - menus = menus.filter(({ key }) => key !== 'show-create-view-statement'); - } - return menus; - }, [tableData.view, tableData.selectStar]); + const titleActions = () => ( + <Flex + align="center" + css={css` + padding-left: ${theme.sizeUnit * 4}px; + `} + > + <ActionButton + label="Refresh table schema" + tooltip={t('Refresh table schema')} + icon="SyncOutlined" + onClick={refreshTableMetadata} + /> + {tableData.selectStar && ( + <ActionButton + label="Copy SELECT statement" + icon="CopyOutlined" + tooltip={t('Copy SELECT statement')} + onClick={() => copyStatementActionRef.current?.click()} + /> + )} + {tableData.view && ( + <ActionButton + label="Show CREATE VIEW statement" + icon="EyeOutlined" + tooltip={t('Show CREATE VIEW statement')} + onClick={() => showViewStatementActionRef.current?.click()} + /> + )} + </Flex> + ); if (isMetadataLoading) { return <Skeleton active />; @@ -282,7 +289,12 @@ const TablePreview: FC<Props> = ({ dbId, catalog, schema, tableName }) => { flex-direction: column; `} > - <Breadcrumb separator=">"> + <Breadcrumb + separator=">" + css={css` + padding-left: ${theme.sizeUnit * 4}px; + `} + > <Breadcrumb.Item>{backend}</Breadcrumb.Item> <Breadcrumb.Item>{databaseName}</Breadcrumb.Item> {catalog && <Breadcrumb.Item>{catalog}</Breadcrumb.Item>} @@ -315,33 +327,7 @@ const TablePreview: FC<Props> = ({ dbId, catalog, schema, tableName }) => { <Title> <Icons.InsertRowAboveOutlined iconSize="l" /> {tableName} - <Dropdown - popupRender={() => ( - <Menu - onClick={({ key }) => { - if (key === 'refresh-table') { - refreshTableMetadata(); - } - if (key === 'copy-select-statement') { - copyStatementActionRef.current?.click(); - } - if (key === 'show-create-view-statement') { - showViewStatementActionRef.current?.click(); - } - }} - items={dropdownMenu} - /> - )} - trigger={['click']} - > - <Button buttonSize="xsmall" buttonStyle="link"> - <Icons.DownSquareOutlined - iconSize="m" - style={{ marginTop: 2, marginLeft: 4 }} - aria-label={t('Table actions')} - /> - </Button> - </Dropdown> + {titleActions()} </Title> {isMetadataRefreshing ? ( <Skeleton active /> @@ -440,7 +426,11 @@ const TablePreview: FC<Props> = ({ dbId, catalog, schema, tableName }) => { css={css` height: ${height}px; `} + tabBarStyle={{ paddingLeft: theme.sizeUnit * 4 }} items={tabItems} + contentStyle={css` + padding-left: ${theme.sizeUnit * 4}px; + `} /> ); }} diff --git a/superset-frontend/src/components/ActionButton/index.tsx b/superset-frontend/src/components/ActionButton/index.tsx new file mode 100644 index 0000000000..64685add0c --- /dev/null +++ b/superset-frontend/src/components/ActionButton/index.tsx @@ -0,0 +1,75 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import type { ReactElement } from 'react'; +import { + Icons, + type IconNameType, + Tooltip, + type TooltipPlacement, +} from '@superset-ui/core/components'; +import { css, useTheme } from '@superset-ui/core'; + +export interface ActionProps { + label: string; + tooltip?: string | ReactElement; + placement?: TooltipPlacement; + icon: string; + onClick: () => void; +} + +export const ActionButton = ({ + label, + tooltip, + placement, + icon, + onClick, +}: ActionProps) => { + const theme = useTheme(); + const ActionIcon = Icons[icon as IconNameType]; + const actionButton = ( + <span + role="button" + tabIndex={0} + css={css` + cursor: pointer; + color: ${theme.colorIcon}; + margin-right: ${theme.sizeUnit}px; + &:hover { + path { + fill: ${theme.colorPrimary}; + } + } + `} + className="action-button" + data-test={label} + onClick={onClick} + > + <ActionIcon iconSize="l" /> + </span> + ); + + return tooltip ? ( + <Tooltip id={`${label}-tooltip`} title={tooltip} placement={placement}> + {actionButton} + </Tooltip> + ) : ( + actionButton + ); +}; diff --git a/superset-frontend/src/components/ListView/ActionsBar.tsx b/superset-frontend/src/components/ListView/ActionsBar.tsx index 7a4f57b7fc..06b76d2e31 100644 --- a/superset-frontend/src/components/ListView/ActionsBar.tsx +++ b/superset-frontend/src/components/ListView/ActionsBar.tsx @@ -37,12 +37,8 @@ */ import { ReactElement } from 'react'; import { styled } from '@superset-ui/core'; -import { - Icons, - IconNameType, - Tooltip, - type TooltipPlacement, -} from '@superset-ui/core/components'; +import { type TooltipPlacement } from '@superset-ui/core/components'; +import { ActionButton } from '../ActionButton'; export type ActionProps = { label: string; @@ -59,49 +55,14 @@ interface ActionsBarProps { const StyledActions = styled.span` white-space: nowrap; min-width: 100px; - .action-button { - cursor: pointer; - color: ${({ theme }) => theme.colorIcon}; - margin-right: ${({ theme }) => theme.sizeUnit}px; - &:hover { - path { - fill: ${({ theme }) => theme.colorPrimary}; - } - } - } `; export function ActionsBar({ actions }: ActionsBarProps) { return ( <StyledActions className="actions"> - {actions.map((action, index) => { - const ActionIcon = Icons[action.icon as IconNameType]; - const actionButton = ( - <span - role="button" - tabIndex={0} - style={{ cursor: 'pointer' }} - className="action-button" - data-test={action.label} - onClick={action.onClick} - key={action.tooltip ? undefined : index} - > - <ActionIcon iconSize="l" /> - </span> - ); - return action.tooltip ? ( - <Tooltip - id={`${action.label}-tooltip`} - title={action.tooltip} - placement={action.placement} - key={index} - > - {actionButton} - </Tooltip> - ) : ( - actionButton - ); - })} + {actions.map((action, index) => ( + <ActionButton key={action.tooltip ? undefined : index} {...action} /> + ))} </StyledActions> ); }
