From 41eaed9640e47defb1ab22b2f5d70793a3d29cae Mon Sep 17 00:00:00 2001 From: Katerina Date: Thu, 24 May 2018 16:17:14 +0100 Subject: [PATCH 01/35] Original development of x-follow-button using x-interaction (largely obsolete). --- .../__snapshots__/snapshots.test.js.snap | 4670 ++++++++++++++++- components/x-follow-button/.bowerrc | 8 + components/x-follow-button/bower.json | 9 + components/x-follow-button/package.json | 31 + components/x-follow-button/readme.md | 107 + components/x-follow-button/rollup.js | 4 + components/x-follow-button/src/Button.jsx | 32 + .../x-follow-button/src/FollowButton.jsx | 66 + .../src/styles/components/FollowButton.scss | 15 + .../x-follow-button/src/styles/main.scss | 7 + .../src/styles/mixins/lozenge/_themes.scss | 42 + .../styles/mixins/lozenge/_toggle-icon.scss | 43 + .../src/styles/mixins/lozenge/main.scss | 64 + .../x-follow-button/stories/follow-button.js | 27 + components/x-follow-button/stories/index.js | 14 + components/x-follow-button/stories/knobs.js | 42 + components/x-styling-demo/.gitignore | 1 + components/x-styling-demo/rollup.config.js | 6 + .../blueprints/component/stories/example.js | 2 + tools/x-workbench/package.json | 0 tools/x-workbench/register-components.js | 0 .../x-workbench/static/components/.gitignore | 2 + web/gatsby-config.js | 8 +- 23 files changed, 5195 insertions(+), 5 deletions(-) create mode 100644 components/x-follow-button/.bowerrc create mode 100644 components/x-follow-button/bower.json create mode 100644 components/x-follow-button/package.json create mode 100644 components/x-follow-button/readme.md create mode 100644 components/x-follow-button/rollup.js create mode 100644 components/x-follow-button/src/Button.jsx create mode 100644 components/x-follow-button/src/FollowButton.jsx create mode 100644 components/x-follow-button/src/styles/components/FollowButton.scss create mode 100644 components/x-follow-button/src/styles/main.scss create mode 100644 components/x-follow-button/src/styles/mixins/lozenge/_themes.scss create mode 100644 components/x-follow-button/src/styles/mixins/lozenge/_toggle-icon.scss create mode 100644 components/x-follow-button/src/styles/mixins/lozenge/main.scss create mode 100644 components/x-follow-button/stories/follow-button.js create mode 100644 components/x-follow-button/stories/index.js create mode 100644 components/x-follow-button/stories/knobs.js create mode 100644 components/x-styling-demo/.gitignore create mode 100644 components/x-styling-demo/rollup.config.js create mode 100644 tools/x-workbench/package.json create mode 100644 tools/x-workbench/register-components.js create mode 100644 tools/x-workbench/static/components/.gitignore diff --git a/__tests__/__snapshots__/snapshots.test.js.snap b/__tests__/__snapshots__/snapshots.test.js.snap index 5e0cd32f6..70f38a348 100644 --- a/__tests__/__snapshots__/snapshots.test.js.snap +++ b/__tests__/__snapshots__/snapshots.test.js.snap @@ -1,6 +1,4674 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`@financial-times/x-gift-article renders a default Free article x-gift-article 1`] = ` +exports[`@financial-times/x-follow-button renders a default Follow Button x-follow-button 1`] = ` +
+
+ + +
+
+`; + +exports[`@financial-times/x-increment renders a default Async x-increment 1`] = ` +
+ + 1 + + +
+`; + +exports[`@financial-times/x-increment renders a default Increment x-increment 1`] = ` +
+ + 1 + + +
+`; + +exports[`@financial-times/x-styling-demo renders a default Styling x-styling-demo 1`] = ` + +`; + +exports[`@financial-times/x-teaser renders a Hero Article x-teaser 1`] = ` +
+
+
+
+ + Sexual misconduct allegations + +
+
+
+ + Inside charity fundraiser where hostesses are put on show + + +
+

+ FT investigation finds groping and sexual harassment at secretive black-tie dinner +

+
+
+
+ +
+
+
+`; + +exports[`@financial-times/x-teaser renders a Hero Content Package x-teaser 1`] = ` +
+
+
+
+ + FT Magazine + +
+
+
+ + The royal wedding + + +
+
+
+
+ +
+
+
+`; + +exports[`@financial-times/x-teaser renders a Hero Opinion Piece x-teaser 1`] = ` +
+
+
+
+ + Gideon Rachman + +
+
+
+ + Anti-Semitism and the threat of identity politics + + +
+

+ Today, hatred of Jews is mixed in with fights about Islam and Israel +

+ +
+
+`; + +exports[`@financial-times/x-teaser renders a Hero Package item x-teaser 1`] = ` +
+
+
+
+ + FT Series + + + Financial crisis: Are we safer now? + +
+
+
+ + Why so little has changed since the crash + + +
+
+
+
+ +
+
+
+`; + +exports[`@financial-times/x-teaser renders a Hero Paid Post x-teaser 1`] = ` +
+
+
+
+ + Paid post + + + by UBS + +
+
+
+ + Why eSports companies are on a winning streak + + +
+

+ ESports is big business and about to get bigger: global revenues could hit $1.5bn by 2020 +

+
+
+
+ +
+
+
+`; + +exports[`@financial-times/x-teaser renders a Hero Podcast x-teaser 1`] = ` +
+
+
+
+ + Tech Tonic podcast + + + 12 mins + +
+
+
+ + Who sets the internet standards? + + +
+

+ Hannah Kuchler talks to American social scientist and cyber security expert Andrea… +

+
+
+
+ +
+
+
+`; + +exports[`@financial-times/x-teaser renders a Hero Top Story x-teaser 1`] = ` +
+
+
+
+ + Sexual misconduct allegations + +
+
+
+ + Inside charity fundraiser where hostesses are put on show + + +
+

+ FT investigation finds groping and sexual harassment at secretive black-tie dinner +

+
+
+
+ +
+
+ +
+`; + +exports[`@financial-times/x-teaser renders a Hero Video x-teaser 1`] = ` +
+
+
+
+ + Global Trade + + + 02:51min + +
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+ + FT View: Donald Trump, man of steel + + +
+
+
+`; + +exports[`@financial-times/x-teaser renders a HeroNarrow Article x-teaser 1`] = ` +
+
+
+ +
+
+ + Inside charity fundraiser where hostesses are put on show + + +
+

+ FT investigation finds groping and sexual harassment at secretive black-tie dinner +

+
+
+
+ +
+
+
+`; + +exports[`@financial-times/x-teaser renders a HeroNarrow Content Package x-teaser 1`] = ` +
+
+
+ +
+
+ + The royal wedding + + +
+

+ Prince Harry and Meghan Markle will tie the knot at Windsor Castle +

+
+
+
+ +
+
+
+`; + +exports[`@financial-times/x-teaser renders a HeroNarrow Opinion Piece x-teaser 1`] = ` +
+
+
+ +
+
+ + Anti-Semitism and the threat of identity politics + + +
+

+ Today, hatred of Jews is mixed in with fights about Islam and Israel +

+ +
+
+`; + +exports[`@financial-times/x-teaser renders a HeroNarrow Package item x-teaser 1`] = ` +
+
+
+ +
+
+ + Why so little has changed since the crash + + +
+

+ Martin Wolf on the power of vested interests in today’s rent-extracting economy +

+
+
+
+ +
+
+
+`; + +exports[`@financial-times/x-teaser renders a HeroNarrow Paid Post x-teaser 1`] = ` +
+
+
+
+ + Paid post + + + by UBS + +
+
+
+ + Why eSports companies are on a winning streak + + +
+

+ ESports is big business and about to get bigger: global revenues could hit $1.5bn by 2020 +

+
+
+
+ +
+
+
+`; + +exports[`@financial-times/x-teaser renders a HeroNarrow Podcast x-teaser 1`] = ` +
+
+
+
+ + Tech Tonic podcast + + + 12 mins + +
+
+
+ + Who sets the internet standards? + + +
+

+ Hannah Kuchler talks to American social scientist and cyber security expert Andrea… +

+
+
+
+ +
+
+
+`; + +exports[`@financial-times/x-teaser renders a HeroNarrow Top Story x-teaser 1`] = ` +
+
+
+ +
+
+ + Inside charity fundraiser where hostesses are put on show + + +
+

+ FT investigation finds groping and sexual harassment at secretive black-tie dinner +

+
+
+
+ +
+
+ +
+`; + +exports[`@financial-times/x-teaser renders a HeroNarrow Video x-teaser 1`] = ` +
+
+
+
+ + Global Trade + + + 02:51min + +
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+ +

+ The FT's Rob Armstrong looks at why Donald Trump is pushing trade tariffs +

+
+
+`; + +exports[`@financial-times/x-teaser renders a HeroOverlay Article x-teaser 1`] = ` +
+
+ + +

+ FT investigation finds groping and sexual harassment at secretive black-tie dinner +

+
+
+
+ +
+
+
+`; + +exports[`@financial-times/x-teaser renders a HeroOverlay Content Package x-teaser 1`] = ` +
+
+ + +
+
+
+ +
+
+
+`; + +exports[`@financial-times/x-teaser renders a HeroOverlay Opinion Piece x-teaser 1`] = ` +
+
+ + +

+ Today, hatred of Jews is mixed in with fights about Islam and Israel +

+ +
+
+`; + +exports[`@financial-times/x-teaser renders a HeroOverlay Package item x-teaser 1`] = ` +
+
+ + +
+
+
+ +
+
+
+`; + +exports[`@financial-times/x-teaser renders a HeroOverlay Paid Post x-teaser 1`] = ` +
+
+
+
+ + Paid post + + + by UBS + +
+
+ +

+ ESports is big business and about to get bigger: global revenues could hit $1.5bn by 2020 +

+
+
+
+ +
+
+
+`; + +exports[`@financial-times/x-teaser renders a HeroOverlay Podcast x-teaser 1`] = ` +
+
+
+
+ + Tech Tonic podcast + + + 12 mins + +
+
+ +

+ Hannah Kuchler talks to American social scientist and cyber security expert Andrea… +

+
+
+
+ +
+
+
+`; + +exports[`@financial-times/x-teaser renders a HeroOverlay Top Story x-teaser 1`] = ` +
+
+ + +

+ FT investigation finds groping and sexual harassment at secretive black-tie dinner +

+
+
+
+ +
+
+ +
+`; + +exports[`@financial-times/x-teaser renders a HeroOverlay Video x-teaser 1`] = ` +
+
+
+
+ + Global Trade + + + 02:51min + +
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+ +
+
+`; + +exports[`@financial-times/x-teaser renders a HeroVideo Article x-teaser 1`] = ` +
+
+ + +

+ FT investigation finds groping and sexual harassment at secretive black-tie dinner +

+
+
+
+ +
+
+
+`; + +exports[`@financial-times/x-teaser renders a HeroVideo Content Package x-teaser 1`] = ` +
+ +
+
+ +
+
+
+`; + +exports[`@financial-times/x-teaser renders a HeroVideo Opinion Piece x-teaser 1`] = ` +
+
+ + +

+ Today, hatred of Jews is mixed in with fights about Islam and Israel +

+ +
+
+`; + +exports[`@financial-times/x-teaser renders a HeroVideo Package item x-teaser 1`] = ` +
+ +
+
+ +
+
+
+`; + +exports[`@financial-times/x-teaser renders a HeroVideo Paid Post x-teaser 1`] = ` +
+
+
+
+ + Paid post + + + by UBS + +
+
+ +

+ ESports is big business and about to get bigger: global revenues could hit $1.5bn by 2020 +

+
+
+
+ +
+
+
+`; + +exports[`@financial-times/x-teaser renders a HeroVideo Podcast x-teaser 1`] = ` +
+
+
+
+ + Tech Tonic podcast + + + 12 mins + +
+
+ +

+ Hannah Kuchler talks to American social scientist and cyber security expert Andrea… +

+
+
+
+ +
+
+
+`; + +exports[`@financial-times/x-teaser renders a HeroVideo Top Story x-teaser 1`] = ` +
+
+ + +

+ FT investigation finds groping and sexual harassment at secretive black-tie dinner +

+
+
+
+ +
+
+ +
+`; + +exports[`@financial-times/x-teaser renders a HeroVideo Video x-teaser 1`] = ` +
+
+
+
+ + Global Trade + + + 02:51min + +
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+ +
+
+`; + +exports[`@financial-times/x-teaser renders a Large Article x-teaser 1`] = ` +
+
+ + +

+ FT investigation finds groping and sexual harassment at secretive black-tie dinner +

+
+
+
+ +
+
+
+`; + +exports[`@financial-times/x-teaser renders a Large Content Package x-teaser 1`] = ` +
+
+ + +

+ Prince Harry and Meghan Markle will tie the knot at Windsor Castle +

+
+
+
+ +
+
+
+`; + +exports[`@financial-times/x-teaser renders a Large Opinion Piece x-teaser 1`] = ` +
+
+ + +

+ Today, hatred of Jews is mixed in with fights about Islam and Israel +

+ +
+
+`; + +exports[`@financial-times/x-teaser renders a Large Package item x-teaser 1`] = ` +
+
+ + +

+ Martin Wolf on the power of vested interests in today’s rent-extracting economy +

+
+
+
+ +
+
+
+`; + +exports[`@financial-times/x-teaser renders a Large Paid Post x-teaser 1`] = ` +
+
+
+
+ + Paid post + + + by UBS + +
+
+ +

+ ESports is big business and about to get bigger: global revenues could hit $1.5bn by 2020 +

+
+
+
+ +
+
+
+`; + +exports[`@financial-times/x-teaser renders a Large Podcast x-teaser 1`] = ` +
+
+
+
+ + Tech Tonic podcast + + + 12 mins + +
+
+ +

+ Hannah Kuchler talks to American social scientist and cyber security expert Andrea… +

+
+
+
+ +
+
+
+`; + +exports[`@financial-times/x-teaser renders a Large Top Story x-teaser 1`] = ` + +`; + +exports[`@financial-times/x-teaser renders a Large Video x-teaser 1`] = ` +
+
+
+
+ + Global Trade + + + 02:51min + +
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+ +

+ The FT's Rob Armstrong looks at why Donald Trump is pushing trade tariffs +

+
+
+`; + +exports[`@financial-times/x-teaser renders a Small Article x-teaser 1`] = ` +
+
+ + +

+ FT investigation finds groping and sexual harassment at secretive black-tie dinner +

+
+
+
+ +
+
+
+`; + +exports[`@financial-times/x-teaser renders a Small Content Package x-teaser 1`] = ` + +`; + +exports[`@financial-times/x-teaser renders a Small Opinion Piece x-teaser 1`] = ` +
+
+ + +

+ Today, hatred of Jews is mixed in with fights about Islam and Israel +

+ +
+
+`; + +exports[`@financial-times/x-teaser renders a Small Package item x-teaser 1`] = ` + +`; + +exports[`@financial-times/x-teaser renders a Small Paid Post x-teaser 1`] = ` +
+
+
+
+ + Paid post + + + by UBS + +
+
+ +

+ ESports is big business and about to get bigger: global revenues could hit $1.5bn by 2020 +

+
+
+
+ +
+
+
+`; + +exports[`@financial-times/x-teaser renders a Small Podcast x-teaser 1`] = ` +
+
+
+
+ + Tech Tonic podcast + + + 12 mins + +
+
+ +

+ Hannah Kuchler talks to American social scientist and cyber security expert Andrea… +

+
+
+
+ +
+
+
+`; + +exports[`@financial-times/x-teaser renders a Small Top Story x-teaser 1`] = ` + +`; + +exports[`@financial-times/x-teaser renders a Small Video x-teaser 1`] = ` +
+
+
+
+ + Global Trade + + + 02:51min + +
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+ +
+
+`; + +exports[`@financial-times/x-teaser renders a SmallHeavy Article x-teaser 1`] = ` +
+
+ + +

+ FT investigation finds groping and sexual harassment at secretive black-tie dinner +

+
+
+
+ +
+
+
+`; + +exports[`@financial-times/x-teaser renders a SmallHeavy Content Package x-teaser 1`] = ` +
+
+ + +

+ Prince Harry and Meghan Markle will tie the knot at Windsor Castle +

+
+
+
+ +
+
+
+`; + +exports[`@financial-times/x-teaser renders a SmallHeavy Opinion Piece x-teaser 1`] = ` +
+
+ + +

+ Today, hatred of Jews is mixed in with fights about Islam and Israel +

+ +
+
+`; + +exports[`@financial-times/x-teaser renders a SmallHeavy Package item x-teaser 1`] = ` +
+
+ + +

+ Martin Wolf on the power of vested interests in today’s rent-extracting economy +

+
+
+
+ +
+
+
+`; + +exports[`@financial-times/x-teaser renders a SmallHeavy Paid Post x-teaser 1`] = ` +
+
+
+
+ + Paid post + + + by UBS + +
+
+ +

+ ESports is big business and about to get bigger: global revenues could hit $1.5bn by 2020 +

+
+
+
+ +
+
+
+`; + +exports[`@financial-times/x-teaser renders a SmallHeavy Podcast x-teaser 1`] = ` +
+
+
+
+ + Tech Tonic podcast + + + 12 mins + +
+
+ +

+ Hannah Kuchler talks to American social scientist and cyber security expert Andrea… +

+
+
+
+ +
+
+
+`; + +exports[`@financial-times/x-teaser renders a SmallHeavy Top Story x-teaser 1`] = ` + +`; + +exports[`@financial-times/x-teaser renders a SmallHeavy Video x-teaser 1`] = ` +
+
+
+
+ + Global Trade + + + 02:51min + +
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+ +

+ The FT's Rob Armstrong looks at why Donald Trump is pushing trade tariffs +

+
+
+`; + +exports[`@financial-times/x-teaser renders a TopStory Article x-teaser 1`] = ` +
+
+ + +

+ FT investigation finds groping and sexual harassment at secretive black-tie dinner +

+
+
+
+ +
+
+
+`; + +exports[`@financial-times/x-teaser renders a TopStory Content Package x-teaser 1`] = ` +
+
+ + +

+ Prince Harry and Meghan Markle will tie the knot at Windsor Castle +

+
+
+
+ +
+
+
+`; + +exports[`@financial-times/x-teaser renders a TopStory Opinion Piece x-teaser 1`] = ` +
+
+ + +

+ Today, hatred of Jews is mixed in with fights about Islam and Israel +

+ +
+
+`; + +exports[`@financial-times/x-teaser renders a TopStory Package item x-teaser 1`] = ` +
+
+ + +

+ Martin Wolf on the power of vested interests in today’s rent-extracting economy +

+
+
+
+ +
+
+
+`; + +exports[`@financial-times/x-teaser renders a TopStory Paid Post x-teaser 1`] = ` +
+
+
+
+ + Paid post + + + by UBS + +
+
+ +

+ ESports is big business and about to get bigger: global revenues could hit $1.5bn by 2020 +

+
+
+
+ +
+
+
+`; + +exports[`@financial-times/x-teaser renders a TopStory Podcast x-teaser 1`] = `
diff --git a/components/x-follow-button/.bowerrc b/components/x-follow-button/.bowerrc new file mode 100644 index 000000000..59e9a5925 --- /dev/null +++ b/components/x-follow-button/.bowerrc @@ -0,0 +1,8 @@ +{ + "registry": { + "search": [ + "https://origami-bower-registry.ft.com", + "https://registry.bower.io" + ] + } +} diff --git a/components/x-follow-button/bower.json b/components/x-follow-button/bower.json new file mode 100644 index 000000000..21c72aba2 --- /dev/null +++ b/components/x-follow-button/bower.json @@ -0,0 +1,9 @@ +{ + "name": "x-follow-button", + "private": true, + "dependencies": { + "o-colors": "^4.4.1", + "o-icons": "^5.7.1", + "o-typography": "^5.7.4" + } +} diff --git a/components/x-follow-button/package.json b/components/x-follow-button/package.json new file mode 100644 index 000000000..598d21404 --- /dev/null +++ b/components/x-follow-button/package.json @@ -0,0 +1,31 @@ +{ + "name": "@financial-times/x-follow-button", + "version": "1.0.0", + "description": "", + "main": "dist/FollowButton.cjs.js", + "style": "dist/FollowButton.css", + "browser": "dist/FollowButton.es5.js", + "module": "dist/FollowButton.esm.js", + "scripts": { + "prepare": "npm run build", + "build": "node rollup.js", + "start": "node rollup.js --watch", + "postinstall": "bower install" + }, + "keywords": [], + "author": "", + "license": "ISC", + "devDependencies": { + "@financial-times/x-rollup": "file:../../packages/x-rollup", + "rollup": "^0.57.1", + "bower": "^1.7.9", + "node-sass": "^3.8.0" + }, + "peerDependencies": { + "@financial-times/x-engine": "file:../../packages/x-engine" + }, + "dependencies": { + "@financial-times/x-interaction": "file:../x-interaction", + "@financial-times/x-engine": "file:../../packages/x-engine" + } +} diff --git a/components/x-follow-button/readme.md b/components/x-follow-button/readme.md new file mode 100644 index 000000000..8b0cf493f --- /dev/null +++ b/components/x-follow-button/readme.md @@ -0,0 +1,107 @@ +# x-follow-button + +This module provides a template for myFT follow button component. + +(and will potentially eventually replace `{{> n-myft-ui/components/follow-button/follow-button}}`) + +## Installation + +```bash +npm install --save @financial-times/x-follow-button +``` + +[engine]: https://github.com/Financial-Times/x-dash/tree/master/packages/x-engine + +## Knobs + +- Text (allows to play with the text on the button, and text inside of accompanied HTML) +- Extra Classes (they won't change anything in the storybook, but you can explore how your component's structure would be affected) +- Flags (what would change if you change the flag) +- Status (is the button selected or not) + +## Usage + +Component provided by this module expects a map of [follow button properties](#properties). They can be used with vanilla JavaScript or JSX (if you are not familiar check out [WTF is JSX][jsx-wtf] first). For example if you were writing your application using React you could use the component like this: + +```jsx +import React from 'react'; +import { FollowButton } from '@financial-times/x-follow-button'; + +// A == B == C +const a = FollowButton(props); +const b = ; +const c = React.createElement(FollowButton, props); +``` + +All `x-` components are designed to be compatible with a variety of runtimes, not just React. Check out the [`x-engine`][engine] documentation for a list of recommended libraries and frameworks. + +[jsx-wtf]: https://jasonformat.com/wtf-is-jsx/ + +### Example: how to use with handlebars +(step-by-step guide) + +1. Install `x-hadlebars` +``` +$ npm install -S @financial-times/x-hadlebars +``` + +2. Add `x-hadlebars` to helpers of `n-ui` express app +``` +const xHandlebars = require('@financial-times/x-handlebars'); + +const app = express({ + helpers: { + x: xHandlebars() + } +}); +``` + +3. Add `x-follow-button` to `package.json` +``` +$ npm install -S @financial-times/x-follow-button +``` +or add `"@financial-times/x-follow-button": "file:../x-dash/components/x-follow-button"` if you want to link it to your local version (note that the path is a path to your local `x-dash` folder) + +4. Specify to `package.json` that you are using "hyperons" engine +``` +"x-dash": { + "engine": { + "server": "hyperons" + } +} +``` + +5. Add your button to wherever you want to use it; this is a very basic example, feel free to play with other props +``` +{{{ + x local="../x-dash/components/x-follow-button" + component="FollowButton" + conceptId="{conceptId}" +}}} +``` + +6. Add `x-dash` CSS used by this component +``` +@import "x-follow-button/dist/FollowButton"; +``` + +And all is ready to go! + +Note: we assume that client side JavaScript is handled separately + +## List of all properties (props) + +(Some of the properties don't influence the way button looks or acts, but can be used for e.g. client-side Javascript in the apps). + +Feature | Type | Default value | Knob +--------------------------|---------|---------------------------|------ +`altButtonText` | String | 'Added' | yes +`buttonText` | String | 'Add to myFT' | yes +`conceptId` | String | '00000-0000-00000-00000' | +`csrfToken` | String | 'testTokenValue' | +`extraButtonClasses` | String | null | yes +`followPlusDigestEmail` | Boolean | true | yes +`isSelected` | Boolean | false | yes +`name` | String | 'Test Name' | yes +`switchFollowDigestEmail` | Boolean | false | +`variant` | String | null | yes diff --git a/components/x-follow-button/rollup.js b/components/x-follow-button/rollup.js new file mode 100644 index 000000000..075823328 --- /dev/null +++ b/components/x-follow-button/rollup.js @@ -0,0 +1,4 @@ +const xRollup = require('@financial-times/x-rollup'); +const pkg = require('./package.json'); + +xRollup({ input: './src/FollowButton.jsx', pkg }); diff --git a/components/x-follow-button/src/Button.jsx b/components/x-follow-button/src/Button.jsx new file mode 100644 index 000000000..ab24b5c23 --- /dev/null +++ b/components/x-follow-button/src/Button.jsx @@ -0,0 +1,32 @@ +import { h } from '@financial-times/x-engine'; +import styles from './styles/main.scss'; + +const Button = ({ + conceptId, + followPlusDigestEmail, + isSelected, + variant, + altButtonText, + buttonText, + name, + ...props +}) => ( + +); + +export default Button; diff --git a/components/x-follow-button/src/FollowButton.jsx b/components/x-follow-button/src/FollowButton.jsx new file mode 100644 index 000000000..5c998017b --- /dev/null +++ b/components/x-follow-button/src/FollowButton.jsx @@ -0,0 +1,66 @@ +import { h } from '@financial-times/x-engine'; +import { withActions } from '@financial-times/x-interaction'; + +import Button from './Button'; + +const followButtonActions = withActions(() => ({ + onSubmitAction(event) { + event.preventDefault(); + }, + onClickAction() { + return ({isSelected}) => ({ + isSelected: !isSelected + }); + } +})); + +const getFormAction = (conceptId, followPlusDigestEmail, isSelected) => { + if (followPlusDigestEmail) { + return `/__myft/api/core/follow-plus-digest-email/${conceptId}?method=put` + } else if (isSelected) { + return `/__myft/api/core/followed/concept/${conceptId}?method=delete` + } else { + return `/__myft/api/core/followed/concept/${conceptId}?method=put` + } +}; + +const BaseFollowButton = ({ + conceptId, + csrfToken, + followPlusDigestEmail, + isSelected, + variant, + altButtonText, + buttonText, + actions, + name +}) => ( +
+ +
- -
+ Add to myFT + + `; exports[`@financial-times/x-increment renders a default Async x-increment 1`] = ` diff --git a/components/x-follow-button/bower.json b/components/x-follow-button/bower.json index 21c72aba2..08ca23073 100644 --- a/components/x-follow-button/bower.json +++ b/components/x-follow-button/bower.json @@ -1,6 +1,7 @@ { "name": "x-follow-button", "private": true, + "main": "dist/FollowButton.es5.js", "dependencies": { "o-colors": "^4.4.1", "o-icons": "^5.7.1", From ef56c8ae5d4dfac9f80bf1a9258136a403377226 Mon Sep 17 00:00:00 2001 From: Dan Searle Date: Wed, 7 Nov 2018 16:27:49 +0000 Subject: [PATCH 03/35] Tidy up after rebase. --- tools/x-workbench/package.json | 0 tools/x-workbench/register-components.js | 0 tools/x-workbench/static/components/.gitignore | 2 -- 3 files changed, 2 deletions(-) delete mode 100644 tools/x-workbench/package.json delete mode 100644 tools/x-workbench/register-components.js delete mode 100644 tools/x-workbench/static/components/.gitignore diff --git a/tools/x-workbench/package.json b/tools/x-workbench/package.json deleted file mode 100644 index e69de29bb..000000000 diff --git a/tools/x-workbench/register-components.js b/tools/x-workbench/register-components.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/tools/x-workbench/static/components/.gitignore b/tools/x-workbench/static/components/.gitignore deleted file mode 100644 index d6b7ef32c..000000000 --- a/tools/x-workbench/static/components/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!.gitignore From 92b229f7ffd71440fafe64adf9e6029ddab6dc64 Mon Sep 17 00:00:00 2001 From: Dan Searle Date: Mon, 12 Nov 2018 10:22:18 +0000 Subject: [PATCH 04/35] Update knobs for Storybook 4 (+1 squashed commit) Squashed commits: [feec44f8] More cleanup after rebase. --- components/x-follow-button/stories/knobs.js | 12 ++++++------ components/x-styling-demo/.gitignore | 1 - components/x-styling-demo/rollup.config.js | 6 ------ private/blueprints/component/stories/example.js | 2 -- 4 files changed, 6 insertions(+), 15 deletions(-) delete mode 100644 components/x-styling-demo/.gitignore delete mode 100644 components/x-styling-demo/rollup.config.js diff --git a/components/x-follow-button/stories/knobs.js b/components/x-follow-button/stories/knobs.js index 8df276159..3f01c0dbe 100644 --- a/components/x-follow-button/stories/knobs.js +++ b/components/x-follow-button/stories/knobs.js @@ -1,6 +1,6 @@ // To ensure that component stories do not need to depend on Storybook themselves we return a // function that may be passed the required dependencies. -module.exports = (data, { text, boolean, selectV2 }) => { +module.exports = (data, { text, boolean, select }) => { const Groups = { Text: 'Text', Status: 'Status', @@ -18,25 +18,25 @@ module.exports = (data, { text, boolean, selectV2 }) => { name () { return text('Name of what we add', data.name, Groups.Text); } - } + }; const Status = { isSelected () { return boolean('isSelected', data.isSelected, Groups.Status); } - } + }; const Flags = { followPlusDigestEmail () { return boolean('followPlusDigestEmail', data.followPlusDigestEmail, Groups.Flags); } - } + }; const Variant = { variant () { - return selectV2('variant', [ null, 'standard', 'inverse', 'opinion', 'monochrome' ], data.variant, Groups.Variant); + return select('variant', [ null, 'standard', 'inverse', 'opinion', 'monochrome' ], data.variant, Groups.Variant); } - } + }; return Object.assign({}, Text, Status, Flags, Variant); }; diff --git a/components/x-styling-demo/.gitignore b/components/x-styling-demo/.gitignore deleted file mode 100644 index 53c37a166..000000000 --- a/components/x-styling-demo/.gitignore +++ /dev/null @@ -1 +0,0 @@ -dist \ No newline at end of file diff --git a/components/x-styling-demo/rollup.config.js b/components/x-styling-demo/rollup.config.js deleted file mode 100644 index bd90aba60..000000000 --- a/components/x-styling-demo/rollup.config.js +++ /dev/null @@ -1,6 +0,0 @@ -import xRollup from '@financial-times/x-rollup'; -import pkg from './package.json'; - -const input = 'src/Button.jsx'; - -export default xRollup({input, pkg}); diff --git a/private/blueprints/component/stories/example.js b/private/blueprints/component/stories/example.js index ab8a32f0f..6df941bf7 100644 --- a/private/blueprints/component/stories/example.js +++ b/private/blueprints/component/stories/example.js @@ -4,8 +4,6 @@ exports.data = { message: 'Hello World!' }; -exports.knobs = ['count']; - // This reference is only required for hot module loading in development // exports.m = module; From d9adab30655b1f6f85e2a43018503c2d23053600 Mon Sep 17 00:00:00 2001 From: Dan Searle Date: Wed, 21 Nov 2018 12:09:20 +0000 Subject: [PATCH 05/35] Dispatch custom event on click of button. Expose more meaningful actions. No need for a separate onClick handler. --- .../__snapshots__/snapshots.test.js.snap | 1 - .../x-follow-button/src/FollowButton.jsx | 33 ++++++++++++++----- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/__tests__/__snapshots__/snapshots.test.js.snap b/__tests__/__snapshots__/snapshots.test.js.snap index 5989befc3..1b03ce473 100644 --- a/__tests__/__snapshots__/snapshots.test.js.snap +++ b/__tests__/__snapshots__/snapshots.test.js.snap @@ -24,7 +24,6 @@ exports[`@financial-times/x-follow-button renders a default Follow Button x-foll data-concept-id="00000-0000-00000-00000" data-trackable="follow" data-trackable-context-messaging="add-to-myft-plus-digest-button" - onClick={[Function]} title="Add Test Name to MyFT" type="submit" > diff --git a/components/x-follow-button/src/FollowButton.jsx b/components/x-follow-button/src/FollowButton.jsx index 5c998017b..8304ed252 100644 --- a/components/x-follow-button/src/FollowButton.jsx +++ b/components/x-follow-button/src/FollowButton.jsx @@ -3,14 +3,26 @@ import { withActions } from '@financial-times/x-interaction'; import Button from './Button'; -const followButtonActions = withActions(() => ({ - onSubmitAction(event) { - event.preventDefault(); +const followButtonActions = withActions(initialProps => ({ + triggerFollowUnfollow(rootElement) { + return currentProps => { + const detail = { + action: currentProps.isSelected ? 'remove' : 'add', + actorType: 'user', + actorId: null, // myft client sets to user id from session + relationshipName: 'followed', + subjectType: 'concept', + subjectId: initialProps.conceptId, + token: initialProps.csrfToken + }; + rootElement.dispatchEvent(new CustomEvent('x-follow-button', { bubbles: true, detail })); + }; }, - onClickAction() { - return ({isSelected}) => ({ - isSelected: !isSelected - }); + followed() { + return { isSelected: true }; + }, + unfollowed() { + return { isSelected: false }; } })); @@ -40,7 +52,10 @@ const BaseFollowButton = ({ data-myft-ui="follow" data-concept-id={conceptId} action={ getFormAction(conceptId, followPlusDigestEmail, isSelected) } - onSubmit={ actions.onSubmitAction } + onSubmit={event => { + event.preventDefault(); + actions.triggerFollowUnfollow(event.target); + }} { ...(followPlusDigestEmail ? { 'data-myft-ui-variant': true } : null) }> + /> ); From 93873f43104abadeac5d21e22b9643dddeaa2673 Mon Sep 17 00:00:00 2001 From: Dan Searle Date: Wed, 21 Nov 2018 15:04:32 +0000 Subject: [PATCH 06/35] Remove data-myft-ui to prevent n-myft-ui generic button handling. (+1 squashed commit) Squashed commits: [4fbe12fc] Add displayName to base component --- __tests__/__snapshots__/snapshots.test.js.snap | 1 - components/x-follow-button/src/FollowButton.jsx | 4 +++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/__tests__/__snapshots__/snapshots.test.js.snap b/__tests__/__snapshots__/snapshots.test.js.snap index 1b03ce473..abfbe4c53 100644 --- a/__tests__/__snapshots__/snapshots.test.js.snap +++ b/__tests__/__snapshots__/snapshots.test.js.snap @@ -4,7 +4,6 @@ exports[`@financial-times/x-follow-button renders a default Follow Button x-foll
({ subjectId: initialProps.conceptId, token: initialProps.csrfToken }; + rootElement.dispatchEvent(new CustomEvent('x-follow-button', { bubbles: true, detail })); }; }, @@ -49,7 +50,6 @@ const BaseFollowButton = ({ }) => ( { @@ -72,6 +72,8 @@ const BaseFollowButton = ({
); +BaseFollowButton.displayName = 'BaseFollowButton'; + const FollowButton = followButtonActions(BaseFollowButton); export { From bfe622cfede220ffcde1b85239c18c4ba9ba0644 Mon Sep 17 00:00:00 2001 From: dan-searle Date: Tue, 27 Nov 2018 15:32:49 +0000 Subject: [PATCH 07/35] Refactor to not use x-interaction. Also simplify the button as it now expects to get rendered by its parent in order to change its state. --- .../__snapshots__/snapshots.test.js.snap | 17 ++- components/x-follow-button/readme.md | 30 ++-- components/x-follow-button/src/Button.jsx | 32 ---- .../x-follow-button/src/FollowButton.jsx | 140 +++++++++--------- .../x-follow-button/stories/follow-button.js | 14 +- components/x-follow-button/stories/knobs.js | 15 +- 6 files changed, 97 insertions(+), 151 deletions(-) delete mode 100644 components/x-follow-button/src/Button.jsx diff --git a/__tests__/__snapshots__/snapshots.test.js.snap b/__tests__/__snapshots__/snapshots.test.js.snap index abfbe4c53..b711e3a64 100644 --- a/__tests__/__snapshots__/snapshots.test.js.snap +++ b/__tests__/__snapshots__/snapshots.test.js.snap @@ -15,19 +15,20 @@ exports[`@financial-times/x-follow-button renders a default Follow Button x-foll value="testTokenValue" /> + /> `; diff --git a/components/x-follow-button/readme.md b/components/x-follow-button/readme.md index 8b0cf493f..e6d7d937a 100644 --- a/components/x-follow-button/readme.md +++ b/components/x-follow-button/readme.md @@ -12,13 +12,6 @@ npm install --save @financial-times/x-follow-button [engine]: https://github.com/Financial-Times/x-dash/tree/master/packages/x-engine -## Knobs - -- Text (allows to play with the text on the button, and text inside of accompanied HTML) -- Extra Classes (they won't change anything in the storybook, but you can explore how your component's structure would be affected) -- Flags (what would change if you change the flag) -- Status (is the button selected or not) - ## Usage Component provided by this module expects a map of [follow button properties](#properties). They can be used with vanilla JavaScript or JSX (if you are not familiar check out [WTF is JSX][jsx-wtf] first). For example if you were writing your application using React you could use the component like this: @@ -89,19 +82,16 @@ And all is ready to go! Note: we assume that client side JavaScript is handled separately -## List of all properties (props) +## Props (Some of the properties don't influence the way button looks or acts, but can be used for e.g. client-side Javascript in the apps). -Feature | Type | Default value | Knob ---------------------------|---------|---------------------------|------ -`altButtonText` | String | 'Added' | yes -`buttonText` | String | 'Add to myFT' | yes -`conceptId` | String | '00000-0000-00000-00000' | -`csrfToken` | String | 'testTokenValue' | -`extraButtonClasses` | String | null | yes -`followPlusDigestEmail` | Boolean | true | yes -`isSelected` | Boolean | false | yes -`name` | String | 'Test Name' | yes -`switchFollowDigestEmail` | Boolean | false | -`variant` | String | null | yes +Feature | Type | Required | Default value | Description +--------------------------|---------|----------|----------------|--------------- +`conceptId` | String | yes | none | UUID of the concept +`conceptName` | String | yes | none | Name of the concept +`buttonText` | String | no | "Add ${conceptName} to myFT" or "Remove ${conceptName} from myFT", depending on `isFollowed` | Text to show in the button. +`isFollowed` | Boolean | no | `false` | Whether the concept is followed or not. +`csrfToken` | String | no | none | value included in a hidden form field. +`variant` | String | no | `standard` | One of `standard`, `inverse`, `opinion` or `monochrome`. Other values will be ignored. +`followPlusDigestEmail` | Boolean | no | `false` | Whether following the topic should also subscribe to the digest. diff --git a/components/x-follow-button/src/Button.jsx b/components/x-follow-button/src/Button.jsx deleted file mode 100644 index ab24b5c23..000000000 --- a/components/x-follow-button/src/Button.jsx +++ /dev/null @@ -1,32 +0,0 @@ -import { h } from '@financial-times/x-engine'; -import styles from './styles/main.scss'; - -const Button = ({ - conceptId, - followPlusDigestEmail, - isSelected, - variant, - altButtonText, - buttonText, - name, - ...props -}) => ( - -); - -export default Button; diff --git a/components/x-follow-button/src/FollowButton.jsx b/components/x-follow-button/src/FollowButton.jsx index 2ea3bd65a..beacd946b 100644 --- a/components/x-follow-button/src/FollowButton.jsx +++ b/components/x-follow-button/src/FollowButton.jsx @@ -1,83 +1,75 @@ import { h } from '@financial-times/x-engine'; -import { withActions } from '@financial-times/x-interaction'; +import styles from './styles/main.scss'; -import Button from './Button'; +export const FollowButton = (props) => { + const { + buttonText = null, + conceptId, + conceptName = 'unnamed', + isFollowed, + csrfToken, + followPlusDigestEmail, + variant + } = props; -const followButtonActions = withActions(initialProps => ({ - triggerFollowUnfollow(rootElement) { - return currentProps => { - const detail = { - action: currentProps.isSelected ? 'remove' : 'add', - actorType: 'user', - actorId: null, // myft client sets to user id from session - relationshipName: 'followed', - subjectType: 'concept', - subjectId: initialProps.conceptId, - token: initialProps.csrfToken - }; + const getFormAction = () => { + if (followPlusDigestEmail) { + return `/__myft/api/core/follow-plus-digest-email/${conceptId}?method=put` + } else if (isFollowed) { + return `/__myft/api/core/followed/concept/${conceptId}?method=delete` + } else { + return `/__myft/api/core/followed/concept/${conceptId}?method=put` + } + }; - rootElement.dispatchEvent(new CustomEvent('x-follow-button', { bubbles: true, detail })); - }; - }, - followed() { - return { isSelected: true }; - }, - unfollowed() { - return { isSelected: false }; - } -})); + const followedConceptNameText = `Remove ${conceptName} from MyFT`; + const unfollowedConceptNameText = `Add ${conceptName} to MyFT`; -const getFormAction = (conceptId, followPlusDigestEmail, isSelected) => { - if (followPlusDigestEmail) { - return `/__myft/api/core/follow-plus-digest-email/${conceptId}?method=put` - } else if (isSelected) { - return `/__myft/api/core/followed/concept/${conceptId}?method=delete` - } else { - return `/__myft/api/core/followed/concept/${conceptId}?method=put` - } -}; - -const BaseFollowButton = ({ - conceptId, - csrfToken, - followPlusDigestEmail, - isSelected, - variant, - altButtonText, - buttonText, - actions, - name -}) => ( -
{ - event.preventDefault(); - actions.triggerFollowUnfollow(event.target); - }} - { ...(followPlusDigestEmail ? { 'data-myft-ui-variant': true } : null) }> - -
-
-`; - -exports[`@financial-times/x-increment renders a default Increment x-increment 1`] = ` -
- - 1 - - -
-`; - -exports[`@financial-times/x-styling-demo renders a default Styling x-styling-demo 1`] = ` - -`; - -exports[`@financial-times/x-teaser renders a Hero Article x-teaser 1`] = ` -
-
- - -

- FT investigation finds groping and sexual harassment at secretive black-tie dinner -

-
-
-
- -
-
-
-`; - -exports[`@financial-times/x-teaser renders a Hero Content Package x-teaser 1`] = ` - -`; - -exports[`@financial-times/x-teaser renders a Hero Opinion Piece x-teaser 1`] = ` -
-
- - -

- Today, hatred of Jews is mixed in with fights about Islam and Israel -

- -
-
-`; - -exports[`@financial-times/x-teaser renders a Hero Package item x-teaser 1`] = ` - -`; - -exports[`@financial-times/x-teaser renders a Hero Paid Post x-teaser 1`] = ` -
-
-
-
- - Paid post - - - by UBS - -
-
- -

- ESports is big business and about to get bigger: global revenues could hit $1.5bn by 2020 -

-
-
-
- -
-
-
-`; - -exports[`@financial-times/x-teaser renders a Hero Podcast x-teaser 1`] = ` -
-
-
-
- - Tech Tonic podcast - - - 12 mins - -
-
- -

- Hannah Kuchler talks to American social scientist and cyber security expert Andrea… -

-
-
-
- -
-
-
-`; - -exports[`@financial-times/x-teaser renders a Hero Top Story x-teaser 1`] = ` - -`; - -exports[`@financial-times/x-teaser renders a Hero Video x-teaser 1`] = ` -
-
-
-
- - Global Trade - - - 02:51min - -
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
- -
-
-`; - -exports[`@financial-times/x-teaser renders a HeroNarrow Article x-teaser 1`] = ` -
-
- - -

- FT investigation finds groping and sexual harassment at secretive black-tie dinner -

-
-
-
- -
-
-
-`; - -exports[`@financial-times/x-teaser renders a HeroNarrow Content Package x-teaser 1`] = ` -
-
- - -

- Prince Harry and Meghan Markle will tie the knot at Windsor Castle -

-
-
-
- -
-
-
-`; - -exports[`@financial-times/x-teaser renders a HeroNarrow Opinion Piece x-teaser 1`] = ` -
-
- - -

- Today, hatred of Jews is mixed in with fights about Islam and Israel -

- -
-
-`; - -exports[`@financial-times/x-teaser renders a HeroNarrow Package item x-teaser 1`] = ` -
-
- - -

- Martin Wolf on the power of vested interests in today’s rent-extracting economy -

-
-
-
- -
-
-
-`; - -exports[`@financial-times/x-teaser renders a HeroNarrow Paid Post x-teaser 1`] = ` -
-
-
-
- - Paid post - - - by UBS - -
-
- -

- ESports is big business and about to get bigger: global revenues could hit $1.5bn by 2020 -

-
-
-
- -
-
-
-`; - -exports[`@financial-times/x-teaser renders a HeroNarrow Podcast x-teaser 1`] = ` -
-
-
-
- - Tech Tonic podcast - - - 12 mins - -
-
- -

- Hannah Kuchler talks to American social scientist and cyber security expert Andrea… -

-
-
-
- -
-
-
-`; - -exports[`@financial-times/x-teaser renders a HeroNarrow Top Story x-teaser 1`] = ` - -`; - -exports[`@financial-times/x-teaser renders a HeroNarrow Video x-teaser 1`] = ` -
-
-
-
- - Global Trade - - - 02:51min - -
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
- -

- The FT's Rob Armstrong looks at why Donald Trump is pushing trade tariffs -

-
-
-`; - -exports[`@financial-times/x-teaser renders a HeroOverlay Article x-teaser 1`] = ` -
-
- - -

- FT investigation finds groping and sexual harassment at secretive black-tie dinner -

-
-
-
- -
-
-
-`; - -exports[`@financial-times/x-teaser renders a HeroOverlay Content Package x-teaser 1`] = ` - -`; - -exports[`@financial-times/x-teaser renders a HeroOverlay Opinion Piece x-teaser 1`] = ` -
-
- - -

- Today, hatred of Jews is mixed in with fights about Islam and Israel -

- -
-
-`; - -exports[`@financial-times/x-teaser renders a HeroOverlay Package item x-teaser 1`] = ` - -`; - -exports[`@financial-times/x-teaser renders a HeroOverlay Paid Post x-teaser 1`] = ` -
-
-
-
- - Paid post - - - by UBS - -
-
- -

- ESports is big business and about to get bigger: global revenues could hit $1.5bn by 2020 -

-
-
-
- -
-
-
-`; - -exports[`@financial-times/x-teaser renders a HeroOverlay Podcast x-teaser 1`] = ` -
-
-
-
- - Tech Tonic podcast - - - 12 mins - -
-
- -

- Hannah Kuchler talks to American social scientist and cyber security expert Andrea… -

-
-
-
- -
-
-
-`; - -exports[`@financial-times/x-teaser renders a HeroOverlay Top Story x-teaser 1`] = ` - -`; - -exports[`@financial-times/x-teaser renders a HeroOverlay Video x-teaser 1`] = ` -
-
-
-
- - Global Trade - - - 02:51min - -
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
- -
-
-`; - -exports[`@financial-times/x-teaser renders a HeroVideo Article x-teaser 1`] = ` -
-
- - -

- FT investigation finds groping and sexual harassment at secretive black-tie dinner -

-
-
-
- -
-
-
-`; - -exports[`@financial-times/x-teaser renders a HeroVideo Content Package x-teaser 1`] = ` - -`; - -exports[`@financial-times/x-teaser renders a HeroVideo Opinion Piece x-teaser 1`] = ` -
-
- - -

- Today, hatred of Jews is mixed in with fights about Islam and Israel -

- -
-
-`; - -exports[`@financial-times/x-teaser renders a HeroVideo Package item x-teaser 1`] = ` - -`; - -exports[`@financial-times/x-teaser renders a HeroVideo Paid Post x-teaser 1`] = ` -
-
-
-
- - Paid post - - - by UBS - -
-
- -

- ESports is big business and about to get bigger: global revenues could hit $1.5bn by 2020 -

-
-
-
- -
-
-
-`; - -exports[`@financial-times/x-teaser renders a HeroVideo Podcast x-teaser 1`] = ` -
-
-
-
- - Tech Tonic podcast - - - 12 mins - -
-
- -

- Hannah Kuchler talks to American social scientist and cyber security expert Andrea… -

-
-
-
- -
-
-
-`; - -exports[`@financial-times/x-teaser renders a HeroVideo Top Story x-teaser 1`] = ` - -`; - -exports[`@financial-times/x-teaser renders a HeroVideo Video x-teaser 1`] = ` -
-
-
-
- - Global Trade - - - 02:51min - -
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
- -
-
-`; - -exports[`@financial-times/x-teaser renders a Large Article x-teaser 1`] = ` -
-
- - -

- FT investigation finds groping and sexual harassment at secretive black-tie dinner -

-
-
-
- -
-
-
-`; - -exports[`@financial-times/x-teaser renders a Large Content Package x-teaser 1`] = ` -
-
- - -

- Prince Harry and Meghan Markle will tie the knot at Windsor Castle -

-
-
-
- -
-
-
-`; - -exports[`@financial-times/x-teaser renders a Large Opinion Piece x-teaser 1`] = ` -
-
- - -

- Today, hatred of Jews is mixed in with fights about Islam and Israel -

- -
-
-`; - -exports[`@financial-times/x-teaser renders a Large Package item x-teaser 1`] = ` -
-
- - -

- Martin Wolf on the power of vested interests in today’s rent-extracting economy -

-
-
-
- -
-
-
-`; - -exports[`@financial-times/x-teaser renders a Large Paid Post x-teaser 1`] = ` -
-
-
-
- - Paid post - - - by UBS - -
-
- -

- ESports is big business and about to get bigger: global revenues could hit $1.5bn by 2020 -

-
-
-
- -
-
-
-`; - -exports[`@financial-times/x-teaser renders a Large Podcast x-teaser 1`] = ` -
-
-
-
- - Tech Tonic podcast - - - 12 mins - -
-
- -

- Hannah Kuchler talks to American social scientist and cyber security expert Andrea… -

-
-
-
- -
-
-
-`; - -exports[`@financial-times/x-teaser renders a Large Top Story x-teaser 1`] = ` - -`; - -exports[`@financial-times/x-teaser renders a Large Video x-teaser 1`] = ` -
-
-
-
- - Global Trade - - - 02:51min - -
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
- -

- The FT's Rob Armstrong looks at why Donald Trump is pushing trade tariffs -

-
-
-`; - -exports[`@financial-times/x-teaser renders a Small Article x-teaser 1`] = ` -
-
- - -

- FT investigation finds groping and sexual harassment at secretive black-tie dinner -

-
-
-
- -
-
-
-`; - -exports[`@financial-times/x-teaser renders a Small Content Package x-teaser 1`] = ` - -`; - -exports[`@financial-times/x-teaser renders a Small Opinion Piece x-teaser 1`] = ` -
-
- - -

- Today, hatred of Jews is mixed in with fights about Islam and Israel -

- -
-
-`; - -exports[`@financial-times/x-teaser renders a Small Package item x-teaser 1`] = ` - -`; - -exports[`@financial-times/x-teaser renders a Small Paid Post x-teaser 1`] = ` -
-
-
-
- - Paid post - - - by UBS - -
-
- -

- ESports is big business and about to get bigger: global revenues could hit $1.5bn by 2020 -

-
-
-
- -
-
-
-`; - -exports[`@financial-times/x-teaser renders a Small Podcast x-teaser 1`] = ` -
-
-
-
- - Tech Tonic podcast - - - 12 mins - -
-
- -

- Hannah Kuchler talks to American social scientist and cyber security expert Andrea… -

-
-
-
- -
-
-
-`; - -exports[`@financial-times/x-teaser renders a Small Top Story x-teaser 1`] = ` - -`; - -exports[`@financial-times/x-teaser renders a Small Video x-teaser 1`] = ` -
-
-
-
- - Global Trade - - - 02:51min - -
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
- -
-
-`; - -exports[`@financial-times/x-teaser renders a SmallHeavy Article x-teaser 1`] = ` -
-
- - -

- FT investigation finds groping and sexual harassment at secretive black-tie dinner -

-
-
-
- -
-
-
-`; - -exports[`@financial-times/x-teaser renders a SmallHeavy Content Package x-teaser 1`] = ` -
-
- - -

- Prince Harry and Meghan Markle will tie the knot at Windsor Castle -

-
-
-
- -
-
-
-`; - -exports[`@financial-times/x-teaser renders a SmallHeavy Opinion Piece x-teaser 1`] = ` -
-
- - -

- Today, hatred of Jews is mixed in with fights about Islam and Israel -

- -
-
-`; - -exports[`@financial-times/x-teaser renders a SmallHeavy Package item x-teaser 1`] = ` -
-
- - -

- Martin Wolf on the power of vested interests in today’s rent-extracting economy -

-
-
-
- -
-
-
-`; - -exports[`@financial-times/x-teaser renders a SmallHeavy Paid Post x-teaser 1`] = ` -
-
-
-
- - Paid post - - - by UBS - -
-
- -

- ESports is big business and about to get bigger: global revenues could hit $1.5bn by 2020 -

-
-
-
- -
-
-
-`; - -exports[`@financial-times/x-teaser renders a SmallHeavy Podcast x-teaser 1`] = ` -
-
-
-
- - Tech Tonic podcast - - - 12 mins - -
-
- -

- Hannah Kuchler talks to American social scientist and cyber security expert Andrea… -

-
-
-
- -
-
-
-`; - -exports[`@financial-times/x-teaser renders a SmallHeavy Top Story x-teaser 1`] = ` - -`; - -exports[`@financial-times/x-teaser renders a SmallHeavy Video x-teaser 1`] = ` -
-
-
-
- - Global Trade - - - 02:51min - -
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
- -

- The FT's Rob Armstrong looks at why Donald Trump is pushing trade tariffs -

-
-
-`; - -exports[`@financial-times/x-teaser renders a TopStory Article x-teaser 1`] = ` -
-
- - -

- FT investigation finds groping and sexual harassment at secretive black-tie dinner -

-
-
-
- -
-
-
-`; - -exports[`@financial-times/x-teaser renders a TopStory Content Package x-teaser 1`] = ` -
-
- - -

- Prince Harry and Meghan Markle will tie the knot at Windsor Castle -

-
-
-
- -
-
-
-`; - -exports[`@financial-times/x-teaser renders a TopStory Opinion Piece x-teaser 1`] = ` -
-
- - -

- Today, hatred of Jews is mixed in with fights about Islam and Israel -

- -
-
-`; - -exports[`@financial-times/x-teaser renders a TopStory Package item x-teaser 1`] = ` -
-
- - -

- Martin Wolf on the power of vested interests in today’s rent-extracting economy -

-
-
-
- -
-
-
-`; - -exports[`@financial-times/x-teaser renders a TopStory Paid Post x-teaser 1`] = ` -
-
-
-
- - Paid post - - - by UBS - -
-
- -

- ESports is big business and about to get bigger: global revenues could hit $1.5bn by 2020 -

-
-
-
- -
-
-
-`; - -exports[`@financial-times/x-teaser renders a TopStory Podcast x-teaser 1`] = ` +exports[`@financial-times/x-gift-article renders a default Free article x-gift-article 1`] = `
From 21a1cc07a0397bf1a58f9981832d42ddc90de6ed Mon Sep 17 00:00:00 2001 From: Katerina Loschinina Date: Fri, 15 Nov 2019 15:51:58 +0000 Subject: [PATCH 32/35] Refactor x-follow-button storybook demo - Use regular story configuration - Clean code from the old-styled stories --- .storybook/config.js | 1 + .storybook/register-components.js | 3 +- .../x-follow-button/stories/follow-button.js | 23 ----------- components/x-follow-button/stories/index.js | 14 ------- components/x-follow-button/stories/knobs.js | 21 ---------- .../x-follow-button/storybook/index.jsx | 39 +++++++++++++++++++ 6 files changed, 41 insertions(+), 60 deletions(-) delete mode 100644 components/x-follow-button/stories/follow-button.js delete mode 100644 components/x-follow-button/stories/index.js delete mode 100644 components/x-follow-button/stories/knobs.js create mode 100644 components/x-follow-button/storybook/index.jsx diff --git a/.storybook/config.js b/.storybook/config.js index f9260ac8d..b21eb1dca 100644 --- a/.storybook/config.js +++ b/.storybook/config.js @@ -13,4 +13,5 @@ configure(() => { // Add regular story definitions (i.e. those using storiesOf() directly below) require('../components/x-increment/storybook/index.jsx'); + require('../components/x-follow-button/storybook/index.jsx'); }, module); diff --git a/.storybook/register-components.js b/.storybook/register-components.js index eeba6aee4..071883d9f 100644 --- a/.storybook/register-components.js +++ b/.storybook/register-components.js @@ -4,8 +4,7 @@ const components = [ require('../components/x-teaser/storybook'), require('../components/x-styling-demo/stories'), require('../components/x-gift-article/stories'), - require('../components/x-podcast-launchers/stories'), - require('../components/x-follow-button/stories') + require('../components/x-podcast-launchers/stories') ]; module.exports = components; diff --git a/components/x-follow-button/stories/follow-button.js b/components/x-follow-button/stories/follow-button.js deleted file mode 100644 index ded6ff6d1..000000000 --- a/components/x-follow-button/stories/follow-button.js +++ /dev/null @@ -1,23 +0,0 @@ -exports.title = 'Follow Button'; - -const data = { - isFollowed: false, - variant: 'standard', - conceptNameAsButtonText: false, - conceptId: '00000-0000-00000-00000', - conceptName: 'UK politics & policy', - followPlusDigestEmail: true, - csrfToken: 'testTokenValue' -}; - -// This data will provide defaults for the Knobs defined below and used -// to render examples in the documentation site. -exports.data = data; - -// A list of properties to pass to the component when rendered in Storybook. If a Knob -// exists for the property then it will be editable with the default as defined above. -exports.knobs = Object.keys(data); - -// This reference is only required for hot module loading in development -// -exports.m = module; diff --git a/components/x-follow-button/stories/index.js b/components/x-follow-button/stories/index.js deleted file mode 100644 index 9d5205a7a..000000000 --- a/components/x-follow-button/stories/index.js +++ /dev/null @@ -1,14 +0,0 @@ -const { FollowButton } = require('../'); - -module.exports = { - component: FollowButton, - dependencies: { - 'o-fonts': '^3.0.0', - 'o-typography': '^5.5.0' - }, - stories: [ - require('./follow-button') - ], - knobs: require('./knobs'), - package: require('../package.json') -}; diff --git a/components/x-follow-button/stories/knobs.js b/components/x-follow-button/stories/knobs.js deleted file mode 100644 index 331320f73..000000000 --- a/components/x-follow-button/stories/knobs.js +++ /dev/null @@ -1,21 +0,0 @@ -// To ensure that component stories do not need to depend on Storybook themselves we return a -// function that may be passed the required dependencies. -module.exports = (data, { text, boolean, select }) => { - return { - conceptNameAsButtonText () { - return boolean('conceptNameAsButtonText', data.conceptNameAsButtonText); - }, - isFollowed () { - return boolean('isFollowed', data.isFollowed); - }, - conceptName () { - return text('Topic name', data.conceptName); - }, - followPlusDigestEmail () { - return boolean('followPlusDigestEmail', data.followPlusDigestEmail); - }, - variant () { - return select('variant', [ 'standard', 'inverse', 'opinion', 'monochrome' ], data.variant); - } - }; -}; diff --git a/components/x-follow-button/storybook/index.jsx b/components/x-follow-button/storybook/index.jsx new file mode 100644 index 000000000..419b73185 --- /dev/null +++ b/components/x-follow-button/storybook/index.jsx @@ -0,0 +1,39 @@ +import React from 'react'; + +import { storiesOf } from '@storybook/react'; +import { withKnobs, text, boolean, select } from '@storybook/addon-knobs'; + +import { FollowButton } from '../src/FollowButton'; + +const defaultProps = { + isFollowed: false, + variant: 'standard', + conceptNameAsButtonText: false, + conceptId: '00000-0000-00000-00000', + conceptName: 'UK politics & policy', + followPlusDigestEmail: true, + csrfToken: 'testTokenValue' +}; + +const toggleConceptNameAsButtonText = () => + boolean('conceptNameAsButtonText', defaultProps.conceptNameAsButtonText); +const toggleIsFollowed = () => boolean('isFollowed', defaultProps.isFollowed); +const toggleConceptName = () => text('Topic name', defaultProps.conceptName); +const toggleFollowPlusDigestEmail = () => + boolean('followPlusDigestEmail', defaultProps.followPlusDigestEmail); +const toggleVariant = () => + select('variant', ['standard', 'inverse', 'opinion', 'monochrome'], defaultProps.variant); + +storiesOf('x-follow-button', module) + .addDecorator(withKnobs) + .add('Follow Button', () => { + const knobs = { + conceptNameAsButtonText: toggleConceptNameAsButtonText(), + isFollowed: toggleIsFollowed(), + conceptName: toggleConceptName(), + followPlusDigestEmail: toggleFollowPlusDigestEmail(), + variant: toggleVariant() + }; + + return ; + }); From 4b5843bef05ea9327a4daddc61cb2a6490503f90 Mon Sep 17 00:00:00 2001 From: Katerina Loschinina Date: Tue, 19 Nov 2019 16:26:43 +0000 Subject: [PATCH 33/35] Add tests - Test conditional behaviour - Modify jest configuration to accomodate tests in the new setup --- __mocks__/styleMock.js | 1 + .../__tests__/x-follow-button.test.jsx | 174 ++++++++++++++++++ components/x-follow-button/package.json | 1 + .../x-follow-button/src/FollowButton.jsx | 22 +-- jest.config.js | 5 +- 5 files changed, 190 insertions(+), 13 deletions(-) create mode 100644 __mocks__/styleMock.js create mode 100644 components/x-follow-button/__tests__/x-follow-button.test.jsx diff --git a/__mocks__/styleMock.js b/__mocks__/styleMock.js new file mode 100644 index 000000000..f053ebf79 --- /dev/null +++ b/__mocks__/styleMock.js @@ -0,0 +1 @@ +module.exports = {}; diff --git a/components/x-follow-button/__tests__/x-follow-button.test.jsx b/components/x-follow-button/__tests__/x-follow-button.test.jsx new file mode 100644 index 000000000..ae149fc84 --- /dev/null +++ b/components/x-follow-button/__tests__/x-follow-button.test.jsx @@ -0,0 +1,174 @@ +const { h } = require('@financial-times/x-engine'); +const { mount } = require('@financial-times/x-test-utils/enzyme'); + +import { FollowButton } from '../src/FollowButton'; + +describe('x-follow-button', () => { + describe('concept name', () => { + it('when conceptNameAsButtonText prop is true, and the topic name is provided, the button is named by this name', () => { + const subject = mount( + + ); + expect(subject.find('button').text()).toEqual('dummy concept name'); + }); + + it('when conceptNameAsButtonText prop is false, the button has a default name', () => { + const subject = mount( + + ); + expect(subject.find('button').text()).toEqual('Add to myFT'); + }); + + it('when conceptNameAsButtonText prop is true, and the topic name is not provided, the button has a default name', () => { + const subject = mount(); + expect(subject.find('button').text()).toEqual('Add to myFT'); + }); + + it('when conceptNameAsButtonText prop is not provided, the button has a default name', () => { + const subject = mount(); + expect(subject.find('button').text()).toEqual('Add to myFT'); + }); + }); + describe('conceptId prop', () => { + it('assigns conceptId prop to data-concept-id attribute of the button', async () => { + const subject = mount(); + expect(subject.find('button').prop('data-concept-id')).toEqual('dummy-id'); + }); + + it('assigns conceptId prop to data-concept-id attribute of the form', async () => { + const subject = mount(); + expect(subject.find('form').prop('data-concept-id')).toEqual('dummy-id'); + }); + }); + + describe('form action', () => { + it('assigns follow-plus-digest-email put action if followPlusDigestEmail is true', async () => { + const subject = mount(); + expect(subject.find('form').prop('action')).toEqual( + '/__myft/api/core/follow-plus-digest-email/dummy-id?method=put' + ); + }); + + it('assigns followed/concept delete action if isFollowed is true', async () => { + const subject = mount(); + expect(subject.find('form').prop('action')).toEqual( + '/__myft/api/core/followed/concept/dummy-id?method=delete' + ); + }); + + it('assigns followed/concept put action if isFollowed and followPlusDigestEmail are not passed', async () => { + const subject = mount(); + expect(subject.find('form').prop('action')).toEqual( + '/__myft/api/core/followed/concept/dummy-id?method=put' + ); + }); + }); + + describe('isFollowed', () => { + describe('when true', () => { + it('button text is "Added"', () => { + const subject = mount(); + expect(subject.find('button').text()).toEqual('Added'); + }); + + it('button aria-pressed is "true"', () => { + const subject = mount(); + expect(subject.find('button').prop('aria-pressed')).toEqual('true'); + }); + + it('button title is "Remove ConceptName from myFT"', () => { + const subject = mount(); + expect(subject.find('button').prop('title')).toEqual('Remove ConceptName from myFT'); + }); + + it('button aria-label is "Remove conceptName from myFT"', () => { + const subject = mount(); + expect(subject.find('button').prop('aria-label')).toEqual('Remove ConceptName from myFT'); + }); + }); + + describe('when false', () => { + it('button text is "Add to myFT"', () => { + const subject = mount(); + expect(subject.find('button').text()).toEqual('Add to myFT'); + }); + + it('button aria-pressed is "false"', () => { + const subject = mount(); + expect(subject.find('button').prop('aria-pressed')).toEqual('false'); + }); + + it('button title is "Add ConceptName to myFT"', () => { + const subject = mount(); + expect(subject.find('button').prop('title')).toEqual('Add ConceptName to myFT'); + }); + + it('button aria-label is "Add ConceptName to myFT"', () => { + const subject = mount(); + expect(subject.find('button').prop('aria-label')).toEqual('Add ConceptName to myFT'); + }); + }); + }); + + describe('followPlusDigestEmail', () => { + describe('when true', () => { + it('form has data-myft-ui-variant property which is true', () => { + const subject = mount(); + expect(subject.find('form').prop('data-myft-ui-variant')).toEqual(true); + }); + + it('button has data-trackable-context-messaging property which is add-to-myft-plus-digest-button', () => { + const subject = mount(); + expect(subject.find('button').prop('data-trackable-context-messaging')).toEqual( + 'add-to-myft-plus-digest-button' + ); + }); + }); + + describe('when false', () => { + it('form has data-myft-ui-variant property which is true', () => { + const subject = mount(); + expect(subject.find('form').prop('data-myft-ui-variant')).toEqual(undefined); + }); + + it('button has data-trackable-context-messaging property which is add-to-myft-plus-digest-button', () => { + const subject = mount(); + expect(subject.find('button').prop('data-trackable-context-messaging')).toEqual(null); + }); + }); + }); + + describe('form properties', () => { + it('method = GET', () => { + const subject = mount(); + expect(subject.find('form').prop('method')).toEqual('GET'); + }); + }); + + describe('button properties', () => { + it('data-trackable="follow"', () => { + const subject = mount(); + expect(subject.find('button').prop('data-trackable')).toEqual('follow'); + }); + + it('type="submit"', () => { + const subject = mount(); + expect(subject.find('button').prop('type')).toEqual('submit'); + }); + }); + + describe('csrf token', () => { + it('if passed creates an invisible input field', () => { + const subject = mount(); + expect(subject.find('input').prop('value')).toEqual('dummyToken'); + expect(subject.find('input').prop('type')).toEqual('hidden'); + expect(subject.find('input').prop('name')).toEqual('token'); + expect(subject.find('input').prop('data-myft-csrf-token')).toEqual(true); + }); + + it('if not passed an invisible input field is not created', () => { + const subject = mount(); + expect(subject.find('input')).toEqual({}); + }); + }); +}); diff --git a/components/x-follow-button/package.json b/components/x-follow-button/package.json index fdb286668..a873b6f42 100644 --- a/components/x-follow-button/package.json +++ b/components/x-follow-button/package.json @@ -16,6 +16,7 @@ "license": "ISC", "devDependencies": { "@financial-times/x-rollup": "file:../../packages/x-rollup", + "@financial-times/x-test-utils": "file:../../packages/x-test-utils", "rollup": "^0.57.1", "bower": "^1.7.9", "node-sass": "^4.9.2" diff --git a/components/x-follow-button/src/FollowButton.jsx b/components/x-follow-button/src/FollowButton.jsx index dab4dc9f3..807e28a72 100644 --- a/components/x-follow-button/src/FollowButton.jsx +++ b/components/x-follow-button/src/FollowButton.jsx @@ -17,30 +17,31 @@ export const FollowButton = (props) => { const getFormAction = () => { if (followPlusDigestEmail) { - return `/__myft/api/core/follow-plus-digest-email/${conceptId}?method=put` + return `/__myft/api/core/follow-plus-digest-email/${conceptId}?method=put`; } else if (isFollowed) { - return `/__myft/api/core/followed/concept/${conceptId}?method=delete` + return `/__myft/api/core/followed/concept/${conceptId}?method=delete`; } else { - return `/__myft/api/core/followed/concept/${conceptId}?method=put` + return `/__myft/api/core/followed/concept/${conceptId}?method=put`; } }; const getButtonText = () => { - if (conceptNameAsButtonText) { + if (conceptNameAsButtonText && conceptName) { return conceptName; } - return isFollowed ? `Added` : `Add to myFT`; + return isFollowed ? 'Added' : 'Add to myFT'; }; - const getAccessibleText = () => isFollowed ? `Remove ${conceptName} from myFT` : `Add ${conceptName} to myFT`; + const getAccessibleText = () => + isFollowed ? `Remove ${conceptName} from myFT` : `Add ${conceptName} to myFT`; return (
{ + onSubmit={(event) => { event.preventDefault(); const detail = { action: isFollowed ? 'remove' : 'add', @@ -59,10 +60,7 @@ export const FollowButton = (props) => { event.target.dispatchEvent(new CustomEvent('x-follow-button', { bubbles: true, detail })); }} {...(followPlusDigestEmail ? { 'data-myft-ui-variant': true } : null)}> - {csrfToken && } + {csrfToken && }