Skip to content

Commit

Permalink
feat: migrate base-grid component to vue 2.7 (#1441)
Browse files Browse the repository at this point in the history
* feat: migrate base-grid component to vue 2.7

* chore: use x-bus composable in base-grid test
  • Loading branch information
lauramargar authored Apr 8, 2024
1 parent b9b8b69 commit 1974715
Show file tree
Hide file tree
Showing 2 changed files with 157 additions and 127 deletions.
281 changes: 155 additions & 126 deletions packages/x-components/src/components/base-grid.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<template>
<component
:is="animation"
ref="gridEl"
:style="style"
class="x-base-grid"
:class="cssClasses"
Expand Down Expand Up @@ -30,12 +31,21 @@
</template>

<script lang="ts">
import Vue from 'vue';
import { Component, Prop } from 'vue-property-decorator';
import {
computed,
defineComponent,
inject,
onBeforeUnmount,
PropType,
Ref,
ref,
watch
} from 'vue';
import { useResizeObserver } from '@vueuse/core';
import { toKebabCase } from '../utils/string';
import { ListItem, VueCSSClasses } from '../utils/types';
import { XEmit } from './decorators/bus.decorators';
import { XInject } from './decorators/injection.decorators';
import { AnimationProp } from '../types/index';
import { useXBus } from '../composables/use-x-bus';
import { LIST_ITEMS_KEY } from './decorators/injection.consts';
/**
Expand All @@ -60,139 +70,158 @@
*
* @public
*/
@Component({})
export default class BaseGrid extends Vue {
/**
* Animation component that will be used to animate the base grid.
*
* @public
*/
@Prop({ default: 'ul' })
protected animation!: Vue | string;
/**
* Number of columns the grid is divided into. By default, its value is 0, setting the grid
* columns mode to auto-fill.
*
* @public
*/
@Prop({ default: 0 })
protected columns!: number;
/**
* The list of items to be rendered.
*
* @remarks The items must have an id property.
*
* @public
*/
@Prop()
protected items!: ListItem[];
/**
* It injects {@link ListItem} provided by an ancestor.
*
* @internal
*/
@XInject(LIST_ITEMS_KEY)
public injectedListItems!: ListItem[];
/**
* Emits the {@link XEventsTypes.RenderedColumnsNumberChanged}
* event whenever the number of columns rendered inside the grid changes.
*
* @internal
*/
@XEmit('RenderedColumnsNumberChanged', { immediate: false })
public renderedColumnsNumber = 0;
/**
* It returns the items passed as props or the injected ones.
*
* @returns List of grid items.
*
* @public
*/
protected get computedItems(): ListItem[] {
return (
this.items ??
this.injectedListItems ??
//TODO: add here logger
//eslint-disable-next-line no-console
console.warn('It is necessary to pass a prop or inject the list of filters')
export default defineComponent({
name: 'BaseGrid',
props: {
/**
* Animation component that will be used to animate the base grid.
*
* @public
*/
animation: {
type: AnimationProp,
default: 'ul'
},
/**
* Number of columns the grid is divided into. By default, its value is 0, setting the grid
* columns mode to auto-fill.
*
* @public
*/
columns: {
type: Number,
default: 0
},
/**
* The list of items to be rendered.
*
* @remarks The items must have an id property.
*
* @public
*/
items: {
type: Array as PropType<ListItem[]>
}
},
setup(props) {
const xBus = useXBus();
/**
* It injects {@link ListItem} provided by an ancestor.
*
* @internal
*/
const injectedListItems = inject<Ref<ListItem[]>>(LIST_ITEMS_KEY as string);
const gridEl = ref<HTMLElement | null>(null);
let renderedColumnsNumber = ref(0);
/**
* Emits the {@link XEventsTypes.RenderedColumnsNumberChanged}
* event whenever the number of columns rendered inside the grid changes.
*
* @internal
*/
watch(
renderedColumnsNumber,
() => xBus.emit('RenderedColumnsNumberChanged', renderedColumnsNumber.value),
{
immediate: false
}
);
}
/**
* CSS class based on the column property value so items inside the grid can fill different
* amount of columns or rows based on how many columns the grid is divided into.
*
* @returns CSS class with the column property value.
*
* @internal
*/
protected get cssClasses(): VueCSSClasses {
return this.columns ? `x-base-grid--cols-${this.columns}` : 'x-base-grid--cols-auto';
}
/**
* It returns the items passed as props or the injected ones.
*
* @returns List of grid items.
*
* @public
*/
const computedItems = computed((): ListItem[] | void => {
return (
props.items ??
injectedListItems?.value ??
//TODO: add here logger
//eslint-disable-next-line no-console
console.warn('It is necessary to pass a prop or inject the list of filters')
);
});
/**
* CSSStyleDeclaration object specifying the number of columns the grid is divided into based on
* the column property value.
*
* @returns A CSSStyleDeclaration to use as the style attribute.
*
* @internal
*/
protected get style(): Partial<CSSStyleDeclaration> {
return {
gridTemplateColumns: this.columns
? `repeat(${this.columns}, minmax(0, 1fr))`
: 'repeat(auto-fill, minmax(var(--x-size-min-width-grid-item, 150px), 1fr))'
};
}
/**
* CSS class based on the column property value so items inside the grid can fill different
* amount of columns or rows based on how many columns the grid is divided into.
*
* @returns CSS class with the column property value.
*
* @internal
*/
const cssClasses = computed(
(): VueCSSClasses =>
props.columns ? `x-base-grid--cols-${props.columns}` : 'x-base-grid--cols-auto'
);
/**
* Maps the item to an object containing: the `item`, its `CSS class` and its slot name.
*
* @returns An array of objects containing the item and its CSS class.
*
* @internal
*/
protected get gridItems(): GridItem[] {
return this.computedItems.map(item => {
const slotName = toKebabCase(item.modelName);
/**
* CSSStyleDeclaration object specifying the number of columns the grid is divided into based on
* the column property value.
*
* @returns A CSSStyleDeclaration to use as the style attribute.
*
* @internal
*/
const style = computed((): Partial<CSSStyleDeclaration> => {
return {
slotName,
item,
cssClass: `x-base-grid__${slotName}`
gridTemplateColumns: props.columns
? `repeat(${props.columns}, minmax(0, 1fr))`
: 'repeat(auto-fill, minmax(var(--x-size-min-width-grid-item, 150px), 1fr))'
};
});
}
/**
* Initialises the rendered columns number and sets a ResizeObserver to keep it updated.
*
* @internal
*/
protected mounted(): void {
/**
* Maps the item to an object containing: the `item`, its `CSS class` and its slot name.
*
* @returns An array of objects containing the item and its CSS class.
*
* @internal
*/
const gridItems = computed((): GridItem[] =>
(computedItems.value as ListItem[]).map(item => {
const slotName = toKebabCase(item.modelName);
return {
slotName,
item,
cssClass: `x-base-grid__${slotName}`
};
})
);
/**
* Updates the number of columns rendered inside the grid.
*
* @internal
*/
const updateRenderedColumnsNumber = (): void => {
if (gridEl.value !== null && gridEl.value instanceof HTMLElement) {
const { gridTemplateColumns } = getComputedStyle(gridEl.value);
renderedColumnsNumber.value = gridTemplateColumns.split(' ').length;
}
};
/**
* Initialises the rendered columns number and sets a ResizeObserver to keep it updated.
*
* @internal
*/
// eslint-disable-next-line @typescript-eslint/unbound-method
const resizeObserver = new ResizeObserver(this.updateRenderedColumnsNumber);
resizeObserver.observe(this.$el);
this.$on('hook:beforeDestroy', () => {
resizeObserver.disconnect();
});
}
const resizeObserver = useResizeObserver(gridEl, updateRenderedColumnsNumber);
onBeforeUnmount(() => resizeObserver.stop());
/**
* Updates the number of columns rendered inside the grid.
*
* @internal
*/
protected updateRenderedColumnsNumber(): void {
const { gridTemplateColumns } = getComputedStyle(this.$el);
this.renderedColumnsNumber = gridTemplateColumns.split(' ').length;
return {
gridItems,
cssClasses,
style,
gridEl
};
}
}
});
</script>

<style lang="scss" scoped>
Expand Down
3 changes: 2 additions & 1 deletion packages/x-components/tests/unit/base-grid.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { ListItem } from '../../src/utils';
import { NextQueriesGroup } from '../../src/x-modules/next-queries/types';
import { e2eAdapter } from '../../src/adapter/e2e-adapter';
import { XDummyBus } from '../../src/__tests__/bus.dummy';
import { useXBus } from '../../src/composables/use-x-bus';
import { loadCss } from './css.utils';

/**
Expand Down Expand Up @@ -60,7 +61,7 @@ function renderBaseGrid({
props: ['items', 'columns'],
template,
beforeCreate() {
XPlugin.bus.on('RenderedColumnsNumberChanged').subscribe(renderedColumnsNumberChangedSpy);
useXBus().on('RenderedColumnsNumberChanged').subscribe(renderedColumnsNumberChangedSpy);
}
},
{
Expand Down

0 comments on commit 1974715

Please sign in to comment.