Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: Migrate preselected filters component #1422

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
abd1332
feat: WIP refactor component
annacv Feb 20, 2024
8e7bb45
test: WIP type mounted Component
annacv Feb 20, 2024
928f86e
Merge branch 'main' of https://github.com/empathyco/x into feature/EM…
annacv Feb 20, 2024
ff2d75b
feat: useNoElementRender composable in component
annacv Feb 20, 2024
cdc2dd8
feat: update branch + component with cpomputed & watcher
annacv Feb 21, 2024
91f5246
chore: rm unused
annacv Feb 21, 2024
1facf0f
Update packages/x-components/src/x-modules/facets/components/preselec…
annacv Feb 26, 2024
c882abe
Update packages/x-components/src/x-modules/facets/components/preselec…
annacv Feb 26, 2024
b905727
Update packages/x-components/src/x-modules/facets/components/preselec…
annacv Feb 26, 2024
0cd0688
feat: use XBus Composable
annacv Feb 26, 2024
c5c7997
feat: rollback useXBus as it does not inject the current location
annacv Feb 26, 2024
c239e8d
feat: make x-bus-composable return 'none' location by default
annacv Feb 29, 2024
274277c
feat: use x-bus-composable in preselected filters
annacv Feb 29, 2024
896ddf3
chore: WIP
annacv Mar 1, 2024
619b397
feat: set the injected snippet as a ref to get it sync when it changes
annacv Mar 4, 2024
a5ea863
test: spy bus emit f() & add emit event metadata
annacv Mar 4, 2024
142b086
Merge branch 'main' of https://github.com/empathyco/x into feature/EM…
annacv Mar 4, 2024
078f700
feat: use hybrid inject
annacv Mar 4, 2024
d268201
chore: rm template & use no elem renderer
annacv Mar 4, 2024
1532f21
Update packages/x-components/src/x-modules/facets/components/preselec…
annacv Mar 5, 2024
42ec203
Update packages/x-components/src/x-modules/facets/components/__tests_…
annacv Mar 5, 2024
2e54d17
Update packages/x-components/src/x-modules/facets/components/preselec…
annacv Mar 5, 2024
0c98c95
Update packages/x-components/src/x-modules/facets/components/preselec…
annacv Mar 5, 2024
b1c518f
chore: rm unused imports
annacv Mar 5, 2024
7ee475b
tests: replace eventMetadata object by any object
annacv Mar 5, 2024
58b9b84
feat: use render() option instead of returning useNoElementRendered()
annacv Mar 6, 2024
1b33509
don't destructure composable and add internal fields to doc
CachedaCodes Mar 13, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/x-components/src/composables/use-x-bus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { PropsWithType } from '../utils/index';
* @returns An object with the `on` and `emit` functions.
*/
export function useXBus(): UseXBusAPI {
const location = inject<FeatureLocation>('location');
const location = inject<FeatureLocation>('location', 'none');

const currentComponent: PrivateExtendedVueComponent | undefined | null =
getCurrentInstance()?.proxy;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
import { createLocalVue, mount, Wrapper } from '@vue/test-utils';
import Vue from 'vue';
import Vue, { ComponentOptions } from 'vue';
import Vuex from 'vuex';
import { Dictionary } from '@empathyco/x-utils';
import { createRawFilters } from '../../../../utils/filters';
import { baseSnippetConfig } from '../../../../views/base-config';
import PreselectedFilters from '../preselected-filters.vue';
import { bus } from '../../../../plugins/index';

function renderPreselectedFilters({
filters,
snippetFilters
}: RenderPreselectedFiltersOptions = {}): RenderPreselectedFiltersAPI {
const emit = jest.fn();
const emit = jest.spyOn(bus, 'emit');
const localVue = createLocalVue();
const snippetConfig = Vue.observable({ ...baseSnippetConfig, filters: snippetFilters });
localVue.use(Vuex);

const wrapper = mount(PreselectedFilters, {
const wrapper = mount(PreselectedFilters as ComponentOptions<Vue>, {
provide: {
snippetConfig: snippetConfig
},
Expand All @@ -24,7 +25,7 @@ function renderPreselectedFilters({
},
localVue,
mocks: {
$x: {
emit: {
emit
}
}
Expand All @@ -49,7 +50,6 @@ describe('testing Preselected filters component', () => {

it('does not emit the event when neither filters nor snippet config filters are provided', () => {
const { emit } = renderPreselectedFilters();

expect(emit).not.toHaveBeenCalled();
});

Expand All @@ -65,7 +65,8 @@ describe('testing Preselected filters component', () => {
expect(emit).toHaveBeenCalledTimes(1);
expect(emit).toHaveBeenCalledWith(
'PreselectedFiltersProvided',
createRawFilters(snippetFilters)
createRawFilters(snippetFilters),
expect.any(Object)
);
});

Expand All @@ -76,7 +77,11 @@ describe('testing Preselected filters component', () => {
});

expect(emit).toHaveBeenCalledTimes(1);
expect(emit).toHaveBeenCalledWith('PreselectedFiltersProvided', createRawFilters(filters));
expect(emit).toHaveBeenCalledWith(
'PreselectedFiltersProvided',
createRawFilters(filters),
expect.any(Object)
);
});

it('emits the event using the snippet config filters as payload when both are provided', () => {
Expand All @@ -93,7 +98,8 @@ describe('testing Preselected filters component', () => {
expect(emit).toHaveBeenCalledTimes(1);
expect(emit).toHaveBeenCalledWith(
'PreselectedFiltersProvided',
createRawFilters(snippetFilters)
createRawFilters(snippetFilters),
expect.any(Object)
);
});

Expand All @@ -107,13 +113,21 @@ describe('testing Preselected filters component', () => {

expect(wrapper.props()).toEqual({ filters: filters });
expect(emit).toHaveBeenCalledTimes(1);
expect(emit).toHaveBeenCalledWith('PreselectedFiltersProvided', createRawFilters(filters));
expect(emit).toHaveBeenCalledWith(
'PreselectedFiltersProvided',
createRawFilters(filters),
expect.any(Object)
);

await wrapper.setProps({ filters: newFilters });

expect(wrapper.props()).toEqual({ filters: newFilters });
expect(emit).toHaveBeenCalledTimes(2);
expect(emit).toHaveBeenCalledWith('PreselectedFiltersProvided', createRawFilters(newFilters));
expect(emit).toHaveBeenCalledWith(
'PreselectedFiltersProvided',
createRawFilters(newFilters),
expect.any(Object)
);
});

it('emits the event when the snippetConfig filters change', async () => {
Expand All @@ -126,7 +140,11 @@ describe('testing Preselected filters component', () => {

expect(wrapper.props()).toEqual({ filters: filters });
expect(emit).toHaveBeenCalledTimes(1);
expect(emit).toHaveBeenCalledWith('PreselectedFiltersProvided', createRawFilters(filters));
expect(emit).toHaveBeenCalledWith(
'PreselectedFiltersProvided',
createRawFilters(filters),
expect.any(Object)
);

await setSnippetConfig({ filters: newFilters });

Expand All @@ -138,7 +156,11 @@ describe('testing Preselected filters component', () => {

// The event is called again with the newFilters provided
expect(emit).toHaveBeenCalledTimes(2);
expect(emit).toHaveBeenCalledWith('PreselectedFiltersProvided', createRawFilters(newFilters));
expect(emit).toHaveBeenCalledWith(
'PreselectedFiltersProvided',
createRawFilters(newFilters),
expect.any(Object)
);
});
});

Expand All @@ -157,7 +179,7 @@ interface RenderPreselectedFiltersOptions {
*/
interface RenderPreselectedFiltersAPI {
/** Mock of the {@link XBus.emit} function. */
emit: jest.Mock;
emit: jest.SpyInstance;
/** The wrapper of the container element.*/
wrapper: Wrapper<Vue>;
/** Helper method to change the snippet config. */
Expand Down
Original file line number Diff line number Diff line change
@@ -1,69 +1,77 @@
<script lang="ts">
import { Component, Inject, Prop, Watch } from 'vue-property-decorator';
import Vue from 'vue';
import { defineComponent, PropType, watch, computed, ref } from 'vue';
import { createRawFilters } from '../../../utils/filters';
import { isArrayEmpty } from '../../../utils/array';
import { SnippetConfig } from '../../../x-installer/api/api.types';
import { useXBus } from '../../../composables/use-x-bus';
import { useHybridInject, useNoElementRender } from '../../../composables';

/**
* This component emits {@link FacetsXEvents.PreselectedFiltersProvided} when a preselected filter
* is set in the snippet config or by using the prop of the component.
*
* @public
*/
@Component
export default class PreselectedFilters extends Vue {
/**
* Injects {@link SnippetConfig} provided by an ancestor as snippetConfig.
*
* @internal
*/
@Inject('snippetConfig')
public snippetConfig?: SnippetConfig;

/**
* A list of filters to preselect.
*
* @remarks Emits the {@link FacetsXEvents.PreselectedFiltersProvided} when the
* component is created.
*
* @public
*/
@Prop({ default: () => [] })
public filters!: string[];

/**
* Gets the provided preselected filters prioritizing the {@link SnippetConfig} over the
* filters prop.
*
* @returns An array of filter's ids.
*/
protected get preselectedFilters(): string[] {
return this.snippetConfig?.filters ?? this.filters;
}

/**
* Emits the {@link FacetsXEvents.PreselectedFiltersProvided} to save
* the provided filters in the state.
*/
@Watch('preselectedFilters')
protected emitPreselectedFilters(): void {
if (!isArrayEmpty(this.preselectedFilters)) {
this.$x.emit('PreselectedFiltersProvided', createRawFilters(this.preselectedFilters));
export default defineComponent({
name: 'PreselectedFilters',
props: {
/**
* A list of filters to preselect.
*
* @remarks Emits the {@link FacetsXEvents.PreselectedFiltersProvided} when the
* component is created.
*
* @public
*/
filters: {
type: Array as PropType<string[]>,
default: () => []
}
},
setup(props) {
const xBus = useXBus();

/**
* Injects {@link SnippetConfig} provided by an ancestor as snippetConfig
* and sets is as a ref to get synced when it changes.
*
* @internal
*/
const snippetConfig = ref(useHybridInject<SnippetConfig>('snippetConfig'));

/**
* Gets the provided preselected filters prioritizing the {@link SnippetConfig} over the
* filters prop.
*
* @returns An array of filter's ids.
* @internal
*/
const preselectedFilters = computed<string[]>(() => {
return snippetConfig.value?.filters ?? props.filters;
});

/**
* Emits the {@link FacetsXEvents.PreselectedFiltersProvided} to save
* the provided filters in the state.
*
* @internal
*/
const emitPreselectedFilters = (): void => {
if (!isArrayEmpty(preselectedFilters.value)) {
xBus.emit('PreselectedFiltersProvided', createRawFilters(preselectedFilters.value));
}
};

/**
* Emits the {@link FacetsXEvents.PreselectedFiltersProvided} when the
* computed prop changes.
*/
watch(preselectedFilters, emitPreselectedFilters, { immediate: true });
},
render() {
return useNoElementRender(this.$slots);
}

/**
* Emits the {@link FacetsXEvents.PreselectedFiltersProvided} when the
* component is created.
*/
created(): void {
this.emitPreselectedFilters();
}

// eslint-disable-next-line @typescript-eslint/no-empty-function
render(): void {}
}
});
</script>

<docs lang="mdx">
Expand Down
Loading