Skip to content

Commit ae167c0

Browse files
committed
Add Glint support to the AuDropdown component
1 parent 522c7b2 commit ae167c0

File tree

5 files changed

+83
-49
lines changed

5 files changed

+83
-49
lines changed

addon/components/au-dropdown.gjs addon/components/au-dropdown.gts

+38-23
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import { AuButton } from '@appuniversum/ember-appuniversum';
2-
import FloatingUiModifier from '@appuniversum/ember-appuniversum/private/modifiers/floating-ui';
31
import { hash } from '@ember/helper';
42
import { on } from '@ember/modifier';
53
import { action } from '@ember/object';
@@ -8,26 +6,41 @@ import { tracked } from '@glimmer/tracking';
86
import { focusTrap } from 'ember-focus-trap';
97
import { modifier } from 'ember-modifier';
108
import { ChevronDownIcon } from './icons/chevron-down';
9+
import AuButton from './au-button';
10+
import type { AuButtonSignature } from './au-button';
11+
import floatingUi from '../private/modifiers/floating-ui';
12+
13+
export interface AuDropdownSignature {
14+
Args: {
15+
alignment?: 'left' | 'right';
16+
alert?: boolean;
17+
hideText?: boolean;
18+
icon?: AuButtonSignature['Args']['icon'];
19+
iconAlignment?: AuButtonSignature['Args']['iconAlignment'];
20+
onClose?: () => unknown;
21+
size?: AuButtonSignature['Args']['size'];
22+
skin?: AuButtonSignature['Args']['skin'];
23+
title?: string;
24+
};
25+
Blocks: {
26+
default: [];
27+
};
28+
Element: HTMLDivElement;
29+
}
1130

12-
export default class AuDropdown extends Component {
13-
@tracked referenceElement = undefined;
14-
@tracked arrowElement = undefined;
31+
export default class AuDropdown extends Component<AuDropdownSignature> {
32+
// We use declare here, so TS doesn't consider `undefined` as part of the type since the initialisation happens after the constructor.
33+
@tracked declare referenceElement: HTMLElement;
34+
@tracked declare arrowElement: HTMLElement;
1535
@tracked dropdownOpen = false;
16-
floatingUi = FloatingUiModifier;
17-
18-
reference = modifier(
19-
(element) => {
20-
this.referenceElement = element;
21-
},
22-
{ eager: false },
23-
);
24-
25-
arrow = modifier(
26-
(element) => {
27-
this.arrowElement = element;
28-
},
29-
{ eager: false },
30-
);
36+
37+
reference = modifier((element: HTMLElement) => {
38+
this.referenceElement = element;
39+
});
40+
41+
arrow = modifier((element: HTMLElement) => {
42+
this.arrowElement = element;
43+
});
3144

3245
@action
3346
openDropdown() {
@@ -50,8 +63,10 @@ export default class AuDropdown extends Component {
5063
}
5164

5265
@action
53-
clickOutsideDeactivates(event) {
54-
let isClosedByToggleButton = this.referenceElement.contains(event.target);
66+
clickOutsideDeactivates(event: Event) {
67+
const isClosedByToggleButton = this.referenceElement?.contains(
68+
event.target as HTMLElement,
69+
);
5570

5671
if (!isClosedByToggleButton) {
5772
this.closeDropdown();
@@ -113,7 +128,7 @@ export default class AuDropdown extends Component {
113128
</AuButton>
114129
{{#if this.dropdownOpen}}
115130
<div
116-
{{this.floatingUi
131+
{{floatingUi
117132
this.referenceElement
118133
this.arrowElement
119134
defaultPlacement=this.alignment

addon/template-registry.ts

+2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import type AuContentHeader from '@appuniversum/ember-appuniversum/components/au
1414
import type AuContent from '@appuniversum/ember-appuniversum/components/au-content';
1515
import type AuDateInput from '@appuniversum/ember-appuniversum/components/au-date-input';
1616
import type AuDatePicker from '@appuniversum/ember-appuniversum/components/au-date-picker';
17+
import type AuDropdown from '@appuniversum/ember-appuniversum/components/au-dropdown';
1718
import type AuFieldset from '@appuniversum/ember-appuniversum/components/au-fieldset';
1819
import type AuFileCard from '@appuniversum/ember-appuniversum/components/au-file-card';
1920
import type AuFileUpload from '@appuniversum/ember-appuniversum/components/au-file-upload';
@@ -65,6 +66,7 @@ export default interface AppuniversumRegistry {
6566
AuContent: typeof AuContent;
6667
AuDateInput: typeof AuDateInput;
6768
AuDatePicker: typeof AuDatePicker;
69+
AuDropdown: typeof AuDropdown;
6870
AuFieldset: typeof AuFieldset;
6971
AuFileCard: typeof AuFileCard;
7072
AuFileUpload: typeof AuFileUpload;

tests/integration/components/au-dropdown-test.js tests/integration/components/au-dropdown-test.gts

+34-26
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,31 @@
1-
import { module, test } from 'qunit';
2-
import { setupRenderingTest } from 'ember-qunit';
1+
import AuDropdown from '@appuniversum/ember-appuniversum/components/au-dropdown';
32
import { click, render } from '@ember/test-helpers';
4-
import { hbs } from 'ember-cli-htmlbars';
3+
import { setupRenderingTest } from 'ember-qunit';
4+
import { module, test } from 'qunit';
55

66
module('Integration | Component | au-dropdown', function (hooks) {
77
setupRenderingTest(hooks);
88

99
test('it renders', async function (assert) {
10-
await render(hbs`
11-
<AuDropdown @title="foo">
12-
<button type="button" data-test-button>baz</button>
13-
</AuDropdown>
14-
`);
10+
await render(
11+
<template>
12+
<AuDropdown @title="foo">
13+
<button type="button" data-test-button>baz</button>
14+
</AuDropdown>
15+
</template>,
16+
);
1517

1618
assert.dom('[data-test-dropdown-title]').hasText('foo');
1719
});
1820

1921
test('it toggles the visibility of its content when clicking the dropdown button', async function (assert) {
20-
await render(hbs`
21-
<AuDropdown @title="foo">
22-
<button type="button" data-test-button>baz</button>
23-
</AuDropdown>
24-
`);
22+
await render(
23+
<template>
24+
<AuDropdown @title="foo">
25+
<button type="button" data-test-button>baz</button>
26+
</AuDropdown>
27+
</template>,
28+
);
2529

2630
assert.dom('[data-test-button]').isNotVisible('it is closed by default');
2731

@@ -39,11 +43,13 @@ module('Integration | Component | au-dropdown', function (hooks) {
3943
});
4044

4145
test('it toggles the visibility of its content when clicking children of the dropdown button', async function (assert) {
42-
await render(hbs`
43-
<AuDropdown @title="foo">
44-
<button type="button" data-test-button>baz</button>
45-
</AuDropdown>
46-
`);
46+
await render(
47+
<template>
48+
<AuDropdown @title="foo">
49+
<button type="button" data-test-button>baz</button>
50+
</AuDropdown>
51+
</template>,
52+
);
4753

4854
assert.dom('[data-test-button]').isNotVisible('it is closed by default');
4955

@@ -61,18 +67,20 @@ module('Integration | Component | au-dropdown', function (hooks) {
6167
});
6268

6369
test('it calls the @onClose function when the dropdown is closed', async function (assert) {
64-
this.onClose = () => {
70+
const onClose = () => {
6571
assert.step('@onClose');
6672
};
6773

68-
await render(hbs`
69-
<AuDropdown @title="foo" @onClose={{this.onClose}}>
70-
<button type="button" data-test-button>baz</button>
71-
</AuDropdown>
72-
`);
74+
await render(
75+
<template>
76+
<AuDropdown @title="foo" @onClose={{onClose}}>
77+
<button type="button" data-test-button>baz</button>
78+
</AuDropdown>
79+
</template>,
80+
);
7381

74-
await click('[data-test-dropdown-button]', 'open the dropdown');
75-
await click('[data-test-dropdown-button]', 'close the dropdown');
82+
await click('[data-test-dropdown-button]'); // open
83+
await click('[data-test-dropdown-button]'); // close
7684

7785
assert.verifySteps(['@onClose']);
7886
});

tests/integration/components/loose-mode-test.ts

+7
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,13 @@ module('Integration | Component | Loose mode', function (hooks) {
115115
`);
116116
});
117117

118+
test('`<AuDropdown>` resolves in loose mode', async function (assert) {
119+
await render(hbs`
120+
<AuDropdown data-test-dropdown />
121+
`);
122+
assert.dom('[data-test-dropdown]').exists();
123+
});
124+
118125
test('`<AuFieldset>` resolves in loose mode', async function (assert) {
119126
await render(hbs`<AuFieldset data-test-fieldset />`);
120127

types/ember-focus-trap/index.d.ts

+2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ type FocusTrapOptions = {
55
fallbackFocus?: string;
66
escapeDeactivates?: () => boolean;
77
allowOutsideClick?: boolean;
8+
clickOutsideDeactivates?: (Event) => boolean;
89
};
910

1011
interface Signature {
@@ -14,6 +15,7 @@ interface Signature {
1415
isActive?: boolean;
1516
additionalElements?: string[] | Element[];
1617
focusTrapOptions?: FocusTrapOptions;
18+
shouldSelfFocus?: boolean;
1719
};
1820
};
1921
}

0 commit comments

Comments
 (0)