From e43aafd0e4c5f34158ea0cdc222833b79b35ab16 Mon Sep 17 00:00:00 2001 From: Danny Banks Date: Mon, 14 Dec 2020 16:36:19 -0800 Subject: [PATCH] feat(format): adding android/resources format --- .../formats/__snapshots__/all.test.js.snap | 14 + .../androidResources.test.js.snap | 50 ++++ __tests__/formats/androidResources.test.js | 239 ++++++++++++++++++ docs/formats.md | 75 ++++++ lib/common/formats.js | 80 ++++++ .../templates/android/resources.template | 51 ++++ 6 files changed, 509 insertions(+) create mode 100644 __tests__/formats/__snapshots__/androidResources.test.js.snap create mode 100644 __tests__/formats/androidResources.test.js create mode 100644 lib/common/templates/android/resources.template diff --git a/__tests__/formats/__snapshots__/all.test.js.snap b/__tests__/formats/__snapshots__/all.test.js.snap index 1aac63570..e8b3de42f 100644 --- a/__tests__/formats/__snapshots__/all.test.js.snap +++ b/__tests__/formats/__snapshots__/all.test.js.snap @@ -53,6 +53,20 @@ exports[`formats all should match android/integers snapshot 1`] = ` " `; +exports[`formats all should match android/resources snapshot 1`] = ` +" + + + + #FF0000 + + +" +`; + exports[`formats all should match android/strings snapshot 1`] = ` " diff --git a/__tests__/formats/__snapshots__/androidResources.test.js.snap b/__tests__/formats/__snapshots__/androidResources.test.js.snap new file mode 100644 index 000000000..b172aa532 --- /dev/null +++ b/__tests__/formats/__snapshots__/androidResources.test.js.snap @@ -0,0 +1,50 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`formats android/resources should match default snapshot 1`] = ` +" + + + + 12rem + 18rem + #ff0000 + #ffffff + + +" +`; + +exports[`formats android/resources with resourceMap override should match snapshot 1`] = ` +" + + + + #F2F3F4 + #000000 + + +" +`; + +exports[`formats android/resources with resourceType override should match snapshot 1`] = ` +" + + + + 12rem + 18rem + #ff0000 + #ffffff + + +" +`; diff --git a/__tests__/formats/androidResources.test.js b/__tests__/formats/androidResources.test.js new file mode 100644 index 000000000..628c8d62b --- /dev/null +++ b/__tests__/formats/androidResources.test.js @@ -0,0 +1,239 @@ +/* + * Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance with + * the License. A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +const formats = require('../../lib/common/formats'); + +const dictionary = { + "properties": { + "size": { + "font": { + "small": { + "value": "12rem", + "original": { + "value": "12px" + }, + "name": "size-font-small", + "attributes": { + "category": "size", + "type": "font", + "item": "small" + }, + "path": [ + "size", + "font", + "small" + ] + }, + "large": { + "value": "18rem", + "original": { + "value": "18px" + }, + "name": "size-font-large", + "attributes": { + "category": "size", + "type": "font", + "item": "large" + }, + "path": [ + "size", + "font", + "large" + ] + } + } + }, + "color": { + "base": { + "red": { + "value": "#ff0000", + "comment": "comment", + "original": { + "value": "#FF0000", + "comment": "comment" + }, + "name": "color-base-red", + "attributes": { + "category": "color", + "type": "base", + "item": "red" + }, + "path": [ + "color", + "base", + "red" + ] + } + }, + "white": { + "value": "#ffffff", + "original": { + "value": "#ffffff" + }, + "name": "color-white", + "attributes": { + "category": "color", + "type": "white" + }, + "path": [ + "color", + "white" + ] + } + }, + }, + "allProperties": [ + { + "value": "12rem", + "original": { + "value": "12px" + }, + "name": "size-font-small", + "attributes": { + "category": "size", + "type": "font", + "item": "small" + }, + "path": [ + "size", + "font", + "small" + ] + }, + { + "value": "18rem", + "original": { + "value": "18px" + }, + "name": "size-font-large", + "attributes": { + "category": "size", + "type": "font", + "item": "large" + }, + "path": [ + "size", + "font", + "large" + ] + }, + { + "value": "#ff0000", + "comment": "comment", + "original": { + "value": "#FF0000", + "comment": "comment" + }, + "name": "color-base-red", + "attributes": { + "category": "color", + "type": "base", + "item": "red" + }, + "path": [ + "color", + "base", + "red" + ] + }, + { + "value": "#ffffff", + "original": { + "value": "#ffffff" + }, + "name": "color-white", + "attributes": { + "category": "color", + "type": "white" + }, + "path": [ + "color", + "white" + ] + }, + ] +}; + +const customDictionary = { + properties: { + fontColor: { + primary: { + name: "fontColorPrimary", + value: "#000000", + attributes: { + category: "fontColor" + } + } + }, + backgroundColor: { + secondary: { + name: "backgroundColorSecondary", + value: "#F2F3F4", + attributes: { + category: "backgroundColor" + } + } + } + }, + allProperties: [{ + name: "backgroundColorSecondary", + value: "#F2F3F4", + attributes: { + category: "backgroundColor" + } + },{ + name: "fontColorPrimary", + value: "#000000", + attributes: { + category: "fontColor" + } + }] +} + +const format = formats['android/resources']; +const file = { + "destination": "__output/", + "format": 'android/resources' +}; + +describe('formats', () => { + + describe(`android/resources`, () => { + // mock the Date.now() call to a fixed value + const constantDate = new Date('2000-01-01'); + const globalDate = global.Date; + global.Date = function() { return constantDate }; + + it('should match default snapshot', () => { + expect(format(dictionary, {}, file)).toMatchSnapshot(); + }); + + it('with resourceType override should match snapshot', () => { + expect(format(dictionary, {}, {resourceType: "dimen"})).toMatchSnapshot(); + }); + + it('with resourceMap override should match snapshot', () => { + expect( + format(customDictionary, {}, { + resourceMap: { + color: 'color', + fontColor: 'color', + backgroundColor: 'color' + } + }) + ).toMatchSnapshot(); + }); + + }); + +}); diff --git a/docs/formats.md b/docs/formats.md index 72c0fec3a..f23d956d9 100644 --- a/docs/formats.md +++ b/docs/formats.md @@ -369,11 +369,46 @@ export const ColorBackgroundAlt = '#fcfcfcfc'; * * * +### android/resources + + +Creates a [resource](https://developer.android.com/guide/topics/resources/providing-resources) xml file. It is recommended to use a filter with this format +as it is generally best practice in Android development to have resource files +organized by type (color, dimension, string, etc.). However, a resource file +with mixed resources will still work. + +This format will try to use the proper resource type for each token based on +the category (color => color, size => dimen, etc.). However if you want to +force a particular resource type you can provide a 'resourceType' attribute +on the file configuration. You can also provide a 'resourceMap' if you +don't use Style Dictionary's built-in CTI structure. + +**Example** +```xml + + + #fffaf3f2 + #fff0cccc + 14sp +``` + +* * * + ### android/colors Creates a color resource xml file with all the colors in your style dictionary. +It is recommended to use the 'android/resources' format with a custom filter +instead of this format: + +```javascript +format: 'android/resources', +filter: { + attributes: { category: 'color' } +} +``` + **Example** ```xml @@ -390,6 +425,16 @@ Creates a color resource xml file with all the colors in your style dictionary. Creates a dimen resource xml file with all the sizes in your style dictionary. +It is recommended to use the 'android/resources' format with a custom filter +instead of this format: + +```javascript +format: 'android/resources', +filter: { + attributes: { category: 'size' } +} +``` + **Example** ```xml @@ -406,6 +451,16 @@ Creates a dimen resource xml file with all the sizes in your style dictionary. Creates a dimen resource xml file with all the font sizes in your style dictionary. +It is recommended to use the 'android/resources' format with a custom filter +instead of this format: + +```javascript +format: 'android/resources', +filter: { + attributes: { category: 'size' } +} +``` + **Example** ```xml @@ -423,6 +478,16 @@ Creates a dimen resource xml file with all the font sizes in your style dictiona Creates a resource xml file with all the integers in your style dictionary. It filters your style properties by `prop.attributes.category === 'time'` +It is recommended to use the 'android/resources' format with a custom filter +instead of this format: + +```javascript +format: 'android/resources', +filter: { + attributes: { category: 'time' } +} +``` + **Todo** - Update the filter on this. @@ -444,6 +509,16 @@ style properties by `prop.attributes.category === 'time'` Creates a resource xml file with all the strings in your style dictionary. Filters your style properties by `prop.attributes.category === 'content'` +It is recommended to use the 'android/resources' format with a custom filter +instead of this format: + +```javascript +format: 'android/resources', +filter: { + attributes: { category: 'content' } +} +``` + **Example** ```xml diff --git a/lib/common/formats.js b/lib/common/formats.js index dfd76d4bf..3775411be 100644 --- a/lib/common/formats.js +++ b/lib/common/formats.js @@ -425,9 +425,49 @@ module.exports = { }, // Android templates + /** + * Creates a [resource](https://developer.android.com/guide/topics/resources/providing-resources) xml file. It is recommended to use a filter with this format + * as it is generally best practice in Android development to have resource files + * organized by type (color, dimension, string, etc.). However, a resource file + * with mixed resources will still work. + * + * This format will try to use the proper resource type for each token based on + * the category (color => color, size => dimen, etc.). However if you want to + * force a particular resource type you can provide a 'resourceType' attribute + * on the file configuration. You can also provide a 'resourceMap' if you + * don't use Style Dictionary's built-in CTI structure. + * + * @memberof Formats + * @kind member + * @example + * ```xml + * + * + * #fffaf3f2 + * #fff0cccc + * 14sp + * ``` + */ + 'android/resources': function(dictionary, platform, file) { + const template = _.template( + fs.readFileSync(__dirname + '/templates/android/resources.template') + ); + return template(Object.assign({}, dictionary, file)); + }, + /** * Creates a color resource xml file with all the colors in your style dictionary. * + * It is recommended to use the 'android/resources' format with a custom filter + * instead of this format: + * + * ```javascript + * format: 'android/resources', + * filter: { + * attributes: { category: 'color' } + * } + * ``` + * * @memberof Formats * @kind member * @example @@ -446,6 +486,16 @@ module.exports = { /** * Creates a dimen resource xml file with all the sizes in your style dictionary. * + * It is recommended to use the 'android/resources' format with a custom filter + * instead of this format: + * + * ```javascript + * format: 'android/resources', + * filter: { + * attributes: { category: 'size' } + * } + * ``` + * * @memberof Formats * @kind member * @example @@ -464,6 +514,16 @@ module.exports = { /** * Creates a dimen resource xml file with all the font sizes in your style dictionary. * + * It is recommended to use the 'android/resources' format with a custom filter + * instead of this format: + * + * ```javascript + * format: 'android/resources', + * filter: { + * attributes: { category: 'size' } + * } + * ``` + * * @memberof Formats * @kind member * @example @@ -483,6 +543,16 @@ module.exports = { * Creates a resource xml file with all the integers in your style dictionary. It filters your * style properties by `prop.attributes.category === 'time'` * + * It is recommended to use the 'android/resources' format with a custom filter + * instead of this format: + * + * ```javascript + * format: 'android/resources', + * filter: { + * attributes: { category: 'time' } + * } + * ``` + * * @memberof Formats * @kind member * @todo Update the filter on this. @@ -503,6 +573,16 @@ module.exports = { * Creates a resource xml file with all the strings in your style dictionary. Filters your * style properties by `prop.attributes.category === 'content'` * + * It is recommended to use the 'android/resources' format with a custom filter + * instead of this format: + * + * ```javascript + * format: 'android/resources', + * filter: { + * attributes: { category: 'content' } + * } + * ``` + * * @memberof Formats * @kind member * @example diff --git a/lib/common/templates/android/resources.template b/lib/common/templates/android/resources.template new file mode 100644 index 000000000..618e1de95 --- /dev/null +++ b/lib/common/templates/android/resources.template @@ -0,0 +1,51 @@ + +<% +// +// Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"). +// You may not use this file except in compliance with the License. +// A copy of the License is located at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// or in the "license" file accompanying this file. This file is distributed +// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +// express or implied. See the License for the specific language governing +// permissions and limitations under the License. + +var resourceType = resourceType || null; + +var resourceMap = resourceMap || { + size: 'dimen', + color: 'color', + string: 'string', + content: 'string', + time: 'integer', + number: 'integer' +}; + +function propToType(prop) { + if (resourceType) { + return resourceType; + } + if (resourceMap[prop.attributes.category]) { + return resourceMap[prop.attributes.category]; + } + return 'string'; +}%> +<% + // for backward compatibility we need to have the user explicitly hide it + var showFileHeader = (this.options && this.options.hasOwnProperty('showFileHeader')) ? this.options.showFileHeader : true; + if(showFileHeader) { + print(""); + } +%> + + <% allProperties.forEach(function(prop) { + %><<%= propToType(prop) %> name="<%= prop.name %>"><%= prop.value %>><% if (prop.comment) { %><% } %> + <% }); %> +