How to destructure form input values from useFormikContext? - formik

I am trying to destructure values to get specific value of the form inputs using useFormikContext()
I have done like:
const { values } = useFormikContext()
const { name, age } = values
but I'm getting error like:
Property 'name' does not exist on type 'unknown'

Fixed it.
I'm using typescript, when I added the interface to the useFormikContext() the error was gone.
Did it like this: useFormikContext<ProfileFields>()

Related

Cannot get property because an index signature declaring the expected key / value type is missing in null or undefined [1]

I am testing PermissionDetail component which has graphql fragment, that is the data of node of PermissionTable component. I am getting a flow type error in this line when getting mock data from query const permissionDetail = data.viewPermissionScheme?.grantGroups[0].grantHolders?.edges[0].node.permission;.
Component hierarchy:
App -> PermissionTable (Paginated component fragment) -> PermissionDetail (fragment)
const TestRenderer = () => {
const data = useLazyLoadQuery<examplesPermissionQuery>(
graphql`
query examplesPermissionQuery #relay_test_operation {
viewPermission(id: "test-scheme-id") {
... on PermissionView {
groups {
holders(first: 10) {
edges {
node {
permission {
...permissionDetailsFragment
}
}
}
}
}
}
}
}
`,
{},
);
// Getting Flowtype Error here: Cannot get `data.viewPermission?.groups[0]` because an index signature declaring the expected key / value type is missing in null or undefined [1]
const permissionDetail =
data.viewPermissionScheme?.grantGroups[0].grantHolders?.edges[0].node.permission;
return permissionDetail ? (<PermissionDetails permissionDetail={permissionDetail}/>) : null;
};
What is the correct way to test such components? I am new to flow and graphql and relay. So need to understand the best way to test this.
I think the error is simply that data.viewPermission?.groups can be null or undefined. Therefore you are not allowed to access an(y) index on this property. One way to fix this is by using data.viewPermission?.groups?[0] to access the property.
You could also make groups non-nullable in your GraphQL schema. Some people like a lot of nullable fields because that allows the server to return as much partial data as possible in the case of an error. But for the developer this means that every field has to be checked for null.

How to override 'data-testid' in the 'findByTestId function from Cypress Testing Library

Most of my existing codebase uses a 'id' only in few places 'data-testId' attribute present.
tried this code
import { configure } from '#testing-library/cypress';
configure({ testIdAttribute: ['data-testId','id'] });
But, still its not working.
Is there any way to use 'id' value in any of the testing-library functions.
My HTML code is something like:
<div class="some random class name" id="userprofile-open" role="button">SB</div>
I want click that element with this code:
cy.findByTestId("userprofile-open", { timeout: 120000 }).click();
I don't think you can configure testing-library with an array of ids, ref API configuration,
import { configure } from '#testing-library/cypress'
configure({ testIdAttribute: 'id' })
But even this fails. Instead you have to use the Cypress command to change the attribute name (only one name is allowed).
cy.configureCypressTestingLibrary({ testIdAttribute: 'id' })
To use either/or attribute name you can change the attribute name on the fly, wrapping it in a custom command (based on Custom Queries)
Cypress.Commands.add('findByTestIdOrId', (idToFind) => {
let result;
const { queryHelpers } = require('#testing-library/dom');
let queryAllByTestId = queryHelpers.queryAllByAttribute.bind(null, 'data-testId');
result = queryAllByTestId(Cypress.$('body')[0], idToFind)
if (result.length) return result;
queryAllByTestId = queryHelpers.queryAllByAttribute.bind(null, 'id');
result = queryAllByTestId(Cypress.$('body')[0], idToFind);
if (result.length) return result;
throw `Unable to find an element by: [data-test-id="${idToFind}"] or [id="${idToFind}"]`
})
cy.findByTestIdOrId('my-id')
.should('have.attr', 'id', 'my-id')
// passes and logs "expected <div#my-id> to have attribute id with the value my-id"
Note this custom command works only for synchronous DOM.
If you need to have Cypress retry and search for either/or attribute, don't use testing-library in the custom command.
Instead use Cypress .should() to enable retry
Cypress.Commands.add('findByTestIdOrId', (selector, idToFind) => {
cy.get(selector)
.should('satisfy', $els => {
const attrs = [...$els].reduce((acc, el) => {
const id = el.id || el.getAttribute('data-test-id') // either/or attribute
if (id) {
acc.push(id)
}
return acc
}, [])
return attrs.some(attr => attr === idToFind); // retries when false
})
.first(); // may be more than one
})
cy.findByTestIdOrId('div', 'my-id')
.should('have.attr', 'id', 'my-id')
// passes and logs "expected <div#my-id> to have attribute id with the value my-id"
The usual cypress way - which has an inherent check on the element visibility and existence as well as included retries for a period of time is using cy.get()
If you want to select element using property like data-id you need this sintax: cy.get('[propertyName="propertyValue"]')
If you want select an element by CSS selector you just pass CSS selector like this:
cy.get('#id')

Unable to access object property except by stringify/parse before the data in Graphql/resolver context

Unable to access my resolver returned object, however, I can see its content, but accessing properties returns an undefined. Only solution I found is using Stringify/Parse on my value.
Using JSON Stringify then PARSE on my Object turns it to be readable, but this is a lame solution :)
const MonkeyResolver = {
Monkey: {
address: (data, args, context) => {
console.log({data}); // Returns the actual entire object (monkey>address)
console.log(data.address); // --> Returns undefined
const newData = JSON.stringify(data);
const parsedData = JSON.parse(newData);
console.log(data.address); // --> Returns the address
}
}
}
My expected object is such as :
Object(monkey)
address:
city
street
What did I misunderstand?
Solved : if the reference database model schema manager does not include the properties, graphql prevent using the properties. I had to check out my defined schemas and solved by adding the needed object properties.

Issue with creating a reselect selector that takes a dynamic argument

I am trying to pass a dynamic argument to a reselect selector. The reason for this is that this argument is actually a angular route parameter that is not known in advance. It cannot be part of the state either.
Here is the relevant code from the subscribing component that passes the route parameter:
this.store.select(fromRoot.getMessagesWithOtherUserAccount(this.route.params['otherId']))
.subscribe(messages => this.messagesWithOtherUserAccount = messages);
Here is the code for the selectors:
const getMessagesState = (state: State) => state.message.messages;
//See error below... How can I pass my otherId argument here??
const messagesWithOtherUserAccount = createSelector(getMessagesState, messagesWithCounterParty);
export const getMessagesWithOtherUserAccount = (otherId: number) => messagesWithOtherUserAccount(otherId);
....
export const messagesWithCounterParty = (messages: Message[]) => (otherId: number) => withOtherUserAccount(otherId, messages);
Here is the error I get:
Argument of type 'number' is not assignable to parameter of type
'State'.
I would like to pass in the otherId argument to the messagesWithOtherUserAccount createSelector, but I am not sure how...
Can someone please help?
I was able to come up with the following solution:
this.store.select(fromRoot.getMessagesWithCounterParty(this.route.snapshot.params['otherId']))
.subscribe(messages => this.messagesWithOtherUserAccount = messages);
export const getMessagesWithCounterParty = (otherId: number) => createSelector(getMessagesState, (messages: Message[]) => withOtherUserAccount(otherId, messages));
createSelector can create selectors able to accept any number of custom/dynamic arguments! See createSelector API.
In your case the pseudo code to achive your result might be:
// ...
export const getMessagesWithCounterParty = createSelector(
getMessagesState, // Accepts the state as 1st argument
(otherId: number) => otherId, // Accepts an Id as 2nd argument
// Result function
(messages: Message[], otherId: number) => withOtherUserAccount(messages, otherId),
);
// Later in your application:
getMessagesWithCounterParty(yourState, 42);
PS.The error you get is not from your application but from your type checker (probably Typescript).

Why am I getting "Cannot convert lambda expression to type 'string' because it is not a delegate type"?

I have a simple table called LookupTable with two fields: Id (int) and Description (nvarchar) that I want to use to populate a drop down list.
The following code gives me the error:
IEnumerable<SelectListItem> items =
_entities.LookupTable.Select(t=> new SelectListItem {Value = t.Id,
Text = t.Description } );
I have a using System.Linq; statement already, and I get a runtime error if I try t.Id.ToString().
I must be missing something simple, right?
It is not clear from your question what is the type of _entities.LookupTable. If we suppose that it is some IQueryable<SomeModel> or IEnumerable<SomeModel> where SomeModel contains two properties Id and Description, you must ensure that you are properly converting those two values to string as the Value and Text properties of a SelectListItem are strings.
You may also try to eagerly execute the query by calling .ToString():
var items = _entities
.LookupTable
.ToList()
.Select(t => new SelectListItem
{
Value = t.Id.ToString(), // <-- notice that you might need a ToString if the Id property is integer
Text = t.Description
});

Resources