Coder Social home page Coder Social logo

tehnix / playground-spa-elm Goto Github PK

View Code? Open in Web Editor NEW
3.0 3.0 0.0 1.05 MB

Playing around with Elm SPAs and structuring them for larger scale applications

License: BSD 3-Clause "New" or "Revised" License

HTML 0.10% Elm 99.76% JavaScript 0.12% SCSS 0.02%
elm

playground-spa-elm's Introduction

Elm SPA Playground

Playing around with Elm SPAs and structuring them for larger scale applications.

Getting Started

Everything is set up in package.json, meaning you just have to run,

$ npm i
  • npm run dev starts a local development server with hot reloading
  • npm run build builds a production optimized file in dist/elm.min.js with a sourcemap
  • npm run test:verify runs the Elm doc/examples tests
  • npm run test and npm run test:watch runs unit tests, with the latter running in watch mode

Structure

A brief overview of the structure inside the src/ folder:

  • Page is where individual pages live
  • Layout is where the document/page layout is controlled
  • Application is where we stitch together all the page views, updates and routes
  • Helper are for convenient helper functions
  • Core should usually not be changed, and is responsible for stitching together everything behind the scenes

Environment Variables

To pass environmental variables to Elm, we do this via a combination of Elm init flags and a pre-processor script for building the index.html file.

This preprocessor simple runs a replacement of process.env... variables in the original index.unprocessed.html, and replaces them with actual values. Validation is performed to ensure no leftover process.env are present after procesing the index file.

The environmental variables that populate process.env both come from the environment, but also from a .env / .env.development file (.env is only when process.env.STAGE is production/staging).

Finally, now that we have substituted our process.env with actual values, they are then passed into Elm via Elm.Main.init({ flags: ... }).

Adding a new environmental variable to be passed to Elm necessitates the following:

  • Add a replacement target/value in index-pre-process.js
  • Set up the variable that takes the target, in index.unprocessed.html, and pass it to the Elm init
  • Make sure the new env variable is either in your environment or in .env / .env.development
  • Handle decoding the flag in Elm, inside src/Application/Config.elm

Authentication

The applications assumes that the access token lives in a cookie accessToken (specified in index.unprocssed.html), and that the API needs this passed to it via a header value as Authorization: Bearer xxx-xx-xxx-xxxx.

Upon receiving a HTTP 401 Unauthorized, the application will redirect the user to the authentication flow. This is controlled by two variables:

  • AUTH_URL specifying the URL of the auth service
  • AUTH_CLIENT_ID specifying the OAuth2 client id

Since this is an SPA, authentication is assumed to be handled elsewhere (e.g. a server/lambda function). For an OAuth2 flow, you'll need a server that has the client secret and can exchange the access code to an access token, upon being redirected back from the authentication service.

I18n Support

Translations are handled via the elm-i18next package. Translations are either dynamically fetched at app initialization and language change, or hardcoded into the application.

The current selected language is extracted first from a i18next cookie, and secondly from the browser language, falling back to a default of en-US. This is passed into Elm via init flags.

We assume that the translations are generated by locize, so we make our test structure in assets conform to this. A brief overview of the important resources:

  • Fetching namespace resource (translations): https://api.locize.io/{projectId}/{version}/{language}/{namespace} ({version} can be lastest)
  • Fetching available languages: https://api.locize.io/languages/{projectId}

NOTE: We don't supported namespaces, since we don't want to dynamically build up necessary translations to fetch. It is assume that everything lives in the shared namespace (can be changes in src/Application/I18n/Locize.elm).

We'll need a project id though, which we'll pass to Elm via init flags, along with a translation endpoint, to have more control over that. The environment variables are:

  • I18N_URL pointing to your endpoint for translations (e.g. https://api.locize.io or /assets for development)
  • I18N_PROJECT with the locize project id (we use i18n for development, so that the path fits)

GraphQL

We use Elm GraphQL for making type-safe GraphQL calls in Elm. This works by generating all the code necessary for Elm, by introspecting your schema.

Generate the Elm code from your schema via:

$ GITHUB_TOKEN=xxxxxxxxxxxxxxxxxx npm run codegen:api

And the code will be put at src/Api/GitHub/.

If you haven't set up a GITHUB_TOKEN yet, you can easily do that, by following Authenticating with the GITHUB_TOKEN.

Material-UI

We use the material-components-web-elm, which integrates quite smoothly with Elm, and is actively developed. To figure out how to use the components, please refer to the library documentation.

Theming is supported via theme.scss, which generates a theme file that overwrites the default colors of the Material Design CSS file we pull in, in assets/css. You can build the sass with npm run build:theme (it's a part of the build step also).

Unit Tests

We use elm-test to run our unit tests, in the tests/ folder.

Run npx elm-test to run the tests-

Verified Examples in Documentation

We use elm-verify-examples to ensure that code in documentation is kept up-to-date. This is similar to tools liek doc-tests in other languages.

A brief example, that'll be validated.

1+1 --> 2

Running npm run test:verify (or npx elm-verify-examples --run-tests) will both generate and validate these docs. Make sure you have added the fils to check in tests/elm-verify-examples.json.

CI

We use Github actions to build the project, run tests, and run doc tests in the CI pipeline. Check out the workflows in .github/workflows/ to see the specific workflows that are set up.

Resources

Tools:

Testing:

Some base library functions that one should be familiar with:

playground-spa-elm's People

Contributors

tehnix avatar dependabot[bot] avatar

Stargazers

Rune Carlsen avatar Karl-Oskar Rikås avatar Tim Kersey avatar

Watchers

Rune Carlsen avatar James Cloos avatar  avatar

playground-spa-elm's Issues

Adding a GraphQL client

GraphQL is the hot-stuff nowadays, so we want a couple of examples of how to call a Query and Mutation in GraphQL in Elm.

There's a couple of options here dillonkearns/elm-graphql, which seem the most promising.

Passing environment variables to Elm

We will inevitably need to pass down environment variables or secrets to the Elm app. If we build these into the app at build time, we can still serve everything from a CDN/S3/static file.

There are some approaches described in:

Most seem to land on passing them to Elm via init flags, and then having a step that'll convert the values that comes from JS into the actual value needed.

i18n support

To be fully fledged, i18n support is necessary. Applications often need to be in multiple languages.

Current plan is to integrate with https://github.com/ChristophP/elm-i18next and pass translations via init flags, something like,

Elm.Main.init({ flags: translations });

The original idea was to have the flow of requests/loading look something like:

  1. index.html is loaded, this starts a process that runs in parallel:
    • our Elm app script download is initiated
    • we kick off a download for our translations for the selected language (cookie first, browser lang secondly)
  2. index.html shows a initializing/loading screen until both are loaded
    • this means we hold off on Elm.Main.init until we have our translations
  3. Finally, the Elm app can initialize

But, since we need to be able to change the language from within the app, and we also would need to have both loading implemented in index.html and in the app, it's been decided that the Elm app will be the one to fetch the translations, although the languages will still come in via flags.

This avoids code duplication for the fetching logic and loading indicators, and still allows you to have flexibility in whether to hardcode the supported languages or fetch them in index.html.

The flow will instead be:

  1. index.html is loaded with a background color that matches the Elm loading screen, and starts a process that runs in parallel:
    • our Elm app script download is initiated
    • we have the opportunity to download the supported languages, or simply hardcode the options
  2. The Elm app initializes
  3. Translations are fetched while showing a loading indicator

This also allows us to speed up loading a /me endpoint, which can result in a redirect to authentication, if we get an unauthorized.

Add a UI library like Material-UI

The days of creating your own UI library is past. We want to reuse existing work, such as material-ui or bootstrap.

For this project specifically, we will take a look at material-ui, making our components using that. The most promising library for that looks to be elm-mdc.

In the end we went with material-components-web-elm which seems to be what the author wants to move on to, and is also actually published on elm-packages.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.