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

[Discover] Suppress "Missing index" toasts #149625

Merged
merged 43 commits into from
Feb 14, 2023
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
e4b39fe
[Discover] Suppress "Missing index" toasts
jughosta Jan 26, 2023
5460f94
Merge branch 'main' into 129020-inline-missing-index-error
jughosta Jan 27, 2023
fa3e071
[Discover] Update the default message
jughosta Jan 27, 2023
dd6ccc3
[Discover] Handle switching data views too
jughosta Jan 27, 2023
eafced6
[Discover] Rename param
jughosta Jan 27, 2023
4cc1910
[Discover] Fix another case
jughosta Jan 27, 2023
6dc15f5
[Discover] Fix checks
jughosta Jan 27, 2023
dd1a27c
[Discover] Fix tests
jughosta Jan 27, 2023
0a98ad9
Merge branch 'main' into 129020-inline-missing-index-error
jughosta Jan 27, 2023
d92cce3
Merge branch 'main' into 129020-inline-missing-index-error
jughosta Jan 30, 2023
a1b4780
[Discover] Add a test
jughosta Jan 30, 2023
2b05940
Merge branch 'main' into 129020-inline-missing-index-error
jughosta Jan 30, 2023
8f1e30e
Merge branch 'main' into 129020-inline-missing-index-error
jughosta Jan 30, 2023
4c386d9
[Discover] Clean up redundant code
jughosta Jan 31, 2023
4902725
[Discover] Update wording
jughosta Jan 31, 2023
a82fcbe
Merge branch 'main' into 129020-inline-missing-index-error
jughosta Jan 31, 2023
07e3b6d
Merge branch 'main' into 129020-inline-missing-index-error
jughosta Jan 31, 2023
c176bbd
[Discover] Update wording
jughosta Feb 1, 2023
d7cb8dd
Update src/plugins/discover/public/application/main/components/no_res…
jughosta Feb 1, 2023
8a3ab85
[Discover] Clean up translations
jughosta Feb 1, 2023
55feb95
Merge remote-tracking branch 'origin/129020-inline-missing-index-erro…
jughosta Feb 1, 2023
4ac71ab
Merge remote-tracking branch 'upstream/main' into 129020-inline-missi…
jughosta Feb 1, 2023
bfa2052
[Discover] Resolve conflicts with main
jughosta Feb 1, 2023
f6c09e0
Merge branch 'main' into 129020-inline-missing-index-error
jughosta Feb 6, 2023
2ec2293
[Discover] Wrap with try/catch
jughosta Feb 6, 2023
9b051f4
Merge branch 'main' into 129020-inline-missing-index-error
jughosta Feb 6, 2023
dc46db3
[Discover] Fix for tests
jughosta Feb 6, 2023
e317a65
Merge remote-tracking branch 'origin/129020-inline-missing-index-erro…
jughosta Feb 6, 2023
eeff695
Merge branch 'main' into 129020-inline-missing-index-error
mattkime Feb 9, 2023
bce9870
Merge branch 'main' into 129020-inline-missing-index-error
jughosta Feb 9, 2023
10e3d8a
Merge branch 'main' into 129020-inline-missing-index-error
jughosta Feb 9, 2023
d58a97c
Merge branch 'main' into 129020-inline-missing-index-error
jughosta Feb 9, 2023
3b866c7
[Data View] Update data view API usage
jughosta Feb 9, 2023
afb6834
Merge branch 'main' into 129020-inline-missing-index-error
jughosta Feb 10, 2023
1b05045
[Data View] Update function signature
jughosta Feb 10, 2023
2a60fb9
Merge remote-tracking branch 'origin/129020-inline-missing-index-erro…
jughosta Feb 10, 2023
5df9cf1
Merge branch 'main' into 129020-inline-missing-index-error
jughosta Feb 10, 2023
cf76c87
[Data View] Update tests
jughosta Feb 10, 2023
fa996e8
Merge remote-tracking branch 'origin/129020-inline-missing-index-erro…
jughosta Feb 10, 2023
c472b93
Merge branch 'main' into 129020-inline-missing-index-error
jughosta Feb 10, 2023
c35a269
Merge branch 'main' into 129020-inline-missing-index-error
jughosta Feb 13, 2023
aee44c4
Merge remote-tracking branch 'upstream/main' into 129020-inline-missi…
jughosta Feb 14, 2023
1aceefa
Merge branch 'main' into 129020-inline-missing-index-error
jughosta Feb 14, 2023
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
24 changes: 23 additions & 1 deletion src/plugins/data_views/common/data_views/data_views.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -420,7 +420,7 @@ describe('IndexPatterns', () => {
describe('getDefaultDataView', () => {
beforeEach(() => {
indexPatterns.clearCache();
jest.resetAllMocks();
jest.clearAllMocks();
});

test('gets default data view', async () => {
Expand All @@ -434,6 +434,27 @@ describe('IndexPatterns', () => {
expect(savedObjectsClient.find).toBeCalledTimes(1);
});

test('gets default data view and passes down correct arguments (refreshFields and displayErrors)', async () => {
uiSettings.get = jest.fn().mockResolvedValue(indexPatternObj.id);
savedObjectsClient.get = jest.fn().mockResolvedValue(indexPatternObj);
savedObjectsClient.find = jest.fn().mockResolvedValue([indexPatternObj]);
jest.spyOn(indexPatterns, 'get');
jest.spyOn(indexPatterns, 'refreshFields');

const dataView = await indexPatterns.get(indexPatternObj.id); // and to cache the result

const refreshFields = true;
const displayErrors = false;
expect(await indexPatterns.getDefaultDataView(refreshFields, displayErrors)).toBeInstanceOf(
DataView
);
expect(savedObjectsClient.get).toBeCalledTimes(1);
expect(savedObjectsClient.find).toBeCalledTimes(1);

expect(indexPatterns.get).toBeCalledWith(indexPatternObj.id, displayErrors, refreshFields);
expect(indexPatterns.refreshFields).toBeCalledWith(dataView, displayErrors);
});

test('returns undefined if no data views exist', async () => {
uiSettings.get = jest.fn().mockResolvedValue('foo');
savedObjectsClient.find = jest.fn().mockResolvedValue([]);
Expand Down Expand Up @@ -487,6 +508,7 @@ describe('IndexPatterns', () => {
});

test('dont set defaultIndex without capability allowing advancedSettings save', async () => {
uiSettings.get = jest.fn().mockResolvedValue(null);
savedObjectsClient.find = jest.fn().mockResolvedValue([
{
id: 'id1',
Expand Down
76 changes: 46 additions & 30 deletions src/plugins/data_views/common/data_views/data_views.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,8 +215,12 @@ export interface DataViewsServicePublicMethods {
/**
* Get default data view, if it doesn't exist, choose and save new default data view and return it.
* @param refreshFields - refresh field list when true
* @param displayErrors - If set false, API consumer is responsible for displaying and handling errors.
*/
getDefaultDataView: (refreshFields?: boolean) => Promise<DataView | null>;
getDefaultDataView: (
refreshFields?: boolean,
displayErrors?: boolean
) => Promise<DataView | null>;
/**
* Get fields for data view
* @param dataView - Data view instance or spec
Expand Down Expand Up @@ -261,7 +265,10 @@ export interface DataViewsServicePublicMethods {
* @params savedObject - Data view saved object
* @params displayErrors - If set false, API consumer is responsible for displaying and handling errors.
*/
savedObjectToSpec: (savedObject: SavedObject<DataViewAttributes>) => DataViewSpec;
savedObjectToSpec: (
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 sure why this is needed as savedObjectToSpec doesn't display toasts.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@mattkime Right, I cleaned that up.

savedObject: SavedObject<DataViewAttributes>,
displayErrors?: boolean
) => DataViewSpec;
/**
* Set default data view.
* @param id - Id of the data view to set as default.
Expand Down Expand Up @@ -570,30 +577,28 @@ export class DataViewsService {
* @param displayErrors - If set false, API consumer is responsible for displaying and handling errors.
*/
refreshFields = async (dataView: DataView, displayErrors: boolean = true) => {
if (!displayErrors) {
return this.refreshFieldsFn(dataView);
}

try {
await this.refreshFieldsFn(dataView);
} catch (err) {
if (err instanceof DataViewMissingIndices) {
this.onNotification(
{ title: err.message, color: 'danger', iconType: 'alert' },
`refreshFields:${dataView.getIndexPattern()}`
if (displayErrors) {
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 confused about the work this change is doing. As best I can tell it swallows errors since the error is caught but not re-thrown which isn't desired.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@mattkime The previous code was causing an unhandled promise error which was not allowing to switch from SQL to regular mode in UI (for a data view with missing index)

if (!displayErrors) {
  return this.refreshFieldsFn(dataView);
}

Screenshot 2023-01-31 at 10 15 43

Copy link
Contributor

Choose a reason for hiding this comment

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

Can you handle the exception in the api consumer instead of changing the api?

Copy link
Contributor Author

@jughosta jughosta Feb 1, 2023

Choose a reason for hiding this comment

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

@mattkime Thanks for your suggestion! The thing is that the consumer is the data views service itself

await this.refreshFields(dataView, displayErrors);

Would you suggest to add try-catch there depending on displayErrors?

Copy link
Contributor

Choose a reason for hiding this comment

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

No, at least in theory the error will propagate up to the api consumer's call.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, at least in theory the error will propagate up to the api consumer's call.

@mattkime dataView.get() could still return a data view object for its consumer if we suppress exceptions for refreshField. If we change as you suggest then the consumer code would not be able to proceed which conflicts with the purpose of this PR.

We can't both prevent unhandled errors AND pass errors to API consumers.

Agree with that. I'm not happy with the PR changes either. I can close it if that's our conclusion.

Copy link
Member

Choose a reason for hiding this comment

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

Agree, there should be one way to handle it: to suppress the errors or to pass errors to the API consumers. This should also be clearly communicated to consumers, currently it seems it's a mixed handling of those cases?

Copy link
Contributor

Choose a reason for hiding this comment

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

If we change as you suggest then the consumer code would not be able to proceed

@jughosta Perhaps we should set up some time to discuss this. There's something I'm not understanding here.

Copy link
Contributor

Choose a reason for hiding this comment

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

@kertal

Agree, there should be one way to handle it: to suppress the errors or to pass errors to the API consumers. This should also be clearly communicated to consumers, currently it seems it's a mixed handling of those cases?

I think our language might be conflating things. Currently, the api either displays errors or throws errors - and thrown errors need to be caught by api consumer code. 'Suppressing errors' could ambiguously mean either of those.

First we need to remove the possibility of errors from our communication regarding errors to successfully deal with errors. 🥲

Copy link
Contributor

@mattkime mattkime Feb 3, 2023

Choose a reason for hiding this comment

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

I created https://github.com/mattkime/kibana/pull/new/129020-inline-missing-index-error-mk to illustrate what I see as the path forward, particularly https://github.com/elastic/kibana/pull/150246/files#diff-eeffc98614da75c1f8e6ce9b07802c11dce0ae41f80b9540784f0f44c2e86133R307 - Now I think I did this before understanding what @jughosta was saying.

@jughosta - It took me a second reading to understand what you were looking to do -

[@mattkime] dataView.get() could still return a data view object for its consumer if we suppress exceptions for refreshField. If we change as you suggest then the consumer code would not be able to proceed which conflicts with the purpose of this PR.

Basically you're saying you want to be able to get a data view even if you can't get the field list. This isn't something thats currently supported by the API. A data view without a field list is of very limited use in most circumstances. Its an interesting idea. Current API consumers always assume the full field list is there - they don't have a notion of a data view with an unloaded field list. This is something I'd like to see changed but it would be a significant effort on the part of API consumers. Its actually something requested by solutions which deal with very large numbers of fields - that way they can choose when the field list is loaded.

if (err instanceof DataViewMissingIndices) {
this.onNotification(
{ title: err.message, color: 'danger', iconType: 'alert' },
`refreshFields:${dataView.getIndexPattern()}`
);
}

this.onError(
err,
{
title: i18n.translate('dataViews.fetchFieldErrorTitle', {
defaultMessage: 'Error fetching fields for data view {title} (ID: {id})',
values: { id: dataView.id, title: dataView.getIndexPattern() },
}),
},
dataView.getIndexPattern()
);
}

this.onError(
err,
{
title: i18n.translate('dataViews.fetchFieldErrorTitle', {
defaultMessage: 'Error fetching fields for data view {title} (ID: {id})',
values: { id: dataView.id, title: dataView.getIndexPattern() },
}),
},
dataView.getIndexPattern()
);
}
};

Expand All @@ -610,7 +615,8 @@ export class DataViewsService {
id: string,
title: string,
options: GetFieldsOptions,
fieldAttrs: FieldAttrs = {}
fieldAttrs: FieldAttrs = {},
displayErrors: boolean = true
) => {
const fieldsAsArr = Object.values(fields);
const scriptedFields = fieldsAsArr.filter((field) => field.scripted);
Expand All @@ -630,10 +636,12 @@ export class DataViewsService {
return { fields: this.fieldArrayToMap(updatedFieldList, fieldAttrs), indices };
} catch (err) {
if (err instanceof DataViewMissingIndices) {
this.onNotification(
{ title: err.message, color: 'danger', iconType: 'alert' },
`refreshFieldSpecMap:${title}`
);
if (displayErrors) {
this.onNotification(
{ title: err.message, color: 'danger', iconType: 'alert' },
`refreshFieldSpecMap:${title}`
);
}
return {};
}

Expand Down Expand Up @@ -739,9 +747,11 @@ export class DataViewsService {
private initFromSavedObjectLoadFields = async ({
savedObjectId,
spec,
displayErrors = true,
}: {
savedObjectId: string;
spec: DataViewSpec;
displayErrors?: boolean;
}) => {
const { title, type, typeMeta, runtimeFieldMap } = spec;
const { fields, indices } = await this.refreshFieldSpecMap(
Expand All @@ -755,7 +765,8 @@ export class DataViewsService {
rollupIndex: typeMeta?.params?.rollup_index,
allowNoIndex: spec.allowNoIndex,
},
spec.fieldAttrs
spec.fieldAttrs,
displayErrors
);

const runtimeFieldSpecs = this.getRuntimeFields(runtimeFieldMap, spec.fieldAttrs);
Expand All @@ -779,6 +790,7 @@ export class DataViewsService {
const fieldsAndIndices = await this.initFromSavedObjectLoadFields({
savedObjectId: savedObject.id,
spec,
displayErrors,
});
fields = fieldsAndIndices.fields;
indices = fieldsAndIndices.indices;
Expand Down Expand Up @@ -883,7 +895,7 @@ export class DataViewsService {
): Promise<DataView> => {
const dataViewFromCache = this.dataViewCache.get(id)?.then(async (dataView) => {
if (dataView && refreshFields) {
await this.refreshFields(dataView);
await this.refreshFields(dataView, displayErrors);
}
return dataView;
});
Expand Down Expand Up @@ -1145,9 +1157,13 @@ export class DataViewsService {
* If no possible data view found to become a default returns null.
*
* @param {boolean} refreshFields - if true, will refresh the fields of the default data view
* @param {boolean} displayErrors - If set false, API consumer is responsible for displaying and handling errors.
* @returns default data view
*/
async getDefaultDataView(refreshFields?: boolean): Promise<DataView | null> {
async getDefaultDataView(
refreshFields?: boolean,
displayErrors: boolean = true
): Promise<DataView | null> {
const patterns = await this.getIdsWithTitle();
let defaultId: string | undefined = await this.config.get('defaultIndex');
const exists = defaultId ? patterns.some((pattern) => pattern.id === defaultId) : false;
Expand All @@ -1168,7 +1184,7 @@ export class DataViewsService {
}

if (defaultId) {
return this.get(defaultId, undefined, refreshFields);
return this.get(defaultId, displayErrors, refreshFields);
} else {
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import { findTestSubject } from '@elastic/eui/lib/test';

import { DiscoverNoResults, DiscoverNoResultsProps } from './no_results';
import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public';
import { getDiscoverStateMock } from '../../../../__mocks__/discover_state.mock';
import { DiscoverMainProvider } from '../../services/discover_state_provider';

beforeEach(() => {
jest.clearAllMocks();
Expand All @@ -27,9 +29,12 @@ function mountAndFindSubjects(props: Omit<DiscoverNoResultsProps, 'onDisableFilt
},
},
};
const stateContainer = getDiscoverStateMock({ isTimeBased: true });
const component = mountWithIntl(
<KibanaContextProvider services={services}>
<DiscoverNoResults onDisableFilters={() => {}} {...props} />
<DiscoverMainProvider value={stateContainer}>
<DiscoverNoResults onDisableFilters={() => {}} {...props} />
</DiscoverMainProvider>
</KibanaContextProvider>
);
return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,29 @@

import React from 'react';
import { FormattedMessage } from '@kbn/i18n-react';
import { EuiDescriptionList, EuiDescriptionListDescription } from '@elastic/eui';
import { EuiDescriptionList, EuiDescriptionListDescription, EuiCode } from '@elastic/eui';
import { useInternalStateSelector } from '../../../services/discover_internal_state_container';

export function NoResultsSuggestionDefault() {
const dataViewPattern = useInternalStateSelector((state) => state.dataView?.getIndexPattern?.());

return (
<EuiDescriptionList compressed>
<EuiDescriptionListDescription data-test-subj="discoverNoResultsCheckIndices">
<FormattedMessage
id="discover.noResults.noDocumentsOrCheckPermissionsDescription"
defaultMessage="Make sure you have permission to view the indices and that they contain documents."
/>
{dataViewPattern ? (
<FormattedMessage
id="discover.noResults.noDocumentsOrCheckIndicesAndPermissionsDescription"
defaultMessage="Make sure the indices matching {dataViewPattern} exist, that you have permission to view them and that they contain documents."
Copy link
Contributor

Choose a reason for hiding this comment

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

My suggestion - although it should probably get 👀 from @gchaps - Make sure data view {dataViewName} with index pattern {dataViewPattern} has matching indices and documents and that you have permission to view them.


Does this work properly for text based languages?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@mattkime Thanks, updated!

Screenshot 2023-01-31 at 10 25 25

For SQL mode we show an error in this case rather than the message:
Screenshot 2023-01-31 at 10 15 15

Copy link
Contributor

Choose a reason for hiding this comment

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

My suggestion is to add the 2 words in bold:

Make sure that the data view {dataViewName} with index pattern {dataViewPattern} has matching indices and documents and that you have permission to view them.

Copy link
Contributor

Choose a reason for hiding this comment

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

Also, can we update the error message:

Instead of

We encountered an error retrieving search results

how about

Unable to retrieve search results

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks @gchaps !

values={{
dataViewPattern: <EuiCode>{dataViewPattern}</EuiCode>,
}}
/>
) : (
<FormattedMessage
id="discover.noResults.noDocumentsOrCheckPermissionsDescription"
defaultMessage="Make sure you have permission to view the indices and that they contain documents."
jughosta marked this conversation as resolved.
Show resolved Hide resolved
/>
)}
</EuiDescriptionListDescription>
</EuiDescriptionList>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ export function DiscoverMainRoute(props: Props) {
return;
}

const defaultDataView = await data.dataViews.getDefaultDataView();
const defaultDataView = await data.dataViews.getDefaultDataView(false, false);

if (!defaultDataView) {
setShowNoDataPage(true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ export function useDiscoverState({
*/
const onChangeDataView = useCallback(
async (id: string) => {
const nextDataView = await dataViews.get(id);
const nextDataView = await dataViews.get(id, false);
Copy link
Contributor

@mattkime mattkime Feb 1, 2023

Choose a reason for hiding this comment

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

Anyplace where we're suppressing errors potentially needs a try/catch

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@mattkime Updated by wrapping with try/catch.

if (nextDataView && dataView) {
const nextAppState = getDataViewAppState(
dataView,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export async function loadDataView(
let fetchedDataView: DataView | undefined;
// try to fetch adhoc data view first
try {
fetchedDataView = fetchId ? await dataViews.get(fetchId) : undefined;
fetchedDataView = fetchId ? await dataViews.get(fetchId, false) : undefined;
if (fetchedDataView && !fetchedDataView.isPersisted()) {
return {
list: dataViewList || [],
Expand All @@ -88,7 +88,7 @@ export async function loadDataView(
loaded: fetchedDataView
? fetchedDataView
: // we can be certain that the data view exists due to an earlier hasData check
((await dataViews.getDefaultDataView()) as DataView),
((await dataViews.getDefaultDataView(false, false)) as DataView),
stateVal: fetchId,
stateValFound: !!fetchId && !!fetchedDataView,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ export const useExistingFieldsFetcher = (
}

const numberOfFetches = (currentInfo?.numberOfFetches ?? 0) + 1;
const dataView = await dataViews.get(dataViewId);
const dataView = await dataViews.get(dataViewId, false);

if (!dataView?.title) {
return;
Expand Down Expand Up @@ -155,8 +155,6 @@ export const useExistingFieldsFetcher = (
info.existingFieldsByFieldNameMap = booleanMap(existingFieldNames);
info.fetchStatus = ExistenceFetchStatus.succeeded;
} catch (error) {
// eslint-disable-next-line no-console
console.error(error);
info.fetchStatus = ExistenceFetchStatus.failed;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ export function useGroupedFields<T extends FieldListItem = DataViewField>({
useEffect(() => {
const getDataView = async () => {
if (dataViewId) {
setDataView(await services.dataViews.get(dataViewId));
setDataView(await services.dataViews.get(dataViewId, false));
}
};
getDataView();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ export function ChangeDataView({
setPopoverIsOpen={setPopoverIsOpen}
onChangeDataView={async (newId) => {
// refreshing the field list
await dataViews.get(newId, undefined, true);
await dataViews.get(newId, false, true);
setSelectedDataViewId(newId);
setPopoverIsOpen(false);
if (isTextBasedLangSelected && !isTextLangTransitionModalDismissed) {
Expand Down Expand Up @@ -355,6 +355,7 @@ export function ChangeDataView({
</EuiFlexItem>
</EuiFlexGroup>,
<TextBasedLanguagesList
key="text-based-languages-list"
textBasedLanguages={textBasedLanguages}
selectedOption={triggerLabel}
onChange={(lang) => {
Expand Down