Skip to content

Commit dcff0c0

Browse files
committed
feat: implement column settings dialog with drag-and-drop functionality
- Added ColumnSettingsDialog component for managing column visibility and order. - Integrated Material-UI components for better UI/UX. - Implemented drag-and-drop for reordering columns. - Added localization support for column settings. - Created services for loading and saving column settings to the repository. - Updated Grid component to support column settings dialog and apply legacy column settings. - Introduced hooks for managing repository column settings. - Enhanced error handling and loading states for column settings.
1 parent 3070a9e commit dcff0c0

15 files changed

Lines changed: 997 additions & 100 deletions

File tree

Lines changed: 34 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,6 @@
11
import { pathWithQueryParams } from '../../../src/services/query-string-builder'
22

3-
const newColumnSettings = {
4-
columns: [
5-
{ field: 'DisplayName', title: 'Test Display' },
6-
{ field: 'AvailableContentTypeFields', title: 'Test' },
7-
],
8-
}
9-
10-
const originalColumnSettings = {
11-
columns: [
12-
{ field: 'DisplayName', title: 'Display Name' },
13-
{ field: 'AvailableContentTypeFields', title: 'Available Content Type Fields' },
14-
],
15-
}
16-
17-
describe('Add new permission entry', () => {
3+
describe('Column settings', () => {
184
before(() => {
195
cy.login('superAdmin')
206
cy.visit(pathWithQueryParams({ path: '/', newParams: { repoUrl: Cypress.env('repoUrl') } }))
@@ -24,34 +10,40 @@ describe('Add new permission entry', () => {
2410
it('It should open Content Explorer and change the Columns', () => {
2511
cy.get('[data-test="drawer-menu-item-content"]').click()
2612
cy.get('[data-test="column-settings"]').click()
27-
28-
cy.get('.react-monaco-editor-container textarea')
29-
.type('{ctrl}a', { force: true })
30-
.clear({ force: true })
31-
.type(JSON.stringify(newColumnSettings), {
32-
parseSpecialCharSequences: false,
33-
})
34-
35-
cy.get('[data-test="monaco-editor-submit"]').click()
36-
37-
cy.get('[data-test="table-header-actions"]').should('be.visible').find('.MuiButtonBase-root').contains('Action')
38-
cy.get('[data-test="table-header-availablecontenttypefields"]')
39-
.should('be.visible')
40-
.find('.MuiButtonBase-root')
41-
.contains('Test')
42-
cy.get('[data-test="table-header-displayname"]')
43-
.should('be.visible')
44-
.find('.MuiButtonBase-root')
45-
.contains('Test Display')
13+
cy.get('[data-test="column-settings-source"]').should('be.visible')
14+
15+
cy.get('[data-test="column-settings-field-search"]').type('CreationDate')
16+
cy.get('[role="option"]').should('contain', 'Creation Date').and('contain', 'CreationDate')
17+
cy.get('[data-test="column-settings-field-search"]').type('{esc}').clear()
18+
19+
const dataTransfer = new DataTransfer()
20+
cy.get('[data-test="column-settings-drag-availablecontenttypefields"]').trigger('dragstart', { dataTransfer })
21+
cy.get('[data-test="column-settings-row-displayname"]')
22+
.trigger('dragover', { dataTransfer })
23+
.trigger('drop', { dataTransfer })
24+
cy.get('[data-test^="column-settings-row-"]')
25+
.first()
26+
.should('have.attr', 'data-test', 'column-settings-row-availablecontenttypefields')
27+
28+
cy.get('[data-test="column-settings-drag-displayname"]').trigger('dragstart', { dataTransfer })
29+
cy.get('[data-test="column-settings-row-availablecontenttypefields"]')
30+
.trigger('dragover', { dataTransfer })
31+
.trigger('drop', { dataTransfer })
32+
33+
cy.get('[data-test="column-settings-row-displayname"] input').clear().type('Test Display')
34+
cy.get('[data-test="column-settings-row-availablecontenttypefields"] input').clear().type('Test')
35+
cy.get('[data-test="column-settings-save"]').click()
36+
37+
cy.get('.ag-header-cell[col-id="Actions"]').should('be.visible').should('not.contain', 'Actions')
38+
cy.get('[data-test="column-settings"]').should('be.visible')
39+
cy.get('.ag-header-cell[col-id="AvailableContentTypeFields"]').should('be.visible').contains('Test')
40+
cy.get('.ag-header-cell[col-id="DisplayName"]').should('be.visible').contains('Test Display')
4641

4742
cy.get('[data-test="column-settings"]').click()
48-
49-
cy.get('.react-monaco-editor-container textarea')
50-
.type('{ctrl}a', { force: true })
51-
.clear({ force: true })
52-
.type(JSON.stringify(originalColumnSettings), {
53-
parseSpecialCharSequences: false,
54-
})
55-
cy.get('[data-test="monaco-editor-submit"]').click()
43+
cy.get('[data-test="column-settings-row-displayname"] input').clear().type('Display Name')
44+
cy.get('[data-test="column-settings-row-availablecontenttypefields"] input')
45+
.clear()
46+
.type('Available Content Type Fields')
47+
cy.get('[data-test="column-settings-save"]').click()
5648
})
5749
})

apps/sensenet/src/components/content-list/content-list.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import { TableCellProps } from 'react-virtualized'
2929
import { ResponsiveContext, ResponsivePersonalSettings } from '../../context'
3030
import { globals, useGlobalStyles } from '../../globalStyles'
3131
import { useLocalization, usePersonalSettings, useSelectionService } from '../../hooks'
32-
import { isImageContent } from '../../services'
32+
import { isImageContent, LegacyColumnSettings } from '../../services'
3333
import { ContentBreadcrumbs } from '../ContentBreadcrumbs'
3434
import { ContentContextMenu } from '../context-menu/content-context-menu'
3535
import { useDialog } from '../dialogs'
@@ -660,8 +660,9 @@ export const ContentList = <T extends GenericContent = GenericContent>(props: Co
660660
})
661661
}
662662

663-
const setCostumColumnSettings = async (newSettings: { columns: Array<ColumnSetting<GenericContent>> }) => {
664-
ColumnSettingsContainer[props.parentIdOrPath] = { columns: newSettings.columns, lastValidation: new Date() }
663+
const setCostumColumnSettings = async (newSettings: LegacyColumnSettings) => {
664+
const legacyColumns = newSettings.columns as Array<ColumnSetting<GenericContent>>
665+
ColumnSettingsContainer[props.parentIdOrPath] = { columns: legacyColumns, lastValidation: new Date() }
665666

666667
const endpoint = 'WriteSettings'
667668

@@ -681,7 +682,7 @@ export const ContentList = <T extends GenericContent = GenericContent>(props: Co
681682
} catch (error) {
682683
console.error(error)
683684
}
684-
setColumnSettings(newSettings.columns)
685+
setColumnSettings(legacyColumns)
685686
closeLastDialog()
686687
}
687688

apps/sensenet/src/components/content/Explore.tsx

Lines changed: 47 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ import { GridKeyEnum } from '../../../src/components/grid/enums/GridKey.enum'
2020
import { ResponsiveContext, ResponsivePersonalSettings } from '../../context'
2121
import { globals, useGlobalStyles } from '../../globalStyles'
2222
import { useQuery, useSelectionService, useSnRoute } from '../../hooks'
23+
import { useRepositoryColumnSettings } from '../../hooks/use-repository-column-settings'
2324
import { getPrimaryActionUrl, navigateToAction } from '../../services'
25+
import { ColumnSettingsSource, LegacyColumnSetting, LegacyColumnSettings } from '../../services/column-settings-service'
2426
import { resolveContentLinkTarget } from '../../services/favorites'
2527
import { ContentBreadcrumbs } from '../ContentBreadcrumbs'
2628
import { DocumentViewer } from '../document-viewer'
@@ -51,19 +53,33 @@ const requiredGridLoadFields: ODataFieldParameter<GenericContent> = [
5153
'Locked',
5254
]
5355

54-
const getGridLoadChildrenSettings = (colDef: ColDef[]): ODataParams<GenericContent> => {
55-
const selectFields = new Set<keyof GenericContent>(requiredGridLoadFields)
56+
const getGridLoadChildrenSettings = (
57+
colDef: ColDef[],
58+
columnSettings?: LegacyColumnSetting[],
59+
): ODataParams<GenericContent> => {
60+
const selectFields = new Set<string>(requiredGridLoadFields)
61+
const expandFields = new Set<string>(['CreatedBy', 'ModifiedBy'])
5662

5763
colDef.forEach((columnDefinition) => {
5864
if (columnDefinition.field && columnDefinition.field !== '0') {
59-
selectFields.add(columnDefinition.field as keyof GenericContent)
65+
selectFields.add(columnDefinition.field)
66+
}
67+
})
68+
69+
columnSettings?.forEach(({ field }) => {
70+
if (!field || field === 'Actions') {
71+
return
72+
}
73+
selectFields.add(field)
74+
if (field.includes('/')) {
75+
expandFields.add(field.split('/')[0])
6076
}
6177
})
6278

6379
return {
6480
orderby: [['DisplayName', 'asc']],
65-
select: Array.from(selectFields),
66-
expand: ['CreatedBy', 'ModifiedBy'],
81+
select: Array.from(selectFields) as ODataFieldParameter<GenericContent>,
82+
expand: Array.from(expandFields) as ODataFieldParameter<GenericContent>,
6783
onlyselectList: true,
6884
}
6985
}
@@ -222,13 +238,16 @@ export type ExploreProps = {
222238

223239
type ExploreGridOrApplicationProps = {
224240
currentPath: string
225-
fieldsToDisplay?: Array<ColumnSetting<GenericContent>>
241+
fieldsToDisplay?: LegacyColumnSetting[]
226242
schema?: string
227243
disableColumnSettings?: boolean
228244
colDef: ColDef[]
229245
gridKey: GridKeyEnum
230246
onNavigate: (content: GenericContent) => void
231247
onActivateItem: (activeItem: GenericContent) => Promise<void>
248+
onColumnSettingsChange: (settings: LegacyColumnSettings, targetIdOrPath?: string | number) => Promise<void>
249+
columnSettingsSource?: ColumnSettingsSource
250+
isColumnSettingsLoading: boolean
232251
}
233252

234253
const ActiveContentRouteSync: React.FC = () => {
@@ -255,6 +274,9 @@ const ExploreGridOrApplication: React.FC<ExploreGridOrApplicationProps> = ({
255274
gridKey,
256275
onNavigate,
257276
onActivateItem,
277+
onColumnSettingsChange,
278+
columnSettingsSource,
279+
isColumnSettingsLoading,
258280
}) => {
259281
const selectionService = useSelectionService()
260282
const currentContent = useContext(CurrentContentContext)
@@ -269,6 +291,9 @@ const ExploreGridOrApplication: React.FC<ExploreGridOrApplicationProps> = ({
269291
style={{ flexGrow: 7, flexShrink: 0, maxHeight: '100%' }}
270292
enableBreadcrumbs={false}
271293
fieldsToDisplay={fieldsToDisplay}
294+
onColumnSettingsChange={onColumnSettingsChange}
295+
columnSettingsSource={columnSettingsSource}
296+
isColumnSettingsLoading={isColumnSettingsLoading}
272297
schema={schema}
273298
onParentChange={onNavigate}
274299
onActivateItem={onActivateItem}
@@ -358,9 +383,19 @@ export function Explore({
358383
const pathFromUrl = useQuery().get('path')
359384
const snRoute = useSnRoute()
360385
const activeAction = snRoute.match!.params.action
386+
const explicitColumnSettings = useMemo(
387+
() =>
388+
fieldsToDisplay?.map(({ field, title }) => ({
389+
field: String(field),
390+
title,
391+
})),
392+
[fieldsToDisplay],
393+
)
394+
const { columnSettings, columnSettingsSource, isColumnSettingsLoading, saveColumnSettings } =
395+
useRepositoryColumnSettings(currentPath, explicitColumnSettings)
361396
const currentChildrenLoadSettings = useMemo(
362-
() => loadChildrenSettings || getGridLoadChildrenSettings(colDef),
363-
[colDef, loadChildrenSettings],
397+
() => loadChildrenSettings || getGridLoadChildrenSettings(colDef, columnSettings),
398+
[colDef, columnSettings, loadChildrenSettings],
364399
)
365400
const onActivateItemOverride = async (activeItem: GenericContent) => {
366401
const contentToOpen = await resolveContentLinkTarget(repository, activeItem)
@@ -434,7 +469,10 @@ export function Explore({
434469
<ContentInfo />
435470
<ExploreGridOrApplication
436471
disableColumnSettings={disableColumnSettings}
437-
fieldsToDisplay={fieldsToDisplay}
472+
fieldsToDisplay={columnSettings}
473+
onColumnSettingsChange={saveColumnSettings}
474+
columnSettingsSource={columnSettingsSource}
475+
isColumnSettingsLoading={isColumnSettingsLoading}
438476
schema={schema}
439477
onNavigate={onNavigate}
440478
onActivateItem={onActivateItemOverride}

0 commit comments

Comments
 (0)