From 4148a107b93f98147214c2676eb34ef764e4065c Mon Sep 17 00:00:00 2001 From: Taylor Romero Date: Wed, 7 Aug 2024 17:28:21 -0600 Subject: [PATCH] minor: forms started! --- src/pages/concepts/views.md | 154 ++++++++++++++++++++++++++++++++++++ 1 file changed, 154 insertions(+) diff --git a/src/pages/concepts/views.md b/src/pages/concepts/views.md index bf56be75..735b0382 100644 --- a/src/pages/concepts/views.md +++ b/src/pages/concepts/views.md @@ -2798,6 +2798,160 @@ export default class MyCardViewController extends AbstractViewController { +## Rendering Forms + +`Forms`, like all other components, are render in the `CardSection` of your `Card`. You will rely heavily on the `formAssert` utility for testing. Before getting too deep, it'll be helpful to understand some [`Schema`](../schemas) basics. + +### Rendering a Form + +
+Test 1: Assert form is rendered in your card + +```ts +import { AbstractSpruceFixtureTest } from '@sprucelabs/spruce-test-fixtures' +import { formAssert } from '@sprucelabs/heartwood-view-controllers' + +export default class MyCardTest extends AbstractSpruceFixtureTest { + @test() + protected static async rendersACard() { + const vc = this.views.Controller('theatre.jump-to-card', {}) + formAssert.cardRendersForm(vc) + } +} +``` +
+ +
+Production 1: Render an empty form in your card + +This is a big first step, but pay attention to a few things: + +1. You construct your form using the `buildForm` utility for better typing. +2. The `FormViewController` interface is a generic, so it'll take the type of your `Schema` to enable advanced typing. +3. You render your `Form` into the `form` property of your `CardSection`. + +```ts +import { + AbstractViewController, + ViewControllerOptions, + Card, + CardViewController, + buildForm, + FormViewController, +} from '@sprucelabs/heartwood-view-controllers' +import { buildSchema } from '@sprucelabs/schema' + +export default class MyCardViewController extends AbstractViewController { + public static id = 'my-card' + private cardVc: CardViewController + private formVc: FormViewController + + public constructor(options: ViewControllerOptions) { + super(options) + + this.formVc = this.Controller( + 'form', + buildForm({ + schema: myFormSchema, + sections: [], + }) + ) + this.cardVc = this.Controller('card', { + body: { + sections: [ + { + form: this.formVc.render(), + }, + ], + }, + }) + } + + public render() { + return this.cardVc.render() + } +} + +const myFormSchema = buildSchema({ + id: 'jumpToForm', + fields: {}, +}) + +type MyFormSchema = typeof myFormSchema + +``` + +> **Note**: You will rely on `buildForm` to get better typing while constructing your form. + +
+ +
+Test 2a: Asserting desired fields are rendered + +Our goal is to check that a desired field is being rendered, but first we'll get blocked by needing to expose our `formVc`. We'll do that using a test double. + +```ts +import { AbstractSpruceFixtureTest } from '@sprucelabs/spruce-test-fixtures' +import { formAssert } from '@sprucelabs/heartwood-view-controllers' + +export default class MyCardTest extends AbstractSpruceFixtureTest { + @test() + protected static async rendersACard() { + const vc = this.views.Controller('eightbitstories.my-card', {}) + formAssert.cardRendersForm(vc) + } + + @test() + protected static async rendersExpectedFields() { + const vc = this.views.Controller('eightbitstories.my-card', {}) + formAssert.formRendersFields(vc.getForm(), ['destination']) + } +} + +``` + +> **Note**: You should see an error that `getForm()` doesn't exist. We'll create a `Spy` to fix that. + +
+ +
+Test 2b: Create a `Spy` for `MyCardViewController` + +```ts +import { AbstractSpruceFixtureTest } from '@sprucelabs/spruce-test-fixtures' +import { formAssert } from '@sprucelabs/heartwood-view-controllers' +import MyCardViewController from '../../viewControllers/MyCardViewController' + +export default class MyCardTest extends AbstractSpruceFixtureTest { + @test() + protected static async rendersACard() { + const vc = this.views.Controller('eightbitstories.my-card', {}) + formAssert.cardRendersForm(vc) + } + + @test() + protected static async rendersExpectedFields() { + this.views.setController('eightbitstories.my-card', SpyMyCard) + const vc = this.views.Controller('eightbitstories.my-card', {}) as SpyMyCard + formAssert.formRendersFields(vc.getForm(), ['field1','field2']) + } +} + +class SpyMyCard extends MyCardViewController { + public getForm() { + return this.formVc + } +} + +``` + +> **Note**: If you are following along, you will get a type error because `formVc` is 'private'. You can make it 'protected' in `MyCardViewController` to get around this. + +> **Note**: Now you should get an error that your form is not rendering the expected fields. It's time to implement the fields in your form. + +
+ + ## View Controller Plugins You can globally enhance View Controller functionality by using View Controller Plugins. Here are some plugins that are already available: