@object-ui/plugin-view 3.1.0 → 3.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -546,7 +546,7 @@ export const ObjectView: React.FC<ObjectViewProps> = ({
546
546
 
547
547
  const filterableFieldDefs = fieldEntries.map(([key, f]: [string, any]) => {
548
548
  const fieldType = f.type || 'text';
549
- let filterType: 'text' | 'number' | 'select' | 'date' | 'boolean' = 'text';
549
+ let filterType: 'text' | 'number' | 'select' | 'multi-select' | 'date' | 'boolean' = 'text';
550
550
  let options: Array<{ label: string; value: any }> | undefined;
551
551
 
552
552
  if (fieldType === 'number' || fieldType === 'currency' || fieldType === 'percent') {
@@ -555,11 +555,18 @@ export const ObjectView: React.FC<ObjectViewProps> = ({
555
555
  filterType = 'boolean';
556
556
  } else if (fieldType === 'date' || fieldType === 'datetime') {
557
557
  filterType = 'date';
558
- } else if (fieldType === 'select' || f.options) {
558
+ } else if (fieldType === 'select' || fieldType === 'status' || f.options) {
559
559
  filterType = 'select';
560
560
  options = (f.options || []).map((o: any) =>
561
561
  typeof o === 'string' ? { label: o, value: o } : { label: o.label, value: o.value },
562
562
  );
563
+ } else if (fieldType === 'lookup' || fieldType === 'master_detail' || fieldType === 'user' || fieldType === 'owner') {
564
+ if (f.options && f.options.length > 0) {
565
+ filterType = 'multi-select';
566
+ options = (f.options || []).map((o: any) =>
567
+ typeof o === 'string' ? { label: o, value: o } : { label: o.label, value: o.value },
568
+ );
569
+ }
563
570
  }
564
571
  return {
565
572
  field: key,
@@ -541,4 +541,101 @@ describe('FilterUI', () => {
541
541
  window.removeEventListener('filter:changed', spy);
542
542
  });
543
543
  });
544
+
545
+ // -------------------------------------------------------------------------
546
+ // 9. Multi-select filter type
547
+ // -------------------------------------------------------------------------
548
+ describe('multi-select filter type', () => {
549
+ const multiSelectFilters: FilterUISchema['filters'] = [
550
+ {
551
+ field: 'tags',
552
+ label: 'Tags',
553
+ type: 'multi-select',
554
+ options: [
555
+ { label: 'Frontend', value: 'frontend' },
556
+ { label: 'Backend', value: 'backend' },
557
+ { label: 'DevOps', value: 'devops' },
558
+ ],
559
+ },
560
+ ];
561
+
562
+ it('renders checkboxes for multi-select type', () => {
563
+ render(
564
+ <FilterUI
565
+ schema={makeSchema({ filters: multiSelectFilters })}
566
+ />,
567
+ );
568
+
569
+ expect(screen.getByText('Tags')).toBeInTheDocument();
570
+ const checkboxes = screen.getAllByTestId('checkbox');
571
+ expect(checkboxes.length).toBe(3);
572
+ expect(screen.getByText('Frontend')).toBeInTheDocument();
573
+ expect(screen.getByText('Backend')).toBeInTheDocument();
574
+ expect(screen.getByText('DevOps')).toBeInTheDocument();
575
+ });
576
+
577
+ it('calls onChange with array when multi-select checkbox is toggled', () => {
578
+ const onChange = vi.fn();
579
+ render(
580
+ <FilterUI
581
+ schema={makeSchema({ filters: multiSelectFilters })}
582
+ onChange={onChange}
583
+ />,
584
+ );
585
+
586
+ const checkboxes = screen.getAllByTestId('checkbox');
587
+ fireEvent.click(checkboxes[0]); // Frontend
588
+ expect(onChange).toHaveBeenCalledWith({ tags: ['frontend'] });
589
+ });
590
+
591
+ it('adds to selection when another checkbox is checked', () => {
592
+ const onChange = vi.fn();
593
+ render(
594
+ <FilterUI
595
+ schema={makeSchema({
596
+ filters: multiSelectFilters,
597
+ values: { tags: ['frontend'] },
598
+ })}
599
+ onChange={onChange}
600
+ />,
601
+ );
602
+
603
+ const checkboxes = screen.getAllByTestId('checkbox');
604
+ fireEvent.click(checkboxes[1]); // Backend
605
+ expect(onChange).toHaveBeenCalledWith({ tags: ['frontend', 'backend'] });
606
+ });
607
+
608
+ it('removes from selection when checkbox is unchecked', () => {
609
+ const onChange = vi.fn();
610
+ render(
611
+ <FilterUI
612
+ schema={makeSchema({
613
+ filters: multiSelectFilters,
614
+ values: { tags: ['frontend', 'backend'] },
615
+ })}
616
+ onChange={onChange}
617
+ />,
618
+ );
619
+
620
+ // Uncheck Frontend (first checkbox)
621
+ const checkboxes = screen.getAllByTestId('checkbox');
622
+ fireEvent.click(checkboxes[0]); // Frontend
623
+ expect(onChange).toHaveBeenCalledWith({ tags: ['backend'] });
624
+ });
625
+
626
+ it('shows selected count in active badge for popover layout', () => {
627
+ render(
628
+ <FilterUI
629
+ schema={makeSchema({
630
+ layout: 'popover',
631
+ filters: multiSelectFilters,
632
+ values: { tags: ['frontend', 'backend'] },
633
+ })}
634
+ />,
635
+ );
636
+
637
+ // Active count should be 1 (tags field has a value)
638
+ expect(screen.getByText('1')).toBeInTheDocument();
639
+ });
640
+ });
544
641
  });