feat(notebooks): introduce minimap (#18270)
* refactor: extract notebook scoped scss variables to own file * feat: WIP introduce minimap components * feat: attempt to add minimap state to AppContext * refactor: update notebooks layout to handle minimap alongside pipe list * refactor: remove border from markdown preview * refactor: add spacing between minimap items * chore: cleanup styles and markup * feat: give panels an ID attribute to facilitate scrolling to their position * feat: enable minimap to update scroll position of pipe list via notebook context * feat: make minimap scrollable * feat: style hidden pipes differently in minimap * fix: remove console logs * refactor: move scrolling related code into separate context * refactor: use refs instead of IDs for getting a panel's scroll position * refactor: remove minimap state from AppSettingProvider * refactor: use persisted redux to store minimap visibility preference Seems like the kind of thing that would have ended up here eventually so I'll save future me some trouble and do it now * fix: add & repair unit testspull/18257/head^2
parent
53794bfae5
commit
7df56f5d6c
|
@ -82,6 +82,7 @@ describe('Dashboards.Selector', () => {
|
|||
autoRefresh: 0,
|
||||
showTemplateControlBar: false,
|
||||
navBarState: 'expanded',
|
||||
notebookMiniMapState: 'expanded',
|
||||
timeZone: 'Local' as TimeZone,
|
||||
theme: 'dark',
|
||||
},
|
||||
|
@ -103,6 +104,7 @@ describe('Dashboards.Selector', () => {
|
|||
autoRefresh: 0,
|
||||
showTemplateControlBar: false,
|
||||
navBarState: 'expanded',
|
||||
notebookMiniMapState: 'expanded',
|
||||
timeZone: 'UTC' as TimeZone,
|
||||
theme: 'dark',
|
||||
},
|
||||
|
|
|
@ -23,6 +23,7 @@ export const localState: LocalStorage = {
|
|||
navBarState: 'expanded',
|
||||
timeZone: 'Local' as TimeZone,
|
||||
theme: 'dark',
|
||||
notebookMiniMapState: 'expanded',
|
||||
},
|
||||
},
|
||||
flags: {
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
@import '@influxdata/clockface/dist/variables.scss';
|
||||
|
||||
$notebook-header-height: 46px;
|
||||
|
||||
$notebook-panel--gutter: $cf-marg-d;
|
||||
$notebook-panel--bg: mix($g1-raven, $g2-kevlar, 50%);
|
||||
|
||||
$notebook-divider-height: ($cf-marg-a * 2) + $cf-border;
|
||||
$notebook-divider-color: $g2-kevlar;
|
|
@ -2,8 +2,10 @@ import React, {FC} from 'react'
|
|||
|
||||
import {Page} from '@influxdata/clockface'
|
||||
import {NotebookProvider} from 'src/notebooks/context/notebook'
|
||||
import {ScrollProvider} from 'src/notebooks/context/scroll'
|
||||
import Header from 'src/notebooks/components/header'
|
||||
import PipeList from 'src/notebooks/components/PipeList'
|
||||
import MiniMap from 'src/notebooks/components/minimap/MiniMap'
|
||||
|
||||
// NOTE: uncommon, but using this to scope the project
|
||||
// within the page and not bleed it's dependancies outside
|
||||
|
@ -13,10 +15,21 @@ import 'src/notebooks/style.scss'
|
|||
const NotebookPage: FC = () => {
|
||||
return (
|
||||
<NotebookProvider>
|
||||
<ScrollProvider>
|
||||
<Page titleTag="Notebook">
|
||||
<Header />
|
||||
<Page.Contents
|
||||
fullWidth={true}
|
||||
scrollable={false}
|
||||
className="notebook-page"
|
||||
>
|
||||
<div className="notebook">
|
||||
<MiniMap />
|
||||
<PipeList />
|
||||
</div>
|
||||
</Page.Contents>
|
||||
</Page>
|
||||
</ScrollProvider>
|
||||
</NotebookProvider>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -3,22 +3,23 @@ import React, {FC, useContext, useCallback} from 'react'
|
|||
|
||||
// Contexts
|
||||
import {NotebookContext} from 'src/notebooks/context/notebook'
|
||||
import {ScrollContext} from 'src/notebooks/context/scroll'
|
||||
|
||||
// Components
|
||||
import NotebookPipe from 'src/notebooks/components/NotebookPipe'
|
||||
import EmptyPipeList from 'src/notebooks/components/EmptyPipeList'
|
||||
import {Page} from '@influxdata/clockface'
|
||||
import {DapperScrollbars} from '@influxdata/clockface'
|
||||
|
||||
const PipeList: FC = () => {
|
||||
const {id, pipes, updatePipe} = useContext(NotebookContext)
|
||||
const {scrollPosition} = useContext(ScrollContext)
|
||||
const update = useCallback(updatePipe, [id])
|
||||
|
||||
const scrollable = !!pipes.length
|
||||
if (!pipes.length) {
|
||||
return <EmptyPipeList />
|
||||
}
|
||||
|
||||
let _pipes: JSX.Element | JSX.Element[] = <EmptyPipeList />
|
||||
|
||||
if (pipes.length) {
|
||||
_pipes = pipes.map((_, index) => {
|
||||
const _pipes = pipes.map((_, index) => {
|
||||
return (
|
||||
<NotebookPipe
|
||||
key={`pipe-${id}-${index}`}
|
||||
|
@ -28,12 +29,16 @@ const PipeList: FC = () => {
|
|||
/>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<Page.Contents fullWidth={true} scrollable={scrollable}>
|
||||
<DapperScrollbars
|
||||
className="notebook-main"
|
||||
autoHide={true}
|
||||
noScrollX={true}
|
||||
scrollTop={scrollPosition}
|
||||
>
|
||||
{_pipes}
|
||||
</Page.Contents>
|
||||
</DapperScrollbars>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ import React, {FC} from 'react'
|
|||
import {Page} from '@influxdata/clockface'
|
||||
import AddButtons from 'src/notebooks/components/AddButtons'
|
||||
import Buttons from 'src/notebooks/components/header/Buttons'
|
||||
import MiniMapToggle from 'src/notebooks/components/minimap/MiniMapToggle'
|
||||
|
||||
const FULL_WIDTH = true
|
||||
|
||||
|
@ -17,6 +18,7 @@ const Header: FC = () => (
|
|||
<AddButtons />
|
||||
</Page.ControlBarLeft>
|
||||
<Page.ControlBarRight>
|
||||
<MiniMapToggle />
|
||||
<Buttons />
|
||||
</Page.ControlBarRight>
|
||||
</Page.ControlBar>
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
@import '@influxdata/clockface/dist/variables.scss';
|
||||
@import '~src/notebooks/NotebookVariables.scss';
|
||||
|
||||
.notebook-minimap {
|
||||
flex: 0 0 200px;
|
||||
border-right: $cf-border solid $notebook-divider-color;
|
||||
}
|
||||
|
||||
.notebook-minimap--list {
|
||||
padding-left: $cf-marg-d;
|
||||
padding-right: $cf-marg-b;
|
||||
}
|
||||
|
||||
.notebook-minimap--item {
|
||||
font-size: 14px;
|
||||
font-weight: $cf-font-weight--medium;
|
||||
color: $g11-sidewalk;
|
||||
transition: color 0.25s ease, background-color 0.25s ease;
|
||||
margin-bottom: $cf-border;
|
||||
height: $cf-marg-d;
|
||||
line-height: $cf-marg-d;
|
||||
padding: 0 $cf-marg-b;
|
||||
border-radius: $cf-radius;
|
||||
user-select: none;
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.notebook-minimap--item__hidden {
|
||||
color: $g8-storm;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.notebook-minimap--item:hover,
|
||||
.notebook-minimap--item__focus {
|
||||
color: $g18-cloud;
|
||||
background-color: $g3-castle;
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
// Libraries
|
||||
import React, {FC, useContext} from 'react'
|
||||
import {connect} from 'react-redux'
|
||||
|
||||
// Contexts
|
||||
import {NotebookContext, PipeMeta} from 'src/notebooks/context/notebook'
|
||||
import {ScrollContext} from 'src/notebooks/context/scroll'
|
||||
|
||||
// Components
|
||||
import {DapperScrollbars} from '@influxdata/clockface'
|
||||
import MiniMapItem from 'src/notebooks/components/minimap/MiniMapItem'
|
||||
|
||||
// Types
|
||||
import {AppState, NotebookMiniMapState} from 'src/types'
|
||||
|
||||
// Styles
|
||||
import 'src/notebooks/components/minimap/MiniMap.scss'
|
||||
|
||||
interface StateProps {
|
||||
notebookMiniMapState: NotebookMiniMapState
|
||||
}
|
||||
|
||||
const MiniMap: FC<StateProps> = ({notebookMiniMapState}) => {
|
||||
const {meta, updateMeta} = useContext(NotebookContext)
|
||||
const {scrollToPipe} = useContext(ScrollContext)
|
||||
|
||||
if (notebookMiniMapState === 'collapsed') {
|
||||
return null
|
||||
}
|
||||
|
||||
const handleClick = (idx: number): void => {
|
||||
const {panelRef} = meta[idx]
|
||||
scrollToPipe(panelRef)
|
||||
updateMeta(idx, {focus: true} as PipeMeta)
|
||||
}
|
||||
|
||||
const pipes = meta.map((pipe, index) => (
|
||||
<MiniMapItem
|
||||
key={`minimap-${pipe.title}-${index}`}
|
||||
title={pipe.title}
|
||||
focus={pipe.focus}
|
||||
visible={pipe.visible}
|
||||
index={index}
|
||||
onClick={handleClick}
|
||||
/>
|
||||
))
|
||||
|
||||
return (
|
||||
<DapperScrollbars className="notebook-minimap" autoHide={true}>
|
||||
<div className="notebook-minimap--list">{pipes}</div>
|
||||
</DapperScrollbars>
|
||||
)
|
||||
}
|
||||
|
||||
const mstp = (state: AppState): StateProps => {
|
||||
const {
|
||||
app: {
|
||||
persisted: {notebookMiniMapState},
|
||||
},
|
||||
} = state
|
||||
|
||||
return {
|
||||
notebookMiniMapState,
|
||||
}
|
||||
}
|
||||
|
||||
export default connect<StateProps, {}>(
|
||||
mstp,
|
||||
null
|
||||
)(MiniMap)
|
|
@ -0,0 +1,30 @@
|
|||
// Libraries
|
||||
import React, {FC} from 'react'
|
||||
import classnames from 'classnames'
|
||||
|
||||
interface Props {
|
||||
title: string
|
||||
focus: boolean
|
||||
visible: boolean
|
||||
index: number
|
||||
onClick: (index: number) => void
|
||||
}
|
||||
|
||||
const MiniMapItem: FC<Props> = ({title, focus, onClick, index, visible}) => {
|
||||
const className = classnames('notebook-minimap--item', {
|
||||
'notebook-minimap--item__focus': focus,
|
||||
'notebook-minimap--item__hidden': !visible,
|
||||
})
|
||||
|
||||
const handleClick = (): void => {
|
||||
onClick(index)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={className} onClick={handleClick}>
|
||||
{title}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default MiniMapItem
|
|
@ -0,0 +1,65 @@
|
|||
// Libraries
|
||||
import React, {FC} from 'react'
|
||||
import {connect} from 'react-redux'
|
||||
|
||||
// Components
|
||||
import {SlideToggle, InputLabel} from '@influxdata/clockface'
|
||||
|
||||
// Actions
|
||||
import {setNotebookMiniMapState} from 'src/shared/actions/app'
|
||||
|
||||
// Types
|
||||
import {AppState, NotebookMiniMapState} from 'src/types'
|
||||
|
||||
interface StateProps {
|
||||
notebookMiniMapState: NotebookMiniMapState
|
||||
}
|
||||
|
||||
interface DispatchProps {
|
||||
handleSetNotebookMiniMapState: typeof setNotebookMiniMapState
|
||||
}
|
||||
|
||||
type Props = StateProps & DispatchProps
|
||||
|
||||
const MiniMapToggle: FC<Props> = ({
|
||||
notebookMiniMapState,
|
||||
handleSetNotebookMiniMapState,
|
||||
}) => {
|
||||
const active = notebookMiniMapState === 'expanded'
|
||||
|
||||
const handleChange = (): void => {
|
||||
if (active) {
|
||||
handleSetNotebookMiniMapState('collapsed')
|
||||
} else {
|
||||
handleSetNotebookMiniMapState('expanded')
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<SlideToggle active={active} onChange={handleChange} />
|
||||
<InputLabel>Minimap</InputLabel>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const mstp = (state: AppState): StateProps => {
|
||||
const {
|
||||
app: {
|
||||
persisted: {notebookMiniMapState},
|
||||
},
|
||||
} = state
|
||||
|
||||
return {
|
||||
notebookMiniMapState,
|
||||
}
|
||||
}
|
||||
|
||||
const mdtp: DispatchProps = {
|
||||
handleSetNotebookMiniMapState: setNotebookMiniMapState,
|
||||
}
|
||||
|
||||
export default connect<StateProps, DispatchProps>(
|
||||
mstp,
|
||||
mdtp
|
||||
)(MiniMapToggle)
|
|
@ -1,5 +1,13 @@
|
|||
// Libraries
|
||||
import React, {FC, useContext, useCallback, ReactNode, MouseEvent} from 'react'
|
||||
import React, {
|
||||
FC,
|
||||
useContext,
|
||||
useCallback,
|
||||
useEffect,
|
||||
ReactNode,
|
||||
MouseEvent,
|
||||
useRef,
|
||||
} from 'react'
|
||||
import classnames from 'classnames'
|
||||
|
||||
// Components
|
||||
|
@ -77,10 +85,15 @@ const NotebookPanelHeader: FC<HeaderProps> = ({index, controls}) => {
|
|||
|
||||
const NotebookPanel: FC<Props> = ({index, children, controls}) => {
|
||||
const {meta, updateMeta} = useContext(NotebookContext)
|
||||
const panelRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
const isVisible = meta[index].visible
|
||||
const isFocused = meta[index].focus
|
||||
|
||||
useEffect(() => {
|
||||
updateMeta(index, {panelRef} as PipeMeta)
|
||||
})
|
||||
|
||||
const panelClassName = classnames('notebook-panel', {
|
||||
[`notebook-panel__visible`]: isVisible,
|
||||
[`notebook-panel__hidden`]: !isVisible,
|
||||
|
@ -105,7 +118,7 @@ const NotebookPanel: FC<Props> = ({index, children, controls}) => {
|
|||
|
||||
return (
|
||||
<ClickOutside onClickOutside={handleClickOutside}>
|
||||
<div className={panelClassName} onClick={handleClick}>
|
||||
<div className={panelClassName} onClick={handleClick} ref={panelRef}>
|
||||
<NotebookPanelHeader index={index} controls={controls} />
|
||||
<div className="notebook-panel--body">{children}</div>
|
||||
</div>
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import React, {FC, useState, useCallback} from 'react'
|
||||
import React, {FC, useState, useCallback, RefObject} from 'react'
|
||||
import {PipeData} from 'src/notebooks'
|
||||
|
||||
export interface PipeMeta {
|
||||
title: string
|
||||
visible: boolean
|
||||
focus: boolean
|
||||
panelRef: RefObject<HTMLDivElement>
|
||||
}
|
||||
|
||||
export interface NotebookContextType {
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
// Libraries
|
||||
import React, {FC, useState, RefObject} from 'react'
|
||||
|
||||
export interface ScrollContextType {
|
||||
scrollPosition: number
|
||||
scrollToPipe: (panelRef: RefObject<HTMLDivElement>) => void
|
||||
}
|
||||
|
||||
export const DEFAULT_CONTEXT: ScrollContextType = {
|
||||
scrollPosition: 0,
|
||||
scrollToPipe: () => {},
|
||||
}
|
||||
|
||||
export const ScrollContext = React.createContext<ScrollContextType>(
|
||||
DEFAULT_CONTEXT
|
||||
)
|
||||
|
||||
export const ScrollProvider: FC = ({children}) => {
|
||||
const [scrollPosition, setListScrollPosition] = useState(
|
||||
DEFAULT_CONTEXT.scrollPosition
|
||||
)
|
||||
|
||||
const scrollToPipe = (panelRef: RefObject<HTMLDivElement>) => {
|
||||
if (panelRef && panelRef.current) {
|
||||
const {offsetTop} = panelRef.current
|
||||
setListScrollPosition(offsetTop)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<ScrollContext.Provider
|
||||
value={{
|
||||
scrollPosition,
|
||||
scrollToPipe,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</ScrollContext.Provider>
|
||||
)
|
||||
}
|
|
@ -1,26 +1,15 @@
|
|||
@import "@influxdata/clockface/dist/variables.scss";
|
||||
@import '@influxdata/clockface/dist/variables.scss';
|
||||
|
||||
$notebook-panel--bg: mix($g1-raven, $g2-kevlar, 50%);
|
||||
|
||||
.notebook-panel--markdown,
|
||||
.notebook-panel--markdown-editor {
|
||||
.notebook-panel--markdown {
|
||||
font-size: 14px;
|
||||
line-height: 16px;
|
||||
padding: $cf-marg-b;
|
||||
border-radius: $cf-radius - 1px;
|
||||
border-width: $cf-border;
|
||||
border-style: solid;
|
||||
transition: border-color 0.25s ease;
|
||||
}
|
||||
|
||||
.notebook-panel--markdown {
|
||||
border-color: $g1-raven;
|
||||
|
||||
.notebook-panel:hover & {
|
||||
border-color: $notebook-panel--bg;
|
||||
}
|
||||
}
|
||||
|
||||
.notebook-panel--body .markdown-editor--monaco {
|
||||
position: relative;
|
||||
|
||||
|
@ -28,4 +17,3 @@ $notebook-panel--bg: mix($g1-raven, $g2-kevlar, 50%);
|
|||
min-height: 100px;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,19 +1,18 @@
|
|||
@import '@influxdata/clockface/dist/variables.scss';
|
||||
|
||||
$notebook-header-height: 46px;
|
||||
$notebook-size-toggle: 16px;
|
||||
$notebook-left-bar: 22px;
|
||||
$notebook-panel--bg: mix($g1-raven, $g2-kevlar, 50%);
|
||||
|
||||
$notebook-divider-height: ($cf-marg-a * 2) + $cf-border;
|
||||
@import '~src/notebooks/NotebookVariables.scss';
|
||||
|
||||
.notebook {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-direction: row;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.notebook-page .cf-page-contents__fluid {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.notebook--add-cell-label {
|
||||
user-select: none;
|
||||
margin: 0 $cf-marg-c 0 0;
|
||||
|
@ -21,16 +20,26 @@ $notebook-divider-height: ($cf-marg-a * 2) + $cf-border;
|
|||
font-weight: $cf-font-weight--medium;
|
||||
}
|
||||
|
||||
.notebook-main,
|
||||
.notebook-empty {
|
||||
flex: 1 0 0;
|
||||
}
|
||||
|
||||
.notebook-empty {
|
||||
padding: 0 $notebook-panel--gutter;
|
||||
}
|
||||
|
||||
.notebook-panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
border-radius: $cf-radius;
|
||||
margin: 0 $notebook-panel--gutter;
|
||||
|
||||
&:after {
|
||||
content: '';
|
||||
height: $cf-border;
|
||||
background-color: $g2-kevlar;
|
||||
background-color: $notebook-divider-color;
|
||||
border-radius: $cf-border / 2;
|
||||
margin: $cf-marg-a 0;
|
||||
}
|
||||
|
@ -121,37 +130,6 @@ $notebook-divider-height: ($cf-marg-a * 2) + $cf-border;
|
|||
top: -2px;
|
||||
}
|
||||
|
||||
.notebook-panel--toggle {
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: $notebook-left-bar;
|
||||
background-color: $g2-kevlar;
|
||||
border-radius: $cf-radius;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
|
||||
&:after {
|
||||
box-sizing: border-box;
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: $notebook-size-toggle;
|
||||
transition: height 0.25s cubic-bezier(0.25, 1, 0.5, 1),
|
||||
border-color 0.25s ease;
|
||||
border: $cf-border solid $g8-storm;
|
||||
border-radius: $cf-radius / 2;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
&:after {
|
||||
border-color: $g13-mist;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.notebook-panel--body {
|
||||
border-radius: 0 0 $cf-radius $cf-radius;
|
||||
padding: $cf-marg-b;
|
||||
|
@ -198,15 +176,6 @@ $notebook-divider-height: ($cf-marg-a * 2) + $cf-border;
|
|||
min-height: $notebook-header-height;
|
||||
}
|
||||
|
||||
/*
|
||||
Notebook Action Bar aka Footer
|
||||
------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
.notebook--actions {
|
||||
margin-top: $cf-marg-c;
|
||||
}
|
||||
|
||||
/*
|
||||
Visualization Panel
|
||||
------------------------------------------------------------------------------
|
||||
|
@ -242,6 +211,6 @@ $notebook-divider-height: ($cf-marg-a * 2) + $cf-border;
|
|||
flex-wrap: wrap;
|
||||
|
||||
> * {
|
||||
margin-left: 4px;
|
||||
margin-left: $cf-marg-a;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,12 +4,13 @@ import {notify} from 'src/shared/actions/notifications'
|
|||
import {presentationMode} from 'src/shared/copy/notifications'
|
||||
|
||||
import {Dispatch} from 'redux'
|
||||
import {TimeZone, Theme, NavBarState} from 'src/types'
|
||||
import {TimeZone, Theme, NavBarState, NotebookMiniMapState} from 'src/types'
|
||||
|
||||
export enum ActionTypes {
|
||||
EnablePresentationMode = 'ENABLE_PRESENTATION_MODE',
|
||||
DisablePresentationMode = 'DISABLE_PRESENTATION_MODE',
|
||||
SetNavBarState = 'SET_NAV_BAR_STATE',
|
||||
SetNotebookMiniMapState = 'SET_NOTEBOOK_MINI_MAP_STATE',
|
||||
SetAutoRefresh = 'SET_AUTOREFRESH',
|
||||
SetTimeZone = 'SET_APP_TIME_ZONE',
|
||||
TemplateControlBarVisibilityToggled = 'TemplateControlBarVisibilityToggledAction',
|
||||
|
@ -20,6 +21,7 @@ export type Action =
|
|||
| ReturnType<typeof enablePresentationMode>
|
||||
| ReturnType<typeof disablePresentationMode>
|
||||
| ReturnType<typeof setNavBarState>
|
||||
| ReturnType<typeof setNotebookMiniMapState>
|
||||
| ReturnType<typeof setAutoRefresh>
|
||||
| ReturnType<typeof setTimeZone>
|
||||
| ReturnType<typeof setTheme>
|
||||
|
@ -54,6 +56,14 @@ export const setNavBarState = (navBarState: NavBarState) =>
|
|||
navBarState,
|
||||
} as const)
|
||||
|
||||
export const setNotebookMiniMapState = (
|
||||
notebookMiniMapState: NotebookMiniMapState
|
||||
) =>
|
||||
({
|
||||
type: ActionTypes.SetNotebookMiniMapState,
|
||||
notebookMiniMapState,
|
||||
} as const)
|
||||
|
||||
export const setAutoRefresh = (milliseconds: number) =>
|
||||
({
|
||||
type: ActionTypes.SetAutoRefresh,
|
||||
|
|
|
@ -4,6 +4,7 @@ import {
|
|||
disablePresentationMode,
|
||||
setTheme,
|
||||
setNavBarState,
|
||||
setNotebookMiniMapState,
|
||||
setAutoRefresh,
|
||||
} from 'src/shared/actions/app'
|
||||
import {TimeZone} from 'src/types'
|
||||
|
@ -18,6 +19,7 @@ describe('Shared.Reducers.appReducer', () => {
|
|||
autoRefresh: 0,
|
||||
showTemplateControlBar: false,
|
||||
navBarState: 'expanded',
|
||||
notebookMiniMapState: 'expanded',
|
||||
timeZone: 'Local' as TimeZone,
|
||||
theme: 'dark',
|
||||
},
|
||||
|
@ -65,6 +67,28 @@ describe('Shared.Reducers.appReducer', () => {
|
|||
expect(reducedState.persisted.navBarState).toBe('expanded')
|
||||
})
|
||||
|
||||
it('should handle SET_NOTEBOOK_MINI_MAP_STATE to collapsed', () => {
|
||||
const reducedState = appReducer(
|
||||
initialState,
|
||||
setNotebookMiniMapState('collapsed')
|
||||
)
|
||||
|
||||
expect(reducedState.persisted.notebookMiniMapState).toBe('collapsed')
|
||||
})
|
||||
|
||||
it('should handle SET_NOTEBOOK_MINI_MAP_STATE to expanded', () => {
|
||||
Object.assign(initialState, {
|
||||
persisted: {notebookMiniMapState: 'collapsed'},
|
||||
})
|
||||
|
||||
const reducedState = appReducer(
|
||||
initialState,
|
||||
setNotebookMiniMapState('expanded')
|
||||
)
|
||||
|
||||
expect(reducedState.persisted.notebookMiniMapState).toBe('expanded')
|
||||
})
|
||||
|
||||
it('should handle SET_AUTOREFRESH', () => {
|
||||
const expectedMs = 15000
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ import {combineReducers} from 'redux'
|
|||
// Types
|
||||
import {ActionTypes, Action} from 'src/shared/actions/app'
|
||||
import {AUTOREFRESH_DEFAULT_INTERVAL} from 'src/shared/constants'
|
||||
import {TimeZone, NavBarState, Theme} from 'src/types'
|
||||
import {TimeZone, NavBarState, Theme, NotebookMiniMapState} from 'src/types'
|
||||
|
||||
export interface AppState {
|
||||
ephemeral: {
|
||||
|
@ -15,6 +15,7 @@ export interface AppState {
|
|||
timeZone: TimeZone
|
||||
navBarState: NavBarState
|
||||
theme: Theme
|
||||
notebookMiniMapState: NotebookMiniMapState
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -28,6 +29,7 @@ const initialState: AppState = {
|
|||
showTemplateControlBar: false,
|
||||
timeZone: 'Local',
|
||||
navBarState: 'collapsed',
|
||||
notebookMiniMapState: 'expanded',
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -82,6 +84,12 @@ const appPersistedReducer = (
|
|||
return {...state, timeZone}
|
||||
}
|
||||
|
||||
case ActionTypes.SetNotebookMiniMapState: {
|
||||
const notebookMiniMapState = action.notebookMiniMapState
|
||||
|
||||
return {...state, notebookMiniMapState}
|
||||
}
|
||||
|
||||
case 'SET_NAV_BAR_STATE': {
|
||||
const navBarState = action.navBarState
|
||||
return {
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
export type CurrentPage = 'dashboard' | 'not set'
|
||||
export type Theme = 'light' | 'dark'
|
||||
export type NavBarState = 'expanded' | 'collapsed'
|
||||
export type NotebookMiniMapState = 'expanded' | 'collapsed'
|
||||
|
|
Loading…
Reference in New Issue