diff --git a/packages/components/src/components/hds/code-editor/description.hbs b/packages/components/src/components/hds/code-editor/description.hbs index 790747472d..a1a817352a 100644 --- a/packages/components/src/components/hds/code-editor/description.hbs +++ b/packages/components/src/components/hds/code-editor/description.hbs @@ -3,6 +3,13 @@ SPDX-License-Identifier: MPL-2.0 }} - + {{yield}} \ No newline at end of file diff --git a/packages/components/src/components/hds/code-editor/description.ts b/packages/components/src/components/hds/code-editor/description.ts index dfd404204a..cd83383f1d 100644 --- a/packages/components/src/components/hds/code-editor/description.ts +++ b/packages/components/src/components/hds/code-editor/description.ts @@ -3,18 +3,22 @@ * SPDX-License-Identifier: MPL-2.0 */ -import TemplateOnlyComponent from '@ember/component/template-only'; +import Component from '@glimmer/component'; import type { HdsTextBodySignature } from '../text/body'; +type HdsCodeEditorDescriptionElement = HdsTextBodySignature['Element']; export interface HdsCodeEditorDescriptionSignature { + Args: { + editorId: string; + onInsert: (element: HdsCodeEditorDescriptionElement) => void; + }; Blocks: { default: []; }; - Element: HdsTextBodySignature['Element']; + Element: HdsCodeEditorDescriptionElement; } -const HdsCodeEditorDescription = - TemplateOnlyComponent(); - -export default HdsCodeEditorDescription; +export default class HdsCodeEditorDescription extends Component { + private _id = `${this.args.editorId}-description`; +} diff --git a/packages/components/src/components/hds/code-editor/index.hbs b/packages/components/src/components/hds/code-editor/index.hbs index be1163bbb2..65cee7d973 100644 --- a/packages/components/src/components/hds/code-editor/index.hbs +++ b/packages/components/src/components/hds/code-editor/index.hbs @@ -18,7 +18,9 @@ {{yield (hash Title=(component "hds/code-editor/title" editorId=this._id onInsert=this.registerTitleElement) - Description=(component "hds/code-editor/description") + Description=(component + "hds/code-editor/description" editorId=this._id onInsert=this.registerDescriptionElement + ) Generic=(component "hds/code-editor/generic") ) }} @@ -50,6 +52,7 @@
{ @tracked private _isSetupComplete = false; @tracked private _value; @tracked private _titleId: string | undefined; + @tracked private _descriptionId: string | undefined; private _id = guidFor(this); @@ -81,6 +74,10 @@ export default class HdsCodeEditor extends Component { return this.args.ariaLabelledBy ?? this._titleId; } + get ariaDescribedBy(): string | undefined { + return this.args.ariaDescribedBy ?? this._descriptionId; + } + get hasActions(): boolean { return (this.args.hasCopyButton || this.args.hasFullScreenButton) ?? false; } @@ -110,6 +107,13 @@ export default class HdsCodeEditor extends Component { this._titleId = element.id; } + @action + registerDescriptionElement( + element: HdsCodeEditorDescriptionSignature['Element'] + ): void { + this._descriptionId = element.id; + } + @action toggleFullScreen(): void { this._isFullScreen = !this._isFullScreen; diff --git a/packages/components/src/modifiers/hds-code-editor.ts b/packages/components/src/modifiers/hds-code-editor.ts index 609554ba91..8b95aef6c0 100644 --- a/packages/components/src/modifiers/hds-code-editor.ts +++ b/packages/components/src/modifiers/hds-code-editor.ts @@ -27,6 +27,7 @@ type HdsCodeEditorBlurHandler = (editor: EditorView, event: FocusEvent) => void; export interface HdsCodeEditorSignature { Args: { Named: { + ariaDescribedBy?: string; ariaLabel?: string; ariaLabelledBy?: string; language?: HdsCodeEditorLanguages; @@ -155,7 +156,7 @@ export default class HdsCodeEditorModifier extends Modifier + ) { + this._setupEditorAriaLabel(editor, { ariaLabel, ariaLabelledBy }); + this._setupEditorAriaDescribedBy(editor, ariaDescribedBy); + } + private _loadLanguageTask = task( { drop: true }, async (language?: HdsCodeEditorLanguages) => { @@ -319,6 +348,7 @@ export default class HdsCodeEditorModifier extends Modifier {}); + + await render( + hbs`` + ); + + assert.dom('.hds-code-editor__description').exists(); + }); + + // @onInsert + test('it should call the `@onInsert` action when the description is inserted', async function (assert) { + const onInsert = sinon.spy(); + this.set('onInsert', onInsert); + + await render( + hbs`Test description` + ); + + assert.true(onInsert.calledOnce); + }); + } +); diff --git a/showcase/tests/integration/components/hds/code-editor/index-test.js b/showcase/tests/integration/components/hds/code-editor/index-test.js index 4eaf002d80..0f4e7e9850 100644 --- a/showcase/tests/integration/components/hds/code-editor/index-test.js +++ b/showcase/tests/integration/components/hds/code-editor/index-test.js @@ -64,6 +64,14 @@ module('Integration | Component | hds/code-editor/index', function (hooks) { await setupCodeEditor(hbs``); assert.dom('hds-code-editor__description').doesNotExist(); }); + test('when aria-describedby is not provided and the `Description` contextual component is yielded, it should use the description element id as the aria-describedby value', async function (assert) { + await setupCodeEditor( + hbs`Test Description` + ); + assert + .dom('.hds-code-editor__editor .cm-editor [role="textbox"]') + .hasAttribute('aria-describedby', 'test-description'); + }); // yielded block content test('it should render custom content in the toolbar when provided', async function (assert) { @@ -191,6 +199,16 @@ module('Integration | Component | hds/code-editor/index', function (hooks) { sinon.restore(); }); + // @ariaDescribedBy + test('it should render the component with an aria-describedby when provided', async function (assert) { + await setupCodeEditor( + hbs`` + ); + assert + .dom('.hds-code-editor__editor .cm-editor [role="textbox"]') + .hasAttribute('aria-describedby', 'test-description'); + }); + // @ariaLabel test('it should render the component with an aria-label when provided', async function (assert) { await setupCodeEditor( diff --git a/showcase/tests/integration/modifiers/hds-code-editor-test.js b/showcase/tests/integration/modifiers/hds-code-editor-test.js index f0a769e773..beeeb754c3 100644 --- a/showcase/tests/integration/modifiers/hds-code-editor-test.js +++ b/showcase/tests/integration/modifiers/hds-code-editor-test.js @@ -83,6 +83,16 @@ module('Integration | Modifier | hds-code-editor', function (hooks) { assert.ok(inputSpy.calledOnceWith('Test string')); }); + // ariaDescribedBy + test('it should render the editor with an aria-describedby when provided', async function (assert) { + await setupCodeEditor( + hbs`
` + ); + assert + .dom('#code-editor-wrapper .cm-editor [role="textbox"]') + .hasAttribute('aria-describedby', 'test-description'); + }); + // ariaLabel test('it should render the editor with an aria-label when provided', async function (assert) { await setupCodeEditor(