How can I create components from markdown in Gatsby like a page? - graphql

I am learning Gatsby and trying to expand their tutorial, which leaves you with a blog-like site. Each post is generated from markdown files, queried with Graphql and processed via onCreateNode and createPages.
I'd like to create markdown files for author bios that can be included on these generated pages. Then if a bio changed, the markdown would be changed in one place, instead of manually changing each markdown file written by that author.
Is it possible to have a similar, generated workflow for components? Or could I pass the author's name/bio file name to a component that does a query and processes the resulting markdown?
Edit: As I've considered this, I don't see much benefit of a markdown for my bios vs a component. Would it be bad form to have a component AuthorBio for the format and several components like JohnDoeBio or JaneSmithBio that return an AuthorBio with some information passed in props to render for them? I am fairly certain I can reference these components from my markdown or let my template choose them based on frontmatter, though that might lead to a large switch...

I had the exact same thought process! This is how I solved it:
BlogPostTemplate.jsx
<Layout>
<ShortBio {...authorData} /> // show the author bio on top of the blog post
<div
className="blog-post"
dangerouslySetInnerHTML={{ __html: html }} // your markup as HTML
/>
</Layout>
ShortBio.jsx
export const ShortBio = ({ authorPic, authorName, datePublished, readingTime }) => {
const postInfo = `${readingTime} · ${datePublished}`;
return (
<AuthorDiv>
<ImageAvatars src={authorPic} alt={authorName} />
<TextStylesDiv>
<Typography>{author}</Typography>
<Typography>{shortBioText}</Typography>
<Typography>{postInfo}</Typography>
</TextStylesDiv>
</AuthorDiv>
);
};
GraphQL:
export const blogQuery = graphql`
query ($slug: String!) {
blog: markdownRemark(fields: {slug: {eq: $slug}}) {
html
frontmatter {
title
date(formatString: "DD MMMM YYYY")
author
}
excerpt(pruneLength: 165)
fields {slug}
wordCount {
words
}
}
}
`;
Use the power of Gatsby and GraphQL and determine the author by its metadata in the markup. Look at the GraphQL query: I have an author tag defined therefore I can dynamically set an author just in the markup of the blog post.
Or could I pass the author's name/bio file name to a component that
does a query and processes the resulting markdown?
Yes, use an author tag in the frontmatter of your markdown.
---
title: Hello World
date: "2018-01-15"
author: "Solid Snake"
---
Edit: As I've considered this, I don't see much benefit of a markdown
for my bios vs a component. Would it be bad form to have a component
AuthorBio for the format and several components like JohnDoeBio or
JaneSmithBio that return an AuthorBio with some information passed in
props to render for them? I am fairly certain I can reference these
components from my markdown or let my template choose them based on
frontmatter, though that might lead to a large switch...
Creating several static author components is fine if you can confidently state that the the amount of author stays small. Be pragmatic. Do it like you said.

Related

Can't set Tailwind colors when using Laravel

#foreach ($tags as $tag)
#{{$tag->name}}
#endforeach
I'm trying to show some hashtags with the correct name and color being pulled from a database. The info is all there, but for some reason, the colors do not work when being set like this. They show up as classes when inspecting, but have no effect.
#roleplay
If I remove the {{}}'s and enter the color manually, as it is in the database, the colors show up correctly. Also worth a mention that sometimes the color would show up for one a tag, but not for the others.
Tailwind doesn’t handle dynamic class names well and even says in its documentation to not construct class names dynamically.
What you could do is either store the entire tag class in your database (text-blue-500), or store just the colour and construct the class name in your controller then provide that to your view as part of the tag data.
From the tailwind documentation, dynamically generated class names must be explicitly included in their safelist.
tailwind.config.js
module.exports = {
content: [
'./pages/**/*.{html,js}',
'./components/**/*.{html,js}',
],
safelist: [
'text-2xl',
'text-3xl',
{
pattern: /bg-(red|green|blue)-(100|200|300)/,
},
],
// ...
}
The above config will allow for exceptions of specific tags - like text-2xl or those following a regex pattern.

How to display custom data in Mgt Person in SPFX

I want to display custom data which is coming from an API which will have data in 4 lines in Person Mgt,
I don't want to use default data from personquery, so how can I pass list of people's data in the Person Mgt also by default it shows data in just three lines but I want to display 4 details of an individual in 4 different lines in SPFX.
I've provided sample implementation of the code,
public render(): React.ReactElement<IMgtComponentsProps> {
return (
<Person
personQuery="me"
//personDetails={personDetails}
view={ViewType.threelines}
fetchImage={true}
avatarType={avatarType.photo}
personCardInteraction={PersonCardInteraction.hover} />
);
}
I'll be having similar type of custom data as shown below so I want to show these details as per user.
const personDetails = {
displayName: 'Bill Gates',
mail: 'nikola#contoso.com',
role:'Developer',
community:'Software Enginnering'
}
I tried passing this object inside personDetails property of Person but it's not working.
Currently a fourth line of data is only support via a custom rendering template
Here's an example of that using the raw web component as opposed to the React Wrapper.
<mgt-person class="my-person" person-query="me">
<template>
<div>
Hello, my name is: {{person.displayName}} <br>
{{person.role}} <br>
{{person.community}} <br>
{{person.mail}}
</div>
</template>
<template data-type="loading">
Loading
</template>
</mgt-person>
const person = document.querySelector('.my-person');
person.personDetails = {
displayName: 'Bill Gates',
mail: 'nikola#contoso.com',
role: 'Developer',
community: 'Software Enginnering'
};
With the existing three line view you can map each of the lines to your custom properties, assuming you have the above custom data being passed correctly you can have the component render the chosen properties on each line.
<mgt-person
class="my-person"
view="threeLines"
line2-property="role"
line2-property="community"
person-card="hover"
fetch-image>
</mgt-person>
The good news for the four-line case is that we are adding that to the library as part of our v3 release which is currently being worked on.

Vue2 + laravel6 - Component implementation

I just started using Vue2 with Laravel6 and got stuck when trying to understand how to use component method.
As I am totally new for Vue, I am using the official tutorial of Vue as a reference. What I learned from here(https://v2.vuejs.org/v2/guide/components.html) about Vue component instantiation is we give options to a component.(e.g. we give 'template:' options for HTML part.)
When I look at laravel6 codes of resouces/js/app.js, it looks something like this:
Vue.component('example-component', require('./components/ExampleComponent.vue').default);
I looked at js/components/ExampleComponent.vue expecting to see some options declared there. However, there's no option in the ExampleComponent.vue file. Instead, I see <template></template> tag. Apparently, the <template></template> tag seems to work as 'template:' option.
I have two questions regarding above:
Does <template></template> tag have the same meaning as 'template:' option?
If question 1 is yes, are other options also replacable with corresponding tags? (e.g. Can I use <props></props> tag for 'props:' option? or <data></data> tag for 'data:' option?
Thanks in advance!
In Vue world, there are two popular types of defining a component
First Type
in this type, you add all of your HTML inside the template property
and the props add as attribute inside the component object to
Vue.component('button-counter', {
data: function () {
return {
count: 0
}
},
template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
})
Second Type
in this type you add your component logic in a separate file ends with .vue
for example in laravel there is an ExampleComponent.vue file you will find on
it just template tag just as a wrapper for your component content and your logic you can write it as it mentions below.
<template>
// some content here
</template>
<script>
export default {
props: [],
methods: {
},
data(){
return {
}
}
}
</script>
Finally
there is no tag called props or data
for more info read this article

Binding Vue.js to all instances of an element, without(?) using Components

Today I'm learning Vue.js, and I have a few ideas of where it might be really useful in a new project that's an off-shoot of an existing, live project.
I like the idea of trying to replace some of my existing functionality with Vue, and I see that Components may be quite handy as quite a lot of functionality is re-used (e.g. Postcode lookups).
Once of the pieces of functionality I've used for an age is for invalid form elements - currently in jQuery when a form input or textarea is blurred I add a class of form__blurred, and that is coupled with some Sass such as:
.form__blurred {
&:not(:focus):invalid {
border-color:$danger;
}
}
This is to avoid styling all required inputs as errors immediately on page load.
I'm totally fine with doing this in jQuery, but I figured maybe it could be done in Vue.
I have an idea of how I might do it with components thanks to the laracasts series, but my form inputs are all rendered by Blade based on data received from Laravel and it doesn't seem like a neat solution to have some of the inputs rendered in Javascript, for a number of reasons (no JS, confusion about where to find input templates, etc).
I figured something like the following simplified example would be handy
<input type="text" class="form__text" v-on:blur="blurred" v-bind:class="{ form__blurred : isBlurred }" />
<script>
var form = new Vue({
el : '.form__input',
data : {
isBlurred : false
},
methods : {
blurred : function() {
this.isBlurred = true;
}
}
});
</script>
That actually works great but, as expected, it seems like using a class selector only selects the first instance, and even if it didn't, I'm guessing changing the properties would apply to all elements, not each one individually.
So the question is - is this possible with pre-rendered HTML, in a way that's smarter than just looping through a selector?
If it is, is there a way to create the Vue on a .form element and apply this function to both .form__input and .form__textarea?
Or, as is probably the case, is this just not a good use-case for Vue (since this is actually a lot more code than the jQuery solution).
Sounds like a great use case for a Custom Directive.
Vue allows you to register your own custom directives. Note that in Vue 2.0, the primary form of code reuse and abstraction is components - however there may be cases where you just need some low-level DOM access on plain elements, and this is where custom directives would still be useful.
<div id="app">
<input type="text" name="myforminput" v-my-directive>
</div>
<script>
Vue.directive('my-directive', {
bind: function (el) {
el.onblur = function () {
el.classList.add('form__blurred');
}
}
});
var app = new Vue({
el: '#app'
});
</script>
You can also add the directive locally to a parent component, if it makes sense for your application.

Joomla editor strips tags

I'm trying to create a custom component in Joomla 2.5 and struggling to get it to stop it stripping all html tags out of the editor field - links, new lines, p tags - the full works. The form field is below:
<field
name="post"
type="editor"
label="COM_HELLO_WORLD_EDITOR_LABEL"
description="COM_HELLO_WORLD_EDITOR_DESC"
class="inputbox"
filter="JComponentHelper::filterText"
required="true"
default=""
/>
Clearly there are many many posts about this around both SO and Joomla forums. However they generally seem to have two clear themes.
Tiny MCE Settings. I've checked after setting my default editor to "None" (i.e. just a text area) and the tags are all still stripped
Joomla Text filter settings. I'm logged in as a Global Admin with the super users set to "no filtering"
I'm overriding the model's save function for this with:
function store()
{
$row =& $this->getTable();
$input = new JInput();
$data = $input->getArray($_POST);
//Sets Users id as current logged in user if not set
if(!$data['jform']['post_user']) {
$data['jform']['post_user']=JFactory::getUser()->id;
}
// Bind the form fields to the post table
if (!$row->bind($data['jform'])) {
$this->setError($this->_db->getErrorMsg());
return false;
}
// Make sure the hello is valid
if (!$row->check()) {
$this->setError($this->_db->getErrorMsg());
return false;
}
// Store the hello table to the database
if (!$row->store()) {
$this->setError($this->_db->getErrorMsg());
return false;
}
return true;
}
My gut instinct is that it's to do with JInput stripping HTML tags. But even adding in the extra line into the save file $data['jform']['post']=$input->getHTML('post'); nothing happened. So I'm not really sure where to go from here. Any ideas?
UPDATE
Just to clarify an issue quickly - I want to use the preset Joomla 'Text Filter' Settings under 'Global Configuration' rather than manually setting each tag in the component!
UPDATE 2
I added filter="raw" to the editor form field. I now see the html <p> tags when I dump out the variable $_POST['jform']['post'], null, 'HTML'). However then when applying just a simple JInput Filter function - let alone applying the Joomla Config values - I'm getting null.
$input = new JInput();
$data = $input->getArray($_POST);
$data['jform']['post']=$input->get($_POST['jform']['post'], null, 'HTML');
Is the sentence here "HTML - Returns a string with HTML entities and tags intact, subject to the white or black lists in the filter." describing the JInput HTML filter referring to the Global Config Text filter settings? Just to confirm?
Try something like this
$input_options = JFilterInput::getInstance(
array(
'img','p','a','u','i','b','strong','span','div','ul','li','ol','h1','h2','h3','h4','h5',
'table','tr','td','th','tbody','theader','tfooter','br'
),
array(
'src','width','height','alt','style','href','rel','target','align','valign','border','cellpading',
'cellspacing','title','id','class'
)
);
$postData = new JInput($_POST,array('filter' => $input_options));
First array it is allowed tags, second array it is allowed attributes.
What is this about? filter="JComponentHelper::filterText"? Did you write a custom filter?
The default filtering like most things in Joomla (also acl for example) is very strict so that if you get xss from not filtering it's a deliberate choice you've made not a security risk in the core. But your core filtering should be being applied ... except that you seem to have perhaps overridden with the unknown filter. So I suspect given this unknown filter it's falling back to very string.
Quite some time later, but just for the record, for anyone encountering the same problem, here my solution.
For me this problem was immediately solved by using JRequest instead of JInput. I believe it's deprecated, but it is still used by Joomla 2.5.14 (most up-to-date Joomla 2.5 at this moment) in the save() function of JControllerForm.

Resources