Ag-grid gridReady event not working in storybook - 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

Related

How to render an html string with vuejs inside in Nuxt3 / Vue3

I'm trying to write a component in Nuxt3 which will allow me to output a string of html (that contains vue elements).
Here is what I have so far for the component / plugin
plugins/RenderVueString.js
export default defineNuxtPlugin(nuxtApp => {
nuxtApp.vueApp.component('RenderVueString', {
props: ['html'],
render(h) {
return h({
template: `<div class="RenderVueString">${this.html}</div>`,
})
}
})
})
And then in pages/index.vue
<template>
<RenderVueString :html="vueHTML" />
</template>
<script>
export default {
data() {
return: {
vueHTML: `<div>This is some vue HTML {{testVar}} <a #click="testFunction()">Run Function</a></div>`,
testVar: 'Var Value Here'
}
},
methods: {
testFunction() {
console.log('test function ran');
}
}
}
</script>
I get this error: TypeError: h is not a function
So I tried adding this to the top of the plugins/RenderVueString:
import {h} from 'vue';
After that there is no console errors, but nothing renders.
I did try rendering something simple with h like this: h('div', 'Hello') and it did output that, but I can't figure out how to output complex html with embedded Vue.
Was able to figure this out by adding the following to nuxt.config.ts
hooks: {
'vite:extendConfig': (config, {isClient, isServer}) => {
if(isClient) {
config.resolve.alias.vue = 'vue/dist/vue.esm-bundler'
}
}
},
nitro: {
commonJS: {
dynamicRequireTargets: [
'./node_modules/#vue/compiler-core',
'./node_modules/#vue/compiler-dom',
'./node_modules/#vue/compiler-ssr',
'./node_modules/vue/server-renderer',
'./node_modules/vue'
]
},
},
alias: {
'#vue/compiler-core': '#vue/compiler-core',
'#vue/compiler-dom': '#vue/compiler-dom',
'#vue/compiler-ssr': '#vue/compiler-ssr',
'vue/server-renderer': 'vue/server-renderer',
'estree-walker': 'estree-walker',
'#babel/parser': '#babel/parser'
},
And then in the plugins/RenderVueString.js
import { h, compile } from 'vue';
export default defineNuxtPlugin(nuxtApp => {
nuxtApp.vueApp.component('RenderVueString', {
props: ['html'],
render() {
return h(compile(this.html), {$emit: this.$emit});
}
})
})
Allows this on the a template:
<template>
<RenderVueString :html="vueHTML" #runFunction="testFunction()" />
</template>
<script>
export default {
data() {
return: {
vueHTML: `<div>This is some vue HTML <a #click="$emit('runFunction')">Run Function</a></div>`,
}
},
methods: {
testFunction() {
console.log('test function ran');
}
}
}
</script>
I could also pass in variables as props into the RenderVueString component if needed.
This type of functionality is very useful if you're trying to allow some advanced coding from items being pulled from a database / CMS.

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
};

How to register functional 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

Imported styles object is empty in Jest

I have a component:
import React from 'react';
import * as styles from './RedComponent.css';
class RedComponent extends React.Component {
render () {
return <div className={ styles.red }></div>;
}
}
This is the test case:
describe('Test suite', () => {
test('RedComponent tests', () => {
const wrapper = shallow(<RedComponent />);
});
console.log(wrapper.debug());
gives
<div className={[undefined]}></div>
instead of
<div className="RedComponent__red"></div>
If I console the imported styles I get
console.log(styles); // {default: {}}
This is only in Jest test cases. Style is not undefined when the app renders in browser.
My jest config:
{
"moduleFileExtensions": [
"js"
],
"moduleDirectories": [
"node_modules"
],
"moduleNameMapper": {
"\\.(css|less)$": "identity-obj-proxy"
},
"setupFiles": [
"./test-setup.js"
],
"collectCoverageFrom": [
"src/**/*.{js}",
"!**/node_modules/**"
],
"testEnvironment": "node",
"transform": {
"^.+\\.js$": "babel-jest",
"\\.(md|ttf|txt|eot|ico|otf|svg|png|gif|woff2|woff|jpeg)$": "./file-transformer.js"
}
}
Using jest v21.2.1, identity-obj-proxy v3.0.0 and React v16.0.0.
You have to change this line
import * as styles from './RedComponent.css';
to this:
import styles from './RedComponent.css';
I assume that you are using css-loader or similar and this is just how the loader works.
Maybe worths to check the example:
https://github.com/keyanzhang/jest-css-modules-example/
I think your moduleNameMapper should be like this:
"^.+\\.(css|less)$": "identity-obj-proxy"
Create a jest/identity-obj-proxy-esm.js file with the following content:
// This works around the fact we use ES named exports for styles, e.g.:
// import * as styles from './styles.scss'.
// https://github.com/keyanzhang/identity-obj-proxy/issues/8
module.exports = new Proxy(
{},
{
get: function getter(target, key) {
if (key === '__esModule') {
// True instead of false to pretend we're an ES module.
return true;
}
return key;
},
},
);
Edit jest.config.js:
// jest.config.js
module.exports = {
...
moduleNameMapper: {
...
'\\.(css|scss)$': '<rootDir>/jest/identity-obj-proxy-esm.js',
}
};
🏆 João Vieira and https://github.com/keyz/identity-obj-proxy/issues/8#issuecomment-430241345

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