diff --git a/changelogs/CHANGELOG_2024.md b/changelogs/CHANGELOG_2024.md index 056d1786d5f..21c582b6089 100644 --- a/changelogs/CHANGELOG_2024.md +++ b/changelogs/CHANGELOG_2024.md @@ -1,3 +1,28 @@ +## [`v93.4.0`](https://github.com/elastic/eui/releases/v93.4.0) + +- Added the following properties to `EuiButtonGroup`'s `options` configs: `toolTipContent`, `toolTipProps`, and `title`. These new properties allow wrapping buttons in `EuiToolTips`, and additionally customizing or disabling the native browser `title` tooltip. ([#7461](https://github.com/elastic/eui/pull/7461)) +- Enhanced `EuiResizeObserver` and `useResizeObserver`'s performance to not trigger page reflows on resize event ([#7575](https://github.com/elastic/eui/pull/7575)) +- Updated `EuiSuperUpdateButton` to support custom button text via an optional `children` prop ([#7576](https://github.com/elastic/eui/pull/7576)) + +**Bug fixes** + +- Fixed `EuiFlyout` to not repeatedly remove/add a body class on resize ([#7462](https://github.com/elastic/eui/pull/7462)) +- Fixed `EuiToast` title text to wrap instead of overflowing out of the container ([#7568](https://github.com/elastic/eui/pull/7568)) +- Fixed a visual bug with `EuiHeaderBreadcrumbs` with popovers ([#7580](https://github.com/elastic/eui/pull/7580)) + +**Deprecations** + +- Deprecated `euiPalettePositive` and `euiPaletteNegative` in favour of a more culturally inclusive `euiPaletteGreen` and `euiPaletteRed` ([#7570](https://github.com/elastic/eui/pull/7570)) +- Deprecated all charts theme exports in favor of `@elastic/charts` exports: ([#7572](https://github.com/elastic/eui/pull/7572)) + - Deprecated `EUI_CHARTS_THEME_` in favor of `_THEME` from `@elastic/charts`. ([#7572](https://github.com/elastic/eui/pull/7572)) + - Deprecated `EUI_SPARKLINE_THEME_PARTIAL` in favor of `useSparklineOverrides` theme from the kibana `charts` plugin `theme` service. + +**Accessibility** + +- Updated `EuiModal` to set an `aria-modal` attribute and a default `dialog` role ([#7564](https://github.com/elastic/eui/pull/7564)) +- Updated `EuiConfirmModal` to set a default `alertdialog` role ([#7564](https://github.com/elastic/eui/pull/7564)) +- Fixed `EuiModal` and `EuiConfirmModal` to properly trap Safari+VoiceOver's virtual cursor ([#7564](https://github.com/elastic/eui/pull/7564)) + ## [`v93.3.0`](https://github.com/elastic/eui/releases/v93.3.0) - Added new `EuiDataGrid` new prop: `cellContext`, an optional object of additional props passed to the cell render function. ([#7374](https://github.com/elastic/eui/pull/7374)) diff --git a/changelogs/upcoming/7461.md b/changelogs/upcoming/7461.md deleted file mode 100644 index 826fa6edcbb..00000000000 --- a/changelogs/upcoming/7461.md +++ /dev/null @@ -1 +0,0 @@ -- Added the following properties to `EuiButtonGroup`'s `options` configs: `toolTipContent`, `toolTipProps`, and `title`. These new properties allow wrapping buttons in `EuiToolTips`, and additionally customizing or disabling the native browser `title` tooltip. diff --git a/changelogs/upcoming/7462.md b/changelogs/upcoming/7462.md deleted file mode 100644 index ef12d56bc8b..00000000000 --- a/changelogs/upcoming/7462.md +++ /dev/null @@ -1,3 +0,0 @@ -**Bug fixes** - -- Fixed `EuiFlyout` to not repeatedly remove/add a body class on resize diff --git a/changelogs/upcoming/7564.md b/changelogs/upcoming/7564.md deleted file mode 100644 index c65b672ceee..00000000000 --- a/changelogs/upcoming/7564.md +++ /dev/null @@ -1,5 +0,0 @@ -**Accessibility** - -- Updated `EuiModal` to set an `aria-modal` attribute and a default `dialog` role -- Updated `EuiConfirmModal` to set a default `alertdialog` role -- Fixed `EuiModal` and `EuiConfirmModal` to properly trap Safari+VoiceOver's virtual cursor diff --git a/changelogs/upcoming/7568.md b/changelogs/upcoming/7568.md deleted file mode 100644 index a2f8bbb4a26..00000000000 --- a/changelogs/upcoming/7568.md +++ /dev/null @@ -1,3 +0,0 @@ -**Bug fixes** - -- Fixed `EuiToast` title text to wrap instead of overflowing out of the container diff --git a/changelogs/upcoming/7570.md b/changelogs/upcoming/7570.md deleted file mode 100644 index 7ad933a7bfa..00000000000 --- a/changelogs/upcoming/7570.md +++ /dev/null @@ -1,3 +0,0 @@ -**Deprecations** - -- Deprecated `euiPalettePositive` and `euiPaletteNegative` in favour of a more culturally inclusive `euiPaletteGreen` and `euiPaletteRed` \ No newline at end of file diff --git a/changelogs/upcoming/7572.md b/changelogs/upcoming/7572.md deleted file mode 100644 index cdb9a9f418d..00000000000 --- a/changelogs/upcoming/7572.md +++ /dev/null @@ -1,5 +0,0 @@ -**Deprecations** - -- Deprecated all charts theme exports in favor of `@elastic/charts` exports: - - Deprecated `EUI_CHARTS_THEME_` in favor of `_THEME` from `@elastic/charts`. ([#7572](https://github.com/elastic/eui/pull/7572)) - - Deprecated `EUI_SPARKLINE_THEME_PARTIAL` in favor of `useSparklineOverrides` theme from the kibana `charts` plugin `theme` service. diff --git a/changelogs/upcoming/7575.md b/changelogs/upcoming/7575.md deleted file mode 100644 index f41064b874d..00000000000 --- a/changelogs/upcoming/7575.md +++ /dev/null @@ -1 +0,0 @@ -- Enhanced `EuiResizeObserver` and `useResizeObserver`'s performance to not trigger page reflows on resize event diff --git a/changelogs/upcoming/7590.md b/changelogs/upcoming/7590.md new file mode 100644 index 00000000000..cbd9929cf56 --- /dev/null +++ b/changelogs/upcoming/7590.md @@ -0,0 +1,3 @@ +**Dependency updates** + +- Updated `@types/refractor` to v3.4.0 diff --git a/changelogs/upcoming/7591.md b/changelogs/upcoming/7591.md new file mode 100644 index 00000000000..3fe73595116 --- /dev/null +++ b/changelogs/upcoming/7591.md @@ -0,0 +1,3 @@ +**Dependency updates** + +- Updated `@types/lodash` to v4.14.202 diff --git a/changelogs/upcoming/7592.md b/changelogs/upcoming/7592.md new file mode 100644 index 00000000000..af299e0af00 --- /dev/null +++ b/changelogs/upcoming/7592.md @@ -0,0 +1,3 @@ +**Dependency updates** + +- Removed `@types/resize-observer-browser` dependency. `ResizeObserver` types should already be baked in to Typescript as of 4.2+ diff --git a/i18ntokens.json b/i18ntokens.json index cbcb2be4d8b..e17fd14f4f5 100644 --- a/i18ntokens.json +++ b/i18ntokens.json @@ -275,17 +275,17 @@ "highlighting": "string", "loc": { "start": { - "line": 157, - "column": 27, - "index": 4839 + "line": 167, + "column": 29, + "index": 4882 }, "end": { - "line": 160, - "column": 3, - "index": 4949 + "line": 173, + "column": 5, + "index": 5215 } }, - "filepath": "src/components/breadcrumbs/breadcrumb.tsx" + "filepath": "src/components/breadcrumbs/_breadcrumb_content.tsx" }, { "token": "euiBreadcrumb.collapsedBadge.ariaLabel", @@ -293,14 +293,14 @@ "highlighting": "string", "loc": { "start": { - "line": 238, + "line": 56, "column": 20, - "index": 7303 + "index": 1691 }, "end": { - "line": 241, + "line": 59, "column": 3, - "index": 7396 + "index": 1784 } }, "filepath": "src/components/breadcrumbs/breadcrumb.tsx" @@ -311,14 +311,14 @@ "highlighting": "string", "loc": { "start": { - "line": 96, + "line": 42, "column": 20, - "index": 2734 + "index": 1274 }, "end": { - "line": 96, + "line": 42, "column": 77, - "index": 2791 + "index": 1331 } }, "filepath": "src/components/breadcrumbs/breadcrumbs.tsx" @@ -923,14 +923,14 @@ "highlighting": "string", "loc": { "start": { - "line": 41, - "column": 4, - "index": 1369 + "line": 44, + "column": 6, + "index": 1456 }, "end": { - "line": 45, - "column": 5, - "index": 1524 + "line": 48, + "column": 7, + "index": 1619 } }, "filepath": "src/components/datagrid/body/cell/data_grid_cell_actions.tsx" @@ -941,14 +941,14 @@ "highlighting": "string", "loc": { "start": { - "line": 121, - "column": 10, - "index": 3572 - }, - "end": { - "line": 129, + "line": 130, "column": 12, "index": 3863 + }, + "end": { + "line": 138, + "column": 14, + "index": 4170 } }, "filepath": "src/components/datagrid/body/cell/data_grid_cell.tsx" @@ -959,14 +959,14 @@ "highlighting": "string", "loc": { "start": { - "line": 133, - "column": 14, - "index": 3942 + "line": 142, + "column": 16, + "index": 4261 }, "end": { - "line": 136, - "column": 16, - "index": 4095 + "line": 145, + "column": 18, + "index": 4420 } }, "filepath": "src/components/datagrid/body/cell/data_grid_cell.tsx" @@ -1085,14 +1085,14 @@ "highlighting": "string", "loc": { "start": { - "line": 197, - "column": 12, - "index": 6252 + "line": 225, + "column": 16, + "index": 7475 }, "end": { - "line": 200, - "column": 14, - "index": 6393 + "line": 228, + "column": 18, + "index": 7628 } }, "filepath": "src/components/datagrid/body/header/data_grid_header_cell.tsx" @@ -1103,14 +1103,14 @@ "highlighting": "string", "loc": { "start": { - "line": 264, + "line": 296, "column": 16, - "index": 8238 + "index": 9645 }, "end": { - "line": 268, + "line": 300, "column": 18, - "index": 8412 + "index": 9819 } }, "filepath": "src/components/datagrid/body/header/data_grid_header_cell.tsx" @@ -1121,14 +1121,14 @@ "highlighting": "string", "loc": { "start": { - "line": 272, + "line": 304, "column": 16, - "index": 8490 + "index": 9897 }, "end": { - "line": 276, + "line": 308, "column": 18, - "index": 8666 + "index": 10073 } }, "filepath": "src/components/datagrid/body/header/data_grid_header_cell.tsx" @@ -1139,14 +1139,14 @@ "highlighting": "string", "loc": { "start": { - "line": 282, + "line": 314, "column": 16, - "index": 8812 + "index": 10219 }, "end": { - "line": 287, + "line": 319, "column": 18, - "index": 9040 + "index": 10447 } }, "filepath": "src/components/datagrid/body/header/data_grid_header_cell.tsx" @@ -1157,14 +1157,14 @@ "highlighting": "string", "loc": { "start": { - "line": 291, + "line": 323, "column": 16, - "index": 9118 + "index": 10525 }, "end": { - "line": 296, + "line": 328, "column": 18, - "index": 9348 + "index": 10755 } }, "filepath": "src/components/datagrid/body/header/data_grid_header_cell.tsx" @@ -1175,14 +1175,14 @@ "highlighting": "string", "loc": { "start": { - "line": 302, + "line": 334, "column": 16, - "index": 9477 + "index": 10884 }, "end": { - "line": 307, + "line": 339, "column": 18, - "index": 9715 + "index": 11122 } }, "filepath": "src/components/datagrid/body/header/data_grid_header_cell.tsx" @@ -1193,14 +1193,14 @@ "highlighting": "string", "loc": { "start": { - "line": 311, + "line": 343, "column": 16, - "index": 9793 + "index": 11200 }, "end": { - "line": 316, + "line": 348, "column": 18, - "index": 10033 + "index": 11440 } }, "filepath": "src/components/datagrid/body/header/data_grid_header_cell.tsx" @@ -1211,14 +1211,14 @@ "highlighting": "string", "loc": { "start": { - "line": 381, + "line": 413, "column": 6, - "index": 11879 + "index": 13286 }, "end": { - "line": 384, + "line": 416, "column": 8, - "index": 12072 + "index": 13479 } }, "filepath": "src/components/datagrid/body/header/data_grid_header_cell.tsx" @@ -1247,14 +1247,14 @@ "highlighting": "string", "loc": { "start": { - "line": 123, - "column": 4, - "index": 3663 + "line": 155, + "column": 12, + "index": 4781 }, "end": { - "line": 123, - "column": 66, - "index": 3725 + "line": 155, + "column": 74, + "index": 4843 } }, "filepath": "src/components/datagrid/controls/column_selector.tsx" @@ -1265,14 +1265,14 @@ "highlighting": "string", "loc": { "start": { - "line": 165, + "line": 161, "column": 12, - "index": 4996 + "index": 4973 }, "end": { - "line": 171, + "line": 167, "column": 13, - "index": 5208 + "index": 5185 } }, "filepath": "src/components/datagrid/controls/column_selector.tsx" @@ -1283,14 +1283,14 @@ "highlighting": "string", "loc": { "start": { - "line": 165, + "line": 161, "column": 12, - "index": 4996 + "index": 4973 }, "end": { - "line": 171, + "line": 167, "column": 13, - "index": 5208 + "index": 5185 } }, "filepath": "src/components/datagrid/controls/column_selector.tsx" @@ -1301,14 +1301,14 @@ "highlighting": "string", "loc": { "start": { - "line": 285, + "line": 281, "column": 18, - "index": 10055 + "index": 10032 }, "end": { - "line": 288, + "line": 284, "column": 20, - "index": 10179 + "index": 10156 } }, "filepath": "src/components/datagrid/controls/column_selector.tsx" @@ -1319,14 +1319,14 @@ "highlighting": "string", "loc": { "start": { - "line": 298, + "line": 294, "column": 18, - "index": 10539 + "index": 10516 }, "end": { - "line": 301, + "line": 297, "column": 20, - "index": 10661 + "index": 10638 } }, "filepath": "src/components/datagrid/controls/column_selector.tsx" @@ -1339,12 +1339,12 @@ "start": { "line": 24, "column": 2, - "index": 1020 + "index": 1033 }, "end": { "line": 24, "column": 76, - "index": 1094 + "index": 1107 } }, "filepath": "src/components/datagrid/controls/column_sorting_draggable.tsx" @@ -1357,12 +1357,12 @@ "start": { "line": 27, "column": 2, - "index": 1138 + "index": 1151 }, "end": { "line": 27, "column": 77, - "index": 1213 + "index": 1226 } }, "filepath": "src/components/datagrid/controls/column_sorting_draggable.tsx" @@ -1375,12 +1375,12 @@ "start": { "line": 68, "column": 30, - "index": 2177 + "index": 2190 }, "end": { "line": 71, "column": 3, - "index": 2263 + "index": 2276 } }, "filepath": "src/components/datagrid/controls/column_sorting_draggable.tsx" @@ -1391,14 +1391,14 @@ "highlighting": "string", "loc": { "start": { - "line": 89, + "line": 109, "column": 14, - "index": 2684 + "index": 3319 }, "end": { - "line": 93, + "line": 113, "column": 15, - "index": 2873 + "index": 3508 } }, "filepath": "src/components/datagrid/controls/column_sorting_draggable.tsx" @@ -1409,14 +1409,14 @@ "highlighting": "string", "loc": { "start": { - "line": 105, + "line": 125, "column": 14, - "index": 3265 + "index": 3900 }, "end": { - "line": 109, + "line": 129, "column": 15, - "index": 3455 + "index": 4090 } }, "filepath": "src/components/datagrid/controls/column_sorting_draggable.tsx" @@ -1427,14 +1427,14 @@ "highlighting": "string", "loc": { "start": { - "line": 161, + "line": 174, "column": 14, - "index": 5360 + "index": 5666 }, "end": { - "line": 165, + "line": 178, "column": 15, - "index": 5546 + "index": 5852 } }, "filepath": "src/components/datagrid/controls/column_sorting_draggable.tsx" @@ -1445,68 +1445,68 @@ "highlighting": "string", "loc": { "start": { - "line": 61, - "column": 28, - "index": 2300 + "line": 62, + "column": 30, + "index": 1986 }, "end": { - "line": 64, - "column": 3, - "index": 2364 + "line": 65, + "column": 5, + "index": 2056 } }, "filepath": "src/components/datagrid/controls/column_sorting.tsx" }, { - "token": "euiColumnSorting.emptySorting", - "defString": "Currently no fields are sorted", + "token": "euiColumnSorting.sortFieldAriaLabel", + "defString": "Sort by: ", "highlighting": "string", "loc": { "start": { - "line": 168, - "column": 12, - "index": 5308 + "line": 66, + "column": 31, + "index": 2089 }, "end": { - "line": 171, - "column": 14, - "index": 5438 + "line": 69, + "column": 5, + "index": 2169 } }, "filepath": "src/components/datagrid/controls/column_sorting.tsx" }, { - "token": "euiColumnSorting.pickFields", - "defString": "Pick fields to sort by", + "token": "euiColumnSorting.emptySorting", + "defString": "Currently no fields are sorted", "highlighting": "string", "loc": { "start": { - "line": 201, - "column": 22, - "index": 6601 + "line": 215, + "column": 14, + "index": 6750 }, "end": { - "line": 204, - "column": 24, - "index": 6751 + "line": 218, + "column": 16, + "index": 6886 } }, "filepath": "src/components/datagrid/controls/column_sorting.tsx" }, { - "token": "euiColumnSorting.sortFieldAriaLabel", - "defString": "Sort by: ", + "token": "euiColumnSorting.pickFields", + "defString": "Pick fields to sort by", "highlighting": "string", "loc": { "start": { - "line": 208, - "column": 18, - "index": 6846 + "line": 248, + "column": 24, + "index": 8109 }, "end": { - "line": 211, - "column": 19, - "index": 6978 + "line": 251, + "column": 26, + "index": 8265 } }, "filepath": "src/components/datagrid/controls/column_sorting.tsx" @@ -1517,14 +1517,14 @@ "highlighting": "string", "loc": { "start": { - "line": 289, - "column": 18, - "index": 10731 + "line": 321, + "column": 20, + "index": 11473 }, "end": { - "line": 292, - "column": 20, - "index": 10858 + "line": 324, + "column": 22, + "index": 11606 } }, "filepath": "src/components/datagrid/controls/column_sorting.tsx" @@ -1591,12 +1591,12 @@ "start": { "line": 260, "column": 10, - "index": 8285 + "index": 8312 }, "end": { "line": 268, "column": 11, - "index": 8606 + "index": 8633 } }, "filepath": "src/components/datagrid/controls/display_selector.tsx" @@ -1609,12 +1609,12 @@ "start": { "line": 260, "column": 10, - "index": 8285 + "index": 8312 }, "end": { "line": 268, "column": 11, - "index": 8606 + "index": 8633 } }, "filepath": "src/components/datagrid/controls/display_selector.tsx" @@ -1627,12 +1627,12 @@ "start": { "line": 260, "column": 10, - "index": 8285 + "index": 8312 }, "end": { "line": 268, "column": 11, - "index": 8606 + "index": 8633 } }, "filepath": "src/components/datagrid/controls/display_selector.tsx" @@ -1645,12 +1645,12 @@ "start": { "line": 260, "column": 10, - "index": 8285 + "index": 8312 }, "end": { "line": 268, "column": 11, - "index": 8606 + "index": 8633 } }, "filepath": "src/components/datagrid/controls/display_selector.tsx" @@ -1663,12 +1663,12 @@ "start": { "line": 303, "column": 10, - "index": 9713 + "index": 9740 }, "end": { "line": 318, "column": 11, - "index": 10186 + "index": 10213 } }, "filepath": "src/components/datagrid/controls/display_selector.tsx" @@ -1681,12 +1681,12 @@ "start": { "line": 303, "column": 10, - "index": 9713 + "index": 9740 }, "end": { "line": 318, "column": 11, - "index": 10186 + "index": 10213 } }, "filepath": "src/components/datagrid/controls/display_selector.tsx" @@ -1699,12 +1699,12 @@ "start": { "line": 303, "column": 10, - "index": 9713 + "index": 9740 }, "end": { "line": 318, "column": 11, - "index": 10186 + "index": 10213 } }, "filepath": "src/components/datagrid/controls/display_selector.tsx" @@ -1717,12 +1717,12 @@ "start": { "line": 303, "column": 10, - "index": 9713 + "index": 9740 }, "end": { "line": 318, "column": 11, - "index": 10186 + "index": 10213 } }, "filepath": "src/components/datagrid/controls/display_selector.tsx" @@ -1735,12 +1735,12 @@ "start": { "line": 303, "column": 10, - "index": 9713 + "index": 9740 }, "end": { "line": 318, "column": 11, - "index": 10186 + "index": 10213 } }, "filepath": "src/components/datagrid/controls/display_selector.tsx" @@ -2273,14 +2273,14 @@ "highlighting": "string", "loc": { "start": { - "line": 343, + "line": 375, "column": 22, - "index": 9585 + "index": 10168 }, "end": { - "line": 347, + "line": 379, "column": 5, - "index": 9757 + "index": 10340 } }, "filepath": "src/components/datagrid/data_grid.tsx" @@ -2291,14 +2291,14 @@ "highlighting": "string", "loc": { "start": { - "line": 348, + "line": 380, "column": 27, - "index": 9786 + "index": 10369 }, "end": { - "line": 352, + "line": 384, "column": 5, - "index": 9927 + "index": 10510 } }, "filepath": "src/components/datagrid/data_grid.tsx" @@ -2309,14 +2309,14 @@ "highlighting": "string", "loc": { "start": { - "line": 478, + "line": 527, "column": 18, - "index": 15428 + "index": 16306 }, "end": { - "line": 481, + "line": 530, "column": 20, - "index": 15581 + "index": 16459 } }, "filepath": "src/components/datagrid/data_grid.tsx" @@ -3762,55 +3762,55 @@ "filepath": "src/components/date_picker/super_date_picker/quick_select_popover/recently_used.tsx" }, { - "token": "euiSuperUpdateButton.refreshButtonLabel", - "defString": "Refresh", + "token": "euiSuperUpdateButton.updatingButtonLabel", + "defString": "Updating", "highlighting": "string", "loc": { "start": { - "line": 126, - "column": 6, - "index": 3158 + "line": 204, + "column": 8, + "index": 5149 }, "end": { - "line": 129, - "column": 8, - "index": 3257 + "line": 207, + "column": 10, + "index": 5256 } }, "filepath": "src/components/date_picker/super_date_picker/super_update_button.tsx" }, { - "token": "euiSuperUpdateButton.updatingButtonLabel", - "defString": "Updating", + "token": "euiSuperUpdateButton.updateButtonLabel", + "defString": "Update", "highlighting": "string", "loc": { "start": { - "line": 133, + "line": 213, "column": 8, - "index": 3342 + "index": 5319 }, "end": { - "line": 136, + "line": 216, "column": 10, - "index": 3449 + "index": 5422 } }, "filepath": "src/components/date_picker/super_date_picker/super_update_button.tsx" }, { - "token": "euiSuperUpdateButton.updateButtonLabel", - "defString": "Update", + "token": "euiSuperUpdateButton.refreshButtonLabel", + "defString": "Refresh", "highlighting": "string", "loc": { "start": { - "line": 138, - "column": 8, - "index": 3470 + "line": 221, + "column": 6, + "index": 5458 }, "end": { - "line": 141, - "column": 10, - "index": 3573 + "line": 224, + "column": 8, + "index": 5557 } }, "filepath": "src/components/date_picker/super_date_picker/super_update_button.tsx" @@ -3821,14 +3821,14 @@ "highlighting": "string", "loc": { "start": { - "line": 148, + "line": 231, "column": 8, - "index": 3669 + "index": 5672 }, "end": { - "line": 151, + "line": 234, "column": 10, - "index": 3781 + "index": 5784 } }, "filepath": "src/components/date_picker/super_date_picker/super_update_button.tsx" @@ -3839,14 +3839,14 @@ "highlighting": "string", "loc": { "start": { - "line": 155, + "line": 240, "column": 8, - "index": 3868 + "index": 5883 }, "end": { - "line": 158, + "line": 243, "column": 10, - "index": 3981 + "index": 5996 } }, "filepath": "src/components/date_picker/super_date_picker/super_update_button.tsx" @@ -4595,14 +4595,14 @@ "highlighting": "string", "loc": { "start": { - "line": 311, + "line": 308, "column": 8, - "index": 9897 + "index": 9762 }, "end": { - "line": 311, + "line": 308, "column": 78, - "index": 9967 + "index": 9832 } }, "filepath": "src/components/flyout/flyout.tsx" @@ -4613,14 +4613,14 @@ "highlighting": "string", "loc": { "start": { - "line": 385, + "line": 382, "column": 12, - "index": 12453 + "index": 12318 }, "end": { - "line": 388, + "line": 385, "column": 14, - "index": 12662 + "index": 12527 } }, "filepath": "src/components/flyout/flyout.tsx" @@ -4631,14 +4631,14 @@ "highlighting": "string", "loc": { "start": { - "line": 390, + "line": 387, "column": 12, - "index": 12691 + "index": 12556 }, "end": { - "line": 393, + "line": 390, "column": 14, - "index": 12863 + "index": 12728 } }, "filepath": "src/components/flyout/flyout.tsx" @@ -4649,14 +4649,14 @@ "highlighting": "string", "loc": { "start": { - "line": 396, + "line": 393, "column": 12, - "index": 12934 + "index": 12799 }, "end": { - "line": 399, + "line": 396, "column": 14, - "index": 13121 + "index": 12986 } }, "filepath": "src/components/flyout/flyout.tsx" @@ -5459,14 +5459,14 @@ "highlighting": "string", "loc": { "start": { - "line": 93, + "line": 101, "column": 10, - "index": 2736 + "index": 3007 }, "end": { - "line": 96, + "line": 104, "column": 11, - "index": 2843 + "index": 3114 } }, "filepath": "src/components/modal/modal.tsx" @@ -6701,14 +6701,14 @@ "highlighting": "string", "loc": { "start": { - "line": 325, - "column": 6, - "index": 10877 + "line": 322, + "column": 8, + "index": 10937 }, "end": { - "line": 332, - "column": 7, - "index": 11146 + "line": 329, + "column": 9, + "index": 11220 } }, "filepath": "src/components/toast/global_toast_list.tsx" @@ -6719,68 +6719,68 @@ "highlighting": "string", "loc": { "start": { - "line": 325, - "column": 6, - "index": 10877 + "line": 322, + "column": 8, + "index": 10937 }, "end": { - "line": 332, - "column": 7, - "index": 11146 + "line": 329, + "column": 9, + "index": 11220 } }, "filepath": "src/components/toast/global_toast_list.tsx" }, { - "token": "euiToast.dismissToast", - "defString": "Dismiss toast", + "token": "euiToast.newNotification", + "defString": "A new notification appears", "highlighting": "string", "loc": { "start": { - "line": 82, - "column": 6, - "index": 2046 + "line": 59, + "column": 10, + "index": 1823 }, "end": { - "line": 82, - "column": 69, - "index": 2109 + "line": 62, + "column": 12, + "index": 1938 } }, "filepath": "src/components/toast/toast.tsx" }, { - "token": "euiToast.newNotification", - "defString": "A new notification appears", + "token": "euiToast.notification", + "defString": "Notification", "highlighting": "string", "loc": { "start": { - "line": 116, - "column": 10, - "index": 2805 + "line": 67, + "column": 6, + "index": 2009 }, "end": { - "line": 119, - "column": 12, - "index": 2920 + "line": 67, + "column": 68, + "index": 2071 } }, "filepath": "src/components/toast/toast.tsx" }, { - "token": "euiToast.notification", - "defString": "Notification", + "token": "euiToast.dismissToast", + "defString": "Dismiss toast", "highlighting": "string", "loc": { "start": { - "line": 123, - "column": 6, - "index": 2970 + "line": 95, + "column": 8, + "index": 2760 }, "end": { - "line": 123, - "column": 68, - "index": 3032 + "line": 95, + "column": 71, + "index": 2823 } }, "filepath": "src/components/toast/toast.tsx" diff --git a/package.json b/package.json index 03661e77d3d..73389ab724e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@elastic/eui", "description": "Elastic UI Component Library", - "version": "93.3.0", + "version": "93.4.0", "license": "SEE LICENSE IN LICENSE.txt", "main": "lib", "module": "es", @@ -59,11 +59,10 @@ ], "dependencies": { "@hello-pangea/dnd": "^16.3.0", - "@types/lodash": "^4.14.198", + "@types/lodash": "^4.14.202", "@types/numeral": "^2.0.5", "@types/react-window": "^1.8.5", - "@types/refractor": "^3.0.2", - "@types/resize-observer-browser": "^0.1.7", + "@types/refractor": "^3.4.0", "chroma-js": "^2.4.2", "classnames": "^2.3.2", "lodash": "^4.17.21", diff --git a/src-docs/src/components/guide_page/versions.json b/src-docs/src/components/guide_page/versions.json index 1a7a81351b6..4198bb86f37 100644 --- a/src-docs/src/components/guide_page/versions.json +++ b/src-docs/src/components/guide_page/versions.json @@ -1,5 +1,6 @@ { "euiVersions": [ + "93.4.0", "93.3.0", "93.2.0", "93.1.1", diff --git a/src-docs/src/views/super_date_picker/playground.js b/src-docs/src/views/super_date_picker/playground.js index 610014ac214..8431a8f80fb 100644 --- a/src-docs/src/views/super_date_picker/playground.js +++ b/src-docs/src/views/super_date_picker/playground.js @@ -77,6 +77,11 @@ export const superUpdateButtonConfig = () => { propsToUse.onClick = simulateFunction(propsToUse.onClick, true); + propsToUse.children = { + ...propsToUse.children, + type: PropTypes.String, + }; + return { config: { componentName: 'EuiSuperUpdateButton', diff --git a/src/components/breadcrumbs/__snapshots__/_breadcrumb_content.test.tsx.snap b/src/components/breadcrumbs/__snapshots__/_breadcrumb_content.test.tsx.snap new file mode 100644 index 00000000000..002da4ac433 --- /dev/null +++ b/src/components/breadcrumbs/__snapshots__/_breadcrumb_content.test.tsx.snap @@ -0,0 +1,104 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`EuiBreadcrumbContent breadcrumbs with popovers renders with \`popoverContent\` 1`] = ` + +
+
+ +
+
+
+
+
+ +
+
+ +`; + +exports[`EuiBreadcrumbContent renders interactive breadcrumbs with href or onClick 1`] = ` +
+ + Link + + +
+`; + +exports[`EuiBreadcrumbContent renders plain uninteractive breadcrumb text 1`] = ` + + Text + +`; diff --git a/src/components/breadcrumbs/__snapshots__/breadcrumb.test.tsx.snap b/src/components/breadcrumbs/__snapshots__/breadcrumb.test.tsx.snap index 116e054b27e..aae72d83cd5 100644 --- a/src/components/breadcrumbs/__snapshots__/breadcrumb.test.tsx.snap +++ b/src/components/breadcrumbs/__snapshots__/breadcrumb.test.tsx.snap @@ -1,30 +1,46 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`EuiBreadcrumbContent breadcrumbs with popovers renders with \`popoverContent\` 1`] = ` +exports[`EuiBreadcrumb is a light
  • wrapper around the content 1`] = ` +
  • + Hello world +
  • +`; + +exports[`EuiBreadcrumbCollapsed renders a ... breadcrumb with collapsed content in a popover 1`] = `
    -
    - -
    + + + … + + + + - Clicking this button will toggle a popover dialog. + + +
    +
    `; - -exports[`EuiBreadcrumbContent renders interactive breadcrumbs with href or onClick 1`] = ` -
    - - Link - - -
    -`; - -exports[`EuiBreadcrumbContent renders plain uninteractive breadcrumb text 1`] = ` -
    - - Text - -
    -`; diff --git a/src/components/breadcrumbs/__snapshots__/breadcrumbs.test.tsx.snap b/src/components/breadcrumbs/__snapshots__/breadcrumbs.test.tsx.snap index 27252750a54..8be50405a28 100644 --- a/src/components/breadcrumbs/__snapshots__/breadcrumbs.test.tsx.snap +++ b/src/components/breadcrumbs/__snapshots__/breadcrumbs.test.tsx.snap @@ -39,15 +39,15 @@ exports[`EuiBreadcrumbs is rendered 1`] = ` data-test-subj="euiBreadcrumb" >
    + )} + /> + ); + + fireEvent.click(getByTestSubject('popoverToggle')); + await waitForEuiPopoverOpen(); + + fireEvent.click(getByTestSubject('popoverClose')); + await waitForEuiPopoverClose(); + }); + }); + + describe('highlightLastBreadcrumb', () => { + it('adds an aria-current attr', () => { + const { getByText } = render( + + ); + expect(getByText('Home')).toHaveAttribute('aria-current', 'page'); + }); + + it('colors both interactive and non-interactive breadcrumbs text-colored', () => { + const { getByTestSubject } = render( + <> + + + + + + ); + expect(getByTestSubject('control')).toHaveStyleRule('color', '#646a77'); + expect(getByTestSubject('text')).toHaveStyleRule('color', '#343741'); + expect(getByTestSubject('link')).toHaveStyleRule('color', '#343741'); + expect(getByTestSubject('popover')).toHaveStyleRule('color', '#343741'); + }); + }); +}); diff --git a/src/components/breadcrumbs/_breadcrumb_content.tsx b/src/components/breadcrumbs/_breadcrumb_content.tsx new file mode 100644 index 00000000000..9547d6718d9 --- /dev/null +++ b/src/components/breadcrumbs/_breadcrumb_content.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, { + FunctionComponent, + HTMLAttributes, + useState, + useCallback, + forwardRef, +} from 'react'; +import { ArrayCSSInterpolation } from '@emotion/css'; +import classNames from 'classnames'; + +import { useEuiMemoizedStyles } from '../../services'; +import { EuiInnerText } from '../inner_text'; +import { EuiTextColor } from '../text'; +import { EuiLink } from '../link'; +import { EuiPopover } from '../popover'; +import { EuiIcon } from '../icon'; +import { useEuiI18n } from '../i18n'; + +import type { EuiBreadcrumbProps, _EuiBreadcrumbProps } from './types'; +import { + euiBreadcrumbContentStyles, + euiBreadcrumbPopoverStyles, +} from './_breadcrumb_content.styles'; + +export const EuiBreadcrumbContent: FunctionComponent< + EuiBreadcrumbProps & _EuiBreadcrumbProps +> = ({ + text, + truncate, + type, + href, + rel, // required by our local href-with-rel eslint rule + onClick, + popoverContent, + popoverProps, + className, + color, + isFirstBreadcrumb, + isLastBreadcrumb, + isOnlyBreadcrumb, + highlightLastBreadcrumb, + truncateLastBreadcrumb, + ...rest +}) => { + const classes = classNames('euiBreadcrumb__content', className); + + const styles = useEuiMemoizedStyles(euiBreadcrumbContentStyles); + const cssStyles = [styles.euiBreadcrumb__content, styles[type]]; + if (type === 'application') { + if (isOnlyBreadcrumb) { + cssStyles.push(styles.applicationStyles.onlyChild); + } else if (isFirstBreadcrumb) { + cssStyles.push(styles.applicationStyles.firstChild); + } else if (isLastBreadcrumb) { + cssStyles.push(styles.applicationStyles.lastChild); + } + } + const truncationStyles = [ + truncate && !truncateLastBreadcrumb && styles.isTruncated, + truncateLastBreadcrumb && styles.isTruncatedLast, + ]; + + const isBreadcrumbWithPopover = !!popoverContent; + const isInteractiveBreadcrumb = href || onClick; + const linkColor = color || (highlightLastBreadcrumb ? 'text' : 'subdued'); + const plainTextColor = highlightLastBreadcrumb ? 'default' : 'subdued'; // Does not inherit `color` prop + const ariaCurrent = highlightLastBreadcrumb ? ('page' as const) : undefined; + + return ( + + {(ref, innerText) => { + const title = innerText === '' ? undefined : innerText; + const baseProps = { + ref, + title, + 'aria-current': ariaCurrent, + className: classes, + css: [...cssStyles, ...truncationStyles], + }; + + if (isBreadcrumbWithPopover) { + const { css: _, ...popoverButtonProps } = baseProps; + return ( + + {text} + + ); + } else if (isInteractiveBreadcrumb) { + return ( + + {text} + + ); + } else { + return ( + + + {text} + + + ); + } + }} + + ); +}; + +type EuiBreadcrumbPopoverProps = HTMLAttributes & + Pick & + Pick<_EuiBreadcrumbProps, 'type' | 'isLastBreadcrumb'> & { + breadcrumbCss: ArrayCSSInterpolation; + truncationCss: ArrayCSSInterpolation; + }; +const EuiBreadcrumbPopover = forwardRef< + HTMLButtonElement, + EuiBreadcrumbPopoverProps +>( + ( + { + popoverContent, + popoverProps, + color, + type, + title, + 'aria-current': ariaCurrent, + className, + isLastBreadcrumb, + breadcrumbCss, + truncationCss, + children, + ...rest + }, + ref + ) => { + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + const closePopover = useCallback(() => setIsPopoverOpen(false), []); + const togglePopover = useCallback( + () => setIsPopoverOpen((isOpen) => !isOpen), + [] + ); + + const popoverAriaLabel = useEuiI18n( + // This component was moved into another file for organization/dev readability, + // but we're keeping the i18n token the same as before for consumer consistency + // eslint-disable-next-line local/i18n + 'euiBreadcrumb.popoverAriaLabel', + 'Clicking this button will toggle a popover dialog.' + ); + + const styles = useEuiMemoizedStyles(euiBreadcrumbPopoverStyles); + const wrapperStyles = [ + styles.popoverWrapper.euiBreadcrumb__popoverWrapper, + !isLastBreadcrumb && styles.popoverWrapper[type], + ]; + const buttonStyles = [ + styles.euiBreadcrumb__popoverButton, + ...breadcrumbCss, + ]; + const truncationStyles = [ + styles.euiBreadcrumb__popoverTruncation, + ...truncationCss, + ]; + + return ( + + {children} + + + } + > + {typeof popoverContent === 'function' + ? popoverContent(closePopover) + : popoverContent} + + ); + } +); +EuiBreadcrumbPopover.displayName = 'EuiBreadcrumbPopover'; diff --git a/src/components/breadcrumbs/breadcrumb.styles.ts b/src/components/breadcrumbs/breadcrumb.styles.ts index 2950c4951e9..126c787cffa 100644 --- a/src/components/breadcrumbs/breadcrumb.styles.ts +++ b/src/components/breadcrumbs/breadcrumb.styles.ts @@ -8,18 +8,12 @@ import { css } from '@emotion/react'; import { UseEuiTheme } from '../../services'; -import { transparentize } from '../../services/color'; -import { - euiFontSize, - euiTextTruncate, - euiFocusRing, - logicalCSS, - logicalBorderRadiusCSS, - mathWithUnits, -} from '../../global_styling'; +import { logicalCSS } from '../../global_styling'; +/** + * Styles cast to
  • element + */ export const euiBreadcrumbStyles = (euiThemeContext: UseEuiTheme) => { - // Styles cast to
  • element const { euiTheme } = euiThemeContext; return { euiBreadcrumb: css` @@ -61,115 +55,3 @@ export const euiBreadcrumbStyles = (euiThemeContext: UseEuiTheme) => { `, }; }; - -export const euiBreadcrumbContentStyles = (euiThemeContext: UseEuiTheme) => { - // Styles cast to , , or collapsed - )} - /> - ); - - fireEvent.click(getByTestSubject('popoverToggle')); - await waitForEuiPopoverOpen(); - - fireEvent.click(getByTestSubject('popoverClose')); - await waitForEuiPopoverClose(); - }); - }); - - describe('highlightLastBreadcrumb', () => { - it('adds an aria-current attr', () => { - const { getByText } = render( - - ); - expect(getByText('Home')).toHaveAttribute('aria-current', 'page'); - }); - - it('colors both interactive and non-interactive breadcrumbs text-colored', () => { - const { getByTestSubject } = render( - <> - - - - - - ); - expect(getByTestSubject('control')).toHaveStyleRule('color', '#646a77'); - expect(getByTestSubject('text')).toHaveStyleRule('color', '#343741'); - expect(getByTestSubject('link')).toHaveStyleRule('color', '#343741'); - expect(getByTestSubject('popover')).toHaveStyleRule('color', '#343741'); - }); + fireEvent.click(getByRole('button')); + waitForEuiPopoverOpen(); + expect(getByText('I render inside the popover')).toBeInTheDocument(); + expect(baseElement).toMatchSnapshot(); }); }); diff --git a/src/components/breadcrumbs/breadcrumb.tsx b/src/components/breadcrumbs/breadcrumb.tsx index 13daeef593e..80c5c813a21 100644 --- a/src/components/breadcrumbs/breadcrumb.tsx +++ b/src/components/breadcrumbs/breadcrumb.tsx @@ -9,86 +9,26 @@ import React, { FunctionComponent, HTMLAttributes, - AriaAttributes, - MouseEventHandler, - ReactNode, - useState, PropsWithChildren, } from 'react'; import classNames from 'classnames'; -import { useEuiTheme } from '../../services'; -import { CommonProps } from '../common'; -import { EuiInnerText } from '../inner_text'; -import { EuiTextColor } from '../text'; -import { EuiLink, EuiLinkColor } from '../link'; -import { EuiPopover, EuiPopoverProps } from '../popover'; -import { EuiIcon } from '../icon'; +import { useEuiMemoizedStyles } from '../../services'; import { useEuiI18n } from '../i18n'; -import { - euiBreadcrumbStyles, - euiBreadcrumbContentStyles, -} from './breadcrumb.styles'; +import type { EuiBreadcrumbProps, _EuiBreadcrumbProps } from './types'; +import { EuiBreadcrumbContent } from './_breadcrumb_content'; -export type EuiBreadcrumbProps = Omit< - HTMLAttributes, - 'color' | 'aria-current' -> & - CommonProps & { - href?: string; - rel?: string; - onClick?: MouseEventHandler; - /** - * Visible label of the breadcrumb - */ - text: ReactNode; - /** - * Force a max-width on the breadcrumb text - */ - truncate?: boolean; - /** - * Accepts any EuiLink `color` when rendered as one (has `href`, `onClick`, or `popoverContent`) - */ - color?: EuiLinkColor; - /** - * Override the existing `aria-current` which defaults to `page` for the last breadcrumb - */ - 'aria-current'?: AriaAttributes['aria-current']; - /** - * Creates a breadcrumb that toggles a popover dialog. Takes any rendered node(s), - * or a render function that will pass callback allowing you to close the - * breadcrumb popover from within your popover content. - * - * If passed, both `href` and `onClick` will be ignored - the breadcrumb's - * click behavior should only trigger a popover. - */ - popoverContent?: ReactNode | ((closePopover: () => void) => ReactNode); - /** - * Allows customizing the popover if necessary. Accepts any props that - * [EuiPopover](/#/layout/popover) accepts, except for props that control state. - */ - popoverProps?: Omit; - }; - -// Used internally only by the parent EuiBreadcrumbs -type _EuiBreadcrumbProps = PropsWithChildren & - Pick & { - type: 'page' | 'application'; - isFirstBreadcrumb?: boolean; - isLastBreadcrumb?: boolean; - isOnlyBreadcrumb?: boolean; - highlightLastBreadcrumb?: boolean; - truncateLastBreadcrumb?: boolean; - }; +import { euiBreadcrumbStyles } from './breadcrumb.styles'; export const EuiBreadcrumb: FunctionComponent< - HTMLAttributes & _EuiBreadcrumbProps + HTMLAttributes & + Pick<_EuiBreadcrumbProps, 'type'> & + Pick > = ({ children, className, type, truncate, ...rest }) => { const classes = classNames('euiBreadcrumb', className); - const euiTheme = useEuiTheme(); - const styles = euiBreadcrumbStyles(euiTheme); + const styles = useEuiMemoizedStyles(euiBreadcrumbStyles); const cssStyles = [ styles.euiBreadcrumb, styles[type], @@ -107,132 +47,10 @@ export const EuiBreadcrumb: FunctionComponent< ); }; -export const EuiBreadcrumbContent: FunctionComponent< - EuiBreadcrumbProps & _EuiBreadcrumbProps -> = ({ - text, - truncate, - type, - href, - rel, // required by our local href-with-rel eslint rule - onClick, - popoverContent, - popoverProps, - className, - color, - isFirstBreadcrumb, - isLastBreadcrumb, - isOnlyBreadcrumb, - highlightLastBreadcrumb, - truncateLastBreadcrumb, - ...rest -}) => { - const classes = classNames('euiBreadcrumb__content', className); - - const euiTheme = useEuiTheme(); - const styles = euiBreadcrumbContentStyles(euiTheme); - const cssStyles = [ - styles.euiBreadcrumb__content, - styles[type], - truncate && !truncateLastBreadcrumb && styles.isTruncated, - truncateLastBreadcrumb && styles.isTruncatedLast, - ]; - if (type === 'application') { - if (isOnlyBreadcrumb) { - cssStyles.push(styles.applicationStyles.onlyChild); - } else if (isFirstBreadcrumb) { - cssStyles.push(styles.applicationStyles.firstChild); - } else if (isLastBreadcrumb) { - cssStyles.push(styles.applicationStyles.lastChild); - } - } - - const isInteractiveBreadcrumb = href || onClick; - const linkColor = color || (highlightLastBreadcrumb ? 'text' : 'subdued'); - const plainTextColor = highlightLastBreadcrumb ? 'default' : 'subdued'; // Does not inherit `color` prop - const ariaCurrent = highlightLastBreadcrumb ? ('page' as const) : undefined; - - const isPopoverBreadcrumb = !!popoverContent; - const [isPopoverOpen, setIsPopoverOpen] = useState(false); - const popoverAriaLabel = useEuiI18n( - 'euiBreadcrumb.popoverAriaLabel', - 'Clicking this button will toggle a popover dialog.' - ); - - return ( - - {(ref, innerText) => { - const title = innerText === '' ? undefined : innerText; - - const baseProps = { ref, title, 'aria-current': ariaCurrent }; - const styleProps = { className: classes, css: cssStyles }; - - if (isPopoverBreadcrumb) { - const closePopover = () => setIsPopoverOpen(false); - return ( - setIsPopoverOpen((isOpen) => !isOpen)} - {...rest} - > - {text} - - - } - > - {typeof popoverContent === 'function' - ? popoverContent(closePopover) - : popoverContent} - - ); - } else if (isInteractiveBreadcrumb) { - return ( - - {text} - - ); - } else { - return ( - - - {text} - - - ); - } - }} - - ); -}; - -export const EuiBreadcrumbCollapsed: FunctionComponent<_EuiBreadcrumbProps> = ({ - children, - isFirstBreadcrumb, - type, -}) => { - const euiTheme = useEuiTheme(); - const styles = euiBreadcrumbStyles(euiTheme); +export const EuiBreadcrumbCollapsed: FunctionComponent< + PropsWithChildren & Pick<_EuiBreadcrumbProps, 'type' | 'isFirstBreadcrumb'> +> = ({ children, isFirstBreadcrumb, type }) => { + const styles = useEuiMemoizedStyles(euiBreadcrumbStyles); const cssStyles = [styles.isCollapsed]; const ariaLabel = useEuiI18n( diff --git a/src/components/breadcrumbs/breadcrumbs.stories.tsx b/src/components/breadcrumbs/breadcrumbs.stories.tsx index 8bab5a82205..e2a7241f11f 100644 --- a/src/components/breadcrumbs/breadcrumbs.stories.tsx +++ b/src/components/breadcrumbs/breadcrumbs.stories.tsx @@ -8,7 +8,8 @@ import type { Meta, StoryObj } from '@storybook/react'; -import { EuiBreadcrumbs, EuiBreadcrumbsProps } from './breadcrumbs'; +import type { EuiBreadcrumbsProps } from './types'; +import { EuiBreadcrumbs } from './breadcrumbs'; const meta: Meta = { title: 'Navigation/EuiBreadcrumbs', diff --git a/src/components/breadcrumbs/breadcrumbs.tsx b/src/components/breadcrumbs/breadcrumbs.tsx index 71d381982cd..5eb4f4937a3 100644 --- a/src/components/breadcrumbs/breadcrumbs.tsx +++ b/src/components/breadcrumbs/breadcrumbs.tsx @@ -9,74 +9,20 @@ import React, { FunctionComponent, useMemo } from 'react'; import classNames from 'classnames'; -import { CommonProps, ExclusiveUnion } from '../common'; +import { ExclusiveUnion } from '../common'; import { useEuiI18n } from '../i18n'; -import { - useEuiTheme, - EuiBreakpointSize, - useCurrentEuiBreakpoint, -} from '../../services'; - -import { - EuiBreadcrumb, - EuiBreadcrumbContent, - EuiBreadcrumbCollapsed, +import { useEuiMemoizedStyles, useCurrentEuiBreakpoint } from '../../services'; + +import type { + EuiBreadcrumbResponsiveMaxCount, + EuiBreadcrumbsProps, EuiBreadcrumbProps, -} from './breadcrumb'; +} from './types'; +import { EuiBreadcrumb, EuiBreadcrumbCollapsed } from './breadcrumb'; +import { EuiBreadcrumbContent } from './_breadcrumb_content'; import { euiBreadcrumbsListStyles } from './breadcrumbs.styles'; -export type EuiBreadcrumbResponsiveMaxCount = { - /** - * Any of the following keys are allowed: `'xs' | 's' | 'm' | 'l' | 'xl'` - * Omitting a key will display all breadcrumbs at that breakpoint - */ - [key in EuiBreakpointSize]?: number; -}; - -export type EuiBreadcrumbsProps = CommonProps & { - /** - * Hides extra (above the max) breadcrumbs under a collapsed item as the window gets smaller. - * Pass a custom #EuiBreadcrumbResponsiveMaxCount object to change the number of breadcrumbs to show at the particular breakpoints. - * - * Pass `false` to turn this behavior off. - * - * Default: `{ xs: 1, s: 2, m: 4 }` - */ - responsive?: boolean | EuiBreadcrumbResponsiveMaxCount; - - /** - * Forces all breadcrumbs to single line and - * truncates each breadcrumb to a particular width, - * except for the last item - */ - truncate?: boolean; - - /** - * Collapses the inner items past the maximum set here - * into a single ellipses item. - * Omitting or passing a `0` value will show all breadcrumbs. - */ - max?: number | null; - - /** - * The array of individual #EuiBreadcrumb items - */ - breadcrumbs: EuiBreadcrumbProps[]; - - /** - * Determines breadcrumbs appearance, with `page` being the default styling. - * Application breadcrumbs should only be once per page, in (e.g.) EuiHeader - */ - type?: 'page' | 'application'; - - /** - * Whether the last breadcrumb should visually (and accessibly, to screen readers) - * be highlighted as the current page. Defaults to true. - */ - lastBreadcrumbIsCurrentPage?: boolean; -}; - const responsiveDefault: EuiBreadcrumbResponsiveMaxCount = { xs: 1, s: 2, @@ -95,8 +41,7 @@ export const EuiBreadcrumbs: FunctionComponent = ({ }) => { const ariaLabel = useEuiI18n('euiBreadcrumbs.nav.ariaLabel', 'Breadcrumbs'); - const euiTheme = useEuiTheme(); - const breadcrumbsListStyles = euiBreadcrumbsListStyles(euiTheme); + const breadcrumbsListStyles = useEuiMemoizedStyles(euiBreadcrumbsListStyles); const cssBreadcrumbsListStyles = [ breadcrumbsListStyles.euiBreadcrumbs__list, truncate && breadcrumbsListStyles.isTruncated, diff --git a/src/components/breadcrumbs/index.ts b/src/components/breadcrumbs/index.ts index 34fd3ae44dd..a90c66c7d51 100644 --- a/src/components/breadcrumbs/index.ts +++ b/src/components/breadcrumbs/index.ts @@ -6,9 +6,9 @@ * Side Public License, v 1. */ -export type { EuiBreadcrumbProps as EuiBreadcrumb } from './breadcrumb'; export type { + EuiBreadcrumbProps as EuiBreadcrumb, EuiBreadcrumbsProps, EuiBreadcrumbResponsiveMaxCount, -} from './breadcrumbs'; +} from './types'; export { EuiBreadcrumbs } from './breadcrumbs'; diff --git a/src/components/breadcrumbs/types.ts b/src/components/breadcrumbs/types.ts new file mode 100644 index 00000000000..21fd76f9a7e --- /dev/null +++ b/src/components/breadcrumbs/types.ts @@ -0,0 +1,126 @@ +/* + * 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 { + ReactNode, + HTMLAttributes, + AriaAttributes, + MouseEventHandler, +} from 'react'; +import type { EuiBreakpointSize } from '../../services'; +import type { CommonProps } from '../common'; +import type { EuiLinkColor } from '../link'; +import type { EuiPopoverProps } from '../popover'; + +/** + * Consumer facing type exports + */ + +export type EuiBreadcrumbResponsiveMaxCount = { + /** + * Any of the following keys are allowed: `'xs' | 's' | 'm' | 'l' | 'xl'` + * Omitting a key will display all breadcrumbs at that breakpoint + */ + [key in EuiBreakpointSize]?: number; +}; + +export type EuiBreadcrumbsProps = CommonProps & { + /** + * Hides extra (above the max) breadcrumbs under a collapsed item as the window gets smaller. + * Pass a custom #EuiBreadcrumbResponsiveMaxCount object to change the number of breadcrumbs to show at the particular breakpoints. + * + * Pass `false` to turn this behavior off. + * + * Default: `{ xs: 1, s: 2, m: 4 }` + */ + responsive?: boolean | EuiBreadcrumbResponsiveMaxCount; + + /** + * Forces all breadcrumbs to single line and + * truncates each breadcrumb to a particular width, + * except for the last item + */ + truncate?: boolean; + + /** + * Collapses the inner items past the maximum set here + * into a single ellipses item. + * Omitting or passing a `0` value will show all breadcrumbs. + */ + max?: number | null; + + /** + * The array of individual #EuiBreadcrumb items + */ + breadcrumbs: EuiBreadcrumbProps[]; + + /** + * Determines breadcrumbs appearance, with `page` being the default styling. + * Application breadcrumbs should only be once per page, in (e.g.) EuiHeader + */ + type?: 'page' | 'application'; + + /** + * Whether the last breadcrumb should visually (and accessibly, to screen readers) + * be highlighted as the current page. Defaults to true. + */ + lastBreadcrumbIsCurrentPage?: boolean; +}; + +export type EuiBreadcrumbProps = Omit< + HTMLAttributes, + 'color' | 'aria-current' +> & + CommonProps & { + href?: string; + rel?: string; + onClick?: MouseEventHandler; + /** + * Visible label of the breadcrumb + */ + text: ReactNode; + /** + * Force a max-width on the breadcrumb text + */ + truncate?: boolean; + /** + * Accepts any EuiLink `color` when rendered as one (has `href`, `onClick`, or `popoverContent`) + */ + color?: EuiLinkColor; + /** + * Override the existing `aria-current` which defaults to `page` for the last breadcrumb + */ + 'aria-current'?: AriaAttributes['aria-current']; + /** + * Creates a breadcrumb that toggles a popover dialog. Takes any rendered node(s), + * or a render function that will pass callback allowing you to close the + * breadcrumb popover from within your popover content. + * + * If passed, both `href` and `onClick` will be ignored - the breadcrumb's + * click behavior should only trigger a popover. + */ + popoverContent?: ReactNode | ((closePopover: () => void) => ReactNode); + /** + * Allows customizing the popover if necessary. Accepts any props that + * [EuiPopover](/#/layout/popover) accepts, except for props that control state. + */ + popoverProps?: Omit; + }; + +/** + * Internal props set by parent EuiBreadcrumbs only + */ + +export type _EuiBreadcrumbProps = { + type: NonNullable; + isFirstBreadcrumb?: boolean; + isLastBreadcrumb?: boolean; + isOnlyBreadcrumb?: boolean; + highlightLastBreadcrumb?: boolean; + truncateLastBreadcrumb?: boolean; +}; diff --git a/src/components/date_picker/super_date_picker/__snapshots__/super_update_button.test.tsx.snap b/src/components/date_picker/super_date_picker/__snapshots__/super_update_button.test.tsx.snap index fa792a8d48e..6fd392b27bd 100644 --- a/src/components/date_picker/super_date_picker/__snapshots__/super_update_button.test.tsx.snap +++ b/src/components/date_picker/super_date_picker/__snapshots__/super_update_button.test.tsx.snap @@ -1,495 +1,25 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`EuiSuperUpdateButton iconOnly 1`] = ` - - - - - - - - - - - - -`; - exports[`EuiSuperUpdateButton is rendered 1`] = ` - - - - - - - - - - - - -`; - -exports[`EuiSuperUpdateButton isDisabled 1`] = ` - - } - delay="regular" - display="inlineBlock" - position="bottom" -> - - - - - - - - - - - -`; - -exports[`EuiSuperUpdateButton isLoading 1`] = ` - - - - - - - - - - - - -`; - -exports[`EuiSuperUpdateButton needsUpdate 1`] = ` - - } - delay="regular" - display="inlineBlock" - position="bottom" -> - - - - - - - - - - - -`; - -exports[`EuiSuperUpdateButton responsive can be all 1`] = ` - - - - - - - - - - - - -`; - -exports[`EuiSuperUpdateButton responsive can be false 1`] = ` - - - - - - - - - - - - -`; - -exports[`EuiSuperUpdateButton showTooltip 1`] = ` - - - - - - - - - - - - +
    + + + +
    `; diff --git a/src/components/date_picker/super_date_picker/super_update_button.test.tsx b/src/components/date_picker/super_date_picker/super_update_button.test.tsx index 6a606c49b59..a2cf931a92a 100644 --- a/src/components/date_picker/super_date_picker/super_update_button.test.tsx +++ b/src/components/date_picker/super_date_picker/super_update_button.test.tsx @@ -7,13 +7,12 @@ */ import React from 'react'; -import { shallow, mount } from 'enzyme'; import { fireEvent } from '@testing-library/react'; -import { waitForEuiToolTipVisible } from '../../../test/rtl'; +import { render, waitForEuiToolTipVisible, within } from '../../../test/rtl'; import { shouldRenderCustomStyles } from '../../../test/internal'; import { EuiSuperUpdateButton } from './super_update_button'; -import { EuiButton, EuiButtonProps } from '../../button'; +import { EuiButtonProps } from '../../button'; const noop = () => {}; @@ -37,109 +36,120 @@ describe('EuiSuperUpdateButton', () => { } ); - test('is rendered', () => { - const component = shallow(); - - expect(component).toMatchSnapshot(); - }); - - test('needsUpdate', () => { - const component = shallow( - + it('is rendered', () => { + const { container, getByRole } = render( + ); - expect(component).toMatchSnapshot(); - }); + const tooltipWrapper = container.querySelector('.euiToolTipAnchor'); + expect(tooltipWrapper).toBeInTheDocument(); - test('isDisabled', () => { - const component = shallow( - - ); + const button = getByRole('button'); + expect(button).toBeInTheDocument(); + expect(button).toHaveClass('euiSuperUpdateButton'); + expect(button).toHaveTextContent('Refresh'); - expect(component).toMatchSnapshot(); + expect(container).toMatchSnapshot(); }); - test('isLoading', () => { - const component = shallow( - - ); + describe('props', () => { + test('needsUpdate', () => { + const { getByRole } = render( + + ); - expect(component).toMatchSnapshot(); - }); + expect(getByRole('button')).toHaveTextContent('Update'); + }); - test('iconOnly', () => { - const component = shallow(); + test('isDisabled', () => { + const { getByRole } = render( + + ); - expect(component).toMatchSnapshot(); - }); + expect(getByRole('button')).toBeDisabled(); + }); - test('showTooltip', () => { - const component = shallow( - - ); + test('isLoading', () => { + const { getByRole } = render( + + ); - expect(component).toMatchSnapshot(); - }); + expect(getByRole('button')).toHaveTextContent('Updating'); - test('responsive can be all', () => { - const component = shallow( - - ); + const icon = getByRole('progressbar'); + expect(icon).toHaveAccessibleName('Loading'); + }); - expect(component).toMatchSnapshot(); - }); + test('iconOnly', () => { + const { getByText, container } = render( + + ); - test('responsive can be false', () => { - const component = shallow( - - ); + const icon = container.querySelector('[data-euiicon-type]'); + expect(icon).toBeInTheDocument(); + expect(icon).toHaveAttribute('data-euiicon-type', 'refresh'); - expect(component).toMatchSnapshot(); - }); + const text = getByText('Refresh'); + expect(text).toBeInTheDocument(); + expect(text).toHaveClass('euiScreenReaderOnly'); + }); - test('forwards props to EuiButton', () => { - const speciallyHandledProps = { - className: 'testClass', - textProps: { - className: 'textPropsTestClass', - id: 'test', - }, - }; - const extraProps: Partial = { - fill: false, - size: 's', - contentProps: { id: 'contentSpan' }, - }; - - const component = mount( - {}} - {...speciallyHandledProps} - {...extraProps} - /> - ); + test('showTooltip', () => { + const { container } = render( + + ); - const { - // props not passed through - isDisabled, - isLoading, - onClick, + const tooltipWrapper = container.querySelector('.euiToolTipAnchor'); + expect(tooltipWrapper).toBeInTheDocument(); + }); - // props with special handling - className, - textProps, + test('children', () => { + const { getByRole } = render( + + + + ); - ...forwardedProps - } = component.find(EuiButton).props(); + const button = getByRole('button'); + expect(button).toHaveTextContent(''); + + const customChildren = within(button).getByTestSubject('custom-children'); + expect(customChildren).toBeInTheDocument(); + }); - expect(className).toBe('euiSuperUpdateButton testClass'); - expect(textProps).toEqual({ - className: 'textPropsTestClass', - id: 'test', + it('forwards props to EuiButton', () => { + const speciallyHandledProps = { + className: 'testClass', + textProps: { + className: 'textPropsTestClass', + id: 'test', + 'data-test-subj': 'text', + }, + }; + const extraProps: Partial = { + fill: false, + size: 's', + contentProps: { id: 'contentSpan', 'data-test-subj': 'content' }, + }; + + const { getByRole, getByTestSubject } = render( + {}} + {...speciallyHandledProps} + {...extraProps} + /> + ); + + const button = getByRole('button'); + expect(button).toHaveClass('testClass'); + + const text = getByTestSubject('text'); + expect(text).toBeInTheDocument(); + expect(text).toHaveAttribute('id', 'test'); + expect(text).toHaveClass('textPropsTestClass'); + + const buttonContent = within(button).getByTestSubject('content'); + expect(buttonContent).toBeInTheDocument(); }); - expect(forwardedProps).toMatchObject(extraProps); }); }); diff --git a/src/components/date_picker/super_date_picker/super_update_button.tsx b/src/components/date_picker/super_date_picker/super_update_button.tsx index 8c67f437ca8..58d7687e251 100644 --- a/src/components/date_picker/super_date_picker/super_update_button.tsx +++ b/src/components/date_picker/super_date_picker/super_update_button.tsx @@ -6,7 +6,12 @@ * Side Public License, v 1. */ -import React, { Component, MouseEventHandler, ElementRef } from 'react'; +import React, { + Component, + MouseEventHandler, + ElementRef, + ReactNode, +} from 'react'; import classNames from 'classnames'; import { EuiButton, EuiButtonProps } from '../../button'; @@ -25,6 +30,15 @@ type EuiSuperUpdateButtonInternalProps = { }; export type EuiSuperUpdateButtonProps = { + /** + * Overrides the default button label with a custom React node. + * + * When defined, you're responsible for updating the custom label + * when the data needs updating (the `needsUpdate` prop) + * or is loading (the `isLoading` prop). + */ + children?: ReactNode; + /** * Show the "Click to apply" tooltip */ @@ -45,7 +59,9 @@ export type EuiSuperUpdateButtonProps = { * Remove completely with `false` or provide your own list of breakpoints. */ responsive?: false | EuiBreakpointSize[]; -} & Partial>; +} & Partial< + Omit +>; export class EuiSuperUpdateButton extends Component< EuiSuperUpdateButtonInternalProps & EuiSuperUpdateButtonProps @@ -104,6 +120,7 @@ export class EuiSuperUpdateButton extends Component< render() { const { + children, className, needsUpdate, isLoading, @@ -122,43 +139,6 @@ export class EuiSuperUpdateButton extends Component< const classes = classNames('euiSuperUpdateButton', className); - let buttonText = ( - - ); - if (needsUpdate || isLoading) { - buttonText = isLoading ? ( - - ) : ( - - ); - } - - let tooltipContent; - if (isDisabled) { - tooltipContent = ( - - ); - } else if (needsUpdate && !isLoading) { - tooltipContent = ( - - ); - } - const sharedButtonProps = { color: needsUpdate || isLoading ? 'success' : 'primary', iconType: needsUpdate || isLoading ? 'kqlFunction' : 'refresh', @@ -167,10 +147,12 @@ export class EuiSuperUpdateButton extends Component< isLoading: isLoading, }; + const buttonContent = this.renderButtonContent(); + return ( @@ -190,7 +172,7 @@ export class EuiSuperUpdateButton extends Component< }} {...rest} > - {buttonText} + {buttonContent} @@ -202,11 +184,64 @@ export class EuiSuperUpdateButton extends Component< textProps={restTextProps} {...rest} > - {buttonText} + {buttonContent} ); } + + private renderButtonContent(): ReactNode { + const { children, isLoading, needsUpdate } = this.props; + + if (children) { + return children; + } + + if (isLoading) { + return ( + + ); + } + + if (needsUpdate) { + return ( + + ); + } + + return ( + + ); + } + + private renderTooltipContent(): ReactNode { + if (this.props.isDisabled) { + return ( + + ); + } + + if (this.props.needsUpdate && !this.props.isLoading) { + return ( + + ); + } + } } diff --git a/src/components/header/__snapshots__/header.test.tsx.snap b/src/components/header/__snapshots__/header.test.tsx.snap index 222aeff6d1c..8c4953f4658 100644 --- a/src/components/header/__snapshots__/header.test.tsx.snap +++ b/src/components/header/__snapshots__/header.test.tsx.snap @@ -53,7 +53,7 @@ exports[`EuiHeader sections render breadcrumbs and props 1`] = ` > Breadcrumb diff --git a/src/components/header/header_breadcrumbs/__snapshots__/header_breadcrumbs.test.tsx.snap b/src/components/header/header_breadcrumbs/__snapshots__/header_breadcrumbs.test.tsx.snap index 593e59847fe..d36a73a77d6 100644 --- a/src/components/header/header_breadcrumbs/__snapshots__/header_breadcrumbs.test.tsx.snap +++ b/src/components/header/header_breadcrumbs/__snapshots__/header_breadcrumbs.test.tsx.snap @@ -14,7 +14,7 @@ exports[`EuiHeaderBreadcrumbs is rendered 1`] = ` data-test-subj="euiBreadcrumb" >
    Edit @@ -79,7 +79,7 @@ exports[`EuiHeaderBreadcrumbs renders only one breadcrumb with all rounded corne > Home diff --git a/src/components/header/header_breadcrumbs/header_breadcrumbs.tsx b/src/components/header/header_breadcrumbs/header_breadcrumbs.tsx index d11b6efcb38..8cf78fcf6e3 100644 --- a/src/components/header/header_breadcrumbs/header_breadcrumbs.tsx +++ b/src/components/header/header_breadcrumbs/header_breadcrumbs.tsx @@ -9,9 +9,9 @@ import React, { FunctionComponent } from 'react'; import classNames from 'classnames'; +import { useEuiMemoizedStyles } from '../../../services'; import { EuiBreadcrumbs, EuiBreadcrumbsProps } from '../../breadcrumbs'; import { euiHeaderBreadcrumbsStyles } from './header_breadcrumbs.styles'; -import { useEuiTheme } from '../../../services'; export const EuiHeaderBreadcrumbs: FunctionComponent = ({ className, @@ -20,9 +20,7 @@ export const EuiHeaderBreadcrumbs: FunctionComponent = ({ }) => { const classes = classNames('euiHeaderBreadcrumbs', className); - const euiTheme = useEuiTheme(); - const styles = euiHeaderBreadcrumbsStyles(euiTheme); - const cssHeaderBreadcrumbStyles = [styles.euiHeaderBreadcrumbs]; + const styles = useEuiMemoizedStyles(euiHeaderBreadcrumbsStyles); return ( = ({ truncate breadcrumbs={breadcrumbs} className={classes} - css={cssHeaderBreadcrumbStyles} + css={styles.euiHeaderBreadcrumbs} type="application" {...rest} /> diff --git a/yarn.lock b/yarn.lock index b812b645cdf..6aa0bdb70d6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4884,11 +4884,16 @@ dependencies: "@types/node" "*" -"@types/lodash@^4.14.167", "@types/lodash@^4.14.198": +"@types/lodash@^4.14.167": version "4.14.198" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.198.tgz#4d27465257011aedc741a809f1269941fa2c5d4c" integrity sha512-trNJ/vtMZYMLhfN45uLq4ShQSw0/S7xCTLLVM+WM1rmFpba/VS42jVUgaO3w/NOLiWR/09lnYk0yMaA/atdIsg== +"@types/lodash@^4.14.202": + version "4.17.0" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.0.tgz#d774355e41f372d5350a4d0714abb48194a489c3" + integrity sha512-t7dhREVv6dbNj0q17X12j7yDG4bD/DHYX7o5/DbDxobP0HnGPgpRz2Ej77aL7TZT3DSw13fqUTj8J4mMnqa7WA== + "@types/mdast@^3.0.0": version "3.0.3" resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-3.0.3.tgz#2d7d671b1cd1ea3deb306ea75036c2a0407d2deb" @@ -5084,18 +5089,13 @@ "@types/scheduler" "*" csstype "^3.0.2" -"@types/refractor@^3.0.2": - version "3.0.2" - resolved "https://registry.yarnpkg.com/@types/refractor/-/refractor-3.0.2.tgz#2d42128d59f78f84d2c799ffc5ab5cadbcba2d82" - integrity sha512-2HMXuwGuOqzUG+KUTm9GDJCHl0LCBKsB5cg28ujEmVi/0qgTb6jOmkVSO5K48qXksyl2Fr3C0Q2VrgD4zbwyXg== +"@types/refractor@^3.4.0": + version "3.4.1" + resolved "https://registry.yarnpkg.com/@types/refractor/-/refractor-3.4.1.tgz#8b109804f77b3da8fad543d3f575fef1ece8835a" + integrity sha512-wYuorIiCTSuvRT9srwt+taF6mH/ww+SyN2psM0sjef2qW+sS8GmshgDGTEDgWB1sTVGgYVE6EK7dBA2MxQxibg== dependencies: "@types/prismjs" "*" -"@types/resize-observer-browser@^0.1.7": - version "0.1.7" - resolved "https://registry.yarnpkg.com/@types/resize-observer-browser/-/resize-observer-browser-0.1.7.tgz#294aaadf24ac6580b8fbd1fe3ab7b59fe85f9ef3" - integrity sha512-G9eN0Sn0ii9PWQ3Vl72jDPgeJwRWhv2Qk/nQkJuWmRmOB4HX3/BhD5SE1dZs/hzPZL/WKnvF0RHdTSG54QJFyg== - "@types/responselike@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29"