Skip to content

Commit 1da9168

Browse files
committed
refactor(table): add columnPicker functionality with column picker dialog, toolbar buttons, visibility state, and tests
- Add MuiColumnPickerDialog with Apply/Export actions, locked-column enforcement, and optional dataColumnIds hint when no data columns are selected - Update MuiTableToolbar to render a trigger button (opens dialog) and a direct export button alongside the existing CSV download icon - Extend useTanStackTableBuilder with column visibility state (localStorage persistence, dynamic reconciliation), onExportFromPicker, onDirectExport, and cross-page selection helpers (selectedCount, toggleAllFiltered, etc.) - Add ColumnPickerTemplate interface and Body.ts selection fields - Add full test coverage for dialog, toolbar, and hook behaviour - Add lib.components.table.* locale keys (en/ko/zh)
1 parent 958e986 commit 1da9168

30 files changed

Lines changed: 1728 additions & 62 deletions

client/app/bundles/course/duplication/pages/Duplication/DuplicateItemsConfirmation/AssessmentsListing.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { FC } from 'react';
22
import { defineMessages } from 'react-intl';
33
import { Card, CardContent, ListSubheader } from '@mui/material';
44

5-
import IndentedCheckbox from 'course/duplication/components/IndentedCheckbox';
65
import TypeBadge from 'course/duplication/components/TypeBadge';
76
import UnpublishedIcon from 'course/duplication/components/UnpublishedIcon';
87
import { selectDuplicationStore } from 'course/duplication/selectors';
@@ -12,6 +11,7 @@ import {
1211
DuplicationTabData,
1312
} from 'course/duplication/types';
1413
import componentTranslations from 'course/translations';
14+
import IndentedCheckbox from 'lib/components/core/IndentedCheckbox';
1515
import { useAppSelector } from 'lib/hooks/store';
1616
import useTranslation from 'lib/hooks/useTranslation';
1717

client/app/bundles/course/duplication/pages/Duplication/DuplicateItemsConfirmation/MaterialsListing.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@ import { FC } from 'react';
22
import { defineMessages } from 'react-intl';
33
import { Card, CardContent, ListSubheader } from '@mui/material';
44

5-
import IndentedCheckbox from 'course/duplication/components/IndentedCheckbox';
65
import TypeBadge from 'course/duplication/components/TypeBadge';
76
import { selectDuplicationStore } from 'course/duplication/selectors';
87
import {
98
DuplicationFolderData,
109
DuplicationMaterialData,
1110
} from 'course/duplication/types';
1211
import componentTranslations from 'course/translations';
12+
import IndentedCheckbox from 'lib/components/core/IndentedCheckbox';
1313
import { useAppSelector } from 'lib/hooks/store';
1414
import useTranslation from 'lib/hooks/useTranslation';
1515

client/app/bundles/course/duplication/pages/Duplication/DuplicateItemsConfirmation/VideosListing.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { FC } from 'react';
22
import { defineMessages } from 'react-intl';
33
import { Card, CardContent, ListSubheader } from '@mui/material';
44

5-
import IndentedCheckbox from 'course/duplication/components/IndentedCheckbox';
65
import TypeBadge from 'course/duplication/components/TypeBadge';
76
import UnpublishedIcon from 'course/duplication/components/UnpublishedIcon';
87
import { selectDuplicationStore } from 'course/duplication/selectors';
@@ -11,6 +10,7 @@ import {
1110
DuplicationVideoTabData,
1211
} from 'course/duplication/types';
1312
import componentTranslations from 'course/translations';
13+
import IndentedCheckbox from 'lib/components/core/IndentedCheckbox';
1414
import { useAppSelector } from 'lib/hooks/store';
1515
import useTranslation from 'lib/hooks/useTranslation';
1616

client/app/bundles/course/duplication/pages/Duplication/ItemsSelector/AchievementsSelector.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@ import { defineMessages } from 'react-intl';
33
import { ListSubheader, Typography } from '@mui/material';
44

55
import BulkSelectors from 'course/duplication/components/BulkSelectors';
6-
import IndentedCheckbox from 'course/duplication/components/IndentedCheckbox';
76
import TypeBadge from 'course/duplication/components/TypeBadge';
87
import UnpublishedIcon from 'course/duplication/components/UnpublishedIcon';
98
import { selectDuplicationStore } from 'course/duplication/selectors';
109
import { actions } from 'course/duplication/store';
1110
import { DuplicationAchievementData } from 'course/duplication/types';
1211
import { getAchievementBadgeUrl } from 'course/helper/achievements';
1312
import componentTranslations from 'course/translations';
13+
import IndentedCheckbox from 'lib/components/core/IndentedCheckbox';
1414
import Thumbnail from 'lib/components/core/Thumbnail';
1515
import { useAppDispatch, useAppSelector } from 'lib/hooks/store';
1616
import useTranslation from 'lib/hooks/useTranslation';

client/app/bundles/course/duplication/pages/Duplication/ItemsSelector/AssessmentsSelector.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import { defineMessages } from 'react-intl';
33
import { ListSubheader, Typography } from '@mui/material';
44

55
import BulkSelectors from 'course/duplication/components/BulkSelectors';
6-
import IndentedCheckbox from 'course/duplication/components/IndentedCheckbox';
76
import TypeBadge from 'course/duplication/components/TypeBadge';
87
import UnpublishedIcon from 'course/duplication/components/UnpublishedIcon';
98
import {
@@ -17,6 +16,7 @@ import {
1716
DuplicationTabData,
1817
} from 'course/duplication/types';
1918
import componentTranslations from 'course/translations';
19+
import IndentedCheckbox from 'lib/components/core/IndentedCheckbox';
2020
import { useAppDispatch, useAppSelector } from 'lib/hooks/store';
2121
import useTranslation from 'lib/hooks/useTranslation';
2222

client/app/bundles/course/duplication/pages/Duplication/ItemsSelector/MaterialsSelector.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import { defineMessages } from 'react-intl';
33
import { ListSubheader, Typography } from '@mui/material';
44

55
import BulkSelectors from 'course/duplication/components/BulkSelectors';
6-
import IndentedCheckbox from 'course/duplication/components/IndentedCheckbox';
76
import TypeBadge from 'course/duplication/components/TypeBadge';
87
import { selectDuplicationStore } from 'course/duplication/selectors';
98
import { actions } from 'course/duplication/store';
@@ -12,6 +11,7 @@ import {
1211
DuplicationMaterialData,
1312
} from 'course/duplication/types';
1413
import componentTranslations from 'course/translations';
14+
import IndentedCheckbox from 'lib/components/core/IndentedCheckbox';
1515
import { useAppDispatch, useAppSelector } from 'lib/hooks/store';
1616
import useTranslation from 'lib/hooks/useTranslation';
1717

client/app/bundles/course/duplication/pages/Duplication/ItemsSelector/SurveysSelector.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ import { defineMessages } from 'react-intl';
33
import { ListSubheader, Typography } from '@mui/material';
44

55
import BulkSelectors from 'course/duplication/components/BulkSelectors';
6-
import IndentedCheckbox from 'course/duplication/components/IndentedCheckbox';
76
import TypeBadge from 'course/duplication/components/TypeBadge';
87
import UnpublishedIcon from 'course/duplication/components/UnpublishedIcon';
98
import { selectDuplicationStore } from 'course/duplication/selectors';
109
import { actions } from 'course/duplication/store';
1110
import { DuplicationSurveyData } from 'course/duplication/types';
1211
import componentTranslations from 'course/translations';
12+
import IndentedCheckbox from 'lib/components/core/IndentedCheckbox';
1313
import { useAppDispatch, useAppSelector } from 'lib/hooks/store';
1414
import useTranslation from 'lib/hooks/useTranslation';
1515

client/app/bundles/course/duplication/pages/Duplication/ItemsSelector/VideosSelector.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import { defineMessages } from 'react-intl';
33
import { ListSubheader, Typography } from '@mui/material';
44

55
import BulkSelectors from 'course/duplication/components/BulkSelectors';
6-
import IndentedCheckbox from 'course/duplication/components/IndentedCheckbox';
76
import TypeBadge from 'course/duplication/components/TypeBadge';
87
import UnpublishedIcon from 'course/duplication/components/UnpublishedIcon';
98
import { selectDuplicationStore } from 'course/duplication/selectors';
@@ -13,6 +12,7 @@ import {
1312
DuplicationVideoTabData,
1413
} from 'course/duplication/types';
1514
import componentTranslations from 'course/translations';
15+
import IndentedCheckbox from 'lib/components/core/IndentedCheckbox';
1616
import { useAppDispatch, useAppSelector } from 'lib/hooks/store';
1717
import useTranslation from 'lib/hooks/useTranslation';
1818

client/app/bundles/course/duplication/components/IndentedCheckbox.tsx renamed to client/app/lib/components/core/IndentedCheckbox.tsx

File renamed without changes.
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { FC, ReactNode } from 'react';
2+
3+
import IndentedCheckbox from 'lib/components/core/IndentedCheckbox';
4+
5+
import { ColumnPickerRenderCtx } from '../builder';
6+
7+
interface ColumnPickerTreeGroupProps {
8+
label: string;
9+
/** All leaf column ids that belong to this group (used to derive parent state). */
10+
childIds: string[];
11+
ctx: ColumnPickerRenderCtx;
12+
/** Ids that are locked visible — parent checkbox is disabled when all children are locked. */
13+
locked?: string[];
14+
indentLevel?: number;
15+
children: ReactNode;
16+
}
17+
18+
/**
19+
* Renders a parent checkbox whose checked/indeterminate state mirrors its children's
20+
* visibility, and whose onChange bulk-toggles all children via ctx.setManyVisible.
21+
* Children are rendered below (not inline), giving a vertical tree layout.
22+
*/
23+
const ColumnPickerTreeGroup: FC<ColumnPickerTreeGroupProps> = ({
24+
label,
25+
childIds,
26+
ctx,
27+
locked = [],
28+
indentLevel = 0,
29+
children,
30+
}) => {
31+
const visibleCount = childIds.filter((id) => ctx.isVisible(id)).length;
32+
const allVisible = childIds.length > 0 && visibleCount === childIds.length;
33+
const someVisible = visibleCount > 0 && !allVisible;
34+
const allLocked =
35+
childIds.length > 0 && childIds.every((id) => locked.includes(id));
36+
37+
return (
38+
<div>
39+
<IndentedCheckbox
40+
checked={allVisible}
41+
disabled={allLocked}
42+
indentLevel={indentLevel}
43+
indeterminate={someVisible}
44+
label={label}
45+
onChange={(e) => ctx.setManyVisible(childIds, e.target.checked)}
46+
/>
47+
<div>{children}</div>
48+
</div>
49+
);
50+
};
51+
52+
export default ColumnPickerTreeGroup;

0 commit comments

Comments
 (0)