Repository: gradio-app/gradio
Pull request: Improvements to `gr.Examples`: adds events as attributes and documents, them, adds `sample_labels`, and `visible` properties
Makes several enhancements to `gr.Examples`:

* Adds `load_input_event` and `cache_event` as public attributes and documents them (closes: #8710)
* Adds `visible` prop (closes: https://github.com/gradio-app/gradio/issues/6390)
* Adds `example_labels` prop (closes: https://github.com/gradio-app/gradio/issues/4268)

Problem statement:
gr.Examples Enable for each example to have a label and a value
- [yes ] I have searched to see if a similar issue already exists.

For example I would like the gr.Examples to display the example as "Person 1" and when it is clicked, to populate the linked component with "John Doe". 


Support `visible` option in `gr.Examples`
- [x] I have searched to see if a similar issue already exists.

It would be useful if `gr.Examples` had `visible` option as well.
(I know we can work around this by wrapping it with a `gr.Row` and changing its visibility.)

Add `.then` to `gr.Examples`
- [x] I have searched to see if a similar issue already exists.

**Is your feature request related to a problem? Please describe.**  
Sometimes the app requires some pre-processing tasks that happen during the interactive session with the user (e.g.: cropping an image when it's uploaded), however, when using `gr.Examples` with `cache_examples`, this should be expedited into running all the tasks that happen in the interactive session. 

**Describe the solution you'd like**  
I'd like to be able to do a `.then` or `.success` after `gr.Examples`, so that once the first `fn` runs, others run in sequence.

Hints from issue discussion:
The way I'm thinking to implement this is to add a `labels` parameter to `gr.Examples()` (and an `examples_labels` parameter to `gr.Interface()`) which is a list of the same length as `examples`. If provided, these text labels appear instead of the original examples, but clicking on the labels populates the inputs in the same way as before.
@abidlabs Is there any update on this? I could need exactly this enhancement for a use case where we have long text examples that we could just abbreviate by a short label.
Thanks @apolinario agreed. I think we can just expose the caching event so that a developer could do .then, .success, cancellation, or anything else that they'd like with the event

Commit message: changes

Patch to review:
diff --git a/.changeset/slow-candles-fail.md b/.changeset/slow-candles-fail.md
new file mode 100644
index 0000000000..6ac84418ed
--- /dev/null
+++ b/.changeset/slow-candles-fail.md
@@ -0,0 +1,7 @@
+---
+"@gradio/dataset": minor
+"gradio": minor
+"website": minor
+---
+
+feat:Improvements to `gr.Examples`: adds events as attributes and documents, them, adds `sample_labels`, and `visible` properties
diff --git a/gradio/components/dataset.py b/gradio/components/dataset.py
index 982bd1f139..7abd835de1 100644
--- a/gradio/components/dataset.py
+++ b/gradio/components/dataset.py
@@ -43,6 +43,7 @@ def __init__(
         scale: int | None = None,
         min_width: int = 160,
         proxy_url: str | None = None,
+        sample_labels: list[str] | None = None,
     ):
         """
         Parameters:
@@ -61,6 +62,7 @@ def __init__(
             scale: relative size compared to adjacent Components. For example if Components A and B are in a Row, and A has scale=2, and B has scale=1, A will be twice as wide as B. Should be an integer. scale applies in Rows, and to top-level Components in Blocks where fill_height=True.
             min_width: minimum pixel width, will wrap if not sufficient screen space to satisfy this value. If a certain scale value results in this Component being narrower than min_width, the min_width parameter will be respected first.
             proxy_url: The URL of the external Space used to load this component. Set automatically when using `gr.load()`. This should not be set manually.
+            sample_labels: An list of labels for each sample. If provided, the length of this list should be the same as the number of samples, and these labels will be used in the UI instead of rendering the sample values.
         """
         super().__init__(
             visible=visible,
@@ -115,6 +117,7 @@ def __init__(
         else:
             self.headers = [c.label or "" for c in self._components]
         self.samples_per_page = samples_per_page
+        self.sample_labels = sample_labels
 
     def api_info(self) -> dict[str, str]:
         return {"type": "integer", "description": "index of selected example"}
@@ -124,6 +127,7 @@ def get_config(self):
 
         config["components"] = []
         config["component_props"] = self.component_props
+        config["sample_labels"] = self.sample_labels
         config["component_ids"] = []
 
         for component in self._components:
diff --git a/gradio/helpers.py b/gradio/helpers.py
index 0da9287b96..4d62b5678e 100644
--- a/gradio/helpers.py
+++ b/gradio/helpers.py
@@ -25,7 +25,7 @@
 from gradio import components, oauth, processing_utils, routes, utils, wasm_utils
 from gradio.context import Context, LocalContext, get_blocks_context
 from gradio.data_classes import GradioModel, GradioRootModel
-from gradio.events import EventData
+from gradio.events import Dependency, EventData
 from gradio.exceptions import Error
 from gradio.flagging import CSVLogger
 
@@ -50,6 +50,9 @@ def create_examples(
     postprocess: bool = True,
     api_name: str | Literal[False] = "load_example",
     batch: bool = False,
+    *,
+    example_labels: list[str] | None = None,
+    visible: bool = True,
     _defer_caching: bool = False,
 ):
     """Top-level synchronous function that creates Examples. Provided for backwards compatibility, i.e. so that gr.Examples(...) can be used to create the Examples component."""
@@ -69,6 +72,8 @@ def create_examples(
         api_name=api_name,
         batch=batch,
         _defer_caching=_defer_caching,
+        example_labels=example_labels,
+        visible=visible,
         _initiated_directly=False,
     )
     examples_obj.create()
@@ -103,6 +108,9 @@ def __init__(
         postprocess: bool = True,
         api_name: str | Literal[False] = "load_example",
         batch: bool = False,
+        *,
+        example_labels: list[str] | None = None,
+        visible: bool = True,
         _defer_caching: bool = False,
         _initiated_directly: bool = True,
     ):
@@ -121,6 +129,8 @@ def __init__(
             postprocess: if True, postprocesses the example output after running the prediction function and before caching. Only applies if `cache_examples` is not False.
             api_name: Defines how the event associated with clicking on the examples appears in the API docs. Can be a string or False. If set to a string, the endpoint will be exposed in the API docs with the given name. If False, the endpoint will not be exposed in the API docs and downstream apps (including those that `gr.load` this app) will not be able to use the example function.
             batch: If True, then the function should process a batch of inputs, meaning that it should accept a list of input values for each parameter. Used only if cache_examples is not False.
+            example_labels: An list of labels for each example. If provided, the length of this list should be the same as the number of examples, and these labels will be used in the UI instead of rendering the example values.
+            visible: If False, the examples component will be hidden in the UI.
         """
         if _initiated_directly:
             warnings.warn(
@@ -221,6 +231,12 @@ def __init__(
             [ex for (ex, keep) in zip(example, input_has_examples) if keep]
             for example in examples
         ]
+        if example_labels is not None and len(example_labels) != len(
+            inputs_with_examples
+        ):
+            raise ValueError(
+                "The length of `example_labels` should be the same as the number of examples."
+            )
 
         self.examples = examples
         self.non_none_examples = non_none_examples
@@ -233,6 +249,7 @@ def __init__(
         self.postprocess = postprocess
         self.api_name: str | Literal[False] = api_name
         self.batch = batch
+        self.example_labels = example_labels
 
         with utils.set_directory(working_directory):
             self.processed_examples = []
@@ -265,6 +282,8 @@ def __init__(
                 label=label,
                 samples_per_page=examples_per_page,
                 elem_id=elem_id,
+                visible=visible,
+                sample_labels=example_labels,
             )
 
         self.cache_logger = CSVLogger(simplify_file_data=False)
@@ -272,6 +291,7 @@ def __init__(
         self.cached_file = Path(self.cached_folder) / "log.csv"
         self.cached_indices_file = Path(self.cached_folder) / "indices.csv"
         self.run_on_click = run_on_click
+        self.cache_event: Dependency | None = None
 
     def create(self) -> None:
         """Caches the examples if self.cache_examples is True and creates the Dataset
@@ -380,7 +400,7 @@ async def lazy_cache(self) -> None:
             lazy_cache_fn = self.async_lazy_cache
         else:
             lazy_cache_fn = self.sync_lazy_cache
-        self.load_input_event.then(
+        self.cache_event = self.load_input_event.then(
             lazy_cache_fn,
             inputs=[self.dataset] + self.inputs,
             outputs=self.outputs,
@@ -466,7 +486,7 @@ async def get_final_item(*args):
             # create a fake dependency to process the examples and get the predictions
             from gradio.events import EventListenerMethod
 
-            dependency, fn_index = blocks_config.set_event_trigger(
+            _, fn_index = blocks_config.set_event_trigger(
                 [EventListenerMethod(Context.root_block, "load")],
                 fn=fn,
                 inputs=self.inputs_with_examples,  # type: ignore
@@ -511,7 +531,7 @@ def load_example(example_id):
             ] + self.load_from_cache(example_id)
             return utils.resolve_singleton(processed_example)
 
-        self.load_input_event = self.dataset.click(
+        self.cache_event = self.load_input_event = self.dataset.click(
             load_example,
             inputs=[self.dataset],
             outputs=self.inputs_with_examples + self.outputs,  # type: ignore
diff --git a/js/_website/src/lib/templates/gradio/04_helpers/11_examples.svx b/js/_website/src/lib/templates/gradio/04_helpers/11_examples.svx
index ec4a90ea22..e7f82d6ac6 100644
--- a/js/_website/src/lib/templates/gradio/04_helpers/11_examples.svx
+++ b/js/_website/src/lib/templates/gradio/04_helpers/11_examples.svx
@@ -10,6 +10,27 @@
     import { style_formatted_text } from "$lib/text";
 
     let obj = get_object("examples");
+
+    obj["attributes"] = [
+            {
+                name: "dataset",
+                annotation: "gradio.Dataset",
+                doc: "The `gr.Dataset` component corresponding to this Examples object.",
+                kwargs: null
+            },
+            {
+                name: "load_input_event",
+                annotation: "gradio.events.Dependency",
+                doc: "The Gradio event that populates the input values when the examples are clicked. You can attach a `.then()` or a `.success()` to this event to trigger subsequent events to fire after this event.",
+                kwargs: null
+            },
+            {
+                name: "cache_event",
+                annotation: "gradio.events.Dependency | None",
+                doc: "The Gradio event that populates the cached output values when the examples are clicked. You can attach a `.then()` or a `.success()` to this event to trigger subsequent events to fire after this event. This event is `None` if `cache_examples` if False, and is the same as `load_input_event` if `cache_examples` is `'lazy'`.",
+                kwargs: null
+            }
+        ]    
 </script>
 
 <!--- Title -->
@@ -37,6 +58,10 @@ None
 ### Initialization
 <ParamTable parameters={obj.parameters} />
 
+<!--- Attributes -->
+### Attributes
+<ParamTable parameters={obj.attributes} />
+
 
 {#if obj.demos && obj.demos.length > 0}
 <!--- Demos -->
diff --git a/js/dataset/Index.svelte b/js/dataset/Index.svelte
index 4b48b431b2..a258523090 100644
--- a/js/dataset/Index.svelte
+++ b/js/dataset/Index.svelte
@@ -13,6 +13,7 @@
 	export let label = "Examples";
 	export let headers: string[];
 	export let samples: any[][] | null = null;
+	export let sample_labels: string[] | null = null;
 	export let elem_id = "";
 	export let elem_classes: string[] = [];
 	export let visible = true;
@@ -33,7 +34,7 @@
 		? `/proxy=${proxy_url}file=`
 		: `${root}/file=`;
 	let page = 0;
-	$: gallery = components.length < 2;
+	$: gallery = components.length < 2 || sample_labels !== null;
 	let paginate = samples ? samples.length > samples_per_page : false;
 
 	let selected_samples: any[][];
@@ -84,21 +85,33 @@
 	}[][] = [];
 
 	async function get_component_meta(selected_samples: any[][]): Promise<void> {
-		component_meta = await Promise.all(
-			selected_samples &&
-				selected_samples.map(
-					async (sample_row) =>
-						await Promise.all(
-							sample_row.map(async (sample_cell, j) => {
-								return {
-									value: sample_cell,
-									component: (await component_map.get(components[j]))
-										?.default as ComponentType<SvelteComponent>
-								};
-							})
-						)
-				)
-		);
+		if (sample_labels !== null) {
+			component_meta = await Promise.all(
+				sample_labels.map(async (label) => [
+					{
+						value: label,
+						component: (await component_map.get("textbox"))
+							?.default as ComponentType<SvelteComponent>
+					}
+				])
+			);
+		} else {
+			component_meta = await Promise.all(
+				selected_samples &&
+					selected_samples.map(
+						async (sample_row) =>
+							await Promise.all(
+								sample_row.map(async (sample_cell, j) => {
+									return {
+										value: sample_cell,
+										component: (await component_map.get(components[j]))
+											?.default as ComponentType<SvelteComponent>
+									};
+								})
+							)
+					)
+			);
+		}
 	}
 
 	$: component_map, get_component_meta(selected_samples);


Code review comments:
diff_hunk: @@ -121,6 +129,8 @@ def __init__(
             postprocess: if True, postprocesses the example output after running the prediction function and before caching. Only applies if `cache_examples` is not False.
             api_name: Defines how the event associated with clicking on the examples appears in the API docs. Can be a string or False. If set to a string, the endpoint will be exposed in the API docs with the given name. If False, the endpoint will not be exposed in the API docs and downstream apps (including those that `gr.load` this app) will not be able to use the example function.
             batch: If True, then the function should process a batch of inputs, meaning that it should accept a list of input values for each parameter. Used only if cache_examples is not False.
+            example_labels: An list of labels for each example. If provided, the length of this list should be the same as the number of examples, and these labels will be used in the UI instead of rendering the example values.
path: gradio/helpers.py
text: @user1:
typo
```suggestion
            example_labels: A list of labels for each example. If provided, the length of this list should be the same as the number of examples, and these labels will be used in the UI instead of rendering the example values.
```

diff_hunk: @@ -61,6 +62,7 @@ def __init__(
             scale: relative size compared to adjacent Components. For example if Components A and B are in a Row, and A has scale=2, and B has scale=1, A will be twice as wide as B. Should be an integer. scale applies in Rows, and to top-level Components in Blocks where fill_height=True.
             min_width: minimum pixel width, will wrap if not sufficient screen space to satisfy this value. If a certain scale value results in this Component being narrower than min_width, the min_width parameter will be respected first.
             proxy_url: The URL of the external Space used to load this component. Set automatically when using `gr.load()`. This should not be set manually.
+            sample_labels: An list of labels for each sample. If provided, the length of this list should be the same as the number of samples, and these labels will be used in the UI instead of rendering the sample values.
path: gradio/components/dataset.py
text: @user1:
typo
```suggestion
            sample_labels: A list of labels for each sample. If provided, the length of this list should be the same as the number of samples, and these labels will be used in the UI instead of rendering the sample values.
```