diff --git a/packages/components/src/components/segment/__snapshots__/segment.spec.ts.snap b/packages/components/src/components/segment/__snapshots__/segment.spec.ts.snap index bf96880a50..1c95275796 100644 --- a/packages/components/src/components/segment/__snapshots__/segment.spec.ts.snap +++ b/packages/components/src/components/segment/__snapshots__/segment.spec.ts.snap @@ -15,6 +15,8 @@ exports[`Segment should match standard snapshot 1`] = ` - Label + + Label + `; diff --git a/packages/components/src/components/segment/readme.md b/packages/components/src/components/segment/readme.md index bc5a198334..249d97f3ad 100644 --- a/packages/components/src/components/segment/readme.md +++ b/packages/components/src/components/segment/readme.md @@ -7,24 +7,24 @@ ## Properties -| Property | Attribute | Description | Type | Default | -| ---------------------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------- | -------------- | -| `adjacentSiblings` | `adjacent-siblings` | | `"left" \| "leftright" \| "right"` | `undefined` | -| `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'` | -| `ariaLabelSegment` | `aria-label-segment` | (optional) aria-label attribute needed for icon-only segments | `string` | `undefined` | -| `ariaLangDeselected` | `aria-lang-deselected` | (optional) translation of 'deselected | `string` | `'deselected'` | -| `ariaLangSelected` | `aria-lang-selected` | (optional) translation of 'selected | `string` | `'selected'` | -| `disabled` | `disabled` | (optional) If `true`, the segment is disabled | `boolean` | `false` | -| `hasIcon` | `has-icon` | (optional) position within group | `boolean` | `undefined` | -| `iconOnly` | `icon-only` | (optional) position within group | `boolean` | `undefined` | -| `position` | `position` | (optional) position within group | `number` | `undefined` | -| `segmentId` | `segment-id` | (optional) segment's id | `string` | `undefined` | -| `selected` | `selected` | (optional) If `true`, the segment is selected | `boolean` | `false` | -| `selectedIndex` | `selected-index` | (optional) the index of the currently selected segment in the segmented-button | `string` | `undefined` | -| `size` | `size` | (optional) The size of the segment | `"large" \| "medium" \| "small"` | `'small'` | -| `styles` | `styles` | (optional) Injected CSS styles | `string` | `undefined` | -| `textOnly` | `text-only` | (optional) position within group | `boolean` | `undefined` | -| `width` | `width` | (optional) Segment width set to ensure that all segments have the same width | `string` | `undefined` | +| Property | Attribute | Description | Type | Default | +| ---------------------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------- | ------------------ | +| `adjacentSiblings` | `adjacent-siblings` | | `"left" \| "leftright" \| "right"` | `undefined` | +| `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'` | +| `ariaLabelSegment` | `aria-label-segment` | (optional) aria-label attribute needed for icon-only segments | `string` | `undefined` | +| `ariaLangDeselected` | `aria-lang-deselected` | (optional) translation of 'deselected | `string` | `'deselected'` | +| `ariaLangSelected` | `aria-lang-selected` | (optional) translation of 'selected | `string` | `'selected'` | +| `disabled` | `disabled` | (optional) If `true`, the segment is disabled | `boolean` | `false` | +| `hasIcon` | `has-icon` | (optional) position within group | `boolean` | `undefined` | +| `iconOnly` | `icon-only` | (optional) position within group | `boolean` | `undefined` | +| `position` | `position` | (optional) position within group | `number` | `undefined` | +| `segmentId` | `segment-id` | (optional) segment's id | `string` | `'segment-' + i++` | +| `selected` | `selected` | (optional) If `true`, the segment is selected | `boolean` | `false` | +| `selectedIndex` | `selected-index` | (optional) the index of the currently selected segment in the segmented-button | `string` | `undefined` | +| `size` | `size` | (optional) The size of the segment | `"large" \| "medium" \| "small"` | `'small'` | +| `styles` | `styles` | (optional) Injected CSS styles | `string` | `undefined` | +| `textOnly` | `text-only` | (optional) position within group | `boolean` | `undefined` | +| `width` | `width` | (optional) Segment width set to ensure that all segments have the same width | `string` | `undefined` | ## Events diff --git a/packages/components/src/components/segment/segment.tsx b/packages/components/src/components/segment/segment.tsx index c2daf67c0c..67b45ffa82 100644 --- a/packages/components/src/components/segment/segment.tsx +++ b/packages/components/src/components/segment/segment.tsx @@ -38,7 +38,7 @@ export class Segment { /** (optional) If `true`, the segment is disabled */ @Prop() disabled?: boolean = false; /** (optional) segment's id */ - @Prop({ reflect: true, mutable: true }) segmentId?: string; + @Prop({ reflect: true }) segmentId?: string = 'segment-' + i++; /** (optional) aria-label attribute needed for icon-only segments */ @Prop() ariaLabelSegment: string; /** (optional) Segment width set to ensure that all segments have the same width */ @@ -84,13 +84,10 @@ export class Segment { async setFocus() { this.focusableElement.focus(); } - componentWillLoad() { - if (this.segmentId == null) { - this.segmentId = 'segment-' + i++; - } + this.handleIcon(); } - componentDidUpdate() { + componentWillUpdate() { this.handleIcon(); } diff --git a/packages/components/src/components/segmented-button/__snapshots__/segmented-button.spec.ts.snap b/packages/components/src/components/segmented-button/__snapshots__/segmented-button.spec.ts.snap index 41c565086d..82e5bc5170 100644 --- a/packages/components/src/components/segmented-button/__snapshots__/segmented-button.spec.ts.snap +++ b/packages/components/src/components/segmented-button/__snapshots__/segmented-button.spec.ts.snap @@ -3,7 +3,7 @@ exports[`SegmentedButton should match selected button snapshot 1`] = ` -
+
@@ -19,7 +19,7 @@ exports[`SegmentedButton should match selected button snapshot 1`] = ` exports[`SegmentedButton should match standard snapshot 1`] = ` -
+
@@ -36,4 +36,4 @@ exports[`SegmentedButton should match standard snapshot 1`] = ` Label -`; +`; \ No newline at end of file diff --git a/packages/components/src/components/segmented-button/segmented-button.tsx b/packages/components/src/components/segmented-button/segmented-button.tsx index b93fea17c1..e32fbc09ef 100644 --- a/packages/components/src/components/segmented-button/segmented-button.tsx +++ b/packages/components/src/components/segmented-button/segmented-button.tsx @@ -52,7 +52,7 @@ export class SegmentedButton { /** (optional) Allow more than one button to be selected */ @Prop() multiSelect: boolean = false; /** (optional) the index of the selected segment */ - @Prop() selectedIndex?: number; + @Prop({ mutable: true }) selectedIndex?: number; /** (optional) If `true`, the button is disabled */ @Prop({ reflect: true }) disabled?: boolean = false; /** (optional) If `true`, expand to container width */ @@ -119,47 +119,52 @@ export class SegmentedButton { }); } - componentDidLoad() { + componentWillLoad() { const tempState: SegmentStatus[] = []; const segments = this.getAllSegments(); this.slottedSegments = segments.length; - const longestButtonWidth = this.getLongestButtonWidth(); - segments.forEach((segment) => { - this.position++; + segments.forEach((segment, i) => { tempState.push({ id: segment.getAttribute('segment-id') || segment.segmentId, selected: segment.hasAttribute('selected') || segment.selected, }); - segment.setAttribute('position', this.position.toString()); + segment.setAttribute('position', `${i + 1}`); segment.setAttribute( 'aria-description-translation', '$position $selected' ); }); + this.setState(tempState); + this.selectedIndex = this.getSelectedIndex(); + this.showHelperText = this.shouldShowHelperText(); + } + componentDidLoad() { + const longestButtonWidth = this.getLongestButtonWidth(); if (!this.fullWidth) { - this.container.style.gridTemplateColumns = `repeat(${ - this.hostElement.children.length - }, ${Math.ceil(longestButtonWidth)}px)`; + this.container.style.gridTemplateColumns = longestButtonWidth + ? `repeat(${this.hostElement.children.length}, ${Math.ceil( + longestButtonWidth + )}px)` + : `repeat(${this.hostElement.children.length}, auto)`; } else { this.container.style.display = 'flex'; } - - this.selectedIndex = this.getSelectedIndex(); this.propagatePropsToChildren(); - this.position = 0; - this.status = tempState; - this.setState(tempState); } componentWillUpdate() { this.selectedIndex = this.getSelectedIndex(); - this.showHelperText = false; + this.showHelperText = this.shouldShowHelperText(); + } + shouldShowHelperText() { + let showHelperText = false; if ( this.invalid && this.status.filter((e) => e.selected === true).length <= 0 ) { - this.showHelperText = true; + showHelperText = true; } + return showHelperText; } getSelectedIndex() { @@ -195,27 +200,29 @@ export class SegmentedButton { // all segmented buttons should have the same width, based on the largest one getLongestButtonWidth() { let tempWidth = 0; - Array.from(this.hostElement.children).forEach((child) => { - const selected = child.hasAttribute('selected'); - const iconOnly = child.hasAttribute('icon-only'); - const checkmark = - this.size === 'small' - ? CHECKMARK_WIDTH_SMALL - : this.size === 'medium' - ? CHECKMARK_WIDTH_MEDIUM - : CHECKMARK_WIDTH_LARGE; - if (selected || iconOnly) { - tempWidth = - child.getBoundingClientRect().width > tempWidth - ? child.getBoundingClientRect().width - : tempWidth; - } else { - tempWidth = - child.getBoundingClientRect().width + checkmark > tempWidth - ? child.getBoundingClientRect().width + checkmark - : tempWidth; - } - }); + Array.from(this.hostElement.children) + .filter((child) => child.getBoundingClientRect().width) + .forEach((child) => { + const selected = child.hasAttribute('selected'); + const iconOnly = child.hasAttribute('icon-only'); + const checkmark = + this.size === 'small' + ? CHECKMARK_WIDTH_SMALL + : this.size === 'medium' + ? CHECKMARK_WIDTH_MEDIUM + : CHECKMARK_WIDTH_LARGE; + if (selected || iconOnly) { + tempWidth = + child.getBoundingClientRect().width > tempWidth + ? child.getBoundingClientRect().width + : tempWidth; + } else { + tempWidth = + child.getBoundingClientRect().width + checkmark > tempWidth + ? child.getBoundingClientRect().width + checkmark + : tempWidth; + } + }); return tempWidth; } diff --git a/packages/components/src/html/segment-button.html b/packages/components/src/html/segment-button.html new file mode 100644 index 0000000000..771e502b3e --- /dev/null +++ b/packages/components/src/html/segment-button.html @@ -0,0 +1,80 @@ + + + + + + Segment Buttons + + + + + + + + + +
Open Me for Disaster
+ + Apple + One+ + Samsung + Huawei + +
+
+ + Apple + One+ + Samsung + Huawei + +
+ + Label + Label + +
+ + + + + Apple + + + + + One+ + + + + + Samsung + + + + + Huawei + + +
+ + + Apple + One+ + Samsung + Huawei + + +