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

teableio / teable / 8389034572

22 Mar 2024 10:38AM CUT coverage: 26.087% (-2.1%) from 28.208%
8389034572

Pull #487

github

web-flow
Merge 3045b1f94 into a06c6afb1
Pull Request #487: refactor: move zod schema to openapi

2100 of 3363 branches covered (62.44%)

282 of 757 new or added lines in 74 files covered. (37.25%)

224 existing lines in 8 files now uncovered.

25574 of 98035 relevant lines covered (26.09%)

5.17 hits per line

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

0.0
/packages/sdk/src/components/grid-enhancements/hooks/use-grid-collapsed-group.ts
NEW
1
import { is, or, and, isNot, hasNoneOf, isNotEmpty, FieldType } from '@teable/core';
×
NEW
2
import type { IFilter, IFilterSet, ILinkCellValue, IOperator, IUserCellValue } from '@teable/core';
×
NEW
3
import { GroupPointType } from '@teable/openapi';
×
NEW
4
import type { IGetRecordsRo, IGroupHeaderPoint, IGroupPointsVo } from '@teable/openapi';
×
5
import { useCallback, useMemo } from 'react';
×
6
import { useFields, useView, useViewId } from '../../../hooks';
×
7
import type { GridView, IFieldInstance } from '../../../model';
×
8
import { useGridCollapsedGroupStore } from '../store';
×
9

×
10
const FILTER_RELATED_FILED_TYPE_SET = new Set([
×
11
  FieldType.MultipleSelect,
×
12
  FieldType.User,
×
13
  FieldType.Link,
×
14
]);
×
15

×
16
export const cellValue2FilterValue = (cellValue: unknown, field: IFieldInstance) => {
×
17
  const { type, isMultipleCellValue } = field;
×
18

×
19
  if (cellValue == null || ![FieldType.User, FieldType.Link].includes(type)) return cellValue;
×
20

×
21
  if (isMultipleCellValue) {
×
22
    return (cellValue as (IUserCellValue | ILinkCellValue)[])?.map((v) => v.id);
×
23
  }
×
24
  return (cellValue as IUserCellValue | ILinkCellValue).id;
×
25
};
×
26

×
27
export const generateFilterItem = (field: IFieldInstance, value: unknown) => {
×
28
  let operator: IOperator = isNot.value;
×
29
  const { id: fieldId, type, isMultipleCellValue } = field;
×
30

×
31
  if (type === FieldType.Checkbox) {
×
32
    operator = is.value;
×
33
    value = !value || null;
×
34
  } else if (value == null) {
×
35
    operator = isNotEmpty.value;
×
36
  } else if (FILTER_RELATED_FILED_TYPE_SET.has(type) && isMultipleCellValue) {
×
37
    operator = hasNoneOf.value;
×
38
  }
×
39

×
40
  return {
×
41
    fieldId,
×
42
    value: cellValue2FilterValue(value, field) as never,
×
43
    operator,
×
44
  };
×
45
};
×
46

×
47
export const useGridCollapsedGroup = (cacheKey: string, groupPoints: IGroupPointsVo) => {
×
48
  const activeViewId = useViewId();
×
49
  const view = useView(activeViewId) as GridView | undefined;
×
50
  const totalFields = useFields({ withHidden: true });
×
51
  const { collapsedGroupMap, setCollapsedGroupMap } = useGridCollapsedGroupStore();
×
52

×
53
  const group = view?.group;
×
54

×
55
  const collapsedGroupIds = useMemo(() => {
×
56
    const collapsedGroupIds = collapsedGroupMap?.[cacheKey];
×
57
    return collapsedGroupIds?.length ? new Set(collapsedGroupIds) : null;
×
58
  }, [cacheKey, collapsedGroupMap]);
×
59

×
60
  const onCollapsedGroupChanged = useCallback(
×
61
    (groupIds: Set<string>) => {
×
62
      setCollapsedGroupMap(cacheKey, [...groupIds]);
×
63
    },
×
64
    [cacheKey, setCollapsedGroupMap]
×
65
  );
×
66

×
67
  const groupId2DataMap = useMemo(() => {
×
68
    if (groupPoints == null) return null;
×
69
    const groupIds: string[] = [];
×
70
    return groupPoints.reduce(
×
71
      (prev, cur) => {
×
72
        if (cur.type !== GroupPointType.Header) {
×
73
          return prev;
×
74
        }
×
75
        const { id, depth } = cur;
×
76

×
77
        groupIds[depth] = id;
×
78
        prev[id] = { ...cur, path: groupIds.slice(0, depth + 1) };
×
79
        return prev;
×
80
      },
×
81
      {} as Record<string, IGroupHeaderPoint & { path: string[] }>
×
82
    );
×
83
  }, [groupPoints]);
×
84

×
85
  const fieldId2DataMap = useMemo(() => {
×
86
    return totalFields.reduce(
×
87
      (prev, field) => {
×
88
        prev[field.id] = field;
×
89
        return prev;
×
90
      },
×
91
      {} as Record<string, IFieldInstance>
×
92
    );
×
93
  }, [totalFields]);
×
94

×
95
  // eslint-disable-next-line sonarjs/cognitive-complexity
×
96
  const viewGroupQuery = useMemo(() => {
×
97
    if (!group?.length) {
×
98
      return undefined;
×
99
    }
×
100

×
101
    if (groupId2DataMap == null || collapsedGroupIds == null || !collapsedGroupIds.size) {
×
102
      return { groupBy: group as IGetRecordsRo['groupBy'] };
×
103
    }
×
104

×
105
    const filterQuery: IFilter = {
×
106
      conjunction: and.value,
×
107
      filterSet: [],
×
108
    };
×
109

×
110
    for (const groupId of collapsedGroupIds) {
×
111
      const groupData = groupId2DataMap[groupId];
×
112

×
113
      if (groupData == null) continue;
×
114

×
115
      const { path } = groupData;
×
116
      const innerFilterSet: IFilterSet = {
×
117
        conjunction: or.value,
×
118
        filterSet: [],
×
119
      };
×
120

×
121
      path.forEach((pathGroupId) => {
×
122
        const pathGroupData = groupId2DataMap[pathGroupId];
×
123

×
124
        if (pathGroupData == null) return;
×
125

×
126
        const { depth } = pathGroupData;
×
127
        const curGroup = group[depth];
×
128

×
129
        if (curGroup == null) return;
×
130

×
131
        const { fieldId } = curGroup;
×
132
        const field = fieldId2DataMap[fieldId];
×
133

×
134
        if (field == null) return;
×
135

×
136
        const filterItem = generateFilterItem(field, pathGroupData.value);
×
137
        innerFilterSet.filterSet.push(filterItem);
×
138
      });
×
139

×
140
      filterQuery.filterSet.push(innerFilterSet);
×
141
    }
×
142

×
143
    return { filter: filterQuery, groupBy: group as IGetRecordsRo['groupBy'] };
×
144
  }, [groupId2DataMap, collapsedGroupIds, fieldId2DataMap, group]);
×
145

×
146
  return useMemo(
×
147
    () => ({
×
148
      viewGroupQuery,
×
149
      collapsedGroupIds,
×
150
      onCollapsedGroupChanged,
×
151
    }),
×
152
    [viewGroupQuery, collapsedGroupIds, onCollapsedGroupChanged]
×
153
  );
×
154
};
×
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