From eb42b17f0f5c0b16a0fb590467748b4255b524d7 Mon Sep 17 00:00:00 2001 From: Sam Van Campenhout Date: Wed, 3 Apr 2024 18:09:15 +0200 Subject: [PATCH] Add Glint support to the `AuRadioGroup` component --- ...{au-radio-group.gjs => au-radio-group.gts} | 26 +++- addon/template-registry.ts | 2 + .../components/au-radio-group-test.gts | 112 ++++++++++++++++++ .../components/au-radio-group-test.js | 87 -------------- .../integration/components/loose-mode-test.ts | 7 ++ 5 files changed, 145 insertions(+), 89 deletions(-) rename addon/components/{au-radio-group.gjs => au-radio-group.gts} (55%) create mode 100644 tests/integration/components/au-radio-group-test.gts delete mode 100644 tests/integration/components/au-radio-group-test.js diff --git a/addon/components/au-radio-group.gjs b/addon/components/au-radio-group.gts similarity index 55% rename from addon/components/au-radio-group.gjs rename to addon/components/au-radio-group.gts index 02dc7dd57..8b268ea43 100644 --- a/addon/components/au-radio-group.gjs +++ b/addon/components/au-radio-group.gts @@ -1,12 +1,34 @@ -import { AuRadio } from '@appuniversum/ember-appuniversum'; import { hash } from '@ember/helper'; import { guidFor } from '@ember/object/internals'; import Component from '@glimmer/component'; +import type { WithBoundArgs } from '@glint/template'; +import AuRadio, { type AuRadioSignature } from './au-radio'; // TODO: replace these with the named imports from ember-truth-helpers v4 once our dependencies support that version import or from 'ember-truth-helpers/helpers/or'; -export default class AuRadioGroup extends Component { +export interface AuRadioGroupSignature { + Args: { + alignment?: 'inline'; + disabled?: AuRadioSignature['Args']['disabled']; + name?: AuRadioSignature['Args']['name']; + onChange?: AuRadioSignature['Args']['onChangeGroup']; + selected?: AuRadioSignature['Args']['groupValue']; + }; + Blocks: { + default: [ + { + Radio: WithBoundArgs< + typeof AuRadio, + 'name' | 'inGroup' | 'groupValue' | 'disabled' | 'onChangeGroup' + >; + }, + ]; + }; + Element: HTMLDivElement; +} + +export default class AuRadioGroup extends Component { uniqueName = guidFor(this); get alignmentClass() { diff --git a/addon/template-registry.ts b/addon/template-registry.ts index 3e5fbf5b2..576e6febb 100644 --- a/addon/template-registry.ts +++ b/addon/template-registry.ts @@ -35,6 +35,7 @@ import type AuModal from '@appuniversum/ember-appuniversum/components/au-modal'; import type AuNavigationLink from '@appuniversum/ember-appuniversum/components/au-navigation-link'; import type AuPanel from '@appuniversum/ember-appuniversum/components/au-panel'; import type AuPill from '@appuniversum/ember-appuniversum/components/au-pill'; +import type AuRadioGroup from '@appuniversum/ember-appuniversum/components/au-radio-group'; import type AuRadio from '@appuniversum/ember-appuniversum/components/au-radio'; import type AuToolbar from '@appuniversum/ember-appuniversum/components/au-toolbar'; @@ -79,6 +80,7 @@ export default interface AppuniversumRegistry { AuNavigationLink: typeof AuNavigationLink; AuPanel: typeof AuPanel; AuPill: typeof AuPill; + AuRadioGroup: typeof AuRadioGroup; AuRadio: typeof AuRadio; AuToolbar: typeof AuToolbar; diff --git a/tests/integration/components/au-radio-group-test.gts b/tests/integration/components/au-radio-group-test.gts new file mode 100644 index 000000000..133af9ac5 --- /dev/null +++ b/tests/integration/components/au-radio-group-test.gts @@ -0,0 +1,112 @@ +import AuRadioGroup from '@appuniversum/ember-appuniversum/components/au-radio-group'; +import { settled } from '@ember/test-helpers'; +import { click, render } from '@ember/test-helpers'; +import { tracked } from '@glimmer/tracking'; +import { setupRenderingTest } from 'dummy/tests/helpers'; +import { module, test } from 'qunit'; + +module('Integration | Component | au-radio-group', function (hooks) { + setupRenderingTest(hooks); + + test('it can be given a name', async function (assert) { + await render( + , + ); + assert.dom('[data-test-foo]').hasAttribute('name', 'foo'); + }); + + test('if it is given no name, it will automatically generate a unique name', async function (assert) { + await render( + , + ); + assert.dom('[data-test-foo]').hasAttribute('name'); + }); + + test('it can be given a value', async function (assert) { + const state = new TestState(); + state.value = 'bar'; + + await render( + , + ); + + assert.dom('[data-test-bar]').isChecked(); + assert.dom('[data-test-foo]').isNotChecked(); + + state.value = 'foo'; + await settled(); + assert.dom('[data-test-bar]').isNotChecked(); + assert.dom('[data-test-foo]').isChecked(); + }); + + test('it can be disabled', async function (assert) { + const state = new TestState(); + state.disabled = true; + await render( + , + ); + + assert.dom('[data-test-foo]').isDisabled(); + + state.disabled = false; + await settled(); + assert.dom('[data-test-foo]').isNotDisabled(); + }); + + test('it calls the provided @onChange action when its state changes by user input', async function (assert) { + let currentValue: string | undefined; + + const handleChange = (value: string | undefined, event: Event) => { + currentValue = value; + assert.true(event instanceof Event); + }; + + await render( + , + ); + + await click('[data-test-bar]'); + assert.strictEqual(currentValue, 'bar'); + + await click('[data-test-foo]'); + assert.strictEqual(currentValue, 'foo'); + }); + + test('it adds any extra attributes to the group element', async function (assert) { + await render( + , + ); + assert.dom('[data-test-radio-group]').hasAttribute('foo', 'bar'); + }); +}); + +class TestState { + @tracked disabled?: boolean; + @tracked value?: string; +} diff --git a/tests/integration/components/au-radio-group-test.js b/tests/integration/components/au-radio-group-test.js deleted file mode 100644 index b8df0a2ba..000000000 --- a/tests/integration/components/au-radio-group-test.js +++ /dev/null @@ -1,87 +0,0 @@ -import { module, test } from 'qunit'; -import { setupRenderingTest } from 'dummy/tests/helpers'; -import { click, render } from '@ember/test-helpers'; -import { hbs } from 'ember-cli-htmlbars'; - -module('Integration | Component | au-radio-group', function (hooks) { - setupRenderingTest(hooks); - - test('it can be given a name', async function (assert) { - await render(hbs` - - Foo - - `); - assert.dom('[data-test-foo]').hasAttribute('name', 'foo'); - }); - - test('if it is given no name, it will automatically generate a unique name', async function (assert) { - await render(hbs` - - Foo - - `); - assert.dom('[data-test-foo]').hasAttribute('name'); - }); - - test('it can be given a value', async function (assert) { - this.groupValue = 'bar'; - await render(hbs` - - Foo - Bar - - `); - - assert.dom('[data-test-bar]').isChecked(); - assert.dom('[data-test-foo]').isNotChecked(); - - this.set('groupValue', 'foo'); - assert.dom('[data-test-bar]').isNotChecked(); - assert.dom('[data-test-foo]').isChecked(); - }); - - test('it can be disabled', async function (assert) { - this.disabled = true; - await render(hbs` - - Foo - - `); - - assert.dom('[data-test-foo]').isDisabled(); - - this.set('disabled', false); - assert.dom('[data-test-foo]').isNotDisabled(); - }); - - test('it calls the provided @onChange action when its state changes by user input', async function (assert) { - this.value = null; - this.handleChange = (value, event) => { - this.set('value', value); - assert.true(event instanceof Event); - }; - - await render(hbs` - - Foo - Bar - - `); - - await click('[data-test-bar]'); - assert.strictEqual(this.value, 'bar'); - - await click('[data-test-foo]'); - assert.strictEqual(this.value, 'foo'); - }); - - test('it adds any extra attributes to the group element', async function (assert) { - await render(hbs` - - Foo - - `); - assert.dom('[data-test-radio-group]').hasAttribute('foo', 'bar'); - }); -}); diff --git a/tests/integration/components/loose-mode-test.ts b/tests/integration/components/loose-mode-test.ts index e599ea0dd..fb81d27ad 100644 --- a/tests/integration/components/loose-mode-test.ts +++ b/tests/integration/components/loose-mode-test.ts @@ -255,6 +255,13 @@ module('Integration | Component | Loose mode', function (hooks) { assert.dom('[data-test-pill]').exists(); }); + test('`` resolves in loose mode', async function (assert) { + await render(hbs` + + `); + assert.dom('[data-test-radio-group]').exists(); + }); + test('`` resolves in loose mode', async function (assert) { await render(hbs`