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(web-console): Add URL deep-linking for UI panels #248

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
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
27 changes: 27 additions & 0 deletions packages/browser-tests/cypress/integration/console/panel.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/// <reference types="cypress" />

const baseUrl = "http://localhost:9999";

describe("URL deep linking", () => {
it("should show import panel", () => {
cy.visit(`${baseUrl}/?bottomPanel=p2`);
cy.get('[data-hook="import-dropbox"]').should("be.visible");
cy.matchImageSnapshot();
});

it("should show news panel", () => {
cy.visit(`${baseUrl}/?sidebar=p1`);
cy.get('[data-hook="news-content"]').should("be.visible");
cy.get('[data-hook="news-panel-button"]').click();
cy.url().should("not.contain", "sidebar=news");
cy.matchImageSnapshot();
});

it("should show create table panel", () => {
cy.visit(`${baseUrl}/?sidebar=p2`);
cy.get('[data-hook="schema-content"]').should("be.visible");
cy.get('[data-hook="create-table-panel-button"]').click();
cy.url().should("not.contain", "sidebar=create");
cy.matchImageSnapshot();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,12 @@ export const CreateTableDialog = () => {
}

useEffect(() => {
setAddTableDialogOpen(activeSidebar === "create" ? "add" : undefined)
setAddTableDialogOpen(activeSidebar === "p2" ? "add" : undefined)
}, [activeSidebar])

useEffect(() => {
if (addTableDialogOpen !== undefined) {
dispatch(actions.console.setActiveSidebar("create"))
dispatch(actions.console.setActiveSidebar("p2"))
}
}, [addTableDialogOpen])

Expand Down Expand Up @@ -72,7 +72,7 @@ export const CreateTableDialog = () => {
onClick: () => {
dispatch(
actions.console.setActiveSidebar(
addTableDialogOpen ? undefined : "create",
addTableDialogOpen ? undefined : "p2",
),
)
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,13 +193,14 @@ export const Dialog = ({
}}
onOpenChange={(isOpen) => {
if (isOpen && action === "add") {
dispatch(
actions.console.setActiveSidebar(isOpen ? "create" : undefined),
)
dispatch(actions.console.setActiveSidebar(isOpen ? "p2" : undefined))
}
}}
>
<StyledContentWrapper mode={action === "add" ? "side" : "modal"}>
<StyledContentWrapper
mode={action === "add" ? "side" : "modal"}
data-hook="schema-content"
>
<Form<SchemaFormValues>
name="table-schema"
defaultValues={defaults}
Expand Down
6 changes: 2 additions & 4 deletions packages/web-console/src/modules/ZeroState/start.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,7 @@ export const Start = () => {
</StyledHeading>
<Actions>
<Action
onClick={() =>
dispatch(actions.console.setActiveBottomPanel("import"))
}
onClick={() => dispatch(actions.console.setActiveBottomPanel("p2"))}
>
<img
alt="File upload icon"
Expand All @@ -79,7 +77,7 @@ export const Start = () => {
<Heading level={5}>Import CSV</Heading>
</Action>
<Action
onClick={() => dispatch(actions.console.setActiveSidebar("create"))}
onClick={() => dispatch(actions.console.setActiveSidebar("p2"))}
>
<img
alt="Create table icon"
Expand Down
30 changes: 15 additions & 15 deletions packages/web-console/src/scenes/Console/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,24 +85,27 @@ const Console = () => {
const importRef = React.useRef<HTMLDivElement>(null)
const horizontalSplitterRef = React.useRef<AllotmentHandle>(null)

const showPanel = (panel: BottomPanel) => {
const showPanel = (panel: BottomPanel | undefined) => {
if (resultRef.current) {
resultRef.current.style.display = panel === "result" ? "flex" : "none"
resultRef.current.style.display =
panel === "p1" && result ? "flex" : "none"
}
if (zeroStateRef.current) {
zeroStateRef.current.style.display =
panel === "zeroState" ? "flex" : "none"
panel === "p0" || (panel === "p1" && !result) ? "flex" : "none"
}
if (importRef.current) {
importRef.current.style.display = panel === "import" ? "flex" : "none"
importRef.current.style.display = panel === "p2" ? "flex" : "none"
}
}

useEffect(() => {
if (resultRef.current && result) {
dispatch(actions.console.setActiveBottomPanel("result"))
} else if (zeroStateRef.current) {
dispatch(actions.console.setActiveBottomPanel("zeroState"))
showPanel("p1")
dispatch(actions.console.setActiveBottomPanel("p1"))
} else if (activeBottomPanel === "p0") {
showPanel("p0")
dispatch(actions.console.setActiveBottomPanel("p0"))
}
}, [result])

Expand Down Expand Up @@ -131,7 +134,7 @@ const Console = () => {
onClick={() => {
dispatch(
actions.console.setActiveTopPanel(
resultsSplitterBasis === 0 ? "tables" : undefined,
resultsSplitterBasis === 0 ? "p1" : undefined,
),
)
updateSettings(
Expand Down Expand Up @@ -189,14 +192,11 @@ const Console = () => {
data-hook={`${mode}-panel-button`}
direction="left"
onClick={() => {
dispatch(
actions.console.setActiveBottomPanel("result"),
)
dispatch(actions.console.setActiveBottomPanel("p1"))
setResultViewMode(mode)
}}
selected={
activeBottomPanel === "result" &&
resultViewMode === mode
activeBottomPanel === "p1" && resultViewMode === mode
}
>
{icon}
Expand All @@ -213,10 +213,10 @@ const Console = () => {
readOnly={readOnly}
{...(!readOnly && {
onClick: () => {
dispatch(actions.console.setActiveBottomPanel("import"))
dispatch(actions.console.setActiveBottomPanel("p2"))
},
})}
selected={activeBottomPanel === "import"}
selected={activeBottomPanel === "p2"}
data-hook="import-panel-button"
>
<ImportIcon size={BUTTON_ICON_SIZE} />
Expand Down
6 changes: 3 additions & 3 deletions packages/web-console/src/scenes/News/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ const News = () => {
}, [newsOpened, enterpriseNews])

useEffect(() => {
setNewsOpened(activeSidebar === "news")
setNewsOpened(activeSidebar === "p1")
}, [activeSidebar])

return (
Expand All @@ -175,7 +175,7 @@ const News = () => {
open={newsOpened}
onOpenChange={async (newsOpened) => {
dispatch(
actions.console.setActiveSidebar(newsOpened ? "news" : undefined),
actions.console.setActiveSidebar(newsOpened ? "p1" : undefined),
)
}}
trigger={
Expand All @@ -197,7 +197,7 @@ const News = () => {
/>
}
>
<Drawer.ContentWrapper mode="side">
<Drawer.ContentWrapper mode="side" data-hook="news-content">
<Items>
{isLoading && !enterpriseNews && (
<Loading>
Expand Down
8 changes: 5 additions & 3 deletions packages/web-console/src/store/Console/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,18 @@ const setConfig = (payload: ConsoleConfigShape): ConsoleAction => ({
type: ConsoleAT.SET_CONFIG,
})

const setActiveTopPanel = (panel: TopPanel): ConsoleAction => ({
const setActiveTopPanel = (panel: TopPanel | undefined): ConsoleAction => ({
payload: panel,
type: ConsoleAT.SET_ACTIVE_TOP_PANEL,
})
const setActiveSidebar = (panel: Sidebar): ConsoleAction => ({
const setActiveSidebar = (panel: Sidebar | undefined): ConsoleAction => ({
payload: panel,
type: ConsoleAT.SET_ACTIVE_SIDEBAR,
})

const setActiveBottomPanel = (panel: BottomPanel): ConsoleAction => ({
const setActiveBottomPanel = (
panel: BottomPanel | undefined,
): ConsoleAction => ({
payload: panel,
type: ConsoleAT.SET_ACTIVE_BOTTOM_PANEL,
})
Expand Down
47 changes: 40 additions & 7 deletions packages/web-console/src/store/Console/reducers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,42 @@ import {
ConsoleAction,
ConsoleAT,
ConsoleStateShape,
} from "../../types"
TopPanel,
Sidebar,
BottomPanel,
} from "./types"

export const initialState: ConsoleStateShape = {
sideMenuOpened: false,
activeTopPanel: "tables",
activeSidebar: undefined,
activeBottomPanel: "zeroState",
const getValidSidebar = (sidebar?: Sidebar) =>
sidebar && ["p1", "p2"].includes(sidebar) ? sidebar : undefined

const getValidTopPanel = (topPanel?: TopPanel) =>
topPanel && ["p1"].includes(topPanel) ? topPanel : undefined

const getValidBottomPanel = (bottomPanel?: BottomPanel): BottomPanel =>
bottomPanel && ["p0", "p1", "p2"].includes(bottomPanel) ? bottomPanel : "p0"

export const getInitialState = (): ConsoleStateShape => {
const url = new URL(window.location.href)
const bottomPanel = (url.searchParams.get("bottomPanel") ?? "") as BottomPanel
const sidebar = (url.searchParams.get("sidebar") ?? "") as Sidebar
const topPanel = (url.searchParams.get("topPanel") ?? "") as TopPanel

return {
sideMenuOpened: getValidSidebar(sidebar) !== undefined,
activeTopPanel: getValidTopPanel(topPanel),
activeSidebar: getValidSidebar(sidebar),
activeBottomPanel: getValidBottomPanel(bottomPanel),
} as ConsoleStateShape
}

const setUrlParam = (key: string, value?: TopPanel | BottomPanel | Sidebar) => {
const url = new URL(window.location.href)
if (value) {
url.searchParams.set(key, value)
} else {
url.searchParams.delete(key)
}
window.history.replaceState({}, "", url.toString())
}

export const defaultConfig: ConsoleConfigShape = {
Expand All @@ -43,9 +72,10 @@ export const defaultConfig: ConsoleConfigShape = {
}

const _console = (
state = initialState,
state = getInitialState(),
action: ConsoleAction,
): ConsoleStateShape => {
const url = new URL(window.location.href)
switch (action.type) {
case ConsoleAT.SET_CONFIG: {
return {
Expand All @@ -65,20 +95,23 @@ const _console = (
}

case ConsoleAT.SET_ACTIVE_TOP_PANEL: {
setUrlParam("topPanel", getValidTopPanel(action.payload))
return {
...state,
activeTopPanel: action.payload,
}
}

case ConsoleAT.SET_ACTIVE_SIDEBAR: {
setUrlParam("sidebar", getValidSidebar(action.payload))
return {
...state,
activeSidebar: action.payload,
}
}

case ConsoleAT.SET_ACTIVE_BOTTOM_PANEL: {
setUrlParam("bottomPanel", getValidBottomPanel(action.payload))
return {
...state,
activeBottomPanel: action.payload,
Expand Down
12 changes: 7 additions & 5 deletions packages/web-console/src/store/Console/selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,16 @@ const getConfig: (store: StoreShape) => ConsoleConfigShape = (store) =>
const getSideMenuOpened: (store: StoreShape) => boolean = (store) =>
store.console.sideMenuOpened

const getActiveTopPanel: (store: StoreShape) => TopPanel = (store) =>
store.console.activeTopPanel
const getActiveTopPanel: (store: StoreShape) => TopPanel | undefined = (
store,
) => store.console.activeTopPanel

const getActiveSidebar: (store: StoreShape) => Sidebar = (store) =>
const getActiveSidebar: (store: StoreShape) => Sidebar | undefined = (store) =>
store.console.activeSidebar

const getActiveBottomPanel: (store: StoreShape) => BottomPanel = (store) =>
store.console.activeBottomPanel
const getActiveBottomPanel: (store: StoreShape) => BottomPanel | undefined = (
store,
) => store.console.activeBottomPanel

export default {
getConfig,
Expand Down
18 changes: 9 additions & 9 deletions packages/web-console/src/store/Console/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@ export type QueryGroup = {
queries: Query[]
}

export type TopPanel = "tables" | undefined
export type TopPanel = "p1"

export type Sidebar = "news" | "create" | undefined
export type Sidebar = "p1" | "p2"

export type BottomPanel = "result" | "zeroState" | "import"
export type BottomPanel = "p0" | "p1" | "p2"

export type ConsoleConfigShape = Readonly<{
githubBanner: boolean
Expand All @@ -48,9 +48,9 @@ export type ConsoleConfigShape = Readonly<{
export type ConsoleStateShape = Readonly<{
config?: ConsoleConfigShape
sideMenuOpened: boolean
activeTopPanel: TopPanel
activeSidebar: Sidebar
activeBottomPanel: BottomPanel
activeTopPanel?: TopPanel
activeSidebar?: Sidebar
activeBottomPanel?: BottomPanel
}>

export enum ConsoleAT {
Expand Down Expand Up @@ -82,17 +82,17 @@ type ToggleSideMenuAction = Readonly<{
}>

type setActiveTopPanelAction = Readonly<{
payload: TopPanel
payload?: TopPanel
type: ConsoleAT.SET_ACTIVE_TOP_PANEL
}>

type setActiveSidebarAction = Readonly<{
payload: Sidebar
payload?: Sidebar
type: ConsoleAT.SET_ACTIVE_SIDEBAR
}>

type setActiveBottomPanelAction = Readonly<{
payload: BottomPanel
payload?: BottomPanel
type: ConsoleAT.SET_ACTIVE_BOTTOM_PANEL
}>

Expand Down
6 changes: 2 additions & 4 deletions packages/web-console/src/store/reducers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,7 @@

import { combineReducers } from "redux"

import _console, {
initialState as consoleInitialState,
} from "./Console/reducers"
import _console, { getInitialState } from "./Console/reducers"

import query, { initialState as queryInitialState } from "./Query/reducers"
import telemetry, {
Expand All @@ -40,7 +38,7 @@ const rootReducer = combineReducers({
})

export const initialState = {
console: consoleInitialState,
console: getInitialState(),
query: queryInitialState,
telemetry: telemetryInitialState,
}
Expand Down