Skip to content

Commit f740a06

Browse files
committed
RI-6638 - No trash icon to remove key if key size column is hidden. And updated texts for the RI-6336
1 parent 517c9d0 commit f740a06

File tree

8 files changed

+285
-95
lines changed

8 files changed

+285
-95
lines changed
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import React from 'react'
2+
import { render, screen, fireEvent } from 'uiSrc/utils/test-utils'
3+
import { stringToBuffer } from 'uiSrc/utils'
4+
import { KeyTypes } from 'uiSrc/constants'
5+
import { DeleteKeyPopover } from './DeleteKeyPopover'
6+
7+
describe('DeleteKeyPopover', () => {
8+
const mockProps = {
9+
nameString: 'test-key',
10+
name: stringToBuffer('test-key'),
11+
type: KeyTypes.String,
12+
rowId: 1,
13+
onDelete: jest.fn(),
14+
onOpenPopover: jest.fn(),
15+
}
16+
17+
beforeEach(() => {
18+
jest.clearAllMocks()
19+
})
20+
21+
it('should render delete button with proper data-testid', () => {
22+
render(<DeleteKeyPopover {...mockProps} />)
23+
24+
expect(screen.getByTestId(`delete-key-btn-${mockProps.nameString}`)).toBeInTheDocument()
25+
})
26+
27+
it('should not show popover content by default', () => {
28+
render(<DeleteKeyPopover {...mockProps} />)
29+
30+
expect(screen.queryByText('will be deleted.')).not.toBeInTheDocument()
31+
expect(screen.queryByTestId('submit-delete-key')).not.toBeInTheDocument()
32+
})
33+
34+
it('should show popover content when deletePopoverId matches rowId', () => {
35+
render(<DeleteKeyPopover {...mockProps} deletePopoverId={mockProps.rowId} />)
36+
37+
expect(screen.getByText('will be deleted.')).toBeInTheDocument()
38+
expect(screen.getByTestId('submit-delete-key')).toBeInTheDocument()
39+
})
40+
41+
it('should call onOpenPopover when delete button is clicked', () => {
42+
render(<DeleteKeyPopover {...mockProps} />)
43+
44+
fireEvent.click(screen.getByTestId(`delete-key-btn-${mockProps.nameString}`))
45+
46+
expect(mockProps.onOpenPopover).toHaveBeenCalledWith(mockProps.rowId, mockProps.type)
47+
})
48+
49+
it('should call onOpenPopover with -1 when closing the popover', () => {
50+
const { container } = render(<DeleteKeyPopover {...mockProps} deletePopoverId={mockProps.rowId} />)
51+
52+
container.querySelector('.euiPopover')
53+
const closePopover = () => mockProps.onOpenPopover(-1, mockProps.type)
54+
closePopover()
55+
56+
expect(mockProps.onOpenPopover).toHaveBeenCalledWith(-1, mockProps.type)
57+
})
58+
59+
it('should call onDelete with proper arguments when confirm button is clicked', () => {
60+
render(<DeleteKeyPopover {...mockProps} deletePopoverId={mockProps.rowId} />)
61+
62+
fireEvent.click(screen.getByTestId('submit-delete-key'))
63+
64+
expect(mockProps.onDelete).toHaveBeenCalledWith(mockProps.name)
65+
})
66+
67+
it('should disable delete button when deleting is true', () => {
68+
render(<DeleteKeyPopover {...mockProps} deletePopoverId={mockProps.rowId} deleting />)
69+
70+
expect(screen.getByTestId('submit-delete-key')).toBeDisabled()
71+
})
72+
73+
it('should format long names in the confirmation message', () => {
74+
const longNameProps = {
75+
...mockProps,
76+
nameString: 'very-long-key-name-that-might-need-formatting',
77+
deletePopoverId: mockProps.rowId
78+
}
79+
80+
render(<DeleteKeyPopover {...longNameProps} />)
81+
82+
expect(screen.getByText(longNameProps.nameString)).toBeInTheDocument()
83+
})
84+
})
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { EuiButton, EuiButtonIcon, EuiPopover, EuiSpacer, EuiText } from '@elastic/eui'
2+
3+
import React from 'react'
4+
5+
import cx from 'classnames'
6+
import { KeyTypes, ModulesKeyTypes } from 'uiSrc/constants'
7+
import { formatLongName } from 'uiSrc/utils'
8+
import { RedisResponseBuffer } from 'uiSrc/slices/interfaces'
9+
10+
export interface DeleteProps {
11+
nameString: string
12+
name: RedisResponseBuffer
13+
type: KeyTypes | ModulesKeyTypes
14+
rowId: number
15+
deletePopoverId?: number
16+
deleting?: boolean
17+
onDelete: (key: RedisResponseBuffer) => void
18+
onOpenPopover: (index: number, type: KeyTypes | ModulesKeyTypes) => void
19+
}
20+
21+
export const DeleteKeyPopover = ({
22+
nameString,
23+
name,
24+
type,
25+
rowId,
26+
deletePopoverId,
27+
deleting,
28+
onDelete,
29+
onOpenPopover,
30+
}: DeleteProps) => (
31+
<EuiPopover
32+
anchorClassName={cx(
33+
'showOnHoverKey',
34+
{ show: deletePopoverId === rowId },
35+
)}
36+
anchorPosition="leftUp"
37+
isOpen={deletePopoverId === rowId}
38+
closePopover={() => onOpenPopover(-1, type)}
39+
panelPaddingSize="l"
40+
button={(
41+
<EuiButtonIcon
42+
iconType="trash"
43+
onClick={() => onOpenPopover(rowId, type)}
44+
aria-label="Delete Key"
45+
data-testid={`delete-key-btn-${nameString}`}
46+
/>
47+
)}
48+
onClick={(e) => e.stopPropagation()}
49+
>
50+
<>
51+
<EuiText size="m">
52+
<h4 style={{ wordBreak: 'break-all' }}><b>{formatLongName(nameString)}</b></h4>
53+
<EuiText size="s">will be deleted.</EuiText>
54+
</EuiText>
55+
<EuiSpacer size="m" />
56+
<EuiButton
57+
fill
58+
size="s"
59+
color="warning"
60+
iconType="trash"
61+
isDisabled={deleting}
62+
onClick={() => onDelete(name)}
63+
data-testid="submit-delete-key"
64+
>
65+
Delete
66+
</EuiButton>
67+
</>
68+
</EuiPopover>
69+
)

redisinsight/ui/src/pages/browser/components/key-list/KeyList.spec.tsx

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,4 +252,22 @@ describe('KeyList', () => {
252252
expect(spy).toHaveBeenCalled()
253253
}, { timeout: 1000 })
254254
})
255+
256+
it.each`
257+
columns | description
258+
${[]} | ${'no columns are shown'}
259+
${[BrowserColumns.TTL]} | ${'only TTL column is shown'}
260+
${[BrowserColumns.Size]} | ${'only Size column is shown'}
261+
${[BrowserColumns.TTL, BrowserColumns.Size]} | ${'both TTL and Size columns are shown'}
262+
`('should render DeleteKeyPopover when $description', ({ columns }) => {
263+
(keysSelector as jest.Mock).mockImplementation(() => ({
264+
...mockedKeySlice,
265+
shownColumns: columns,
266+
}))
267+
268+
const { container } = render(<KeyList {...propsMock} />)
269+
270+
expect(container.querySelector(`[data-testid="delete-key-btn-${propsMock.keysState.keys[0].nameString}"]`))
271+
.toBeInTheDocument()
272+
})
255273
})

redisinsight/ui/src/pages/browser/components/key-list/KeyList.tsx

Lines changed: 62 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import KeyRowType from 'uiSrc/pages/browser/components/key-row-type'
4141
import { GetKeyInfoResponse } from 'apiSrc/modules/browser/keys/dto'
4242

4343
import NoKeysMessage from '../no-keys-message'
44+
import { DeleteKeyPopover } from '../delete-key-popover/DeleteKeyPopover'
4445
import styles from './styles.module.scss'
4546

4647
export interface Props {
@@ -297,6 +298,9 @@ const KeyList = forwardRef((props: Props, ref) => {
297298
rerender({})
298299
}
299300

301+
const isTtlTheLastColumn = !shownColumns.includes(BrowserColumns.Size)
302+
const ttlColumnSize = isTtlTheLastColumn ? 146 : 86
303+
300304
const columns: ITableColumn[] = [
301305
{
302306
id: 'type',
@@ -312,19 +316,50 @@ const KeyList = forwardRef((props: Props, ref) => {
312316
label: 'Key',
313317
minWidth: 94,
314318
truncateText: true,
315-
render: (cellData: string) => (
316-
<KeyRowName nameString={cellData} shortName={cellData} />
319+
render: (cellData: string, { name, type }: IKeyPropTypes, _expanded, rowIndex) => (
320+
<>
321+
<KeyRowName nameString={cellData} shortName={cellData} />
322+
{columns[columns.length - 1].id === 'nameString' && (
323+
<DeleteKeyPopover
324+
deletePopoverId={deletePopoverIndex}
325+
nameString={cellData}
326+
name={name}
327+
type={type}
328+
rowId={rowIndex || 0}
329+
onDelete={handleRemoveKey}
330+
onOpenPopover={handleDeletePopoverOpen}
331+
/>
332+
)}
333+
</>
317334
)
318335
},
319336
shownColumns.includes(BrowserColumns.TTL) ? {
320337
id: 'ttl',
321338
label: 'TTL',
322-
absoluteWidth: 86,
323-
minWidth: 86,
339+
absoluteWidth: ttlColumnSize,
340+
minWidth: ttlColumnSize,
324341
truncateText: true,
325342
alignment: TableCellAlignment.Right,
326-
render: (cellData: number, { nameString }: IKeyPropTypes, _expanded, rowIndex) => (
327-
<KeyRowTTL ttl={cellData} nameString={nameString} deletePopoverId={deletePopoverIndex} rowId={rowIndex || 0} />
343+
render: (cellData: number, { nameString, name, type }: IKeyPropTypes, _expanded, rowIndex) => (
344+
<>
345+
<KeyRowTTL
346+
ttl={cellData}
347+
nameString={nameString}
348+
deletePopoverId={deletePopoverIndex}
349+
rowId={rowIndex || 0}
350+
/>
351+
{isTtlTheLastColumn && (
352+
<DeleteKeyPopover
353+
deletePopoverId={deletePopoverIndex}
354+
nameString={nameString}
355+
name={name}
356+
type={type}
357+
rowId={rowIndex || 0}
358+
onDelete={handleRemoveKey}
359+
onOpenPopover={handleDeletePopoverOpen}
360+
/>
361+
)}
362+
</>
328363
)
329364
} : null,
330365
shownColumns.includes(BrowserColumns.Size) ? {
@@ -334,26 +369,28 @@ const KeyList = forwardRef((props: Props, ref) => {
334369
minWidth: 90,
335370
alignment: TableCellAlignment.Right,
336371
textAlignment: TableCellTextAlignment.Right,
337-
render: (
338-
cellData: number,
339-
{ nameString, type, name: bufferName }: IKeyPropTypes,
340-
_expanded,
341-
rowIndex
342-
) => (
343-
<KeyRowSize
344-
size={cellData}
345-
nameString={nameString}
346-
nameBuffer={bufferName}
347-
deletePopoverId={deletePopoverIndex}
348-
rowId={rowIndex || 0}
349-
type={type}
350-
deleting={deleting}
351-
setDeletePopoverId={setDeletePopoverIndex}
352-
handleDeletePopoverOpen={handleDeletePopoverOpen}
353-
handleDelete={handleRemoveKey}
354-
/>
372+
render: (cellData: number, { nameString, name, type }: IKeyPropTypes, _expanded, rowIndex) => (
373+
<>
374+
<KeyRowSize
375+
size={cellData}
376+
nameString={nameString}
377+
deletePopoverId={deletePopoverIndex}
378+
rowId={rowIndex || 0}
379+
/>
380+
{columns[columns.length - 1].id === 'size' && (
381+
<DeleteKeyPopover
382+
deletePopoverId={deletePopoverIndex}
383+
nameString={nameString}
384+
name={name}
385+
type={type}
386+
rowId={rowIndex || 0}
387+
onDelete={handleRemoveKey}
388+
onOpenPopover={handleDeletePopoverOpen}
389+
/>
390+
)}
391+
</>
355392
)
356-
} : null,
393+
} : null
357394
].filter((el) => !!el)
358395

359396
const noItemsMessage = NoItemsMessage()

redisinsight/ui/src/pages/browser/components/key-row-size/KeyRowSize.tsx

Lines changed: 1 addition & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,28 @@
11
import React from 'react'
22
import cx from 'classnames'
33
import {
4-
EuiButton,
5-
EuiButtonIcon,
64
EuiLoadingContent,
7-
EuiPopover,
8-
EuiSpacer,
95
EuiText,
106
EuiToolTip,
117
} from '@elastic/eui'
128
import { isUndefined } from 'lodash'
139

14-
import { Maybe, formatBytes, formatLongName } from 'uiSrc/utils'
15-
import { KeyTypes, ModulesKeyTypes } from 'uiSrc/constants'
16-
import { RedisResponseBuffer } from 'uiSrc/slices/interfaces'
10+
import { Maybe, formatBytes } from 'uiSrc/utils'
1711
import styles from './styles.module.scss'
1812

1913
export interface Props {
2014
size: Maybe<number>
2115
deletePopoverId: Maybe<number | string>
2216
rowId: number | string
2317
nameString: string
24-
type: KeyTypes | ModulesKeyTypes
25-
deleting: boolean
26-
nameBuffer: RedisResponseBuffer
27-
setDeletePopoverId: (id: any) => void
28-
handleDeletePopoverOpen: (id: any, type: KeyTypes | ModulesKeyTypes) => void
29-
handleDelete: (key: RedisResponseBuffer) => void
3018
}
3119

3220
const KeyRowSize = (props: Props) => {
3321
const {
3422
size,
3523
nameString,
36-
nameBuffer,
3724
deletePopoverId,
38-
deleting,
3925
rowId,
40-
type,
41-
setDeletePopoverId,
42-
handleDeletePopoverOpen,
43-
handleDelete,
4426
} = props
4527

4628
if (isUndefined(size)) {
@@ -88,46 +70,6 @@ const KeyRowSize = (props: Props) => {
8870
</EuiToolTip>
8971
</div>
9072
</EuiText>
91-
<EuiPopover
92-
anchorClassName={cx(
93-
styles.deleteAnchor,
94-
'showOnHoverKey',
95-
{ show: deletePopoverId === rowId },
96-
)}
97-
anchorPosition="rightUp"
98-
isOpen={deletePopoverId === rowId}
99-
closePopover={() => setDeletePopoverId(undefined)}
100-
panelPaddingSize="l"
101-
panelClassName={styles.deletePopover}
102-
button={(
103-
<EuiButtonIcon
104-
iconType="trash"
105-
onClick={() => handleDeletePopoverOpen(rowId, type)}
106-
aria-label="Delete Key"
107-
data-testid={`delete-key-btn-${nameString}`}
108-
/>
109-
)}
110-
onClick={(e) => e.stopPropagation()}
111-
>
112-
<>
113-
<EuiText size="m">
114-
<h4 style={{ wordBreak: 'break-all' }}><b>{formatLongName(nameString)}</b></h4>
115-
<EuiText size="s">will be deleted.</EuiText>
116-
</EuiText>
117-
<EuiSpacer size="m" />
118-
<EuiButton
119-
fill
120-
size="s"
121-
color="warning"
122-
iconType="trash"
123-
isDisabled={deleting}
124-
onClick={() => handleDelete(nameBuffer)}
125-
data-testid="submit-delete-key"
126-
>
127-
Delete
128-
</EuiButton>
129-
</>
130-
</EuiPopover>
13173
</>
13274
)
13375
}

0 commit comments

Comments
 (0)