Skip to content

Commit 44529f0

Browse files
thetaPCIonitronbrandyscarney
authored
feat(button): add circular shape as round (#29161)
Co-authored-by: ionitron <[email protected]> Co-authored-by: Brandy Carney <[email protected]>
1 parent 500854d commit 44529f0

File tree

145 files changed

+288
-59
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

145 files changed

+288
-59
lines changed

core/src/components/button/button.ios.scss

+41-6
Original file line numberDiff line numberDiff line change
@@ -114,11 +114,6 @@
114114
font-size: #{$button-ios-small-font-size};
115115
}
116116

117-
:host(.button-has-icon-only) {
118-
--padding-top: 0;
119-
--padding-bottom: 0;
120-
}
121-
122117

123118
// iOS Round Button
124119
// --------------------------------------------------
@@ -131,14 +126,54 @@
131126
--padding-bottom: #{$button-ios-round-padding-bottom};
132127
}
133128

134-
135129
// iOS Strong Button
136130
// --------------------------------------------------
137131

138132
:host(.button-strong) {
139133
font-weight: #{$button-ios-strong-font-weight};
140134
}
141135

136+
// iOS Icon Only Button
137+
// --------------------------------------------------
138+
139+
:host(.button-has-icon-only) {
140+
--padding-top: 0;
141+
--padding-bottom: var(--padding-top);
142+
--padding-end: var(--padding-top);
143+
--padding-start: var(--padding-end);
144+
// TODO(FW-6053): replace em value with the min-height variable.
145+
min-width: clamp(30px, 2.125em, 60px);
146+
147+
// TODO(FW-6053): replace em value with the min-height variable.
148+
min-height: clamp(30px, 2.125em, 60px);
149+
}
150+
151+
::slotted(ion-icon[slot="icon-only"]) {
152+
font-size: 1.125em;
153+
}
154+
155+
:host(.button-small.button-has-icon-only) {
156+
// TODO(FW-6053): replace em value with the min-height variable.
157+
min-width: clamp(23px, 2.16em, 54px);
158+
// TODO(FW-6053): replace em value with the min-height variable.
159+
min-height: clamp(23px, 2.16em, 54px);
160+
161+
}
162+
163+
:host(.button-small) ::slotted(ion-icon[slot="icon-only"]) {
164+
font-size: 1.4em;
165+
}
166+
167+
:host(.button-large.button-has-icon-only) {
168+
// TODO(FW-6053): replace em value with the min-height variable.
169+
min-width: clamp(46px, 2.5em, 78px);
170+
// TODO(FW-6053): replace em value with the min-height variable.
171+
min-height: clamp(46px, 2.5em, 78px);
172+
}
173+
174+
:host(.button-large) ::slotted(ion-icon[slot="icon-only"]) {
175+
font-size: 1em;
176+
}
142177

143178
// iOS Button Focused
144179
// --------------------------------------------------

core/src/components/button/button.md.scss

+43-7
Original file line numberDiff line numberDiff line change
@@ -113,23 +113,59 @@
113113
font-size: #{$button-md-small-font-size};
114114
}
115115

116-
:host(.button-has-icon-only) {
117-
--padding-top: 0;
118-
--padding-bottom: 0;
119-
}
120-
121-
122116
// MD strong Button
123117
// --------------------------------------------------
124118

125119
:host(.button-strong) {
126120
font-weight: #{$button-md-strong-font-weight};
127121
}
128122

123+
// MD Icon Only Button
124+
//
125+
// MD does not specify a small size, these
126+
// styles are based on the iOS small size.
127+
//
128+
// MD does not specify a large size, these
129+
// styles are based on the iOS large size.
130+
// --------------------------------------------------
131+
132+
:host(.button-has-icon-only) {
133+
--padding-top: 0;
134+
--padding-bottom: var(--padding-top);
135+
--padding-end: var(--padding-top);
136+
--padding-start: var(--padding-end);
137+
// TODO(FW-6053): replace em value with the min-height variable.
138+
min-width: clamp(30px, 2.86em, 60px);
139+
140+
// TODO(FW-6053): replace em value with the min-height variable.
141+
min-height: clamp(30px, 2.86em, 60px);
142+
}
143+
129144
::slotted(ion-icon[slot="icon-only"]) {
130-
@include padding(0);
145+
font-size: 1.6em;
131146
}
132147

148+
:host(.button-small.button-has-icon-only) {
149+
// TODO(FW-6053): replace em value with the min-height variable.
150+
min-width: clamp(23px, 2.16em, 54px);
151+
// TODO(FW-6053): replace em value with the min-height variable.
152+
min-height: clamp(23px, 2.16em, 54px);
153+
}
154+
155+
:host(.button-small) ::slotted(ion-icon[slot="icon-only"]) {
156+
font-size: 1.23em;
157+
}
158+
159+
:host(.button-large.button-has-icon-only) {
160+
// TODO(FW-6053): replace em value with the min-height variable.
161+
min-width: clamp(46px, 2.5em, 78px);
162+
// TODO(FW-6053): replace em value with the min-height variable.
163+
min-height: clamp(46px, 2.5em, 78px);
164+
}
165+
166+
:host(.button-large) ::slotted(ion-icon[slot="icon-only"]) {
167+
font-size: 1.4em;
168+
}
133169

134170
// Material Design Button: Hover
135171
// --------------------------------------------------

core/src/components/button/button.scss

-5
Original file line numberDiff line numberDiff line change
@@ -235,11 +235,6 @@
235235
@include margin(0, -0.2em, 0, 0.3em);
236236
}
237237

238-
::slotted(ion-icon[slot="icon-only"]) {
239-
font-size: 1.8em;
240-
}
241-
242-
243238
// Button Ripple effect
244239
// --------------------------------------------------
245240

core/src/components/button/button.tsx

+19-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { ComponentInterface, EventEmitter } from '@stencil/core';
2-
import { Component, Element, Event, Host, Prop, Watch, h } from '@stencil/core';
2+
import { Component, Element, Event, Host, Prop, Watch, State, h } from '@stencil/core';
33
import type { AnchorInterface, ButtonInterface } from '@utils/element-interface';
44
import type { Attributes } from '@utils/helpers';
55
import { inheritAriaAttributes, hasShadowDom } from '@utils/helpers';
@@ -38,6 +38,11 @@ export class Button implements ComponentInterface, AnchorInterface, ButtonInterf
3838

3939
@Element() el!: HTMLElement;
4040

41+
/**
42+
* If `true`, the button only has an icon.
43+
*/
44+
@State() isCircle: boolean = false;
45+
4146
/**
4247
* The color to use from your application's color palette.
4348
* Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`.
@@ -295,6 +300,18 @@ export class Button implements ComponentInterface, AnchorInterface, ButtonInterf
295300
this.ionBlur.emit();
296301
};
297302

303+
private slotChanged = () => {
304+
/**
305+
* Ensures that the 'has-icon-only' class is properly added
306+
* or removed from `ion-button` when manipulating the
307+
* `icon-only` slot.
308+
*
309+
* Without this, the 'has-icon-only' class is only checked
310+
* or added when `ion-button` component first renders.
311+
*/
312+
this.isCircle = this.hasIconOnly;
313+
};
314+
298315
render() {
299316
const mode = getIonMode(this);
300317
const {
@@ -374,7 +391,7 @@ export class Button implements ComponentInterface, AnchorInterface, ButtonInterf
374391
{...inheritedAttributes}
375392
>
376393
<span class="button-inner">
377-
<slot name="icon-only"></slot>
394+
<slot name="icon-only" onSlotchange={this.slotChanged}></slot>
378395
<slot name="start"></slot>
379396
<slot></slot>
380397
<slot name="end"></slot>

core/src/components/button/button.vars.scss

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,4 @@ $button-round-padding-bottom: $button-round-padding-top !default;
1616
$button-round-padding-start: $button-round-padding-end !default;
1717

1818
/// @prop - Border radius of the round button
19-
$button-round-border-radius: 64px !default;
19+
$button-round-border-radius: 999px !default;

core/src/components/button/test/round/button.e2e.ts

+56-4
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,64 @@ import { configs, test } from '@utils/test/playwright';
66
*/
77
configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
88
test.describe(title('button: round'), () => {
9-
test('should not have visual regressions', async ({ page }) => {
10-
await page.goto(`/src/components/button/test/round`, config);
9+
test.describe('default', () => {
10+
test('should not have visual regressions', async ({ page }) => {
11+
await page.goto(`/src/components/button/test/round`, config);
1112

12-
await page.setIonViewport();
13+
await page.setIonViewport();
1314

14-
await expect(page).toHaveScreenshot(screenshot(`button-round`));
15+
const container = page.locator('#default');
16+
17+
await expect(container).toHaveScreenshot(screenshot(`button-round`));
18+
});
19+
});
20+
21+
test.describe('outline', () => {
22+
test('should not have visual regressions', async ({ page }) => {
23+
await page.goto(`/src/components/button/test/round`, config);
24+
25+
await page.setIonViewport();
26+
27+
const container = page.locator('#outline');
28+
29+
await expect(container).toHaveScreenshot(screenshot(`button-outline-round`));
30+
});
31+
});
32+
33+
test.describe('clear', () => {
34+
test('should not have visual regressions', async ({ page }) => {
35+
await page.goto(`/src/components/button/test/round`, config);
36+
37+
await page.setIonViewport();
38+
39+
const container = page.locator('#clear');
40+
41+
await expect(container).toHaveScreenshot(screenshot(`button-clear-round`));
42+
});
43+
});
44+
45+
test.describe('color', () => {
46+
test('should not have visual regressions', async ({ page }) => {
47+
await page.goto(`/src/components/button/test/round`, config);
48+
49+
await page.setIonViewport();
50+
51+
const container = page.locator('#color');
52+
53+
await expect(container).toHaveScreenshot(screenshot(`button-color-round`));
54+
});
55+
});
56+
57+
test.describe('expand', () => {
58+
test('should not have visual regressions', async ({ page }) => {
59+
await page.goto(`/src/components/button/test/round`, config);
60+
61+
await page.setIonViewport();
62+
63+
const container = page.locator('#expand');
64+
65+
await expect(container).toHaveScreenshot(screenshot(`button-expand-round`));
66+
});
1567
});
1668
});
1769
});

0 commit comments

Comments
 (0)