Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ES|QL] Enhances the inline documentation experience #192156

Merged
merged 43 commits into from
Sep 19, 2024
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
6e46314
Push flyout implementation
stratoula Sep 2, 2024
de146d8
Merge branch 'main' into esql-docs-flyout
stratoula Sep 4, 2024
3816c7b
Separate flyout content
stratoula Sep 4, 2024
d8884d8
Improve flyout width
stratoula Sep 5, 2024
f29fdb9
Flyout search and navigation
stratoula Sep 5, 2024
558dc9c
Small fixes
stratoula Sep 5, 2024
06250da
Inline documentation component
stratoula Sep 6, 2024
a2d31da
Merge with main and resolve conflicts
stratoula Sep 9, 2024
edb0a1c
Add flyout to the help menu
stratoula Sep 9, 2024
612d5f4
Fix wrong function removal
stratoula Sep 9, 2024
12524d5
Adds unit tests
stratoula Sep 9, 2024
31c560f
[CI] Auto-commit changed files from 'node scripts/notice'
kibanamachine Sep 9, 2024
d726cf7
Merge branch 'main' into esql-docs-flyout
stratoula Sep 10, 2024
aeb74f4
Cleanup
stratoula Sep 10, 2024
1f74945
Remove docs from editor when in not inline
stratoula Sep 10, 2024
317049f
Cloeanup
stratoula Sep 10, 2024
6a605ea
Move ESQL docs to the documentation package
stratoula Sep 10, 2024
3cf6fb4
i18n fix
stratoula Sep 10, 2024
cf5fa03
Merge branch 'main' into esql-docs-flyout
stratoula Sep 10, 2024
5e98010
[CI] Auto-commit changed files from 'node scripts/notice'
kibanamachine Sep 10, 2024
d9ed853
Fix paths
stratoula Sep 10, 2024
93f4e4d
Cleanup
stratoula Sep 10, 2024
6f7bb8a
Fix i18n check
stratoula Sep 10, 2024
ef10eb7
More i18n fixes
stratoula Sep 10, 2024
deac8a7
Cleanup unused translations
stratoula Sep 10, 2024
5c2d4cf
Merge with main and resolve conflicts
stratoula Sep 10, 2024
24ed1ab
Merge branch 'main' into esql-docs-flyout
stratoula Sep 11, 2024
bbd8615
Merge with main and resolve conflicts
stratoula Sep 12, 2024
3fa6639
Fix the wrong key
stratoula Sep 12, 2024
4ab9e09
Cleanup translations
stratoula Sep 12, 2024
6bff966
Fix the broken script
stratoula Sep 13, 2024
d8f69a0
Merge branch 'main' into esql-docs-flyout
stratoula Sep 16, 2024
a056700
Give a better layout in the ESQL alert layout
stratoula Sep 16, 2024
4d099f2
Revert the docs button from the footer
stratoula Sep 16, 2024
3b1998c
Change layout of flyout header
ryankeairns Sep 16, 2024
ad2a53b
Add search back
ryankeairns Sep 16, 2024
e426631
Merge pull request #34 from ryankeairns/esql-docs-flyout
stratoula Sep 17, 2024
46005df
Fixes in the merged PR
stratoula Sep 17, 2024
86c81d3
Close the popover when a child is clio
stratoula Sep 17, 2024
aaa8a39
Merge branch 'main' into esql-docs-flyout
stratoula Sep 18, 2024
fe970f4
fix docs generation script
drewdaemon Sep 18, 2024
aa0c6ab
Merge branch 'main' into esql-docs-flyout
elasticmachine Sep 18, 2024
937c5e4
Merge branch 'main' into esql-docs-flyout
elasticmachine Sep 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions packages/kbn-language-documentation-popover/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

export { LanguageDocumentationPopover } from './src/components/documentation_popover';
export { LanguageDocumentationPopoverContent } from './src/components/documentation_content';
export type { LanguageDocumentationSections } from './src/components/documentation_content';
export { LanguageDocumentationPopover } from './src/components/as_popover';
export { LanguageDocumentationPopoverContent } from './src/components/as_popover/popover_content';
export { LanguageDocumentationFlyout } from './src/components/as_flyout';
export { LanguageDocumentationInline } from './src/components/as_inline';
export type { LanguageDocumentationSections } from './src/types';
11 changes: 11 additions & 0 deletions packages/kbn-language-documentation-popover/setup_tests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*
* 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".
*/

// eslint-disable-next-line import/no-extraneous-dependencies
import '@testing-library/jest-dom';
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

import React from 'react';
import { storiesOf } from '@storybook/react';
import { LanguageDocumentationPopover } from '../components/documentation_popover';
import { LanguageDocumentationPopover } from '../components/as_popover';

const sections = {
groups: [
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
* 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 from 'react';
import { screen, render, fireEvent } from '@testing-library/react';
import { Markdown } from '@kbn/shared-ux-markdown';
import { LanguageDocumentationFlyout } from '.';

describe('###Documentation flyout component', () => {
const sections = {
groups: [
{
label: 'Section one',
description: 'Section 1 description',
items: [],
},
{
label: 'Section two',
items: [
{
label: 'Section two item 1',
description: (
<Markdown readOnly markdownContent={`## Section two item 1 description `} />
),
},
{
label: 'Section two item 2',
description: (
<Markdown readOnly markdownContent={`## Section two item 2 description `} />
),
},
],
},
{
label: 'Section three',
items: [
{
label: 'Section three item 1',
description: (
<Markdown readOnly markdownContent={`## Section three item 1 description `} />
),
},
{
label: 'Section three item 2',
description: (
<Markdown readOnly markdownContent={`## Section three item 2 description `} />
),
},
],
},
],
initialSection: <span>Here is the initial section</span>,
};
const renderFlyout = (linkToDocumentation?: string) => {
return render(
<LanguageDocumentationFlyout
isHelpMenuOpen={true}
onHelpMenuVisibilityChange={jest.fn()}
sections={sections}
linkToDocumentation={linkToDocumentation}
/>
);
};
it('has a header element for navigation through the sections', () => {
renderFlyout();
expect(screen.getByTestId('language-documentation-navigation-search')).toBeInTheDocument();
expect(screen.getByTestId('language-documentation-navigation-dropdown')).toBeInTheDocument();
expect(screen.queryByTestId('language-documentation-navigation-link')).not.toBeInTheDocument();
});

it('has a link if linkToDocumentation prop is given', () => {
renderFlyout('meow');
expect(screen.getByTestId('language-documentation-navigation-link')).toBeInTheDocument();
stratoula marked this conversation as resolved.
Show resolved Hide resolved
});

it('contains the two last sections', () => {
renderFlyout();
expect(screen.getByText('Section two')).toBeInTheDocument();
expect(screen.getByText('Section three')).toBeInTheDocument();
});

it('contains the correct section if user updates the search input', () => {
renderFlyout();
const input = screen.getByTestId('language-documentation-navigation-search');
fireEvent.change(input, { target: { value: 'two' } });
expect(screen.getByText('Section two')).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* 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, { useCallback, useEffect, useState, useRef, useMemo } from 'react';
import { EuiFlyout, useEuiTheme, EuiFlyoutBody, EuiFlyoutHeader } from '@elastic/eui';
import { getFilteredGroups } from '../../utils/get_filtered_groups';
import { DocumentationMainContent, DocumentationNavigation } from '../shared';
import type { LanguageDocumentationSections } from '../../types';

interface DocumentationFlyoutProps {
isHelpMenuOpen: boolean;
onHelpMenuVisibilityChange: (status: boolean) => void;
sections?: LanguageDocumentationSections;
searchInDescription?: boolean;
linkToDocumentation?: string;
}

function DocumentationFlyout({
sections,
searchInDescription,
linkToDocumentation,
isHelpMenuOpen,
onHelpMenuVisibilityChange,
}: DocumentationFlyoutProps) {
const { euiTheme } = useEuiTheme();
const DEFAULT_WIDTH = euiTheme.base * 34;

const [selectedSection, setSelectedSection] = useState<string | undefined>();
const [searchText, setSearchText] = useState('');

const scrollTargets = useRef<Record<string, HTMLElement>>({});

const filteredGroups = useMemo(() => {
return getFilteredGroups(searchText, searchInDescription, sections, 1);
}, [sections, searchText, searchInDescription]);

const onNavigationChange = useCallback((selectedOptions) => {
setSelectedSection(selectedOptions.length ? selectedOptions[0].label : undefined);
if (selectedOptions.length) {
const scrollToElement = scrollTargets.current[selectedOptions[0].label];
scrollToElement.scrollIntoView();
}
}, []);

useEffect(() => {
onHelpMenuVisibilityChange(isHelpMenuOpen ?? false);
}, [isHelpMenuOpen, onHelpMenuVisibilityChange]);

return (
<>
{isHelpMenuOpen && (
<EuiFlyout
ownFocus
onClose={() => onHelpMenuVisibilityChange(false)}
aria-labelledby="esqlInlineDocumentationFlyout"
type="push"
size={DEFAULT_WIDTH}
>
<EuiFlyoutHeader hasBorder>
<DocumentationNavigation
searchText={searchText}
setSearchText={setSearchText}
onNavigationChange={onNavigationChange}
filteredGroups={filteredGroups}
linkToDocumentation={linkToDocumentation}
selectedSection={selectedSection}
/>
</EuiFlyoutHeader>
<EuiFlyoutBody>
<DocumentationMainContent
searchText={searchText}
scrollTargets={scrollTargets}
filteredGroups={filteredGroups}
sections={sections}
/>
</EuiFlyoutBody>
</EuiFlyout>
)}
</>
);
}

export const LanguageDocumentationFlyout = React.memo(DocumentationFlyout);
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* 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 from 'react';
import { screen, render, fireEvent } from '@testing-library/react';
import { Markdown } from '@kbn/shared-ux-markdown';
import { LanguageDocumentationInline } from '.';

describe('###Documentation flyout component', () => {
const sections = {
groups: [
{
label: 'Section one',
description: 'Section 1 description',
items: [],
},
{
label: 'Section two',
items: [
{
label: 'Section two item 1',
description: (
<Markdown readOnly markdownContent={`## Section two item 1 description `} />
),
},
{
label: 'Section two item 2',
description: (
<Markdown readOnly markdownContent={`## Section two item 2 description `} />
),
},
],
},
{
label: 'Section three',
items: [
{
label: 'Section three item 1',
description: <Markdown readOnly markdownContent={`## Section three blah blah `} />,
},
{
label: 'Section three item 2',
description: (
<Markdown readOnly markdownContent={`## Section three item 2 description `} />
),
},
],
},
],
initialSection: <span>Here is the initial section</span>,
};
const renderInlineComponent = (searchInDescription = false) => {
return render(
<LanguageDocumentationInline sections={sections} searchInDescription={searchInDescription} />
);
};
it('has a header element for navigation through the sections', () => {
renderInlineComponent();
expect(screen.getByTestId('language-documentation-navigation-search')).toBeInTheDocument();
expect(screen.getByTestId('language-documentation-navigation-dropdown')).toBeInTheDocument();
});

it('contains the two last sections', () => {
renderInlineComponent();
expect(screen.getByText('Section two')).toBeInTheDocument();
expect(screen.getByText('Section three')).toBeInTheDocument();
});

it('contains the correct section if user updates the search input', () => {
renderInlineComponent();
const input = screen.getByTestId('language-documentation-navigation-search');
fireEvent.change(input, { target: { value: 'two' } });
expect(screen.getByText('Section two')).toBeInTheDocument();
});

it('contains the correct section if user updates the search input with a text that exist in the description', () => {
renderInlineComponent(true);
const input = screen.getByTestId('language-documentation-navigation-search');
fireEvent.change(input, { target: { value: 'blah' } });
expect(screen.getByText('Section three')).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* 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, { useCallback, useState, useRef, useMemo } from 'react';
import { css } from '@emotion/react';
import { useEuiTheme, euiScrollBarStyles, EuiSpacer } from '@elastic/eui';
import { getFilteredGroups } from '../../utils/get_filtered_groups';
import { DocumentationMainContent, DocumentationNavigation } from '../shared';
import type { LanguageDocumentationSections } from '../../types';

interface DocumentationInlineProps {
sections?: LanguageDocumentationSections;
searchInDescription?: boolean;
}

const MAX_HEIGHT = 250;

function DocumentationInline({ sections, searchInDescription }: DocumentationInlineProps) {
const theme = useEuiTheme();
const scrollBarStyles = euiScrollBarStyles(theme);
const [selectedSection, setSelectedSection] = useState<string | undefined>();
const [searchText, setSearchText] = useState('');

const scrollTargets = useRef<Record<string, HTMLElement>>({});

const filteredGroups = useMemo(() => {
return getFilteredGroups(searchText, searchInDescription, sections, 1);
}, [sections, searchText, searchInDescription]);

const onNavigationChange = useCallback((selectedOptions) => {
setSelectedSection(selectedOptions.length ? selectedOptions[0].label : undefined);
if (selectedOptions.length) {
const scrollToElement = scrollTargets.current[selectedOptions[0].label];
scrollToElement.scrollIntoView();
}
}, []);

return (
<div
css={css`
padding: ${theme.euiTheme.size.base};
max-height: ${MAX_HEIGHT}px;
overflow-y: auto;
${scrollBarStyles}
`}
>
<DocumentationNavigation
searchText={searchText}
setSearchText={setSearchText}
onNavigationChange={onNavigationChange}
filteredGroups={filteredGroups}
selectedSection={selectedSection}
/>
<EuiSpacer size="s" />
<DocumentationMainContent
searchText={searchText}
scrollTargets={scrollTargets}
filteredGroups={filteredGroups}
sections={sections}
/>
</div>
);
}

export const LanguageDocumentationInline = React.memo(DocumentationInline);
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,8 @@ import {
EuiButtonIconProps,
EuiOutsideClickDetector,
} from '@elastic/eui';
import {
type LanguageDocumentationSections,
LanguageDocumentationPopoverContent,
} from './documentation_content';
import { LanguageDocumentationPopoverContent } from './popover_content';
import type { LanguageDocumentationSections } from '../../types';

interface DocumentationPopoverProps {
language: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import React from 'react';
import { mountWithIntl, findTestSubject } from '@kbn/test-jest-helpers';
import { act } from 'react-dom/test-utils';
import { Markdown } from '@kbn/shared-ux-markdown';
import { LanguageDocumentationPopoverContent } from './documentation_content';
import { LanguageDocumentationPopoverContent } from './popover_content';

describe('###Documentation popover content', () => {
const sections = {
Expand Down
Loading