Skip to content

RI-7190 Field Box component #4716

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

Conversation

valkirilov
Copy link
Collaborator

Description

Create base components for the "field box" and a "group of field boxes" that can be used in the "Create Index" step part of the new Vector Search feature

  • integrate the MultiBoxSelectionGroup component from Redis UI
  • created two standalone components, FieldBox and FieldBoxesGroup, that can be used together to present the interface for picking which fields to be added to the new Vector Search index
image

How to test it

These are base components that will be included in the new wizard for the Vector Search, but currently, you can't find them integrated anywhere in the flow so far. In the first version of the feature, they should look similar to the following preview

image

How to use it

FieldBox component

// Example field details
const box: VectorSearchBox = {
    value: 'id',
    label: 'id',
    text: 'Unique product identifier',
    tag: FieldTypes.TAG,
    disabled: true,
};

// And here is how we init the component itself, always wrapped inside a group parent
<BoxSelectionGroup.Compose>
    <FieldBox box={box} />
</BoxSelectionGroup.Compose>

FieldBoxesGroup component

// Example data fields
const boxes: VectorSearchBox[] = [
  {
    value: 'id',
    label: 'id',
    text: 'Unique product identifier',
    tag: FieldTypes.TAG,
    disabled: true,
  },
  // and more fields here...
]

// Helper state variables and modifiers
const [selectedBoxes, setSelectedBoxes] = useState<string[]>(
    getDefaultSelectedBoxes(boxes),
  )

const handleBoxSelectionChange = (newValue: string[] | undefined) => {
    setSelectedBoxes(newValue || [])
}

// And here is how we init the component itself
<FieldBoxesGroup
   boxes={boxes}
   value={selectedBoxes}
   onChange={handleBoxSelectionChange}
/>

Full Example

  • wrapper component
const ExampleWrapper = () => {
  const { boxes } = useIndexFields(BIKES_INDEX_FIELDS)

  const [selectedBoxes, setSelectedBoxes] = useState<string[]>(
    getDefaultSelectedBoxes(boxes),
  )

  const handleBoxSelectionChange = (newValue: string[] | undefined) => {
    setSelectedBoxes(newValue || [])
  }

  return (
      <FieldBoxesGroup
         boxes={boxes}
         value={selectedBoxes}
         onChange={handleBoxSelectionChange}
      />
  )
}
  • preset with example data
export interface RedisIndexField {
  name: string
  type: FieldTypes
  description?: string
}

// Note: We start with this preset of hardcoded fields for the Bikes index, but in the future, we might want to fetch these fields from a server or allow users to customize them.
export const BIKES_INDEX_FIELDS: RedisIndexField[] = [
  {
    name: 'id',
    type: FieldTypes.TAG,
    description: 'Unique product identifier',
  },
  {
    name: 'description',
    type: FieldTypes.TEXT,
    description: 'Product description',
  },
  {
    name: 'price',
    type: FieldTypes.NUMERIC,
    description: 'Product price',
  },
  {
    name: 'price_1',
    type: FieldTypes.NUMERIC,
    description: 'Product price',
  },
  {
    name: 'name',
    type: FieldTypes.TEXT,
    description: 'Product name',
  },
  { name: 'category', type: FieldTypes.TAG, description: 'Product category' },
  {
    name: 'embedding',
    type: FieldTypes.VECTOR,
    description: 'Product embedding vector',
  },
  {
    name: 'embedding_1',
    type: FieldTypes.VECTOR,
    description: 'Product embedding vector',
  },
]
  • helper hooks and utils to keep the state somehow isolated from the main component
interface UseIndexFieldsHook {
  boxes: VectorSearchBox[]
}

export const useIndexFields = (
  indexFields: RedisIndexField[],
): UseIndexFieldsHook => {
  const boxes = indexFields.map(convertIndexFieldToBoxSelectionGroupBox)

  return {
    boxes,
  }
}

const convertIndexFieldToBoxSelectionGroupBox = (
  field: RedisIndexField,
  index: number,
): VectorSearchBox => ({
  value: field.name,
  label: field.name,
  text: field?.description ?? '',
  tag: field.type,
  disabled: true,
})

const getDefaultSelectedBoxes = (boxes: VectorSearchBox[]): string[] => boxes.map((box) => box.value)

FieldTypes,
} from 'uiSrc/pages/browser/components/create-redisearch-index/constants'

// TODO: Add colors mapping for tags when @redis-ui/components v38.6.0 is released
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Badge component in @redis-ui/components v38.6.0 supports custom color out of the box (docs), so we can colorize the different types of fields, once we update the dependency version.

Note: Currently @redis-ui/components and @redis-ui/styles. Unfortunately, the latter comes with some breaking changes in its latest version, so the upgrade process might need additional effort, and I won't do it as part of this task.

import { VectorSearchBox } from 'uiSrc/components/new-index/create-index-step/field-box/types'
import { FieldTypes } from 'uiSrc/pages/browser/components/create-redisearch-index/constants'

// TODO: Maybe transform this to a factory function, so it can be reused more easily
Copy link
Collaborator Author

@valkirilov valkirilov Jul 11, 2025

Choose a reason for hiding this comment

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

Using factories (like Fishery) makes it easier to generate consistent, realistic mock data in our tests without repeating the same setup code over and over. It helps keep the tests clean and focused on what they're actually testing - not on building all the data manually every time.

For example, instead of having this static object and reusing it everywhere, we can define a factory:

const userFactory = Factory.define(() => ({
  id: '123',
  name: 'Test User',
  email: '[email protected]',
}));

And then just use:

// Create a new example object
const user = userFactory.build();

// Or create an array of example objects
const users = userfactory.buildList(3)

// We can also easily override fields when needed
const adminUser = userFactory.build({ role: 'admin' });

This keeps tests flexible, readable, and easier to maintain as our data model grows, because we have to update the interface in a single place in the code.

PS: It's just a proposal for a future enhancement, so we can discuss it, and I would be happy to hear thoughts or alternatives!

import { FieldTypes } from 'uiSrc/pages/browser/components/create-redisearch-index/constants'

// TODO: Maybe transform this to a factory function, so it can be reused more easily
// TODO: Maybe make the values more dynamic with faker, so we can test more cases
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

By combining Faker with our mocks/factories, we can easily generate realistic, randomized data for our test cases. This helps avoid overly hardcoded or repetitive test data and gives us more variety and confidence that our code works with different inputs - not just the same values every time.

For example:

import { Factory } from 'fishery';
import { faker } from '@faker-js/faker';

const userFactory = Factory.define(() => ({
  id: faker.string.uuid(),
  name: faker.person.fullName(),
  email: faker.internet.email(),
}));

Now each test run gets slightly different (but still valid) data, which can help us catch edge cases or assumptions we might otherwise miss.

Of course, if we need consistent values for a specific test, we can always override them manually:

userFactory.build({ email: '[email protected]' });

So it’s a nice balance of flexibility and realism in our tests!

PS: It's just a proposal for a future enhancement, so we can discuss it, and I would be happy to hear thoughts or alternatives!

@valkirilov valkirilov changed the title Fe/feature/ri 7190 field box component RI-7190 Field Box component Jul 11, 2025
@valkirilov valkirilov marked this pull request as ready for review July 14, 2025 06:02
@valkirilov valkirilov self-assigned this Jul 14, 2025
@pawelangelow pawelangelow force-pushed the feature/RI-6855/vector-search branch from dde0494 to 0664a81 Compare July 18, 2025 12:54
pawelangelow
pawelangelow previously approved these changes Jul 22, 2025
expect(buildNewIndexTabContent).toBeInTheDocument()
})

it('shouldn\'t switch to "Build new index" tab when clicked, since it is disabled', () => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

very very very nit: maybe it's personal pref, but should not or "shouldn't.." is read better IMO

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

No worries, I was under the impression we have forced preferences over '.
Now the title of the test is fixed, as suggested.

@@ -0,0 +1,40 @@
import React from 'react'
Copy link
Collaborator

Choose a reason for hiding this comment

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

Just checking - was CreateIndexStepWrapper intentionally added to this PR?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good question - initially, I started the work on this component, based on the branch related to the Tabs component, with the idea to add the Field Boxes inside the "Use Preset Index" tab. Later on, I gave up on this idea and provided this PR, with the component being stand-alone.

So, for safety’s sake, I just rebased the branch with the latest state of feature/RI-6855/vector-search, and I hope we're safe to move on with this one.

@valkirilov valkirilov force-pushed the fe/feature/RI-7190_field-box-component branch from ee4dfbd to 71a9d5a Compare July 22, 2025 14:12
Copy link
Contributor

Code Coverage - Frontend unit tests

St.
Category Percentage Covered / Total
🟢 Statements 81.39% 18875/23192
🟡 Branches 66.95% 8240/12307
🟡 Functions 75.09% 4940/6579
🟢 Lines 81.8% 18474/22585

Test suite run success

4816 tests passing in 635 suites.

Report generated by 🧪jest coverage report action from 71a9d5a

@valkirilov valkirilov merged commit d69f0c5 into feature/RI-6855/vector-search Jul 23, 2025
17 checks passed
@valkirilov valkirilov deleted the fe/feature/RI-7190_field-box-component branch July 23, 2025 07:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants