Skip to content

Commit 8e2c2c4

Browse files
committed
#RI-5033 - Refactor details of key
1 parent f06642f commit 8e2c2c4

File tree

23 files changed

+362
-239
lines changed

23 files changed

+362
-239
lines changed

redisinsight/ui/src/pages/browser/modules/key-details-header/KeyDetailsHeader.spec.tsx

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import React from 'react'
22
import { mock } from 'ts-mockito'
3-
import { render, screen, fireEvent } from 'uiSrc/utils/test-utils'
3+
import { cloneDeep } from 'lodash'
4+
import { render, screen, fireEvent, mockedStore, cleanup } from 'uiSrc/utils/test-utils'
45
import { stringDataSelector } from 'uiSrc/slices/browser/string'
56
import { KeyTypes } from 'uiSrc/constants'
7+
import { deleteSelectedKey } from 'uiSrc/slices/browser/keys'
68
import { Props, KeyDetailsHeader } from './KeyDetailsHeader'
79

810
const mockedProps = mock<Props>()
@@ -11,6 +13,15 @@ const KEY_INPUT_TEST_ID = 'edit-key-input'
1113
const KEY_BTN_TEST_ID = 'edit-key-btn'
1214
const TTL_INPUT_TEST_ID = 'edit-ttl-input'
1315
const EDIT_VALUE_BTN_TEST_ID = 'edit-key-value-btn'
16+
const DELETE_KEY_BTN_TEST_ID = 'delete-key-btn'
17+
const DELETE_KEY_CONFIRM_BTN_TEST_ID = 'delete-key-confirm-btn'
18+
19+
let store: typeof mockedStore
20+
beforeEach(() => {
21+
cleanup()
22+
store = cloneDeep(mockedStore)
23+
store.clearActions()
24+
})
1425

1526
jest.mock('uiSrc/slices/browser/string', () => ({
1627
...jest.requireActual('uiSrc/slices/browser/string'),
@@ -117,10 +128,23 @@ describe('KeyDetailsHeader', () => {
117128
})
118129

119130
describe('should call onRefresh', () => {
120-
test.each(Object.values(KeyTypes))('should call onRefresh', (keyType) => {
131+
test.each(Object.values(KeyTypes))('should call onRefresh for keyType: %s', (keyType) => {
121132
const component = render(<KeyDetailsHeader {...mockedProps} keyType={keyType} />)
122133
fireEvent.click(screen.getByTestId('refresh-key-btn'))
123134
expect(component).toBeTruthy()
124135
})
125136
})
137+
138+
describe('should call onDelete', () => {
139+
test.each(Object.values(KeyTypes))('should call onDelete for keyType: %s', (keyType) => {
140+
const onRemoveKeyMock = jest.fn()
141+
const component = render(<KeyDetailsHeader {...mockedProps} keyType={keyType} onRemoveKey={onRemoveKeyMock} />)
142+
fireEvent.click(screen.getByTestId(DELETE_KEY_BTN_TEST_ID))
143+
fireEvent.click(screen.getByTestId(DELETE_KEY_CONFIRM_BTN_TEST_ID))
144+
expect(component).toBeTruthy()
145+
146+
const expectedActions = [deleteSelectedKey()]
147+
expect(store.getActions()).toEqual(expect.arrayContaining(expectedActions))
148+
})
149+
})
126150
})
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,32 @@
11
import React from 'react'
22
import { instance, mock } from 'ts-mockito'
33
import { render } from 'uiSrc/utils/test-utils'
4+
import { KeyTypes } from 'uiSrc/constants'
45
import { Props, KeyDetailsHeaderActions } from './KeyDetailsHeaderActions'
56

67
const mockedProps = mock<Props>()
78

9+
const actionsExistsTests: any[] = [
10+
[KeyTypes.Hash, ['add-key-value-items-btn']],
11+
[KeyTypes.List, ['add-key-value-items-btn', 'remove-key-value-items-btn']],
12+
[KeyTypes.Set, ['add-key-value-items-btn']],
13+
[KeyTypes.ZSet, ['add-key-value-items-btn']],
14+
[KeyTypes.String, ['edit-key-value-btn']],
15+
]
16+
817
describe('KeyDetailsHeaderActions', () => {
918
it('should render', () => {
1019
expect(render(<KeyDetailsHeaderActions {...instance(mockedProps)} />)).toBeTruthy()
1120
})
21+
22+
test.each(actionsExistsTests)('for keyType: %s, actions test id should exist: %s', (keyType: KeyTypes, testIds: string[]) => {
23+
const { queryByTestId } = render(<KeyDetailsHeaderActions
24+
{...instance(mockedProps)}
25+
keyType={keyType}
26+
/>)
27+
28+
testIds.forEach((testId) => {
29+
expect(queryByTestId(testId)).toBeInTheDocument()
30+
})
31+
})
1232
})

redisinsight/ui/src/pages/browser/modules/key-details-header/components/key-details-header-actions/KeyDetailsHeaderActions.tsx

Lines changed: 80 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -57,96 +57,96 @@ const KeyDetailsHeaderActions = ({
5757
return (
5858
<>
5959
{KEY_TYPES_ACTIONS[keyType] && 'addItems' in KEY_TYPES_ACTIONS[keyType] && (
60-
<EuiToolTip
61-
content={width > MIDDLE_SCREEN_RESOLUTION ? '' : KEY_TYPES_ACTIONS[keyType].addItems?.name}
62-
position="left"
63-
anchorClassName={cx(styles.actionBtn, { [styles.iiu]: width > MIDDLE_SCREEN_RESOLUTION })}
64-
>
65-
<>
66-
{width > MIDDLE_SCREEN_RESOLUTION ? (
67-
<EuiButton
68-
size="s"
69-
iconType="plusInCircle"
70-
color="secondary"
71-
aria-label={KEY_TYPES_ACTIONS[keyType].addItems?.name}
72-
onClick={onAddItem}
73-
data-testid="add-key-value-items-btn"
74-
>
75-
{KEY_TYPES_ACTIONS[keyType].addItems?.name}
76-
</EuiButton>
77-
) : (
78-
<EuiButtonIcon
79-
iconType="plusInCircle"
80-
color="primary"
81-
aria-label={KEY_TYPES_ACTIONS[keyType].addItems?.name}
82-
onClick={onAddItem}
83-
data-testid="add-key-value-items-btn"
84-
/>
85-
)}
86-
</>
87-
</EuiToolTip>
60+
<EuiToolTip
61+
content={width > MIDDLE_SCREEN_RESOLUTION ? '' : KEY_TYPES_ACTIONS[keyType].addItems?.name}
62+
position="left"
63+
anchorClassName={cx(styles.actionBtn, { [styles.iiu]: width > MIDDLE_SCREEN_RESOLUTION })}
64+
>
65+
<>
66+
{width > MIDDLE_SCREEN_RESOLUTION ? (
67+
<EuiButton
68+
size="s"
69+
iconType="plusInCircle"
70+
color="secondary"
71+
aria-label={KEY_TYPES_ACTIONS[keyType].addItems?.name}
72+
onClick={onAddItem}
73+
data-testid="add-key-value-items-btn"
74+
>
75+
{KEY_TYPES_ACTIONS[keyType].addItems?.name}
76+
</EuiButton>
77+
) : (
78+
<EuiButtonIcon
79+
iconType="plusInCircle"
80+
color="primary"
81+
aria-label={KEY_TYPES_ACTIONS[keyType].addItems?.name}
82+
onClick={onAddItem}
83+
data-testid="add-key-value-items-btn"
84+
/>
85+
)}
86+
</>
87+
</EuiToolTip>
8888
)}
8989
{keyType === KeyTypes.Stream && (
90-
<EuiToolTip
91-
content={width > MIDDLE_SCREEN_RESOLUTION ? '' : STREAM_ADD_ACTION[streamViewType].name}
92-
position="left"
93-
anchorClassName={cx(styles.actionBtn, { [styles.withText]: width > MIDDLE_SCREEN_RESOLUTION })}
94-
>
95-
<>
96-
{width > MIDDLE_SCREEN_RESOLUTION ? (
97-
<EuiButton
98-
size="s"
99-
iconType="plusInCircle"
100-
color="secondary"
101-
aria-label={STREAM_ADD_ACTION[streamViewType].name}
102-
onClick={onAddItem}
103-
data-testid="add-key-value-items-btn"
104-
>
105-
{STREAM_ADD_ACTION[streamViewType].name}
106-
</EuiButton>
107-
) : (
108-
<EuiButtonIcon
109-
iconType="plusInCircle"
110-
color="primary"
111-
aria-label={STREAM_ADD_ACTION[streamViewType].name}
112-
onClick={onAddItem}
113-
data-testid="add-key-value-items-btn"
114-
/>
115-
)}
116-
</>
117-
</EuiToolTip>
90+
<EuiToolTip
91+
content={width > MIDDLE_SCREEN_RESOLUTION ? '' : STREAM_ADD_ACTION[streamViewType].name}
92+
position="left"
93+
anchorClassName={cx(styles.actionBtn, { [styles.withText]: width > MIDDLE_SCREEN_RESOLUTION })}
94+
>
95+
<>
96+
{width > MIDDLE_SCREEN_RESOLUTION ? (
97+
<EuiButton
98+
size="s"
99+
iconType="plusInCircle"
100+
color="secondary"
101+
aria-label={STREAM_ADD_ACTION[streamViewType].name}
102+
onClick={onAddItem}
103+
data-testid="add-key-value-items-btn"
104+
>
105+
{STREAM_ADD_ACTION[streamViewType].name}
106+
</EuiButton>
107+
) : (
108+
<EuiButtonIcon
109+
iconType="plusInCircle"
110+
color="primary"
111+
aria-label={STREAM_ADD_ACTION[streamViewType].name}
112+
onClick={onAddItem}
113+
data-testid="add-key-value-items-btn"
114+
/>
115+
)}
116+
</>
117+
</EuiToolTip>
118118
)}
119119
{KEY_TYPES_ACTIONS[keyType] && 'removeItems' in KEY_TYPES_ACTIONS[keyType] && (
120-
<EuiToolTip
121-
content={KEY_TYPES_ACTIONS[keyType].removeItems?.name}
122-
position="left"
123-
anchorClassName={styles.actionBtn}
124-
>
125-
<EuiButtonIcon
126-
iconType="minusInCircle"
127-
color="primary"
128-
aria-label={KEY_TYPES_ACTIONS[keyType].removeItems?.name}
129-
onClick={onRemoveItem}
130-
data-testid="remove-key-value-items-btn"
131-
/>
132-
</EuiToolTip>
133-
)}
134-
{KEY_TYPES_ACTIONS[keyType] && 'editItem' in KEY_TYPES_ACTIONS[keyType] && (
135-
<div className={styles.actionBtn}>
136120
<EuiToolTip
137-
content={editToolTip}
138-
data-testid="edit-key-value-tooltip"
121+
content={KEY_TYPES_ACTIONS[keyType].removeItems?.name}
122+
position="left"
123+
anchorClassName={styles.actionBtn}
139124
>
140125
<EuiButtonIcon
141-
disabled={!isEditable || !isStringEditable}
142-
iconType="pencil"
126+
iconType="minusInCircle"
143127
color="primary"
144-
aria-label={KEY_TYPES_ACTIONS[keyType].editItem?.name}
145-
onClick={onEditItem}
146-
data-testid="edit-key-value-btn"
128+
aria-label={KEY_TYPES_ACTIONS[keyType].removeItems?.name}
129+
onClick={onRemoveItem}
130+
data-testid="remove-key-value-items-btn"
147131
/>
148132
</EuiToolTip>
149-
</div>
133+
)}
134+
{KEY_TYPES_ACTIONS[keyType] && 'editItem' in KEY_TYPES_ACTIONS[keyType] && (
135+
<div className={styles.actionBtn}>
136+
<EuiToolTip
137+
content={editToolTip}
138+
data-testid="edit-key-value-tooltip"
139+
>
140+
<EuiButtonIcon
141+
disabled={!isEditable || !isStringEditable}
142+
iconType="pencil"
143+
color="primary"
144+
aria-label={KEY_TYPES_ACTIONS[keyType].editItem?.name}
145+
onClick={onEditItem}
146+
data-testid="edit-key-value-btn"
147+
/>
148+
</EuiToolTip>
149+
</div>
150150
)}
151151
</>
152152
)

redisinsight/ui/src/pages/browser/modules/key-details-header/components/key-details-header-delete/KeyDetailsHeaderDelete.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,7 @@ export interface Props {
2121
onDelete: (key: RedisResponseBuffer, type: string) => void
2222
}
2323

24-
const KeyDetailsHeaderDelete = ({
25-
onDelete,
26-
}: Props) => {
24+
const KeyDetailsHeaderDelete = ({ onDelete }: Props) => {
2725
const {
2826
type,
2927
nameString: keyProp,
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,35 @@
11
import React from 'react'
22
import { instance, mock } from 'ts-mockito'
33
import { render } from 'uiSrc/utils/test-utils'
4+
import { KeyTypes } from 'uiSrc/constants'
5+
import { StreamViewType } from 'uiSrc/slices/interfaces/stream'
46
import { Props, AddItemsPanel } from './AddItemsPanel'
57

68
const mockedProps = mock<Props>()
79

10+
const AddItemsPanelTypeTests: any[] = [
11+
[KeyTypes.Hash, 'add-hash-field-panel'],
12+
[KeyTypes.ZSet, 'add-zset-field-panel'],
13+
[KeyTypes.Set, 'add-set-field-panel'],
14+
[KeyTypes.List, 'add-list-field-panel'],
15+
[KeyTypes.Stream, 'add-stream-field-panel', StreamViewType.Data],
16+
[KeyTypes.Stream, 'add-stream-groups-field-panel', StreamViewType.Groups],
17+
[KeyTypes.Stream, 'add-stream-groups-field-panel', StreamViewType.Consumers],
18+
[KeyTypes.Stream, 'add-stream-groups-field-panel', StreamViewType.Messages],
19+
]
20+
821
describe('AddItemsPanel', () => {
922
it('should render', () => {
1023
expect(render(<AddItemsPanel {...instance(mockedProps)} />)).toBeTruthy()
1124
})
25+
26+
it.each(AddItemsPanelTypeTests)('for key type: %s (reply), data-subj should exists: %s',
27+
(type: KeyTypes, subj: string, strViewType: StreamViewType = StreamViewType.Data) => {
28+
const { container } = render(<AddItemsPanel
29+
{...instance(mockedProps)}
30+
selectedKeyType={type}
31+
streamViewType={strViewType}
32+
/>)
33+
expect(container.querySelector(`[data-test-subj=${subj}]`)).toBeInTheDocument()
34+
})
1235
})

redisinsight/ui/src/pages/browser/modules/key-details/components/add-items-panel/AddItemsPanel.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,16 @@ const AddItemsPanel = (props: Props) => {
2929
return (
3030
<div className={cx('formFooterBar', styles.contentActive)}>
3131
{selectedKeyType === KeyTypes.Hash && (
32-
<AddHashFields onCancel={closeAddItemPanel} />
32+
<AddHashFields onCancel={closeAddItemPanel} />
3333
)}
3434
{selectedKeyType === KeyTypes.ZSet && (
35-
<AddZsetMembers onCancel={closeAddItemPanel} />
35+
<AddZsetMembers onCancel={closeAddItemPanel} />
3636
)}
3737
{selectedKeyType === KeyTypes.Set && (
38-
<AddSetMembers onCancel={closeAddItemPanel} />
38+
<AddSetMembers onCancel={closeAddItemPanel} />
3939
)}
4040
{selectedKeyType === KeyTypes.List && (
41-
<AddListElements onCancel={closeAddItemPanel} />
41+
<AddListElements onCancel={closeAddItemPanel} />
4242
)}
4343
{selectedKeyType === KeyTypes.Stream && (
4444
<>
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,34 @@
11
import React from 'react'
22
import { instance, mock } from 'ts-mockito'
33
import { render } from 'uiSrc/utils/test-utils'
4+
import { KeyTypes, ModulesKeyTypes } from 'uiSrc/constants'
45
import { Props, DynamicTypeDetails } from './DynamicTypeDetails'
56

67
const mockedProps = mock<Props>()
78

9+
const DynamicTypeDetailsTypeTests: any[] = [
10+
[KeyTypes.Hash, 'hash-details'],
11+
[KeyTypes.ZSet, 'zset-details'],
12+
[KeyTypes.Set, 'set-details'],
13+
[KeyTypes.List, 'list-details'],
14+
[KeyTypes.Stream, 'stream-details'],
15+
[KeyTypes.ReJSON, 'json-details'],
16+
[ModulesKeyTypes.Graph, 'modules-type-details'],
17+
[ModulesKeyTypes.TimeSeries, 'modules-type-details'],
18+
['123', 'unsupported-type-details'],
19+
]
20+
821
describe('DynamicTypeDetails', () => {
922
it('should render', () => {
1023
expect(render(<DynamicTypeDetails {...instance(mockedProps)} />)).toBeTruthy()
1124
})
25+
26+
it.each(DynamicTypeDetailsTypeTests)('for key type: %s (reply), data-subj should exists: %s',
27+
(type: KeyTypes, testId: string) => {
28+
const { queryByTestId } = render(<DynamicTypeDetails
29+
{...instance(mockedProps)}
30+
selectedKeyType={type}
31+
/>)
32+
expect(queryByTestId(testId)).toBeInTheDocument()
33+
})
1234
})

redisinsight/ui/src/pages/browser/modules/key-details/components/dynamic-type-details/DynamicTypeDetails.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,8 @@ const DynamicTypeDetails = (props: Props) => {
6666
}
6767

6868
// Unsupported key type
69-
if (Object.values(KeyTypes).includes(selectedKeyType as KeyTypes)) {
69+
if (!(Object.values(KeyTypes).includes(selectedKeyType as KeyTypes))
70+
&& !(Object.values(ModulesKeyTypes).includes(selectedKeyType as ModulesKeyTypes))) {
7071
return <UnsupportedTypeDetails />
7172
}
7273

redisinsight/ui/src/pages/browser/modules/key-details/components/hash-details/HashDetails.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,7 @@ const HashDetails = (props: Props) => {
477477
return (
478478
<>
479479
<div
480+
data-testid="hash-details"
480481
className={cx(
481482
'key-details-table',
482483
'hash-fields-container',

redisinsight/ui/src/pages/browser/modules/key-details/components/key-details-add-items/add-hash-fields/AddHashFields.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ const AddHashFields = (props: Props) => {
139139
color="transparent"
140140
hasShadow={false}
141141
borderRadius="none"
142+
data-test-subj="add-hash-field-panel"
142143
className={cx(styles.content, 'eui-yScroll', 'flexItemNoFullWidth', 'inlineFieldsNoSpace')}
143144
>
144145
{fields.map((item, index) => (

0 commit comments

Comments
 (0)