|
1 | | -import { describe, expect, it } from 'vitest' |
| 1 | +import { describe, expect, it, vi } from 'vitest' |
2 | 2 | import { render } from '@solidjs/testing-library' |
3 | 3 | import { formOptions } from '@tanstack/form-core' |
4 | 4 | import userEvent from '@testing-library/user-event' |
| 5 | +import { createEffect, createSignal } from 'solid-js' |
5 | 6 | import { createFormHook, createFormHookContexts, useStore } from '../src' |
6 | 7 |
|
7 | 8 | const user = userEvent.setup() |
@@ -535,6 +536,129 @@ describe('createFormHook', () => { |
535 | 536 | render(() => <Parent />) |
536 | 537 | }) |
537 | 538 |
|
| 539 | + it('should keep props reactive in JSX when passed to withForm component', async () => { |
| 540 | + const formOpts = formOptions({ defaultValues: { name: '' } }) |
| 541 | + |
| 542 | + const StatusForm = withForm({ |
| 543 | + ...formOpts, |
| 544 | + props: { |
| 545 | + status: 'idle' as 'idle' | 'loading', |
| 546 | + count: 0, |
| 547 | + }, |
| 548 | + render: (props) => ( |
| 549 | + <div> |
| 550 | + <span data-testid="status">{props.status}</span> |
| 551 | + <span data-testid="count">{props.count}</span> |
| 552 | + </div> |
| 553 | + ), |
| 554 | + }) |
| 555 | + |
| 556 | + const Parent = () => { |
| 557 | + const form = useAppForm(() => formOpts) |
| 558 | + const [status, setStatus] = createSignal<'idle' | 'loading'>('idle') |
| 559 | + const [count, setCount] = createSignal(0) |
| 560 | + return ( |
| 561 | + <div> |
| 562 | + <StatusForm form={form} status={status()} count={count()} /> |
| 563 | + <button data-testid="btn-status" onClick={() => setStatus('loading')}> |
| 564 | + change |
| 565 | + </button> |
| 566 | + <button |
| 567 | + data-testid="btn-count" |
| 568 | + onClick={() => setCount((c) => c + 1)} |
| 569 | + > |
| 570 | + inc |
| 571 | + </button> |
| 572 | + </div> |
| 573 | + ) |
| 574 | + } |
| 575 | + |
| 576 | + const { getByTestId } = render(() => <Parent />) |
| 577 | + |
| 578 | + expect(getByTestId('status')).toHaveTextContent('idle') |
| 579 | + expect(getByTestId('count')).toHaveTextContent('0') |
| 580 | + |
| 581 | + await user.click(getByTestId('btn-status')) |
| 582 | + expect(getByTestId('status')).toHaveTextContent('loading') |
| 583 | + |
| 584 | + await user.click(getByTestId('btn-count')) |
| 585 | + expect(getByTestId('count')).toHaveTextContent('1') |
| 586 | + }) |
| 587 | + |
| 588 | + it('should re-run createEffect when reactive props change in withForm render', async () => { |
| 589 | + const formOpts = formOptions({ defaultValues: { name: '' } }) |
| 590 | + const spy = vi.fn() |
| 591 | + |
| 592 | + const StatusForm = withForm({ |
| 593 | + ...formOpts, |
| 594 | + props: { status: 'idle' as 'idle' | 'loading' }, |
| 595 | + render: (props) => { |
| 596 | + createEffect(() => { |
| 597 | + spy(props.status) |
| 598 | + }) |
| 599 | + return <div data-testid="status">{props.status}</div> |
| 600 | + }, |
| 601 | + }) |
| 602 | + |
| 603 | + const Parent = () => { |
| 604 | + const form = useAppForm(() => formOpts) |
| 605 | + const [status, setStatus] = createSignal<'idle' | 'loading'>('idle') |
| 606 | + return ( |
| 607 | + <div> |
| 608 | + <StatusForm form={form} status={status()} /> |
| 609 | + <button data-testid="btn" onClick={() => setStatus('loading')}> |
| 610 | + change |
| 611 | + </button> |
| 612 | + </div> |
| 613 | + ) |
| 614 | + } |
| 615 | + |
| 616 | + const { getByTestId } = render(() => <Parent />) |
| 617 | + |
| 618 | + expect(spy).toHaveBeenCalledTimes(1) |
| 619 | + expect(spy).toHaveBeenLastCalledWith('idle') |
| 620 | + |
| 621 | + await user.click(getByTestId('btn')) |
| 622 | + expect(spy).toHaveBeenCalledTimes(2) |
| 623 | + expect(spy).toHaveBeenLastCalledWith('loading') |
| 624 | + }) |
| 625 | + |
| 626 | + it('should keep props reactive in withFieldGroup component', async () => { |
| 627 | + const formOpts = formOptions({ |
| 628 | + defaultValues: { person: { firstName: 'John' } }, |
| 629 | + }) |
| 630 | + |
| 631 | + const PersonGroup = withFieldGroup({ |
| 632 | + defaultValues: formOpts.defaultValues.person, |
| 633 | + props: { label: 'default' }, |
| 634 | + render: (props) => ( |
| 635 | + <div> |
| 636 | + <span data-testid="label">{props.label}</span> |
| 637 | + </div> |
| 638 | + ), |
| 639 | + }) |
| 640 | + |
| 641 | + const Parent = () => { |
| 642 | + const form = useAppForm(() => formOpts) |
| 643 | + const [label, setLabel] = createSignal('initial') |
| 644 | + return ( |
| 645 | + <div> |
| 646 | + <PersonGroup form={form} fields="person" label={label()} /> |
| 647 | + <button data-testid="btn" onClick={() => setLabel('updated')}> |
| 648 | + change |
| 649 | + </button> |
| 650 | + </div> |
| 651 | + ) |
| 652 | + } |
| 653 | + |
| 654 | + const { getByTestId } = render(() => <Parent />) |
| 655 | + |
| 656 | + expect(getByTestId('label')).toHaveTextContent('initial') |
| 657 | + |
| 658 | + await user.click(getByTestId('btn')) |
| 659 | + expect(getByTestId('label')).toHaveTextContent('updated') |
| 660 | + }) |
| 661 | + |
538 | 662 | it('should accept formId and return it', async () => { |
539 | 663 | function Submit() { |
540 | 664 | const form = useFormContext() |
|
0 commit comments