-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(RSS-ECOMM-2_14): set default address during registration (#131)
* feat: add CountryChoice component * fix: postalCode validation * feat: update state * refactor: separate CountryChoice component into a module * feat: add settings address by default
- Loading branch information
Showing
18 changed files
with
788 additions
and
272 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
import getStore from '@/shared/Store/Store.ts'; | ||
import { setBillingCountry, setShippingCountry } from '@/shared/Store/actions.ts'; | ||
import observeStore, { selectBillingCountry, selectShippingCountry } from '@/shared/Store/observer.ts'; | ||
import { EVENT_NAMES, REGISTRATION_FORM_BILLING_ADDRESS_COUNTRY_FIELD_PARAMS } from '@/shared/constants/enums.ts'; | ||
import getCountryIndex from '@/shared/utils/getCountryIndex.ts'; | ||
|
||
import CountryChoiceView from '../view/CountryChoiceView.ts'; | ||
|
||
class CountryChoiceModel { | ||
private view: CountryChoiceView; | ||
|
||
constructor(input: HTMLInputElement) { | ||
this.view = new CountryChoiceView(input); | ||
this.setCountryItemsHandlers(input); | ||
this.setInputHandler(input); | ||
|
||
const action = | ||
input.id === REGISTRATION_FORM_BILLING_ADDRESS_COUNTRY_FIELD_PARAMS.inputParams.id | ||
? selectBillingCountry | ||
: selectShippingCountry; | ||
|
||
observeStore(action, () => { | ||
const event = new Event(EVENT_NAMES.INPUT); | ||
input.dispatchEvent(event); | ||
}); | ||
} | ||
|
||
private setCountryItemsHandlers(input: HTMLInputElement): boolean { | ||
const inputHTML = input; | ||
this.view.getCountryItems().forEach((countryItem) => { | ||
const currentItem = countryItem; | ||
currentItem.addEventListener(EVENT_NAMES.CLICK, () => { | ||
if (currentItem.textContent) { | ||
inputHTML.value = currentItem.textContent; | ||
this.setCountryToStore(currentItem, inputHTML.id); | ||
const event = new Event(EVENT_NAMES.INPUT); | ||
inputHTML.dispatchEvent(event); | ||
this.view.hideCountryChoice(); | ||
} | ||
}); | ||
}); | ||
return true; | ||
} | ||
|
||
private setCountryToStore(element: HTMLDivElement | HTMLInputElement, key: string): boolean { | ||
const currentCountryIndex = getCountryIndex( | ||
element instanceof HTMLDivElement ? element.textContent || '' : element.value, | ||
); | ||
|
||
const action = | ||
key === REGISTRATION_FORM_BILLING_ADDRESS_COUNTRY_FIELD_PARAMS.inputParams.id | ||
? setBillingCountry | ||
: setShippingCountry; | ||
getStore().dispatch(action(currentCountryIndex)); | ||
return true; | ||
} | ||
|
||
private setInputHandler(input: HTMLInputElement): boolean { | ||
input.addEventListener(EVENT_NAMES.FOCUS, () => this.view.showCountryChoice()); | ||
input.addEventListener(EVENT_NAMES.INPUT, () => { | ||
this.view.switchVisibilityCountryItems(input); | ||
this.setCountryToStore(input, input.id); | ||
}); | ||
return true; | ||
} | ||
|
||
public getHTML(): HTMLDivElement { | ||
return this.view.getHTML(); | ||
} | ||
|
||
public getView(): CountryChoiceView { | ||
return this.view; | ||
} | ||
} | ||
|
||
export default CountryChoiceModel; |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
import { COUNTRIES, EVENT_NAMES, TAG_NAMES } from '@/shared/constants/enums.ts'; | ||
import createBaseElement from '@/shared/utils/createBaseElement.ts'; | ||
|
||
import styles from './countryChoiceView.module.scss'; | ||
|
||
class CountryChoiceView { | ||
private countryChoice: HTMLDivElement; | ||
|
||
private countryDropList: HTMLDivElement; | ||
|
||
private countryItems: HTMLDivElement[] = []; | ||
|
||
constructor(input: HTMLInputElement) { | ||
this.countryDropList = this.createCountryDropList(); | ||
this.countryChoice = this.createHTML(); | ||
|
||
// TBD replace node document with some element because listener works two times (twice) | ||
document.addEventListener(EVENT_NAMES.CLICK, (event) => { | ||
if (!this.countryDropList.classList.contains(styles.hidden) && event.target !== input) { | ||
this.hideCountryChoice(); | ||
} | ||
}); | ||
} | ||
|
||
private createCountryDropList(): HTMLDivElement { | ||
this.countryDropList = createBaseElement({ | ||
cssClasses: [styles.countryDropList], | ||
tag: TAG_NAMES.DIV, | ||
}); | ||
|
||
Object.entries(COUNTRIES).forEach(([countryCode]) => | ||
this.countryDropList.append(this.createCountryItem(countryCode)), | ||
); | ||
|
||
return this.countryDropList; | ||
} | ||
|
||
private createCountryItem(countryCode: string): HTMLDivElement { | ||
const countryItem = createBaseElement({ | ||
cssClasses: [styles.countryItem], | ||
innerContent: countryCode, | ||
tag: TAG_NAMES.DIV, | ||
}); | ||
|
||
this.countryItems.push(countryItem); | ||
|
||
return countryItem; | ||
} | ||
|
||
private createHTML(): HTMLDivElement { | ||
this.countryChoice = createBaseElement({ | ||
cssClasses: [styles.countryChoice, styles.hidden], | ||
tag: TAG_NAMES.DIV, | ||
}); | ||
|
||
this.countryChoice.append(this.countryDropList); | ||
|
||
return this.countryChoice; | ||
} | ||
|
||
public getCountryDropList(): HTMLDivElement { | ||
return this.countryDropList; | ||
} | ||
|
||
public getCountryItems(): HTMLDivElement[] { | ||
return this.countryItems; | ||
} | ||
|
||
public getHTML(): HTMLDivElement { | ||
return this.countryChoice; | ||
} | ||
|
||
public hideCountryChoice(): void { | ||
this.countryChoice.classList.add(styles.hidden); | ||
document.body.classList.remove(styles.stopScroll); | ||
} | ||
|
||
public showCountryChoice(): void { | ||
this.countryChoice.classList.remove(styles.hidden); | ||
document.body.classList.add(styles.stopScroll); | ||
} | ||
|
||
public switchVisibilityCountryItems(inputHTML: HTMLInputElement): boolean { | ||
const filterValue = inputHTML.value.toLowerCase(); | ||
this.countryItems.forEach((countryItem) => { | ||
const itemValue = countryItem.textContent?.toLowerCase(); | ||
countryItem.classList.toggle(styles.hidden, !itemValue?.includes(filterValue)); | ||
}); | ||
|
||
return true; | ||
} | ||
} | ||
|
||
export default CountryChoiceView; |
109 changes: 109 additions & 0 deletions
109
src/features/CountryChoice/view/countryChoiceView.module.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
.countryChoice { | ||
position: sticky; | ||
z-index: 5; | ||
grid-column: 1; | ||
grid-row: 4; | ||
|
||
@media (max-width: 768px) { | ||
grid-column: 1; | ||
grid-row: 5; | ||
} | ||
} | ||
|
||
.countryItem { | ||
padding: calc(var(--extra-small-offset) / 4) calc(var(--extra-small-offset) / 2); | ||
font: var(--regular-font); | ||
letter-spacing: 1px; | ||
text-align: end; | ||
color: var(--noble-gray-800); | ||
transition: | ||
color 0.2s, | ||
background-color 0.2s; | ||
animation: show 0.5s ease-in forwards; | ||
cursor: pointer; | ||
|
||
@media (hover: hover) { | ||
&:hover { | ||
color: var(--steam-green-800); | ||
background-color: var(--noble-gray-200); | ||
} | ||
} | ||
|
||
@media (max-width: 768px) { | ||
text-align: start; | ||
} | ||
|
||
&.hidden { | ||
animation: hide 0.5s ease-in forwards; | ||
} | ||
} | ||
|
||
@keyframes hide { | ||
0% { | ||
opacity: 1; | ||
visibility: visible; | ||
} | ||
|
||
50% { | ||
opacity: 0; | ||
visibility: hidden; | ||
} | ||
|
||
100% { | ||
display: none; | ||
} | ||
} | ||
|
||
@keyframes show { | ||
0% { | ||
display: block; | ||
opacity: 0; | ||
visibility: hidden; | ||
} | ||
|
||
100% { | ||
opacity: 1; | ||
visibility: visible; | ||
} | ||
} | ||
|
||
.hidden { | ||
opacity: 0; | ||
visibility: hidden; | ||
pointer-events: none; | ||
} | ||
|
||
.countryDropList { | ||
position: absolute; | ||
right: 0; | ||
overflow-y: scroll; | ||
border: 1px solid var(--noble-gray-300); | ||
border-radius: var(--small-br); | ||
width: 100%; | ||
min-height: 200px; | ||
max-height: 200px; | ||
background-color: var(--white); | ||
opacity: 1; | ||
visibility: visible; | ||
transition: | ||
opacity 0.2s, | ||
visibility 0.2s; | ||
|
||
&::-webkit-scrollbar { | ||
width: 8px; | ||
} | ||
|
||
&::-webkit-scrollbar-track { | ||
background: var(--noble-gray-200); | ||
} | ||
|
||
&::-webkit-scrollbar-thumb { | ||
border-radius: var(--small-br); | ||
background-color: var(--steam-green-800); | ||
cursor: pointer; | ||
} | ||
} | ||
|
||
.stopScroll { | ||
overflow-y: hidden; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.