Skip to content

Commit d69f0c5

Browse files
authored
RI-7190 Field Box component (#4716)
* feat: created base field box component * feat: created field boxes group component re #RI-7190
1 parent 556c73d commit d69f0c5

File tree

14 files changed

+297
-3
lines changed

14 files changed

+297
-3
lines changed

redisinsight/ui/src/components/new-index/CreateIndexStep/CreateIndexStepWrapper.spec.tsx renamed to redisinsight/ui/src/components/new-index/create-index-step/CreateIndexStepWrapper.spec.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
import React from 'react'
2-
import { fireEvent, render, screen } from 'uiSrc/utils/test-utils'
2+
import { cleanup, fireEvent, render, screen } from 'uiSrc/utils/test-utils'
33
import { CreateIndexStepWrapper } from './CreateIndexStepWrapper'
44

55
const renderComponent = () => render(<CreateIndexStepWrapper />)
66

77
describe('CreateIndexStepWrapper', () => {
8+
beforeEach(() => {
9+
cleanup()
10+
})
11+
812
it('should render', () => {
913
const { container } = renderComponent()
1014

@@ -37,7 +41,7 @@ describe('CreateIndexStepWrapper', () => {
3741
expect(buildNewIndexTabContent).toBeInTheDocument()
3842
})
3943

40-
it('shouldn\'t switch to "Build new index" tab when clicked, since it is disabled', () => {
44+
it("shouldn't switch to 'Build new index' tab when clicked, since it is disabled", () => {
4145
renderComponent()
4246

4347
const buildNewIndexTabTriggerLabel = screen.getByText('Build new index')

redisinsight/ui/src/components/new-index/CreateIndexStep/CreateIndexStepWrapper.tsx renamed to redisinsight/ui/src/components/new-index/create-index-step/CreateIndexStepWrapper.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React from 'react'
22
import { TabsProps } from '@redis-ui/components'
33
import Tabs, { TabInfo } from 'uiSrc/components/base/layout/tabs'
4-
import { BuildNewIndexTabTrigger } from './BuildNewIndex/BuildNewIndexTabTrigger'
4+
import { BuildNewIndexTabTrigger } from './build-new-index-tab/BuildNewIndexTabTrigger'
55

66
export enum VectorIndexTab {
77
BuildNewIndex = 'build-new-index',
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 { BoxSelectionGroup } from '@redis-ui/components'
3+
4+
import { FieldTypes } from 'uiSrc/pages/browser/components/create-redisearch-index/constants'
5+
import { MOCK_VECTOR_SEARCH_BOX } from 'uiSrc/constants/mocks/mock-vector-index-search'
6+
import { cleanup, fireEvent, render, screen } from 'uiSrc/utils/test-utils'
7+
8+
import { FieldBox, FieldBoxProps } from './FieldBox'
9+
import { VectorSearchBox } from './types'
10+
11+
const renderFieldBoxComponent = (props?: FieldBoxProps) => {
12+
const defaultProps: FieldBoxProps = {
13+
box: MOCK_VECTOR_SEARCH_BOX,
14+
}
15+
16+
return render(
17+
<BoxSelectionGroup.Compose>
18+
<FieldBox {...defaultProps} {...props} />
19+
</BoxSelectionGroup.Compose>,
20+
)
21+
}
22+
23+
describe('CreateIndexStepWrapper', () => {
24+
beforeEach(() => {
25+
cleanup()
26+
})
27+
28+
it('should render', () => {
29+
const props: FieldBoxProps = {
30+
box: {
31+
...MOCK_VECTOR_SEARCH_BOX,
32+
value: 'id',
33+
label: 'id',
34+
text: 'Unique product identifier',
35+
tag: FieldTypes.TAG,
36+
disabled: false,
37+
},
38+
}
39+
40+
const { container } = renderFieldBoxComponent(props)
41+
42+
expect(container).toBeTruthy()
43+
44+
// Check if the box is rendered with the correct visual elements
45+
const label = screen.getByText(props.box.label!)
46+
const description = screen.getByText(props.box.text!)
47+
const tag = screen.getByText(props.box.tag.toUpperCase()!)
48+
const checkbox = screen.getByRole('checkbox')
49+
50+
expect(label).toBeInTheDocument()
51+
expect(description).toBeInTheDocument()
52+
expect(tag).toBeInTheDocument()
53+
expect(checkbox).toBeInTheDocument()
54+
})
55+
56+
it('should select the box when clicked', () => {
57+
renderFieldBoxComponent()
58+
59+
const checkbox = screen.getByRole('checkbox')
60+
expect(checkbox).not.toBeChecked()
61+
62+
const box = screen.getByTestId(`field-box-${MOCK_VECTOR_SEARCH_BOX.value}`)
63+
fireEvent.click(box)
64+
65+
expect(checkbox).toBeChecked()
66+
})
67+
68+
it('should not select the box when clicked if disabled', () => {
69+
const disabledBox: VectorSearchBox = {
70+
...MOCK_VECTOR_SEARCH_BOX,
71+
disabled: true,
72+
}
73+
74+
renderFieldBoxComponent({ box: disabledBox })
75+
76+
const checkbox = screen.getByRole('checkbox')
77+
expect(checkbox).not.toBeChecked()
78+
79+
const box = screen.getByTestId(`field-box-${disabledBox.value}`)
80+
fireEvent.click(box)
81+
82+
expect(checkbox).not.toBeChecked()
83+
})
84+
})
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { BoxSelectionGroup } from '@redis-ui/components'
2+
import styled from 'styled-components'
3+
4+
export const StyledFieldBox = styled(BoxSelectionGroup.Item.Compose)`
5+
display: flex;
6+
flex-direction: column;
7+
justify-content: space-between;
8+
width: 100%;
9+
padding: ${({ theme }) => theme.core.space.space100};
10+
gap: ${({ theme }) => theme.components.boxSelectionGroup.defaultItem.gap};
11+
`
12+
13+
export const BoxHeader = styled.div`
14+
display: flex;
15+
align-items: center;
16+
justify-content: space-between;
17+
`
18+
19+
export const BoxHeaderActions = styled.div`
20+
display: flex;
21+
align-items: center;
22+
gap: ${({ theme }) => theme.components.boxSelectionGroup.defaultItem.gap};
23+
`
24+
25+
export const BoxContent = styled.div``
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import React from 'react'
2+
import {
3+
BoxSelectionGroup,
4+
BoxSelectionGroupItemComposeProps,
5+
Checkbox,
6+
} from '@redis-ui/components'
7+
8+
import { EditIcon } from 'uiSrc/components/base/icons'
9+
import { IconButton } from 'uiSrc/components/base/forms/buttons/IconButton'
10+
import { Text } from 'uiSrc/components/base/text'
11+
12+
import {
13+
BoxContent,
14+
BoxHeader,
15+
BoxHeaderActions,
16+
StyledFieldBox,
17+
} from './FieldBox.styles'
18+
import { FieldTag } from './FieldTag'
19+
import { VectorSearchBox } from './types'
20+
21+
export interface FieldBoxProps extends BoxSelectionGroupItemComposeProps {
22+
box: VectorSearchBox
23+
}
24+
25+
export const FieldBox = ({ box, ...rest }: FieldBoxProps) => {
26+
const { label, text, tag, disabled } = box
27+
28+
return (
29+
<StyledFieldBox box={box} data-testid={`field-box-${box.value}`} {...rest}>
30+
<BoxHeader>
31+
<BoxSelectionGroup.Item.StateIndicator>
32+
{(props) => <Checkbox {...props} />}
33+
</BoxSelectionGroup.Item.StateIndicator>
34+
35+
<BoxHeaderActions>
36+
<FieldTag tag={tag} />
37+
<IconButton icon={EditIcon} size="XL" disabled={disabled} />
38+
</BoxHeaderActions>
39+
</BoxHeader>
40+
<BoxContent>
41+
<Text size="L" variant="semiBold">
42+
{label}
43+
</Text>
44+
45+
{text && (
46+
<Text size="L" color="secondary" ellipsis tooltipOnEllipsis>
47+
{text}
48+
</Text>
49+
)}
50+
</BoxContent>
51+
</StyledFieldBox>
52+
)
53+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import React from 'react'
2+
import { Badge } from '@redis-ui/components'
3+
import {
4+
FIELD_TYPE_OPTIONS,
5+
FieldTypes,
6+
} from 'uiSrc/pages/browser/components/create-redisearch-index/constants'
7+
8+
// TODO: Add colors mapping for tags when @redis-ui/components v38.6.0 is released
9+
export const FieldTag = ({ tag }: { tag: FieldTypes }) => {
10+
const tagLabel = FIELD_TYPE_OPTIONS.find(
11+
(option) => option.value === tag,
12+
)?.text
13+
14+
return tagLabel ? <Badge label={tagLabel} /> : null
15+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { BoxSelectionGroupBox } from '@redis-ui/components'
2+
import { FieldTypes } from 'uiSrc/pages/browser/components/create-redisearch-index/constants'
3+
4+
export interface VectorSearchBox extends BoxSelectionGroupBox {
5+
text: string
6+
tag: FieldTypes
7+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import React from 'react'
2+
3+
import { fireEvent, render, screen } from 'uiSrc/utils/test-utils'
4+
import { MOCK_VECTOR_SEARCH_BOX } from 'uiSrc/constants/mocks/mock-vector-index-search'
5+
import { FieldBoxesGroup, FieldBoxesGroupProps } from './FieldBoxesGroup'
6+
7+
const renderFieldBoxesGroupComponent = (
8+
props?: Partial<FieldBoxesGroupProps>,
9+
) => {
10+
const defaultProps: FieldBoxesGroupProps = {
11+
boxes: [MOCK_VECTOR_SEARCH_BOX],
12+
value: [MOCK_VECTOR_SEARCH_BOX.value],
13+
onChange: jest.fn(),
14+
}
15+
16+
return render(<FieldBoxesGroup {...defaultProps} {...props} />)
17+
}
18+
19+
describe('FieldBoxesGroup', () => {
20+
it('should render', () => {
21+
const { container } = renderFieldBoxesGroupComponent()
22+
23+
expect(container).toBeTruthy()
24+
25+
const fieldBoxesGroup = screen.getByTestId('field-boxes-group')
26+
expect(fieldBoxesGroup).toBeInTheDocument()
27+
28+
const fieldBoxes = screen.getAllByTestId(/field-box-/)
29+
expect(fieldBoxes).toHaveLength(1)
30+
})
31+
32+
it('should call onChange when clicking on a box to select it', () => {
33+
const onChangeMock = jest.fn()
34+
const value: string[] = []
35+
36+
renderFieldBoxesGroupComponent({ value, onChange: onChangeMock })
37+
38+
const box = screen.getByTestId(`field-box-${MOCK_VECTOR_SEARCH_BOX.value}`)
39+
40+
fireEvent.click(box)
41+
expect(onChangeMock).toHaveBeenCalledWith([MOCK_VECTOR_SEARCH_BOX.value])
42+
})
43+
44+
it('should call onChange when clicking on a box to deselect it', () => {
45+
const onChangeMock = jest.fn()
46+
const value: string[] = [MOCK_VECTOR_SEARCH_BOX.value]
47+
48+
renderFieldBoxesGroupComponent({ value, onChange: onChangeMock })
49+
50+
const box = screen.getByTestId(`field-box-${MOCK_VECTOR_SEARCH_BOX.value}`)
51+
52+
fireEvent.click(box)
53+
expect(onChangeMock).toHaveBeenCalledWith([])
54+
})
55+
})

0 commit comments

Comments
 (0)