Skip to content

Commit

Permalink
feat(sqllab): Make LeftBar width resizable (#21300)
Browse files Browse the repository at this point in the history
  • Loading branch information
justinpark committed Sep 2, 2022
1 parent 38782bb commit 2d70ef6
Show file tree
Hide file tree
Showing 11 changed files with 206 additions and 112 deletions.
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 @@ -305,7 +305,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

0 comments on commit 2d70ef6

Please sign in to comment.