From 998697989f377e1677989ee3abe1798c58429fed Mon Sep 17 00:00:00 2001 From: jmacaluso711 Date: Thu, 22 Aug 2024 15:15:32 -0400 Subject: [PATCH 01/27] Basic segmented control functionality --- .../ids-segmented-control/demos/example.html | 29 +++++++ .../ids-segmented-control/demos/index.html | 21 +++++ .../ids-segmented-control/demos/index.ts | 12 +++ .../ids-segmented-control/demos/index.yaml | 10 +++ .../ids-segmented-control/demos/listing.ts | 8 ++ .../ids-segmented-control.scss | 0 .../ids-segmented-control.ts | 80 +++++++++++++++++++ .../ids-segmented-control/readme.md | 0 .../ids-toggle-button/ids-toggle-button.ts | 16 ++++ src/enterprise-wc.ts | 1 + 10 files changed, 177 insertions(+) create mode 100644 src/components/ids-segmented-control/demos/example.html create mode 100644 src/components/ids-segmented-control/demos/index.html create mode 100644 src/components/ids-segmented-control/demos/index.ts create mode 100644 src/components/ids-segmented-control/demos/index.yaml create mode 100644 src/components/ids-segmented-control/demos/listing.ts create mode 100644 src/components/ids-segmented-control/ids-segmented-control.scss create mode 100644 src/components/ids-segmented-control/ids-segmented-control.ts create mode 100644 src/components/ids-segmented-control/readme.md diff --git a/src/components/ids-segmented-control/demos/example.html b/src/components/ids-segmented-control/demos/example.html new file mode 100644 index 0000000000..915663bff2 --- /dev/null +++ b/src/components/ids-segmented-control/demos/example.html @@ -0,0 +1,29 @@ + + + + <%= htmlWebpackPlugin.options.title %> + <%= htmlWebpackPlugin.options.font %> + + + + + diff --git a/src/components/ids-segmented-control/demos/index.html b/src/components/ids-segmented-control/demos/index.html new file mode 100644 index 0000000000..2825e710fc --- /dev/null +++ b/src/components/ids-segmented-control/demos/index.html @@ -0,0 +1,21 @@ + + + + <%= htmlWebpackPlugin.options.title %> + <%= htmlWebpackPlugin.options.font %> + + + + + diff --git a/src/components/ids-segmented-control/demos/index.ts b/src/components/ids-segmented-control/demos/index.ts new file mode 100644 index 0000000000..4afdd02370 --- /dev/null +++ b/src/components/ids-segmented-control/demos/index.ts @@ -0,0 +1,12 @@ +// Supporting components +import '../ids-segmented-control'; +import '../../ids-toggle-button/ids-toggle-button'; + +document.addEventListener('DOMContentLoaded', () => { + // Add an event listener to test clickable links + document.querySelectorAll('ids-toggle-button').forEach((idsButton) => { + idsButton.addEventListener('click', (e: any) => { + e.target.toggle(); + }); + }); +}); diff --git a/src/components/ids-segmented-control/demos/index.yaml b/src/components/ids-segmented-control/demos/index.yaml new file mode 100644 index 0000000000..742a70e816 --- /dev/null +++ b/src/components/ids-segmented-control/demos/index.yaml @@ -0,0 +1,10 @@ +--- + component: Segmented Control + link: ids-segmented-control + description: Multiple toggle button control + category: Navigation and Interaction + keywords: menu, selection, button, segmented control + examples: + - link: example.html + type: Main Example + description: Shows a basic segmented control diff --git a/src/components/ids-segmented-control/demos/listing.ts b/src/components/ids-segmented-control/demos/listing.ts new file mode 100644 index 0000000000..0151f6acc7 --- /dev/null +++ b/src/components/ids-segmented-control/demos/listing.ts @@ -0,0 +1,8 @@ +// Listing Page +import '../../ids-demo-app/ids-demo-listing'; +import indexYaml from './index.yaml'; + +const demoListing: any = document.querySelector('ids-demo-listing'); +if (demoListing) { + demoListing.data = indexYaml.examples; +} diff --git a/src/components/ids-segmented-control/ids-segmented-control.scss b/src/components/ids-segmented-control/ids-segmented-control.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/components/ids-segmented-control/ids-segmented-control.ts b/src/components/ids-segmented-control/ids-segmented-control.ts new file mode 100644 index 0000000000..9bc9392929 --- /dev/null +++ b/src/components/ids-segmented-control/ids-segmented-control.ts @@ -0,0 +1,80 @@ +import { customElement, scss } from '../../core/ids-decorators'; + +import IdsLocaleMixin from '../../mixins/ids-locale-mixin/ids-locale-mixin'; +import IdsEventsMixin from '../../mixins/ids-events-mixin/ids-events-mixin'; +import IdsKeyboardMixin from '../../mixins/ids-keyboard-mixin/ids-keyboard-mixin'; +import IdsElement from '../../core/ids-element'; + +import styles from './ids-segmented-control.scss'; + +const Base = IdsKeyboardMixin( + IdsLocaleMixin( + IdsEventsMixin( + IdsElement + ) + ) +); + +@customElement('ids-segmented-control') +@scss(styles) +export default class IdsSegementedControl extends Base { + constructor() { + super(); + } + + connectedCallback() { + super.connectedCallback(); + this.#attachEventHandlers(); + } + + disconnectedCallback() { + super.disconnectedCallback(); + this.#detachEventHandlers(); + } + + template(): string { + return `
+ +
`; + } + + /** + * Attaches event handlers for managing the toggle button states. + */ + #attachEventHandlers(): void { + this.onEvent('click', this.container, (event: Event) => this.handleToggleClick(event)); + } + + /** + * Handles the toggle button click event. + * @param {Event} event the click event + */ + handleToggleClick(event: Event): void { + const target = event.target as HTMLElement; + if (target.tagName.toLowerCase() === 'ids-toggle-button') { + this.updateToggleState(target); + } + } + + /** + * Updates the state of the toggle buttons, activating the clicked one and resetting others. + * @param {HTMLElement} clickedButton the button that was clicked + */ + updateToggleState(clickedButton: HTMLElement): void { + const buttons = this.querySelectorAll('ids-toggle-button'); + buttons.forEach((button: any) => { + if (button === clickedButton) { + button.pressed = true; + } else { + button.pressed = false; + } + }); + } + + /** + * Remove event handlers + */ + #detachEventHandlers(): void { + this.offEvent('click', this.container); + } +} diff --git a/src/components/ids-segmented-control/readme.md b/src/components/ids-segmented-control/readme.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/components/ids-toggle-button/ids-toggle-button.ts b/src/components/ids-toggle-button/ids-toggle-button.ts index 12c47ff213..c30fc1df05 100644 --- a/src/components/ids-toggle-button/ids-toggle-button.ts +++ b/src/components/ids-toggle-button/ids-toggle-button.ts @@ -36,6 +36,7 @@ export default class IdsToggleButton extends IdsButton { attributes.TEXT_OFF, attributes.TEXT_ON, attributes.PRESSED, + 'disable-icon' ]); } @@ -132,6 +133,20 @@ export default class IdsToggleButton extends IdsButton { return this.getAttribute(attributes.ICON_ON) || DEFAULT_ICON_ON; } + set disableIcon(val: string) { + const value = stringToBool(val); + if (value) { + this.setAttribute('disable-icon', ''); + this.removeAttribute(attributes.ICON_OFF); + this.removeAttribute(attributes.ICON_ON); + this.removeAttribute(attributes.ICON); + } + } + + get disableIcon(): boolean { + return this.hasAttribute('disable-icon'); + } + /** * Defines the `unpressed/off` toggle state text. * @param {string} val `unpressed/off` description text @@ -180,6 +195,7 @@ export default class IdsToggleButton extends IdsButton { * @returns {void} */ refreshIcon(): void { + if (this.disableIcon) return; this.icon = this[this.pressed ? 'iconOn' : 'iconOff']; } diff --git a/src/enterprise-wc.ts b/src/enterprise-wc.ts index 3e41b8d758..d7c98154ff 100644 --- a/src/enterprise-wc.ts +++ b/src/enterprise-wc.ts @@ -78,6 +78,7 @@ import './components/ids-rating/ids-rating'; import './components/ids-layout-flex/ids-scroll-container'; import './components/ids-scroll-view/ids-scroll-view'; import './components/ids-search-field/ids-search-field'; +import './components/ids-segmented-control/ids-segmented-control'; import './components/ids-separator/ids-separator'; import './components/ids-skip-link/ids-skip-link'; import './components/ids-slider/ids-slider'; From 5b17f980628e214066c3ed7dd6a92f05c9b2d49c Mon Sep 17 00:00:00 2001 From: jmacaluso711 Date: Thu, 22 Aug 2024 15:58:26 -0400 Subject: [PATCH 02/27] Basic styles --- src/components/ids-button/ids-button.scss | 20 +++++++++++++++++++ .../ids-segmented-control.scss | 5 +++++ .../ids-segmented-control.ts | 13 +++++++++++- 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/components/ids-button/ids-button.scss b/src/components/ids-button/ids-button.scss index 376a810ab7..64036b3576 100644 --- a/src/components/ids-button/ids-button.scss +++ b/src/components/ids-button/ids-button.scss @@ -376,3 +376,23 @@ button { :host(ids-toggle-button[pressed][color='red']) { --ids-button-tertiary-color-text-default: var(--ids-button-toggle-color-text-pressed-red); } + +:host(ids-toggle-button.ids-toggle-button-segmented) { + button { + border-radius: var(--ids-border-radius-circle); + + &:hover { + color: #333; + background-color: #fff; + } + } +} + +:host(ids-toggle-button.ids-toggle-button-segmented[pressed]) { + button { + color: #333; + background-color: #fff; + box-shadow: 0px 3px 14px 0px #16161817; + border-color: transparent; + } +} diff --git a/src/components/ids-segmented-control/ids-segmented-control.scss b/src/components/ids-segmented-control/ids-segmented-control.scss index e69de29bb2..886b1499e7 100644 --- a/src/components/ids-segmented-control/ids-segmented-control.scss +++ b/src/components/ids-segmented-control/ids-segmented-control.scss @@ -0,0 +1,5 @@ +.ids-segmented-control { + background-color: var(--ids-color-accent-gray-weaker); + padding: var(--ids-space-xxs); + border-radius: var(--ids-border-radius-circle); +} diff --git a/src/components/ids-segmented-control/ids-segmented-control.ts b/src/components/ids-segmented-control/ids-segmented-control.ts index 9bc9392929..ab6e512c1c 100644 --- a/src/components/ids-segmented-control/ids-segmented-control.ts +++ b/src/components/ids-segmented-control/ids-segmented-control.ts @@ -24,6 +24,7 @@ export default class IdsSegementedControl extends Base { connectedCallback() { super.connectedCallback(); + this.#addSegmentedClass(); this.#attachEventHandlers(); } @@ -33,11 +34,21 @@ export default class IdsSegementedControl extends Base { } template(): string { - return `
+ return `
`; } + /** + * Adds the '.ids-toggle-button-segmented' class to each ids-toggle-button. + */ + #addSegmentedClass(): void { + const buttons = this.querySelectorAll('ids-toggle-button'); + buttons.forEach((button: any) => { + button.classList.add('ids-toggle-button-segmented'); + }); + } + /** * Attaches event handlers for managing the toggle button states. */ From eda4b5149779e8ec0399b99b3ea118b450493871 Mon Sep 17 00:00:00 2001 From: jmacaluso711 Date: Thu, 22 Aug 2024 16:10:54 -0400 Subject: [PATCH 03/27] Add more examples --- .../ids-segmented-control/demos/example.html | 79 +++++++++++++++---- .../ids-segmented-control.scss | 2 +- 2 files changed, 63 insertions(+), 18 deletions(-) diff --git a/src/components/ids-segmented-control/demos/example.html b/src/components/ids-segmented-control/demos/example.html index 915663bff2..dc2deda85b 100644 --- a/src/components/ids-segmented-control/demos/example.html +++ b/src/components/ids-segmented-control/demos/example.html @@ -7,23 +7,68 @@ diff --git a/src/components/ids-segmented-control/ids-segmented-control.scss b/src/components/ids-segmented-control/ids-segmented-control.scss index 886b1499e7..966962efa8 100644 --- a/src/components/ids-segmented-control/ids-segmented-control.scss +++ b/src/components/ids-segmented-control/ids-segmented-control.scss @@ -1,5 +1,5 @@ .ids-segmented-control { - background-color: var(--ids-color-accent-gray-weaker); + background-color: var(--ids-color-accent-gray-weakest); padding: var(--ids-space-xxs); border-radius: var(--ids-border-radius-circle); } From 735ebe1a38272a0dc37e8eb4da385439b695a2fa Mon Sep 17 00:00:00 2001 From: jmacaluso711 Date: Fri, 23 Aug 2024 13:26:16 -0400 Subject: [PATCH 04/27] Minor style adjustments --- src/components/ids-button/ids-button.scss | 10 +++++----- .../ids-segmented-control/ids-segmented-control.scss | 7 +++++-- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/components/ids-button/ids-button.scss b/src/components/ids-button/ids-button.scss index 64036b3576..43c55ad2c3 100644 --- a/src/components/ids-button/ids-button.scss +++ b/src/components/ids-button/ids-button.scss @@ -382,17 +382,17 @@ button { border-radius: var(--ids-border-radius-circle); &:hover { - color: #333; - background-color: #fff; + color: var(--ids-color-foreground-dark-default); + background-color: var(--ids-color-white-100); } } } :host(ids-toggle-button.ids-toggle-button-segmented[pressed]) { button { - color: #333; - background-color: #fff; - box-shadow: 0px 3px 14px 0px #16161817; + color: var(--ids-color-foreground-dark-default); + background-color: var(--ids-color-white-100); + box-shadow: 0px 1px 10px 0px #1616180A; border-color: transparent; } } diff --git a/src/components/ids-segmented-control/ids-segmented-control.scss b/src/components/ids-segmented-control/ids-segmented-control.scss index 966962efa8..9b53c03837 100644 --- a/src/components/ids-segmented-control/ids-segmented-control.scss +++ b/src/components/ids-segmented-control/ids-segmented-control.scss @@ -1,5 +1,8 @@ .ids-segmented-control { - background-color: var(--ids-color-accent-gray-weakest); - padding: var(--ids-space-xxs); + align-items: center; + background-color: var(--ids-color-accent-gray-weaker); border-radius: var(--ids-border-radius-circle); + display: inline-flex; + gap: var(--ids-space-xxs); + padding: var(--ids-space-xxs) var(--ids-space-xs); } From 2c829e0401dd61db54f3a33156a2ef3c26b24702 Mon Sep 17 00:00:00 2001 From: jmacaluso711 Date: Fri, 23 Aug 2024 13:41:09 -0400 Subject: [PATCH 05/27] Add JSDocs comments to new toggle button settings --- .../ids-segmented-control.ts | 22 +++++++++++++------ .../ids-toggle-button/ids-toggle-button.ts | 14 +++++++++--- src/core/ids-attributes.ts | 1 + 3 files changed, 27 insertions(+), 10 deletions(-) diff --git a/src/components/ids-segmented-control/ids-segmented-control.ts b/src/components/ids-segmented-control/ids-segmented-control.ts index ab6e512c1c..a75fed8ac9 100644 --- a/src/components/ids-segmented-control/ids-segmented-control.ts +++ b/src/components/ids-segmented-control/ids-segmented-control.ts @@ -42,7 +42,7 @@ export default class IdsSegementedControl extends Base { /** * Adds the '.ids-toggle-button-segmented' class to each ids-toggle-button. */ - #addSegmentedClass(): void { + #addSegmentedClass() { const buttons = this.querySelectorAll('ids-toggle-button'); buttons.forEach((button: any) => { button.classList.add('ids-toggle-button-segmented'); @@ -52,15 +52,23 @@ export default class IdsSegementedControl extends Base { /** * Attaches event handlers for managing the toggle button states. */ - #attachEventHandlers(): void { + #attachEventHandlers() { this.onEvent('click', this.container, (event: Event) => this.handleToggleClick(event)); } + /** + * Get the toggle buttons + * @returns {HTMLElement} the toggle buttons + */ + get toggleButtons() { + return this.querySelectorAll('ids-toggle-button'); + } + /** * Handles the toggle button click event. * @param {Event} event the click event */ - handleToggleClick(event: Event): void { + handleToggleClick(event: Event) { const target = event.target as HTMLElement; if (target.tagName.toLowerCase() === 'ids-toggle-button') { this.updateToggleState(target); @@ -71,9 +79,9 @@ export default class IdsSegementedControl extends Base { * Updates the state of the toggle buttons, activating the clicked one and resetting others. * @param {HTMLElement} clickedButton the button that was clicked */ - updateToggleState(clickedButton: HTMLElement): void { - const buttons = this.querySelectorAll('ids-toggle-button'); - buttons.forEach((button: any) => { + updateToggleState(clickedButton: HTMLElement) { + const buttons = this.toggleButtons; + buttons.forEach((button: HTMLElement | any) => { if (button === clickedButton) { button.pressed = true; } else { @@ -85,7 +93,7 @@ export default class IdsSegementedControl extends Base { /** * Remove event handlers */ - #detachEventHandlers(): void { + #detachEventHandlers() { this.offEvent('click', this.container); } } diff --git a/src/components/ids-toggle-button/ids-toggle-button.ts b/src/components/ids-toggle-button/ids-toggle-button.ts index c30fc1df05..df7b91933d 100644 --- a/src/components/ids-toggle-button/ids-toggle-button.ts +++ b/src/components/ids-toggle-button/ids-toggle-button.ts @@ -36,7 +36,7 @@ export default class IdsToggleButton extends IdsButton { attributes.TEXT_OFF, attributes.TEXT_ON, attributes.PRESSED, - 'disable-icon' + attributes.DISABLE_ICON ]); } @@ -133,18 +133,26 @@ export default class IdsToggleButton extends IdsButton { return this.getAttribute(attributes.ICON_ON) || DEFAULT_ICON_ON; } + /** + * Defines if the icon is disabled + * @param {string} val `true` to disable the icon + */ set disableIcon(val: string) { const value = stringToBool(val); if (value) { - this.setAttribute('disable-icon', ''); + this.setAttribute(attributes.DISABLE_ICON, ''); this.removeAttribute(attributes.ICON_OFF); this.removeAttribute(attributes.ICON_ON); this.removeAttribute(attributes.ICON); } } + /** + * Get the `disable-icon` attribute + * @returns {boolean} `true` if the icon is disabled + */ get disableIcon(): boolean { - return this.hasAttribute('disable-icon'); + return this.hasAttribute(attributes.DISABLE_ICON); } /** diff --git a/src/core/ids-attributes.ts b/src/core/ids-attributes.ts index 777ac5708a..9b8ebb06ce 100644 --- a/src/core/ids-attributes.ts +++ b/src/core/ids-attributes.ts @@ -134,6 +134,7 @@ export const attributes = { DIRTY_TRACKER: 'dirty-tracker', DISABLE_CLIENT_FILTER: 'disable-client-filter', DISABLE_EVENTS: 'disable-native-events', + DISABLE_ICON: 'disable-icon', DISABLE_ROW_HIGHLIGHT: 'disable-row-highlight', DISABLED: 'disabled', DISMISSIBLE: 'dismissible', From 33ae78628915d6ef1d19faf8ec6ca347605762b9 Mon Sep 17 00:00:00 2001 From: jmacaluso711 Date: Fri, 23 Aug 2024 14:00:34 -0400 Subject: [PATCH 06/27] Add segmented control documentation --- .../ids-segmented-control/readme.md | 157 ++++++++++++++++++ 1 file changed, 157 insertions(+) diff --git a/src/components/ids-segmented-control/readme.md b/src/components/ids-segmented-control/readme.md index e69de29bb2..3c71d2bfe9 100644 --- a/src/components/ids-segmented-control/readme.md +++ b/src/components/ids-segmented-control/readme.md @@ -0,0 +1,157 @@ +# Ids Segmented Control + +## Description + +The IDS Segmented Control component is a UI element designed to group a series of toggle buttons, allowing users to select one option from a set. It acts as a container for ids-toggle-button elements, managing their state so that only one button can be active at any given time. When a button is selected, it is highlighted, and all other buttons in the group are reset to their default state. This component is ideal for creating segmented controls in user interfaces, where users need to choose between mutually exclusive options. + +## Use Cases + +- **Option Selection**: Use the IdsSegmentedControl to create a set of mutually exclusive options, such as selecting a view (e.g., list view vs. grid view), choosing a filter (e.g., most recent vs. most popular), or switching between different modes (e.g., edit vs. preview). + +- **Navigation**: Implement segmented controls for navigation within different sections of a UI, such as tabbed interfaces where each toggle button represents a different tab or view. + +- **Toolbars**: Integrate the segmented control into toolbars for applications, allowing users to toggle between different tools or settings with a single click. + +- **Forms**: Use in forms where a user needs to select one option out of several (e.g., selecting a payment method, choosing a subscription plan). + +## Terminology + +- **Segmented Control**: A UI component that groups multiple toggle buttons together, allowing users to select one option from a set. It ensures that only one button can be active at any time, with the others reverting to their default state. + +- **Toggle Button**: A button that can be switched between two states: active (pressed) and inactive (unpressed). In the context of a segmented control, a toggle button is used to represent an option within the control. + +- **Active State**: The state of a toggle button when it is selected or pressed. In a segmented control, the active state is visually distinguished (e.g., highlighted) and indicates the current selection. + +- **Default State**: The state of a toggle button when it is not selected. In a segmented control, buttons in the default state are not highlighted, indicating that they are not the current selection. + +- **Slot**: A placeholder within the segmented control's DOM structure where ids-toggle-button elements are placed. This allows for the flexible arrangement of toggle buttons within the segmented control component. + +- **CSS**: The `.ids-toggle-button-segmented` class is added to each toggle button within the segmented control to apply specific styling associated with the segmented control layout. + +## Features (With Code Examples) + +### Single Selection + +The IdsSegmentedControl ensures that only one ids-toggle-button can be selected at a time. When a user clicks on a toggle button, it becomes active, and all other buttons are set to their default state. + +```html + + + + + +``` + +In this example, when one of the toggle buttons is clicked, it will become active, and the others will automatically deactivate. + +### Toggle Buttons Without Icons + +Toggle buttons can be used without icons, relying solely on text to convey the option to the user. + +```html + + + + + +``` + +This example demonstrates toggle buttons that display only text, with no icons. + +### Toggle Buttons With Icons Only + +Toggle buttons can also be used with icons only, without any text. This is useful when you want a compact control or when the icon is universally understood. + +```html + + + + + +``` + +In this example, the toggle buttons use only icons to represent different states, making the control more compact and icon-focused. + +### Toggle Buttons With Both Icons and Text + +Toggle buttons can include both icons and text to provide clear, descriptive options to the user. + +```html + + + + + +``` +This example shows toggle buttons that use both icons and text, giving a more descriptive and visually informative control. + + +## States and Variations + +### "Default" Appearance + +In the default state, all toggle buttons within the segmented control are unpressed and styled according to their default appearance. + +```html + + + + + +``` + +### Active State + +When a toggle button is clicked, it transitions to the active state. The button is highlighted, and any associated text and icon are updated to reflect the active state. + +```html + + + + + +``` + +### Selected by default + +You can set one of the toggle buttons to be selected by default when the segmented control is first rendered. + +```html + + + + + +``` + +### Dsiabled State + +The disabled state prevents user interaction with the toggle buttons. When a button is disabled, it is visually indicated and does not respond to clicks or keyboard events. + +```html + + + + + +``` + +## Keyboard Guidelines + +## Responsive Guidelines + +## Proposed Changes + +## Test Plan + +1. Accessibility - Axe +1. Visual Regression Test +1. Repeat Tests in All Supported Browsers +1. Some of these as test cases from the [WC gold standard](https://github.com/webcomponents/gold-standard/wiki#api) +1. Can be consumed in NG/Vue/React (pull it in standalone/built see it works standalone) + +## Accessibility Guidelines + +## Regional Considerations + +Be conscious of the layout of content within your buttons when they are present in RTL situations. From 193515636bbec7474f55c22195d5b6049b9659e7 Mon Sep 17 00:00:00 2001 From: jmacaluso711 Date: Fri, 23 Aug 2024 14:01:44 -0400 Subject: [PATCH 07/27] Fix typo in readme file --- src/components/ids-segmented-control/readme.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/ids-segmented-control/readme.md b/src/components/ids-segmented-control/readme.md index 3c71d2bfe9..b51d167419 100644 --- a/src/components/ids-segmented-control/readme.md +++ b/src/components/ids-segmented-control/readme.md @@ -50,9 +50,9 @@ Toggle buttons can be used without icons, relying solely on text to convey the o ```html - - - + + + ``` From eab388cff3db8c9bbbbc701bc9f77672978eb058 Mon Sep 17 00:00:00 2001 From: jmacaluso711 Date: Wed, 28 Aug 2024 15:52:10 -0400 Subject: [PATCH 08/27] Implement semantic tokens for theming --- src/components/ids-button/ids-button.scss | 10 +++++----- .../ids-segmented-control/ids-segmented-control.scss | 6 +++--- src/themes/default/ids-theme-default-core.scss | 6 ++++++ src/themes/default/ids-theme-default-dark.scss | 5 +++++ 4 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/components/ids-button/ids-button.scss b/src/components/ids-button/ids-button.scss index fbba11732a..b59d0cb03b 100644 --- a/src/components/ids-button/ids-button.scss +++ b/src/components/ids-button/ids-button.scss @@ -382,17 +382,17 @@ button { border-radius: var(--ids-border-radius-circle); &:hover { - color: var(--ids-color-foreground-dark-default); - background-color: var(--ids-color-white-100); + color: var(--ids-segmented-control-text-color); + background-color: var(--ids-segmented-control-background-hover); } } } :host(ids-toggle-button.ids-toggle-button-segmented[pressed]) { button { - color: var(--ids-color-foreground-dark-default); - background-color: var(--ids-color-white-100); - box-shadow: 0px 1px 10px 0px #1616180A; + color: var(--ids-segmented-control-text-color); + background-color: var(--ids-segmented-control-background-hover); + box-shadow: var(--ids-segmented-control-box-shadow); border-color: transparent; } } diff --git a/src/components/ids-segmented-control/ids-segmented-control.scss b/src/components/ids-segmented-control/ids-segmented-control.scss index 9b53c03837..e43ebe4cbf 100644 --- a/src/components/ids-segmented-control/ids-segmented-control.scss +++ b/src/components/ids-segmented-control/ids-segmented-control.scss @@ -1,8 +1,8 @@ .ids-segmented-control { align-items: center; - background-color: var(--ids-color-accent-gray-weaker); + background-color: var(--ids-color-accent-neutral-weaker); border-radius: var(--ids-border-radius-circle); display: inline-flex; - gap: var(--ids-space-xxs); - padding: var(--ids-space-xxs) var(--ids-space-xs); + gap: var(--ids-space-2xs); + padding: var(--ids-space-2xs) var(--ids-space-xs); } diff --git a/src/themes/default/ids-theme-default-core.scss b/src/themes/default/ids-theme-default-core.scss index 9a955fa7c8..4e0f28200d 100644 --- a/src/themes/default/ids-theme-default-core.scss +++ b/src/themes/default/ids-theme-default-core.scss @@ -1310,6 +1310,12 @@ --ids-search-field-card-color-text: var(--ids-color-text-default); --ids-search-field-card-color-background: var(--ids-color-neutral-10); + // Segmented Control + --ids-segmented-control-box-shadow: 0px 1px 10px 0px rgab(0 0 0 / 10%); + --ids-segmented-control-text-color: var(--ids-color-foreground-dark-default); + --ids-segmented-control-background-hover: var(--ids-color-background-elevated); + --ids-segmented-control-background-selected: var(--ids-color-background-elevated); + // Separator --ids-separator-color-background: var(--ids-color-accent-neutral-default); --ids-separator-height: var(--ids-space-md); diff --git a/src/themes/default/ids-theme-default-dark.scss b/src/themes/default/ids-theme-default-dark.scss index 4957202100..d813187c8f 100644 --- a/src/themes/default/ids-theme-default-dark.scss +++ b/src/themes/default/ids-theme-default-dark.scss @@ -434,6 +434,11 @@ --ids-search-field-header-color-text: var(--ids-color-text-default); --ids-search-field-header-color-icon-default: var(--ids-color-neutral-10); + // Segmented Control + --ids-segmented-control-text-color: var(--ids-color-foreground-default-secondary); + --ids-segmented-control-background-hover: var(--ids-color-background-default); + --ids-segmented-control-background-selected: var(--ids-color-background-default); + // Slider --ids-slider-tick-color-background-default: var(--ids-color-primary-60); --ids-slider-thumb-color-background-default: var(--ids-color-primary-60); From 3ff942e53ddfca3623b456f3a163521fb16cbc0f Mon Sep 17 00:00:00 2001 From: jmacaluso711 Date: Wed, 28 Aug 2024 16:01:27 -0400 Subject: [PATCH 09/27] Fix lint errors --- src/components/ids-segmented-control/readme.md | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/components/ids-segmented-control/readme.md b/src/components/ids-segmented-control/readme.md index b51d167419..9288d338ad 100644 --- a/src/components/ids-segmented-control/readme.md +++ b/src/components/ids-segmented-control/readme.md @@ -7,25 +7,17 @@ The IDS Segmented Control component is a UI element designed to group a series o ## Use Cases - **Option Selection**: Use the IdsSegmentedControl to create a set of mutually exclusive options, such as selecting a view (e.g., list view vs. grid view), choosing a filter (e.g., most recent vs. most popular), or switching between different modes (e.g., edit vs. preview). - - **Navigation**: Implement segmented controls for navigation within different sections of a UI, such as tabbed interfaces where each toggle button represents a different tab or view. - - **Toolbars**: Integrate the segmented control into toolbars for applications, allowing users to toggle between different tools or settings with a single click. - - **Forms**: Use in forms where a user needs to select one option out of several (e.g., selecting a payment method, choosing a subscription plan). ## Terminology - **Segmented Control**: A UI component that groups multiple toggle buttons together, allowing users to select one option from a set. It ensures that only one button can be active at any time, with the others reverting to their default state. - - **Toggle Button**: A button that can be switched between two states: active (pressed) and inactive (unpressed). In the context of a segmented control, a toggle button is used to represent an option within the control. - - **Active State**: The state of a toggle button when it is selected or pressed. In a segmented control, the active state is visually distinguished (e.g., highlighted) and indicates the current selection. - - **Default State**: The state of a toggle button when it is not selected. In a segmented control, buttons in the default state are not highlighted, indicating that they are not the current selection. - - **Slot**: A placeholder within the segmented control's DOM structure where ids-toggle-button elements are placed. This allows for the flexible arrangement of toggle buttons within the segmented control component. - - **CSS**: The `.ids-toggle-button-segmented` class is added to each toggle button within the segmented control to apply specific styling associated with the segmented control layout. ## Features (With Code Examples) @@ -83,8 +75,8 @@ Toggle buttons can include both icons and text to provide clear, descriptive opt ``` -This example shows toggle buttons that use both icons and text, giving a more descriptive and visually informative control. +This example shows toggle buttons that use both icons and text, giving a more descriptive and visually informative control. ## States and Variations From cb281c86c8bb3e25b4bcc4385ebb4d93b97c48dc Mon Sep 17 00:00:00 2001 From: jmacaluso711 Date: Wed, 28 Aug 2024 16:08:12 -0400 Subject: [PATCH 10/27] Add segmented control to demo app --- src/components/ids-demo-app/demos/index.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/ids-demo-app/demos/index.ts b/src/components/ids-demo-app/demos/index.ts index c3fe4fe3f3..d20acbf50f 100644 --- a/src/components/ids-demo-app/demos/index.ts +++ b/src/components/ids-demo-app/demos/index.ts @@ -74,6 +74,7 @@ import radioYaml from '../../ids-radio/demos/index.yaml'; import ratingYaml from '../../ids-rating/demos/index.yaml'; import scrollViewYaml from '../../ids-scroll-view/demos/index.yaml'; import searchFieldYaml from '../../ids-search-field/demos/index.yaml'; +import segmentedControlYaml from '../../ids-segmented-control/demos/index.yaml'; import separatorYaml from '../../ids-separator/demos/index.yaml'; import skipLinkYaml from '../../ids-skip-link/demos/index.yaml'; import sliderYaml from '../../ids-slider/demos/index.yaml'; @@ -196,6 +197,7 @@ addYaml(radioYaml); addYaml(ratingYaml); addYaml(scrollViewYaml); addYaml(searchFieldYaml); +addYaml(segmentedControlYaml); addYaml(separatorYaml); addYaml(skipLinkYaml); addYaml(sliderYaml); From 5783d0074754faf07337eff444ca828633d14953 Mon Sep 17 00:00:00 2001 From: jmacaluso711 Date: Thu, 29 Aug 2024 13:59:25 -0400 Subject: [PATCH 11/27] Add segmented control test --- .../ids-segmented-control.spec.ts | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 tests/ids-segmented-control/ids-segmented-control.spec.ts diff --git a/tests/ids-segmented-control/ids-segmented-control.spec.ts b/tests/ids-segmented-control/ids-segmented-control.spec.ts new file mode 100644 index 0000000000..7d72417160 --- /dev/null +++ b/tests/ids-segmented-control/ids-segmented-control.spec.ts @@ -0,0 +1,106 @@ +import AxeBuilder from '@axe-core/playwright'; +import percySnapshot from '@percy/playwright'; +import { expect } from '@playwright/test'; +import { test } from '../base-fixture'; + +import IdsSegmentedControl from '../../src/components/ids-segmented-control/ids-segmented-control'; + +test.describe('IdsSegmentedControl tests', () => { + const url = '/ids-segmented-control/example.html'; + let segmentedControl: any; + + test.beforeEach(async ({ page }) => { + await page.goto(url); + segmentedControl = await page.locator('ids-segmented-control').first(); + }); + + test.describe('general page checks', () => { + test('should have a title', async ({ page }) => { + await expect(page).toHaveTitle('IDS Segmented Control Component'); + }); + + test('should not have errors', async ({ page, browserName }) => { + if (browserName === 'firefox') return; + let exceptions = null; + await page.on('pageerror', (error) => { + exceptions = error; + }); + + await page.goto(url); + await page.waitForLoadState(); + await expect(exceptions).toBeNull(); + }); + }); + + test.describe('accessibility tests', () => { + test('should pass an Axe scan', async ({ page, browserName }) => { + if (browserName !== 'chromium') return; + const accessibilityScanResults = await new AxeBuilder({ page } as any) + .disableRules(['nested-interactive', 'scrollable-region-focusable']) + .analyze(); + expect(accessibilityScanResults.violations).toEqual([]); + }); + }); + + test.describe('snapshot tests', () => { + test('should match innerHTML snapshot', async ({ page, browserName }) => { + if (browserName !== 'chromium') return; + const html = await page.evaluate(() => { + const element = document.querySelector('ids-segmented-control'); + return element?.outerHTML; + }); + await expect(html).toMatchSnapshot('segmented-control-html'); + }); + + test('should match shadowRoot snapshot', async ({ page, browserName }) => { + if (browserName !== 'chromium') return; + const shadowRootHtml = await page.evaluate(() => { + const element = document.querySelector('ids-segmented-control'); + const shadowRoot = element?.shadowRoot; + shadowRoot?.querySelector('style')?.remove(); + return shadowRoot?.innerHTML; + }); + await expect(shadowRootHtml).toMatchSnapshot('segmented-control-shadow'); + }); + + test('should match the visual snapshot in percy', async ({ page, browserName }) => { + if (browserName !== 'chromium') return; + await percySnapshot(page, 'ids-segmented-control-light'); + }); + }); + + test.describe('functionality tests', () => { + test('can handle toggle button clicks', async ({ page }) => { + const button1 = await page.locator('#button1'); + const button2 = await page.locator('#button2'); + + // Click the first button and verify it is pressed + await button1.click(); + await expect(button1).toHaveAttribute('pressed', 'true'); + await expect(button2).not.toHaveAttribute('pressed'); + + // Click the second button and verify the state updates + await button2.click(); + await expect(button1).not.toHaveAttribute('pressed'); + await expect(button2).toHaveAttribute('pressed', 'true'); + }); + + test('should add the "ids-toggle-button-segmented" class to each toggle button', async ({ page }) => { + const hasSegmentedClass = await page.evaluate(() => { + const buttons = document.querySelectorAll('ids-toggle-button'); + return Array.from(buttons).every((button) => button.classList.contains('ids-toggle-button-segmented')); + }); + expect(hasSegmentedClass).toBeTruthy(); + }); + + test('can handle event detachment', async ({ page }) => { + await segmentedControl.evaluate((el: IdsSegmentedControl) => { + el.remove(); + }); + + // Ensure that no errors are thrown after the component is removed + const button1 = await page.locator('#button1'); + await expect(button1.click()).resolves.not.toThrow(); + }); + }); +}); From 228d69defad9bc4b56dfb517a62c49e00bcfe705 Mon Sep 17 00:00:00 2001 From: jmacaluso711 Date: Thu, 29 Aug 2024 14:05:19 -0400 Subject: [PATCH 12/27] Add changelog note --- doc/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/CHANGELOG.md b/doc/CHANGELOG.md index 5d7a90252a..e59dc2dc8a 100644 --- a/doc/CHANGELOG.md +++ b/doc/CHANGELOG.md @@ -7,6 +7,7 @@ - `[Card]` Adds `draggable` attribute to ids-card which allows card to be used in drag and drop scenarios. ([#2423](https://github.com/infor-design/enterprise-wc/issues/2423)) - `[Datagrid]` Fix Clear Row / Eraser button so that changes persist throughout pagination. ([#2615]https://github.com/infor-design/enterprise-wc/issues/2615) - `[NotificationBanner]` Added a notification service which can be used to manage notification banners on a page. ([#2160]https://github.com/infor-design/enterprise-wc/issues/2160) +- `[SegmentedControl]` Added the IdsSegmentedControl component. ([#2180]https://github.com/infor-design/enterprise-wc/issues/2180) - `[Switch]` Added a label position setting that allows positioning the label on either the right or left side of the slider. ([#2579](https://github.com/infor-design/enterprise-wc/issues/2579)) - `[Themes]` Added a setting `IdsGlobal.themePath` that you can use to set the location of the theme files. ([#2125]https://github.com/infor-design/enterprise-wc/issues/2125) - `[Themes]` Added latest round of semantic tokens. ([#2471]https://github.com/infor-design/enterprise-wc/issues/2471) From 86f2794e685bcad43b84e1f775cd3b89a8367ec8 Mon Sep 17 00:00:00 2001 From: Tim McConechy Date: Thu, 29 Aug 2024 14:25:06 -0400 Subject: [PATCH 13/27] Add snap shots and fix axe scan --- src/components/ids-segmented-control/demos/example.html | 4 ++++ .../snapshots/segmented-control-html.snap | 5 +++++ .../snapshots/segmented-control-shadow.snap | 3 +++ 3 files changed, 12 insertions(+) create mode 100644 tests/ids-segmented-control/snapshots/segmented-control-html.snap create mode 100644 tests/ids-segmented-control/snapshots/segmented-control-shadow.snap diff --git a/src/components/ids-segmented-control/demos/example.html b/src/components/ids-segmented-control/demos/example.html index dc2deda85b..84daf7cb12 100644 --- a/src/components/ids-segmented-control/demos/example.html +++ b/src/components/ids-segmented-control/demos/example.html @@ -7,6 +7,10 @@