(() => {
+ if (!renderOption) {
+ return undefined;
+ }
+ const toGroupItem = (item: ListboxGroupItem): OptionListProps.OptionGroupItem => ({
+ ...item,
+ option: toPublicGroup(item.option),
+ });
+ return ({ item }) => {
+ if (item.type === 'item') {
+ const parent = item.parent && toGroupItem(item.parent);
+ return renderOption({ item: { ...item, option: toPublicOption(item.option), parent } });
+ }
+ if (item.type === 'group') {
+ return renderOption({ item: toGroupItem(item) });
+ }
+ // The listbox renders only options and group headers; it has no trigger or select-all item.
+ return null;
+ };
+ }, [renderOption, toPublicOption, toPublicGroup]);
+
+ // The root, not the list, clears the highlight when the pointer leaves, so the leave covers the whole component.
+ const { onMouseLeave } = controller.getListProps();
+
+ return (
+
+ {
+ const leafIndex = leafIndexes.get(option);
+ return {
+ ...controller.getOptionProps(option, index),
+ // Inline, there is no dropdown border above the list, so the first item has no top divider.
+ hideTopDivider: index === 0,
+ className: leafIndex === undefined ? testClasses['group-header'] : testClasses.option,
+ // 1-based option position, used by test-utils. Group headers are not counted.
+ 'data-option-index': leafIndex === undefined ? undefined : leafIndex + 1,
+ 'data-value': option.option.value,
+ };
+ }}
+ />
+
+ );
+ }
+);
+
+export default InternalOptionList;
diff --git a/src/option-list/styles.scss b/src/option-list/styles.scss
new file mode 100644
index 00000000000..2df9f238ed1
--- /dev/null
+++ b/src/option-list/styles.scss
@@ -0,0 +1,20 @@
+/*
+ Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ SPDX-License-Identifier: Apache-2.0
+*/
+
+@use '../internal/styles' as styles;
+@use '../internal/styles/tokens' as awsui;
+
+.root {
+ @include styles.styles-reset;
+ display: block;
+}
+
+// Wraps the `icon` slot the way the shared option wraps the built-in icon of Select and Multiselect options.
+.icon {
+ padding-inline-end: awsui.$space-xs;
+ align-content: center;
+ display: flex;
+ flex-shrink: 0;
+}
diff --git a/src/option-list/test-classes/styles.scss b/src/option-list/test-classes/styles.scss
new file mode 100644
index 00000000000..a9e28fbeae3
--- /dev/null
+++ b/src/option-list/test-classes/styles.scss
@@ -0,0 +1,9 @@
+/*
+ Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ SPDX-License-Identifier: Apache-2.0
+*/
+.root,
+.option,
+.group-header {
+ /* used in test-utils */
+}
diff --git a/src/select/__tests__/arrow-key-wrap.test.tsx b/src/select/__tests__/arrow-key-wrap.test.tsx
new file mode 100644
index 00000000000..c14b6a46a79
--- /dev/null
+++ b/src/select/__tests__/arrow-key-wrap.test.tsx
@@ -0,0 +1,59 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+import * as React from 'react';
+import { render } from '@testing-library/react';
+
+import { KeyCode } from '@cloudscape-design/test-utils-core/utils';
+
+import Select, { SelectProps } from '../../../lib/components/select';
+import createWrapper from '../../../lib/components/test-utils/dom';
+
+const HOME = 36;
+const END = 35;
+
+function renderSelect(options: SelectProps.Options) {
+ const { container } = render(