mirror of https://github.com/apache/superset.git
Migrates Collapse component from Bootstrap to AntD (#12920)
This commit is contained in:
parent
e4a0233181
commit
9a05d6afe5
|
@ -27,8 +27,8 @@ import { supersetTheme, ThemeProvider } from '@superset-ui/core';
|
|||
import '../src/theme.ts';
|
||||
import './storybook.css';
|
||||
|
||||
const themeDecorator = storyFn => (
|
||||
<ThemeProvider theme={supersetTheme}>{storyFn()}</ThemeProvider>
|
||||
const themeDecorator = Story => (
|
||||
<ThemeProvider theme={supersetTheme}>{<Story />}</ThemeProvider>
|
||||
);
|
||||
|
||||
addDecorator(jsxDecorator);
|
||||
|
|
|
@ -21,12 +21,13 @@ import { mount, shallow } from 'enzyme';
|
|||
import { Provider } from 'react-redux';
|
||||
import configureStore from 'redux-mock-store';
|
||||
import { supersetTheme, ThemeProvider } from '@superset-ui/core';
|
||||
import Collapse from 'src/common/components/Collapse';
|
||||
|
||||
import { IconTooltip } from 'src/components/IconTooltip';
|
||||
import Fade from 'src/common/components/Fade';
|
||||
import TableElement from 'src/SqlLab/components/TableElement';
|
||||
import ColumnElement from 'src/SqlLab/components/ColumnElement';
|
||||
|
||||
import waitForComponentToPaint from 'spec/helpers/waitForComponentToPaint';
|
||||
import { mockedActions, table } from './fixtures';
|
||||
|
||||
describe('TableElement', () => {
|
||||
|
@ -43,9 +44,19 @@ describe('TableElement', () => {
|
|||
it('renders with props', () => {
|
||||
expect(React.isValidElement(<TableElement {...mockedProps} />)).toBe(true);
|
||||
});
|
||||
it('has 2 IconTooltip elements', () => {
|
||||
const wrapper = shallow(<TableElement {...mockedProps} />);
|
||||
expect(wrapper.find(IconTooltip)).toHaveLength(2);
|
||||
it('has 4 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 = shallow(<TableElement {...mockedProps} />);
|
||||
|
@ -64,20 +75,45 @@ describe('TableElement', () => {
|
|||
},
|
||||
);
|
||||
});
|
||||
it('fades table', () => {
|
||||
const wrapper = shallow(<TableElement {...mockedProps} />);
|
||||
expect(wrapper.state().hovered).toBe(false);
|
||||
it('fades table', async () => {
|
||||
const wrapper = mount(
|
||||
<Provider store={store}>
|
||||
<TableElement {...mockedProps} />
|
||||
</Provider>,
|
||||
{
|
||||
wrappingComponent: ThemeProvider,
|
||||
wrappingComponentProps: {
|
||||
theme: supersetTheme,
|
||||
},
|
||||
},
|
||||
);
|
||||
expect(wrapper.find(TableElement).state().hovered).toBe(false);
|
||||
expect(wrapper.find(Fade).props().hovered).toBe(false);
|
||||
wrapper.find('div.TableElement').simulate('mouseEnter');
|
||||
expect(wrapper.state().hovered).toBe(true);
|
||||
wrapper.find('.header-container').hostNodes().simulate('mouseEnter');
|
||||
await waitForComponentToPaint(wrapper, 300);
|
||||
expect(wrapper.find(TableElement).state().hovered).toBe(true);
|
||||
expect(wrapper.find(Fade).props().hovered).toBe(true);
|
||||
});
|
||||
it('sorts columns', () => {
|
||||
const wrapper = shallow(<TableElement {...mockedProps} />);
|
||||
expect(wrapper.state().sortColumns).toBe(false);
|
||||
const wrapper = mount(
|
||||
<Provider store={store}>
|
||||
<Collapse>
|
||||
<TableElement {...mockedProps} />
|
||||
</Collapse>
|
||||
</Provider>,
|
||||
{
|
||||
wrappingComponent: ThemeProvider,
|
||||
wrappingComponentProps: {
|
||||
theme: supersetTheme,
|
||||
},
|
||||
},
|
||||
);
|
||||
expect(wrapper.find(TableElement).state().sortColumns).toBe(false);
|
||||
wrapper.find('.header-container').hostNodes().simulate('click');
|
||||
expect(wrapper.find(ColumnElement).first().props().column.name).toBe('id');
|
||||
wrapper.find('.sort-cols').simulate('click');
|
||||
expect(wrapper.state().sortColumns).toBe(true);
|
||||
wrapper.find('.header-container').simulate('mouseEnter');
|
||||
wrapper.find('.sort-cols').hostNodes().simulate('click');
|
||||
expect(wrapper.find(TableElement).state().sortColumns).toBe(true);
|
||||
expect(wrapper.find(ColumnElement).first().props().column.name).toBe(
|
||||
'active',
|
||||
);
|
||||
|
@ -99,10 +135,18 @@ describe('TableElement', () => {
|
|||
expect(mockedActions.collapseTable.called).toBe(true);
|
||||
});
|
||||
it('removes the table', () => {
|
||||
const wrapper = shallow(<TableElement {...mockedProps} />);
|
||||
expect(wrapper.state().expanded).toBe(true);
|
||||
wrapper.find('.table-remove').simulate('click');
|
||||
expect(wrapper.state().expanded).toBe(false);
|
||||
const wrapper = mount(
|
||||
<Provider store={store}>
|
||||
<TableElement {...mockedProps} />
|
||||
</Provider>,
|
||||
{
|
||||
wrappingComponent: ThemeProvider,
|
||||
wrappingComponentProps: {
|
||||
theme: supersetTheme,
|
||||
},
|
||||
},
|
||||
);
|
||||
wrapper.find('.table-remove').hostNodes().simulate('click');
|
||||
expect(mockedActions.removeDataPreview.called).toBe(true);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -19,7 +19,8 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import Button from 'src/components/Button';
|
||||
import { t, styled } from '@superset-ui/core';
|
||||
import { t, styled, css } from '@superset-ui/core';
|
||||
import Collapse from 'src/common/components/Collapse';
|
||||
import TableElement from './TableElement';
|
||||
import TableSelector from '../../components/TableSelector';
|
||||
|
||||
|
@ -145,13 +146,38 @@ export default class SqlEditorLeftBar extends React.PureComponent {
|
|||
<div className="divider" />
|
||||
<StyledScrollbarContainer>
|
||||
<StyledScrollbarContent contentHeight={tableMetaDataHeight}>
|
||||
{this.props.tables.map(table => (
|
||||
<TableElement
|
||||
table={table}
|
||||
key={table.id}
|
||||
actions={this.props.actions}
|
||||
/>
|
||||
))}
|
||||
<Collapse
|
||||
ghost
|
||||
expandIconPosition="right"
|
||||
css={theme => css`
|
||||
.ant-collapse-item {
|
||||
margin-bottom: ${theme.gridUnit * 3}px;
|
||||
}
|
||||
.ant-collapse-header {
|
||||
padding: 0px !important;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.ant-collapse-content-box {
|
||||
padding: 0px ${theme.gridUnit * 4}px 0px 0px !important;
|
||||
}
|
||||
.ant-collapse-arrow {
|
||||
top: ${theme.gridUnit * 2}px !important;
|
||||
color: ${theme.colors.primary.dark1} !important;
|
||||
&: hover {
|
||||
color: ${theme.colors.primary.dark2} !important;
|
||||
}
|
||||
}
|
||||
`}
|
||||
>
|
||||
{this.props.tables.map(table => (
|
||||
<TableElement
|
||||
table={table}
|
||||
key={table.id}
|
||||
actions={this.props.actions}
|
||||
/>
|
||||
))}
|
||||
</Collapse>
|
||||
</StyledScrollbarContent>
|
||||
</StyledScrollbarContainer>
|
||||
{shouldShowReset && (
|
||||
|
|
|
@ -18,10 +18,12 @@
|
|||
*/
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Collapse, Well } from 'react-bootstrap';
|
||||
import { Well } from 'react-bootstrap';
|
||||
import Collapse from 'src/common/components/Collapse';
|
||||
import ButtonGroup from 'src/components/ButtonGroup';
|
||||
import shortid from 'shortid';
|
||||
import { t, styled } from '@superset-ui/core';
|
||||
import { debounce } from 'lodash';
|
||||
|
||||
import Fade from 'src/common/components/Fade';
|
||||
import { Tooltip } from 'src/common/components/Tooltip';
|
||||
|
@ -35,13 +37,11 @@ import Loading from '../../components/Loading';
|
|||
const propTypes = {
|
||||
table: PropTypes.object,
|
||||
actions: PropTypes.object,
|
||||
timeout: PropTypes.number, // used for tests
|
||||
};
|
||||
|
||||
const defaultProps = {
|
||||
actions: {},
|
||||
table: null,
|
||||
timeout: 500,
|
||||
};
|
||||
|
||||
const StyledSpan = styled.span`
|
||||
|
@ -57,13 +57,11 @@ class TableElement extends React.PureComponent {
|
|||
super(props);
|
||||
this.state = {
|
||||
sortColumns: false,
|
||||
expanded: true,
|
||||
hovered: false,
|
||||
};
|
||||
this.removeFromStore = this.removeFromStore.bind(this);
|
||||
this.toggleSortColumns = this.toggleSortColumns.bind(this);
|
||||
this.removeTable = this.removeTable.bind(this);
|
||||
this.setHover = this.setHover.bind(this);
|
||||
this.setHover = debounce(this.setHover.bind(this), 100);
|
||||
}
|
||||
|
||||
setHover(hovered) {
|
||||
|
@ -91,18 +89,14 @@ class TableElement extends React.PureComponent {
|
|||
}
|
||||
|
||||
removeTable() {
|
||||
this.setState({ expanded: false });
|
||||
this.props.actions.removeDataPreview(this.props.table);
|
||||
this.props.actions.removeTable(this.props.table);
|
||||
}
|
||||
|
||||
toggleSortColumns() {
|
||||
this.setState(prevState => ({ sortColumns: !prevState.sortColumns }));
|
||||
}
|
||||
|
||||
removeFromStore() {
|
||||
this.props.actions.removeTable(this.props.table);
|
||||
}
|
||||
|
||||
renderWell() {
|
||||
const { table } = this.props;
|
||||
let header;
|
||||
|
@ -208,7 +202,11 @@ class TableElement extends React.PureComponent {
|
|||
renderHeader() {
|
||||
const { table } = this.props;
|
||||
return (
|
||||
<div className="clearfix header-container">
|
||||
<div
|
||||
className="clearfix header-container"
|
||||
onMouseEnter={() => this.setHover(true)}
|
||||
onMouseLeave={() => this.setHover(false)}
|
||||
>
|
||||
<Tooltip
|
||||
id="copy-to-clipboard-tooltip"
|
||||
placement="top"
|
||||
|
@ -231,21 +229,13 @@ class TableElement extends React.PureComponent {
|
|||
{table.isMetadataLoading || table.isExtraMetadataLoading ? (
|
||||
<Loading position="inline" />
|
||||
) : (
|
||||
<Fade hovered={this.state.hovered}>{this.renderControls()}</Fade>
|
||||
<Fade
|
||||
hovered={this.state.hovered}
|
||||
onClick={e => e.stopPropagation()}
|
||||
>
|
||||
{this.renderControls()}
|
||||
</Fade>
|
||||
)}
|
||||
<i
|
||||
role="button"
|
||||
aria-label="Toggle table"
|
||||
tabIndex={0}
|
||||
onClick={e => {
|
||||
this.toggleTable(e);
|
||||
}}
|
||||
className={
|
||||
'text-primary pointer m-l-10 ' +
|
||||
'fa fa-lg ' +
|
||||
`fa-angle-${table.expanded ? 'up' : 'down'}`
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -270,39 +260,36 @@ class TableElement extends React.PureComponent {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
const metadata = (
|
||||
<Collapse in={table.expanded} timeout={this.props.timeout}>
|
||||
<div
|
||||
onMouseEnter={() => this.setHover(true)}
|
||||
onMouseLeave={() => this.setHover(false)}
|
||||
css={{ paddingTop: 6 }}
|
||||
>
|
||||
{this.renderWell()}
|
||||
<div>
|
||||
{this.renderWell()}
|
||||
<div className="table-columns m-t-5">
|
||||
{cols &&
|
||||
cols.map(col => <ColumnElement column={col} key={col.name} />)}
|
||||
</div>
|
||||
{cols &&
|
||||
cols.map(col => <ColumnElement column={col} key={col.name} />)}
|
||||
</div>
|
||||
</Collapse>
|
||||
</div>
|
||||
);
|
||||
return metadata;
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Collapse
|
||||
in={this.state.expanded}
|
||||
timeout={this.props.timeout}
|
||||
onExited={this.removeFromStore}
|
||||
<Collapse.Panel
|
||||
{...this.props}
|
||||
header={this.renderHeader()}
|
||||
className="TableElement"
|
||||
>
|
||||
<div
|
||||
className="TableElement table-schema m-b-10"
|
||||
onMouseEnter={() => this.setHover(true)}
|
||||
onMouseLeave={() => this.setHover(false)}
|
||||
>
|
||||
{this.renderHeader()}
|
||||
<div>{this.renderBody()}</div>
|
||||
</div>
|
||||
</Collapse>
|
||||
{this.renderBody()}
|
||||
</Collapse.Panel>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
TableElement.propTypes = propTypes;
|
||||
TableElement.defaultProps = defaultProps;
|
||||
|
||||
|
|
|
@ -413,8 +413,6 @@ div.tablePopover {
|
|||
}
|
||||
|
||||
.TableElement {
|
||||
margin-right: 10px;
|
||||
|
||||
.well {
|
||||
margin-top: 5px;
|
||||
margin-bottom: 5px;
|
||||
|
@ -428,18 +426,22 @@ div.tablePopover {
|
|||
|
||||
.header-container {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
|
||||
.table-name {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
font-size: 16px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.header-right-side {
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: 33px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
{
|
||||
"rules": {
|
||||
"no-restricted-imports": 0
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
/**
|
||||
* 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 { useTheme } from '@superset-ui/core';
|
||||
import Collapse, { CollapseProps } from '.';
|
||||
|
||||
export default {
|
||||
title: 'Collapse',
|
||||
component: Collapse,
|
||||
};
|
||||
|
||||
export const InteractiveCollapse = (args: CollapseProps) => {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<Collapse
|
||||
defaultActiveKey={['1']}
|
||||
style={
|
||||
args.light ? { background: theme.colors.grayscale.light2 } : undefined
|
||||
}
|
||||
{...args}
|
||||
>
|
||||
<Collapse.Panel header="Header 1" key="1">
|
||||
Content 1
|
||||
</Collapse.Panel>
|
||||
<Collapse.Panel header="Header 2" key="2">
|
||||
Content 2
|
||||
</Collapse.Panel>
|
||||
</Collapse>
|
||||
);
|
||||
};
|
||||
|
||||
InteractiveCollapse.args = {
|
||||
ghost: false,
|
||||
bordered: true,
|
||||
accordion: false,
|
||||
};
|
||||
|
||||
InteractiveCollapse.argTypes = {
|
||||
theme: {
|
||||
table: {
|
||||
disable: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
InteractiveCollapse.story = {
|
||||
parameters: {
|
||||
actions: {
|
||||
disabled: true,
|
||||
},
|
||||
knobs: {
|
||||
disabled: true,
|
||||
},
|
||||
},
|
||||
};
|
|
@ -0,0 +1,110 @@
|
|||
/**
|
||||
* 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 { render, screen } from 'spec/helpers/testing-library';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { supersetTheme } from '@superset-ui/core';
|
||||
import { hexToRgb } from 'src/utils/colorUtils';
|
||||
import Collapse, { CollapseProps } from '.';
|
||||
|
||||
function renderCollapse(props?: CollapseProps) {
|
||||
return render(
|
||||
<Collapse {...props}>
|
||||
<Collapse.Panel header="Header 1" key="1">
|
||||
Content 1
|
||||
</Collapse.Panel>
|
||||
<Collapse.Panel header="Header 2" key="2">
|
||||
Content 2
|
||||
</Collapse.Panel>
|
||||
</Collapse>,
|
||||
);
|
||||
}
|
||||
|
||||
test('renders collapsed with default props', () => {
|
||||
renderCollapse();
|
||||
|
||||
const headers = screen.getAllByRole('button');
|
||||
|
||||
expect(headers[0]).toHaveTextContent('Header 1');
|
||||
expect(headers[1]).toHaveTextContent('Header 2');
|
||||
expect(screen.queryByText('Content 1')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('Content 2')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('renders with one item expanded by default', () => {
|
||||
renderCollapse({ defaultActiveKey: ['1'] });
|
||||
|
||||
const headers = screen.getAllByRole('button');
|
||||
|
||||
expect(headers[0]).toHaveTextContent('Header 1');
|
||||
expect(headers[1]).toHaveTextContent('Header 2');
|
||||
expect(screen.getByText('Content 1')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Content 2')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('expands on click', () => {
|
||||
renderCollapse();
|
||||
|
||||
expect(screen.queryByText('Content 1')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('Content 2')).not.toBeInTheDocument();
|
||||
|
||||
userEvent.click(screen.getAllByRole('button')[0]);
|
||||
|
||||
expect(screen.getByText('Content 1')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Content 2')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('collapses on click', () => {
|
||||
renderCollapse({ defaultActiveKey: ['1'] });
|
||||
|
||||
expect(screen.getByText('Content 1')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Content 2')).not.toBeInTheDocument();
|
||||
|
||||
userEvent.click(screen.getAllByRole('button')[0]);
|
||||
|
||||
expect(screen.getByText('Content 1').parentNode).toHaveClass(
|
||||
'ant-collapse-content-hidden',
|
||||
);
|
||||
expect(screen.queryByText('Content 2')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('renders with custom properties', () => {
|
||||
renderCollapse({
|
||||
light: true,
|
||||
bigger: true,
|
||||
bold: true,
|
||||
animateArrows: true,
|
||||
});
|
||||
|
||||
const header = document.getElementsByClassName('ant-collapse-header')[0];
|
||||
const arrow = document.getElementsByClassName('ant-collapse-arrow')[0]
|
||||
.children[0];
|
||||
|
||||
const headerStyle = window.getComputedStyle(header);
|
||||
const arrowStyle = window.getComputedStyle(arrow);
|
||||
|
||||
expect(headerStyle.fontWeight).toBe(
|
||||
supersetTheme.typography.weights.bold.toString(),
|
||||
);
|
||||
expect(headerStyle.fontSize).toBe(`${supersetTheme.gridUnit * 4}px`);
|
||||
expect(headerStyle.color).toBe(
|
||||
hexToRgb(supersetTheme.colors.grayscale.light4),
|
||||
);
|
||||
expect(arrowStyle.transition).toBe('transform 0.24s');
|
||||
});
|
|
@ -18,11 +18,10 @@
|
|||
*/
|
||||
import React from 'react';
|
||||
import { styled } from '@superset-ui/core';
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { Collapse as AntdCollapse } from 'antd';
|
||||
import { CollapseProps as AntdCollapseProps } from 'antd/lib/collapse';
|
||||
|
||||
interface CollapseProps extends AntdCollapseProps {
|
||||
export interface CollapseProps extends AntdCollapseProps {
|
||||
light?: boolean;
|
||||
bigger?: boolean;
|
||||
bold?: boolean;
|
||||
|
@ -34,13 +33,7 @@ const Collapse = Object.assign(
|
|||
styled(({ light, bigger, bold, animateArrows, ...props }: CollapseProps) => (
|
||||
<AntdCollapse {...props} />
|
||||
))`
|
||||
height: 100%;
|
||||
.ant-collapse-item {
|
||||
border: 0;
|
||||
height: 100%;
|
||||
&:last-of-type.ant-collapse-item-active {
|
||||
padding-bottom: ${({ theme }) => theme.gridUnit * 3}px;
|
||||
}
|
||||
.ant-collapse-header {
|
||||
font-weight: ${({ bold, theme }) =>
|
||||
bold
|
||||
|
@ -80,9 +73,7 @@ const Collapse = Object.assign(
|
|||
`}
|
||||
}
|
||||
.ant-collapse-content {
|
||||
height: 100%;
|
||||
.ant-collapse-content-box {
|
||||
height: 100%;
|
||||
.loading.inline {
|
||||
margin: ${({ theme }) => theme.gridUnit * 12}px auto;
|
||||
display: block;
|
|
@ -17,9 +17,9 @@
|
|||
* under the License.
|
||||
*/
|
||||
import { styled } from '@superset-ui/core';
|
||||
import { Radio as BaseRadio } from 'src/common/components';
|
||||
import { Radio as AntdRadio } from 'antd';
|
||||
|
||||
const StyledRadio = styled(BaseRadio)`
|
||||
const StyledRadio = styled(AntdRadio)`
|
||||
.ant-radio-inner {
|
||||
top: -1px;
|
||||
left: 2px;
|
||||
|
@ -51,7 +51,7 @@ const StyledRadio = styled(BaseRadio)`
|
|||
}
|
||||
}
|
||||
`;
|
||||
const StyledGroup = styled(BaseRadio.Group)`
|
||||
const StyledGroup = styled(AntdRadio.Group)`
|
||||
font-size: inherit;
|
||||
`;
|
||||
|
||||
|
|
|
@ -18,13 +18,13 @@
|
|||
*/
|
||||
import React from 'react';
|
||||
import { useTheme } from '@superset-ui/core';
|
||||
import { Tooltip as BaseTooltip } from 'src/common/components';
|
||||
import { Tooltip as AntdTooltip } from 'antd';
|
||||
import { TooltipProps } from 'antd/lib/tooltip';
|
||||
|
||||
export const Tooltip = (props: TooltipProps) => {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<BaseTooltip
|
||||
<AntdTooltip
|
||||
overlayStyle={{ fontSize: theme.typography.sizes.s, lineHeight: '1.6' }}
|
||||
color={`${theme.colors.grayscale.dark2}e6`}
|
||||
{...props}
|
||||
|
|
|
@ -34,7 +34,6 @@ import {
|
|||
} from './DatePicker';
|
||||
import Badge from './Badge';
|
||||
import ProgressBar from './ProgressBar';
|
||||
import Collapse from './Collapse';
|
||||
import { CronPicker, CronError } from './CronPicker';
|
||||
|
||||
export default {
|
||||
|
@ -264,75 +263,6 @@ export const BadgeSuccess = () => <Badge status="success" text="Success" />;
|
|||
export const BadgeError = () => <Badge status="error" text="Error" />;
|
||||
export const BadgeSmall = () => <Badge count={100} size="small" />;
|
||||
|
||||
export const CollapseDefault = () => (
|
||||
<Collapse defaultActiveKey={['1']}>
|
||||
<Collapse.Panel header="Hi! I am a header" key="1">
|
||||
Hi! I am a sample content
|
||||
</Collapse.Panel>
|
||||
<Collapse.Panel header="Hi! I am another header" key="2">
|
||||
Hi! I am another sample content
|
||||
</Collapse.Panel>
|
||||
</Collapse>
|
||||
);
|
||||
export const CollapseGhost = () => (
|
||||
<Collapse defaultActiveKey={['1']} ghost>
|
||||
<Collapse.Panel header="Hi! I am a header" key="1">
|
||||
Hi! I am a sample content
|
||||
</Collapse.Panel>
|
||||
<Collapse.Panel header="Hi! I am another header" key="2">
|
||||
Hi! I am another sample content
|
||||
</Collapse.Panel>
|
||||
</Collapse>
|
||||
);
|
||||
export const CollapseBold = () => (
|
||||
<Collapse defaultActiveKey={['1']} bold>
|
||||
<Collapse.Panel header="Hi! I am a header" key="1">
|
||||
Hi! I am a sample content
|
||||
</Collapse.Panel>
|
||||
<Collapse.Panel header="Hi! I am another header" key="2">
|
||||
Hi! I am another sample content
|
||||
</Collapse.Panel>
|
||||
</Collapse>
|
||||
);
|
||||
export const CollapseBigger = () => (
|
||||
<Collapse defaultActiveKey={['1']} bigger>
|
||||
<Collapse.Panel header="Hi! I am a header" key="1">
|
||||
Hi! I am a sample content
|
||||
</Collapse.Panel>
|
||||
<Collapse.Panel header="Hi! I am another header" key="2">
|
||||
Hi! I am another sample content
|
||||
</Collapse.Panel>
|
||||
</Collapse>
|
||||
);
|
||||
export const CollapseTextLight = () => (
|
||||
<Collapse defaultActiveKey={['1']} light>
|
||||
<Collapse.Panel
|
||||
header="Hi! I am a header"
|
||||
key="1"
|
||||
style={{ background: '#BBB' }}
|
||||
>
|
||||
Hi! I am a sample content
|
||||
</Collapse.Panel>
|
||||
<Collapse.Panel
|
||||
header="Hi! I am another header"
|
||||
key="2"
|
||||
style={{ background: '#BBB' }}
|
||||
>
|
||||
Hi! I am another sample content
|
||||
</Collapse.Panel>
|
||||
</Collapse>
|
||||
);
|
||||
export const CollapseAnimateArrows = () => (
|
||||
<Collapse animateArrows defaultActiveKey={['1']}>
|
||||
<Collapse.Panel header="Hi! I am a header" key="1">
|
||||
Hi! I am a sample content
|
||||
</Collapse.Panel>
|
||||
<Collapse.Panel header="Hi! I am another header" key="2">
|
||||
Hi! I am another sample content
|
||||
</Collapse.Panel>
|
||||
</Collapse>
|
||||
);
|
||||
|
||||
export function StyledCronPicker() {
|
||||
// @ts-ignore
|
||||
const inputRef = useRef<Input>(null);
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
*/
|
||||
import React from 'react';
|
||||
import { styled } from '@superset-ui/core';
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { Dropdown, Menu as AntdMenu, Input as AntdInput, Skeleton } from 'antd';
|
||||
import { DropDownProps } from 'antd/lib/dropdown';
|
||||
/*
|
||||
|
@ -26,11 +25,9 @@ import { DropDownProps } from 'antd/lib/dropdown';
|
|||
|
||||
For documentation, see https://ant.design/components/overview/
|
||||
*/
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
export {
|
||||
AutoComplete,
|
||||
Avatar,
|
||||
Button,
|
||||
Card,
|
||||
Checkbox,
|
||||
Col,
|
||||
|
@ -45,7 +42,6 @@ export {
|
|||
Tree,
|
||||
Popover,
|
||||
Slider,
|
||||
Radio,
|
||||
Row,
|
||||
Space,
|
||||
Select,
|
||||
|
@ -60,7 +56,7 @@ export { default as Alert, AlertProps } from 'antd/lib/alert';
|
|||
export { TreeProps } from 'antd/lib/tree';
|
||||
export { FormInstance } from 'antd/lib/form';
|
||||
export { RadioChangeEvent } from 'antd/lib/radio';
|
||||
export { default as Collapse } from './Collapse';
|
||||
|
||||
export { default as Badge } from './Badge';
|
||||
export { default as Progress } from './ProgressBar';
|
||||
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
{
|
||||
"rules": {
|
||||
"no-restricted-imports": 0
|
||||
}
|
||||
}
|
|
@ -20,7 +20,7 @@ import React, { CSSProperties, Children, ReactElement } from 'react';
|
|||
import { kebabCase } from 'lodash';
|
||||
import { mix } from 'polished';
|
||||
import cx from 'classnames';
|
||||
import { Button as AntdButton } from 'src/common/components';
|
||||
import { Button as AntdButton } from 'antd';
|
||||
import { useTheme } from '@superset-ui/core';
|
||||
import { Tooltip } from 'src/common/components/Tooltip';
|
||||
|
||||
|
|
|
@ -24,7 +24,8 @@ import {
|
|||
CheckCircleFilled,
|
||||
ExclamationCircleFilled,
|
||||
} from '@ant-design/icons';
|
||||
import { Collapse, Popover } from 'src/common/components/index';
|
||||
import { Popover } from 'src/common/components/index';
|
||||
import Collapse from 'src/common/components/Collapse';
|
||||
import { Global } from '@emotion/core';
|
||||
import {
|
||||
Indent,
|
||||
|
@ -173,7 +174,7 @@ const DetailsPanelPopover = ({
|
|||
</Title>
|
||||
}
|
||||
>
|
||||
<Indent>
|
||||
<Indent css={{ paddingBottom: theme.gridUnit * 3 }}>
|
||||
{appliedIndicators.map(indicator => (
|
||||
<Indicator
|
||||
key={indicatorKey(indicator)}
|
||||
|
@ -197,7 +198,7 @@ const DetailsPanelPopover = ({
|
|||
</Title>
|
||||
}
|
||||
>
|
||||
<Indent>
|
||||
<Indent css={{ paddingBottom: theme.gridUnit * 3 }}>
|
||||
{incompatibleIndicators.map(indicator => (
|
||||
<Indicator
|
||||
key={indicatorKey(indicator)}
|
||||
|
@ -219,7 +220,7 @@ const DetailsPanelPopover = ({
|
|||
}
|
||||
disabled={!unsetIndicators.length}
|
||||
>
|
||||
<Indent>
|
||||
<Indent css={{ paddingBottom: theme.gridUnit * 3 }}>
|
||||
{unsetIndicators.map(indicator => (
|
||||
<Indicator
|
||||
key={indicatorKey(indicator)}
|
||||
|
|
|
@ -24,8 +24,8 @@ import { connect } from 'react-redux';
|
|||
import { t, styled, getChartControlPanelRegistry } from '@superset-ui/core';
|
||||
|
||||
import Tabs from 'src/common/components/Tabs';
|
||||
import { Collapse } from 'src/common/components';
|
||||
import Alert from 'src/components/Alert';
|
||||
import Collapse from 'src/common/components/Collapse';
|
||||
import { PluginContext } from 'src/components/DynamicPlugins';
|
||||
import Loading from 'src/components/Loading';
|
||||
import { InfoTooltipWithTrigger } from '@superset-ui/chart-controls';
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
* under the License.
|
||||
*/
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { styled, t } from '@superset-ui/core';
|
||||
import { Collapse } from 'src/common/components';
|
||||
import { styled, t, css } from '@superset-ui/core';
|
||||
import Collapse from 'src/common/components/Collapse';
|
||||
import Tabs from 'src/common/components/Tabs';
|
||||
import Loading from 'src/components/Loading';
|
||||
import TableView, { EmptyWrapperType } from 'src/components/TableView';
|
||||
|
@ -264,8 +264,27 @@ export const DataTablesPane = ({
|
|||
onChange={handleCollapseChange}
|
||||
bold
|
||||
ghost
|
||||
css={css`
|
||||
height: 100%;
|
||||
|
||||
.ant-collapse-item {
|
||||
height: 100%;
|
||||
|
||||
.ant-collapse-content {
|
||||
height: 100%;
|
||||
|
||||
.ant-collapse-content-box {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
`}
|
||||
>
|
||||
<Collapse.Panel header={t('Data')} key={DATAPANEL_KEY}>
|
||||
<Collapse.Panel
|
||||
header={t('Data')}
|
||||
key={DATAPANEL_KEY}
|
||||
css={{ paddingBottom: 12 }}
|
||||
>
|
||||
<Tabs
|
||||
fullWidth={false}
|
||||
tabBarExtraContent={TableControls}
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { styled, t } from '@superset-ui/core';
|
||||
import { Collapse } from 'src/common/components';
|
||||
import Collapse from 'src/common/components/Collapse';
|
||||
import {
|
||||
ColumnOption,
|
||||
MetricOption,
|
||||
|
|
|
@ -18,9 +18,10 @@
|
|||
*/
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Panel } from 'react-bootstrap';
|
||||
|
||||
import { css } from '@emotion/core';
|
||||
import { t } from '@superset-ui/core';
|
||||
import Label from 'src/components/Label';
|
||||
import Collapse from 'src/common/components/Collapse';
|
||||
import TextControl from './TextControl';
|
||||
import MetricsControl from './MetricControl/MetricsControl';
|
||||
import ControlHeader from '../ControlHeader';
|
||||
|
@ -54,7 +55,6 @@ export default class FixedOrMetricControl extends React.Component {
|
|||
this.setType = this.setType.bind(this);
|
||||
this.setFixedValue = this.setFixedValue.bind(this);
|
||||
this.setMetric = this.setMetric.bind(this);
|
||||
this.toggle = this.toggle.bind(this);
|
||||
const type =
|
||||
(props.value ? props.value.type : props.default.type) ||
|
||||
controlTypes.fixed;
|
||||
|
@ -89,12 +89,6 @@ export default class FixedOrMetricControl extends React.Component {
|
|||
this.setState({ metricValue }, this.onChange);
|
||||
}
|
||||
|
||||
toggle() {
|
||||
this.setState(prevState => ({
|
||||
expanded: !prevState.expanded,
|
||||
}));
|
||||
}
|
||||
|
||||
render() {
|
||||
const value = this.props.value || this.props.default;
|
||||
const type = value.type || controlTypes.fixed;
|
||||
|
@ -107,67 +101,88 @@ export default class FixedOrMetricControl extends React.Component {
|
|||
return (
|
||||
<div>
|
||||
<ControlHeader {...this.props} />
|
||||
<Label onClick={this.toggle}>
|
||||
{this.state.type === controlTypes.fixed && (
|
||||
<span>{this.state.fixedValue}</span>
|
||||
)}
|
||||
{this.state.type === controlTypes.metric && (
|
||||
<span>
|
||||
<span style={{ fontWeight: 'normal' }}>metric: </span>
|
||||
<strong>
|
||||
{this.state.metricValue ? this.state.metricValue.label : null}
|
||||
</strong>
|
||||
</span>
|
||||
)}
|
||||
</Label>
|
||||
<Panel
|
||||
className="panel-spreaded"
|
||||
collapsible
|
||||
expanded={this.state.expanded}
|
||||
onToggle={this.toggle}
|
||||
<Collapse
|
||||
ghost
|
||||
css={theme => css`
|
||||
&.ant-collapse
|
||||
> .ant-collapse-item.ant-collapse-no-arrow
|
||||
> .ant-collapse-header {
|
||||
border: 0px;
|
||||
padding: 0px 0px ${theme.gridUnit * 2}px 0px;
|
||||
display: inline-block;
|
||||
}
|
||||
&.ant-collapse-ghost
|
||||
> .ant-collapse-item
|
||||
> .ant-collapse-content
|
||||
> .ant-collapse-content-box {
|
||||
padding: 0px;
|
||||
|
||||
& .well {
|
||||
margin-bottom: 0px;
|
||||
padding: ${theme.gridUnit * 2}px;
|
||||
}
|
||||
}
|
||||
`}
|
||||
>
|
||||
<Panel.Collapse>
|
||||
<Panel.Body>
|
||||
<div className="well">
|
||||
<PopoverSection
|
||||
title="Fixed"
|
||||
isSelected={type === controlTypes.fixed}
|
||||
onSelect={() => {
|
||||
<Collapse.Panel
|
||||
showArrow={false}
|
||||
header={
|
||||
<Label onClick={() => undefined}>
|
||||
{this.state.type === controlTypes.fixed && (
|
||||
<span>{this.state.fixedValue}</span>
|
||||
)}
|
||||
{this.state.type === controlTypes.metric && (
|
||||
<span>
|
||||
<span>metric: </span>
|
||||
<strong>
|
||||
{this.state.metricValue
|
||||
? this.state.metricValue.label
|
||||
: null}
|
||||
</strong>
|
||||
</span>
|
||||
)}
|
||||
</Label>
|
||||
}
|
||||
>
|
||||
<div className="well">
|
||||
<PopoverSection
|
||||
title={t('Fixed')}
|
||||
isSelected={type === controlTypes.fixed}
|
||||
onSelect={() => {
|
||||
this.setType(controlTypes.fixed);
|
||||
}}
|
||||
>
|
||||
<TextControl
|
||||
isFloat
|
||||
onChange={this.setFixedValue}
|
||||
onFocus={() => {
|
||||
this.setType(controlTypes.fixed);
|
||||
}}
|
||||
>
|
||||
<TextControl
|
||||
isFloat
|
||||
onChange={this.setFixedValue}
|
||||
onFocus={() => {
|
||||
this.setType(controlTypes.fixed);
|
||||
}}
|
||||
value={this.state.fixedValue}
|
||||
/>
|
||||
</PopoverSection>
|
||||
<PopoverSection
|
||||
title="Based on a metric"
|
||||
isSelected={type === controlTypes.metric}
|
||||
onSelect={() => {
|
||||
value={this.state.fixedValue}
|
||||
/>
|
||||
</PopoverSection>
|
||||
<PopoverSection
|
||||
title={t('Based on a metric')}
|
||||
isSelected={type === controlTypes.metric}
|
||||
onSelect={() => {
|
||||
this.setType(controlTypes.metric);
|
||||
}}
|
||||
>
|
||||
<MetricsControl
|
||||
name="metric"
|
||||
columns={columns}
|
||||
savedMetrics={metrics}
|
||||
multi={false}
|
||||
onFocus={() => {
|
||||
this.setType(controlTypes.metric);
|
||||
}}
|
||||
>
|
||||
<MetricsControl
|
||||
name="metric"
|
||||
columns={columns}
|
||||
savedMetrics={metrics}
|
||||
multi={false}
|
||||
onFocus={() => {
|
||||
this.setType(controlTypes.metric);
|
||||
}}
|
||||
onChange={this.setMetric}
|
||||
value={this.state.metricValue}
|
||||
/>
|
||||
</PopoverSection>
|
||||
</div>
|
||||
</Panel.Body>
|
||||
</Panel.Collapse>
|
||||
</Panel>
|
||||
onChange={this.setMetric}
|
||||
value={this.state.metricValue}
|
||||
/>
|
||||
</PopoverSection>
|
||||
</div>
|
||||
</Collapse.Panel>
|
||||
</Collapse>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -233,15 +233,3 @@ h2.section-header {
|
|||
margin-top: 0;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.panel-spreaded,
|
||||
.panel-spreaded .panel-body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.panel-spreaded .well {
|
||||
margin-top: 8px;
|
||||
margin-bottom: 0;
|
||||
padding: 8px;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
export function hexToRgb(h: string) {
|
||||
let r = '0';
|
||||
let g = '0';
|
||||
let b = '0';
|
||||
|
||||
// 3 digits
|
||||
if (h.length === 4) {
|
||||
r = `0x${h[1]}${h[1]}`;
|
||||
g = `0x${h[2]}${h[2]}`;
|
||||
b = `0x${h[3]}${h[3]}`;
|
||||
|
||||
// 6 digits
|
||||
} else if (h.length === 7) {
|
||||
r = `0x${h[1]}${h[2]}`;
|
||||
g = `0x${h[3]}${h[4]}`;
|
||||
b = `0x${h[5]}${h[6]}`;
|
||||
}
|
||||
|
||||
return `rgb(${+r}, ${+g}, ${+b})`;
|
||||
}
|
||||
|
||||
export function rgbToHex(red: number, green: number, blue: number) {
|
||||
let r = red.toString(16);
|
||||
let g = green.toString(16);
|
||||
let b = blue.toString(16);
|
||||
|
||||
if (r.length === 1) r = `0${r}`;
|
||||
if (g.length === 1) g = `0${g}`;
|
||||
if (b.length === 1) b = `0${b}`;
|
||||
|
||||
return `#${r}${g}${b}`;
|
||||
}
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { styled, t } from '@superset-ui/core';
|
||||
import { Collapse } from 'src/common/components';
|
||||
import Collapse from 'src/common/components/Collapse';
|
||||
import { User } from 'src/types/bootstrapTypes';
|
||||
import { reject } from 'lodash';
|
||||
import withToasts from 'src/messageToasts/enhancers/withToasts';
|
||||
|
|
Loading…
Reference in New Issue