Skip to content
Open
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
//
// Copyright © 2026 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
//

import { defaultValueUpdate, nextDefaultValue } from '../attributeDefaultValue'

describe('nextDefaultValue', () => {
it('takes a new value from the editor', () => {
expect(nextDefaultValue({ defaultValue: 'b' }, 'a')).toBe('b')
})

it('clears the value when the editor sends undefined', () => {
expect(nextDefaultValue({ type: {}, defaultValue: undefined }, 'a')).toBeUndefined()
})

it('keeps the value when the editor does not send it', () => {
expect(nextDefaultValue({ extra: { showInPresenter: true } }, 'a')).toBe('a')
expect(nextDefaultValue(undefined, 'a')).toBe('a')
})

it('keeps falsy values', () => {
expect(nextDefaultValue({ defaultValue: false }, true)).toBe(false)
expect(nextDefaultValue({ defaultValue: 0 }, 5)).toBe(0)
})
})

describe('defaultValueUpdate', () => {
it('returns nothing when unchanged', () => {
expect(defaultValueUpdate('a', 'a')).toEqual({})
expect(defaultValueUpdate(undefined, undefined)).toEqual({})
})

it('sets a new value', () => {
expect(defaultValueUpdate('b', 'a')).toEqual({ defaultValue: 'b' })
expect(defaultValueUpdate('a', undefined)).toEqual({ defaultValue: 'a' })
expect(defaultValueUpdate(false, true)).toEqual({ defaultValue: false })
})

it('unsets a cleared value instead of writing undefined', () => {
expect(defaultValueUpdate(undefined, 'a')).toEqual({ $unset: { defaultValue: true } })
expect(defaultValueUpdate(null, 'a')).toEqual({ $unset: { defaultValue: true } })
})
})
39 changes: 39 additions & 0 deletions plugins/setting-resources/src/attributeDefaultValue.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//
// Copyright © 2026 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
//

/**
* Resolves the attribute default value after a type editor `change` event.
* An editor that clears the default emits `defaultValue: undefined`, which must
* replace the current value; an editor that does not send the key at all
* (e.g. only `{ extra }`) must keep it.
*/
export function nextDefaultValue (detail: Record<string, any> | undefined, current: any): any {
if (detail !== undefined && 'defaultValue' in detail) {
return detail.defaultValue
}
return current
}

/**
* Builds the attribute update for a changed default value. A cleared default is
* removed with `$unset`, since an `undefined` field is dropped from the transaction.
*/
export function defaultValueUpdate (next: any, current: any): { defaultValue?: any, $unset?: { defaultValue: true } } {
if (next === current) return {}
if (next === undefined || next === null) {
return current === undefined || current === null ? {} : { $unset: { defaultValue: true } }
}
return { defaultValue: next }
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
import { IconPicker } from '@hcengineering/view-resources'
import setting from '../plugin'
import { clearSettingsStore } from '../store'
import { nextDefaultValue } from '../attributeDefaultValue'
import { debug } from 'console'

export let _class: Ref<Class<Doc>>
Expand Down Expand Up @@ -125,7 +126,7 @@
extra = e.detail?.extra ?? {}
} else {
index = e.detail?.index ?? index
defaultValue = e.detail?.defaultValue ?? defaultValue
defaultValue = nextDefaultValue(e.detail, defaultValue)
extra = e.detail?.extra ?? extra
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
} from '@hcengineering/ui'
import { DropdownIntlItem } from '@hcengineering/ui/src/types'
import setting from '../plugin'
import { nextDefaultValue } from '../attributeDefaultValue'
import view from '@hcengineering/view'
import { createEventDispatcher } from 'svelte'
import { IconPicker } from '@hcengineering/view-resources'
Expand Down Expand Up @@ -122,7 +123,7 @@
extra = e.detail?.extra ?? {}
} else {
index = e.detail?.index ?? index
defaultValue = e.detail?.defaultValue ?? defaultValue
defaultValue = nextDefaultValue(e.detail, defaultValue)
extra = e.detail?.extra ?? extra
}
}
Expand Down
7 changes: 3 additions & 4 deletions plugins/setting-resources/src/components/EditAttribute.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
import view from '@hcengineering/view-resources/src/plugin'
import setting from '../plugin'
import { clearSettingsStore } from '../store'
import { defaultValueUpdate, nextDefaultValue } from '../attributeDefaultValue'

export let attribute: AnyAttribute
export let exist: boolean
Expand Down Expand Up @@ -77,9 +78,7 @@
if (newLabel !== attribute.label) {
update.label = newLabel
}
if (defaultValue !== attribute.defaultValue) {
update.defaultValue = defaultValue
}
Object.assign(update, defaultValueUpdate(defaultValue, attribute.defaultValue))
if (icon !== attribute.icon) {
update.icon = icon
}
Expand Down Expand Up @@ -144,7 +143,7 @@
extra = e.detail?.extra ?? {}
} else {
index = e.detail?.index ?? index
defaultValue = e.detail?.defaultValue ?? defaultValue
defaultValue = nextDefaultValue(e.detail, defaultValue)
extra = e.detail?.extra ?? extra
}
}
Expand Down