What is a prevRowCountState in mui-data-grid? - material-ui

Server pagination, I understand the logic but can't figure out how does the grid know the value of prevRowCountState. Is it a undocumented thing, prefix a const/property with prev then the grid knows it's for the previous value?
const [rowCountState, setRowCountState] = React.useState(rowCount);
React.useEffect(() => {
setRowCountState((prevRowCountState) =>
rowCount !== undefined ? rowCount : prevRowCountState,
);
}, [rowCount, setRowCountState]);
<DataGrid rowCount={rowCountState} />;

set<Something> is action of setState method for Something. When supplying an argument in a form of function such as set<Something>((whatever)=> {...});, whatever is the previous state of something.
References:
React's State, starting "To fix it, use a second form of setState()..."
Geek to Geek, example 4,

Related

Where to call reset method for RTKQ useMutation()?

Since RTK 1.7.0, mutation result object has a reset() method to unsubscribe from the state update of a mutation from another component:
const [mutate, { isLoading, reset }] = useMutation({ fixedCacheKey });
I use it to get isLoading state from another component's mutation. But I don't know where to call the cleanup reset():
In the click handler:
await mutate()
reset()
Or in the cleanup
useEffect(() => () => reset(), [reset])
If it's this case, then do I need to do this for both (original and subscriber) components? And this is weird: the identity of reset function changes after state update, so the isLoading is cleared right after it changes.
I think the docs about this function is not very clear. Hope someone can improve it.
I'm using React 17.0.2, RTK 1.7.1.
Mutations never share state between usage in different components in the fist place - unless you force that by using fixedCacheKey. Are you maybe trying to solve a non-problem here?

Is there a onScroll event for ag-grid

I am looking for a scroll event on ag-grid, I want to know when the scroll reaches the end and load the next set of rows, I know if you set the infinite scroll mode then ag-grid calles the getRows method, but in my application I do not get the next set of rows right away, I make a call to the server and server sends a separate message to the client with the new set of rows
After getting in deep, I found the perfect solution to this problem.
Please note here I am used AngularJS, But very easy to understand.
onBodyScroll:function(params) {
var bottom_px = $scope.gridOptions.api.getVerticalPixelRange().bottom;
var grid_height = $scope.gridOptions.api.getDisplayedRowCount() * $scope.gridOptions.api.getSizesForCurrentTheme().rowHeight;
if(bottom_px == grid_height)
{
alert('Bottom')
}
},
There's a grid event called 'onBodyScroll' which you can attach an event handler to it.
This event is somewhat secret as it was not there on their GridOptions type before version 18, even though it does work.
see this comment: https://github.com/ag-grid/ag-grid-enterprise/issues/89#issuecomment-264477535
They do have this event in document tho: https://www.ag-grid.com/javascript-grid-events/#miscellaneous
BodyScrollEvent
bodyScroll - The body was scrolled horizontally or vertically.
onBodyScroll = (event: BodyScrollEvent) => void;
interface BodyScrollEvent {
// Event identifier
type: string;
api: GridApi;
columnApi: ColumnApi;
direction: ScrollDirection;
left: number;
top: number;
}
You should be able to do that thing (loading the data from the server) as per below example.
First of all, define your dataSource.
const dataSource: IServerSideDatasource = {
getRows: (params: IServerSideGetRowsParams) => this._getRows(params, [])
};
this.gridApi.setServerSideDatasource(dataSource);
Declare _getRows method like this.
private _getRows(params: IServerSideGetRowsParams, data: any[]) {
this.gridApi.showLoadingOverlay();
service.getData(params) // the payload your service understands
.subscribe((result: any[]) => {
params.successCallback(result, -1);
params.failCallback = () => console.log('some error occured while loading new chunk of data');
this.gridApi.hideOverlay();
},
error => this._serverErrorHandler(error)
);
}
This is pretty much self-explanatory. Let's me know if anything is unclear to you.
BTW, I've used typescript for the example, javascript example would be kind of the same for ag-grid-react

Best practice for testing for data-testid in a nested component with React Testing Library?

I'm trying to write a test to check if my app is rendering correctly. On the initial page Ive added a data-testid of "start". So my top level test checks that the initial component has been rendered.
import React from "react";
import { render } from "react-testing-library";
import App from "../App";
test("App - Check the choose form is rendered", () => {
const wrapper = render(<App />);
const start = wrapper.getByTestId("start");
// console.log(start)
// start.debug();
});
If I console.log(start) the I can see all the properties of the node. However if I try and debug() then it errors saying it's not a function.
My test above does seem to work. If I change the getByTestId from start to anything else then it does error. But I'm not using the expect function so am I violating best practices?
There are two parts to this question -
Why console.log(start) works and why not start.debug()?
getByTestId returns an HTMLElement. When you use console.log(start), the HTMLElement details are logged. But an HTMLElement does not have debug function. Instead, react-testing-library provides you with a debug function when you use render to render a component. So instead of using start.debug(), you should use wrapper.debug().
Because you don't have an expect function, is it a good practice to write such tests ?
I am not sure about what could be a great answer to this, but I will tell the way I use it. There are two variants for getting an element using data-testid - getByTestId and queryByTestId. The difference is that getByTestId throws error if an element with the test id is not found whereas queryByTestId returns null in such case. This means that getByTestId in itself is an assertion for presence of element. So having another expect which checks if the element was found or not will be redundant in case you are using getByTestId. I would rather use queryByTestId if I am to assert the presence/absence of an element. Example below -
test("App - Check the "Submit" button is rendered", () => {
const { queryByTestId } = render(<App />)
expect(queryByTestId('submit')).toBeTruthy()
});
I would use getByTestId in such tests where I know that the element is present and we have expects for the element's properties (not on the element's presence/absence). Example below -
test("App - Check the "Submit" button is disabled by default", () => {
const { getByTestId } = render(<App />)
expect(getByTestId('submit')).toHaveClass('disabled')
});
In the above test, if getByTestId is not able to find the submit button, it fails by throwing an error, and does not execute the toHaveClass. Here we don't need to test for presence/absence of the element, as this test is concerned only with the "disabled" state of the button.

setting initial facet/refinement values when using instantsearch.js

I'm using instantsearch.js and a combination of widgets to display my search results (pretty much modeled exactly after the demos).
I need to set some initial values for facets so certain items are filtered on by default. How do I do this? I know the AlgoliaSearchHelper (helper) object has a method toggleRefinement that should allow me to do this but I can't seem to access this helper prior to calling search.start() which does the initial query.
Any advice or insight on how to set some default refinements would be appreciated. Thanks.
Update: This isn't a duplicate - my issue seems to have been with the instantsearch.widget.toggle. It looks like this widget sets default values behind the scenes before the initial search. I've adjusted my code to not use this widget and to just set the searchParameters.tagFilters property instead. It was the toggle widget throwing things off for me as I couldn't figure out how to override its default filtering.
The easiest way to add initial filters to your instantsearch.js instance is to use an extra custom widget:
var search = instantsearch({
appId: 'YourApplicationID',
apiKey: 'YourSearchOnlyAPIKey',
indexName: 'YourIndexName'
});
search.addWidget(
instantsearch.widgets.searchBox({
container: '#search-box',
placeholder: 'Search for FIXME...'
})
);
search.addWidget(
instantsearch.widgets.hits({
container: '#hits-container',
templates: {
item: 'Hit {{objectID}}: FIXME'
}
})
);
// setup initial filters
search.addWidget({
init: function(options) {
// use options.helper (https://github.com/algolia/algoliasearch-helper-js) to initialize the search with custom filters & parameters
options.helper.addFacetRefinement('MyFacet', 'my value');
}
});
search.start();
This is what worked for us:
search.addWidgets([{
init: function(options) {
options.helper.toggleRefinement('attribute', 'value');
}
}]);
My issue seems to have been with the instantsearch.widget.toggle. It looks like this widget sets default values behind the scenes before the initial search. I've adjusted my code to not use this widget and to just set the searchParameters.tagFilters property instead. It was the toggle widget throwing things off for me as I couldn't figure out how to override its default filtering.
You are indeed right, under the hood the toggle widget uses the off value if provided:
if (userValues.off === undefined) {
return;
}
// Add filtering on the 'off' value if set
let isRefined = state.isFacetRefined(attributeName, userValues.on);
if (!isRefined) {
helper.addFacetRefinement(attributeName, userValues.off);
}
To avoid this incomprehension from other users, there is now a PR on instantsearch.js with the following update:
Note that if you provide an "off" option, it will be refined at initialization.

How do I dynamically add a Dijit widget to a Dojo form?

I'm trying to add a new FilteringSelect widget dynamically to a preexisting form I made out of declarative tags (on page load).
prereqs = 0;
function addAnotherPrerequisite(){
var newPreReqCursor = dijit.byId("Prerequisite"+(prereqs-1)).domNode;
dojo.create("input",{
id:"prerequisite"+prereqs,
jsId:"Prerequisite"+prereqs,
dojoType:"dijit.form.FilteringSelect",
store:"PrerequisitesStore",
searchAttr:"name",
style:"width: 350px;",
required:"true",
class: "appendedPreReq"},newPreReqCursor,"after");
dojo.parser.parse( newPreReqCursor.parentNode );
prereqs++;
}
This code properly builds a FilteringSelect widget, but the widget does not seem to be registered with the form. Whenever I submit the form, none of the values in the new widgets appear. The validation attribute works, though, and it properly pulls the values from the store.I can even call the new widget via its jsId(Prerequisite1, Prerequisite2, etc) It just won't POST!
Instead of dojo.create I also tried called the FilteringSelect widget directly. This also made the widget, but did not register the values with the form during POSTing.
var filteringSelect = new dijit.form.FilteringSelect({
id: "prereq"+prereqs,
jsId: "Prerequisite"+prereqs,
store: PrerequisitesStore,
searchAttr: "name",
required: true,
style: 'width: 350px;',
class: 'appendedPreReq'
},
"prerequisite"+prereqs).startup();
I'm going crazy trying to figure this out.
So it looks like there's some sort of bug or something. I had to define the 'name' attribute explicitly to get the widget to show up in my form's .getDependents() method. That's how dijit.forms gets its list of form values. After doing this I also couldn't access this widget by dijit.byId (didn't return anything, silently caught the error I guess), so I returned the object via its jsId with an eval.
prereqs = 0;
function(){
var newPreReqCursor = eval("Prerequisite"+(prereqs-1));
newPreReqCursor = newPreReqCursor.domNode;
dojo.create("input",{
id:"Prerequisite"+prereqs,
name:"Prerequisite"+prereqs,
jsId:"Prerequisite"+prereqs,
dojoType:"dijit.form.FilteringSelect",
store:"PrerequisitesStore",
searchAttr:"name",
style:"width: 350px;",
required:"true",
class: "appendedPreReq"},newPreReqCursor,"after");
var filterSelect = dojo.parser.parse( newPreReqCursor.parentNode );
}
It is very easy. Just create a new object like that:
// first let's create an empty node (you can reuse the existing one)
var node = dojo.create("div", {
// all necessary node attributes
className: "appendedPreReq",
style: {
width: "350px"
}
}, "myAnchorNodeId", "after");
// now let's create a widget
var widget = new dijit.form.FilteringSelect(
{
// all necessary widget properties
id: "prereq" + prereqs,
store: PrerequisitesStore,
searchAttr: "name",
required: true
},
node // optional node to replace with the widget
);
Read all about it:
http://docs.dojocampus.org/dijit/info
http://docs.dojocampus.org/dijit/form/FilteringSelect
yes while creating widgets as said by Eugene Lazutkin the input type hidden related with the filtering select gets the name as of the id, and also the value of the hidden field is updating correctly. But when the filtering select is created thr .create() method we need to give the name , and also the value of the hidden field is not updating after we select some values from the filtering select(even when we blur out). Eugene Lazutkin can you let me know why its happening so... how to update the value of hidden field in the .create() method.