superset/superset-frontend/cypress-base/cypress/integration/dashboard/tabs.test.js
David Aaron Suddjian 865beae3d8
feat(dashboard): Dashboard-Native Filters (#11814)
* wip: filter create modal

* add a feature flag

* automatic changes to package lock

* wip

* filter sidebar and basic state management

* move create button to the sidebar

* first step for edit filterconfig

* partially fix tests...

* edits to types and comments

* respect feature flag on the filter sidebar

* add filterconfig form

* get input state working

* feat: tree filter scopes UI

* fix: turn on flag

* sticky filter bar

* stop preferring default export

* feat: finish filter scoping

* fix: under toggle

* fix: title

* fix: add licence

* refactor: update TS

* fix: fix on reopen modal + validation

* new filter bar menu

* adding, but commenting out, bulk scoping action

* adding some placeholder buttons and styles therefor

* feat: add filter chart

* add relative path to package.json

* update modal

* a little input styling... just getting warmed up

* Revert "feat: add filter chart"

This reverts commit b1302d35b6.

* Revert "add relative path to package.json"

This reverts commit 26a7b40e18.

* https package lock idk

* feat: add filter chart

* add relative path to package.json

* flexboxes all the way down

* dynamically generate groupby and datasource in select control

* big wip

* fix target column name

* no importing nonexistent things

* styles and name editing

* Add hook for retrieval of all filter states

* start with a new filter when clicking add filter

* handle removed filters gracefully

* fix incorrect default filter configuration

* add fields to useAllFilterState

* add redux for filterconfigs

* add support for native_filters

* remove consoles

* improve filter removal

* unbreak infinite loop

* basic sidebar toggling working!

* collapsing and menu working more smoothly

* linting

* make dataset and column inputs work

* save filter values properly

* add dashboard event for filter updates

* guarded

* apply filters properly

* fix schema

* making New Filter button a link

* gridunits ftw

* centering modal

* tis not a button anymore! nixing type.

* plus and collapse buttons instead of "more" menu

* updating full size filter icons

* adding icons to filter collapsing/expanding

* turning off animation, but leaving class-based animation css

* fix linting error

* fix native filters for legacy charts

* updates test

* no individual apply buttons

* fix bugs with filter config modal

* remove redundant code

* switch to the filter with validation errors on submit

* separate form validation

* switch config button from add to edit

* update tests

* oops forgot to add the fancy new useChangeEffect hook

* comments and code reorganization

* rename native_filters to extr_form_data and move hook

* disable native filters in viz selector

* add cascading

* implement new extra form data api

* cleanup

* updates tests

* bump npm packages

* fix bad merge on package.json + lock

* lint

* replace in and not in with uppercase

* lint

* lint

* lint

* lint

* bulk test fix

* Sort select input alphabetically

* Change type for sorting elements

* fix rest of unit tests

* make filter operators all uppercase

* Hide Filter bar when there are no filters

* Show edit button for dashboard owners only

* Add visible argument to filters toggle function to avoid future regression

* Improve Toggle filters bar function

* lint

* fix js lint + set createNewOnOpen

* Handle setting extra form data in Filter Bar instead of Filter Control

* Add Handle apply filter function to Apply button

* Allow applying changes instantly

* Fix types

* remove console logs

* Add Error Boundary component to Filter bar and Filter Config Modal

* fix jest tests

* update native filters tests to pass

* reset cypress baseUrl

* remove unnecessary field

* cleanup: remove unused state fields

* move unrelated types to an appropriate location

* remove misplaced resource fetch error logic

* fix cascadeParentIds error

* fix cypress password

* initial attempt at fixing scope issue

* fix bad merge

* fix lint

* trying out makeApi for saving filters

* remove unused import

* fix test

* silence bad test

* add native-filter feat flag config

* oops fix here

* remove space

* Update superset-frontend/src/common/components/index.tsx

Co-authored-by: Evan Rusackas <evan@preset.io>

* Update superset-frontend/cypress-base/cypress/integration/dashboard/nativeFilters.test.ts

Co-authored-by: Evan Rusackas <evan@preset.io>

* use styledMount in tests

* comment

Co-authored-by: Evan Rusackas <evan@preset.io>

* Update superset-frontend/src/dashboard/components/nativeFilters/FilterBar.tsx

Co-authored-by: Evan Rusackas <evan@preset.io>

* Update superset-frontend/src/dashboard/components/nativeFilters/FilterConfigForm.tsx

Co-authored-by: Evan Rusackas <evan@preset.io>

* Update superset-frontend/src/dashboard/components/nativeFilters/FilterConfigModal.tsx

Co-authored-by: Evan Rusackas <evan@preset.io>

* Update superset-frontend/src/dashboard/components/nativeFilters/FilterConfigurationLink.tsx

Co-authored-by: Evan Rusackas <evan@preset.io>

* address PR feedback

* fix package lock

* null guards

* Fix charts resizing

* fix cypress tests

* add in nativefilters to form data

* fix lint and test

Co-authored-by: Phillip Kelley-Dotson <pkelleydotson@yahoo.com>
Co-authored-by: Simcha Shats <simcha.shats@nielsen.com>
Co-authored-by: amitNielsen <amit.miran@nielsen.com>
Co-authored-by: Ville Brofeldt <ville.v.brofeldt@gmail.com>
Co-authored-by: Evan Rusackas <evan@preset.io>
Co-authored-by: Agata Stawarz-Pastewska <agata.stawarz-pastewska@polidea.com>
Co-authored-by: Ville Brofeldt <33317356+villebro@users.noreply.github.com>
2020-12-18 17:06:37 -08:00

243 lines
7.5 KiB
JavaScript

/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { TABBED_DASHBOARD } from './dashboard.helper';
describe('Dashboard tabs', () => {
let filterId;
let treemapId;
let linechartId;
let boxplotId;
let dashboardId;
// cypress can not handle window.scrollTo
// https://github.com/cypress-io/cypress/issues/2761
// add this exception handler to pass test
const handleException = () => {
// return false to prevent the error from
// failing this test
cy.on('uncaught:exception', () => false);
};
beforeEach(() => {
cy.server();
cy.login();
cy.visit(TABBED_DASHBOARD);
cy.get('#app').then(data => {
const bootstrapData = JSON.parse(data[0].dataset.bootstrap);
const dashboard = bootstrapData.dashboard_data;
dashboardId = dashboard.id;
filterId = dashboard.slices.find(
slice => slice.form_data.viz_type === 'filter_box',
).slice_id;
boxplotId = dashboard.slices.find(
slice => slice.form_data.viz_type === 'box_plot',
).slice_id;
treemapId = dashboard.slices.find(
slice => slice.form_data.viz_type === 'treemap',
).slice_id;
linechartId = dashboard.slices.find(
slice => slice.form_data.viz_type === 'line',
).slice_id;
const filterFormdata = {
slice_id: filterId,
};
const filterRequest = `/superset/explore_json/?form_data=${JSON.stringify(
filterFormdata,
)}&dashboard_id=${dashboardId}`;
cy.route('POST', filterRequest).as('filterRequest');
const treemapFormdata = {
slice_id: treemapId,
};
const treemapRequest = `/superset/explore_json/?form_data=${JSON.stringify(
treemapFormdata,
)}&dashboard_id=${dashboardId}`;
cy.route('POST', treemapRequest).as('treemapRequest');
const linechartFormdata = {
slice_id: linechartId,
};
const linechartRequest = `/superset/explore_json/?form_data=${JSON.stringify(
linechartFormdata,
)}&dashboard_id=${dashboardId}`;
cy.route('POST', linechartRequest).as('linechartRequest');
const boxplotFormdata = {
slice_id: boxplotId,
};
const boxplotRequest = `/superset/explore_json/?form_data=${JSON.stringify(
boxplotFormdata,
)}&dashboard_id=${dashboardId}`;
cy.route('POST', boxplotRequest).as('boxplotRequest');
});
});
it('should switch active tab on click', () => {
cy.wait('@filterRequest');
cy.wait('@treemapRequest');
cy.get('[data-test="dashboard-component-tabs"]')
.first()
.find('[data-test="nav-list"] .ant-tabs-nav-list > .ant-tabs-tab')
.as('top-level-tabs');
cy.get('@top-level-tabs')
.first()
.click()
.should('have.class', 'ant-tabs-tab-active');
cy.get('@top-level-tabs')
.last()
.should('not.have.class', 'ant-tabs-tab-active');
cy.get('@top-level-tabs')
.last()
.click()
.should('have.class', 'ant-tabs-tab-active');
cy.get('@top-level-tabs')
.first()
.should('not.have.class', 'ant-tabs-tab-active');
});
it('should load charts when tab is visible', () => {
// landing in first tab, should see 2 charts
cy.wait('@filterRequest');
cy.get('[data-test="grid-container"]')
.find('.filter_box')
.should('be.visible');
cy.wait('@treemapRequest');
cy.get('[data-test="grid-container"]')
.find('.treemap')
.should('be.visible');
cy.get('[data-test="grid-container"]')
.find('.box_plot')
.should('not.be.visible');
cy.get('[data-test="grid-container"]')
.find('.line')
.should('not.be.visible');
// click row level tab, see 1 more chart
cy.get('[data-test="dashboard-component-tabs"]')
.last()
.find('[data-test="nav-list"] .ant-tabs-nav-list > .ant-tabs-tab')
.as('row-level-tabs');
cy.get('@row-level-tabs').last().click();
cy.wait('@linechartRequest');
cy.get('[data-test="grid-container"]').find('.line').should('be.visible');
// click top level tab, see 1 more chart
handleException();
cy.get('[data-test="dashboard-component-tabs"]')
.first()
.find('[data-test="nav-list"] .ant-tabs-nav-list > .ant-tabs-tab')
.as('top-level-tabs');
cy.get('@top-level-tabs').last().click();
// should exist a visible box_plot element
cy.get('[data-test="grid-container"]').find('.box_plot');
});
it('should send new queries when tab becomes visible', () => {
// landing in first tab
cy.wait('@filterRequest');
cy.wait('@treemapRequest');
// apply filter
cy.get('.Select__control').first().should('be.visible');
cy.get('.Select__control').first().click({ force: true });
cy.get('.Select__control input[type=text]')
.first()
.should('be.visible')
.type('South Asia{enter}', { force: true });
// send new query from same tab
cy.wait('@treemapRequest').then(xhr => {
const requestFormData = xhr.request.body;
const requestParams = JSON.parse(requestFormData.get('form_data'));
expect(requestParams.extra_filters[0]).deep.eq({
col: 'region',
op: 'IN',
val: ['South Asia'],
});
});
// click row level tab, send 1 more query
cy.get('[data-test="dashboard-component-tabs"]')
.last()
.find('[data-test="nav-list"]')
.children()
.as('row-level-tabs');
cy.get('@row-level-tabs').last().click();
cy.wait('@linechartRequest').then(xhr => {
const requestFormData = xhr.request.body;
const requestParams = JSON.parse(requestFormData.get('form_data'));
expect(requestParams.extra_filters[0]).deep.eq({
col: 'region',
op: 'IN',
val: ['South Asia'],
});
});
// click top level tab, send 1 more query
cy.get('[data-test="dashboard-component-tabs"]')
.first()
.find('[data-test="nav-list"]')
.children()
.as('top-level-tabs');
cy.get('@top-level-tabs').last().click();
cy.wait('@boxplotRequest').then(xhr => {
const requestFormData = xhr.request.body;
const requestParams = JSON.parse(requestFormData.get('form_data'));
expect(requestParams.extra_filters[0]).deep.eq({
col: 'region',
op: 'IN',
val: ['South Asia'],
});
});
// navigate to filter and clear filter
cy.get('@top-level-tabs').first().click();
cy.get('@top-level-tabs').first().click();
cy.get('.Select__clear-indicator').click();
// trigger 1 new query
cy.wait('@treemapRequest');
// make sure query API not requested multiple times
cy.on('fail', err => {
expect(err.message).to.include('timed out waiting');
return false;
});
cy.wait('@boxplotRequest', { timeout: 1000 }).then(() => {
throw new Error('Unexpected API call.');
});
});
});