Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(FileAction): Allow to set destructive flag #1076

Merged
merged 2 commits into from
Sep 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
237 changes: 118 additions & 119 deletions __tests__/fileAction.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@
* SPDX-FileCopyrightText: 2023-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/no-non-null-assertion */
/* eslint-disable no-new */
import { beforeEach, describe, expect, test, vi } from 'vitest'
import type { Node } from '../lib/files/node'
import type { View } from '../lib/navigation/view'

import { getFileActions, registerFileAction, FileAction, DefaultType } from '../lib/fileAction'
import { beforeEach, describe, expect, test, vi } from 'vitest'
import { getFileActions, registerFileAction, FileAction, DefaultType, FileActionData } from '../lib/fileAction'
import logger from '../lib/utils/logger'

describe('FileActions init', () => {
Expand All @@ -34,8 +33,8 @@ describe('FileActions init', () => {
})

expect(action.id).toBe('test')
expect(action.displayName([], {})).toBe('Test')
expect(action.iconSvgInline([], {})).toBe('<svg></svg>')
expect(action.displayName([], {} as unknown as View)).toBe('Test')
expect(action.iconSvgInline([], {} as unknown as View)).toBe('<svg></svg>')

registerFileAction(action)

Expand Down Expand Up @@ -95,131 +94,129 @@ describe('FileActions init', () => {

describe('Invalid FileAction creation', () => {
test('Invalid id', () => {
expect(() => {
new FileAction({
displayName: () => 'Test',
iconSvgInline: () => '<svg></svg>',
exec: async () => true,
} as any as FileAction)
}).toThrowError('Invalid id')
expect(() => new FileAction({
displayName: () => 'Test',
iconSvgInline: () => '<svg></svg>',
exec: async () => true,
} as unknown as FileAction),
).toThrowError('Invalid id')
})
test('Invalid displayName', () => {
expect(() => {
new FileAction({
id: 'test',
displayName: 'Test',
iconSvgInline: () => '<svg></svg>',
exec: async () => true,
} as any as FileAction)
}).toThrowError('Invalid displayName function')
expect(() => new FileAction({
id: 'test',
displayName: 'Test',
iconSvgInline: () => '<svg></svg>',
exec: async () => true,
} as unknown as FileAction),
).toThrowError('Invalid displayName function')
})
test('Invalid title', () => {
expect(() => {
new FileAction({
id: 'test',
displayName: () => 'Test',
title: 'Test',
iconSvgInline: () => '<svg></svg>',
exec: async () => true,
} as any as FileAction)
}).toThrowError('Invalid title function')
expect(() => new FileAction({
id: 'test',
displayName: () => 'Test',
title: 'Test',
iconSvgInline: () => '<svg></svg>',
exec: async () => true,
} as unknown as FileAction),
).toThrowError('Invalid title function')
})
test('Invalid iconSvgInline', () => {
expect(() => {
new FileAction({
id: 'test',
displayName: () => 'Test',
iconSvgInline: '<svg></svg>',
exec: async () => true,
} as any as FileAction)
}).toThrowError('Invalid iconSvgInline function')
expect(() => new FileAction({
id: 'test',
displayName: () => 'Test',
iconSvgInline: '<svg></svg>',
exec: async () => true,
} as unknown as FileAction),
).toThrowError('Invalid iconSvgInline function')
})
test('Invalid exec', () => {
expect(() => {
new FileAction({
id: 'test',
displayName: () => 'Test',
iconSvgInline: () => '<svg></svg>',
exec: false,
} as any as FileAction)
}).toThrowError('Invalid exec function')
expect(() => new FileAction({
id: 'test',
displayName: () => 'Test',
iconSvgInline: () => '<svg></svg>',
exec: false,
} as unknown as FileAction),
).toThrowError('Invalid exec function')
})
test('Invalid enabled', () => {
expect(() => {
new FileAction({
id: 'test',
displayName: () => 'Test',
iconSvgInline: () => '<svg></svg>',
exec: async () => true,
enabled: false,
} as any as FileAction)
}).toThrowError('Invalid enabled function')
expect(() => new FileAction({
id: 'test',
displayName: () => 'Test',
iconSvgInline: () => '<svg></svg>',
exec: async () => true,
enabled: false,
} as unknown as FileAction),
).toThrowError('Invalid enabled function')
})
test('Invalid execBatch', () => {
expect(() => {
new FileAction({
id: 'test',
displayName: () => 'Test',
iconSvgInline: () => '<svg></svg>',
exec: async () => true,
execBatch: false,
} as any as FileAction)
}).toThrowError('Invalid execBatch function')
expect(() => new FileAction({
id: 'test',
displayName: () => 'Test',
iconSvgInline: () => '<svg></svg>',
exec: async () => true,
execBatch: false,
} as unknown as FileAction),
).toThrowError('Invalid execBatch function')
})
test('Invalid order', () => {
expect(() => {
new FileAction({
id: 'test',
displayName: () => 'Test',
iconSvgInline: () => '<svg></svg>',
exec: async () => true,
order: 'invalid',
} as any as FileAction)
}).toThrowError('Invalid order')
expect(() => new FileAction({
id: 'test',
displayName: () => 'Test',
iconSvgInline: () => '<svg></svg>',
exec: async () => true,
order: 'invalid',
} as unknown as FileAction),
).toThrowError('Invalid order')
})
test('Invalid parent', () => {
expect(() => {
new FileAction({
id: 'test',
displayName: () => 'Test',
iconSvgInline: () => '<svg></svg>',
exec: async () => true,
parent: true,
} as any as FileAction)
}).toThrowError('Invalid parent')
expect(() => new FileAction({
id: 'test',
displayName: () => 'Test',
iconSvgInline: () => '<svg></svg>',
exec: async () => true,
parent: true,
} as unknown as FileAction),
).toThrowError('Invalid parent')
})
test('Invalid default', () => {
expect(() => {
new FileAction({
id: 'test',
displayName: () => 'Test',
iconSvgInline: () => '<svg></svg>',
exec: async () => true,
default: 'invalid',
} as any as FileAction)
}).toThrowError('Invalid default')
expect(() => new FileAction({
id: 'test',
displayName: () => 'Test',
iconSvgInline: () => '<svg></svg>',
exec: async () => true,
default: 'invalid',
} as unknown as FileAction),
).toThrowError('Invalid default')
})
test('Invalid destructives', () => {
expect(() => new FileAction({
id: 'test',
displayName: () => 'Test',
iconSvgInline: () => '<svg></svg>',
exec: async () => true,
destructive: 'false',
} as unknown as FileActionData),
).toThrowError('Invalid destructive')
})
test('Invalid inline', () => {
expect(() => {
new FileAction({
id: 'test',
displayName: () => 'Test',
iconSvgInline: () => '<svg></svg>',
exec: async () => true,
inline: true,
} as any as FileAction)
}).toThrowError('Invalid inline function')
expect(() => new FileAction({
id: 'test',
displayName: () => 'Test',
iconSvgInline: () => '<svg></svg>',
exec: async () => true,
inline: true,
} as unknown as FileAction),
).toThrowError('Invalid inline function')

expect(() => {
new FileAction({
id: 'test',
displayName: () => 'Test',
iconSvgInline: () => '<svg></svg>',
exec: async () => true,
inline: () => true,
renderInline: false,
} as any as FileAction)
}).toThrowError('Invalid renderInline function')
expect(() => new FileAction({
id: 'test',
displayName: () => 'Test',
iconSvgInline: () => '<svg></svg>',
exec: async () => true,
inline: () => true,
renderInline: false,
} as unknown as FileAction),
).toThrowError('Invalid renderInline function')
})
})

Expand All @@ -236,6 +233,7 @@ describe('FileActions creation', () => {
enabled: () => true,
order: 100,
parent: '123',
destructive: true,
default: DefaultType.DEFAULT,
inline: () => true,
renderInline: async () => {
Expand All @@ -246,16 +244,17 @@ describe('FileActions creation', () => {
})

expect(action.id).toBe('test')
expect(action.displayName([], {} as any)).toBe('Test')
expect(action.title?.([], {} as any)).toBe('Test title')
expect(action.iconSvgInline([], {} as any)).toBe('<svg></svg>')
await expect(action.exec({} as any, {} as any, '/')).resolves.toBe(true)
await expect(action.execBatch?.([], {} as any, '/')).resolves.toStrictEqual([true])
expect(action.enabled?.({} as any, {} as any)).toBe(true)
expect(action.displayName([], {} as unknown as View)).toBe('Test')
expect(action.title?.([], {} as unknown as View)).toBe('Test title')
expect(action.iconSvgInline([], {} as unknown as View)).toBe('<svg></svg>')
await expect(action.exec({} as unknown as Node, {} as unknown as View, '/')).resolves.toBe(true)
await expect(action.execBatch?.([], {} as unknown as View, '/')).resolves.toStrictEqual([true])
expect(action.enabled?.([], {} as unknown as View)).toBe(true)
expect(action.order).toBe(100)
expect(action.parent).toBe('123')
expect(action.destructive).toBe(true)
expect(action.default).toBe(DefaultType.DEFAULT)
expect(action.inline?.({} as any, {} as any)).toBe(true)
expect((await action.renderInline?.({} as any, {} as any))?.outerHTML).toBe('<span>test</span>')
expect(action.inline?.({} as unknown as Node, {} as unknown as View)).toBe(true)
expect((await action.renderInline?.({} as unknown as Node, {} as unknown as View))?.outerHTML).toBe('<span>test</span>')
})
})
16 changes: 15 additions & 1 deletion lib/fileAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export enum DefaultType {
HIDDEN = 'hidden',
}

interface FileActionData {
export interface FileActionData {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I missed this.
wasn't it a leftover from your other proposal @susnux ?

/** Unique ID */
id: string
/** Translatable string displayed in the menu */
Expand Down Expand Up @@ -42,6 +42,12 @@ interface FileActionData {
/** This action order in the list */
order?: number,

/**
* Set to true if this action is a destructive action, like "delete".
* This will change the appearance in the action menu more prominent (e.g. red colored)
*/
destructive?: boolean

/**
* This action's parent id in the list.
* If none found, will be displayed as a top-level action.
Expand Down Expand Up @@ -118,6 +124,10 @@ export class FileAction {
return this._action.default
}

get destructive() {
return this._action.destructive
}

get inline() {
return this._action.inline
}
Expand Down Expand Up @@ -160,6 +170,10 @@ export class FileAction {
throw new Error('Invalid order')
}

if (action.destructive !== undefined && typeof action.destructive !== 'boolean') {
throw new Error('Invalid destructive flag')
}

if ('parent' in action && typeof action.parent !== 'string') {
throw new Error('Invalid parent')
}
Expand Down
Loading