← Back to components list

PluginFieldsSelect

A dropdown for selecting Tracker fields within a plugin. Accepts a flat list of fields with category metadata and automatically groups them into collapsible sections. Supports search, multiple selection, apply/reset buttons, and a loading state.

Basic usage

import {PluginFieldsSelect} from '@weavix/tracker-components';
import type {PluginFieldsSelectField} from '@weavix/tracker-components';

const fields: PluginFieldsSelectField[] = [
    {id: 'summary', name: 'Summary', category: {id: '1', display: 'System fields'}},
    {id: 'description', name: 'Description', category: {id: '1', display: 'System fields'}},
    {id: 'sprint', name: 'Sprint', category: {id: '2', display: 'Custom fields'}},
];

<PluginFieldsSelect
    open={true}
    fields={fields}
    onOpenChange={(open) => console.log(open)}
    onItemSelect={(item) => console.log('selected', item)}
    onItemDeselect={(item) => console.log('deselected', item)}
/>;

Example 1 — Initial global fields loading only

Loads all global fields once when mounted. Search works on the client side using the already loaded list.

import React, {useEffect, useState} from 'react';
import {PluginFieldsSelect} from '@weavix/tracker-components';
import type {PluginFieldsSelectField, TrackerSelectItem} from '@weavix/tracker-components';

interface Props {
    trackerApi: TrackerApi;
    onSubmit: (selected: TrackerSelectItem[]) => void;
}

export const GlobalFieldsSelect: React.FC<Props> = ({trackerApi, onSubmit}) => {
    const [fields, setFields] = useState<PluginFieldsSelectField[]>([]);
    const [loading, setLoading] = useState(true);
    const [open, setOpen] = useState(false);
    const [selectedItems, setSelectedItems] = useState<TrackerSelectItem[]>([]);

    useEffect(() => {
        trackerApi.v3
            .get['/fields']()
            .then((result) => setFields(result.data))
            .finally(() => setLoading(false));
    }, [trackerApi]);

    return (
        <PluginFieldsSelect
            open={open}
            fields={fields}
            loading={loading}
            selectedItems={selectedItems}
            onOpenChange={setOpen}
            onItemSelect={(item) => setSelectedItems((prev) => [...prev, item])}
            onItemDeselect={(item) =>
                setSelectedItems((prev) => prev.filter((s) => s.id !== item.id))
            }
            onSubmit={() => onSubmit(selectedItems)}
            expandSystemGroup
        />
    );
};

Example 2 — Global suggest on user input

Uses initial loading for the initial list and switches to the suggest endpoint when text is entered.

import React, {useEffect, useState} from 'react';
import {PluginFieldsSelect} from '@weavix/tracker-components';
import type {PluginFieldsSelectField, TrackerSelectItem} from '@weavix/tracker-components';

export const GlobalFieldsSelectWithSuggest: React.FC<Props> = ({trackerApi, onSubmit}) => {
    const [initialFields, setInitialFields] = useState<PluginFieldsSelectField[]>([]);
    const [fields, setFields] = useState<PluginFieldsSelectField[]>([]);
    const [loading, setLoading] = useState(true);
    const [open, setOpen] = useState(false);
    const [selectedItems, setSelectedItems] = useState<TrackerSelectItem[]>([]);

    useEffect(() => {
        trackerApi.v3
            .get['/fields']()
            .then((result) => {
                setInitialFields(result.data);
                setFields(result.data);
            })
            .finally(() => setLoading(false));
    }, [trackerApi]);

    const handleSearchChange = async (text: string) => {
        // Suggest returns an empty response for empty input —
        // revert to the initial list when search is cleared
        if (!text) {
            setFields(initialFields);
            return;
        }
        const result = await trackerApi.v3.post['/fields/_suggest']({
            bodyParams: {input: text, readOnly: true, localFields: false, extended: true},
        });
        setFields(result.data);
    };

    return (
        <PluginFieldsSelect
            open={open}
            fields={fields}
            loading={loading}
            selectedItems={selectedItems}
            onOpenChange={setOpen}
            onSearchChange={handleSearchChange}
            onItemSelect={(item) => setSelectedItems((prev) => [...prev, item])}
            onItemDeselect={(item) =>
                setSelectedItems((prev) => prev.filter((s) => s.id !== item.id))
            }
            onSubmit={() => onSubmit(selectedItems)}
            expandGroupsOnSearch
        />
    );
};

Example 3 — Global + local fields for specific queues

Extends Example 2 by adding queue-specific fields. If queueKeys is empty — only global suggest is used, since localFields: true with an empty queues array causes a request error.

import React, {useEffect, useState} from 'react';
import {PluginFieldsSelect} from '@weavix/tracker-components';
import type {PluginFieldsSelectField, TrackerSelectItem} from '@weavix/tracker-components';

interface Props {
    trackerApi: TrackerApi;
    /** You must pass at least one queue key when using local fields */
    queueKeys: string[];
    onSubmit: (selected: TrackerSelectItem[]) => void;
}

export const LocalFieldsSelectWithSuggest: React.FC<Props> = ({
    trackerApi,
    queueKeys,
    onSubmit,
}) => {
    const [initialFields, setInitialFields] = useState<PluginFieldsSelectField[]>([]);
    const [fields, setFields] = useState<PluginFieldsSelectField[]>([]);
    const [loading, setLoading] = useState(true);
    const [open, setOpen] = useState(false);
    const [selectedItems, setSelectedItems] = useState<TrackerSelectItem[]>([]);

    useEffect(() => {
        trackerApi.v3
            .get['/fields']()
            .then((result) => {
                setInitialFields(result.data);
                setFields(result.data);
            })
            .finally(() => setLoading(false));
    }, [trackerApi]);

    const handleSearchChange = async (text: string) => {
        if (!text) {
            setFields(initialFields);
            return;
        }
        const hasQueues = queueKeys.length > 0;
        const result = await trackerApi.v3.post['/fields/_suggest']({
            bodyParams: {
                input: text,
                readOnly: true,
                localFields: hasQueues,
                extended: true,
                ...(hasQueues && {queues: queueKeys}),
            },
        });
        setFields(result.data);
    };

    return (
        <PluginFieldsSelect
            open={open}
            fields={fields}
            loading={loading}
            selectedItems={selectedItems}
            onOpenChange={setOpen}
            onSearchChange={handleSearchChange}
            onItemSelect={(item) => setSelectedItems((prev) => [...prev, item])}
            onItemDeselect={(item) =>
                setSelectedItems((prev) => prev.filter((s) => s.id !== item.id))
            }
            onSubmit={() => onSubmit(selectedItems)}
            expandGroupsOnSearch
        />
    );
};

Main props

Prop

Type

Default

Description

fields

PluginFieldsSelectField[]

—

Flat list of fields to display

open

boolean

—

Whether the dropdown is open

onOpenChange

(open: boolean) =>
  void

—

Callback when the open state changes

selectedItems

TrackerSelectItem[]

—

Currently selected items

loading

boolean

false

Loading state — shows spinner

onItemSelect

(item: TrackerSelectItem) =>
  void

—

Callback when an item is selected

onItemDeselect

(item: TrackerSelectItem) =>
  void

—

Callback when an item is deselected

onSubmit

() => void
  | Promise<void>

—

Callback when the apply button is clicked

onReset

() =>
  void

—

Callback when the reset button is clicked

onSearchChange

(search: string) =>
  void

—

Callback when search text changes

showSearch

boolean

true

Show search field

showSubmit

boolean

true

Show apply button

showReset

boolean

false

Show reset button

submitButtonText

string

—

Apply button text

resetButtonText

string

—

Reset button text

expandGroupsOnSearch

boolean

false

Expand groups when typing in the search field

expandSystemGroup

boolean

false

Expand the system fields group (id='1') by default

singleSelect

boolean

false

Single selection mode — closes the list after selection

hideCheckbox

boolean

false

Hide checkboxes next to items

showMeta

boolean

false

Show meta line under the item name

disabledItemsIds

string[]

—

IDs of items to display as unavailable

footer

ReactNode

—

Additional content below the item list

className

string

—

Additional CSS class for the root element