diff --git a/frontend/src/metabase/xray/containers/CardXRay.jsx b/frontend/src/metabase/xray/containers/CardXRay.jsx index aa4a82544c3306eb77fe9a73a566c8e11b2155b1..b03b51909a34102a128940e804db84799d668986 100644 --- a/frontend/src/metabase/xray/containers/CardXRay.jsx +++ b/frontend/src/metabase/xray/containers/CardXRay.jsx @@ -5,6 +5,7 @@ import { connect } from 'react-redux' import { saturated } from 'metabase/lib/colors' import { fetchCardXray } from 'metabase/xray/xray' +import { getLoadingStatus } from 'metabase/xray/selectors' import Icon from 'metabase/components/Icon' import Tooltip from 'metabase/components/Tooltip' import LoadingAndErrorWrapper from 'metabase/components/LoadingAndErrorWrapper' @@ -15,6 +16,7 @@ import Periodicity from 'metabase/xray/components/Periodicity' type Props = { fetchCardXray: () => void, + isLoading: boolean, xray: {} } @@ -57,10 +59,10 @@ class CardXRay extends Component { render () { - const { xray } = this.props + const { xray, isLoading } = this.props const { error } = this.state return ( - <LoadingAndErrorWrapper loading={!xray} error={error}> + <LoadingAndErrorWrapper loading={isLoading} error={error}> { () => <XRayPageWrapper> <div className="mt4 mb2"> @@ -178,7 +180,8 @@ class CardXRay extends Component { } const mapStateToProps = state => ({ - xray: state.xray.cardXray, + xray: state.xray.xray, + isLoading: getLoadingStatus(state) }) const mapDispatchToProps = { diff --git a/frontend/src/metabase/xray/containers/FieldXray.jsx b/frontend/src/metabase/xray/containers/FieldXray.jsx index f8a6072287d85a79c8b0100a9dc4d604c96e2f0c..612af2bd03644db0317173cb46bb0de34466d818 100644 --- a/frontend/src/metabase/xray/containers/FieldXray.jsx +++ b/frontend/src/metabase/xray/containers/FieldXray.jsx @@ -7,7 +7,7 @@ import { Link } from 'react-router' import { isDate } from 'metabase/lib/schema_metadata' import { fetchFieldXray } from 'metabase/xray/xray' -import { getFieldXray } from 'metabase/xray/selectors' +import { getFieldXray, getLoadingStatus } from 'metabase/xray/selectors' import COSTS from 'metabase/xray/costs' @@ -31,6 +31,7 @@ import type { Table } from 'metabase/meta/types/Table' type Props = { fetchFieldXray: () => void, + isLoading: boolean, xray: { table: Table, field: Field, @@ -45,7 +46,8 @@ type Props = { } const mapStateToProps = state => ({ - xray: getFieldXray(state) + xray: getFieldXray(state), + isLoading: getLoadingStatus(state) }) const mapDispatchToProps = { @@ -83,11 +85,11 @@ class FieldXRay extends Component { } render () { - const { xray, params } = this.props + const { xray, params, isLoading } = this.props const { error } = this.state return ( <LoadingAndErrorWrapper - loading={!xray} + loading={isLoading} error={error} noBackground > diff --git a/frontend/src/metabase/xray/containers/TableXRay.jsx b/frontend/src/metabase/xray/containers/TableXRay.jsx index e09cb0ad24816dcbd896f66e314f8e93b28d5b14..6ed883d14161bf306c3d4d53f0de5b745767644a 100644 --- a/frontend/src/metabase/xray/containers/TableXRay.jsx +++ b/frontend/src/metabase/xray/containers/TableXRay.jsx @@ -14,7 +14,8 @@ import Constituent from 'metabase/xray/components/Constituent' import { getTableConstituents, - getTableXray + getTableXray, + getLoadingStatus } from 'metabase/xray/selectors' import Icon from 'metabase/components/Icon' @@ -25,6 +26,7 @@ import type { Table } from 'metabase/meta/types/Table' type Props = { constituents: [], fetchTableXray: () => void, + isLoading: boolean, xray: { table: Table }, @@ -36,7 +38,8 @@ type Props = { const mapStateToProps = state => ({ xray: getTableXray(state), - constituents: getTableConstituents(state) + constituents: getTableConstituents(state), + isLoading: getLoadingStatus(state) }) const mapDispatchToProps = { @@ -74,13 +77,13 @@ class TableXRay extends Component { } render () { - const { constituents, xray, params } = this.props + const { constituents, xray, params, isLoading } = this.props const { error } = this.state return ( <XRayPageWrapper> <LoadingAndErrorWrapper - loading={!constituents} + loading={isLoading} error={error} noBackground > diff --git a/frontend/src/metabase/xray/selectors.js b/frontend/src/metabase/xray/selectors.js index 556af3f2f8d64fc4f4c265f8400c59f0da9c23c2..35154b046774f6cb8ae08f0f04a88de52477b7d5 100644 --- a/frontend/src/metabase/xray/selectors.js +++ b/frontend/src/metabase/xray/selectors.js @@ -1,26 +1,31 @@ import { createSelector } from 'reselect' import { normal } from 'metabase/lib/colors' +export const getLoadingStatus = (state) => + state.xray.loading + +/* TODO - these can be collapsed into getXray */ export const getFieldXray = (state) => - state.xray.fieldXray && state.xray.fieldXray.features + state.xray.xray && state.xray.xray.features export const getTableXray = (state) => - state.xray.tableXray && state.xray.tableXray.features + state.xray.xray && state.xray.xray.features export const getSegmentXray = (state) => - state.xray.segmentXray && state.xray.segmentXray.features + state.xray.xray && state.xray.xray.features +/* TODO - these can be collapsed into getConstituents */ export const getTableConstituents = (state) => - state.xray.tableXray && ( - Object.keys(state.xray.tableXray.constituents).map(key => - state.xray.tableXray.constituents[key] + state.xray.xray && ( + Object.keys(state.xray.xray.constituents).map(key => + state.xray.xray.constituents[key] ) ) export const getSegmentConstituents = (state) => - state.xray.segmentXray && ( - Object.keys(state.xray.segmentXray.constituents).map(key => - state.xray.segmentXray.constituents[key] + state.xray.xray && ( + Object.keys(state.xray.xray.constituents).map(key => + state.xray.xray.constituents[key] ) ) diff --git a/frontend/src/metabase/xray/xray.js b/frontend/src/metabase/xray/xray.js index f464f051cd859366070203db8d2205e372b4dfee..b5ae41b00c4a46b0f3d4b9fa6ae4bee46b3e48fa 100644 --- a/frontend/src/metabase/xray/xray.js +++ b/frontend/src/metabase/xray/xray.js @@ -1,4 +1,4 @@ -import { assoc } from 'icepick' +import { chain, assoc } from 'icepick' import COSTS from 'metabase/xray/costs' @@ -12,10 +12,11 @@ import { XRayApi } from 'metabase/services' export const FETCH_FIELD_XRAY = 'metabase/xray/FETCH_FIELD_XRAY' export const fetchFieldXray = createThunkAction(FETCH_FIELD_XRAY, (fieldId, cost) => - async () => { + async (dispatch) => { + dispatch(startLoad()) try { const xray = await XRayApi.field_xray({ fieldId, ...cost.method }) - return xray + return dispatch(loadXray(xray)) } catch (error) { console.error(error) } @@ -24,10 +25,11 @@ export const fetchFieldXray = createThunkAction(FETCH_FIELD_XRAY, (fieldId, cost export const FETCH_TABLE_XRAY = 'metabase/xray/FETCH_TABLE_XRAY' export const fetchTableXray = createThunkAction(FETCH_TABLE_XRAY, (tableId, cost) => - async () => { + async (dispatch) => { + dispatch(startLoad()) try { const xray = await XRayApi.table_xray({ tableId, ...cost.method }) - return xray + return dispatch(loadXray(xray)) } catch (error) { console.error(error) } @@ -37,10 +39,11 @@ export const fetchTableXray = createThunkAction(FETCH_TABLE_XRAY, (tableId, cost export const FETCH_SEGMENT_XRAY = 'metabase/xray/FETCH_SEGMENT_XRAY' export const fetchSegmentXray = createThunkAction(FETCH_SEGMENT_XRAY, (segmentId, cost) => - async () => { + async (dispatch) => { + dispatch(startLoad()) try { const xray = await XRayApi.segment_xray({ segmentId, ...cost.method }) - return xray + return dispatch(loadXray(xray)) } catch (error) { console.error(error) } @@ -49,51 +52,26 @@ export const fetchSegmentXray = createThunkAction(FETCH_SEGMENT_XRAY, (segmentId export const FETCH_CARD_XRAY = 'metabase/xray/FETCH_CARD_XRAY'; export const fetchCardXray = createThunkAction(FETCH_CARD_XRAY, (cardId, cost) => - async () => { + async (dispatch) => { + const c = COSTS[cost] + dispatch(startLoad()) try { - const c = COSTS[cost] const xray = await XRayApi.card_xray({ cardId, ...c.method }); - return xray; + dispatch(loadXray(xray)); + return false } catch (error) { console.error(error); } } ) -export const FETCH_FIELD_COMPARISON = 'metabase/xray/FETCH_FIELD_COMPARISON'; -export const fetchFieldComparison = createThunkAction( - FETCH_FIELD_COMPARISON, - (fieldId1, fieldId2) => - async (dispatch) => { - try { - const comparison = await XRayApi.field_compare({ fieldId1, fieldId2 }) - dispatch(loadComparison(comparison)) - return false - } catch (error) { - console.error(error) - } - } -) -const FETCH_TABLE_COMPARISON = 'metabase/xray/FETCH_TABLE_COMPARISON'; -export const fetchTableComparison = createThunkAction( - FETCH_TABLE_COMPARISON, - (tableId1, tableId2) => - async () => { - try { - const comparison = await XRayApi.table_compare({ tableId1, tableId2 }) - return comparison - } catch (error) { - console.error(error) - } - } -) - export const FETCH_SEGMENT_COMPARISON = 'metabase/xray/FETCH_SEGMENT_COMPARISON'; export const fetchSegmentComparison = createThunkAction( FETCH_SEGMENT_COMPARISON, (segmentId1, segmentId2, cost) => async (dispatch) => { const c = COSTS[cost] + dispatch(startLoad()) try { const comparison = await XRayApi.segment_compare({ segmentId1, segmentId2, ...c.method }) return dispatch(loadComparison(comparison)) @@ -109,6 +87,7 @@ export const fetchSegmentTableComparison = createThunkAction( (segmentId, tableId, cost) => async (dispatch) => { const c = COSTS[cost] + dispatch(startLoad()) try { const comparison = await XRayApi.segment_table_compare({ segmentId, tableId, ...c.method }) return dispatch(loadComparison(comparison)) @@ -118,29 +97,66 @@ export const fetchSegmentTableComparison = createThunkAction( } ) -export const FETCH_METRIC_COMPARISON = 'metabase/xray/FETCH_METRIC_COMPARISON'; -export const fetchMetricComparison = createThunkAction(FETCH_METRIC_COMPARISON, function(metricId1, metricId2) { +const FETCH_TABLE_COMPARISON = 'metabase/xray/FETCH_TABLE_COMPARISON'; +export const fetchTableComparison = createThunkAction( + FETCH_TABLE_COMPARISON, + (tableId1, tableId2) => + async () => { + try { + const comparison = await XRayApi.table_compare({ tableId1, tableId2 }) + return comparison + } catch (error) { + console.error(error) + } + } +) + +export const FETCH_CARD_COMPARISON = 'metabase/xray/FETCH_CARD_COMPARISON'; +export const fetchCardComparison = createThunkAction(FETCH_CARD_COMPARISON, (cardId1, cardId2) => async () => { try { - const comparison = await XRayApi.metric_compare({ metricId1, metricId2 }) + const comparison = await XRayApi.card_compare({ cardId1, cardId2 }) return comparison } catch (error) { console.error(error) } } -}) +) -export const FETCH_CARD_COMPARISON = 'metabase/xray/FETCH_CARD_COMPARISON'; -export const fetchCardComparison = createThunkAction(FETCH_CARD_COMPARISON, (cardId1, cardId2) => +export const FETCH_FIELD_COMPARISON = 'metabase/xray/FETCH_FIELD_COMPARISON'; +export const fetchFieldComparison = createThunkAction( + FETCH_FIELD_COMPARISON, + (fieldId1, fieldId2) => + async (dispatch) => { + try { + const comparison = await XRayApi.field_compare({ fieldId1, fieldId2 }) + dispatch(loadComparison(comparison)) + return false + } catch (error) { + console.error(error) + } + } +) + +/* + * NOTE Kyle Doherty 9/8/17 - future comparisons + + +export const FETCH_METRIC_COMPARISON = 'metabase/xray/FETCH_METRIC_COMPARISON'; +export const fetchMetricComparison = createThunkAction(FETCH_METRIC_COMPARISON, function(metricId1, metricId2) { async () => { try { - const comparison = await XRayApi.card_compare({ cardId1, cardId2 }) + const comparison = await XRayApi.metric_compare({ metricId1, metricId2 }) return comparison } catch (error) { console.error(error) } } -) +}) + + + +*/ export const FETCH_SEGMENT_TABLE_FIELD_COMPARISON = 'metabase/xray/FETCH_SEGMENT_TABLE_FIELD_COMPARISON'; export const fetchSegmentTableFieldComparison = createThunkAction( @@ -148,6 +164,7 @@ export const fetchSegmentTableFieldComparison = createThunkAction( (requestParams) => async (dispatch) => { requestParams.cost = COSTS[requestParams.cost].method + dispatch(startLoad()) try { const comparison = await XRayApi.segment_table_field_compare(requestParams) return dispatch(loadComparison(comparison)) @@ -163,6 +180,7 @@ export const fetchSegmentFieldComparison = createThunkAction( (requestParams) => async (dispatch) => { requestParams.cost = COSTS[requestParams.cost].method + dispatch(startLoad()) try { const comparison = await XRayApi.segment_field_compare(requestParams) return dispatch(loadComparison(comparison)) @@ -172,27 +190,34 @@ export const fetchSegmentFieldComparison = createThunkAction( } ) +export const START_LOAD = 'metabase/xray/START_LOAD' +export const startLoad = createAction(START_LOAD) + +export const LOAD_XRAY = 'metabase/xray/LOAD_XRAY' +export const loadXray = createAction(LOAD_XRAY) + export const LOAD_COMPARISON = 'metabase/xray/LOAD_COMPARISON' export const loadComparison = createAction(LOAD_COMPARISON) export default handleActions({ - [FETCH_FIELD_XRAY]: { - next: (state, { payload }) => assoc(state, 'fieldXray', payload) - }, - [FETCH_TABLE_XRAY]: { - next: (state, { payload }) => assoc(state, 'tableXray', payload) - }, - [FETCH_CARD_XRAY]: { - next: (state, { payload }) => assoc(state, 'cardXray', payload) + [START_LOAD]: { + next: (state, { payload }) => assoc(state, 'loading', true) }, - [FETCH_SEGMENT_XRAY]: { - next: (state, { payload }) => assoc(state, 'segmentXray', payload) - }, - [FETCH_FIELD_COMPARISON]: { - next: (state, { payload }) => assoc(state, 'fieldComparison', payload) + [LOAD_XRAY]: { + next: (state, { payload }) => + chain(state) + .assoc('xray', payload) + .assoc('loading', false) + .value() }, [LOAD_COMPARISON]: { - next: (state, { payload }) => assoc(state, 'comparison', payload) + next: (state, { payload }) => + chain(state) + .assoc('comparison', payload) + .assoc('loading', false) + .value() } -}, {}) +}, { + loading: false +}) diff --git a/frontend/test/xray/xray.integ.spec.js b/frontend/test/xray/xray.integ.spec.js index 0f040b399604cefb82efaa70d14fdb5b29012459..7468d71f8c82945ccfa680fd0283fbfae5211df4 100644 --- a/frontend/test/xray/xray.integ.spec.js +++ b/frontend/test/xray/xray.integ.spec.js @@ -11,7 +11,7 @@ import { mount } from "enzyme"; import { CardApi, SegmentApi } from "metabase/services"; import { delay } from "metabase/lib/promise"; -import { FETCH_CARD_XRAY, FETCH_SEGMENT_XRAY, FETCH_TABLE_XRAY } from "metabase/xray/xray"; +import { FETCH_CARD_XRAY, FETCH_SEGMENT_XRAY, FETCH_TABLE_XRAY, LOAD_XRAY } from "metabase/xray/xray"; import TableXRay from "metabase/xray/containers/TableXRay"; import CostSelect from "metabase/xray/components/CostSelect"; import Constituent from "metabase/xray/components/Constituent"; @@ -66,7 +66,7 @@ describe("xray integration tests", () => { store.pushPath(`/xray/table/1/approximate`); const app = mount(store.getAppContainer()); - await store.waitForActions(FETCH_TABLE_XRAY, { timeout: 20000 }) + await store.waitForActions(FETCH_TABLE_XRAY, LOAD_XRAY, { timeout: 20000 }) const tableXRay = app.find(TableXRay) expect(tableXRay.length).toBe(1) @@ -95,7 +95,7 @@ describe("xray integration tests", () => { click(xrayOptionIcon); - await store.waitForActions(FETCH_CARD_XRAY, {timeout: 5000}) + await store.waitForActions(FETCH_CARD_XRAY, LOAD_XRAY, {timeout: 5000}) expect(store.getPath()).toBe(`/xray/card/${timeBreakoutQuestion.id()}/extended`) const cardXRay = app.find(CardXRay) @@ -115,7 +115,7 @@ describe("xray integration tests", () => { const xrayOptionIcon = actionsWidget.find('.Icon.Icon-beaker') click(xrayOptionIcon); - await store.waitForActions(FETCH_SEGMENT_XRAY, { timeout: 5000 }) + await store.waitForActions(FETCH_SEGMENT_XRAY, LOAD_XRAY, { timeout: 5000 }) expect(store.getPath()).toBe(`/xray/segment/${segmentId}/approximate`) const segmentXRay = app.find(SegmentXRay) @@ -128,4 +128,4 @@ describe("xray integration tests", () => { afterAll(async () => { await delay(2000) }) -}); \ No newline at end of file +});