Skip to content

Commit

Permalink
fix(stack-view): space not working in textarea (backport to 16.x) (#1482
Browse files Browse the repository at this point in the history
)

Backport 4c06b91 from #1480. <br> ## PR
Checklist

Please check if your PR fulfills the following requirements:

- [x] Tests for the changes have been added (for bug fixes / features)
- [ ] Docs have been added / updated (for bug fixes / features)
- [ ] If applicable, have a visual design approval

## PR Type

What kind of change does this PR introduce?

- [x] Bugfix
- [ ] Feature
- [ ] Code style update (formatting, local variables)
- [ ] Refactoring (no functional changes, no api changes)
- [ ] Build related changes
- [ ] CI related changes
- [ ] Documentation content changes
- [ ] Other... Please describe:

## What is the current behavior?
Using `Space` button in textarea in stack view triggers expand/collapse
and is not registred in it.

Issue Number: CDE-1939

## What is the new behavior?
Using `Space` button on any interactable HTML element (Textarea, Input,
Select, Link, Button) register the event on the element and don&#39;t
trigger expand/collapse on stack view block.

## Does this PR introduce a breaking change?

- [ ] Yes
- [x] No


## Other information

Co-authored-by: Valentin Mladenov <valentin.mladenov@broadcom.com>
  • Loading branch information
github-actions[bot] and valentin-mladenov authored Jul 16, 2024
1 parent 05d7b49 commit fed07eb
Show file tree
Hide file tree
Showing 2 changed files with 157 additions and 2 deletions.
155 changes: 154 additions & 1 deletion projects/angular/src/data/stack-view/stack-block.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,13 @@ import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testin
import { FormsModule } from '@angular/forms';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';

import { Keys } from '../../utils/enums/keys.enum';
import { ClrStackBlock } from './stack-block';
import { ClrStackView } from './stack-view';
import { ClrStackViewModule } from './stack-view.module';

import Spy = jasmine.Spy;

@Component({
template: `
<clr-stack-block [clrStackViewLevel]="ariaLevel">
Expand Down Expand Up @@ -82,6 +85,42 @@ class DynamicBlockWithInput {
expanded = false;
}

@Component({
template: `
<clr-stack-block #main [(clrSbExpanded)]="expanded">
<clr-stack-label>Label</clr-stack-label>
<clr-stack-content>Content</clr-stack-content>
<clr-stack-block #test [(clrSbExpanded)]="testExpanded">
<clr-stack-label>Button content</clr-stack-label>
<clr-stack-content>
<button class="btn btn-primary" (click)="content = content + ' Button click'">Button</button>
<input type="text" clrStackInput [(ngModel)]="content" />
<a href="javascript://">Link</a>
<select>
<option>Option 1</option>
<option>Option 2</option>
</select>
<textarea clrTextarea [(ngModel)]="content"></textarea>
<div id="to-open" tabindex="0">Text</div>
</clr-stack-content>
<clr-stack-block #inner>
<clr-stack-label>Inner label</clr-stack-label>
<clr-stack-content>Inner content</clr-stack-content>
</clr-stack-block>
</clr-stack-block>
</clr-stack-block>
`,
})
class BlocksWithIinteractiveElements {
@ViewChild('main') blockInstance: ClrStackBlock;
@ViewChild('test') testBlockInstance: ClrStackBlock;
@ViewChild('inner') innerBlockInstance: ClrStackBlock;

content = '';
expanded = true;
testExpanded = false;
}

export default function (): void {
'use strict';
describe('StackBlock', () => {
Expand All @@ -91,7 +130,7 @@ export default function (): void {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [ClrStackViewModule, NoopAnimationsModule, FormsModule],
declarations: [BasicBlock, DynamicBlock, DynamicBlockWithInput, NestedBlocks],
declarations: [BasicBlock, DynamicBlock, DynamicBlockWithInput, NestedBlocks, BlocksWithIinteractiveElements],
providers: [ClrStackView],
});
});
Expand Down Expand Up @@ -367,5 +406,119 @@ export default function (): void {
expect(blok.getAttribute('aria-level')).toBe('4');
});
}));

describe('Space interaction with elements', () => {
let spy: Spy<any>;
const keyDown = new KeyboardEvent('keydown', { key: Keys.Space, bubbles: true });
const keyUp = new KeyboardEvent('keyup', { key: Keys.Space, bubbles: true });

function executeElementTest(elementName: string) {
const element = fixture.nativeElement.querySelector(elementName) as HTMLElement;

element.focus();

element.dispatchEvent(keyDown);
element.dispatchEvent(keyUp);
fixture.detectChanges();

expect(spy).toHaveBeenCalledWith(keyUp);
}

beforeEach(fakeAsync(() => {
fixture = TestBed.createComponent(BlocksWithIinteractiveElements);
fixture.whenRenderingDone();
fixture.detectChanges();
spy = spyOn(fixture.componentInstance.testBlockInstance, 'toggleExpand').and.callThrough();
}));

it('Events sent through input element should NOT expand block', () => {
expect(getBlockInstance(fixture).expanded).toBeTruthy();
expect(fixture.componentInstance.testBlockInstance.expanded).toBeFalsy();
expect(fixture.componentInstance.testExpanded).toBeFalsy();

executeElementTest('input');

// Expect no changes
expect(fixture.componentInstance.testBlockInstance.expanded).toBeFalsy();
expect(fixture.componentInstance.testExpanded).toBeFalsy();
});

it('Events sent through textarea element should NOT expand block', () => {
expect(getBlockInstance(fixture).expanded).toBeTruthy();
expect(fixture.componentInstance.testBlockInstance.expanded).toBeFalsy();
expect(fixture.componentInstance.testExpanded).toBeFalsy();

executeElementTest('textarea');

// Expect no changes
expect(fixture.componentInstance.testBlockInstance.expanded).toBeFalsy();
expect(fixture.componentInstance.testExpanded).toBeFalsy();
});

it('Events sent through button element should NOT expand block', () => {
expect(getBlockInstance(fixture).expanded).toBeTruthy();
expect(fixture.componentInstance.testBlockInstance.expanded).toBeFalsy();
expect(fixture.componentInstance.testExpanded).toBeFalsy();

executeElementTest('button');

// Expect no changes
expect(fixture.componentInstance.testBlockInstance.expanded).toBeFalsy();
expect(fixture.componentInstance.testExpanded).toBeFalsy();
});

it('Events sent through link element should NOT expand block', () => {
expect(getBlockInstance(fixture).expanded).toBeTruthy();
expect(fixture.componentInstance.testBlockInstance.expanded).toBeFalsy();
expect(fixture.componentInstance.testExpanded).toBeFalsy();

executeElementTest('a');

// Expect no changes
expect(fixture.componentInstance.testBlockInstance.expanded).toBeFalsy();
expect(fixture.componentInstance.testExpanded).toBeFalsy();
});

it('Events sent through select element should NOT expand block', () => {
expect(getBlockInstance(fixture).expanded).toBeTruthy();
expect(fixture.componentInstance.testBlockInstance.expanded).toBeFalsy();
expect(fixture.componentInstance.testExpanded).toBeFalsy();

executeElementTest('select');

// Expect no changes
expect(fixture.componentInstance.testBlockInstance.expanded).toBeFalsy();
expect(fixture.componentInstance.testExpanded).toBeFalsy();
});

it('Events sent through select option element should NOT expand block', () => {
expect(getBlockInstance(fixture).expanded).toBeTruthy();
expect(fixture.componentInstance.testBlockInstance.expanded).toBeFalsy();
expect(fixture.componentInstance.testExpanded).toBeFalsy();

const select = fixture.nativeElement.querySelector('select') as HTMLElement;

select.click();
fixture.detectChanges();

executeElementTest('option');

// Expect no changes
expect(fixture.componentInstance.testBlockInstance.expanded).toBeFalsy();
expect(fixture.componentInstance.testExpanded).toBeFalsy();
});

it('Events sent through div element should expand block', () => {
expect(getBlockInstance(fixture).expanded).toBeTruthy();
expect(fixture.componentInstance.testBlockInstance.expanded).toBeFalsy();
expect(fixture.componentInstance.testExpanded).toBeFalsy();

executeElementTest('div#to-open[tabindex="0"]');

// Expect changes
expect(fixture.componentInstance.testBlockInstance.expanded).toBeTruthy();
expect(fixture.componentInstance.testExpanded).toBeTruthy();
});
});
});
}
4 changes: 3 additions & 1 deletion projects/angular/src/data/stack-view/stack-block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,5 +207,7 @@ export class ClrStackBlock implements OnInit {
function eventIsInputEvent(event?: Event) {
const targetElement = event?.target as HTMLElement;

return targetElement?.tagName === 'INPUT';
return targetElement?.tagName
? ['INPUT', 'TEXTAREA', 'BUTTON', 'A', 'SELECT', 'OPTION'].includes(targetElement.tagName)
: false;
}

0 comments on commit fed07eb

Please sign in to comment.