Skip to content

Commit

Permalink
Merge pull request #3076 from salesforce-ux/feature/welcome-mat
Browse files Browse the repository at this point in the history
feat(welcome-mat): add welcome mat component, docs, and tests
  • Loading branch information
zahnster committed Feb 15, 2018
2 parents 73989b0 + d8eee45 commit 5d2d818
Show file tree
Hide file tree
Showing 28 changed files with 7,146 additions and 18 deletions.
Binary file added assets/images/welcome-mat/bg-info@2x.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/images/welcome-mat/trailhead_badge@2x.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1,408 changes: 1,408 additions & 0 deletions test/__tests__/__snapshots__/Welcome_Mat_Base.json

Large diffs are not rendered by default.

1,408 changes: 1,408 additions & 0 deletions test/__tests__/__snapshots__/Welcome_Mat_Partially_Completed_Steps.json

Large diffs are not rendered by default.

1,528 changes: 1,528 additions & 0 deletions test/__tests__/__snapshots__/Welcome_Mat_Trailhead.json

Large diffs are not rendered by default.

1,470 changes: 1,470 additions & 0 deletions test/__tests__/__snapshots__/Welcome_Mat_Trailhead_Complete.json

Large diffs are not rendered by default.

6 changes: 5 additions & 1 deletion ui/components/_index.scss
Expand Up @@ -239,4 +239,8 @@
'regions/base/index',

// Vertical Tabs
'vertical-tabs/base/index';
'vertical-tabs/base/index',

// Welcome Mat
'welcome-mat/base/index',
'welcome-mat/trailhead-connected/index';
41 changes: 24 additions & 17 deletions ui/components/visual-picker/link/example.jsx
Expand Up @@ -10,30 +10,37 @@ import { UtilityIcon } from '../../icons/base/example';
// Partial(s)
/// ////////////////////////////////////////

export let VisualPickerMediaObject = props => (
<a
href="javascript:void(0);"
className={classNames(
'slds-box slds-box_link slds-box_x-small slds-media',
props.className
)}
>
<div className="slds-media__figure slds-media__figure_fixed-width slds-align_absolute-center slds-m-left_xx-small">
<UtilityIcon className="slds-icon-text-default" symbol="knowledge_base" />
</div>
<div className="slds-media__body slds-border_left slds-p-around_small">
{props.children}
</div>
</a>
);
export let VisualPickerMediaObject = props => {
const symbol = props.symbol || 'knowledge_base';
const iconContent = props.icon || (
<UtilityIcon className="slds-icon-text-default" symbol={symbol} />
);

return (
<a
href="javascript:void(0);"
className={classNames(
'slds-box slds-box_link slds-box_x-small slds-media',
props.className
)}
>
<div className="slds-media__figure slds-media__figure_fixed-width slds-align_absolute-center slds-m-left_xx-small">
{iconContent}
</div>
<div className="slds-media__body slds-border_left slds-p-around_small">
{props.children}
</div>
</a>
);
};

/// ////////////////////////////////////////
// Export
/// ////////////////////////////////////////

export default (
<div className="demo-only" style={{ width: '24rem' }}>
<VisualPickerMediaObject symbol="user">
<VisualPickerMediaObject symbol="knowledge_base">
<h2
className="slds-truncate slds-text-heading_small"
title="Share the knowledge"
Expand Down
52 changes: 52 additions & 0 deletions ui/components/welcome-mat/WelcomeMatContent.jsx
@@ -0,0 +1,52 @@
// Copyright (c) 2015-present, salesforce.com, inc. All rights reserved
// Licensed under BSD 3-Clause - see LICENSE.txt or git.io/sfdc-license

import React from 'react';
import PropTypes from 'prop-types';
import { ProgressBar } from '../progress-bar/base/example';

class WelcomeMatContent extends React.Component {
render() {
const { complete, total, labelId } = this.props;
const completePercent = complete / total * 100;

return (
<div>
<h2 id={labelId} className="slds-welcome-mat__info-title">
Empower Your Agents with Service Cloud
</h2>
<p className="slds-welcome-mat__info-description">
Your 30-day trial is under way. Learn how easy it is to use and set up
Lightning Service Desk. You'll be your company's service expert by the
time you're done!
</p>

<div className="slds-welcome-mat__info-progress">
<p>
<strong>
{complete}/{total} Walkthroughs completed
</strong>
</p>
</div>
<ProgressBar
value={completePercent}
className="slds-progress-bar_circular"
/>
</div>
);
}
}

WelcomeMatContent.propTypes = {
complete: PropTypes.number.isRequired,
total: PropTypes.number.isRequired,
labelId: PropTypes.string.isRequired
};

WelcomeMatContent.defaultProps = {
complete: 0,
total: 5,
labelId: 'welcome-mat-label-1'
};

export default WelcomeMatContent;
90 changes: 90 additions & 0 deletions ui/components/welcome-mat/WelcomeMatContentTrailhead.jsx
@@ -0,0 +1,90 @@
// Copyright (c) 2015-present, salesforce.com, inc. All rights reserved
// Licensed under BSD 3-Clause - see LICENSE.txt or git.io/sfdc-license

import React from 'react';
import classNames from 'classnames';
import PropTypes from 'prop-types';

import { ProgressBar } from '../progress-bar/base/example';
import { Button } from '../buttons/base/example';
import { ActionIcon } from '../icons/action/example';

class WelcomeMatContentTrailhead extends React.Component {
render() {
const { complete, total, labelId } = this.props;
const completePercent = complete / total * 100;
const isComplete = completePercent === 100;

const badgeProgressMessage = isComplete ? (
<p>Cha-ching! You earned the badge.</p>
) : (
<p>
{complete} of {total} modules completed
</p>
);

return (
<div>
<h2 id={labelId} className="slds-welcome-mat__info-title">
Hey there, Trailblazer!
</h2>
<p className="slds-welcome-mat__info-description">
Your 30-day trial is under way. Learn how easy it is to use and set up
Lightning Service Desk. You'll be your company's service expert by the
time you're done!
</p>

<div
className={classNames('slds-welcome-mat__info-progress', {
'slds-welcome-mat__info-progress_complete': isComplete
})}
>
<div className="slds-welcome-mat__info-badge-container">
<img
className="slds-welcome-mat__info-badge"
src="/assets/images/welcome-mat/trailhead_badge@2x.png"
width="50"
height="50"
alt=""
/>
<ActionIcon
title="Completed"
assistiveText="Completed"
className="slds-welcome-mat__icon-check slds-icon_xx-small"
symbol="check"
/>
</div>
<p>
<strong>Essentials Explorer</strong>
</p>
{badgeProgressMessage}
</div>

{isComplete ? (
<Button className="slds-button slds-button_brand">
View on your Trailblazer Profile
</Button>
) : (
<ProgressBar
value={completePercent}
className="slds-progress-bar_circular"
/>
)}
</div>
);
}
}

WelcomeMatContentTrailhead.propTypes = {
complete: PropTypes.number.isRequired,
total: PropTypes.number.isRequired,
labelId: PropTypes.string.isRequired
};

WelcomeMatContentTrailhead.defaultProps = {
complete: 0,
total: 5,
labelId: 'welcome-mat-label-1'
};

export default WelcomeMatContentTrailhead;
61 changes: 61 additions & 0 deletions ui/components/welcome-mat/WelcomeMatTile.jsx
@@ -0,0 +1,61 @@
// Copyright (c) 2015-present, salesforce.com, inc. All rights reserved
// Licensed under BSD 3-Clause - see LICENSE.txt or git.io/sfdc-license

import React from 'react';
import classNames from 'classnames';
import PropTypes from 'prop-types';

import { VisualPickerMediaObject } from '../visual-picker/link/example';
import { UtilityIcon } from '../icons/base/example';
import { ActionIcon } from '../icons/action/example';

class WelcomeMatTile extends React.Component {
render() {
const { tile, completed } = this.props;
const className = classNames('slds-welcome-mat__tile', {
'slds-welcome-mat__tile_complete': completed
});

return (
<VisualPickerMediaObject
symbol={tile.symbol}
className={className}
icon={
<div className="slds-welcome-mat__tile-icon-container">
<UtilityIcon
className="slds-icon-text-default"
symbol={tile.symbol}
title={false}
assistiveText={false}
/>
<ActionIcon
title="Completed"
assistiveText="Completed"
className="slds-welcome-mat__icon-check"
symbol="check"
/>
</div>
}
>
<h3 className="slds-welcome-mat__tile-title">{tile.title}</h3>
<p className="slds-welcome-mat__tile-description">{tile.description}</p>
</VisualPickerMediaObject>
);
}
}

WelcomeMatTile.propTypes = {
tile: PropTypes.object.isRequired,
completed: PropTypes.bool.isRequired
};

WelcomeMatTile.defaultProps = {
tile: {
symbol: 'animal_and_nature',
title: 'Welcome to Salesforce!',
description: 'Lorem ipsum dolor sit amet, lorem ipsum dolor.'
},
completed: false
};

export default WelcomeMatTile;

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

117 changes: 117 additions & 0 deletions ui/components/welcome-mat/__tests__/enzyme.spec.js
@@ -0,0 +1,117 @@
// Copyright (c) 2015-present, salesforce.com, inc. All rights reserved
// Licensed under BSD 3-Clause - see LICENSE.txt or git.io/sfdc-license
/* eslint-env jest */

import React from 'react';
import ReactDOM from 'react-dom';
import { shallow, render } from 'enzyme';

import WelcomeMat from '../';
import WelcomeMatTile from '../WelcomeMatTile';
import WelcomeMatContent from '../WelcomeMatContent';
import WelcomeMatContentTrailhead from '../WelcomeMatContentTrailhead';

describe('Welcome Mat', () => {
it('renders without crashing', () => {
ReactDOM.render(<WelcomeMat />, document.createElement('div'));
});

it('correctly renders the desired amount of completed steps', () => {
const completeCount = 3;
const rendered = render(<WelcomeMat complete={completeCount} />);

expect(rendered.find('.slds-welcome-mat__tile_complete')).toHaveLength(
completeCount
);
});
});

describe('Welcome Mat Tile', () => {
it('renders a tile without crashing', () => {
ReactDOM.render(<WelcomeMatTile />, document.createElement('div'));
});

it('correctly adds default tile content', () => {
const rendered = render(<WelcomeMatTile />);
expect(rendered.find('.slds-icon-utility-animal_and_nature').length).toBe(
1
);
expect(rendered.find('.slds-welcome-mat__tile-title').text()).toBe(
'Welcome to Salesforce!'
);
expect(rendered.find('.slds-welcome-mat__tile-description').text()).toBe(
'Lorem ipsum dolor sit amet, lorem ipsum dolor.'
);
});

it('correctly applies completed prop', () => {
const rendered = shallow(<WelcomeMatTile completed />);
expect(rendered.find('.slds-welcome-mat__tile_complete').exists()).toBe(
true
);
});
});

describe('Welcome Mat Content', () => {
const complete = 3;
const total = 5;
const completePercent = complete / total * 100;
const example = render(
<WelcomeMatContent complete={complete} total={total} />
);

it('renders without crashing', () => {
ReactDOM.render(<WelcomeMatContent />, document.createElement('div'));
});

it('correctly implements completed count props', () => {
expect(example.find('strong').html()).toBe(
`${complete}/${total} Walkthroughs completed`
);
});

it('shows the correct progress bar progress', () => {
expect(example.find('.slds-progress-bar__value').attr('style')).toBe(
`width:${completePercent}%;`
);
});
});

describe('Welcome Mat Trailhead Content', () => {
const shallowNew = shallow(<WelcomeMatContentTrailhead />);
const shallowComplete = shallow(
<WelcomeMatContentTrailhead complete={5} total={5} />
);

it('renders without crashing', () => {
ReactDOM.render(
<WelcomeMatContentTrailhead />,
document.createElement('div')
);
});

it('shows the correct initial progress', () => {
const initialText = ['Essentials Explorer', '0 of 5 modules completed'];

shallowNew.find('.slds-welcome-mat__info-progress p').forEach((p, pi) => {
expect(p.text()).toBe(initialText[pi]);
});
});

it('shows the correct completed message', () => {
const completedText = [
'Essentials Explorer',
'Cha-ching! You earned the badge.'
];

shallowComplete
.find('.slds-welcome-mat__info-progress p')
.forEach((p, pi) => {
expect(p.text()).toBe(completedText[pi]);
});
});

it('renders a button when complete', () => {
expect(shallowComplete.find('.slds-button').exists()).toBe(true);
});
});

0 comments on commit 5d2d818

Please sign in to comment.