kibana/x-pack/examples/lens_embeddable_inline_editing_example
Marta Bondyra 65bdf1ff8e
Optimize existing image assets with lossless compression (#223998)
## Summary

This PR applies **lossless compression** to all SVG and JPG/PNG assets
across Kibana using:

- [`svgo`](https://github.com/svg/svgo) — for optimizing SVGs  
- [`image-optimize`](https://www.npmjs.com/package/image-optimize) — for
JPG/PNG compression

‼️**Please scroll to ''Unknown metric groups" accordion to see what's
the gain for your code.**
<img width="542" alt="Screenshot 2025-06-18 at 13 24 20"
src="https://github.com/user-attachments/assets/191afb28-44fc-4551-9026-756a8385c66a"
/>

The goal is to reduce asset size and improve load performance without
compromising visual quality.

This PR achieves a **23 MB** reduction in asset size across all images
bundled in Kibana’s running code—meaning these compressed images
directly impact what ships in Kibana.
Some assets get bundled into chunks due to our bundling strategy but
might not actually be requested at runtime.

Additionally, I ran the same optimization script on the docs assets as a
harmless extra step, but those savings aren’t included in the 23 MB
total.

---

## Why

While working on Emotion rewrites, I noticed some SVGs seemed
unnecessarily heavy. That led to a broader investigation into our image
assets, and it turns out we’re not consistently optimizing them during
development or build.


---

## Notes

- Visual fidelity of optimized assets has been manually verified — no
visible differences
- The optimization is **lossless**, meaning no quality degradation
- Some assets (like large background images) could benefit further from
**lossy compression**

---

## Follow-ups / Ideas

1. **Automate compression in the dev/build pipeline**
   - e.g. add `svgo` as a pre-commit or CI step for SVGs
2. **Improve CI reporting**  
- Currently, bundle size diffs for images are hidden under "Unknown
metric groups" in the GitHub CI comment. We may want to make these more
visible.
   - 
3. **Audit large assets manually** — apply lossy compression where
appropriate
4. **Avoid redundant image loading**  
- e.g. background images on the login page are loaded again on the space
selector page since they’re bundled twice. I’m working on a separate PR
to address that.

## Snippets I used to apply the compression

```
# Find SVG files
find . -type f -iname "*.svg" \
  -not -path "*/node_modules/*" \
  -not -path "*/functional/*" > svg-files.txt

# Compress SVGs
while IFS= read -r file; do
  svgo "$file"
done < svg-files.txt
```

This snippet has been used for png and jpg, but the example below is for
png:
```
# Find PNG files
find . -type f -iname "*.png \
  -not -path "*/node_modules/*" \
  -not -path "*/functional/*" > png-files.txt

# Compress PNGs
while IFS= read -r file; do
  image-optimize -f jpg "$file"
done < png-files.txt
```
2025-06-19 16:44:13 +02:00
..
public Optimize existing image assets with lossless compression (#223998) 2025-06-19 16:44:13 +02:00
.eslintrc.json
kibana.jsonc
README.md
tsconfig.json [Lens] Embeddable react refactor (#186642) 2024-11-26 09:34:13 +01:00

Inline editing of Lens embeddable

To run this example plugin, use the command yarn start --run-examples.

This plugin contains examples on how to integrate the inline editing capabilities to your Lens embeddable.

Steps:

  • Add UIActions on your start dependencies
  • On your embeddable use the onLoad callback to store in the local state the adapters and lensEmbeddableOutput$.
  // my Lens embeddable
 <LensComponent {...embeddableInput} onLoad={onLoad} />
  const [lensLoadEvent, setLensLoadEvent] = useState<InlineEditLensEmbeddableContext['lensEvent'] | null>(null);
  // my onLoad callback
   const onLoad = useCallback(
    (
      isLoading: boolean,
      adapters: LensChartLoadEvent['adapters'] | undefined,
      lensEmbeddableOutput$?: LensChartLoadEvent['embeddableOutput$']
    ) => {
      const adapterTables = adapters?.tables?.tables;
      if (adapterTables && !isLoading) {
        setLensLoadEvent({
          adapters,
          embeddableOutput$: lensEmbeddableOutput$,
        });
      }
    },
    []
  );
  • Create the triggerOptions. You will need:
    • The attributes of the lens embeddable input
    • The lensChartEvent that you retrieved from the onLoad callback
    • An onUpdate callback to update the embeddable input with the new attributes
    • An optional onApply callback if you want to add an action when the Apply button is clicked
    • An optional onCancel callback if you want to add an action when the Cancel button is clicked
  // my trigger options
   const triggerOptions = {
    attributes: embeddableInput?.attributes,
    lensEvent: lensLoadEvent,
    onUpdate: (newAttributes: TypedLensByValueInput['attributes']) => {
      // onUpdate I need to update my embeddableInput.attributes
    },
    onApply: () => {
      // optional callback when Apply button is clicked
    },
    onCancel: () => {
      // optional callback when Cancel button is clicked
    },
  };
  • Add a button which will open the editing flyout. Execute the IN_APP_EMBEDDABLE_EDIT_TRIGGER trigger onClick
uiActions.getTrigger('IN_APP_EMBEDDABLE_EDIT_TRIGGER').exec(triggerOptions);

Important note

In case you don't want to open a push flyout you can pass an html element to the triggerOptions, the container property. This is going to render the inline editing component to this container element. In that case you will need an extra handling on your side. Check the 3rd example for implementation details.