Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(sqllab): Make LeftBar width resizable #21300

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 41 additions & 13 deletions superset-frontend/src/SqlLab/components/SqlEditor/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import StyledModal from 'src/components/Modal';
import Mousetrap from 'mousetrap';
import Button from 'src/components/Button';
import Timer from 'src/components/Timer';
import ResizableSidebar from 'src/components/ResizableSidebar';
import { AntdDropdown, AntdSwitch } from 'src/components';
import { Input } from 'src/components/Input';
import { Menu } from 'src/components/Menu';
Expand Down Expand Up @@ -60,6 +61,7 @@ import {
SQL_EDITOR_GUTTER_HEIGHT,
SQL_EDITOR_GUTTER_MARGIN,
SQL_TOOLBAR_HEIGHT,
SQL_EDITOR_LEFTBAR_WIDTH,
} from 'src/SqlLab/constants';
import {
getItem,
Expand Down Expand Up @@ -127,6 +129,15 @@ const StyledToolbar = styled.div`
}
`;

const StyledSidebar = styled.div`
flex: 0 0 ${({ width }) => width}px;
width: ${({ width }) => width}px;
padding: ${({ hide }) => (hide ? 0 : 10)}px;
border-right: 1px solid
${({ theme, hide }) =>
hide ? 'transparent' : theme.colors.grayscale.light2};
`;

const propTypes = {
actions: PropTypes.object.isRequired,
database: PropTypes.object,
Expand Down Expand Up @@ -674,7 +685,6 @@ class SqlEditor extends React.PureComponent {
this.state.createAs === CtasEnum.VIEW
? 'Specify name to CREATE VIEW AS schema in: public'
: 'Specify name to CREATE TABLE AS schema in: public';

const leftBarStateClass = this.props.hideLeftBar
? 'schemaPane-exit-done'
: 'schemaPane-enter-done';
Expand All @@ -685,15 +695,28 @@ class SqlEditor extends React.PureComponent {
in={!this.props.hideLeftBar}
timeout={300}
>
<div className={`schemaPane ${leftBarStateClass}`}>
<SqlEditorLeftBar
database={this.props.database}
queryEditor={this.props.queryEditor}
tables={this.props.tables}
actions={this.props.actions}
setEmptyState={this.setEmptyState}
/>
</div>
<ResizableSidebar
id={`sqllab:${this.props.queryEditor.id}`}
minWidth={SQL_EDITOR_LEFTBAR_WIDTH}
initialWidth={SQL_EDITOR_LEFTBAR_WIDTH}
enable={!this.props.hideLeftBar}
>
{adjustedWidth => (
<StyledSidebar
className={`schemaPane ${leftBarStateClass}`}
width={adjustedWidth}
hide={this.props.hideLeftBar}
>
<SqlEditorLeftBar
database={this.props.database}
queryEditor={this.props.queryEditor}
tables={this.props.tables}
actions={this.props.actions}
setEmptyState={this.setEmptyState}
/>
</StyledSidebar>
)}
</ResizableSidebar>
</CSSTransition>
{this.state.showEmptyState ? (
<EmptyStateBig
Expand Down Expand Up @@ -754,17 +777,22 @@ SqlEditor.defaultProps = defaultProps;
SqlEditor.propTypes = propTypes;

function mapStateToProps({ sqlLab }, { queryEditor }) {
let { latestQueryId, dbId } = queryEditor;
let { latestQueryId, dbId, hideLeftBar } = queryEditor;
if (sqlLab.unsavedQueryEditor.id === queryEditor.id) {
const { latestQueryId: unsavedQID, dbId: unsavedDBID } =
sqlLab.unsavedQueryEditor;
const {
latestQueryId: unsavedQID,
dbId: unsavedDBID,
hideLeftBar: unsavedHideLeftBar,
} = sqlLab.unsavedQueryEditor;
latestQueryId = unsavedQID || latestQueryId;
dbId = unsavedDBID || dbId;
hideLeftBar = unsavedHideLeftBar || hideLeftBar;
}
const database = sqlLab.databases[dbId];
const latestQuery = sqlLab.queries[latestQueryId];

return {
hideLeftBar,
queryEditors: sqlLab.queryEditors,
latestQuery,
database,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,6 @@ class TabbedSqlEditors extends React.PureComponent {
editorQueries={this.state.queriesArray}
dataPreviewQueries={this.state.dataPreviewQueries}
actions={this.props.actions}
hideLeftBar={qe.hideLeftBar}
defaultQueryLimit={this.props.defaultQueryLimit}
maxRow={this.props.maxRow}
displayLimit={this.props.displayLimit}
Expand Down
1 change: 1 addition & 0 deletions superset-frontend/src/SqlLab/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export const TIME_OPTIONS = [
export const SQL_EDITOR_GUTTER_HEIGHT = 5;
export const SQL_EDITOR_GUTTER_MARGIN = 3;
export const SQL_TOOLBAR_HEIGHT = 51;
export const SQL_EDITOR_LEFTBAR_WIDTH = 400;

// kilobyte storage
export const KB_STORAGE = 1024;
Expand Down
4 changes: 1 addition & 3 deletions superset-frontend/src/SqlLab/main.less
Original file line number Diff line number Diff line change
Expand Up @@ -283,16 +283,14 @@ div.Workspace {
display: flex;
flex-direction: row;
height: 100%;
padding: 10px;

.schemaPane {
flex: 0 0 400px;
transition: transform @timing-normal ease-in-out;
}

.queryPane {
flex: 1 1 auto;
padding-left: 10px;
padding: 10px;
overflow-y: none;
overflow-x: scroll;
}
Expand Down
82 changes: 82 additions & 0 deletions superset-frontend/src/components/ResizableSidebar/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/**
* 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 { Resizable } from 're-resizable';
import { styled } from '@superset-ui/core';
import useStoredSidebarWidth from './useStoredSidebarWidth';

const ResizableWrapper = styled.div`
position: absolute;
height: 100%;

:hover .sidebar-resizer::after {
background-color: ${({ theme }) => theme.colors.primary.base};
}

.sidebar-resizer {
// @z-index-above-sticky-header (100) + 1 = 101
z-index: 101;
}

.sidebar-resizer::after {
display: block;
content: '';
width: 1px;
height: 100%;
margin: 0 auto;
}
`;

type Props = {
id: string;
initialWidth: number;
enable: boolean;
minWidth?: number;
maxWidth?: number;
children: (width: number) => React.ReactNode;
};

const ResizableSidebar: React.FC<Props> = ({
id,
initialWidth,
minWidth,
maxWidth,
enable,
children,
}) => {
const [width, setWidth] = useStoredSidebarWidth(id, initialWidth);

return (
<>
<ResizableWrapper>
<Resizable
enable={{ right: enable }}
handleClasses={{ right: 'sidebar-resizer' }}
size={{ width, height: '100%' }}
minWidth={minWidth}
maxWidth={maxWidth}
onResizeStop={(e, direction, ref, d) => setWidth(width + d.width)}
/>
</ResizableWrapper>
{children(width)}
</>
);
};

export default ResizableSidebar;
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,11 @@ import {
setItem,
getItem,
} from 'src/utils/localStorageHelpers';
import { OPEN_FILTER_BAR_WIDTH } from 'src/dashboard/constants';
import useStoredFilterBarWidth from './useStoredFilterBarWidth';
import useStoredSidebarWidth from './useStoredSidebarWidth';

describe('useStoredFilterBarWidth', () => {
const INITIAL_WIDTH = 300;

describe('useStoredSidebarWidth', () => {
beforeEach(() => {
localStorage.clear();
});
Expand All @@ -34,38 +35,44 @@ describe('useStoredFilterBarWidth', () => {
localStorage.clear();
});

it('returns a default filterBar width by OPEN_FILTER_BAR_WIDTH', () => {
const dashboardId = '123';
const { result } = renderHook(() => useStoredFilterBarWidth(dashboardId));
it('returns a default filterBar width by initialWidth', () => {
const id = '123';
const { result } = renderHook(() =>
useStoredSidebarWidth(id, INITIAL_WIDTH),
);
const [actualWidth] = result.current;

expect(actualWidth).toEqual(OPEN_FILTER_BAR_WIDTH);
expect(actualWidth).toEqual(INITIAL_WIDTH);
});

it('returns a stored filterBar width from localStorage', () => {
const dashboardId = '123';
const id = '123';
const expectedWidth = 378;
setItem(LocalStorageKeys.dashboard__custom_filter_bar_widths, {
[dashboardId]: expectedWidth,
setItem(LocalStorageKeys.common__resizable_sidebar_widths, {
[id]: expectedWidth,
'456': 250,
});
const { result } = renderHook(() => useStoredFilterBarWidth(dashboardId));
const { result } = renderHook(() =>
useStoredSidebarWidth(id, INITIAL_WIDTH),
);
const [actualWidth] = result.current;

expect(actualWidth).toEqual(expectedWidth);
expect(actualWidth).not.toEqual(250);
});

it('returns a setter for filterBar width that stores the state in localStorage together', () => {
const dashboardId = '123';
const id = '123';
const expectedWidth = 378;
const otherDashboardId = '456';
const otherDashboardWidth = 253;
setItem(LocalStorageKeys.dashboard__custom_filter_bar_widths, {
[dashboardId]: 300,
setItem(LocalStorageKeys.common__resizable_sidebar_widths, {
[id]: 300,
[otherDashboardId]: otherDashboardWidth,
});
const { result } = renderHook(() => useStoredFilterBarWidth(dashboardId));
const { result } = renderHook(() =>
useStoredSidebarWidth(id, INITIAL_WIDTH),
);
const [prevWidth, setter] = result.current;

expect(prevWidth).toEqual(300);
Expand All @@ -74,10 +81,10 @@ describe('useStoredFilterBarWidth', () => {

const updatedWidth = result.current[0];
const widthsMap = getItem(
LocalStorageKeys.dashboard__custom_filter_bar_widths,
LocalStorageKeys.common__resizable_sidebar_widths,
{},
);
expect(widthsMap[dashboardId]).toEqual(expectedWidth);
expect(widthsMap[id]).toEqual(expectedWidth);
expect(widthsMap[otherDashboardId]).toEqual(otherDashboardWidth);
expect(updatedWidth).toEqual(expectedWidth);
expect(updatedWidth).not.toEqual(250);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,30 +22,30 @@ import {
setItem,
getItem,
} from 'src/utils/localStorageHelpers';
import { OPEN_FILTER_BAR_WIDTH } from 'src/dashboard/constants';

export default function useStoredFilterBarWidth(dashboardId: string) {
export default function useStoredSidebarWidth(
id: string,
initialWidth: number,
) {
const widthsMapRef = useRef<Record<string, number>>();
const [filterBarWidth, setFilterBarWidth] = useState<number>(
OPEN_FILTER_BAR_WIDTH,
);
const [sidebarWidth, setSidebarWidth] = useState<number>(initialWidth);

useEffect(() => {
widthsMapRef.current =
widthsMapRef.current ??
getItem(LocalStorageKeys.dashboard__custom_filter_bar_widths, {});
if (widthsMapRef.current[dashboardId]) {
setFilterBarWidth(widthsMapRef.current[dashboardId]);
getItem(LocalStorageKeys.common__resizable_sidebar_widths, {});
if (widthsMapRef.current[id]) {
setSidebarWidth(widthsMapRef.current[id]);
}
}, [dashboardId]);
}, [id]);

function setStoredFilterBarWidth(updatedWidth: number) {
setFilterBarWidth(updatedWidth);
setItem(LocalStorageKeys.dashboard__custom_filter_bar_widths, {
function setStoredSidebarWidth(updatedWidth: number) {
setSidebarWidth(updatedWidth);
setItem(LocalStorageKeys.common__resizable_sidebar_widths, {
...widthsMapRef.current,
[dashboardId]: updatedWidth,
[id]: updatedWidth,
});
}

return [filterBarWidth, setStoredFilterBarWidth] as const;
return [sidebarWidth, setStoredSidebarWidth] as const;
}
5 changes: 4 additions & 1 deletion superset-frontend/src/components/TableSelector/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,14 @@ import { useToasts } from 'src/components/MessageToasts/withToasts';
import { SchemaOption } from 'src/SqlLab/types';
import { useTables, Table } from 'src/hooks/apiResources';

const REFRESH_WIDTH = 30;

const TableSelectorWrapper = styled.div`
${({ theme }) => `
.refresh {
display: flex;
align-items: center;
width: 30px;
width: ${REFRESH_WIDTH}px;
margin-left: ${theme.gridUnit}px;
margin-top: ${theme.gridUnit * 5}px;
}
Expand All @@ -66,6 +68,7 @@ const TableSelectorWrapper = styled.div`

.select {
flex: 1;
max-width: calc(100% - ${theme.gridUnit + REFRESH_WIDTH}px)
}
`}
`;
Expand Down
Loading