I created a drop down list. It works fine, but I'm having trouble translating the "Select..." and "No options" placeholders. The photos show what I mean.
There is an example of creating this drop-down list:
Student entity:
#Column({ type: 'varchar' })
studyStatus!: string;
StudyStatusEnum
enum StudyStatusEnum {
studying = 'studying',
completed = 'completed studies',
academic = 'academic leave',
deducted = 'deducted',
}
export default StudyStatusEnum;
admin-panel/student/translations
export default {
[StudyStatusEnum.academic]: 'MyTranslatedText',
[StudyStatusEnum.completed]: 'MyTranslatedText1',
[StudyStatusEnum.deducted]: 'MyTranslatedText2',
[StudyStatusEnum.studying]: 'MyTranslatedText3',
};
admin-panel/student/utils
const getStudentStudyStatus = (): IAvailableValues[] => {
return Object.values(StudyStatusEnum).map((value) => {
const label = studentStudyStatusTranslation[value as StudyStatusEnum];
return { label, value };
});
};
export default getStudentStudyStatus;
student.constants
const FIELDS = {
STUDY_STATUS: 'studyStatus',
};
export default Object.freeze({
FIELDS,
LIST_PROPERTIES: [
FIELDS.STUDY_STATUS, ],
SHOW_PROPERTIES: [],
EDIT_PROPERTIES: [],
FILTER_PROPERTIES: [],
});
student.resource
export default () => ({
resource: StudentEntity,
options: {
navigation: { icon: 'Education' },
properties: {
[StudentConstants.FIELDS.STUDY_STATUS]: {
availableValues: studyStatus,
},
},
listProperties: StudentConstants.LIST_PROPERTIES,
},
} as ResourceWithOptions);
I use Algolia autocomplete in "standalone" mode, meaning without the commercial Algolia service to return the search results; I have my own server respond with an array of search results.
How do I highlight matches in my returned items/strings (search results)?
The backend must return results that are already highlighted using a tag such as: <mark>this-is-to-be-highlighted</mark>. Here's an example result array for an search for "pie":
const items = [
{ some_attribute: 'Apple <mark>pie</mark>' },
{ some_attribute: 'American <mark>pie</mark>' },
{ some_attribute: 'Chocolate <mark>pie</mark>' }
]
The complete javascript code would then be something like this:
import { autocomplete } from "#algolia/autocomplete-js"
autocomplete({
container: '#search_input',
// ...
getSources({ query }) {
// This would be an example response from your server
const items = [
{ some_attribute: 'Apple <mark>pie</mark>' },
{ some_attribute: 'American <mark>pie</mark>' },
{ some_attribute: 'Chocolate <mark>pie</mark>' }
]
return [
{
sourceId: 'pies',
getItems({ query }) {
const HIGHLIGHT_PRE_TAG = '__aa-highlight__'
const HIGHLIGHT_POST_TAG = '__/aa-highlight__'
return items.map((item) => ({
item,
_highlightResult: {
some_attribute: {
value: item.some_attribute
.replace(/<mark>/g, HIGHLIGHT_PRE_TAG)
.replace(/<\/mark>/g, HIGHLIGHT_POST_TAG)
}
}
}))
},
templates: {
// ...
item({ item, components, html }) {
return html`<div className="aa-ItemWrapper">
<div className="aa-ItemContent">
<div className="aa-ItemContentBody">
<div className="aa-ItemContentTitle">
${components.Highlight({ hit: item, attribute: 'some_attribute' })}
</div>
</div>
</div>
</div>`
},
// ...
}
},
// ...
]
},
// ...
})
I have got 2 cards with 2 candidates, every candidate has a button where user can have a unique vote, the card have a box with total votes also, if the user make a vote for the first candidate, then the value of total in both cards is updated (in a card is substracted in another is added), . But when I press the button many times and very fast, the data returned by the backend is incorrect. Someone can explain what is happening please.
TEMPLATE OF COMPONENT IN ANGULAR
<mat-card class="example-card" *ngFor="let candidate of candidates; let i = index">
<mat-card-actions>
<button mat-raised-button class="button-vote" (click)="saveVote(candidate.userVotes[0], candidate)">
<mat-icon color="accent" aria-hidden="false" aria-label="Example home icon">pan_tool</mat-icon>
</button>
<div class="result" mat-fab>{{candidate.totalVotes}} votos</div>
</mat-card-actions>
</mat-card>
COMPONENENT.TS
export class CandidatesContainerComponent implements OnInit {
candidates!: any[]
constructor(
private candidatesService: CandidatesService,
) { }
ngOnInit(): void {
this.obtainCandidates()
}
obtainCandidates() {
this.candidatesService.getCandidates().subscribe((response: any) => {
this.candidates = response
})
}
saveVote(userVote: any, candidate: object | any) {
this.candidatesService.updateVote(candidate._id, userVote.thisChoosed).subscribe(response => {
if (response) {
this.obtainCandidates()
}
})
}
}
CONTROLLER IN NODE.JS
const updateVote = async ( req = request, res = response ) => {
const { candidateId, thisChoosed } = req.params
const query = thisChoosed === 'true' ? candidateId: { $ne:candidateId }
const previousVote = await Candidate.findOneAndUpdate(
{ _id:query },
{
'$set': { 'userVotes.$[el].thisChoose': false },
'$inc': { 'totalVotes': -1 }
},
{ arrayFilters: [
{"el.user": req.user._id }]
}
)
const newVote = await Candidate.findOneAndUpdate(
{ _id:candidateId },
{
'$set': { 'userVotes.$[el].thisChoose': true },
'$inc': { 'totalVotes': 1 }
},
{
arrayFilters: [ {"el.user": req.user._id }, ], new:true
}
)
res.json( newVote )
}
DATABASE
const VoteSchema = Schema({
user:{
type:Schema.ObjectId,
ref:'User',
required:[true, 'User is required - vote user']
},
thisChoosed:{
type:Boolean,
default:false
},
firstVote:{
type:Boolean,
default:true
}
})
const CandidateSchema = Schema({
names:{
type:String,
required:[true]
},
surnames:{
type:String,
required:[true]
},
userVotes:[VoteSchema],
totalVotes:{
type:Number,
default:0
},
})
I have a Switch component that renders Routes based on a switch statement that takes useRouteMatch().path as the argument and returns a string.
In my unit test I want to dynamically mock this but it seems I can only set one jest.mock per file.
SectionComponent.tsx:
import React from 'react'
import { useRouteMatch } from 'react-router-dom'
import * as strings from '../../../strings'
import Header from '../../shared/header/Header'
const SectionComponent: React.FC = ({ children }) => {
const { path } = useRouteMatch()
const sectionTitle = () => {
switch (path) {
case '/header':
return strings.demo.header.title
case '/navbars':
return strings.demo.navbar.title
case '/button':
// ...etc
default:
return ''
}
}
return (
<div>
<Header
title={sectionTitle()}
titleColor={strings.theme.orange}
titleSize="S"
bgColor={strings.theme.white}
size="S"
/>
{children}
</div>
)
}
export default SectionComponent
SectionComponent.spec.tsx:
import React from 'react'
import { render } from '#testing-library/react'
import SectionComponent from './SectionComponent'
import * as strings from '../../../strings'
// list I want to dynamically generate useRouteMatch().path and expected result from
const options = [
{ name: 'header', to: '/header' },
{ name: 'navbars', to: '/navbars' },
{ name: 'button', to: '/button' },
{ name: 'typography', to: '/typography' },
{ name: 'form', to: '/form' },
{ name: 'navs', to: '/navs' },
{ name: 'indicators', to: '/indicators' },
{ name: 'list-group', to: '/list-group' },
{ name: 'cards', to: '/cards' },
]
// Can only do this once per file
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'), // use actual for all non-hook parts
useRouteMatch: () => ({ path: '/header' }), // return value can't access outside variables eg option.to ('/header')
}))
describe('SectionComponent', () => {
// ideally want to map thorugh options and test here:
it('should render with each Title', () => {
const { getByText } = render(<SectionComponent />)
expect(getByText(strings.demo.header.title)).toBeInTheDocument()
})
})
Does anyone have any suggestions? I'm happy to provide more info if necessary
Figured a workaround using jest.fn().mockReturnValueOnce() multiple times
import React from 'react'
import { render } from '#testing-library/react'
import SectionComponent from './SectionComponent'
const options = [
{ name: 'Headers' },
{ name: 'Navbars' },
{ name: 'Button' },
{ name: 'Typography' },
{ name: 'Forms' },
{ name: 'Navs' },
{ name: 'Indicators' },
{ name: 'List Group' },
{ name: 'Cards' },
]
jest.mock('react-router', () => ({
useRouteMatch: jest
.fn()
.mockReturnValueOnce({ path: '/header' })
.mockReturnValueOnce({ path: '/navbars' })
.mockReturnValueOnce({ path: '/button' })
.mockReturnValueOnce({ path: '/typography' })
.mockReturnValueOnce({ path: '/form' })
.mockReturnValueOnce({ path: '/navs' })
.mockReturnValueOnce({ path: '/indicators' })
.mockReturnValueOnce({ path: '/list-group' })
.mockReturnValueOnce({ path: '/cards' }),
}))
options.forEach(option => {
describe('SectionComponent', () => {
it('should render with each Title', () => {
const { getByText } = render(<SectionComponent />)
expect(getByText(option.name)).toBeInTheDocument()
})
})
})
Maybe not the most efficient but it works
I am implementing a search feature with react and relay.
Below is my schema.js
var { nodeInterface, nodeField } = nodeDefinitions(
(globalId) => {
var { type, id } = fromGlobalId(globalId);
if (type === 'User') {
return getUser(id);
}else if (type === 'Post') {
return getPost(id);
}else if (type === 'Setting') {
return getSetting(id);
}
return null;
},
(obj) => {
if (obj instanceof User) {
return userType;
}else if (obj instanceof Post) {
return postType;
}else if (obj instanceof Setting) {
return settingType;
}
return null;
}
);
var postType = new GraphQLObjectType({
name: 'Post',
fields: {
_id: {
type: new GraphQLNonNull(GraphQLID)
},
createdAt: {
type: GraphQLString
},
id: globalIdField('Post'),
title: {
type: GraphQLString
},
color: {
type: GraphQLString
},
userId: globalIdField('User'),
username: {
type: GraphQLString,
resolve: (post) => getUserById(post.userId),
},
content: {
type: GraphQLString
},
images: {
type: postImageType,
description: "Post's main image links"
}
},
interfaces: [nodeInterface]
});
const {
connectionType: postConnection,
} = connectionDefinitions({name: 'Post', nodeType: postType});
var settingType = new GraphQLObjectType({
name: 'Setting',
fields: {
_id: {
type: new GraphQLNonNull(GraphQLID)
},
id: globalIdField('Setting'),
amount: {
type: GraphQLString
},
all_posts: {
type: postConnection,
args: {
...connectionArgs,
query: {type: GraphQLString}
},
resolve: (rootValue, args) => connectionFromPromisedArray(
getAllPosts(rootValue, args),
args
),
},
},
interfaces: [nodeInterface]
});
var Root = new GraphQLObjectType({
name: 'Root',
fields: () => ({
node: nodeField,
setting: {
type: settingType,
args: {
...connectionArgs,
currency: {type: GraphQLString}
},
resolve: (rootValue, args) => {
return getSetting(args.currency).then(function(data){
return data[0];
}).then(null,function(err){
return err;
});
}
},
})
});
Below is my database.js
export function getAllPosts(params,args) {
let findTitle = {};
let findContent = {};
if (args.query) {
findTitle.title = new RegExp(args.query, 'i');
findContent.content = new RegExp(args.query, 'i');
}
console.log("getAllPosts",args)
return new Promise((resolve, reject) => {
Post.find({$or: [findTitle,findContent]}).sort({createdAt: 'descending'}).exec({}, function(err, posts) {
if (err) {
resolve({})
} else {
resolve(posts)
}
});
})
}
Now I want to fetch all posts by $query variable
So in view I wrote like this
import React, { Component } from 'react';
import Relay from 'react-relay';
class BlogList extends Component {
constructor(props) {
super(props);
this.state = {
query: '',
};
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(){
this.props.relay.setVariables({query: this.state.query});
}
render() {
return (
<div className="input-group col-md-12">
<input type="text" onChange={this.handleChange.bind(this,"query")} value={this.state.query} name="query" placeholder="Enter Title or content"/><br/>
<span className="input-group-btn">
<button type="button" onClick={this.handleSubmit} className="btn btn-info btn-lg">
<i className="glyphicon glyphicon-search"></i>
</button>
</span>
</div>
)
}
};
export default Relay.createContainer(BlogList, {
initialVariables: {
query: ''
},
fragments: {
viewer: () => Relay.QL`
fragment on Setting {
id,
all_posts(first: 10000000,query: $query) {
edges {
node {
id,
_id,
title,
content,
createdAt,
username,
color,
images{
full
}
}
}
}
}
`,
},
});
And in routes I have
const SettingQueries = {
viewer: () => Relay.QL`query{
setting(currency: "USD")
}`,
}
export default [{
path: '/',
component: App,
queries: UserQueries,PostQueries,SettingQueries,
indexRoute: {
component: IndexBody,
},
childRoutes: [
,{
path: 'settings',
component: Setting,
queries: SettingQueries,
}]
}]
Things are working on /graphql as
but when I search from website it generates error in response
{
"data": {
"node": null
},
"errors": [
{
"message": "Abstract type Node must resolve to an Object type at runtime for field Root.node with value \"\",received \"null\".",
"locations": [
{
"line": 2,
"column": 3
}
]
}
]
}
as my web-browser is sending requests as below
Please suggest me what am I missing?
Also If I need to add some additional information please let me know.
The problem might be in your nodeDefinitions() function. First callback, also named idFetcher must return a single object. However, i see in your definition that you return a collection
var { nodeInterface, nodeField } = nodeDefinitions(
(globalId) => {
var { type, id } = fromGlobalId(globalId);
...
}else if (type === 'Post') {
return getPosts(); // this should be getPost(id)
}
);
And thats why your next callback, known as typeResolver fails and returns you a null.
var { nodeInterface, nodeField } = nodeDefinitions(
...
(obj) => {
...
// here you get Promise/Collection instead of single Post instance, therefore condition failed
}else if (obj instanceof Post) {
return postType;
}
return null;
}
);
LordDave's answer revealed one problem in your code. As you commented in his answer, all_posts field of settingType was not working.
If you used mongoose library in your DB code, I see a problem with your query:
Post.find({$or: [findTitle,findContent]}).sort({createdAt: 'descending'}).exec({}, function(err, posts) {
if (err) {
resolve({})
} else {
resolve(posts)
}
});
Based on documentation of exec, change your query to
return Post.find({$or: [findTitle,findContent]}).sort({createdAt: 'descending'}).exec(function(err, posts) {
if (err) {
resolve({})
} else {
resolve(posts)
}
});
As exec returns a promise, you can even do
return Post.find({$or: [findTitle,findContent]}).sort({createdAt: 'descending'}).exec();
Finally I got it working by creating a new type 'postList' and defined it as below
var { nodeInterface, nodeField } = nodeDefinitions(
(globalId) => {
var { type, id } = fromGlobalId(globalId);
if (type === 'User') {
return getUser(id);
}else if (type==='postList') {
return getpostList(id);
} else{
return null;
}
},
(obj) => {
if (obj instanceof User) {
return userType;
}else if (obj instanceof postList) {
return postListType;
}else{
return null;
}
}
);
In database.js
class postList {}
postList.id = "Post_id";
export {postList}
export function getpostList(id) {
return new postList
}
and under root fields as below
var postListType = new GraphQLObjectType({
name: 'postList',
description: 'List of posts',
fields: () => ({
id: globalIdField('postList'),
posts: {
type: postConnection,
description: 'List of posts',
args: {
...connectionArgs,
query: {type: GraphQLString}
},
resolve: (_, args) => connectionFromPromisedArray(getAllPosts(_,args), args),
},
}),
interfaces: [nodeInterface],
});
var Root = new GraphQLObjectType({
name: 'Root',
fields: () => ({
node: nodeField,
postList: {
type: postListType,
resolve:(rootValue)=> {
return getpostList()
}
},
})
});
I ran into this issue when I was using an InterfaceType and checked for the InterfaceType before the specialized ObjectType in the if-elseif-else of my TypeResolver