Skip to content

Commit e334baa

Browse files
committed
Feat: Add onFocusOut tests to PopoverPrimitive and related components
1 parent f6d0478 commit e334baa

File tree

4 files changed

+278
-0
lines changed

4 files changed

+278
-0
lines changed

showcase/tests/integration/components/hds/dropdown/index-test.gts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { module, test } from 'qunit';
77
import { on } from '@ember/modifier';
88
import { render, click, find } from '@ember/test-helpers';
99
import style from 'ember-style-modifier';
10+
import { TrackedObject } from 'tracked-built-ins';
1011

1112
import { HdsDropdown } from '@hashicorp/design-system-components/components';
1213

@@ -329,4 +330,65 @@ module('Integration | Component | hds/dropdown/index', function (hooks) {
329330
assert.dom('#test-dropdown ul').hasAttribute('role', 'listbox');
330331
assert.dom('#test-dropdown ul').hasAttribute('aria-labelledby', buttonId);
331332
});
333+
334+
// CALLBACKS
335+
336+
test('it should invoke the `onClose` callback', async function (assert) {
337+
let status;
338+
const onClose = () => {
339+
status = 'closed';
340+
};
341+
342+
await render(
343+
<template>
344+
<HdsDropdown @onClose={{onClose}} id="test-dropdown" as |D|>
345+
<D.ToggleButton @text="toggle button" id="test-toggle-button" />
346+
</HdsDropdown>
347+
</template>,
348+
);
349+
// toggle the visibility
350+
await click('button#test-toggle-button');
351+
// toggle it again
352+
await click('button#test-toggle-button');
353+
assert.strictEqual(status, 'closed');
354+
});
355+
356+
test('it should invoke the `onFocusOut` callback', async function (assert) {
357+
const context = new TrackedObject({
358+
status: '',
359+
showButton: true,
360+
});
361+
362+
const onFocusOut = () => {
363+
context.status = 'focus-out';
364+
};
365+
366+
const onClickDemoButton = () => {
367+
context.showButton = false;
368+
};
369+
370+
await render(
371+
<template>
372+
<HdsDropdown @onFocusOut={{onFocusOut}} id="test-dropdown" as |D|>
373+
<D.ToggleButton @text="toggle button" id="test-toggle-button" />
374+
<D.Generic>
375+
{{#if context.showButton}}
376+
<button
377+
type="button"
378+
id="test-dropdown-demo-button"
379+
{{on "click" onClickDemoButton}}
380+
>
381+
Hide me
382+
</button>
383+
{{/if}}
384+
</D.Generic>
385+
</HdsDropdown>
386+
</template>,
387+
);
388+
// toggle the visibility
389+
await click('button#test-toggle-button');
390+
// click outside of the dropdown to trigger focus out
391+
await click('#test-dropdown-demo-button');
392+
assert.strictEqual(context.status, 'focus-out');
393+
});
332394
});

showcase/tests/integration/components/hds/filter-bar/filters-dropdown-test.gts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import { module, test } from 'qunit';
77
import { setupRenderingTest } from 'showcase/tests/helpers';
8+
import { on } from '@ember/modifier';
89
import { render, click } from '@ember/test-helpers';
910
import { TrackedObject } from 'tracked-built-ins';
1011

@@ -187,5 +188,55 @@ module(
187188
assert.ok(context.isClicked);
188189
assert.true(Object.keys(context.filters).length === 0);
189190
});
191+
192+
// CALLBACKS: ONFOCUSOUT
193+
194+
test('it should call the onFocusOut callback', async function (assert) {
195+
const context = new TrackedObject({
196+
status: '',
197+
showButton: true,
198+
});
199+
200+
const onFocusOut = () => {
201+
context.status = 'focus-out';
202+
};
203+
204+
const onClickDemoButton = () => {
205+
context.showButton = false;
206+
};
207+
208+
await render(
209+
<template>
210+
<HdsFilterBarFiltersDropdown
211+
@filters={{EMPTY_FILTERS}}
212+
@onFocusOut={{onFocusOut}}
213+
id="test-filters-dropdown"
214+
as |D|
215+
>
216+
<D.FilterGroup
217+
@key="demo-generic"
218+
@type="generic"
219+
@text="Generic"
220+
as |F|
221+
>
222+
<F.Generic>
223+
{{#if context.showButton}}
224+
<button
225+
type="button"
226+
id="test-filters-dropdown-demo-button"
227+
{{on "click" onClickDemoButton}}
228+
>
229+
Hide me
230+
</button>
231+
{{/if}}
232+
</F.Generic>
233+
</D.FilterGroup>
234+
</HdsFilterBarFiltersDropdown>
235+
</template>,
236+
);
237+
await click('.hds-dropdown-toggle-button');
238+
await click('#test-filters-dropdown-demo-button');
239+
assert.strictEqual(context.status, 'focus-out');
240+
});
190241
},
191242
);

showcase/tests/integration/components/hds/popover-primitive/index-test.gts

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
settled,
1414
setupOnerror,
1515
} from '@ember/test-helpers';
16+
import { on } from '@ember/modifier';
1617
import { hash } from '@ember/helper';
1718
import { TrackedObject } from 'tracked-built-ins';
1819

@@ -274,6 +275,121 @@ module(
274275
assert.strictEqual(context.status, 'closed');
275276
});
276277

278+
test('it should invoke the `onFocusOut` callback if focus is lost, and not close the popover', async function (assert) {
279+
const context = new TrackedObject({
280+
status: '',
281+
showButton: true,
282+
});
283+
284+
const onFocusOut = () => {
285+
context.status = 'focus-out';
286+
};
287+
288+
const onClickDemoButton = () => {
289+
context.showButton = false;
290+
};
291+
292+
await render(
293+
<template>
294+
<HdsPopoverPrimitive
295+
@enableClickEvents={{true}}
296+
@onFocusOut={{onFocusOut}}
297+
as |PP|
298+
>
299+
<div {{PP.setupPrimitiveContainer}}>
300+
<button
301+
{{PP.setupPrimitiveToggle}}
302+
id="test-popover-primitive-toggle"
303+
type="button"
304+
/>
305+
<div
306+
{{PP.setupPrimitivePopover anchoredPositionOptions=(hash)}}
307+
id="test-popover-primitive-content"
308+
>
309+
{{#if context.showButton}}
310+
<button
311+
type="button"
312+
id="test-popover-primitive-demo-button"
313+
{{on "click" onClickDemoButton}}
314+
>
315+
Hide me
316+
</button>
317+
{{/if}}
318+
</div>
319+
</div>
320+
</HdsPopoverPrimitive>
321+
</template>,
322+
);
323+
324+
await click('#test-popover-primitive-toggle');
325+
assert.dom('#test-popover-primitive-content').isVisible();
326+
327+
// trigger the dynamic template change that will cause the popover content to lose focus
328+
await click('#test-popover-primitive-demo-button');
329+
assert.strictEqual(context.showButton, false);
330+
assert.strictEqual(context.status, 'focus-out');
331+
assert.dom('#test-popover-primitive-content').isVisible();
332+
});
333+
334+
test('it should not invoke the `onFocusOut` callback if focus is moved outside of the popover to another element', async function (assert) {
335+
const context = new TrackedObject({
336+
status: '',
337+
showButton: true,
338+
});
339+
340+
const onFocusOut = () => {
341+
context.status = 'focus-out';
342+
};
343+
344+
const onClickDemoButton = () => {
345+
context.showButton = false;
346+
};
347+
348+
await render(
349+
<template>
350+
<HdsPopoverPrimitive
351+
@enableClickEvents={{true}}
352+
@onFocusOut={{onFocusOut}}
353+
as |PP|
354+
>
355+
<div {{PP.setupPrimitiveContainer}}>
356+
<button
357+
{{PP.setupPrimitiveToggle}}
358+
id="test-popover-primitive-toggle"
359+
type="button"
360+
/>
361+
<div
362+
{{PP.setupPrimitivePopover anchoredPositionOptions=(hash)}}
363+
id="test-popover-primitive-content"
364+
>
365+
<button
366+
type="button"
367+
id="test-popover-primitive-demo-button"
368+
{{on "click" onClickDemoButton}}
369+
>
370+
Demo button
371+
</button>
372+
</div>
373+
</div>
374+
</HdsPopoverPrimitive>
375+
<button type="button" id="test-popover-primitive-external-button">
376+
External button
377+
</button>
378+
</template>,
379+
);
380+
381+
await click('#test-popover-primitive-toggle');
382+
assert.dom('#test-popover-primitive-content').isVisible();
383+
384+
// focus on the button inside the popover
385+
await focus('#test-popover-primitive-demo-button');
386+
// move focus to the button outside of the popover
387+
await focus('#test-popover-primitive-external-button');
388+
// the popover should be closed, and the `onFocusOut` callback should not be invoked
389+
assert.dom('#test-popover-primitive-content').isNotVisible();
390+
assert.strictEqual(context.status, '');
391+
});
392+
277393
// ANCHORED POSITION OPTIONS
278394

279395
// notice: since these options are forwarded to the `hds-anchored-position` modifier and there are specific tests for it, we're not going to test them here

showcase/tests/integration/components/hds/rich-tooltip/index-test.gts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import {
1212
render,
1313
resetOnerror,
1414
} from '@ember/test-helpers';
15+
import { on } from '@ember/modifier';
16+
import { TrackedObject } from 'tracked-built-ins';
1517

1618
import { HdsRichTooltip } from '@hashicorp/design-system-components/components';
1719

@@ -149,6 +151,53 @@ module('Integration | Component | hds/rich-tooltip/index', function (hooks) {
149151
assert.strictEqual(status, 'closed');
150152
});
151153

154+
test('it should invoke the `onFocusOut` callback if focus is lost, and not close the popover', async function (assert) {
155+
const context = new TrackedObject({
156+
status: '',
157+
showButton: true,
158+
});
159+
160+
const onFocusOut = () => {
161+
context.status = 'focus-out';
162+
};
163+
164+
const onClickDemoButton = () => {
165+
context.showButton = false;
166+
};
167+
168+
await render(
169+
<template>
170+
<HdsRichTooltip
171+
@enableClickEvents={{true}}
172+
@onFocusOut={{onFocusOut}}
173+
as |RT|
174+
>
175+
<RT.Toggle />
176+
<RT.Bubble>
177+
{{#if context.showButton}}
178+
<button
179+
type="button"
180+
id="test-rich-tooltip-demo-button"
181+
{{on "click" onClickDemoButton}}
182+
>
183+
Hide me
184+
</button>
185+
{{/if}}
186+
</RT.Bubble>
187+
</HdsRichTooltip>
188+
</template>,
189+
);
190+
191+
await click('button.hds-rich-tooltip__toggle');
192+
assert.dom('.hds-rich-tooltip__bubble').isVisible();
193+
194+
// trigger the dynamic template change that will cause the tooltip bubble content to lose focus
195+
await click('#test-rich-tooltip-demo-button');
196+
assert.strictEqual(context.showButton, false);
197+
assert.strictEqual(context.status, 'focus-out');
198+
assert.dom('.hds-rich-tooltip__bubble').isVisible();
199+
});
200+
152201
// ANCHORED POSITION OPTIONS
153202
// unfortunately there is no easy/reliable way to test them here
154203

0 commit comments

Comments
 (0)