Skip to content

RI-7197 Rework the manage index section #4785

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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
45 changes: 34 additions & 11 deletions redisinsight/ui/src/mocks/factories/redisearch/IndexInfo.factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import {
IndexAttibuteDto,
IndexInfoDto,
} from 'apiSrc/modules/browser/redisearch/dto'
import { INDEX_INFO_SEPARATORS } from './IndexInfoTableData.factory'

export const INDEX_INFO_SEPARATORS: string[] = [',', ';', '|', ':']

// Note: Current data is replica of the sample data, but we can make it more realistic/diverse in the future
export const indexInfoFactory = Factory.define<IndexInfoDto>(() => ({
Expand Down Expand Up @@ -76,21 +77,43 @@ export const indexInfoFactory = Factory.define<IndexInfoDto>(() => ({
'field statistics': indexInfoFieldStatisticsFactory.buildList(3),
}))

export const indexInfoAttributeFactory = Factory.define<IndexAttibuteDto>(
() => {
const name = faker.word.noun()
type IndexInfoAttributeFactoryTransientParams = {
includeWeight?: boolean
includeSeparator?: boolean
includeNoIndex?: boolean
}

return {
identifier: `$.${name}`,
attribute: name,
type: faker.helpers.enumValue(FieldTypes).toString(),
export const indexInfoAttributeFactory = Factory.define<
IndexAttibuteDto,
IndexInfoAttributeFactoryTransientParams
>(({ transientParams }) => {
const name = faker.word.noun()

const {
includeWeight = faker.datatype.boolean(),
includeSeparator = faker.datatype.boolean(),
includeNoIndex = faker.datatype.boolean(),
} = transientParams

return {
identifier: `$.${name}`,
attribute: name,
type: faker.helpers.enumValue(FieldTypes).toString(),

// Optional fields
...(includeWeight && {
WEIGHT: faker.number
.float({ min: 0.1, max: 10, fractionDigits: 1 })
.toString(),
}),
...(includeSeparator && {
SEPARATOR: faker.helpers.arrayElement(INDEX_INFO_SEPARATORS),
}
},
)
}),
...(includeNoIndex && {
NOINDEX: faker.datatype.boolean(),
}),
}
})

export const indexInfoFieldStatisticsFactory =
Factory.define<FieldStatisticsDto>(() => {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import React from 'react'
import { cleanup, render, screen } from 'uiSrc/utils/test-utils'
import { indexInfoTableDataFactory } from 'uiSrc/mocks/factories/redisearch/IndexInfoTableData.factory'
import {
indexInfoAttributeFactory,
indexInfoFactory,
} from 'uiSrc/mocks/factories/redisearch/IndexInfo.factory'
import {
IndexAttributesList,
IndexAttributesListProps,
} from './IndexAttributesList'

const renderComponent = (props?: Partial<IndexAttributesListProps>) => {
const defaultProps: IndexAttributesListProps = {
data: indexInfoTableDataFactory.buildList(3),
indexInfo: indexInfoFactory.build(),
}

return render(<IndexAttributesList {...defaultProps} {...props} />)
Expand All @@ -21,12 +24,41 @@ describe('IndexAttributesList', () => {

it('should render', () => {
const props: IndexAttributesListProps = {
data: [
indexInfoTableDataFactory.build(
{},
{ transient: { includeWeight: true, includeSeparator: true } },
),
],
indexInfo: indexInfoFactory.build(),
}

const { container } = renderComponent(props)
expect(container).toBeTruthy()

const list = screen.getByTestId('index-attributes-list')
expect(list).toBeInTheDocument()

const table = screen.getByTestId('index-attributes-list--table')
const summaryInfo = screen.getByTestId(
'index-attributes-list--summary-info',
)

expect(table).toBeInTheDocument()
expect(summaryInfo).toBeInTheDocument()
})

it('should render loader when index info is not provided', () => {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Image

renderComponent({ indexInfo: undefined })

const loader = screen.getByTestId('index-attributes-list--loader')
expect(loader).toBeInTheDocument()
})

it('should render index attributes in the table', () => {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Image

const mockIndexAttribute = indexInfoAttributeFactory.build(
{},
{ transient: { includeWeight: true, includeNoIndex: true } },
)

const props: IndexAttributesListProps = {
indexInfo: indexInfoFactory.build({
attributes: [mockIndexAttribute],
}),
}

const { container } = renderComponent(props)
Expand All @@ -36,14 +68,67 @@ describe('IndexAttributesList', () => {
expect(list).toBeInTheDocument()

// Verify data is rendered correctly
const attribute = screen.getByText(props.data[0].attribute)
const type = screen.getByText(props.data[0].type)
const weight = screen.getByText(props.data[0].weight!)
const separator = screen.getByText(props.data[0].separator!)
const identifier = screen.getByText(mockIndexAttribute.identifier)
const attribute = screen.getByText(mockIndexAttribute.attribute)
const type = screen.getByText(mockIndexAttribute.type)
const weight = screen.getByText(mockIndexAttribute.WEIGHT!)
const noIndex = screen.getByTestId('index-attributes-list--noindex-icon')

expect(identifier).toBeInTheDocument()
expect(attribute).toBeInTheDocument()
expect(type).toBeInTheDocument()
expect(weight).toBeInTheDocument()
expect(separator).toBeInTheDocument()
expect(noIndex).toBeInTheDocument()
expect(noIndex).toHaveAttribute(
'data-attribute',
mockIndexAttribute.NOINDEX?.toString(),
)
})

it('should display index summary info', () => {
const mockIndexInfo = indexInfoFactory.build()

const props: IndexAttributesListProps = {
indexInfo: mockIndexInfo,
}

renderComponent(props)

const summaryInfo = screen.getByTestId(
'index-attributes-list--summary-info',
)
expect(summaryInfo).toBeInTheDocument()

// Verify Number of documents
const numberOfDocumentLabel = screen.getByText(/Number of docs:/)
const numberOfDocumentValue = screen.getByText(
new RegExp(mockIndexInfo.num_docs),
)
expect(numberOfDocumentLabel).toBeInTheDocument()
expect(numberOfDocumentValue).toBeInTheDocument()

// Verify Max document ID
const maxDocumentIdLabel = screen.getByText(/max/)
const maxDocumentIdValue = screen.getByText(
new RegExp(mockIndexInfo.max_doc_id!),
)
expect(maxDocumentIdLabel).toBeInTheDocument()
expect(maxDocumentIdValue).toBeInTheDocument()

// Verify Number of records
const numberOfRecordsLabel = screen.getByText(/Number of records:/)
const numberOfRecordsValue = screen.getByText(
new RegExp(mockIndexInfo.num_records!),
)
expect(numberOfRecordsLabel).toBeInTheDocument()
expect(numberOfRecordsValue).toBeInTheDocument()

// Verify Number of terms
const numberOfTermsLabel = screen.getByText(/Number of terms:/)
const numberOfTermsValue = screen.getByText(
new RegExp(mockIndexInfo.num_terms!),
)
expect(numberOfTermsLabel).toBeInTheDocument()
expect(numberOfTermsValue).toBeInTheDocument()
})
})
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
import styled from 'styled-components'

export const StyledIndexAttributesList = styled.div`
> *:first-child {
box-shadow: none !important;
border-radius: 0;
margin: -30px 0;
}
display: flex;
gap: ${({ theme }) => theme.core.space.space150};
flex-direction: column;
`

export const StyledIndexAttributesTable = styled.div`
// Drawer width (60rem) minus its padding (2 * 3.2rem), we don't have them as variables in Redis UI
width: calc(60rem - 2 * 3.2rem);
`

export const StyledIndexSummaryInfo = styled.div`
font-size: ${({ theme }) => theme.core.font.fontSize.s12};
`
Original file line number Diff line number Diff line change
@@ -1,15 +1,28 @@
import React from 'react'
import { ColumnDefinition, Table } from 'uiSrc/components/base/layout/table'
import { StyledIndexAttributesList } from './IndexAttributesList.styles'
import { RiIcon } from 'uiSrc/components/base/icons'
import { Loader } from 'uiSrc/components/base/display'
import { IndexInfoDto } from 'apiSrc/modules/browser/redisearch/dto'
import {
StyledIndexAttributesList,
StyledIndexAttributesTable,
StyledIndexSummaryInfo,
} from './IndexAttributesList.styles'

export interface IndexInfoTableData {
identifier: string
attribute: string
type: string
weight?: string
separator?: string
noindex?: boolean
}

const tableColumns: ColumnDefinition<IndexInfoTableData>[] = [
{
header: 'Identifier',
id: 'identifier',
accessorKey: 'identifier',
},
{
header: 'Attribute',
id: 'attribute',
Expand All @@ -28,20 +41,59 @@ const tableColumns: ColumnDefinition<IndexInfoTableData>[] = [
enableSorting: false,
},
{
header: 'Separator',
id: 'separator',
accessorKey: 'separator',
header: 'Noindex',
id: 'noindex',
accessorKey: 'noindex',
enableSorting: false,
cell: ({ row }) => (
<RiIcon
type={row.original.noindex ? 'CheckBoldIcon' : 'CancelIcon'}
color={row.original.noindex ? 'primary400' : 'danger500'}
data-testid="index-attributes-list--noindex-icon"
data-attribute={row.original.noindex}
/>
),
},
]

export interface IndexAttributesListProps {
data: IndexInfoTableData[]
indexInfo: IndexInfoDto | undefined
}

export const IndexAttributesList = ({
indexInfo,
}: IndexAttributesListProps) => {
// eslint-disable-next-line @typescript-eslint/naming-convention
const { num_docs, max_doc_id, num_records, num_terms } = indexInfo || {}

if (!indexInfo) {
return <Loader data-testid="index-attributes-list--loader" />
}

return (
<StyledIndexAttributesList data-testid="index-attributes-list" as="div">
<StyledIndexAttributesTable
as="div"
data-testid="index-attributes-list--table"
>
<Table columns={tableColumns} data={parseIndexAttributes(indexInfo)} />
</StyledIndexAttributesTable>

<StyledIndexSummaryInfo>
<p data-testid="index-attributes-list--summary-info">
Number of docs: {num_docs} (max {max_doc_id}) | Number of records:{' '}
{num_records} | Number of terms: {num_terms}
</p>
</StyledIndexSummaryInfo>
</StyledIndexAttributesList>
)
}

export const IndexAttributesList = ({ data }: IndexAttributesListProps) => (
// @ts-expect-error - styled-components typing issue: The TypeScript definitions for styled-components
<StyledIndexAttributesList data-testid="index-attributes-list">
<Table columns={tableColumns} data={data} />
</StyledIndexAttributesList>
)
const parseIndexAttributes = (indexInfo: IndexInfoDto): IndexInfoTableData[] =>
indexInfo.attributes.map((field) => ({
identifier: field.identifier,
attribute: field.attribute,
type: field.type,
weight: field.WEIGHT,
noindex: field.NOINDEX ?? true,
}))
Loading
Loading