Skip to content

Commit

Permalink
refactor(dropdown, dropdown-item-group, dropdown-item): `getElementPr…
Browse files Browse the repository at this point in the history
…op` is refactored out in favor of inheritable props set directly on parent (#7582)

**Related Issue:** #6038

## Summary

`getElementProp` is refactored out across child components as an
outdated pattern in favor of inheritable props set directly on the
parent.

The logic for setting these props thus moves to the parent, getting rid
of the `getElementProp` altogether. The parent component gets a
`mutationObserver` to do this as well as `watchers` for when it needs to
modify the children.

Inherited props addressed:

- [x]  `scale` is inherited from `dropdown`,
- [x] `selectionMode` is inherited from `dropdown-item-group`, so there
can be a mix of types within one dropdown.
  • Loading branch information
Elijbet authored Aug 24, 2023
1 parent e4dd104 commit b5fa7f4
Show file tree
Hide file tree
Showing 7 changed files with 1,139 additions and 1,066 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,24 @@
import { renders, hidden } from "../../tests/commonTests";
import { defaults, hidden, reflects, renders } from "../../tests/commonTests";

describe("calcite-dropdown-group", () => {
describe("defaults", () => {
defaults("calcite-dropdown-group", [
{
propertyName: "selectionMode",
defaultValue: "single",
},
]);
});

describe("reflects", () => {
reflects("calcite-dropdown-group", [
{
propertyName: "selectionMode",
value: "single",
},
]);
});

describe("renders", () => {
renders("calcite-dropdown-group", { display: "block" });
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ import {
Listen,
Prop,
VNode,
Watch,
} from "@stencil/core";
import { getElementProp } from "../../utils/dom";
import { Scale, SelectionMode } from "../interfaces";
import { RequestedItem } from "./interfaces";
import { CSS } from "./resources";
import { createObserver } from "../../utils/observers";
import { CSS } from "../dropdown-item/resources";

/**
* @slot - A slot for adding `calcite-dropdown-item`s.
*/
Expand All @@ -34,18 +36,25 @@ export class DropdownGroup {
@Prop({ reflect: true }) groupTitle: string;

/**
* Specifies the component's selection mode, where
* `"multiple"` allows any number of (or no) selected `calcite-dropdown-item`s,
* `"single"` allows and requires one selected `calcite-dropdown-item`, and
* `"none"` does not allow selection on `calcite-dropdown-item`s.
* Specifies the size of the component inherited from the parent `calcite-dropdown`, defaults to `m`.
*
* @internal
*/
@Prop({ reflect: true }) selectionMode: Extract<"single" | "none" | "multiple", SelectionMode> =
"single";
@Prop() scale: Scale = "m";

/**
* Specifies the size of the component.
* Specifies the selection mode for `calcite-dropdown-item` children, defaults to `single`:
* - `multiple` allows any number of selected items,
* - `single` allows only one selection (default),
* - `none` doesn't allow for any selection.
*/
@Prop({ reflect: true }) scale: Scale;
@Prop({ reflect: true }) selectionMode: Extract<"none" | "single" | "multiple", SelectionMode> =
"single";

@Watch("selectionMode")
handlePropsChange(): void {
this.updateItems();
}

//--------------------------------------------------------------------------
//
Expand All @@ -64,12 +73,15 @@ export class DropdownGroup {
//
//--------------------------------------------------------------------------

connectedCallback(): void {
this.updateItems();
}

componentWillLoad(): void {
this.groupPosition = this.getGroupPosition();
}

render(): VNode {
const scale: Scale = this.scale || getElementProp(this.el, "scale", "m");
const groupTitle = this.groupTitle ? (
<span aria-hidden="true" class="dropdown-title">
{this.groupTitle}
Expand All @@ -83,12 +95,9 @@ export class DropdownGroup {
<Host aria-label={this.groupTitle} role="group">
<div
class={{
container: true,
[CSS.containerSmall]: scale === "s",
[CSS.containerMedium]: scale === "m",
[CSS.containerLarge]: scale === "l",
[CSS.container]: true,
[`${CSS.container}--${this.scale}`]: true,
}}
title={this.groupTitle}
>
{dropdownSeparator}
{groupTitle}
Expand Down Expand Up @@ -131,6 +140,14 @@ export class DropdownGroup {
/** the requested item */
private requestedDropdownItem: HTMLCalciteDropdownItemElement;

private updateItems = (): void => {
Array.from(this.el.querySelectorAll("calcite-dropdown-item")).forEach(
(item) => (item.selectionMode = this.selectionMode)
);
};

mutationObserver = createObserver("mutation", () => this.updateItems());

//--------------------------------------------------------------------------
//
// Private Methods
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
Prop,
VNode,
} from "@stencil/core";
import { getElementProp, toAriaBoolean } from "../../utils/dom";
import { toAriaBoolean } from "../../utils/dom";
import { ItemKeyboardEvent } from "../dropdown/interfaces";
import { RequestedItem } from "../dropdown-group/interfaces";
import { FlipContext, Scale, SelectionMode } from "../interfaces";
Expand Down Expand Up @@ -99,6 +99,23 @@ export class DropdownItem implements LoadableComponent {
this.el?.focus();
}

/**
* Specifies the selection mode inherited from `calcite-dropdown-group`, defaults to `single`:
* - `multiple` allows any number of selected items,
* - `single` allows only one selection (default),
* - `none` doesn't allow for any selection.
*
* @internal
*/
@Prop() selectionMode: Extract<"none" | "single" | "multiple", SelectionMode> = "single";

/**
* Specifies the size of the component inherited from `calcite-dropdown`, defaults to `m`.
*
* @internal
*/
@Prop() scale: Scale = "m";

//--------------------------------------------------------------------------
//
// Lifecycle
Expand All @@ -119,8 +136,8 @@ export class DropdownItem implements LoadableComponent {
}

render(): VNode {
const scale = getElementProp(this.el, "scale", this.scale);
const { href, selectionMode, label, iconFlipRtl } = this;
const { href, selectionMode, label, iconFlipRtl, scale } = this;

const iconStartEl = (
<calcite-icon
class={CSS.iconStart}
Expand Down Expand Up @@ -183,11 +200,9 @@ export class DropdownItem implements LoadableComponent {
<Host aria-checked={itemAria} aria-label={!href ? label : ""} role={itemRole} tabindex="0">
<div
class={{
container: true,
[CSS.container]: true,
[CSS.containerLink]: !!href,
[CSS.containerSmall]: scale === "s",
[CSS.containerMedium]: scale === "m",
[CSS.containerLarge]: scale === "l",
[`${CSS.container}--${scale}`]: true,
[CSS.containerMulti]: selectionMode === "multiple",
[CSS.containerSingle]: selectionMode === "single",
[CSS.containerNone]: selectionMode === "none",
Expand Down Expand Up @@ -273,23 +288,16 @@ export class DropdownItem implements LoadableComponent {
/** requested item */
private requestedDropdownItem: HTMLCalciteDropdownItemElement;

/** what selection mode is the parent dropdown group in */
private selectionMode: Extract<"none" | "single" | "multiple", SelectionMode>;

/** if href is requested, track the rendered child link*/
private childLink: HTMLAnchorElement;

/** Specifies the scale of dropdown-item controlled by the parent, defaults to m */
scale: Scale = "m";

//--------------------------------------------------------------------------
//
// Private Methods
//
//--------------------------------------------------------------------------

private initialize(): void {
this.selectionMode = getElementProp(this.el, "selection-mode", "single");
this.parentDropdownGroupEl = this.el.closest("calcite-dropdown-group");
if (this.selectionMode === "none") {
this.selected = false;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
export const CSS = {
container: "container",
containerLink: "container--link",
containerSmall: "container--s",
containerMedium: "container--m",
containerLarge: "container--l",
containerMulti: "container--multi-selection",
containerSingle: "container--single-selection",
containerNone: "container--none-selection",
Expand Down
Loading

0 comments on commit b5fa7f4

Please sign in to comment.