Skip to content

Commit

Permalink
In-progress: use custom events for inputs
Browse files Browse the repository at this point in the history
  • Loading branch information
drwpow committed Mar 20, 2019
1 parent 500e8d4 commit 8ad8c50
Show file tree
Hide file tree
Showing 11 changed files with 120 additions and 40 deletions.
11 changes: 6 additions & 5 deletions src/components/custom-plan-feature/custom-plan-feature.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Component, Prop, FunctionalComponent } from '@stencil/core';
import { FEATURE_CHANGE } from '../../global/events';
import { $ } from '../../utils/currency';

const STRING = 'string';
Expand Down Expand Up @@ -49,23 +50,23 @@ const Toggle: FunctionalComponent<ToggleProps> = () => {
})
export class CustomPlanFeature {
@Prop() feature: Catalog.ExpandedFeature;
@Prop() selectedValue: string;
@Prop() selectedValue?: string;
@Prop() planLabel: string = '';

get featureID() {
return `plan-${this.planLabel}-feature-${this.feature.label}`;
return this.feature.label;
}

render() {
const { values = [], value } = this.feature;

switch (this.feature.type) {
case STRING:
return <Dropdown name={this.featureID} values={values} />;
return <Dropdown name={this.featureID} values={values} event-name={FEATURE_CHANGE} />;
case NUMBER:
return <Slider name={this.featureID} value={value} />;
return <Slider name={this.featureID} value={value} event-name={FEATURE_CHANGE} />;
case BOOLEAN:
return <Toggle name={this.featureID} values={values} />;
return <Toggle name={this.featureID} values={values} event-name={FEATURE_CHANGE} />;
default:
return null;
}
Expand Down
11 changes: 5 additions & 6 deletions src/components/custom-plan-feature/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,11 @@

## Properties

| Property | Attribute | Description | Type | Default |
| --------------- | ---------------- | ----------- | ---------------------------------------- | ----------- |
| `feature` | -- | | `ExpandedFeature` | `undefined` |
| `planLabel` | `plan-label` | | `string` | `''` |
| `selectedValue` | `selected-value` | | `string` | `undefined` |
| `setFeature` | -- | | `(label: string, value: string) => void` | `undefined` |
| Property | Attribute | Description | Type | Default |
| --------------- | ---------------- | ----------- | ----------------- | ----------- |
| `feature` | -- | | `ExpandedFeature` | `undefined` |
| `planLabel` | `plan-label` | | `string` | `''` |
| `selectedValue` | `selected-value` | | `string` | `undefined` |


----------------------------------------------
Expand Down
18 changes: 13 additions & 5 deletions src/components/mf-select/mf-select.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Component, Prop } from '@stencil/core';
import { Component, Prop, Event, EventEmitter } from '@stencil/core';
import { Option, Value } from 'types/Select';

@Component({
Expand All @@ -7,15 +7,23 @@ import { Option, Value } from 'types/Select';
scoped: true,
})
export class MfSelect {
@Prop() options: Option[] = [];
@Prop() selectedValue?: Value;
@Prop() eventName?: string;
@Prop() name: string;
@Prop() options: Option[] = [];
@Prop() required?: boolean;
@Prop() onChange: (e: UIEvent) => void;
@Prop() selectedValue?: Value;
@Event({ eventName: this.eventName }) onChange: EventEmitter;

private onChangeHandler(e: Event) {
console.log('changed');
if (!e.target || !this.eventName) return;
const el = e.target as HTMLSelectElement;
this.onChange.emit({ name: el.getAttribute('name'), value: el.value });
}

render() {
return (
<select name={this.name} required={this.required} onChange={this.onChange}>
<select name={this.name} required={this.required} onChange={this.onChangeHandler}>
{this.options.map(({ label, value }) => (
<option value={value} selected={this.selectedValue === value}>
{label}
Expand Down
9 changes: 8 additions & 1 deletion src/components/mf-select/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,20 @@

| Property | Attribute | Description | Type | Default |
| --------------- | ---------------- | ----------- | ------------------------------- | ----------- |
| `eventName` | `event-name` | | `string \| undefined` | `undefined` |
| `name` | `name` | | `string` | `undefined` |
| `onChange` | -- | | `(e: UIEvent) => void` | `undefined` |
| `options` | -- | | `Option[]` | `[]` |
| `required` | `required` | | `boolean \| undefined` | `undefined` |
| `selectedValue` | `selected-value` | | `number \| string \| undefined` | `undefined` |


## Events

| Event | Description | Type |
| ---------- | ----------- | ------------------- |
| `onChange` | | `CustomEvent<void>` |


----------------------------------------------

*Built with [StencilJS](https://stenciljs.com/)*
25 changes: 15 additions & 10 deletions src/components/mf-slider/mf-slider.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
import { Component, Prop } from '@stencil/core';
import { Component, Prop, Event, EventEmitter } from '@stencil/core';

@Component({
tag: 'mf-slider',
styleUrl: 'mf-slider.css',
scoped: true,
})
export class MfSlider {
@Prop() min: number = 0;
@Prop() max: number;
@Prop() error?: string;
@Prop() eventName?: string;
@Prop() increment: number = 1;
@Prop() max: number;
@Prop() min: number = 0;
@Prop() name: string = '';
@Prop() selectedValue: number;
@Prop() suffix: string = '';
@Prop() onChange: (e: Event) => void;
@Prop() error?: string;
@Event({ eventName: this.eventName }) onChange: EventEmitter;

private onChangeHandler(e: Event) {
if (!e.target || !this.eventName) return;
const el = e.target as HTMLInputElement;
this.onChange.emit({ name: el.getAttribute('name'), value: parseInt(el.value, 10) });
}

get positionCount() {
return (this.max - this.min) / this.increment;
Expand All @@ -31,8 +38,6 @@ export class MfSlider {
min={this.min}
step={this.increment}
value={value}
onInput={e => this.onChange(e)}
onChange={e => this.onChange(e)}
style={{
'--slider-position': `${value / (this.max - this.min)}%`,
}}
Expand All @@ -49,13 +54,13 @@ export class MfSlider {
)}
<div class="number-wrapper">
<input
type="number"
max={this.max}
min={this.min}
value={value}
onChange={e => this.onChange(e)}
onChange={this.onChangeHandler}
required
step={this.increment}
type="number"
value={value}
/>
<div class="display-units">{this.suffix}</div>
</div>
Expand Down
9 changes: 8 additions & 1 deletion src/components/mf-slider/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,22 @@
| Property | Attribute | Description | Type | Default |
| --------------- | ---------------- | ----------- | --------------------- | ----------- |
| `error` | `error` | | `string \| undefined` | `undefined` |
| `eventName` | `event-name` | | `string \| undefined` | `undefined` |
| `increment` | `increment` | | `number` | `1` |
| `max` | `max` | | `number` | `undefined` |
| `min` | `min` | | `number` | `0` |
| `name` | `name` | | `string` | `''` |
| `onChange` | -- | | `(e: Event) => void` | `undefined` |
| `selectedValue` | `selected-value` | | `number` | `undefined` |
| `suffix` | `suffix` | | `string` | `''` |


## Events

| Event | Description | Type |
| ---------- | ----------- | ------------------- |
| `onChange` | | `CustomEvent<void>` |


----------------------------------------------

*Built with [StencilJS](https://stenciljs.com/)*
19 changes: 14 additions & 5 deletions src/components/mf-toggle/mf-toggle.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,33 @@
import { Component, Prop } from '@stencil/core';
import { Component, Prop, Event, EventEmitter } from '@stencil/core';

@Component({
tag: 'mf-toggle',
styleUrl: 'mf-toggle.css',
scoped: true,
})
export class MfToggle {
@Prop() ariaLabelledby?: string;
@Prop() disabled?: boolean;
@Prop() eventName?: string;
@Prop() label?: string;
@Prop() name: string = '';
@Prop() disabled?: boolean;
@Prop() ariaLabelledby?: string;
@Event({ eventName: this.eventName }) onChange: EventEmitter;

private onChangeHandler(e: Event) {
if (!e.target || !this.eventName) return;
const el = e.target as HTMLInputElement;
this.onChange.emit({ name: el.getAttribute('name'), value: el.checked === true });
}

render() {
return (
<label class="wrapper">
<input
name={this.name}
aria-labelledby={this.ariaLabelledby}
type="checkbox"
disabled={this.disabled}
name={this.name}
onChange={this.onChangeHandler}
type="checkbox"
/>
<div class="toggle" />
{this.label && <div class="label">{this.label}</div>}
Expand Down
8 changes: 8 additions & 0 deletions src/components/mf-toggle/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,18 @@
| ---------------- | ----------------- | ----------- | ---------------------- | ----------- |
| `ariaLabelledby` | `aria-labelledby` | | `string \| undefined` | `undefined` |
| `disabled` | `disabled` | | `boolean \| undefined` | `undefined` |
| `eventName` | `event-name` | | `string \| undefined` | `undefined` |
| `label` | `label` | | `string \| undefined` | `undefined` |
| `name` | `name` | | `string` | `''` |


## Events

| Event | Description | Type |
| ---------- | ----------- | ------------------- |
| `onChange` | | `CustomEvent<void>` |


----------------------------------------------

*Built with [StencilJS](https://stenciljs.com/)*
47 changes: 41 additions & 6 deletions src/components/plan-details/plan-details.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Component, Prop } from '@stencil/core';
import { Component, Prop, State, Listen } from '@stencil/core';
import { $ } from '../../utils/currency';
import { FEATURE_CHANGE } from '../../global/events';

const RESOURCE_CREATE = '/resource/create?product='; // TODO get actual url
const NUMBER_FEATURE_COIN = 10000000; // Numeric features are a ten-millionth of a cent, because floats stink
Expand All @@ -9,14 +10,28 @@ const YES = 'Yes';
const featureCost = (number: number) => $(number / NUMBER_FEATURE_COIN);
const singularize = (word: string) => word.replace(/s$/i, '');

export const tagName = 'plan-details';

interface UserFeature {
[key: string]: string | number | boolean;
}

@Component({
tag: 'plan-details',
tag: tagName,
styleUrl: 'plan-details.css',
shadow: true,
})
export class PlanDetails {
@Prop() plan: Catalog.ExpandedPlan;
@Prop() product: Catalog.ExpandedProduct;
@State() features: UserFeature;
@Listen(FEATURE_CHANGE) handleFeatureChange(e: CustomEvent) {
console.log(e);
}

componentWillLoad() {
this.features = this.initialFeatures();
}

// TODO clean this up
featureValue({
Expand Down Expand Up @@ -58,7 +73,6 @@ export class PlanDetails {

const highEnd = featureCost(sortedCosts[sortedCosts.length - 1].cost_multiple || 0);

// eslint-disable-next-line no-irregular-whitespace
return `${lowEnd} - ${highEnd} / ${singularize(suffix)}${freeText}`;
}

Expand All @@ -75,8 +89,28 @@ export class PlanDetails {
}
}

selectedValue(feature: Catalog.ExpandedFeature): string {
return feature.value_string || '';
private initialFeatures(): UserFeature {
if (!this.plan.body.expanded_features) return {};

return this.plan.body.expanded_features.reduce((obj, feature) => {
// 1. If not customizable, don’t worry about it
if (!feature.customizable) return obj;

// 2. Grab feature name
const name = feature.label;

// 3. Grab the initial value, which should be declared in the schema
let value = feature.value_string; // string
if (feature.value && feature.value.label) value = feature.value.label; // boolean
if (feature.value && feature.value.numeric_details)
value = feature.value.numeric_details.min || 0; // number

return { ...obj, [name]: value };
}, {});
}

selectedValue(feature: Catalog.ExpandedFeature): string | undefined {
return feature.value;
}

customFeatureValue(feature: Catalog.ExpandedFeature) {
Expand Down Expand Up @@ -132,9 +166,10 @@ export class PlanDetails {
})}
</dl>
</div>
<code>Features selected: {JSON.stringify(this.features)}</code>
<footer class="footer">
<div class="cost" itemprop="price">
{$(cost)} <small>&nbsp;/ mo</small>
{$(cost)}&nbsp;<small>/ mo</small>
</div>
<link-button
href={`${RESOURCE_CREATE}${productLabel}&plan=${this.plan.id}`}
Expand Down
1 change: 1 addition & 0 deletions src/global/events.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const FEATURE_CHANGE = 'FEATURE_CHANGE';
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"jsxFactory": "h",
"strictNullChecks": true,
"paths": {
"types/*": ["types/*"]
"*": ["../node_modules/*", "*"]
}
},
"include": ["src"],
Expand Down

0 comments on commit 8ad8c50

Please sign in to comment.