diff --git a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiAutoRefresh_EuiAutoRefreshButton_Playground.png b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiAutoRefresh_EuiAutoRefreshButton_Playground.png new file mode 100644 index 00000000000..6d0b8ee7104 Binary files /dev/null and b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiAutoRefresh_EuiAutoRefreshButton_Playground.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiAutoRefresh_EuiAutoRefresh_Playground.png b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiAutoRefresh_EuiAutoRefresh_Playground.png new file mode 100644 index 00000000000..dd2820521b5 Binary files /dev/null and b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiAutoRefresh_EuiAutoRefresh_Playground.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiComboBox_Groups.png b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiComboBox_Groups.png new file mode 100644 index 00000000000..ace976e1d0e Binary files /dev/null and b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiComboBox_Groups.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiComboBox_Nested_Options_Groups.png b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiComboBox_Nested_Options_Groups.png new file mode 100644 index 00000000000..ace976e1d0e Binary files /dev/null and b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiComboBox_Nested_Options_Groups.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiComboBox_With_Tooltip.png b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiComboBox_With_Tooltip.png index a0492a4d36b..93d473c7ac2 100644 Binary files a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiComboBox_With_Tooltip.png and b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiComboBox_With_Tooltip.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiDatePickerRange_Playground.png b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiDatePickerRange_Playground.png new file mode 100644 index 00000000000..18622764ca5 Binary files /dev/null and b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiDatePickerRange_Playground.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiDualRange_Input.png b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiDualRange_Input.png new file mode 100644 index 00000000000..000b7d4b2b3 Binary files /dev/null and b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiDualRange_Input.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiDualRange_Input_With_Popover.png b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiDualRange_Input_With_Popover.png new file mode 100644 index 00000000000..b781691869b Binary files /dev/null and b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiDualRange_Input_With_Popover.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiDualRange_Levels.png b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiDualRange_Levels.png new file mode 100644 index 00000000000..c93304ee86e Binary files /dev/null and b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiDualRange_Levels.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiDualRange_Playground.png b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiDualRange_Playground.png new file mode 100644 index 00000000000..2a378e8174a Binary files /dev/null and b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiDualRange_Playground.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiDualRange_Ticks.png b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiDualRange_Ticks.png new file mode 100644 index 00000000000..44eaffca969 Binary files /dev/null and b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiDualRange_Ticks.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiFilePicker_Playground.png b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiFilePicker_Playground.png new file mode 100644 index 00000000000..45181358c68 Binary files /dev/null and b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiFilePicker_Playground.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiRange_Input.png b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiRange_Input.png new file mode 100644 index 00000000000..972b4616d3d Binary files /dev/null and b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiRange_Input.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiRange_Levels.png b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiRange_Levels.png new file mode 100644 index 00000000000..11e517d1520 Binary files /dev/null and b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiRange_Levels.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiRange_Playground.png b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiRange_Playground.png new file mode 100644 index 00000000000..b6e3afee420 Binary files /dev/null and b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiRange_Playground.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiRange_Ticks.png b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiRange_Ticks.png new file mode 100644 index 00000000000..c6c284363ae Binary files /dev/null and b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiRange_Ticks.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiRange_Value_Tooltip.png b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiRange_Value_Tooltip.png new file mode 100644 index 00000000000..5e46f6d9296 Binary files /dev/null and b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiRange_Value_Tooltip.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiRefreshInterval_Playground.png b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiRefreshInterval_Playground.png new file mode 100644 index 00000000000..aa54bb7f355 Binary files /dev/null and b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiRefreshInterval_Playground.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiSelect_Playground.png b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiSelect_Playground.png new file mode 100644 index 00000000000..a7706db03da Binary files /dev/null and b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiSelect_Playground.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiSelectable_EuiSelectableList_EuiSelectableListItem_Playground.png b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiSelectable_Subcomponents_EuiSelectableListItem_Playground.png similarity index 100% rename from packages/eui/.loki/reference/chrome_desktop_Forms_EuiSelectable_EuiSelectableList_EuiSelectableListItem_Playground.png rename to packages/eui/.loki/reference/chrome_desktop_Forms_EuiSelectable_Subcomponents_EuiSelectableListItem_Playground.png diff --git a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiSelectable_Subcomponents_EuiSelectableList_Groups.png b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiSelectable_Subcomponents_EuiSelectableList_Groups.png new file mode 100644 index 00000000000..2daa4fdeec0 Binary files /dev/null and b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiSelectable_Subcomponents_EuiSelectableList_Groups.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiSelectable_EuiSelectableList_EuiSelectableList_Playground.png b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiSelectable_Subcomponents_EuiSelectableList_Playground.png similarity index 100% rename from packages/eui/.loki/reference/chrome_desktop_Forms_EuiSelectable_EuiSelectableList_EuiSelectableList_Playground.png rename to packages/eui/.loki/reference/chrome_desktop_Forms_EuiSelectable_Subcomponents_EuiSelectableList_Playground.png diff --git a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiSuperDatePicker_EuiSuperDatePicker_Custom_Quick_Select_Panel.png b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiSuperDatePicker_EuiSuperDatePicker_Custom_Quick_Select_Panel.png new file mode 100644 index 00000000000..26048176939 Binary files /dev/null and b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiSuperDatePicker_EuiSuperDatePicker_Custom_Quick_Select_Panel.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiSuperDatePicker_EuiSuperDatePicker_Playground.png b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiSuperDatePicker_EuiSuperDatePicker_Playground.png new file mode 100644 index 00000000000..26048176939 Binary files /dev/null and b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiSuperDatePicker_EuiSuperDatePicker_Playground.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiSuperDatePicker_EuiSuperUpdateButton_Playground.png b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiSuperDatePicker_EuiSuperUpdateButton_Playground.png new file mode 100644 index 00000000000..893c4671193 Binary files /dev/null and b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiSuperDatePicker_EuiSuperUpdateButton_Playground.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiSuperSelect_Playground.png b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiSuperSelect_Playground.png new file mode 100644 index 00000000000..682f2f6532b Binary files /dev/null and b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiSuperSelect_Playground.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiBasicTable_Expanded_Row.png b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiBasicTable_Expanded_Row.png new file mode 100644 index 00000000000..a44dd152a70 Binary files /dev/null and b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiBasicTable_Expanded_Row.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiBasicTable_Playground.png b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiBasicTable_Playground.png new file mode 100644 index 00000000000..674d3c737b6 Binary files /dev/null and b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiBasicTable_Playground.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiAutoRefresh_EuiAutoRefreshButton_Playground.png b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiAutoRefresh_EuiAutoRefreshButton_Playground.png new file mode 100644 index 00000000000..ec959f32488 Binary files /dev/null and b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiAutoRefresh_EuiAutoRefreshButton_Playground.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiAutoRefresh_EuiAutoRefresh_Playground.png b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiAutoRefresh_EuiAutoRefresh_Playground.png new file mode 100644 index 00000000000..a040e509bdb Binary files /dev/null and b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiAutoRefresh_EuiAutoRefresh_Playground.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiComboBox_Groups.png b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiComboBox_Groups.png new file mode 100644 index 00000000000..b0614dadb18 Binary files /dev/null and b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiComboBox_Groups.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiComboBox_Nested_Options_Groups.png b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiComboBox_Nested_Options_Groups.png new file mode 100644 index 00000000000..b0614dadb18 Binary files /dev/null and b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiComboBox_Nested_Options_Groups.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiComboBox_With_Tooltip.png b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiComboBox_With_Tooltip.png index f9684a55a0f..3ce0666290a 100644 Binary files a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiComboBox_With_Tooltip.png and b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiComboBox_With_Tooltip.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiDatePickerRange_Playground.png b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiDatePickerRange_Playground.png new file mode 100644 index 00000000000..3308e676f78 Binary files /dev/null and b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiDatePickerRange_Playground.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiDualRange_Input.png b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiDualRange_Input.png new file mode 100644 index 00000000000..184897331ea Binary files /dev/null and b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiDualRange_Input.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiDualRange_Input_With_Popover.png b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiDualRange_Input_With_Popover.png new file mode 100644 index 00000000000..3bba38ea319 Binary files /dev/null and b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiDualRange_Input_With_Popover.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiDualRange_Levels.png b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiDualRange_Levels.png new file mode 100644 index 00000000000..401129f683a Binary files /dev/null and b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiDualRange_Levels.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiDualRange_Playground.png b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiDualRange_Playground.png new file mode 100644 index 00000000000..60f1bfb3811 Binary files /dev/null and b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiDualRange_Playground.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiDualRange_Ticks.png b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiDualRange_Ticks.png new file mode 100644 index 00000000000..10e9a6fd26d Binary files /dev/null and b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiDualRange_Ticks.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiFilePicker_Playground.png b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiFilePicker_Playground.png new file mode 100644 index 00000000000..a5eb1a89a61 Binary files /dev/null and b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiFilePicker_Playground.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiRange_Input.png b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiRange_Input.png new file mode 100644 index 00000000000..8d2224d650b Binary files /dev/null and b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiRange_Input.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiRange_Levels.png b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiRange_Levels.png new file mode 100644 index 00000000000..f254f7e93b2 Binary files /dev/null and b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiRange_Levels.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiRange_Playground.png b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiRange_Playground.png new file mode 100644 index 00000000000..d9f2cade223 Binary files /dev/null and b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiRange_Playground.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiRange_Ticks.png b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiRange_Ticks.png new file mode 100644 index 00000000000..af4377d32de Binary files /dev/null and b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiRange_Ticks.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiRange_Value_Tooltip.png b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiRange_Value_Tooltip.png new file mode 100644 index 00000000000..be7cbcd659f Binary files /dev/null and b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiRange_Value_Tooltip.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiRefreshInterval_Playground.png b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiRefreshInterval_Playground.png new file mode 100644 index 00000000000..c2fd50f6db6 Binary files /dev/null and b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiRefreshInterval_Playground.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiSelect_Playground.png b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiSelect_Playground.png new file mode 100644 index 00000000000..69842e4a721 Binary files /dev/null and b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiSelect_Playground.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiSelectable_EuiSelectableList_EuiSelectableListItem_Playground.png b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiSelectable_Subcomponents_EuiSelectableListItem_Playground.png similarity index 100% rename from packages/eui/.loki/reference/chrome_mobile_Forms_EuiSelectable_EuiSelectableList_EuiSelectableListItem_Playground.png rename to packages/eui/.loki/reference/chrome_mobile_Forms_EuiSelectable_Subcomponents_EuiSelectableListItem_Playground.png diff --git a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiSelectable_Subcomponents_EuiSelectableList_Groups.png b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiSelectable_Subcomponents_EuiSelectableList_Groups.png new file mode 100644 index 00000000000..4582f2c9ef7 Binary files /dev/null and b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiSelectable_Subcomponents_EuiSelectableList_Groups.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiSelectable_EuiSelectableList_EuiSelectableList_Playground.png b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiSelectable_Subcomponents_EuiSelectableList_Playground.png similarity index 100% rename from packages/eui/.loki/reference/chrome_mobile_Forms_EuiSelectable_EuiSelectableList_EuiSelectableList_Playground.png rename to packages/eui/.loki/reference/chrome_mobile_Forms_EuiSelectable_Subcomponents_EuiSelectableList_Playground.png diff --git a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiSuperDatePicker_EuiSuperDatePicker_Custom_Quick_Select_Panel.png b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiSuperDatePicker_EuiSuperDatePicker_Custom_Quick_Select_Panel.png new file mode 100644 index 00000000000..d03a4589ff9 Binary files /dev/null and b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiSuperDatePicker_EuiSuperDatePicker_Custom_Quick_Select_Panel.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiSuperDatePicker_EuiSuperDatePicker_Playground.png b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiSuperDatePicker_EuiSuperDatePicker_Playground.png new file mode 100644 index 00000000000..d03a4589ff9 Binary files /dev/null and b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiSuperDatePicker_EuiSuperDatePicker_Playground.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiSuperDatePicker_EuiSuperUpdateButton_Playground.png b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiSuperDatePicker_EuiSuperUpdateButton_Playground.png new file mode 100644 index 00000000000..bad8dda6f65 Binary files /dev/null and b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiSuperDatePicker_EuiSuperUpdateButton_Playground.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiSuperSelect_Playground.png b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiSuperSelect_Playground.png new file mode 100644 index 00000000000..056ab1e904a Binary files /dev/null and b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiSuperSelect_Playground.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiBasicTable_Expanded_Row.png b/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiBasicTable_Expanded_Row.png new file mode 100644 index 00000000000..a04e3dfb042 Binary files /dev/null and b/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiBasicTable_Expanded_Row.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiBasicTable_Playground.png b/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiBasicTable_Playground.png new file mode 100644 index 00000000000..d0b7f27918f Binary files /dev/null and b/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiBasicTable_Playground.png differ diff --git a/packages/eui/.storybook/loki.ts b/packages/eui/.storybook/loki.ts index c588bc63e58..06770517202 100644 --- a/packages/eui/.storybook/loki.ts +++ b/packages/eui/.storybook/loki.ts @@ -26,6 +26,11 @@ export const LOKI_SELECTORS = { * Portal element content selector */ portal: '[data-euiportal="true"]', + /** + * Body selector + * TODO: remove when LOKI_SELECTORS.portal selector works as expected again + */ + body: 'body', } as const; /** diff --git a/packages/eui/changelogs/CHANGELOG_2024.md b/packages/eui/changelogs/CHANGELOG_2024.md index 986aacc914f..06e533dcdc8 100644 --- a/packages/eui/changelogs/CHANGELOG_2024.md +++ b/packages/eui/changelogs/CHANGELOG_2024.md @@ -1,3 +1,11 @@ +## [`v94.6.0`](https://github.com/elastic/eui/releases/v94.6.0) + +- Updated `EuiComboBox` to support rendering `option.append` and `option.prepend` in group labels ([#7800](https://github.com/elastic/eui/pull/7800)) + +**Accessibility** + +- Improved the accessibility experience of `EuiBetaBadge` ([#7805](https://github.com/elastic/eui/pull/7805)) + ## [`v94.5.2`](https://github.com/elastic/eui/releases/v94.5.2) **Bug fixes** diff --git a/packages/eui/changelogs/upcoming/7682.md b/packages/eui/changelogs/upcoming/7682.md new file mode 100644 index 00000000000..453bc73d9db --- /dev/null +++ b/packages/eui/changelogs/upcoming/7682.md @@ -0,0 +1,4 @@ +**Breaking changes** + +- Removed deprecated `EUI_CHARTS_THEME_DARK`, `EUI_CHARTS_THEME_LIGHT` and `EUI_SPARKLINE_THEME_PARTIAL` exports + diff --git a/packages/eui/changelogs/upcoming/7806.md b/packages/eui/changelogs/upcoming/7806.md new file mode 100644 index 00000000000..208631e50e6 --- /dev/null +++ b/packages/eui/changelogs/upcoming/7806.md @@ -0,0 +1,3 @@ +**Bug fixes** + +- Fixed `EuiSearchBar`'s filter configs to always respect `autoClose: false` diff --git a/packages/eui/changelogs/upcoming/7808.md b/packages/eui/changelogs/upcoming/7808.md new file mode 100644 index 00000000000..5fc45bb05b2 --- /dev/null +++ b/packages/eui/changelogs/upcoming/7808.md @@ -0,0 +1,3 @@ +**Breaking changes** + +- Removed deprecated `euiPalettePositive` and `euiPaletteNegative`. Use `euiPaletteGreen` and `euiPaletteRed` instead diff --git a/packages/eui/i18ntokens.json b/packages/eui/i18ntokens.json index 759968ab518..b5bf3b756ea 100644 --- a/packages/eui/i18ntokens.json +++ b/packages/eui/i18ntokens.json @@ -851,14 +851,14 @@ "highlighting": "string", "loc": { "start": { - "line": 335, + "line": 339, "column": 12, - "index": 9517 + "index": 9584 }, "end": { - "line": 338, + "line": 342, "column": 14, - "index": 9640 + "index": 9707 } }, "filepath": "src/components/combo_box/combo_box_options_list/combo_box_options_list.tsx" @@ -869,14 +869,14 @@ "highlighting": "string", "loc": { "start": { - "line": 348, + "line": 352, "column": 16, - "index": 10079 + "index": 10146 }, "end": { - "line": 352, + "line": 356, "column": 18, - "index": 10310 + "index": 10377 } }, "filepath": "src/components/combo_box/combo_box_options_list/combo_box_options_list.tsx" @@ -887,14 +887,14 @@ "highlighting": "string", "loc": { "start": { - "line": 367, + "line": 371, "column": 16, - "index": 10739 + "index": 10806 }, "end": { - "line": 373, + "line": 377, "column": 18, - "index": 11012 + "index": 11079 } }, "filepath": "src/components/combo_box/combo_box_options_list/combo_box_options_list.tsx" @@ -905,14 +905,14 @@ "highlighting": "string", "loc": { "start": { - "line": 402, + "line": 406, "column": 20, - "index": 12003 + "index": 12070 }, "end": { - "line": 408, + "line": 412, "column": 22, - "index": 12301 + "index": 12368 } }, "filepath": "src/components/combo_box/combo_box_options_list/combo_box_options_list.tsx" @@ -923,14 +923,14 @@ "highlighting": "string", "loc": { "start": { - "line": 419, + "line": 423, "column": 12, - "index": 12505 + "index": 12572 }, "end": { - "line": 423, + "line": 427, "column": 14, - "index": 12726 + "index": 12793 } }, "filepath": "src/components/combo_box/combo_box_options_list/combo_box_options_list.tsx" @@ -941,14 +941,14 @@ "highlighting": "string", "loc": { "start": { - "line": 430, + "line": 434, "column": 10, - "index": 12845 + "index": 12912 }, "end": { - "line": 433, + "line": 437, "column": 12, - "index": 12985 + "index": 13052 } }, "filepath": "src/components/combo_box/combo_box_options_list/combo_box_options_list.tsx" @@ -959,14 +959,14 @@ "highlighting": "string", "loc": { "start": { - "line": 439, + "line": 443, "column": 10, - "index": 13098 + "index": 13165 }, "end": { - "line": 442, + "line": 446, "column": 12, - "index": 13241 + "index": 13308 } }, "filepath": "src/components/combo_box/combo_box_options_list/combo_box_options_list.tsx" diff --git a/packages/eui/package.json b/packages/eui/package.json index 36c452ab978..78a042cd860 100644 --- a/packages/eui/package.json +++ b/packages/eui/package.json @@ -1,7 +1,7 @@ { "name": "@elastic/eui", "description": "Elastic UI Component Library", - "version": "94.5.2", + "version": "94.6.0", "license": "SEE LICENSE IN LICENSE.txt", "main": "lib", "module": "es", diff --git a/packages/eui/scripts/compile-eui.js b/packages/eui/scripts/compile-eui.js index 0cd2c285a0c..cda2d448953 100755 --- a/packages/eui/scripts/compile-eui.js +++ b/packages/eui/scripts/compile-eui.js @@ -26,7 +26,6 @@ const IGNORE_TESTENV = [ ]; const IGNORE_PACKAGES = [ '**/react-datepicker/test/**/*.js', - '**/themes/charts/themes.ts', ]; function compileLib() { @@ -207,27 +206,6 @@ function compileBundle() { } ); console.log(chalk.green('✔ Finished test utils files')); - - console.log('Building chart theme module...'); - execSync('webpack --config=src/themes/charts/webpack.config.js', { - stdio: 'inherit', - }); - dtsGenerator({ - prefix: '', - out: 'dist/eui_charts_theme.d.ts', - baseDir: path.resolve(__dirname, '..', 'src/themes/charts/'), - files: ['themes.ts'], - resolveModuleId() { - return '@elastic/eui/dist/eui_charts_theme'; - }, - resolveModuleImport(params) { - if (params.importedModuleId === '../../components/common') { - return '@elastic/eui/src/components/common'; - } - return null; - }, - }); - console.log(chalk.green('✔ Finished chart theme module')); } compileLib(); diff --git a/packages/eui/scripts/dtsgenerator.js b/packages/eui/scripts/dtsgenerator.js index f8eacd8f665..0b261b7a723 100644 --- a/packages/eui/scripts/dtsgenerator.js +++ b/packages/eui/scripts/dtsgenerator.js @@ -37,7 +37,6 @@ const generator = dtsGenerator({ '**/*.stories.{ts,tsx}', '**/*.mock.{ts,tsx}', '**/__mocks__/*', - 'src/themes/charts/*', // A separate d.ts file is generated for the charts theme file 'src/test/**/*', // Separate d.ts files are generated for test utils 'src-docs/**/*', // Don't include src-docs '**/*.mdx', // Don't include storybook mdx files diff --git a/packages/eui/src-docs/src/components/guide_page/versions.json b/packages/eui/src-docs/src/components/guide_page/versions.json index 6a739946e5f..f41b2fe9981 100644 --- a/packages/eui/src-docs/src/components/guide_page/versions.json +++ b/packages/eui/src-docs/src/components/guide_page/versions.json @@ -1,5 +1,6 @@ { "euiVersions": [ + "94.6.0", "94.5.2", "94.5.1", "94.5.0", diff --git a/packages/eui/src-docs/src/views/elastic_charts/sparklines.tsx b/packages/eui/src-docs/src/views/elastic_charts/sparklines.tsx index 55efb9e8173..cc441a6b72c 100644 --- a/packages/eui/src-docs/src/views/elastic_charts/sparklines.tsx +++ b/packages/eui/src-docs/src/views/elastic_charts/sparklines.tsx @@ -7,10 +7,9 @@ import { Tooltip, LineSeries, AreaSeries, + PartialTheme, } from '@elastic/charts'; -import { EUI_SPARKLINE_THEME_PARTIAL } from '../../../../src/themes/charts/themes'; - import { EuiPanel, EuiStat, @@ -28,6 +27,28 @@ import { } from '../../../../src/services'; import { useChartBaseTheme } from './utils/use_chart_base_theme'; +/** + * Example sparkline styles to match `ThemeService.useSparklineOverrides` from kibana `charts` plugin + * + * See https://github.com/elastic/kibana/blob/82fdf0414d61a1419038eed395bcdf941d72a58c/src/plugins/charts/public/services/theme/theme.ts#L55-L77 + */ +const sparklineOverrides: PartialTheme = { + lineSeriesStyle: { + point: { + visible: false, + strokeWidth: 1, + radius: 1, + }, + }, + areaSeriesStyle: { + point: { + visible: false, + strokeWidth: 1, + radius: 1, + }, + }, +}; + export const TIME_DATA_SMALL = [ [1551438630000, 8.515625], [1551438660000, 10.796875], @@ -60,7 +81,7 @@ export default () => { @@ -91,7 +112,7 @@ export default () => { @@ -130,7 +151,7 @@ export default () => { @@ -161,7 +182,7 @@ export default () => { diff --git a/packages/eui/src-docs/src/views/flyout/flyout_example.js b/packages/eui/src-docs/src/views/flyout/flyout_example.js index 2a894039a23..fbe28aa1633 100644 --- a/packages/eui/src-docs/src/views/flyout/flyout_example.js +++ b/packages/eui/src-docs/src/views/flyout/flyout_example.js @@ -402,7 +402,6 @@ export const FlyoutExample = { }, { title: 'Resizable flyouts', - isBeta: true, source: [ { type: GuideSectionTypes.JS, diff --git a/packages/eui/src-docs/src/views/search_bar/props_info.js b/packages/eui/src-docs/src/views/search_bar/props_info.js index 900b7d37224..6616f9270f0 100644 --- a/packages/eui/src-docs/src/views/search_bar/props_info.js +++ b/packages/eui/src-docs/src/views/search_bar/props_info.js @@ -279,7 +279,7 @@ export const propsInfo = { }, autoClose: { description: - 'Should the dropdown close after the user selects a value. Ignored if multiSelect is true.', + 'Should the dropdown close after the user selects a value. If not explicitly passed, will auto-close for single selection and remain open for multi-selection.', required: false, defaultValue: { value: 'true' }, type: { name: 'boolean' }, diff --git a/packages/eui/src/components/badge/beta_badge/__snapshots__/beta_badge.test.tsx.snap b/packages/eui/src/components/badge/beta_badge/__snapshots__/beta_badge.test.tsx.snap index fe9bd1520a6..031c5216127 100644 --- a/packages/eui/src/components/badge/beta_badge/__snapshots__/beta_badge.test.tsx.snap +++ b/packages/eui/src/components/badge/beta_badge/__snapshots__/beta_badge.test.tsx.snap @@ -124,7 +124,6 @@ exports[`EuiBetaBadge props tooltip and anchorProps are rendered 1`] = ` > Beta diff --git a/packages/eui/src/components/badge/beta_badge/beta_badge.tsx b/packages/eui/src/components/badge/beta_badge/beta_badge.tsx index 6cf6484be6c..bcaca40f742 100644 --- a/packages/eui/src/components/badge/beta_badge/beta_badge.tsx +++ b/packages/eui/src/components/badge/beta_badge/beta_badge.tsx @@ -230,13 +230,7 @@ export const EuiBetaBadge: FunctionComponent = ({ title={title || label} anchorProps={anchorProps} > - + {icon || label} diff --git a/packages/eui/src/components/basic_table/basic_table.stories.tsx b/packages/eui/src/components/basic_table/basic_table.stories.tsx new file mode 100644 index 00000000000..e39245cbf34 --- /dev/null +++ b/packages/eui/src/components/basic_table/basic_table.stories.tsx @@ -0,0 +1,299 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { useCallback, useEffect, useState } from 'react'; +import type { Meta, StoryObj } from '@storybook/react'; +import { action } from '@storybook/addon-actions'; +import { faker } from '@faker-js/faker'; +import { moveStorybookControlsToCategory } from '../../../.storybook/utils'; + +import { EuiLink } from '../link'; +import { EuiHealth } from '../health'; + +import type { + CriteriaWithPagination, + EuiBasicTableColumn, +} from './basic_table'; +import { EuiBasicTable, EuiBasicTableProps } from './basic_table'; + +// Set static seed so that the generated faker data is consistent between page loads +faker.seed(8_02_2010); + +const meta: Meta> = { + title: 'Tabular Content/EuiBasicTable', + // @ts-ignore complex + component: EuiBasicTable, + argTypes: { + noItemsMessage: { control: 'text' }, + }, + args: { + error: '', + loading: false, + // Inherited from EuiTable + responsiveBreakpoint: 'm', + tableLayout: 'fixed', + // set up for easier testing/QA + cellProps: { + 'data-test-subj': `basic-table-cell`, + }, + rowProps: { + 'data-test-subj': `basic-table-row`, + }, + noItemsMessage: '', + }, +}; +moveStorybookControlsToCategory( + meta, + ['responsiveBreakpoint', 'tableLayout'], + 'EuiTable props' +); + +export default meta; +type Story = StoryObj>; + +type User = { + id: number; + firstName: string | null | undefined; + lastName: string; + online: boolean; + location: { + city: string; + country: string; + }; +}; + +const users: User[] = []; + +for (let i = 0; i < 5; i++) { + users.push({ + id: i + 1, + firstName: faker.person.firstName(), + lastName: faker.person.lastName(), + online: faker.datatype.boolean(), + location: { + city: faker.location.city(), + country: faker.location.country(), + }, + }); +} + +const columns: Array> = [ + { + field: 'firstName', + name: 'First Name', + sortable: true, + truncateText: true, + mobileOptions: { + render: (user: User) => ( + + {user.firstName} {user.lastName} + + ), + header: false, + truncateText: false, + enlarge: true, + width: '100%', + }, + }, + { + field: 'lastName', + name: 'Last Name', + truncateText: true, + sortable: true, + mobileOptions: { + show: false, + }, + }, + { + field: 'location', + name: 'Location', + truncateText: true, + textOnly: true, + render: (location: User['location']) => { + return `${location.city}, ${location.country}`; + }, + }, + { + field: 'online', + name: 'Online', + dataType: 'boolean', + render: (online: User['online']) => { + const color = online ? 'success' : 'danger'; + const label = online ? 'Online' : 'Offline'; + return {label}; + }, + sortable: true, + mobileOptions: { + show: false, + }, + }, + { + name: 'Actions', + actions: [ + { + name: 'User profile', + description: ({ firstName, lastName }) => + `Visit ${firstName} ${lastName}'s profile`, + icon: 'editorLink', + color: 'primary', + type: 'icon', + enabled: ({ online }) => !!online, + href: ({ id }) => `${window.location.href}?id=${id}`, + target: '_self', + 'data-test-subj': 'action-outboundlink', + }, + { + name: <>Clone, + description: 'Clone this user', + icon: 'copy', + type: 'icon', + onClick: () => {}, + 'data-test-subj': 'action-clone', + }, + { + name: (user: User) => (user.id ? 'Delete' : 'Remove'), + description: ({ firstName, lastName }) => + `Delete ${firstName} ${lastName}`, + icon: 'trash', + color: 'danger', + type: 'icon', + onClick: () => {}, + isPrimary: true, + 'data-test-subj': ({ id }) => `action-delete-${id}`, + }, + { + name: 'Edit', + isPrimary: true, + available: ({ online }) => !online, + enabled: ({ online }) => !!online, + description: 'Edit this user', + icon: 'pencil', + type: 'icon', + onClick: () => {}, + 'data-test-subj': 'action-edit', + }, + { + name: 'Share', + isPrimary: true, + description: 'Share this user', + icon: 'share', + type: 'icon', + onClick: () => {}, + 'data-test-subj': 'action-share', + }, + ], + }, + { + name: 'Custom actions', + actions: [ + { + render: () => ( + {}} color="danger"> + Delete + + ), + showOnHover: true, + }, + { + render: () => {}}>Edit, + }, + ], + }, +]; + +const initialPageSize = 3; + +export const Playground: Story = { + args: { + tableCaption: 'EuiBasicTable playground', + items: users, + itemId: 'id', + rowHeader: 'firstName', + columns, + itemIdToExpandedRowMap: {}, + pagination: { + pageIndex: 0, + totalItemCount: 5, + pageSize: initialPageSize, + pageSizeOptions: [3, 5], + }, + sorting: { + sort: { + field: 'lastName', + direction: 'asc' as const, + }, + }, + selection: { + selectable: (user) => user.online, + selectableMessage: (selectable) => + !selectable ? 'User is currently offline' : '', + onSelectionChange: action('onSelectionChange'), + }, + onChange: (criteria: CriteriaWithPagination) => + action('onChange')(criteria), + }, + render: (args: EuiBasicTableProps) => , +}; + +export const ExpandedRow: Story = { + parameters: { + controls: { + include: ['columns', 'items', 'itemIdToExpandedRowMap'], + }, + }, + args: { + tableCaption: 'EuiBasicTable playground', + items: users, + itemId: 'id', + rowHeader: 'firstName', + columns, + itemIdToExpandedRowMap: { + 1: ( +
+ Expanded row 1 +

lorem ipsum dolor sit

+
+ ), + }, + }, +}; + +const StatefulPlayground = ({ + items, + pagination, + sorting, + ...rest +}: EuiBasicTableProps) => { + const [_items, setItems] = useState([]); + + const updateItems = useCallback(() => { + let sortedItems = [...items]; + + if (sorting?.sort) { + const { field, direction } = sorting?.sort; + const directionIndex = direction === 'desc' ? -1 : 1; + + sortedItems = sortedItems.sort((a, b) => + a[field]! > b[field]! ? directionIndex : -directionIndex + ); + } + + setItems(() => { + return [...sortedItems].splice( + 0, + pagination?.pageSize ?? initialPageSize + ); + }); + }, [items, pagination, sorting]); + + useEffect(() => { + updateItems(); + }, [items, pagination, sorting, updateItems]); + + return ; +}; diff --git a/packages/eui/src/components/collapsible_nav_beta/_kibana_solution/__snapshots__/collapsible_nav_kibana_solution.test.tsx.snap b/packages/eui/src/components/collapsible_nav_beta/_kibana_solution/__snapshots__/collapsible_nav_kibana_solution.test.tsx.snap index c1745228514..f55129006bb 100644 --- a/packages/eui/src/components/collapsible_nav_beta/_kibana_solution/__snapshots__/collapsible_nav_kibana_solution.test.tsx.snap +++ b/packages/eui/src/components/collapsible_nav_beta/_kibana_solution/__snapshots__/collapsible_nav_kibana_solution.test.tsx.snap @@ -161,7 +161,7 @@ exports[`KibanaCollapsibleNavSolution renders with a solution switcher 1`] = `
diff --git a/packages/eui/src/components/collapsible_nav_beta/collapsible_nav_beta.stories.tsx b/packages/eui/src/components/collapsible_nav_beta/collapsible_nav_beta.stories.tsx index 6d60cc17f7d..ae847270f47 100644 --- a/packages/eui/src/components/collapsible_nav_beta/collapsible_nav_beta.stories.tsx +++ b/packages/eui/src/components/collapsible_nav_beta/collapsible_nav_beta.stories.tsx @@ -93,24 +93,10 @@ export const Playground: Story = { render: ({ ...args }) => ( - - + + - - - - - - -
{ return { euiCollapsibleNavItem__items: css``, - isGroup: css` - ${logicalCSS('padding-top', euiTheme.size.xs)} - ${logicalCSS('padding-left', euiTheme.size.s)} - `, isTopItem: css` ${logicalCSS('padding-top', euiTheme.size.xs)} ${logicalCSS('padding-left', euiTheme.size.xl)} @@ -76,3 +72,25 @@ export const euiCollapsibleNavSubItemsStyles = ({ euiTheme }: UseEuiTheme) => { `, }; }; + +/** + * Top-level item only styles + */ + +export const euiCollapsibleNavTopItemStyles = ({ euiTheme }: UseEuiTheme) => { + return { + // If this is the only top-level item in the list, assume it's a solution nav and + // reduce its default left padding + increase its relative icon size + euiCollapsibleNavTopItem: css` + &:only-child { + .euiCollapsibleNavItem__items { + ${logicalCSS('padding-left', euiTheme.size.s)} + } + + .euiCollapsibleNavItem__icon { + transform: scale(1.25); + } + } + `, + }; +}; diff --git a/packages/eui/src/components/collapsible_nav_beta/collapsible_nav_item/collapsible_nav_item.test.tsx b/packages/eui/src/components/collapsible_nav_beta/collapsible_nav_item/collapsible_nav_item.test.tsx index b0e1e040c22..6157e662e11 100644 --- a/packages/eui/src/components/collapsible_nav_beta/collapsible_nav_item/collapsible_nav_item.test.tsx +++ b/packages/eui/src/components/collapsible_nav_beta/collapsible_nav_item/collapsible_nav_item.test.tsx @@ -17,6 +17,7 @@ import { EuiCollapsibleNavItem } from './collapsible_nav_item'; describe('EuiCollapsibleNavItem', () => { shouldRenderCustomStyles(, { childProps: ['linkProps'], + skip: { css: true }, // The custom CSS is there but the merged order is unpredictable }); shouldRenderCustomStyles( , diff --git a/packages/eui/src/components/collapsible_nav_beta/collapsible_nav_item/collapsible_nav_item.tsx b/packages/eui/src/components/collapsible_nav_beta/collapsible_nav_item/collapsible_nav_item.tsx index dbb46a61d84..b1b9bc71c7f 100644 --- a/packages/eui/src/components/collapsible_nav_beta/collapsible_nav_item/collapsible_nav_item.tsx +++ b/packages/eui/src/components/collapsible_nav_beta/collapsible_nav_item/collapsible_nav_item.tsx @@ -28,6 +28,7 @@ import { EuiCollapsibleNavAccordion } from './collapsible_nav_accordion'; import { EuiCollapsibleNavGroup } from './collapsible_nav_group'; import { EuiCollapsibleNavLink } from './collapsible_nav_link'; import { + euiCollapsibleNavTopItemStyles, euiCollapsibleNavItemTitleStyles, euiCollapsibleNavSubItemsStyles, } from './collapsible_nav_item.styles'; @@ -199,7 +200,16 @@ export const EuiCollapsibleNavItemTitle: FunctionComponent< return ( <> - {icon && } + {icon && ( + + )} & _EuiCollapsibleNavItemDisplayProps & { items: EuiCollapsibleNavSubItemProps[]; - isGroup?: boolean; }; export const EuiCollapsibleNavSubItems: FunctionComponent< EuiCollapsibleNavSubItemsProps -> = ({ items, isSubItem, isGroup, className, ...rest }) => { +> = ({ items, isSubItem, className, ...rest }) => { const classes = classNames('euiCollapsibleNavItem__items', className); const styles = useEuiMemoizedStyles(euiCollapsibleNavSubItemsStyles); const cssStyles = [ styles.euiCollapsibleNavItem__items, - isGroup ? styles.isGroup : isSubItem ? styles.isSubItem : styles.isTopItem, + isSubItem ? styles.isSubItem : styles.isTopItem, ]; const itemsHaveIcons = useMemo( @@ -285,6 +294,7 @@ export const EuiCollapsibleNavItem: FunctionComponent< EuiCollapsibleNavItemProps > = ({ className, ...props }) => { const classes = classNames('euiCollapsibleNavItem', className); + const styles = useEuiMemoizedStyles(euiCollapsibleNavTopItemStyles); const { isCollapsed, isPush } = useContext(EuiCollapsibleNavContext); @@ -294,6 +304,7 @@ export const EuiCollapsibleNavItem: FunctionComponent< ); diff --git a/packages/eui/src/components/combo_box/combo_box.stories.tsx b/packages/eui/src/components/combo_box/combo_box.stories.tsx index 50402b5270c..9d404ce9079 100644 --- a/packages/eui/src/components/combo_box/combo_box.stories.tsx +++ b/packages/eui/src/components/combo_box/combo_box.stories.tsx @@ -12,9 +12,11 @@ import { action } from '@storybook/addon-actions'; import { userEvent, waitFor, within, expect } from '@storybook/test'; import { LOKI_SELECTORS, lokiPlayDecorator } from '../../../.storybook/loki'; -import { EuiComboBox, EuiComboBoxProps } from './combo_box'; -import { EuiComboBoxOptionMatcher } from './types'; import { EuiCode } from '../code'; +import { EuiFlexItem } from '../flex'; + +import { EuiComboBoxOptionMatcher } from './types'; +import { EuiComboBox, EuiComboBoxProps } from './combo_box'; const toolTipProps = { toolTipContent: 'This is a tooltip!', @@ -85,7 +87,8 @@ export const WithTooltip: Story = { }, loki: { // popover and tooltip are rendered in a portal - chromeSelector: LOKI_SELECTORS.portal, + // LOKI_SELECTOR.portal currently doesn't work so we use body instead + chromeSelector: LOKI_SELECTORS.body, }, }, args: { @@ -160,6 +163,65 @@ export const CustomMatcher: Story = { }, }; +export const Groups: Story = { + parameters: { + controls: { + include: ['options'], + }, + loki: { + chromeSelector: LOKI_SELECTORS.body, + }, + }, + args: { + options: [ + { label: 'Group 1', isGroupLabelOption: true }, + ...[...options].splice(0, 3), + { + label: 'Group 2', + isGroupLabelOption: true, + prepend: '#prepend ', + append: ( + (append) + ), + }, + ...[...options].splice(3, options.length), + ], + autoFocus: true, + }, + render: (args) => , +}; + +export const NestedOptionsGroups: Story = { + parameters: { + controls: { + include: ['options'], + }, + loki: { + chromeSelector: LOKI_SELECTORS.body, + }, + }, + args: { + options: [ + { + label: 'Group 1', + isGroupLabelOption: true, + options: [...options].splice(0, 3), + }, + { + label: 'Group 2', + isGroupLabelOption: true, + prepend: '#prepend ', + append: ( + (append) + ), + options: [...options].splice(3, options.length), + }, + ], + autoFocus: true, + }, + render: (args) => , +}; + const StatefulComboBox = ({ singleSelection, onCreateOption, diff --git a/packages/eui/src/components/combo_box/combo_box_options_list/_combo_box_title.scss b/packages/eui/src/components/combo_box/combo_box_options_list/_combo_box_title.scss index 253c5e0ed62..5a0232e7bc4 100644 --- a/packages/eui/src/components/combo_box/combo_box_options_list/_combo_box_title.scss +++ b/packages/eui/src/components/combo_box/combo_box_options_list/_combo_box_title.scss @@ -3,9 +3,10 @@ * works. */ .euiComboBoxTitle { - font-size: $euiFontSizeXS; - padding: ($euiSizeXS + $euiSizeS - 1px) $euiSizeS $euiSizeXS; /* 1 */ + display: flex; width: 100%; + padding: ($euiSizeXS + $euiSizeS - 1px) $euiSizeS $euiSizeXS; /* 1 */ + font-size: $euiFontSizeXS; font-weight: $euiFontWeightBold; color: $euiColorFullShade; } diff --git a/packages/eui/src/components/combo_box/combo_box_options_list/combo_box_options_list.tsx b/packages/eui/src/components/combo_box/combo_box_options_list/combo_box_options_list.tsx index aef427c28ab..2174cc76055 100644 --- a/packages/eui/src/components/combo_box/combo_box_options_list/combo_box_options_list.tsx +++ b/packages/eui/src/components/combo_box/combo_box_options_list/combo_box_options_list.tsx @@ -171,7 +171,11 @@ export class EuiComboBoxOptionsList extends Component< if (isGroupLabelOption) { return (
- {label} + + {prepend} + {label} + {append} +
); } diff --git a/packages/eui/src/components/combo_box/matching_options.ts b/packages/eui/src/components/combo_box/matching_options.ts index 74344f6e1d9..5d80eab36de 100644 --- a/packages/eui/src/components/combo_box/matching_options.ts +++ b/packages/eui/src/components/combo_box/matching_options.ts @@ -169,6 +169,8 @@ export const getMatchingOptions = ({ key: option.key, label: option.label, isGroupLabelOption: true, + append: option.append, + prepend: option.prepend, }); // Add matching options for group // use concat over spreading to support large arrays - https://mathiasbynens.be/demo/javascript-argument-count diff --git a/packages/eui/src/components/date_picker/auto_refresh/auto_refresh.stories.tsx b/packages/eui/src/components/date_picker/auto_refresh/auto_refresh.stories.tsx new file mode 100644 index 00000000000..0da22bd1990 --- /dev/null +++ b/packages/eui/src/components/date_picker/auto_refresh/auto_refresh.stories.tsx @@ -0,0 +1,56 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { Meta, StoryObj } from '@storybook/react'; + +import { enableFunctionToggleControls } from '../../../../.storybook/utils'; +import { REFRESH_UNIT_OPTIONS } from '../types'; + +import { EuiAutoRefresh, EuiAutoRefreshProps } from './auto_refresh'; + +const meta: Meta = { + title: 'Forms/EuiAutoRefresh/EuiAutoRefresh', + component: EuiAutoRefresh, + parameters: { + loki: { + // TODO: uncomment once loki CLI is fixed for portal component stories + // chromeSelector: LOKI_SELECTORS.portal, + }, + }, + argTypes: { + intervalUnits: { + control: 'radio', + options: [undefined, ...REFRESH_UNIT_OPTIONS], + }, + }, + args: { + isPaused: true, + refreshInterval: 1000, + minInterval: 0, + readOnly: true, + isDisabled: false, + }, +}; +enableFunctionToggleControls(meta, ['onRefreshChange']); + +export default meta; +type Story = StoryObj; + +export const Playground: Story = { + // TODO: uncomment once loki CLI is fixed for portal component stories + // play: lokiPlayDecorator(async (context) => { + // const { bodyElement, step } = context; + // const canvas = within(bodyElement); + // await step('show popover on click of the input', async () => { + // await userEvent.click(canvas.getByLabelText('Auto refresh')); + // await waitFor(() => { + // expect(canvas.getByRole('dialog')).toBeVisible(); + // }); + // }); + // }), +}; diff --git a/packages/eui/src/components/date_picker/auto_refresh/auto_refresh_button.stories.tsx b/packages/eui/src/components/date_picker/auto_refresh/auto_refresh_button.stories.tsx new file mode 100644 index 00000000000..9839676fa94 --- /dev/null +++ b/packages/eui/src/components/date_picker/auto_refresh/auto_refresh_button.stories.tsx @@ -0,0 +1,71 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { Meta, StoryObj } from '@storybook/react'; + +import { + disableStorybookControls, + enableFunctionToggleControls, + moveStorybookControlsToCategory, +} from '../../../../.storybook/utils'; + +import { FLUSH_TYPES } from '../../button/button_empty/button_empty'; +import { REFRESH_UNIT_OPTIONS } from '../types'; + +import { + EuiAutoRefreshButton, + EuiAutoRefreshButtonProps, +} from './auto_refresh'; + +const meta: Meta = { + title: 'Forms/EuiAutoRefresh/EuiAutoRefreshButton', + component: EuiAutoRefreshButton, + argTypes: { + intervalUnits: { + control: 'radio', + options: [undefined, ...REFRESH_UNIT_OPTIONS], + }, + flush: { + control: 'radio', + options: [undefined, ...FLUSH_TYPES], + }, + }, + args: { + isPaused: true, + refreshInterval: 1000, + minInterval: 0, + shortHand: false, + size: 's', + color: 'text', + isDisabled: false, + isLoading: false, + }, +}; +enableFunctionToggleControls(meta, ['onRefreshChange']); +disableStorybookControls(meta, ['buttonRef']); +moveStorybookControlsToCategory( + meta, + [ + 'size', + 'color', + 'isDisabled', + 'isLoading', + 'target', + 'href', + 'rel', + 'flush', + 'buttonRef', + 'contentProps', + ], + 'EuiButtonEmpty props' +); + +export default meta; +type Story = StoryObj; + +export const Playground: Story = {}; diff --git a/packages/eui/src/components/date_picker/auto_refresh/refresh_interval.stories.tsx b/packages/eui/src/components/date_picker/auto_refresh/refresh_interval.stories.tsx new file mode 100644 index 00000000000..febb92b0944 --- /dev/null +++ b/packages/eui/src/components/date_picker/auto_refresh/refresh_interval.stories.tsx @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { Meta, StoryObj } from '@storybook/react'; + +import { enableFunctionToggleControls } from '../../../../.storybook/utils'; +import { REFRESH_UNIT_OPTIONS } from '../types'; + +import { + EuiRefreshInterval, + EuiRefreshIntervalProps, +} from './refresh_interval'; + +const meta: Meta = { + title: 'Forms/EuiRefreshInterval', + component: EuiRefreshInterval, + argTypes: { + intervalUnits: { + control: 'radio', + options: [undefined, ...REFRESH_UNIT_OPTIONS], + }, + }, + args: { + isPaused: true, + refreshInterval: 1000, + minInterval: 0, + }, +}; +enableFunctionToggleControls(meta, ['onRefreshChange']); + +export default meta; +type Story = StoryObj; + +export const Playground: Story = {}; diff --git a/packages/eui/src/components/date_picker/date_picker_range.stories.tsx b/packages/eui/src/components/date_picker/date_picker_range.stories.tsx new file mode 100644 index 00000000000..53b156a39d6 --- /dev/null +++ b/packages/eui/src/components/date_picker/date_picker_range.stories.tsx @@ -0,0 +1,85 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { useState } from 'react'; +import type { Meta, StoryObj } from '@storybook/react'; + +import { enableFunctionToggleControls } from '../../../.storybook/utils'; +import { + EuiDatePickerRange, + EuiDatePickerRangeProps, +} from './date_picker_range'; +import { EuiDatePicker } from './date_picker'; + +const meta: Meta = { + title: 'Forms/EuiDatePickerRange', + component: EuiDatePickerRange, + args: { + iconType: true, + shadow: true, + // set up for easier testing/QA + compressed: false, + inline: false, + fullWidth: false, + isCustom: false, + readOnly: false, + isLoading: false, + isInvalid: false, + disabled: false, + append: '', + prepend: '', + }, +}; +enableFunctionToggleControls(meta, ['onFocus', 'onBlur']); + +export default meta; +type Story = StoryObj; + +export const Playground: Story = { + parameters: { + controls: { + exclude: ['children'], // not actually used + }, + }, + args: { + startDateControl: , + endDateControl: , + }, + render: (args) => , +}; + +const StatefulPlayground = ({ + startDateControl, + endDateControl, + ...rest +}: EuiDatePickerRangeProps) => { + const [selectedStartDate, setSelectedStartDate] = useState< + moment.Moment | null | undefined + >(); + const [selectedEndDate, setSelectedEndDate] = useState< + moment.Moment | null | undefined + >(); + + const startControl = React.cloneElement(startDateControl, { + selected: selectedStartDate, + onChange: setSelectedStartDate, + }); + + const endControl = React.cloneElement(endDateControl, { + selected: selectedEndDate, + onChange: setSelectedEndDate, + }); + + return ( + + ); +}; diff --git a/packages/eui/src/components/date_picker/super_date_picker/super_date_picker.stories.tsx b/packages/eui/src/components/date_picker/super_date_picker/super_date_picker.stories.tsx new file mode 100644 index 00000000000..be308e909ca --- /dev/null +++ b/packages/eui/src/components/date_picker/super_date_picker/super_date_picker.stories.tsx @@ -0,0 +1,104 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import type { Meta, StoryObj } from '@storybook/react'; + +import { enableFunctionToggleControls } from '../../../../.storybook/utils'; +import { EuiLink } from '../../link'; +import { ApplyTime, REFRESH_UNIT_OPTIONS } from '../types'; + +import { + EuiSuperDatePicker, + EuiSuperDatePickerProps, +} from './super_date_picker'; + +const meta: Meta = { + title: 'Forms/EuiSuperDatePicker/EuiSuperDatePicker', + component: EuiSuperDatePicker, + parameters: { + loki: { + // TODO: uncomment once loki CLI is fixed for portal component stories + // chromeSelector: LOKI_SELECTORS.portal, + }, + }, + argTypes: { + refreshIntervalUnits: { + control: 'radio', + options: [undefined, ...REFRESH_UNIT_OPTIONS], + }, + }, + args: { + dateFormat: 'MMM D, YYYY @ HH:mm:ss.SSS', + end: 'now', + isAutoRefreshOnly: false, + isDisabled: false, + isPaused: true, + recentlyUsedRanges: [], + refreshInterval: 1000, + showUpdateButton: true, + canRoundRelativeUnits: true, + start: 'now-15m', + timeFormat: 'HH:mm', + width: 'restricted', + // set up for easier testing/QA + compressed: false, + isLoading: false, + isQuickSelectOnly: false, + commonlyUsedRanges: [{ start: 'now/d', end: 'now/d', label: 'Today' }], + }, +}; +enableFunctionToggleControls(meta, ['onTimeChange']); + +export default meta; +type Story = StoryObj; + +export const Playground: Story = {}; +enableFunctionToggleControls(Playground, [ + 'onFocus', + 'onRefresh', + 'onRefreshChange', +]); + +export const CustomQuickSelectPanel: Story = { + parameters: { + controls: { + include: ['customQuickSelectPanels', 'onTimeChange'], + }, + }, + args: { + customQuickSelectPanels: [ + { + title: 'Custom quick select panel', + content: , + }, + ], + }, + // TODO: uncomment once loki CLI is fixed for portal component stories + // play: lokiPlayDecorator(async (context) => { + // const { bodyElement, step } = context; + // const canvas = within(bodyElement); + // await step('show popover on click of the quick select button', async () => { + // await userEvent.click(canvas.getByLabelText('Date quick select')); + // await waitFor(() => { + // expect(canvas.getByRole('dialog')).toBeVisible(); + // expect(canvas.getByText('Custom quick select panel')).toBeVisible(); + // }); + // }); + // }), +}; + +function CustomPanel({ applyTime }: { applyTime?: ApplyTime }) { + function applyMyCustomTime() { + applyTime!({ start: 'now-30d', end: 'now+7d' }); + } + + return ( + Entire dataset timerange + ); +} diff --git a/packages/eui/src/components/date_picker/super_date_picker/super_update_button.stories.tsx b/packages/eui/src/components/date_picker/super_date_picker/super_update_button.stories.tsx new file mode 100644 index 00000000000..9037596b687 --- /dev/null +++ b/packages/eui/src/components/date_picker/super_date_picker/super_update_button.stories.tsx @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { Meta, StoryObj } from '@storybook/react'; + +import { enableFunctionToggleControls } from '../../../../.storybook/utils'; + +import { EuiSuperUpdateButton } from './super_update_button'; + +type EuiSuperUpdateButtonProps = typeof EuiSuperUpdateButton; + +const meta: Meta = { + title: 'Forms/EuiSuperDatePicker/EuiSuperUpdateButton', + component: EuiSuperUpdateButton, + args: { + needsUpdate: false, + isLoading: false, + isDisabled: false, + showTooltip: false, + responsive: ['xs', 's'], + fill: true, + // set up for easier testing/QA + iconOnly: false, + toolTipProps: {}, + }, +}; +enableFunctionToggleControls(meta, ['onClick']); + +export default meta; +type Story = StoryObj; + +export const Playground: Story = {}; diff --git a/packages/eui/src/components/date_picker/types.ts b/packages/eui/src/components/date_picker/types.ts index ff9835d876a..54e2fbf2968 100644 --- a/packages/eui/src/components/date_picker/types.ts +++ b/packages/eui/src/components/date_picker/types.ts @@ -58,7 +58,8 @@ export interface RelativeOption { value: TimeUnitAllId; } -export type RefreshUnitsOptions = 's' | 'm' | 'h'; +export const REFRESH_UNIT_OPTIONS = ['s', 'm', 'h'] as const; +export type RefreshUnitsOptions = (typeof REFRESH_UNIT_OPTIONS)[number]; export type OnRefreshChangeProps = { isPaused: boolean; diff --git a/packages/eui/src/components/form/file_picker/file_picker.stories.tsx b/packages/eui/src/components/form/file_picker/file_picker.stories.tsx new file mode 100644 index 00000000000..cc0cf14e867 --- /dev/null +++ b/packages/eui/src/components/form/file_picker/file_picker.stories.tsx @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { Meta, StoryObj } from '@storybook/react'; + +import { enableFunctionToggleControls } from '../../../../.storybook/utils'; + +import { EuiFilePicker, EuiFilePickerProps } from './file_picker'; + +const meta: Meta = { + title: 'Forms/EuiFilePicker', + component: EuiFilePicker, + args: { + initialPromptText: 'Select or drag and drop a file', + compressed: false, + display: 'large', + // set up for easier testing/QA + disabled: false, + fullWidth: false, + isInvalid: false, + isLoading: false, + id: '', + name: '', + }, +}; +enableFunctionToggleControls(meta, ['onChange']); + +export default meta; +type Story = StoryObj; + +export const Playground: Story = {}; diff --git a/packages/eui/src/components/form/file_picker/file_picker.tsx b/packages/eui/src/components/form/file_picker/file_picker.tsx index 76c6d279425..f65127353b2 100644 --- a/packages/eui/src/components/form/file_picker/file_picker.tsx +++ b/packages/eui/src/components/form/file_picker/file_picker.tsx @@ -67,7 +67,7 @@ export interface EuiFilePickerProps export class EuiFilePicker extends Component { static contextType = FormContext; - static defaultProps = { + static defaultProps: Partial = { initialPromptText: ( = { + title: 'Forms/EuiDualRange', + component: EuiDualRange, + argTypes: { + append: { + control: 'radio', + options: [undefined, 'icon', 'text'], + mapping: { + icon: , + text: 'Appended', + undefined: undefined, + }, + if: { arg: 'showInput', eq: 'inputWithPopover' }, + }, + prepend: { + control: 'radio', + options: [undefined, 'icon', 'text'], + mapping: { + icon: , + text: 'Prepended', + undefined: undefined, + }, + if: { arg: 'showInput', eq: 'inputWithPopover' }, + }, + showInput: { + control: 'radio', + options: [true, false, 'inputWithPopover'], + }, + inputPopoverProps: { + if: { arg: 'showInput', eq: 'inputWithPopover' }, + }, + }, + args: { + min: 0, + max: 100, + step: 1, + compressed: false, + isLoading: false, + showLabels: false, + showInput: false, + showRange: true, + showTicks: false, + levels: [], + //set up for easier testin/QA + fullWidth: false, + isInvalid: false, + isDraggable: false, + // adding tickInterval value to prevent error about + // too many ticks when enabling showTicks + tickInterval: 10, + minInputProps: {}, + maxInputProps: {}, + inputPopoverProps: {}, + ticks: [], + }, +}; +moveStorybookControlsToCategory( + meta, + ['append', 'prepend', 'inputPopoverProps'], + 'Input with popover' +); + +export default meta; +type Story = StoryObj; + +export const Playground: Story = { + args: { + value: [25, 50], + }, + render: (args) => , +}; +enableFunctionToggleControls(Playground, ['onChange', 'onFocus', 'onBlur']); +moveStorybookControlsToCategory( + Playground, + [ + 'showInput', + 'append', + 'prepend', + 'inputPopoverProps', + 'isInvalid', + 'isLoading', + 'minInputProps', + 'maxInputProps', + ], + 'Input' +); +moveStorybookControlsToCategory( + Playground, + ['showTicks', 'compressed', 'tickInterval', 'ticks'], + 'Ticks' +); + +export const Ticks: Story = { + parameters: { + controls: { + include: ['ticks', 'showTicks', 'showRange', 'max', 'min', 'value'], + }, + }, + args: { + value: [25, 50], + showTicks: true, + ticks: [ + { label: '0 kilobytes', value: 0 }, + { label: '50 kilobytes', value: 50 }, + { label: '100 kilobytes', value: 100 }, + ], + }, + render: (args) => , +}; + +export const Input: Story = { + parameters: { + controls: { + include: [ + 'showInput', + 'append', + 'prepend', + 'inputPopoverProps', + 'isInvalid', + 'isLoading', + 'max', + 'min', + 'value', + 'minInputProps', + 'maxInputProps', + ], + }, + }, + args: { + value: [25, 50], + showInput: true, + }, + render: (args) => , +}; + +export const InputWithPopover: Story = { + parameters: { + controls: { + include: [ + 'showInput', + 'append', + 'prepend', + 'inputPopoverProps', + 'isInvalid', + 'isLoading', + 'max', + 'min', + 'value', + 'minInputProps', + 'maxInputProps', + ], + }, + }, + args: { + value: [25, 50], + showInput: 'inputWithPopover', + }, + render: (args) => , +}; + +export const Levels: Story = { + parameters: { + controls: { + include: ['levels', 'max', 'min', 'value', 'showLabels'], + }, + }, + args: { + value: [25, 50], + levels: [ + { min: 0, max: 20, color: 'danger' }, + { min: 20, max: 100, color: 'success' }, + ], + showLabels: true, + }, + render: (args) => , +}; + +const StatefulPlayground = ({ + value, + onChange, + ...rest +}: EuiDualRangeProps) => { + const [values, setValues] = useState(value); + + useEffect(() => { + if (value) { + setValues(value); + } + }, [value]); + + const handelOnChange = ( + values: EuiDualRangeProps['value'], + isValid: boolean, + e?: _DualRangeChangeEvent + ) => { + setValues(values); + onChange?.(values, isValid, e); + }; + + return ; +}; diff --git a/packages/eui/src/components/form/range/range.stories.tsx b/packages/eui/src/components/form/range/range.stories.tsx new file mode 100644 index 00000000000..1885e0afef0 --- /dev/null +++ b/packages/eui/src/components/form/range/range.stories.tsx @@ -0,0 +1,222 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { useEffect, useState } from 'react'; +import type { Meta, StoryObj } from '@storybook/react'; + +import { + enableFunctionToggleControls, + moveStorybookControlsToCategory, +} from '../../../../.storybook/utils'; +import { EuiIcon } from '../../icon'; +import { _SingleRangeChangeEvent, EuiRangeProps } from './types'; +import { EuiRange } from './range'; + +const meta: Meta = { + title: 'Forms/EuiRange', + component: EuiRange, + argTypes: { + append: { + control: 'radio', + options: [undefined, 'icon', 'text'], + mapping: { + icon: , + text: 'Appended', + undefined: undefined, + }, + }, + prepend: { + control: 'radio', + options: [undefined, 'icon', 'text'], + mapping: { + icon: , + text: 'Prepended', + undefined: undefined, + }, + }, + valueAppend: { + control: 'radio', + options: [undefined, 'icon', 'text'], + mapping: { + icon: ( + <> + {' '} + + + ), + text: ' Appended', + undefined: undefined, + }, + }, + valuePrepend: { + control: 'radio', + options: [undefined, 'icon', 'text'], + mapping: { + icon: ( + <> + {' '} + + ), + text: 'Prepended ', + undefined: undefined, + }, + }, + }, + args: { + min: 0, + max: 100, + step: 1, + compressed: false, + isLoading: false, + showLabels: false, + showInput: false, + showRange: false, + showTicks: false, + showValue: false, + levels: [], + // set up for easier testing/QA + id: '', + name: '', + isInvalid: false, + fullWidth: false, + inputPopoverProps: {}, + // adding tickInterval value to prevent error about + // too many ticks when enabling showTicks + tickInterval: 10, + ticks: [], + }, +}; + +export default meta; +type Story = StoryObj; + +export const Playground: Story = { + args: { + value: 50, + }, + render: (args) => , +}; +enableFunctionToggleControls(Playground, ['onChange', 'onFocus', 'onBlur']); +moveStorybookControlsToCategory( + Playground, + [ + 'showInput', + 'append', + 'prepend', + 'inputPopoverProps', + 'isInvalid', + 'isLoading', + ], + 'Input' +); +moveStorybookControlsToCategory( + Playground, + ['showTicks', 'compressed', 'tickInterval', 'ticks'], + 'Ticks' +); +moveStorybookControlsToCategory( + Playground, + ['showValue', 'valueAppend', 'valuePrepend'], + 'Value tooltip' +); + +export const ValueTooltip: Story = { + parameters: { + controls: { + include: [ + 'showValue', + 'valueAppend', + 'valuePrepend', + 'max', + 'min', + 'value', + 'showRange', + ], + }, + }, + args: { + value: 50, + showValue: true, + }, + render: (args) => , +}; + +export const Ticks: Story = { + parameters: { + controls: { + include: ['ticks', 'showTicks', 'showRange', 'max', 'min', 'value'], + }, + }, + args: { + value: 50, + showTicks: true, + ticks: [ + { label: '0 kilobytes', value: 0 }, + { label: '50 kilobytes', value: 50 }, + { label: '100 kilobytes', value: 100 }, + ], + }, + render: (args) => , +}; + +export const Input: Story = { + parameters: { + controls: { + include: [ + 'showInput', + 'append', + 'prepend', + 'inputPopoverProps', + 'isInvalid', + 'isLoading', + 'max', + 'min', + 'value', + ], + }, + }, + args: { + value: 50, + showInput: true, + }, + render: (args) => , +}; + +export const Levels: Story = { + parameters: { + controls: { + include: ['levels', 'max', 'min', 'value', 'showLabels'], + }, + }, + args: { + value: 50, + levels: [ + { min: 0, max: 20, color: 'danger' }, + { min: 20, max: 100, color: 'success' }, + ], + showLabels: true, + }, + render: (args) => , +}; + +const StatefulPlayground = ({ value, onChange, ...rest }: EuiRangeProps) => { + const [_value, setValue] = useState(value); + + useEffect(() => { + if (value) { + setValue(value); + } + }, [value]); + + const handelOnChange = (e: _SingleRangeChangeEvent, isValid: boolean) => { + setValue(e.currentTarget.value); + onChange?.(e, isValid); + }; + + return ; +}; diff --git a/packages/eui/src/components/form/select/select.stories.tsx b/packages/eui/src/components/form/select/select.stories.tsx new file mode 100644 index 00000000000..8fd4011123d --- /dev/null +++ b/packages/eui/src/components/form/select/select.stories.tsx @@ -0,0 +1,76 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import type { Meta, StoryObj } from '@storybook/react'; + +import { + disableStorybookControls, + enableFunctionToggleControls, +} from '../../../../.storybook/utils'; +import { EuiIcon } from '../../icon'; + +import { EuiSelect, EuiSelectProps } from './select'; + +const meta: Meta = { + title: 'Forms/EuiSelect', + component: EuiSelect, + argTypes: { + append: { + control: 'radio', + options: [undefined, 'icon', 'text'], + mapping: { + icon: , + text: 'Appended', + undefined: undefined, + }, + }, + prepend: { + control: 'radio', + options: [undefined, 'icon', 'text'], + mapping: { + icon: , + text: 'Prepended', + undefined: undefined, + }, + }, + }, + args: { + fullWidth: false, + isLoading: false, + hasNoInitialSelection: false, + compressed: false, + // set up for easier testing/QA + isInvalid: false, + disabled: false, + id: '', + name: '', + }, +}; +// adding onChange for visibility +enableFunctionToggleControls(meta, ['onChange', 'onMouseUp']); +disableStorybookControls(meta, ['inputRef']); + +export default meta; +type Story = StoryObj; + +export const Playground: Story = { + parameters: { + controls: { + exclude: ['onMouseUp'], + }, + }, + args: { + defaultValue: 'option-2', + options: [ + { value: 'option-1', text: 'Option 1' }, + { value: 'option-2', text: 'Option 2' }, + { value: 'option-3', text: 'Option 3' }, + ], + }, +}; diff --git a/packages/eui/src/components/form/super_select/super_select.stories.tsx b/packages/eui/src/components/form/super_select/super_select.stories.tsx new file mode 100644 index 00000000000..29619c6a715 --- /dev/null +++ b/packages/eui/src/components/form/super_select/super_select.stories.tsx @@ -0,0 +1,124 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { useEffect, useState } from 'react'; +import type { Meta, StoryObj } from '@storybook/react'; + +import { + disableStorybookControls, + enableFunctionToggleControls, +} from '../../../../.storybook/utils'; +import { EuiIcon } from '../../icon'; +import { EuiText } from '../../text'; + +import { EuiSuperSelect, EuiSuperSelectProps } from './super_select'; + +const meta: Meta = { + title: 'Forms/EuiSuperSelect', + component: EuiSuperSelect, + argTypes: { + append: { + control: 'radio', + options: [undefined, 'icon', 'text'], + mapping: { + icon: , + text: 'Appended', + undefined: undefined, + }, + }, + prepend: { + control: 'radio', + options: [undefined, 'icon', 'text'], + mapping: { + icon: , + text: 'Prepended', + undefined: undefined, + }, + }, + placeholder: { control: 'text' }, + valueOfSelected: { control: 'text' }, + }, + args: { + hasDividers: false, + fullWidth: false, + compressed: false, + isInvalid: false, + isLoading: false, + itemLayoutAlign: 'center', + // set up for easier testing/QA + name: '', + placeholder: '', + isOpen: false, + readOnly: false, + popoverProps: {}, + }, +}; +enableFunctionToggleControls(meta, ['onChange', 'onBlur', 'onFocus']); +disableStorybookControls(meta, ['buttonRef']); + +export default meta; +type Story = StoryObj; + +export const Playground: Story = { + args: { + valueOfSelected: 'option-1', + options: [ + { + value: 'option-1', + inputDisplay: 'Option 1', + dropdownDisplay: ( + <> + Option One + +

Has a short description giving more detail to the option.

+
+ + ), + }, + { + value: 'option-2', + inputDisplay: 'Option 2', + dropdownDisplay: 'Option Two', + }, + { + value: 'option-3', + inputDisplay: 'Option 3', + dropdownDisplay: 'Option Three', + }, + ], + }, + render: (args) => , +}; + +const StatefulPlayground = ({ + valueOfSelected, + onChange, + ...rest +}: EuiSuperSelectProps) => { + const [selected, setSelected] = useState(valueOfSelected ?? ''); + + useEffect(() => { + if (valueOfSelected) { + setSelected(valueOfSelected); + } + }, [valueOfSelected]); + + const handleOnChange = (value: string) => { + setSelected(value); + + onChange?.(value); + }; + + return ( + + ); +}; diff --git a/packages/eui/src/components/form/validatable_control/validatable_control.stories.tsx b/packages/eui/src/components/form/validatable_control/validatable_control.stories.tsx new file mode 100644 index 00000000000..87626c82b65 --- /dev/null +++ b/packages/eui/src/components/form/validatable_control/validatable_control.stories.tsx @@ -0,0 +1,65 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import type { Meta, StoryObj } from '@storybook/react'; + +import { hideStorybookControls } from '../../../../.storybook/utils'; +import { EuiCode } from '../../code'; +import { EuiSpacer } from '../../spacer'; + +import { EuiValidatableControl } from './validatable_control'; + +type EuiValidatableControlProps = typeof EuiValidatableControl; + +const meta: Meta = { + title: 'Forms/EuiValidatableControl', + component: EuiValidatableControl, + parameters: { + loki: { + // there are no visual features for this component, + // it only adds attributes in the DOM + skip: true, + }, + }, + decorators: [ + (Story, { args }) => ( + <> +

+ Inspect the DOM to see that the input will be enhanced with{' '} + aria-invalid="true" when{' '} + isInValid=true +

+ + + + ), + ], + argTypes: { + children: { + type: { + // @ts-ignore - name is required; overwrite type to match props type + name: 'ReactElement', + required: true, + }, + }, + }, + args: { + isInvalid: false, + }, +}; +hideStorybookControls(meta, ['aria-label']); + +export default meta; +type Story = StoryObj; + +export const Playground: Story = { + args: { + children: , + }, +}; diff --git a/packages/eui/src/components/search_bar/filters/field_value_selection_filter.spec.tsx b/packages/eui/src/components/search_bar/filters/field_value_selection_filter.spec.tsx index 112d68ce846..0189dd6e18c 100644 --- a/packages/eui/src/components/search_bar/filters/field_value_selection_filter.spec.tsx +++ b/packages/eui/src/components/search_bar/filters/field_value_selection_filter.spec.tsx @@ -225,6 +225,119 @@ describe('FieldValueSelectionFilter', () => { }); }); + describe('auto-close testing', () => { + const FieldValueSelectionFilterWithState = ({ + autoClose, + multiSelect, + }: { + autoClose: undefined | boolean; + multiSelect: 'or' | boolean; + }) => { + const [query, setQuery] = useState(Query.parse('')); + const onChange = (newQuery: Query) => setQuery(newQuery); + + const props: FieldValueSelectionFilterProps = { + ...requiredProps, + index: 0, + onChange, + query, + config: { + type: 'field_value_selection', + field: 'tag', + name: 'Tag', + multiSelect, + autoClose, + options: staticOptions, + }, + }; + + return ; + }; + const selectFilter = () => { + // Open popover + cy.get('button').click(); + cy.get('.euiPopover__panel').should('exist'); + + // Select filter option + cy.get('li[role="option"][title="feature"]') + .should('have.attr', 'aria-checked', 'false') + .click(); + }; + + describe('undefined', () => { + it('multi select: does not close popover', () => { + cy.mount( + + ); + selectFilter(); + cy.get('.euiPopover__panel').should('exist'); + }); + + it('single select: closes popover', () => { + cy.mount( + + ); + selectFilter(); + cy.get('.euiPopover__panel').should('not.exist'); + }); + }); + + describe('false', () => { + it('multi select: never closes popover', () => { + cy.mount( + + ); + selectFilter(); + cy.get('.euiPopover__panel').should('exist'); + }); + + it('single select: never closes popover', () => { + cy.mount( + + ); + selectFilter(); + cy.get('.euiPopover__panel').should('exist'); + }); + }); + + describe('true', () => { + it('multi select: always closes popover', () => { + cy.mount( + + ); + selectFilter(); + cy.get('.euiPopover__panel').should('not.exist'); + }); + + it('single select: always closes popover', () => { + cy.mount( + + ); + + selectFilter(); + cy.get('.euiPopover__panel').should('not.exist'); + }); + }); + }); + it('has inactive filters, field is global', () => { const props: FieldValueSelectionFilterProps = { ...requiredProps, diff --git a/packages/eui/src/components/search_bar/filters/field_value_selection_filter.tsx b/packages/eui/src/components/search_bar/filters/field_value_selection_filter.tsx index af27e8b0225..9ceef0f9a6b 100644 --- a/packages/eui/src/components/search_bar/filters/field_value_selection_filter.tsx +++ b/packages/eui/src/components/search_bar/filters/field_value_selection_filter.tsx @@ -252,35 +252,37 @@ export class FieldValueSelectionFilter extends Component< ) { const multiSelect = this.resolveMultiSelect(); const { - config: { autoClose = true, operator = Operator.EQ }, + config: { autoClose, operator = Operator.EQ }, } = this.props; - // we're closing popover only if the user can only select one item... if the - // user can select more, we'll leave it open so she can continue selecting - - if (!multiSelect && autoClose) { + // If the consumer explicitly sets `autoClose`, always defer to that. + // Otherwise, default to auto-closing for single selections and leaving the + // popover open for multi-select (so users can continue selecting options) + const shouldClosePopover = autoClose ?? !multiSelect; + if (shouldClosePopover) { this.closePopover(); + } + + if (!multiSelect) { const query = checked ? this.props.query .removeSimpleFieldClauses(field) .addSimpleFieldValue(field, value, true, operator) : this.props.query.removeSimpleFieldClauses(field); + this.props.onChange(query); + } else if (multiSelect === 'or') { + const query = checked + ? this.props.query.addOrFieldValue(field, value, true, operator) + : this.props.query.removeOrFieldValue(field, value); + this.props.onChange(query); } else { - if (multiSelect === 'or') { - const query = checked - ? this.props.query.addOrFieldValue(field, value, true, operator) - : this.props.query.removeOrFieldValue(field, value); - - this.props.onChange(query); - } else { - const query = checked - ? this.props.query.addSimpleFieldValue(field, value, true, operator) - : this.props.query.removeSimpleFieldValue(field, value); - - this.props.onChange(query); - } + const query = checked + ? this.props.query.addSimpleFieldValue(field, value, true, operator) + : this.props.query.removeSimpleFieldValue(field, value); + + this.props.onChange(query); } } diff --git a/packages/eui/src/components/selectable/selectable_list/selectable_list.stories.tsx b/packages/eui/src/components/selectable/selectable_list/selectable_list.stories.tsx index 76da896605b..e0d3b635bf2 100644 --- a/packages/eui/src/components/selectable/selectable_list/selectable_list.stories.tsx +++ b/packages/eui/src/components/selectable/selectable_list/selectable_list.stories.tsx @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import React from 'react'; import type { Meta, StoryObj } from '@storybook/react'; import { action } from '@storybook/addon-actions'; @@ -13,7 +14,10 @@ import { enableFunctionToggleControls, moveStorybookControlsToCategory, } from '../../../../.storybook/utils'; +import { EuiFlexItem } from '../../flex'; +import { EuiIcon } from '../../icon'; import { EuiSelectableOption } from '../selectable_option'; + import { EuiSelectableList, EuiSelectableListProps } from './selectable_list'; const options: EuiSelectableOption[] = [ @@ -107,3 +111,39 @@ export const Playground: Story = { }, }, }; + +export const Groups: Story = { + parameters: { + controls: { + include: ['options'], + }, + }, + args: { + options: [ + { label: 'Group 1', isGroupLabel: true }, + ...[...options].splice(0, 4), + { + label: 'Group 2', + isGroupLabel: true, + prepend: ( + ({ marginRight: euiTheme.size.s })} + /> + ), + append: ( + (append) + ), + }, + ...[...options].splice(4, options.length), + ], + activeOptionIndex: 0, + makeOptionId: (index) => `selectable_list_item-${index}`, + // ensuring that onOptionClick triggers an action as it's + // only called through setActiveOptionIndex callback + setActiveOptionIndex: (index, callback) => { + callback?.(); + action('setActiveOptionIndex')(index); + }, + }, +}; diff --git a/packages/eui/src/components/table/table_header_cell_checkbox.tsx b/packages/eui/src/components/table/table_header_cell_checkbox.tsx index 85ae9dcc6d4..4bec76aa064 100644 --- a/packages/eui/src/components/table/table_header_cell_checkbox.tsx +++ b/packages/eui/src/components/table/table_header_cell_checkbox.tsx @@ -33,8 +33,6 @@ export const EuiTableHeaderCellCheckbox: FunctionComponent< const styles = useEuiMemoizedStyles(euiTableCellCheckboxStyles); const inlineStyles = resolveWidthAsStyle(style, width); - console.log('inlineStyles', inlineStyles); - return ( _THEME.lineAnnotation` theme from `@elastic/charts` - */ - lineAnnotation: { - line: { - strokeWidth: 1, - stroke: colors.darkShade, - opacity: 1, - }, - }, - /** - * @deprecated use `_THEME` theme from `@elastic/charts` - */ - theme: { - background: { - color: colors.emptyShade, - }, - chartMargins: { - left: 0, - right: 0, - top: 0, - bottom: 0, - }, - lineSeriesStyle: { - line: { - strokeWidth: 2, - }, - point: { - fill: colors.emptyShade, - strokeWidth: 2, - radius: 3, - }, - }, - areaSeriesStyle: { - area: { - opacity: 0.3, - }, - line: { - strokeWidth: 2, - }, - point: { - visible: false, - fill: colors.emptyShade, - strokeWidth: 2, - radius: 3, - }, - }, - barSeriesStyle: { - displayValue: { - fontSize: 10, - fontFamily: fontFamily, - fill: { - textBorder: 0, - }, - alignment: { - horizontal: 'center', - vertical: 'middle', - }, - }, - }, - scales: { - barsPadding: 0.25, - histogramPadding: 0.05, - }, - axes: { - axisTitle: { - fontSize: 12, - fontFamily: fontFamily, - fill: colors.text, - padding: { - inner: 10, - outer: 0, - }, - }, - axisLine: { - stroke: colors.chartLines, - }, - tickLabel: { - fontSize: 10, - fontFamily: fontFamily, - fill: colors.subduedText, - padding: { - outer: 8, - inner: 10, - }, - }, - tickLine: { - visible: false, - stroke: colors.chartLines, - strokeWidth: 1, - }, - gridLine: { - horizontal: { - visible: true, - stroke: colors.chartLines, - strokeWidth: 1, - opacity: 1, - dash: [0, 0], - }, - vertical: { - visible: true, - stroke: colors.chartLines, - strokeWidth: 1, - opacity: 1, - dash: [4, 4], - }, - }, - }, - colors: { - vizColors: euiPaletteColorBlind({ sortBy: 'natural' }), - defaultVizColor: DEFAULT_VISUALIZATION_COLOR, - }, - crosshair: { - band: { - fill: colors.chartBand, - }, - line: { - stroke: colors.darkShade, - strokeWidth: 1, - dash: [4, 4], - }, - crossLine: { - stroke: colors.darkShade, - strokeWidth: 1, - dash: [4, 4], - }, - }, - goal: { - tickLabel: { - fontFamily: fontFamily, - fill: colors.subduedText, - }, - majorLabel: { - fontFamily: fontFamily, - fill: colors.text, - }, - minorLabel: { - fontFamily: fontFamily, - fill: colors.subduedText, - }, - majorCenterLabel: { - fontFamily: fontFamily, - fill: colors.text, - }, - minorCenterLabel: { - fontFamily: fontFamily, - fill: colors.subduedText, - }, - targetLine: { - stroke: colors.darkestShade, - }, - tickLine: { - stroke: colors.mediumShade, - }, - progressLine: { - stroke: colors.darkestShade, - }, - }, - partition: { - fontFamily: fontFamily, - minFontSize: 8, - maxFontSize: 16, - fillLabel: { - valueFont: { - fontWeight: 700, - }, - }, - linkLabel: { - maxCount: 5, - fontSize: 11, - textColor: colors.text, - }, - outerSizeRatio: 1, - circlePadding: 4, - sectorLineStroke: colors.emptyShade, - sectorLineWidth: 1.5, - }, - }, - }; -} - -// Build a static output of the EUI Amsterdam theme colors -// TODO: At some point, should Elastic Charts be able to inherit or create a theme dynamically from our theme provider? -const KEY = '_EUI_CHART_THEME_AMSTERDAM'; -const builtTheme = buildTheme({}, KEY) as typeof EuiThemeAmsterdam; -const lightColors = getComputed(EuiThemeAmsterdam, builtTheme, 'LIGHT').colors; -const darkColors = getComputed(EuiThemeAmsterdam, builtTheme, 'DARK').colors; - -/** - * @deprecated use `LIGHT_THEME` & `LIGHT_THEME.lineAnnotation` - */ -export const EUI_CHARTS_THEME_LIGHT: EuiChartThemeType = createTheme({ - ...lightColors, - chartLines: shade(lightColors.lightestShade, 0.03), - chartBand: lightColors.lightestShade, -}); - -/** - * @deprecated use `DARK_THEME` & `DARK_THEME.lineAnnotation` theme from `@elastic/charts` - */ -export const EUI_CHARTS_THEME_DARK: EuiChartThemeType = createTheme({ - ...darkColors, - chartLines: darkColors.lightShade, - chartBand: tint(darkColors.lightestShade, 0.025), -}); - -/** - * @deprecated use `useSparklineOverrides` hook from the kibana `charts` plugin `theme` service - */ -export const EUI_SPARKLINE_THEME_PARTIAL: PartialTheme = { - lineSeriesStyle: { - point: { - visible: false, - strokeWidth: 1, - radius: 1, - }, - }, - areaSeriesStyle: { - point: { - visible: false, - strokeWidth: 1, - radius: 1, - }, - }, -}; diff --git a/packages/eui/src/themes/charts/webpack.config.js b/packages/eui/src/themes/charts/webpack.config.js deleted file mode 100644 index bd43d8e1e26..00000000000 --- a/packages/eui/src/themes/charts/webpack.config.js +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -/* eslint-disable @typescript-eslint/no-var-requires */ - -const path = require('path'); -const webpack = require('webpack'); -const CircularDependencyPlugin = require('circular-dependency-plugin'); -const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin'); - -const plugins = [ - new webpack.NoEmitOnErrorsPlugin(), - new CircularDependencyPlugin({ - exclude: /node_modules/, - failOnError: true, - }), - // run TypeScript during webpack build - new ForkTsCheckerWebpackPlugin({ - typescript: { - configFile: path.resolve(__dirname, '..', '..', '..', 'tsconfig.json'), - }, - async: false, // makes errors more visible, but potentially less performant - }), -]; - -module.exports = { - mode: 'development', - - devtool: 'source-map', - - entry: { - guide: './themes.ts', - }, - - context: __dirname, - - output: { - path: path.resolve(__dirname, '../../../dist'), - filename: 'eui_charts_theme.js', - library: { - type: 'commonjs', - }, - }, - - resolve: { - extensions: ['.ts', '.tsx', '.js', '.json'], - }, - - module: { - rules: [ - { - test: /\.(js|tsx?)$/, - loader: 'babel-loader', - exclude: /node_modules/, - }, - { - test: /\.scss$/, - use: ['style-loader', 'css-loader', 'postcss-loader', 'sass-loader'], - exclude: /node_modules/, - }, - ], - strictExportPresence: true, - }, - - plugins, -};