diff --git a/packages/buefy-next/rollup.config.mjs b/packages/buefy-next/rollup.config.mjs index 5b67d1ccc..77039cad1 100644 --- a/packages/buefy-next/rollup.config.mjs +++ b/packages/buefy-next/rollup.config.mjs @@ -35,12 +35,9 @@ const JS_COMPONENTS = [ 'colorpicker', 'datepicker', 'datetimepicker', - 'dialog', 'numberinput', - 'select', 'table', 'timepicker', - 'upload', ] const entries = { diff --git a/packages/buefy-next/src/components/dialog/Dialog.spec.js b/packages/buefy-next/src/components/dialog/Dialog.spec.ts similarity index 80% rename from packages/buefy-next/src/components/dialog/Dialog.spec.js rename to packages/buefy-next/src/components/dialog/Dialog.spec.ts index 709c6ad2b..ba38e66ea 100644 --- a/packages/buefy-next/src/components/dialog/Dialog.spec.js +++ b/packages/buefy-next/src/components/dialog/Dialog.spec.ts @@ -1,11 +1,13 @@ import { shallowMount } from '@vue/test-utils' -import BDialog from '@components/dialog/Dialog' +import type { VueWrapper } from '@vue/test-utils' +import { beforeEach, describe, expect, it, vi } from 'vitest' +import BDialog from '@components/dialog/Dialog.vue' import config, { setOptions } from '@utils/config' -let wrapper +let wrapper: VueWrapper> describe('BDialog', () => { - HTMLElement.prototype.focus = jest.fn() + HTMLElement.prototype.focus = vi.fn() beforeEach(() => { wrapper = shallowMount(BDialog, { attachTo: document.body @@ -19,7 +21,7 @@ describe('BDialog', () => { it('gives focus to the input element if it contains one', async () => { await wrapper.setProps({ hasInput: true }) - expect(wrapper.vm.$refs.input.focus).toHaveBeenCalled() + expect((wrapper.vm.$refs.input as HTMLInputElement).focus).toHaveBeenCalled() }) it('manage default config props values', () => { @@ -60,16 +62,16 @@ describe('BDialog', () => { }) it('close on confirm', async () => { - await wrapper.setProps({ confirmCallback: jest.fn() }) + await wrapper.setProps({ confirmCallback: vi.fn() }) wrapper.vm.confirm() expect(wrapper.vm.isActive).toBeFalsy() expect(wrapper.vm.confirmCallback).toHaveBeenCalled() - expect(wrapper.emitted().confirm).toEqual([['']]) + expect(wrapper.emitted().confirm[0][0]).toEqual('') }) it('async confirm and keep Loading state', async () => { await wrapper.setProps({ - confirmCallback: jest.fn((confirmValue, { startLoading }) => { + confirmCallback: vi.fn((confirmValue, { startLoading }) => { startLoading() expect(wrapper.vm.isLoading).toBeTruthy() }), @@ -83,7 +85,7 @@ describe('BDialog', () => { it('async confirm and close Loading state', async () => { await wrapper.setProps({ - confirmCallback: jest.fn((confirmValue, { startLoading, cancelLoading }) => { + confirmCallback: vi.fn((confirmValue, { startLoading, cancelLoading }) => { startLoading() expect(wrapper.vm.isLoading).toBeTruthy() cancelLoading() @@ -97,10 +99,10 @@ describe('BDialog', () => { }) it('closeOnConfirm prop equals false', async () => { - await wrapper.setProps({ confirmCallback: jest.fn(), closeOnConfirm: false }) + await wrapper.setProps({ confirmCallback: vi.fn(), closeOnConfirm: false }) wrapper.vm.confirm() expect(wrapper.vm.isActive).toBeTruthy() expect(wrapper.vm.confirmCallback).toHaveBeenCalled() - expect(wrapper.emitted().confirm).toEqual([['']]) + expect(wrapper.emitted().confirm[0][0]).toEqual('') }) }) diff --git a/packages/buefy-next/src/components/dialog/Dialog.vue b/packages/buefy-next/src/components/dialog/Dialog.vue index d41a0ba65..1ed21afe3 100644 --- a/packages/buefy-next/src/components/dialog/Dialog.vue +++ b/packages/buefy-next/src/components/dialog/Dialog.vue @@ -26,7 +26,7 @@ v-if="hasIcon && (icon || iconByType)" > - diff --git a/packages/buefy-next/src/components/dialog/index.js b/packages/buefy-next/src/components/dialog/index.ts similarity index 56% rename from packages/buefy-next/src/components/dialog/index.js rename to packages/buefy-next/src/components/dialog/index.ts index 254493112..0bcada042 100644 --- a/packages/buefy-next/src/components/dialog/index.js +++ b/packages/buefy-next/src/components/dialog/index.ts @@ -1,18 +1,47 @@ import { createApp, h as createElement } from 'vue' +import type { App, ComponentPublicInstance } from 'vue' import Dialog from './Dialog.vue' +import type { DialogProps } from './Dialog.vue' import config from '../../utils/config' -import { merge, copyAppContext, getComponentFromVNode } from '../../utils/helpers' +import type { ModalCancellableOption } from '../../utils/config' +import { copyAppContext, getComponentFromVNode } from '../../utils/helpers' import { registerComponent, registerComponentProgrammatic } from '../../utils/plugins' -function open(propsData, app) { - let slot +type DialogInstance = InstanceType + +export type DialogOpenParams = Omit< + DialogProps, + 'programmatic' | 'cancelCallback' | 'confirmCallback' +> & { + onConfirm?: (value: string, dialog: DialogInstance) => void + onCancel?: (method: ModalCancellableOption) => void +} + +// Minimal definition of a programmatically opened dialog. +// +// ESLint does not like `{}` as a type but allowed here to make them look +// similar to Vue's definition. +/* eslint-disable @typescript-eslint/ban-types */ +type DialogProgrammaticInstance = ComponentPublicInstance< + {}, // P + {}, // B + {}, // D + {}, // C + { close: () => void } // M +> + +function open(propsData: DialogProps, app?: App) { + let slot: DialogProps['message'] if (Array.isArray(propsData.message)) { slot = propsData.message delete propsData.message } - function createDialog(onConfirm, onCancel) { + function createDialog( + onConfirm?: (value: string) => void, + onCancel?: (method: ModalCancellableOption) => void + ) { const container = document.createElement('div') // Vue 3 requires a new app to mount another component const vueInstance = createApp({ @@ -25,19 +54,19 @@ function open(propsData, app) { close() { const dialog = getComponentFromVNode(this.dialogVNode) if (dialog) { - dialog.close() + (dialog as DialogInstance).close() } }, startLoading() { const dialog = getComponentFromVNode(this.dialogVNode) if (dialog) { - dialog.startLoading() + (dialog as DialogInstance).startLoading() } }, cancelLoading() { const dialog = getComponentFromVNode(this.dialogVNode) if (dialog) { - dialog.cancelLoading() + (dialog as DialogInstance).cancelLoading() } } }, @@ -48,27 +77,27 @@ function open(propsData, app) { ...propsData, // intentionally overrides propsData.onConfirm // to prevent propsData.onConfirm from receiving a "confirm" event - onConfirm: (...args) => { + onConfirm: (value: string) => { if (onConfirm != null) { - onConfirm(...args) + onConfirm(value) } }, // intentionally override propsData.onCancel // to prevent propsData.onCancel from receiving a "cancel" event - onCancel: (...args) => { + onCancel: (method: ModalCancellableOption) => { if (onCancel != null) { - onCancel(...args) + onCancel(method) } vueInstance.unmount() }, - confirmCallback: (...args) => { + confirmCallback: (value: string, dialog: DialogInstance) => { if (propsData.onConfirm != null) { - propsData.onConfirm(...args) + propsData.onConfirm(value, dialog) } }, - cancelCallback: (...args) => { + cancelCallback: (method: ModalCancellableOption) => { if (propsData.onCancel != null) { - propsData.onCancel(...args) + propsData.onCancel(method) } } }, @@ -80,7 +109,7 @@ function open(propsData, app) { if (app) { copyAppContext(app, vueInstance) } - return vueInstance.mount(container) + return vueInstance.mount(container) as DialogProgrammaticInstance } if (!config.defaultProgrammaticPromise) { return createDialog() @@ -94,40 +123,39 @@ function open(propsData, app) { } class DialogProgrammatic { - constructor(app) { + private app: App | undefined + + constructor(app?: App) { this.app = app // may be undefined in the testing environment } - alert(params) { + alert(params: string | DialogOpenParams) { + let newParams: DialogOpenParams if (typeof params === 'string') { - params = { + newParams = { message: params } + } else { + newParams = params } - const defaultParam = { - canCancel: false + newParams = { + canCancel: false, + ...newParams } - const propsData = merge(defaultParam, params) - return open(propsData, this.app) + return open(newParams, this.app) } - confirm(params) { - const defaultParam = {} - const propsData = merge(defaultParam, params) - return open(propsData, this.app) + confirm(params: DialogOpenParams) { + return open(params, this.app) } - prompt(params) { - const defaultParam = { - hasInput: true - } - const propsData = merge(defaultParam, params) - return open(propsData, this.app) + prompt(params: DialogOpenParams) { + return open({ hasInput: true, ...params }, this.app) } } const Plugin = { - install(Vue) { + install(Vue: App) { registerComponent(Vue, Dialog) registerComponentProgrammatic(Vue, 'dialog', new DialogProgrammatic(Vue)) } diff --git a/packages/buefy-next/src/components/select/Select.spec.js b/packages/buefy-next/src/components/select/Select.spec.ts similarity index 92% rename from packages/buefy-next/src/components/select/Select.spec.js rename to packages/buefy-next/src/components/select/Select.spec.ts index 0e7f5866f..f0e7ad7c5 100644 --- a/packages/buefy-next/src/components/select/Select.spec.js +++ b/packages/buefy-next/src/components/select/Select.spec.ts @@ -1,7 +1,9 @@ import { shallowMount } from '@vue/test-utils' -import BSelect from '@components/select/Select' +import type { VueWrapper } from '@vue/test-utils' +import { beforeEach, describe, expect, it } from 'vitest' +import BSelect from '@components/select/Select.vue' -let wrapper +let wrapper: VueWrapper> describe('BSelect', () => { beforeEach(() => { diff --git a/packages/buefy-next/src/components/select/Select.vue b/packages/buefy-next/src/components/select/Select.vue index 921ad4dd5..7d9c0d0d4 100644 --- a/packages/buefy-next/src/components/select/Select.vue +++ b/packages/buefy-next/src/components/select/Select.vue @@ -12,7 +12,7 @@ :multiple="multiple" :size="nativeSize" v-bind="fallthroughAttrs" - @blur="$emit('blur', $event) && checkHtml5Validity()" + @blur="onBlur" @focus="$emit('focus', $event)" > @@ -42,27 +42,40 @@ - diff --git a/packages/buefy-next/src/components/select/__snapshots__/Select.spec.js.snap b/packages/buefy-next/src/components/select/__snapshots__/Select.spec.js.snap deleted file mode 100644 index c0df5c593..000000000 --- a/packages/buefy-next/src/components/select/__snapshots__/Select.spec.js.snap +++ /dev/null @@ -1,7 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`BSelect render correctly 1`] = ` -
- -
-`; diff --git a/packages/buefy-next/src/components/select/__snapshots__/Select.spec.ts.snap b/packages/buefy-next/src/components/select/__snapshots__/Select.spec.ts.snap new file mode 100644 index 000000000..349994a31 --- /dev/null +++ b/packages/buefy-next/src/components/select/__snapshots__/Select.spec.ts.snap @@ -0,0 +1,7 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`BSelect > render correctly 1`] = ` +"
+ +
" +`; diff --git a/packages/buefy-next/src/components/select/index.js b/packages/buefy-next/src/components/select/index.ts similarity index 79% rename from packages/buefy-next/src/components/select/index.js rename to packages/buefy-next/src/components/select/index.ts index 5511250a1..032f2886c 100644 --- a/packages/buefy-next/src/components/select/index.js +++ b/packages/buefy-next/src/components/select/index.ts @@ -1,9 +1,10 @@ +import type { App } from 'vue' import Select from './Select.vue' import { registerComponent } from '../../utils/plugins' const Plugin = { - install(Vue) { + install(Vue: App) { registerComponent(Vue, Select) } } diff --git a/packages/buefy-next/src/components/upload/Upload.spec.js b/packages/buefy-next/src/components/upload/Upload.spec.ts similarity index 90% rename from packages/buefy-next/src/components/upload/Upload.spec.js rename to packages/buefy-next/src/components/upload/Upload.spec.ts index e778bdec3..1343a259b 100644 --- a/packages/buefy-next/src/components/upload/Upload.spec.js +++ b/packages/buefy-next/src/components/upload/Upload.spec.ts @@ -1,7 +1,9 @@ import { shallowMount } from '@vue/test-utils' -import BUpload from '@components/upload/Upload' +import type { VueWrapper } from '@vue/test-utils' +import { beforeEach, describe, expect, it } from 'vitest' +import BUpload from '@components/upload/Upload.vue' -let wrapper +let wrapper: VueWrapper> describe('BUpload', () => { beforeEach(() => { diff --git a/packages/buefy-next/src/components/upload/Upload.vue b/packages/buefy-next/src/components/upload/Upload.vue index 9d339ffea..e21fc9028 100644 --- a/packages/buefy-next/src/components/upload/Upload.vue +++ b/packages/buefy-next/src/components/upload/Upload.vue @@ -37,17 +37,20 @@ - diff --git a/packages/buefy-next/src/components/upload/__snapshots__/Upload.spec.js.snap b/packages/buefy-next/src/components/upload/__snapshots__/Upload.spec.js.snap deleted file mode 100644 index 96a27f0b2..000000000 --- a/packages/buefy-next/src/components/upload/__snapshots__/Upload.spec.js.snap +++ /dev/null @@ -1,3 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`BUpload render correctly 1`] = ``; diff --git a/packages/buefy-next/src/components/upload/__snapshots__/Upload.spec.ts.snap b/packages/buefy-next/src/components/upload/__snapshots__/Upload.spec.ts.snap new file mode 100644 index 000000000..8d3235b68 --- /dev/null +++ b/packages/buefy-next/src/components/upload/__snapshots__/Upload.spec.ts.snap @@ -0,0 +1,3 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`BUpload > render correctly 1`] = `""`; diff --git a/packages/buefy-next/src/components/upload/index.js b/packages/buefy-next/src/components/upload/index.ts similarity index 79% rename from packages/buefy-next/src/components/upload/index.js rename to packages/buefy-next/src/components/upload/index.ts index e057fe57d..f2a0ed55b 100644 --- a/packages/buefy-next/src/components/upload/index.js +++ b/packages/buefy-next/src/components/upload/index.ts @@ -1,9 +1,9 @@ +import type { App } from 'vue' import Upload from './Upload.vue' - import { registerComponent } from '../../utils/plugins' const Plugin = { - install(Vue) { + install(Vue: App) { registerComponent(Vue, Upload) } } diff --git a/packages/buefy-next/src/utils/FormElementMixin.ts b/packages/buefy-next/src/utils/FormElementMixin.ts index 4e1d684b8..28a299c0b 100644 --- a/packages/buefy-next/src/utils/FormElementMixin.ts +++ b/packages/buefy-next/src/utils/FormElementMixin.ts @@ -182,11 +182,15 @@ const FormElementMixin = defineComponent({ * If validation fail, send 'is-danger' type, * and error message to parent if it's a Field. */ - checkHtml5Validity() { - if (!this.useHtml5Validation) return + checkHtml5Validity(): boolean { + if (!this.useHtml5Validation) { + return false + } const el = this.getElement() - if (el == null) return + if (el == null) { + return false + } if (!el.checkValidity()) { this.setInvalid() diff --git a/packages/buefy-next/src/utils/vue-augmentation.ts b/packages/buefy-next/src/utils/vue-augmentation.ts index 12593cd74..019fdccd8 100644 --- a/packages/buefy-next/src/utils/vue-augmentation.ts +++ b/packages/buefy-next/src/utils/vue-augmentation.ts @@ -7,6 +7,7 @@ // otherwise the post processing will be messed up. import 'vue' +import type { DialogProgrammatic } from '../components/dialog' import type { LoadingProgrammatic } from '../components/loading' import type { ModalProgrammatic } from '../components/modal' import type { NotificationProgrammatic } from '../components/notification' @@ -23,6 +24,7 @@ declare module '@vue/runtime-core' { $buefy: { config: typeof ConfigComponent, globalNoticeInterval?: ReturnType, + dialog: DialogProgrammatic, loading: LoadingProgrammatic, modal: ModalProgrammatic, notification: NotificationProgrammatic, diff --git a/packages/docs/src/pages/components/dialog/Dialog.vue b/packages/docs/src/pages/components/dialog/Dialog.vue index d4f20f6c3..3090f454c 100644 --- a/packages/docs/src/pages/components/dialog/Dialog.vue +++ b/packages/docs/src/pages/components/dialog/Dialog.vue @@ -20,21 +20,34 @@ - diff --git a/packages/docs/src/pages/components/dialog/api/dialog.js b/packages/docs/src/pages/components/dialog/api/dialog.ts similarity index 100% rename from packages/docs/src/pages/components/dialog/api/dialog.js rename to packages/docs/src/pages/components/dialog/api/dialog.ts diff --git a/packages/docs/src/pages/components/dialog/examples/ExAlertDialog.vue b/packages/docs/src/pages/components/dialog/examples/ExAlertDialog.vue index e5943e639..275091ce9 100644 --- a/packages/docs/src/pages/components/dialog/examples/ExAlertDialog.vue +++ b/packages/docs/src/pages/components/dialog/examples/ExAlertDialog.vue @@ -22,8 +22,12 @@ - diff --git a/packages/docs/src/pages/components/dialog/examples/ExConfirmDialog.vue b/packages/docs/src/pages/components/dialog/examples/ExConfirmDialog.vue index ae4c1b2f0..f4e6e4d1e 100644 --- a/packages/docs/src/pages/components/dialog/examples/ExConfirmDialog.vue +++ b/packages/docs/src/pages/components/dialog/examples/ExConfirmDialog.vue @@ -22,8 +22,12 @@ - diff --git a/packages/docs/src/pages/components/dialog/examples/ExPromptDialog.vue b/packages/docs/src/pages/components/dialog/examples/ExPromptDialog.vue index fac5a3e42..4d7a88880 100644 --- a/packages/docs/src/pages/components/dialog/examples/ExPromptDialog.vue +++ b/packages/docs/src/pages/components/dialog/examples/ExPromptDialog.vue @@ -32,8 +32,12 @@ - diff --git a/packages/docs/src/pages/components/dialog/outside-vue-instance.js b/packages/docs/src/pages/components/dialog/outside-vue-instance.js new file mode 100644 index 000000000..04c42eb9d --- /dev/null +++ b/packages/docs/src/pages/components/dialog/outside-vue-instance.js @@ -0,0 +1,2 @@ +import { DialogProgrammatic as Dialog } from 'buefy' +Dialog.alert('We can use confirm and prompt methods as well') diff --git a/packages/docs/src/pages/components/dialog/promise.js b/packages/docs/src/pages/components/dialog/promise.js new file mode 100644 index 000000000..3af7154c9 --- /dev/null +++ b/packages/docs/src/pages/components/dialog/promise.js @@ -0,0 +1,4 @@ +const { result, dialog } = await this.$buefy.dialog.confirm({ + message: 'Are you sure?', + closeOnConfirm: false +}); diff --git a/packages/docs/src/pages/components/select/Select.vue b/packages/docs/src/pages/components/select/Select.vue index b0a898651..aebd8b3f6 100644 --- a/packages/docs/src/pages/components/select/Select.vue +++ b/packages/docs/src/pages/components/select/Select.vue @@ -12,23 +12,32 @@ - diff --git a/packages/docs/src/pages/components/select/api/select.js b/packages/docs/src/pages/components/select/api/select.ts similarity index 100% rename from packages/docs/src/pages/components/select/api/select.js rename to packages/docs/src/pages/components/select/api/select.ts diff --git a/packages/docs/src/pages/components/select/examples/ExIcons.vue b/packages/docs/src/pages/components/select/examples/ExIcons.vue index 90072ebac..956a4f440 100644 --- a/packages/docs/src/pages/components/select/examples/ExIcons.vue +++ b/packages/docs/src/pages/components/select/examples/ExIcons.vue @@ -37,3 +37,7 @@ + + diff --git a/packages/docs/src/pages/components/select/examples/ExMultiple.vue b/packages/docs/src/pages/components/select/examples/ExMultiple.vue index 2cc622c4a..aa4a38d89 100644 --- a/packages/docs/src/pages/components/select/examples/ExMultiple.vue +++ b/packages/docs/src/pages/components/select/examples/ExMultiple.vue @@ -20,8 +20,15 @@ - diff --git a/packages/docs/src/pages/components/select/examples/ExSizes.vue b/packages/docs/src/pages/components/select/examples/ExSizes.vue index 321801660..9661245a7 100644 --- a/packages/docs/src/pages/components/select/examples/ExSizes.vue +++ b/packages/docs/src/pages/components/select/examples/ExSizes.vue @@ -40,3 +40,7 @@ + + diff --git a/packages/docs/src/pages/components/upload/Upload.vue b/packages/docs/src/pages/components/upload/Upload.vue index 70005bea2..0f2c48c7a 100644 --- a/packages/docs/src/pages/components/upload/Upload.vue +++ b/packages/docs/src/pages/components/upload/Upload.vue @@ -14,27 +14,36 @@ - diff --git a/packages/docs/src/pages/components/upload/api/upload.js b/packages/docs/src/pages/components/upload/api/upload.ts similarity index 100% rename from packages/docs/src/pages/components/upload/api/upload.js rename to packages/docs/src/pages/components/upload/api/upload.ts diff --git a/packages/docs/src/pages/components/upload/examples/ExDragDrop.vue b/packages/docs/src/pages/components/upload/examples/ExDragDrop.vue index 71683a996..f9ef18faf 100644 --- a/packages/docs/src/pages/components/upload/examples/ExDragDrop.vue +++ b/packages/docs/src/pages/components/upload/examples/ExDragDrop.vue @@ -32,17 +32,25 @@ - diff --git a/packages/docs/src/pages/components/upload/examples/ExExpanded.vue b/packages/docs/src/pages/components/upload/examples/ExExpanded.vue index 74aed7e37..50deeac0c 100644 --- a/packages/docs/src/pages/components/upload/examples/ExExpanded.vue +++ b/packages/docs/src/pages/components/upload/examples/ExExpanded.vue @@ -30,18 +30,26 @@ - diff --git a/packages/docs/src/pages/components/upload/examples/ExRounded.vue b/packages/docs/src/pages/components/upload/examples/ExRounded.vue index e3e21f026..c1d584c5e 100644 --- a/packages/docs/src/pages/components/upload/examples/ExRounded.vue +++ b/packages/docs/src/pages/components/upload/examples/ExRounded.vue @@ -22,18 +22,26 @@ {{ file2.name }} - + - diff --git a/packages/docs/src/pages/components/upload/examples/ExSimple.vue b/packages/docs/src/pages/components/upload/examples/ExSimple.vue index 090672792..4c06de334 100644 --- a/packages/docs/src/pages/components/upload/examples/ExSimple.vue +++ b/packages/docs/src/pages/components/upload/examples/ExSimple.vue @@ -12,12 +12,20 @@ - diff --git a/packages/docs/src/pages/components/upload/examples/ExValidation.vue b/packages/docs/src/pages/components/upload/examples/ExValidation.vue index 40625063f..ee7e0f582 100644 --- a/packages/docs/src/pages/components/upload/examples/ExValidation.vue +++ b/packages/docs/src/pages/components/upload/examples/ExValidation.vue @@ -12,12 +12,20 @@ -