Docusaurus v2 - recent blogs list for homepage - docusaurus

in Docusaurus v2 is there a way to generate a list of recent blogs that can then be used to populate a block on the front page (src/pages/index.js)? I'm thinking something similar to how the features list which is then set by <Feature ... />.
In Docusaurus v1, this was accomplashied by the code below but I can't find a simple way to get the list of all blog posts equivalent to MetadataBlog
const MetadataBlog = require("../../core/MetadataBlog.js");
...
<h2>Latest Blog Posts</h2>
<ul>
{MetadataBlog.slice(0, 5).map((item, index) => (
<li key={index}>
<a href={`/blog/${item.path}`}>{item.title}</a>{" "}
<small>
{new Date(item.date).toLocaleDateString("en-US", {
weekday: undefined,
year: "numeric",
month: "long",
day: "numeric"
})}
</small>
</li>
))}
</ul>
Having dug around the code a bit, I believe I just want to be able to call the generateBlogPosts junction from #docusaurus/plugin-content-blog/lib/blogUtils.js. Any pointers on how I can call that function from src/pages/index.js? Thanks

I was able to hack this together with a slight change to the OP code:
const recentPosts = require("../../.docusaurus/docusaurus-plugin-content-blog/default/blog-post-list-prop-default.json");
<ul>
{recentPosts.items.slice(0, 5).map((item, index) => (
<li key={index}>
<a href={`${item.permalink}`}>{item.title}</a>{" "}
</li>
))}
</ul>
It seems to work, although it depends on internals that might change.

There's no elegant way as of now I'm afraid. You could write your own plugin that is similar to the blog plugin and pull the most recent pages and generate a JSON file to be imported on the index page.

Related

Using vue 3 vue-i18n build-in modifiers in code/template

Vue 3s' i18n plugin has build-in modifiers for (linked) messages.
message: {
edit: 'edit',
editDesc: '#. capitalize:edit description'
}
which works fine, but the problem is I couldn't find a way to use those modifiers from within a template.
For example if I want an edit button with an capitalized 'e': <button>Edit</button>
<button>
{{ $t("edit") }}
</button>
i tried several things:
<button>
{{ $t("edit.capitalize") }}
</button>
<button>
{{ $t("capitalize.edit") }}
</button>
<button>
{{ $t(":capitalize.edit") }}
</button>
but none of these worked.
Is it even possible to use those modifiers in a template/from code, or to I have to use plain js for it.
If it is possible, how?
The modifiers can only used in i18n config messages, I suggest to define the message with capitalized format and lower it when it used in the middle/end of sentence :
message: {
edit: 'Edit',
editDesc: '#:message.edit description',
editApprove: 'Please approve the #.lower:message.edit'
}
I found it easiest to modify this in the template using plain JavaScript String methods. It has been more useful in my experience to store the message strings capitalized, then use the toLowerCase method.
<button>
{{ $t("edit").toLocaleLowerCase() }}
</button>
I have the same issue. As a workaround I use temporary linked message:
message: {
tmp: 'Actual text',
content: "#.lower:tmp"
}
And then in the template:
<p>{{ $t('message.content') }}</p>
It's ugly tho...

React Testing Library : check if multiple elements have href

I'd like to check if all links in a <ul> have a href attribute.
Component (uses Gatsby's <Link>) :
const DropdownMenu = ({ open }) => {
return (
<ul className="dropdown-menu">
{open && (
<>
<li>
<Link to="/link1/">Link 1</Link>
</li>
<li>
<Link to="/link2/">Link 2</Link>
</li>
<li>
<Link to="/link3/">Link 3</Link>
</li>
</>
)}
</ul>
)
}
Rendered HTML :
...
<ul
class="dropdown-menu"
>
<li>
<a
href="/link1/"
>
Link 1
</a>
</li>
<li>
<a
href="/link2/"
>
Link 2
</a>
</li>
<li>
<a
href="/link3/"
>
Link 3
</a>
</li>
</ul>
...
Test :
test('links have href', () => {
render(<DropdownMenu open={true}></DropdownMenu>)
const listLinks = screen.getAllByRole('link')
expect(listLinks).toHaveAttribute('href')
})
Output :
received value must be an HTMLElement or an SVGElement.
Received has type: array
Received has value: [Link 1, Link 2 ...]
Is there a way (like a map or something) to check if all elements have correct href ?
I might be using the wrong approch here.
As the error says, .toHaveAttribute should be called on an HTMLElement, but you called it on listLinks which is an array of HTMLElement (HTMLElement[]).
If you really want to test the href value of each link, you simply need to make your assertions in a loop :
listLinks.forEach((link) => {
expect(link).toHaveAttribute('href', expected);
});
I'm afraid there is no such utility method as a map or something for this use case.
That being said, if there is no condition to set each to prop to the links (like the code you shared here), I think that this test is not necessary.
What you are actually testing here is Gatsby Link set the correct href attribute to the generated link from the to prop that has been passed, but this is theoretically none of your business, Gatsby already makes sure it works properly.
In my humble opinion, what you should test is the logic you define : making sure the links are rendered if open prop is true.
The rest is for extra safety, but probably not necessary.
.forEach in Jest could give false positives, use it.each instead

How to replace "You may also like" to have "Related products" in cart preview page in Bigcommerce

When Customer clicks "Add to cart" button for any product a preview pop up appears, it has "You may also like" it is automatic and is giving wrong products. I want to replace it with "Related products" which we can manage from dashboard.
The code which need to be changed is in stencil under templates/components/cart/preview.html
I see suggested_products I want to remove it and add related_products. I don't know how to do it, I don't know if there is a variable available for related_products
{{#if cart.suggested_products}}
<section class="suggestiveCart">
<h3>
{{lang 'cart.added_to_cart.you_might_also_like'}}…
</h3>
<ul class="productGrid">
{{#each cart.suggested_products}}
<li class="product">
{{> components/products/card hide_product_quick_view=true theme_settings=../theme_settings}}
</li>
{{/each}}
</ul>
</section>
{{/if}}
Related product data is scoped just to the product page--it isn't available in the cart the way suggested products are. The best solution would be to make an ajax request for the product data using the stencil utils library.
https://developer.bigcommerce.com/stencil-docs/adding-event-hooks-to-your-theme/stencil-utils-api-reference#stencil-utils_product
api.product.getById(ID, 'template', (err, response) => {
console.log(response);
});

Use a Vue component for multiple templates

I'm fairly new to Vue and I'm not even sure if I've phrased my question correctly. Here is what I am trying to achieve. I am using a card cascade from bootstrap, each card show part of a blog post. Each card has a link to a webpage for the whole blog.
To try and achieve this I have two vue files. cardCascade.vue and singleBlog.vue. My problem is at the moment I have to create a different singleBlog.vue files for each blog I have.
For example, suppose I have two blog posts in my database. cardCascade.vue will also have two links to individual blog posts. Blog post 1 will then use singleBlog1.vue and blog post 2 will then use singleBlog2.vue
What can I do so that I can achieve this more efficiently such that I only need one singleBlog.vue and it dynamically adjusts the content based on the link I select from cardCascade.vue?
What I have right now for parts of the cardCascade.vue
<b-card v-for="blog in blogs_duplicate" title="Title" img-src="https://placekitten.com/500/350" img-alt="Image" img-top>
<b-card-text>
<!--{{getBlogOfType("Vue",blog.id)}}-->
{{getBlogOfType("Vue",blog.id)}}
</b-card-text>
<b-card-text class="small text-muted">Last updated 3 mins ago</b-card-text>
</b-card>
Below is what I have right now for singleBlog.vue, keep in mind right now it just displays all the blog posts in a list.
<template>
<div id="single-blog">
<ul>
<article>
<li v-for="blog in blogs" v-bind:key="blog.id">
<h1>{{blog.title}}</h1>
<i class="fa fa-cogs" aria-hidden="true"></i>
<router-link v-bind:to="{name:'datascience-single', params: {blog_id: blog.blog_id}}">
<p>{{blog.content}}</p>
</router-link>
</li>
</article>
</ul>
</div>
</template>
<script>
import db from './firebaseInit'
export default{
data(){
return{
id:this.$route.params.id,
blogs:[],
}
},
created(){
//this.$http.get('http://jsonplaceholder.typicode.com/posts/' + this.id).then(function(data){
//this.blog = data.body;
db.collection('Blogs').orderBy('Type').get().then(querySnapshot =>{
querySnapshot.forEach(doc => {
//console.log(doc.data());
const data={
'id': doc.id,
'content': doc.data().Content,
'type': doc.data().Type,
'title': doc.data().Title,
'blog_id':doc.data().blog_id,
}
this.blogs.push(data)
})
})
}
}
</script>
It seems as though you should be giving your common component as a props information as to what it is rendering. Meaning you would make the call to the api in the parent and then make your child a "dumb" component. In your case you should make the calls to the api in cardCascade and then pass into your singleBlog component an id as props. Although in your case I do not see you using this component in the parent at all, where is it?
Like Michael said, the right way to do that is making a props and receives that prop from your router. Then, when you make your requisition, you'll pass this prop id. You can read more about props in here: https://v2.vuejs.org/v2/guide/components-props.html

Attach multiple v-models to the loop of dynamic child components in vuejs 2

Recently I ran into a problem, where I had to display many different components inside a loop, but: each of them should share it's state with parent (kinda knockout.js style). I was digging thru the docs where clearly was pointed out, that Vue.js pass properties one way down to childs, and those can eventually speak with some events. Also, docs says that there can be only one v-model per component, so finally I came up with something like this:
<li :is="field.type" v-for="(field, i) in fields" :key="i" :title="field.title" v-on:title-change="title = $event" :somevalue="field.somevalue" v-on:somevalue-change="somevalue = $event"></li>
And so on... Yet, after fifth parameter I quickly realized that the code is basically messy. Is there some less messy way to attach multiple two-way data bindings to child components?
The solution happened to be a .sync method and proper naming of events. While sync has been deprecated and removed in vue.js 2, since 2.3 version has been rewritten and added again in some similar form. As in fact it's only a syntatic sugar, my component now looks more decent I believe.
<ol>
<li :is="field.type"
v-for="(field, i) in fields"
:key="field.id"
v-bind.sync="field"
v-on:remove="fields.splice(i, 1)"></li>
</ol>
Vue.component('bool', {
template: '\
<li>\
<input type="text" v-bind:value="title" #input="$emit(\'update:title\', $event.target.value)">\
<button v-bind:value="value" #click="$emit(\'update:value\', !$event.target.value)" :class="{active: value}">{{value}}</button>\
<input type="checkbox" value="1" v-bind:checked="istrue" #change="$emit(\'update:istrue\', $event.target.checked)">\
<button #click="$emit(\'remove\')">Remove</button>\
</li>\
',
data () {
return {}
},
props: ['title', 'value', 'availablevalues']
})