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

[Runtime fields editor] Form UI #81766

Merged

Conversation

sebelga
Copy link
Contributor

@sebelga sebelga commented Oct 27, 2020

This PR contains the UI component of the runtime field editor form. It is a presentational component that wraps the form with the 3 required fields:

  • name
  • type
  • script.

Interface

The component has the following interface

interface Props {
  docsBaseUri: string; // Required.
  defaultValue?: RuntimeField;
  onChange?: (state: FormState) => void;
}

<RuntimeFieldForm
  docsBaseUri="https://elastic.co"
  defaultValue={defaultRuntimeField}
  onChange={onRuntimeFieldChange}
/>

The component is not meant to be consumed directly, there will be another PR with the "container" component that will act as a layer between the apps consuming the runtime field editor and this UI component.

I've also made the x-pack "runtime_fields" plugin a bundle dependency of the "index_management" plugin and imported from there the constants and TS type required for the mappings editor "runtime" field type.

Note: The flyout, EuiCodeBlock and "Update" button are not part of what is about to be merged. This PR is only the UI form component for the runtime field.

Screenshot 2020-10-27 at 16 12 18

How to test

Replace the content of the x-pack/plugins/index_management/public/application/sections/home/home.tsx file with the code below and navigate to the "index management" app.

/*
 * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
 * or more contributor license agreements. Licensed under the Elastic License;
 * you may not use this file except in compliance with the Elastic License.
 */

import React, { useEffect, useState, useCallback, useRef } from 'react';
import { Route, RouteComponentProps, Switch } from 'react-router-dom';
import { FormattedMessage } from '@kbn/i18n/react';
import {
  EuiButtonEmpty,
  EuiFlexGroup,
  EuiFlexItem,
  EuiPageBody,
  EuiPageContent,
  EuiSpacer,
  EuiTab,
  EuiTabs,
  EuiTitle,
  EuiFlyout,
  EuiFlyoutHeader,
  EuiFlyoutFooter,
  EuiFlyoutBody,
  EuiButton,
  EuiCodeBlock,
} from '@elastic/eui';
import { RuntimeFieldForm, RuntimeField } from '../../../../../runtime_fields/public';
import { documentationService } from '../../services/documentation';
import { DataStreamList } from './data_stream_list';
import { IndexList } from './index_list';
import { TemplateList } from './template_list';
import { ComponentTemplateList } from '../../components/component_templates';
import { breadcrumbService } from '../../services/breadcrumbs';

export enum Section {
  Indices = 'indices',
  DataStreams = 'data_streams',
  IndexTemplates = 'templates',
  ComponentTemplates = 'component_templates',
}

export const homeSections = [
  Section.Indices,
  Section.DataStreams,
  Section.IndexTemplates,
  Section.ComponentTemplates,
];

interface MatchParams {
  section: Section;
}

const defaultRuntimeField: RuntimeField = { name: 'myField', type: 'date', script: 'test=123' };

export const IndexManagementHome: React.FunctionComponent<RouteComponentProps<MatchParams>> = ({
  match: {
    params: { section },
  },
  history,
}) => {
  const tabs = [
    {
      id: Section.Indices,
      name: <FormattedMessage id="xpack.idxMgmt.home.indicesTabTitle" defaultMessage="Indices" />,
    },
    {
      id: Section.DataStreams,
      name: (
        <FormattedMessage
          id="xpack.idxMgmt.home.dataStreamsTabTitle"
          defaultMessage="Data Streams"
        />
      ),
    },
    {
      id: Section.IndexTemplates,
      name: (
        <FormattedMessage
          id="xpack.idxMgmt.home.indexTemplatesTabTitle"
          defaultMessage="Index Templates"
        />
      ),
    },
    {
      id: Section.ComponentTemplates,
      name: (
        <FormattedMessage
          id="xpack.idxMgmt.home.componentTemplatesTabTitle"
          defaultMessage="Component Templates"
        />
      ),
    },
  ];

  const onSectionChange = (newSection: Section) => {
    history.push(`/${newSection}`);
  };

  const [updatedRuntimeField, setUpdatedRuntimeField] = useState(defaultRuntimeField);
  const formStateRef = useRef<any>(null);
  const [isRuntimeFieldEditorVisible, setIsRuntimeFieldEditorVisible] = useState(true);

  const onRuntimeFieldChange = useCallback(async (updated: any) => {
    formStateRef.current = updated;
  }, []);

  const updateRuntimeFieldPreview = useCallback(async () => {
    const { isValid, data } = await formStateRef.current.submit();
    if (isValid) {
      setUpdatedRuntimeField(data);
    }
  }, []);

  useEffect(() => {
    breadcrumbService.setBreadcrumbs('home');
  }, []);

  return (
    <EuiPageBody>
      <EuiPageContent>
        <EuiTitle size="l">
          <EuiFlexGroup alignItems="center">
            <EuiFlexItem grow={true}>
              <h1 data-test-subj="appTitle">
                <FormattedMessage
                  id="xpack.idxMgmt.home.appTitle"
                  defaultMessage="Index Management"
                />
              </h1>
            </EuiFlexItem>
            <EuiFlexItem grow={false}>
              <EuiButtonEmpty
                href={documentationService.getIdxMgmtDocumentationLink()}
                target="_blank"
                iconType="help"
                data-test-subj="documentationLink"
              >
                <FormattedMessage
                  id="xpack.idxMgmt.home.idxMgmtDocsLinkText"
                  defaultMessage="Index Management docs"
                />
              </EuiButtonEmpty>
            </EuiFlexItem>
          </EuiFlexGroup>
        </EuiTitle>

        {isRuntimeFieldEditorVisible && (
          <EuiFlyout size="m" maxWidth={720} onClose={() => setIsRuntimeFieldEditorVisible(false)}>
            <EuiFlyoutHeader>
              <EuiTitle size="m">
                <h2 id="dataStreamDetailPanelTitle">Create new field</h2>
              </EuiTitle>
            </EuiFlyoutHeader>

            <EuiFlyoutBody>
              <RuntimeFieldForm
                docsBaseUri="https://elastic.co"
                defaultValue={defaultRuntimeField}
                onChange={onRuntimeFieldChange}
              />
              <EuiSpacer size="l" />
              <EuiCodeBlock lang="json">
                {JSON.stringify(updatedRuntimeField, null, 2)}
              </EuiCodeBlock>
              <EuiButtonEmpty onClick={updateRuntimeFieldPreview}>Update</EuiButtonEmpty>
            </EuiFlyoutBody>

            <EuiFlyoutFooter>
              <EuiFlexGroup justifyContent="spaceBetween" alignItems="center">
                <EuiFlexItem grow={false}>
                  <EuiButtonEmpty
                    iconType="cross"
                    flush="left"
                    onClick={() => setIsRuntimeFieldEditorVisible(false)}
                    data-test-subj="closeFlyoutButton"
                  >
                    Close
                  </EuiButtonEmpty>
                </EuiFlexItem>

                <EuiFlexItem grow={false}>
                  <EuiButton color="primary" onClick={() => console.log('Saving...')} fill>
                    Save
                  </EuiButton>
                </EuiFlexItem>
              </EuiFlexGroup>
            </EuiFlyoutFooter>
          </EuiFlyout>
        )}

        <EuiSpacer size="m" />

        <EuiTabs>
          {tabs.map((tab) => (
            <EuiTab
              onClick={() => onSectionChange(tab.id)}
              isSelected={tab.id === section}
              key={tab.id}
              data-test-subj={`${tab.id}Tab`}
            >
              {tab.name}
            </EuiTab>
          ))}
        </EuiTabs>

        <EuiSpacer size="m" />

        <Switch>
          <Route
            exact
            path={[`/${Section.DataStreams}`, `/${Section.DataStreams}/:dataStreamName?`]}
            component={DataStreamList}
          />
          <Route exact path={`/${Section.Indices}`} component={IndexList} />
          <Route
            exact
            path={[`/${Section.IndexTemplates}`, `/${Section.IndexTemplates}/:templateName?`]}
            component={TemplateList}
          />
          <Route
            exact
            path={[
              `/${Section.ComponentTemplates}`,
              `/${Section.ComponentTemplates}/:componentTemplateName?`,
            ]}
            component={ComponentTemplateList}
          />
        </Switch>
      </EuiPageContent>
    </EuiPageBody>
  );
};

@sebelga sebelga marked this pull request as ready for review October 27, 2020 15:33
@sebelga sebelga requested a review from a team as a code owner October 27, 2020 15:33
@sebelga sebelga added Project:RuntimeFields Team:Kibana Management Dev Tools, Index Management, Upgrade Assistant, ILM, Ingest Node Pipelines, and more v7.11 v8.0.0 labels Oct 27, 2020
@elasticmachine
Copy link
Contributor

Pinging @elastic/es-ui (Team:Elasticsearch UI)

@sebelga sebelga changed the title Runtime fields/ui form [Runtime fields editor] Form UI Oct 27, 2020
@sebelga
Copy link
Contributor Author

sebelga commented Oct 28, 2020

@elasticmachine merge upstream

@sebelga sebelga requested a review from a team as a code owner October 28, 2020 13:02
Copy link
Member

@mistic mistic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes in files under operations team code owners LGTM

@sebelga
Copy link
Contributor Author

sebelga commented Oct 28, 2020

Thanks for the review @mistic ! Do you know why the build is failing? Not sure I understand the error:

‼️ ERROR: metrics for af0f496 were not reported

@mistic
Copy link
Member

mistic commented Oct 28, 2020

@elasticmachine merge upstream

@spalger
Copy link
Contributor

spalger commented Oct 28, 2020

@sebelga the problem is that baselines for each commit to the feature/runtime-field-editor branch are not being established, so the ci metrics are unable to determine the change this PR will have on the metrics... Thinking about how we can handle this, really don't want to start merging PRs which don't have green reports from CI but we probably need to make changes to the Jenkins pipeline to support PRs like this...

@spalger
Copy link
Contributor

spalger commented Oct 28, 2020

Exploring a solution in #81938, mind giving me a few hours to see how feasible this solution is?

@spalger
Copy link
Contributor

spalger commented Oct 28, 2020

@elasticmachine merge upstream

@kibanamachine
Copy link
Contributor

💚 Build Succeeded

Metrics [docs]

‼️ ERROR: metrics for 7a9d27a were not reported

History

To update your PR or re-run it, just comment with:
@elasticmachine merge upstream

@spalger
Copy link
Contributor

spalger commented Oct 28, 2020

#81938 changed the CI metrics check to only fail if the PR is merging into a tracked branch, like master. The ‼️ ERROR: metrics for 7a9d27a were not reported error in the report is caused by the fact that CI fails for the commit 7a9d27a because it doesn't have the changes in this PR. If that commit was from master Operations would be rushing to fix it, but since it's in a feature branch we assume that the contributors owning that branch have it figured out, so you can ignore the error for now.

If a similar error was to occur on a branch targeting master then the report would say it was failed.

@sebelga
Copy link
Contributor Author

sebelga commented Oct 29, 2020

Awesome, thanks for looking into this @spalger !

@timroes timroes added v7.11.0 and removed v7.11 labels Oct 29, 2020
Copy link
Contributor

@alisonelizabeth alisonelizabeth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work @sebelga! It's exciting to start seeing this take shape. I left a few comments, nothing blocking. Testing locally and worked as expected.

@@ -460,6 +460,10 @@ Elastic.
|Welcome to the Kibana rollup plugin! This plugin provides Kibana support for Elasticsearch's rollup feature. Please refer to the Elasticsearch documentation to understand rollup indices and how to create rollup jobs.


|{kib-repo}blob/{branch}/x-pack/plugins/runtime_fields[runtimeFields]
|WARNING: Missing README.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add a readme? I understand it might not contain much info at this point and is subject to change.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have the README.md ready in my following PR 😊

<EuiFlexGroup justifyContent="flexEnd">
<EuiFlexItem grow={false}>
<EuiLink
href={`${docsBaseUri}/to-be-defined`}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I'd prefer to comment the link component out for now, rather than merge it with an invalid href.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have updated the href in my following PR to point correctly to https://www.elastic.co/guide/en/elasticsearch/painless/master/painless-lang-spec.html

fullWidth
>
<CodeEditor
languageId="painless"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should be able to import the language id from kbn-monaco now.

import { PainlessLang } from '@kbn-monaco';

<CodeEditor 
  languageId={PainlessLang.ID}
  ....
/>

Copy link
Contributor Author

@sebelga sebelga Oct 30, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good to know, I will update it. Although IMO it should be the CodeEditor that should have the languageId correctly typed with available options ('painless'|'other')

return (
<>
<EuiFormRow label={label} fullWidth>
<EuiComboBox
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not as familiar with the proposed design - but would the EuiSelect suffice here, as there are only 6 supported types?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For now, I've mirrored what CJ did for the mappings editor "runtime" field type in #79940
I'm happy to revisit this later if we don't think the ComboBox brings value 👍

@sebelga
Copy link
Contributor Author

sebelga commented Oct 30, 2020

Thanks for the review @alisonelizabeth ! I've addressed your comments in my following PR

@sebelga sebelga merged commit cf7bf91 into elastic:feature/runtime-field-editor Oct 30, 2020
@sebelga sebelga deleted the runtime-fields/ui-form branch October 30, 2020 08:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Project:RuntimeFields Team:Kibana Management Dev Tools, Index Management, Upgrade Assistant, ILM, Ingest Node Pipelines, and more v7.11.0 v8.0.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants