diff --git a/changelogs/upcoming/7601.md b/changelogs/upcoming/7601.md
new file mode 100644
index 00000000000..fa7e7be1bd8
--- /dev/null
+++ b/changelogs/upcoming/7601.md
@@ -0,0 +1,3 @@
+**Dependency updates**
+
+- Updated `remark-rehype` to v8.1.0
\ No newline at end of file
diff --git a/changelogs/upcoming/7672.md b/changelogs/upcoming/7672.md
new file mode 100644
index 00000000000..2e4d197f73c
--- /dev/null
+++ b/changelogs/upcoming/7672.md
@@ -0,0 +1,3 @@
+**Accessibility**
+
+- Improved `EuiBasicTable` and `EuiInMemoryTable`'s selection checkboxes to have unique aria-labels per row
diff --git a/changelogs/upcoming/7676.md b/changelogs/upcoming/7676.md
new file mode 100644
index 00000000000..74353df485e
--- /dev/null
+++ b/changelogs/upcoming/7676.md
@@ -0,0 +1,5 @@
+- Updated `getDefaultEuiMarkdownPlugins()` to allow excluding the following plugins in addition to `tooltip`:
+ - `checkbox`
+ - `linkValidator`
+ - `lineBreaks`
+ - `emoji`
diff --git a/changelogs/upcoming/7678.md b/changelogs/upcoming/7678.md
new file mode 100644
index 00000000000..e8d28d7fb38
--- /dev/null
+++ b/changelogs/upcoming/7678.md
@@ -0,0 +1,5 @@
+**Bug fixes**
+
+- Visual fixes for `EuiRange`s with `showInput`:
+ - Longer `append`/`prepend` labels no longer cause a background bug
+ - Inputs can no longer overwhelm the actual range in width
diff --git a/changelogs/upcoming/7681.md b/changelogs/upcoming/7681.md
new file mode 100644
index 00000000000..cfc8b0b8ce4
--- /dev/null
+++ b/changelogs/upcoming/7681.md
@@ -0,0 +1,3 @@
+**Bug fixes**
+
+- Fixed a visual text alignment regression in `EuiTableRowCell`s with the `row` header scope
diff --git a/changelogs/upcoming/7683.md b/changelogs/upcoming/7683.md
new file mode 100644
index 00000000000..600f59376ec
--- /dev/null
+++ b/changelogs/upcoming/7683.md
@@ -0,0 +1 @@
+- Updated `EuiSelectable`'s `isPreFiltered` prop to allow passing a configuration object, which allows disabling search highlighting in addition to search filtering
diff --git a/i18ntokens.json b/i18ntokens.json
index b7f7fe182c6..bf47576d721 100644
--- a/i18ntokens.json
+++ b/i18ntokens.json
@@ -127,18 +127,18 @@
},
{
"token": "euiBasicTable.selectThisRow",
- "defString": "Select this row",
+ "defString": "Select row {index}",
"highlighting": "string",
"loc": {
"start": {
- "line": 1103,
+ "line": 1109,
"column": 8,
- "index": 30673
+ "index": 30781
},
"end": {
- "line": 1103,
- "column": 79,
- "index": 30744
+ "line": 1113,
+ "column": 9,
+ "index": 30936
}
},
"filepath": "src/components/basic_table/basic_table.tsx"
@@ -149,14 +149,14 @@
"highlighting": "string",
"loc": {
"start": {
- "line": 1329,
+ "line": 1339,
"column": 8,
- "index": 37489
+ "index": 37663
},
"end": {
- "line": 1333,
+ "line": 1343,
"column": 9,
- "index": 37648
+ "index": 37822
}
},
"filepath": "src/components/basic_table/basic_table.tsx"
diff --git a/package.json b/package.json
index 6f26daecd9c..6407227a07d 100644
--- a/package.json
+++ b/package.json
@@ -84,7 +84,7 @@
"remark-breaks": "^2.0.2",
"remark-emoji": "^2.1.0",
"remark-parse-no-trim": "^8.0.4",
- "remark-rehype": "^8.0.0",
+ "remark-rehype": "^8.1.0",
"tabbable": "^5.3.3",
"text-diff": "^1.0.1",
"unified": "^9.2.2",
@@ -152,7 +152,7 @@
"@wojtekmaj/enzyme-adapter-react-17": "^0.6.6",
"assert": "^2.0.0",
"autoprefixer": "^9.8.6",
- "axe-core": "^4.8.2",
+ "axe-core": "^4.9.0",
"babel-jest": "^24.1.0",
"babel-loader": "^9.1.2",
"babel-plugin-add-module-exports": "^1.0.4",
diff --git a/src-docs/src/views/markdown_editor/markdown_editor.js b/src-docs/src/views/markdown_editor/markdown_editor.js
index 3e6b98a12c0..aa88476100c 100644
--- a/src-docs/src/views/markdown_editor/markdown_editor.js
+++ b/src-docs/src/views/markdown_editor/markdown_editor.js
@@ -21,7 +21,6 @@ The editor also ships with some built in plugins. For example it can handle chec
- [ ] Or empty
It can also handle emojis! :smile:
-
And it can render !{tooltip[tooltips like this](Look! I'm a very helpful tooltip content!)}
`;
diff --git a/src-docs/src/views/markdown_editor/markdown_editor_example.js b/src-docs/src/views/markdown_editor/markdown_editor_example.js
index baa91ddf511..81f795fb07d 100644
--- a/src-docs/src/views/markdown_editor/markdown_editor_example.js
+++ b/src-docs/src/views/markdown_editor/markdown_editor_example.js
@@ -60,7 +60,15 @@ const markdownEditorNoPluginsSnippet = `const {
parsingPlugins,
processingPlugins,
uiPlugins,
-} = getDefaultEuiMarkdownPlugins({ exclude: ['tooltip'] });
+} = getDefaultEuiMarkdownPlugins({
+ exclude: [
+ 'tooltip',
+ 'checkbox',
+ 'linkValidator',
+ 'lineBreaks',
+ 'emoji',
+ ],
+});
- The EuiMarkdownEditor comes with a default plugin for{' '}
- tooltip support. However, this may be unfamiliar or
- unnecessary in some contexts, and you can unregister this plugin by
- excluding it from the
- parsingPlugins ,{' '}
+ The EuiMarkdownEditor comes with several default
+ plugins, demo'd below. If these defaults are unnecessary for your
+ use-case or context, you can unregister these plugins by excluding
+ them from the parsingPlugins ,{' '}
processingPlugins and uiPlugins {' '}
options with a single exclude parameter passed to{' '}
getDefaultEuiMarkdownPlugins() . This will ensure
- the syntax won't be identified or rendered and no additional UI,
- like the button and help syntax, will be displayed.
+ the syntax won't be identified or rendered, and no additional UI (like
+ toolbar buttons or help syntax) will be displayed by the unregistered
+ plugins.
),
props: {
diff --git a/src-docs/src/views/markdown_editor/markdown_editor_no_plugins.js b/src-docs/src/views/markdown_editor/markdown_editor_no_plugins.js
index 5b4dc9b2e64..f77e9207cc0 100644
--- a/src-docs/src/views/markdown_editor/markdown_editor_no_plugins.js
+++ b/src-docs/src/views/markdown_editor/markdown_editor_no_plugins.js
@@ -1,35 +1,112 @@
-import React, { useState } from 'react';
+import React, { useState, useMemo } from 'react';
import {
EuiMarkdownEditor,
getDefaultEuiMarkdownPlugins,
+ EuiFlexGroup,
+ EuiFlexItem,
+ EuiSwitch,
} from '../../../../src/components';
-const initialContent = `## This is how we do it :smile:
+const initialContent = `
+### tooltip
-In this example, we unregistered the built in **tooltip** plugin. So the button in the toolbar and the help syntax won't be displayed.
-
-And the following syntax no longer works.
+When disabled, the button in the toolbar and the help syntax won't be displayed, and the following syntax will no longer works.
!{tooltip[anchor text](Tooltip content)}
-`;
-const { parsingPlugins, processingPlugins, uiPlugins } =
- getDefaultEuiMarkdownPlugins({ exclude: ['tooltip'] });
+### checkbox
+
+When disabled, a EuiCheckbox will no longer render.
+
+- [ ] TODO: Disable some default plugins
+
+### emoji
+
+When disabled, text will render instead of an emoji.
+
+:smile:
+
+### linkValidator
+
+When disabled, all links will render as links instead of as text.
+
+[This is a sus link](file://)
+
+### lineBreaks
+
+When disabled, these sentence will be in the same line.
+When enabled, these sentences will be separated by a line break.
+
+Two blank lines in a row will create a new paragraph as usual.
+`;
export default () => {
const [value, setValue] = useState(initialContent);
+ const [tooltip, excludeTooltips] = useState(false);
+ const [checkbox, excludeCheckboxes] = useState(true);
+ const [emoji, excludeEmojis] = useState(true);
+ const [linkValidator, excludeLinkValidator] = useState(true);
+ const [lineBreaks, excludeLineBreaks] = useState(false);
+
+ const { parsingPlugins, processingPlugins, uiPlugins } = useMemo(() => {
+ const excludedPlugins = [];
+ if (!tooltip) excludedPlugins.push('tooltip');
+ if (!checkbox) excludedPlugins.push('checkbox');
+ if (!emoji) excludedPlugins.push('emoji');
+ if (!linkValidator) excludedPlugins.push('linkValidator');
+ if (!lineBreaks) excludedPlugins.push('lineBreaks');
+
+ return getDefaultEuiMarkdownPlugins({
+ exclude: excludedPlugins,
+ });
+ }, [tooltip, checkbox, emoji, linkValidator, lineBreaks]);
+
return (
<>
-
+
+
+ excludeTooltips(!tooltip)}
+ />
+ excludeCheckboxes(!checkbox)}
+ />
+ excludeEmojis(!emoji)}
+ />
+ excludeLinkValidator(!linkValidator)}
+ />
+ excludeLineBreaks(!lineBreaks)}
+ />
+
+
+
+
+
>
);
};
diff --git a/src-docs/src/views/selectable/selectable_sizing.tsx b/src-docs/src/views/selectable/selectable_sizing.tsx
index db6cb59549f..088824723ad 100644
--- a/src-docs/src/views/selectable/selectable_sizing.tsx
+++ b/src-docs/src/views/selectable/selectable_sizing.tsx
@@ -9,102 +9,154 @@ import {
EuiPopoverFooter,
EuiPopoverTitle,
EuiSelectable,
- EuiSelectableOption,
+ type EuiSelectableOption,
+ type EuiSelectableProps,
EuiSpacer,
EuiTitle,
+ EuiInputPopover,
} from '../../../../src';
-export default () => {
- const [isPopoverOpen, setIsPopoverOpen] = useState(false);
- const [isFlyoutVisible, setIsFlyoutVisible] = useState(false);
+const OPTIONS: EuiSelectableOption[] = [
+ { label: 'Titan' },
+ { label: 'Enceladus' },
+ { label: 'Mimas', checked: 'on' },
+ { label: 'Dione' },
+ { label: 'Iapetus' },
+ { label: 'Phoebe' },
+ { label: 'Rhea' },
+ { label: 'Pandora' },
+ { label: 'Tethys' },
+ { label: 'Hyperion' },
+ { label: 'Pan' },
+ { label: 'Atlas' },
+ { label: 'Prometheus' },
+ { label: 'Janus' },
+ { label: 'Epimetheus' },
+ { label: 'Amalthea' },
+ { label: 'Thebe' },
+ { label: 'Io' },
+ { label: 'Europa' },
+ { label: 'Ganymede' },
+ { label: 'Callisto' },
+ { label: 'Himalia' },
+ { label: 'Phobos' },
+ { label: 'Deimos' },
+ { label: 'Puck' },
+ { label: 'Miranda' },
+ { label: 'Ariel' },
+ { label: 'Umbriel' },
+ { label: 'Titania' },
+ { label: 'Oberon' },
+ { label: 'Despina' },
+ { label: 'Galatea' },
+ { label: 'Larissa' },
+ { label: 'Triton' },
+ { label: 'Nereid' },
+ { label: 'Charon' },
+ { label: 'Styx' },
+ { label: 'Nix' },
+ { label: 'Kerberos' },
+ { label: 'Hydra' },
+];
- const [options, setOptions] = useState([
- { label: 'Titan' },
- { label: 'Enceladus' },
- { label: 'Mimas', checked: 'on' },
- { label: 'Dione' },
- { label: 'Iapetus', checked: 'on' },
- { label: 'Phoebe' },
- { label: 'Rhea' },
- { label: 'Pandora' },
- { label: 'Tethys' },
- { label: 'Hyperion' },
- { label: 'Pan' },
- { label: 'Atlas' },
- { label: 'Prometheus' },
- { label: 'Janus' },
- { label: 'Epimetheus' },
- { label: 'Amalthea' },
- { label: 'Thebe' },
- { label: 'Io' },
- { label: 'Europa' },
- { label: 'Ganymede' },
- { label: 'Callisto' },
- { label: 'Himalia' },
- { label: 'Phobos' },
- { label: 'Deimos' },
- { label: 'Puck' },
- { label: 'Miranda' },
- { label: 'Ariel' },
- { label: 'Umbriel' },
- { label: 'Titania' },
- { label: 'Oberon' },
- { label: 'Despina' },
- { label: 'Galatea' },
- { label: 'Larissa' },
- { label: 'Triton' },
- { label: 'Nereid' },
- { label: 'Charon' },
- { label: 'Styx' },
- { label: 'Nix' },
- { label: 'Kerberos' },
- { label: 'Hydra' },
- ]);
+export default () => {
+ const [options, setOptions] = useState(OPTIONS);
const onChange = (options: EuiSelectableOption[]) => {
setOptions(options);
};
return (
<>
- setIsPopoverOpen(!isPopoverOpen)}
- >
- Show popover
-
- }
- isOpen={isPopoverOpen}
- closePopover={() => setIsPopoverOpen(false)}
+
+
+
+
+
+
+
+ In an input popover
+
+
+
+
+
+
+
+ Using listProps.bordered=true and{' '}
+
+ listProps.paddingSize="none"
+
+
+
+
+
- list}
+
+ >
+ );
+};
+
+const SelectablePopover = (
+ props: Pick
+) => {
+ const [isPopoverOpen, setIsPopoverOpen] = useState(false);
+ const { options, onChange } = props;
+
+ return (
+ setIsPopoverOpen(!isPopoverOpen)}
>
- {(list, search) => (
-
- {search}
- {list}
-
-
- Manage this list
-
-
-
- )}
-
-
+ Show popover
+
+ }
+ isOpen={isPopoverOpen}
+ closePopover={() => setIsPopoverOpen(false)}
+ >
+
+ {(list, search) => (
+
+ {search}
+ {list}
+
+
+ Manage this list
+
+
+
+ )}
+
+
+ );
+};
-
+const SelectableFlyout = (
+ props: Pick
+) => {
+ const [isFlyoutVisible, setIsFlyoutVisible] = useState(false);
+ const { options, onChange } = props;
+ return (
+ <>
setIsFlyoutVisible(true)}>
Show flyout
@@ -116,7 +168,7 @@ export default () => {
aria-labelledby="selectableFlyout"
>
{
)}
+ >
+ );
+};
-
-
-
-
- Using listProps.bordered=true and{' '}
-
- listProps.paddingSize="none"
-
-
-
+const SelectableInputPopover = () => {
+ const [options, setOptions] = useState(OPTIONS);
+ const [isOpen, setIsOpen] = useState(false);
+ const [inputValue, setInputValue] = useState('');
+ const [isSearching, setIsSearching] = useState(true);
-
+ return (
+ {
+ setOptions(newOptions);
+ setIsOpen(false);
-
- {(list) => list}
-
- >
+ if (changedOption.checked === 'on') {
+ setInputValue(changedOption.label);
+ setIsSearching(false);
+ } else {
+ setInputValue('');
+ }
+ }}
+ singleSelection
+ searchable
+ searchProps={{
+ value: inputValue,
+ onChange: (value) => {
+ setInputValue(value);
+ setIsSearching(true);
+ },
+ onKeyDown: (event) => {
+ if (event.key === 'Tab') return setIsOpen(false);
+ if (event.key !== 'Escape') return setIsOpen(true);
+ },
+ onClick: () => setIsOpen(true),
+ onFocus: () => setIsOpen(true),
+ }}
+ isPreFiltered={isSearching ? false : { highlightSearch: false }} // Shows the full list when not actively typing to search
+ listProps={{
+ css: { '.euiSelectableList__list': { maxBlockSize: 200 } },
+ }}
+ >
+ {(list, search) => (
+ setIsOpen(false)}
+ disableFocusTrap
+ closeOnScroll
+ isOpen={isOpen}
+ input={search!}
+ panelPaddingSize="none"
+ >
+ {list}
+
+ )}
+
);
};
diff --git a/src-docs/src/views/tables/auto/auto.tsx b/src-docs/src/views/tables/auto/auto.tsx
index e0f98b12dc5..c82018ec59a 100644
--- a/src-docs/src/views/tables/auto/auto.tsx
+++ b/src-docs/src/views/tables/auto/auto.tsx
@@ -217,6 +217,7 @@ export default () => {
tableCaption="Demo of EuiBasicTable's table layout options"
items={users}
columns={columns}
+ rowHeader="firstName"
tableLayout={tableLayout === 'tableLayoutAuto' ? 'auto' : 'fixed'}
/>
>
diff --git a/src/components/basic_table/__snapshots__/basic_table.test.tsx.snap b/src/components/basic_table/__snapshots__/basic_table.test.tsx.snap
index 3fa583c6f2a..741253a831f 100644
--- a/src/components/basic_table/__snapshots__/basic_table.test.tsx.snap
+++ b/src/components/basic_table/__snapshots__/basic_table.test.tsx.snap
@@ -271,11 +271,11 @@ exports[`EuiBasicTable renders (kitchen sink) with pagination, selection, sortin
class="euiCheckbox euiCheckbox--inList euiCheckbox--noLabel emotion-euiCheckbox"
>
extends Component<
hasPagination(this.props) && this.pageSize > 0
? this.props.pagination.pageIndex * this.pageSize + index
: index;
- return this.renderItemRow(item, tableItemIndex);
+ return this.renderItemRow(item, tableItemIndex, index);
});
}
@@ -950,7 +950,7 @@ export class EuiBasicTable
extends Component<
);
}
- renderItemRow(item: T, rowIndex: number) {
+ renderItemRow(item: T, rowIndex: number, displayedRowIndex: number) {
const { columns, selection, rowHeader, itemIdToExpandedRowMap } =
this.props;
@@ -974,7 +974,8 @@ export class EuiBasicTable extends Component<
const [checkboxCell, isDisabled] = this.renderItemSelectionCell(
itemId,
item,
- selected
+ selected,
+ displayedRowIndex
);
cells.push(checkboxCell);
rowSelectionDisabled = !!isDisabled;
@@ -1075,7 +1076,12 @@ export class EuiBasicTable extends Component<
);
}
- renderItemSelectionCell(itemId: ItemId, item: T, selected: boolean) {
+ renderItemSelectionCell(
+ itemId: ItemId,
+ item: T,
+ selected: boolean,
+ displayedRowIndex: number
+ ) {
const { selection } = this.props;
const key = `_selection_column_${itemId}`;
const checked = selected;
@@ -1100,7 +1106,11 @@ export class EuiBasicTable extends Component<
};
return [
-
+
{(selectThisRow: string) => (
{
display: flex;
align-items: center;
+ /* TODO: When converting forms to Emotion, allow passing wrapperProps
+ to EuiFieldNumber and then move this CSS to range_input.styles.ts */
> .euiFormControlLayout {
- /* There's no way to target the layout of the extra input, so we must
- * use the descendant selector to allow the width to shrink. */
+ /* Allow the width to shrink */
inline-size: auto;
+ /* Don't allow inputs to overwhelm the actual range in width */
+ max-inline-size: 50%;
+
+ /* The input should take priority over prepend/append labels */
+ .euiFormControlLayout__childrenWrapper {
+ flex-shrink: 0;
+ }
+
+ .euiFormControlLayout__prepend,
+ .euiFormControlLayout__append {
+ flex-shrink: 1;
+ /* Remove the default 50% max-width on prepend/appends, as a max-width is
+ already set on the wrapper, and a static width already set on the input */
+ max-inline-size: none;
+ }
}
`,
regular: css`
diff --git a/src/components/markdown_editor/plugins/markdown_default_plugins/parsing_plugins.ts b/src/components/markdown_editor/plugins/markdown_default_plugins/parsing_plugins.ts
index b2a9e708b79..d0ebcb12d82 100644
--- a/src/components/markdown_editor/plugins/markdown_default_plugins/parsing_plugins.ts
+++ b/src/components/markdown_editor/plugins/markdown_default_plugins/parsing_plugins.ts
@@ -32,30 +32,40 @@ import {
euiMarkdownLinkValidator,
EuiMarkdownLinkValidatorOptions,
} from '../markdown_link_validator';
+import type { ExcludableDefaultPlugins, DefaultPluginsConfig } from './plugins';
export type DefaultEuiMarkdownParsingPlugins = PluggableList;
+const DEFAULT_PARSING_PLUGINS: Record<
+ ExcludableDefaultPlugins,
+ DefaultEuiMarkdownParsingPlugins[0]
+> = {
+ emoji: [emoji, { emoticon: false }],
+ lineBreaks: [breaks, {}],
+ linkValidator: [
+ euiMarkdownLinkValidator,
+ {
+ allowRelative: true,
+ allowProtocols: ['https:', 'http:', 'mailto:'],
+ } as EuiMarkdownLinkValidatorOptions,
+ ],
+ checkbox: [MarkdownCheckbox.parser, {}],
+ tooltip: [MarkdownTooltip.parser, {}],
+};
+
export const getDefaultEuiMarkdownParsingPlugins = ({
exclude,
-}: { exclude?: Array<'tooltip'> } = {}): DefaultEuiMarkdownParsingPlugins => {
- const excludeSet = new Set(exclude);
+}: DefaultPluginsConfig = {}): DefaultEuiMarkdownParsingPlugins => {
const parsingPlugins: PluggableList = [
[markdown, {}],
[highlight, {}],
- [emoji, { emoticon: false }],
- [breaks, {}],
- [
- euiMarkdownLinkValidator,
- {
- allowRelative: true,
- allowProtocols: ['https:', 'http:', 'mailto:'],
- } as EuiMarkdownLinkValidatorOptions,
- ],
- [MarkdownCheckbox.parser, {}],
];
- if (!excludeSet.has('tooltip'))
- parsingPlugins.push([MarkdownTooltip.parser, {}]);
+ Object.entries(DEFAULT_PARSING_PLUGINS).forEach(([pluginName, plugin]) => {
+ if (!exclude?.includes(pluginName as ExcludableDefaultPlugins)) {
+ parsingPlugins.push(plugin);
+ }
+ });
return parsingPlugins;
};
diff --git a/src/components/markdown_editor/plugins/markdown_default_plugins/plugins.test.ts b/src/components/markdown_editor/plugins/markdown_default_plugins/plugins.test.ts
new file mode 100644
index 00000000000..211b95a6f76
--- /dev/null
+++ b/src/components/markdown_editor/plugins/markdown_default_plugins/plugins.test.ts
@@ -0,0 +1,62 @@
+/*
+ * 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 { getDefaultEuiMarkdownPlugins } from './plugins';
+
+describe('default plugins', () => {
+ test('no exclusions', () => {
+ const { parsingPlugins, processingPlugins, uiPlugins } =
+ getDefaultEuiMarkdownPlugins();
+
+ expect(parsingPlugins).toHaveLength(7);
+ expect(Object.keys(processingPlugins[1][1].components)).toHaveLength(8);
+ expect(uiPlugins).toHaveLength(1);
+
+ expect(processingPlugins[1][1].components.tooltipPlugin).toBeDefined();
+ expect(processingPlugins[1][1].components.checkboxPlugin).toBeDefined();
+ });
+
+ test('exclude tooltips', () => {
+ const { parsingPlugins, processingPlugins, uiPlugins } =
+ getDefaultEuiMarkdownPlugins({
+ exclude: ['tooltip'],
+ });
+
+ expect(parsingPlugins).toHaveLength(6);
+ expect(processingPlugins[1][1].components.tooltipPlugin).toBeUndefined();
+ expect(uiPlugins).toHaveLength(0);
+ });
+
+ test('exclude checkboxes', () => {
+ const { parsingPlugins, processingPlugins, uiPlugins } =
+ getDefaultEuiMarkdownPlugins({
+ exclude: ['checkbox'],
+ });
+
+ expect(parsingPlugins).toHaveLength(6);
+ expect(processingPlugins[1][1].components.checkboxPlugin).toBeUndefined();
+ expect(uiPlugins).toHaveLength(1);
+ });
+
+ test('all exclusions', () => {
+ const { parsingPlugins, processingPlugins, uiPlugins } =
+ getDefaultEuiMarkdownPlugins({
+ exclude: [
+ 'tooltip',
+ 'checkbox',
+ 'lineBreaks',
+ 'linkValidator',
+ 'emoji',
+ ],
+ });
+
+ expect(parsingPlugins).toHaveLength(2);
+ expect(Object.keys(processingPlugins[1][1].components)).toHaveLength(6);
+ expect(uiPlugins).toHaveLength(0);
+ });
+});
diff --git a/src/components/markdown_editor/plugins/markdown_default_plugins/plugins.ts b/src/components/markdown_editor/plugins/markdown_default_plugins/plugins.ts
index 77c658f14c2..9a8c3001d61 100644
--- a/src/components/markdown_editor/plugins/markdown_default_plugins/plugins.ts
+++ b/src/components/markdown_editor/plugins/markdown_default_plugins/plugins.ts
@@ -19,8 +19,19 @@ import {
DefaultEuiMarkdownProcessingPlugins,
} from './processing_plugins';
+export type ExcludableDefaultPlugins =
+ | 'emoji'
+ | 'lineBreaks'
+ | 'linkValidator'
+ | 'checkbox'
+ | 'tooltip';
+
+export type DefaultPluginsConfig =
+ | undefined
+ | { exclude?: ExcludableDefaultPlugins[] };
+
export const getDefaultEuiMarkdownPlugins = (
- config: undefined | { exclude?: Array<'tooltip'> }
+ config?: DefaultPluginsConfig
): {
parsingPlugins: DefaultEuiMarkdownParsingPlugins;
processingPlugins: DefaultEuiMarkdownProcessingPlugins;
diff --git a/src/components/markdown_editor/plugins/markdown_default_plugins/processing_plugins.tsx b/src/components/markdown_editor/plugins/markdown_default_plugins/processing_plugins.tsx
index af79901d268..04c575d826a 100644
--- a/src/components/markdown_editor/plugins/markdown_default_plugins/processing_plugins.tsx
+++ b/src/components/markdown_editor/plugins/markdown_default_plugins/processing_plugins.tsx
@@ -28,13 +28,16 @@ import { Options as Remark2RehypeOptions, Handler } from 'mdast-util-to-hast';
import all from 'mdast-util-to-hast/lib/all';
import rehype2react from 'rehype-react';
import remark2rehype from 'remark-rehype';
-import * as MarkdownTooltip from '../markdown_tooltip';
-import * as MarkdownCheckbox from '../markdown_checkbox';
-import { FENCED_CLASS } from '../remark/remark_prismjs';
+
import { EuiLink } from '../../../link';
import { EuiCodeBlock, EuiCode } from '../../../code';
import { EuiHorizontalRule } from '../../../horizontal_rule';
+import { FENCED_CLASS } from '../remark/remark_prismjs';
+import * as MarkdownTooltip from '../markdown_tooltip';
+import * as MarkdownCheckbox from '../markdown_checkbox';
+import type { ExcludableDefaultPlugins, DefaultPluginsConfig } from './plugins';
+
const unknownHandler: Handler = (h, node) => {
return h(node, node.type, node, all(h, node));
};
@@ -50,12 +53,26 @@ export type DefaultEuiMarkdownProcessingPlugins = [
...PluggableList // any additional are generic
];
+const DEFAULT_COMPONENT_RENDERERS: Partial<
+ Record>
+> = {
+ checkbox: MarkdownCheckbox.renderer,
+ tooltip: MarkdownTooltip.renderer,
+};
+
export const getDefaultEuiMarkdownProcessingPlugins = ({
exclude,
-}: {
- exclude?: Array<'tooltip'>;
-} = {}): DefaultEuiMarkdownProcessingPlugins => {
- const excludeSet = new Set(exclude);
+}: DefaultPluginsConfig = {}): DefaultEuiMarkdownProcessingPlugins => {
+ const componentPluginsWithExclusions: Rehype2ReactOptions['components'] = {};
+
+ Object.entries(DEFAULT_COMPONENT_RENDERERS).forEach(
+ ([excludeName, renderer]) => {
+ if (!exclude?.includes(excludeName as ExcludableDefaultPlugins)) {
+ const pluginName = `${excludeName}Plugin`;
+ componentPluginsWithExclusions[pluginName] = renderer;
+ }
+ }
+ );
const plugins: DefaultEuiMarkdownProcessingPlugins = [
[
@@ -99,15 +116,12 @@ export const getDefaultEuiMarkdownProcessingPlugins = ({
),
hr: (props) => ,
- checkboxPlugin: MarkdownCheckbox.renderer,
+ ...componentPluginsWithExclusions,
},
},
],
];
- if (!excludeSet.has('tooltip'))
- plugins[1][1].components.tooltipPlugin = MarkdownTooltip.renderer;
-
return plugins;
};
diff --git a/src/components/markdown_editor/plugins/markdown_default_plugins/ui_plugins.ts b/src/components/markdown_editor/plugins/markdown_default_plugins/ui_plugins.ts
index f5dc92cb9c8..131f3daed9a 100644
--- a/src/components/markdown_editor/plugins/markdown_default_plugins/ui_plugins.ts
+++ b/src/components/markdown_editor/plugins/markdown_default_plugins/ui_plugins.ts
@@ -6,18 +6,28 @@
* Side Public License, v 1.
*/
-import * as MarkdownTooltip from '../markdown_tooltip';
import { EuiMarkdownEditorUiPlugin } from './../../markdown_types';
+import * as MarkdownTooltip from '../markdown_tooltip';
+import type { ExcludableDefaultPlugins, DefaultPluginsConfig } from './plugins';
export type DefaultEuiMarkdownUiPlugins = EuiMarkdownEditorUiPlugin[];
+const DEFAULT_UI_PLUGINS: Partial<
+ Record
+> = {
+ tooltip: MarkdownTooltip.plugin,
+};
+
export const getDefaultEuiMarkdownUiPlugins = ({
exclude,
-}: { exclude?: Array<'tooltip'> } = {}): DefaultEuiMarkdownUiPlugins => {
- const excludeSet = new Set(exclude);
- const uiPlugins = [];
+}: DefaultPluginsConfig = {}): DefaultEuiMarkdownUiPlugins => {
+ const uiPlugins: EuiMarkdownEditorUiPlugin[] = [];
- if (!excludeSet.has('tooltip')) uiPlugins.push(MarkdownTooltip.plugin);
+ Object.entries(DEFAULT_UI_PLUGINS).forEach(([pluginName, plugin]) => {
+ if (!exclude?.includes(pluginName as ExcludableDefaultPlugins)) {
+ uiPlugins.push(plugin);
+ }
+ });
return uiPlugins;
};
diff --git a/src/components/selectable/selectable.tsx b/src/components/selectable/selectable.tsx
index 938b8788a4a..819e265570e 100644
--- a/src/components/selectable/selectable.tsx
+++ b/src/components/selectable/selectable.tsx
@@ -168,10 +168,17 @@ export type EuiSelectableProps = CommonProps &
*/
errorMessage?: ReactElement | string | null;
/**
- * Control whether or not options get filtered internally or if consumer will filter
- * Default: false
+ * Control whether or not options get filtered internally (i.e., whether filtering is
+ * handled by EUI or by you, the consumer).
+ * If set to `true`, all passed `options` will be displayed regardless of the user's
+ * search input.
+ *
+ * Additionally allows passing a configuration object which enables turning off
+ * search highlighting if needed.
+ *
+ * @default false
*/
- isPreFiltered?: boolean;
+ isPreFiltered?: boolean | { highlightSearch?: boolean };
/**
* Optional screen reader instructions to announce upon focus/interaction. This text is read out
* after the `EuiSelectable` label and a brief pause, but before the default keyboard instructions for
@@ -222,7 +229,7 @@ export class EuiSelectable extends Component<
const visibleOptions = getMatchingOptions(
options,
initialSearchValue,
- isPreFiltered
+ !!isPreFiltered
);
searchProps?.onChange?.(initialSearchValue, visibleOptions);
@@ -262,7 +269,7 @@ export class EuiSelectable extends Component<
stateUpdate.visibleOptions = getMatchingOptions(
options,
stateUpdate.searchValue ?? '',
- isPreFiltered
+ !!isPreFiltered
);
if (
@@ -482,7 +489,7 @@ export class EuiSelectable extends Component<
const visibleOptions = getMatchingOptions(
options,
searchValue,
- isPreFiltered
+ !!isPreFiltered
);
this.setState({ visibleOptions });
@@ -712,7 +719,7 @@ export class EuiSelectable extends Component<
listId={this.optionsListRef.current ? this.listId : undefined} // Only pass the listId if it exists on the page
aria-activedescendant={this.makeOptionId(activeOptionIndex)} // the current faux-focused option
placeholder={placeholderName}
- isPreFiltered={isPreFiltered ?? false}
+ isPreFiltered={!!isPreFiltered}
inputRef={(node) => {
this.inputRef = node;
searchProps?.inputRef?.(node);
@@ -781,6 +788,7 @@ export class EuiSelectable extends Component<
options={options}
visibleOptions={visibleOptions}
searchValue={searchValue}
+ isPreFiltered={isPreFiltered}
activeOptionIndex={activeOptionIndex}
setActiveOptionIndex={(index, cb) => {
this.setState({ activeOptionIndex: index }, cb);
diff --git a/src/components/selectable/selectable_list/selectable_list.test.tsx b/src/components/selectable/selectable_list/selectable_list.test.tsx
index 897721906bd..aa6484b7d88 100644
--- a/src/components/selectable/selectable_list/selectable_list.test.tsx
+++ b/src/components/selectable/selectable_list/selectable_list.test.tsx
@@ -99,6 +99,19 @@ describe('EuiSelectableListItem', () => {
container.querySelector('.euiTextTruncate')
);
});
+
+ it('does not highlight/mark the current `searchValue` if `isPreFiltered.highlightSearch` is false', () => {
+ const { container } = render(
+
+ );
+
+ expect(container.querySelector('.euiMark')).not.toBeInTheDocument();
+ });
});
test('renderOption', () => {
diff --git a/src/components/selectable/selectable_list/selectable_list.tsx b/src/components/selectable/selectable_list/selectable_list.tsx
index 5f9a4a540b7..874344e333e 100644
--- a/src/components/selectable/selectable_list/selectable_list.tsx
+++ b/src/components/selectable/selectable_list/selectable_list.tsx
@@ -31,8 +31,11 @@ import { EuiHighlight } from '../../highlight';
import { EuiMark } from '../../mark';
import { EuiTextTruncate } from '../../text_truncate';
-import { EuiSelectableOption } from '../selectable_option';
-import { EuiSelectableOnChangeEvent } from '../selectable';
+import type { EuiSelectableOption } from '../selectable_option';
+import type {
+ EuiSelectableOnChangeEvent,
+ EuiSelectableProps,
+} from '../selectable';
import {
EuiSelectableListItem,
EuiSelectableListItemProps,
@@ -153,6 +156,7 @@ export type EuiSelectableListProps = EuiSelectableOptionsListProps & {
*/
allowExclusions?: boolean;
searchable?: boolean;
+ isPreFiltered?: EuiSelectableProps['isPreFiltered'];
makeOptionId: (index: number | undefined) => string;
listId: string;
setActiveOptionIndex: (index: number, cb?: () => void) => void;
@@ -329,6 +333,7 @@ export class EuiSelectableList extends Component<
setActiveOptionIndex,
searchable,
searchValue,
+ isPreFiltered,
isVirtualized,
} = this.props;
@@ -351,6 +356,14 @@ export class EuiSelectableList extends Component<
const id = makeOptionId(index);
const isFocused = activeOptionIndex === index;
+ // Search highlighting
+ const hasSearch = !!searchValue;
+ const highlightSearch =
+ hasSearch &&
+ (typeof isPreFiltered === 'object'
+ ? isPreFiltered.highlightSearch !== false
+ : true);
+
// Text wrapping
const canWrap = !isVirtualized;
const _textWrap = option.textWrap ?? this.props.textWrap;
@@ -359,7 +372,7 @@ export class EuiSelectableList extends Component<
// Truncation config (if any). If none, CSS truncation is used
const truncationProps =
textWrap === 'truncate'
- ? this.getTruncationProps(option, isFocused)
+ ? this.getTruncationProps(option, highlightSearch, isFocused)
: undefined;
return (
@@ -397,7 +410,7 @@ export class EuiSelectableList extends Component<
{ ..._option, ...optionData },
searchValue
)
- : searchValue
+ : highlightSearch
? this.renderSearchedText(label, truncationProps)
: truncationProps
? this.renderTruncatedText(label, truncationProps)
@@ -513,7 +526,11 @@ export class EuiSelectableList extends Component<
});
};
- getTruncationProps = (option: EuiSelectableOption, isFocused: boolean) => {
+ getTruncationProps = (
+ option: EuiSelectableOption,
+ highlightSearch: boolean,
+ isFocused: boolean
+ ) => {
// Individual truncation settings should override component-wide settings
const truncationProps = {
...this.props.truncationProps,
@@ -522,7 +539,7 @@ export class EuiSelectableList extends Component<
// If we're not actually using EuiTextTruncate, no need to continue
const hasComplexTruncation =
- this.props.searchValue || Object.keys(truncationProps).length > 0;
+ highlightSearch || Object.keys(truncationProps).length > 0;
if (!hasComplexTruncation) return undefined;
// Determine whether we can use the optimized default option width
@@ -618,6 +635,7 @@ export class EuiSelectableList extends Component<
'aria-labelledby': ariaLabelledby,
'aria-describedby': ariaDescribedby,
role,
+ isPreFiltered,
isVirtualized,
textWrap,
truncationProps,
diff --git a/src/components/table/table_row_cell.styles.ts b/src/components/table/table_row_cell.styles.ts
index 0ef75bb310e..1524075e5ca 100644
--- a/src/components/table/table_row_cell.styles.ts
+++ b/src/components/table/table_row_cell.styles.ts
@@ -14,6 +14,7 @@ import {
euiFontSize,
euiTextTruncate,
logicalCSS,
+ logicalTextAlignCSS,
} from '../../global_styling';
import { euiTableVariables } from './table.styles';
@@ -32,8 +33,9 @@ export const euiTableRowCellStyles = (euiThemeContext: UseEuiTheme) => {
color: ${euiTheme.colors.text};
`,
rowHeader: css`
- /* Unset the automatic browser bolding applied to [th] elements */
+ /* Unset the automatic browser bolding and center alignment applied to [th] elements */
font-weight: ${euiTheme.font.weight.regular};
+ ${logicalTextAlignCSS('left')}
`,
isExpander: css`
${hasIcons}
diff --git a/yarn.lock b/yarn.lock
index 7d5af2fd5da..7e311289a6d 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -6535,10 +6535,10 @@ axe-core@^4.6.2:
resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.7.2.tgz#040a7342b20765cb18bb50b628394c21bccc17a0"
integrity sha512-zIURGIS1E1Q4pcrMjp+nnEh+16G56eG/MUllJH8yEvw7asDo7Ac9uhC9KIH5jzpITueEZolfYglnCGIuSBz39g==
-axe-core@^4.8.2:
- version "4.8.2"
- resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.8.2.tgz#2f6f3cde40935825cf4465e3c1c9e77b240ff6ae"
- integrity sha512-/dlp0fxyM3R8YW7MFzaHWXrf4zzbr0vaYb23VBFCl83R7nWNPg/yaQw2Dc8jzCMmDVLhSdzH8MjrsuIUuvX+6g==
+axe-core@^4.9.0:
+ version "4.9.0"
+ resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.9.0.tgz#b18971494551ab39d4ff5f7d4c6411bd20cc7c2a"
+ integrity sha512-H5orY+M2Fr56DWmMFpMrq5Ge93qjNdPVqzBv5gWK3aD1OvjBEJlEzxf09z93dGVQeI0LiW+aCMIx1QtShC/zUw==
axios@^0.18.0, axios@^0.18.1:
version "0.18.1"
@@ -15107,20 +15107,6 @@ mdast-util-definitions@^4.0.0:
dependencies:
unist-util-visit "^2.0.0"
-mdast-util-to-hast@^10.0.0:
- version "10.0.0"
- resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-10.0.0.tgz#744dfe7907bac0263398a68af5aba16d104a9a08"
- integrity sha512-dRyAC5S4eDcIOdkz4jg0wXbUdlf+5YFu7KppJNHOsMaD7ql5bKIqVcvXYYkcrKjzUkfX8JsKFVMthsU8OWxQ+w==
- dependencies:
- "@types/mdast" "^3.0.0"
- "@types/unist" "^2.0.0"
- mdast-util-definitions "^4.0.0"
- mdurl "^1.0.0"
- unist-builder "^2.0.0"
- unist-util-generated "^1.0.0"
- unist-util-position "^3.0.0"
- unist-util-visit "^2.0.0"
-
mdast-util-to-hast@^10.2.0:
version "10.2.0"
resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-10.2.0.tgz#61875526a017d8857b71abc9333942700b2d3604"
@@ -18965,12 +18951,12 @@ remark-parse-no-trim@^8.0.4:
vfile-location "^3.0.0"
xtend "^4.0.1"
-remark-rehype@^8.0.0:
- version "8.0.0"
- resolved "https://registry.yarnpkg.com/remark-rehype/-/remark-rehype-8.0.0.tgz#5a8afc8262a59d205fba21dafb27a673fb3b92fa"
- integrity sha512-gVvOH02TMFqXOWoL6iXU7NXMsDJguNkNuMrzfkQeA4V6WCyHQnOKptn+IQBVVPuIH2sMJBwo8hlrmtn1MLTh9w==
+remark-rehype@^8.1.0:
+ version "8.1.0"
+ resolved "https://registry.yarnpkg.com/remark-rehype/-/remark-rehype-8.1.0.tgz#610509a043484c1e697437fa5eb3fd992617c945"
+ integrity sha512-EbCu9kHgAxKmW1yEYjx3QafMyGY3q8noUbNUI5xyKbaFP89wbhDrKxyIQNukNYthzjNHZu6J7hwFg7hRm1svYA==
dependencies:
- mdast-util-to-hast "^10.0.0"
+ mdast-util-to-hast "^10.2.0"
remote-origin-url@^0.4.0:
version "0.4.0"
@@ -20329,7 +20315,7 @@ string-template@~0.2.1:
resolved "https://registry.yarnpkg.com/string-template/-/string-template-0.2.1.tgz#42932e598a352d01fc22ec3367d9d84eec6c9add"
integrity sha1-QpMuWYo1LQH8IuwzZ9nYTuxsmt0=
-"string-width-cjs@npm:string-width@^4.2.0":
+"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.2.2, string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@@ -20356,15 +20342,6 @@ string-width@^1.0.1:
is-fullwidth-code-point "^1.0.0"
strip-ansi "^3.0.0"
-"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.2.2, string-width@^4.2.3:
- version "4.2.3"
- resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
- integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
- dependencies:
- emoji-regex "^8.0.0"
- is-fullwidth-code-point "^3.0.0"
- strip-ansi "^6.0.1"
-
string-width@^2.0.0, string-width@^2.1.0, string-width@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e"
@@ -20518,7 +20495,7 @@ stringify-entities@^3.0.1:
is-decimal "^1.0.2"
is-hexadecimal "^1.0.0"
-"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
+"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
@@ -20546,13 +20523,6 @@ strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0:
dependencies:
ansi-regex "^4.1.0"
-strip-ansi@^6.0.0, strip-ansi@^6.0.1:
- version "6.0.1"
- resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
- integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
- dependencies:
- ansi-regex "^5.0.1"
-
strip-ansi@^7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.0.1.tgz#61740a08ce36b61e50e65653f07060d000975fb2"
@@ -22708,7 +22678,7 @@ worker-farm@^1.7.0:
dependencies:
errno "~0.1.7"
-"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
+"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
@@ -22743,15 +22713,6 @@ wrap-ansi@^6.2.0:
string-width "^4.1.0"
strip-ansi "^6.0.0"
-wrap-ansi@^7.0.0:
- version "7.0.0"
- resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
- integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
- dependencies:
- ansi-styles "^4.0.0"
- string-width "^4.1.0"
- strip-ansi "^6.0.0"
-
wrap-ansi@^8.0.1:
version "8.0.1"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.0.1.tgz#2101e861777fec527d0ea90c57c6b03aac56a5b3"