diff --git a/.github/workflows/add-to-imui-project.yml b/.github/workflows/add-to-imui-project.yml new file mode 100644 index 00000000000000..3cf120b2e81bc5 --- /dev/null +++ b/.github/workflows/add-to-imui-project.yml @@ -0,0 +1,31 @@ +name: Add to Infra Monitoring UI project +on: + issues: + types: + - labeled +jobs: + add_to_project: + runs-on: ubuntu-latest + if: | + contains(github.event.issue.labels.*.name, 'Team:Infra Monitoring UI') || + contains(github.event.issue.labels.*.name, 'Feature:Stack Monitoring') || + contains(github.event.issue.labels.*.name, 'Feature:Logs UI') || + contains(github.event.issue.labels.*.name, 'Feature:Metrics UI') + steps: + - uses: octokit/graphql-action@v2.x + id: add_to_project + with: + headers: '{"GraphQL-Features": "projects_next_graphql"}' + query: | + mutation add_to_project($projectid:ID!,$contentid:ID!) { + addProjectNextItem(input:{projectId:$projectid contentId:$contentid}) { + projectNextItem { + id + } + } + } + projectid: ${{ env.PROJECT_ID }} + contentid: ${{ github.event.issue.node_id }} + env: + PROJECT_ID: "PN_kwDOAGc3Zs1EEA" + GITHUB_TOKEN: ${{ secrets.PROJECT_ASSIGNER_TOKEN }} diff --git a/.github/workflows/project-assigner.yml b/.github/workflows/project-assigner.yml index 65808dffd801f5..8c381dd1ecdefa 100644 --- a/.github/workflows/project-assigner.yml +++ b/.github/workflows/project-assigner.yml @@ -18,8 +18,6 @@ jobs: {"label": "Feature:Canvas", "projectNumber": 38, "columnName": "Inbox"}, {"label": "Feature:Dashboard", "projectNumber": 68, "columnName": "Inbox"}, {"label": "Feature:Drilldowns", "projectNumber": 68, "columnName": "Inbox"}, - {"label": "Feature:Input Controls", "projectNumber": 72, "columnName": "Inbox"}, - {"label": "Team:Security", "projectNumber": 320, "columnName": "Awaiting triage", "projectScope": "org"}, - {"label": "Team:Operations", "projectNumber": 314, "columnName": "Triage", "projectScope": "org"} + {"label": "Feature:Input Controls", "projectNumber": 72, "columnName": "Inbox"} ] ghToken: ${{ secrets.PROJECT_ASSIGNER_TOKEN }} diff --git a/src/core/server/ui_settings/settings/date_formats.ts b/src/core/server/ui_settings/settings/date_formats.ts index c626c4a83cc4cc..039ead326a2361 100644 --- a/src/core/server/ui_settings/settings/date_formats.ts +++ b/src/core/server/ui_settings/settings/date_formats.ts @@ -31,7 +31,7 @@ export const getDateFormatSettings = (): Record => { }), value: 'MMM D, YYYY @ HH:mm:ss.SSS', description: i18n.translate('core.ui_settings.params.dateFormatText', { - defaultMessage: 'When displaying a pretty formatted date, use this {formatLink}', + defaultMessage: 'The {formatLink} for pretty formatted dates.', description: 'Part of composite text: core.ui_settings.params.dateFormatText + ' + 'core.ui_settings.params.dateFormat.optionsLinkText', @@ -48,15 +48,11 @@ export const getDateFormatSettings = (): Record => { }, 'dateFormat:tz': { name: i18n.translate('core.ui_settings.params.dateFormat.timezoneTitle', { - defaultMessage: 'Timezone for date formatting', + defaultMessage: 'Time zone', }), value: 'Browser', description: i18n.translate('core.ui_settings.params.dateFormat.timezoneText', { - defaultMessage: - 'Which timezone should be used. {defaultOption} will use the timezone detected by your browser.', - values: { - defaultOption: '"Browser"', - }, + defaultMessage: 'The default time zone.', }), type: 'select', options: timezones, @@ -115,7 +111,7 @@ export const getDateFormatSettings = (): Record => { }), value: defaultWeekday, description: i18n.translate('core.ui_settings.params.dateFormat.dayOfWeekText', { - defaultMessage: 'What day should weeks start on?', + defaultMessage: 'The day that starts the week.', }), type: 'select', options: weekdays, @@ -141,7 +137,7 @@ export const getDateFormatSettings = (): Record => { }), value: 'MMM D, YYYY @ HH:mm:ss.SSSSSSSSS', description: i18n.translate('core.ui_settings.params.dateNanosFormatText', { - defaultMessage: 'Used for the {dateNanosLink} datatype of Elasticsearch', + defaultMessage: 'The format for {dateNanosLink} data.', values: { dateNanosLink: '' + diff --git a/src/plugins/dashboard/public/application/top_nav/editor_menu.tsx b/src/plugins/dashboard/public/application/top_nav/editor_menu.tsx index 44b1aec226fd66..5fece7ff959ced 100644 --- a/src/plugins/dashboard/public/application/top_nav/editor_menu.tsx +++ b/src/plugins/dashboard/public/application/top_nav/editor_menu.tsx @@ -153,7 +153,8 @@ export const EditorMenu = ({ dashboardContainer, createNewVisType }: Props) => { }; const getEmbeddableFactoryMenuItem = ( - factory: EmbeddableFactoryDefinition + factory: EmbeddableFactoryDefinition, + closePopover: () => void ): EuiContextMenuPanelItemDescriptor => { const icon = factory?.getIconType ? factory.getIconType() : 'empty'; @@ -164,6 +165,7 @@ export const EditorMenu = ({ dashboardContainer, createNewVisType }: Props) => { icon, toolTipContent, onClick: async () => { + closePopover(); if (trackUiMetric) { trackUiMetric(METRIC_TYPE.CLICK, factory.type); } @@ -192,42 +194,47 @@ export const EditorMenu = ({ dashboardContainer, createNewVisType }: Props) => { defaultMessage: 'Aggregation based', }); - const editorMenuPanels = [ - { - id: 0, - items: [ - ...visTypeAliases.map(getVisTypeAliasMenuItem), - ...Object.values(factoryGroupMap).map(({ id, appName, icon, panelId }) => ({ - name: appName, - icon, - panel: panelId, - 'data-test-subj': `dashboardEditorMenu-${id}Group`, - })), - ...ungroupedFactories.map(getEmbeddableFactoryMenuItem), - ...promotedVisTypes.map(getVisTypeMenuItem), - { - name: aggsPanelTitle, - icon: 'visualizeApp', - panel: aggBasedPanelID, - 'data-test-subj': `dashboardEditorAggBasedMenuItem`, - }, - ...toolVisTypes.map(getVisTypeMenuItem), - ], - }, - { - id: aggBasedPanelID, - title: aggsPanelTitle, - items: aggsBasedVisTypes.map(getVisTypeMenuItem), - }, - ...Object.values(factoryGroupMap).map( - ({ appName, panelId, factories: groupFactories }: FactoryGroup) => ({ - id: panelId, - title: appName, - items: groupFactories.map(getEmbeddableFactoryMenuItem), - }) - ), - ]; - + const getEditorMenuPanels = (closePopover: () => void) => { + return [ + { + id: 0, + items: [ + ...visTypeAliases.map(getVisTypeAliasMenuItem), + ...Object.values(factoryGroupMap).map(({ id, appName, icon, panelId }) => ({ + name: appName, + icon, + panel: panelId, + 'data-test-subj': `dashboardEditorMenu-${id}Group`, + })), + ...ungroupedFactories.map((factory) => { + return getEmbeddableFactoryMenuItem(factory, closePopover); + }), + ...promotedVisTypes.map(getVisTypeMenuItem), + { + name: aggsPanelTitle, + icon: 'visualizeApp', + panel: aggBasedPanelID, + 'data-test-subj': `dashboardEditorAggBasedMenuItem`, + }, + ...toolVisTypes.map(getVisTypeMenuItem), + ], + }, + { + id: aggBasedPanelID, + title: aggsPanelTitle, + items: aggsBasedVisTypes.map(getVisTypeMenuItem), + }, + ...Object.values(factoryGroupMap).map( + ({ appName, panelId, factories: groupFactories }: FactoryGroup) => ({ + id: panelId, + title: appName, + items: groupFactories.map((factory) => { + return getEmbeddableFactoryMenuItem(factory, closePopover); + }), + }) + ), + ]; + }; return ( { panelPaddingSize="none" data-test-subj="dashboardEditorMenuButton" > - {() => ( + {({ closePopover }: { closePopover: () => void }) => ( { await PageObjects.common.navigateToApp('dashboard'); - await PageObjects.dashboard.preserveCrossAppState(); - await PageObjects.dashboard.loadSavedDashboard('few panels'); + await PageObjects.dashboard.clickNewDashboard(); + await PageObjects.dashboard.switchToEditMode(); + await dashboardAddPanel.clickEditorMenuButton(); + await dashboardAddPanel.clickAddNewEmbeddableLink('LOG_STREAM_EMBEDDABLE'); + await dashboardAddPanel.expectEditorMenuClosed(); }); describe('add new visualization link', () => { + before(async () => { + await PageObjects.common.navigateToApp('dashboard'); + await PageObjects.dashboard.preserveCrossAppState(); + await PageObjects.dashboard.loadSavedDashboard('few panels'); + }); + it('adds new visualization via the top nav link', async () => { const originalPanelCount = await PageObjects.dashboard.getPanelCount(); await PageObjects.dashboard.switchToEditMode(); diff --git a/test/functional/services/dashboard/add_panel.ts b/test/functional/services/dashboard/add_panel.ts index 43ab1f966bc9a0..e42c221a494759 100644 --- a/test/functional/services/dashboard/add_panel.ts +++ b/test/functional/services/dashboard/add_panel.ts @@ -46,6 +46,11 @@ export class DashboardAddPanelService extends FtrService { async clickEditorMenuButton() { this.log.debug('DashboardAddPanel.clickEditorMenuButton'); await this.testSubjects.click('dashboardEditorMenuButton'); + await this.testSubjects.existOrFail('dashboardEditorContextMenu'); + } + + async expectEditorMenuClosed() { + await this.testSubjects.missingOrFail('dashboardEditorContextMenu'); } async clickAggBasedVisualizations() { diff --git a/x-pack/plugins/security_solution/cypress/integration/cases/creation.spec.ts b/x-pack/plugins/security_solution/cypress/integration/cases/creation.spec.ts index 90ab1d098aef50..d08f11a95b1945 100644 --- a/x-pack/plugins/security_solution/cypress/integration/cases/creation.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/cases/creation.spec.ts @@ -50,7 +50,8 @@ import { loginAndWaitForPageWithoutDateRange } from '../../tasks/login'; import { CASES_URL } from '../../urls/navigation'; -describe('Cases', () => { +// Flaky: https://github.com/elastic/kibana/issues/69847 +describe.skip('Cases', () => { beforeEach(() => { cleanKibana(); createTimeline(getCase1().timeline).then((response) => diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_rules/export_rule.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_rules/export_rule.spec.ts index 0314c0c3a66b62..1e1abaa326bd47 100644 --- a/x-pack/plugins/security_solution/cypress/integration/detection_rules/export_rule.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/detection_rules/export_rule.spec.ts @@ -13,7 +13,8 @@ import { loginAndWaitForPageWithoutDateRange } from '../../tasks/login'; import { DETECTIONS_RULE_MANAGEMENT_URL } from '../../urls/navigation'; -describe('Export rules', () => { +// Flaky https://github.com/elastic/kibana/issues/69849 +describe.skip('Export rules', () => { beforeEach(() => { cleanKibana(); cy.intercept( diff --git a/x-pack/plugins/security_solution/cypress/integration/hosts/events_viewer.spec.ts b/x-pack/plugins/security_solution/cypress/integration/hosts/events_viewer.spec.ts index 048efd00d276b3..c28c55e0eb3f7f 100644 --- a/x-pack/plugins/security_solution/cypress/integration/hosts/events_viewer.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/hosts/events_viewer.spec.ts @@ -108,7 +108,6 @@ describe('Events Viewer', () => { it('resets all fields in the events viewer when `Reset Fields` is clicked', () => { const filterInput = 'host.geo.c'; - filterFieldsBrowser(filterInput); cy.get(HOST_GEO_COUNTRY_NAME_HEADER).should('not.exist'); addsHostGeoCountryNameToHeader(); diff --git a/x-pack/plugins/security_solution/cypress/integration/timelines/fields_browser.spec.ts b/x-pack/plugins/security_solution/cypress/integration/timelines/fields_browser.spec.ts index be726f0323d48c..07ea4078ce7c4b 100644 --- a/x-pack/plugins/security_solution/cypress/integration/timelines/fields_browser.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/timelines/fields_browser.spec.ts @@ -32,6 +32,7 @@ import { import { loginAndWaitForPage } from '../../tasks/login'; import { openTimelineUsingToggle } from '../../tasks/security_main'; import { openTimelineFieldsBrowser, populateTimeline } from '../../tasks/timeline'; +import { ecsFieldMap } from '../../../../rule_registry/common/assets/field_maps/ecs_field_map'; import { HOSTS_URL } from '../../urls/navigation'; @@ -109,7 +110,27 @@ describe('Fields Browser', () => { filterFieldsBrowser(filterInput); - cy.get(FIELDS_BROWSER_SELECTED_CATEGORY_COUNT).should('have.text', '5'); + const fieldsThatMatchFilterInput = Object.keys(ecsFieldMap).filter((fieldName) => { + const dotDelimitedFieldParts = fieldName.split('.'); + const fieldPartMatch = dotDelimitedFieldParts.filter((fieldPart) => { + const camelCasedStringsMatching = fieldPart + .split('_') + .some((part) => part.startsWith(filterInput)); + if (fieldPart.startsWith(filterInput)) { + return true; + } else if (camelCasedStringsMatching) { + return true; + } else { + return false; + } + }); + return fieldName.startsWith(filterInput) || fieldPartMatch.length > 0; + }).length; + + cy.get(FIELDS_BROWSER_SELECTED_CATEGORY_COUNT).should( + 'have.text', + fieldsThatMatchFilterInput + ); }); }); diff --git a/x-pack/plugins/security_solution/cypress/integration/timelines/local_storage.spec.ts b/x-pack/plugins/security_solution/cypress/integration/timelines/local_storage.spec.ts index 617f04697c9513..b3139d94aa6258 100644 --- a/x-pack/plugins/security_solution/cypress/integration/timelines/local_storage.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/timelines/local_storage.spec.ts @@ -14,7 +14,7 @@ import { waitsForEventsToBeLoaded } from '../../tasks/hosts/events'; import { removeColumn } from '../../tasks/timeline'; // TODO: Fix bug in persisting the columns of timeline -describe('persistent timeline', () => { +describe.skip('persistent timeline', () => { beforeEach(() => { cleanKibana(); loginAndWaitForPage(HOSTS_URL); diff --git a/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts b/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts index 8475ef7247c2c6..ab09aca83f575a 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts @@ -233,7 +233,7 @@ export const changeRowsPerPageTo = (rowsCount: number) => { cy.get(PAGINATION_POPOVER_BTN).click({ force: true }); cy.get(rowsPerPageSelector(rowsCount)) .pipe(($el) => $el.trigger('click')) - .should('not.be.visible'); + .should('not.exist'); }; export const changeRowsPerPageTo100 = () => { diff --git a/x-pack/plugins/security_solution/cypress/tasks/fields_browser.ts b/x-pack/plugins/security_solution/cypress/tasks/fields_browser.ts index ee8bdb3b023dde..941a19669f2efb 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/fields_browser.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/fields_browser.ts @@ -34,17 +34,24 @@ export const addsHostGeoContinentNameToTimeline = () => { }; export const clearFieldsBrowser = () => { + cy.clock(); cy.get(FIELDS_BROWSER_FILTER_INPUT).type('{selectall}{backspace}'); + cy.wait(0); + cy.tick(1000); }; export const closeFieldsBrowser = () => { cy.get(CLOSE_BTN).click({ force: true }); + cy.get(FIELDS_BROWSER_FILTER_INPUT).should('not.exist'); }; export const filterFieldsBrowser = (fieldName: string) => { - cy.get(FIELDS_BROWSER_FILTER_INPUT) - .type(fieldName) - .should('not.have.class', 'euiFieldSearch-isLoading'); + cy.clock(); + cy.get(FIELDS_BROWSER_FILTER_INPUT).type(fieldName, { delay: 50 }); + cy.wait(0); + cy.tick(1000); + // the text filter is debounced by 250 ms, wait 1s for changes to be applied + cy.get(FIELDS_BROWSER_FILTER_INPUT).should('not.have.class', 'euiFieldSearch-isLoading'); }; export const removesMessageField = () => { diff --git a/x-pack/plugins/timelines/public/components/fields_browser/index.tsx b/x-pack/plugins/timelines/public/components/fields_browser/index.tsx index 02fd0553f4016c..31b8e9f62803ec 100644 --- a/x-pack/plugins/timelines/public/components/fields_browser/index.tsx +++ b/x-pack/plugins/timelines/public/components/fields_browser/index.tsx @@ -28,10 +28,7 @@ export const FieldBrowserWrappedComponent = (props: FieldBrowserWrappedComponent return ( - + ); diff --git a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/field_browser.test.tsx b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/field_browser.test.tsx index dc9837007e1538..d435d7a280840b 100644 --- a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/field_browser.test.tsx +++ b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/field_browser.test.tsx @@ -32,6 +32,7 @@ const testProps = { browserFields: mockBrowserFields, filteredBrowserFields: mockBrowserFields, searchInput: '', + appliedFilterInput: '', isSearching: false, onCategorySelected: jest.fn(), onHide, @@ -84,6 +85,7 @@ describe('FieldsBrowser', () => { browserFields={mockBrowserFields} filteredBrowserFields={mockBrowserFields} searchInput={''} + appliedFilterInput={''} isSearching={false} onCategorySelected={jest.fn()} onHide={jest.fn()} diff --git a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/field_browser.tsx b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/field_browser.tsx index fea22e4efe77c1..e55f54e946ad13 100644 --- a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/field_browser.tsx +++ b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/field_browser.tsx @@ -75,6 +75,8 @@ type Props = Pick & isSearching: boolean; /** The text displayed in the search input */ searchInput: string; + /** The text actually being applied to the result set, a debounced version of searchInput */ + appliedFilterInput: string; /** * The category selected on the left-hand side of the field browser */ @@ -115,6 +117,7 @@ const FieldsBrowserComponent: React.FC = ({ onHide, restoreFocusTo, searchInput, + appliedFilterInput, selectedCategoryId, timelineId, width = FIELD_BROWSER_WIDTH, @@ -237,7 +240,7 @@ const FieldsBrowserComponent: React.FC = ({ filteredBrowserFields={filteredBrowserFields} onCategorySelected={onCategorySelected} onUpdateColumns={onUpdateColumns} - searchInput={searchInput} + searchInput={appliedFilterInput} selectedCategoryId={selectedCategoryId} timelineId={timelineId} width={FIELDS_PANE_WIDTH} diff --git a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/fields_pane.tsx b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/fields_pane.tsx index 5345475a025018..d1d0254d0c917d 100644 --- a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/fields_pane.tsx +++ b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/fields_pane.tsx @@ -98,19 +98,30 @@ export const FieldsPane = React.memo( [filteredBrowserFields] ); + const fieldItems = useMemo(() => { + return getFieldItems({ + category: filteredBrowserFields[selectedCategoryId], + columnHeaders, + highlight: searchInput, + timelineId, + toggleColumn, + }); + }, [ + columnHeaders, + filteredBrowserFields, + searchInput, + selectedCategoryId, + timelineId, + toggleColumn, + ]); + if (filteredBrowserFieldsExists) { return ( = ({ /** all field names shown in the field browser must contain this string (when specified) */ const [filterInput, setFilterInput] = useState(''); + + const [appliedFilterInput, setAppliedFilterInput] = useState(''); /** all fields in this collection have field names that match the filterInput */ const [filteredBrowserFields, setFilteredBrowserFields] = useState(null); /** when true, show a spinner in the input to indicate the field browser is searching for matching field names */ @@ -51,15 +53,6 @@ export const StatefulFieldsBrowserComponent: React.FC = ({ const [selectedCategoryId, setSelectedCategoryId] = useState(DEFAULT_CATEGORY_NAME); /** show the field browser */ const [show, setShow] = useState(false); - useEffect(() => { - return () => { - if (inputTimeoutId.current !== 0) { - // ⚠️ mutation: cancel any remaining timers and zero-out the timer id: - clearTimeout(inputTimeoutId.current); - inputTimeoutId.current = 0; - } - }; - }, []); /** Shows / hides the field browser */ const onShow = useCallback(() => { @@ -69,52 +62,68 @@ export const StatefulFieldsBrowserComponent: React.FC = ({ /** Invoked when the field browser should be hidden */ const onHide = useCallback(() => { setFilterInput(''); + setAppliedFilterInput(''); setFilteredBrowserFields(null); setIsSearching(false); setSelectedCategoryId(DEFAULT_CATEGORY_NAME); setShow(false); }, []); + const newFilteredBrowserFields = useMemo(() => { + return filterBrowserFieldsByFieldName({ + browserFields: mergeBrowserFieldsWithDefaultCategory(browserFields), + substring: appliedFilterInput, + }); + }, [appliedFilterInput, browserFields]); + + const newSelectedCategoryId = useMemo(() => { + if (appliedFilterInput === '' || Object.keys(newFilteredBrowserFields).length === 0) { + return DEFAULT_CATEGORY_NAME; + } else { + return Object.keys(newFilteredBrowserFields) + .sort() + .reduce((selected, category) => { + const filteredBrowserFieldsByCategory = + (newFilteredBrowserFields[category] && newFilteredBrowserFields[category].fields) || []; + const filteredBrowserFieldsBySelected = + (newFilteredBrowserFields[selected] && newFilteredBrowserFields[selected].fields) || []; + return newFilteredBrowserFields[category].fields != null && + newFilteredBrowserFields[selected].fields != null && + Object.keys(filteredBrowserFieldsByCategory).length > + Object.keys(filteredBrowserFieldsBySelected).length + ? category + : selected; + }, Object.keys(newFilteredBrowserFields)[0]); + } + }, [appliedFilterInput, newFilteredBrowserFields]); + /** Invoked when the user types in the filter input */ - const updateFilter = useCallback( - (newFilterInput: string) => { - setFilterInput(newFilterInput); - setIsSearching(true); - if (inputTimeoutId.current !== 0) { - clearTimeout(inputTimeoutId.current); // ⚠️ mutation: cancel any previous timers - } - // ⚠️ mutation: schedule a new timer that will apply the filter when it fires: - inputTimeoutId.current = window.setTimeout(() => { - const newFilteredBrowserFields = filterBrowserFieldsByFieldName({ - browserFields: mergeBrowserFieldsWithDefaultCategory(browserFields), - substring: newFilterInput, - }); - setFilteredBrowserFields(newFilteredBrowserFields); - setIsSearching(false); - - const newSelectedCategoryId = - newFilterInput === '' || Object.keys(newFilteredBrowserFields).length === 0 - ? DEFAULT_CATEGORY_NAME - : Object.keys(newFilteredBrowserFields) - .sort() - .reduce( - (selected, category) => - newFilteredBrowserFields[category].fields != null && - newFilteredBrowserFields[selected].fields != null && - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - Object.keys(newFilteredBrowserFields[category].fields!).length > - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - Object.keys(newFilteredBrowserFields[selected].fields!).length - ? category - : selected, - Object.keys(newFilteredBrowserFields)[0] - ); - setSelectedCategoryId(newSelectedCategoryId); - }, INPUT_TIMEOUT); - }, - // eslint-disable-next-line react-hooks/exhaustive-deps - [browserFields, filterInput, inputTimeoutId.current] - ); + const updateFilter = useCallback((newFilterInput: string) => { + setFilterInput(newFilterInput); + setIsSearching(true); + }, []); + + useEffect(() => { + if (inputTimeoutId.current !== 0) { + clearTimeout(inputTimeoutId.current); // ⚠️ mutation: cancel any previous timers + } + // ⚠️ mutation: schedule a new timer that will apply the filter when it fires: + inputTimeoutId.current = window.setTimeout(() => { + setIsSearching(false); + setAppliedFilterInput(filterInput); + }, INPUT_TIMEOUT); + return () => { + clearTimeout(inputTimeoutId.current); + }; + }, [filterInput]); + + useEffect(() => { + setFilteredBrowserFields(newFilteredBrowserFields); + }, [newFilteredBrowserFields]); + + useEffect(() => { + setSelectedCategoryId(newSelectedCategoryId); + }, [newSelectedCategoryId]); // only merge in the default category if the field browser is visible const browserFieldsWithDefaultCategory = useMemo(() => { @@ -152,6 +161,7 @@ export const StatefulFieldsBrowserComponent: React.FC = ({ onSearchInputChange={updateFilter} restoreFocusTo={customizeColumnsButtonRef} searchInput={filterInput} + appliedFilterInput={appliedFilterInput} selectedCategoryId={selectedCategoryId} timelineId={timelineId} width={width} diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 96327533d4e291..99e4c6a4ca1f30 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -1254,18 +1254,13 @@ "core.toasts.errorToast.seeFullError": "Voir l'erreur en intégralité", "core.ui_settings.params.darkModeText": "Activez le mode sombre pour l'interface utilisateur Kibana. Vous devez actualiser la page pour que ce paramètre s’applique.", "core.ui_settings.params.darkModeTitle": "Mode sombre", - "core.ui_settings.params.dateFormat.dayOfWeekText": "Quel est le premier jour de la semaine ?", "core.ui_settings.params.dateFormat.dayOfWeekTitle": "Jour de la semaine", "core.ui_settings.params.dateFormat.optionsLinkText": "format", "core.ui_settings.params.dateFormat.scaled.intervalsLinkText": "Intervalles ISO8601", "core.ui_settings.params.dateFormat.scaledText": "Les valeurs qui définissent le format utilisé lorsque les données temporelles sont rendues dans l'ordre, et lorsque les horodatages formatés doivent s'adapter à l'intervalle entre les mesures. Les clés sont {intervalsLink}.", "core.ui_settings.params.dateFormat.scaledTitle": "Format de date scalé", "core.ui_settings.params.dateFormat.timezone.invalidValidationMessage": "Fuseau horaire non valide : {timezone}", - "core.ui_settings.params.dateFormat.timezoneText": "Fuseau horaire à utiliser. L’option {defaultOption} utilise le fuseau horaire détecté par le navigateur.", - "core.ui_settings.params.dateFormat.timezoneTitle": "Fuseau horaire pour le format de date", - "core.ui_settings.params.dateFormatText": "{formatLink} utilisé pour les dates formatées", "core.ui_settings.params.dateFormatTitle": "Format de date", - "core.ui_settings.params.dateNanosFormatText": "Utilisé pour le type de données {dateNanosLink} d'Elasticsearch", "core.ui_settings.params.dateNanosFormatTitle": "Date au format nanosecondes", "core.ui_settings.params.dateNanosLinkTitle": "date_nanos", "core.ui_settings.params.dayOfWeekText.invalidValidationMessage": "Jour de la semaine non valide : {dayOfWeek}", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index d47ff1ed31496a..70d65550a40563 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -1567,18 +1567,13 @@ "core.toasts.errorToast.seeFullError": "完全なエラーを表示", "core.ui_settings.params.darkModeText": "Kibana UIのダークモードを有効にします。この設定を適用するにはページの更新が必要です。", "core.ui_settings.params.darkModeTitle": "ダークモード", - "core.ui_settings.params.dateFormat.dayOfWeekText": "週の初めの曜日を設定します", "core.ui_settings.params.dateFormat.dayOfWeekTitle": "曜日", "core.ui_settings.params.dateFormat.optionsLinkText": "フォーマット", "core.ui_settings.params.dateFormat.scaled.intervalsLinkText": "ISO8601間隔", "core.ui_settings.params.dateFormat.scaledText": "時間ベースのデータが順番にレンダリングされ、フォーマットされたタイムスタンプが測定値の間隔に適応すべき状況で使用されるフォーマットを定義する値です。キーは{intervalsLink}です。", "core.ui_settings.params.dateFormat.scaledTitle": "スケーリングされたデータフォーマットです", "core.ui_settings.params.dateFormat.timezone.invalidValidationMessage": "無効なタイムゾーン:{timezone}", - "core.ui_settings.params.dateFormat.timezoneText": "使用されるタイムゾーンです。{defaultOption}ではご使用のブラウザーにより検知されたタイムゾーンが使用されます。", - "core.ui_settings.params.dateFormat.timezoneTitle": "データフォーマットのタイムゾーン", - "core.ui_settings.params.dateFormatText": "きちんとフォーマットされたデータを表示する際、この{formatLink}を使用します", "core.ui_settings.params.dateFormatTitle": "データフォーマット", - "core.ui_settings.params.dateNanosFormatText": "Elasticsearchの{dateNanosLink}データタイプに使用されます", "core.ui_settings.params.dateNanosFormatTitle": "ナノ秒フォーマットでの日付", "core.ui_settings.params.dateNanosLinkTitle": "date_nanos", "core.ui_settings.params.dayOfWeekText.invalidValidationMessage": "無効な曜日:{dayOfWeek}", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 96f9e892ba0b93..90eb0d3c35f653 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -1574,18 +1574,13 @@ "core.toasts.errorToast.seeFullError": "请参阅完整的错误信息", "core.ui_settings.params.darkModeText": "对 Kibana UI 启用深色模式。需要刷新页面,才能应用设置。", "core.ui_settings.params.darkModeTitle": "深色模式", - "core.ui_settings.params.dateFormat.dayOfWeekText": "一周应该从哪一天开始?", "core.ui_settings.params.dateFormat.dayOfWeekTitle": "周内日", "core.ui_settings.params.dateFormat.optionsLinkText": "格式", "core.ui_settings.params.dateFormat.scaled.intervalsLinkText": "ISO8601 时间间隔", "core.ui_settings.params.dateFormat.scaledText": "定义在以下场合中采用的格式的值:基于时间的数据按顺序呈现,且经格式化的时间戳应适应度量之间的时间间隔。键是{intervalsLink}。", "core.ui_settings.params.dateFormat.scaledTitle": "标度日期格式", "core.ui_settings.params.dateFormat.timezone.invalidValidationMessage": "时区无效:{timezone}", - "core.ui_settings.params.dateFormat.timezoneText": "应使用哪个时区。{defaultOption} 将使用您的浏览器检测到的时区。", - "core.ui_settings.params.dateFormat.timezoneTitle": "用于设置日期格式的时区", - "core.ui_settings.params.dateFormatText": "显示格式正确的日期时,请使用此{formatLink}", "core.ui_settings.params.dateFormatTitle": "日期格式", - "core.ui_settings.params.dateNanosFormatText": "用于 Elasticsearch 的 {dateNanosLink} 数据类型", "core.ui_settings.params.dateNanosFormatTitle": "纳秒格式的日期", "core.ui_settings.params.dateNanosLinkTitle": "date_nanos", "core.ui_settings.params.dayOfWeekText.invalidValidationMessage": "周内日无效:{dayOfWeek}", diff --git a/x-pack/test/security_solution_cypress/es_archives/auditbeat/mappings.json b/x-pack/test/security_solution_cypress/es_archives/auditbeat/mappings.json index 3196232e59643f..061748d72b77b0 100644 --- a/x-pack/test/security_solution_cypress/es_archives/auditbeat/mappings.json +++ b/x-pack/test/security_solution_cypress/es_archives/auditbeat/mappings.json @@ -1735,6 +1735,10 @@ "ignore_above": 1024, "type": "keyword" }, + "continent_code": { + "ignore_above": 1024, + "type": "keyword" + }, "continent_name": { "ignore_above": 1024, "type": "keyword" @@ -3110,6 +3114,7 @@ "group.name", "host.architecture", "host.geo.city_name", + "host.geo.continent_code", "host.geo.continent_name", "host.geo.country_iso_code", "host.geo.country_name",