kibana/examples/developer_examples/public/app.tsx
Julia Rechkunova 705df212b8
[Discover][Tabs] New tabs package and initial implementation of components (#210674)
- Closes https://github.com/elastic/kibana/issues/210500
- Closes https://github.com/elastic/kibana/issues/210502

## Summary

This PR introduces a new package `@kbn/unified-tabs` which includes an
initial implementation for tabs UI/UX which we are planning to add to
Discover.

- [x] New package
- [x] Storybook support 
- [x] Initial styles and interactions
- [x] New example plugin for testing together with UnifiedSearch bar 
- [x] Minimal tests

### In the new Storybook

Start Storybook with `NODE_OPTIONS="--openssl-legacy-provider" node
scripts/storybook unified_tabs` and navigate to `http://localhost:9001`.

<img width="1024" alt="Screenshot 2025-02-12 at 13 35 46"
src="https://github.com/user-attachments/assets/0723b0c4-c3f7-44f8-af8d-f68d7a7b6ea8"
/>

### In the new Unified Tabs example plugin

Start Kibana with `yarn start --run-examples`. Then navigate to the
Unified Tabs example plugin
`http://localhost:5601/app/unifiedTabsExamples`.

<img width="1221" alt="Screenshot 2025-02-12 at 16 11 55"
src="https://github.com/user-attachments/assets/2edff817-0aae-424c-978c-c4c67450c9eb"
/>
<img width="1219" alt="Screenshot 2025-02-12 at 16 13 57"
src="https://github.com/user-attachments/assets/2e6e6b0b-88e9-4689-a175-9612e8507535"
/>

![Feb-12-2025
16-13-17](https://github.com/user-attachments/assets/a1d511da-d554-432c-bea9-cbe8349f9772)


### Checklist

- [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)
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [x] The PR description includes the appropriate Release Notes section,
and the correct `release_note:*` label is applied per the
[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Davis McPhee <davismcphee@hotmail.com>
2025-02-21 14:00:40 +01:00

132 lines
4.4 KiB
TypeScript

/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import React, { useState } from 'react';
import ReactDOM from 'react-dom';
import {
EuiText,
EuiPageTemplate,
EuiCard,
EuiPageHeader,
EuiFlexGroup,
EuiFlexItem,
EuiFieldSearch,
EuiListGroup,
EuiHighlight,
EuiLink,
EuiButtonIcon,
} from '@elastic/eui';
import {
AnalyticsServiceStart,
AppMountParameters,
I18nStart,
ThemeServiceStart,
UserProfileService,
} from '@kbn/core/public';
import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render';
import { ExampleDefinition } from './types';
interface StartServices {
analytics: Pick<AnalyticsServiceStart, 'reportEvent'>;
i18n: I18nStart;
theme: Pick<ThemeServiceStart, 'theme$'>;
userProfile: UserProfileService;
}
interface Props {
startServices: StartServices;
examples: ExampleDefinition[];
navigateToApp: (appId: string) => void;
getUrlForApp: (appId: string) => string;
}
function DeveloperExamples({ startServices, examples, navigateToApp, getUrlForApp }: Props) {
const [search, setSearch] = useState<string>('');
const lcSearch = search.toLowerCase();
const filteredExamples = !lcSearch
? examples
: examples.filter((def) => {
if (def.description.toLowerCase().indexOf(lcSearch) >= 0) return true;
if (def.title.toLowerCase().indexOf(lcSearch) >= 0) return true;
return false;
});
return (
<KibanaRenderContextProvider {...startServices}>
<EuiPageTemplate offset={0}>
<EuiPageTemplate.Header>
<EuiFlexGroup justifyContent={'spaceBetween'}>
<EuiFlexItem>
<EuiPageHeader pageTitle={'Developer examples'} />
<EuiText>
The following examples showcase services and APIs that are available to developers.
</EuiText>
</EuiFlexItem>
<EuiFlexItem>
<EuiFieldSearch
fullWidth
placeholder="Search"
value={search}
onChange={(e) => setSearch(e.target.value)}
isClearable={true}
aria-label="Search developer examples"
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPageTemplate.Header>
<EuiPageTemplate.Section>
<EuiFlexGroup wrap>
{filteredExamples.map((def) => (
<EuiFlexItem style={{ minWidth: 300, maxWidth: 500 }} key={def.appId}>
<EuiCard
description={
<EuiHighlight search={search} highlightAll={true}>
{def.description}
</EuiHighlight>
}
title={
<React.Fragment>
<EuiLink
onClick={() => {
navigateToApp(def.appId);
}}
>
<EuiHighlight search={search} highlightAll={true}>
{def.title}
</EuiHighlight>
</EuiLink>
<EuiButtonIcon
iconType="popout"
onClick={() =>
window.open(getUrlForApp(def.appId), '_blank', 'noopener, noreferrer')
}
>
Open in new tab
</EuiButtonIcon>
</React.Fragment>
}
image={def.image}
footer={def.links ? <EuiListGroup size={'s'} listItems={def.links} /> : undefined}
/>
</EuiFlexItem>
))}
</EuiFlexGroup>
</EuiPageTemplate.Section>
</EuiPageTemplate>
</KibanaRenderContextProvider>
);
}
export const renderApp = (props: Props, element: AppMountParameters['element']) => {
ReactDOM.render(<DeveloperExamples {...props} />, element);
return () => ReactDOM.unmountComponentAtNode(element);
};