mirror of
https://github.com/apache/superset.git
synced 2026-04-24 18:44:53 +00:00
chore(sqllab): Remove table metadata from state (#24371)
This commit is contained in:
@@ -1,161 +0,0 @@
|
||||
/**
|
||||
* 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 React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import { Provider } from 'react-redux';
|
||||
import configureStore from 'redux-mock-store';
|
||||
import thunk from 'redux-thunk';
|
||||
import { supersetTheme, ThemeProvider } from '@superset-ui/core';
|
||||
import Collapse from 'src/components/Collapse';
|
||||
import { IconTooltip } from 'src/components/IconTooltip';
|
||||
import TableElement from 'src/SqlLab/components/TableElement';
|
||||
import ColumnElement from 'src/SqlLab/components/ColumnElement';
|
||||
import waitForComponentToPaint from 'spec/helpers/waitForComponentToPaint';
|
||||
import { initialState, table } from 'src/SqlLab/fixtures';
|
||||
|
||||
const middlewares = [thunk];
|
||||
const mockStore = configureStore(middlewares);
|
||||
|
||||
describe('TableElement', () => {
|
||||
const store = mockStore(initialState);
|
||||
const mockedProps = {
|
||||
table,
|
||||
timeout: 0,
|
||||
};
|
||||
it('renders', () => {
|
||||
expect(React.isValidElement(<TableElement />)).toBe(true);
|
||||
});
|
||||
it('renders with props', () => {
|
||||
expect(React.isValidElement(<TableElement {...mockedProps} />)).toBe(true);
|
||||
});
|
||||
it('has 5 IconTooltip elements', () => {
|
||||
const wrapper = mount(
|
||||
<Provider store={store}>
|
||||
<TableElement {...mockedProps} />
|
||||
</Provider>,
|
||||
{
|
||||
wrappingComponent: ThemeProvider,
|
||||
wrappingComponentProps: {
|
||||
theme: supersetTheme,
|
||||
},
|
||||
},
|
||||
);
|
||||
expect(wrapper.find(IconTooltip)).toHaveLength(4);
|
||||
});
|
||||
it('has 14 columns', () => {
|
||||
const wrapper = mount(
|
||||
<Provider store={store}>
|
||||
<TableElement {...mockedProps} />
|
||||
</Provider>,
|
||||
{
|
||||
wrappingComponent: ThemeProvider,
|
||||
wrappingComponentProps: {
|
||||
theme: supersetTheme,
|
||||
},
|
||||
},
|
||||
);
|
||||
expect(wrapper.find(ColumnElement)).toHaveLength(14);
|
||||
});
|
||||
it('mounts', () => {
|
||||
const wrapper = mount(
|
||||
<Provider store={store}>
|
||||
<TableElement {...mockedProps} />
|
||||
</Provider>,
|
||||
{
|
||||
wrappingComponent: ThemeProvider,
|
||||
wrappingComponentProps: {
|
||||
theme: supersetTheme,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
expect(wrapper.find(TableElement)).toHaveLength(1);
|
||||
});
|
||||
it('fades table', async () => {
|
||||
const wrapper = mount(
|
||||
<Provider store={store}>
|
||||
<TableElement {...mockedProps} />
|
||||
</Provider>,
|
||||
{
|
||||
wrappingComponent: ThemeProvider,
|
||||
wrappingComponentProps: {
|
||||
theme: supersetTheme,
|
||||
},
|
||||
},
|
||||
);
|
||||
expect(wrapper.find('[data-test="fade"]').first().props().hovered).toBe(
|
||||
false,
|
||||
);
|
||||
wrapper.find('.header-container').hostNodes().simulate('mouseEnter');
|
||||
await waitForComponentToPaint(wrapper, 300);
|
||||
expect(wrapper.find('[data-test="fade"]').first().props().hovered).toBe(
|
||||
true,
|
||||
);
|
||||
});
|
||||
it('sorts columns', () => {
|
||||
const wrapper = mount(
|
||||
<Provider store={store}>
|
||||
<Collapse>
|
||||
<TableElement {...mockedProps} />
|
||||
</Collapse>
|
||||
</Provider>,
|
||||
{
|
||||
wrappingComponent: ThemeProvider,
|
||||
wrappingComponentProps: {
|
||||
theme: supersetTheme,
|
||||
},
|
||||
},
|
||||
);
|
||||
expect(
|
||||
wrapper.find(IconTooltip).at(1).hasClass('fa-sort-alpha-asc'),
|
||||
).toEqual(true);
|
||||
expect(
|
||||
wrapper.find(IconTooltip).at(1).hasClass('fa-sort-numeric-asc'),
|
||||
).toEqual(false);
|
||||
wrapper.find('.header-container').hostNodes().simulate('click');
|
||||
expect(wrapper.find(ColumnElement).first().props().column.name).toBe('id');
|
||||
wrapper.find('.header-container').simulate('mouseEnter');
|
||||
wrapper.find('.sort-cols').hostNodes().simulate('click');
|
||||
expect(
|
||||
wrapper.find(IconTooltip).at(1).hasClass('fa-sort-numeric-asc'),
|
||||
).toEqual(true);
|
||||
expect(
|
||||
wrapper.find(IconTooltip).at(1).hasClass('fa-sort-alpha-asc'),
|
||||
).toEqual(false);
|
||||
expect(wrapper.find(ColumnElement).first().props().column.name).toBe(
|
||||
'active',
|
||||
);
|
||||
});
|
||||
it('removes the table', () => {
|
||||
const wrapper = mount(
|
||||
<Provider store={store}>
|
||||
<TableElement {...mockedProps} />
|
||||
</Provider>,
|
||||
{
|
||||
wrappingComponent: ThemeProvider,
|
||||
wrappingComponentProps: {
|
||||
theme: supersetTheme,
|
||||
},
|
||||
},
|
||||
);
|
||||
wrapper.find('.table-remove').hostNodes().simulate('click');
|
||||
expect(store.getActions()).toHaveLength(1);
|
||||
expect(store.getActions()[0].type).toEqual('REMOVE_DATA_PREVIEW');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,177 @@
|
||||
/**
|
||||
* 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 React from 'react';
|
||||
import fetchMock from 'fetch-mock';
|
||||
import * as featureFlags from 'src/featureFlags';
|
||||
import { FeatureFlag } from '@superset-ui/core';
|
||||
import TableElement, { Column } from 'src/SqlLab/components/TableElement';
|
||||
import { table, initialState } from 'src/SqlLab/fixtures';
|
||||
import { render, waitFor, fireEvent } from 'spec/helpers/testing-library';
|
||||
|
||||
jest.mock('src/components/Loading', () => () => (
|
||||
<div data-test="mock-loading" />
|
||||
));
|
||||
jest.mock('src/components/IconTooltip', () => ({
|
||||
IconTooltip: ({
|
||||
onClick,
|
||||
tooltip,
|
||||
}: {
|
||||
onClick: () => void;
|
||||
tooltip: string;
|
||||
}) => (
|
||||
<button type="button" data-test="mock-icon-tooltip" onClick={onClick}>
|
||||
{tooltip}
|
||||
</button>
|
||||
),
|
||||
}));
|
||||
jest.mock(
|
||||
'src/SqlLab/components/ColumnElement',
|
||||
() =>
|
||||
({ column }: { column: Column }) =>
|
||||
<div data-test="mock-column-element">{column.name}</div>,
|
||||
);
|
||||
const getTableMetadataEndpoint = 'glob:**/api/v1/database/*/table/*/*/';
|
||||
const getExtraTableMetadataEndpoint =
|
||||
'glob:**/api/v1/database/*/table_extra/*/*/';
|
||||
const updateTableSchemaEndpoint = 'glob:*/tableschemaview/*/expanded';
|
||||
|
||||
beforeEach(() => {
|
||||
fetchMock.get(getTableMetadataEndpoint, table);
|
||||
fetchMock.get(getExtraTableMetadataEndpoint, {});
|
||||
fetchMock.post(updateTableSchemaEndpoint, {});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fetchMock.reset();
|
||||
});
|
||||
|
||||
const mockedProps = {
|
||||
table: {
|
||||
...table,
|
||||
initialized: true,
|
||||
},
|
||||
};
|
||||
|
||||
test('renders', () => {
|
||||
expect(React.isValidElement(<TableElement table={table} />)).toBe(true);
|
||||
});
|
||||
|
||||
test('renders with props', () => {
|
||||
expect(React.isValidElement(<TableElement {...mockedProps} />)).toBe(true);
|
||||
});
|
||||
|
||||
test('has 4 IconTooltip elements', async () => {
|
||||
const { getAllByTestId } = render(<TableElement {...mockedProps} />, {
|
||||
useRedux: true,
|
||||
initialState,
|
||||
});
|
||||
await waitFor(() =>
|
||||
expect(getAllByTestId('mock-icon-tooltip')).toHaveLength(4),
|
||||
);
|
||||
});
|
||||
|
||||
test('has 14 columns', async () => {
|
||||
const { getAllByTestId } = render(<TableElement {...mockedProps} />, {
|
||||
useRedux: true,
|
||||
initialState,
|
||||
});
|
||||
await waitFor(() =>
|
||||
expect(getAllByTestId('mock-column-element')).toHaveLength(14),
|
||||
);
|
||||
});
|
||||
|
||||
test('fades table', async () => {
|
||||
const { getAllByTestId } = render(<TableElement {...mockedProps} />, {
|
||||
useRedux: true,
|
||||
initialState,
|
||||
});
|
||||
await waitFor(() =>
|
||||
expect(getAllByTestId('mock-icon-tooltip')).toHaveLength(4),
|
||||
);
|
||||
const style = window.getComputedStyle(getAllByTestId('fade')[0]);
|
||||
expect(style.opacity).toBe('0');
|
||||
fireEvent.mouseEnter(getAllByTestId('table-element-header-container')[0]);
|
||||
await waitFor(() =>
|
||||
expect(window.getComputedStyle(getAllByTestId('fade')[0]).opacity).toBe(
|
||||
'1',
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
test('sorts columns', async () => {
|
||||
const { getAllByTestId, getByText } = render(
|
||||
<TableElement {...mockedProps} />,
|
||||
{
|
||||
useRedux: true,
|
||||
initialState,
|
||||
},
|
||||
);
|
||||
await waitFor(() =>
|
||||
expect(getAllByTestId('mock-icon-tooltip')).toHaveLength(4),
|
||||
);
|
||||
expect(
|
||||
getAllByTestId('mock-column-element').map(el => el.textContent),
|
||||
).toEqual(table.columns.map(col => col.name));
|
||||
fireEvent.click(getByText('Sort columns alphabetically'));
|
||||
const sorted = [...table.columns.map(col => col.name)].sort();
|
||||
expect(
|
||||
getAllByTestId('mock-column-element').map(el => el.textContent),
|
||||
).toEqual(sorted);
|
||||
expect(getAllByTestId('mock-column-element')[0]).toHaveTextContent('active');
|
||||
});
|
||||
|
||||
test('removes the table', async () => {
|
||||
const updateTableSchemaEndpoint = 'glob:*/tableschemaview/*';
|
||||
fetchMock.delete(updateTableSchemaEndpoint, {});
|
||||
const isFeatureEnabledMock = jest
|
||||
.spyOn(featureFlags, 'isFeatureEnabled')
|
||||
.mockImplementation(
|
||||
featureFlag => featureFlag === FeatureFlag.SQLLAB_BACKEND_PERSISTENCE,
|
||||
);
|
||||
const { getAllByTestId, getByText } = render(
|
||||
<TableElement {...mockedProps} />,
|
||||
{
|
||||
useRedux: true,
|
||||
initialState,
|
||||
},
|
||||
);
|
||||
await waitFor(() =>
|
||||
expect(getAllByTestId('mock-icon-tooltip')).toHaveLength(4),
|
||||
);
|
||||
expect(fetchMock.calls(updateTableSchemaEndpoint)).toHaveLength(0);
|
||||
fireEvent.click(getByText('Remove table preview'));
|
||||
await waitFor(() =>
|
||||
expect(fetchMock.calls(updateTableSchemaEndpoint)).toHaveLength(1),
|
||||
);
|
||||
isFeatureEnabledMock.mockClear();
|
||||
});
|
||||
|
||||
test('fetches table metadata when expanded', async () => {
|
||||
render(<TableElement {...mockedProps} />, {
|
||||
useRedux: true,
|
||||
initialState,
|
||||
});
|
||||
expect(fetchMock.calls(getTableMetadataEndpoint)).toHaveLength(0);
|
||||
expect(fetchMock.calls(getExtraTableMetadataEndpoint)).toHaveLength(0);
|
||||
await waitFor(() =>
|
||||
expect(fetchMock.calls(getTableMetadataEndpoint)).toHaveLength(1),
|
||||
);
|
||||
expect(fetchMock.calls(updateTableSchemaEndpoint)).toHaveLength(0);
|
||||
expect(fetchMock.calls(getExtraTableMetadataEndpoint)).toHaveLength(1);
|
||||
});
|
||||
@@ -16,7 +16,7 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import React, { useState, useRef } from 'react';
|
||||
import React, { useState, useRef, useEffect } from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import Collapse from 'src/components/Collapse';
|
||||
import Card from 'src/components/Card';
|
||||
@@ -24,16 +24,26 @@ import ButtonGroup from 'src/components/ButtonGroup';
|
||||
import { css, t, styled } from '@superset-ui/core';
|
||||
import { debounce } from 'lodash';
|
||||
|
||||
import { removeDataPreview, removeTables } from 'src/SqlLab/actions/sqlLab';
|
||||
import {
|
||||
removeDataPreview,
|
||||
removeTables,
|
||||
addDangerToast,
|
||||
syncTable,
|
||||
} from 'src/SqlLab/actions/sqlLab';
|
||||
import {
|
||||
useTableExtendedMetadataQuery,
|
||||
useTableMetadataQuery,
|
||||
} from 'src/hooks/apiResources';
|
||||
import { Tooltip } from 'src/components/Tooltip';
|
||||
import CopyToClipboard from 'src/components/CopyToClipboard';
|
||||
import { IconTooltip } from 'src/components/IconTooltip';
|
||||
import ModalTrigger from 'src/components/ModalTrigger';
|
||||
import Loading from 'src/components/Loading';
|
||||
import useEffectEvent from 'src/hooks/useEffectEvent';
|
||||
import ColumnElement, { ColumnKeyTypeType } from '../ColumnElement';
|
||||
import ShowSQL from '../ShowSQL';
|
||||
|
||||
interface Column {
|
||||
export interface Column {
|
||||
name: string;
|
||||
keys?: { type: ColumnKeyTypeType }[];
|
||||
type: string;
|
||||
@@ -41,18 +51,12 @@ interface Column {
|
||||
|
||||
export interface Table {
|
||||
id: string;
|
||||
dbId: number;
|
||||
schema: string;
|
||||
name: string;
|
||||
partitions?: {
|
||||
partitionQuery: string;
|
||||
latest: object[];
|
||||
};
|
||||
metadata?: Record<string, string>;
|
||||
indexes?: object[];
|
||||
selectStar?: string;
|
||||
view?: string;
|
||||
isMetadataLoading: boolean;
|
||||
isExtraMetadataLoading: boolean;
|
||||
columns: Column[];
|
||||
dataPreviewQueryId?: string | null;
|
||||
expanded?: boolean;
|
||||
initialized?: boolean;
|
||||
}
|
||||
|
||||
export interface TableElementProps {
|
||||
@@ -106,7 +110,61 @@ const StyledCollapsePanel = styled(Collapse.Panel)`
|
||||
`;
|
||||
|
||||
const TableElement = ({ table, ...props }: TableElementProps) => {
|
||||
const { dbId, schema, name, expanded } = table;
|
||||
const dispatch = useDispatch();
|
||||
const {
|
||||
data: tableMetadata,
|
||||
isSuccess: isMetadataSuccess,
|
||||
isLoading: isMetadataLoading,
|
||||
isError: hasMetadataError,
|
||||
} = useTableMetadataQuery(
|
||||
{
|
||||
dbId,
|
||||
schema,
|
||||
table: name,
|
||||
},
|
||||
{ skip: !expanded },
|
||||
);
|
||||
const {
|
||||
data: tableExtendedMetadata,
|
||||
isSuccess: isExtraMetadataSuccess,
|
||||
isLoading: isExtraMetadataLoading,
|
||||
isError: hasExtendedMetadataError,
|
||||
} = useTableExtendedMetadataQuery(
|
||||
{
|
||||
dbId,
|
||||
schema,
|
||||
table: name,
|
||||
},
|
||||
{ skip: !expanded },
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (hasMetadataError || hasExtendedMetadataError) {
|
||||
dispatch(
|
||||
addDangerToast(t('An error occurred while fetching table metadata')),
|
||||
);
|
||||
}
|
||||
}, [hasMetadataError, hasExtendedMetadataError, dispatch]);
|
||||
|
||||
const tableData = {
|
||||
...tableMetadata,
|
||||
...tableExtendedMetadata,
|
||||
};
|
||||
|
||||
// TODO: migrate syncTable logic by SIP-93
|
||||
const syncTableMetadata = useEffectEvent(() => {
|
||||
const { initialized } = table;
|
||||
if (!initialized) {
|
||||
dispatch(syncTable(table, tableData));
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (isMetadataSuccess && isExtraMetadataSuccess) {
|
||||
syncTableMetadata();
|
||||
}
|
||||
}, [isMetadataSuccess, isExtraMetadataSuccess, syncTableMetadata]);
|
||||
|
||||
const [sortColumns, setSortColumns] = useState(false);
|
||||
const [hovered, setHovered] = useState(false);
|
||||
@@ -128,11 +186,11 @@ const TableElement = ({ table, ...props }: TableElementProps) => {
|
||||
const renderWell = () => {
|
||||
let partitions;
|
||||
let metadata;
|
||||
if (table.partitions) {
|
||||
if (tableData.partitions) {
|
||||
let partitionQuery;
|
||||
let partitionClipBoard;
|
||||
if (table.partitions.partitionQuery) {
|
||||
({ partitionQuery } = table.partitions);
|
||||
if (tableData.partitions.partitionQuery) {
|
||||
({ partitionQuery } = tableData.partitions);
|
||||
const tt = t('Copy partition query to clipboard');
|
||||
partitionClipBoard = (
|
||||
<CopyToClipboard
|
||||
@@ -143,7 +201,7 @@ const TableElement = ({ table, ...props }: TableElementProps) => {
|
||||
/>
|
||||
);
|
||||
}
|
||||
const latest = Object.entries(table.partitions?.latest || [])
|
||||
const latest = Object.entries(tableData.partitions?.latest || [])
|
||||
.map(([key, value]) => `${key}=${value}`)
|
||||
.join('/');
|
||||
|
||||
@@ -157,8 +215,8 @@ const TableElement = ({ table, ...props }: TableElementProps) => {
|
||||
);
|
||||
}
|
||||
|
||||
if (table.metadata) {
|
||||
metadata = Object.entries(table.metadata).map(([key, value]) => (
|
||||
if (tableData.metadata) {
|
||||
metadata = Object.entries(tableData.metadata).map(([key, value]) => (
|
||||
<div>
|
||||
<small>
|
||||
<strong>{key}:</strong> {value}
|
||||
@@ -183,17 +241,17 @@ const TableElement = ({ table, ...props }: TableElementProps) => {
|
||||
const renderControls = () => {
|
||||
let keyLink;
|
||||
const KEYS_FOR_TABLE_TEXT = t('Keys for table');
|
||||
if (table?.indexes?.length) {
|
||||
if (tableData?.indexes?.length) {
|
||||
keyLink = (
|
||||
<ModalTrigger
|
||||
modalTitle={`${KEYS_FOR_TABLE_TEXT} ${table.name}`}
|
||||
modalBody={table.indexes.map((ix, i) => (
|
||||
modalTitle={`${KEYS_FOR_TABLE_TEXT} ${name}`}
|
||||
modalBody={tableData.indexes.map((ix, i) => (
|
||||
<pre key={i}>{JSON.stringify(ix, null, ' ')}</pre>
|
||||
))}
|
||||
triggerNode={
|
||||
<IconTooltip
|
||||
className="fa fa-key pull-left m-l-2"
|
||||
tooltip={t('View keys & indexes (%s)', table.indexes.length)}
|
||||
tooltip={t('View keys & indexes (%s)', tableData.indexes.length)}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
@@ -214,7 +272,7 @@ const TableElement = ({ table, ...props }: TableElementProps) => {
|
||||
: t('Sort columns alphabetically')
|
||||
}
|
||||
/>
|
||||
{table.selectStar && (
|
||||
{tableData.selectStar && (
|
||||
<CopyToClipboard
|
||||
copyNode={
|
||||
<IconTooltip
|
||||
@@ -224,13 +282,13 @@ const TableElement = ({ table, ...props }: TableElementProps) => {
|
||||
<i aria-hidden className="fa fa-clipboard pull-left m-l-2" />
|
||||
</IconTooltip>
|
||||
}
|
||||
text={table.selectStar}
|
||||
text={tableData.selectStar}
|
||||
shouldShowText={false}
|
||||
/>
|
||||
)}
|
||||
{table.view && (
|
||||
{tableData.view && (
|
||||
<ShowSQL
|
||||
sql={table.view}
|
||||
sql={tableData.view}
|
||||
tooltipText={t('Show CREATE VIEW statement')}
|
||||
title={t('CREATE VIEW statement')}
|
||||
/>
|
||||
@@ -253,6 +311,7 @@ const TableElement = ({ table, ...props }: TableElementProps) => {
|
||||
|
||||
return (
|
||||
<div
|
||||
data-test="table-element-header-container"
|
||||
className="clearfix header-container"
|
||||
onMouseEnter={() => setHover(true)}
|
||||
onMouseLeave={() => setHover(false)}
|
||||
@@ -260,7 +319,7 @@ const TableElement = ({ table, ...props }: TableElementProps) => {
|
||||
<Tooltip
|
||||
id="copy-to-clipboard-tooltip"
|
||||
style={{ cursor: 'pointer' }}
|
||||
title={table.name}
|
||||
title={name}
|
||||
trigger={trigger}
|
||||
>
|
||||
<StyledSpan
|
||||
@@ -268,12 +327,12 @@ const TableElement = ({ table, ...props }: TableElementProps) => {
|
||||
ref={tableNameRef}
|
||||
className="table-name"
|
||||
>
|
||||
<strong>{table.name}</strong>
|
||||
<strong>{name}</strong>
|
||||
</StyledSpan>
|
||||
</Tooltip>
|
||||
|
||||
<div className="pull-right header-right-side">
|
||||
{table.isMetadataLoading || table.isExtraMetadataLoading ? (
|
||||
{isMetadataLoading || isExtraMetadataLoading ? (
|
||||
<Loading position="inline" />
|
||||
) : (
|
||||
<Fade
|
||||
@@ -291,8 +350,8 @@ const TableElement = ({ table, ...props }: TableElementProps) => {
|
||||
|
||||
const renderBody = () => {
|
||||
let cols;
|
||||
if (table.columns) {
|
||||
cols = table.columns.slice();
|
||||
if (tableData.columns) {
|
||||
cols = tableData.columns.slice();
|
||||
if (sortColumns) {
|
||||
cols.sort((a: Column, b: Column) => {
|
||||
const colA = a.name.toUpperCase();
|
||||
|
||||
Reference in New Issue
Block a user