diff --git a/packages/documentation/components/ComponentInfo.vue b/packages/documentation/components/ComponentInfo.vue index c8ee3b6d4c..c594ae9dae 100644 --- a/packages/documentation/components/ComponentInfo.vue +++ b/packages/documentation/components/ComponentInfo.vue @@ -130,6 +130,7 @@ import { Kotti } from '@3yourmind/kotti-ui' import { Yoco } from '@3yourmind/yoco' import { Dashes } from '@metatypes/typography' import { computed, defineComponent, ref } from '@vue/composition-api' +import { kebabCase } from 'lodash' import ComponentInfoSlots from './component-info/Slots.vue' @@ -141,7 +142,11 @@ type VuePropType = | StringConstructor export default defineComponent<{ - component: { meta: Kotti.Meta; name: string } + component: { + meta: Kotti.Meta + name: string + props?: Record + } }>({ props: { component: { required: true, type: Object }, @@ -162,11 +167,15 @@ export default defineComponent<{ }> = [] const { - addedVersion, - deprecated, - designs, - typeScript, - } = props.component.meta + meta: { addedVersion, deprecated, designs, typeScript }, + name, + } = props.component + + const componentSourceFolder = props.component.props + ? `https://github.com/3YOURMIND/kotti/blob/master/packages/kotti-ui/source/${kebabCase( + name.replace(/^Kt/, 'Kotti'), + )}` + : null if (deprecated !== null) result.push({ @@ -212,6 +221,9 @@ export default defineComponent<{ backgroundColor: 'var(--primary-20)', color: 'var(--primary-70)', left: 'TS', + link: componentSourceFolder + ? `${componentSourceFolder}/types.ts` + : undefined, right: typeScript.namespace, }) @@ -229,6 +241,15 @@ export default defineComponent<{ }), }) + if (componentSourceFolder) + result.push({ + backgroundColor: 'var(--purple-20)', + color: 'var(--purple-70)', + left: 'Source', + link: `${componentSourceFolder}/${name}.vue`, + right: `${name}.vue`, + }) + return result }), showProps: ref(false), diff --git a/packages/kotti-ui/source/index.ts b/packages/kotti-ui/source/index.ts index f09d3bb0a9..93fa2c6c17 100644 --- a/packages/kotti-ui/source/index.ts +++ b/packages/kotti-ui/source/index.ts @@ -55,15 +55,16 @@ import { KtFieldTextArea } from './kotti-field-text-area' export * from './kotti-field-text-area' import { KtFieldToggle, KtFieldToggleGroup } from './kotti-field-toggle' export * from './kotti-field-toggle' -import { KtFilters } from './kotti-filter' -export * from './kotti-filter' -import { - KtForm, - KtFormControllerList, - KtFormControllerObject, - KtFormSubmit, -} from './kotti-form' +import { KtFilters } from './kotti-filters' +export * from './kotti-filters' +import { KtForm } from './kotti-form' export * from './kotti-form' +import { KtFormControllerList } from './kotti-form-controller-list' +export * from './kotti-form-controller-list' +import { KtFormControllerObject } from './kotti-form-controller-object' +export * from './kotti-form-controller-object' +import { KtFormSubmit } from './kotti-form-submit' +export * from './kotti-form-submit' import { KtHeading } from './kotti-heading' export * from './kotti-heading' import { KtInlineEdit } from './kotti-inline-edit' @@ -95,7 +96,6 @@ import { KtTableColumn, KtTableProvider, KtTableConsumer, - KtTableColumnsStateMixin, } from './kotti-table' export * from './kotti-table' import { KtTheme } from './kotti-theme' @@ -165,7 +165,6 @@ export default { KtTableColumn, KtTableProvider, KtTableConsumer, - KtTableColumnsStateMixin, KtTheme, KtToaster, KtTranslationContext, diff --git a/packages/kotti-ui/source/kotti-filter/KtFilters.vue b/packages/kotti-ui/source/kotti-filters/KtFilters.vue similarity index 100% rename from packages/kotti-ui/source/kotti-filter/KtFilters.vue rename to packages/kotti-ui/source/kotti-filters/KtFilters.vue diff --git a/packages/kotti-ui/source/kotti-filter/components/ButtonLink.vue b/packages/kotti-ui/source/kotti-filters/components/ButtonLink.vue similarity index 100% rename from packages/kotti-ui/source/kotti-filter/components/ButtonLink.vue rename to packages/kotti-ui/source/kotti-filters/components/ButtonLink.vue diff --git a/packages/kotti-ui/source/kotti-filter/components/FilterActions.vue b/packages/kotti-ui/source/kotti-filters/components/FilterActions.vue similarity index 100% rename from packages/kotti-ui/source/kotti-filter/components/FilterActions.vue rename to packages/kotti-ui/source/kotti-filters/components/FilterActions.vue diff --git a/packages/kotti-ui/source/kotti-filter/components/FilterList.vue b/packages/kotti-ui/source/kotti-filters/components/FilterList.vue similarity index 100% rename from packages/kotti-ui/source/kotti-filter/components/FilterList.vue rename to packages/kotti-ui/source/kotti-filters/components/FilterList.vue diff --git a/packages/kotti-ui/source/kotti-filter/components/FilterRow.vue b/packages/kotti-ui/source/kotti-filters/components/FilterRow.vue similarity index 100% rename from packages/kotti-ui/source/kotti-filter/components/FilterRow.vue rename to packages/kotti-ui/source/kotti-filters/components/FilterRow.vue diff --git a/packages/kotti-ui/source/kotti-filter/components/FilterSearch.vue b/packages/kotti-ui/source/kotti-filters/components/FilterSearch.vue similarity index 100% rename from packages/kotti-ui/source/kotti-filter/components/FilterSearch.vue rename to packages/kotti-ui/source/kotti-filters/components/FilterSearch.vue diff --git a/packages/kotti-ui/source/kotti-filter/index.ts b/packages/kotti-ui/source/kotti-filters/index.ts similarity index 100% rename from packages/kotti-ui/source/kotti-filter/index.ts rename to packages/kotti-ui/source/kotti-filters/index.ts diff --git a/packages/kotti-ui/source/kotti-filter/types.ts b/packages/kotti-ui/source/kotti-filters/types.ts similarity index 100% rename from packages/kotti-ui/source/kotti-filter/types.ts rename to packages/kotti-ui/source/kotti-filters/types.ts diff --git a/packages/kotti-ui/source/kotti-filter/utils.ts b/packages/kotti-ui/source/kotti-filters/utils.ts similarity index 100% rename from packages/kotti-ui/source/kotti-filter/utils.ts rename to packages/kotti-ui/source/kotti-filters/utils.ts diff --git a/packages/kotti-ui/source/kotti-filter/validators.ts b/packages/kotti-ui/source/kotti-filters/validators.ts similarity index 100% rename from packages/kotti-ui/source/kotti-filter/validators.ts rename to packages/kotti-ui/source/kotti-filters/validators.ts diff --git a/packages/kotti-ui/source/kotti-form/controllers/KtFormControllerList.vue b/packages/kotti-ui/source/kotti-form-controller-list/KtFormControllerList.vue similarity index 93% rename from packages/kotti-ui/source/kotti-form/controllers/KtFormControllerList.vue rename to packages/kotti-ui/source/kotti-form-controller-list/KtFormControllerList.vue index 066f0d7c62..a519ee8574 100644 --- a/packages/kotti-ui/source/kotti-form/controllers/KtFormControllerList.vue +++ b/packages/kotti-ui/source/kotti-form-controller-list/KtFormControllerList.vue @@ -5,7 +5,7 @@ name="header" :values="cloneDeep(valuesList)" /> - - + import { defineComponent, provide, computed } from '@vue/composition-api' -import { KT_FORM_CONTEXT } from '../constants' -import { KottiForm } from '../types' - -import { KottiFormControllerListItem } from './types' +import { KT_FORM_CONTEXT } from '../../kotti-form/constants' +import { KottiForm } from '../../kotti-form/types' +import { KottiFormControllerListItem } from '../types' export default defineComponent({ - name: 'KtFormControllerListItem', + name: 'FormControllerListItem', props: { context: { required: true, type: Object }, formKey: { required: true, type: String }, diff --git a/packages/kotti-ui/source/kotti-form-controller-list/index.ts b/packages/kotti-ui/source/kotti-form-controller-list/index.ts new file mode 100644 index 0000000000..3ea2979ec6 --- /dev/null +++ b/packages/kotti-ui/source/kotti-form-controller-list/index.ts @@ -0,0 +1,74 @@ +import { Kotti } from '../types' +import { attachMeta, makeInstallable } from '../utilities' + +import KtFormControllerListVue from './KtFormControllerList.vue' + +export const sharedHeaderFooterScope: Kotti.Meta['slots'][string]['scope'] = { + addAfter: { + description: 'append an item to the list', + type: 'function', + }, + addBefore: { + description: 'prepend an item to the list', + type: 'function', + }, + setValues: { + description: 'replace the list', + type: 'function', + }, + values: { + description: 'all items in the list', + type: 'object', + }, +} + +export const KtFormControllerList = attachMeta( + makeInstallable(KtFormControllerListVue), + { + addedVersion: '2.0.0', + deprecated: null, + designs: null, + slots: { + default: { + description: null, + scope: { + addAfter: { + description: null, + type: 'function', + }, + addBefore: { + description: null, + type: 'function', + }, + deleteSelf: { + description: null, + type: 'function', + }, + index: { + description: null, + type: 'integer', + }, + setValues: { + description: null, + type: 'function', + }, + values: { + description: null, + type: 'object', + }, + }, + }, + footer: { + description: 'inserted below the list, exposes list utilities', + scope: sharedHeaderFooterScope, + }, + header: { + description: 'inserted above the list, exposes list utilities', + scope: sharedHeaderFooterScope, + }, + }, + typeScript: { + namespace: 'Kotti.FormControllerList', + }, + }, +) diff --git a/packages/kotti-ui/source/kotti-form/controllers/tests.ts b/packages/kotti-ui/source/kotti-form-controller-list/tests.ts similarity index 61% rename from packages/kotti-ui/source/kotti-form/controllers/tests.ts rename to packages/kotti-ui/source/kotti-form-controller-list/tests.ts index 3f757440cc..0fc38d28d5 100644 --- a/packages/kotti-ui/source/kotti-form/controllers/tests.ts +++ b/packages/kotti-ui/source/kotti-form-controller-list/tests.ts @@ -3,16 +3,15 @@ require('jsdom-global')() import { defineComponent, ref } from '@vue/composition-api' import { mount, Wrapper } from '@vue/test-utils' -import { KOTTI_FIELD_PROPS } from '../../kotti-field/constants' -import { useField } from '../../kotti-field/hooks' -import KtField from '../../kotti-field/KtField.vue' -import { KottiField } from '../../kotti-field/types' -import { useTranslationProvide } from '../../kotti-translation/hooks' -import { localVue } from '../../test-utils/index' -import KtForm from '../KtForm.vue' +import { KOTTI_FIELD_PROPS } from '../kotti-field/constants' +import { useField } from '../kotti-field/hooks' +import KtField from '../kotti-field/KtField.vue' +import { KottiField } from '../kotti-field/types' +import KtForm from '../kotti-form/KtForm.vue' +import { useTranslationProvide } from '../kotti-translation/hooks' +import { localVue } from '../test-utils/index' import KtFormControllerList from './KtFormControllerList.vue' -import KtFormControllerObject from './KtFormControllerObject.vue' const TestField = defineComponent({ name: 'TestField', @@ -53,20 +52,6 @@ const TestControllerListForm = { template: ``, } -const TestControllerObject = { - components: { TestField, KtFormControllerObject }, - template: ``, -} - -const TestControllerObjectForm = { - components: { KtForm, TestControllerObject }, - props: { - controllerProps: { required: true, type: Object }, - formProps: { required: true, type: Object }, - }, - template: ``, -} - const getField = ( // eslint-disable-next-line @typescript-eslint/no-explicit-any wrapper: Wrapper, @@ -190,79 +175,3 @@ describe('KtFormControllerList', () => { }) }) }) - -describe('KtFormControllerObject', () => { - it('provides context with nested data, and passes-down the other properties of the KtFormContext', () => { - const wrapper = mount(TestControllerObjectForm, { - localVue, - propsData: { - controllerProps: { formKey: 'parentKey' }, - formProps: { - hideValidation: true, - validators: { testKey: () => ({ type: 'empty' }) }, - value: { parentKey: { testKey: 'something' } }, - }, - }, - }) - - const field = getField(wrapper, 0) - - expect(field.currentValue).toBe('something') - expect(field.hideValidation).toBe(true) - expect(field.validation).toEqual({ type: 'empty' }) - }) - - it('implements setValue properly', async () => { - const wrapper = mount(TestControllerObjectForm, { - localVue, - propsData: { - controllerProps: { formKey: 'parentKey' }, - formProps: { - hideValidation: true, - validators: { - testKey: () => ({ type: 'empty' }), - }, - value: { parentKey: { testKey: 'something' } }, - }, - }, - }) - const field = getField(wrapper, 0) - - field.setValue('setSomething') - - await wrapper.vm.$nextTick() - - expect(wrapper.emitted('input')).toEqual([ - [{ parentKey: { testKey: 'setSomething' } }], - ]) - }) - - describe('reactivity', () => { - it('hideValidation', async () => { - const FORM_PROPS = { - hideValidation: false, - value: { - parentKey: { testKey: 'something' }, - }, - } - - const wrapper = mount(TestControllerObjectForm, { - localVue, - propsData: { - controllerProps: { formKey: 'parentKey' }, - formProps: FORM_PROPS, - }, - }) - - const field = getField(wrapper, 0) - - expect(field.hideValidation).toBe(false) - - wrapper.setProps({ formProps: { ...FORM_PROPS, hideValidation: true } }) - - await wrapper.vm.$nextTick() - - expect(field.hideValidation).toBe(true) - }) - }) -}) diff --git a/packages/kotti-ui/source/kotti-form/controllers/types.ts b/packages/kotti-ui/source/kotti-form-controller-list/types.ts similarity index 62% rename from packages/kotti-ui/source/kotti-form/controllers/types.ts rename to packages/kotti-ui/source/kotti-form-controller-list/types.ts index 72006742c0..27590a2b0c 100644 --- a/packages/kotti-ui/source/kotti-form/controllers/types.ts +++ b/packages/kotti-ui/source/kotti-form-controller-list/types.ts @@ -1,18 +1,11 @@ -import { KottiField } from '../../kotti-field/types' -import { KottiForm } from '../types' +import { KottiField } from '../kotti-field/types' +import { KottiForm } from '../kotti-form/types' export namespace KottiFormControllerList { export type Props = { formKey: string } } - -export namespace KottiFormControllerObject { - export type Props = { - formKey: string - } -} - export namespace KottiFormControllerListItem { export type Props = { context: KottiForm.Context diff --git a/packages/kotti-ui/source/kotti-form/controllers/KtFormControllerObject.vue b/packages/kotti-ui/source/kotti-form-controller-object/KtFormControllerObject.vue similarity index 94% rename from packages/kotti-ui/source/kotti-form/controllers/KtFormControllerObject.vue rename to packages/kotti-ui/source/kotti-form-controller-object/KtFormControllerObject.vue index dce6f31cc9..8674bae7fe 100644 --- a/packages/kotti-ui/source/kotti-form/controllers/KtFormControllerObject.vue +++ b/packages/kotti-ui/source/kotti-form-controller-object/KtFormControllerObject.vue @@ -16,8 +16,8 @@ import { provide, } from '@vue/composition-api' -import { KT_FORM_CONTEXT } from '../constants' -import { KottiForm } from '../types' +import { KT_FORM_CONTEXT } from '../kotti-form/constants' +import { KottiForm } from '../kotti-form/types' import { KottiFormControllerObject } from './types' diff --git a/packages/kotti-ui/source/kotti-form-controller-object/index.ts b/packages/kotti-ui/source/kotti-form-controller-object/index.ts new file mode 100644 index 0000000000..e48844c1e3 --- /dev/null +++ b/packages/kotti-ui/source/kotti-form-controller-object/index.ts @@ -0,0 +1,34 @@ +import { attachMeta, makeInstallable } from '../utilities' + +import KtFormControllerObjectVue from './KtFormControllerObject.vue' + +export const KtFormControllerObject = attachMeta( + makeInstallable(KtFormControllerObjectVue), + { + addedVersion: '2.0.0', + deprecated: null, + designs: null, + slots: { + default: { + description: null, + scope: { + setValue: { + description: 'sets a key on the controller object', + type: 'function', + }, + setValues: { + description: 'sets value of the controller object', + type: 'function', + }, + values: { + description: 'current value of the controller object', + type: 'object', + }, + }, + }, + }, + typeScript: { + namespace: 'Kotti.FormControllerObject', + }, + }, +) diff --git a/packages/kotti-ui/source/kotti-form-controller-object/tests.ts b/packages/kotti-ui/source/kotti-form-controller-object/tests.ts new file mode 100644 index 0000000000..dabf17be0f --- /dev/null +++ b/packages/kotti-ui/source/kotti-form-controller-object/tests.ts @@ -0,0 +1,137 @@ +// eslint-disable-next-line @typescript-eslint/no-var-requires +require('jsdom-global')() +import { defineComponent, ref } from '@vue/composition-api' +import { mount, Wrapper } from '@vue/test-utils' + +import { KOTTI_FIELD_PROPS } from '../kotti-field/constants' +import { useField } from '../kotti-field/hooks' +import KtField from '../kotti-field/KtField.vue' +import { KottiField } from '../kotti-field/types' +import KtForm from '../kotti-form/KtForm.vue' +import { useTranslationProvide } from '../kotti-translation/hooks' +import { localVue } from '../test-utils/index' + +import KtFormControllerObject from './KtFormControllerObject.vue' + +const TestField = defineComponent({ + name: 'TestField', + components: { KtField }, + props: KOTTI_FIELD_PROPS, + setup: (props: KottiField.Props, { emit }) => { + useTranslationProvide(ref('en-US'), ref({})) + + return { + field: useField({ + emit, + isCorrectDataType: (value): value is string | null => + typeof value === 'string' || value === null, + isEmpty: (value) => value === null, + props, + supports: { + clear: true, + decoration: true, + tabIndex: true, + }, + }), + } + }, + template: `FIELD`, +}) + +const TestControllerObject = { + components: { TestField, KtFormControllerObject }, + template: ``, +} + +const TestControllerObjectForm = { + components: { KtForm, TestControllerObject }, + props: { + controllerProps: { required: true, type: Object }, + formProps: { required: true, type: Object }, + }, + template: ``, +} + +const getField = ( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + wrapper: Wrapper, + index: number, +): KottiField.Hook.Returns => + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (wrapper.findAllComponents({ name: 'KtField' }).at(index).vm as any).field + +describe('KtFormControllerObject', () => { + it('provides context with nested data, and passes-down the other properties of the KtFormContext', () => { + const wrapper = mount(TestControllerObjectForm, { + localVue, + propsData: { + controllerProps: { formKey: 'parentKey' }, + formProps: { + hideValidation: true, + validators: { testKey: () => ({ type: 'empty' }) }, + value: { parentKey: { testKey: 'something' } }, + }, + }, + }) + + const field = getField(wrapper, 0) + + expect(field.currentValue).toBe('something') + expect(field.hideValidation).toBe(true) + expect(field.validation).toEqual({ type: 'empty' }) + }) + + it('implements setValue properly', async () => { + const wrapper = mount(TestControllerObjectForm, { + localVue, + propsData: { + controllerProps: { formKey: 'parentKey' }, + formProps: { + hideValidation: true, + validators: { + testKey: () => ({ type: 'empty' }), + }, + value: { parentKey: { testKey: 'something' } }, + }, + }, + }) + const field = getField(wrapper, 0) + + field.setValue('setSomething') + + await wrapper.vm.$nextTick() + + expect(wrapper.emitted('input')).toEqual([ + [{ parentKey: { testKey: 'setSomething' } }], + ]) + }) + + describe('reactivity', () => { + it('hideValidation', async () => { + const FORM_PROPS = { + hideValidation: false, + value: { + parentKey: { testKey: 'something' }, + }, + } + + const wrapper = mount(TestControllerObjectForm, { + localVue, + propsData: { + controllerProps: { formKey: 'parentKey' }, + formProps: FORM_PROPS, + }, + }) + + const field = getField(wrapper, 0) + + expect(field.hideValidation).toBe(false) + + wrapper.setProps({ formProps: { ...FORM_PROPS, hideValidation: true } }) + + await wrapper.vm.$nextTick() + + expect(field.hideValidation).toBe(true) + }) + }) +}) diff --git a/packages/kotti-ui/source/kotti-form-controller-object/types.ts b/packages/kotti-ui/source/kotti-form-controller-object/types.ts new file mode 100644 index 0000000000..bfeb2a5d86 --- /dev/null +++ b/packages/kotti-ui/source/kotti-form-controller-object/types.ts @@ -0,0 +1,5 @@ +export namespace KottiFormControllerObject { + export type Props = { + formKey: string + } +} diff --git a/packages/kotti-ui/source/kotti-form/KtFormSubmit.vue b/packages/kotti-ui/source/kotti-form-submit/KtFormSubmit.vue similarity index 87% rename from packages/kotti-ui/source/kotti-form/KtFormSubmit.vue rename to packages/kotti-ui/source/kotti-form-submit/KtFormSubmit.vue index ad79cd6796..99ec13a0ef 100644 --- a/packages/kotti-ui/source/kotti-form/KtFormSubmit.vue +++ b/packages/kotti-ui/source/kotti-form-submit/KtFormSubmit.vue @@ -46,13 +46,14 @@ import { Yoco } from '@3yourmind/yoco' import { defineComponent, inject, computed } from '@vue/composition-api' +import { KT_FORM_SUBMIT_CONTEXT } from '../kotti-form/constants' +import { KottiForm } from '../kotti-form/types' import { useTranslationNamespace } from '../kotti-translation/hooks' -import { KT_FORM_SUBMIT_CONTEXT } from './constants' -import { KtFormErrors } from './errors' -import { KottiForm } from './types' +import { KtFormSubmitErrors } from './errors' +import { KottiFormSubmit } from './types' -export default defineComponent({ +export default defineComponent({ name: 'KtFormSubmit', props: { label: { default: 'Submit', type: String }, @@ -63,7 +64,8 @@ export default defineComponent({ null, ) - if (context === null) throw new KtFormErrors.InvalidSubmitOutsideContext() + if (context === null) + throw new KtFormSubmitErrors.InvalidSubmitOutsideContext() const { isLoading, isValid, validationSummary } = context const errors = computed(() => validationSummary.value.errors) @@ -85,6 +87,8 @@ export default defineComponent({