Skip to content

Commit caa8a97

Browse files
committed
feat(cc-plan-configurator): init component
1 parent 8f11ed8 commit caa8a97

File tree

6 files changed

+650
-1
lines changed

6 files changed

+650
-1
lines changed
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
import { css, html } from 'lit';
2+
import { dispatchCustomEvent } from '../../lib/events.js';
3+
import { CcFormControlElement } from '../../lib/form/cc-form-control-element.abstract.js';
4+
import { accessibilityStyles } from '../../styles/accessibility.js';
5+
import { i18n } from '../../translations/translation.js';
6+
import '../cc-plan-picker/cc-plan-picker.js';
7+
8+
/**
9+
* @typedef {import('../cc-plan-picker/cc-plan-picker.types.js').PlanItem} PlanItem
10+
* @typedef {import('./cc-plan-configurator.types.js').ConfiguratorPlan} ConfiguratorPlan
11+
* @typedef {import('lit').PropertyValues<CcPlanConfigurator>} CcPlanConfiguratorPropertyValues
12+
* @typedef {import('../../lib/events.types.js').GenericEventWithTarget<InputEvent, HTMLInputElement>} HTMLInputElementEvent
13+
*/
14+
15+
/**
16+
* A component that allows you to select a plan from a list of plans and refine your choice in a sub picker if needed.
17+
*
18+
* @cssdisplay block
19+
*
20+
* @fires {CustomEvent<string>} cc-plan-configurator:input - Fires the id of the selected plan
21+
*/
22+
export class CcPlanConfigurator extends CcFormControlElement {
23+
static get properties() {
24+
return {
25+
...super.properties,
26+
plans: { type: Array },
27+
readonly: { type: Boolean },
28+
value: { type: String },
29+
_currentPlanId: { type: String },
30+
_currentRelatedPlans: { type: Array },
31+
};
32+
}
33+
34+
constructor() {
35+
super();
36+
37+
/** @type {ConfiguratorPlan[]} List of plans and their possible related plans **/
38+
this.plans = [];
39+
40+
/** @type {boolean} Whether all the form controls should be readonly **/
41+
this.readonly = false;
42+
43+
/** @type {string} Current selected plan */
44+
this.value = '';
45+
46+
/** @type {string} **/
47+
this._currentPlanId = null;
48+
49+
/** @type {PlanItem[]} **/
50+
this._currentRelatedPlans = [];
51+
}
52+
53+
/**
54+
* @param {HTMLInputElementEvent} e
55+
* @private
56+
*/
57+
_onChangePlan(e) {
58+
const currentPlan = this._findPlan(this.plans, e.target.value);
59+
console.log(currentPlan);
60+
this._currentPlanId = e.target.value;
61+
if (currentPlan.relatedPlans != null && currentPlan.relatedPlans.length > 0) {
62+
this.value = currentPlan.relatedPlans[0].id;
63+
this._currentRelatedPlans = currentPlan.relatedPlans;
64+
} else {
65+
this.value = this._currentPlanId;
66+
this._currentRelatedPlans = [];
67+
}
68+
69+
dispatchCustomEvent(this, 'input', this.value);
70+
}
71+
72+
/**
73+
* @param {HTMLInputElementEvent} e
74+
* @private
75+
*/
76+
_onChangeRelatedPlan(e) {
77+
this.value = e.target.value;
78+
dispatchCustomEvent(this, 'input', this.value);
79+
}
80+
81+
/**
82+
* Find a plan by its ID in a list of plans.
83+
*
84+
* @param {ConfiguratorPlan[]} plans - List of plans to search through
85+
* @param {string} planId - ID of the plan to find
86+
* @returns {ConfiguratorPlan} The found plan or the first plan in the list if not found
87+
* @private
88+
*/
89+
_findPlan(plans, planId) {
90+
// We're trying to find if the plan is a related plan or if the plan is a plan with no related plans
91+
const exactPlan = plans.find((plan) => {
92+
if (plan.relatedPlans == null || plan.relatedPlans.length === 0) {
93+
return plan.id === planId;
94+
}
95+
return plan.relatedPlans.find((relatedPlan) => relatedPlan.id === planId) != null;
96+
});
97+
98+
// If it was the case we return the plan
99+
if (exactPlan != null) {
100+
return exactPlan;
101+
}
102+
103+
// If it's not one of the case above we try to find the "parent" plan
104+
const parentPlan = this.plans.find((plan) => {
105+
return plan.id === planId;
106+
});
107+
108+
if (parentPlan != null) {
109+
return parentPlan;
110+
}
111+
112+
// If we don't find anything, the id provided wasn't correct so we default to the first plan of the list
113+
return plans[0];
114+
}
115+
116+
/**
117+
* @param {CcPlanConfiguratorPropertyValues} changedProperties
118+
*/
119+
willUpdate(changedProperties) {
120+
if (changedProperties.has('plans') && changedProperties.has('value')) {
121+
const currentPlan = this._findPlan(this.plans, this.value);
122+
if (currentPlan.relatedPlans != null && currentPlan.relatedPlans.length > 0) {
123+
const valueExistsInRelated = currentPlan.relatedPlans.find((plan) => plan.id === this.value) != null;
124+
this.value = valueExistsInRelated ? this.value : currentPlan.relatedPlans[0].id;
125+
} else {
126+
this.value = currentPlan.id;
127+
}
128+
this._currentPlanId = currentPlan.id;
129+
this._currentRelatedPlans = currentPlan.relatedPlans;
130+
}
131+
}
132+
133+
render() {
134+
return html`
135+
<cc-plan-picker
136+
@input="${this._onChangePlan}"
137+
name="plan"
138+
.plans=${this.plans}
139+
value="${this._currentPlanId}"
140+
?readonly="${this.readonly}"
141+
></cc-plan-picker>
142+
${this._currentRelatedPlans != null && this._currentRelatedPlans.length > 0
143+
? html`
144+
<cc-plan-picker
145+
@input="${this._onChangeRelatedPlan}"
146+
name="customize-plans"
147+
.plans="${this._currentRelatedPlans}"
148+
value="${this.value}"
149+
legend="${i18n('cc-plan-configurator.legend.customize')}"
150+
?readonly="${this.readonly}"
151+
></cc-plan-picker>
152+
`
153+
: ''}
154+
`;
155+
}
156+
157+
static get styles() {
158+
return [
159+
accessibilityStyles,
160+
// language=CSS
161+
css`
162+
:host {
163+
display: block;
164+
}
165+
166+
legend {
167+
display: flex;
168+
gap: 0.25em;
169+
}
170+
171+
fieldset {
172+
border: none;
173+
margin: 0;
174+
padding: 0;
175+
}
176+
177+
.plan-legend-icon {
178+
--cc-icon-color: var(--cc-color-text-primary);
179+
180+
align-self: center;
181+
}
182+
183+
.plan-legend-text {
184+
--ct-form-label-font-family: 'Source Sans 3';
185+
--ct-form-label-font-size: 1.625em;
186+
--ct-form-label-font-weight: 500;
187+
--ct-form-input-font-size: 1.25em;
188+
189+
color: var(--cc-color-text-primary-strongest);
190+
font-family: var(--ct-form-label-font-family), sans-serif;
191+
font-size: var(--ct-form-label-font-size);
192+
font-weight: var(--ct-form-label-font-weight);
193+
}
194+
`,
195+
];
196+
}
197+
}
198+
199+
window.customElements.define('cc-plan-configurator', CcPlanConfigurator);

0 commit comments

Comments
 (0)