How to register functional component - vue-component

I have a functional compopnent in dialog-functional.tsx
import { Slot } from 'vue';
export interface IDialog {
background: string;
alignRight: boolean;
};
type TSlotNames = 'default' | 'header' | 'footer';
type TDialogSlots = Readonly<Record<TSlotNames, Slot | undefined>>;
export default (props: { info: IDialog}, {slots}: {slots: TDialogSlots}) => {
return <div>
{/* header */}
<h1>Дијалог: {slots?.header?.()} </h1>
{/* body */}
<div style={{
"background-color" : props.info?.background ?? undefined,
"text-align": props.info?.alignRight ? "right" : undefined,
}}>
<div>Садржај:</div>
{ slots?.default?.() }
<div>
info: { JSON.stringify(props.info)}
</div>
</div>
<hr/>
{/* footer */}
<div>
Подножје: {slots?.footer?.()}
</div>
</div>
}
and I use it in test-demo.tsx:
import { defineComponent } from 'vue';
import csfDialog, { IDialog } from "./dialog-functional";
const proba = {
title: "Проба, аха",
};
const dlgInfo: IDialog = {
background: '#ffaaaa',
alignRight: true,
}
export default defineComponent({
// https://stackoverflow.com/a/64115658
components: {
csfDialog,
},
render() {
// use an array to return multiple root nodes
return <div>
<h1>Дијалог (with "v-slots"):</h1>
<csfDialog info={dlgInfo} v-slots={
{
footer: () => <div>Садржај <b>подножја</b></div>,
header: () => `Наслов`
}
}>
Ово је основни садржај са <b>анотацијом</b> гдје год треба
</csfDialog>
</div>
}
})
However I get an error when registering the functional component csfDialog with:
export default defineComponent({
components: {
csfDialog,
},
// ...
}
No overload matches this call.
The last overload gave the following error.
Type '(props: { info: IDialog;}, { slots }: { slots: TDialogSlots; }) => JSX.Element' is not assignable to type 'Component<any, any, any, ComputedOptions, MethodOptions>'.ts(2769)
NOTE 1
If I cast it into a Vue Component it works fine:
import { Component } from 'vue';
export default defineComponent({
components: {
csfDialog: (csfDialog as unknown) as Component,
},
// ...
}
NOTE 2
It seems that if the functional component is imported with the .jsx extension (even though it has .tsx extension):
// import csfDialog, { IDialog } from "./dialog-functional";
import csfDialog, { IDialog } from "./dialog-functional.jsx";
``
The types will be better recognized but I cannot enumerate slots anymore, as I get error:
Types of property 'slots' are incompatible.
Type 'Readonly' is missing the following properties from type 'Readonly<Record<TSlotNames, Slot | undefined>>': default, header, footerts(2769)
so I have to reduce typings:
```ts
import { Slot, Slots } from 'vue';
// ...
export default (props: { info: IDialog}, {slots}: {slots: Slots}) => {
// ...
}
With this I have no errors anymore, but
awkard file extension issue .tsx -> .jsx (I assume something with JSX/Vue management of TS imports)
not being able to strict slots anymore

Related

Nuxt3 can't import component in tests

I'm trying to run a component unit test on Nuxt 3 but I get an error telling me that the component cannot be found..
FAIL test/components/button.test.ts [ test/components/button.test.ts ]
Error: Failed to resolve import "#/components/Texts/Button/ButtonText.vue" from "components\Button\Button.vue". Does the file exist?
button.spec.ts
import {test, expect} from 'vitest';
import {mount} from '#vue/test-utils';
import Button from "../../components/Button/Button.vue";
test('Button Component', async () => {
const button = mount(Button, {
props: {
text: 'My Test Button'
}
});
expect(button.text()).toEqual('My Test Button');
});
Button.vue
<template>
<div class="Button">
<slot name="left" />
<ButtonText :text="text" />
<slot name="right" />
</div>
</template>
<script lang="ts">
export default {
name: 'Button',
components: {ButtonText},
props: {
// Text to display in the button
text: {
type: String as () => string,
default: 'Button',
required: true,
},
}
}
</script>
any ideas ?
Assuming, that #/components/Texts/Button/ButtonText.vue actually exists, a solution to your problem might be adding aliases to your ./vitest.config.ts like that:
// vitest.config.ts
import { defineConfig } from 'vite'
import { aliases } from './aliases'
export default defineConfig({
resolve: { aliases },
// ... further settings
})
// aliases.ts
import { resolve } from 'path';
const r = (p: string) => resolve(__dirname, p);
export const alias: Record<string, string> = {
'~~': r('.'),
'~~/': r('./'),
'##': r('.'),
'##/': r('./'),
// ... other aliases
};

NEXT JS: Build exportPathMap for a dynamic page Route

I wanted to build a static export for my NEXT project that looks like following:
- pages
---- index.tsx
---- [pageRoute].tsx
Now I want to statically generate routeId for home page that I have handled as shown below:
import { useRouter } from 'next/router';
import React from 'react';
import { PAGE_ROUTES } from '../constants/config';
import Home from './Home/Home';
type Props = {};
export default function Base({}: Props) {
const router = useRouter();
const route = router.query.pageRoute as string;
let RenderComponent = <div>404: Page Not Found</div>;
switch (route) {
case PAGE_ROUTES.HOME: {
RenderComponent = <Home />;
break;
}
default: {
}
}
return (
<div className='flex flex-col items-center max-w-sm mx-auto'>
{RenderComponent}
</div>
);
}
I am not sure what do I specify in exportPathMaps in next.config.js in order to create static export of home page:
/** #type {import('next').NextConfig} */
module.exports = {
reactStrictMode: true,
exportPathMap: async function (
defaultPathMap,
{ dev, dir, outDir, distDir, buildId }
) {
return {
'/': { page: '/' },
// how do I add configuration for '/home': {page: '/[pageRoute]',query:{pageRoute:'home'}}
};
},
};
when I do this:
'/home': { page: '/[pageRoute]', query: { pageRoute: 'home' } },
It throws error saying:
Error: you provided query values for /home which is an auto-exported page. These can not be applied since the page can no longer be re-rendered on the server. To disable auto-export for this page addgetInitia
lProps
In order to statically pre-render dynamic paths, you should return them from getStaticPaths:
import { useRouter } from 'next/router';
import React from 'react';
import { PAGE_ROUTES } from '../constants/config';
import Home from './Home/Home';
import type { GetStaticPaths } from 'next'
export const getStaticPaths: GetStaticPaths = async () => {
const paths = Object.values(PAGE_ROUTES)
.map(route => [{ params: { pageRoute: route } }])
return {
paths,
fallback: false, // meaning any path not returned by `getStaticPaths` will result in a 404 page
}
}
type Props = {};
export default function Base({}: Props) {
return (
<div className='flex flex-col items-center max-w-sm mx-auto'>
<Home />
</div>
);
}
And, as #juliomalves said, in that case you don't need exportPathMap in next.config.js.
For custom 404 page create 404.tsx in /pages
More about getStaticPaths - https://nextjs.org/docs/api-reference/data-fetching/get-static-paths
fallback: false - https://nextjs.org/docs/api-reference/data-fetching/get-static-paths#fallback-false

Ag-grid gridReady event not working in storybook

I'm facing a issue with AG-grid in Storybook.
I'm trying to do filtration in AG-grid added as storybook template. But gridReady event doesn't get fired
Here is the code for reference
someApp.component.html
<div style="float: right; width: 100%;display: flex; <=== this is dive to have search field. We can add input here.
flex-direction: row;
justify-content: flex-end;">
<mat-search-bar (keyup)="applyFilter($event.target.value)" (onClose)="clearFilter()" [placeholder]="Search"></mat-search-bar>
</div> <===== on keyup it takes the input from search field and get the filter data in AG-Grid
</mat-toolbar>
<div class=" ag-grid-container">
<ag-grid-angular
class="ag-theme-material ag-grid-container"
[columnDefs]="columnDefs"
[rowData]="rowdef"
[suppressColumnMoveAnimation]="true"
[suppressDragLeaveHidesColumns]="true"
[overlayNoRowsTemplate]="overlayNoRowsTemplate"
(gridReady)="onGridReady($event)"
>
</ag-grid-angular>
In the above code, I get the value of search and call applyFilter to set quickfilter in grid
someApp.component.ts
columnDefs = [
{ headerName:'Sports', field: 'sports'},
{ headerName:'No: of Players', field:'player'},
];
gridApi!: GridApi;
Search = "Search";
onGridReady (params) {
console.log('calling grid Api');
this.gridApi = params.api;
console.log('called grid Api: '+this.gridApi);
}
ngOnInit() {
}
public overlayNoRowsTemplate =
'<div *ngIf="rowDef.length === 0" class="no-records">No database found.</div>';
applyFilter(value: string) {
console.log(value);
this.gridApi.setQuickFilter(
value
);
}
clearFilter() {
this.gridApi.setQuickFilter(""
);
}
someApp.stories.ts
import {
GridApi,
GridReadyEvent,
ICellRendererParams
} from 'ag-grid-community';
import { moduleMetadata, Meta } from '#storybook/angular';
import { ActionsComponent } from 'projects/web-component-library/src/lib/components/actions/app-actions.component';
import {someApp} from '../../projects/web-component-library/src/lib/components/inventory/inventory.component';
import { MaterialModule } from '../../projects/web-component-library/src/lib/components/material';
import { BrowserAnimationsModule, NoopAnimationsModule } from '#angular/platform-browser/animations';
import { NgMatSearchBarModule } from 'ng-mat-search-bar';
import { AgGridModule } from 'ag-grid-angular';
import { RouterModule } from '#angular/router';
import { CustomActionComponent } from 'projects/web-component-library/src/lib/components/custom-action/custom-action.component';
const rowdef = [{sports:'Cricket', player:11},
{sports:'Basketball', player:5}
]
export default {
title: 'Data Inventory',
component: someApp,
decorators: [
moduleMetadata({
declarations: [someApp,CustomActionComponent],
imports: [MaterialModule, BrowserAnimationsModule, NoopAnimationsModule,NgMatSearchBarModule,AgGridModule.withComponents([CustomActionComponent]),RouterModule]
}),
],
} as Meta;
export const Default = (args: someApp) => ({
component: someApp,
props: args
});
Default.args = {
rowdef
};
export const NoData = (args: someApp) => ({
component: someApp,
props: args
});
NoData.args = {
rowdef:[]
};
When I try to search something
it gives error as this.gridApi is undefined. whereas when I add this in parent HTML as below and run as 'ng serve', its works fine
App.component.html
<some-app><some-app>
Seems like onGridReady is not fired properly in storybook.
Using
Storybook 6.0.12
Angular 8
npm 6.13.4
node v10.19.0
Log of error in storybook

#vue/test-utils how to test v-if in vue3 when the parameter is imported

here are all code. parameter isInApp is imported from tools.ts. I had mount the vue component and add options ,In this case, how to mock isInApp value to finish the test
// a.vue
<template>
<div class="test" v-if="isInApp">test</div>
</template>
<script lang="ts">
import { isInApp } from './tools'
export default {
setup() {
return {
isInApp,
}
},
}
</script>
//tools.ts
export const isInApp = navigator.userAgent.indexOf('baidu') > -1
// a.spec.ts
import { mount } from '#vue/test-utils'
import a from './a.vue'
test('test',async ()=>{
const wrapper = mount(a,{
data(){
return{
isInApp: true, // I had set the data here, but it doesn't work, how to fixed it ?
}
}
})
expect(wrapper.find('.test').exists()).toBeTruthy() // Received: false
})

Connecting React Component with Redux Store

Very basic simple GET example for react-redux
I have a "MockAPI" which simulates a GET request to an API like so:
const dashboards = [
{
"Id":1,
"title":"Overview"
},
{
"Id":2,
"title":"Overview"
},
{
"Id":3,
"title":"Overview"
},
{
"Id":4,
"title":"Overview"
}
];
class DashboardApi {
static getAllDashboards() {
return new Promise((resolve) => {
setTimeout(() => {
resolve(Object.assign([], dashboards));
}, delay);
});
}
}
I am trying to develop in a react-redux flow of dispatching an action via a button click and then updating the component via the redux store.
Here is my component code:
import React, { PropTypes } from 'react';
import { connect } from 'react-redux';
import * as dashboardActions from '../../actions/dashboardActions';
class HomePage extends React.Component {
constructor(props) {
super(props);
this.loadDashboards = this.loadDashboards.bind(this);
}
loadDashboards() {
this.props.dispatch(dashboardActions.loadDashboards());
}
dashboardItem(dashboard, index) {
return <p key={index}>{dashboard.title}</p>;
}
render() {
return (
<div>
<h1>
Hello World!
<button onClick={this.loadDashboards}>load</button>
</h1>
{this.props.dashboards.map(this.dashboardItem)}
</div>
);
}
}
HomePage.propTypes = {
dashboards: PropTypes.array.isRequired,
dispatch: PropTypes.func.isRequired
};
function mapStateToProps(state) {
return {
dashboards: state.dashboards
};
}
export default connect(mapStateToProps)(HomePage);
And here is my dashboardActions.js:
import * as types from './actionTypes';
import dashboardApi from '../mockApi/mockDashboardApi';
export function loadDashboardsSuccess(dashboards) {
return { type: types.LOAD_DASHBOARDS_SUCCESS, dashboards };
}
export function loadDashboards() {
return dispatch => {
return dashboardApi
.getAllDashboards()
.then(dashboards => {
dispatch(loadDashboardsSuccess(dashboards));
});
};
}
And here is my reducer:
import initialState from './initialState';
import * as types from '../actions/actionTypes';
export default function dashboardReducer(state = initialState.dashboards, action) {
switch(action.types) {
case types.LOAD_DASHBOARDS_SUCCESS:
return action.dashboards;
default:
return state;
}
}
I am trying to get the onClick to load in the dashboards array and to render as <p> tags simply displaying the title value. Unfortunately it is not happening.
I see that the LOAD_DASHBOARDS_SUCCESS action is getting loaded, but I see that the dashboards property in the store is still an empty array instead of showing the returned data...
What am I missing here?
You've got a typo in your reducer. switch(action.types) should be switch(action.type) with no 's'

Resources