diff --git a/packages/elements/src/components/ino-range/ino-range.e2e.ts b/packages/elements/src/components/ino-range/ino-range.e2e.ts deleted file mode 100644 index 5374b65b0d..0000000000 --- a/packages/elements/src/components/ino-range/ino-range.e2e.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { setupPageWithContent } from '../../util/e2etests-setup'; - -const INO_RANGE = ``; -const INO_RANGE_SELECTOR = 'ino-range'; -const MDC_INPUT_SELECTOR = 'ino-range input'; -const MDC_SELECTOR = 'ino-range .mdc-slider'; - -describe('InoRange', () => { - describe('Properties', () => { - it('should disable the range component if disabled property is set to true', async () => { - const page = await setupPageWithContent(INO_RANGE); - const inoRange = await page.find(INO_RANGE_SELECTOR); - const mdcSlider = await page.find(MDC_SELECTOR); - - await inoRange.setAttribute('disabled', true); - await page.waitForChanges(); - - expect(mdcSlider).toHaveClass('mdc-slider--disabled'); - }); - - it('should set the min and max value of the range component', async () => { - const page = await setupPageWithContent(INO_RANGE); - const inoRange = await page.find(INO_RANGE_SELECTOR); - const mdcSlider = await page.find(MDC_INPUT_SELECTOR); - - await inoRange.setAttribute('min', 20); - await inoRange.setAttribute('max', 400); - await page.waitForChanges(); - - const min = await mdcSlider.getAttribute('min'); - const max = await mdcSlider.getAttribute('max'); - - expect(min).toBe('20'); - expect(max).toBe('400'); - }); - - it('should set the step value of the range component', async () => { - const page = await setupPageWithContent(INO_RANGE); - const inoRange = await page.find(INO_RANGE_SELECTOR); - const mdcSlider = await page.find(MDC_INPUT_SELECTOR); - - await inoRange.setAttribute('step', 5); - await page.waitForChanges(); - - const step = await mdcSlider.getAttribute('step'); - - expect(step).toBe('5'); - }); - - it('should render as a discrete slider if discrete is true', async () => { - const page = await setupPageWithContent(INO_RANGE); - const inoRange = await page.find(INO_RANGE_SELECTOR); - const mdcSlider = await page.find(MDC_SELECTOR); - - await inoRange.setAttribute('discrete', true); - await page.waitForChanges(); - - expect(mdcSlider).toHaveClass('mdc-slider--discrete'); - }); - - it('should render with tick marks if markers is true', async () => { - const page = await setupPageWithContent(INO_RANGE); - const inoRange = await page.find(INO_RANGE_SELECTOR); - const mdcSlider = await page.find(MDC_SELECTOR); - - await inoRange.setAttribute('markers', true); - await page.waitForChanges(); - - expect(mdcSlider).toHaveClass('mdc-slider--tick-marks'); - }); - }); - - describe('Events', () => { - it('should prevent the propagation of the MDCSlider:change event', async () => { - const page = await setupPageWithContent(INO_RANGE); - const mdcSlider = await page.find(MDC_INPUT_SELECTOR); - const valueChangeEvent = await page.spyOnEvent('MDCSlider:change'); - - await mdcSlider.triggerEvent('MDCSlider:change'); - await page.waitForChanges(); - - expect(valueChangeEvent).not.toHaveReceivedEvent(); - }); - }); -}); diff --git a/packages/elements/src/components/ino-range/ino-range.tsx b/packages/elements/src/components/ino-range/ino-range.tsx index 38847b1cce..eea495e32e 100644 --- a/packages/elements/src/components/ino-range/ino-range.tsx +++ b/packages/elements/src/components/ino-range/ino-range.tsx @@ -34,6 +34,10 @@ export class Range implements ComponentInterface { * Disables this element. */ @Prop() disabled?: boolean; + @Watch('disabled') + handleDisabledChange(isDisabled: boolean) { + this.sliderInstance.setDisabled(isDisabled); + } /** * Restricts the slider to only allow discrete values. @@ -131,7 +135,7 @@ export class Range implements ComponentInterface { ); this.inputElStart?.setAttribute('value', `${this.valueStart}`); this.sliderInstance = new MDCSlider(this.sliderEl); - + this.sliderInstance.setDisabled(this.disabled); this.sliderInstance.listen('MDCSlider:change', preventEvent); this.sliderInstance.listen('MDCSlider:input', this.handleInput); } @@ -144,7 +148,6 @@ export class Range implements ComponentInterface { private handleInput = (e: CustomEvent) => { e.stopPropagation(); - const { thumb, value } = e.detail; if (!this.ranged) { diff --git a/packages/storybook/src/stories/ino-button/ino-button.scss b/packages/storybook/src/stories/ino-button/ino-button.scss index ef66288e31..16ea9cf0b9 100644 --- a/packages/storybook/src/stories/ino-button/ino-button.scss +++ b/packages/storybook/src/stories/ino-button/ino-button.scss @@ -1,5 +1,7 @@ +@import '../utils'; + #story--buttons-ino-button--leading-and-trailing-icon-inner { - #root-inner { + @include story-container { display: flex; column-gap: 20px; } diff --git a/packages/storybook/src/stories/ino-fab-set/ino-fab-set.scss b/packages/storybook/src/stories/ino-fab-set/ino-fab-set.scss index f3717e9f8e..1c29669540 100644 --- a/packages/storybook/src/stories/ino-fab-set/ino-fab-set.scss +++ b/packages/storybook/src/stories/ino-fab-set/ino-fab-set.scss @@ -1,3 +1,5 @@ +@import '../utils'; + // only selects Stories of ino-fab-set without decorating stories with wrapper classes .sbdocs.sbdocs-content:has(#anchor--buttons-ino-fab-set--default) { [scale] { @@ -5,7 +7,7 @@ } } -#root-inner { +@include story-container { min-width: 10px; min-height: 10px; } diff --git a/packages/storybook/src/stories/ino-input/ino-input.scss b/packages/storybook/src/stories/ino-input/ino-input.scss index 7957be50fc..537b03451c 100644 --- a/packages/storybook/src/stories/ino-input/ino-input.scss +++ b/packages/storybook/src/stories/ino-input/ino-input.scss @@ -1,12 +1,16 @@ +@import '../utils'; + .sbdocs.sbdocs-content:has(#anchor--input-ino-input--default) { [scale] { width: 100%; } - #story--input-ino-input--states-inner #root-inner, - #story--input-ino-input--labels-inner #root-inner { - display: flex; - flex-direction: column; - gap: 15px; + #story--input-ino-input--states-inner, + #story--input-ino-input--labels-inner { + @include story-container { + display: flex; + flex-direction: column; + gap: 15px; + } } } diff --git a/packages/storybook/src/stories/ino-popover/ino-popover.scss b/packages/storybook/src/stories/ino-popover/ino-popover.scss index 8867a98518..4aee364103 100644 --- a/packages/storybook/src/stories/ino-popover/ino-popover.scss +++ b/packages/storybook/src/stories/ino-popover/ino-popover.scss @@ -1,3 +1,5 @@ +@import '../utils'; + .sbdocs.sbdocs-content:has(#anchor--notification-ino-popover--default) { [scale] { width: 100%; @@ -16,7 +18,7 @@ .innerZoomElementWrapper > div > div { overflow: unset; - #root-inner { + @include story-container { display: flex; gap: 5px; } diff --git a/packages/storybook/src/stories/ino-range/ino-range.scss b/packages/storybook/src/stories/ino-range/ino-range.scss index a63f4c53b6..9dc5ac0f6b 100644 --- a/packages/storybook/src/stories/ino-range/ino-range.scss +++ b/packages/storybook/src/stories/ino-range/ino-range.scss @@ -1,5 +1,11 @@ +@import '../utils'; + .sbdocs.sbdocs-content:has(#anchor--input-ino-range--default) { [scale] { width: 100%; } } + +@include story-container { + min-width: 200px; +} diff --git a/packages/storybook/src/stories/ino-range/ino-range.spec.ts b/packages/storybook/src/stories/ino-range/ino-range.spec.ts new file mode 100644 index 0000000000..b15aab433f --- /dev/null +++ b/packages/storybook/src/stories/ino-range/ino-range.spec.ts @@ -0,0 +1,64 @@ +import { expect, Page, test } from '@playwright/test'; +import { goToStory, setAttribute } from '../test-utils'; + +test.describe('ino-range', () => { + const move = async (page: Page, targetPercentage: number) => { + const slider = page.locator('.mdc-slider'); + const knob = slider.locator('.mdc-slider__thumb-knob'); + const knobBox = await knob.boundingBox(); + const sliderBox = await slider.boundingBox(); + + // Start from the middle of the slider's thumb + const start = { + x: knobBox.x + knobBox.width / 2, + y: knobBox.y + knobBox.height / 2, + }; + // Slide it to some endpoint determined by the target percentage + const end = { + x: sliderBox.x + sliderBox.width * targetPercentage, + y: knobBox.y + knobBox.height / 2, + }; + + await page.mouse.move(start.x, start.y); + await page.mouse.down(); + await page.mouse.move(end.x, end.y); + await page.mouse.up(); + }; + + test('should not move thumb knob when is disabled', async ({ page }) => { + await goToStory(page, ['Input', 'ino-range', 'default']); + const inoRange = page.locator('ino-range'); + const input = inoRange.getByRole('slider'); + await expect(input).toHaveValue('70'); + + await setAttribute(inoRange, 'disabled', 'disabled'); + await expect(input).toBeDisabled(); + + await move(page, 0.1); + await expect(input).toHaveValue('70'); + }); + + test('should move thumb knob', async ({ page }) => { + await goToStory(page, ['Input', 'ino-range', 'default']); + const input = page.getByRole('slider'); + await expect(input).toHaveValue('70'); + + await move(page, 0.95); + await expect(input).toHaveValue('95'); + await move(page, 0.17); + await expect(input).toHaveValue('17'); + }); + + test('should apply custom step value', async ({ page }) => { + await goToStory(page, ['Input', 'ino-range', 'default']); + const inoRange = page.locator('ino-range'); + await setAttribute(inoRange, 'step', '10'); + const input = page.getByRole('slider'); + await expect(input).toHaveValue('70'); + + await move(page, 0.61); + await expect(input).toHaveValue('60'); + await move(page, 0.86); + await expect(input).toHaveValue('90'); + }); +}); diff --git a/packages/storybook/src/stories/ino-select/ino-select.spec.ts b/packages/storybook/src/stories/ino-select/ino-select.spec.ts index c10f1f864f..80e0d7bea1 100644 --- a/packages/storybook/src/stories/ino-select/ino-select.spec.ts +++ b/packages/storybook/src/stories/ino-select/ino-select.spec.ts @@ -2,35 +2,26 @@ import { expect, Locator, test } from '@playwright/test'; import { goToStory, setAttribute } from '../test-utils'; test.describe('ino-select - Properties', () => { - let inoSelect: Locator; - test.beforeEach(async ({ page }) => { await goToStory(page, ['Input', 'ino-select', 'default']); - inoSelect = page.locator('ino-select'); }); - test('should render with the disabled property set to true', async () => { + test('should render with the disabled property set to true', async ({ + page, + }) => { + const inoSelect = page.locator('ino-select'); await setAttribute(inoSelect, 'disabled', 'true'); await inoSelect.click(); await expect(inoSelect.locator('li').first()).toBeHidden(); }); - test('should render with the required property set to true', async () => { + test('should render with the required property set to true', async ({ + page, + }) => { + const inoSelect = page.locator('ino-select'); await setAttribute(inoSelect, 'required', 'true'); await expect(inoSelect.locator('input[required]')).toBeAttached(); }); - - test('should render as an outlined element if inoOutlined is true', async () => { - await inoSelect.hover(); - const bBoxDefault = await inoSelect.boundingBox(); - - await setAttribute(inoSelect, 'outline', 'true'); - await inoSelect.hover(); - const bBoxOutline = await inoSelect.boundingBox(); - - expect(bBoxOutline.height).toBe(bBoxDefault.height); - expect(bBoxOutline.width).toBeGreaterThan(bBoxDefault.width); - }); }); test.describe('ino-select - Form integration', () => { diff --git a/packages/storybook/src/stories/ino-textarea/ino-textarea.scss b/packages/storybook/src/stories/ino-textarea/ino-textarea.scss index ce56c91908..765f072fcc 100644 --- a/packages/storybook/src/stories/ino-textarea/ino-textarea.scss +++ b/packages/storybook/src/stories/ino-textarea/ino-textarea.scss @@ -1,3 +1,5 @@ +@import '../utils'; + .sbdocs.sbdocs-content:has(#anchor--input-ino-textarea--default) { [scale] { width: 100%; @@ -8,8 +10,10 @@ margin-bottom: 15px; } - #story--input-ino-textarea--label-inner #root-inner { - display: flex; - gap: 15px; + #story--input-ino-textarea--label-inner { + @include story-container { + display: flex; + gap: 15px; + } } } diff --git a/packages/storybook/src/stories/ino-textarea/ino-textarea.spec.ts b/packages/storybook/src/stories/ino-textarea/ino-textarea.spec.ts index cc072c96de..6c48f9008a 100644 --- a/packages/storybook/src/stories/ino-textarea/ino-textarea.spec.ts +++ b/packages/storybook/src/stories/ino-textarea/ino-textarea.spec.ts @@ -46,13 +46,15 @@ test.describe('ino-textarea', () => { page, }) => { const inoTextArea = page.locator('ino-textarea'); + const textarea = inoTextArea.getByRole('textbox'); + await setAttribute(inoTextArea, 'cols', '1'); - await inoTextArea.hover(); - const { width: oneColWidth } = await inoTextArea.boundingBox(); + await inoTextArea.blur(); + const { width: oneColWidth } = await textarea.boundingBox(); await setAttribute(inoTextArea, 'cols', '10'); - await inoTextArea.hover(); - const { width: tenColsWidth } = await inoTextArea.boundingBox(); + await inoTextArea.blur(); + const { width: tenColsWidth } = await textarea.boundingBox(); expect(oneColWidth).toBeLessThan(tenColsWidth); }); diff --git a/packages/storybook/src/stories/ino-tooltip/ino-tooltip.scss b/packages/storybook/src/stories/ino-tooltip/ino-tooltip.scss index 4e42a0d05a..62c668c6a6 100644 --- a/packages/storybook/src/stories/ino-tooltip/ino-tooltip.scss +++ b/packages/storybook/src/stories/ino-tooltip/ino-tooltip.scss @@ -1,3 +1,5 @@ +@import '../utils'; + .story-tooltip { display: flex; justify-content: center; @@ -21,9 +23,11 @@ align-items: flex-end; height: 110px; - #story--notification-ino-tooltip--color-scheme-inner #root-inner { - display: flex; - gap: 15px; + #story--notification-ino-tooltip--color-scheme-inner { + @include story-container { + display: flex; + gap: 15px; + } } h4 { diff --git a/packages/storybook/src/stories/utils.scss b/packages/storybook/src/stories/utils.scss new file mode 100644 index 0000000000..fa303e4f36 --- /dev/null +++ b/packages/storybook/src/stories/utils.scss @@ -0,0 +1,9 @@ +/** + * Applies styling to the container of a story. + * The styling is encapsulated within this mixin because the ID may change in the future. + */ +@mixin story-container { + #root-inner { + @content; + } +}