Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
## [`main`](https://github.com/elastic/eui/tree/main)

- Added virtulized rendering option to `EuiSelectableList` with `isVirtualized` ([#5521](https://github.com/elastic/eui/pull/5521))
- Added expanded option properties to `EuiSelectableOption` with `data` ([#5521](https://github.com/elastic/eui/pull/5521))

**Breaking changes**

- Changed `EuiSearchBar` to preserve phrases with leading and trailing spaces, instead of dropping surrounding whitespace ([#5514](https://github.com/elastic/eui/pull/5514))
Expand Down
5 changes: 4 additions & 1 deletion src-docs/src/views/selectable/selectable_custom_render.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ export default () => {
prepend: country.flag,
append: <EuiBadge>{country.code}</EuiBadge>,
showIcons: false,
data: {
secondaryContent: 'I am secondary content, I am!',
},
};
});

Expand All @@ -46,7 +49,7 @@ export default () => {
<EuiTextColor color="subdued">
<small>
<EuiHighlight search={searchValue}>
I am secondary content, I am!
{option.secondaryContent}
</EuiHighlight>
</small>
</EuiTextColor>
Expand Down
17 changes: 14 additions & 3 deletions src-docs/src/views/selectable/selectable_example.js
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,20 @@ export const SelectableExample = {
single <EuiCode>option</EuiCode> object and the{' '}
<EuiCode>searchValue</EuiCode> to use for highlighting.
</p>
<p>
To provide data that can be used by the{' '}
<EuiCode>renderOption</EuiCode> function that does not match the
standard option API, use <EuiCode>option.data</EuiCode> which will
make custom data available in the <EuiCode>option</EuiCode>{' '}
parameter. See the <EuiCode>secondaryContent</EuiCode> configuration
in the following example.
</p>
<p>
<strong>Every row must be the same height</strong> unless{' '}
<EuiCode>listProps.isVirtualized</EuiCode> is set to{' '}
<EuiCode>false</EuiCode>, in which case we recommend having a large
enough container to accomodate all optons and eliminate scrolling.
</p>
<p>
In order for the list to know how to scroll to the selected or
highlighted option, it must also know the height of the rows. It
Expand All @@ -364,9 +378,6 @@ export const SelectableExample = {
you will need to recalculate this height and apply it via{' '}
<EuiCode>listProps.rowHeight</EuiCode>.
</p>
<p>
<strong>Every row must be the same height.</strong>
</p>
</Fragment>
),
demo: <SelectableCustomRender />,
Expand Down
102 changes: 102 additions & 0 deletions src/components/selectable/__snapshots__/selectable.test.tsx.snap
Original file line number Diff line number Diff line change
@@ -1,5 +1,107 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`EuiSelectable custom options with data 1`] = `
<div
class="euiSelectable"
>
<div
class="euiSelectableList"
>
<div
data-eui="EuiAutoSizer"
>
<div
class="euiSelectableList__list"
style="position:relative;height:96px;width:600px;overflow:auto;-webkit-overflow-scrolling:touch;will-change:transform;direction:ltr"
>
<ul
style="height:96px;width:100%"
>
<li
aria-posinset="1"
aria-selected="false"
aria-setsize="3"
class="euiSelectableListItem"
id="generated-id_listbox_option-0"
role="option"
style="position:absolute;left:0;top:0;height:32px;width:100%"
title="Titan"
>
<span
class="euiSelectableListItem__content"
>
<span
class="euiSelectableListItem__icon"
data-euiicon-type="empty"
/>
<span
class="euiSelectableListItem__text"
>
<span>
VI: Titan
</span>
</span>
</span>
</li>
<li
aria-posinset="2"
aria-selected="false"
aria-setsize="3"
class="euiSelectableListItem"
id="generated-id_listbox_option-1"
role="option"
style="position:absolute;left:0;top:32px;height:32px;width:100%"
title="Enceladus"
>
<span
class="euiSelectableListItem__content"
>
<span
class="euiSelectableListItem__icon"
data-euiicon-type="empty"
/>
<span
class="euiSelectableListItem__text"
>
<span>
II: Enceladus
</span>
</span>
</span>
</li>
<li
aria-posinset="3"
aria-selected="false"
aria-setsize="3"
class="euiSelectableListItem"
id="generated-id_listbox_option-2"
role="option"
style="position:absolute;left:0;top:64px;height:32px;width:100%"
title="Pandora is one of Saturn's moons, named for a Titaness of Greek mythology"
>
<span
class="euiSelectableListItem__content"
>
<span
class="euiSelectableListItem__icon"
data-euiicon-type="empty"
/>
<span
class="euiSelectableListItem__text"
>
<span>
XVII: Pandora is one of Saturn's moons, named for a Titaness of Greek mythology
</span>
</span>
</span>
</li>
</ul>
</div>
</div>
</div>
</div>
`;

exports[`EuiSelectable is rendered 1`] = `
<div
class="euiSelectable testClass1 testClass2"
Expand Down
43 changes: 43 additions & 0 deletions src/components/selectable/selectable.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -208,5 +208,48 @@ describe('EuiSelectable', () => {
(component.find('EuiSelectableList').props() as any).visibleOptions
).toEqual(options);
});

test('with data', () => {
type WithData = {
numeral?: string;
};
const options = [
{
label: 'Titan',
data: {
numeral: 'VI',
},
},
{
label: 'Enceladus',
data: {
numeral: 'II',
},
},
{
label:
"Pandora is one of Saturn's moons, named for a Titaness of Greek mythology",
data: {
numeral: 'XVII',
},
},
];
const component = render(
<EuiSelectable<WithData>
options={options}
renderOption={(option) => {
return (
<span>
{option.numeral}: {option.label}
</span>
);
}}
>
{(list) => list}
</EuiSelectable>
);

expect(component).toMatchSnapshot();
});
});
});
23 changes: 21 additions & 2 deletions src/components/selectable/selectable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ import classNames from 'classnames';
import { CommonProps, ExclusiveUnion } from '../common';
import { EuiSelectableSearch } from './selectable_search';
import { EuiSelectableMessage } from './selectable_message';
import { EuiSelectableList } from './selectable_list';
import {
EuiSelectableList,
EuiSelectableOptionsListVirtualizedProps,
} from './selectable_list';
import { EuiLoadingSpinner } from '../loading';
import { EuiSpacer } from '../spacer';
import { getMatchingOptions } from './matching_options';
Expand Down Expand Up @@ -434,8 +437,23 @@ export class EuiSelectable<T = {}> extends Component<
const {
'aria-label': listAriaLabel,
'aria-describedby': listAriaDescribedby,
isVirtualized,
rowHeight,
...cleanedListProps
} = listProps || unknownAccessibleName;
} = (listProps || unknownAccessibleName) as typeof listProps &
typeof unknownAccessibleName;

let virtualizedProps: EuiSelectableOptionsListVirtualizedProps;

if (isVirtualized === false) {
virtualizedProps = {
isVirtualized,
};
} else if (rowHeight != null) {
virtualizedProps = {
rowHeight,
};
}

const classes = classNames(
'euiSelectable',
Expand Down Expand Up @@ -629,6 +647,7 @@ export class EuiSelectable<T = {}> extends Component<
? listAccessibleName
: searchable && { 'aria-label': placeholderName })}
{...cleanedListProps}
{...virtualizedProps}
/>
)}
</EuiI18n>
Expand Down
Loading