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

[Lens] Disable the global timepicker for index pattern without primary timefield and visualizations without timefield #108052

Merged
48 changes: 47 additions & 1 deletion x-pack/plugins/lens/public/app_plugin/app.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,52 @@ describe('Lens App', () => {
});
});

describe('TopNavMenu#showDatePicker', () => {
it('shows date picker if any used index pattern isTimeBased', async () => {
const customServices = makeDefaultServices(sessionIdSubject);
customServices.data.indexPatterns.get = jest
.fn()
.mockImplementation((id) =>
Promise.resolve({ id, isTimeBased: () => true } as IndexPattern)
);
const { services } = await mountWith({ services: customServices });
expect(services.navigation.ui.TopNavMenu).toHaveBeenCalledWith(
expect.objectContaining({ showDatePicker: true }),
{}
);
});
it('shows date picker if active datasource isTimeBased', async () => {
const customServices = makeDefaultServices(sessionIdSubject);
customServices.data.indexPatterns.get = jest
.fn()
.mockImplementation((id) =>
Promise.resolve({ id, isTimeBased: () => true } as IndexPattern)
);
const customProps = makeDefaultProps();
customProps.datasourceMap.testDatasource.isTimeBased = () => true;
const { services } = await mountWith({ props: customProps, services: customServices });
expect(services.navigation.ui.TopNavMenu).toHaveBeenCalledWith(
expect.objectContaining({ showDatePicker: true }),
{}
);
});
it('does not show date picker if index pattern nor active datasource is not time based', async () => {
const customServices = makeDefaultServices(sessionIdSubject);
customServices.data.indexPatterns.get = jest
.fn()
.mockImplementation((id) =>
Promise.resolve({ id, isTimeBased: () => true } as IndexPattern)
);
const customProps = makeDefaultProps();
customProps.datasourceMap.testDatasource.isTimeBased = () => false;
const { services } = await mountWith({ props: customProps, services: customServices });
expect(services.navigation.ui.TopNavMenu).toHaveBeenCalledWith(
expect.objectContaining({ showDatePicker: false }),
{}
);
});
});

describe('persistence', () => {
it('passes query and indexPatterns to TopNavMenu', async () => {
const { instance, lensStore, services } = await mountWith({ preloadedState: {} });
Expand All @@ -294,7 +340,7 @@ describe('Lens App', () => {
expect(services.navigation.ui.TopNavMenu).toHaveBeenCalledWith(
expect.objectContaining({
query: 'fake query',
indexPatterns: [{ id: 'mockip' }],
indexPatterns: [{ id: 'mockip', isTimeBased: expect.any(Function) }],
}),
{}
);
Expand Down
12 changes: 11 additions & 1 deletion x-pack/plugins/lens/public/app_plugin/lens_top_nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ export const LensTopNavMenu = ({
activeDatasourceId,
datasourceStates,
} = useLensSelector((state) => state.lens);
const allLoaded = Object.values(datasourceStates).every(({ isLoading }) => isLoading === false);

useEffect(() => {
const activeDatasource =
Expand Down Expand Up @@ -390,7 +391,16 @@ export const LensTopNavMenu = ({
dateRangeTo={to}
indicateNoData={indicateNoData}
showSearchBar={true}
showDatePicker={true}
showDatePicker={
indexPatterns.some((ip) => ip.isTimeBased()) ||
Boolean(
allLoaded &&
activeDatasourceId &&
datasourceMap[activeDatasourceId].isTimeBased(
datasourceStates[activeDatasourceId].state
)
)
}
showQueryBar={true}
showFilterBar={true}
data-test-subj="lnsApp_topNav"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1544,4 +1544,83 @@ describe('IndexPattern Data Source', () => {
});
});
});
describe('#isTimeBased', () => {
it('should return true if date histogram exists in any layer', () => {
const state = enrichBaseState({
currentIndexPatternId: '1',
layers: {
first: {
indexPatternId: '1',
columnOrder: ['metric'],
columns: {
metric: {
label: 'Count of records2',
dataType: 'number',
isBucketed: false,
sourceField: 'Records',
operationType: 'count',
},
},
},
second: {
indexPatternId: '1',
columnOrder: ['bucket1', 'bucket2', 'metric2'],
columns: {
metric2: {
label: 'Count of records',
dataType: 'number',
isBucketed: false,
sourceField: 'Records',
operationType: 'count',
},
bucket1: {
label: 'Date',
dataType: 'date',
isBucketed: true,
operationType: 'date_histogram',
sourceField: 'timestamp',
params: {
interval: '1d',
},
},
bucket2: {
label: 'Terms',
dataType: 'string',
isBucketed: true,
operationType: 'terms',
sourceField: 'geo.src',
params: {
orderBy: { type: 'alphabetical' },
orderDirection: 'asc',
size: 10,
},
},
},
},
},
});
expect(indexPatternDatasource.isTimeBased(state)).toEqual(true);
});
it('should return false if date histogram does not exist in any layer', () => {
const state = enrichBaseState({
currentIndexPatternId: '1',
layers: {
first: {
indexPatternId: '1',
columnOrder: ['metric'],
columns: {
metric: {
label: 'Count of records',
dataType: 'number',
isBucketed: false,
sourceField: 'Records',
operationType: 'count',
},
},
},
},
});
expect(indexPatternDatasource.isTimeBased(state)).toEqual(false);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,16 @@ export function getIndexPatternDatasource({
const ids = Object.values(state.layers || {}).map(({ indexPatternId }) => indexPatternId);
return ids.filter((id) => !state.indexPatterns[id]);
},
isTimeBased: (state) => {
const { layers } = state;
return (
Boolean(layers) &&
Object.values(layers).some((layer) => {
const buckets = layer.columnOrder.filter((colId) => layer.columns[colId].isBucketed);
return buckets.some((colId) => layer.columns[colId].operationType === 'date_histogram');
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: let's replace 'date_histogram' -> BUCKET_TYPES.DATE_HISTOGRAM from the data plugin

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'll leave it as it is as it is spread like this across all the files in Lens... Maybe we could address it at once in separate PR.

})
);
},
};

return indexPatternDatasource;
Expand Down
5 changes: 2 additions & 3 deletions x-pack/plugins/lens/public/mocks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ export function createMockDatasource(id: string): DatasourceMock {
publicAPIMock,
getErrorMessages: jest.fn((_state) => undefined),
checkIntegrity: jest.fn((_state) => []),
isTimeBased: jest.fn(),
};
}

Expand Down Expand Up @@ -309,9 +310,7 @@ export function mockDataPlugin(sessionIdSubject = new Subject<string>()) {
state$: new Observable(),
},
indexPatterns: {
get: jest.fn((id) => {
return new Promise((resolve) => resolve({ id }));
}),
get: jest.fn().mockImplementation((id) => Promise.resolve({ id, isTimeBased: () => true })),
},
search: createMockSearchService(),
nowProvider: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,9 +117,7 @@ function makeDefaultData(): jest.Mocked<DataPublicPluginStart> {
state$: new Observable(),
},
indexPatterns: {
get: jest.fn((id) => {
return new Promise((resolve) => resolve({ id }));
}),
get: jest.fn().mockImplementation((id) => Promise.resolve({ id, isTimeBased: () => true })),
},
search: createMockSearchService(),
nowProvider: {
Expand Down
4 changes: 4 additions & 0 deletions x-pack/plugins/lens/public/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,10 @@ export interface Datasource<T = unknown, P = unknown> {
* The frame calls this function to display warnings about visualization
*/
getWarningMessages?: (state: T, frame: FramePublicAPI) => React.ReactNode[] | undefined;
/**
* Checks if the visualization created is time based, for example date histogram
*/
isTimeBased: (state: T) => boolean;
}

export interface DatasourceFixAction<T> {
Expand Down