diff --git a/core/src/components.d.ts b/core/src/components.d.ts index 2cbca79a7a3..550ad28183c 100644 --- a/core/src/components.d.ts +++ b/core/src/components.d.ts @@ -12,13 +12,13 @@ import { OverlayEventDetail } from "./utils/overlays-interface"; import { IonicSafeString } from "./utils/sanitization"; import { AlertButton, AlertInput } from "./components/alert/alert-interface"; import { Hue } from "./themes/themes.interfaces"; -import { IonBadgeShape, IonBadgeSize, IonBadgeVerticalPosition } from "./components/badge/badge.interfaces"; +import { IonBadgeShape, IonBadgeSize, IonBadgeVerticalPosition } from "./components/badge/badge.interface"; import { RouteID, RouterDirection, RouterEventDetail, RouteWrite } from "./components/router/utils/interface"; import { BreadcrumbCollapsedClickEventDetail } from "./components/breadcrumb/breadcrumb-interface"; import { CheckboxChangeEventDetail } from "./components/checkbox/checkbox-interface"; -import { IonChipFill, IonChipShape, IonChipSize } from "./components/chip/chip.interfaces"; +import { IonChipFill, IonChipShape, IonChipSize } from "./components/chip/chip.interface"; import { IonColValue } from "./components/col/col.interface"; -import { ScrollBaseDetail, ScrollDetail } from "./components/content/content.interfaces"; +import { ScrollBaseDetail, ScrollDetail } from "./components/content/content.interface"; import { DatetimeChangeEventDetail, DatetimeHighlight, DatetimeHighlightCallback, DatetimeHourCycle, DatetimeParts, DatetimePresentation, FormatOptions, TitleSelectedDatesFormatter } from "./components/datetime/datetime-interface"; import { FooterScrollEffect } from "./components/footer/footer-interface"; import { IonGalleryColumns, IonGalleryGap } from "./components/gallery/gallery.interface"; @@ -33,7 +33,7 @@ import { ViewController } from "./components/nav/view-controller"; import { PickerChangeEventDetail } from "./components/picker/picker-interfaces"; import { PickerColumnChangeEventDetail, PickerColumnValue } from "./components/picker-column/picker-column-interfaces"; import { PopoverSize, PositionAlign, PositionReference, PositionSide, TriggerAction } from "./components/popover/popover-interface"; -import { IonProgressBarShape } from "./components/progress-bar/progress-bar.interfaces"; +import { IonProgressBarShape } from "./components/progress-bar/progress-bar.interface"; import { RadioGroupChangeEventDetail, RadioGroupCompareFn } from "./components/radio-group/radio-group-interface"; import { PinFormatter, RangeChangeEventDetail, RangeKnobMoveEndEventDetail, RangeKnobMoveStartEventDetail, RangeValue } from "./components/range/range-interface"; import { RefresherEventDetail, RefresherPullEndEventDetail } from "./components/refresher/refresher-interface"; @@ -47,7 +47,7 @@ import { SegmentViewScrollEvent } from "./components/segment-view/segment-view-i import { SelectChangeEventDetail, SelectCompareFn, SelectInterface } from "./components/select/select-interface"; import { SelectModalOption } from "./components/select-modal/select-modal-interface"; import { SelectPopoverOption } from "./components/select-popover/select-popover-interface"; -import { SpinnerSize } from "./components/spinner/spinner.interfaces"; +import { SpinnerSize } from "./components/spinner/spinner.interface"; import { TabBarChangedEventDetail, TabBarScrollEffect, TabButtonClickEventDetail, TabButtonLayout } from "./components/tab-bar/tab-bar-interface"; import { TextareaChangeEventDetail, TextareaInputEventDetail } from "./components/textarea/textarea-interface"; import { IonTitleSize } from "./components/title/title.interface"; @@ -60,13 +60,13 @@ export { OverlayEventDetail } from "./utils/overlays-interface"; export { IonicSafeString } from "./utils/sanitization"; export { AlertButton, AlertInput } from "./components/alert/alert-interface"; export { Hue } from "./themes/themes.interfaces"; -export { IonBadgeShape, IonBadgeSize, IonBadgeVerticalPosition } from "./components/badge/badge.interfaces"; +export { IonBadgeShape, IonBadgeSize, IonBadgeVerticalPosition } from "./components/badge/badge.interface"; export { RouteID, RouterDirection, RouterEventDetail, RouteWrite } from "./components/router/utils/interface"; export { BreadcrumbCollapsedClickEventDetail } from "./components/breadcrumb/breadcrumb-interface"; export { CheckboxChangeEventDetail } from "./components/checkbox/checkbox-interface"; -export { IonChipFill, IonChipShape, IonChipSize } from "./components/chip/chip.interfaces"; +export { IonChipFill, IonChipShape, IonChipSize } from "./components/chip/chip.interface"; export { IonColValue } from "./components/col/col.interface"; -export { ScrollBaseDetail, ScrollDetail } from "./components/content/content.interfaces"; +export { ScrollBaseDetail, ScrollDetail } from "./components/content/content.interface"; export { DatetimeChangeEventDetail, DatetimeHighlight, DatetimeHighlightCallback, DatetimeHourCycle, DatetimeParts, DatetimePresentation, FormatOptions, TitleSelectedDatesFormatter } from "./components/datetime/datetime-interface"; export { FooterScrollEffect } from "./components/footer/footer-interface"; export { IonGalleryColumns, IonGalleryGap } from "./components/gallery/gallery.interface"; @@ -81,7 +81,7 @@ export { ViewController } from "./components/nav/view-controller"; export { PickerChangeEventDetail } from "./components/picker/picker-interfaces"; export { PickerColumnChangeEventDetail, PickerColumnValue } from "./components/picker-column/picker-column-interfaces"; export { PopoverSize, PositionAlign, PositionReference, PositionSide, TriggerAction } from "./components/popover/popover-interface"; -export { IonProgressBarShape } from "./components/progress-bar/progress-bar.interfaces"; +export { IonProgressBarShape } from "./components/progress-bar/progress-bar.interface"; export { RadioGroupChangeEventDetail, RadioGroupCompareFn } from "./components/radio-group/radio-group-interface"; export { PinFormatter, RangeChangeEventDetail, RangeKnobMoveEndEventDetail, RangeKnobMoveStartEventDetail, RangeValue } from "./components/range/range-interface"; export { RefresherEventDetail, RefresherPullEndEventDetail } from "./components/refresher/refresher-interface"; @@ -95,7 +95,7 @@ export { SegmentViewScrollEvent } from "./components/segment-view/segment-view-i export { SelectChangeEventDetail, SelectCompareFn, SelectInterface } from "./components/select/select-interface"; export { SelectModalOption } from "./components/select-modal/select-modal-interface"; export { SelectPopoverOption } from "./components/select-popover/select-popover-interface"; -export { SpinnerSize } from "./components/spinner/spinner.interfaces"; +export { SpinnerSize } from "./components/spinner/spinner.interface"; export { TabBarChangedEventDetail, TabBarScrollEffect, TabButtonClickEventDetail, TabButtonLayout } from "./components/tab-bar/tab-bar-interface"; export { TextareaChangeEventDetail, TextareaInputEventDetail } from "./components/textarea/textarea-interface"; export { IonTitleSize } from "./components/title/title.interface"; diff --git a/core/src/components/avatar/test/badge/avatar.e2e.ts b/core/src/components/avatar/test/badge/avatar.e2e.ts index b417dddd25a..ca89867ebee 100644 --- a/core/src/components/avatar/test/badge/avatar.e2e.ts +++ b/core/src/components/avatar/test/badge/avatar.e2e.ts @@ -1,7 +1,7 @@ import { expect } from '@playwright/test'; import { configs, test } from '@utils/test/playwright'; -import { ION_BADGE_SIZES, ION_BADGE_VERTICAL_POSITIONS } from '../../../badge/badge.interfaces'; +import { ION_BADGE_SIZES, ION_BADGE_VERTICAL_POSITIONS } from '../../../badge/badge.interface'; configs({ modes: ['md', 'ios', 'ionic-md'] }).forEach(({ config, screenshot, title }) => { test.describe(title('avatar: badge'), () => { diff --git a/core/src/components/badge/badge.interfaces.ts b/core/src/components/badge/badge.interface.ts similarity index 100% rename from core/src/components/badge/badge.interfaces.ts rename to core/src/components/badge/badge.interface.ts diff --git a/core/src/components/badge/badge.tsx b/core/src/components/badge/badge.tsx index f534030ac3c..b449f933890 100644 --- a/core/src/components/badge/badge.tsx +++ b/core/src/components/badge/badge.tsx @@ -6,7 +6,7 @@ import { config } from '../../global/config'; import type { Color } from '../../interface'; import type { Hue } from '../../themes/themes.interfaces'; -import type { IonBadgeShape, IonBadgeSize, IonBadgeVerticalPosition } from './badge.interfaces'; +import type { IonBadgeShape, IonBadgeSize, IonBadgeVerticalPosition } from './badge.interface'; /** * @virtualProp {"ios" | "md"} mode - The mode determines the platform behaviors of the component. diff --git a/core/src/components/badge/test/shape/badge.e2e.ts b/core/src/components/badge/test/shape/badge.e2e.ts index fe788e93ae4..04a0b7ce445 100644 --- a/core/src/components/badge/test/shape/badge.e2e.ts +++ b/core/src/components/badge/test/shape/badge.e2e.ts @@ -1,7 +1,7 @@ import { expect } from '@playwright/test'; import { configs, test } from '@utils/test/playwright'; -import { ION_BADGE_SHAPES } from '../../../badge/badge.interfaces'; +import { ION_BADGE_SHAPES } from '../../../badge/badge.interface'; /** * This behavior does not vary across directions. diff --git a/core/src/components/badge/test/size/badge.e2e.ts b/core/src/components/badge/test/size/badge.e2e.ts index 25d131cd4ad..750fe168078 100644 --- a/core/src/components/badge/test/size/badge.e2e.ts +++ b/core/src/components/badge/test/size/badge.e2e.ts @@ -1,7 +1,7 @@ import { expect } from '@playwright/test'; import { configs, test } from '@utils/test/playwright'; -import { ION_BADGE_SIZES } from '../../../badge/badge.interfaces'; +import { ION_BADGE_SIZES } from '../../../badge/badge.interface'; /** * This behavior does not vary across directions. diff --git a/core/src/components/button/test/badge/button.e2e.ts b/core/src/components/button/test/badge/button.e2e.ts index f4854147528..b4c4ad86449 100644 --- a/core/src/components/button/test/badge/button.e2e.ts +++ b/core/src/components/button/test/badge/button.e2e.ts @@ -1,7 +1,7 @@ import { expect } from '@playwright/test'; import { configs, test } from '@utils/test/playwright'; -import { ION_BADGE_SIZES, ION_BADGE_VERTICAL_POSITIONS } from '../../../badge/badge.interfaces'; +import { ION_BADGE_SIZES, ION_BADGE_VERTICAL_POSITIONS } from '../../../badge/badge.interface'; configs({ modes: ['md', 'ios', 'ionic-md'] }).forEach(({ config, screenshot, title }) => { test.describe(title('button: badge'), () => { diff --git a/core/src/components/chip/chip.interfaces.ts b/core/src/components/chip/chip.interface.ts similarity index 81% rename from core/src/components/chip/chip.interfaces.ts rename to core/src/components/chip/chip.interface.ts index c66a56a1a3c..da809d6e389 100644 --- a/core/src/components/chip/chip.interfaces.ts +++ b/core/src/components/chip/chip.interface.ts @@ -2,7 +2,9 @@ import type { IonPadding, IonMargin, Hue } from '../../themes/themes.interfaces' export type IonChipRecipe = { letterSpacing?: string | number; - lineHeight?: string | number; + line?: { + height?: string | number; + }; margin?: IonMargin; padding?: IonPadding; gap?: string | number; @@ -71,7 +73,9 @@ type IonChipColorDefinition = { }; type IonChipSizeDefinition = { - minHeight?: string | number; + min?: { + height?: string | number; + }; font?: { size?: string | number; @@ -116,6 +120,9 @@ export type IonChipConfig = { shape?: IonChipShape; }; -export type IonChipFill = 'outline' | 'solid'; -export type IonChipSize = 'small' | 'large'; -export type IonChipShape = 'soft' | 'round' | 'rectangular'; +export const ION_CHIP_FILLS = ['solid', 'outline'] as const; +export type IonChipFill = (typeof ION_CHIP_FILLS)[number]; +export const ION_CHIP_SIZES = ['small', 'large'] as const; +export type IonChipSize = (typeof ION_CHIP_SIZES)[number]; +export const ION_CHIP_SHAPES = ['soft', 'round', 'rectangular'] as const; +export type IonChipShape = (typeof ION_CHIP_SHAPES)[number]; diff --git a/core/src/components/chip/chip.tsx b/core/src/components/chip/chip.tsx index efc5f5266f3..34fabfbe48b 100644 --- a/core/src/components/chip/chip.tsx +++ b/core/src/components/chip/chip.tsx @@ -7,7 +7,7 @@ import { config } from '../../global/config'; import type { Color } from '../../interface'; import type { Hue } from '../../themes/themes.interfaces'; -import type { IonChipFill, IonChipSize, IonChipShape } from './chip.interfaces'; +import type { IonChipFill, IonChipSize, IonChipShape } from './chip.interface'; /** * @virtualProp {"ios" | "md"} mode - The mode determines the platform behaviors of the component. diff --git a/core/src/components/chip/test/hue/chip.e2e.ts b/core/src/components/chip/test/hue/chip.e2e.ts index 18bf32e04c5..5afcca620ae 100644 --- a/core/src/components/chip/test/hue/chip.e2e.ts +++ b/core/src/components/chip/test/hue/chip.e2e.ts @@ -1,217 +1,58 @@ import { expect } from '@playwright/test'; import { configs, test } from '@utils/test/playwright'; +import { HUES } from '../../../../themes/themes.interfaces'; +import { ION_CHIP_FILLS } from '../../chip.interface'; + +const COLORS = [ + undefined, + 'primary', + 'secondary', + 'tertiary', + 'success', + 'warning', + 'danger', + 'light', + 'medium', + 'dark', +]; + /** * This behavior does not vary across directions. */ configs({ directions: ['ltr'], modes: ['ionic-md', 'md', 'ios'] }).forEach(({ config, screenshot, title }) => { test.describe(title('chip: hue'), () => { - test('should render subtle chips', async ({ page }) => { - await page.setContent( - ` - - -
- - - Chip - - - - Chip - - - - Chip - - - - Chip - - - - Chip - - - - Chip - - - - Chip - - - - Chip - - - - Chip - - - - Chip - - -
- - - - Chip - - - - Chip - - - - Chip - - - - Chip - - - - Chip - - - - Chip - - - - Chip - - - - Chip - - - - Chip - - - - Chip - -
- `, - config - ); - - const container = page.locator('#container'); - - await expect(container).toHaveScreenshot(screenshot(`chip-hue-subtle`)); - }); - - test('should render bold chips', async ({ page }) => { - await page.setContent( - ` - - -
- - - Chip - - - - Chip - - - - Chip - - - - Chip - - - - Chip - - - - Chip - - - - Chip - - - - Chip - - - - Chip - - - - Chip - - -
- - - - Chip - - - - Chip - - - - Chip - - - - Chip - - - - Chip - - - - Chip - - - - Chip - - - - Chip - - - - Chip - - - - Chip - -
- `, - config - ); - - const container = page.locator('#container'); - - await expect(container).toHaveScreenshot(screenshot(`chip-hue-bold`)); + HUES.forEach((hue) => { + test(`should render ${hue} chips`, async ({ page }) => { + const rows = ION_CHIP_FILLS.map((fill) => + COLORS.map( + (color) => ` + + + Chip + + ` + ).join('') + ).join('
'); + + await page.setContent( + ` + + +
${rows}
+ `, + config + ); + + const container = page.locator('#container'); + + await expect(container).toHaveScreenshot(screenshot(`chip-hue-${hue}`)); + }); }); }); }); diff --git a/core/src/components/chip/test/shape/chip.e2e.ts b/core/src/components/chip/test/shape/chip.e2e.ts index f75df323ea7..8dc19a00ee4 100644 --- a/core/src/components/chip/test/shape/chip.e2e.ts +++ b/core/src/components/chip/test/shape/chip.e2e.ts @@ -1,6 +1,8 @@ import { expect } from '@playwright/test'; import { configs, test } from '@utils/test/playwright'; +import { ION_CHIP_SHAPES } from '../../chip.interface'; + /** * This behavior does not vary across directions */ @@ -10,27 +12,11 @@ configs({ modes: ['ionic-md', 'md', 'ios'], directions: ['ltr'] }).forEach(({ ti await page.goto(`/src/components/chip/test/shape`, config); }); - test.describe('soft', () => { - test('should not have visual regressions', async ({ page }) => { - const container = page.locator('#soft'); - - await expect(container).toHaveScreenshot(screenshot(`chip-soft`)); - }); - }); - - test.describe('round', () => { - test('should not have visual regressions', async ({ page }) => { - const container = page.locator('#round'); - - await expect(container).toHaveScreenshot(screenshot(`chip-round`)); - }); - }); - - test.describe('rectangular', () => { - test('should not have visual regressions', async ({ page }) => { - const container = page.locator('#rectangular'); + ION_CHIP_SHAPES.forEach((shape) => { + test(`should not have visual regressions with ${shape} shape`, async ({ page }) => { + const container = page.locator(`#${shape}`); - await expect(container).toHaveScreenshot(screenshot(`chip-rectangular`)); + await expect(container).toHaveScreenshot(screenshot(`chip-${shape}`)); }); }); }); diff --git a/core/src/components/chip/test/size/chip.e2e.ts b/core/src/components/chip/test/size/chip.e2e.ts index aaa26e1effe..2a7ddc16e03 100644 --- a/core/src/components/chip/test/size/chip.e2e.ts +++ b/core/src/components/chip/test/size/chip.e2e.ts @@ -1,35 +1,26 @@ import { expect } from '@playwright/test'; import { configs, test } from '@utils/test/playwright'; +import { ION_CHIP_SIZES } from '../../chip.interface'; + /** * This behavior does not vary across directions. */ configs({ modes: ['ionic-md', 'md', 'ios'], directions: ['ltr'] }).forEach(({ title, screenshot, config }) => { test.describe(title('chip: size'), () => { - test('should render small chip', async ({ page }) => { - await page.setContent( - ` - Small - `, - config - ); - - const chip = page.locator('ion-chip'); - - await expect(chip).toHaveScreenshot(screenshot(`chip-size-small`)); - }); - - test('should render large chip', async ({ page }) => { - await page.setContent( - ` - Large - `, - config - ); + ION_CHIP_SIZES.forEach((size) => { + test(`should render ${size} chip`, async ({ page }) => { + await page.setContent( + ` + ${size.charAt(0).toUpperCase() + size.slice(1)} + `, + config + ); - const chip = page.locator('ion-chip'); + const chip = page.locator('ion-chip'); - await expect(chip).toHaveScreenshot(screenshot(`chip-size-large`)); + await expect(chip).toHaveScreenshot(screenshot(`chip-size-${size}`)); + }); }); }); }); diff --git a/core/src/components/content/content.interfaces.ts b/core/src/components/content/content.interface.ts similarity index 100% rename from core/src/components/content/content.interfaces.ts rename to core/src/components/content/content.interface.ts diff --git a/core/src/components/content/content.tsx b/core/src/components/content/content.tsx index 0c6bce921f8..8522ed8235c 100644 --- a/core/src/components/content/content.tsx +++ b/core/src/components/content/content.tsx @@ -25,7 +25,7 @@ import { config } from '../../global/config'; import { getIonMode } from '../../global/ionic-global'; import type { Color, Mode } from '../../interface'; -import type { ScrollBaseDetail, ScrollDetail } from './content.interfaces'; +import type { ScrollBaseDetail, ScrollDetail } from './content.interface'; /** * How long to wait (in ms) after the last `resize` event before recalculating. diff --git a/core/src/components/item-divider/item-divider.interfaces.ts b/core/src/components/item-divider/item-divider.interface.ts similarity index 98% rename from core/src/components/item-divider/item-divider.interfaces.ts rename to core/src/components/item-divider/item-divider.interface.ts index 15e7c3a82ba..4f158c505de 100644 --- a/core/src/components/item-divider/item-divider.interfaces.ts +++ b/core/src/components/item-divider/item-divider.interface.ts @@ -3,7 +3,9 @@ import type { IonPadding, IonMargin } from '../../themes/themes.interfaces'; export type IonItemDividerRecipe = { background?: string; color?: string; - minHeight?: string; + min?: { + height?: string; + }; padding?: IonPadding; diff --git a/core/src/components/progress-bar/progress-bar.interfaces.ts b/core/src/components/progress-bar/progress-bar.interface.ts similarity index 100% rename from core/src/components/progress-bar/progress-bar.interfaces.ts rename to core/src/components/progress-bar/progress-bar.interface.ts diff --git a/core/src/components/progress-bar/progress-bar.tsx b/core/src/components/progress-bar/progress-bar.tsx index 1e1fb652693..e4e1d74480b 100644 --- a/core/src/components/progress-bar/progress-bar.tsx +++ b/core/src/components/progress-bar/progress-bar.tsx @@ -7,7 +7,7 @@ import { createColorClasses } from '@utils/theme'; import { config } from '../../global/config'; import type { Color } from '../../interface'; -import type { IonProgressBarShape } from './progress-bar.interfaces'; +import type { IonProgressBarShape } from './progress-bar.interface'; /** * @virtualProp {"ios" | "md"} mode - The mode determines the platform behaviors of the component. diff --git a/core/src/components/progress-bar/test/shape/progress-bar.e2e.ts b/core/src/components/progress-bar/test/shape/progress-bar.e2e.ts index 4e736237cd7..03f8510bfc8 100644 --- a/core/src/components/progress-bar/test/shape/progress-bar.e2e.ts +++ b/core/src/components/progress-bar/test/shape/progress-bar.e2e.ts @@ -1,7 +1,7 @@ import { expect } from '@playwright/test'; import { configs, test } from '@utils/test/playwright'; -import { ION_PROGRESS_BAR_SHAPES } from '../../progress-bar.interfaces'; +import { ION_PROGRESS_BAR_SHAPES } from '../../progress-bar.interface'; /** * This behavior does not vary across directions diff --git a/core/src/components/skeleton-text/skeleton-text.interface.ts b/core/src/components/skeleton-text/skeleton-text.interface.ts index 689e785b75e..b832d828c0e 100644 --- a/core/src/components/skeleton-text/skeleton-text.interface.ts +++ b/core/src/components/skeleton-text/skeleton-text.interface.ts @@ -25,7 +25,9 @@ export type IonSkeletonTextRecipe = { }; }; - lineHeight?: string; + line?: { + height?: string; + }; margin?: Omit; }; diff --git a/core/src/components/spinner/spinner-configs.ts b/core/src/components/spinner/spinner-configs.ts index 362503d6275..6c9582fc480 100644 --- a/core/src/components/spinner/spinner-configs.ts +++ b/core/src/components/spinner/spinner-configs.ts @@ -1,4 +1,4 @@ -import type { SpinnerDefinitions } from './spinner.interfaces'; +import type { SpinnerDefinitions } from './spinner.interface'; const spinners = { bubbles: { diff --git a/core/src/components/spinner/spinner.interfaces.ts b/core/src/components/spinner/spinner.interface.ts similarity index 88% rename from core/src/components/spinner/spinner.interfaces.ts rename to core/src/components/spinner/spinner.interface.ts index 0b45e370de5..15bf74c52cc 100644 --- a/core/src/components/spinner/spinner.interfaces.ts +++ b/core/src/components/spinner/spinner.interface.ts @@ -81,4 +81,5 @@ export type IonSpinnerConfig = { size?: SpinnerSize; }; -export type SpinnerSize = 'xsmall' | 'small' | 'medium' | 'large' | 'xlarge'; +export const ION_SPINNER_SIZES = ['xsmall', 'small', 'medium', 'large', 'xlarge'] as const; +export type SpinnerSize = (typeof ION_SPINNER_SIZES)[number]; diff --git a/core/src/components/spinner/spinner.tsx b/core/src/components/spinner/spinner.tsx index 25232666b3f..3da5f1a5284 100644 --- a/core/src/components/spinner/spinner.tsx +++ b/core/src/components/spinner/spinner.tsx @@ -8,7 +8,7 @@ import type { Color } from '../../interface'; import type { SpinnerTypes } from './spinner-configs'; import { SPINNERS } from './spinner-configs'; -import type { SpinnerSize, SpinnerDefinition } from './spinner.interfaces'; +import type { SpinnerSize, SpinnerDefinition } from './spinner.interface'; /** * @virtualProp {"ios" | "md"} mode - The mode determines the platform behaviors of the component. diff --git a/core/src/components/spinner/test/size/spinner.e2e.ts b/core/src/components/spinner/test/size/spinner.e2e.ts index 79452e7b885..1bddaff741c 100644 --- a/core/src/components/spinner/test/size/spinner.e2e.ts +++ b/core/src/components/spinner/test/size/spinner.e2e.ts @@ -1,6 +1,8 @@ import { expect } from '@playwright/test'; import { configs, test } from '@utils/test/playwright'; +import { ION_SPINNER_SIZES } from '../../spinner.interface'; + /** * This behavior does not vary across directions. * @@ -8,69 +10,19 @@ import { configs, test } from '@utils/test/playwright'; */ configs({ directions: ['ltr'], modes: ['md', 'ionic-md'] }).forEach(({ config, screenshot, title }) => { test.describe(title('spinner: size'), () => { - test('should render xsmall spinner', async ({ page }) => { - await page.setContent( - ` - - `, - config - ); - - const spinner = page.locator('ion-spinner'); - - await expect(spinner).toHaveScreenshot(screenshot(`spinner-size-xsmall`)); - }); - - test('should render small spinner', async ({ page }) => { - await page.setContent( - ` - - `, - config - ); - - const spinner = page.locator('ion-spinner'); - - await expect(spinner).toHaveScreenshot(screenshot(`spinner-size-small`)); - }); - - test('should render medium spinner', async ({ page }) => { - await page.setContent( - ` - - `, - config - ); - - const spinner = page.locator('ion-spinner'); - - await expect(spinner).toHaveScreenshot(screenshot(`spinner-size-medium`)); - }); - - test('should render large spinner', async ({ page }) => { - await page.setContent( - ` - - `, - config - ); - - const spinner = page.locator('ion-spinner'); - - await expect(spinner).toHaveScreenshot(screenshot(`spinner-size-large`)); - }); - - test('should render xlarge spinner', async ({ page }) => { - await page.setContent( - ` - - `, - config - ); - - const spinner = page.locator('ion-spinner'); - - await expect(spinner).toHaveScreenshot(screenshot(`spinner-size-xlarge`)); + ION_SPINNER_SIZES.forEach((size) => { + test(`should render ${size} spinner`, async ({ page }) => { + await page.setContent( + ` + + `, + config + ); + + const spinner = page.locator('ion-spinner'); + + await expect(spinner).toHaveScreenshot(screenshot(`spinner-size-${size}`)); + }); }); }); }); diff --git a/core/src/components/tab-button/test/badge/tab-button.e2e.ts b/core/src/components/tab-button/test/badge/tab-button.e2e.ts index f004c3a7ef3..baf6959949b 100644 --- a/core/src/components/tab-button/test/badge/tab-button.e2e.ts +++ b/core/src/components/tab-button/test/badge/tab-button.e2e.ts @@ -1,9 +1,10 @@ import { expect } from '@playwright/test'; import { configs, test } from '@utils/test/playwright'; +import { ION_BADGE_SIZES, ION_BADGE_VERTICAL_POSITIONS } from '../../../badge/badge.interface'; + configs({ modes: ['md', 'ios', 'ionic-md'] }).forEach(({ config, screenshot, title }) => { test.describe(title('tab-button: badge'), () => { - const positions = ['top', 'bottom']; const layouts = ['icon-top', 'icon-bottom', 'icon-start', 'icon-end', 'icon-hide', 'label-hide']; const icons = ['star', 'globe', 'logo-facebook', 'chatbox']; const colors = ['danger', 'primary', 'warning', 'success']; @@ -15,24 +16,23 @@ configs({ modes: ['md', 'ios', 'ionic-md'] }).forEach(({ config, screenshot, tit { label: 'label-only', hasIcon: false, hasLabel: true }, ]; - ['small', 'medium', 'large'].forEach((badgeSize) => { + ION_BADGE_SIZES.forEach((badgeSize) => { test(`should not have visual regressions with ${badgeSize} badges`, async ({ page }) => { - const tabBars = positions - .map((position) => { - const combinedLayouts: Record = { - 'icon-only': layouts.filter((l) => l !== 'icon-hide'), - 'label-only': layouts.filter((l) => l !== 'label-hide'), - }; - - const variantGroups = slotVariants - .map((variant) => { - if (variant.label === 'icon-only' || variant.label === 'label-only') { - const tabs = combinedLayouts[variant.label] - .map((layout, i) => { - const label = variant.hasLabel ? `Tab ${i + 1}` : ''; - const icon = variant.hasIcon ? `` : ''; + const tabBars = ION_BADGE_VERTICAL_POSITIONS.map((position) => { + const combinedLayouts: Record = { + 'icon-only': layouts.filter((l) => l !== 'icon-hide'), + 'label-only': layouts.filter((l) => l !== 'label-hide'), + }; + + const variantGroups = slotVariants + .map((variant) => { + if (variant.label === 'icon-only' || variant.label === 'label-only') { + const tabs = combinedLayouts[variant.label] + .map((layout, i) => { + const label = variant.hasLabel ? `Tab ${i + 1}` : ''; + const icon = variant.hasIcon ? `` : ''; - return ` + return ` ${label} ${icon} @@ -41,52 +41,51 @@ configs({ modes: ['md', 'ios', 'ionic-md'] }).forEach(({ config, screenshot, tit }" size="${badgeSize}" vertical="${position}">${contents[i % contents.length]} `; - }) - .join('\n'); + }) + .join('\n'); - return ` + return `

${variant.label}

${tabs} `; - } + } - const layoutRows = layouts - .map((layout) => { - const tabs = contents - .map((html, i) => { - const label = variant.hasLabel ? `Tab ${i + 1}` : ''; - const icon = variant.hasIcon ? `` : ''; + const layoutRows = layouts + .map((layout) => { + const tabs = contents + .map((html, i) => { + const label = variant.hasLabel ? `Tab ${i + 1}` : ''; + const icon = variant.hasIcon ? `` : ''; - return ` + return ` ${label} ${icon} ${html} `; - }) - .join('\n'); + }) + .join('\n'); - return ` + return `

${layout}

${tabs} `; - }) - .join('\n'); + }) + .join('\n'); - return ` + return `

${variant.label}

${layoutRows} `; - }) - .join('\n'); + }) + .join('\n'); - return ` + return `

badge position: ${position}

${variantGroups} `; - }) - .join('\n'); + }).join('\n'); await page.setContent( ` diff --git a/core/src/components/tabs/tabs.interfaces.ts b/core/src/components/tabs/tabs.interface.ts similarity index 100% rename from core/src/components/tabs/tabs.interfaces.ts rename to core/src/components/tabs/tabs.interface.ts diff --git a/core/src/components/text/text.interfaces.ts b/core/src/components/text/text.interface.ts similarity index 100% rename from core/src/components/text/text.interfaces.ts rename to core/src/components/text/text.interface.ts diff --git a/core/src/components/thumbnail/thumbnail.interfaces.ts b/core/src/components/thumbnail/thumbnail.interface.ts similarity index 100% rename from core/src/components/thumbnail/thumbnail.interfaces.ts rename to core/src/components/thumbnail/thumbnail.interface.ts diff --git a/core/src/components/title/title.interface.ts b/core/src/components/title/title.interface.ts index 16a8692e0d4..ce9e29fab2c 100644 --- a/core/src/components/title/title.interface.ts +++ b/core/src/components/title/title.interface.ts @@ -37,9 +37,7 @@ export type IonTitleRecipe = { }; }; - letter?: { - spacing?: string; - }; + letterSpacing?: string; // TODO(FW-6864): remove when ion-toolbar titlePlacement works in every theme (ios center, md start) position?: { diff --git a/core/src/interface.d.ts b/core/src/interface.d.ts index 40afdea3849..4bb53d494e3 100644 --- a/core/src/interface.d.ts +++ b/core/src/interface.d.ts @@ -7,7 +7,7 @@ export { AccordionGroupCustomEvent } from './components/accordion-group/accordio export { AlertOptions } from './components/alert/alert-interface'; export { ActionSheetOptions } from './components/action-sheet/action-sheet-interface'; export { BreadcrumbCustomEvent } from './components/breadcrumb/breadcrumb-interface'; -export { ScrollBaseCustomEvent, ScrollCallback, ScrollCustomEvent } from './components/content/content.interfaces'; +export { ScrollBaseCustomEvent, ScrollCallback, ScrollCustomEvent } from './components/content/content.interface'; export { CheckboxCustomEvent } from './components/checkbox/checkbox-interface'; export { DatetimeCustomEvent, DatetimeHighlightStyle } from './components/datetime/datetime-interface'; export { InfiniteScrollCustomEvent } from './components/infinite-scroll/infinite-scroll-interface'; @@ -32,7 +32,7 @@ export { export { SearchbarCustomEvent } from './components/searchbar/searchbar-interface'; export { SegmentCustomEvent } from './components/segment/segment-interface'; export { SelectCustomEvent, SelectCompareFn } from './components/select/select-interface'; -export { TabsCustomEvent } from './components/tabs/tabs.interfaces'; +export { TabsCustomEvent } from './components/tabs/tabs.interface'; export { TextareaCustomEvent } from './components/textarea/textarea-interface'; export { ToastOptions } from './components/toast/toast-interface'; export { ToggleCustomEvent } from './components/toggle/toggle-interface'; diff --git a/core/src/themes/ionic/default.tokens.ts b/core/src/themes/ionic/default.tokens.ts index ce168adb25c..81bd9db51fe 100644 --- a/core/src/themes/ionic/default.tokens.ts +++ b/core/src/themes/ionic/default.tokens.ts @@ -314,7 +314,9 @@ export const defaultTheme: DefaultTheme = { start: 'var(--ion-spacing-sm)', }, - lineHeight: '100%', + line: { + height: '100%', + }, letterSpacing: '0%', gap: 'var(--ion-spacing-xxs)', @@ -325,13 +327,17 @@ export const defaultTheme: DefaultTheme = { // Sizes size: { small: { - minHeight: 'var(--ion-scaling-xs)', + min: { + height: 'var(--ion-scaling-xs)', + }, font: { size: 'var(--ion-font-size-xs)', }, }, large: { - minHeight: 'var(--ion-scaling-md)', + min: { + height: 'var(--ion-scaling-md)', + }, font: { size: 'var(--ion-font-size-sm)', }, @@ -637,7 +643,9 @@ export const defaultTheme: DefaultTheme = { IonItemDivider: { background: baseColors.backgroundColor, color: `var(--ion-text-color-step-600, ${mix(baseColors.white, baseColors.black, '40%')})`, - minHeight: 'var(--ion-scaling-750)', + min: { + height: 'var(--ion-scaling-750)', + }, padding: { top: 'var(--ion-spacing-0)', @@ -926,7 +934,9 @@ export const defaultTheme: DefaultTheme = { }, }, - lineHeight: 'var(--ion-scaling-250)', + line: { + height: 'var(--ion-scaling-250)', + }, margin: { bottom: 'var(--ion-spacing-xxs)', @@ -1060,9 +1070,7 @@ export const defaultTheme: DefaultTheme = { }, }, - letter: { - spacing: '0', - }, + letterSpacing: '0', size: { small: { diff --git a/core/src/themes/ios/default.tokens.ts b/core/src/themes/ios/default.tokens.ts index dc0cf5b5712..2f50868d822 100644 --- a/core/src/themes/ios/default.tokens.ts +++ b/core/src/themes/ios/default.tokens.ts @@ -328,7 +328,9 @@ export const defaultTheme: DefaultTheme = { // Sizes size: { small: { - minHeight: 'var(--ion-scaling-xs)', + min: { + height: 'var(--ion-scaling-xs)', + }, font: { size: clamp( @@ -340,7 +342,9 @@ export const defaultTheme: DefaultTheme = { }, large: { - minHeight: 'var(--ion-scaling-md)', + min: { + height: 'var(--ion-scaling-md)', + }, font: { size: clamp('13px', `${(components.chip.font.size / global.root).toFixed(2)}rem`, '22px'), @@ -781,7 +785,9 @@ export const defaultTheme: DefaultTheme = { IonItemDivider: { background: `var(--ion-background-color-step-100, ${mix(baseColors.black, baseColors.white, '90%')})`, color: `var(--ion-text-color-step-150, ${mix(baseColors.white, baseColors.black, '85%')})`, - minHeight: 'var(--ion-scaling-sm)', + min: { + height: 'var(--ion-scaling-sm)', + }, padding: { top: 'var(--ion-spacing-0)', @@ -960,7 +966,9 @@ export const defaultTheme: DefaultTheme = { }, }, - lineHeight: 'var(--ion-scaling-250)', + line: { + height: 'var(--ion-scaling-250)', + }, margin: { bottom: 'var(--ion-spacing-xxs)', diff --git a/core/src/themes/md/default.tokens.ts b/core/src/themes/md/default.tokens.ts index 06da2f5e884..a05e7543be2 100644 --- a/core/src/themes/md/default.tokens.ts +++ b/core/src/themes/md/default.tokens.ts @@ -328,14 +328,18 @@ export const defaultTheme: DefaultTheme = { // Sizes size: { small: { - minHeight: 'var(--ion-scaling-xs)', + min: { + height: 'var(--ion-scaling-xs)', + }, font: { size: `${((components.chip.font.size - 2) / global.root).toFixed(2)}rem`, }, }, large: { - minHeight: 'var(--ion-scaling-md)', + min: { + height: 'var(--ion-scaling-md)', + }, font: { size: `${(components.chip.font.size / global.root).toFixed(2)}rem`, @@ -775,7 +779,9 @@ export const defaultTheme: DefaultTheme = { IonItemDivider: { background: baseColors.backgroundColor, color: `var(--ion-text-color-step-600, ${mix(baseColors.white, baseColors.black, '40%')})`, - minHeight: 'var(--ion-scaling-750)', + min: { + height: 'var(--ion-scaling-750)', + }, padding: { top: 'var(--ion-spacing-0)', @@ -1076,7 +1082,9 @@ export const defaultTheme: DefaultTheme = { }, }, - lineHeight: 'var(--ion-scaling-250)', + line: { + height: 'var(--ion-scaling-250)', + }, margin: { bottom: 'var(--ion-spacing-xxs)', @@ -1206,9 +1214,7 @@ export const defaultTheme: DefaultTheme = { }, }, - letter: { - spacing: '0.0075em', - }, + letterSpacing: '0.0075em', size: { small: { diff --git a/core/src/themes/themes.interfaces.ts b/core/src/themes/themes.interfaces.ts index b1b77c7ebad..e759c31bce2 100644 --- a/core/src/themes/themes.interfaces.ts +++ b/core/src/themes/themes.interfaces.ts @@ -1,17 +1,17 @@ -import type { IonBadgeConfig, IonBadgeRecipe } from '../components/badge/badge.interfaces'; -import type { IonChipConfig, IonChipRecipe } from '../components/chip/chip.interfaces'; +import type { IonBadgeConfig, IonBadgeRecipe } from '../components/badge/badge.interface'; +import type { IonChipConfig, IonChipRecipe } from '../components/chip/chip.interface'; import type { IonColRecipe } from '../components/col/col.interface'; -import type { IonContentRecipe } from '../components/content/content.interfaces'; +import type { IonContentRecipe } from '../components/content/content.interface'; import type { IonGalleryRecipe } from '../components/gallery/gallery.interface'; import type { IonGridRecipe } from '../components/grid/grid.interface'; -import type { IonItemDividerRecipe } from '../components/item-divider/item-divider.interfaces'; -import type { IonProgressBarConfig, IonProgressBarRecipe } from '../components/progress-bar/progress-bar.interfaces'; +import type { IonItemDividerRecipe } from '../components/item-divider/item-divider.interface'; +import type { IonProgressBarConfig, IonProgressBarRecipe } from '../components/progress-bar/progress-bar.interface'; import type { IonRippleEffectRecipe } from '../components/ripple-effect/ripple-effect.interface'; import type { IonRowRecipe } from '../components/row/row.interface'; import type { IonSkeletonTextRecipe } from '../components/skeleton-text/skeleton-text.interface'; -import type { IonSpinnerConfig, IonSpinnerRecipe } from '../components/spinner/spinner.interfaces'; -import type { IonTextConfig, IonTextRecipe } from '../components/text/text.interfaces'; -import type { IonThumbnailRecipe } from '../components/thumbnail/thumbnail.interfaces'; +import type { IonSpinnerConfig, IonSpinnerRecipe } from '../components/spinner/spinner.interface'; +import type { IonTextConfig, IonTextRecipe } from '../components/text/text.interface'; +import type { IonThumbnailRecipe } from '../components/thumbnail/thumbnail.interface'; import type { IonTitleConfig, IonTitleRecipe } from '../components/title/title.interface'; import type { IonicConfig as IonicGlobalConfig } from '../utils/config'; diff --git a/docs/themes/README.md b/docs/themes/README.md index 88af9ab9770..7eb1d21005c 100644 --- a/docs/themes/README.md +++ b/docs/themes/README.md @@ -36,6 +36,20 @@ core/ └── mixins.scss # Sass mixins ``` +## Component Files + +A component that uses the theme system keeps its recipe and config types next to its source: + +```bash +core/src/components/my-component/ +├── my-component.tsx # Reads config defaults and renders CSS custom properties +├── my-component.scss # Single base stylesheet for every theme +├── my-component.interface.ts # Recipe and config types, plus const arrays for prop values +└── my-component.constant.ts # Optional shared values, such as defaults +``` + +- Back each prop union with a `const` array, such as `ION_CHIP_SIZES`, so tests can loop over every value. + ## Available Themes ### iOS Theme (`@ionic/theme/ios`)