Skip to content
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
8 changes: 7 additions & 1 deletion redisinsight/ui/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,14 +79,20 @@ module.exports = {
'index',
],
pathGroups: [
{
pattern: 'apiSrc/**',
group: 'internal',
position: 'after'
},
{
pattern: '{.,..}/*.scss', // same directory only
// pattern: '{.,..}/**/*\.scss' // same & outside directories (e.g. import '../foo/foo.scss')
group: 'object',
position: 'after'
}
],
warnOnUnassignedImports: true
warnOnUnassignedImports: true,
pathGroupsExcludedImportTypes: ['builtin']
},
],
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { useContext, useEffect, useRef, useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import cx from 'classnames'
import { v4 as uuidv4 } from 'uuid'
import { EuiFlexItem, EuiIcon, EuiLoadingContent, EuiTextColor } from '@elastic/eui'
import { pluginApi } from 'uiSrc/services/PluginAPI'
import { ThemeContext } from 'uiSrc/contexts/themeContext'
Expand Down Expand Up @@ -210,7 +211,7 @@ const QueryCardCliPlugin = (props: Props) => {
useEffect(() => {
const view = visualizations.find((visualization: IPluginVisualization) => visualization.uniqId === id)
if (view) {
generatedIframeNameRef.current = `${view.plugin.name}-${Date.now()}`
generatedIframeNameRef.current = `${view.plugin.name}-${uuidv4()}`
setCurrentView(view)

const { plugin } = view
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useHistory, useLocation } from 'react-router-dom'
import { useSelector, useDispatch } from 'react-redux'
import cx from 'classnames'
import { EuiListGroup, EuiLoadingContent } from '@elastic/eui'
import { CodeButtonParams, ExecuteButtonMode } from 'uiSrc/pages/workbench/components/enablement-area/interfaces'
import { EnablementAreaComponent, IEnablementAreaItem } from 'uiSrc/slices/interfaces'
import { EnablementAreaProvider, IInternalPage } from 'uiSrc/pages/workbench/contexts/enablementAreaContext'
import { appContextWorkbenchEA, resetWorkbenchEAItem } from 'uiSrc/slices/app/context'
Expand All @@ -14,7 +15,7 @@ import {
InternalLink,
LazyCodeButton,
LazyInternalPage,
PlainText
PlainText,
} from './components'

import styles from './styles.module.scss'
Expand All @@ -25,7 +26,11 @@ export interface Props {
guides: Record<string, IEnablementAreaItem>
tutorials: Record<string, IEnablementAreaItem>
loading: boolean
openScript: (script: string, path?: string, name?: string) => void
openScript: (
script: string,
execute?: { mode?: ExecuteButtonMode, params?: CodeButtonParams },
file?: { path?: string, name?: string }
) => void
onOpenInternalPage: (page: IInternalPage) => void
isCodeBtnDisabled?: boolean
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import React from 'react'
import { instance, mock } from 'ts-mockito'
import { fireEvent, render } from 'uiSrc/utils/test-utils'
import { EnablementAreaProvider, defaultValue } from 'uiSrc/pages/workbench/contexts/enablementAreaContext'
import { MONACO_MANUAL } from 'uiSrc/constants'
import { ExecuteButtonMode } from 'uiSrc/pages/workbench/components/enablement-area/interfaces'
import { defaultValue, EnablementAreaProvider } from 'uiSrc/pages/workbench/contexts/enablementAreaContext'
import { fireEvent, render, screen } from 'uiSrc/utils/test-utils'

import Code, { Props } from './Code'

Expand All @@ -29,6 +30,21 @@ describe('Code', () => {

const link = queryByTestId(`preselect-${label}`)
fireEvent.click(link as Element)
expect(setScript).toBeCalledWith(MONACO_MANUAL)
expect(setScript).toBeCalledWith(MONACO_MANUAL, {}, undefined)
})

it('should correctly set script with auto execute', () => {
const setScript = jest.fn()
const label = 'Manual'

render(
<EnablementAreaProvider value={{ ...defaultValue, setScript }}>
<Code {...instance(mockedProps)} label={label} mode={ExecuteButtonMode.Auto}>{MONACO_MANUAL}</Code>
</EnablementAreaProvider>
)

screen.debug()
fireEvent.click(screen.queryByTestId(`preselect-auto-${label}`) as Element)
expect(setScript).toBeCalledWith(MONACO_MANUAL, { mode: ExecuteButtonMode.Auto }, undefined)
})
})
Original file line number Diff line number Diff line change
@@ -1,33 +1,48 @@
import React, { useContext } from 'react'
import { startCase } from 'lodash'
import React, { useContext } from 'react'
import { useLocation } from 'react-router-dom'

import { getFileInfo, parseParams } from 'uiSrc/pages/workbench/components/enablement-area/EnablementArea/utils'
import { CodeButtonParams, ExecuteButtonMode } from 'uiSrc/pages/workbench/components/enablement-area/interfaces'
import EnablementAreaContext from 'uiSrc/pages/workbench/contexts/enablementAreaContext'
import { getFileInfo } from 'uiSrc/pages/workbench/components/enablement-area/EnablementArea/utils/getFileInfo'
import { Maybe } from 'uiSrc/utils'

import CodeButton from '../CodeButton'

export interface Props {
label: string;
children: string;
label: string
children: string
params?: string
mode?: ExecuteButtonMode
}

const Code = ({ children, ...rest }: Props) => {
const Code = ({ children, params, mode, ...rest }: Props) => {
const { search } = useLocation()
const { setScript, isCodeBtnDisabled } = useContext(EnablementAreaContext)

const loadContent = () => {
const loadContent = (execute: { mode?: ExecuteButtonMode, params?: CodeButtonParams }) => {
const pagePath = new URLSearchParams(search).get('item')
let file: Maybe<{ path: string, name: string }>

if (pagePath) {
const pageInfo = getFileInfo(pagePath)
setScript(children, `${pageInfo.location}/${pageInfo.name}`, startCase(rest.label))
} else {
setScript(children)
file = {
path: `${pageInfo.location}/${pageInfo.name}`,
name: startCase(rest.label)
}
}

setScript(children, execute, file)
}

return (
<CodeButton className="mb-s mt-s" onClick={loadContent} disabled={isCodeBtnDisabled} {...rest} />
<CodeButton
className="mb-s mt-s"
onClick={loadContent}
params={parseParams(params)}
mode={mode}
disabled={isCodeBtnDisabled}
{...rest}
/>
)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react'
import { instance, mock } from 'ts-mockito'
import { ExecuteButtonMode } from 'uiSrc/pages/workbench/components/enablement-area/interfaces'
import { fireEvent, render, screen } from 'uiSrc/utils/test-utils'
import CodeButton, { Props } from './CodeButton'

Expand All @@ -14,6 +15,14 @@ describe('CodeButton', () => {
expect(component).toBeTruthy()
expect(container).toHaveTextContent(label)
})

it('should not render auto-execute button', () => {
const label = 'Manual'
render(<CodeButton {...instance(mockedProps)} label={label} />)

expect(screen.queryByTestId(`preselect-auto-${label}`)).not.toBeInTheDocument()
})

it('should call onClick function', () => {
const onClick = jest.fn()
const label = 'Manual'
Expand All @@ -23,4 +32,22 @@ describe('CodeButton', () => {

expect(onClick).toBeCalled()
})

it('should call onClick with auto execute param', () => {
const onClick = jest.fn()
const label = 'Auto'

render(
<CodeButton
{...instance(mockedProps)}
label={label}
onClick={onClick}
mode={ExecuteButtonMode.Auto}
params={{}}
/>
)
fireEvent.click(screen.getByTestId(`preselect-auto-${label}`))

expect(onClick).toBeCalledWith({ mode: ExecuteButtonMode.Auto, params: {} })
})
})
Original file line number Diff line number Diff line change
@@ -1,32 +1,48 @@
import { EuiButton, EuiIcon } from '@elastic/eui'
import cx from 'classnames'
import React from 'react'
import { EuiButton } from '@elastic/eui'
import { CodeButtonParams, ExecuteButtonMode } from 'uiSrc/pages/workbench/components/enablement-area/interfaces'
import { truncateText } from 'uiSrc/utils'

import styles from './styles.module.scss'

export interface Props {
onClick: () => void
onClick: (execute: { mode?: ExecuteButtonMode, params?: CodeButtonParams }) => void
label: string
isLoading?: boolean
disabled?: boolean
className?: string
params?: CodeButtonParams
mode?: ExecuteButtonMode
}
const CodeButton = ({ onClick, label, isLoading, className, disabled, params, mode, ...rest }: Props) => {
const isAutoExecute = mode === ExecuteButtonMode.Auto

return (
<EuiButton
iconSide="right"
isLoading={isLoading}
size="s"
onClick={() => onClick({ mode, params })}
fullWidth
color="secondary"
className={cx(className, styles.button)}
textProps={{ className: styles.buttonText }}
data-testid={`preselect-${isAutoExecute ? 'auto-' : ''}${label}`}
disabled={disabled}
{...rest}
>
<>
{truncateText(label, 86)}
{isAutoExecute && (
<EuiIcon
className={styles.autoExecuteIcon}
type="playFilled"
/>
)}
</>
</EuiButton>
)
}
const CodeButton = ({ onClick, label, isLoading, className, disabled, ...rest }: Props) => (
<EuiButton
iconSide="right"
isLoading={isLoading}
size="s"
onClick={onClick}
fullWidth
color="secondary"
className={[className, styles.button].join(' ')}
textProps={{ className: styles.buttonText }}
data-testid={`preselect-${label}`}
disabled={disabled}
{...rest}
>
{truncateText(label, 86)}
</EuiButton>
)

export default CodeButton
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,27 @@

.button {
justify-content: space-between;
position: relative;
&[class*='euiButton--secondary']:not([class*='isDisabled']) {
&:hover,
&:focus,
&:focus-within {
&:hover {
background-color: var(--euiColorSecondary) !important;
border-color: var(--euiColorSecondary) !important;
color: var(--euiColorPrimaryText) !important;
}
}
&:not(:hover) {
span {
color: var(--euiTextSubduedColor);
}

:global(.euiButton__content) {
padding: 0 24px !important;
}

.autoExecuteIcon {
position: absolute;
top: 50%;
right: 4px;
transform: translateY(-50%);

width: 16px;
height: 16px;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const LazyCodeButton = ({ path = '', ...rest }: Props) => {
if (isStatusSuccessful(status)) {
setLoading(false)
const pageInfo = getFileInfo(path)
setScript(data, pageInfo.location, startCase(pageInfo.name))
setScript(data, {}, { path: pageInfo.location, name: startCase(pageInfo.name) })
}
} catch (error) {
setLoading(false)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export * from './parseParams'
export * from './getFileInfo'
export * from './remarkImage'
export * from './rehypeLinks'
export * from './remarkRedisCode'
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { CodeButtonParams } from 'uiSrc/pages/workbench/components/enablement-area/interfaces'

export const parseParams = (params?: string): CodeButtonParams | undefined => {
if (params?.match(/(^\[).+(]$)/g)) {
return params
?.replace(/^\[|]$/g, '')
?.split(';')
.reduce((prev: {}, next: string) => {
const [key, value] = next.split('=')
return {
...prev,
[key]: value
}
}, {})
}
return undefined
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,30 @@
import { visit } from 'unist-util-visit'
import { ExecuteButtonMode } from 'uiSrc/pages/workbench/components/enablement-area/interfaces'

enum ButtonLang {
Redis = 'redis',
RedisAuto = 'redis-auto'
}

const PARAMS_SEPARATOR = ':'

export const remarkRedisCode = (): (tree: Node) => void => (tree: any) => {
// Find code node in syntax tree
visit(tree, 'code', (codeNode) => {
const { value, meta, lang } = codeNode

if (!lang) return

// Check that it has a language unsupported by our editor
if (lang === 'redis') {
if (lang.startsWith(ButtonLang.Redis)) {
const execute = lang.startsWith(ButtonLang.RedisAuto)
? ExecuteButtonMode.Auto
: ExecuteButtonMode.Manual
const [, params] = lang?.split(PARAMS_SEPARATOR)

codeNode.type = 'html'
// Replace it with our custom component
codeNode.value = `<Code label="${meta}" >{${JSON.stringify(value)}}</Code>`
codeNode.value = `<Code label="${meta}" params="${params}" mode="${execute}">{${JSON.stringify(value)}}</Code>`
}
})
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { MOCK_GUIDES_ITEMS } from 'uiSrc/constants'
import { getFileInfo, getPagesInsideGroup } from './getFileInfo'
import { getFileInfo, getPagesInsideGroup } from '../getFileInfo'

const getFileInfoTests = [
{
Expand Down
Loading