Repository: elastic/kibana
Pull request #217243: [Onboarding] search playground UX improvements
Author: login: saarikabhasi
Labels: name: release_note:skip
description: Skip the PR/issue when compiling release notes
name: backport:skip
description: This PR does not require backporting
name: v9.1.0
Created: 2025-04-04T18:13:07Z | Merged: 2025-04-08T16:17:21Z

## Summary

Update search playground UI to align with new design. Updated following in this PR

- [x] Remove pagination from top of search results.
- [x] Hide Field & Contents when document is not expanded
- [x] Remove space between search results but shows space when expanded. 
- [x] Enable clear search for the search bar and updated to search as user types
- [x] Update empty state text

## Demo
https://github.com/user-attachments/assets/9242061b-5f40-4cb0-b2c5-b3a4bdebd982




### Checklist

Check the PR satisfies following conditions. 

Reviewers should verify this PR satisfies this list as well.

- [x] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md)



Diff:
diff --git a/x-pack/solutions/search/packages/kbn-search-index-documents/components/document_list.tsx b/x-pack/solutions/search/packages/kbn-search-index-documents/components/document_list.tsx
index 273d186e15577..33b55cb96612d 100644
--- a/x-pack/solutions/search/packages/kbn-search-index-documents/components/document_list.tsx
+++ b/x-pack/solutions/search/packages/kbn-search-index-documents/components/document_list.tsx
@@ -70,15 +70,6 @@ export const DocumentList: React.FC<DocumentListProps> = ({
   const pageCount = meta?.pageSize ? Math.ceil(meta.totalItemCount / meta?.pageSize) : 0;
   return (
     <>
-      <EuiPagination
-        aria-label={i18n.translate('searchIndexDocuments.documentList.paginationAriaLabel', {
-          defaultMessage: 'Pagination for document list',
-        })}
-        pageCount={pageCount}
-        activePage={meta.pageIndex}
-        onPageClick={onPaginate}
-      />
-      <EuiSpacer size="m" />
       <EuiFlexGroup alignItems="center" justifyContent="spaceBetween" gutterSize="s">
         <EuiFlexItem>
           <EuiText size="xs">
@@ -123,7 +114,6 @@ export const DocumentList: React.FC<DocumentListProps> = ({
               onDocumentClick={onDocumentClick ? () => onDocumentClick(doc) : undefined}
               {...resultProps}
             />
-            <EuiSpacer size="s" />
           </React.Fragment>
         );
       })}
diff --git a/x-pack/solutions/search/packages/kbn-search-index-documents/components/result/result.tsx b/x-pack/solutions/search/packages/kbn-search-index-documents/components/result/result.tsx
index 4d28b75605905..3750712985eaa 100644
--- a/x-pack/solutions/search/packages/kbn-search-index-documents/components/result/result.tsx
+++ b/x-pack/solutions/search/packages/kbn-search-index-documents/components/result/result.tsx
@@ -12,6 +12,7 @@ import {
   EuiFlexGroup,
   EuiFlexItem,
   EuiHorizontalRule,
+  EuiSpacer,
   EuiSplitPanel,
   EuiToolTip,
 } from '@elastic/eui';
@@ -65,68 +66,77 @@ export const Result: React.FC<ResultProps> = ({
         });
   const toolTipContent = <>{tooltipText}</>;
 
+  const showResultsFields = isExpanded ? fields.length > 0 : defaultVisibleFields > 0;
+
   return (
-    <EuiSplitPanel.Outer hasBorder={true} data-test-subj="search-index-documents-result">
-      <EuiSplitPanel.Inner paddingSize="m" color="plain" className="resultHeaderContainer">
-        <EuiFlexGroup gutterSize="none" alignItems="center">
-          <EuiFlexItem>
-            {compactCard && (
-              <ResultHeader
-                title={
-                  metaData.title ??
-                  i18n.translate('searchIndexDocuments.result.title.id', {
-                    defaultMessage: 'Document id: {id}',
-                    values: { id: metaData.id },
-                  })
-                }
-                metaData={metaData}
-              />
-            )}
+    <>
+      <EuiSplitPanel.Outer hasBorder={true} data-test-subj="search-index-documents-result">
+        <EuiSplitPanel.Inner paddingSize="m" color="plain" className="resultHeaderContainer">
+          <EuiFlexGroup gutterSize="none" alignItems="center">
+            <EuiFlexItem>
+              {compactCard && (
+                <ResultHeader
+                  title={
+                    metaData.title ??
+                    i18n.translate('searchIndexDocuments.result.title.id', {
+                      defaultMessage: 'Document id: {id}',
+                      values: { id: metaData.id },
+                    })
+                  }
+                  metaData={metaData}
+                />
+              )}
 
-            {!compactCard && (
-              <RichResultHeader
-                showScore={showScore}
-                title={
-                  metaData.title ??
-                  i18n.translate('searchIndexDocuments.result.title.id', {
-                    defaultMessage: 'Document id: {id}',
-                    values: { id: metaData.id },
-                  })
-                }
-                onTitleClick={onDocumentClick}
-                metaData={{
-                  ...metaData,
-                  onDocumentDelete,
-                  hasDeleteDocumentsPrivilege,
-                }}
-              />
-            )}
-          </EuiFlexItem>
-          <EuiFlexItem grow={false}>
-            <EuiToolTip position="left" content={toolTipContent}>
-              <EuiButtonIcon
-                size="xs"
-                iconType={isExpanded ? 'fold' : 'unfold'}
-                color={isExpanded ? 'danger' : 'primary'}
-                data-test-subj={isExpanded ? 'documentShowLessFields' : 'documentShowMoreFields'}
-                onClick={(e: React.MouseEvent<HTMLElement>) => {
-                  e.stopPropagation();
-                  setIsExpanded(!isExpanded);
-                }}
-                aria-label={tooltipText}
+              {!compactCard && (
+                <RichResultHeader
+                  showScore={showScore}
+                  title={
+                    metaData.title ??
+                    i18n.translate('searchIndexDocuments.result.title.id', {
+                      defaultMessage: 'Document id: {id}',
+                      values: { id: metaData.id },
+                    })
+                  }
+                  onTitleClick={onDocumentClick}
+                  metaData={{
+                    ...metaData,
+                    onDocumentDelete,
+                    hasDeleteDocumentsPrivilege,
+                  }}
+                />
+              )}
+            </EuiFlexItem>
+            <EuiFlexItem grow={false}>
+              <EuiToolTip position="left" content={toolTipContent}>
+                <EuiButtonIcon
+                  size="xs"
+                  iconType={isExpanded ? 'fold' : 'unfold'}
+                  color={isExpanded ? 'danger' : 'primary'}
+                  data-test-subj={isExpanded ? 'documentShowLessFields' : 'documentShowMoreFields'}
+                  onClick={(e: React.MouseEvent<HTMLElement>) => {
+                    e.stopPropagation();
+                    setIsExpanded(!isExpanded);
+                  }}
+                  aria-label={tooltipText}
+                />
+              </EuiToolTip>
+            </EuiFlexItem>
+          </EuiFlexGroup>
+        </EuiSplitPanel.Inner>
+        {showResultsFields && (
+          <>
+            <EuiHorizontalRule margin="none" />
+            <EuiSplitPanel.Inner paddingSize="m">
+              <ResultFields
+                documentId={metaData.id}
+                isExpanded={isExpanded}
+                fields={isExpanded ? fields : fields.slice(0, defaultVisibleFields)}
               />
-            </EuiToolTip>
-          </EuiFlexItem>
-        </EuiFlexGroup>
-      </EuiSplitPanel.Inner>
-      <EuiHorizontalRule margin="none" />
-      <EuiSplitPanel.Inner paddingSize="m">
-        <ResultFields
-          documentId={metaData.id}
-          isExpanded={isExpanded}
-          fields={isExpanded ? fields : fields.slice(0, defaultVisibleFields)}
-        />
-      </EuiSplitPanel.Inner>
-    </EuiSplitPanel.Outer>
+            </EuiSplitPanel.Inner>
+          </>
+        )}
+      </EuiSplitPanel.Outer>
+      {showResultsFields && <EuiSpacer size="s" />}
+    </>
   );
 };
diff --git a/x-pack/solutions/search/packages/kbn-search-index-documents/components/result/results_fields.tsx b/x-pack/solutions/search/packages/kbn-search-index-documents/components/result/results_fields.tsx
index 885e7df8ab0c2..3218d08866462 100644
--- a/x-pack/solutions/search/packages/kbn-search-index-documents/components/result/results_fields.tsx
+++ b/x-pack/solutions/search/packages/kbn-search-index-documents/components/result/results_fields.tsx
@@ -39,7 +39,6 @@ export const ResultFields: React.FC<Props> = ({ documentId, fields, isExpanded }
           })}
         </EuiTableHeaderCell>
       </EuiTableHeader>
-
       <EuiTableBody>
         {fields.map((field) => (
           <ResultField
diff --git a/x-pack/solutions/search/plugins/search_playground/public/components/search_mode/search_mode.tsx b/x-pack/solutions/search/plugins/search_playground/public/components/search_mode/search_mode.tsx
index b33751306de45..626eed2267216 100644
--- a/x-pack/solutions/search/plugins/search_playground/public/components/search_mode/search_mode.tsx
+++ b/x-pack/solutions/search/plugins/search_playground/public/components/search_mode/search_mode.tsx
@@ -7,10 +7,9 @@
 
 import {
   EuiEmptyPrompt,
-  EuiFieldText,
+  EuiFieldSearch,
   EuiFlexGroup,
   EuiFlexItem,
-  EuiForm,
   useEuiTheme,
 } from '@elastic/eui';
 import React from 'react';
@@ -28,7 +27,7 @@ import { useIndexMappings } from '../../hooks/use_index_mappings';
 
 export const SearchMode: React.FC = () => {
   const { euiTheme } = useEuiTheme();
-  const { control, handleSubmit } = useFormContext<ChatForm>();
+  const { control } = useFormContext<ChatForm>();
   const {
     field: { value: searchBarValue },
     formState: { isSubmitting },
@@ -75,26 +74,26 @@ export const SearchMode: React.FC = () => {
         >
           <EuiFlexGroup direction="column">
             <EuiFlexItem grow={false}>
-              <EuiForm component="form" onSubmit={handleSubmit(() => handleSearch())}>
-                <Controller
-                  control={control}
-                  name={ChatFormFields.searchQuery}
-                  render={({ field }) => (
-                    <EuiFieldText
-                      data-test-subj="searchPlaygroundSearchModeFieldText"
-                      {...field}
-                      value={searchBarValue}
-                      icon="search"
-                      fullWidth
-                      placeholder={i18n.translate(
-                        'xpack.searchPlayground.searchMode.searchBar.placeholder',
-                        { defaultMessage: 'Search for documents' }
-                      )}
-                      isLoading={isSubmitting}
-                    />
-                  )}
-                />
-              </EuiForm>
+              <Controller
+                control={control}
+                name={ChatFormFields.searchQuery}
+                render={({ field }) => (
+                  <EuiFieldSearch
+                    data-test-subj="searchPlaygroundSearchModeFieldText"
+                    {...field}
+                    value={searchBarValue}
+                    fullWidth
+                    placeholder={i18n.translate(
+                      'xpack.searchPlayground.searchMode.searchBar.placeholder',
+                      { defaultMessage: 'Search for documents' }
+                    )}
+                    isLoading={isSubmitting}
+                    isClearable
+                    incremental
+                    onSearch={handleSearch}
+                  />
+                )}
+              />
             </EuiFlexItem>
             <EuiFlexItem>
               <EuiFlexGroup direction="column">
@@ -109,12 +108,10 @@ export const SearchMode: React.FC = () => {
                     />
                   ) : (
                     <EuiEmptyPrompt
-                      iconType={'checkInCircleFilled'}
-                      iconColor="success"
                       title={
                         <h2>
                           {i18n.translate('xpack.searchPlayground.searchMode.readyToSearch', {
-                            defaultMessage: 'Ready to search',
+                            defaultMessage: 'We are ready to search!',
                           })}
                         </h2>
                       }
@@ -122,7 +119,7 @@ export const SearchMode: React.FC = () => {
                         <p>
                           {i18n.translate('xpack.searchPlayground.searchMode.searchPrompt', {
                             defaultMessage:
-                              'Type in a query in the search bar above or view the query we automatically created for you.',
+                              'Fine tune a traditional search with your data. Start by entering a query above to see what results you get and go from there!',
                           })}
                         </p>
                       }


Reviews:
user: login: TattdCodeMonkey
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-04T19:53:17Z
user: login: saarikabhasi
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-07T16:25:59Z
user: login: saarikabhasi
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-07T16:26:03Z
user: login: saarikabhasi
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-07T16:39:18Z
user: login: mdefazio
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T20:13:38Z
user: login: TattdCodeMonkey
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-08T16:13:22Z

Review comments:
diff_hunk: @@ -65,68 +66,79 @@ export const Result: React.FC<ResultProps> = ({
         });
   const toolTipContent = <>{tooltipText}</>;
 
+  const showResultsFields = isExpanded
+    ? fields.length > 0
+    : fields.slice(0, defaultVisibleFields).length > 0;
path: x-pack/solutions/search/packages/kbn-search-index-documents/components/result/result.tsx
user: login: TattdCodeMonkey
body: do we need to do the slice here? I feel like `defaultVisibleFields > 0 && fields.length > 0` would give us the same result without having to do any array operations.
created_at: 2025-04-04T19:44:39Z
author_association: CONTRIBUTOR
diff_hunk: @@ -75,22 +75,29 @@ export const SearchMode: React.FC = () => {
         >
           <EuiFlexGroup direction="column">
             <EuiFlexItem grow={false}>
-              <EuiForm component="form" onSubmit={handleSubmit(() => handleSearch())}>
+              <EuiForm
path: x-pack/solutions/search/plugins/search_playground/public/components/search_mode/search_mode.tsx
user: login: TattdCodeMonkey
body: we can probably remove this form is we aren't using it to submit the search.
created_at: 2025-04-04T19:47:11Z
author_association: CONTRIBUTOR
diff_hunk: @@ -70,15 +70,6 @@ export const DocumentList: React.FC<DocumentListProps> = ({
   const pageCount = meta?.pageSize ? Math.ceil(meta.totalItemCount / meta?.pageSize) : 0;
   return (
     <>
-      <EuiPagination
path: x-pack/solutions/search/packages/kbn-search-index-documents/components/document_list.tsx
user: login: TattdCodeMonkey
body: It might be worth adding a prop to control this instead of just removing it completely?

Something like `paginationDisplay={Top|Bottom|Both}`, but also not sure if we're losing much by removing this in the other components that use the `DocumentList`.

@julianrosado @mdefazio opinions?
created_at: 2025-04-04T19:52:12Z
author_association: CONTRIBUTOR
diff_hunk: @@ -75,22 +75,29 @@ export const SearchMode: React.FC = () => {
         >
           <EuiFlexGroup direction="column">
             <EuiFlexItem grow={false}>
-              <EuiForm component="form" onSubmit={handleSubmit(() => handleSearch())}>
+              <EuiForm
path: x-pack/solutions/search/plugins/search_playground/public/components/search_mode/search_mode.tsx
user: login: saarikabhasi
body: updated in https://github.com/elastic/kibana/pull/217243/commits/c5e377796e25a43b6f9a50e2428c4b645ee3029a
created_at: 2025-04-07T16:25:59Z
author_association: MEMBER
diff_hunk: @@ -65,68 +66,79 @@ export const Result: React.FC<ResultProps> = ({
         });
   const toolTipContent = <>{tooltipText}</>;
 
+  const showResultsFields = isExpanded
+    ? fields.length > 0
+    : fields.slice(0, defaultVisibleFields).length > 0;
path: x-pack/solutions/search/packages/kbn-search-index-documents/components/result/result.tsx
user: login: saarikabhasi
body: updated in https://github.com/elastic/kibana/pull/217243/commits/c5e377796e25a43b6f9a50e2428c4b645ee3029a
created_at: 2025-04-07T16:26:03Z
author_association: MEMBER
diff_hunk: @@ -70,15 +70,6 @@ export const DocumentList: React.FC<DocumentListProps> = ({
   const pageCount = meta?.pageSize ? Math.ceil(meta.totalItemCount / meta?.pageSize) : 0;
   return (
     <>
-      <EuiPagination
path: x-pack/solutions/search/packages/kbn-search-index-documents/components/document_list.tsx
user: login: saarikabhasi
body: Yep! we could do that. Added this  based on my discussion with @mdefazio and @julianrosado that we are okay to apply the changes in this PR to all the  components that use [kbn-search-index-documents](https://github.com/elastic/kibana/tree/main/x-pack/solutions/search/packages/kbn-search-index-documents).  
But currently only this change(removing top pagination), is the only change affecting all consuming components, which is `connector details -> documents tab` 
created_at: 2025-04-07T16:39:17Z
author_association: MEMBER
diff_hunk: @@ -70,15 +70,6 @@ export const DocumentList: React.FC<DocumentListProps> = ({
   const pageCount = meta?.pageSize ? Math.ceil(meta.totalItemCount / meta?.pageSize) : 0;
   return (
     <>
-      <EuiPagination
path: x-pack/solutions/search/packages/kbn-search-index-documents/components/document_list.tsx
user: login: mdefazio
body: I don't think we need the pagination on the top though—I don't think we do this anywhere else.  Confirmed with @julianrosado that we don't need them on top for any specific reason. 
created_at: 2025-04-07T20:13:38Z
author_association: CONTRIBUTOR

PR comments:
user: login: elasticmachine
created_at: 2025-04-07T18:26:16Z
body: ## :green_heart: Build Succeeded
* [Buildkite Build](https://buildkite.com/elastic/kibana-pull-request/builds/290914)
* Commit: 3748b36683ce2e529a020fa8ace171e414b19381

### Metrics [[docs](https://www.elastic.co/guide/en/kibana/master/ci-metrics.html)]
#### Async chunks
> Total size of all lazy-loaded chunks that will be downloaded as the user navigates the app


| id | [before](https://github.com/elastic/kibana/commit/a19e4331ff2fc49618b5572137d76385869f7f6c) | [after](https://github.com/elastic/kibana/commit/3748b36683ce2e529a020fa8ace171e414b19381) | diff |
| --- | --- | --- | --- |
| `enterpriseSearch` | 1.3MB | 1.3MB | -164.0B |
| `searchIndices` | 180.3KB | 180.5KB | +117.0B |
| `searchPlayground` | 205.0KB | 204.8KB | -221.0B |
| `serverlessSearch` | 343.6KB | 343.4KB | -164.0B |
| total |  |  | -432.0B |



### History
* :broken_heart: [Build #290446](https://buildkite.com/elastic/kibana-pull-request/builds/290446) failed eb40c0b9a75f87b50b9e3ea1aa7b23969f66827a

<!--buildkite-pr-comment-kibana-pull-request
{"builds":[{"buildStatus":{"state":"passed","success":true,"hasRetries":true,"hasNonPreemptionRetries":false},"url":"https://buildkite.com/elastic/kibana-pull-request/builds/290914","number":290914,"commit":"3748b36683ce2e529a020fa8ace171e414b19381"},{"buildStatus":{"state":"failed","success":false,"hasRetries":true,"hasNonPreemptionRetries":true},"url":"https://buildkite.com/elastic/kibana-pull-request/builds/290446","number":290446,"commit":"eb40c0b9a75f87b50b9e3ea1aa7b23969f66827a"}],"number":290914}
buildkite-pr-comment-->
author_association: CONTRIBUTOR