Fix searching/filtering bugs #235
- Fix a bug (introduced in 1b9be8fe) preventing the tree view from being
visible during a search.
- Fix a minor bug where the scripts view does not render based on the
initial filter.
- Add Vue component tests for `TheScriptView` to prevent regressions.
- Refactor `isSearching` in `TheScriptView` to simplify its logic.
This commit is contained in:
@@ -1,19 +1,24 @@
|
||||
<template>
|
||||
<div class="scripts">
|
||||
<div v-if="!isSearching">
|
||||
<CardList v-if="currentView === ViewType.Cards" />
|
||||
<div class="tree" v-else-if="currentView === ViewType.Tree">
|
||||
<ScriptsTree />
|
||||
</div>
|
||||
<template v-if="currentView === ViewType.Cards">
|
||||
<CardList />
|
||||
</template>
|
||||
<template v-else-if="currentView === ViewType.Tree">
|
||||
<div class="tree">
|
||||
<ScriptsTree />
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div v-else> <!-- Searching -->
|
||||
<div class="search">
|
||||
<div class="search__query">
|
||||
<div>Searching for "{{ trimmedSearchQuery }}"</div>
|
||||
<div class="search__query__close-button">
|
||||
<font-awesome-icon
|
||||
:icon="['fas', 'times']"
|
||||
v-on:click="clearSearchQuery()" />
|
||||
<div
|
||||
class="search__query__close-button"
|
||||
v-on:click="clearSearchQuery()"
|
||||
>
|
||||
<font-awesome-icon :icon="['fas', 'times']" />
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="!searchHasMatches" class="search-no-matches">
|
||||
@@ -41,6 +46,7 @@ import ScriptsTree from '@/presentation/components/Scripts/View/ScriptsTree/Scri
|
||||
import CardList from '@/presentation/components/Scripts/View/Cards/CardList.vue';
|
||||
import { ViewType } from '@/presentation/components/Scripts/Menu/View/ViewType';
|
||||
import { IReadOnlyUserFilter } from '@/application/Context/State/Filter/IUserFilter';
|
||||
import { IFilterResult } from '@/application/Context/State/Filter/IFilterResult';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
@@ -58,8 +64,8 @@ export default defineComponent({
|
||||
const { info } = inject(useApplicationKey);
|
||||
|
||||
const repositoryUrl = computed<string>(() => info.repositoryWebUrl);
|
||||
const searchQuery = ref<string>();
|
||||
const isSearching = ref(false);
|
||||
const searchQuery = ref<string | undefined>();
|
||||
const isSearching = computed(() => Boolean(searchQuery.value));
|
||||
const searchHasMatches = ref(false);
|
||||
const trimmedSearchQuery = computed(() => {
|
||||
const query = searchQuery.value;
|
||||
@@ -72,8 +78,9 @@ export default defineComponent({
|
||||
|
||||
onStateChange((newState) => {
|
||||
events.unsubscribeAll();
|
||||
updateFromInitialFilter(newState.filter.currentFilter);
|
||||
subscribeToFilterChanges(newState.filter);
|
||||
});
|
||||
}, { immediate: true });
|
||||
|
||||
function clearSearchQuery() {
|
||||
modifyCurrentState((state) => {
|
||||
@@ -82,17 +89,21 @@ export default defineComponent({
|
||||
});
|
||||
}
|
||||
|
||||
function updateFromInitialFilter(filter?: IFilterResult) {
|
||||
searchQuery.value = filter?.query;
|
||||
searchHasMatches.value = filter?.hasAnyMatches();
|
||||
}
|
||||
|
||||
function subscribeToFilterChanges(filter: IReadOnlyUserFilter) {
|
||||
events.register(
|
||||
filter.filterChanged.on((event) => {
|
||||
event.visit({
|
||||
onApply: (newFilter) => {
|
||||
searchQuery.value = newFilter.query;
|
||||
isSearching.value = true;
|
||||
searchHasMatches.value = newFilter.hasAnyMatches();
|
||||
},
|
||||
onClear: () => {
|
||||
isSearching.value = false;
|
||||
searchQuery.value = undefined;
|
||||
},
|
||||
});
|
||||
}),
|
||||
|
||||
@@ -2,6 +2,7 @@ import { ref, computed, readonly } from 'vue';
|
||||
import { IApplicationContext, IReadOnlyApplicationContext } from '@/application/Context/IApplicationContext';
|
||||
import { ICategoryCollectionState, IReadOnlyCategoryCollectionState } from '@/application/Context/State/ICategoryCollectionState';
|
||||
import { EventSubscriptionCollection } from '@/infrastructure/Events/EventSubscriptionCollection';
|
||||
import { IEventSubscriptionCollection } from '@/infrastructure/Events/IEventSubscriptionCollection';
|
||||
|
||||
export function useCollectionState(context: IApplicationContext) {
|
||||
if (!context) {
|
||||
@@ -18,13 +19,6 @@ export function useCollectionState(context: IApplicationContext) {
|
||||
}),
|
||||
);
|
||||
|
||||
type NewStateEventHandler = (
|
||||
newState: IReadOnlyCategoryCollectionState,
|
||||
oldState: IReadOnlyCategoryCollectionState | undefined,
|
||||
) => void;
|
||||
interface IStateCallbackSettings {
|
||||
readonly immediate: boolean;
|
||||
}
|
||||
const defaultSettings: IStateCallbackSettings = {
|
||||
immediate: false,
|
||||
};
|
||||
@@ -49,9 +43,6 @@ export function useCollectionState(context: IApplicationContext) {
|
||||
}
|
||||
}
|
||||
|
||||
type StateModifier = (
|
||||
state: ICategoryCollectionState,
|
||||
) => void;
|
||||
function modifyCurrentState(mutator: StateModifier) {
|
||||
if (!mutator) {
|
||||
throw new Error('missing state mutator');
|
||||
@@ -59,9 +50,6 @@ export function useCollectionState(context: IApplicationContext) {
|
||||
mutator(context.state);
|
||||
}
|
||||
|
||||
type ContextModifier = (
|
||||
state: IApplicationContext,
|
||||
) => void;
|
||||
function modifyCurrentContext(mutator: ContextModifier) {
|
||||
if (!mutator) {
|
||||
throw new Error('missing context mutator');
|
||||
@@ -75,6 +63,23 @@ export function useCollectionState(context: IApplicationContext) {
|
||||
onStateChange,
|
||||
currentContext: context as IReadOnlyApplicationContext,
|
||||
currentState: readonly(computed<IReadOnlyCategoryCollectionState>(() => currentState.value)),
|
||||
events,
|
||||
events: events as IEventSubscriptionCollection,
|
||||
};
|
||||
}
|
||||
|
||||
export type NewStateEventHandler = (
|
||||
newState: IReadOnlyCategoryCollectionState,
|
||||
oldState: IReadOnlyCategoryCollectionState | undefined,
|
||||
) => void;
|
||||
|
||||
export interface IStateCallbackSettings {
|
||||
readonly immediate: boolean;
|
||||
}
|
||||
|
||||
export type StateModifier = (
|
||||
state: ICategoryCollectionState,
|
||||
) => void;
|
||||
|
||||
export type ContextModifier = (
|
||||
state: IApplicationContext,
|
||||
) => void;
|
||||
|
||||
Reference in New Issue
Block a user