From 85e9e764e827f21861f29b35e3b5453a3615e211 Mon Sep 17 00:00:00 2001 From: Tony Ward <8069555+ynotdraw@users.noreply.github.com> Date: Tue, 25 Apr 2023 10:53:18 -0400 Subject: [PATCH 1/8] Expose named blocks for textarea --- .../src/-private/textarea-field.hbs | 95 ++++++++++++++++--- .../src/-private/textarea-field.ts | 11 ++- .../toucan-form/form-textarea-test.gts | 67 +++++++++++++ 3 files changed, 156 insertions(+), 17 deletions(-) diff --git a/packages/ember-toucan-form/src/-private/textarea-field.hbs b/packages/ember-toucan-form/src/-private/textarea-field.hbs index 8858a87a6..25544ae83 100644 --- a/packages/ember-toucan-form/src/-private/textarea-field.hbs +++ b/packages/ember-toucan-form/src/-private/textarea-field.hbs @@ -1,16 +1,83 @@ +{{! + Regarding Conditionals + + This looks really messy, but Form::Fields::Textarea exposes named blocks; HOWEVER, + we cannot conditionally render named blocks due to https://github.com/emberjs/rfcs/issues/735. + + We *can* conditionally render components though, based on the blocks and argument combinations + users provide us. This is very brittle, but until https://github.com/emberjs/rfcs/issues/735 + is resolved and a solution is found, this appears to be the only way to truly expose + conditional named blocks. + + --- + + Regarding glint-expect-error + + "@onChange" of the textarea only expects a string typed value, but field.setValue is generic, + accepting anything that DATA[KEY] could be. Similar case with "@value", but there casting to + a string is easy. +}} <@form.Field @name={{@name}} as |field|> - + {{#if (this.hasOnlyLabelBlock (has-block 'label') (has-block 'hint'))}} + + <:label>{{yield to='label'}} + + {{else if (this.hasHintAndLabelBlocks (has-block 'label') (has-block 'hint')) + }} + + <:label>{{yield to='label'}} + <:hint>{{yield to='hint'}} + + {{else if (this.hasLabelArgAndHintBlock @label (has-block 'hint'))}} + + <:hint>{{yield to='hint'}} + + {{else}} + {{! Argument-only case }} + + {{/if}} \ No newline at end of file diff --git a/packages/ember-toucan-form/src/-private/textarea-field.ts b/packages/ember-toucan-form/src/-private/textarea-field.ts index ce94d8139..a9164e7ae 100644 --- a/packages/ember-toucan-form/src/-private/textarea-field.ts +++ b/packages/ember-toucan-form/src/-private/textarea-field.ts @@ -25,15 +25,20 @@ export interface ToucanFormTextareaFieldComponentSignature< */ form: HeadlessFormBlock; }; - Blocks: { - default: []; - }; + Blocks: BaseTextareaFieldSignature['Blocks']; } export default class ToucanFormTextareaFieldComponent< DATA extends UserData, KEY extends FormKey> = FormKey> > extends Component> { + hasOnlyLabelBlock = (hasLabel: boolean, hasHint: boolean) => + hasLabel && !hasHint; + hasHintAndLabelBlocks = (hasLabel: boolean, hasHint: boolean) => + hasLabel && hasHint; + hasLabelArgAndHintBlock = (hasLabel: string | undefined, hasHint: boolean) => + hasLabel && hasHint; + mapErrors = (errors?: ValidationError[]) => { if (!errors) { return; diff --git a/test-app/tests/integration/components/toucan-form/form-textarea-test.gts b/test-app/tests/integration/components/toucan-form/form-textarea-test.gts index 008859758..af4e4f17d 100644 --- a/test-app/tests/integration/components/toucan-form/form-textarea-test.gts +++ b/test-app/tests/integration/components/toucan-form/form-textarea-test.gts @@ -31,4 +31,71 @@ module('Integration | Component | ToucanForm | Textarea', function (hooks) { assert.dom('[data-textarea]').hasAttribute('readonly'); }); + + test('it renders `@label` and `@hint` component arguments', async function (assert) { + const data: TestData = { + text: 'multi-line text', + }; + + await render(); + + assert.dom('[data-label]').exists(); + assert.dom('[data-hint]').exists(); + }); + + test('it renders a `:label` named block', async function (assert) { + const data: TestData = { + text: 'multi-line text', + }; + + await render(); + + assert.dom('[data-label-block]').exists(); + }); + + test('it renders a `:hint` named block', async function (assert) { + const data: TestData = { + text: 'multi-line text', + }; + + await render(); + + // NOTE: `data-label` comes from `@label`. + assert.dom('[data-label]').exists(); + assert.dom('[data-hint-block]').exists(); + }); + + test('it renders both a `:label` and `:hint` named block', async function (assert) { + const data: TestData = { + text: 'multi-line text', + }; + + await render(); + + assert.dom('[data-label-block]').exists(); + assert.dom('[data-hint-block]').exists(); + }); }); From d64d47c75560498693ab72c01f136a63c61c7356 Mon Sep 17 00:00:00 2001 From: Tony Ward <8069555+ynotdraw@users.noreply.github.com> Date: Wed, 26 Apr 2023 08:09:06 -0400 Subject: [PATCH 2/8] Update test-app/tests/integration/components/toucan-form/form-textarea-test.gts Co-authored-by: nicole chung <771170+nicolechung@users.noreply.github.com> --- .../integration/components/toucan-form/form-textarea-test.gts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-app/tests/integration/components/toucan-form/form-textarea-test.gts b/test-app/tests/integration/components/toucan-form/form-textarea-test.gts index af4e4f17d..5df8ec1f9 100644 --- a/test-app/tests/integration/components/toucan-form/form-textarea-test.gts +++ b/test-app/tests/integration/components/toucan-form/form-textarea-test.gts @@ -63,7 +63,7 @@ module('Integration | Component | ToucanForm | Textarea', function (hooks) { assert.dom('[data-label-block]').exists(); }); - test('it renders a `:hint` named block', async function (assert) { + test('it renders a `:hint` named block with a @label arg', async function (assert) { const data: TestData = { text: 'multi-line text', }; From 0ac02499eacbe66df93846a5d4bd5c950a7fd9c2 Mon Sep 17 00:00:00 2001 From: Tony Ward <8069555+ynotdraw@users.noreply.github.com> Date: Wed, 26 Apr 2023 08:12:09 -0400 Subject: [PATCH 3/8] tests: Added additional tests for named blocks --- .../components/toucan-form/form-textarea-test.gts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/test-app/tests/integration/components/toucan-form/form-textarea-test.gts b/test-app/tests/integration/components/toucan-form/form-textarea-test.gts index 5df8ec1f9..b8e50ea93 100644 --- a/test-app/tests/integration/components/toucan-form/form-textarea-test.gts +++ b/test-app/tests/integration/components/toucan-form/form-textarea-test.gts @@ -47,23 +47,27 @@ module('Integration | Component | ToucanForm | Textarea', function (hooks) { assert.dom('[data-hint]').exists(); }); - test('it renders a `:label` named block', async function (assert) { + test('it renders a `:label` named block with a `@hint` argument', async function (assert) { const data: TestData = { text: 'multi-line text', }; await render(); assert.dom('[data-label-block]').exists(); + + // NOTE: `data-hint` comes from `@hint`. + assert.dom('[data-hint]').exists(); + assert.dom('[data-hint]').hasText('Hint'); }); - test('it renders a `:hint` named block with a @label arg', async function (assert) { + test('it renders a `:hint` named block with a `@label` argument', async function (assert) { const data: TestData = { text: 'multi-line text', }; @@ -78,6 +82,8 @@ module('Integration | Component | ToucanForm | Textarea', function (hooks) { // NOTE: `data-label` comes from `@label`. assert.dom('[data-label]').exists(); + assert.dom('[data-label]').hasText('Label'); + assert.dom('[data-hint-block]').exists(); }); From 84788ef73f0723bb2caf736fd5bf218e73fa3a1f Mon Sep 17 00:00:00 2001 From: Tony Ward <8069555+ynotdraw@users.noreply.github.com> Date: Wed, 26 Apr 2023 08:22:47 -0400 Subject: [PATCH 4/8] toucan-form: Input named block support --- .../src/-private/input-field.hbs | 95 ++++++++++++++++--- .../src/-private/input-field.ts | 11 ++- .../toucan-form/form-input-test.gts | 73 ++++++++++++++ 3 files changed, 162 insertions(+), 17 deletions(-) diff --git a/packages/ember-toucan-form/src/-private/input-field.hbs b/packages/ember-toucan-form/src/-private/input-field.hbs index b2a61b5f1..37952ab5a 100644 --- a/packages/ember-toucan-form/src/-private/input-field.hbs +++ b/packages/ember-toucan-form/src/-private/input-field.hbs @@ -1,16 +1,83 @@ +{{! + Regarding Conditionals + + This looks really messy, but Form::Fields::Input exposes named blocks; HOWEVER, + we cannot conditionally render named blocks due to https://github.com/emberjs/rfcs/issues/735. + + We *can* conditionally render components though, based on the blocks and argument combinations + users provide us. This is very brittle, but until https://github.com/emberjs/rfcs/issues/735 + is resolved and a solution is found, this appears to be the only way to truly expose + conditional named blocks. + + --- + + Regarding glint-expect-error + + "@onChange" of the input only expects a string typed value, but field.setValue is generic, + accepting anything that DATA[KEY] could be. Similar case with "@value", but there casting to + a string is easy. +}} <@form.Field @name={{@name}} as |field|> - + {{#if (this.hasOnlyLabelBlock (has-block 'label') (has-block 'hint'))}} + + <:label>{{yield to='label'}} + + {{else if (this.hasHintAndLabelBlocks (has-block 'label') (has-block 'hint')) + }} + + <:label>{{yield to='label'}} + <:hint>{{yield to='hint'}} + + {{else if (this.hasLabelArgAndHintBlock @label (has-block 'hint'))}} + + <:hint>{{yield to='hint'}} + + {{else}} + {{! Argument-only case }} + + {{/if}} \ No newline at end of file diff --git a/packages/ember-toucan-form/src/-private/input-field.ts b/packages/ember-toucan-form/src/-private/input-field.ts index d593bfed5..825990b88 100644 --- a/packages/ember-toucan-form/src/-private/input-field.ts +++ b/packages/ember-toucan-form/src/-private/input-field.ts @@ -25,15 +25,20 @@ export interface ToucanFormInputFieldComponentSignature< */ form: HeadlessFormBlock; }; - Blocks: { - default: []; - }; + Blocks: BaseInputFieldSignature['Blocks']; } export default class ToucanFormInputFieldComponent< DATA extends UserData, KEY extends FormKey> = FormKey> > extends Component> { + hasOnlyLabelBlock = (hasLabel: boolean, hasHint: boolean) => + hasLabel && !hasHint; + hasHintAndLabelBlocks = (hasLabel: boolean, hasHint: boolean) => + hasLabel && hasHint; + hasLabelArgAndHintBlock = (hasLabel: string | undefined, hasHint: boolean) => + hasLabel && hasHint; + mapErrors = (errors?: ValidationError[]) => { if (!errors) { return; diff --git a/test-app/tests/integration/components/toucan-form/form-input-test.gts b/test-app/tests/integration/components/toucan-form/form-input-test.gts index 0d58a69e5..31ba4d52f 100644 --- a/test-app/tests/integration/components/toucan-form/form-input-test.gts +++ b/test-app/tests/integration/components/toucan-form/form-input-test.gts @@ -31,4 +31,77 @@ module('Integration | Component | ToucanForm | Input', function (hooks) { assert.dom('[data-input]').hasAttribute('readonly'); }); + + test('it renders `@label` and `@hint` component arguments', async function (assert) { + const data: TestData = { + text: 'text', + }; + + await render(); + + assert.dom('[data-label]').exists(); + assert.dom('[data-hint]').exists(); + }); + + test('it renders a `:label` named block with a `@hint` argument', async function (assert) { + const data: TestData = { + text: 'text', + }; + + await render(); + + assert.dom('[data-label-block]').exists(); + + // NOTE: `data-hint` comes from `@hint`. + assert.dom('[data-hint]').exists(); + assert.dom('[data-hint]').hasText('Hint'); + }); + + test('it renders a `:hint` named block with a `@label` argument', async function (assert) { + const data: TestData = { + text: 'text', + }; + + await render(); + + // NOTE: `data-label` comes from `@label`. + assert.dom('[data-label]').exists(); + assert.dom('[data-label]').hasText('Label'); + + assert.dom('[data-hint-block]').exists(); + }); + + test('it renders both a `:label` and `:hint` named block', async function (assert) { + const data: TestData = { + text: 'text', + }; + + await render(); + + assert.dom('[data-label-block]').exists(); + assert.dom('[data-hint-block]').exists(); + }); }); From 5e99d1f1dfa241e6a203add52d9eca713f19bd53 Mon Sep 17 00:00:00 2001 From: Tony Ward <8069555+ynotdraw@users.noreply.github.com> Date: Wed, 26 Apr 2023 08:42:13 -0400 Subject: [PATCH 5/8] toucan-form: Checkbox named block support --- .../src/-private/checkbox-field.hbs | 95 ++++++++++++++++--- .../src/-private/checkbox-field.ts | 11 ++- .../toucan-form/form-checkbox-test.gts | 73 ++++++++++++++ 3 files changed, 162 insertions(+), 17 deletions(-) diff --git a/packages/ember-toucan-form/src/-private/checkbox-field.hbs b/packages/ember-toucan-form/src/-private/checkbox-field.hbs index 66e3ea7e1..750b545a9 100644 --- a/packages/ember-toucan-form/src/-private/checkbox-field.hbs +++ b/packages/ember-toucan-form/src/-private/checkbox-field.hbs @@ -1,16 +1,83 @@ +{{! + Regarding Conditionals + + This looks really messy, but Form::Fields::Checkbox exposes named blocks; HOWEVER, + we cannot conditionally render named blocks due to https://github.com/emberjs/rfcs/issues/735. + + We *can* conditionally render components though, based on the blocks and argument combinations + users provide us. This is very brittle, but until https://github.com/emberjs/rfcs/issues/735 + is resolved and a solution is found, this appears to be the only way to truly expose + conditional named blocks. + + --- + + Regarding glint-expect-error + + "@onChange" of the checkbox only expects a boolean typed value, but field.setValue is generic, + accepting anything that DATA[KEY] could be. Similar case with "@isChecked", but there casting to + a boolean is easy. +}} <@form.Field @name={{@name}} as |field|> - + {{#if (this.hasOnlyLabelBlock (has-block 'label') (has-block 'hint'))}} + + <:label>{{yield to='label'}} + + {{else if (this.hasHintAndLabelBlocks (has-block 'label') (has-block 'hint')) + }} + + <:label>{{yield to='label'}} + <:hint>{{yield to='hint'}} + + {{else if (this.hasLabelArgAndHintBlock @label (has-block 'hint'))}} + + <:hint>{{yield to='hint'}} + + {{else}} + {{! Argument-only case }} + + {{/if}} \ No newline at end of file diff --git a/packages/ember-toucan-form/src/-private/checkbox-field.ts b/packages/ember-toucan-form/src/-private/checkbox-field.ts index fa72d6391..90ec65932 100644 --- a/packages/ember-toucan-form/src/-private/checkbox-field.ts +++ b/packages/ember-toucan-form/src/-private/checkbox-field.ts @@ -25,15 +25,20 @@ export interface ToucanFormCheckboxFieldComponentSignature< */ form: HeadlessFormBlock; }; - Blocks: { - default: []; - }; + Blocks: BaseCheckboxFieldSignature['Blocks']; } export default class ToucanFormTextareaFieldComponent< DATA extends UserData, KEY extends FormKey> = FormKey> > extends Component> { + hasOnlyLabelBlock = (hasLabel: boolean, hasHint: boolean) => + hasLabel && !hasHint; + hasHintAndLabelBlocks = (hasLabel: boolean, hasHint: boolean) => + hasLabel && hasHint; + hasLabelArgAndHintBlock = (hasLabel: string | undefined, hasHint: boolean) => + hasLabel && hasHint; + mapErrors = (errors?: ValidationError[]) => { if (!errors) { return; diff --git a/test-app/tests/integration/components/toucan-form/form-checkbox-test.gts b/test-app/tests/integration/components/toucan-form/form-checkbox-test.gts index 3ff77ff95..acf11e984 100644 --- a/test-app/tests/integration/components/toucan-form/form-checkbox-test.gts +++ b/test-app/tests/integration/components/toucan-form/form-checkbox-test.gts @@ -31,4 +31,77 @@ module('Integration | Component | ToucanForm | Checkbox', function (hooks) { assert.dom('[data-checkbox]').hasAttribute('readonly'); }); + + test('it renders `@label` and `@hint` component arguments', async function (assert) { + const data: TestData = { + checked: false, + }; + + await render(); + + assert.dom('[data-label]').exists(); + assert.dom('[data-hint]').exists(); + }); + + test('it renders a `:label` named block with a `@hint` argument', async function (assert) { + const data: TestData = { + checked: false, + }; + + await render(); + + assert.dom('[data-label-block]').exists(); + + // NOTE: `data-hint` comes from `@hint`. + assert.dom('[data-hint]').exists(); + assert.dom('[data-hint]').hasText('Hint'); + }); + + test('it renders a `:hint` named block with a `@label` argument', async function (assert) { + const data: TestData = { + checked: false, + }; + + await render(); + + // NOTE: `data-label` comes from `@label`. + assert.dom('[data-label]').exists(); + assert.dom('[data-label]').hasText('Label'); + + assert.dom('[data-hint-block]').exists(); + }); + + test('it renders both a `:label` and `:hint` named block', async function (assert) { + const data: TestData = { + checked: false, + }; + + await render(); + + assert.dom('[data-label-block]').exists(); + assert.dom('[data-hint-block]').exists(); + }); }); From 6065a25d85b73fb04d5637f8e0ba2ae36d64bcf1 Mon Sep 17 00:00:00 2001 From: Tony Ward <8069555+ynotdraw@users.noreply.github.com> Date: Wed, 26 Apr 2023 12:48:20 -0400 Subject: [PATCH 6/8] toucan-form: CheckboxGroup named block support --- .../src/-private/checkbox-group-field.hbs | 110 +++++++++++-- .../src/-private/checkbox-group-field.ts | 15 +- .../toucan-form/form-checkbox-group-test.gts | 149 ++++++++++++++++++ 3 files changed, 250 insertions(+), 24 deletions(-) diff --git a/packages/ember-toucan-form/src/-private/checkbox-group-field.hbs b/packages/ember-toucan-form/src/-private/checkbox-group-field.hbs index ea2b095c8..bd8a5cb21 100644 --- a/packages/ember-toucan-form/src/-private/checkbox-group-field.hbs +++ b/packages/ember-toucan-form/src/-private/checkbox-group-field.hbs @@ -1,19 +1,95 @@ +{{! + Regarding Conditionals + + This looks really messy, but Form::Fields::CheckboxGroup exposes named blocks; HOWEVER, + we cannot conditionally render named blocks due to https://github.com/emberjs/rfcs/issues/735. + + We *can* conditionally render components though, based on the blocks and argument combinations + users provide us. This is very brittle, but until https://github.com/emberjs/rfcs/issues/735 + is resolved and a solution is found, this appears to be the only way to truly expose + conditional named blocks. + + --- + + Regarding glint-expect-error + + "@onChange" of the checkbox-group only expects an array of strings typed value, but field.setValue is generic, + accepting anything that DATA[KEY] could be. Similar case with "@isChecked", but there casting to + an array of strings is easy. +}} <@form.Field @name={{@name}} as |field|> - - {{yield (hash CheckboxField=group.CheckboxField)}} - + {{#if (this.hasOnlyLabelBlock (has-block 'label') (has-block 'hint'))}} + + <:label>{{yield to='label'}} + <:default as |group|> + {{yield (hash CheckboxField=group.CheckboxField) to='default'}} + + + {{else if (this.hasHintAndLabelBlocks (has-block 'label') (has-block 'hint')) + }} + + <:label>{{yield to='label'}} + <:hint>{{yield to='hint'}} + <:default as |group|> + {{yield (hash CheckboxField=group.CheckboxField)}} + + + {{else if (this.hasLabelArgAndHintBlock @label (has-block 'hint'))}} + + <:hint>{{yield to='hint'}} + <:default as |group|> + {{yield (hash CheckboxField=group.CheckboxField)}} + + + {{else}} + {{! Argument-only case }} + + {{yield (hash CheckboxField=group.CheckboxField)}} + + {{/if}} \ No newline at end of file diff --git a/packages/ember-toucan-form/src/-private/checkbox-group-field.ts b/packages/ember-toucan-form/src/-private/checkbox-group-field.ts index 65f1ac455..64b0e540d 100644 --- a/packages/ember-toucan-form/src/-private/checkbox-group-field.ts +++ b/packages/ember-toucan-form/src/-private/checkbox-group-field.ts @@ -25,19 +25,20 @@ export interface ToucanFormCheckboxGroupFieldComponentSignature< */ form: HeadlessFormBlock; }; - Blocks: { - default: [ - { - CheckboxField: BaseCheckboxGroupFieldSignature['Blocks']['default'][0]['CheckboxField']; - } - ]; - }; + Blocks: BaseCheckboxGroupFieldSignature['Blocks']; } export default class ToucanFormTextareaFieldComponent< DATA extends UserData, KEY extends FormKey> = FormKey> > extends Component> { + hasOnlyLabelBlock = (hasLabel: boolean, hasHint: boolean) => + hasLabel && !hasHint; + hasHintAndLabelBlocks = (hasLabel: boolean, hasHint: boolean) => + hasLabel && hasHint; + hasLabelArgAndHintBlock = (hasLabel: string | undefined, hasHint: boolean) => + hasLabel && hasHint; + mapErrors = (errors?: ValidationError[]) => { if (!errors) { return; diff --git a/test-app/tests/integration/components/toucan-form/form-checkbox-group-test.gts b/test-app/tests/integration/components/toucan-form/form-checkbox-group-test.gts index dc715abe8..9825141d7 100644 --- a/test-app/tests/integration/components/toucan-form/form-checkbox-group-test.gts +++ b/test-app/tests/integration/components/toucan-form/form-checkbox-group-test.gts @@ -84,5 +84,154 @@ module( assert.dom('[data-checkbox-group-2]').hasAttribute('readonly'); assert.dom('[data-checkbox-group-3]').hasNoAttribute('readonly'); }); + + test('it renders `@label` and `@hint` component arguments', async function (assert) { + const data: TestData = { + checkboxes: [], + }; + + await render(); + + assert.dom('[data-label]').exists(); + assert.dom('[data-hint]').exists(); + }); + + test('it renders a `:label` named block with a `@hint` argument', async function (assert) { + const data: TestData = { + checkboxes: [], + }; + + await render(); + + assert.dom('[data-label-block]').exists(); + + // NOTE: `data-hint` comes from `@hint`. + assert.dom('[data-hint]').exists(); + assert.dom('[data-hint]').hasText('Hint'); + }); + + test('it renders a `:hint` named block with a `@label` argument', async function (assert) { + const data: TestData = { + checkboxes: [], + }; + + await render(); + + // NOTE: `data-label` comes from `@label`. + assert.dom('[data-label]').exists(); + assert.dom('[data-label]').hasText('Label'); + + assert.dom('[data-hint-block]').exists(); + }); + + test('it renders both a `:label` and `:hint` named block', async function (assert) { + const data: TestData = { + checkboxes: [], + }; + + await render(); + + assert.dom('[data-label-block]').exists(); + assert.dom('[data-hint-block]').exists(); + }); } ); From 1455a27629dd54e3c98d243932c2a1ac8f11c24c Mon Sep 17 00:00:00 2001 From: Tony Ward <8069555+ynotdraw@users.noreply.github.com> Date: Wed, 26 Apr 2023 13:20:00 -0400 Subject: [PATCH 7/8] toucan-form: RadioGroup named block support --- .../src/-private/radio-group-field.hbs | 110 +++++++++++++--- .../src/-private/radio-group-field.ts | 15 ++- .../toucan-form/form-radio-group-test.gts | 120 ++++++++++++++++++ 3 files changed, 221 insertions(+), 24 deletions(-) diff --git a/packages/ember-toucan-form/src/-private/radio-group-field.hbs b/packages/ember-toucan-form/src/-private/radio-group-field.hbs index 828b8303b..c68b4c9b4 100644 --- a/packages/ember-toucan-form/src/-private/radio-group-field.hbs +++ b/packages/ember-toucan-form/src/-private/radio-group-field.hbs @@ -1,19 +1,95 @@ +{{! + Regarding Conditionals + + This looks really messy, but Form::Fields::RadioGroup exposes named blocks; HOWEVER, + we cannot conditionally render named blocks due to https://github.com/emberjs/rfcs/issues/735. + + We *can* conditionally render components though, based on the blocks and argument combinations + users provide us. This is very brittle, but until https://github.com/emberjs/rfcs/issues/735 + is resolved and a solution is found, this appears to be the only way to truly expose + conditional named blocks. + + --- + + Regarding glint-expect-error + + "@onChange" of the radio-group only expects a string typed value, but field.setValue is generic, + accepting anything that DATA[KEY] could be. Similar case with "@isChecked", but there casting to + a string is easy. +}} <@form.Field @name={{@name}} as |field|> - - {{yield (hash RadioField=group.RadioField)}} - + {{#if (this.hasOnlyLabelBlock (has-block 'label') (has-block 'hint'))}} + + <:label>{{yield to='label'}} + <:default as |group|> + {{yield (hash RadioField=group.RadioField)}} + + + {{else if (this.hasHintAndLabelBlocks (has-block 'label') (has-block 'hint')) + }} + + <:label>{{yield to='label'}} + <:hint>{{yield to='hint'}} + <:default as |group|> + {{yield (hash RadioField=group.RadioField)}} + + + {{else if (this.hasLabelArgAndHintBlock @label (has-block 'hint'))}} + + <:hint>{{yield to='hint'}} + <:default as |group|> + {{yield (hash RadioField=group.RadioField)}} + + + {{else}} + {{! Argument-only case }} + + {{yield (hash RadioField=group.RadioField)}} + + {{/if}} \ No newline at end of file diff --git a/packages/ember-toucan-form/src/-private/radio-group-field.ts b/packages/ember-toucan-form/src/-private/radio-group-field.ts index 60ba5ecbf..e82d9143e 100644 --- a/packages/ember-toucan-form/src/-private/radio-group-field.ts +++ b/packages/ember-toucan-form/src/-private/radio-group-field.ts @@ -25,19 +25,20 @@ export interface ToucanFormRadioGroupFieldComponentSignature< */ form: HeadlessFormBlock; }; - Blocks: { - default: [ - { - RadioField: BaseRadioGroupFieldSignature['Blocks']['default'][0]['RadioField']; - } - ]; - }; + Blocks: BaseRadioGroupFieldSignature['Blocks']; } export default class ToucanFormTextareaFieldComponent< DATA extends UserData, KEY extends FormKey> = FormKey> > extends Component> { + hasOnlyLabelBlock = (hasLabel: boolean, hasHint: boolean) => + hasLabel && !hasHint; + hasHintAndLabelBlocks = (hasLabel: boolean, hasHint: boolean) => + hasLabel && hasHint; + hasLabelArgAndHintBlock = (hasLabel: string | undefined, hasHint: boolean) => + hasLabel && hasHint; + mapErrors = (errors?: ValidationError[]) => { if (!errors) { return; diff --git a/test-app/tests/integration/components/toucan-form/form-radio-group-test.gts b/test-app/tests/integration/components/toucan-form/form-radio-group-test.gts index 44a1ac7d8..8af891945 100644 --- a/test-app/tests/integration/components/toucan-form/form-radio-group-test.gts +++ b/test-app/tests/integration/components/toucan-form/form-radio-group-test.gts @@ -58,4 +58,124 @@ module('Integration | Component | ToucanForm | RadioGroup', function (hooks) { assert.dom('[data-radio-1]').hasNoAttribute('readonly'); assert.dom('[data-radio-2]').hasAttribute('readonly'); }); + + test('it renders `@label` and `@hint` component arguments', async function (assert) { + const data: TestData = { + radio: 'option-2', + }; + + await render(); + + assert.dom('[data-label]').exists(); + assert.dom('[data-hint]').exists(); + }); + + test('it renders a `:label` named block with a `@hint` argument', async function (assert) { + const data: TestData = { + radio: 'option-2', + }; + + await render(); + + assert.dom('[data-label-block]').exists(); + + // NOTE: `data-hint` comes from `@hint`. + assert.dom('[data-hint]').exists(); + assert.dom('[data-hint]').hasText('Hint'); + }); + + test('it renders a `:hint` named block with a `@label` argument', async function (assert) { + const data: TestData = { + radio: 'option-2', + }; + + await render(); + + // NOTE: `data-label` comes from `@label`. + assert.dom('[data-label]').exists(); + assert.dom('[data-label]').hasText('Label'); + + assert.dom('[data-hint-block]').exists(); + }); + + test('it renders both a `:label` and `:hint` named block', async function (assert) { + const data: TestData = { + radio: 'option-2', + }; + + await render(); + + assert.dom('[data-label-block]').exists(); + assert.dom('[data-hint-block]').exists(); + }); }); From 20d433f330a4a6ee3a5d31acfc20f48ccc1bb950 Mon Sep 17 00:00:00 2001 From: Tony Ward <8069555+ynotdraw@users.noreply.github.com> Date: Thu, 27 Apr 2023 12:59:19 -0400 Subject: [PATCH 8/8] Added changeset --- .changeset/light-olives-notice.md | 38 +++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 .changeset/light-olives-notice.md diff --git a/.changeset/light-olives-notice.md b/.changeset/light-olives-notice.md new file mode 100644 index 000000000..d54fd3057 --- /dev/null +++ b/.changeset/light-olives-notice.md @@ -0,0 +1,38 @@ +--- +'@crowdstrike/ember-toucan-form': patch +--- + +Exposed named blocks from the underlying `toucan-core` components. This allows users to add custom content in `:hint` or `:label` named blocks. You can combine the arguments and named blocks as well! Below are some examples. + +```hbs + + + <:label>Label + <:hint>Hint + + +``` + +```hbs + + + <:hint>Hint + + +``` + +```hbs + + + <:label>Label + + +``` + +Or you can continue to use the arguments if you're only working with strings! + +```hbs + + + +```