vue#nextTick TypeScript Examples

The following examples show how to use vue#nextTick. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: use-is-mounted.spec.ts    From vooks with MIT License 6 votes vote down vote up
describe('# useIsMounted', () => {
  it('works', (done) => {
    const wrapper = mount(defineComponent({
      setup () {
        const isMounted = useIsMounted()
        expect(isMounted.value).toEqual(false)
        return {
          isMounted
        }
      },
      beforeMount () {
        expect(this.isMounted).toEqual(false)
      },
      mounted () {
        nextTick(() => {
          expect(this.isMounted).toEqual(true)
          wrapper.unmount()
          done()
        })
      }
    }))
  })
})
Example #2
Source File: useTitle.test.ts    From vhook with MIT License 6 votes vote down vote up
describe('test useTitle when restoreOnUnMount is true', () => {
  let wrapper: VueWrapper<any>, setTitle: (title: string) => void
  beforeEach(() => {
    const comp = defineComponent({
      template: '<div>test</div>',
      setup () {
        setTitle = useTitle('test', true)
      }
    })
    document.title = 'init'
    wrapper = mount(comp)
  })

  test('useTitle should be defined', () => {
    expect(useTitle).toBeDefined()
  })

  test('document.title should be test after component mounted', () => {
    expect(document.title).toBe('test')
  })

  test('document.title should change after invoking setTitle', (done) => {
    setTitle('change')
    nextTick(() => {
      expect(document.title).toBe('change')
      done()
    })
  })

  test('document.title should be reset to init after component unmounted', () => {
    wrapper.unmount()
    expect(document.title).toBe('init')
  })
})
Example #3
Source File: mixin.test.ts    From vue-i18n-next with MIT License 6 votes vote down vote up
test.skip('beforeDestroy', async () => {
  const i18n = createI18n({
    legacy: true,
    locale: 'en',
    messages: {
      en: {
        hello: 'hello!'
      }
    }
  })

  const App = defineComponent({ template: '<br/>' })
  const { app, vm } = await mount(App, i18n)

  app.unmount()
  await nextTick()

  expect(vm.$i18n).toBeUndefined()
})
Example #4
Source File: createInput.spec.ts    From formkit with MIT License 6 votes vote down vote up
describe('custom input behaviors', () => {
  it('does not emit prop:{property} events for input props', async () => {
    const pseudoPropEvent = jest.fn()
    const nativePropEvent = jest.fn()
    const input = createInput('test input', {
      props: ['bizBaz'],
      features: [
        (node: FormKitNode) => {
          node.on('prop:bizBaz', pseudoPropEvent)
          node.on('prop:delay', nativePropEvent)
        },
      ],
    })
    mount(FormKit, {
      props: {
        type: input,
        bizBaz: 'hello',
        delay: 10,
      },
      global: {
        plugins: [[plugin, defaultConfig]],
      },
    })
    await nextTick()
    expect(nativePropEvent).toHaveBeenCalledTimes(0)
    expect(pseudoPropEvent).toHaveBeenCalledTimes(0)
  })
})
Example #5
Source File: dom.ts    From fect with MIT License 6 votes vote down vote up
trigger = (evt: keyof WindowEventMap, wrapper: Wrapper, x = 0, y = 0, offsetX = 0, offsetY = 0) => {
  const el = 'element' in wrapper ? wrapper.element : wrapper
  const eventObserver = new CustomEvent<typeof evt>(evt)
  Object.assign(eventObserver, {
    clientX: x,
    clientY: y,
    offsetX,
    offsetY
  })
  el.dispatchEvent(eventObserver)
  return nextTick()
}
Example #6
Source File: use-false-until-truthy.spec.ts    From vooks with MIT License 6 votes vote down vote up
describe('# useFalseUntilTruthy', () => {
  it('works with init value `false`', async () => {
    const originalRef = ref<any>(false)
    const testRef = useFalseUntilTruthy(originalRef)
    expect(testRef.value).toEqual(false)
    originalRef.value = null
    await nextTick()
    expect(testRef.value).toEqual(false)
    originalRef.value = true
    await nextTick()
    expect(testRef.value).toEqual(true)
    originalRef.value = false
    await nextTick()
    expect(testRef.value).toEqual(true)
  })
  it('works with init value `true`', async () => {
    const originalRef = ref(true)
    const testRef = useFalseUntilTruthy(originalRef)
    expect(testRef.value).toEqual(true)
    originalRef.value = false
    await nextTick()
    expect(testRef.value).toEqual(true)
  })
})
Example #7
Source File: useTimeoutFn.test.ts    From vhook with MIT License 5 votes vote down vote up
test('setTimeout should be called with default delay', async () => {
  useTimeoutFn(callback!)
  expect(setTimeout).toHaveBeenCalledTimes(1)
  expect(setTimeout).toHaveBeenCalledWith(expect.any(Function), 1000)
  jest.advanceTimersByTime(1000)
  await nextTick()
  expect(callback!).toHaveBeenCalledTimes(1)
})
Example #8
Source File: wc.test.ts    From vue-i18n-next with MIT License 5 votes vote down vote up
test('custom blocks', async () => {
  const i18n = createI18n<false>({
    legacy: false,
    locale: 'en',
    messages: {
      en: {
        hello: 'hello web components!'
      },
      ja: {
        hello: 'こんにちは Web コンポーネント!'
      }
    }
  })

  const Provider = defineCustomElement({
    setup() {
      provide(I18nInjectionKey, i18n)
      return () => h('my-child-block')
    }
  })
  customElements.define('my-provider-block', Provider)
  const Child = defineCustomElement({
    setup() {
      const instance = getCurrentInstance()
      if (instance == null) {
        throw new Error()
      }
      const options = instance.type as ComponentOptions
      options.__i18n = [
        {
          locale: 'en',
          resource: { foo: 'foo!' }
        },
        {
          locale: 'ja',
          resource: { foo: 'ふー!' }
        }
      ]
      const { t } = useI18n({
        inheritLocale: true,
        useScope: 'local'
      })
      return () => h('div', t('foo'))
    }
  })
  customElements.define('my-child-block', Child)

  container.innerHTML = `<my-provider-block></my-provider-block>`
  await nextTick()
  const provider = container.childNodes[0] as VueElement
  const child = provider.shadowRoot!.childNodes[0] as VueElement
  expect(child.shadowRoot!.innerHTML).toBe(`<div>foo!</div>`)

  i18n.global.locale.value = 'ja'
  await nextTick()
  expect(child.shadowRoot!.innerHTML).toBe(`<div>ふー!</div>`)
})
Example #9
Source File: LiveAtlasLayerControl.ts    From LiveAtlas with Apache License 2.0 5 votes vote down vote up
_initLayout() {
		const className = 'leaflet-control-layers',
			container = this._container = DomUtil.create('div', className),
			section = this._section = DomUtil.create('section', className + '-list'),
			button = this._layersButton = DomUtil.create('button', className + '-toggle', container);

		DomEvent.disableClickPropagation(container);
		DomEvent.disableScrollPropagation(container);

		//Open layer list on ArrowRight from button
		DomEvent.on(button,'keydown', (e: Event) => {
			if((e as KeyboardEvent).key === 'ArrowRight') {
				store.commit(MutationTypes.SET_UI_ELEMENT_VISIBILITY, {element: 'layers', state: true});
			}
		});

		DomEvent.on(container, 'keydown', (e: Event) => {
			//Close layer list on ArrowLeft from within list
			if((e as KeyboardEvent).key === 'ArrowLeft') {
				e.preventDefault();
				store.commit(MutationTypes.SET_UI_ELEMENT_VISIBILITY, {element: 'layers', state: false});
				nextTick(() => button.focus());
			}

			const elements = Array.from(container.querySelectorAll('input')) as HTMLElement[];
			handleKeyboardEvent(e as KeyboardEvent, elements);
		});
		DomEvent.on(button,'click', () => store.commit(MutationTypes.TOGGLE_UI_ELEMENT_VISIBILITY, 'layers'));

		section.style.display = 'none';

		button.title = store.state.messages.layersTitle;
		button.setAttribute('aria-expanded', 'false');
		button.innerHTML = `
			<svg class="svg-icon" aria-hidden="true">
			  <use xlink:href="#icon--layers" />
			</svg>`;


		//Use vuex track expanded state
		watch(store.state.ui.visibleElements, (newValue) => {
			if(newValue.has('layers') && !this.visible) {
				this.expand();
			} else if(this.visible && !newValue.has('layers')) {
				this.collapse();
			}

			this.visible = store.state.ui.visibleElements.has('layers');
		});

		watch(store.state.messages, (newValue) => (button.title = newValue.layersTitle));//

		this.visible = store.state.ui.visibleElements.has('layers');

		if (this.visible) {
			this.expand();
		}

		this._baseLayersList = DomUtil.create('div', className + '-base', section);
		this._separator = DomUtil.create('div', className + '-separator', section);
		this._overlaysList = DomUtil.create('div', className + '-overlays', section);

		container.appendChild(section);

		window.addEventListener('resize', () => this.handleResize());
		this.handleResize();
	}
Example #10
Source File: plugin.spec.ts    From formkit with MIT License 5 votes vote down vote up
describe('plugins', () => {
  it('can define props in a standard plugin', () => {
    const customPlugin = (node: FormKitNode) => {
      node.addProps(['fooBar'])
      expect(node.props.fooBar).toBe('123')
    }
    const wrapper = mount(FormKit, {
      props: {
        type: 'text',
        'foo-bar': '123',
        plugins: [customPlugin],
      },
      global: {
        plugins: [[plugin, defaultConfig]],
      },
    })
    expect(wrapper.find('[foo-bar]').exists()).toBe(false)
  })

  it('can add props after the node as already been created', async () => {
    const id = token()
    const customPlugin = (node: FormKitNode) => {
      node.addProps(['fooBar'])
      expect(node.props.fooBar).toBe('123')
    }
    const wrapper = mount(FormKit, {
      props: {
        type: 'text',
        id,
        'foo-bar': '123',
      },
      global: {
        plugins: [[plugin, defaultConfig]],
      },
    })
    expect(wrapper.find('[foo-bar]').exists()).toBe(true)
    getNode(id)?.use(customPlugin)
    await nextTick()
    expect(wrapper.find('[foo-bar]').exists()).toBe(false)
  })
})
Example #11
Source File: useInput.ts    From vue3-treeview with MIT License 5 votes vote down vote up
export default function useInput(cmn: IUseCommon): {} {
    const node = cmn.node; 
    const config = cmn.config;
    const wrapper = cmn.wrapper;
    const editable = cmn.editable;
    const editing = cmn.editing;
    const input = ref<HTMLInputElement>(null);

    const text = computed({
        get: () => node.value.text,
        set: (val: string) => node.value.text = val 
    });

    const editableClass = computed(() => {
        if (!cmn.editable.value) {
            return null;
        }

        return config.value.editableClass ? config.value.editableClass : "editable";
    });

    watch(editing, (nv: boolean, ov: boolean) => {
        if (!eq(nv, ov) && nv) {
            nextTick(() => {
                input.value.focus();
            });
        }
    });

    const focusInput = (() => {
        if (editable.value && !cmn.disabled.value) {
            config.value.editing = node.value.id;
            cmn.root.emit(nodeEvents.edit, node.value);
        }
    });

    const esc = ((event: Event) => {
        if (editable.value && config.value.keyboardNavigation) {
            cmn.blur(event);
            wrapper.value.focus();
        }
    });

    const enter = (() => {
        if (editable.value && !cmn.disabled.value && config.value.keyboardNavigation) {
            focusInput();
        }
    });

    return {
        text,
        input,
        editing,
        editable,
        editableClass,
        focusInput,
        esc,
        enter
    };
}
Example #12
Source File: composer.test.ts    From vue-i18n-next with MIT License 5 votes vote down vote up
describe('inheritLocale', () => {
  test('default value', () => {
    const root = createComposer({ locale: 'en' })
    const { inheritLocale, locale } = createComposer({
      locale: 'ja',
      __root: root
    })
    expect(inheritLocale).toEqual(true)
    expect(locale.value).toEqual('en')
  })

  test('initialize with composer option', () => {
    const root = createComposer({ locale: 'en' })
    const { inheritLocale, locale } = createComposer({
      locale: 'ja',
      inheritLocale: false,
      __root: root
    })
    expect(inheritLocale).toEqual(false)
    expect(locale.value).toEqual('ja')
  })

  test('sync root locale, fallbackLocale', async () => {
    const root = createComposer({
      locale: 'en',
      fallbackLocale: ['ja', 'fr']
    })
    const composer = createComposer({
      locale: 'ja',
      fallbackLocale: ['zh', 'de'],
      inheritLocale: true,
      __root: root
    })
    await nextTick()

    expect(composer.locale.value).toEqual('en')
    expect(composer.fallbackLocale.value).toEqual(['ja', 'fr'])

    root.locale.value = 'ja'
    root.fallbackLocale.value = ['zh', 'de']
    await nextTick()

    expect(composer.locale.value).toEqual('ja')
    expect(composer.fallbackLocale.value).toEqual(['zh', 'de'])

    composer.inheritLocale = false
    await nextTick()

    root.locale.value = 'en'
    root.fallbackLocale.value = ['ja', 'fr']
    await nextTick()

    expect(composer.locale.value).toEqual('ja')
    expect(composer.fallbackLocale.value).toEqual(['zh', 'de'])

    composer.inheritLocale = true
    await nextTick()

    expect(composer.locale.value).toEqual('en')
    expect(composer.fallbackLocale.value).toEqual(['ja', 'fr'])
  })
})
Example #13
Source File: bindings.ts    From formkit with MIT License 4 votes vote down vote up
vueBindings: FormKitPlugin = function vueBindings(node) {
  /**
   * Start a validity counter on all blocking messages.
   */
  node.ledger.count('blocking', (m) => m.blocking)
  const isValid = ref<boolean>(!node.ledger.value('blocking'))
  /**
   * Start an error message counter.
   */
  node.ledger.count('errors', (m) => m.type === 'error')
  const hasErrors = ref<boolean>(!!node.ledger.value('errors'))

  /**
   * Keep track of the first time a Vue tick cycle has passed.
   */
  let hasTicked = false
  nextTick(() => {
    hasTicked = true
  })

  /**
   * All messages with the visibility state set to true.
   */
  const availableMessages = reactive<Record<string, FormKitMessage>>(
    node.store.reduce((store, message) => {
      if (message.visible) {
        store[message.key] = message
      }
      return store
    }, {} as Record<string, FormKitMessage>)
  )
  /**
   * A flag that determines when validation messages should be displayed.
   */
  const validationVisibility = ref<string>(
    node.props.validationVisibility || 'blur'
  )
  node.on('prop:validationVisibility', ({ payload }) => {
    validationVisibility.value = payload
  })

  /**
   * Keep track of if this input has ever shown validation errors.
   */
  const hasShownErrors = ref(validationVisibility.value === 'live')

  /**
   * The current visibility state of validation messages.
   */
  const validationVisible = computed<boolean>(() => {
    if (context.state.submitted) return true
    if (!hasShownErrors.value && !context.state.settled) {
      return false
    }
    switch (validationVisibility.value) {
      case 'live':
        return true
      case 'blur':
        return context.state.blurred
      case 'dirty':
        return context.state.dirty
      default:
        return false
    }
  })

  /**
   * Determines if the input should be considered "complete".
   */
  const isComplete = computed<boolean>(() => {
    return hasValidation.value
      ? isValid.value && !hasErrors.value
      : context.state.dirty && !empty(context.value)
  })

  /**
   * If the input has validation rules or not.
   */
  const hasValidation = ref<boolean>(
    Array.isArray(node.props.parsedRules) && node.props.parsedRules.length > 0
  )
  node.on('prop:parsedRules', ({ payload: rules }) => {
    hasValidation.value = Array.isArray(rules) && rules.length > 0
  })

  /**
   * All messages that are currently on display to an end user. This changes
   * based on the current message type visibility, like errorVisibility.
   */
  const messages = computed<Record<string, FormKitMessage>>(() => {
    const visibleMessages: Record<string, FormKitMessage> = {}
    for (const key in availableMessages) {
      const message = availableMessages[key]
      if (message.type !== 'validation' || validationVisible.value) {
        visibleMessages[key] = message
      }
    }
    return visibleMessages
  })

  /**
   * UI Messages.
   */
  const ui = reactive(
    node.store.reduce((messages, message) => {
      if (message.type === 'ui' && message.visible)
        messages[message.key] = message
      return messages
    }, {} as Record<string, FormKitMessage>)
  )

  /**
   * This is the reactive data object that is provided to all schemas and
   * forms. It is a subset of data in the core node object.
   */
  const cachedClasses = reactive({})
  const classes = new Proxy(cachedClasses as Record<PropertyKey, string>, {
    get(...args) {
      const [target, property] = args
      let className = Reflect.get(...args)
      if (!className && typeof property === 'string') {
        if (!has(target, property) && !property.startsWith('__v')) {
          const observedNode = createObserver(node)
          observedNode.watch((node) => {
            const rootClasses =
              typeof node.config.rootClasses === 'function'
                ? node.config.rootClasses(property, node)
                : {}
            const globalConfigClasses = node.config.classes
              ? createClasses(property, node, node.config.classes[property])
              : {}
            const classesPropClasses = createClasses(
              property,
              node,
              node.props[`_${property}Class`]
            )
            const sectionPropClasses = createClasses(
              property,
              node,
              node.props[`${property}Class`]
            )
            className = generateClassList(
              node,
              property,
              rootClasses,
              globalConfigClasses,
              classesPropClasses,
              sectionPropClasses
            )
            target[property] = className
          })
        }
      }
      return className
    },
  })

  const describedBy = computed<string | undefined>(() => {
    const describers = []
    if (context.help) {
      describers.push(`help-${node.props.id}`)
    }
    for (const key in messages.value) {
      describers.push(`${node.props.id}-${key}`)
    }
    return describers.length ? describers.join(' ') : undefined
  })

  const value = ref(node.value)
  const _value = ref(node.value)

  const context: FormKitFrameworkContext = reactive({
    _value,
    attrs: node.props.attrs,
    disabled: node.props.disabled,
    describedBy,
    fns: {
      length: (obj: Record<PropertyKey, any>) => Object.keys(obj).length,
      number: (value: any) => Number(value),
      string: (value: any) => String(value),
      json: (value: any) => JSON.stringify(value),
      eq,
    },
    handlers: {
      blur: () =>
        node.store.set(
          createMessage({ key: 'blurred', visible: false, value: true })
        ),
      touch: () => {
        node.store.set(
          createMessage({ key: 'dirty', visible: false, value: true })
        )
      },
      DOMInput: (e: Event) => {
        node.input((e.target as HTMLInputElement).value)
        node.emit('dom-input-event', e)
      },
    },
    help: node.props.help,
    id: node.props.id as string,
    label: node.props.label,
    messages,
    node: markRaw(node),
    options: node.props.options,
    state: {
      blurred: false,
      complete: isComplete,
      dirty: false,
      submitted: false,
      settled: node.isSettled,
      valid: isValid,
      errors: hasErrors,
      rules: hasValidation,
      validationVisible,
    },
    type: node.props.type,
    ui,
    value,
    classes,
  })

  /**
   * Ensure the context object is properly configured after booting up.
   */
  node.on('created', () => {
    if (!eq(context.value, node.value)) {
      _value.value = node.value
      value.value = node.value
      triggerRef(value)
      triggerRef(_value)
    }
  })

  /**
   * Sets the settled state.
   */
  node.on('settled', ({ payload: isSettled }) => {
    context.state.settled = isSettled
  })

  /**
   * Observes node.props properties explicitly and updates them in the context
   * object.
   * @param observe - Props to observe and register as context data.
   */
  function observeProps(observe: string[]) {
    observe.forEach((prop) => {
      prop = camel(prop)
      if (!has(context, prop) && has(node.props, prop)) {
        context[prop] = node.props[prop]
      }
      node.on(`prop:${prop}`, ({ payload }) => {
        context[prop as keyof FormKitFrameworkContext] = payload
      })
    })
  }

  /**
   * We use a node observer to individually observe node props.
   */
  const rootProps = [
    'help',
    'label',
    'disabled',
    'options',
    'type',
    'attrs',
    'preserve',
    'preserveErrors',
    'id',
  ]
  observeProps(rootProps)

  /**
   * Once the input is defined, deal with it.
   * @param definition - Type definition.
   */
  function definedAs(definition: FormKitTypeDefinition) {
    if (definition.props) observeProps(definition.props)
  }

  node.props.definition && definedAs(node.props.definition)

  /**
   * When new props are added to the core node as "props" (ie not attrs) then
   * we automatically need to start tracking them here.
   */
  node.on('added-props', ({ payload }) => observeProps(payload))

  /**
   * Watch for input events from core.
   */
  node.on('input', ({ payload }) => {
    _value.value = payload
    triggerRef(_value)
  })

  /**
   * Watch for input commits from core.
   */
  node.on('commit', ({ payload }) => {
    value.value = _value.value = payload
    triggerRef(value)
    node.emit('modelUpdated')
    // The input is dirty after a value has been input by a user
    if (!context.state.dirty && node.isCreated && hasTicked)
      context.handlers.touch()
    if (
      isComplete &&
      node.type === 'input' &&
      hasErrors.value &&
      !undefine(node.props.preserveErrors)
    ) {
      node.store.filter(
        (message) =>
          !(message.type === 'error' && message.meta?.autoClear === true)
      )
    }
  })

  /**
   * Update the local state in response to messages.
   * @param message - A formkit message
   */
  const updateState = async (message: FormKitMessage) => {
    if (
      message.type === 'ui' &&
      message.visible &&
      !message.meta.showAsMessage
    ) {
      ui[message.key] = message
    } else if (message.visible) {
      availableMessages[message.key] = message
    } else if (message.type === 'state') {
      // await node.settled
      context.state[message.key] = !!message.value
    }
  }

  /**
   * Listen to message events and modify the local message data values.
   */
  node.on('message-added', (e) => updateState(e.payload))
  node.on('message-updated', (e) => updateState(e.payload))
  node.on('message-removed', ({ payload: message }) => {
    delete ui[message.key]
    delete availableMessages[message.key]
    delete context.state[message.key]
  })
  node.on('settled:blocking', () => {
    isValid.value = true
  })
  node.on('unsettled:blocking', () => {
    isValid.value = false
  })
  node.on('settled:errors', () => {
    hasErrors.value = false
  })
  node.on('unsettled:errors', () => {
    hasErrors.value = true
  })

  /**
   * Watch the validation visible prop and set the hasShownErrors state.
   */
  watch(validationVisible, (value) => {
    if (value) {
      hasShownErrors.value = true
    }
  })

  node.context = context

  // The context is complete
  node.emit('context', node, false)
}
Example #14
Source File: useECharts.ts    From vite-vue3-ts with MIT License 4 votes vote down vote up
export function useECharts(
  elRef: Ref<HTMLDivElement>,
  theme: 'light' | 'dark' | 'default' = 'default',
) {
  const getDarkMode = computed(() => {
    return theme;
  });
  let chartInstance: echarts.ECharts | null = null;
  let resizeFn: Fn = resize;
  const cacheOptions = ref({}) as Ref<EChartsOption>;
  let removeResizeFn: Fn = () => {};

  resizeFn = useDebounceFn(resize, 200);

  const getOptions = computed(() => {
    return {
      backgroundColor: 'transparent',
      ...cacheOptions.value,
    } as EChartsOption;
  });

  function initCharts() {
    const el = unref(elRef);
    if (!el || !unref(el)) {
      return;
    }

    chartInstance = echarts.init(el);
    const { removeEvent } = useEventListener({
      el: window,
      name: 'resize',
      listener: resizeFn,
    });
    removeResizeFn = removeEvent;
    if (el.offsetHeight === 0) {
      useTimeoutFn(() => {
        resizeFn();
      }, 30);
    }
  }

  function setOptions(options: EChartsOption, clear = true) {
    cacheOptions.value = options;
    if (unref(elRef)?.offsetHeight === 0) {
      useTimeoutFn(() => {
        setOptions(unref(getOptions));
      }, 30);
      return;
    }
    nextTick(() => {
      useTimeoutFn(() => {
        if (!chartInstance) {
          initCharts();

          if (!chartInstance) return;
        }
        clear && chartInstance?.clear();

        chartInstance?.setOption(unref(getOptions));
      }, 30);
    });
  }

  function resize() {
    chartInstance?.resize();
  }

  watch(
    () => getDarkMode.value,
    () => {
      if (chartInstance) {
        chartInstance.dispose();
        initCharts();
        setOptions(cacheOptions.value);
      }
    },
  );

  tryOnUnmounted(() => {
    if (!chartInstance) return;
    removeResizeFn();
    chartInstance.dispose();
    chartInstance = null;
  });

  function getInstance(): echarts.ECharts | null {
    if (!chartInstance) {
      initCharts();
    }
    return chartInstance;
  }

  return {
    setOptions,
    resize,
    echarts,
    getInstance,
  };
}
Example #15
Source File: actions.ts    From LiveAtlas with Apache License 2.0 4 votes vote down vote up
actions: ActionTree<State, State> & Actions = {
	async [ActionTypes.LOAD_CONFIGURATION]({commit, state, dispatch}): Promise<void> {
		await dispatch(ActionTypes.STOP_UPDATES, undefined);
		commit(MutationTypes.RESET, undefined);

		if(!state.currentServer) {
			console.warn('No current server');
			return;
		}

		await state.currentMapProvider!.loadServerConfiguration();

		//Skip default map/ui visibility logic if we already have a map selected (i.e config reload after hash change)
		if(state.currentMap) {
			return;
		}

		//Make UI visible if configured, there's enough space to do so, and this is the first config load
		if(!state.ui.visibleElements.size && state.configuration.expandUI && !state.ui.smallScreen) {
			commit(MutationTypes.SET_UI_ELEMENT_VISIBILITY, {element: 'players', state: true});
			commit(MutationTypes.SET_UI_ELEMENT_VISIBILITY, {element: 'maps', state: true});

			if(!state.ui.disableMarkerUI) {
				commit(MutationTypes.SET_UI_ELEMENT_VISIBILITY, {element: 'markers', state: true});
			}
		}

		let worldName, mapName;

		// Use config default world if it exists
		if(state.configuration.defaultWorld && state.worlds.has(state.configuration.defaultWorld)) {
			worldName = state.configuration.defaultWorld;
		}

		// Prefer world from parsed url if present and it exists
		if(state.parsedUrl?.world && state.worlds.has(state.parsedUrl.world)) {
			worldName = state.parsedUrl.world;
		}

		// Use first world, if any, if neither of the above exist
		if(!worldName) {
			worldName = state.worlds.size ? state.worlds.entries().next().value[1].name : undefined;
		}

		if(worldName) {
			// Use config default map if it exists
			if(state.configuration.defaultMap && state.maps.has(`${worldName}_${state.configuration.defaultMap}`)) {
				mapName = state.configuration.defaultMap;
			}

			// Prefer map from parsed url if present and it exists
			if(state.parsedUrl?.map && state.maps.has(`${worldName}_${state.parsedUrl.map}`)) {
				mapName = state.parsedUrl.map;
			}

			// Use first map, if any, if neither of the above exist
			if(!mapName) {
				const world = state.worlds.get(worldName) as LiveAtlasWorldDefinition;
				mapName = world.maps.size ? world.maps.values().next().value.name : undefined;
			}
		}

		if(worldName && mapName) {
			commit(MutationTypes.SET_CURRENT_MAP, {
				worldName, mapName
			});
		}

		await nextTick(() => commit(MutationTypes.SET_LOADED, undefined));
	},

	async [ActionTypes.START_UPDATES]({state}) {
		if(!state.currentWorld) {
			return Promise.reject("No current world");
		}

		state.currentMapProvider!.startUpdates();
		startUpdateHandling();
	},

	async [ActionTypes.STOP_UPDATES]({state}) {
		state.currentMapProvider!.stopUpdates();
		stopUpdateHandling();
	},

	[ActionTypes.SET_PLAYERS]({commit, state}, players: Set<LiveAtlasPlayer>) {
		const keep: Set<string> = new Set();

		for(const player of players) {
			keep.add(player.name);
		}

		//Remove any players that aren't in the set
		commit(MutationTypes.SYNC_PLAYERS, keep);

		const processQueue = (players: Set<LiveAtlasPlayer>, resolve: Function) => {
			commit(MutationTypes.SET_PLAYERS_ASYNC, players);

			if(!players.size) {
				resolve();
			} else {
				requestAnimationFrame(() => processQueue(players, resolve));
			}
		}

		//Set players every frame until done
		return new Promise((resolve) => {
			requestAnimationFrame(() => processQueue(players, resolve));
		});
	},

	async [ActionTypes.POP_MARKER_UPDATES]({commit, state}, amount: number): Promise<DynmapMarkerUpdate[]> {
		const updates = state.pendingMarkerUpdates.slice(0, amount);

		commit(MutationTypes.POP_MARKER_UPDATES, amount);

		return updates;
	},

	async [ActionTypes.POP_TILE_UPDATES]({commit, state}, amount: number): Promise<Array<DynmapTileUpdate>> {
		const updates = state.pendingTileUpdates.slice(0, amount);

		commit(MutationTypes.POP_TILE_UPDATES, amount);

		return updates;
	},

	async [ActionTypes.SEND_CHAT_MESSAGE]({commit, state}, message: string): Promise<void> {
		await state.currentMapProvider!.sendChatMessage(message);
	},

	async [ActionTypes.LOGIN]({state, commit}, data: any): Promise<void> {
		await state.currentMapProvider!.login(data);
	},

	async [ActionTypes.LOGOUT]({state}): Promise<void> {
		await state.currentMapProvider!.logout();
	},

	async [ActionTypes.REGISTER]({state}, data: any): Promise<void> {
		await state.currentMapProvider!.register(data);
	},
}
Example #16
Source File: composer.test.ts    From vue-i18n-next with MIT License 4 votes vote down vote up
describe('t', () => {
  test('basic', () => {
    const { t } = createComposer({
      locale: 'en',
      messages: {
        en: { hi: 'hi kazupon !' }
      }
    })
    expect(t('hi')).toEqual('hi kazupon !')
  })

  test('list', () => {
    const { t } = createComposer({
      locale: 'en',
      messages: {
        en: { hi: 'hi {0} !' }
      }
    })
    expect(t('hi', ['kazupon'])).toEqual('hi kazupon !')
  })

  test('named', () => {
    const { t } = createComposer({
      locale: 'en',
      messages: {
        en: { hi: 'hi {name} !' }
      }
    })
    expect(t('hi', { name: 'kazupon' })).toEqual('hi kazupon !')
  })

  test('literal', () => {
    const { t } = createComposer({
      locale: 'en',
      messages: {
        en: {
          ascii: `hi {'kazupon'} !`,
          multibytes: `hi {'かずぽん'} !`,
          emoji: `hi {'?'} !`,
          specials: `hi {'${'!#%^&*()-_+=[]:;?.<>"`'}'} !`,
          escapedSingleQuote: `hi {'\\''} !`,
          escapedSlash: `hi {'\\\\'} !`,
          unicode4digits: `hi {'${'\u0041'}'} !`,
          escapedUnicode4digits: `hi {'\\\\u0041'} !`,
          unicode6digits: `hi {'${'U01F602'}'} !`,
          escapedUnicode6digits: `hi {'\\\\U01F602'} !`
        }
      }
    })
    expect(t('ascii')).toEqual('hi kazupon !')
    expect(t('multibytes')).toEqual('hi かずぽん !')
    expect(t('emoji')).toEqual('hi ? !')
    expect(t('specials')).toEqual(`hi ${'!#%^&*()-_+=[]:;?.<>"`'} !`)
    expect(t('escapedSingleQuote')).toEqual(`hi ' !`)
    expect(t('escapedSlash')).toEqual('hi \\ !')
    expect(t('unicode4digits')).toEqual('hi A !')
    expect(t('escapedUnicode4digits')).toEqual(`hi \\u0041 !`)
    expect(t('unicode6digits')).toEqual('hi U01F602 !')
    expect(t('escapedUnicode6digits')).toEqual(`hi \\U01F602 !`)
  })

  test('linked', () => {
    const { t } = createComposer({
      locale: 'en',
      messages: {
        en: {
          name: 'kazupon',
          hi: 'hi @.upper:name !',
          actions: {
            photo: 'added {count} @:photo'
          },
          photo: 'photo | photos',
          collection: 'Collection | Collections',
          file: 'File | Files',
          collection_name: '@:collection @:name',
          file_name: '@:file @:name'
        }
      }
    })
    expect(t('hi')).toEqual('hi KAZUPON !')
    expect(t('actions.photo', { count: 2 })).toEqual('added 2 photos') // linked pluralization
    expect(t('collection_name', { count: 2 })).toEqual('Collections kazupon')
    expect(t('file_name', { count: 1 })).toEqual('File kazupon')
  })

  test('plural', () => {
    const { t } = createComposer({
      locale: 'en',
      messages: {
        en: { apple: 'no apples | one apple | {count} apples' }
      }
    })
    expect(t('apple', 0)).toEqual('no apples')
    expect(t('apple', 1)).toEqual('one apple')
    expect(t('apple', 10)).toEqual('10 apples')
    expect(t('apple', { count: 20 }, 10)).toEqual('20 apples')
  })

  test('missing', () => {
    const missing = (locale: Locale, key: string) => {
      return key.toUpperCase()
    }
    const { t } = createComposer({
      locale: 'en',
      missing,
      messages: {
        en: {}
      }
    })
    expect(t('foo.bar.buz')).toEqual('FOO.BAR.BUZ')
  })

  test('computed property name', () => {
    const { t } = createComposer({
      locale: 'en',
      messages: {
        en: {
          [ErrorCodes.Code1]: 'computed property name'
        }
      }
    })

    expect(t(ErrorCodes.Code1)).toEqual('computed property name')
  })

  test('reactivity', async () => {
    const EN_HELLO = 'Hello!'
    const JA_HELLO = 'こんにちは!'
    const { t, locale } = createComposer({
      locale: 'en',
      messages: {
        en: { hello: EN_HELLO },
        ja: { hello: JA_HELLO }
      }
    })

    expect(t('hello')).toEqual(EN_HELLO)
    locale.value = 'ja'
    await nextTick()
    expect(t('hello')).toEqual(JA_HELLO)
    locale.value = 'en'
    await nextTick()
    expect(t('hello')).toEqual(EN_HELLO)
    locale.value = 'ja'
    await nextTick()
    expect(t('hello')).toEqual(JA_HELLO)
  })
})
Example #17
Source File: EventMonitoring.ts    From screen-shot with MIT License 4 votes vote down vote up
// 鼠标抬起事件
  private mouseUpEvent = () => {
    // 当前操作的是撤销
    if (this.toolName == "undo") return;
    // 绘制结束
    this.dragging = false;
    this.draggingTrim = false;
    if (
      this.screenShortController.value == null ||
      this.screenShortCanvas == null ||
      this.screenShortImageController == null
    ) {
      return;
    }

    // 工具栏未点击且鼠标未拖动且单击截屏状态为false则复原裁剪框位置
    if (
      !this.data.getToolClickStatus().value &&
      !this.dragFlag &&
      !this.clickCutFullScreen
    ) {
      // 复原裁剪框的坐标
      this.drawGraphPosition.startX = this.drawGraphPrevX;
      this.drawGraphPosition.startY = this.drawGraphPrevY;
      return;
    }
    // 调用者尚未拖拽生成选区
    // 鼠标尚未拖动
    // 单击截取屏幕状态为true
    // 则截取整个屏幕
    const cutBoxPosition = this.data.getCutOutBoxPosition();
    if (
      cutBoxPosition.width === 0 &&
      cutBoxPosition.height === 0 &&
      cutBoxPosition.startX === 0 &&
      cutBoxPosition.startY === 0 &&
      !this.dragFlag &&
      this.clickCutFullScreen
    ) {
      this.getFullScreenStatus = true;
      // 设置裁剪框位置为全屏
      this.tempGraphPosition = drawCutOutBox(
        0,
        0,
        this.screenShortController.value.width - this.borderSize / 2,
        this.screenShortController.value.height - this.borderSize / 2,
        this.screenShortCanvas,
        this.borderSize,
        this.screenShortController.value,
        this.screenShortImageController
      ) as drawCutOutBoxReturnType;
    }
    if (this.data.getToolClickStatus().value) {
      // 保存绘制记录
      this.addHistory();
      return;
    }
    // 保存绘制后的图形位置信息
    this.drawGraphPosition = this.tempGraphPosition;
    // 如果工具栏未点击则保存裁剪框位置
    if (!this.data.getToolClickStatus().value) {
      const { startX, startY, width, height } = this.drawGraphPosition;
      this.data.setCutOutBoxPosition(startX, startY, width, height);
    }
    // 保存边框节点信息
    this.cutOutBoxBorderArr = saveBorderArrInfo(
      this.borderSize,
      this.drawGraphPosition
    );
    if (this.screenShortController.value != null) {
      // 修改鼠标状态为拖动
      this.screenShortController.value.style.cursor = "move";
      // 复原拖动状态
      this.dragFlag = false;
      // 显示截图工具栏
      this.data.setToolStatus(true);
      nextTick().then(() => {
        if (this.toolController.value != null) {
          // 计算截图工具栏位置
          const toolLocation = calculateToolLocation(
            this.drawGraphPosition,
            this.toolController.value?.offsetWidth
          );
          // 当前截取的是全屏,则修改工具栏的位置到截图容器最底部,防止超出
          if (this.getFullScreenStatus) {
            toolLocation.mouseY -= 64;
          }
          // 设置截图工具栏位置
          this.data.setToolInfo(toolLocation.mouseX, toolLocation.mouseY);
          // 状态重置
          this.getFullScreenStatus = false;
        }
      });
    }
  };
Example #18
Source File: checkbox.spec.ts    From formkit with MIT License 4 votes vote down vote up
describe('single checkbox', () => {
  it('can render a single checkbox', () => {
    const wrapper = mount(FormKit, {
      props: {
        type: 'checkbox',
      },
      ...global,
    })
    expect(wrapper.html()).toContain('<input type="checkbox"')
  })

  it('can check a single checkbox with a true value', () => {
    const wrapper = mount(FormKit, {
      props: {
        type: 'checkbox',
        value: true,
      },
      ...global,
    })
    expect(wrapper.find('input').element.checked).toBe(true)
  })

  it('can uncheck a single checkbox with a false value', () => {
    const wrapper = mount(FormKit, {
      props: {
        type: 'checkbox',
        value: false,
      },
      ...global,
    })
    expect(wrapper.find('input').element.checked).toBe(false)
  })

  it('can check/uncheck single checkbox with v-model', async () => {
    const wrapper = mount(
      {
        data() {
          return {
            value: false,
          }
        },
        template: '<FormKit :delay="0" type="checkbox" v-model="value" />',
      },
      {
        ...global,
      }
    )
    const checkbox = wrapper.find('input')
    expect(checkbox.element.checked).toBe(false)
    wrapper.setData({ value: true })
    await nextTick()
    expect(checkbox.element.checked).toBe(true)
    checkbox.element.checked = false
    checkbox.trigger('input')
    await new Promise((r) => setTimeout(r, 5))
    expect(wrapper.vm.value).toBe(false)
  })

  it('can use custom on-value and off-value', async () => {
    const wrapper = mount(
      {
        data() {
          return {
            value: 'foo',
          }
        },
        template:
          '<FormKit :delay="0" type="checkbox" on-value="foo" off-value="bar" v-model="value" />',
      },
      {
        ...global,
      }
    )
    const checkbox = wrapper.find('input')
    expect(checkbox.element.checked).toBe(true)
    wrapper.setData({ value: 'bar' })
    await nextTick()
    expect(checkbox.element.checked).toBe(false)
    checkbox.element.checked = true
    checkbox.trigger('input')
    await new Promise((r) => setTimeout(r, 5))
    expect(wrapper.vm.value).toBe('foo')
  })

  it('can use an object as an on-value and off-value', () => {
    const wrapper = mount(
      {
        template:
          '<FormKit :delay="0" type="checkbox" :on-value="{ a: 123 }" :off-value="{ b: 456 }" :value="{ a: 123 }" />',
      },
      {
        ...global,
      }
    )
    const checkbox = wrapper.find('input')
    expect(checkbox.element.checked).toBe(true)
  })

  it('outputs a data-disabled on the wrapper', () => {
    const wrapper = mount(FormKit, {
      props: {
        type: 'checkbox',
        disabled: true,
        value: false,
      },
      ...global,
    })
    expect(wrapper.find('.formkit-wrapper[data-disabled]').exists()).toBe(true)
  })
})
Example #19
Source File: useNode.spec.ts    From vue3-treeview with MIT License 4 votes vote down vote up
describe("test use node", () => {
    let fakeCmn = null;

    let useTest = null;

    let wrapper = null;

    let props = null;

    let node = null;

    let nodes = null;

    let node2 = null;

    let c1 = null;

    let c2 = null;

    let c3 = null;

    let config = null;

    let storeProps = null;

    let state = null;

    let v = require("vue");

    v.onMounted = jest.fn();

    v.onUnmounted = jest.fn();

    beforeEach(() => {
        node = {
            text: "id1",
            id: "id1",
            children: ["id11", "id12"],
            state: {}
        };
        node2 = {
            text: "id2",
            id: "id2",
            children: ["id21"],
            state:{}
        };
        c1 = {
            id: "id11",
            text: "id11",
            parent: "id1",
            state: {}
        };
        c2 = {
            id: "id12",
            text: "id12",
            parent: "id1",
            state: {}
        };
        c3 = {
            id: "id21",
            text: "id21",
            parent: "id2",
            state: {}
        };
        config = ref({
            roots: ["id1", "id2"]
        });
        nodes = {
            id1: node,
            id11: c1,
            id12: c2,
            id2: node2,
            id21: c3
        };
        storeProps = reactive({
            nodes,
            config
        });
        wrapper = ref(document.createElement("div"));
        state = {
            nodes: ref(nodes),
            config: ref(config),
            focusable: ref(null),
            focusFunc: new Map<string, Function>()
        };
        fakeCmn = {
            node: ref(node),
            config,
            wrapper,
            editing: ref(false),
            disabled: ref(false),
            focused: ref(false),
            root: {
                emit: jest.fn()
            },
            state
        };
        props = {
            depth: ref(0),
            index: ref(0)
        };
        useTest = useNode(fakeCmn, props);
    });

    it("Expect to have id", () => {
        expect(useTest.id.value).toBe("id1");
    });

    it("Expect to have node", () => {
        expect(useTest.hasNode.value).toBeTruthy();
    });

    it("Expect not to be loading", () => {
        expect(useTest.isLoading.value).toBeFalsy();
    });

    it("Expect level to be null", () => {
        expect(useTest.level.value).toBeNull();
    });

    it("Expect node to be closed", () => {
        expect(useTest.opened.value).toBeFalsy();
    });

    it("Expect disabledClass to be null", () => {
        expect(useTest.disabledClass.value).toBeNull();
    });

    it("Expect to have default disabled class", () => {
        fakeCmn.disabled.value = true;
        expect(useTest.disabledClass.value).toBe(defaultDisabledClass);
    });

    it("Expect to have config disalbed class", () => {
        fakeCmn.disabled.value = true;
        fakeCmn.config.value.disabledClass = "test";
        expect(useTest.disabledClass.value).toBe("test");
    });

    it("Expect icons not to be hidden", () => {
        expect(useTest.hideIcons.value).toBeFalsy();
    });

    it("Expect to have icon hidden", () => {
        fakeCmn.node.value.children = [];
        fakeCmn.config.value.roots = ["id1"];
        expect(useTest.hideIcons.value).toBeTruthy();
    });

    it("Expect not to be leaf", () => {
        expect(useTest.isLeaf.value).toBeFalsy();
    });

    it("Expect to be leaf", () => {
        fakeCmn.node.value.children = [];
        expect(useTest.isLeaf.value).toBeTruthy();
    });

    it("Expect to be leaf by config", () => {
        fakeCmn.config.value.leaves = ["id1"];
        fakeCmn.node.value.id = "id1";
        expect(useTest.isLeaf.value).toBeTruthy();
    });

    it("Expect tabindex to be 0", () => {
        expect(useTest.tabIndex.value).toBe(0);
    });

    it("Expect tabindex to be -1", () => {
        props.depth.value = 1;
        expect(useTest.tabIndex.value).toBe(-1);
    });

    it("Expect tabindex to be 0 and to be focused", () => {
        fakeCmn.node.value.id = "test";
        useTest.focus();
        nextTick(() => {
            nextTick(() => {
                expect(useTest.tabIndex.value).toBe(0);
                expect(useTest.focusClass.value).toBe(defaultFocusClass);
            })
        })
    });

    it("Expect to have config focus class", () => {
        fakeCmn.node.value.id = "test";
        fakeCmn.config.value.focusClass = "focusClass";
        useTest.focus();
        nextTick(() => {
            nextTick(() => {
                expect(useTest.focusClass.value).toBe("focusClass");
            })
        });
    });

    it("Expect focusClass to be null", () => {
        expect(useTest.focusClass.value).toBeNull();
    });

    it("Expect to have style", () => {
        expect(useTest.style.value).toMatchObject({
            display: "flex"
        });
    });

    it("Expect not to display level", () => {
        expect(useTest.displayLevel.value).toBeFalsy();
    });

    it("Expect not to display loading", () => {
        expect(useTest.displayLoading.value).toBeFalsy();
    });

    it("Expect to focus node", () => {
        const spy = jest.spyOn(fakeCmn.root, "emit");
        const focusSpy = jest.spyOn(wrapper.value, "focus");
        fakeCmn.node.value.id = "id";
        useTest.focus();
        nextTick(() => {
            nextTick(() => {
                expect(fakeCmn.focused.value).toBeTruthy();
                expect(focusSpy).toBeCalled();
                expect(spy).toBeCalledWith(nodeEvents.focus, fakeCmn.node.value);
            });
        });
    });

    it("Expect to toggle", () => {
        const spy = jest.spyOn(fakeCmn.root, "emit");
        useTest.toggle();
        expect(fakeCmn.node.value.state.opened).toBeTruthy();
        expect(spy).toBeCalledWith(nodeEvents.toggle, fakeCmn.node.value);
        nextTick(() => {
            expect(spy).toBeCalledWith(nodeEvents.opened, fakeCmn.node.value);
            useTest.toggle();
            expect(fakeCmn.node.value.state.opened).toBeFalsy();
            expect(spy).toBeCalledWith(nodeEvents.toggle, fakeCmn.node.value);
            nextTick(() => {
                expect(spy).toBeCalledWith(nodeEvents.closed, fakeCmn.node.value);
            });
        });
    });

    it("Expect to do nothing on right if no keyboard navigation", () => {
        useTest.right();
        expect(useTest.opened.value).toBeFalsy();
    });

    it("Expect to do nothing on left if no keyboard navigation", () => {
        fakeCmn.node.value.state.opened = true;
        useTest.left();
        expect(useTest.opened.value).toBeTruthy();
    });

    it("Expect to open node on right arrow and close on left", () => {
        fakeCmn.config.value.keyboardNavigation = true;
        useTest.right();
        expect(useTest.opened.value).toBeTruthy();
        useTest.left();
        expect(useTest.opened.value).toBeFalsy();
    });

    it("Expect to exec function on down", () => {
        fakeCmn.config.value.keyboardNavigation = true;
        const f = jest.fn();
        fakeCmn.state.focusFunc.set("id2", f);
        useTest.down();
        expect(f).toHaveBeenCalled();
    });

    it("Expect to exec function on up", () => {
        const f = jest.fn();
        fakeCmn.state.focusFunc.set("id1", f);
        fakeCmn.config.value.keyboardNavigation = true;
        fakeCmn.node.value = c1;
        fakeCmn.node.value.state.opened = true;
        useTest.up();
        expect(f).toHaveBeenCalled();
    });

    it("Expect to go to next root on down", () => {
        const next = useTest.nextVisible("id1");
        expect(next).toBe("id2");
    });

    it("Expect to go next child on down", () => {
        fakeCmn.node.value.state.opened = true;
        const next = useTest.nextVisible("id1");
        expect(next).toBe("id11");
    });

    it("Expect to go next root on last node", () => {
        fakeCmn.node.value = c2;
        fakeCmn.node.value.state.opened = true;
        const next = useTest.nextVisible("id12");
        expect(next).toBe("id2");
    });

    it("Expect to go to next children on down", () => {
        fakeCmn.node.value = c1;
        fakeCmn.node.value.state.opened = true;
        const next = useTest.nextVisible("id11");
        expect(next).toBe("id12");
    });

    it("Expect to stay on last child on down", () => {
        fakeCmn.node.value = c3;
        state.nodes.value.id2.state.opened = true;
        const next = useTest.nextVisible("id2");
        expect(next).toBe("id21");
    });

    it("Expect to focus nothing when first root", () => {
        const prev = useTest.prevVisible("id1");
        expect(prev).toBe(null);
    });

    it("Expect to go first root on up", () => {
        fakeCmn.node.value = c1;
        fakeCmn.node.value.state.opened = true;
        const prev = useTest.prevVisible("id11");
        expect(prev).toBe("id1");
    });

    it("Expect to go first root on up", () => {
        fakeCmn.node.value = node2;
        const prev = useTest.prevVisible("id2");
        expect(prev).toBe("id1");
    });

    it("Expect to go first child on up", () => {
        fakeCmn.node.value = c2;
        fakeCmn.node.value.state.opened = true;
        const prev = useTest.prevVisible("id12");
        expect(prev).toBe("id11");
    });
});
Example #20
Source File: FormKit.spec.ts    From formkit with MIT License 4 votes vote down vote up
describe('validation', () => {
  it('shows validation errors on a standard input', () => {
    const wrapper = mount(FormKit, {
      props: {
        validation: 'required|length:5',
        validationVisibility: 'live',
      },
      global: {
        plugins: [[plugin, defaultConfig]],
      },
    })
    expect(wrapper.find('li.formkit-message').exists()).toBe(true)
  })

  it('can use arbitrarily created validation rules and messages', () => {
    const id = token()
    const wrapper = mount(
      {
        template: `
          <FormKit
            id="${id}"
            label="ABC"
            validation="abc"
            :validation-rules="{
              abc: ({ value }) => value === 'abc'
            }"
            :validation-messages="{
              abc: ({ name }) => name + ' should be abc'
            }"
            validation-visibility="live"
            value="foo"
          />
        `,
      },
      {
        global: {
          plugins: [[plugin, defaultConfig]],
        },
      }
    )
    expect(wrapper.html()).toContain(
      `<li class="formkit-message" id="${id}-rule_abc" data-message-type="validation">ABC should be abc</li>`
    )
  })

  it('can override the validation label', () => {
    const id = token()
    const wrapper = mount(
      {
        template: `
          <FormKit
            id="${id}"
            label="foo"
            :validation="[['required']]"
            validation-label="bar"
            validation-visibility="live"
          />
        `,
      },
      {
        global: {
          plugins: [[plugin, defaultConfig]],
        },
      }
    )
    expect(wrapper.html()).toContain(
      `<li class="formkit-message" id="${id}-rule_required" data-message-type="validation">Bar is required.</li>`
    )
  })

  it('can override the validation label strategy', async () => {
    const id = token()
    const wrapper = mount(FormKit, {
      props: {
        id,
        label: 'foo',
        validation: 'required',
        validationVisibility: 'live',
        validationLabel: (node: FormKitNode) => {
          return node.props.attrs['data-foo']
        },
        'data-foo': 'hi there',
      },
      global: {
        plugins: [[plugin, defaultConfig]],
      },
    })
    expect(wrapper.html()).toContain(
      `<li class="formkit-message" id="${id}-rule_required" data-message-type="validation">Hi there is required.</li>`
    )
  })

  it('knows the validation state of a form', async () => {
    const wrapper = mount(
      {
        template: `
        <div>
          <FormKit
            type="group"
            v-slot="{ state: { valid }}"
          >
            <FormKit
              type="text"
              validation="required|email"
              :delay="0"
            />
            <FormKit
              type="text"
              validation="required|length:5"
              :delay="0"
            />
            <button :disabled="!valid">{{ valid }}</button>
          </FormKit>
        </div>
      `,
      },
      {
        global: {
          plugins: [[plugin, defaultConfig]],
        },
      }
    )
    await new Promise((r) => setTimeout(r, 5))
    expect(wrapper.find('button').attributes()).toHaveProperty('disabled')
    // TODO - Remove the .get() here when @vue/test-utils > rc.19
    const [email, name] = wrapper.get('div').findAll('input')
    email.setValue('[email protected]')
    name.setValue('Rockefeller')
    await new Promise((r) => setTimeout(r, 40))
    expect(wrapper.find('button').attributes()).not.toHaveProperty('disabled')
  })

  it('knows the state of validation visibility when set to blur', async () => {
    const id = token()
    const wrapper = mount(FormKit, {
      props: {
        id,
        type: 'text',
        validation: 'required',
      },
      global: {
        plugins: [[plugin, defaultConfig]],
      },
    })
    const node = getNode(id)
    expect(node?.context?.state.validationVisible).toBe(false)
    wrapper.find('input').trigger('blur')
    await nextTick()
    expect(node?.context?.state.validationVisible).toBe(true)
  })

  it('knows the state of validation visibility when set to live', () => {
    const id = token()
    mount(FormKit, {
      props: {
        id,
        type: 'text',
        validation: 'required',
        validationVisibility: 'live',
      },
      global: {
        plugins: [[plugin, defaultConfig]],
      },
    })
    const node = getNode(id)
    expect(node?.context?.state.validationVisible).toBe(true)
  })

  it('knows the state of validation visibility when set to dirty', async () => {
    const id = token()
    const wrapper = mount(FormKit, {
      props: {
        id,
        type: 'text',
        delay: 0,
        validation: 'required',
        validationVisibility: 'dirty',
      },
      global: {
        plugins: [[plugin, defaultConfig]],
      },
    })
    const node = getNode(id)
    expect(node?.context?.state.validationVisible).toBe(false)
    wrapper.find('input').element.value = 'foobar'
    wrapper.find('input').trigger('input')
    await new Promise((r) => setTimeout(r, 10))
    expect(node?.context?.state.validationVisible).toBe(true)
  })

  it('knows the state of validation visibility when set to submit', async () => {
    const id = token()
    const formId = token()
    const wrapper = mount(
      {
        template: `<FormKit type="form" id="${formId}" @submit="() => {}">
          <FormKit
            id="${id}"
            validation="required"
            validation-visibility="submit"
            :delay="0"
          />
        </FormKit>`,
      },
      {
        global: {
          plugins: [[plugin, defaultConfig]],
        },
      }
    )
    const node = getNode(id)
    expect(node?.context?.state.validationVisible).toBe(false)
    wrapper.find('input').element.value = 'foobar'
    wrapper.find('input').trigger('input')
    wrapper.find('input').trigger('blur')
    await new Promise((r) => setTimeout(r, 10))
    expect(node?.context?.state.validationVisible).toBe(false)
    wrapper.find('form').trigger('submit')
    await nextTick()
    expect(node?.context?.state.validationVisible).toBe(true)
  })

  it('can show validation on blur', async () => {
    const wrapper = mount(FormKit, {
      props: {
        validation: 'required',
      },
      global: {
        plugins: [[plugin, defaultConfig]],
      },
    })
    expect(wrapper.find('.formkit-messages').exists()).toBe(false)
    wrapper.find('input').trigger('blur')
    await nextTick()
    expect(wrapper.find('.formkit-messages').exists()).toBe(true)
  })

  it('does not show validation errors till after the input has settled', async () => {
    const id = token()
    const wrapper = mount(FormKit, {
      props: {
        id,
        validation: 'required|length:5',
      },
      global: {
        plugins: [[plugin, defaultConfig]],
      },
    })
    const node = getNode(id)
    wrapper.find('input').setValue('abc')
    wrapper.find('input').trigger('input')
    wrapper.find('input').trigger('blur')
    expect(node?.context!.state.settled).toBe(false)
    await new Promise((r) => setTimeout(r, 10))
    expect(wrapper.find('.formkit-messages').exists()).toBe(false)
    await new Promise((r) => setTimeout(r, 20))
    expect(wrapper.find('.formkit-messages').exists()).toBe(true)
  })

  it('can show validation immediately', async () => {
    const wrapper = mount(FormKit, {
      props: {
        validation: 'required',
        validationVisibility: 'live',
      },
      global: {
        plugins: [[plugin, defaultConfig]],
      },
    })
    expect(wrapper.find('.formkit-messages').exists()).toBe(true)
  })

  it('can show validation when dirty', async () => {
    const wrapper = mount(FormKit, {
      props: {
        validation: 'required|length:10',
        validationVisibility: 'dirty',
      },
      global: {
        plugins: [[plugin, defaultConfig]],
      },
    })
    expect(wrapper.find('.formkit-messages').exists()).toBe(false)
    wrapper.find('input').setValue('foo')
    await new Promise((r) => setTimeout(r, 35))
    expect(wrapper.find('.formkit-messages').exists()).toBe(true)
  })

  it('can alter a validation message', async () => {
    const wrapper = mount(FormKit, {
      props: {
        value: 'formkit',
        validation: 'length:10',
        validationVisibility: 'live',
        validationMessages: {
          length: 'Too short',
        },
      },
      global: {
        plugins: [[plugin, defaultConfig]],
      },
    })
    expect(wrapper.html()).toContain('Too short')
  })

  it('changes state.rules when the validation prop changes', async () => {
    const id = token()
    const wrapper = mount(FormKit, {
      props: {
        id,
        validation: 'required|length:10',
      },
      global: {
        plugins: [[plugin, defaultConfig]],
      },
    })
    const node = getNode(id)
    expect(node?.context?.state.rules).toBe(true)
    wrapper.setProps({ validation: '' })
    await nextTick()
    expect(node?.context?.state.rules).toBe(false)
  })

  it('is complete when the input has validation rules that are passing', async () => {
    const id = token()
    const wrapper = mount(FormKit, {
      props: {
        id,
        delay: 0,
        validation: 'required|length:10',
      },
      global: {
        plugins: [[plugin, defaultConfig]],
      },
    })
    const node = getNode(id)
    expect(node?.context?.state.complete).toBe(false)
    wrapper.find('input').element.value = 'its not the end yet'
    wrapper.find('input').trigger('input')
    await new Promise((r) => setTimeout(r, 20))
    expect(node?.context?.state.complete).toBe(true)
  })

  it('is complete when it has no validation rules is not dirty', async () => {
    const id = token()
    const wrapper = mount(FormKit, {
      props: {
        id,
        delay: 0,
      },
      global: {
        plugins: [[plugin, defaultConfig]],
      },
    })
    const node = getNode(id)
    expect(node?.context?.state.complete).toBe(false)
    wrapper.find('input').element.value = 'yes'
    wrapper.find('input').trigger('input')
    await new Promise((r) => setTimeout(r, 10))
    expect(node?.context?.state.complete).toBe(true)
  })

  it('is not complete when the input has explicit error messages', async () => {
    const id = token()
    const wrapper = mount(FormKit, {
      props: {
        id,
        delay: 0,
        validation: 'required',
        errors: ['This is an error'],
      },
      global: {
        plugins: [[plugin, defaultConfig]],
      },
    })
    const node = getNode(id)
    expect(node?.context?.state.complete).toBe(false)
    wrapper.find('input').element.value = 'yes'
    wrapper.find('input').trigger('input')
    await new Promise((r) => setTimeout(r, 10))
    expect(node?.context?.state.complete).toBe(false)
  })

  it('can dynamically change the validation-visibility', async () => {
    const wrapper = mount(
      {
        data() {
          return {
            visibility: 'blur',
          }
        },
        template: `<FormKit
        validation="required"
        :validation-visibility="visibility"
      />`,
      },
      {
        global: {
          plugins: [[plugin, defaultConfig]],
        },
      }
    )
    expect(wrapper.find('.formkit-messages').exists()).toBe(false)
    wrapper.vm.visibility = 'live'
    await new Promise((r) => setTimeout(r, 20))
    expect(wrapper.find('.formkit-messages').exists()).toBe(true)
  })

  it('does automatically set the validation state to dirty', async () => {
    const firstNode = token()
    mount(
      {
        data() {
          return {
            visibility: 'blur',
          }
        },
        template: `
        <FormKit type="form">
          <FormKit id="${firstNode}"/>
          <FormKit />
        </FormKit>`,
      },
      {
        global: {
          plugins: [[plugin, defaultConfig]],
        },
      }
    )
    await nextTick()
    expect(getNode(firstNode)?.store.dirty).toBe(undefined)
  })

  it('avoids recursive updates when using state.valid and array computed array rules (#255)', async () => {
    const warning = jest.fn()
    const mock = jest.spyOn(global.console, 'warn').mockImplementation(warning)
    mount(
      {
        setup() {
          const list = [{ url: 'a' }, { url: 'b' }]
          return { list }
        },
        template: `
      <FormKit
        type="form"
        #default="{ state: { valid } }"
      >
        {{ valid }}
        <FormKit
          :validation="[
            ['not', ...list.map(e => e.url)]
          ]"
        />
      </FormKit>
      `,
      },
      {
        global: {
          plugins: [[plugin, defaultConfig]],
        },
      }
    )
    await new Promise((r) => setTimeout(r, 500))
    mock.mockRestore()
    expect(warning).not.toHaveBeenCalled()
  })

  it('can use reactive values in validation rules defined with array syntax', async () => {
    const wrapper = mount(
      {
        setup() {
          const list = ref(['a', 'b', 'c'])
          const addD = () => list.value.push('d')
          return { list, addD }
        },
        template: `
      <FormKit
        type="form"
        #default="{ state: { valid } }"
      >
        <FormKit
          value="d"
          :validation="[['is', ...list]]"
        />
        <span class="validity" @click="addD">{{ valid }}</span>
      </FormKit>
      `,
      },
      {
        global: {
          plugins: [[plugin, defaultConfig]],
        },
      }
    )
    await nextTick()
    expect(wrapper.find('.validity').text()).toBe('false')
    wrapper.find('.validity').trigger('click')
    await new Promise((r) => setTimeout(r, 20))
    expect(wrapper.find('.validity').text()).toBe('true')
  })
})
Example #21
Source File: useInput.spec.ts    From vue3-treeview with MIT License 4 votes vote down vote up
describe("test useInput", () => {
    let fakeCmn = null;

    let useTest = null;

    const wrapper = ref(document.createElement("div"));

    beforeEach(() => {
        fakeCmn = {
            node: ref({
                id: "id",
                text: "test"
            }),
            config: ref({
                editableClass: null,
                editing: "",
                keyboardNavigation: true
            }),
            wrapper,
            editable: ref(false),
            editing: ref(false),
            disabled: ref(false),
            blur: jest.fn(),
            root: {
                emit: jest.fn()
            }
        };
            
        useTest = useInput(fakeCmn);
    });

    it("Expect to have test", () => {
        expect(useTest.text.value).toBe("test");
    });

    it("Expect to set text", () => {
        useTest.text.value = "newVal";
        expect(fakeCmn.node.value.text).toBe("newVal");
    });

    it("Expect to have no class", () => {
        expect(useTest.editableClass.value).toBeNull();
    });

    it("Expect to have default editableClass", () => {
        fakeCmn.editable.value = true;
        expect(useTest.editableClass.value).toBe("editable");
    });

    it("Expect to have editable class", () => {
        fakeCmn.editable.value = true;
        fakeCmn.config.value.editableClass = "editableClass";
        expect(useTest.editableClass.value).toBe("editableClass");
    });

    it("Epect to focus input element", async () => {
        const input = document.createElement("input");
        const spy = jest.spyOn(input, "focus");
        useTest.input.value = input;
        fakeCmn.editing.value = true;
        nextTick(() => nextTick(() => {
            expect(spy).toBeCalled();
        }));
    });

    it("Expect to focus input", () => {
        const spy = jest.spyOn(fakeCmn.root, "emit");
        fakeCmn.editable.value = true;
        useTest.focusInput();
        expect(fakeCmn.config.value.editing).toBe("id");
        expect(spy).toBeCalledWith(nodeEvents.edit, fakeCmn.node.value);
    });

    it("Expect to focus wrapper on esc", () => {
        const spy = jest.spyOn(fakeCmn, "blur");
        const spy1 = jest.spyOn(wrapper.value, "focus");
        fakeCmn.editable.value = true;
        const fakeEvent = { event: "test" }; 
        useTest.esc(fakeEvent);
        expect(spy).toBeCalledWith(fakeEvent);
        expect(spy1).toBeCalled();
    });

    it("Expect to focus input on press enter", () => {
        const spy = jest.spyOn(fakeCmn.root, "emit");
        fakeCmn.editable.value = true;
        useTest.enter();
        expect(fakeCmn.config.value.editing).toBe("id");
        expect(spy).toBeCalledWith(nodeEvents.edit, fakeCmn.node.value);
    });
});
Example #22
Source File: FormKitSchema.spec.ts    From formkit with MIT License 4 votes vote down vote up
describe('parsing dom elements', () => {
  it('can render a single simple dom element', () => {
    const wrapper = mount(FormKitSchema, {
      props: {
        schema: [
          {
            $el: 'h1',
            children: 'Hello world',
            attrs: {
              'data-foo': 'bar',
            },
          },
        ],
      },
    })
    expect(wrapper.html()).toBe('<h1 data-foo="bar">Hello world</h1>')
  })

  it('can render a multiple children', () => {
    const wrapper = mount(FormKitSchema, {
      props: {
        schema: [
          {
            $el: 'h1',
            children: [
              {
                $el: 'em',
                children: 'Hello',
              },
              ' world',
            ],
            attrs: {
              'data-foo': 'bar',
            },
          },
        ],
      },
    })
    expect(wrapper.html()).toBe('<h1 data-foo="bar"><em>Hello</em> world</h1>')
  })

  it('can update data by replacing prop', async () => {
    const wrapper = mount(FormKitSchema, {
      props: {
        data: { a: { b: 'c' } },
        schema: [{ $el: 'h1', children: '$a.b' }, { $el: 'input' }],
      },
    })
    expect(wrapper.html()).toContain('c')
    wrapper.find('input').setValue('hello world')
    wrapper.setProps({ data: { a: { b: 'f' } } })
    await nextTick()
    expect(wrapper.html()).toContain('f')
    expect(wrapper.find('input').element.value).toBe('hello world')
  })

  it('can update new data by changing reactive prop', async () => {
    const data = reactive({ a: { b: 'c' } })
    const wrapper = mount(FormKitSchema, {
      props: {
        data,
        schema: [{ $el: 'h1', children: '$a.b' }],
      },
    })
    expect(wrapper.html()).toContain('c')
    data.a.b = 'f'
    await nextTick()
    expect(wrapper.html()).toContain('f')
  })

  it('can update new data by changing sub-object prop', async () => {
    const data = reactive({ a: { b: 'c' } })
    const wrapper = mount(FormKitSchema, {
      props: {
        data,
        schema: [{ $el: 'h1', children: '$a.b' }],
      },
    })
    data.a = { b: 'g' }
    await nextTick()
    expect(wrapper.html()).toContain('g')
  })

  it('can remove a node with the "if" property', async () => {
    const data = reactive({ a: { b: 'c' } })
    const wrapper = mount(FormKitSchema, {
      props: {
        data,
        schema: [{ $el: 'h1', children: '$a.b', if: "$a.b === 'c'" }],
      },
    })
    expect(wrapper.html()).toBe('<h1>c</h1>')
    data.a = { b: 'g' }
    await nextTick()
    expect(wrapper.html()).toBe('')
    data.a.b = 'c'
    await nextTick()
    expect(wrapper.html()).toBe('<h1>c</h1>')
  })

  it('can render different children with if/then/else at root', async () => {
    const data = reactive({ value: 100 })
    const wrapper = mount(FormKitSchema, {
      props: {
        data,
        schema: {
          if: '$value >= 100',
          then: [{ $el: 'h1', children: ['$', '$value'] }],
          else: {
            if: '$value > 50',
            then: [{ $el: 'h2', children: ['$', '$value'] }],
            else: [{ $el: 'h3', children: 'You need a job!' }],
          },
        },
      },
    })
    expect(wrapper.html()).toBe('<h1>$100</h1>')
    data.value = 75
    await nextTick()
    expect(wrapper.html()).toBe('<h2>$75</h2>')
    data.value = 50
    await nextTick()
    expect(wrapper.html()).toBe('<h3>You need a job!</h3>')
  })

  it('can render different sibling children with if/then/else at root', async () => {
    const data = reactive({ value: 100 })
    const wrapper = mount(FormKitSchema, {
      props: {
        data,
        schema: [
          {
            $el: 'label',
            children: 'What is your salary?',
          },
          {
            if: '$value >= 100',
            then: [{ $el: 'h1', children: ['$', '$value'] }],
            else: {
              if: '$value > 20',
              then: [{ $el: 'h2', children: ['$', '$value'] }],
              else: [{ $el: 'h3', children: 'You need a new job!' }],
            },
          },
          {
            $el: 'footer',
            children: '© All rights reserved.',
          },
        ],
      },
    })
    expect(wrapper.html()).toBe(
      `<label>What is your salary?</label>
<h1>$100</h1>
<footer>© All rights reserved.</footer>`
    )
    data.value = 75
    await nextTick()
    expect(wrapper.html()).toContain('<h2>$75</h2>')
    data.value = 20
    await nextTick()
    expect(wrapper.html()).toContain('<h3>You need a new job!</h3>')
  })

  it('can render attributes', () => {
    const wrapper = mount(FormKitSchema, {
      props: {
        schema: [
          {
            $el: 'button',
            attrs: {
              type: 'submit',
            },
          },
        ],
      },
    })
    expect(wrapper.html()).toBe('<button type="submit"></button>')
  })

  it('can render dynamic attribute values', async () => {
    const data = reactive({ type: 'foo' })
    const wrapper = mount(FormKitSchema, {
      props: {
        data,
        schema: [
          {
            $el: 'button',
            attrs: {
              'data-type': '$type',
            },
          },
        ],
      },
    })
    expect(wrapper.html()).toBe('<button data-type="foo"></button>')
    data.type = 'bar'
    await flushPromises()
    expect(wrapper.html()).toBe('<button data-type="bar"></button>')
  })

  it('can render dynamic attribute values at depth', async () => {
    const data = reactive({ color: 'red' })
    const wrapper = mount(FormKitSchema, {
      props: {
        data,
        schema: [
          {
            $el: 'button',
            attrs: {
              style: {
                color: '$color',
              },
            },
          },
        ],
      },
    })
    expect(wrapper.html()).toBe('<button style="color: red;"></button>')
    data.color = 'green'
    await flushPromises()
    expect(wrapper.html()).toBe('<button style="color: green;"></button>')
  })

  it('can perform string concatenation in dynamic attributes', async () => {
    const data = reactive({ size: 10 })
    const wrapper = mount(FormKitSchema, {
      props: {
        data,
        schema: [
          {
            $el: 'button',
            attrs: {
              style: {
                fontSize: '$size + 1 + em',
              },
            },
          },
        ],
      },
    })
    expect(wrapper.html()).toBe('<button style="font-size: 11em;"></button>')
    data.size = 5
    await nextTick()
    expect(wrapper.html()).toBe('<button style="font-size: 6em;"></button>')
  })

  it('can render conditional set of attributes', async () => {
    const data = reactive({ status: 'warning' })
    const wrapper = mount(FormKitSchema, {
      props: {
        data,
        schema: [
          {
            $el: 'div',
            attrs: {
              if: "$status === 'warning'",
              then: {
                'data-status': '$status',
                style: {
                  color: 'red',
                },
              },
              else: {
                if: '$status === information',
                then: {
                  'data-info': 'true',
                },
                else: {
                  'data-status': '$status',
                },
              },
            },
          },
        ],
      },
    })
    expect(wrapper.html()).toBe(
      '<div data-status="warning" style="color: red;"></div>'
    )
    data.status = 'information'
    await nextTick()
    expect(wrapper.html()).toBe('<div data-info="true"></div>')
    data.status = 'error'
    await nextTick()
    expect(wrapper.html()).toBe('<div data-status="error"></div>')
  })

  it('can render a single complex conditional attribute', async () => {
    const data = reactive({ size: 1 })
    const wrapper = mount(FormKitSchema, {
      props: {
        data,
        schema: [
          {
            $el: 'button',
            attrs: {
              'data-size': {
                if: '$size < 5',
                then: 'extra-small',
                else: {
                  if: '$size >= 5 && $size < 10',
                  then: 'medium',
                  else: {
                    if: '$size >= 10 && $size < 20',
                    then: 'large',
                    else: 'extra-large',
                  },
                },
              },
            },
          },
        ],
      },
    })
    expect(wrapper.html()).toBe('<button data-size="extra-small"></button>')
    data.size = 5
    await nextTick()
    expect(wrapper.html()).toBe('<button data-size="medium"></button>')
    data.size = 10
    await nextTick()
    expect(wrapper.html()).toBe('<button data-size="large"></button>')
    data.size = 50
    await nextTick()
    expect(wrapper.html()).toBe('<button data-size="extra-large"></button>')
  })

  it('can render an conditional attribute with compiled values', async () => {
    const data = reactive({ status: 'warning' })
    const wrapper = mount(FormKitSchema, {
      props: {
        data,
        schema: [
          {
            $el: 'div',
            attrs: {
              id: {
                if: '$status === warning',
                then: '$status',
                else: 'no-warning',
              },
            },
          },
        ],
      },
    })
    expect(wrapper.html()).toBe('<div id="warning"></div>')
    data.status = 'ok'
    await nextTick()
    expect(wrapper.html()).toBe('<div id="no-warning"></div>')
  })

  it('can render a list of items', () => {
    const wrapper = mount(FormKitSchema, {
      props: {
        schema: [
          {
            $el: 'ul',
            children: [
              {
                $el: 'li',
                for: ['value', 'key', ['a', 'b', 'c']],
                children: [
                  {
                    $el: 'span',
                    for: ['price', 2],
                    children: ['$key', ':', '$value', ', ', '$price'],
                  },
                ],
              },
            ],
          },
        ],
      },
    })
    expect(wrapper.html()).toBe(
      `<ul>
  <li><span>0:a, 0</span><span>0:a, 1</span></li>
  <li><span>1:b, 0</span><span>1:b, 1</span></li>
  <li><span>2:c, 0</span><span>2:c, 1</span></li>
</ul>`
    )
  })

  it('reacts to iteration data changes', async () => {
    const data = reactive({
      alphabet: ['a', 'b', 'c'],
    })
    const wrapper = mount(FormKitSchema, {
      props: {
        data,
        schema: [
          {
            $el: 'span',
            for: ['value', '$alphabet'],
            children: '$value',
          },
        ],
      },
    })
    expect(wrapper.html()).toBe(`<span>a</span>
<span>b</span>
<span>c</span>`)
    data.alphabet[1] = 'd'
    await nextTick()
    expect(wrapper.html()).toBe(`<span>a</span>
<span>d</span>
<span>c</span>`)
  })

  it('can access nested iteration data', async () => {
    const data = reactive({
      accounts: [{ user: { name: 'bob' } }, { user: { name: 'ted' } }],
    })
    const wrapper = mount(FormKitSchema, {
      props: {
        data,
        schema: [
          {
            $el: 'span',
            for: ['account', '$accounts'],
            children: '$account.user.name',
          },
        ],
      },
    })
    expect(wrapper.html()).toBe(`<span>bob</span>
<span>ted</span>`)
    data.accounts.unshift({ user: { name: 'fred' } })
    await nextTick()
    expect(wrapper.html()).toBe(`<span>fred</span>
<span>bob</span>
<span>ted</span>`)
  })

  it('can shadow nested loop scoped variables', async () => {
    const data = reactive({
      users: ['fred', 'ted'],
      foods: ['ice cream', 'pizza'],
    })
    const wrapper = mount(FormKitSchema, {
      props: {
        data,
        schema: [
          {
            $el: 'div',
            for: ['foobar', '$users'],
            children: [
              {
                $el: 'h2',
                children: '$foobar',
              },
              {
                $el: 'ul',
                children: [
                  {
                    $el: 'li',
                    for: ['foobar', '$foods'],
                    children: '$foobar',
                  },
                ],
              },
            ],
          },
        ],
      },
    })
    expect(wrapper.html()).toBe(
      `<div>
  <h2>fred</h2>
  <ul>
    <li>ice cream</li>
    <li>pizza</li>
  </ul>
</div>
<div>
  <h2>ted</h2>
  <ul>
    <li>ice cream</li>
    <li>pizza</li>
  </ul>
</div>`
    )
  })

  it('can render slots as the only child', () => {
    const wrapper = mount(FormKitSchema, {
      slots: {
        default: 'click me',
      },
      props: {
        schema: [
          {
            $el: 'button',
            children: '$slots.default',
          },
        ],
      },
    })
    expect(wrapper.html()).toBe('<button>click me</button>')
  })

  it('can render slots as one of many children', () => {
    const wrapper = mount(FormKitSchema, {
      slots: {
        default: 'click me',
      },
      props: {
        schema: [
          {
            $el: 'button',
            children: ['$slots.default', ' to buy'],
          },
        ],
      },
    })
    expect(wrapper.html()).toBe('<button>click me to buy</button>')
  })

  it('can render the loop data inside the default slot', () => {
    const wrapper = mount(FormKitSchema, {
      props: {
        data: {
          items: ['a', 'b', 'c'],
        },
        schema: [
          {
            $cmp: 'FormKit',
            for: ['item', 'index', '$items'],
            props: {
              type: 'group',
            },
            children: '$index + ": " + $item',
          },
        ],
      },
      global: {
        plugins: [[plugin, defaultConfig]],
      },
    })
    expect(wrapper.text()).toBe('0: a1: b2: c')
  })

  it('can render the loop data inside the default slot when nested in an $el', async () => {
    const colors = ref(['red', 'green', 'blue'])
    const items = ref(['a', 'b', 'c'])
    const wrapper = mount(FormKitSchema, {
      props: {
        data: {
          colors,
          items,
        },
        schema: [
          {
            $el: 'div',
            for: ['color', '$colors'],
            children: [
              {
                $el: 'span',
                for: ['item', 'index', '$items'],
                children: [
                  {
                    $cmp: 'FormKit',
                    props: {
                      type: 'group',
                    },
                    children: '$color + ": " + $index + " : " + $item + "|"',
                  },
                ],
              },
            ],
          },
        ],
      },
      global: {
        plugins: [[plugin, defaultConfig]],
      },
    })
    expect(wrapper.text()).toBe(
      'red: 0 : a|red: 1 : b|red: 2 : c|green: 0 : a|green: 1 : b|green: 2 : c|blue: 0 : a|blue: 1 : b|blue: 2 : c|'
    )
    colors.value.shift()
    await nextTick()
    expect(wrapper.text()).toBe(
      'green: 0 : a|green: 1 : b|green: 2 : c|blue: 0 : a|blue: 1 : b|blue: 2 : c|'
    )
    items.value.push('d')
    await nextTick()
    expect(wrapper.text()).toBe(
      'green: 0 : a|green: 1 : b|green: 2 : c|green: 3 : d|blue: 0 : a|blue: 1 : b|blue: 2 : c|blue: 3 : d|'
    )
  })

  it('can render iteration data inside the slot of a conditional component', async () => {
    const colors = ref(['red', 'green', 'blue'])
    const wrapper = mount(FormKitSchema, {
      props: {
        data: {
          colors,
        },
        schema: [
          {
            $el: 'div',
            for: ['color', '$colors'],
            children: {
              if: '$color === "red"',
              then: 'RED!',
              else: {
                $cmp: 'FormKit',
                props: {
                  type: 'group',
                },
                children: '$color + "|"',
              },
            },
          },
        ],
      },
      global: {
        plugins: [[plugin, defaultConfig]],
      },
    })
    expect(wrapper.text()).toBe('RED!green|blue|')
  })

  it('can render iteration data in an element that is in the slot of a conditional component', async () => {
    const letters = ref(['a', 'b', 'c'])
    const wrapper = mount(FormKitSchema, {
      props: {
        data: {
          letters,
        },
        schema: [
          {
            $el: 'div',
            for: ['letter', 'index', '$letters'],
            attrs: {
              class: 'repeated',
            },
            children: [
              {
                if: '$letter !== "b"',
                then: {
                  $el: 'h2',
                  children: 'Not B',
                },
                else: {
                  $cmp: 'FormKit',
                  props: {
                    type: 'group',
                  },
                  children: [
                    {
                      $el: 'h1',
                      children: '$letter',
                    },
                  ],
                },
              },
            ],
          },
        ],
      },
      global: {
        plugins: [[plugin, defaultConfig]],
      },
    })
    expect(wrapper.text()).toBe('Not BbNot B')
  })

  it('can render functional data reactively', async () => {
    const data = reactive({
      price: 10,
      quantity: 2,
      cost: (p: number, q: number) => p * q,
    })
    const wrapper = mount(FormKitSchema, {
      props: {
        data,
        schema: [
          {
            $el: 'button',
            children: ['Total $', '$cost($price, $quantity + 2) + 1'],
          },
        ],
      },
    })
    expect(wrapper.html()).toBe('<button>Total $41</button>')
    data.price = 11
    await nextTick()
    expect(wrapper.html()).toBe('<button>Total $45</button>')
  })

  it('can bind arbitrary objects as attrs', async () => {
    const data = reactive({
      details: {
        type: 'number',
        name: 'foobar',
        min: '20',
        step: '1',
      },
    })
    const wrapper = mount(FormKitSchema, {
      props: {
        data,
        schema: [
          {
            $el: 'input',
            bind: '$details',
          },
        ],
      },
    })
    expect(wrapper.html()).toBe(
      '<input type="number" name="foobar" min="20" step="1">'
    )
    data.details.name = 'barfoo'
    await nextTick()
    expect(wrapper.html()).toBe(
      '<input type="number" name="barfoo" min="20" step="1">'
    )
  })

  it('can bind arbitrary objects as attrs but attrs override them', async () => {
    const data = reactive({
      details: {
        type: 'number',
        name: 'foobar',
        min: '20',
        step: '1',
      },
    })
    const wrapper = mount(FormKitSchema, {
      props: {
        data,
        schema: [
          {
            $el: 'input',
            bind: '$details',
            attrs: {
              type: 'text',
            },
          },
        ],
      },
    })
    expect(wrapper.html()).toBe(
      '<input name="foobar" min="20" step="1" type="text">'
    )
    data.details.type = 'jimbo'
    await nextTick()
    expect(wrapper.html()).toBe(
      '<input name="foobar" min="20" step="1" type="text">'
    )
  })

  it('can "unwrap" a schema node by having a null value.', () => {
    const wrapper = mount(FormKitSchema, {
      props: {
        schema: [
          {
            $el: null,
            children: [
              {
                $el: 'label',
                children: [
                  {
                    $el: 'input',
                    attrs: {
                      type: 'checkbox',
                    },
                  },
                ],
              },
            ],
          },
        ],
      },
    })
    expect(wrapper.html()).toBe('<label><input type="checkbox"></label>')
  })
})
Example #23
Source File: useNode.ts    From vue3-treeview with MIT License 4 votes vote down vote up
export function useNode(cmn: IUseCommon, props: INodeProps): IUseNode { 
    const state = cmn.state;
    const node = cmn.node;
    const config = cmn.config;
    const wrapper = cmn.wrapper;
    const editing = cmn.editing;
    const level = ref(null);
    const depth = ref(props.depth);
    const index = ref(props.index);
    const id = computed(() => {
        return hasNode.value && node.value.id;
    });

    const hasNode = computed(() => {
        return !isNil(node);
    });

    const hasState = computed(() => {
        return hasNode.value && !isNil(node.value.state);
    });

    const roots = computed(() => {
        return config.value.roots || [];
    });

    const children = computed(() => {
        return isNil(node.value.children) ? [] : node.value.children;
    });

    const nbChildren = computed(() => {
        return children.value.length;
    });

    const hasChildren = computed(() => {
        return nbChildren.value > 0;
    });

    const opened = computed(() => {
        return hasState.value && node.value.state.opened || false;
    });

    const isLoading = computed(() => {
        return hasState.value && node.value.state.isLoading || false;
    });

    const displayLoading = computed(() => {
        return isLoading.value && !hasChildren.value && opened.value; 
    });

    const displayLevel = computed(() => {
        return !isLoading.value && hasChildren.value && opened.value;
    });

    const style = computed(() => {
        return {
            display: "flex"
        };
    });

    const disabledClass = computed(() => {
        if (!cmn.disabled.value) {
            return null;
        }

        return config.value.disabledClass ? config.value.disabledClass : defaultDisabledClass;
    });

    const hideIcons = computed(() => {
        for (const id of roots.value) {
            const node = state.nodes.value[id];

            if (node.children && node.children.length > 0) {
                return false;
            }
        }

        return true;
    });

    const isLeaf = computed(() => {
        if (isArray(config.value.leaves)) {
            const arr: string[] = config.value.leaves;
            const idx = arr.indexOf(id.value);
            return Number.isFinite(idx) && idx >= 0;
        }

        return isNil(node.value.children) || !isArray(node.value.children) || node.value.children.length === 0;
    });

    const isFocusable = computed(() => {
        return state.focusable.value === node.value.id;
    });

    const tabIndex = computed(() => {
        if (depth.value === 0 && index.value === 0 && isNil(state.focusable.value)) {
            return 0; 
        }

        return isFocusable.value ? 0 : -1;
    });

    const focusClass = computed(() =>  {
        if (!cmn.focused.value) {
            return null;
        }

        return config.value.focusClass ? config.value.focusClass : defaultFocusClass;
    });

    watch(opened, (nv: boolean) => {
        nv ? cmn.root.emit(nodeEvents.opened, node.value) : cmn.root.emit(nodeEvents.closed, node.value);
    });

    const focus = (() => {
        state.focusable.value = node.value.id;

        nextTick(() => {
            wrapper.value.focus();
            cmn.focused.value = true;
            cmn.root.emit(nodeEvents.focus, node.value);
        });
    });

    const toggle = (() => {
        node.value.state.opened = !node.value.state.opened;
        cmn.root.emit(nodeEvents.toggle, node.value);
    });

    const right = (() => {
        if (!editing.value && config.value.keyboardNavigation) {
            node.value.state.opened = true;
        }
    });

    const left = (() => {
        if (!editing.value && config.value.keyboardNavigation) {
            node.value.state.opened = false;
        }
    });

    const move = ((getFunc: (s: string) => string) => {
        const id = getFunc(node.value.id);

        if (!isNil(id) && config.value.keyboardNavigation) {
            const f = state.focusFunc.get(id);

            if(f) {
                f();
            }
        }
    });

    const up = () => move(prevVisible);

    const prev = ((id: string): string => { 
        const n = state.nodes.value[id];

        if (n.children && n.children.length > 0) {
            const idx = n.children.indexOf(node.value.id);
            const prev = n.children[idx - 1];
            
            if (!isNil(prev)) {
                return lastChild(prev);
            }
        } 

        return n.id;
    });

    const prevVisible = ((id: string) => {
        const n = state.nodes.value[id];  
        const p = state.nodes.value[n.parent];

        if (!p) {
            const idx = roots.value.indexOf(id);
            return lastChild(roots.value[idx - 1]) || null;            
        }

        return prev(p.id);
    });

    const lastChild = ((id: string): string => {
        const n = state.nodes.value[id];

        if (!n) {
            return null;
        }  

        if (n.children && n.children.length > 0 && n.state.opened) {
            const last = n.children[n.children.length - 1];

            if (!isNil(last)) {
                return lastChild(last);
            }
        }

        return n.id;
    });

    const down = () => move(nextVisible);

    const nextRoot = ((id: string) => {
        const idx = roots.value.indexOf(id);
        return roots.value[idx + 1] || null;
    });

    const next = ((p: INode, id: string): string => {
        const idx = p.children.indexOf(id);

        if (p.children[idx + 1]) {
            return p.children[idx + 1];
        }

        if (p.parent) {
            return next(state.nodes.value[p.parent], p.id);
        }

        return nextRoot(p.id);
    });

    const nextVisible = ((id: string): string => {
        const n = state.nodes.value[id];

        if (n.children && n.children.length > 0 && n.state.opened) {
            return n.children[0];
        }

        const p = state.nodes.value[n.parent];

        return p ? next(p, id) : nextRoot(id);
    });

    onMounted(() => {
        state.focusFunc.set(node.value.id, focus);
    });

    onUnmounted(() => {
        state.focusFunc.delete(node.value.id);
    });

    return {
        id,
        level,
        style,
        opened,
        hasNode,
        hideIcons,
        hasChildren,
        tabIndex,
        focusClass,
        disabledClass,
        isLeaf,
        isLoading,
        displayLoading,
        displayLevel,
        right,
        left,
        up,
        down,
        toggle,
        focus,
        prevVisible,
        nextVisible
    };
}
Example #24
Source File: form.spec.ts    From formkit with MIT License 4 votes vote down vote up
describe('value propagation', () => {
  it('can set the state of checkboxes from a v-model on the form', async () => {
    const wrapper = mount(
      {
        data() {
          return {
            options: ['a', 'b', 'c'],
            values: {
              boxes: [] as string[],
            },
          }
        },
        template: `<FormKit type="form" v-model="values">
        <FormKit type="checkbox" name="boxes" :options="options" />
      </FormKit>`,
      },
      {
        ...global,
      }
    )
    await new Promise((r) => setTimeout(r, 5))
    // TODO - Remove the .get() here when @vue/test-utils > rc.19
    const inputs = wrapper.get('form').findAll('input')
    expect(inputs.length).toBe(3)
    expect(
      inputs.map((input) => (input.element as HTMLInputElement).checked)
    ).toStrictEqual([false, false, false])
    wrapper.vm.values.boxes = ['a', 'b', 'c']
    await new Promise((r) => setTimeout(r, 50))
    expect(
      inputs.map((input) => (input.element as HTMLInputElement).checked)
    ).toStrictEqual([true, true, true])
    expect(wrapper.vm.values.boxes).toStrictEqual(['a', 'b', 'c'])
  })

  it('can set the state of checkboxes from a v-model using vue ref object', async () => {
    const wrapper = mount(
      {
        setup() {
          const options = ['a', 'b', 'c']
          const values = ref({ boxes: [] as string[] })
          const changeValues = () => {
            values.value.boxes = ['a', 'c']
          }
          return { options, values, changeValues }
        },
        template: `<FormKit type="form" v-model="values">
        <FormKit type="checkbox" name="boxes" :options="options" />
        <button type="button" @click="changeValues">Change</button>
      </FormKit>`,
      },
      {
        ...global,
      }
    )
    // TODO - Remove the .get() here when @vue/test-utils > rc.19
    const inputs = wrapper.get('form').findAll('input[type="checkbox"]')
    expect(inputs.length).toBe(3)
    expect(
      inputs.map((input) => (input.element as HTMLInputElement).checked)
    ).toStrictEqual([false, false, false])
    wrapper.find('button[type="button"').trigger('click')
    await nextTick()
    expect(
      inputs.map((input) => (input.element as HTMLInputElement).checked)
    ).toStrictEqual([true, false, true])
  })

  it('can set the state of text input from a v-model using vue reactive object', async () => {
    const warn = jest.spyOn(console, 'warn').mockImplementation(() => {})
    const wrapper = mount(
      {
        setup() {
          const values = reactive<{ form: Record<string, any> }>({
            form: { abc: '123' },
          })
          const changeValues = async () => {
            values.form.foo = 'bar bar'
          }
          return { values, changeValues }
        },
        template: `<FormKit type="form" v-model="values.form">
        <FormKit type="text" name="foo" value="foo" />
        <button type="button" @click="changeValues">Change</button>
      </FormKit>`,
      },
      {
        ...global,
      }
    )
    // TODO - Remove the .get() here when @vue/test-utils > rc.19
    const inputs = wrapper.get('form').findAll('input[type="text"]')
    expect(inputs.length).toBe(1)
    expect(wrapper.find('input').element.value).toEqual('foo')
    // await new Promise((r) => setTimeout(r, 20))
    wrapper.find('button[type="button"]').trigger('click')
    await new Promise((r) => setTimeout(r, 50))
    expect(wrapper.find('input').element.value).toStrictEqual('bar bar')
    warn.mockRestore()
  })
})
Example #25
Source File: wrapper.ts    From vugel with Apache License 2.0 4 votes vote down vote up
Vugel = defineComponent({
    props: {
        settings: { type: Object },
        position: { type: String, default: "relative" },
    },
    setup(props, setupContext) {
        const elRef: Ref<HTMLCanvasElement | undefined> = ref();

        const maxWidth = ref(4096);
        const maxHeight = ref(4096);

        const vugelComponentInstance = getCurrentInstance()!;

        onMounted(() => {
            let vugelRenderer: RootRenderFunction;
            let stage: VugelStage;
            let stageRoot: Root;

            if (elRef.value) {
                stage = new Stage(elRef.value, { ...props.settings }) as VugelStage;
                stage.eventHelpers = setupEvents(props.settings?.eventsTarget || elRef.value, stage);

                vugelRenderer = createRendererForStage(stage);
                stageRoot = new Root(stage, stage.root);

                // Auto-inherit dimensions.
                stageRoot["func-w"] = (w: number) => w;
                stageRoot["func-h"] = (w: number, h: number) => h;

                // Keep correct aspect-ratio issues when the page is zoomed out.
                const maxTextureSize = stage.getMaxTextureSize();
                maxWidth.value = maxTextureSize / stage.pixelRatio;
                maxHeight.value = maxTextureSize / stage.pixelRatio;
            }

            const defaultSlot = setupContext.slots.default;
            if (defaultSlot) {
                // We must wait until nextTick to prevent interference in the effect queue.
                nextTick().then(() => {
                    const node = h(Connector, defaultSlot);
                    vugelRenderer(node, stageRoot);
                });
            } else {
                console.warn("No default slot is defined");
            }
        });

        /**
         * Since vugel uses its own renderer, the ancestor vue's appContext, root and provides would normally be lost in
         * the vugel components.
         *
         * We can fix this by overriding the component's parent, root, appContext and provides before rendering the slot
         * contents.
         */
        const Connector = defineComponent({
            setup(props, setupContext) {
                const instance = getCurrentInstance()!;

                // @see runtime-core createComponentInstance
                instance.parent = vugelComponentInstance;
                instance.appContext = vugelComponentInstance.appContext;
                instance.root = vugelComponentInstance.root;
                (instance as any).provides = (vugelComponentInstance as any).provides;

                const defaultSlot = setupContext.slots.default!;
                return () => h(Fragment, defaultSlot());
            },
        });

        // We need to use a wrapper for flexible size layouting to work with tree2d pixelRatio canvas auto-resizing.
        return () =>
            h(
                "div",
                {
                    class: "custom-renderer-wrapper",
                    style: { position: props.position, maxWidth: maxWidth.value, maxHeight: maxHeight.value },
                },
                [
                    h("canvas", {
                        class: "custom-renderer",
                        style: { position: "absolute", width: "100%", height: "100%" },
                        ref: elRef,
                    }),
                ],
            );
    },
})