Skip to content
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

Adds gov-uk custom filters lib file #171

Closed
wants to merge 13 commits into from
Closed

Adds gov-uk custom filters lib file #171

wants to merge 13 commits into from

Conversation

paulmsmith
Copy link
Contributor

Adds custom nunjucks filters lib file and automatically registers them with Nunjucks. Adds filter for outputting a standard UK date as per the gov.uk guidance (with scope for adding more features via the moment.js api).

In templates using this filter you could do: {{ '09/12/1981' | formatDate }} and that would output 9 December 1981

This PR is essentially the same as one I did earlier but perhaps addresses issues like: #170 - Create filter / macro for outputting dates and allows some future extensibility.

The name of the 'standardDate' filter I've created could easily be changed to something more name-spaced or understandable.

It's also possible to leverage all the API from moment.js (Moment.js formatting docs) should we need to. As a quick test I've made it should you can pass in a format string for example:

{{ '09/12/1981' | formatDate('ll') }} outputs 9 Dec 1981 for those cases where space is limited.

Also included in this PR is the ability to 'log' template/context data to the browser. Makes it quick and easily to debug things and see what the output of a filter such as 'standardDate' is, etc. For example:

{{ '09/12/1981' | formatDate | log | safe }} 

http://studio.airfront.co.uk/directory/temp/grabs/2016_04_14__uunei.png

Discussed a little in this issue with @tsmorgan.

Adds custom nunjucks filters file and automattically registers them with Nunjucks.

Add filter for outputting a standard UK date as per the gov.uk guidance.
@joelanman
Copy link
Contributor

how about formatDate?

@paulmsmith
Copy link
Contributor Author

That's much better. Done.

@edwardhorsford
Copy link
Contributor

Thanks for working on this @paulmsmith.

Open question - we're trying to get to a point where everything a regular user might want to touch is in /app and all the kit stuff is outside. Does this imply two filter files? one in app and one in lib? Or do we not expect users to make filters? (cc @timpaul @joelanman ).

I wonder if a few of the variables should be spelt out more explicitly: would help for readability where more novice users are seeing how things are done. eg filter rather than fi.

For the date formatting:

  • Is dd/mm/yyyy the common format? For my use case on wednesday I had a javascript date as it was dynamically generated - would this work?

@joelanman
Copy link
Contributor

I think it does imply two - it makes updating easier if we keep /app as vanilla as possible. A blank app/filters file might be good

@paulmsmith
Copy link
Contributor Author

paulmsmith commented Apr 15, 2016

No problem. Good points.

I think @joelanman is right. It implies two. A 'core' set of generic standardised filters such as the 'formatDate' example and then a blank file for users to create their own with some commented or documented guidance for how they can do so.

As for date formatting, I'm I can make it so that it will accept a range of date styles in accordance with the moment.js api to allow people to use what they have. (A javascript date being one of them).

I'm quite busy for the next few days but as I see it, I can do the following:

  • Separate the filters to a 'core' + 'instance specific' set of files. This will need to encompass some feature to ensure users don't run into method naming collisions, etc.
  • Update the basic example of the formatDate filter to allow a range of common date patterns to be converted to the agreed GOV.UK standard.
  • Make the JS a little more friendly for less experienced developers/kit users

How does that sound? Incidentally, I'm at the XGov Design meeting in a week's time, so be good to finally say Hi. :)

@edwardhorsford
Copy link
Contributor

What is the use case for toHyphenated and toCamelCase?

@joelanman
Copy link
Contributor

what's the use case for newDate in a template, and can you call a filter with no input?

@paulmsmith
Copy link
Contributor Author

paulmsmith commented Apr 15, 2016

I write my filters functionally so my thinking was they could be used in templates as well as to compose other filters. toHypthenated, toCamelCase are operations I seem to do a lot when dealing with JSON data within templates so they are useful as filters. The same goes for newDate I exposed that as a filter because it might have been useful.

@paulmsmith
Copy link
Contributor Author

@joelanman Off the top of my head. I don't believe you can call a filter in templates without passing it something.

I think without passing something you'd have to create an 'extension' (some people refer to them as 'helpers'): see Nunjucks custom tags

*/
fi.newDate = function date(d) {
var dateArr = d.split('/');
return dateArr.length === 3 ? new Date(dateArr[2], parseInt(dateArr[1]) - 1, dateArr[0]) : NaN;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is one parseInt and the others aren't?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was just ensuring int for the calculation. ;)

@joelanman
Copy link
Contributor

Ah sorry I misread newDate and thought it didn't take input. I wonder if toDate might be clearer? I'm not totally sure about putting low level filters like this one in the templates. If you need to do complex data manipulation I think it'd be easier and more flexible in a route

* @param {Any} a any type
* @return {String} a script tag with a console.log call.
* @example {{ "hello world" | log }}
* @example {{ "hello world" | log | safe }} [for environments with autoescaping turned on]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you turned auto escaping off? Do you think that might be a better default for the kit? Otherwise log will always have to be followed by safe, is it possible to just call log and incorporate the safe behaviour?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd say auto-escaping should stay and I'll make it escape the log by default as you suggest.

Copy link
Contributor Author

@paulmsmith paulmsmith Apr 15, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. That might be more tricky than first thought actually. I personally don't mind always piping to safe. I'll investigate further. I think turning off auto escaping might cause issues for people that are outputting HTML examples in their kits?

Copy link
Contributor Author

@paulmsmith paulmsmith Apr 15, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1. disable auto-escaping by default.

This will help us when it comes to filters because it appears there is no way to get around auto-escaping, it will always get escaped on the way out.

If we did this then anybody wishing to escape content could either use the Nunjucks filters inline or on a block of content.

Inline on a string
{{ '<h1>Escape this</h1>' | escape }} => &lt;h1&gt;Escape this&lt;/h1&gt;

On a chunk of content/mark-up

{% set sampleHTMLChunk %}
<div>
<h1>This HTML will be escaped</h1>
</div>
{% endset %}

{{ sampleHTMLChunk | escape }}

Which would produce

&lt;div&gt;
&lt;h1&gt;This HTML will be escaped&lt;/h1&gt;
&lt;/div&gt;

2. Keep auto-escaping

We keep auto-escaping as-is and use safe in the same way we'd have used escape above.

Copy link
Contributor

@glenjamin glenjamin May 17, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Filters can call other filters on the JS side, so you can encorporate safe into the filter definition.

Here's an example from another project

    var nunjucksSafe = env.getFilter('safe');
    env.addFilter('jsonAttr', function(data, attr) {
        var value = JSON.stringify(data).replace(/\'/g, '&apos;');
        return nunjucksSafe(attr + "='" + value + "'");
    });

I would highly recommend leaving auto-escaping on by default.

Copy link
Contributor Author

@paulmsmith paulmsmith May 17, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh yeh! I'll give that a try. Awesome @glenjamin :) - Worth noting that some of the double escaping stuff has been fixed in newer releases of nunjucks, so that should stop the need for some of the other workarounds that might have been needed.

@paulmsmith
Copy link
Contributor Author

Fixed conflicts with latest from master.

@joelanman
Copy link
Contributor

This is great work, and we do want to include filters in the kit, even if it's just to show how to use them so people could add their own. However I'm not sure we know that all the filters included in lib/filters have a general user need. log looks like it could be generally useful, but I'm not sure on the others?

* gov.uk core filters by creating filter methods of the same name.
* @type {Object}
*/
var filter = {};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be filters, since it contains multiple filters?

Copy link
Contributor Author

@paulmsmith paulmsmith May 17, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@joelanman I debated that one. You're probably right. Just felt easier to read as the singular when composing them but that could just be me.

@paulmsmith
Copy link
Contributor Author

@joelanman I can give some usecases for the others, very quickly:

toHyphenated:

You want to dynamically create a class, slug or string from post data being played back to the user?

toCamelCase:

You want to create an ID on headings/content based on dynamic or structured data.

I name them as what they do functionally but we could expose them via aliases that make more sense to users perhaps? E.g:

filter.toCamelCase = filter.createDynamicId = function toCamelCase(b, md) {
...
}

Paul Smith added 5 commits May 17, 2016 16:13
@paulmsmith
Copy link
Contributor Author

To recap some private slack talk. I've stripped this back a little. Removed dependancy on lodash and given the ability for filters to get access to some nunjucks API methods such as 'getFilter', etc: (https://mozilla.github.io/nunjucks/api.html#getfilter)

@paulmsmith
Copy link
Contributor Author

paulmsmith commented May 20, 2016

@joelanman Does this look like it can be merged now?

@paulmsmith
Copy link
Contributor Author

Closing this PR in favour of a new one with clean commits and easier audit trail. Could rebase but probably nicer that just the details necessary are in the PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants