Skip to content

Commit 6700025

Browse files
author
Amir Ashkan Baghdoust
committed
fix: update segment component to work with empty child-width
1 parent c8ba489 commit 6700025

File tree

5 files changed

+111
-65
lines changed

5 files changed

+111
-65
lines changed

packages/components/src/components/segment/readme.md

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,24 @@
77

88
## Properties
99

10-
| Property | Attribute | Description | Type | Default |
11-
| ---------------------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------- | -------------- |
12-
| `adjacentSiblings` | `adjacent-siblings` | | `"left" \| "leftright" \| "right"` | `undefined` |
13-
| `ariaDescriptionTranslation` | `aria-description-translation` | a11y text for getting meaningful value. `$buttonNumber` and `$selected` are template variables and will be replaces by their corresponding properties. | `string` | `'$selected'` |
14-
| `ariaLabelSegment` | `aria-label-segment` | (optional) aria-label attribute needed for icon-only segments | `string` | `undefined` |
15-
| `ariaLangDeselected` | `aria-lang-deselected` | (optional) translation of 'deselected | `string` | `'deselected'` |
16-
| `ariaLangSelected` | `aria-lang-selected` | (optional) translation of 'selected | `string` | `'selected'` |
17-
| `disabled` | `disabled` | (optional) If `true`, the segment is disabled | `boolean` | `false` |
18-
| `hasIcon` | `has-icon` | (optional) position within group | `boolean` | `undefined` |
19-
| `iconOnly` | `icon-only` | (optional) position within group | `boolean` | `undefined` |
20-
| `position` | `position` | (optional) position within group | `number` | `undefined` |
21-
| `segmentId` | `segment-id` | (optional) segment's id | `string` | `undefined` |
22-
| `selected` | `selected` | (optional) If `true`, the segment is selected | `boolean` | `false` |
23-
| `selectedIndex` | `selected-index` | (optional) the index of the currently selected segment in the segmented-button | `string` | `undefined` |
24-
| `size` | `size` | (optional) The size of the segment | `"large" \| "medium" \| "small"` | `'small'` |
25-
| `styles` | `styles` | (optional) Injected CSS styles | `string` | `undefined` |
26-
| `textOnly` | `text-only` | (optional) position within group | `boolean` | `undefined` |
27-
| `width` | `width` | (optional) Segment width set to ensure that all segments have the same width | `string` | `undefined` |
10+
| Property | Attribute | Description | Type | Default |
11+
| ---------------------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------- | ------------------ |
12+
| `adjacentSiblings` | `adjacent-siblings` | | `"left" \| "leftright" \| "right"` | `undefined` |
13+
| `ariaDescriptionTranslation` | `aria-description-translation` | a11y text for getting meaningful value. `$buttonNumber` and `$selected` are template variables and will be replaces by their corresponding properties. | `string` | `'$selected'` |
14+
| `ariaLabelSegment` | `aria-label-segment` | (optional) aria-label attribute needed for icon-only segments | `string` | `undefined` |
15+
| `ariaLangDeselected` | `aria-lang-deselected` | (optional) translation of 'deselected | `string` | `'deselected'` |
16+
| `ariaLangSelected` | `aria-lang-selected` | (optional) translation of 'selected | `string` | `'selected'` |
17+
| `disabled` | `disabled` | (optional) If `true`, the segment is disabled | `boolean` | `false` |
18+
| `hasIcon` | `has-icon` | (optional) position within group | `boolean` | `undefined` |
19+
| `iconOnly` | `icon-only` | (optional) position within group | `boolean` | `undefined` |
20+
| `position` | `position` | (optional) position within group | `number` | `undefined` |
21+
| `segmentId` | `segment-id` | (optional) segment's id | `string` | `'segment-' + i++` |
22+
| `selected` | `selected` | (optional) If `true`, the segment is selected | `boolean` | `false` |
23+
| `selectedIndex` | `selected-index` | (optional) the index of the currently selected segment in the segmented-button | `string` | `undefined` |
24+
| `size` | `size` | (optional) The size of the segment | `"large" \| "medium" \| "small"` | `'small'` |
25+
| `styles` | `styles` | (optional) Injected CSS styles | `string` | `undefined` |
26+
| `textOnly` | `text-only` | (optional) position within group | `boolean` | `undefined` |
27+
| `width` | `width` | (optional) Segment width set to ensure that all segments have the same width | `string` | `undefined` |
2828

2929

3030
## Events

packages/components/src/components/segment/segment.tsx

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export class Segment {
3838
/** (optional) If `true`, the segment is disabled */
3939
@Prop() disabled?: boolean = false;
4040
/** (optional) segment's id */
41-
@Prop({ reflect: true, mutable: true }) segmentId?: string;
41+
@Prop({ reflect: true }) segmentId?: string = 'segment-' + i++;
4242
/** (optional) aria-label attribute needed for icon-only segments */
4343
@Prop() ariaLabelSegment: string;
4444
/** (optional) Segment width set to ensure that all segments have the same width */
@@ -85,12 +85,7 @@ export class Segment {
8585
this.focusableElement.focus();
8686
}
8787

88-
componentWillLoad() {
89-
if (this.segmentId == null) {
90-
this.segmentId = 'segment-' + i++;
91-
}
92-
}
93-
componentDidUpdate() {
88+
componentWillUpdate() {
9489
this.handleIcon();
9590
}
9691

@@ -230,4 +225,4 @@ export class Segment {
230225
this.iconOnly && `${prefix}icon-only`
231226
);
232227
}
233-
}
228+
}

packages/components/src/components/segmented-button/__snapshots__/segmented-button.spec.ts.snap

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
exports[`SegmentedButton should match selected button snapshot 1`] = `
44
<scale-segmented-button>
55
<mock:shadow-root>
6-
<div aria-label="segment button with 2" class="segmented-button segmented-button--small" part="segmented-button small" role="group" style="grid-template-columns: repeat(2, 0px);">
6+
<div aria-label="segment button with 2" class="segmented-button segmented-button--small" part="segmented-button small" role="group" style="grid-template-columns: repeat(2, auto);">
77
<slot></slot>
88
</div>
99
</mock:shadow-root>
@@ -19,7 +19,7 @@ exports[`SegmentedButton should match selected button snapshot 1`] = `
1919
exports[`SegmentedButton should match standard snapshot 1`] = `
2020
<scale-segmented-button>
2121
<mock:shadow-root>
22-
<div aria-label="segment button with 4" class="segmented-button segmented-button--small" part="segmented-button small" role="group" style="grid-template-columns: repeat(4, 14px);">
22+
<div aria-label="segment button with 4" class="segmented-button segmented-button--small" part="segmented-button small" role="group" style="grid-template-columns: repeat(4, auto);">
2323
<slot></slot>
2424
</div>
2525
</mock:shadow-root>

packages/components/src/components/segmented-button/segmented-button.tsx

Lines changed: 38 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export class SegmentedButton {
5252
/** (optional) Allow more than one button to be selected */
5353
@Prop() multiSelect: boolean = false;
5454
/** (optional) the index of the selected segment */
55-
@Prop() selectedIndex?: number;
55+
@Prop({ mutable: true }) selectedIndex?: number;
5656
/** (optional) If `true`, the button is disabled */
5757
@Prop({ reflect: true }) disabled?: boolean = false;
5858
/** (optional) If `true`, expand to container width */
@@ -119,38 +119,37 @@ export class SegmentedButton {
119119
});
120120
}
121121

122-
componentDidLoad() {
122+
componentWillLoad() {
123123
const tempState: SegmentStatus[] = [];
124124
const segments = this.getAllSegments();
125125
this.slottedSegments = segments.length;
126-
const longestButtonWidth = this.getLongestButtonWidth();
127-
segments.forEach((segment) => {
128-
this.position++;
126+
segments.forEach((segment, i) => {
129127
tempState.push({
130128
id: segment.getAttribute('segment-id') || segment.segmentId,
131129
selected: segment.hasAttribute('selected') || segment.selected,
132130
});
133-
segment.setAttribute('position', this.position.toString());
131+
segment.setAttribute('position', `${i + 1}`);
134132
segment.setAttribute(
135133
'aria-description-translation',
136134
'$position $selected'
137135
);
138136
});
137+
this.setState(tempState);
138+
this.selectedIndex = this.getSelectedIndex();
139+
}
140+
componentDidLoad() {
141+
const longestButtonWidth = this.getLongestButtonWidth();
139142
if (!this.fullWidth) {
140-
this.container.style.gridTemplateColumns = `repeat(${
141-
this.hostElement.children.length
142-
}, ${Math.ceil(longestButtonWidth)}px)`;
143+
this.container.style.gridTemplateColumns = longestButtonWidth
144+
? `repeat(${this.hostElement.children.length}, ${Math.ceil(
145+
longestButtonWidth
146+
)}px)`
147+
: `repeat(${this.hostElement.children.length}, auto)`;
143148
} else {
144149
this.container.style.display = 'flex';
145150
}
146-
147-
this.selectedIndex = this.getSelectedIndex();
148151
this.propagatePropsToChildren();
149-
this.position = 0;
150-
this.status = tempState;
151-
this.setState(tempState);
152152
}
153-
154153
componentWillUpdate() {
155154
this.selectedIndex = this.getSelectedIndex();
156155
this.showHelperText = false;
@@ -195,27 +194,29 @@ export class SegmentedButton {
195194
// all segmented buttons should have the same width, based on the largest one
196195
getLongestButtonWidth() {
197196
let tempWidth = 0;
198-
Array.from(this.hostElement.children).forEach((child) => {
199-
const selected = child.hasAttribute('selected');
200-
const iconOnly = child.hasAttribute('icon-only');
201-
const checkmark =
202-
this.size === 'small'
203-
? CHECKMARK_WIDTH_SMALL
204-
: this.size === 'medium'
205-
? CHECKMARK_WIDTH_MEDIUM
206-
: CHECKMARK_WIDTH_LARGE;
207-
if (selected || iconOnly) {
208-
tempWidth =
209-
child.getBoundingClientRect().width > tempWidth
210-
? child.getBoundingClientRect().width
211-
: tempWidth;
212-
} else {
213-
tempWidth =
214-
child.getBoundingClientRect().width + checkmark > tempWidth
215-
? child.getBoundingClientRect().width + checkmark
216-
: tempWidth;
217-
}
218-
});
197+
Array.from(this.hostElement.children)
198+
.filter((child) => child.getBoundingClientRect().width)
199+
.forEach((child) => {
200+
const selected = child.hasAttribute('selected');
201+
const iconOnly = child.hasAttribute('icon-only');
202+
const checkmark =
203+
this.size === 'small'
204+
? CHECKMARK_WIDTH_SMALL
205+
: this.size === 'medium'
206+
? CHECKMARK_WIDTH_MEDIUM
207+
: CHECKMARK_WIDTH_LARGE;
208+
if (selected || iconOnly) {
209+
tempWidth =
210+
child.getBoundingClientRect().width > tempWidth
211+
? child.getBoundingClientRect().width
212+
: tempWidth;
213+
} else {
214+
tempWidth =
215+
child.getBoundingClientRect().width + checkmark > tempWidth
216+
? child.getBoundingClientRect().width + checkmark
217+
: tempWidth;
218+
}
219+
});
219220
return tempWidth;
220221
}
221222

@@ -293,4 +294,4 @@ export class SegmentedButton {
293294
this.fullWidth && `${prefix}full-width`
294295
);
295296
}
296-
}
297+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<!DOCTYPE html>
2+
<html dir="ltr" lang="en">
3+
<head>
4+
<meta charset="utf-8" />
5+
<meta
6+
name="viewport"
7+
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=5.0"
8+
/>
9+
<title>Loading spinners</title>
10+
11+
<script type="module" src="build/scale-components.esm.js"></script>
12+
<script nomodule src="build/scale-components.js"></script>
13+
<link rel="stylesheet" href="build/scale-components.css" />
14+
<style>
15+
section {
16+
padding: 1rem;
17+
}
18+
.bg-black {
19+
background: black;
20+
color: white;
21+
}
22+
</style>
23+
</head>
24+
25+
<body>
26+
<scale-collapsible>
27+
<div slot="heading">Open Me for Disaster</div>
28+
<scale-segmented-button>
29+
<scale-segment selected>Apple</scale-segment>
30+
<scale-segment>One+</scale-segment>
31+
<scale-segment>Samsung</scale-segment>
32+
<scale-segment>Huawei</scale-segment>
33+
</scale-segmented-button>
34+
</scale-collapsible>
35+
36+
37+
<scale-segmented-button>
38+
<scale-segment >Apple</scale-segment>
39+
<scale-segment>One+</scale-segment>
40+
<scale-segment>Samsung</scale-segment>
41+
<scale-segment>Huawei</scale-segment>
42+
</scale-segmented-button>
43+
44+
45+
<scale-segmented-button>
46+
<scale-segment selected>Label</scale-segment>
47+
<scale-segment selected>Label</scale-segment>
48+
</scale-segmented-button>
49+
</body>
50+
</html>

0 commit comments

Comments
 (0)