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: enhance useParentProvider types #1145

Merged
merged 2 commits into from
Dec 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions packages/oruga/src/components/field/Field.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { getDefault } from "@/utils/config";
import { isVNodeEmpty } from "@/utils/helpers";
import { defineClasses, useMatchMedia } from "@/composables";

import { injectField, provideField } from "./fieldInjection";
import { injectField, provideField, type FieldData } from "./fieldInjection";

import type { FieldProps } from "./props";

Expand Down Expand Up @@ -149,7 +149,7 @@ const inputAttrs = computed(() => ({
}));

// Provided data is a computed ref to enjure reactivity.
const provideData = computed(() => ({
const provideData = computed<FieldData>(() => ({
$el: rootRef.value,
props,
hasInnerField: hasInnerField.value,
Expand Down
2 changes: 1 addition & 1 deletion packages/oruga/src/components/field/fieldInjection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
import { getOption } from "@/utils/config";
import type { FieldProps } from "./props";

type FieldData = {
export type FieldData = {
$el: Element | null;
props: FieldProps;
hasInnerField: boolean;
Expand Down
8 changes: 4 additions & 4 deletions packages/oruga/src/components/steps/StepItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ const providedData = computed<StepItemComponent<T>>(() => ({
value: itemValue,
$slots: slots,
navClasses: navItemClasses.value,
classes: stepClasses.value,
stepClasses: stepClasses.value,
labelClasses: stepLabelClasses.value,
iconClasses: stepIconClasses.value,
isTransitioning: isTransitioning.value,
Expand All @@ -62,9 +62,9 @@ const providedData = computed<StepItemComponent<T>>(() => ({
}));

/** inject functionalities and data from the parent component */
const { parent, item } = useProviderChild<StepsComponent>({
data: providedData,
});
const { parent, item } = useProviderChild<StepsComponent, StepItemComponent<T>>(
{ data: providedData },
);

const transitionName = ref();

Expand Down
23 changes: 11 additions & 12 deletions packages/oruga/src/components/steps/Steps.vue
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ const vmodel = defineModel<ModelValue>({ default: undefined });

// provided data is a computed ref to enjure reactivity
const provideData = computed<StepsComponent>(() => ({
activeIndex: activeItem.value?.index || 0,
activeIndex: activeItem.value?.index ?? 0,
labelPosition: props.labelPosition,
vertical: props.vertical,
animated: props.animated,
Expand Down Expand Up @@ -133,11 +133,10 @@ watch(
},
);

const activeItem = ref<StepItem<T>>(
items.value.find((item) => item.value === props.modelValue) ||
items.value[0],
);
/** the active item */
const activeItem = ref<StepItem<T>>();

// set the active item immediate and every time the vmodel changes
watchEffect(() => {
activeItem.value = isDefined(vmodel.value)
? items.value.find((item) => item.value === vmodel.value) ||
Expand All @@ -162,7 +161,7 @@ const prevItem = computed(() => {
let prevItem: StepItem<T> | undefined;
let idx =
items.value.findIndex(
(item) => item.identifier === activeItem.value.identifier,
(item) => item.identifier === activeItem.value?.identifier,
) - 1;
for (; idx >= 0; idx--) {
if (items.value[idx].visible) {
Expand All @@ -178,7 +177,7 @@ const nextItem = computed(() => {
let nextItem: StepItem<T> | undefined;
let idx = activeItem.value
? items.value.findIndex(
(item) => item.identifier === activeItem.value.identifier,
(item) => item.identifier === activeItem.value?.identifier,
) + 1
: 0;
for (; idx < items.value.length; idx++) {
Expand All @@ -193,7 +192,7 @@ const nextItem = computed(() => {
/** Return if the step should be clickable or not. */
function isItemClickable(item: StepItem<T>): boolean {
if (item.clickable === undefined)
return item.index < activeItem.value?.index;
return item.index < (activeItem.value?.index ?? 0);
return item.clickable;
}

Expand All @@ -214,7 +213,7 @@ function itemClick(item: StepItem<T>): void {

/** Activate next child and deactivate prev child */
function performAction(newValue: T): void {
const oldValue = activeItem.value.value;
const oldValue = activeItem.value?.value;
const oldItem = activeItem.value;
const newItem =
items.value.find((item) => item.value === newValue) || items.value[0];
Expand Down Expand Up @@ -313,17 +312,17 @@ const navigationClasses = defineClasses([
:class="childItem.navClasses"
:role="childItem.ariaRole"
:aria-current="
childItem.value === activeItem.value ? 'step' : undefined
childItem.value === activeItem?.value ? 'step' : undefined
"
:aria-controls="`tabpanel-${childItem.identifier}`"
:aria-selected="childItem.value === activeItem.value">
:aria-selected="childItem.value === activeItem?.value">
<span v-if="index > 0" :class="dividerClasses" />

<component
:is="childItem.tag"
role="button"
:tabindex="isItemClickable(childItem) ? 0 : null"
:class="childItem.classes"
:class="childItem.stepClasses"
@click="isItemClickable(childItem) && itemClick(childItem)"
@keydown.enter="
isItemClickable(childItem) && itemClick(childItem)
Expand Down
24 changes: 12 additions & 12 deletions packages/oruga/src/components/steps/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,6 @@ import type { ClassBind } from "@/types";

import type { StepItemProps } from "./props";

export type StepItemComponent<T> = StepItemProps<T, Component> & {
$slots: Slots;
navClasses: ClassBind[];
classes: ClassBind[];
iconClasses: ClassBind[];
labelClasses: ClassBind[];
isTransitioning: boolean;
activate: (index: number) => void;
deactivate: (index: number) => void;
};

export type StepsComponent = {
activeIndex: number;
labelPosition: string;
Expand All @@ -26,4 +15,15 @@ export type StepsComponent = {
variant: string;
};

export type StepItem<T> = Omit<ProviderItem<T>, "data"> & StepItemComponent<T>;
export type StepItemComponent<T> = StepItemProps<T, Component> & {
$slots: Slots;
navClasses: ClassBind[];
stepClasses: ClassBind[];
iconClasses: ClassBind[];
labelClasses: ClassBind[];
isTransitioning: boolean;
activate: (index: number) => void;
deactivate: (index: number) => void;
};

export type StepItem<T> = Omit<ProviderItem, "data"> & StepItemComponent<T>;
7 changes: 4 additions & 3 deletions packages/oruga/src/components/table/TableColumn.vue
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,10 @@ const providedData = computed<TableColumnComponent<T>>(() => ({
}));

/** inject functionalities and data from the parent component */
const { parent, item } = useProviderChild<TableComponent>({
data: providedData,
});
const { parent, item } = useProviderChild<
TableComponent,
TableColumnComponent<T>
>({ data: providedData });

// --- Computed Component Classes ---

Expand Down
4 changes: 2 additions & 2 deletions packages/oruga/src/components/tabs/TabItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ const providedData = computed<TabItemComponent<T>>(() => ({
value: itemValue,
$slots: slots,
navClasses: navItemClasses.value,
classes: tabClasses.value,
tabClasses: tabClasses.value,
iconClasses: tabIconClasses.value,
labelClasses: tabLabelClasses.value,
isTransitioning: isTransitioning.value,
Expand All @@ -58,7 +58,7 @@ const providedData = computed<TabItemComponent<T>>(() => ({
}));

/** inject functionalities and data from the parent component */
const { parent, item } = useProviderChild<TabsComponent>({
const { parent, item } = useProviderChild<TabsComponent, TabItemComponent<T>>({
data: providedData,
});

Expand Down
23 changes: 10 additions & 13 deletions packages/oruga/src/components/tabs/Tabs.vue
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ const vmodel = defineModel<ModelValue>({ default: undefined });

// provided data is a computed ref to enjure reactivity
const provideData = computed<TabsComponent>(() => ({
activeIndex: activeItem.value?.index || 0,
activeIndex: activeItem.value?.index ?? 0,
type: props.type,
vertical: props.vertical,
animated: props.animated,
Expand Down Expand Up @@ -125,20 +125,17 @@ watch(
},
);

const activeItem = ref<TabItem<T>>(
items.value.find((item) => item.value === props.modelValue) ||
items.value[0],
);
/** the active item */
const activeItem = ref<TabItem<T>>();

// set the active item immediate and every time the vmodel changes
watchEffect(() => {
activeItem.value = isDefined(vmodel.value)
? items.value.find((item) => item.value === vmodel.value) ||
items.value[0]
: items.value[0];
});

const activeIndex = computed(() => activeItem.value.index);

const isTransitioning = computed(() =>
items.value.some((item) => item.isTransitioning),
);
Expand Down Expand Up @@ -221,7 +218,7 @@ function getFirstViableItem(
let newIndex = startingIndex;
for (
;
newIndex !== activeIndex.value;
newIndex !== activeItem.value?.index;
newIndex = mod(newIndex + direction, items.value.length)
) {
// Break if the item at this index is viable (not disabled and is visible)
Expand Down Expand Up @@ -322,18 +319,18 @@ const contentClasses = defineClasses(
:class="childItem.navClasses"
role="tab"
:aria-controls="`tabpanel-${childItem.identifier}`"
:aria-selected="childItem.value === activeItem.value"
:aria-selected="childItem.value === activeItem?.value"
:tabindex="
childItem.value === activeItem.value ? undefined : '-1'
childItem.value === activeItem?.value ? undefined : '-1'
">
<o-slot-component
v-if="childItem.$slots.header"
:component="childItem"
:tag="childItem.tag"
name="header"
:class="childItem.classes"
:class="childItem.tabClasses"
:props="{
active: childItem.index === activeIndex,
active: childItem.index === activeItem?.index,
}"
@click="tabClick(childItem)"
@keydown.enter="tabClick(childItem)"
Expand All @@ -349,7 +346,7 @@ const contentClasses = defineClasses(
v-else
role="button"
:tabindex="0"
:class="childItem.classes"
:class="childItem.tabClasses"
@click="tabClick(childItem)"
@keydown.enter="tabClick(childItem)"
@keydown.left="prev($event, childItem.index)"
Expand Down
18 changes: 10 additions & 8 deletions packages/oruga/src/components/tabs/tests/tabs.integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ describe("OTab with OTabItem tests", () => {

test("renders the tab buttons", async () => {
const wrapper = mount(componentWrapper);
await wrapper.vm.$nextTick();
await nextTick();

const tabButtons = wrapper.findAll(".o-tabs__nav-item");

Expand All @@ -81,7 +81,7 @@ describe("OTab with OTabItem tests", () => {

test("renders the tab panels", async () => {
const wrapper = mount(componentWrapper, { attachTo: document.body });
await wrapper.vm.$nextTick();
await nextTick();

const tabPanls = wrapper.findAll(`[data-oruga="tabs-item"]`);

Expand All @@ -96,22 +96,24 @@ describe("OTab with OTabItem tests", () => {

test("switches the content based on the tab clicked", async () => {
const wrapper = mount(componentWrapper, { attachTo: document.body });
await wrapper.vm.$nextTick();
await nextTick();

const tabs = wrapper.findAll(".o-tabs__tab");
const tabPanls = wrapper.findAll(`[data-oruga="tabs-item"]`);
const tabPanels = wrapper.findAll(`[data-oruga="tabs-item"]`);
expect(tabs).toHaveLength(3);
expect(tabPanels).toHaveLength(3);

// clicking the second tab reveals the second content
// and hides the first content
await tabs[1].trigger("click");
expect(tabPanls[1].isVisible()).toBeTruthy();
expect(tabPanls[0].isVisible()).toBeFalsy();
expect(tabPanels[1].isVisible()).toBeTruthy();
expect(tabPanels[0].isVisible()).toBeFalsy();

// clicking the third tab reveals the third content
// and hides the second content
await tabs[2].trigger("click");
expect(tabPanls[2].isVisible()).toBeTruthy();
expect(tabPanls[1].isVisible()).toBeFalsy();
expect(tabPanels[2].isVisible()).toBeTruthy();
expect(tabPanels[1].isVisible()).toBeFalsy();
});

test("render item with component prop correctly", () => {
Expand Down
24 changes: 12 additions & 12 deletions packages/oruga/src/components/tabs/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,6 @@ import type { ClassBind } from "@/types";

import type { TabItemProps } from "./props";

export type TabItemComponent<T> = TabItemProps<T, Component> & {
$slots: Slots;
navClasses: ClassBind[];
classes: ClassBind[];
iconClasses: ClassBind[];
labelClasses: ClassBind[];
isTransitioning: boolean;
activate: (index: number) => void;
deactivate: (index: number) => void;
};

export type TabsComponent = {
activeIndex: number;
type: string;
Expand All @@ -25,4 +14,15 @@ export type TabsComponent = {
destroyOnHide: boolean;
};

export type TabItem<T> = Omit<ProviderItem<T>, "data"> & TabItemComponent<T>;
export type TabItemComponent<T> = TabItemProps<T, Component> & {
$slots: Slots;
navClasses: ClassBind[];
tabClasses: ClassBind[];
iconClasses: ClassBind[];
labelClasses: ClassBind[];
isTransitioning: boolean;
activate: (index: number) => void;
deactivate: (index: number) => void;
};

export type TabItem<T> = Omit<ProviderItem, "data"> & TabItemComponent<T>;
Loading
Loading