Skip to content

Commit

Permalink
allow programmatic control of the open property on select and combobox (
Browse files Browse the repository at this point in the history
microsoft#5590)

* allow programmatic control of the open property for select and combobox

* Change files
  • Loading branch information
radium-v authored Feb 24, 2022
1 parent ebe1234 commit 2caf345
Show file tree
Hide file tree
Showing 11 changed files with 83 additions and 19 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "none",
"comment": "allow programmatic control of the open property for select and combobox",
"packageName": "@microsoft/fast-components",
"email": "john.kreitlow@microsoft.com",
"dependentChangeType": "none"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "allow programmatic control of the open property for select and combobox",
"packageName": "@microsoft/fast-foundation",
"email": "john.kreitlow@microsoft.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ <h2>Default</h2>
<h2>Combobox with no options</h2>
<fast-combobox id="combobox-empty"></fast-combobox>

<h2>Combobox with open attribute</h2>
<fast-combobox id="combobox-with-open-attribute" open>
<fast-option>Small</fast-option>
<fast-option>Medium</fast-option>
<fast-option>Large</fast-option>
<fast-option>Extra Large</fast-option>
</fast-combobox>

<h2>Combobox with adjacent label</h2>
<label for="combobox-with-adjacent-label">Adjacent Label:</label>
<fast-combobox id="combobox-with-adjacent-label">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ <h2>Default</h2>
<h2>Select with no options</h2>
<fast-select id="select-empty"></fast-select>

<h2>Select with open attribute</h2>
<fast-select id="select-with-open-attribute" open>
<fast-option value="s">Small</fast-option>
<fast-option value="m">Medium</fast-option>
<fast-option value="l">Large</fast-option>
<fast-option value="xl">Extra Large</fast-option>
</fast-select>

<h2>Select with adjacent label</h2>
<label for="select-with-adjacent-label">Adjacent Label:</label>
<fast-select id="select-with-adjacent-label">
Expand Down
2 changes: 2 additions & 0 deletions packages/web-components/fast-foundation/docs/api-report.md
Original file line number Diff line number Diff line change
Expand Up @@ -2161,6 +2161,8 @@ export class Select extends FormAssociatedSelect {
// @internal
listbox: HTMLDivElement;
// @internal
listboxId: string;
// @internal
maxHeight: number;
// @internal
open: boolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,18 @@ describe("Combobox", () => {
await disconnect();
});

it("should display the listbox when the `open` property is true before connecting", async () => {
const { element, connect, disconnect } = await setup();

element.open = true;

await connect();

expect(element.hasAttribute("open")).to.be.true;

await disconnect();
});

describe("should NOT emit a 'change' event when the value changes by user input while open", () => {
it("via arrow down key", async () => {
const { element, connect, disconnect } = await setup();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export const comboboxTemplate: FoundationElementTemplate<
autocomplete="${x => x.autocomplete}"
class="${x => (x.open ? "open" : "")} ${x =>
x.disabled ? "disabled" : ""} ${x => x.position}"
?open="${x => x.open}"
tabindex="${x => (!x.disabled ? "0" : null)}"
@click="${(x, c) => x.clickHandler(c.event as MouseEvent)}"
@focusout="${(x, c) => x.focusoutHandler(c.event as FocusEvent)}"
Expand Down Expand Up @@ -55,6 +56,7 @@ export const comboboxTemplate: FoundationElementTemplate<
</div>
<div
class="listbox"
id="${x => x.listboxId}"
part="listbox"
role="listbox"
?disabled="${x => x.disabled}"
Expand Down
16 changes: 8 additions & 8 deletions packages/web-components/fast-foundation/src/combobox/combobox.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { attr, Observable, observable } from "@microsoft/fast-element";
import { attr, DOM, Observable, observable } from "@microsoft/fast-element";
import type { SyntheticViewTemplate } from "@microsoft/fast-element";
import { limit, uniqueId } from "@microsoft/fast-web-utilities";
import type { FoundationElementDefinition } from "../foundation-element";
Expand Down Expand Up @@ -105,7 +105,7 @@ export class Combobox extends FormAssociatedCombobox {
}

/**
* The unique id of the internal listbox.
* The unique id for the internal listbox element.
*
* @internal
*/
Expand Down Expand Up @@ -135,11 +135,15 @@ export class Combobox extends FormAssociatedCombobox {
public open: boolean = false;
protected openChanged() {
if (this.open) {
this.ariaControls = this.listbox.id;
this.ariaControls = this.listboxId;
this.ariaExpanded = "true";

this.setPositioning();
this.focusAndScrollOptionIntoView();

// focus is directed to the element when `open` is changed programmatically
DOM.queueUpdate(() => this.focus());

return;
}

Expand Down Expand Up @@ -280,10 +284,6 @@ export class Combobox extends FormAssociatedCombobox {
if (this.value) {
this.initialValue = this.value;
}

if (!this.listbox.id) {
this.listbox.id = uniqueId("listbox-");
}
}

/**
Expand Down Expand Up @@ -340,7 +340,7 @@ export class Combobox extends FormAssociatedCombobox {
this.control.focus();
if (this.firstSelectedOption) {
requestAnimationFrame(() => {
this.firstSelectedOption.scrollIntoView({ block: "nearest" });
this.firstSelectedOption?.scrollIntoView({ block: "nearest" });
});
}
}
Expand Down
16 changes: 13 additions & 3 deletions packages/web-components/fast-foundation/src/select/select.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,18 @@ describe("Select", () => {
await disconnect();
});

it("should display the listbox when the `open` property is true before connecting", async () => {
const { element, connect, disconnect } = await setup();

element.open = true;

await connect();

expect(element.hasAttribute("open")).to.be.true;

await disconnect();
});

describe("should NOT emit a 'change' event when the value changes by user input while open", () => {
it("via arrow down key", async () => {
const { element, connect, disconnect } = await setup();
Expand Down Expand Up @@ -805,9 +817,7 @@ describe("Select", () => {

const listboxId = element.listbox.id;

expect(element.getAttribute("aria-controls")).to.exist;

expect(element.getAttribute("aria-controls")).to.be.empty;
expect(element.getAttribute("aria-controls")).to.exist.and.be.empty;

element.open = true;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export const selectTemplate: FoundationElementTemplate<
aria-disabled="${x => x.ariaDisabled}"
aria-expanded="${x => x.ariaExpanded}"
aria-haspopup="listbox"
?open="${x => x.open}"
role="combobox"
tabindex="${x => (!x.disabled ? "0" : null)}"
@click="${(x, c) => x.clickHandler(c.event as MouseEvent)}"
Expand All @@ -43,6 +44,7 @@ export const selectTemplate: FoundationElementTemplate<
</div>
<div
class="listbox"
id="${x => x.listboxId}"
part="listbox"
role="listbox"
?disabled="${x => x.disabled}"
Expand Down
22 changes: 14 additions & 8 deletions packages/web-components/fast-foundation/src/select/select.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import { attr, Observable, observable } from "@microsoft/fast-element";
import { attr, DOM, Observable, observable } from "@microsoft/fast-element";
import type { SyntheticViewTemplate } from "@microsoft/fast-element";
import { uniqueId } from "@microsoft/fast-web-utilities";
import type { FoundationElementDefinition } from "../foundation-element";
import { DelegatesARIAListbox } from "../listbox";
import { DelegatesARIAListbox, Listbox } from "../listbox";
import type { ListboxOption } from "../listbox-option/listbox-option";
import { StartEnd } from "../patterns/start-end";
import type { StartEndOptions } from "../patterns/start-end";
import { applyMixins } from "../utilities/apply-mixins";
import { Listbox } from "../listbox";
import { FormAssociatedSelect } from "./select.form-associated";
import { SelectPosition } from "./select.options";

Expand Down Expand Up @@ -36,12 +35,16 @@ export class Select extends FormAssociatedSelect {
public open: boolean = false;
protected openChanged() {
if (this.open) {
this.ariaControls = this.listbox.id;
this.ariaControls = this.listboxId;
this.ariaExpanded = "true";

this.setPositioning();
this.focusAndScrollOptionIntoView();
this.indexWhenOpened = this.selectedIndex;

// focus is directed to the element when `open` is changed programmatically
DOM.queueUpdate(() => this.focus());

return;
}

Expand Down Expand Up @@ -166,6 +169,13 @@ export class Select extends FormAssociatedSelect {
*/
public listbox: HTMLDivElement;

/**
* The unique id for the internal listbox element.
*
* @internal
*/
public listboxId: string = uniqueId("listbox-");

/**
* Calculate and apply listbox positioning based on available viewport space.
*
Expand Down Expand Up @@ -402,10 +412,6 @@ export class Select extends FormAssociatedSelect {
public connectedCallback() {
super.connectedCallback();
this.forcedPosition = !!this.positionAttribute;

if (!this.listbox.id) {
this.listbox.id = uniqueId("listbox-");
}
}
}

Expand Down

0 comments on commit 2caf345

Please sign in to comment.