feat: delete a custom type
This commit is contained in:
1 parent
76e9e1220c
commit
5404291af2
25 files changed
+1768
-46
No files matched your search
@@ -0,0 +1,125 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { resolveSchemaTypeView, type SchemaTypeInputs } from '@/lib/schemaTypeView'
|
||||
|
||||
const SETTLED: SchemaTypeInputs = {
|
||||
listsForbidden: false,
|
||||
listsLoading: false,
|
||||
customFailed: false,
|
||||
builtinFailed: false,
|
||||
hasBuiltin: false,
|
||||
hasCustom: false,
|
||||
skeletonVisible: false,
|
||||
countStatus: 'idle',
|
||||
}
|
||||
|
||||
const resolve = (patch: Partial<SchemaTypeInputs>) =>
|
||||
resolveSchemaTypeView({ ...SETTLED, ...patch })
|
||||
|
||||
describe('resolveSchemaTypeView', () => {
|
||||
it.each([
|
||||
['forbidden lists', { listsForbidden: true }],
|
||||
['forbidden count', { countStatus: 'forbidden' }],
|
||||
[
|
||||
'forbidden over loading',
|
||||
{ listsForbidden: true, listsLoading: true, skeletonVisible: true },
|
||||
],
|
||||
['forbidden over a custom type', { countStatus: 'forbidden', hasCustom: true }],
|
||||
] as const)('is forbidden and bleeds for %s', (_, patch) => {
|
||||
expect(resolve(patch)).toEqual({
|
||||
view: 'forbidden',
|
||||
editable: false,
|
||||
loading: false,
|
||||
bleed: true,
|
||||
})
|
||||
})
|
||||
|
||||
it.each([
|
||||
['lists loading', { listsLoading: true }],
|
||||
['count idle on a custom type', { hasCustom: true, countStatus: 'idle' }],
|
||||
['count loading on a custom type', { hasCustom: true, countStatus: 'loading' }],
|
||||
] as const)('shows the skeleton only once visible, %s', (_, patch) => {
|
||||
expect(resolve({ ...patch, skeletonVisible: true })).toEqual({
|
||||
view: 'skeleton',
|
||||
editable: false,
|
||||
loading: true,
|
||||
bleed: false,
|
||||
})
|
||||
expect(resolve({ ...patch, skeletonVisible: false })).toEqual({
|
||||
view: 'blank',
|
||||
editable: false,
|
||||
loading: true,
|
||||
bleed: false,
|
||||
})
|
||||
})
|
||||
|
||||
it('does not wait for the count when there is no custom type', () => {
|
||||
expect(resolve({ hasBuiltin: true, countStatus: 'loading' }).loading).toBe(false)
|
||||
})
|
||||
|
||||
it('reports a failed custom list before anything else', () => {
|
||||
expect(
|
||||
resolve({
|
||||
customFailed: true,
|
||||
builtinFailed: true,
|
||||
hasBuiltin: true,
|
||||
hasCustom: true,
|
||||
countStatus: 'failed',
|
||||
}),
|
||||
).toEqual({ view: 'listsFailed', editable: false, loading: false, bleed: false })
|
||||
})
|
||||
|
||||
it('reports a failed built-in list after the custom one', () => {
|
||||
expect(resolve({ builtinFailed: true, hasBuiltin: true, countStatus: 'failed' })).toEqual({
|
||||
view: 'builtinListFailed',
|
||||
editable: false,
|
||||
loading: false,
|
||||
bleed: false,
|
||||
})
|
||||
})
|
||||
|
||||
it('reports a failed count on a custom type', () => {
|
||||
expect(resolve({ hasCustom: true, hasBuiltin: true, countStatus: 'failed' })).toEqual({
|
||||
view: 'countFailed',
|
||||
editable: false,
|
||||
loading: false,
|
||||
bleed: false,
|
||||
})
|
||||
})
|
||||
|
||||
it('is editable and bleeds for a custom type with a ready count', () => {
|
||||
expect(resolve({ hasCustom: true, hasBuiltin: true, countStatus: 'ready' })).toEqual({
|
||||
view: 'editable',
|
||||
editable: true,
|
||||
loading: false,
|
||||
bleed: true,
|
||||
})
|
||||
})
|
||||
|
||||
it.each(['idle', 'ready', 'missing'] as const)(
|
||||
'shows a built-in type without bleeding, count %s',
|
||||
(countStatus) => {
|
||||
expect(resolve({ hasBuiltin: true, countStatus })).toEqual({
|
||||
view: 'builtin',
|
||||
editable: false,
|
||||
loading: false,
|
||||
bleed: false,
|
||||
})
|
||||
},
|
||||
)
|
||||
|
||||
it.each(['idle', 'missing', 'ready'] as const)(
|
||||
'is not found and bleeds with no type at all, count %s',
|
||||
(countStatus) => {
|
||||
expect(resolve({ countStatus })).toEqual({
|
||||
view: 'notFound',
|
||||
editable: false,
|
||||
loading: false,
|
||||
bleed: true,
|
||||
})
|
||||
},
|
||||
)
|
||||
|
||||
it('is not found for a custom type whose count went missing', () => {
|
||||
expect(resolve({ hasCustom: true, countStatus: 'missing' }).view).toBe('notFound')
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,5 @@
|
||||
export type DeleteStage = 'closed' | 'blocked' | 'confirm' | 'failed' | 'conflict'
|
||||
|
||||
export function isConfirmStage(stage: DeleteStage) {
|
||||
return stage === 'confirm' || stage === 'failed'
|
||||
}
|
||||
@@ -19,3 +19,9 @@ export function entriesLine(name: string, total: number) {
|
||||
formatNumber(total),
|
||||
])
|
||||
}
|
||||
|
||||
export function entriesCountLine(name: string, total: number) {
|
||||
return total === 1
|
||||
? t('{0} has 1 entry', [name])
|
||||
: t('{0} has {1} entries', [name, formatNumber(total)])
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
import type { EntryCountStatus } from '@/composables/useEntryCount'
|
||||
|
||||
export type SchemaTypeViewName =
|
||||
| 'forbidden'
|
||||
| 'skeleton'
|
||||
| 'listsFailed'
|
||||
| 'builtinListFailed'
|
||||
| 'countFailed'
|
||||
| 'editable'
|
||||
| 'builtin'
|
||||
| 'notFound'
|
||||
| 'blank'
|
||||
|
||||
export interface SchemaTypeInputs {
|
||||
listsForbidden: boolean
|
||||
listsLoading: boolean
|
||||
customFailed: boolean
|
||||
builtinFailed: boolean
|
||||
hasBuiltin: boolean
|
||||
hasCustom: boolean
|
||||
countStatus: EntryCountStatus
|
||||
skeletonVisible: boolean
|
||||
}
|
||||
|
||||
export interface SchemaTypeState {
|
||||
view: SchemaTypeViewName
|
||||
editable: boolean
|
||||
loading: boolean
|
||||
bleed: boolean
|
||||
}
|
||||
|
||||
export function resolveSchemaTypeView(input: SchemaTypeInputs): SchemaTypeState {
|
||||
const forbidden = input.listsForbidden || input.countStatus === 'forbidden'
|
||||
const countPending =
|
||||
input.hasCustom && (input.countStatus === 'idle' || input.countStatus === 'loading')
|
||||
const loading = !forbidden && (input.listsLoading || countPending)
|
||||
const settled = !forbidden && !loading
|
||||
const editable = settled && input.hasCustom && input.countStatus === 'ready'
|
||||
const listsFailed = settled && input.customFailed
|
||||
const builtinListFailed = settled && !listsFailed && input.builtinFailed
|
||||
const countFailed = settled && input.countStatus === 'failed'
|
||||
const notFound =
|
||||
settled &&
|
||||
!listsFailed &&
|
||||
!builtinListFailed &&
|
||||
!countFailed &&
|
||||
!input.hasBuiltin &&
|
||||
!editable
|
||||
|
||||
return {
|
||||
view: pickView({
|
||||
forbidden,
|
||||
skeleton: loading && input.skeletonVisible,
|
||||
listsFailed,
|
||||
builtinListFailed,
|
||||
countFailed,
|
||||
editable,
|
||||
builtin: input.hasBuiltin,
|
||||
notFound,
|
||||
}),
|
||||
editable,
|
||||
loading,
|
||||
bleed: forbidden || notFound || editable,
|
||||
}
|
||||
}
|
||||
|
||||
interface ViewFlags {
|
||||
forbidden: boolean
|
||||
skeleton: boolean
|
||||
listsFailed: boolean
|
||||
builtinListFailed: boolean
|
||||
countFailed: boolean
|
||||
editable: boolean
|
||||
builtin: boolean
|
||||
notFound: boolean
|
||||
}
|
||||
|
||||
function pickView(flags: ViewFlags): SchemaTypeViewName {
|
||||
if (flags.forbidden) return 'forbidden'
|
||||
if (flags.skeleton) return 'skeleton'
|
||||
if (flags.listsFailed) return 'listsFailed'
|
||||
if (flags.builtinListFailed) return 'builtinListFailed'
|
||||
if (flags.countFailed) return 'countFailed'
|
||||
if (flags.editable) return 'editable'
|
||||
if (flags.builtin) return 'builtin'
|
||||
if (flags.notFound) return 'notFound'
|
||||
return 'blank'
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export const TOPBAR_ACTION_ID = 'topbar-action'
|
||||
Reference in new issue
Block a user