[Connection Details] Move tabs into the header (#183484)

## Summary

Closes https://github.com/elastic/kibana/issues/182966

*Connection Details* flyout tabs are now in the header of the flyout.
See the before/after below:

<img width="762" alt="image"
src="640378ad-3d96-4fd5-8353-883a1609b55c">

This saves screen real-estate for more content in the flyout body.



### Checklist

Delete any items that are not applicable to this PR.

- [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/packages/kbn-i18n/README.md)
- [x]
[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials
- [x] This renders correctly on smaller devices using a responsive
layout. (You can test this [in your
browser](https://www.browserstack.com/guide/responsive-testing-on-local-server))
- [x] This was checked for [cross-browser
compatibility](https://www.elastic.co/support/matrix#matrix_browsers)

### For maintainers

- [x] This was checked for breaking API changes and was [labeled
appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)
This commit is contained in:
Vadim Kibana 2024-05-16 10:21:25 +02:00 committed by GitHub
parent 520f19d0a2
commit 668f0966fe
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 85 additions and 52 deletions

View file

@ -7,61 +7,21 @@
*/
import * as React from 'react';
import { EuiSpacer, EuiTab, EuiTabs } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { useConnectionDetailsOpts } from './context';
import { useConnectionDetailsService } from './context';
import { EndpointsTab } from './tabs/endpoints_tab';
import { ApiKeysTab } from './tabs/api_keys_tab';
import { useBehaviorSubject } from './hooks/use_behavior_subject';
export const ConnectionDetails: React.FC = () => {
type TabID = 'endpoints' | 'apiKeys';
type Tab = [id: TabID, name: string, content: React.ReactNode];
const service = useConnectionDetailsService();
const tab = useBehaviorSubject(service.tabId$);
const ctx = useConnectionDetailsOpts();
const [tab, setTab] = React.useState<TabID>('endpoints');
const tabs: Tab[] = [];
if (ctx.endpoints) {
tabs.push([
'endpoints',
i18n.translate('cloud.connectionDetails.tab.endpoints', {
defaultMessage: 'Endpoints',
}),
<EndpointsTab />,
]);
switch (tab) {
case 'endpoints':
return <EndpointsTab />;
case 'apiKeys':
return <ApiKeysTab />;
default:
return null;
}
if (ctx.apiKeys) {
tabs.push([
'apiKeys',
i18n.translate('cloud.connectionDetails.tab.apiKeys', {
defaultMessage: 'API key',
}),
<ApiKeysTab />,
]);
}
if (tabs.length === 0) {
return null;
}
return (
<>
<EuiTabs>
{tabs.map(([id, name]) => (
<EuiTab
key={id}
onClick={() => setTab(id)}
isSelected={tab === id}
data-test-subj={`connectionDetailsTabBtn-${id}`}
>
{name}
</EuiTab>
))}
</EuiTabs>
<EuiSpacer />
{tabs.find(([id]) => id === tab)?.[2] || null}
</>
);
};

View file

@ -18,6 +18,7 @@ import {
import { i18n } from '@kbn/i18n';
import { ConnectionDetails } from './connection_details';
import { useConnectionDetailsOpts } from './context';
import { Tabs } from './tabs';
export const ConnectionDetailsFlyoutContent: React.FC = () => {
const ctx = useConnectionDetailsOpts();
@ -46,6 +47,10 @@ export const ConnectionDetailsFlyoutContent: React.FC = () => {
)}
</p>
</EuiText>
{/* The -25px is as per EUI example: https://eui.elastic.co/#/layout/flyout */}
<div style={{ marginBottom: '-25px' }}>
<Tabs />
</div>
</EuiFlyoutHeader>
);

View file

@ -10,9 +10,10 @@ import { BehaviorSubject } from 'rxjs';
import { i18n } from '@kbn/i18n';
import { ApiKey } from './tabs/api_keys_tab/views/success_form/types';
import type { Format } from './tabs/api_keys_tab/views/success_form/format_select';
import type { ConnectionDetailsOpts } from './types';
import type { ConnectionDetailsOpts, TabID } from './types';
export class ConnectionDetailsService {
public readonly tabId$ = new BehaviorSubject<TabID>('endpoints');
public readonly showCloudId$ = new BehaviorSubject<boolean>(false);
public readonly apiKeyName$ = new BehaviorSubject<string>('');
public readonly apiKeyStatus$ = new BehaviorSubject<'configuring' | 'creating'>('configuring');
@ -33,6 +34,10 @@ export class ConnectionDetailsService {
});
}
public readonly setTab = (tab: TabID) => {
this.tabId$.next(tab);
};
public readonly toggleShowCloudId = () => {
this.showCloudId$.next(!this.showCloudId$.getValue());
};

View file

@ -0,0 +1,61 @@
/*
* 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 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 or the Server
* Side Public License, v 1.
*/
import * as React from 'react';
import { EuiTab, EuiTabs } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { useConnectionDetailsOpts, useConnectionDetailsService } from './context';
import { useBehaviorSubject } from './hooks/use_behavior_subject';
import { TabID } from './types';
export const Tabs: React.FC = () => {
type Tab = [id: TabID, name: string];
const ctx = useConnectionDetailsOpts();
const service = useConnectionDetailsService();
const tab = useBehaviorSubject(service.tabId$);
const tabs: Tab[] = [];
if (ctx.endpoints) {
tabs.push([
'endpoints',
i18n.translate('cloud.connectionDetails.tab.endpoints', {
defaultMessage: 'Endpoints',
}),
]);
}
if (ctx.apiKeys) {
tabs.push([
'apiKeys',
i18n.translate('cloud.connectionDetails.tab.apiKeys', {
defaultMessage: 'API key',
}),
]);
}
if (tabs.length === 0) {
return null;
}
return (
<EuiTabs>
{tabs.map(([id, name]) => (
<EuiTab
key={id}
onClick={() => service.setTab(id)}
isSelected={tab === id}
data-test-subj={`connectionDetailsTabBtn-${id}`}
>
{name}
</EuiTab>
))}
</EuiTabs>
);
};

View file

@ -31,3 +31,5 @@ export interface ConnectionDetailsOptsApiKeys {
}>;
hasPermission: () => Promise<boolean>;
}
export type TabID = 'endpoints' | 'apiKeys';