espeakers
v6.2.26
Published
Common code for eSpeakers projects written in React
Downloads
21
Maintainers
Readme
React-es
Library of common React components used on the front-end applications developed for eSpeakers.
When developing on a local machine run this
$ yarn run test
When ready to publish a new version. Use Semantic Versioning to determine the version number. i.e.
$ yarn run prod-patch #for bug fixes, cleanup or other backwards-compatible changes
$ yarn run prod-minor #adding new features in a backwards compatable way
$ yarn run prod-major #breaking changes, including things like updating to a react version that has breaking changes
Installation
To install, use yarn
or npm
yarn install espeakers
Then you can use the various components in your projects
import {
actions,
reducers,
redux,
components,
exceptions,
forms,
utils
} from 'espeakers';
or
import { Text } from 'espeakers/forms';
import { Constants } from 'espeakers/utils';
import { Balboa } from 'espeakers/actions';
import { BalboaReducer } from 'espeakers/reducers';
import { AsyncComponent } from 'espeakers/components';
Components
AsyncComponent
Loads the component only when it is requested. Useful when building with modules so the browser doesn't have to load every component/page in a single file.
import { AsyncComponent } from 'espeakers/components';
const AsyncHomePage = AsyncComponent(<HomePage />);
Calendar
Displays the date in a calendar icon. Useful for events and calendar items.
import { Calendar } from 'espeakers/components';
<Calendar date={"2019-01-15"} format={"YYYY-MM-DD"} />
LoadingEllipsis
Displays an animated ellipses, useful when loading content on a page
import { LoadingEllipsis } from 'espeakers/components';
<LoadingEllipsis />
LoadingIndicator
Displays an animated loading circle similar to Google
import { LoadingIndicator } from 'espeakers/components';
<LoadingIndicator />
OauthButtons
Implementation of signon buttons for Google, Facebook, LinkedIn, and Mpi. Used to sign on through Oauth
import { Oauth } from 'espeakers/components';
// Define the callback function that will receive the state from the Oauth service
const callback = (service) => (
window.location.protocol + "//" + window.location.host + "/oauth-callback/" + service + "/"
);
...
// Render the buttons on the page
<Oauth.Google onSuccess={"/home"} redirect={callback}><div className={"btn btn-danger"}>Google</div></Oauth.Google>
<Oauth.Facebook onSuccess={"/home"} redirect={callback}><div className={"btn btn-primary"}>Facebook</div></Oauth.Facebook>
<Oauth.LinkedIn onSuccess={"/home"} redirect={callback}><div className={"btn btn-warning"}>LinkedIn</div></Oauth.Google>
<Oauth.Mpi onSuccess={"/home"} redirect={callback}><div className={"btn btn-default"}>Mpi</div></Oauth.Google>
PopoverMenu
Displays a menu inside a Bootstrap popover. Very useful to show a form field inside a popover
import { PopoverMenu } from 'espeakers/components';
// Render the buttons on the page
<OverlayTrigger trigger="click" placement="bottom" overlay={
<PopoverMenu>
<Dropdown.Item>Item #1</Dropdown.Item>
<Dropdown.Item>Item #2</Dropdown.Item>
<Dropdown.Item>Item #3</Dropdown.Item>
</PopoverMenu>
}>
<Button>Click me</Button>
</OverlayTrigger>
Exceptions
AuthenticationException
Thrown when a username or password is incorrect
import { AuthenticationException } from 'espeakers/exceptions';
throw new AuthenticationException('Invalid username or password');
AuthorizationException
Thrown when a user does not have access to a resource
import { AuthorizationException } from 'espeakers/exceptions';
throw new AuthorizationException('Not authorized');
SpeakerMissingException
Thrown when a speaker is loaded but they do not exist, or their profile is private, or their profile is not linked to the bureau.
It's recommended to show a 404 page or suggestsions for other speakers if this is thrown.
import { SpeakerMissingException } from 'espeakers/exceptions';
throw new SpeakerMissingException('Speaker not found');
TokenException
Thrown when an api token is invalid or expired. It's recommended to have the user login again or refresh their token.
import { TokenException } from 'espeakers/exceptions';
throw new TokenException('Token expired');
Forms
Many redux-forms components that can be used when building forms
Amount
Displays an amount with a $
. Also validates that only numbers are entered
import { Amount } from 'espeakers/forms';
<Field
name="name"
component={Amount}
/>
Checkbox
Displays an chekbox
import { Checkbox } from 'espeakers/forms';
<Field
name="name"
component={Checkbox}
/>
CheckboxDropdown
Displays an list of checkboxes using PopoverMenu
import { CheckboxDropdown } from 'espeakers/forms';
<Field
name="name"
component={CheckboxDropdown}
/>
ColorPicker
Displays a button that opens the system color picker
import { ColorPicker } from 'espeakers/forms';
<Field
name="name"
component={ColorPicker}
/>
DateRangePickerField
Displays an instance of the bootstrap-daterangepicker component
import { DateRangePickerField } from 'espeakers/forms';
<Field
name="name"
type={text|button}
startDate={"01/01/2019"}
format={"MM/DD/YYYY"}
component={DateRangePickerField}
/>
EmailConsent
Displays checkbox agreeing to receive emails
import { EmailConsent } from 'espeakers/forms';
<Field
name="name"
component={EmailConsent}
/>
FileUpoad
Displays file upload control using react-dropzone
import { FileUpoad } from 'espeakers/forms';
<Field
name="name"
component={FileUpoad}
/>
Initials
Displays a textbox for user initials, along with their name and date
import { Initials } from 'espeakers/forms';
<Field
name="name"
component={Initials}
/>
Message
Displays a textarea
import { Message } from 'espeakers/forms';
<Field
name="name"
rows={5}
component={Message}
/>
OfferAmount
Displays an <Amount />
with $ USD
in the input-group-addon
import { OfferAmount } from 'espeakers/forms';
<Field
name="name"
component={OfferAmount}
/>
Password
Displays a password field
import { Password } from 'espeakers/forms';
<Field
name="name"
component={Password}
/>
Phone
Displays a phone nubmer field that automatically formats domestic and international phone numbers
import { Phone } from 'espeakers/forms';
<Field
name="name"
component={Phone}
/>
Radio
Displays a radio button field
import { Radio } from 'espeakers/forms';
<Field
name="name"
component={Radio}
/>
Rating
Displays multiple stars for saving a rating
import { Rating } from 'espeakers/forms';
<Field
placeholder="Select a rating"
label="Rate me"
count={7}
value={5}
help_text="Click on a star to rate"
component={Rating}
size={"5x"}
name="rating"
/>
Reason
Displays radio buttons in a group
import { Reason } from 'espeakers/forms';
<Field
name="color"
component={Reason}
label="Red"
/>
<Field
name="color"
component={Reason}
label="Green"
/>
<Field
name="color"
component={Reason}
label="Blue"
/>
SearchSort
Displays a dropdown to select sorting
import { SearchSort } from 'espeakers/forms';
<Field
name="sort"
component={SearchSort}
/>
Selectize
Displays a select component using react-select
import { Selectize } from 'espeakers/forms';
const countries = [
{label: "United States", value: "US"},
{label: "Canada", value: "CA"},
{label: "Mexico", value: "MX"},
];
...
<Field
name="country"
component={Selectize}
options={countries}
normalize={(value) => (_.get(value, ["value"], value))}
format={(value) => ({
label: _.get(_.keyBy(countries, "value"), [value, "label"], value),
value: value
})}
/>
SliderDropdown
Displays a slider within a PopoverMenu
import { SliderDropdown } from 'espeakers/forms';
const marks = {
0: "Free",
2: "$1,000",
5: "$2,500",
10: "$5,000",
15: "$7,500",
20: "$10,000",
30: "$15,000",
40: "$20,000",
50: "$20,000+"
};
...
<Field
component={SliderDropdown}
name="budget"
placeholder="- Any budget -"
width="medium"
defaultValue={[10,20]}
any_placeholder="Any Budget"
min={0}
max={50}
step={5}
dots={true}
marks={marks}
/>
Text
Displays a standard text field
import { Text } from 'espeakers/forms';
<Field
component={Text}
name="name"
label="Name:"
/>
TextWithInputGroup
Displays a standard text field with an input-group-addon before and/or after it
import { TextWithInputGroup } from 'espeakers/forms';
<Field
component={TextWithInputGroup}
name="amount"
label="Amount:"
input_group_before={<span className="input-group-addon">$</span>}
input_group_after={<span className="input-group-addon">.00</span>}
/>
Toggle
Displays a toggle for on/off fields using react-toggle
import { Toggle } from 'espeakers/forms';
<Field
component={Toggle}
name="option"
label="Enable:"
/>
ToggleJustified
Displays a toggle for on/off fields using react-toggle to the right
import { ToggleJustified } from 'espeakers/forms';
<Field
component={ToggleJustified}
name="option"
label="Enable:"
help_text="Click to enable this option"
/>
Username
Displays a username field
import { Username } from 'espeakers/forms';
<Field
component={Username}
label="Username:"
/>
Redux Actions
Login.OauthSignin
Used to login to eSpeakers using an Oauth token
import { Oauth } from 'espeakers/actions';
handleSubmit(values, dispatch) {
// values contains:
// {
// service: "google|facebook|linkedin|mpi",
// access_token|code: "",
// state: "buyer[$]1375[$]/home" // user type (buyer, speaker, oauth), bureau, redirect url
// }
let state_parts = _.split(_.get(values, ["state"]), "[$]");
let redir = _.last(state_parts);
if (_.get(values, ["service"]) === "google") {
return dispatch(Oauth.Google.signin(_.get(values, ["access_token"]), _.join(state_parts, "[$]"), Oauth.Google.getCallbackUrl("google")))
.then((response) => {
ownProps.history.push(redir);
})
.catch((err) => {
throw err;
});
} else if (_.get(values, ["service"]) === "facebook") {
return dispatch(Oauth.Facebook.signin(_.get(values, ["code"]), _.join(state_parts, "[$]"), Oauth.Facebook.getCallbackUrl("facebook")))
.then((response) => {
ownProps.history.push(redir);
})
.catch((err) => {
throw err;
});
} else if (_.get(values, ["service"]) === "linkedin") {
return dispatch(Oauth.LinkedIn.signin(_.get(values, ["code"]), _.join(state_parts, "[$]"), Oauth.LinkedIn.getCallbackUrl("linkedin")))
.then((response) => {
ownProps.history.push(redir);
})
.catch((err) => {
throw err;
});
} else if (_.get(values, ["service"]) === "mpi") {
return dispatch(Oauth.Mpi.signin(_.get(values, ["code"]), _.join(state_parts, "[$]"), Oauth.Mpi.getCallbackUrl("mpi")))
.then((response) => {
ownProps.history.push(redir);
})
.catch((err) => {
throw err;
});
} else {
return new Promise((resolve, reject) => {
return reject("Cannot authenticate without a service");
});
}
}
Login.Signin
Used to login to eSpeakers using a username and password
import { Login } from 'espeakers/actions';
handleSubmit(values, dispatch) {
// values contains:
// {
// username: "",
// password: "",
// redir: "/dashboard"
// }
return dispatch(Login.Signin(values))
.catch((err) => {
throw new SubmissionError({_error: err});
});
}
Login.TokenSignin
Used to login to eSpeakers using an eSpeakers Authentication token
import { Login } from 'espeakers/actions';
handleSubmit(values, dispatch) {
// values contains:
// {
// estoken: "",
// redir: "/dashboard"
// }
return dispatch(Login.TokenSignin(values))
.catch((err) => {
throw new SubmissionError({_error: err});
});
}
Oauth.Facebook
Used to exchange and Oauth state from Facebook to a profile and later a token for signin into eSpeakers
See Login.OauthSignin above
Oauth.Google
Used to exchange and Oauth state from Google to a profile and later a token for signin into eSpeakers
See Login.OauthSignin above
Oauth.LinkedIn
Used to exchange and Oauth state from LinkedIn to a profile and later a token for signin into eSpeakers
See Login.OauthSignin above
Oauth.Mpi
Used to exchange and Oauth state from Mpi to a profile and later a token for signin into eSpeakers
See Login.OauthSignin above
Balboa
Thunk used in Redux Actions to send an API Request to the Balboa API on eSpeakers
import { Balboa } from "espeakers/actions";
return function(dispatch, getState) {
return Balboa(dispatch, getState)
.get(key, "/record/" + id, {})
.then((response) => {
dispatch({
type: RECORD_GET,
id: id,
data: _.get(response, ["data"])
});
return response;
});
}
Rest
Implementation of REST actions within an action. You should extend this object in any Redux Actions
import { Rest, Balboa } from "espeakers/actions";
export const RECORD_GET = 'RECORD_GET';
export const RECORD_GET_ALL = 'RECORD_GET_ALL';
export const RECORD_POST = 'RECORD_POST';
export const RECORD_PUT = 'RECORD_PUT';
export const RECORD_DELETE = 'RECORD_DELETE';
export default _.assign({}, Rest, {
/**
* Retrieves a single record.
* @param id
* @returns {Function}
*/
get: function(id) {
let key = [RECORD_GET];
return (dispatch, getState) => {
return this.api(
dispatch,
key,
Balboa(dispatch, getState)
.get(key, "/record/" + id, {})
.then((response) => {
dispatch({
type: RECORD_GET,
id: id,
data: _.get(response, ["data"])
});
return response;
})
);
};
},
/**
* Retrieves all the records.
* @param params
* @returns {Function}
*/
find: function(params) {
let key = [RECORD_GET_ALL];
return (dispatch, getState) => {
return this.api(
dispatch,
key,
Balboa(dispatch, getState)
.get(key, "/record", params)
.then((response) => {
dispatch({
type: RECORD_GET_ALL,
data: _.get(response, ["data"])
});
return response;
})
);
};
},
/**
* Inserts a record. Leave {params.id} 0, otherwise it will be updated
* @param params
* @returns {Function}
*/
add: function(params) {
let key = [RECORD_POST];
return (dispatch, getState) => {
return this.api(
dispatch,
key,
Balboa(dispatch, getState)
.post(key, "/record", params)
.then((response) => {
dispatch({
type: RECORD_POST,
data: _.get(response, ["data"])
});
return response;
})
);
};
},
/**
* Updates a record. Pass in {params.id} to specify the record to update, otherwise it will be added
* @param params
* @returns {Function}
*/
update: function(params) {
let key = [RECORD_PUT];
return (dispatch, getState) => {
return this.api(
dispatch,
key,
Balboa(dispatch, getState)
.put(key, "/record/" + _.get(params, ["id"]), params)
.then((response) => {
dispatch({
type: RECORD_PUT,
data: _.get(response, ["data"])
});
return response;
})
);
};
},
/**
* Removes a record. Pass in {params.id} to specify the record to remove
* @param params
* @returns {Function}
*/
remove: function(id) {
let key = [RECORD_DELETE];
return (dispatch, getState) => {
return self.api(
dispatch,
key,
Balboa(dispatch, getState)
.del(key, "/record/" + id, {})
.then((response) => {
dispatch({
type: RECORD_DELETE,
id: id,
data: _.get(response, ["data"])
});
return response;
})
);
};
}
});
Redux Reducers
BalboaReducer
Adds the appropriate reducers to the Redux Store to be used by the Balboa Redux Action
import { BalboaReducer } from "espeakers/reducers";
export default combineReducers({
//...
// Global App reducers
balboa3: BalboaReducer,
//...
});
Utils
Balboa3
Wrapper for fetch that connects to the Balboa API used by eSpeakers
import { Balboa3 } from "espeakers/utils";
const balboa3 = new Balboa3({
default_params: {
estoken: token // estoken used for authentication, throws TokenException if invalid
}
});
...
balboa3.ajax(url, params, method)
.then((response) => {
// process response here
});
Constants
Various site constants used within the system
import { Constants } from 'espeakers/utils';
console.log(Constants);
console.log(Constants.TRUNK_BUREAU_ID);
formatCurrency
Formats the text as USD
import { formatCurrency } from 'espeakers/utils';
console.log(formatCurrency(5)); // $5.00
formatNumber
Formats the text as a number
import { formatNumber } from 'espeakers/utils';
console.log(formatNumber(5)); // 5
console.log(formatNumber(5, 2)); // 5.00
console.log(formatNumber(5.275, 1)); // 5.3
console.log(formatNumber("5", 1)); // 5.0
console.log(formatNumber("abc", 1)); // 0.0
formatPhone
Formats the text as a domestic or international phone number
import { formatPhone } from 'espeakers/utils';
console.log(formatPhone("8005551212")); // (800) 555-1212
console.log(formatPhone("18005551212")); // 1 (800) 555-1212
console.log(formatPhone("1425551212")); // 1425551212
console.log(formatPhone("5551212")); // 555-1212
formatPriceRange
Formats a 2-element array as a price range in the format of $min - $max
import { formatPriceRange } from 'espeakers/utils';
console.log(formatPriceRange([0,1000], "Free")); // Free - $1000
console.log(formatPriceRange([1,1000], "Free")); // $1 - $1000
console.log(formatPriceRange([1000,Infinity], "Free")); // More than $1000