diff --git a/docs/_config.yml b/docs/_config.yml index 513b27d6b..fe7146988 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -134,20 +134,26 @@ sidebar: url: /plugins-directory/ - title: Plugin Recipes url: /plugin-recipes/ + - title: Tutorials + children: + - title: Creating a Basic Plugin + url: /building-basic-plugin/ + - title: Customizing Plugins with Coral UI + url: /customizing-plugins-coral-ui/ - title: API children: - title: Server Plugins url: /reference/server/ - title: GraphQL url: /reference/graphql/ - - title: Contact - url: /contact/ - title: Migrating children: - title: Migrating to v4.0.0 url: /migration/4/ - title: Migrating to v4.1.0 url: /migration/4.1/ + - title: Contact + url: /contact/ marked: gfm: true diff --git a/docs/source/05-01-building-basic-plugin.md b/docs/source/05-01-building-basic-plugin.md new file mode 100644 index 000000000..77e33e29c --- /dev/null +++ b/docs/source/05-01-building-basic-plugin.md @@ -0,0 +1,256 @@ +--- +title: Creating a Basic Pride Reaction Plugin +permalink: /building-basic-plugin/ +--- + +In this tutorial, we will build a basic reaction plugin. + +## What is a plugin? + +Talk has two parts - the first is core. Our core code includes all commenting and moderation features that are necessary for a comment section, and ones that we believe are important to be universal. This code can be found in our [Talk repo](https://github.com/coralproject/talk). + +The other part is plugins. Plugins are additional functionality which are optional to use with Talk. You can turn these on or off, depending on your specific needs. Plugins are either part of our core plugins, which ship with Talk, or they are developed by 3rd parties and either used privately and internally, or are open sourced for use across the greater community. + +## Reactions + +Talk exposes a friendly API to create new reactions. To explore the capabilities of Talk we are going to create a new reaction together step-by-step. + +In Talk, there are currently three ways commenters can react to comments: Like, Love, and Respect. These reactions are separated into plugins so that you can customize which reactions you want to use by toggling them on or off - or by adding your own custom reactions, which is what we are going to do today. + +We can create a new plugin from scratch or we can use the Talk CLI to generate a plugin template for us to use. CLI stands for Command Line Interface, meaning can access utilities via the command line that make it easy to interact and integrate with Talk. + +Please note this tutorial assumes you have already [installed and configured Talk locally](/talk/). + +## Creating a new plugin using the Talk CLI + +* Open your terminal +* Go to the Talk folder +* Enter `./bin/cli-plugins` in the command line + + +![Using the Plugin CLI](/talk/images/pride_reaction_tutorial_1.png) + +You will see 3 options: `create`, `list` and `reconcile` + +* `create`: This is what we use to create new plugins. It will display a wizard and ask us a couple of questions in order to understand how we want to build our plugin. +* `list`: Shows a list of all plugins. +* `reconcile`: Reconciles local plugins and downloads their external dependencies. + +In order to create our new plugin, enter this: `./bin/cli-plugins create`. + +The CLI will now ask us 4 questions: + +![Generating our Plugin](/talk/images/pride_reaction_tutorial_2.png) + +#### Explaining the questions of the cli-plugins create + +1. This is where you will submit the name of your plugin; our usual naming convention is `talk-plugin-`, so we will enter `talk-plugin-pride` for ours. + +2. *Does this plugin extend the capabilities of the server?* If your plugin needs to extend the schema of the database, or interact with route or services you will say `yes`. In this case, we will need to store the user's comment reaction, so we will say `yes` + +3. *Does this plugin extend the capabilities of the client?* If your plugin adds visual content to Talk, you will say `yes`. In this case we need to add a button with which the users can react to the comments, so we will again put `yes`. + +4. *Should we add it to plugins.json?* Choosing yes will activate our plugin instantly. Select `yes` in this case. + +So now a plugin has been created inside our local `/plugins` folder. We can see our plugin here now, listed as `talk-plugin-pride`. + +## The structure of our plugin + +This is the structure of our plugin. Let's see what each piece does. + +![The Structure of a Plugin](/talk/images/pride_reaction_tutorial_3.png) + +* `index.js` +The index file contains everything we export to the server. In this case, we see only one thing: `module.exports = {}`. This means we are currently not exporting anything to the server - but we will do this later. + +* `/client` +The `client` folder contains all the necessary files to extend the client. + + * `index.js` + In this file we will describe how we are going to extend our client. It is generally useful to indicate where the plugin will be embedded. In our case we want to put it in each comment. Later we will see how to do this. It also serves to add functionality such as how to use `reducers` and `translations`. + + * `.eslintrc.json` + These are the ESLint rules. By default they are the ones that Talk uses. + + * `translations.yml` + This file is not mandatory but we can use it to add translations of the copy that is shown to users. + + * `/components` + These are the components. By default we will find the generated file `MyPluginComponent.js` and its CSS styles in `ModulesMyPluginComponent.css` + +Now let's run our Talk instance. We can see the plugin was generated and we can see it in our embed: + +![Viewing our Plugin](/talk/images/pride_reaction_tutorial_4.png) + +It is important to note that Talk does not dictate the architecture of the plugins. But for reasons of performance and consistency it is important that we follow certain basic guidelines. + +To create components you must be familiar with React. If you're not, I recommend the official guides, especially [Components and Props - React](https://reactjs.org/docs/components-and-props.html). + +It's important to note that the files that were generated by default with the plugin creator can be deleted or reused. Whatever your preference! + +Now that we know what all of our plugin files do, let's create our plugin :sunglasses: + +## Building our plugin + +The first thing we should think about is what our plugin consists of and what experience we want to offer. We know that we want there to be a button, that it can be clicked, and that it creates a reaction in the comment. So let's build it. + +Since our button is a component let's create a new file inside that folder. Let's call this `PrideButton.js`. + +The minimum expression of our button looks like this: + + +```js +import React from 'react'; + +class PrideButton extends React.Component { + render() { + return ; + } +} + +export default PrideButton; +``` + +Alright, we have our button. So now we want to tell it where to show - in this case, we want it under every comment. To do this we are going to make use of **slots**. Slots are small places inside Talk where we can place plugins. We already have a slot in Talk where we can place reactions. This slot is called `commentReactions`. + +To add it there, we will go to `client/index.js` and add the following lines: + +``` +import PrideButton from './components/PrideButton'; + +export default { + slots: { + commentReactions: [PrideButton], + }, +}; + +``` + +You will notice that we deleted the slot object `MyPluginComponent`. This is because we don't want to show the little example code that the CLI generated for us. We can also delete any files we won't us, or we can just leave them but not export them; if they're not exported, they won't be added to the Talk `bundle.js`. + +Now, if we go to Talk we will see that our `PrideButton` is now there on each comment - now it's time to tell the button what to do. + +![Our Newly Created Pride Button](/talk/images/pride_reaction_tutorial_5.png) + +## Adding functionality with the Talk API + +Talk exposes a series of tools that plugins can use. In this case we can use `withReaction`. `withReaction` is a HOC (High Order Component) that adds functionality to our components. + +We will use it like so: + + +```js +import React from 'react'; +import { withReaction } from 'plugin-api/beta/client/hocs'; + +class PrideButton extends React.Component { + render() { + return ; + } +} + +export default withReaction('pride')(PrideButton); +``` + +The first parameter we passed to `withReactions` is the name of the reaction. In our case, we will use 'pride'. We must be consistent with this since this will impact storing our data later. + +In our next step, let's make clicking our button either generate a reaction or remove the reaction, in case they have already acted on the comment with the same reaction. + +```js +import React from 'react'; +import { withReaction } from 'plugin-api/beta/client/hocs'; + +class PrideButton extends React.Component { + handleClick = () => { + const { postReaction, deleteReaction, alreadyReacted } = this.props; + + if (alreadyReacted) { + deleteReaction(); + } else { + postReaction(); + } + }; + + render() { + return ; + } +} + +export default withReaction('pride')(PrideButton); +``` + +`withReactions` makes the component receive `postReaction`, `deleteReaction` and `alreadyReacted`: + +* `postReaction`: Posts the reaction to the served +* `deleteReaction`: Removes the reaction +* `alreadyReacted`: Lets us know if a user has already reaction to the comment +* `count`: Tells us the number of times that users have reacted to the comment + +Now, our frontend functionality is complete, but for all this to work, we still need to add something else to our `index.js` in our main plugin folder. This time we want to extend the server. + + +```js +const { getReactionConfig } = require('../../plugin-api/beta/server'); +module.exports = getReactionConfig('pride'); +``` + +`getReactionConfig` adds the necessary functionality on the server side. + +Now our plugin works! People can react to comments with pride! + +We don't want to stop quite yet though - let's improve how our button looks visually - and also we aren't checking if a user has already reacted to the comment or not. Let's change that. + + +### Adding CSS + +We are going to create a `PrideButton.css` inside of the folder components. Let's make our button noticable and bright: + +```css +.reacted { + background: red; +} + +.button { + background: wheat +} +``` + +And we will add the the use case if someone has already reacted: + +```js +import React from 'react'; +import styles from './PrideButton.css'; +import { withReaction } from 'plugin-api/beta/client/hocs'; + +class PrideButton extends React.Component { + handleClick = () => { + const { postReaction, deleteReaction, alreadyReacted } = this.props; + + if (alreadyReacted) { + deleteReaction(); + } else { + postReaction(); + } + }; + + render() { + const { alreadyReacted, count } = this.props; + return ( + + ); + } +} + +export default withReaction('pride')(PrideButton); +```` + + +And that's it! You've created your first reaction button! :rainbow: + +If you would like to continue to the next part of our Plugin Tutorial, see Part 2 in the sidebar. diff --git a/docs/source/05-02-customizing-plugins-coral-ui.md b/docs/source/05-02-customizing-plugins-coral-ui.md new file mode 100644 index 000000000..f6cb23d81 --- /dev/null +++ b/docs/source/05-02-customizing-plugins-coral-ui.md @@ -0,0 +1,337 @@ +--- +title: Customizing Plugins with Coral UI +permalink: /customizing-plugins-coral-ui/ +--- + +This is Part 2 of our Plugin Tutorial and assumes you've already completed [Building a Basic Plugin](/building-basic-plugin.md). + +Note: We will be using Sketch in this tutorial to generate our SVG code. You can download Sketch here: https://www.sketchapp.com/. + +## Coral UI + +Within Talk, we have a set of tools we can leverage for our user interface, or UI. We simply call these tools Coral UI. + +Within Coral UI, we have icons, buttons, alerts and other components. You can see all the elements available to use within `client/coral-ui`. + +To get started using Coral UI, we're going to import it into our component. + +```js +import { Icon, Button } from 'plugin-api/beta/client/components/ui'; + +const myButton = () => + +``` + +The Coral UI Icon component uses icons from Material Design. You can see the entire list of icons and their respective names at [Material icons - Material Design](https://material.io/icons/). + +## Using SVGs + +For the Pride Plugin icon, none of the Material icons really seemed to fit so we decided to be a little creative and make our own from scratch. + +To do this, we needed to create two states: + +- The inactive icon (before someone has clicked/reacted) +- The active icon (after someone has clicked/reacted) + +To add a little additional creativity here, we thought that the inactive icon could be grayscale and the active one could be in full color. And a rainbow would be a great idea! + +![Mockups for our Pride icon](/talk/images/pride_reaction_tutorial_6.png) + +## Export / Copy SVG code + +[Sketch](https://www.sketchapp.com/) gives us a way to export the SVG code: + +* Right click on the SVG +* Click "Copy SVG code" or "Copy SVG code" + +![Exporting SVG Code from Sketch](/talk/images/pride_reaction_tutorial_7.png) + +We can export it as a file or copy the inline code to our component. We personally prefer to have the inline code to have more control over the classes and the customization. In this case, we can pass different color palettes, `grayscale` and the other colors `colored`. + +Then we can create `RainBowIcon.js` and write the following code: + +```js +import React from 'react'; +import PropTypes from 'prop-types'; + +// Las paletas de colores que vamos a utilizar +const colorPalette = { + grayscale: ['#C6C6C6', '#C6C6C6', '#7E7E7E', '#7C7C7C', '#7C7C7C', '#9F9F9F'], + colored: ['#F5C15F', '#EB7835', '#EB5242', '#CB4AB0', '#49B1DE', '#61C482'], +}; + +const RainbowIcon = ({ paletteType = 'colored', palette = [] }) => { + return ( + + + + + + + + + + + + + + + ); +}; + +// This is important to do so we pass the correct properties to the component +RainbowIcon.propTypes = { + paletteType: PropTypes.oneOf(['colored', 'grayscale']), + palette: PropTypes.array, +}; + +export default RainbowIcon; +```` + +Most of the component is code generated by Sketch, except for the properties that we can control control with the palettes. The color of the rainbow lines will be given based on the order of the colors of the palette. + +We have two props for our component: `paletteType` and `palette`: + +`paletteType`: since we we have two palettes we've created, we can pass these directly as `colored` and `greyscale` + +`palette`: if we want to pass an array of colors we can do it using this property + +Ready! So now we have our icon. Now let's modify the our button `PrideButton.js` to use our new icon. + + +```js +import React from 'react'; +import cn from 'classnames'; +import styles from './PrideButton.css'; +import { withReaction } from 'plugin-api/beta/client/hocs'; +import RainbowIcon from './RainbowIcon'; + +class PrideButton extends React.Component { + handleClick = () => { + const { postReaction, deleteReaction, alreadyReacted } = this.props; + + if (alreadyReacted) { + deleteReaction(); + } else { + postReaction(); + } + }; + + render() { + const { alreadyReacted } = this.props; + return ( +
+ + {alreadyReacted ? ( + + ) : ( + + )} + +
+ ); + } +} + +export default withReaction('pride')(PrideButton); +``` + +We will use the property `alreadyReacted` to change the icon and render one in grayscale (using the property `grayscale`). + +There are many pros and cons of using inline SVGs that are outside the scope of this tutorial. If you'd like to learn more, you can read [5 Gotchas You're Gonna Face Getting Inline SVG Into Production](https://css-tricks.com/gotchas-on-getting-svg-into-production/) and its follow-up post [Part 2 Gotchas](https://css-tricks.com/gotchas-getting-inline-svg-production-part-ii/). + +You can view the source code up to this point here: [talk-plugin-pride @ ae5c1a5](https://github.com/coralproject/talk-plugin-pride/commit/ae5c1a5e26390b9374c87ce5530d60c10b5c325e). + +To keep performance top of mine, and given that this portion of SVG code can not be cached, we will create separate SVG files for the two icon states. + +We will create the folder `assets` and place our two files inside it: `ColoredRainbowIcon.svg` and `GrayscaleRainbowIcon.svg`. We can export them both with Sketch or simply copy the SVG code into each file. + +## Using an SVG in our components + +We are going to import our SVG icons just as we did with our components, the only difference is the `.svg` at the end. + +```js +import ColoredRainbowIcon from '../assets/ColoredRainbowIcon.svg'; +import GrayscaleRainbowIcon from '../assets/GrayscaleRainbowIcon.svg'; +``` + +Since Webpack will give us the new url of the resource, we can us it like this: + +```js + +``` + +## Using media queries + +Now of course we will need to support several devices and browsers, so we'll need to make sure our plugin responds correctly. For this we can use media queries. + +In this case, we want to make sure that on mobile devices that are less than 425px, the reaction label is not shown. + +``` +@media (max-width: 425px) { + .label { + display: none; + } +} +``` + +If you look at our PostCSS configuration, you will notice that we use PreCSS. PreCSS allows us to optionally use a syntax that is similar to Sass and allows us to make use of variables: + +```css +@custom-media --viewport-medium (width <= 50rem); +@custom-selector :--heading h1, h2, h3, h4, h5, h6; + +:root { + --fontSize: 1rem; + --mainColor: #12345678; +} + +@media (--viewport-medium) { + body { + color: var(--mainColor); + font-family: system-ui; + font-size: var(--fontSize); + line-height: calc(var(--fontSize) * 1.5); + overflow-wrap: break-word; + padding-inline: calc((var(--fontSize) / 2) + 1px); + } +} +``` + +To learn more about PreCSS: https://github.com/jonathantneal/precss + + +## Adding animations + +To make the user experience even more fun, we wanted the user to see a small animation when they click on our Pride Button: + + +```css +.reacted { + animation: rainbow 1s 1; +} + +@keyframes rainbow{ + 20%{color: #EB5242;} + 40%{color: #F5C15F;} + 60%{color: #61C482;} + 80%{color: #49B1DE;} + 100%{color: #EB7835;} +} +``` + +Now lets add this styling through our `classnames` library: + + +```js + + + ); + } +} + +export default withReaction('pride')(PrideButton); + +``` + +To view the completed source code, look here: https://github.com/coralproject/talk-plugin-pride diff --git a/docs/source/07-01-faq.md b/docs/source/07-01-faq.md new file mode 100644 index 000000000..06d0b0669 --- /dev/null +++ b/docs/source/07-01-faq.md @@ -0,0 +1,17 @@ +--- +title: FAQ +permalink: /faq/ +--- + +## How can I get help integrating Talk into my newsroom? + +We're here to help with newsrooms of all sizes. Email our Integration Engineer +([jeff@mozillafoundation.org](mailto:jeff@mozillafoundation.org)) to set up a meeting. + +## How do I request a feature or submit a bug? + +The best way is to [submit a Github issue](https://github.com/coralproject/talk/issues). Make sure you give plenty of details, our Core Team can usually respond within a few hours on weekdays. + +## How can our dev team contribute to Talk? + +We are lucky to work with newsroom dev teams and individual contributors who span the world, and come from newsrooms of all sizes. You can read our [Contribution Guide](https://github.com/coralproject/talk/blob/master/CONTRIBUTING.md) to get started, but feel free to reach out to us via Github, or get in touch directly with Jeff via jeff@mozillafoundation.org. diff --git a/docs/source/images/pride_reaction_tutorial_1.png b/docs/source/images/pride_reaction_tutorial_1.png new file mode 100755 index 000000000..50f2014e2 Binary files /dev/null and b/docs/source/images/pride_reaction_tutorial_1.png differ diff --git a/docs/source/images/pride_reaction_tutorial_2.png b/docs/source/images/pride_reaction_tutorial_2.png new file mode 100755 index 000000000..43336c08e Binary files /dev/null and b/docs/source/images/pride_reaction_tutorial_2.png differ diff --git a/docs/source/images/pride_reaction_tutorial_3.png b/docs/source/images/pride_reaction_tutorial_3.png new file mode 100755 index 000000000..26b37e3a5 Binary files /dev/null and b/docs/source/images/pride_reaction_tutorial_3.png differ diff --git a/docs/source/images/pride_reaction_tutorial_4.png b/docs/source/images/pride_reaction_tutorial_4.png new file mode 100755 index 000000000..2d3ddf902 Binary files /dev/null and b/docs/source/images/pride_reaction_tutorial_4.png differ diff --git a/docs/source/images/pride_reaction_tutorial_5.png b/docs/source/images/pride_reaction_tutorial_5.png new file mode 100755 index 000000000..991de139c Binary files /dev/null and b/docs/source/images/pride_reaction_tutorial_5.png differ diff --git a/docs/source/images/pride_reaction_tutorial_6.png b/docs/source/images/pride_reaction_tutorial_6.png new file mode 100755 index 000000000..fc580b809 Binary files /dev/null and b/docs/source/images/pride_reaction_tutorial_6.png differ diff --git a/docs/source/images/pride_reaction_tutorial_7.png b/docs/source/images/pride_reaction_tutorial_7.png new file mode 100755 index 000000000..467c0c9c8 Binary files /dev/null and b/docs/source/images/pride_reaction_tutorial_7.png differ diff --git a/docs/themes/coral/source/css/talk.scss b/docs/themes/coral/source/css/talk.scss index 06ddf7557..391337876 100644 --- a/docs/themes/coral/source/css/talk.scss +++ b/docs/themes/coral/source/css/talk.scss @@ -15,6 +15,9 @@ body { } } +img { + max-width: 800px; +} #graphql-docs { & > div > div { @@ -444,4 +447,4 @@ a.brand { .plugin { display: none; -} \ No newline at end of file +}