@data-visuals/create 7.2.0 → 7.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +55 -32
- package/package.json +1 -1
- package/templates/__common__/_package.json +4 -4
- package/templates/{graphic → __common__}/app/scripts/embeds/frames.js +0 -0
- package/templates/__common__/app/scripts/utils/clamp.js +10 -0
- package/templates/__common__/app/scripts/utils/get-current-url.js +5 -0
- package/templates/__common__/app/scripts/utils/serialize-query-string.js +1 -1
- package/templates/__common__/app/scripts/utils/sum-values.js +7 -0
- package/templates/__common__/app/templates/macros/prose.html +2 -2
- package/templates/__common__/config/tasks/graphics-meta.js +10 -2
- package/templates/__common__/project-metadata.md +186 -0
- package/templates/__common__/utils/deployment/deploy.js +0 -27
- package/templates/__common__/utils/fetch/authorize.js +8 -4
- package/templates/feature/README.md +15 -7
- package/templates/feature/app/scripts/packs/graphic-embed.js +31 -0
- package/templates/feature/app/scripts/packs/main-embed.js +7 -0
- package/templates/feature/app/scripts/utils/ad-loader.js +7 -3
- package/templates/feature/app/templates/{base-graphic.html → base-embed.html} +3 -2
- package/templates/feature/app/templates/{graphic.html → embed.html} +6 -6
- package/templates/feature/app/templates/includes/metas.html +1 -1
- package/templates/feature/app/templates/macros/processors.html +6 -2
- package/templates/graphic/app/templates/base.html +3 -2
- package/templates/graphic/app/templates/graphic-static.html +4 -4
- package/templates/graphic/app/templates/graphic.html +4 -4
- package/templates/graphic/graphics-meta.md +0 -120
package/README.md
CHANGED
|
@@ -36,16 +36,16 @@ npm start
|
|
|
36
36
|
- [Usage](#usage)
|
|
37
37
|
- [Development and testing](#development-and-testing)
|
|
38
38
|
- [Folder structure](#folder-structure)
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
39
|
+
- [config/](#config)
|
|
40
|
+
- [data/](#data)
|
|
41
|
+
- [workspace/](#workspace)
|
|
42
|
+
- [project.config.js](#projectconfigjs)
|
|
43
|
+
- [app/](#app)
|
|
44
|
+
- [app/index.html, app/static.html](#appindexhtml-appstatichtml)
|
|
45
|
+
- [app/templates/](#apptemplates)
|
|
46
|
+
- [app/scripts/](#appscripts)
|
|
47
|
+
- [app/styles/](#appstyles)
|
|
48
|
+
- [app/assets/](#appassets)
|
|
49
49
|
- [Other directories you may see](#other-directories-you-may-see)
|
|
50
50
|
- [.tmp/](#tmp)
|
|
51
51
|
- [dist/](#dist)
|
|
@@ -57,13 +57,13 @@ npm start
|
|
|
57
57
|
- [Creating a new entrypoint](#creating-a-new-entrypoint)
|
|
58
58
|
- [Connecting an entrypoint to an HTML file](#connecting-an-entrypoint-to-an-html-file)
|
|
59
59
|
- [Available commands](#available-commands)
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
60
|
+
- [`npm start` or `npm run serve`](#npm-start-or-npm-run-serve)
|
|
61
|
+
- [`npm run deploy`](#npm-run-deploy)
|
|
62
|
+
- [`npm run data:fetch`](#npm-run-datafetch)
|
|
63
|
+
- [`npm run assets:push`](#npm-run-assetspush)
|
|
64
|
+
- [`npm run assets:pull`](#npm-run-assetspull)
|
|
65
|
+
- [`npm run workspace:push`](#npm-run-workspacepush)
|
|
66
|
+
- [`npm run workspace:pull`](#npm-run-workspacepull)
|
|
67
67
|
- [Environment variables and authentication](#environment-variables-and-authentication)
|
|
68
68
|
- [AWS](#aws)
|
|
69
69
|
- [Google](#google)
|
|
@@ -87,13 +87,18 @@ npm install -g @data-visuals/create
|
|
|
87
87
|
npx @data-visuals/create@latest <project-type> <project-name>
|
|
88
88
|
```
|
|
89
89
|
|
|
90
|
-
Currently there are two project types available — `graphic` and `feature`.
|
|
90
|
+
Currently there are two project types available — `graphic` and `feature`.
|
|
91
|
+
|
|
92
|
+
`graphic` - embeddable graphics, like the [district race lookup embedded in this voter guide](https://www.texastribune.org/2022/01/17/texas-primary-election-2022-voter-guide/)
|
|
93
|
+
`features` - entire page projects, like this [2022 primary ballot page](https://apps.texastribune.org/features/2022/texas-election-results-2022-primary/)
|
|
94
|
+
|
|
95
|
+
The project name should be passed in as a slug, i.e. `my-beautiful-project`.
|
|
91
96
|
|
|
92
97
|
```sh
|
|
93
98
|
npx @data-visuals/create@latest graphic school-funding
|
|
94
99
|
```
|
|
95
100
|
|
|
96
|
-
This will create a directory for you, copy in the files, install the dependencies
|
|
101
|
+
This will create a directory for you, copy in the files, install the dependencies and do your first `git commit`.
|
|
97
102
|
|
|
98
103
|
The directory name will be formatted like this:
|
|
99
104
|
|
|
@@ -106,10 +111,6 @@ graphic-school-funding-2018-01
|
|
|
106
111
|
|
|
107
112
|
This is to ensure consistent naming of our directories!
|
|
108
113
|
|
|
109
|
-
## Development and testing
|
|
110
|
-
|
|
111
|
-
If you make changes locally to `@data-visuals/create` and want to test them, you can run `data-visuals-create/bin/data-visuals-create <project-type> <project-name>` to generate a graphic or feature and see if your changes were included. Run the command one level above this repo, or you'll create a graphic or feature within `data-visuals-create`.
|
|
112
|
-
|
|
113
114
|
## Folder structure
|
|
114
115
|
|
|
115
116
|
After creation, your project directory should look something like this:
|
|
@@ -147,23 +148,37 @@ The `workspace` directory is for storing all of your analysis, production and ra
|
|
|
147
148
|
|
|
148
149
|
#### project.config.js
|
|
149
150
|
|
|
150
|
-
Where all the configuration for a project belongs. This is where you can change the S3 deploy parameters, manage the Google Drive documents that sync with this project, set up a bespoke API or add custom filters to Nunjucks.
|
|
151
|
+
Where all the configuration for a project belongs. This is where you can change the S3 deploy parameters, manage the Google Drive documents that sync with this project, format data pulled from Google Drive documents, set up a bespoke API or add custom filters to Nunjucks.
|
|
152
|
+
|
|
153
|
+
- `dataMutators` - Modify what's returned by the data fetch. This is a good place to restructure raw data, or to do joins with other data you may have. [Here's an example from our coronavirus tracker.](https://github.com/texastribune/feature-tx-coronavirus-tracker-2020-03/blob/master/project.config.js#L188)
|
|
154
|
+
- `createAPI` - Bake out a series of JSON files that get deployed with your project. This is a great way to partition data that users only need a small slice of based on how they interact with our project. The kit expects this to return an array of objects. Each object should have a "key" and a "value" - the "key" determines the URL, the "value" is what is saved at that URL. [Here's an example from our voter guide.](https://github.com/texastribune/newsapps-dailies/blob/master/2022/graphic-voter-guide-primaries-2022-01/project.config.js#L136)
|
|
155
|
+
- `customFilters` - Where custom filters for Nunjucks are added. Each key should be the name of the filter, and each value should be a function it will call. (journalize comes built in and does not need to be added manually.) [Here's an example from our voter guide.](https://github.com/texastribune/newsapps-dailies/blob/master/2022/graphic-voter-guide-primaries-2022-01/project.config.js#L405)
|
|
151
156
|
|
|
152
157
|
#### app/
|
|
153
158
|
|
|
154
159
|
Where you'll spend most of your time! Here are where all the assets that go into building your project live.
|
|
155
160
|
|
|
156
|
-
#### app/index.html
|
|
161
|
+
#### app/index.html
|
|
157
162
|
|
|
158
|
-
|
|
163
|
+
This is the landing page for graphics and features. For features, this page provides a full-page template to start from. For embeddable graphics, this page has instructions on how to create embeddable graphics and which templates in `app/templates/` to clone.
|
|
159
164
|
|
|
160
|
-
|
|
165
|
+
#### app/templates/
|
|
161
166
|
|
|
162
|
-
|
|
167
|
+
Where all the Nunjucks templates (including the `base.html` template that `app/index.html` inherits from), `includes` and `macros` live.
|
|
163
168
|
|
|
164
|
-
|
|
169
|
+
### Embeddable graphics
|
|
165
170
|
|
|
166
|
-
|
|
171
|
+
- `base.html` - base template used across all graphics
|
|
172
|
+
- `graphic-static.html` - template for static graphics, like Illustrator embeds
|
|
173
|
+
- `graphic.html` - template for graphics using JS, like ones that require D3
|
|
174
|
+
|
|
175
|
+
### Features
|
|
176
|
+
|
|
177
|
+
- `base.html` - base template used across all features
|
|
178
|
+
- `base-embed.html` - base template used across all embeddable graphics associated with the feature
|
|
179
|
+
- `embed.html` - template for embeddable graphics associated with the feature
|
|
180
|
+
|
|
181
|
+
If your project is only a single page (or graphic), you can pick one of them where you do all your HTML work. No special configuration is required to create new HTML files - just creating a new `.html` file in in the `app` directory (but _not_ within `app/scripts/` or `/app/templates/` - HTML files have special meanings in those directories) is enough to tell the kit about new pages it should compile.
|
|
167
182
|
|
|
168
183
|
#### app/scripts/
|
|
169
184
|
|
|
@@ -231,7 +246,7 @@ ArchieML Google Docs work as documented on the [ArchieML](http://archieml.org/)
|
|
|
231
246
|
|
|
232
247
|
Our kit can display variables pulled in from Google Docs in the template. This is helpful when we want to show data in our text that is in the `data/` folder. Nunjucks finds the variable syntax (anything in curly braces) in our Google Doc text and displays the corresponding value.
|
|
233
248
|
|
|
234
|
-
By default, Nunjucks has access to every file in our `data/` folder as an object. For example, if there are two files in the `data/` folder named `data.json` and `text.json` respectively, it will be structured as:
|
|
249
|
+
By default, Nunjucks has access to every file in our `data/` folder as an object. For example, if there are two files in the `data/` folder named `data.json` and `text.json` respectively, it will be structured as:
|
|
235
250
|
|
|
236
251
|
```json
|
|
237
252
|
{
|
|
@@ -291,7 +306,7 @@ touch app/scripts/packs/maps.js
|
|
|
291
306
|
|
|
292
307
|
Because there's a lot more going on behind the scenes than just adding a `<script>` tag, you have to set a special variable in a template in order to get the right entrypoint into the right HTML file.
|
|
293
308
|
|
|
294
|
-
Set `jsPackName` anywhere in the HTML file to the name of your entrypoint (
|
|
309
|
+
Set `jsPackName` anywhere in the HTML file to the name of your entrypoint (**without** the extension) to route the right JavaScript files to it.
|
|
295
310
|
|
|
296
311
|
```html
|
|
297
312
|
{% set jsPackName = 'map' %}
|
|
@@ -312,6 +327,14 @@ The main command for development. This will build your HTML pages, prepare your
|
|
|
312
327
|
|
|
313
328
|
The main command for deployment. It will always run `npm run build` first to ensure the compiled version is up-to-date. Use this when you want to put your project online. This will use the `bucket` and `folder` values in the `project.config.js` file to determine where it should be deployed on S3. Make sure those are set the appropriate values!
|
|
314
329
|
|
|
330
|
+
#### `npm run build`
|
|
331
|
+
|
|
332
|
+
The main command for compiling files. Stores compiled files in the `dist/` folder. Also runs `npm run parse` which parses project for metadata.
|
|
333
|
+
|
|
334
|
+
#### `npm run parse`
|
|
335
|
+
|
|
336
|
+
The main command for parsing metadata from projects. Refer to `project-metadata.md` for more information.
|
|
337
|
+
|
|
315
338
|
#### `npm run data:fetch`
|
|
316
339
|
|
|
317
340
|
This command uses the array of files listed under the `files` key in `project.config.js` to download data to the project. This data will be processed and made available in the `data` folder in the root of the project.
|
package/package.json
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"workspace:push": "node ./utils/deployment/push-workspace.js"
|
|
18
18
|
},
|
|
19
19
|
"devDependencies": {
|
|
20
|
-
"@babel/core": "^7.
|
|
20
|
+
"@babel/core": "^7.17.8",
|
|
21
21
|
"@babel/plugin-proposal-class-properties": "^7.1.0",
|
|
22
22
|
"@babel/plugin-syntax-dynamic-import": "^7.0.0",
|
|
23
23
|
"@babel/plugin-transform-react-jsx": "^7.0.0",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"ansi-colors": "^3.2.3",
|
|
29
29
|
"archieml": "^0.4.1",
|
|
30
30
|
"assets-webpack-plugin": "^6.1.2",
|
|
31
|
-
"autoprefixer": "^
|
|
31
|
+
"autoprefixer": "^10.4.2",
|
|
32
32
|
"aws-sdk": "^2.372.0",
|
|
33
33
|
"babel-eslint": "^10.0.1",
|
|
34
34
|
"babel-loader": "^8.0.0",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"fast-glob": "^2.2.2",
|
|
51
51
|
"fs-extra": "^7.0.0",
|
|
52
52
|
"glob-watcher": "^5.0.3",
|
|
53
|
-
"googleapis": "^
|
|
53
|
+
"googleapis": "^99.0.0",
|
|
54
54
|
"html-minifier": "^3.5.21",
|
|
55
55
|
"imagemin": "^7.0.0",
|
|
56
56
|
"imagemin-gifsicle": "^7.0.0",
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"journalize": "^2.1.0",
|
|
61
61
|
"mime-types": "^2.1.17",
|
|
62
62
|
"nunjucks": "^3.0.1",
|
|
63
|
-
"postcss": "^
|
|
63
|
+
"postcss": "^8.4.12",
|
|
64
64
|
"postcss-flexbugs-fixes": "^4.1.0",
|
|
65
65
|
"prettier": "^1.12.1",
|
|
66
66
|
"puppeteer": "^5.5.0",
|
|
File without changes
|
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns the minimum if n is less than the minimum.
|
|
3
|
+
* Returns the maximum if n is more than the maximum.
|
|
4
|
+
* Returns n if n is between the minimum and maximum.
|
|
5
|
+
*
|
|
6
|
+
* @param {Number} n
|
|
7
|
+
* @param {Number} min
|
|
8
|
+
* @param {Number} max
|
|
9
|
+
* @return {Number}
|
|
10
|
+
*/
|
|
1
11
|
export default function clamp(n, min, max) {
|
|
2
12
|
return Math.min(Math.max(n, min), max);
|
|
3
13
|
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
const encode = encodeURIComponent;
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
* Converts an object of key/value pairs into
|
|
10
|
+
* Converts an object of key/value pairs into an encoded query string.
|
|
11
11
|
*
|
|
12
12
|
* @param {Object} params The params of the query string
|
|
13
13
|
* @return {String}
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sums up values of a property across objects in an array.
|
|
3
|
+
*
|
|
4
|
+
* @param {Array} arr An array of objects.
|
|
5
|
+
* @param {String} key The property to sum across objects.
|
|
6
|
+
* @return {Number}
|
|
7
|
+
*/
|
|
1
8
|
export default function sumValues(arr, key) {
|
|
2
9
|
return arr.reduce((acc, obj) => acc + obj[key], 0);
|
|
3
10
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{% import 'macros/processors.html' as processors %}
|
|
2
2
|
|
|
3
|
-
{% macro prose(content, context, data) %}
|
|
3
|
+
{% macro prose(content, context, data, type) %}
|
|
4
4
|
{% for item in content %}
|
|
5
5
|
{% if processors[item.type] %}
|
|
6
|
-
{{ processors[item.type](item.value, context, data) }}
|
|
6
|
+
{{ processors[item.type](item.value, context, data, type) }}
|
|
7
7
|
{% else %}
|
|
8
8
|
{{ processors['raw'](item.value, context, data) }}
|
|
9
9
|
{% endif %}
|
|
@@ -170,7 +170,7 @@ const parseGraphic = async (
|
|
|
170
170
|
// create array from credits
|
|
171
171
|
if (credits.length > 0) {
|
|
172
172
|
// separate by commas or and
|
|
173
|
-
credits = credits.
|
|
173
|
+
credits = credits.split(/, *| and */g);
|
|
174
174
|
} else {
|
|
175
175
|
credits = [];
|
|
176
176
|
}
|
|
@@ -180,7 +180,15 @@ const parseGraphic = async (
|
|
|
180
180
|
const caption = await getText({ key: 'caption', page });
|
|
181
181
|
const altText = await getText({ key: 'alt-text', page });
|
|
182
182
|
const note = await getText({ key: 'note', page });
|
|
183
|
-
|
|
183
|
+
let source = await getText({ key: 'source', page });
|
|
184
|
+
|
|
185
|
+
// create array from source
|
|
186
|
+
if (source.length > 0) {
|
|
187
|
+
// separate by commas or and
|
|
188
|
+
source = source.split(/, *| and */g);
|
|
189
|
+
} else {
|
|
190
|
+
source = [];
|
|
191
|
+
}
|
|
184
192
|
|
|
185
193
|
const links = await page.$$eval('a', links =>
|
|
186
194
|
links.map(link => {
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
## Graphics and features metadata
|
|
2
|
+
|
|
3
|
+
In order to keep track of our graphics and features, metadata is grabbed from each project when we deploy them! (Hooray!) Metadata is scraped via specific selectors present in the HTML. The values for these selectors are parsed and output into a `manifest.json` file.
|
|
4
|
+
|
|
5
|
+
The metadata from `manifest.json` is then pasted into our [work log](https://docs.google.com/spreadsheets/d/1hCP5zGx8dNxk59gI9wBSFY2juJVM8OFCDY45VnNb2nI/edit#gid=965603489), which powers the CMS graphics plugin. Only embeddable graphics are present in the plugin — full-page features are not. The plugin surfaces our graphics for editors to embed them into stories.
|
|
6
|
+
|
|
7
|
+
| Selector/Variable | Alt Text | Example | Output in manifest.json |
|
|
8
|
+
|--|--|--|--|
|
|
9
|
+
| `[data-graphic]` or `[data-feature]` | If present, the HTML of the page will be parsed for metadata and surfaced in the CMS | `<div class="app" data-graphic>` or `<main data-feature>` | N/A - If no `[data-graphic]` or `[data-feature]` selector is found, the project won't output in manifest. |
|
|
10
|
+
| `{{ graphicTitle }}` or `[data-title]` | The title of the project in the CMS. **If this is missing in an embeddable graphic, the graphic will not surface in the CMS.** | `{% set graphicTitle = 'Some title' %}` or `<h1 class="graphic-title" data-title>Some title</h1>` | `title: string` |
|
|
11
|
+
| `{{ graphicAltText }}` | The alt text of the graphic in the CMS. This will also be read by screenreaders in platforms like Apple News. | `{% set graphicAltText = 'This is a bar chart showing xyz' %}` | `altText: string` |
|
|
12
|
+
| `{{ graphicCaption }}` or `[data-caption]` | The caption of the graphic in the CMS. The text typically below the title. | `{% set graphicCaption = 'Summarizing statement about the values in the graphic.' %}` or `<span data-caption>{{ prose(context.prose, context, graphicData) }}</span>` | `caption: string` |
|
|
13
|
+
| `{{ graphicNote }}` or `[data-note]` | Note or disclaimer attached to the graphic. | `{% set graphicNote = 'Important disclaimer about this graphic.' %}` or `<li data-note>Note: Important disclaimer about this graphic.</li>` | `note: string` |
|
|
14
|
+
| `{{ graphicSource }}` or `[data-source]` | The source of the graphic in the CMS | `{% set graphicSource = 'TXDOT' %}` or `<li data-source>Source: TXDOT</li>` | `source: string` |
|
|
15
|
+
| `{{ graphicCredit }}` or `[data-credit]` | The author names for the project in the CMS. | `{% set graphicCredit = 'Texas Tribune Staff' %}` or `<li data-credit>Texas Tribune Staff</li>` | `credits: array` |
|
|
16
|
+
| `{{ graphicTags }}` | The tags for the project in the CMS. | `{% set graphicTags = context.guten_tags %}` | `tags: array` |
|
|
17
|
+
|
|
18
|
+
The metadata as a variable, i.e. `graphicTitle` and `graphicCaption`, takes precedent over metadata in selectors, i.e. `[data-title]` and `[data-caption]`.
|
|
19
|
+
|
|
20
|
+
### Full example with an embeddable graphic
|
|
21
|
+
|
|
22
|
+
`context` is data from the project's ArchieML Google Doc.
|
|
23
|
+
|
|
24
|
+
```html
|
|
25
|
+
{% extends 'base.html' %}
|
|
26
|
+
{% set context = data.text %}
|
|
27
|
+
{% set graphicAltText = context.alttext %}
|
|
28
|
+
{% set graphicTags = context.guten_tags %}
|
|
29
|
+
{% block content %}
|
|
30
|
+
<div class="app" data-graphic>
|
|
31
|
+
<h1 class="graphic-title" data-title>{{ context.headline }}</h1>
|
|
32
|
+
<span data-caption>{{ prose(context.prose, context, graphicData) }}</span>
|
|
33
|
+
<div id="graphic" class="graphic"></div>
|
|
34
|
+
<ul class="graphic-footer">
|
|
35
|
+
<li data-note>Note: {{ context.note }}</li>
|
|
36
|
+
<li data-source>Source: {{ context.source }}</li>
|
|
37
|
+
<li data-credit>Credit: {{ context.credit }}</li>
|
|
38
|
+
</ul>
|
|
39
|
+
</div>
|
|
40
|
+
{% endblock content %}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Full example with a full-page feature
|
|
44
|
+
|
|
45
|
+
`context` is data from the project's ArchieML Google Doc.
|
|
46
|
+
|
|
47
|
+
```html
|
|
48
|
+
{% set graphicTags = context.guten_tags %}
|
|
49
|
+
<main data-feature>
|
|
50
|
+
<article class="has-giant-btm-marg">
|
|
51
|
+
{% block content %}
|
|
52
|
+
<div class="has-page-padding has-giant-btm-marg">
|
|
53
|
+
<header class="t-align-center">
|
|
54
|
+
<h1 class="l-container l-container--m t-headline t-serif t-lh-s has-s-btm-marg" data-title>{{ context.headline | widont }}</h1>
|
|
55
|
+
<p class="t-byline t-links t-uppercase t-lsp-m t-size-xs has-text-gray-dark has-b-btm-marg">
|
|
56
|
+
<span class="t-byline__item">By <span data-credit>{%- for author in context.authors -%}
|
|
57
|
+
{% if not loop.last %}{{ authorComma() }}{% elif not loop.first %} and{% endif %} <span class="l-display-ib">{{ author }}</span>
|
|
58
|
+
{%- endfor -%}</span>
|
|
59
|
+
</span>
|
|
60
|
+
</p>
|
|
61
|
+
</header>
|
|
62
|
+
</div>
|
|
63
|
+
{% endblock content %}
|
|
64
|
+
</article>
|
|
65
|
+
</main>
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Full example with ai2html graphic
|
|
69
|
+
|
|
70
|
+
Sometimes in Illustrator graphics, we set the title and other info in the Illustrator file itself and not the HTML, so we'd need to add those as nunjucks variables to surface the metadata. `context` is data from the project's ArchieML Google Doc.
|
|
71
|
+
|
|
72
|
+
```html
|
|
73
|
+
{% extends 'base.html' %}
|
|
74
|
+
{% set context = data.text %}
|
|
75
|
+
{% set graphicTitle = context.headline %}
|
|
76
|
+
{% set graphicCaption = context.prose %}
|
|
77
|
+
{% set graphicAltText = context.alttext %}
|
|
78
|
+
{% set graphicNote = context.note %}
|
|
79
|
+
{% set graphicSource = context.source %}
|
|
80
|
+
{% set graphicCredit = context.credit %}
|
|
81
|
+
{% set graphicTags = context.guten_tags %}
|
|
82
|
+
{% block content %}
|
|
83
|
+
<div class="app" data-graphic>
|
|
84
|
+
<h1 class="graphic-title" data-title>{{ context.headline | widont }}</h1>
|
|
85
|
+
<span data-caption>{{ prose(context.prose, context, graphicData) }}</span>
|
|
86
|
+
{% set ai2html = "border-map-full" %}
|
|
87
|
+
{% include "ai2html-output/" + ai2html + ".html" %}
|
|
88
|
+
<ul class="graphic-footer">
|
|
89
|
+
<li data-note>Note: {{ context.note }}</li>
|
|
90
|
+
<li data-source>Source: {{ context.source }}</li>
|
|
91
|
+
<li data-credit>Credit: {{ context.credit }}</li>
|
|
92
|
+
</ul>
|
|
93
|
+
</div>
|
|
94
|
+
{% endblock content %}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Other metadata
|
|
98
|
+
|
|
99
|
+
Along with HTML selectors, we also get metadata about graphics through the `project.config.js`. These are global to the whole project.
|
|
100
|
+
|
|
101
|
+
Project config keys output in `manifest.json`
|
|
102
|
+
|
|
103
|
+
- `type`
|
|
104
|
+
- `bucket`
|
|
105
|
+
- `createMonth`
|
|
106
|
+
- `createYear`
|
|
107
|
+
- `lastBuildTime`
|
|
108
|
+
- `folder`
|
|
109
|
+
- `id`
|
|
110
|
+
- `parserOptions`
|
|
111
|
+
- `appleNewsIgnore` - **Only applies to embeddable graphics** This is an array of files or folders that should be flagged in the CMS as not compatible with Apple News. Use this for graphics that are too dynamic to be accurately captured in a screenshot.
|
|
112
|
+
|
|
113
|
+
### Ignoring projects
|
|
114
|
+
|
|
115
|
+
If any projects should be ignored in `manifest.json`, add the paths in `metadataIgnore` in `project.config.js`.
|
|
116
|
+
|
|
117
|
+
- `parserOptions`
|
|
118
|
+
- `metadataIgnore` - This is an array of files or folders that should not be parsed for metadata and displayed in the CMS graphics plugin. For example, any graphics or features created for testing but are not publishable should be added here.
|
|
119
|
+
|
|
120
|
+
### Sample `manifest.json` output for embeddable graphics
|
|
121
|
+
|
|
122
|
+
```json
|
|
123
|
+
[
|
|
124
|
+
{
|
|
125
|
+
"type": "graphic",
|
|
126
|
+
"title": "Title of graphic",
|
|
127
|
+
"altText": "Alt text of graphic",
|
|
128
|
+
"bucket": "graphics.texastribune.org",
|
|
129
|
+
"projectPath": "graphics/new-test-2-2021-02/static",
|
|
130
|
+
"projectURL": "https://graphics.texastribune.org/graphics/new-test-2-2021-02/static/",
|
|
131
|
+
"caption": "Caption of graphic",
|
|
132
|
+
"createMonth": "02",
|
|
133
|
+
"createYear": "2021",
|
|
134
|
+
"credits": ["Mandi Cai", "Darla Cameron", "Carla Astudillo", "Chris Essig"],
|
|
135
|
+
"folder": "graphics/new-test-2-2021-02",
|
|
136
|
+
"id": "uniqueString",
|
|
137
|
+
"lastBuildTime": "2021-04-22T19:28:30.269Z",
|
|
138
|
+
"label": "static/index.html",
|
|
139
|
+
"links": [
|
|
140
|
+
{
|
|
141
|
+
"url": "https://www.texastribune.org/series/news-apps-graphics-databases/",
|
|
142
|
+
"text": "See more graphics like this",
|
|
143
|
+
"isCTA": true
|
|
144
|
+
}
|
|
145
|
+
],
|
|
146
|
+
"note": "Note: Texas Department of State Health Services was missing data last year.",
|
|
147
|
+
"previews": {
|
|
148
|
+
"large": "https://graphics.texastribune.org/graphics/new-test-2-2021-02/static/preview-large.png",
|
|
149
|
+
"small": "https://graphics.texastribune.org/graphics/new-test-2-2021-02/static/preview-small.png"
|
|
150
|
+
},
|
|
151
|
+
"showInAppleNews": true,
|
|
152
|
+
"source": "Source: Texas Department of State Health Services and U.S. Census ACS 2018 population estimates",
|
|
153
|
+
"tags": ["subject-politics"]
|
|
154
|
+
}
|
|
155
|
+
]
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Sample `manifest.json` output for full-page features
|
|
159
|
+
|
|
160
|
+
```json
|
|
161
|
+
[
|
|
162
|
+
{
|
|
163
|
+
"type": "feature",
|
|
164
|
+
"title": "Title of the feature",
|
|
165
|
+
"bucket": "apps.texastribune.org",
|
|
166
|
+
"projectPath": "features/2022/new-test-2-2021-02",
|
|
167
|
+
"projectURL": "https://apps.texastribune.org/features/2022/new-test-2-2021-02/",
|
|
168
|
+
"createMonth": "04",
|
|
169
|
+
"createYear": "2022",
|
|
170
|
+
"credits": ["Texas Tribune Staff"],
|
|
171
|
+
"folder": "features/2022/new-test-2-2021-02",
|
|
172
|
+
"id": "uniqueString",
|
|
173
|
+
"lastBuildTime": "2022-04-21T15:49:19.130Z",
|
|
174
|
+
"label": "index.html",
|
|
175
|
+
"tags": ["subject-politics"]
|
|
176
|
+
}
|
|
177
|
+
]
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### How it works
|
|
181
|
+
|
|
182
|
+
The `npm run parse` step will use [Puppeteer](https://github.com/puppeteer/puppeteer) and a local Chrome install to emulate the project in a browser. This will help build metadata based on a project's HTML and, for embeddable graphics, export image-based previews of the graphic as well as a manifest of all the metadata (`manifest.json`).
|
|
183
|
+
|
|
184
|
+
#### Troubleshooting
|
|
185
|
+
|
|
186
|
+
By default, this process assumes you're using MacOS. To change this for other operating systems, rerun the command with the correct install path variable: `CHROME_INSTALL_PATH="local/path/to/chrome" npm run parse`.
|
|
@@ -35,31 +35,4 @@ ${colors.blue.underline(mainPath)} (This has been copied to your clipboard.)
|
|
|
35
35
|
Did you run ${colors.yellow(
|
|
36
36
|
`npm run data:fetch`
|
|
37
37
|
)} before deploying to get the latest data?`);
|
|
38
|
-
|
|
39
|
-
if (projectType === 'feature') {
|
|
40
|
-
console.log(`
|
|
41
|
-
If you are deploying a feature, check Facebook/Twitter/other social platforms to make sure the
|
|
42
|
-
share image shows up.`);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
if (projectType === 'graphic') {
|
|
46
|
-
console.log(`
|
|
47
|
-
If you are deploying a graphic in a CMS story, there are a few steps. First,
|
|
48
|
-
add this in the Content section of the Raw Plugin:
|
|
49
|
-
${colors.yellow(
|
|
50
|
-
`<div class="dv201808-graphic dv201808-graphic--centered dv201808-graphic--centered-narrow" data-frame-src="${mainPath}" data-frame-sandbox="allow-scripts allow-same-origin allow-top-navigation-by-user-activation allow-top-navigation"></div>`
|
|
51
|
-
)}`);
|
|
52
|
-
|
|
53
|
-
console.log(`
|
|
54
|
-
Next, add the style code snippet found in ${colors.yellow(
|
|
55
|
-
'app/styles/raw-plugin-styles.html'
|
|
56
|
-
)} to the CSS content section of the Raw Plugin`);
|
|
57
|
-
|
|
58
|
-
console.log(`
|
|
59
|
-
Then, add this line to the JavaScript content section of the Raw Plugin:
|
|
60
|
-
${colors.yellow(
|
|
61
|
-
'<script src="https://cdn.texastribune.org/lib/@newswire/frames@0.3.1/index.umd.js"></script>'
|
|
62
|
-
)}
|
|
63
|
-
${colors.yellow('<script>newswireFrames.autoInitFrames();</script>')}`);
|
|
64
|
-
}
|
|
65
38
|
});
|
|
@@ -79,8 +79,10 @@ async function getAuth() {
|
|
|
79
79
|
function getGoogleToken(auth, googleTokenFile) {
|
|
80
80
|
return new Promise((resolve, reject) => {
|
|
81
81
|
const url = auth.generateAuthUrl({
|
|
82
|
-
access_type: 'offline',
|
|
83
82
|
scope: SCOPES,
|
|
83
|
+
response_type: "code",
|
|
84
|
+
redirect_uri: auth.redirectUri,
|
|
85
|
+
client_id: auth._clientId
|
|
84
86
|
});
|
|
85
87
|
|
|
86
88
|
console.log(
|
|
@@ -96,10 +98,12 @@ function getGoogleToken(auth, googleTokenFile) {
|
|
|
96
98
|
});
|
|
97
99
|
|
|
98
100
|
rl.question(
|
|
99
|
-
colors.bold(
|
|
100
|
-
async
|
|
101
|
+
colors.bold("Sign in with your @texastribune.org account and hit 'Allow' to grant access to News Apps Graphics Kit.\nAfter you are redirected, the page will look broken, but copy the URL and paste it here:\n"),
|
|
102
|
+
async url => {
|
|
101
103
|
rl.close();
|
|
102
104
|
|
|
105
|
+
const code = url.split(/[?=&]+/)[2];
|
|
106
|
+
|
|
103
107
|
let tokens;
|
|
104
108
|
|
|
105
109
|
try {
|
|
@@ -118,4 +122,4 @@ function getGoogleToken(auth, googleTokenFile) {
|
|
|
118
122
|
});
|
|
119
123
|
}
|
|
120
124
|
|
|
121
|
-
module.exports = { getAuth };
|
|
125
|
+
module.exports = { getAuth };
|
|
@@ -10,22 +10,30 @@ This is a running list of things you should do before you launch any project on
|
|
|
10
10
|
|
|
11
11
|
Our process for pitching and executing projects (which should happen before all of this) can be found on [this doc](https://docs.google.com/document/d/1E7QE8gp29h20EAafzSui8VjQ_9TG5-XhR33tbAP0hBA/edit).
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
### Final editing checklist
|
|
14
14
|
Before your embedded graphic or feature goes live, here's the editing steps you need to take:
|
|
15
15
|
|
|
16
|
+
#### Initial steps
|
|
16
17
|
- [ ] Spell check and self-edit — does everything make sense?
|
|
17
|
-
- [ ] Data visuals editor for a visual edit
|
|
18
|
-
- [ ] Design feedback channel (optional for more complex graphics or apps)
|
|
19
18
|
- [ ] Story reporter, if a collaboration
|
|
19
|
+
|
|
20
|
+
#### Editing
|
|
21
|
+
Copy editors have a deadline of 5 pm so all editing should be done early afternoon the day before publication (at the latest)
|
|
20
22
|
- [ ] Story or beat editor for a line edit to check facts
|
|
21
|
-
- [ ]
|
|
22
|
-
- [ ]
|
|
23
|
+
- [ ] Data visuals editor for edits and/or fact check
|
|
24
|
+
- [ ] DV team in the secret channel (for a final gut check. Darla can also provide visual edits in this channel, if she's available)
|
|
25
|
+
- [ ] Optional: Design feedback channel (can provide design edits)
|
|
26
|
+
|
|
27
|
+
#### Final steps
|
|
28
|
+
- [ ] Copy editor — their deadline is 5 p.m.
|
|
23
29
|
- [ ] Be available the night before publication for any last-minute changes, or let other DV teammates know how to make edits
|
|
24
30
|
|
|
25
|
-
### Headline
|
|
26
|
-
- [ ]
|
|
31
|
+
### Headline and pointer
|
|
32
|
+
- [ ] If it's an apps page, we need to get the page's headline, slug and summary all approved by an editor. This is done in the team-editors channel. As of June 2022, only the Data Visuals Editor has access to that channel so they will need to hoedown this information for you. Also, it's likely you will need to hoedown a SEO headline as well for the pointer inside the CMS. These are created so a link to the apps page can show up on our website. Here's [an example](https://www.texastribune.org/admin/articles/articlelink/40354/change/) of one we've done.
|
|
27
33
|
|
|
28
34
|
### Article
|
|
35
|
+
If you're creating an apps page, make sure you complete these.
|
|
36
|
+
|
|
29
37
|
- [ ] Add ads (three is typically the minimum; add more if longer)
|
|
30
38
|
- [ ] Make sure there's related articles (powered by the `guten_tag` property in the feature's ArchieML doc)
|
|
31
39
|
- [ ] Add any sigs, icons, or lead art
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/* eslint-disable no-unused-vars */
|
|
2
|
+
import * as d3 from 'd3';
|
|
3
|
+
import createBase from '../utils/d3-base';
|
|
4
|
+
|
|
5
|
+
// a reference to the default graphic container, change if needed
|
|
6
|
+
const container = d3.select('#graphic');
|
|
7
|
+
|
|
8
|
+
// a helper function to clear the container of its contents
|
|
9
|
+
const clearContainer = () => container.html('');
|
|
10
|
+
|
|
11
|
+
// a helper function to grab the container's width
|
|
12
|
+
const getFrameWidth = () => container.node().offsetWidth;
|
|
13
|
+
|
|
14
|
+
// import data by getting the window variable, OR by importing the filepath
|
|
15
|
+
// let data = window.DATA;
|
|
16
|
+
// import data from '../../../data/data.json';
|
|
17
|
+
// import data from 'Data/data.json';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* This function is called to render a graphic, using d3 or a library of your choice.
|
|
21
|
+
*
|
|
22
|
+
* @return {void}
|
|
23
|
+
*/
|
|
24
|
+
export default function renderGraphic() {
|
|
25
|
+
// pass the recalculated frameWidth to parts of your chart (like an axis) that change with resize!
|
|
26
|
+
clearContainer();
|
|
27
|
+
const frameWidth = getFrameWidth();
|
|
28
|
+
//
|
|
29
|
+
// rest of your code goes here
|
|
30
|
+
// use createBase() to create the base of an d3 chart
|
|
31
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { frameLoader } from '../embeds/frames';
|
|
2
|
+
import renderGraphic from './graphic-embed';
|
|
3
|
+
|
|
4
|
+
// included in index.html by default
|
|
5
|
+
// initiates frame so your graphic is wrapped in an AMP-compatible iframe
|
|
6
|
+
// renderGraphic() renders a coded graphic on load and on resize
|
|
7
|
+
frameLoader(renderGraphic);
|
|
@@ -99,9 +99,13 @@ class AdLoader {
|
|
|
99
99
|
adElementId
|
|
100
100
|
);
|
|
101
101
|
|
|
102
|
-
//
|
|
103
|
-
|
|
104
|
-
|
|
102
|
+
// make ad square if it includes the `dv-gpt-ad-square` class
|
|
103
|
+
if (attributes.class.includes('dv-gpt-ad-square')) {
|
|
104
|
+
gptAdUnit.defineSizeMapping([300, 250]);
|
|
105
|
+
} else {
|
|
106
|
+
// else use banner dimensions
|
|
107
|
+
gptAdUnit.defineSizeMapping(banner);
|
|
108
|
+
}
|
|
105
109
|
|
|
106
110
|
if (options.targetingKey && options.targetingValue) {
|
|
107
111
|
gptAdUnit.setTargeting(options.targetingKey, options.targetingValue);
|
|
@@ -22,8 +22,9 @@
|
|
|
22
22
|
{% if graphicSource %}
|
|
23
23
|
<meta name="tt-graphic-source" content="{{ graphicSource }}" />
|
|
24
24
|
{% endif %}
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
{% if graphicCredit %}
|
|
26
|
+
<meta name="tt-graphic-credit" content="{{ graphicCredit }}" />
|
|
27
|
+
{% endif %}
|
|
27
28
|
<meta name="tt-graphic-tags" content="{{ graphicTags or ['subject-politics'] }}" />
|
|
28
29
|
|
|
29
30
|
<link rel="dns-prefetch" href="https://www.google-analytics.com">
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{# Template for any scripted graphics, i.e. D3 charts #}
|
|
2
|
-
{% extends 'base-
|
|
2
|
+
{% extends 'base-embed.html' %}
|
|
3
3
|
{% from 'macros/prose.html' import prose %}
|
|
4
4
|
|
|
5
5
|
{# set pack that provides JS #}
|
|
6
|
-
{% set jsPackName = 'main' %}
|
|
6
|
+
{% set jsPackName = 'main-embed' %}
|
|
7
7
|
|
|
8
8
|
{# data.text --> data/text.json #}
|
|
9
9
|
{% set context = data.text %}
|
|
@@ -23,16 +23,16 @@
|
|
|
23
23
|
{# data-title is used to grab the title in the CMS #}
|
|
24
24
|
<h1 class="graphic-title" data-title>{{ context.headline | widont }}</h1>
|
|
25
25
|
{# data-caption is used to grab the caption in the CMS #}
|
|
26
|
-
<span data-caption>{{ prose(context.
|
|
26
|
+
<span data-caption>{{ prose(context.embed_prose, context, graphicData, 'embed') }}</span>
|
|
27
27
|
|
|
28
28
|
{# container that JS is attached to #}
|
|
29
29
|
<div id="graphic" class="graphic"></div>
|
|
30
30
|
|
|
31
31
|
{# data-source, data-credit, and data-note are also used in the CMS #}
|
|
32
32
|
<ul class="graphic-footer">
|
|
33
|
-
{% if context.
|
|
34
|
-
{% if context.
|
|
35
|
-
{% if context.
|
|
33
|
+
{% if context.embed_note %}<li>Note: <span data-note>{{ context.embed_note }}</span></li>{% endif %}
|
|
34
|
+
{% if context.embed_source %}<li>Source: <span data-source>{{ context.embed_source }}</span></li>{% endif %}
|
|
35
|
+
{% if context.embed_credit %}<li>Credit: <span data-credit>{{ context.embed_credit }}</span></li>{% endif %}
|
|
36
36
|
</ul>
|
|
37
37
|
</div>
|
|
38
38
|
{% endblock content %}
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
<meta property="og:site_name" content="The Texas Tribune">
|
|
20
20
|
<meta property="og:image" content="{{ staticAbsolute('assets/images/share-art.jpg', true) }}">
|
|
21
21
|
<meta property="fb:app_id" content="154122474650943">
|
|
22
|
-
<meta name="parsely-title" content="{{ context.
|
|
22
|
+
<meta name="parsely-title" content="{{ context.seo_headline|escape }}" />
|
|
23
23
|
<meta name="parsely-link" content="{{ CURRENT_PAGE_URL }}" />
|
|
24
24
|
<meta name="parsely-type" content="post" />
|
|
25
25
|
<meta name="parsely-image-url" content="{{ staticAbsolute('assets/images/share-art.jpg', true) }}" />
|
|
@@ -4,8 +4,12 @@
|
|
|
4
4
|
{{ value | renderStringWithNunjucks(data) }}
|
|
5
5
|
{% endmacro %}
|
|
6
6
|
|
|
7
|
-
{% macro text(value, context, data) %}
|
|
8
|
-
|
|
7
|
+
{% macro text(value, context, data, type) %}
|
|
8
|
+
{% if type == 'embed' %}
|
|
9
|
+
<p class="graphic-prose">{{ value | renderStringWithNunjucks(data) }}</p>
|
|
10
|
+
{% else %}
|
|
11
|
+
<p class="copy">{{ value | renderStringWithNunjucks(data) }}</p>
|
|
12
|
+
{% endif %}
|
|
9
13
|
{% endmacro %}
|
|
10
14
|
|
|
11
15
|
{% macro list(value) %}
|
|
@@ -22,8 +22,9 @@
|
|
|
22
22
|
{% if graphicSource %}
|
|
23
23
|
<meta name="tt-graphic-source" content="{{ graphicSource }}" />
|
|
24
24
|
{% endif %}
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
{% if graphicCredit %}
|
|
26
|
+
<meta name="tt-graphic-credit" content="{{ graphicCredit }}" />
|
|
27
|
+
{% endif %}
|
|
27
28
|
<meta name="tt-graphic-tags" content="{{ graphicTags or ['subject-politics'] }}" />
|
|
28
29
|
|
|
29
30
|
<link rel="dns-prefetch" href="https://www.google-analytics.com">
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
{# data-graphic signifies that this can be embedded in the CMS #}
|
|
28
28
|
<div class="app" data-graphic>
|
|
29
29
|
{# data-title is used to grab the title in the CMS #}
|
|
30
|
-
<h1 class="graphic-title" data-title>{{ context.headline | widont }}</h1>
|
|
30
|
+
<h1 class="graphic-title" data-title>{{ context.headline | widont or 'The only member-supported, digital-first, nonpartisan media organization' | widont }}</h1>
|
|
31
31
|
{# data-caption is used to grab the caption in the CMS #}
|
|
32
32
|
<span data-caption>{{ prose(context.prose, context, graphicData) }}</span>
|
|
33
33
|
|
|
@@ -37,9 +37,9 @@
|
|
|
37
37
|
|
|
38
38
|
{# data-source and data-credit are also used in the CMS #}
|
|
39
39
|
<ul class="graphic-footer">
|
|
40
|
-
{% if context.note %}<li data-note>
|
|
41
|
-
{% if context.source %}<li data-source>
|
|
42
|
-
{% if context.credit %}<li data-credit>
|
|
40
|
+
{% if context.note %}<li>Note: <span data-note>{{ context.note }}</span></li>{% endif %}
|
|
41
|
+
{% if context.source %}<li>Source: <span data-source>{{ context.source }}</span></li>{% endif %}
|
|
42
|
+
{% if context.credit %}<li>Credit: <span data-credit>{{ context.credit }}</span></li>{% endif %}
|
|
43
43
|
</ul>
|
|
44
44
|
</div>
|
|
45
45
|
{% endblock content %}
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
{# data-graphic signifies that this can be embedded in the CMS #}
|
|
22
22
|
<div class="app" data-graphic>
|
|
23
23
|
{# data-title is used to grab the title in the CMS #}
|
|
24
|
-
<h1 class="graphic-title" data-title>{{ context.headline | widont }}</h1>
|
|
24
|
+
<h1 class="graphic-title" data-title>{{ context.headline | widont or 'The only member-supported, digital-first, nonpartisan media organization' | widont }}</h1>
|
|
25
25
|
{# data-caption is used to grab the caption in the CMS #}
|
|
26
26
|
<span data-caption>{{ prose(context.prose, context, graphicData) }}</span>
|
|
27
27
|
|
|
@@ -30,9 +30,9 @@
|
|
|
30
30
|
|
|
31
31
|
{# data-source, data-credit, and data-note are also used in the CMS #}
|
|
32
32
|
<ul class="graphic-footer">
|
|
33
|
-
{% if context.note %}<li data-note>
|
|
34
|
-
{% if context.source %}<li data-source>
|
|
35
|
-
{% if context.credit %}<li data-credit>
|
|
33
|
+
{% if context.note %}<li>Note: <span data-note>{{ context.note }}</span></li>{% endif %}
|
|
34
|
+
{% if context.source %}<li>Source: <span data-source>{{ context.source }}</span></li>{% endif %}
|
|
35
|
+
{% if context.credit %}<li>Credit: <span data-credit>{{ context.credit }}</span></li>{% endif %}
|
|
36
36
|
</ul>
|
|
37
37
|
</div>
|
|
38
38
|
{% endblock content %}
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
## Graphics metadata
|
|
2
|
-
|
|
3
|
-
Graphics intended to be used as embeds in the CMS should have specific selectors present in the HTML. The values for these selectors are parsed and output into a `manifest.json` file which is used by the graphics plugin to organize our graphics inventory.
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
| Selector/Variable | Alt Text | Example | Output in manifest.json |
|
|
7
|
-
|--|--|--|--|
|
|
8
|
-
| `[data-graphic]` | If present, the HTML of the page will be parsed for metadata and surfaced in the CMS | `<div class="app" data-graphic>` | N/A - If no `[data-graphic]` selector is found, the graphic won't output in manifest. |
|
|
9
|
-
| `{{ graphicTitle }}` or `[data-title]` | The title of the graphic in the CMS. **If this is missing, the graphic will not surface in the CMS.** | `{% set graphicTitle = 'Some title' %}` or `<h1 class="graphic-title" data-title>Some title</h1>` | `title: string` |
|
|
10
|
-
| `{{ graphicAltText }}` | The alt text of the graphic in the CMS. This will also be read by screenreaders in platforms like Apple News. | `{% set graphicAltText = 'This is a bar chart showing xyz' %}` | `altText: string` |
|
|
11
|
-
| `{{ graphicCaption }}` | The caption of the graphic in the CMS. The text typically below the title. | `{% set graphicCaption = 'Summarizing statement about the values in the graphic.' %}` | `caption: string` |
|
|
12
|
-
| `{{ graphicNote }}` or `[data-note]` | Note or disclaimer attached to the graphic. | `{% set graphicNote = 'Important disclaimer about this graphic.' %}` or `<li data-note>Note: Important disclaimer about this graphic.</li>` | `note: string` |
|
|
13
|
-
| `{{ graphicSource }}` or `[data-source]` | The source of the graphic in the CMS | `{% set graphicSource = 'TXDOT' %}` or `<li data-source>Source: TXDOT</li>` | `source: string` |
|
|
14
|
-
| `{{ graphicCredit }}` or `[data-credit]` | The author names for the graphic in the CMS. | `{% set graphicCredit = 'Trib Tribington, Super Cool Corgi' %}` or `<li data-credit>Trib Tribington and Super Cool Corgi</li>` | `credits: array` |
|
|
15
|
-
|
|
16
|
-
### Full example
|
|
17
|
-
```html
|
|
18
|
-
{% extends 'base.html' %}
|
|
19
|
-
{% set context = data.text %}
|
|
20
|
-
{% set graphicAltText = 'Alt text of graphic' %}
|
|
21
|
-
{% block content %}
|
|
22
|
-
<div class="app" data-graphic>
|
|
23
|
-
<h1 class="graphic-title" data-title>{{ context.headline }}</h1>
|
|
24
|
-
<span data-caption>{{ prose(context.prose, context, graphicData) }}</span>
|
|
25
|
-
<div id="graphic" class="graphic"></div>
|
|
26
|
-
<ul class="graphic-footer">
|
|
27
|
-
<li data-note>Note: {{ context.note }}</li>
|
|
28
|
-
<li data-source>Source: {{ context.source }}</li>
|
|
29
|
-
<li data-credit>Credit: {{ context.credit }}</li>
|
|
30
|
-
</ul>
|
|
31
|
-
</div>
|
|
32
|
-
{% endblock content %}
|
|
33
|
-
```
|
|
34
|
-
### Full example with ai2html graphic
|
|
35
|
-
|
|
36
|
-
For Illustrator graphics, we typically set the title and other info in the Illustrator file itself and not the HTML, so we'd need to add those as nunjucks variables to surface the metadata.
|
|
37
|
-
|
|
38
|
-
```html
|
|
39
|
-
{% extends 'base.html' %}
|
|
40
|
-
{% set context = data.text %}
|
|
41
|
-
{% set graphicTitle = 'Headline from AI graphic' %}
|
|
42
|
-
{% set graphicAltText = 'Alt text of graphic' %}
|
|
43
|
-
{% set graphicCaption = 'Chatter from AI graphic' %}
|
|
44
|
-
{% set graphicNote = 'Note from AI graphic' %}
|
|
45
|
-
{% set graphicSource = 'Source from AI graphic' %}
|
|
46
|
-
{% set graphicCredit = 'Credit from AI graphic' %}
|
|
47
|
-
{% block content %}
|
|
48
|
-
<div class="app" data-graphic>
|
|
49
|
-
<h1 class="graphic-title" data-title>{{ context.headline }}</h1>
|
|
50
|
-
{{ prose(context.prose, context, graphicData) }}
|
|
51
|
-
{% set ai2html = "border-map-full" %}
|
|
52
|
-
{% include "ai2html-output/" + ai2html + ".html" %}
|
|
53
|
-
<ul class="graphic-footer">
|
|
54
|
-
<li data-note>Note: {{ context.note }}</li>
|
|
55
|
-
<li data-source>Source: {{ context.source }}</li>
|
|
56
|
-
<li data-credit>Credit: {{ context.credit }}</li>
|
|
57
|
-
</ul>
|
|
58
|
-
</div>
|
|
59
|
-
{% endblock content %}
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
### Other metadata
|
|
63
|
-
|
|
64
|
-
Along with HTML selectors, we also get metadata about graphics through the `project.config.js`. These are global to the whole project and the same for every graphic in the project.
|
|
65
|
-
|
|
66
|
-
Project config keys output in `manifest.json`
|
|
67
|
-
- `bucket`
|
|
68
|
-
- `createMonth`
|
|
69
|
-
- `createYear`
|
|
70
|
-
- `lastBuildTime`
|
|
71
|
-
- `folder`
|
|
72
|
-
- `id`
|
|
73
|
-
- `tags`
|
|
74
|
-
- `parserOptions`
|
|
75
|
-
- `metadataIgnore` - This is an array of files or folders that should not be parsed for metadata and displayed in the CMS graphics plugin. For example, any graphics or features created for testing but are not publishable should be added here.
|
|
76
|
-
- `appleNewsIgnore` - This is an array of files or folders that should be flagged in the CMS as not compatible with Apple News. Use this for graphics that are too dynamic to be accurately captured in a screenshot.
|
|
77
|
-
|
|
78
|
-
### Sample `manifest.json` output
|
|
79
|
-
|
|
80
|
-
```json
|
|
81
|
-
[
|
|
82
|
-
{
|
|
83
|
-
"title": "Title of graphic",
|
|
84
|
-
"altText": "Alt text of graphic",
|
|
85
|
-
"bucket": "graphics.texastribune.org",
|
|
86
|
-
"graphicPath": "graphics/new-test-2-2021-02/static",
|
|
87
|
-
"graphicURL": "https://graphics.texastribune.org/graphics/new-test-2-2021-02/static/",
|
|
88
|
-
"createMonth": "02",
|
|
89
|
-
"createYear": "2021",
|
|
90
|
-
"credits": ["Mandi Cai", "Darla Cameron", "Carla Astudillo", "Chris Essig"],
|
|
91
|
-
"folder": "graphics/new-test-2-2021-02",
|
|
92
|
-
"id": "uniqueString",
|
|
93
|
-
"label": "static/index.html",
|
|
94
|
-
"lastBuild": "2021-04-22T19:28:30.269Z",
|
|
95
|
-
"links": [
|
|
96
|
-
{
|
|
97
|
-
"url": "https://www.texastribune.org/series/news-apps-graphics-databases/",
|
|
98
|
-
"text": "See more graphics like this",
|
|
99
|
-
"isCTA": true
|
|
100
|
-
}
|
|
101
|
-
],
|
|
102
|
-
"note": "Note: Texas Department of State Health Services was missing data last year.",
|
|
103
|
-
"previews": {
|
|
104
|
-
"large": "https://graphics.texastribune.org/graphics/new-test-2-2021-02/static/preview-large.png",
|
|
105
|
-
"small": "https://graphics.texastribune.org/graphics/new-test-2-2021-02/static/preview-small.png"
|
|
106
|
-
},
|
|
107
|
-
"showInAppleNews": true,
|
|
108
|
-
"source": "Texas Department of State Health Services and U.S. Census ACS 2018 population estimates"
|
|
109
|
-
}
|
|
110
|
-
]
|
|
111
|
-
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
### How it works
|
|
116
|
-
|
|
117
|
-
The `npm run parse` step will use [Puppeteer](https://github.com/puppeteer/puppeteer) and a local Chrome install to emulate the project in a browser. This will help build metadata based on a graphic's HTML and export image-based previews of the graphic as well as a manifest of all the graphics in the project (`manifest.json`).
|
|
118
|
-
|
|
119
|
-
#### Troubleshooting
|
|
120
|
-
By default, this process assumes you're using MacOS. To change this for other operating systems, rerun the command with the correct install path variable: `CHROME_INSTALL_PATH="local/path/to/chrome" npm run parse`.
|