-
Notifications
You must be signed in to change notification settings - Fork 43
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How to create a Elm package #126
Merged
Merged
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,238 @@ | ||
# What | ||
|
||
A step by step guide on how to create a dropdown/filters | ||
Elm package that you can reuse on your projects. | ||
|
||
This Readme will not only show you how to use the package on your project but | ||
also how it is created and published. | ||
The process of writing this step by step guide | ||
is to force us to question the choices we make for the package's implementation | ||
and to discover some new questions problems that we will need to answer. | ||
This will help us to improve and enhance the package and | ||
hopefully motivate also others to contribute to the project. | ||
|
||
This document aims to reflect and describe as we implement the package our thoughts, ideas and solutions. **It is our learning process for creating a useful and reusable Elm package.** | ||
|
||
# Requirements | ||
|
||
**Before creating a new elm package, it's a good idea to read the | ||
[design guidlines](https://package.elm-lang.org/help/design-guidelines) documentation:** | ||
|
||
> Design for a concrete use case | ||
|
||
This is the research phase and it should help you to answer why | ||
we want to create a package. In our case we want to be able to | ||
have a list of selectable filters on multiple levels. | ||
[Searching](https://package.elm-lang.org/) for similar existing packages with the terms | ||
`filter` or `dropdown` wasn't very fruitful. | ||
This research step is in progress and creating this guide is also a way to learn | ||
how to create a useful elm package. | ||
|
||
> Avoid gratuitous abstraction | ||
|
||
We'll try to avoid abstracting too soon and we'll try to keep the API simple and useful. | ||
For example the first version of the package limit the level of filters to 2 | ||
instead of having an unlimited number of levels. This will simplify the API and UI. | ||
|
||
> Write helpful documentation with examples | ||
|
||
Read https://package.elm-lang.org/help/documentation-format. | ||
The second link in this section (https://package.elm-lang.org/help/docs-preview) returns 404 | ||
and will be removed (see https://discourse.elm-lang.org/t/docs-preview-404s/1687) | ||
|
||
The last three sections of the guidelines | ||
(`The data structure is always the last argument`, `Keep tags and record constructors secret` and `Naming`) | ||
are a bit more technical and we will come back to them when we start building our package | ||
|
||
This guide help us to define our requirements for the package: | ||
|
||
![filters](https://user-images.githubusercontent.com/6057298/49383414-9409ae80-f710-11e8-9a00-e092bbf4ad29.png) | ||
|
||
- Open/Close filter when click on the pill | ||
- Close filter when click outside of the dropdown/filters. | ||
This allow to use multiple dropdown but only have one open at a time (similar to normal dropdown) | ||
- Allow to select first and second level filters | ||
- The module must be responsive. On mobile the filter will take the full screen | ||
- When a filter is selected it is underlined | ||
- The user must be able to use the dropdown/filter with the keyboard | ||
- A first level filter can be open/close if it contains sub filters | ||
- The user can reset the filters by clicking on the "x" button | ||
|
||
|
||
# How | ||
|
||
1. Create the package (ie create **documentation**, test and code) | ||
2. Publish the package | ||
3. Use the package | ||
|
||
## Minimum setup | ||
|
||
### Initialise the package | ||
|
||
**Elm is using Github to name and publish packages. So the first step when creating a new package is to create a new Github repository.** | ||
|
||
Then we can initialise our new Elm package with the command `elm init`, this will create a `src` folder and a `elm.json` file. | ||
This json file contains the description of our new project (name, author, version, dependencies,...). See https://elm-lang.org/0.19.0/init | ||
to get more informaiton on how to create a new project. | ||
|
||
As we are creating a package that will be `import`ed in other projects and not a full application in itself, we need first to update the `elm.json` | ||
|
||
> The [elm/compiler](https://github.com/elm/compiler) repository contains the documentation for the two types of elm.json file (application and pakage), see https://github.com/elm/compiler/tree/master/docs/elm.json | ||
|
||
|
||
So the `elm.json` file should have the following format: | ||
```json | ||
{ | ||
"type": "package", | ||
"name": "dwyl/elm-criteria", | ||
"summary": "Dropdown with selectable filters", | ||
"license": "BSD-3-Clause", | ||
"version": "1.0.0", | ||
"exposed-modules": [], | ||
"elm-version": "0.19.0 <= v < 0.20.0", | ||
"dependencies": { | ||
"elm/core": "1.0.0 <= v < 2.0.0" | ||
}, | ||
"test-dependencies": {} | ||
} | ||
``` | ||
|
||
- Make sure the `type` value is `package` | ||
- update the name of your package. The name must be the name of the repository of the package | ||
- `exposed-modules` will contain the modules' name that will exposed the API of the package | ||
- `dependencies` is the list of dependencies necessary for the package. This will be updated if necessary when a new package is installed with `elm install` | ||
|
||
We will have a few more things to add later on to be able to publish our package (e.g license file, tests, documentation) but firt to be able to play with the code will come with we first create an example folder. | ||
|
||
### Create the example folder | ||
|
||
run `mkdir examples` to create the folder. | ||
|
||
The `example` folder will allow us to test rapidly our implementation and ideas and make sure the code for the package is working correctly. At the end of the development process it will also be small project example that our users can take ideas from. | ||
|
||
Create the elm example application: `cd examples && elm init`. This time the `type`'s' value in the generated `elm.json` doesn't need to be modified as it's already an application. Instead of using the `src` folder we can use the current folder of the application, change the value ` "source-directories": ["src"]` to `"source-directories": ["."]`. It makes a bit easier to see the list of examples. | ||
|
||
|
||
Let's create a simple (dummy for now) Elm app in `examples/Example.elm`: | ||
|
||
```elm | ||
import Browser | ||
import Html exposing (Html, button, div, h1, text) | ||
|
||
|
||
main = | ||
Browser.sandbox { init = init, update = update, view = view } | ||
|
||
|
||
-- MODEL | ||
|
||
type alias Model = String | ||
|
||
init : Model | ||
init = | ||
"Hello" | ||
|
||
|
||
-- UPDATE | ||
|
||
type Msg = None | ||
|
||
update : Msg -> Model -> Model | ||
update msg model = | ||
case msg of | ||
None -> | ||
model | ||
|
||
-- VIEW | ||
|
||
view : Model -> Html Msg | ||
view model = | ||
div [] [ h1 [] [text model] ] | ||
|
||
``` | ||
|
||
Still in the `examples` folder run `elm reactor` and open the application on `http://localhost:8000/Example.elm`, you should see `Hello` | ||
|
||
|
||
### Create the package | ||
|
||
Now that we have the `Example.elm` file we can start creating our package and try to plug it and use it with our new example application. | ||
|
||
Create a file `Criteria.elm` which will have for API only a `view` function (let's try to keep things simple while we make sure that everything is initialised and plugged together) | ||
|
||
```elm | ||
module Criteria exposing (view) | ||
import Html exposing (Html, button, div, h1, text) | ||
|
||
view : Html msg | ||
view = | ||
div [] [ h1 [] [text "text from package"] ] | ||
``` | ||
|
||
As our package is not yet published, to allow our example to use it we need to update the `elm.json` file to expose our new module: | ||
|
||
```json | ||
"source-directories": [ | ||
".", | ||
"../src" | ||
], | ||
``` | ||
|
||
Then in our `Example.elm` file we can use the `view` function by importing the package then using the function in the example's view | ||
|
||
```elm | ||
... | ||
import Criteria | ||
... | ||
view : Model -> Html Msg | ||
view model = | ||
div [] | ||
[ h1 [] [text model] | ||
, Criteria.view | ||
] | ||
``` | ||
|
||
We have now a minimum version of a package (not published yet) and an example using it! We can start to think what are the next steps to be able to publish our first version! | ||
|
||
# First version | ||
|
||
From our requirement list above we can try to trim down the features and make a first basic version with the following elements | ||
|
||
- a button/pillbox with a default text to open/close the filters | ||
- 1 level of filters (instead of 2) | ||
- a filter consist of a displayed text, a value linked to this text (similar to normal dropdown) and a filter can be selected or not. | ||
- The package will accept a list of text/value filters | ||
|
||
## Reusable? | ||
|
||
Our main concern when creating the package is to make it **valuable** for our users and one way to do this is for the package to be **reusable** on different kind of projects. | ||
|
||
When we think about reusable code we'll most often think about components. However Elm is a functional programming language and is not really designed for creating components. To try to understand this you can read/watch the following links | ||
|
||
- https://twitter.com/czaplic/status/903266544544878592 (components are objects) | ||
- https://www.reddit.com/r/elm/comments/6x5w07/components_in_elm_why_not_and_how/ | ||
|
||
- https://www.youtube.com/watch?v=DoA4Txr4GUs | ||
|
||
- https://www.reddit.com/r/elm/comments/5oi8jx/creating_a_simple_reusable_view_module_in_elm/ | ||
|
||
- https://medium.com/@mickey.vip/an-approach-to-nested-reusable-view-functions-in-elm-a1531b9abaf3 | ||
|
||
- https://discourse.elm-lang.org/t/api-design-reusable-modules/2623 | ||
|
||
- https://github.com/evancz/elm-sortable-table | ||
|
||
So instead of creating components we should focus on creating reusable view. | ||
**Views are functions and Elm uses function composition to build applications.** | ||
|
||
Some concepts to keep in mind (extract from the above links) while designing the API of the package: | ||
|
||
- build reusable view not components (elm is a Functional Programming language not an Object-Oriented programming language) | ||
- Keep the API simple and short | ||
- Try to keep the types signature simple | ||
- Single source of truth for the data to avoid synchronisation issues | ||
- Avoid parent-child communication - don't build tree of objects/components | ||
|
||
Now that we have a better idea of resusable code with Elm, let's start to design our API | ||
|
||
## Designing the API |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
{ | ||
"type": "application", | ||
"source-directories": [ | ||
"src" | ||
], | ||
"elm-version": "0.19.0", | ||
"dependencies": { | ||
"direct": { | ||
"elm/browser": "1.0.1", | ||
"elm/core": "1.0.2", | ||
"elm/html": "1.0.0" | ||
}, | ||
"indirect": { | ||
"elm/json": "1.1.2", | ||
"elm/time": "1.0.0", | ||
"elm/url": "1.0.0", | ||
"elm/virtual-dom": "1.0.2" | ||
} | ||
}, | ||
"test-dependencies": { | ||
"direct": {}, | ||
"indirect": {} | ||
} | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider moving this file to
/tutorials/elm-package/README.md
as the/examples
is more for basic code examples whereas/tutorials
appears to be for more long-form step-by-step walk through.