@ethlete/components 1.0.0-next.69 → 1.0.0-next.70
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +29 -0
- package/design/button.ts +57 -0
- package/design/form-field.ts +210 -0
- package/design/select.ts +110 -0
- package/fesm2022/ethlete-components-testing.mjs +3 -1
- package/fesm2022/ethlete-components-testing.mjs.map +1 -1
- package/fesm2022/ethlete-components.mjs +823 -105
- package/fesm2022/ethlete-components.mjs.map +1 -1
- package/package.json +20 -14
- package/src/lib/accordion/accordion-group.component.css +12 -0
- package/src/lib/accordion/accordion.component.css +185 -0
- package/src/lib/avatar/avatar-group.component.css +31 -0
- package/src/lib/avatar/avatar.component.css +75 -0
- package/src/lib/badge/badge.component.css +111 -0
- package/src/lib/banner/banner.component.css +94 -0
- package/src/lib/bracket/bracket-default-continue.component.css +29 -0
- package/src/lib/bracket/bracket-default-final-match.component.css +76 -0
- package/src/lib/bracket/bracket-default-match.component.css +21 -0
- package/src/lib/bracket/bracket-default-round-header.component.css +36 -0
- package/src/lib/bracket/bracket-participants.component.css +13 -0
- package/src/lib/bracket/bracket-pick-card.component.css +232 -0
- package/src/lib/bracket/bracket-rounds-list.component.css +75 -0
- package/src/lib/bracket/bracket-skeleton.component.css +55 -0
- package/src/lib/bracket/bracket.component.css +102 -0
- package/src/lib/bracket/layouts/swiss/bracket-swiss-styles.component.css +10 -0
- package/src/lib/breadcrumb/breadcrumb-overflow.component.css +38 -0
- package/src/lib/breadcrumb/breadcrumb.component.css +120 -0
- package/src/lib/button/button-properties-styles.component.css +143 -0
- package/src/lib/button/button.component.css +196 -0
- package/src/lib/button/fab.component.css +252 -0
- package/src/lib/button/icon-button.component.css +143 -0
- package/src/lib/button/split-button.component.css +77 -0
- package/src/lib/button/text-button.component.css +140 -0
- package/src/lib/button/window-control-button.component.css +158 -0
- package/src/lib/calendar/calendar-coarse-grid-styles.component.css +29 -0
- package/src/lib/calendar/calendar-comparison-band-styles.component.css +36 -0
- package/src/lib/calendar/calendar-week-numbers-styles.component.css +32 -0
- package/src/lib/calendar/calendar.component.css +366 -0
- package/src/lib/card/card.component.css +39 -0
- package/src/lib/carousel/carousel-autoplay-styles.component.css +166 -0
- package/src/lib/carousel/carousel-transition-styles.component.css +179 -0
- package/src/lib/carousel/carousel.component.css +142 -0
- package/src/lib/chart/bar-chart.component.css +176 -0
- package/src/lib/chart/chart-axis.component.css +40 -0
- package/src/lib/chart/chart-grid.component.css +6 -0
- package/src/lib/chart/chart-legend.component.css +46 -0
- package/src/lib/chart/chart-tooltip.component.css +34 -0
- package/src/lib/chart/line-chart-tooltip.component.css +51 -0
- package/src/lib/chart/line-chart.component.css +234 -0
- package/src/lib/chart/pie-chart.component.css +176 -0
- package/src/lib/chart/sankey-chart.component.css +221 -0
- package/src/lib/chip/chip.component.css +155 -0
- package/src/lib/command-palette/command-palette-item.component.css +94 -0
- package/src/lib/command-palette/command-palette.component.css +100 -0
- package/src/lib/description-list/description-list.component.css +77 -0
- package/src/lib/divider/divider.component.css +36 -0
- package/src/lib/empty-state/empty-state.component.css +73 -0
- package/src/lib/floating-action/floating-action-styles.component.css +78 -0
- package/src/lib/focus-ring/focus-ring-styles.component.css +13 -0
- package/src/lib/forms/cascader/cascader-breadcrumb-styles.component.css +98 -0
- package/src/lib/forms/cascader/cascader-panel.component.css +386 -0
- package/src/lib/forms/cascader/cascader-sheet-styles.component.css +241 -0
- package/src/lib/forms/cascader/cascader.component.css +52 -0
- package/src/lib/forms/checkbox/checkbox.component.css +247 -0
- package/src/lib/forms/choice-field/choice-field-card-styles.component.css +59 -0
- package/src/lib/forms/choice-field/choice-field.component.css +186 -0
- package/src/lib/forms/color-input/color-input.component.css +108 -0
- package/src/lib/forms/color-input/color-picker-panel.component.css +349 -0
- package/src/lib/forms/date-time/date-input/date-input.component.css +35 -0
- package/src/lib/forms/date-time/date-picker-panel.component.css +115 -0
- package/src/lib/forms/date-time/date-range-input/date-range-input.component.css +16 -0
- package/src/lib/forms/date-time/date-range-presets.component.css +60 -0
- package/src/lib/forms/date-time/date-time-input/date-time-input.component.css +151 -0
- package/src/lib/forms/date-time/date-time-range-input/date-time-range-input.component.css +121 -0
- package/src/lib/forms/date-time/duration-input/duration-input.component.css +88 -0
- package/src/lib/forms/date-time/range-input-shell-styles.component.css +55 -0
- package/src/lib/forms/date-time/time-input/time-input.component.css +35 -0
- package/src/lib/forms/date-time/time-range-input/time-range-input.component.css +16 -0
- package/src/lib/forms/dropzone/dropzone-item.component.css +51 -0
- package/src/lib/forms/dropzone/dropzone-preview-styles.component.css +67 -0
- package/src/lib/forms/dropzone/dropzone-readonly-styles.component.css +34 -0
- package/src/lib/forms/dropzone/dropzone.component.css +267 -0
- package/src/lib/forms/form-field/counter.component.css +25 -0
- package/src/lib/forms/form-field/form-field-control-suffix-styles.component.css +116 -0
- package/src/lib/forms/form-field/form-field-rich-text-styles.component.css +31 -0
- package/src/lib/forms/form-field/form-field-suffix-overlap-styles.component.css +66 -0
- package/src/lib/forms/form-field/form-field-text-shell-styles.component.css +312 -0
- package/src/lib/forms/form-field/form-field-textarea-styles.component.css +32 -0
- package/src/lib/forms/form-field/form-field.component.css +328 -0
- package/src/lib/forms/form-field/form-support-styles.component.css +102 -0
- package/src/lib/forms/form-field/headless/label.directive.css +25 -0
- package/src/lib/forms/form-field/hint.component.css +20 -0
- package/src/lib/forms/input/input.component.css +49 -0
- package/src/lib/forms/input/number-input.component.css +128 -0
- package/src/lib/forms/input/password-input.component.css +103 -0
- package/src/lib/forms/multi-language-rich-text-editor/multi-language-rich-text-editor.component.css +8 -0
- package/src/lib/forms/multi-language-rich-text-editor/tools/multi-language-rich-text-editor-language-tool.component.css +104 -0
- package/src/lib/forms/otp-input/otp-input.component.css +194 -0
- package/src/lib/forms/phone-input/phone-input.component.css +126 -0
- package/src/lib/forms/rating/rating.component.css +197 -0
- package/src/lib/forms/rich-text-editor/rich-text-content-styles.component.css +234 -0
- package/src/lib/forms/rich-text-editor/rich-text-editor-floating-toolbar.component.css +90 -0
- package/src/lib/forms/rich-text-editor/rich-text-editor-image-editor.component.css +172 -0
- package/src/lib/forms/rich-text-editor/rich-text-editor-link-editor.component.css +149 -0
- package/src/lib/forms/rich-text-editor/rich-text-editor-token-palette.component.css +18 -0
- package/src/lib/forms/rich-text-editor/rich-text-editor-token-popup.component.css +199 -0
- package/src/lib/forms/rich-text-editor/rich-text-editor.component.css +175 -0
- package/src/lib/forms/rich-text-editor/rich-text-viewer.component.css +6 -0
- package/src/lib/forms/rich-text-editor/tools/rich-text-editor-align-tool.component.css +5 -0
- package/src/lib/forms/rich-text-editor/tools/rich-text-editor-heading-tool.component.css +5 -0
- package/src/lib/forms/rich-text-editor/tools/rich-text-editor-image-styles.component.css +109 -0
- package/src/lib/forms/rich-text-editor/tools/rich-text-editor-table-styles.component.css +21 -0
- package/src/lib/forms/rich-text-editor/tools/rich-text-editor-table-tool.component.css +44 -0
- package/src/lib/forms/select/select-all-option.component.css +14 -0
- package/src/lib/forms/select/select-extras-styles.component.css +143 -0
- package/src/lib/forms/select/select-option-group.component.css +35 -0
- package/src/lib/forms/select/select-option-styles.component.css +137 -0
- package/src/lib/forms/select/select-panel.component.css +112 -0
- package/src/lib/forms/select/select.component.css +180 -0
- package/src/lib/forms/selection-card-styles.component.css +136 -0
- package/src/lib/forms/selection-list/checkbox-group/checkbox-group-select-all.component.css +89 -0
- package/src/lib/forms/selection-list/checkbox-group/checkbox-group.component.css +142 -0
- package/src/lib/forms/selection-list/checkbox-group/checkbox-option.component.css +153 -0
- package/src/lib/forms/selection-list/radio-group/radio-group.component.css +134 -0
- package/src/lib/forms/selection-list/radio-group/radio.component.css +137 -0
- package/src/lib/forms/selection-list/segmented-button-group/segmented-button-group.component.css +149 -0
- package/src/lib/forms/selection-list/segmented-button-group/segmented-button.component.css +180 -0
- package/src/lib/forms/slider/range-slider.component.css +348 -0
- package/src/lib/forms/slider/slider.component.css +342 -0
- package/src/lib/forms/switch/switch.component.css +187 -0
- package/src/lib/forms/tag-input/tag-input.component.css +54 -0
- package/src/lib/forms/textarea/textarea-autosize-styles.component.css +17 -0
- package/src/lib/forms/textarea/textarea.component.css +40 -0
- package/src/lib/grid/grid-debug.component.css +130 -0
- package/src/lib/grid/grid-item-default-actions.component.css +11 -0
- package/src/lib/grid/grid-item.component.css +172 -0
- package/src/lib/grid/grid.component.css +56 -0
- package/src/lib/icon/headless/icon-styles.component.css +7 -0
- package/src/lib/kbd/kbd.component.css +62 -0
- package/src/lib/loader/brand-loader/brand-loader.component.css +130 -0
- package/src/lib/loader/progress-bar/progress-bar.component.css +176 -0
- package/src/lib/loader/spinner/spinner.component.css +216 -0
- package/src/lib/masonry/masonry-styles.component.css +84 -0
- package/src/lib/match/match-card.component.css +441 -0
- package/src/lib/match/match-participant.component.css +137 -0
- package/src/lib/match/match-score.component.css +74 -0
- package/src/lib/menu/menu-group-label.component.css +12 -0
- package/src/lib/menu/menu-item-shortcut.component.css +10 -0
- package/src/lib/menu/menu-item-submenu-icon.component.css +18 -0
- package/src/lib/menu/menu-item.component.css +32 -0
- package/src/lib/menu/menu-search-styles.component.css +56 -0
- package/src/lib/menu/menu-selection-item.component.css +88 -0
- package/src/lib/menu/menu-separator.component.css +9 -0
- package/src/lib/menu/menu.component.css +296 -0
- package/src/lib/notification/notification-stack.component.css +114 -0
- package/src/lib/notification/notification.component.css +271 -0
- package/src/lib/overlay/alert-dialog/alert-dialog.component.css +45 -0
- package/src/lib/overlay/floating-panel-styles.component.css +71 -0
- package/src/lib/overlay/floating-tip-styles.component.css +78 -0
- package/src/lib/overlay/overlay-container.component.css +167 -0
- package/src/lib/overlay/overlay-main-styles.component.css +105 -0
- package/src/lib/overlay/overlay-template-host.component.css +7 -0
- package/src/lib/overlay/routing/overlay-route-header-template-outlet.component.css +36 -0
- package/src/lib/overlay/routing/overlay-router-outlet.component.css +207 -0
- package/src/lib/overlay/routing/overlay-shared-route-template-outlet.component.css +5 -0
- package/src/lib/overlay/sidebar/overlay-sidebar.component.css +16 -0
- package/src/lib/overlay/strategies/anchored-dialog-styles.component.css +51 -0
- package/src/lib/overlay/strategies/dialog-styles.component.css +50 -0
- package/src/lib/overlay/strategies/full-screen-dialog-styles.component.css +83 -0
- package/src/lib/overlay/strategies/overlay-arrow-styles.component.css +112 -0
- package/src/lib/overlay/strategies/overlay-origin-clone.component.css +63 -0
- package/src/lib/overlay/strategies/sheet-styles.component.css +143 -0
- package/src/lib/pagination/page-size-select.component.css +102 -0
- package/src/lib/pagination/pagination.component.css +192 -0
- package/src/lib/picture/picture.component.css +66 -0
- package/src/lib/progress-steps/progress-step.component.css +187 -0
- package/src/lib/progress-steps/progress-steps.component.css +26 -0
- package/src/lib/query-error/query-error.component.css +21 -0
- package/src/lib/scheduler/scheduler-agenda-view.component.css +97 -0
- package/src/lib/scheduler/scheduler-appointment-drag-styles.component.css +56 -0
- package/src/lib/scheduler/scheduler-appointment-styles.component.css +111 -0
- package/src/lib/scheduler/scheduler-edit-color.component.css +16 -0
- package/src/lib/scheduler/scheduler-edit-surface-actions.component.css +14 -0
- package/src/lib/scheduler/scheduler-edit-surface-breadcrumb.component.css +26 -0
- package/src/lib/scheduler/scheduler-edit-surface-children.component.css +57 -0
- package/src/lib/scheduler/scheduler-edit-surface-fields.component.css +11 -0
- package/src/lib/scheduler/scheduler-edit-surface-footer.component.css +7 -0
- package/src/lib/scheduler/scheduler-edit-surface-header.component.css +15 -0
- package/src/lib/scheduler/scheduler-edit-time-range.component.css +11 -0
- package/src/lib/scheduler/scheduler-month-view.component.css +143 -0
- package/src/lib/scheduler/scheduler-time-grid-view.component.css +260 -0
- package/src/lib/scheduler/scheduler.component.css +87 -0
- package/src/lib/scrollable/headless/scrollable-buttons.component.css +122 -0
- package/src/lib/scrollable/headless/scrollable-footer.css +25 -0
- package/src/lib/scrollable/headless/scrollable-masks.component.css +112 -0
- package/src/lib/scrollable/headless/scrollable-navigation.component.css +94 -0
- package/src/lib/scrollable/scrollable.component.css +192 -0
- package/src/lib/scrollbar/scrollbar.component.css +110 -0
- package/src/lib/skeleton/skeleton.component.css +108 -0
- package/src/lib/standings/standings-pick.component.css +172 -0
- package/src/lib/standings/standings.component.css +201 -0
- package/src/lib/stat-tile/stat-tile-sparkline.component.css +49 -0
- package/src/lib/stat-tile/stat-tile.component.css +111 -0
- package/src/lib/stream/pip/pip-chrome.component.css +96 -0
- package/src/lib/stream/pip/pip-player.component.css +40 -0
- package/src/lib/stream/pip/pip-window.component.css +222 -0
- package/src/lib/stream/stream-manager-styles.component.css +10 -0
- package/src/lib/stream/stream-overlay-card-styles.component.css +43 -0
- package/src/lib/stream/stream-player-slot-styles.component.css +14 -0
- package/src/lib/table/table-column-chooser.component.css +17 -0
- package/src/lib/table/table-column-menu-trigger.component.css +31 -0
- package/src/lib/table/table-detail-styles.component.css +103 -0
- package/src/lib/table/table-drag-scroll-styles.component.css +18 -0
- package/src/lib/table/table-expander-cell.component.css +58 -0
- package/src/lib/table/table-filter-trigger.component.css +44 -0
- package/src/lib/table/table-group-header-row.component.css +38 -0
- package/src/lib/table/table-inline-edit-styles.component.css +20 -0
- package/src/lib/table/table-page-sticky-header-styles.component.css +77 -0
- package/src/lib/table/table-reorder-overlay.component.css +42 -0
- package/src/lib/table/table-resize-grip.component.css +65 -0
- package/src/lib/table/table-row-box-styles.component.css +260 -0
- package/src/lib/table/table-select-cell.component.css +89 -0
- package/src/lib/table/table-skeleton-rows.component.css +19 -0
- package/src/lib/table/table-sort-priority-styles.component.css +15 -0
- package/src/lib/table/table-sticky-columns-styles.component.css +80 -0
- package/src/lib/table/table-virtual-scroll-styles.component.css +9 -0
- package/src/lib/table/table.component.css +829 -0
- package/src/lib/tabs/nav-tabs/nav-tab-link-styles.component.css +33 -0
- package/src/lib/tabs/tab-bar-styles.component.css +173 -0
- package/src/lib/tabs/tab-scale-styles.component.css +61 -0
- package/src/lib/time-picker/time-picker.component.css +220 -0
- package/src/lib/timeline/timeline-item.component.css +116 -0
- package/src/lib/timeline/timeline.component.css +7 -0
- package/src/lib/toggletip/toggletip.component.css +83 -0
- package/src/lib/toolbar/toolbar.component.css +40 -0
- package/src/lib/tooltip/tooltip.component.css +64 -0
- package/src/lib/tree/tree.component.css +221 -0
- package/types/ethlete-components-testing.d.ts +1 -1
- package/types/ethlete-components.d.ts +172 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.0.0-next.70
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- New `@ethlete/components/design/button`: a framework-free `button()` mock that draws the real `et-button` DOM and CSS for design calls in Ethlete Studio.
|
|
8
|
+
- New `@ethlete/components/design/form-field` and `/design/select`: framework-free `formField()`, `input()` and `select()` mocks that draw the real DOM and CSS, with an open select panel drawn below the field.
|
|
9
|
+
- `provideDateLocale` also accepts a function `(locale) => dateFnsLocale`, which follows `injectLocale().currentLocale` so date controls, calendars, pickers and range validators reformat on a live language switch. `injectDateLocaleSignal()` exposes the resolved locale as a signal; a control's `locale` input still wins.
|
|
10
|
+
- Every label set that shipped English only (calendar, chip, avatar, stat-tile, alert-dialog, table, pagination, forms, date-time, rich-text-editor and the rest) now also ships German defaults that follow `injectLocale().currentLocale`.
|
|
11
|
+
- `OverlayRef.layout` is a signal with the layout the active strategy shows the overlay as (`'bottom-sheet'`, `'dialog'`, …). It follows breakpoint switches, so a consumer no longer keys on the `.et-overlay--bottom-sheet` class.
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- An anchored overlay strategy's `width`, such as `anchoredDialogOverlayStrategy({ width: '42rem' })`, now sizes the pane instead of being cleared by anchored positioning.
|
|
16
|
+
- `et-badge` no longer stretches to the full width of a flex or grid parent and sizes to its content.
|
|
17
|
+
- A bottom sheet whose content reaches its max height keeps the drag handle's row, so the content scrolls instead of sliding under the handle.
|
|
18
|
+
- `--et-button-gap` now spaces every child of the button content, so a plain `<svg>` next to text gets the gap too.
|
|
19
|
+
- A looping `et-carousel` dragged with the mouse past its first or last slide lands on the wrapped slide instead of sweeping back through every slide.
|
|
20
|
+
- An overlay strategy that sets `arrow` without `enableAnchoredOverlayPositionExtras()` now throws `ET1212` in dev mode instead of silently rendering no arrow.
|
|
21
|
+
- An open overlay, for example a select panel, now follows its opener to another surface type, as when an app switches between dark and light mode. Before, it kept the surface it had when it opened.
|
|
22
|
+
- Overlays are about 0.9 kB gz smaller again: the dark or light switch in an open overlay no longer pulls in the generic element mutation helper.
|
|
23
|
+
- `setupComponentsTestEnvironment()` now stubs `Element.prototype.scroll`, which `et-carousel` calls.
|
|
24
|
+
- Setting `--et-description-list-term-font-size`, `--et-description-list-detail-font-size` and the row and stacked gap tokens on the `<dl>` now reaches its terms and details.
|
|
25
|
+
- `et-form-field size="sm"` is now a 32px control instead of 42px, and a static label with the `hidden` attribute no longer reserves its line of height.
|
|
26
|
+
- An overlay backdrop gets its visible class at once on a server render, so it no longer ships at opacity 0 in SSR markup.
|
|
27
|
+
- `etOverlay` writes its `placement` to a `data-overlay-placement` attribute on its host, so a server render shows where an open panel belongs.
|
|
28
|
+
- `etOverlayTrigger` sets `aria-controls` to the stable `panelId` of its `etOverlay`, which the mounted overlay element carries as its `id`.
|
|
29
|
+
- Show the pointer cursor over the whole select trigger, not only over the value text.
|
|
30
|
+
- The theming docs and skill warn that `hover:bg-et-surface-interaction-hover` is a full-strength tint and name `etSurfaceInteractive` for hover rows.
|
|
31
|
+
|
|
3
32
|
## 1.0.0-next.69
|
|
4
33
|
|
|
5
34
|
### Major Changes
|
package/design/button.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { interactiveStyles } from '@ethlete/core/design/interactive-styles';
|
|
2
|
+
import focusRing from '../src/lib/focus-ring/focus-ring-styles.component.css?inline';
|
|
3
|
+
import buttonProperties from '../src/lib/button/button-properties-styles.component.css?inline';
|
|
4
|
+
import buttonBase from '../src/lib/button/button.component.css?inline';
|
|
5
|
+
|
|
6
|
+
export type ButtonMockOptions = {
|
|
7
|
+
/** The label, as HTML. */
|
|
8
|
+
label: string;
|
|
9
|
+
/** The icon, as HTML. Leave it out for a button without one. */
|
|
10
|
+
icon?: string;
|
|
11
|
+
iconAlignment?: 'start' | 'end';
|
|
12
|
+
size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
|
|
13
|
+
variant?: 'filled' | 'outline' | 'tonal' | 'transparent';
|
|
14
|
+
/** A color theme name the app registers. Left out, the button takes the color theme around it. */
|
|
15
|
+
color?: string;
|
|
16
|
+
/** Makes the button a toggle: `false` draws it unpressed, left out it is no toggle. */
|
|
17
|
+
pressed?: boolean;
|
|
18
|
+
disabled?: boolean;
|
|
19
|
+
tag?: 'button' | 'a';
|
|
20
|
+
class?: string;
|
|
21
|
+
style?: string;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const PRESSED_VARIANT = { filled: 'outline', outline: 'filled', tonal: 'filled', transparent: 'tonal' } as const;
|
|
25
|
+
|
|
26
|
+
/** The stylesheets `button()` draws with: the real `et-button` CSS. */
|
|
27
|
+
export const buttonStyles = `${interactiveStyles}\n${focusRing}\n${buttonProperties}\n${buttonBase}`;
|
|
28
|
+
|
|
29
|
+
/** Draws `<button et-button>` as HTML, with the DOM and classes the component renders. */
|
|
30
|
+
export const button = (options: ButtonMockOptions) => {
|
|
31
|
+
const { label, icon, iconAlignment = 'start', size = 'md', variant = 'filled', tag = 'button' } = options;
|
|
32
|
+
const iconBox = `<div class="et-button-icon">${icon ?? ''}</div>`;
|
|
33
|
+
|
|
34
|
+
const classes = [
|
|
35
|
+
'et-surface-interactive',
|
|
36
|
+
'et-color-interactive',
|
|
37
|
+
'et-button',
|
|
38
|
+
'et-focus-ring',
|
|
39
|
+
options.color ? `et-color--${options.color}` : 'et-color--inherited',
|
|
40
|
+
options.class,
|
|
41
|
+
].filter(Boolean);
|
|
42
|
+
|
|
43
|
+
const attributes = [
|
|
44
|
+
`class="${classes.join(' ')}"`,
|
|
45
|
+
tag === 'button' && 'type="button"',
|
|
46
|
+
options.pressed && 'data-pressed="true"',
|
|
47
|
+
options.pressed !== undefined && `aria-pressed="${options.pressed}"`,
|
|
48
|
+
options.disabled && (tag === 'button' ? 'disabled aria-disabled="true"' : 'aria-disabled="true"'),
|
|
49
|
+
`data-icon-alignment="${iconAlignment}"`,
|
|
50
|
+
`data-variant="${variant}"`,
|
|
51
|
+
`data-size="${size}"`,
|
|
52
|
+
options.pressed && `data-pressed-variant="${PRESSED_VARIANT[variant]}"`,
|
|
53
|
+
options.style && `style="${options.style}"`,
|
|
54
|
+
].filter(Boolean);
|
|
55
|
+
|
|
56
|
+
return `<${tag} ${attributes.join(' ')}>${iconAlignment === 'start' ? iconBox : ''}<div class="et-button-contents">${label}</div>${iconAlignment === 'end' ? iconBox : ''}</${tag}>`;
|
|
57
|
+
};
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { interactiveStyles } from '@ethlete/core/design/interactive-styles';
|
|
2
|
+
import formFieldBase from '../src/lib/forms/form-field/form-field.component.css?inline';
|
|
3
|
+
import textShell from '../src/lib/forms/form-field/form-field-text-shell-styles.component.css?inline';
|
|
4
|
+
import suffixOverlap from '../src/lib/forms/form-field/form-field-suffix-overlap-styles.component.css?inline';
|
|
5
|
+
import label from '../src/lib/forms/form-field/headless/label.directive.css?inline';
|
|
6
|
+
import hint from '../src/lib/forms/form-field/hint.component.css?inline';
|
|
7
|
+
import inputBase from '../src/lib/forms/input/input.component.css?inline';
|
|
8
|
+
import iconStyles from '../src/lib/icon/headless/icon-styles.component.css?inline';
|
|
9
|
+
|
|
10
|
+
/** The field state a control draws itself from. */
|
|
11
|
+
export type FormFieldMockState = {
|
|
12
|
+
id: string;
|
|
13
|
+
disabled: boolean;
|
|
14
|
+
readonly: boolean;
|
|
15
|
+
required: boolean;
|
|
16
|
+
invalid: boolean;
|
|
17
|
+
focused: boolean;
|
|
18
|
+
open: boolean;
|
|
19
|
+
labelId: string | null;
|
|
20
|
+
describedBy: string | null;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/** A control drawn inside `formField()`, for example `input()` or `select()`. */
|
|
24
|
+
export type FormFieldControlMock = {
|
|
25
|
+
controlType: string;
|
|
26
|
+
hasValue: boolean;
|
|
27
|
+
/** Can be opened, like a select: `open` then draws the field expanded. */
|
|
28
|
+
expandable?: boolean;
|
|
29
|
+
html: (state: FormFieldMockState) => string;
|
|
30
|
+
/** The control's own affordances, drawn first in the suffix area. */
|
|
31
|
+
suffix?: (state: FormFieldMockState) => string;
|
|
32
|
+
/** Drawn below the field, where the overlay would open. */
|
|
33
|
+
panel?: (state: FormFieldMockState) => string;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export type FormFieldMockOptions = {
|
|
37
|
+
/** The control, from `input()` or `select()`, or plain HTML for a text input. */
|
|
38
|
+
control: FormFieldControlMock | string;
|
|
39
|
+
/** The label, as HTML. */
|
|
40
|
+
label?: string;
|
|
41
|
+
/** Unique per frame: the ids that link the label, the control and the messages derive from it. */
|
|
42
|
+
id?: string;
|
|
43
|
+
/** The hint, as HTML. */
|
|
44
|
+
hint?: string;
|
|
45
|
+
/** The error message, as HTML. Draws the field in its error state. */
|
|
46
|
+
error?: string;
|
|
47
|
+
/** The color theme name the app registers for errors. */
|
|
48
|
+
errorColor?: string;
|
|
49
|
+
/** Prefix content, as HTML, for example an icon. */
|
|
50
|
+
prefix?: string;
|
|
51
|
+
/** Suffix content, as HTML. */
|
|
52
|
+
suffix?: string;
|
|
53
|
+
appearance?: 'box' | 'underline';
|
|
54
|
+
fill?: 'transparent' | 'filled';
|
|
55
|
+
labelMode?: 'static' | 'inline' | 'floating-inside' | 'floating-outside';
|
|
56
|
+
size?: 'sm' | 'md' | 'lg';
|
|
57
|
+
/** A color theme name the app registers. */
|
|
58
|
+
color?: string;
|
|
59
|
+
/** A surface theme name the app registers. */
|
|
60
|
+
surface?: string;
|
|
61
|
+
required?: boolean;
|
|
62
|
+
disabled?: boolean;
|
|
63
|
+
readonly?: boolean;
|
|
64
|
+
focused?: boolean;
|
|
65
|
+
/** Opens an expandable control, like a select. */
|
|
66
|
+
open?: boolean;
|
|
67
|
+
class?: string;
|
|
68
|
+
style?: string;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export type InputMockOptions = {
|
|
72
|
+
value?: string;
|
|
73
|
+
placeholder?: string;
|
|
74
|
+
type?: string;
|
|
75
|
+
textAlign?: 'start' | 'center' | 'end';
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
/** The stylesheets `formField()` and `input()` draw with: the real `et-form-field` CSS. */
|
|
79
|
+
export const formFieldStyles = /* @__PURE__ */ [
|
|
80
|
+
interactiveStyles,
|
|
81
|
+
iconStyles,
|
|
82
|
+
formFieldBase,
|
|
83
|
+
textShell,
|
|
84
|
+
suffixOverlap,
|
|
85
|
+
label,
|
|
86
|
+
hint,
|
|
87
|
+
inputBase,
|
|
88
|
+
`.et-design-panel-anchor { position: relative; }
|
|
89
|
+
.et-design-panel { position: absolute; inset-inline: 0; inset-block-start: calc(100% + 0.3rem); z-index: 1; }`,
|
|
90
|
+
].join('\n');
|
|
91
|
+
|
|
92
|
+
const attributesOf = (entries: (string | false | null | undefined)[]) => entries.filter(Boolean).join(' ');
|
|
93
|
+
|
|
94
|
+
/** Draws `<et-input>` as a control for `formField()`. */
|
|
95
|
+
export const input = (options: InputMockOptions = {}): FormFieldControlMock => ({
|
|
96
|
+
controlType: 'text-input',
|
|
97
|
+
hasValue: !!options.value,
|
|
98
|
+
html: (state) => {
|
|
99
|
+
const attributes = attributesOf([
|
|
100
|
+
'class="et-input-native"',
|
|
101
|
+
`type="${options.type ?? 'text'}"`,
|
|
102
|
+
options.value && `value="${options.value}"`,
|
|
103
|
+
state.disabled && 'disabled',
|
|
104
|
+
state.readonly && 'readonly',
|
|
105
|
+
state.required && 'required',
|
|
106
|
+
options.placeholder && `placeholder="${options.placeholder}"`,
|
|
107
|
+
state.invalid && 'aria-invalid="true"',
|
|
108
|
+
state.describedBy && `aria-describedby="${state.describedBy}"`,
|
|
109
|
+
state.labelId && `aria-labelledby="${state.labelId}"`,
|
|
110
|
+
`data-text-align="${options.textAlign ?? 'start'}"`,
|
|
111
|
+
]);
|
|
112
|
+
|
|
113
|
+
return `<et-input class="et-color-interactive et-input"><input ${attributes}></et-input>`;
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
/** Draws `<et-form-field>` with a control, its label, affixes and messages, as the component renders them. */
|
|
118
|
+
export const formField = (options: FormFieldMockOptions) => {
|
|
119
|
+
const control: FormFieldControlMock =
|
|
120
|
+
typeof options.control === 'string'
|
|
121
|
+
? { controlType: 'text-input', hasValue: false, html: () => options.control as string }
|
|
122
|
+
: options.control;
|
|
123
|
+
|
|
124
|
+
const {
|
|
125
|
+
id = 'field',
|
|
126
|
+
appearance = 'box',
|
|
127
|
+
fill = 'transparent',
|
|
128
|
+
labelMode = 'static',
|
|
129
|
+
size = 'md',
|
|
130
|
+
error,
|
|
131
|
+
hint,
|
|
132
|
+
} = options;
|
|
133
|
+
const open = !!control.expandable && !!options.open;
|
|
134
|
+
const focused = !!options.focused || open;
|
|
135
|
+
const labelId = options.label ? `et-label-${id}` : null;
|
|
136
|
+
const errorId = `et-form-field-error-${id}`;
|
|
137
|
+
const hintId = `et-form-field-hint-${id}`;
|
|
138
|
+
|
|
139
|
+
const state: FormFieldMockState = {
|
|
140
|
+
id,
|
|
141
|
+
disabled: !!options.disabled,
|
|
142
|
+
readonly: !!options.readonly,
|
|
143
|
+
required: !!options.required,
|
|
144
|
+
invalid: error !== undefined,
|
|
145
|
+
focused,
|
|
146
|
+
open,
|
|
147
|
+
labelId,
|
|
148
|
+
describedBy: error !== undefined ? errorId : hint !== undefined ? hintId : null,
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
const errorColor = error !== undefined && options.errorColor;
|
|
152
|
+
const floating = labelMode === 'floating-inside' || labelMode === 'floating-outside';
|
|
153
|
+
|
|
154
|
+
const classes = [
|
|
155
|
+
'et-color-interactive--has-focus',
|
|
156
|
+
'et-form-field',
|
|
157
|
+
errorColor ? `et-color--${errorColor}` : `et-color--${options.color ?? 'inherited'}`,
|
|
158
|
+
`et-surface--${options.surface ?? 'inherited'}`,
|
|
159
|
+
options.class,
|
|
160
|
+
].filter(Boolean);
|
|
161
|
+
|
|
162
|
+
const attributes = attributesOf([
|
|
163
|
+
`class="${classes.join(' ')}"`,
|
|
164
|
+
`data-control-type="${control.controlType}"`,
|
|
165
|
+
error !== undefined && 'data-error="true"',
|
|
166
|
+
open && 'data-expanded=""',
|
|
167
|
+
focused && 'data-focused=""',
|
|
168
|
+
state.readonly && 'data-readonly=""',
|
|
169
|
+
state.disabled && 'data-disabled=""',
|
|
170
|
+
`data-appearance="${appearance}"`,
|
|
171
|
+
`data-fill="${fill}"`,
|
|
172
|
+
floating && (focused || control.hasValue) && 'data-label-floated=""',
|
|
173
|
+
options.label && 'data-has-label=""',
|
|
174
|
+
`data-label-mode="${labelMode}"`,
|
|
175
|
+
`data-size="${size}"`,
|
|
176
|
+
'data-text-shell="true"',
|
|
177
|
+
options.style && `style="${options.style}"`,
|
|
178
|
+
]);
|
|
179
|
+
|
|
180
|
+
const labelAreaClasses = [
|
|
181
|
+
'et-form-field-label-area',
|
|
182
|
+
floating && 'et-form-field-label-area--floating',
|
|
183
|
+
labelMode === 'inline' && 'et-form-field-label-area--inline',
|
|
184
|
+
].filter(Boolean);
|
|
185
|
+
|
|
186
|
+
const labelHtml = options.label
|
|
187
|
+
? `<et-label id="${labelId}"${state.disabled ? ' data-disabled="true"' : ''}><span class="et-label-content">${options.label}</span>${state.required ? '<span class="et-label-required-marker" aria-hidden="true">*</span>' : ''}</et-label>`
|
|
188
|
+
: '';
|
|
189
|
+
|
|
190
|
+
const errorHtml =
|
|
191
|
+
error !== undefined
|
|
192
|
+
? `<div class="et-form-field-support-content et-form-field-errors${errorColor ? ` et-color--${errorColor}` : ''}" id="${errorId}" aria-atomic="true" aria-live="polite" data-active="true" data-direction="from-below" data-state="active"><et-form-error class="et-form-error">${error}</et-form-error></div>`
|
|
193
|
+
: '';
|
|
194
|
+
|
|
195
|
+
const hintHtml =
|
|
196
|
+
hint !== undefined && error === undefined
|
|
197
|
+
? `<div class="et-form-field-support-content et-form-field-hint" id="${hintId}" data-active="true" data-direction="from-above" data-state="active"><et-hint>${hint}</et-hint></div>`
|
|
198
|
+
: '';
|
|
199
|
+
|
|
200
|
+
const support =
|
|
201
|
+
error !== undefined || hint !== undefined
|
|
202
|
+
? `<div class="et-color-interactive-exclude et-form-field-support"><div class="et-form-field-support-row"><div class="et-form-field-support-stack">${errorHtml}${hintHtml}</div><div class="et-form-field-support-counter${options.errorColor ? ` et-color--${options.errorColor}` : ''}" hidden></div></div></div>`
|
|
203
|
+
: '';
|
|
204
|
+
|
|
205
|
+
const field = `<et-form-field ${attributes}><div class="et-form-field-shell"><div class="et-form-field-control-frame"><div class="et-form-field-affix et-form-field-prefix">${options.prefix ?? ''}</div><div class="et-form-field-control-area"><div class="${labelAreaClasses.join(' ')}">${labelHtml}</div><div class="et-form-field-control-slot">${control.html(state)}</div></div><div class="et-form-field-affix et-form-field-suffix">${control.suffix?.(state) ?? ''}${options.suffix ?? ''}</div></div></div>${support}</et-form-field>`;
|
|
206
|
+
|
|
207
|
+
if (!open || !control.panel) return field;
|
|
208
|
+
|
|
209
|
+
return `<div class="et-design-panel-anchor">${field}<div class="et-design-panel">${control.panel(state)}</div></div>`;
|
|
210
|
+
};
|
package/design/select.ts
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import controlSuffix from '../src/lib/forms/form-field/form-field-control-suffix-styles.component.css?inline';
|
|
2
|
+
import selectBase from '../src/lib/forms/select/select.component.css?inline';
|
|
3
|
+
import selectOption from '../src/lib/forms/select/select-option-styles.component.css?inline';
|
|
4
|
+
import selectPanel from '../src/lib/forms/select/select-panel.component.css?inline';
|
|
5
|
+
import { FormFieldControlMock, formFieldStyles, FormFieldMockState } from './form-field';
|
|
6
|
+
|
|
7
|
+
export const CHECK_ICON_SVG = `<svg width="100%" height="100%" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M438.6 105.4c12.5 12.5 12.5 32.8 0 45.3l-256 256c-12.5 12.5-32.8 12.5-45.3 0l-128-128c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0L160 338.7 393.4 105.4c12.5-12.5 32.8-12.5 45.3 0z"/></svg>`;
|
|
8
|
+
|
|
9
|
+
export const CHEVRON_ICON_SVG = `<svg width="100%" height="100%" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M244.7 116.7c6.2-6.2 16.4-6.2 22.6 0l192 192c6.2 6.2 6.2 16.4 0 22.6s-16.4 6.2-22.6 0L256 150.6 75.3 331.3c-6.2 6.2-16.4 6.2-22.6 0s-6.2-16.4 0-22.6l192-192z"/></svg>`;
|
|
10
|
+
|
|
11
|
+
export const TIMES_ICON_SVG = `<svg width="100%" height="100%" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path fill="currentColor" d="M324.5 411.1c6.2 6.2 16.4 6.2 22.6 0s6.2-16.4 0-22.6L214.6 256 347.1 123.5c6.2-6.2 6.2-16.4 0-22.6s-16.4-6.2-22.6 0L192 233.4 59.6 100.9c-6.2-6.2-16.4-6.2-22.6 0s-6.2 16.4 0 22.6L169.4 256 36.9 388.5c-6.2 6.2-6.2 16.4 0 22.6s16.4 6.2 22.6 0L192 278.6 324.5 411.1z"/></svg>`;
|
|
12
|
+
|
|
13
|
+
export type SelectMockOption = { value: string; label: string };
|
|
14
|
+
|
|
15
|
+
export type SelectMockOptions = {
|
|
16
|
+
options: SelectMockOption[];
|
|
17
|
+
/** The selected option's `value`. Left out, the select shows its placeholder. */
|
|
18
|
+
value?: string;
|
|
19
|
+
placeholder?: string;
|
|
20
|
+
/** The `value` of the option the keyboard points at while the panel is open. */
|
|
21
|
+
active?: string;
|
|
22
|
+
/** Shows the clear button while the field has focus and a value. Defaults to `true`. */
|
|
23
|
+
clearable?: boolean;
|
|
24
|
+
clearLabel?: string;
|
|
25
|
+
/** The app's default color theme name: the panel provides it to the options. */
|
|
26
|
+
panelColor?: string;
|
|
27
|
+
/** A surface theme name the app registers, for the panel. */
|
|
28
|
+
panelSurface?: string;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/** The stylesheets `formField()` with a `select()` control draws with: the real `et-select` CSS. */
|
|
32
|
+
export const selectStyles = /* @__PURE__ */ [
|
|
33
|
+
formFieldStyles,
|
|
34
|
+
controlSuffix,
|
|
35
|
+
selectBase,
|
|
36
|
+
selectOption,
|
|
37
|
+
selectPanel,
|
|
38
|
+
].join('\n');
|
|
39
|
+
|
|
40
|
+
const icon = (name: string, svg: string) =>
|
|
41
|
+
`<i eticon="${name}" aria-hidden="true" class="et-icon et-icon--${name}">${svg}</i>`;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Draws `<et-select>` as a control for `formField()`. Pass `open: true` to `formField()` to draw
|
|
45
|
+
* the panel below the field, where the overlay would open.
|
|
46
|
+
*/
|
|
47
|
+
export const select = (options: SelectMockOptions): FormFieldControlMock => {
|
|
48
|
+
const selected = options.options.find((option) => option.value === options.value);
|
|
49
|
+
|
|
50
|
+
const optionHtml = (state: FormFieldMockState) => (option: SelectMockOption, index: number) => {
|
|
51
|
+
const isSelected = option === selected;
|
|
52
|
+
const isActive = state.open && option.value === options.active;
|
|
53
|
+
const attributes = [
|
|
54
|
+
'role="option"',
|
|
55
|
+
`value="${option.value}"`,
|
|
56
|
+
'class="et-select-option"',
|
|
57
|
+
`id="et-select-option-${state.id}-${index}"`,
|
|
58
|
+
`aria-selected="${isSelected}"`,
|
|
59
|
+
isSelected && 'data-selected="true"',
|
|
60
|
+
isActive && 'data-active="true" data-active-source="keyboard"',
|
|
61
|
+
].filter(Boolean);
|
|
62
|
+
|
|
63
|
+
return `<et-select-option ${attributes.join(' ')}><span class="et-select-option-label">${option.label}</span><span class="et-select-option-check">${icon('et-check', CHECK_ICON_SVG)}</span></et-select-option>`;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const optionsHtml = (state: FormFieldMockState) => options.options.map(optionHtml(state)).join('');
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
controlType: 'select',
|
|
70
|
+
hasValue: !!selected,
|
|
71
|
+
expandable: true,
|
|
72
|
+
html: (state) => {
|
|
73
|
+
const triggerId = `et-select-trigger-${state.id}`;
|
|
74
|
+
const activeIndex = options.options.findIndex((option) => option.value === options.active);
|
|
75
|
+
const triggerAttributes = [
|
|
76
|
+
'class="et-select-trigger"',
|
|
77
|
+
`id="${triggerId}"`,
|
|
78
|
+
'role="combobox"',
|
|
79
|
+
'aria-haspopup="listbox"',
|
|
80
|
+
`aria-expanded="${state.open}"`,
|
|
81
|
+
`aria-labelledby="${[state.labelId, triggerId].filter(Boolean).join(' ')}"`,
|
|
82
|
+
`tabindex="${state.disabled ? -1 : 0}"`,
|
|
83
|
+
state.disabled && 'aria-disabled="true" data-disabled="true"',
|
|
84
|
+
state.open && activeIndex >= 0 && `aria-activedescendant="et-select-option-${state.id}-${activeIndex}"`,
|
|
85
|
+
state.open && 'data-select-open="true"',
|
|
86
|
+
state.open && `aria-controls="et-select-listbox-${state.id}"`,
|
|
87
|
+
].filter(Boolean);
|
|
88
|
+
|
|
89
|
+
const value = selected
|
|
90
|
+
? `<span class="et-select-value">${selected.label}</span>`
|
|
91
|
+
: `<span data-placeholder="true" class="et-select-value">${options.placeholder ?? ''}</span>`;
|
|
92
|
+
|
|
93
|
+
const detached = state.open
|
|
94
|
+
? ''
|
|
95
|
+
: `<div aria-hidden="true" hidden class="et-select-detached-options">${optionsHtml(state)}</div>`;
|
|
96
|
+
|
|
97
|
+
return `<et-select class="et-color-interactive et-select"${state.open ? ' data-select-open="true"' : ''}><div ${triggerAttributes.join(' ')}><div class="et-select-trigger-content">${value}</div></div>${detached}</et-select>`;
|
|
98
|
+
},
|
|
99
|
+
suffix: (state) => {
|
|
100
|
+
const clear =
|
|
101
|
+
(options.clearable ?? true) && selected && state.focused && !state.disabled && !state.readonly
|
|
102
|
+
? `<button aria-label="${options.clearLabel ?? 'Clear'}" class="et-input-clear" tabindex="-1" type="button">${icon('et-times', TIMES_ICON_SVG)}</button>`
|
|
103
|
+
: '';
|
|
104
|
+
|
|
105
|
+
return `${clear}<button aria-hidden="true" tabindex="-1" type="button" class="et-input-picker-trigger et-select-arrow"${state.disabled ? ' disabled' : ''}>${icon('et-chevron', CHEVRON_ICON_SVG)}</button>`;
|
|
106
|
+
},
|
|
107
|
+
panel: (state) =>
|
|
108
|
+
`<et-select-panel class="et-select-panel et-color--inherited${options.panelColor ? ` et-color--${options.panelColor}` : ''} et-surface--${options.panelSurface ?? 'inherited'}"><div class="et-select-panel-scroller"><div class="et-select-panel-body"><div role="listbox" class="et-select-listbox" id="et-select-listbox-${state.id}"${state.labelId ? ` aria-labelledby="${state.labelId}"` : ''}>${optionsHtml(state)}</div><div class="et-select-panel-extras"></div></div></div></et-select-panel>`,
|
|
109
|
+
};
|
|
110
|
+
};
|
|
@@ -258,6 +258,7 @@ const createTestOverlayRef = (config = {}) => {
|
|
|
258
258
|
elements: null,
|
|
259
259
|
config,
|
|
260
260
|
headerTemplate: signal(null).asReadonly(),
|
|
261
|
+
layout: signal(null).asReadonly(),
|
|
261
262
|
componentInstance: () => null,
|
|
262
263
|
close: (result) => requestClose(result, 'api', false),
|
|
263
264
|
busy,
|
|
@@ -505,7 +506,7 @@ const defineMissing = (target, key, value) => {
|
|
|
505
506
|
};
|
|
506
507
|
/**
|
|
507
508
|
* Installs the browser APIs jsdom lacks and `@ethlete/components` needs to render: `ResizeObserver`,
|
|
508
|
-
* `IntersectionObserver`, `matchMedia` and `Element.prototype.
|
|
509
|
+
* `IntersectionObserver`, `matchMedia`, `Element.prototype.animate` and `Element.prototype.scroll`. Call it once in a test setup file;
|
|
509
510
|
* an API that already exists is left alone. The fakes never report: observers never fire, no media query
|
|
510
511
|
* matches, and an animation finishes on the next microtask.
|
|
511
512
|
*/
|
|
@@ -514,6 +515,7 @@ const setupComponentsTestEnvironment = () => {
|
|
|
514
515
|
defineMissing(globalThis, 'IntersectionObserver', IntersectionObserverFake);
|
|
515
516
|
defineMissing(globalThis, 'matchMedia', fakeMediaQueryList);
|
|
516
517
|
defineMissing(Element.prototype, 'animate', () => new AnimationFake());
|
|
518
|
+
defineMissing(Element.prototype, 'scroll', () => undefined);
|
|
517
519
|
};
|
|
518
520
|
|
|
519
521
|
/**
|