refactors email for avatars

This commit is contained in:
Seyi Adebajo 2018-09-26 21:57:16 -07:00
parent 026e7a878c
commit 10e8fdec8a
10 changed files with 117 additions and 74 deletions

View File

@ -11,7 +11,7 @@ import { IOwner, IOwnerResponse } from 'wherehows-web/typings/api/datasets/owner
import { getAvatarProps } from 'wherehows-web/constants/avatars/avatars'; import { getAvatarProps } from 'wherehows-web/constants/avatars/avatars';
import { confirmedOwners, avatarWithDropDownOption } from 'wherehows-web/constants/datasets/owner'; import { confirmedOwners, avatarWithDropDownOption } from 'wherehows-web/constants/datasets/owner';
import { containerDataSource } from 'wherehows-web/utils/components/containers/data-source'; import { containerDataSource } from 'wherehows-web/utils/components/containers/data-source';
import { isLiUrn } from 'wherehows-web/utils/validators/urn'; import { decodeUrn, isLiUrn } from 'wherehows-web/utils/validators/urn';
import { IAppConfig } from 'wherehows-web/typings/api/configurator/configurator'; import { IAppConfig } from 'wherehows-web/typings/api/configurator/configurator';
@classNames('dataset-owner-list') @classNames('dataset-owner-list')
@ -59,7 +59,12 @@ export default class DatasetOwnerListContainer extends Component {
@computed('owners') @computed('owners')
get avatars(): Array<IAvatar> { get avatars(): Array<IAvatar> {
const { avatarEntityProps, owners } = this; const { avatarEntityProps, owners } = this;
return arrayPipe(arrayMap(getAvatarProps(avatarEntityProps)), arrayMap(avatarWithDropDownOption))(owners); const [getAvatarProperties, augmentAvatarsWithDropDownOption] = [
arrayMap(getAvatarProps(avatarEntityProps)),
arrayMap(avatarWithDropDownOption)
];
return arrayPipe(getAvatarProperties, augmentAvatarsWithDropDownOption)(owners);
} }
/** /**
@ -69,7 +74,7 @@ export default class DatasetOwnerListContainer extends Component {
getOwnersTask = task(function*(this: DatasetOwnerListContainer): IterableIterator<Promise<IOwnerResponse>> { getOwnersTask = task(function*(this: DatasetOwnerListContainer): IterableIterator<Promise<IOwnerResponse>> {
const { urn } = this; const { urn } = this;
if (isLiUrn(urn)) { if (isLiUrn(decodeUrn(urn))) {
const { owners = [] }: IOwnerResponse = yield readDatasetOwnersByUrn(urn); const { owners = [] }: IOwnerResponse = yield readDatasetOwnersByUrn(urn);
set(this, 'owners', confirmedOwners(owners)); set(this, 'owners', confirmedOwners(owners));

View File

@ -165,7 +165,7 @@ const avatarWithDropDownOption = (avatar: IAvatar): IAvatar & Required<Pick<IAva
avatarOptions: [ avatarOptions: [
{ {
// if the owner avatar does not have an email then a null value is returned with no action performed // if the owner avatar does not have an email then a null value is returned with no action performed
value: ({ email = '' }: IAvatar): Window | null => value: ({ email }: IAvatar): Window | null =>
email ? window.open(buildMailToUrl({ to: email || '' }), '_blank') : null, email ? window.open(buildMailToUrl({ to: email || '' }), '_blank') : null,
label: email label: email
} }

View File

@ -71,7 +71,7 @@
} }
} }
&--trigger { &__trigger {
outline: none; outline: none;
} }
} }

View File

@ -21,7 +21,7 @@
renderInPlace=true renderInPlace=true
onSelect=(action "onAvatarOptionSelected" avatar) as |dd|}} onSelect=(action "onAvatarOptionSelected" avatar) as |dd|}}
{{#dd.trigger class="avatar-item--trigger"}} {{#dd.trigger class="avatar-item__trigger"}}
{{avatars/avatar-image avatar=avatar {{avatars/avatar-image avatar=avatar
class=(if dd.isExpanded "avatar--stacked avatar--focused" "avatar--stacked")}} class=(if dd.isExpanded "avatar--stacked avatar--focused" "avatar--stacked")}}
{{/dd.trigger}} {{/dd.trigger}}

View File

@ -9,7 +9,7 @@ type MailerHeaders = 'to' | 'cc' | 'bcc' | 'subject' | 'body';
* Alias for a string of list of string email header values * Alias for a string of list of string email header values
* @alias * @alias
*/ */
export type MailerHeaderValue = Maybe<string | Array<string>>; export type MailerHeaderValue = Maybe<string>;
/** /**
* An optional record of email headers to it's value - MailerHeaderValue * An optional record of email headers to it's value - MailerHeaderValue

View File

@ -1,18 +1,24 @@
import { encode, decode } from 'wherehows-web/utils/encode-decode-uri-component-with-space'; import { encode, decode } from 'wherehows-web/utils/encode-decode-uri-component-with-space';
import { isBlank } from '@ember/utils'; import { isBlank } from '@ember/utils';
import { isObject } from 'wherehows-web/utils/object';
/** /**
* Construct a url by appending a query pair (?key=value | &key=value) to a base url and * Construct a url by appending a query pair (?key=value | &key=value) to a base url and
* encoding the query value in the pair * encoding the query value in the pair
* @param {String} baseUrl the base or original url that will be appended with a query string * @param baseUrl the base or original url that will be appended with a query string
* @param {String} queryParam * @param queryParamOrMap a map of query keys to values or a single query param key
* @param {String} queryValue * @param queryValue if a queryParam is supplied, then a queryValue can be expected
* @param useEncoding flag indicating if the query values should be encoded
* @returns {string} * @returns {string}
*/ */
function buildUrl(): string; function buildUrl(baseUrl: string, queryParamOrMap?: {}, useEncoding?: boolean): string;
function buildUrl(baseUrl: string, mapParams: Record<string, any>): string; function buildUrl(baseUrl: string, queryParamOrMap?: string, queryValue?: string, useEncoding?: boolean): string;
function buildUrl(baseUrl: string, queryKey: string, queryValue: string): string; function buildUrl(
function buildUrl(baseUrl?: string, queryParamOrMap?: string | Record<string, string>, queryValue?: string): string { baseUrl: string,
queryParamOrMap: string | Record<string, any> = {},
queryValue?: string | boolean,
useEncoding: boolean = true
): string {
if (!baseUrl) { if (!baseUrl) {
return ''; return '';
} }
@ -21,16 +27,24 @@ function buildUrl(baseUrl?: string, queryParamOrMap?: string | Record<string, st
return baseUrl; return baseUrl;
} }
let paramMap: { [x: string]: string }; let paramMap: Record<string, any> = {};
// queryParamOrMap is a string then, reify paramMap object with supplied value
if (typeof queryParamOrMap === 'string') { if (typeof queryParamOrMap === 'string') {
paramMap = { paramMap = {
[queryParamOrMap]: queryValue || '' [queryParamOrMap]: queryValue
}; };
} else {
paramMap = queryParamOrMap;
} }
return Object.keys(paramMap).reduce((url, paramKey) => { if (isObject(queryParamOrMap)) {
paramMap = queryParamOrMap;
if (typeof queryValue === 'boolean') {
useEncoding = queryValue;
}
}
return Object.keys(paramMap).reduce((url: string, paramKey: string): string => {
// If the query string already contains the initial question mark append // If the query string already contains the initial question mark append
// kv-pair with ampersand // kv-pair with ampersand
const separator = String(url).includes('?') ? '&' : '?'; const separator = String(url).includes('?') ? '&' : '?';
@ -44,13 +58,14 @@ function buildUrl(baseUrl?: string, queryParamOrMap?: string | Record<string, st
return url; return url;
} }
if (useEncoding) {
// Malformed URL will cause decodeURIComponent to throw // Malformed URL will cause decodeURIComponent to throw
// handle and encode queryValue in such instance // handle and encode queryValue in such instance
try { try {
// Check if queryValue is already encoded, // Check if queryValue is already encoded,
// otherwise encode queryValue before composing url // otherwise encode queryValue before composing url
// e.g. if user directly enters query in location bar // e.g. if user directly enters query in location bar
if (decode(paramValue) === queryValue) { if (decode(paramValue) === paramValue) {
paramValue = encode(paramValue); paramValue = encode(paramValue);
} }
} catch (err) { } catch (err) {
@ -60,6 +75,7 @@ function buildUrl(baseUrl?: string, queryParamOrMap?: string | Record<string, st
throw err; throw err;
} }
}
return `${url}${separator}${paramKey}=${paramValue}`; return `${url}${separator}${paramKey}=${paramValue}`;
}, baseUrl); }, baseUrl);

View File

@ -1,6 +1,5 @@
import { arrayReduce } from 'wherehows-web/utils/array';
import buildUrl from 'wherehows-web/utils/build-url'; import buildUrl from 'wherehows-web/utils/build-url';
import { IMailHeaderRecord, MailerHeaderValue } from 'wherehows-web/typings/app/helpers/email'; import { IMailHeaderRecord } from 'wherehows-web/typings/app/helpers/email';
/** /**
* Constructs a `mailto:` address with supplied email headers as query parameters * Constructs a `mailto:` address with supplied email headers as query parameters
@ -9,14 +8,9 @@ import { IMailHeaderRecord, MailerHeaderValue } from 'wherehows-web/typings/app/
*/ */
const buildMailToUrl = (headers: IMailHeaderRecord = {}): string => { const buildMailToUrl = (headers: IMailHeaderRecord = {}): string => {
const { to = '', ...otherHeaders } = headers; const { to = '', ...otherHeaders } = headers;
const [...otherHeaderPairs] = Object.entries(otherHeaders); const mailTo = `mailto:${encodeURIComponent(to)}`;
const mailTo = `mailto:${to}`;
return arrayReduce( return buildUrl(mailTo, otherHeaders);
(mailTo: string, [headerName, headerValue = '']: [string, MailerHeaderValue]) =>
buildUrl(mailTo, headerName, String(headerValue)),
mailTo
)([...otherHeaderPairs]);
}; };
export { buildMailToUrl }; export { buildMailToUrl };

View File

@ -7,51 +7,47 @@ const base = 'mailto:';
const mailToEmail = `${base}${emailAddress}`; const mailToEmail = `${base}${emailAddress}`;
const emailMailToAsserts = [ const emailMailToAsserts = [
{ {
__expected__: base, expected: base,
__args__: void 0, args: void 0,
__assert_msg__: 'it should return a basic mailto: string without an email when no arguments are passed' assertMsg: 'it should return a basic mailto: string without an email when no arguments are passed'
}, },
{ {
__expected__: base, expected: base,
__args__: {}, args: {},
__assert_msg__: 'it should return a basic mailto: string without an email when an empty object is passed' assertMsg: 'it should return a basic mailto: string without an email when an empty object is passed'
}, },
{ {
__expected__: base, expected: base,
__args__: { to: '' }, args: { to: '' },
__assert_msg__: assertMsg:
'it should return a basic mailto: string without an email when an object with only an empty string in the `to` field is passed' 'it should return a basic mailto: string without an email when an object with only an empty string in the `to` field is passed'
}, },
{ {
__expected__: `${mailToEmail}`, expected: `${mailToEmail}`,
__args__: { to: emailAddress }, args: { to: emailAddress },
__assert_msg__: 'it should return a mailto: string with an email when an object with only the `to` field is passed' assertMsg: 'it should return a mailto: string with an email when an object with only the `to` field is passed'
}, },
{ {
__expected__: `${mailToEmail}?cc=${encodeURIComponent(cc)}`, expected: `${mailToEmail}?cc=${cc}`,
__args__: { to: emailAddress, cc }, args: { to: emailAddress, cc },
__assert_msg__: 'it should return a mailto: string with an email and a cc query when to and cc are passed in' assertMsg: 'it should return a mailto: string with an email and a cc query when to and cc are passed in'
}, },
{ {
__expected__: `${mailToEmail}?cc=${encodeURIComponent(cc)}&subject=${encodeURIComponent(subject)}`, expected: `${mailToEmail}?cc=${cc}&subject=${subject}`,
__args__: { to: emailAddress, cc, subject }, args: { to: emailAddress, cc, subject },
__assert_msg__: assertMsg:
'it should return a mailto: string with an email, subject, and a cc query when to, subject, and cc are passed in' 'it should return a mailto: string with an email, subject, and a cc query when to, subject, and cc are passed in'
}, },
{ {
__expected__: `${mailToEmail}?cc=${encodeURIComponent(cc)}&subject=${encodeURIComponent( expected: `${mailToEmail}?cc=${cc}&subject=${subject}&bcc=${bcc}`,
subject args: { to: emailAddress, cc, subject, bcc },
)}&bcc=${encodeURIComponent(bcc)}`, assertMsg:
__args__: { to: emailAddress, cc, subject, bcc },
__assert_msg__:
'it should return a mailto: string with an email, subject, bcc, and a cc query when to, subject, bcc, and cc are passed in' 'it should return a mailto: string with an email, subject, bcc, and a cc query when to, subject, bcc, and cc are passed in'
}, },
{ {
__expected__: `${mailToEmail}?cc=${encodeURIComponent(cc)}&subject=${encodeURIComponent( expected: `${mailToEmail}?cc=${cc}&subject=${subject}&bcc=${bcc}&body=${body}`,
subject args: { to: emailAddress, cc, subject, bcc, body },
)}&bcc=${encodeURIComponent(bcc)}&body=${encodeURIComponent(body)}`, assertMsg:
__args__: { to: emailAddress, cc, subject, bcc, body },
__assert_msg__:
'it should return a mailto: string with an email, subject, bcc, body, and a cc query when to, subject, bcc, body, and cc are passed in' 'it should return a mailto: string with an email, subject, bcc, body, and a cc query when to, subject, bcc, body, and cc are passed in'
} }
]; ];

View File

@ -3,10 +3,42 @@ import { module, test } from 'qunit';
module('Unit | Utility | build url', function() { module('Unit | Utility | build url', function() {
const baseUrl = 'https://www.linkedin.com'; const baseUrl = 'https://www.linkedin.com';
const unEncodedString = 'string@string';
const encodedString = encodeURIComponent(unEncodedString);
test('baseUrl', function(assert) { test('baseUrl', function(assert) {
let result = buildUrl(); let result = buildUrl(baseUrl);
assert.equal(result, '', 'returns an empty string when no arguments are passed'); assert.equal(result, baseUrl, 'it returns the base url when no other arguments are passed');
result = buildUrl(baseUrl, {});
assert.equal(result, baseUrl, 'it returns the base url when an empty object is supplied as query parameter');
result = buildUrl(baseUrl, '');
assert.equal(result, baseUrl, 'it returns the base url when an empty string is supplied as query parameter');
result = buildUrl(baseUrl, '', true);
assert.equal(
result,
baseUrl,
'it returns the base url when the queryParam is an empty string and the useEncoding is set'
);
result = buildUrl(baseUrl, 'query', true);
assert.equal(
result,
`${baseUrl}?query=true`,
'it returns the base url with a query key set to true when true is passed as a query value'
);
result = buildUrl(baseUrl, 'query', unEncodedString, true);
assert.equal(
result,
`${baseUrl}?query=${encodedString}`,
'it returns the encoded value when the useEncoding flag is true'
);
result = buildUrl(baseUrl, { query: unEncodedString }, true);
assert.equal(result, `${baseUrl}?query=${encodedString}`, 'it returns the encoded string on a queryParams object');
result = buildUrl(baseUrl, '', ''); result = buildUrl(baseUrl, '', '');
assert.equal(result, baseUrl, 'returns the baseUrl when no query parameter is supplied'); assert.equal(result, baseUrl, 'returns the baseUrl when no query parameter is supplied');

View File

@ -10,8 +10,8 @@ module('Unit | Utility | helpers/email', function(): void {
test('buildMailToUrl generates expected url values', function(assert): void { test('buildMailToUrl generates expected url values', function(assert): void {
assert.expect(emailMailToAsserts.length); assert.expect(emailMailToAsserts.length);
emailMailToAsserts.forEach(({ __expected__, __args__, __assert_msg__ }) => { emailMailToAsserts.forEach(({ expected, args, assertMsg }) => {
assert.equal(buildMailToUrl(__args__), __expected__, __assert_msg__); assert.equal(buildMailToUrl(args), expected, assertMsg);
}); });
}); });
}); });