Skip to content

Commit fb3e1dc

Browse files
josercarcamobenelan
authored andcommitted
feat(split-button): make downloadable and linkable (#11520)
**Related Issue:** #9126 ## Summary Implemented ability to make the primary part of a split button into a downloadable link or an href link.
1 parent 0a68f03 commit fb3e1dc

File tree

4 files changed

+253
-1
lines changed

4 files changed

+253
-1
lines changed

packages/calcite-components/src/components/split-button/split-button.e2e.ts

+65
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,22 @@ describe("calcite-split-button", () => {
2121
propertyName: "placement",
2222
defaultValue: "bottom-end",
2323
},
24+
{
25+
propertyName: "download",
26+
defaultValue: false,
27+
},
28+
{
29+
propertyName: "href",
30+
defaultValue: undefined,
31+
},
32+
{
33+
propertyName: "rel",
34+
defaultValue: undefined,
35+
},
36+
{
37+
propertyName: "target",
38+
defaultValue: undefined,
39+
},
2440
]);
2541
});
2642

@@ -30,6 +46,22 @@ describe("calcite-split-button", () => {
3046
propertyName: "placement",
3147
value: "bottom-end",
3248
},
49+
{
50+
propertyName: "download",
51+
value: true,
52+
},
53+
{
54+
propertyName: "href",
55+
value: "/",
56+
},
57+
{
58+
propertyName: "rel",
59+
value: "external",
60+
},
61+
{
62+
propertyName: "target",
63+
value: "_blank",
64+
},
3365
]);
3466
});
3567

@@ -261,4 +293,37 @@ describe("calcite-split-button", () => {
261293
await dropdownCloseEvent;
262294
expect(await positionContainer.isVisible()).toBe(false);
263295
});
296+
297+
it("sets download attribute", async () => {
298+
const page = await newE2EPage();
299+
await page.setContent(`<calcite-split-button href="/">Continue</calcite-split-button>`);
300+
301+
const elementAsButton = await page.find("calcite-split-button >>> calcite-button");
302+
303+
expect(elementAsButton).not.toBeNull();
304+
expect(await elementAsButton.getProperty("download")).toBe(false);
305+
expect(elementAsButton).not.toHaveAttribute("download");
306+
307+
const element = await page.find("calcite-split-button");
308+
309+
element.setProperty("download", true);
310+
await page.waitForChanges();
311+
312+
expect(await elementAsButton.getProperty("download")).toEqual(true);
313+
expect(elementAsButton).toHaveAttribute("download");
314+
expect(elementAsButton.getAttribute("download")).toBe("");
315+
316+
const newFilename = "my-cool-file.jpg";
317+
element.setProperty("download", newFilename);
318+
await page.waitForChanges();
319+
320+
expect(await elementAsButton.getProperty("download")).toBe(newFilename);
321+
expect(elementAsButton.getAttribute("download")).toBe(newFilename);
322+
323+
element.setProperty("download", false);
324+
await page.waitForChanges();
325+
326+
expect(await elementAsButton.getProperty("download")).toBe(false);
327+
expect(elementAsButton).not.toHaveAttribute("download");
328+
});
264329
});

packages/calcite-components/src/components/split-button/split-button.stories.ts

+32
Original file line numberDiff line numberDiff line change
@@ -287,3 +287,35 @@ export const appearanceAndKindCombinations_TestOnly = (): string => html`
287287
`;
288288

289289
export const loadingAndDisabled_TestOnly = (): string => html`<calcite-button loading disabled>Test</calcite-button>`;
290+
291+
export const primaryAsALink = (): string =>
292+
html` <calcite-split-button
293+
scale="m"
294+
primary-text="Primary Option"
295+
dropdown-icon-type="overflow"
296+
href="split-button.html"
297+
rel="external"
298+
target="_blank"
299+
>
300+
<calcite-dropdown-group selection-mode="none">
301+
<calcite-dropdown-item>Option 2</calcite-dropdown-item>
302+
<calcite-dropdown-item>Option 3</calcite-dropdown-item>
303+
<calcite-dropdown-item>Option 4</calcite-dropdown-item>
304+
</calcite-dropdown-group>
305+
</calcite-split-button>`;
306+
307+
export const primaryAsADownload = (): string =>
308+
html` <calcite-split-button
309+
download
310+
scale="m"
311+
primary-text="Primary Option"
312+
primary-icon-start="download"
313+
dropdown-icon-type="overflow"
314+
href="/"
315+
>
316+
<calcite-dropdown-group selection-mode="none">
317+
<calcite-dropdown-item>Option 2</calcite-dropdown-item>
318+
<calcite-dropdown-item>Option 3</calcite-dropdown-item>
319+
<calcite-dropdown-item>Option 4</calcite-dropdown-item>
320+
</calcite-dropdown-group>
321+
</calcite-split-button>`;

packages/calcite-components/src/components/split-button/split-button.tsx

+38-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
// @ts-strict-ignore
2-
import { LitElement, property, createEvent, h, method, JsxNode } from "@arcgis/lumina";
2+
import {
3+
LitElement,
4+
property,
5+
createEvent,
6+
h,
7+
method,
8+
JsxNode,
9+
stringOrBoolean,
10+
} from "@arcgis/lumina";
311
import { FlipPlacement, MenuPlacement, OverlayPositioning } from "../../utils/floating-ui";
412
import {
513
InteractiveComponent,
@@ -67,6 +75,14 @@ export class SplitButton extends LitElement implements InteractiveComponent, Loa
6775
/** When `true`, interaction is prevented and the component is displayed with lower opacity. */
6876
@property({ reflect: true }) disabled = false;
6977

78+
/**
79+
* Prompts the user to save the linked URL instead of navigating to it. Can be used with or without a value:
80+
* Without a value, the browser will suggest a filename/extension.
81+
*
82+
* @see [Global download attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#download).
83+
*/
84+
@property({ reflect: true, converter: stringOrBoolean }) download: string | boolean = false;
85+
7086
/** Specifies the icon used for the dropdown menu. */
7187
@property({ reflect: true }) dropdownIconType: DropdownIconType = "chevron";
7288

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

95+
/** Specifies the URL of the linked resource, which can be set as an absolute or relative path. */
96+
@property({ reflect: true }) href: string;
97+
7998
/** Specifies the kind of the component, which will apply to border and background, if applicable. */
8099
@property({ reflect: true }) kind: Extract<"brand" | "danger" | "inverse" | "neutral", Kind> =
81100
"brand";
@@ -92,6 +111,13 @@ export class SplitButton extends LitElement implements InteractiveComponent, Loa
92111
*/
93112
@property({ reflect: true }) overlayPositioning: OverlayPositioning = "absolute";
94113

114+
/**
115+
* Defines the relationship between the `href` value and the current document.
116+
*
117+
* @mdn [rel](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel)
118+
*/
119+
@property({ reflect: true }) rel: string;
120+
95121
/**
96122
* Determines where the component will be positioned relative to the container element.
97123
*
@@ -117,6 +143,13 @@ export class SplitButton extends LitElement implements InteractiveComponent, Loa
117143
/** Specifies the size of the component. */
118144
@property({ reflect: true }) scale: Scale = "m";
119145

146+
/**
147+
* Specifies where to open the linked document defined in the `href` property.
148+
*
149+
* @mdn [target](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#attr-target)
150+
*/
151+
@property({ reflect: true }) target: string;
152+
120153
/** Specifies the width of the component. [Deprecated] The `"half"` value is deprecated, use `"full"` instead. */
121154
@property({ reflect: true }) width: Extract<Width, "auto" | "half" | "full"> = "auto";
122155

@@ -181,15 +214,19 @@ export class SplitButton extends LitElement implements InteractiveComponent, Loa
181214
<calcite-button
182215
appearance={this.appearance}
183216
disabled={this.disabled}
217+
download={this.download}
218+
href={this.href}
184219
iconEnd={this.primaryIconEnd ? this.primaryIconEnd : null}
185220
iconFlipRtl={this.primaryIconFlipRtl ? this.primaryIconFlipRtl : null}
186221
iconStart={this.primaryIconStart ? this.primaryIconStart : null}
187222
kind={this.kind}
188223
label={this.primaryLabel}
189224
loading={this.loading}
190225
onClick={this.calciteSplitButtonPrimaryClickHandler}
226+
rel={this.rel}
191227
scale={this.scale}
192228
splitChild={"primary"}
229+
target={this.target}
193230
type="button"
194231
width={buttonWidth}
195232
>

packages/calcite-components/src/demos/split-button.html

+118
Original file line numberDiff line numberDiff line change
@@ -5568,6 +5568,124 @@
55685568
</div>
55695569
</div>
55705570

5571+
<!--
5572+
**************************************************
5573+
* DOWNLOAD
5574+
**************************************************
5575+
-->
5576+
<div class="parent-flex">
5577+
<div class="child-flex right-aligned-text">When primary is download</div>
5578+
<div class="child-flex">
5579+
<calcite-split-button
5580+
download
5581+
dropdown-icon-type="chevron"
5582+
scale="s"
5583+
primary-text="Primary Option"
5584+
primary-icon-start="download"
5585+
href="/"
5586+
>
5587+
<calcite-dropdown-group selection-mode="none">
5588+
<calcite-dropdown-item>Option 2</calcite-dropdown-item>
5589+
<calcite-dropdown-item>Option 3</calcite-dropdown-item>
5590+
<calcite-dropdown-item>Option 4</calcite-dropdown-item>
5591+
</calcite-dropdown-group>
5592+
</calcite-split-button>
5593+
</div>
5594+
5595+
<div class="child-flex">
5596+
<calcite-split-button
5597+
download
5598+
dropdown-icon-type="chevron"
5599+
scale="m"
5600+
primary-text="Primary Option"
5601+
primary-icon-start="download"
5602+
href="/"
5603+
>
5604+
<calcite-dropdown-group selection-mode="none">
5605+
<calcite-dropdown-item>Option 2</calcite-dropdown-item>
5606+
<calcite-dropdown-item>Option 3</calcite-dropdown-item>
5607+
<calcite-dropdown-item>Option 4</calcite-dropdown-item>
5608+
</calcite-dropdown-group>
5609+
</calcite-split-button>
5610+
</div>
5611+
5612+
<div class="child-flex">
5613+
<calcite-split-button
5614+
download
5615+
dropdown-icon-type="chevron"
5616+
scale="l"
5617+
primary-text="Primary Option"
5618+
primary-icon-start="download"
5619+
href="/"
5620+
>
5621+
<calcite-dropdown-group selection-mode="none">
5622+
<calcite-dropdown-item>Option 2</calcite-dropdown-item>
5623+
<calcite-dropdown-item>Option 3</calcite-dropdown-item>
5624+
<calcite-dropdown-item>Option 4</calcite-dropdown-item>
5625+
</calcite-dropdown-group>
5626+
</calcite-split-button>
5627+
</div>
5628+
</div>
5629+
5630+
<!--
5631+
**************************************************
5632+
* LINK
5633+
**************************************************
5634+
-->
5635+
<div class="parent-flex">
5636+
<div class="child-flex right-aligned-text">When primary is link</div>
5637+
<div class="child-flex">
5638+
<calcite-split-button
5639+
dropdown-icon-type="chevron"
5640+
scale="s"
5641+
primary-text="Primary Option"
5642+
href="/"
5643+
rel="external"
5644+
target="_blank"
5645+
>
5646+
<calcite-dropdown-group selection-mode="none">
5647+
<calcite-dropdown-item>Option 2</calcite-dropdown-item>
5648+
<calcite-dropdown-item>Option 3</calcite-dropdown-item>
5649+
<calcite-dropdown-item>Option 4</calcite-dropdown-item>
5650+
</calcite-dropdown-group>
5651+
</calcite-split-button>
5652+
</div>
5653+
5654+
<div class="child-flex">
5655+
<calcite-split-button
5656+
dropdown-icon-type="chevron"
5657+
scale="m"
5658+
primary-text="Primary Option"
5659+
href="/"
5660+
rel="external"
5661+
target="_blank"
5662+
>
5663+
<calcite-dropdown-group selection-mode="none">
5664+
<calcite-dropdown-item>Option 2</calcite-dropdown-item>
5665+
<calcite-dropdown-item>Option 3</calcite-dropdown-item>
5666+
<calcite-dropdown-item>Option 4</calcite-dropdown-item>
5667+
</calcite-dropdown-group>
5668+
</calcite-split-button>
5669+
</div>
5670+
5671+
<div class="child-flex">
5672+
<calcite-split-button
5673+
dropdown-icon-type="chevron"
5674+
scale="l"
5675+
primary-text="Primary Option"
5676+
href="/"
5677+
rel="external"
5678+
target="_blank"
5679+
>
5680+
<calcite-dropdown-group selection-mode="none">
5681+
<calcite-dropdown-item>Option 2</calcite-dropdown-item>
5682+
<calcite-dropdown-item>Option 3</calcite-dropdown-item>
5683+
<calcite-dropdown-item>Option 4</calcite-dropdown-item>
5684+
</calcite-dropdown-group>
5685+
</calcite-split-button>
5686+
</div>
5687+
</div>
5688+
55715689
<hr />
55725690

55735691
<!--

0 commit comments

Comments
 (0)