• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

teableio / teable / 8536869866

03 Apr 2024 10:05AM CUT coverage: 21.234% (-0.3%) from 21.535%
8536869866

Pull #514

github

web-flow
Merge 91a25d710 into 45ee7ebb3
Pull Request #514: refactor: user and link selector

1394 of 2532 branches covered (55.06%)

27 of 1620 new or added lines in 60 files covered. (1.67%)

4 existing lines in 2 files now uncovered.

14588 of 68702 relevant lines covered (21.23%)

2.02 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/packages/sdk/src/components/filter/component/FilterUserSelect.tsx
1
import { useQuery } from '@tanstack/react-query';
×
2
import { isMeTag, Me } from '@teable/core';
×
3
import { User as UserIcon } from '@teable/icons';
×
4
import { getBaseCollaboratorList } from '@teable/openapi';
×
NEW
5
import { useCallback, useMemo } from 'react';
×
6
import { ReactQueryKeys } from '../../../config';
×
7
import { useTranslation } from '../../../context/app/i18n';
×
8
import { useBase, useSession } from '../../../hooks';
×
9
import type { UserField } from '../../../model';
×
NEW
10
import { UserOption, UserTag } from '../../editor';
×
11
import { BaseMultipleSelect, BaseSingleSelect } from './base';
×
12

×
13
interface IFilterUserProps {
×
14
  field: UserField;
×
15
  operator: string;
×
NEW
16
  value: string[] | string | null;
×
17
  onSelect: (value: string[] | string | null) => void;
×
18
}
×
19

×
NEW
20
interface IFilterUserBaseProps extends IFilterUserProps {
×
NEW
21
  data?: {
×
NEW
22
    userId: string;
×
NEW
23
    userName: string;
×
NEW
24
    avatar?: string | null;
×
NEW
25
  }[];
×
NEW
26
  disableMe?: boolean;
×
NEW
27
}
×
NEW
28

×
29
const SINGLE_SELECT_OPERATORS = ['is', 'isNot'];
×
30

×
NEW
31
const FilterUserSelectBase = (props: IFilterUserBaseProps) => {
×
NEW
32
  const { value, onSelect, operator, data, disableMe } = props;
×
NEW
33

×
34
  const { user: currentUser } = useSession();
×
35
  const { t } = useTranslation();
×
36
  const values = useMemo<string | string[] | null>(() => value, [value]);
×
37

×
38
  const options = useMemo(() => {
×
NEW
39
    if (!data?.length) return [];
×
40

×
NEW
41
    const map = data.map(({ userId, userName, avatar }) => ({
×
42
      value: userId,
×
43
      label: userName,
×
44
      avatar: avatar,
×
45
    }));
×
46

×
NEW
47
    if (!disableMe && currentUser) {
×
48
      map.unshift({
×
49
        value: Me,
×
50
        label: t('filter.currentUser'),
×
51
        avatar: null,
×
52
      });
×
53
    }
×
54
    return map;
×
NEW
55
  }, [data, disableMe, currentUser, t]);
×
56

×
57
  const displayRender = useCallback((option: (typeof options)[number]) => {
×
58
    return (
×
NEW
59
      <div className="rounded-lg pr-2 text-secondary-foreground" key={option.value}>
×
60
        <div className="flex items-center space-x-2">
×
NEW
61
          <UserTag
×
NEW
62
            avatar={
×
NEW
63
              isMeTag(option.value) ? (
×
NEW
64
                <span className="flex size-full items-center justify-center bg-background">
×
NEW
65
                  <UserIcon className="size-4" />
×
NEW
66
                </span>
×
NEW
67
              ) : (
×
NEW
68
                option.avatar
×
NEW
69
              )
×
NEW
70
            }
×
NEW
71
            name={option.label}
×
NEW
72
            readonly
×
NEW
73
          />
×
74
        </div>
×
75
      </div>
×
76
    );
×
77
  }, []);
×
78

×
79
  const optionRender = useCallback((option: (typeof options)[number]) => {
×
80
    return (
×
81
      <div
×
82
        key={option.value}
×
83
        className="truncate rounded-lg bg-secondary px-2 text-secondary-foreground"
×
84
      >
×
NEW
85
        <UserOption
×
NEW
86
          className="gap-2"
×
NEW
87
          avatar={
×
NEW
88
            isMeTag(option.value) ? (
×
89
              <span className="flex size-full items-center justify-center">
×
90
                <UserIcon className="size-4" />
×
91
              </span>
×
92
            ) : (
×
NEW
93
              option.avatar
×
NEW
94
            )
×
NEW
95
          }
×
NEW
96
          name={option.label}
×
NEW
97
        />
×
98
      </div>
×
99
    );
×
100
  }, []);
×
101

×
102
  return (
×
103
    <>
×
104
      {SINGLE_SELECT_OPERATORS.includes(operator) ? (
×
105
        <BaseSingleSelect
×
106
          options={options}
×
107
          onSelect={onSelect}
×
108
          value={values as string}
×
109
          displayRender={displayRender}
×
110
          optionRender={optionRender}
×
111
          className="w-64"
×
112
          popoverClassName="w-64"
×
113
        />
×
114
      ) : (
×
115
        <BaseMultipleSelect
×
116
          options={options}
×
117
          onSelect={onSelect}
×
118
          value={values as string[]}
×
119
          displayRender={displayRender}
×
120
          optionRender={optionRender}
×
121
          className="w-64"
×
122
          popoverClassName="w-64"
×
123
        />
×
124
      )}
×
125
    </>
×
126
  );
×
127
};
×
128

×
129
const FilterUserSelect = (props: IFilterUserProps) => {
×
NEW
130
  const { id: baseId } = useBase();
×
NEW
131
  const { data: collaboratorsData } = useQuery({
×
NEW
132
    queryKey: ReactQueryKeys.baseCollaboratorList(baseId),
×
NEW
133
    queryFn: ({ queryKey }) => getBaseCollaboratorList(queryKey[1]),
×
NEW
134
  });
×
NEW
135
  return <FilterUserSelectBase {...props} data={collaboratorsData?.data} />;
×
136
};
×
137

×
NEW
138
export { FilterUserSelect, FilterUserSelectBase };
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc