Skip to content

Commit

Permalink
feat(split-button): make downloadable and linkable (#11520)
Browse files Browse the repository at this point in the history
**Related Issue:** #9126 

## Summary
Implemented ability to make the primary part of a split button into a downloadable link or an href link.
  • Loading branch information
josercarcamo authored Feb 12, 2025
1 parent 94b032c commit f87097f
Show file tree
Hide file tree
Showing 4 changed files with 253 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,22 @@ describe("calcite-split-button", () => {
propertyName: "placement",
defaultValue: "bottom-end",
},
{
propertyName: "download",
defaultValue: false,
},
{
propertyName: "href",
defaultValue: undefined,
},
{
propertyName: "rel",
defaultValue: undefined,
},
{
propertyName: "target",
defaultValue: undefined,
},
]);
});

Expand All @@ -30,6 +46,22 @@ describe("calcite-split-button", () => {
propertyName: "placement",
value: "bottom-end",
},
{
propertyName: "download",
value: true,
},
{
propertyName: "href",
value: "/",
},
{
propertyName: "rel",
value: "external",
},
{
propertyName: "target",
value: "_blank",
},
]);
});

Expand Down Expand Up @@ -261,4 +293,37 @@ describe("calcite-split-button", () => {
await dropdownCloseEvent;
expect(await positionContainer.isVisible()).toBe(false);
});

it("sets download attribute", async () => {
const page = await newE2EPage();
await page.setContent(`<calcite-split-button href="/">Continue</calcite-split-button>`);

const elementAsButton = await page.find("calcite-split-button >>> calcite-button");

expect(elementAsButton).not.toBeNull();
expect(await elementAsButton.getProperty("download")).toBe(false);
expect(elementAsButton).not.toHaveAttribute("download");

const element = await page.find("calcite-split-button");

element.setProperty("download", true);
await page.waitForChanges();

expect(await elementAsButton.getProperty("download")).toEqual(true);
expect(elementAsButton).toHaveAttribute("download");
expect(elementAsButton.getAttribute("download")).toBe("");

const newFilename = "my-cool-file.jpg";
element.setProperty("download", newFilename);
await page.waitForChanges();

expect(await elementAsButton.getProperty("download")).toBe(newFilename);
expect(elementAsButton.getAttribute("download")).toBe(newFilename);

element.setProperty("download", false);
await page.waitForChanges();

expect(await elementAsButton.getProperty("download")).toBe(false);
expect(elementAsButton).not.toHaveAttribute("download");
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -287,3 +287,35 @@ export const appearanceAndKindCombinations_TestOnly = (): string => html`
`;

export const loadingAndDisabled_TestOnly = (): string => html`<calcite-button loading disabled>Test</calcite-button>`;

export const primaryAsALink = (): string =>
html` <calcite-split-button
scale="m"
primary-text="Primary Option"
dropdown-icon-type="overflow"
href="split-button.html"
rel="external"
target="_blank"
>
<calcite-dropdown-group selection-mode="none">
<calcite-dropdown-item>Option 2</calcite-dropdown-item>
<calcite-dropdown-item>Option 3</calcite-dropdown-item>
<calcite-dropdown-item>Option 4</calcite-dropdown-item>
</calcite-dropdown-group>
</calcite-split-button>`;

export const primaryAsADownload = (): string =>
html` <calcite-split-button
download
scale="m"
primary-text="Primary Option"
primary-icon-start="download"
dropdown-icon-type="overflow"
href="/"
>
<calcite-dropdown-group selection-mode="none">
<calcite-dropdown-item>Option 2</calcite-dropdown-item>
<calcite-dropdown-item>Option 3</calcite-dropdown-item>
<calcite-dropdown-item>Option 4</calcite-dropdown-item>
</calcite-dropdown-group>
</calcite-split-button>`;
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
// @ts-strict-ignore
import { LitElement, property, createEvent, h, method, JsxNode } from "@arcgis/lumina";
import {
LitElement,
property,
createEvent,
h,
method,
JsxNode,
stringOrBoolean,
} from "@arcgis/lumina";
import { FlipPlacement, MenuPlacement, OverlayPositioning } from "../../utils/floating-ui";
import {
InteractiveComponent,
Expand Down Expand Up @@ -67,6 +75,14 @@ export class SplitButton extends LitElement implements InteractiveComponent, Loa
/** When `true`, interaction is prevented and the component is displayed with lower opacity. */
@property({ reflect: true }) disabled = false;

/**
* Prompts the user to save the linked URL instead of navigating to it. Can be used with or without a value:
* Without a value, the browser will suggest a filename/extension.
*
* @see [Global download attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#download).
*/
@property({ reflect: true, converter: stringOrBoolean }) download: string | boolean = false;

/** Specifies the icon used for the dropdown menu. */
@property({ reflect: true }) dropdownIconType: DropdownIconType = "chevron";

Expand All @@ -76,6 +92,9 @@ export class SplitButton extends LitElement implements InteractiveComponent, Loa
/** Specifies the component's fallback slotted content `placement` when it's initial or specified `placement` has insufficient space available. */
@property() flipPlacements: FlipPlacement[];

/** Specifies the URL of the linked resource, which can be set as an absolute or relative path. */
@property({ reflect: true }) href: string;

/** Specifies the kind of the component, which will apply to border and background, if applicable. */
@property({ reflect: true }) kind: Extract<"brand" | "danger" | "inverse" | "neutral", Kind> =
"brand";
Expand All @@ -92,6 +111,13 @@ export class SplitButton extends LitElement implements InteractiveComponent, Loa
*/
@property({ reflect: true }) overlayPositioning: OverlayPositioning = "absolute";

/**
* Defines the relationship between the `href` value and the current document.
*
* @mdn [rel](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel)
*/
@property({ reflect: true }) rel: string;

/**
* Determines where the component will be positioned relative to the container element.
*
Expand All @@ -117,6 +143,13 @@ export class SplitButton extends LitElement implements InteractiveComponent, Loa
/** Specifies the size of the component. */
@property({ reflect: true }) scale: Scale = "m";

/**
* Specifies where to open the linked document defined in the `href` property.
*
* @mdn [target](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#attr-target)
*/
@property({ reflect: true }) target: string;

/** Specifies the width of the component. [Deprecated] The `"half"` value is deprecated, use `"full"` instead. */
@property({ reflect: true }) width: Extract<Width, "auto" | "half" | "full"> = "auto";

Expand Down Expand Up @@ -181,15 +214,19 @@ export class SplitButton extends LitElement implements InteractiveComponent, Loa
<calcite-button
appearance={this.appearance}
disabled={this.disabled}
download={this.download}
href={this.href}
iconEnd={this.primaryIconEnd ? this.primaryIconEnd : null}
iconFlipRtl={this.primaryIconFlipRtl ? this.primaryIconFlipRtl : null}
iconStart={this.primaryIconStart ? this.primaryIconStart : null}
kind={this.kind}
label={this.primaryLabel}
loading={this.loading}
onClick={this.calciteSplitButtonPrimaryClickHandler}
rel={this.rel}
scale={this.scale}
splitChild={"primary"}
target={this.target}
type="button"
width={buttonWidth}
>
Expand Down
118 changes: 118 additions & 0 deletions packages/calcite-components/src/demos/split-button.html
Original file line number Diff line number Diff line change
Expand Up @@ -5568,6 +5568,124 @@
</div>
</div>

<!--
**************************************************
* DOWNLOAD
**************************************************
-->
<div class="parent-flex">
<div class="child-flex right-aligned-text">When primary is download</div>
<div class="child-flex">
<calcite-split-button
download
dropdown-icon-type="chevron"
scale="s"
primary-text="Primary Option"
primary-icon-start="download"
href="/"
>
<calcite-dropdown-group selection-mode="none">
<calcite-dropdown-item>Option 2</calcite-dropdown-item>
<calcite-dropdown-item>Option 3</calcite-dropdown-item>
<calcite-dropdown-item>Option 4</calcite-dropdown-item>
</calcite-dropdown-group>
</calcite-split-button>
</div>

<div class="child-flex">
<calcite-split-button
download
dropdown-icon-type="chevron"
scale="m"
primary-text="Primary Option"
primary-icon-start="download"
href="/"
>
<calcite-dropdown-group selection-mode="none">
<calcite-dropdown-item>Option 2</calcite-dropdown-item>
<calcite-dropdown-item>Option 3</calcite-dropdown-item>
<calcite-dropdown-item>Option 4</calcite-dropdown-item>
</calcite-dropdown-group>
</calcite-split-button>
</div>

<div class="child-flex">
<calcite-split-button
download
dropdown-icon-type="chevron"
scale="l"
primary-text="Primary Option"
primary-icon-start="download"
href="/"
>
<calcite-dropdown-group selection-mode="none">
<calcite-dropdown-item>Option 2</calcite-dropdown-item>
<calcite-dropdown-item>Option 3</calcite-dropdown-item>
<calcite-dropdown-item>Option 4</calcite-dropdown-item>
</calcite-dropdown-group>
</calcite-split-button>
</div>
</div>

<!--
**************************************************
* LINK
**************************************************
-->
<div class="parent-flex">
<div class="child-flex right-aligned-text">When primary is link</div>
<div class="child-flex">
<calcite-split-button
dropdown-icon-type="chevron"
scale="s"
primary-text="Primary Option"
href="/"
rel="external"
target="_blank"
>
<calcite-dropdown-group selection-mode="none">
<calcite-dropdown-item>Option 2</calcite-dropdown-item>
<calcite-dropdown-item>Option 3</calcite-dropdown-item>
<calcite-dropdown-item>Option 4</calcite-dropdown-item>
</calcite-dropdown-group>
</calcite-split-button>
</div>

<div class="child-flex">
<calcite-split-button
dropdown-icon-type="chevron"
scale="m"
primary-text="Primary Option"
href="/"
rel="external"
target="_blank"
>
<calcite-dropdown-group selection-mode="none">
<calcite-dropdown-item>Option 2</calcite-dropdown-item>
<calcite-dropdown-item>Option 3</calcite-dropdown-item>
<calcite-dropdown-item>Option 4</calcite-dropdown-item>
</calcite-dropdown-group>
</calcite-split-button>
</div>

<div class="child-flex">
<calcite-split-button
dropdown-icon-type="chevron"
scale="l"
primary-text="Primary Option"
href="/"
rel="external"
target="_blank"
>
<calcite-dropdown-group selection-mode="none">
<calcite-dropdown-item>Option 2</calcite-dropdown-item>
<calcite-dropdown-item>Option 3</calcite-dropdown-item>
<calcite-dropdown-item>Option 4</calcite-dropdown-item>
</calcite-dropdown-group>
</calcite-split-button>
</div>
</div>

<hr />

<!--
Expand Down

0 comments on commit f87097f

Please sign in to comment.