Skip to content

Commit

Permalink
feat(entities-shared): add useTableState (#1858)
Browse files Browse the repository at this point in the history
* feat(entities-shared): add useTableState

* fix(entities-shared): fix typo
  • Loading branch information
Justineo authored Dec 18, 2024
1 parent 2b06228 commit 243160b
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -341,41 +341,7 @@ const handleSortChanged = (sortParams: TableSortParams): void => {
emit('sort', sortParams)
}
let previousQuery = ''
const hasRecords = ref(false)
const tableState = ref<TableStateParams | null>(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: <from-cache> }, query: ''
// - After revalidation: { state: 'success', hasData: <from-backend> }, 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: <from-cache> }, previousQuery: 'foo', query: '' <- just check previous query
// - After revalidation: { state: 'success', hasData: <from-backend> }, previousQuery: '', query: ''query: ''
hasRecords.value = false
}
previousQuery = props.query
tableState.value = { ...stateParams }
}
const { hideTableToolbar, handleStateChange } = composables.useTableState(() => props.query)
const { setTablePreferences, getTablePreferences } = useTablePreferences()
Expand Down
2 changes: 2 additions & 0 deletions packages/entities/entities-shared/src/composables/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -31,4 +32,5 @@ export default {
useValidators,
useSchemaProvider,
useSubSchema,
useTableState,
}
58 changes: 58 additions & 0 deletions packages/entities/entities-shared/src/composables/useTableState.ts
Original file line number Diff line number Diff line change
@@ -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> | (() => string)) {
let previousQuery = ''

const hasRecords = ref(false)
const tableState = ref<TableStateParams | null>(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: <from-cache> }, query: ''
// - After revalidation: { state: 'success', hasData: <from-backend> }, 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: <from-cache> }, previousQuery: 'foo', query: '' <- just check previous query
// - After revalidation: { state: 'success', hasData: <from-backend> }, previousQuery: '', query: ''
hasRecords.value = false
}

if (query) {
previousQuery = toValue(query)
}

tableState.value = { ...stateParams }
}

return {
tableState,
hasRecords,
hideTableToolbar,
handleStateChange,
}
}
4 changes: 2 additions & 2 deletions packages/entities/entities-shared/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'

0 comments on commit 243160b

Please sign in to comment.