diff --git a/superset-frontend/packages/superset-core/src/theme/types.ts b/superset-frontend/packages/superset-core/src/theme/types.ts index bc1f9ac8409..f72ca7388fa 100644 --- a/superset-frontend/packages/superset-core/src/theme/types.ts +++ b/superset-frontend/packages/superset-core/src/theme/types.ts @@ -258,6 +258,16 @@ export interface SupersetSpecificTokens { dashboardTileBorder?: string; dashboardTileBorderRadius?: number; dashboardTileBoxShadow?: string; + + /** + * Results grid customization tokens. + * Control the appearance of AG Grid-backed result tables (e.g. SQL Lab). + */ + resultsGridRowHeight?: number; + resultsGridHeaderFontWeight?: number; + resultsGridHeaderFontSize?: number; + resultsGridBorderRadius?: number; + resultsGridNoStriping?: boolean; } /** diff --git a/superset-frontend/src/SqlLab/components/ResultSet/buildResultsGridThemeOverrides.test.ts b/superset-frontend/src/SqlLab/components/ResultSet/buildResultsGridThemeOverrides.test.ts new file mode 100644 index 00000000000..67f59dce045 --- /dev/null +++ b/superset-frontend/src/SqlLab/components/ResultSet/buildResultsGridThemeOverrides.test.ts @@ -0,0 +1,94 @@ +/** + * 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 { SupersetTheme } from '@apache-superset/core/theme'; +import { buildResultsGridThemeOverrides } from './buildResultsGridThemeOverrides'; + +test('returns undefined when no resultsGrid tokens are set', () => { + const theme = {} as SupersetTheme; + expect(buildResultsGridThemeOverrides(theme)).toBeUndefined(); +}); + +test('maps resultsGridHeaderFontSize to headerFontSize', () => { + const theme = { resultsGridHeaderFontSize: 14 } as SupersetTheme; + expect(buildResultsGridThemeOverrides(theme)).toEqual({ + headerFontSize: 14, + }); +}); + +test('maps resultsGridHeaderFontWeight to headerFontWeight', () => { + const theme = { resultsGridHeaderFontWeight: 700 } as SupersetTheme; + expect(buildResultsGridThemeOverrides(theme)).toEqual({ + headerFontWeight: 700, + }); +}); + +test('maps resultsGridRowHeight to both rowHeight and headerHeight', () => { + const theme = { resultsGridRowHeight: 40 } as SupersetTheme; + expect(buildResultsGridThemeOverrides(theme)).toEqual({ + rowHeight: 40, + headerHeight: 40, + }); +}); + +test('maps resultsGridBorderRadius to both borderRadius and wrapperBorderRadius', () => { + const theme = { resultsGridBorderRadius: 8 } as SupersetTheme; + expect(buildResultsGridThemeOverrides(theme)).toEqual({ + borderRadius: 8, + wrapperBorderRadius: 8, + }); +}); + +test('maps resultsGridNoStriping to oddRowBackgroundColor transparent', () => { + const theme = { resultsGridNoStriping: true } as SupersetTheme; + expect(buildResultsGridThemeOverrides(theme)).toEqual({ + oddRowBackgroundColor: 'transparent', + }); +}); + +test('does not map resultsGridNoStriping when false', () => { + const theme = { resultsGridNoStriping: false } as SupersetTheme; + expect(buildResultsGridThemeOverrides(theme)).toBeUndefined(); +}); + +test('maps all tokens together when all are set', () => { + const theme = { + resultsGridHeaderFontSize: 12, + resultsGridHeaderFontWeight: 600, + resultsGridRowHeight: 36, + resultsGridBorderRadius: 4, + resultsGridNoStriping: true, + } as SupersetTheme; + expect(buildResultsGridThemeOverrides(theme)).toEqual({ + headerFontSize: 12, + headerFontWeight: 600, + rowHeight: 36, + headerHeight: 36, + borderRadius: 4, + wrapperBorderRadius: 4, + oddRowBackgroundColor: 'transparent', + }); +}); + +test('ignores non-number values for numeric tokens', () => { + const theme = { + resultsGridRowHeight: '40', + resultsGridHeaderFontSize: undefined, + } as unknown as SupersetTheme; + expect(buildResultsGridThemeOverrides(theme)).toBeUndefined(); +}); diff --git a/superset-frontend/src/SqlLab/components/ResultSet/buildResultsGridThemeOverrides.ts b/superset-frontend/src/SqlLab/components/ResultSet/buildResultsGridThemeOverrides.ts new file mode 100644 index 00000000000..528c6a8acd2 --- /dev/null +++ b/superset-frontend/src/SqlLab/components/ResultSet/buildResultsGridThemeOverrides.ts @@ -0,0 +1,46 @@ +/** + * 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 { SupersetTheme } from '@apache-superset/core/theme'; +import type { GridThemeOverrides } from 'src/components/GridTable/types'; + +export function buildResultsGridThemeOverrides( + theme: SupersetTheme, +): GridThemeOverrides | undefined { + const overrides: GridThemeOverrides = {}; + + if (typeof theme.resultsGridHeaderFontSize === 'number') { + overrides.headerFontSize = theme.resultsGridHeaderFontSize; + } + if (typeof theme.resultsGridHeaderFontWeight === 'number') { + overrides.headerFontWeight = theme.resultsGridHeaderFontWeight; + } + if (typeof theme.resultsGridRowHeight === 'number') { + overrides.rowHeight = theme.resultsGridRowHeight; + overrides.headerHeight = theme.resultsGridRowHeight; + } + if (typeof theme.resultsGridBorderRadius === 'number') { + overrides.borderRadius = theme.resultsGridBorderRadius; + overrides.wrapperBorderRadius = theme.resultsGridBorderRadius; + } + if (theme.resultsGridNoStriping === true) { + overrides.oddRowBackgroundColor = 'transparent'; + } + + return Object.keys(overrides).length > 0 ? overrides : undefined; +} diff --git a/superset-frontend/src/SqlLab/components/ResultSet/index.tsx b/superset-frontend/src/SqlLab/components/ResultSet/index.tsx index cb6ee6883ac..cfd8ea6832e 100644 --- a/superset-frontend/src/SqlLab/components/ResultSet/index.tsx +++ b/superset-frontend/src/SqlLab/components/ResultSet/index.tsx @@ -44,6 +44,8 @@ import { FilterableTable, ErrorMessageWithStackTrace, } from 'src/components'; +import type { GridThemeOverrides } from 'src/components/GridTable/types'; +import { buildResultsGridThemeOverrides } from './buildResultsGridThemeOverrides'; import { nanoid } from 'nanoid'; import { t } from '@apache-superset/core/translation'; import { @@ -214,6 +216,12 @@ const ResultSet = ({ extensionsRegistry.get('sqleditor.extension.resultTable') ?? FilterableTable; const theme = useTheme(); + + const resultsGridThemeOverrides = useMemo( + () => buildResultsGridThemeOverrides(theme), + [theme], + ); + const [searchText, setSearchText] = useState(''); const [cachedData, setCachedData] = useState[]>([]); const [showSaveDatasetModal, setShowSaveDatasetModal] = useState(false); @@ -708,6 +716,7 @@ const ResultSet = ({ filterText: searchText, expandedColumns, allowHTML, + themeOverrides: resultsGridThemeOverrides, }; return ( diff --git a/superset-frontend/src/SqlLab/components/TabbedSqlEditors/index.tsx b/superset-frontend/src/SqlLab/components/TabbedSqlEditors/index.tsx index 6df0ddea4a1..f386bd86aac 100644 --- a/superset-frontend/src/SqlLab/components/TabbedSqlEditors/index.tsx +++ b/superset-frontend/src/SqlLab/components/TabbedSqlEditors/index.tsx @@ -44,40 +44,40 @@ const StyledEditableTabs = styled(EditableTabs)` height: 100%; display: flex; flex-direction: column; - & .ant-tabs-nav::before { - border-color: ${({ theme }) => theme.colorBorder} !important; + && .ant-tabs-nav::before { + border-color: ${({ theme }) => theme.colorBorder}; } - & .ant-tabs-nav-add { - border-color: ${({ theme }) => theme.colorBorder} !important; + && .ant-tabs-nav-add { + border-color: ${({ theme }) => theme.colorBorder}; height: 34px; } - & .ant-tabs-nav-list { + && .ant-tabs-nav-list { align-items: end; padding-top: 1px; column-gap: ${({ theme }) => theme.sizeUnit}px; } - & .ant-tabs-tab-active { - border-left-color: ${({ theme }) => theme.colorPrimaryActive} !important; - border-top-color: ${({ theme }) => theme.colorPrimaryActive} !important; - border-right-color: ${({ theme }) => theme.colorPrimaryActive} !important; - box-shadow: 0 0 2px ${({ theme }) => theme.colorPrimaryActive} !important; + && .ant-tabs-tab-active { + border-left-color: ${({ theme }) => theme.colorPrimaryActive}; + border-top-color: ${({ theme }) => theme.colorPrimaryActive}; + border-right-color: ${({ theme }) => theme.colorPrimaryActive}; + box-shadow: 0 0 2px ${({ theme }) => theme.colorPrimaryActive}; border-top: 2px; } - & .ant-tabs-tab { - border-radius: 2px 2px 0px 0px !important; + && .ant-tabs-tab { + border-radius: 2px 2px 0px 0px; padding: ${({ theme }) => theme.sizeUnit}px - ${({ theme }) => theme.sizeUnit * 2}px !important; + ${({ theme }) => theme.sizeUnit * 2}px; & + .ant-tabs-nav-add { margin-right: ${({ theme }) => theme.sizeUnit * 4}px; } &:not(.ant-tabs-tab-active) { - border-color: ${({ theme }) => theme.colorBorder} !important; - box-shadow: inset 0 0 1px ${({ theme }) => theme.colorBorder} !important; + border-color: ${({ theme }) => theme.colorBorder}; + box-shadow: inset 0 0 1px ${({ theme }) => theme.colorBorder}; } } - & .ant-tabs-nav-add { - border-radius: 2px 2px 0px 0px !important; - min-height: auto !important; + && .ant-tabs-nav-add { + border-radius: 2px 2px 0px 0px; + min-height: auto; align-self: flex-end; } `; diff --git a/superset-frontend/src/components/FilterableTable/index.tsx b/superset-frontend/src/components/FilterableTable/index.tsx index d6db0915d9b..acb85a0c799 100644 --- a/superset-frontend/src/components/FilterableTable/index.tsx +++ b/superset-frontend/src/components/FilterableTable/index.tsx @@ -65,6 +65,7 @@ export const FilterableTable = ({ expandedColumns = [], allowHTML = true, striped, + themeOverrides, }: FilterableTableProps) => { const getCellContent = useCellContentParser({ columnKeys: orderedColumnKeys, @@ -131,6 +132,7 @@ export const FilterableTable = ({ striped={striped} enableActions columnReorderable + themeOverrides={themeOverrides} /> ); diff --git a/superset-frontend/src/components/FilterableTable/types.ts b/superset-frontend/src/components/FilterableTable/types.ts index 97292aedd45..98eab598132 100644 --- a/superset-frontend/src/components/FilterableTable/types.ts +++ b/superset-frontend/src/components/FilterableTable/types.ts @@ -16,6 +16,8 @@ * specific language governing permissions and limitations * under the License. */ +import type { GridThemeOverrides } from '../GridTable/types'; + export type CellDataType = string | number | null; export type Datum = Record; @@ -31,4 +33,5 @@ export interface FilterableTableProps { striped?: boolean; expandedColumns?: string[]; allowHTML?: boolean; + themeOverrides?: GridThemeOverrides; } diff --git a/superset-frontend/src/components/GridTable/index.tsx b/superset-frontend/src/components/GridTable/index.tsx index 83acd1fe815..b54f6b47545 100644 --- a/superset-frontend/src/components/GridTable/index.tsx +++ b/superset-frontend/src/components/GridTable/index.tsx @@ -46,6 +46,7 @@ export function GridTable({ enableActions, size = GridSize.Middle, striped, + themeOverrides, }: TableProps) { const theme = useTheme(); const isExternalFilterPresent = useCallback( @@ -138,18 +139,20 @@ export function GridTable({ [columnReorderable, enableActions, sortable], ); - const rowHeight = theme.sizeUnit * (size === GridSize.Middle ? 9 : 7); + const defaultRowHeight = theme.sizeUnit * (size === GridSize.Middle ? 9 : 7); + const rowHeight = themeOverrides?.rowHeight ?? defaultRowHeight; + const headerHeight = themeOverrides?.headerHeight ?? rowHeight; const gridOptions = useMemo( () => ({ enableCellTextSelection: true, ensureDomOrder: true, suppressFieldDotNotation: true, - headerHeight: rowHeight, + headerHeight, rowSelection: 'multiple', rowHeight, }), - [rowHeight], + [rowHeight, headerHeight], ); return ( @@ -200,6 +203,7 @@ export function GridTable({ components={gridComponents} gridOptions={gridOptions} onCellKeyDown={onKeyDown} + themeOverrides={themeOverrides} /> ); diff --git a/superset-frontend/src/components/GridTable/types.ts b/superset-frontend/src/components/GridTable/types.ts index 8dda781f6cf..3937014c14e 100644 --- a/superset-frontend/src/components/GridTable/types.ts +++ b/superset-frontend/src/components/GridTable/types.ts @@ -26,6 +26,16 @@ export type ColDef = { field: string; }; +export interface GridThemeOverrides { + headerFontSize?: number; + headerFontWeight?: number; + rowHeight?: number; + headerHeight?: number; + borderRadius?: number; + wrapperBorderRadius?: number; + oddRowBackgroundColor?: string; +} + export interface TableProps { /** * Data that will populate the each row and map to the column key. @@ -59,4 +69,6 @@ export interface TableProps { usePagination?: boolean; striped?: boolean; + + themeOverrides?: GridThemeOverrides; } diff --git a/superset-frontend/src/theme/utils/antdTokenNames.ts b/superset-frontend/src/theme/utils/antdTokenNames.ts index a3b1b9e01c9..4ca6f54fcf3 100644 --- a/superset-frontend/src/theme/utils/antdTokenNames.ts +++ b/superset-frontend/src/theme/utils/antdTokenNames.ts @@ -100,6 +100,13 @@ const SUPERSET_CUSTOM_TOKENS: Set = new Set([ 'dashboardTileBorder', 'dashboardTileBorderRadius', 'dashboardTileBoxShadow', + + // Results grid tokens + 'resultsGridRowHeight', + 'resultsGridHeaderFontWeight', + 'resultsGridHeaderFontSize', + 'resultsGridBorderRadius', + 'resultsGridNoStriping', ]); /**