Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add Stories for o-banner demos #523

Merged
merged 9 commits into from
Jan 11, 2022
4 changes: 2 additions & 2 deletions components/o-banner/demos/src/custom.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<p>Limited time only</p>
<h1>You qualify for a special offer: Save 33%</h1>
</header>
<p>Pay just $4.29 per week for annual Standard Digital access.</p>
<p>Pay just $4.29 per week for annual Standard Digital access.</p>
<ul>
<li>Global news and opinion from experts in 50+ countries</li>
<li>Access on desktop and mobile</li>
Expand All @@ -19,7 +19,7 @@
<p>Limited time only</p>
<h1>You qualify for a special offer: Save 33%</h1>
</header>
<p>Pay just $4.29 per week for annual Standard Digital access.</p>
<p>Pay just $4.29 per week for annual Standard Digital access.</p>
</div>
<div class="o-banner__actions">
<div class="o-banner__action">
Expand Down
1 change: 1 addition & 0 deletions components/o-banner/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Banner from './src/js/banner.js';

function constructAll () {
Banner.init();

document.removeEventListener('o.DOMContentLoaded', constructAll);
}

Expand Down
12 changes: 11 additions & 1 deletion components/o-banner/src/js/banner.js
Original file line number Diff line number Diff line change
Expand Up @@ -270,10 +270,20 @@ class Banner {
}, {});
}

/**
* Undo the init method
*/
destroy() {
notlee marked this conversation as resolved.
Show resolved Hide resolved
if(this.closeButtonElement) {
this.closeButtonElement.remove();
delete this.closeButtonElement;
}
}

/**
* Initialise banner components.
*
* @param {(HTMLElement | string)} rootElement - The root element to intialise banners in, or a CSS selector for the root element
* @param {(HTMLElement | string)} [rootElement] - The root element to intialise banners in, or a CSS selector for the root element
* @param {object} [options={}] - An options object for configuring the banners
* @returns {Banner | Banner[]} - The newly instantiated Banner (or Banners, if rootElement was not a banner)
*/
Expand Down
109 changes: 109 additions & 0 deletions components/o-banner/src/tsx/banner.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
export interface BannerSubmitButton {
action: string;
encoding: 'application/x-www-form-urlencoded' | 'multipart/form-data' | 'text/plain';
method: 'GET' | 'HEAD' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
copy: string;
}

export interface BannerLink {
copy: string;
url: string;
}

export interface BannerProps {
heading?: string;
abbreviatedHeading?: string;
content: string;
abbreviatedContent?: string;
showCloseButton?: boolean;
closeButtonLabel?: string;
primaryAction?: BannerLink | BannerSubmitButton;
secondaryAction?: BannerLink
theme: 'product' | 'marketing' | '';
layout: 'small' | 'compact' | '';
}

function isBannerSubmitButton(action: BannerSubmitButton | BannerLink): action is BannerSubmitButton {
return (action as BannerSubmitButton).action !== undefined;
}

export function Banner({
showCloseButton = true,
heading = '',
abbreviatedHeading = '',
content = 'Hello!',
abbreviatedContent = '',
closeButtonLabel = 'Close',
primaryAction = {
copy: 'OK',
url: '#',
},
secondaryAction = undefined,
theme = '',
layout = '',
}: BannerProps) {
const classNames = ['o-banner'];
const dataAttributes = {};

if(layout) {
classNames.push(`o-banner--${layout}`);
}

if(theme) {
classNames.push(`o-banner--${theme}`);
}

if(!showCloseButton) {
dataAttributes['data-o-banner-suppress-close-button'] = true;
}

if(showCloseButton && closeButtonLabel) {
dataAttributes['data-o-banner-close-button-label'] = closeButtonLabel;
}

return (
<div className={classNames.join(' ')} {...dataAttributes} data-o-component="o-banner">
<div className="o-banner__outer">
<div className="o-banner__inner" data-o-banner-inner>
{content && (
<div className="o-banner__content o-banner__content--long">
{heading && (
<header className="o-banner__heading">
<h2>{heading}</h2>
</header>
)}
<p>{content}</p>
</div>
)}
{(abbreviatedContent || abbreviatedHeading) && (
<div className="o-banner__content o-banner__content--short">
{abbreviatedHeading && (
<header className="o-banner__heading">
<h2>{abbreviatedHeading || heading}</h2>
</header>
)}
<p>{abbreviatedContent || content}</p>
</div>
)}
<div className="o-banner__actions">
{primaryAction && !isBannerSubmitButton(primaryAction) && (
<div className="o-banner__action">
<a href="{primaryAction.url}" className="o-banner__button">{primaryAction.copy}</a>
</div>
)}
{primaryAction && isBannerSubmitButton(primaryAction) && (
<form className="o-banner__action" method={primaryAction.method} encType={primaryAction.encoding} action={primaryAction.action}>
<input className="o-banner__button" type="submit" value={primaryAction.copy} />
</form>
)}
{secondaryAction && (
<div className="o-banner__action o-banner__action--secondary">
<a href="{secondaryAction.url}" className="o-banner__link">{secondaryAction.copy}</a>
</div>
)}
</div>
</div>
</div>
</div>
);
}
7 changes: 7 additions & 0 deletions components/o-banner/stories/banner.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
@import "@financial-times/o-fonts/main";
@import "@financial-times/o-normalise/main";
@include oFonts();
@include oNormalise();

@import "@financial-times/o-banner/main";
@include oBanner();
166 changes: 166 additions & 0 deletions components/o-banner/stories/banner.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
import {withDesign} from 'storybook-addon-designs';
import {Banner} from '../src/tsx/banner';
import {useEffect} from 'react';
import javascript from '../main';
import './banner.scss';
import withHtml from 'origami-storybook-addon-html';

export default {
title: 'Banner',
component: Banner,
decorators: [withDesign, withHtml],
args: {
},
parameters: {
design: {
type: 'figma',
url: 'https://www.figma.com/file/MyHQ1qdwYyek5IBdhEEaND/FT-UI-Library?node-id=0%3A1489',
},
guidelines: {
notion: '37cdc7ac2cac4d60a4c9f451c47a4647',
JakeChampion marked this conversation as resolved.
Show resolved Hide resolved
},
html: {},
},
};

const Story = args => {
useEffect(() => {
let banners = javascript.init();
return function cleanup() {
banners = Array.isArray(banners) ? banners : [banners];
banners.forEach(banner => banner.destroy());
}
}, [args.showCloseButton, args.closeButtonLabel]);
return <Banner {...args} />;
};

export const Default = Story.bind({});
Default.args = {
content: 'Try the new compact homepage. A list view of today\'s homepage with fewer images.',
abbreviatedContent: 'Try it now',
showCloseButton: true,
closeButtonLabel: 'Close',
primaryAction: {
copy: 'Try it now',
url: '#',
},
secondaryAction: {
copy: 'Give feedback',
url: '#'
},
theme: '',
layout: ''
};
// Exclude layout and heading controls to discourage use of the default layout with a heading.
// https://github.com/Financial-Times/origami/pull/523
Default.parameters = {controls: { exclude: ['heading', 'abbreviatedHeading', 'layout'] }};

export const FormPrimaryAction = Story.bind({});
FormPrimaryAction.args = {
content: 'Try the new compact homepage. A list view of today\'s homepage with fewer images.',
abbreviatedContent: 'Try it now',
showCloseButton: true,
closeButtonLabel: 'Close',
primaryAction: {
action: '#',
encoding: 'application/x-www-form-urlencoded',
method: 'POST',
copy: 'Try it now'
},
secondaryAction: {
copy: 'Give feedback',
url: '#'
},
theme: '',
layout: ''
};
// Exclude layout and heading controls to discourage use of the default layout with a heading.
// https://github.com/Financial-Times/origami/pull/523
FormPrimaryAction.parameters = {controls: { exclude: ['heading', 'abbreviatedHeading', 'layout'] }};


export const Small = Story.bind({});
Small.args = {
heading: 'FT Compact',
abbreviatedHeading: 'FT Compact',
content: 'Try the new compact homepage. A list view of today\'s homepage with fewer images.',
abbreviatedContent: 'Try it now',
showCloseButton: true,
closeButtonLabel: 'Close',
primaryAction: {
copy: 'Try it now',
url: '#',
},
secondaryAction: {
copy: 'Give feedback',
url: '#'
},
theme: '',
layout: 'small'
};
// Exclude layout to discourage use of the default layout with a heading.
// https://github.com/Financial-Times/origami/pull/523
Small.parameters = {controls: { exclude: ['layout'] }};

export const Compact = Story.bind({});
Compact.args = {
heading: 'FT Compact',
abbreviatedHeading: 'FT Compact',
content: 'Try the new compact homepage. A list view of today\'s homepage with fewer images.',
abbreviatedContent: 'Try it now',
showCloseButton: true,
closeButtonLabel: 'Close',
primaryAction: {
copy: 'Try it now',
url: '#',
},
secondaryAction: {
copy: 'Give feedback',
url: '#'
},
theme: '',
layout: 'compact'
};
// Exclude layout to discourage use of the default layout with a heading.
// https://github.com/Financial-Times/origami/pull/523
Compact.parameters = {controls: { exclude: ['layout'] }};

export const Marketing = Story.bind({});
Marketing.args = {
heading: 'FT Compact',
abbreviatedHeading: 'FT Compact',
content: 'Try the new compact homepage. A list view of today\'s homepage with fewer images.',
abbreviatedContent: 'Try it now',
showCloseButton: true,
closeButtonLabel: 'Close',
primaryAction: {
copy: 'Try it now',
url: '#',
},
secondaryAction: {
copy: 'Give feedback',
url: '#'
},
theme: 'marketing',
layout: 'small'
};

export const Product = Story.bind({});
Product.args = {
heading: 'FT Compact',
abbreviatedHeading: 'FT Compact',
content: 'Try the new compact homepage. A list view of today\'s homepage with fewer images.',
abbreviatedContent: 'Try it now',
showCloseButton: true,
closeButtonLabel: 'Close',
primaryAction: {
copy: 'Try it now',
url: '#',
},
secondaryAction: {
copy: 'Give feedback',
url: '#'
},
theme: 'product',
layout: 'small'
};
19 changes: 19 additions & 0 deletions components/o-banner/test/banner.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1053,4 +1053,23 @@ describe('Banner', () => {

});

describe('.destroy', () => {

beforeEach(() => {
testArea.innerHTML = mainFixture;
});

describe('when the banner is initialised with a close button', () => {
it('removes the close button created on init', () => {
const banner = Banner.init()[0];
const closeButtonElement = banner.closeButtonElement;
proclaim.isTrue(document.body.contains(closeButtonElement), 'Close button not added to the document as expected.');
proclaim.isInstanceOf(banner.closeButtonElement, HTMLElement, 'Banner does not have a reference to a close button as expected.');
banner.destroy();
proclaim.isUndefined(banner.closeButtonElement, 'The destroy method did not remove the Banner\'s reference to its close button as expected.');
proclaim.isFalse(document.body.contains(closeButtonElement), 'The close button created on Banner init is still in the document after calling the destroy method.');

});
});
});
});
Loading