diff --git a/packages/entities/entities-shared/src/components/entity-base-table/EntityBaseTable.vue b/packages/entities/entities-shared/src/components/entity-base-table/EntityBaseTable.vue index 48d4448323..2031398c31 100644 --- a/packages/entities/entities-shared/src/components/entity-base-table/EntityBaseTable.vue +++ b/packages/entities/entities-shared/src/components/entity-base-table/EntityBaseTable.vue @@ -341,41 +341,7 @@ const handleSortChanged = (sortParams: TableSortParams): void => { emit('sort', sortParams) } -let previousQuery = '' -const hasRecords = ref(false) -const tableState = ref(null) -const hideTableToolbar = computed(() => { - if (hasRecords.value) { - return false - } - - // Initial state, hide the toolbar - if (!tableState.value) { - return true - } - return !tableState.value.hasData && !props.query -}) - -const handleStateChange = (stateParams: TableStateParams): void => { - if (stateParams.hasData) { - // In our scenario, as long as the table contains any data at any time, - // it indicates that there is at least a corresponding entity record in the backend. - hasRecords.value = true - } else if (stateParams.state === 'success' && !stateParams.hasData && !previousQuery) { - // If the table is in a success state but has no data and no query, it means there are no records - // Why do we record the previous query: - // When we clear the query, the table `state` event will be emitted in the following order: - // - Immediately: { state: 'success', hasData: }, query: '' - // - After revalidation: { state: 'success', hasData: }, query: '' <- This is the one we want to capture - // So we'll have to record the previous query to reset `hasRecords` correctly after revalidation - // - Immediately: { state: 'success', hasData: }, previousQuery: 'foo', query: '' <- just check previous query - // - After revalidation: { state: 'success', hasData: }, previousQuery: '', query: ''query: '' - hasRecords.value = false - } - - previousQuery = props.query - tableState.value = { ...stateParams } -} +const { hideTableToolbar, handleStateChange } = composables.useTableState(() => props.query) const { setTablePreferences, getTablePreferences } = useTablePreferences() diff --git a/packages/entities/entities-shared/src/composables/index.ts b/packages/entities/entities-shared/src/composables/index.ts index e57d7e7615..8810f51680 100644 --- a/packages/entities/entities-shared/src/composables/index.ts +++ b/packages/entities/entities-shared/src/composables/index.ts @@ -12,6 +12,7 @@ import useGatewayFeatureSupported from './useGatewayFeatureSupported' import useTruncationDetector from './useTruncationDetector' import useValidators from './useValidators' import { useSchemaProvider, useSubSchema } from './useSchema' +import useTableState from './useTableState' // All composables must be exported as part of the default object for Cypress test stubs export default { @@ -31,4 +32,5 @@ export default { useValidators, useSchemaProvider, useSubSchema, + useTableState, } diff --git a/packages/entities/entities-shared/src/composables/useTableState.ts b/packages/entities/entities-shared/src/composables/useTableState.ts new file mode 100644 index 0000000000..55e664cf44 --- /dev/null +++ b/packages/entities/entities-shared/src/composables/useTableState.ts @@ -0,0 +1,58 @@ +import { toValue, ref, computed } from 'vue' +import type { Ref } from 'vue' +import type { TableStateParams } from '../types' + +// This composable is used to access the table data state and provide shared logic +// for hiding toolbars, etc. +export default function useTableState(query?: Ref | (() => string)) { + let previousQuery = '' + + const hasRecords = ref(false) + const tableState = ref(null) + + const hideTableToolbar = computed(() => { + let showTableToolbar + + if (hasRecords.value) { + showTableToolbar = true + } else if (!tableState.value) { + // Initial state, hide the toolbar + showTableToolbar = false + } else { + showTableToolbar = tableState.value.hasData || (query && toValue(query)) + } + + return !showTableToolbar + }) + + const handleStateChange = (stateParams: TableStateParams): void => { + if (stateParams.hasData) { + // In our scenario, as long as the table contains any data at any time, + // it indicates that there is at least a corresponding entity record in the backend. + hasRecords.value = true + } else if (stateParams.state === 'success' && !stateParams.hasData && (!query || !previousQuery)) { + // If the table is in a success state but has no data and no query, it means there are no records + // Why do we record the previous query: + // When we clear the query, the table `state` event will be emitted in the following order: + // - Immediately: { state: 'success', hasData: }, query: '' + // - After revalidation: { state: 'success', hasData: }, query: '' <- This is the one we want to capture + // So we'll have to record the previous query to reset `hasRecords` correctly after revalidation + // - Immediately: { state: 'success', hasData: }, previousQuery: 'foo', query: '' <- just check previous query + // - After revalidation: { state: 'success', hasData: }, previousQuery: '', query: '' + hasRecords.value = false + } + + if (query) { + previousQuery = toValue(query) + } + + tableState.value = { ...stateParams } + } + + return { + tableState, + hasRecords, + hideTableToolbar, + handleStateChange, + } +} diff --git a/packages/entities/entities-shared/src/index.ts b/packages/entities/entities-shared/src/index.ts index db38e9508c..0bce1608ae 100644 --- a/packages/entities/entities-shared/src/index.ts +++ b/packages/entities/entities-shared/src/index.ts @@ -18,13 +18,13 @@ import TableTags from './components/common/TableTags.vue' import composables from './composables' // Extract specific composables to export -const { useAxios, useDeleteUrlBuilder, useErrors, useExternalLinkCreator, useFetchUrlBuilder, useFetcher, useFetcherCacheKey, useDebouncedFilter, useStringHelpers, useHelpers, useGatewayFeatureSupported, useTruncationDetector, useValidators, useSchemaProvider } = composables +const { useAxios, useDeleteUrlBuilder, useErrors, useExternalLinkCreator, useFetchUrlBuilder, useFetcher, useFetcherCacheKey, useDebouncedFilter, useStringHelpers, useHelpers, useGatewayFeatureSupported, useTruncationDetector, useValidators, useSchemaProvider, useTableState } = composables // Components export { EntityBaseConfigCard, ConfigCardItem, ConfigCardDisplay, InternalLinkItem, EntityBaseForm, EntityBaseTable, EntityDeleteModal, EntityFilter, EntityToggleModal, PermissionsWrapper, EntityFormSection, EntityLink, EntityEmptyState, JsonCodeBlock, TerraformCodeBlock, YamlCodeBlock, TableTags } // Composables -export { useAxios, useDeleteUrlBuilder, useErrors, useExternalLinkCreator, useFetchUrlBuilder, useFetcher, useFetcherCacheKey, useDebouncedFilter, useStringHelpers, useHelpers, useGatewayFeatureSupported, useTruncationDetector, useValidators, useSchemaProvider } +export { useAxios, useDeleteUrlBuilder, useErrors, useExternalLinkCreator, useFetchUrlBuilder, useFetcher, useFetcherCacheKey, useDebouncedFilter, useStringHelpers, useHelpers, useGatewayFeatureSupported, useTruncationDetector, useValidators, useSchemaProvider, useTableState } // Types export * from './types'