Skip to content

Commit

Permalink
chore: starting component dropdown
Browse files Browse the repository at this point in the history
Co-authored-by: Alexandre Gomes <[email protected]>

feat: create emit event for dropdown component

chore: dropdown parameter received

fix: dropdown html ajusted

chore: change padding value for rem

fix: fit: change error message from Portuguese to English

BREAKING CHANGE: fit: change error message from Portuguese to English

fix devhatt#47

chore: method of adding and removing items implemented

fix: fix: implemeted emit in add and remove method

refactor: Dropdown component refactor

chore: test and storybook for Dropdown component

test: tested the dropdwon component

fix: message error for english

chore: add label and width in props

chore: implemented receive css class by props

chore: adjusting dropdwon's position property

fix: removes unnecessary tests

fix: tests names

fix: changing component classes to BEM css

fix: Dropdown component style ajusted

chore: toggleDropdown, openDropdown, closeDropdown,  setLabel, getLabel, setValue, getValue, clearOptions, clearDropdown methods implemented

fix: closeDropdown fixed

reactor: refactor dropdown component

fix: setValue and OnSelect methods fixed
  • Loading branch information
DominMFD authored and PiluVitu committed Mar 27, 2024
1 parent 6efd4cd commit 24c2ec9
Show file tree
Hide file tree
Showing 8 changed files with 1,712 additions and 1,178 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"@storybook/html": "8.0.0-alpha.16",
"@storybook/html-vite": "8.0.0-alpha.16",
"@storybook/test": "8.0.0-alpha.16",
"@testing-library/dom": "^9.3.4",
"commitizen": "^4.3.0",
"cross-env": "^7.0.3",
"eslint": "^8.56.0",
Expand Down
2,337 changes: 1,159 additions & 1,178 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

60 changes: 60 additions & 0 deletions src/components/Dropdown/components/DropdownItem/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { Component } from 'pet-dex-utilities';
import './index.scss';

const events = ['text:change', 'value:change', 'select', 'unselect'];

const html = `
<li data-select='option' class="dropdown__options--option"></li>
`;

export default function DropdownItem({ text = '', value = '' } = {}) {
Component.call(this, { html, events });

this.setText(text);
this.setValue(value);

this.selected.get('option').addEventListener('click', () => this.toggle());
}

DropdownItem.prototype = Object.assign(
DropdownItem.prototype,
Component.prototype,
{
getText() {
return this.selected.get('option').textContent;
},

setText(text = '') {
this.emit('text:change', text);
this.selected.get('option').textContent = text;
},

getValue() {
return this.selected.get('option').dataset.value;
},

setValue(value = '') {
this.emit('value:change', value);
this.selected.get('option').dataset.value = value;
},

isSelected() {
return this.selected.get('option').dataset.selected === 'true';
},

toggle(condition = this.isSelected()) {
if (condition) this.unselect();
else this.select();
},

select() {
this.selected.get('option').dataset.selected = true;
this.emit('select', this);
},

unselect() {
this.selected.get('option').dataset.selected = false;
this.emit('unselect', this);
},
},
);
14 changes: 14 additions & 0 deletions src/components/Dropdown/components/DropdownItem/index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
@use '~styles/colors.scss' as colors;

.dropdown__options--option {
padding: 0.3rem;

&:hover {
background-color: colors.$blue500;
}

&--selected,
&[data-selected='true'] {
color: colors.$blue600;
}
}
186 changes: 186 additions & 0 deletions src/components/Dropdown/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
import { Component } from 'pet-dex-utilities';
import DropdownItem from './components/DropdownItem';
import './index.scss';

const events = [
'select',
'unselect',
'item:add',
'item:remove',
'open',
'close',
'value:change',
'text:change',
'items:clear',
];

const html = `
<div class="dropdown" data-select="dropdown-container">
<div class="dropdown__toggle" data-select="dropdown-toggle">
<span class="dropdown__selected dropdown__selected--label" data-select="dropdown-selected"></span>
<span class="dropdown__icon" data-select="dropdown-icon">
<svg fill="currentColor" stroke-width="0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style="overflow: visible; color: currentcolor;" height="1em" width="1em">
<path d="M233.4 406.6c12.5 12.5 32.8 12.5 45.3 0l192-192c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L256 338.7 86.6 169.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l192 192z"></path>
</svg>
</span>
</div>
<ul class="dropdown__options" data-select="dropdown-options"></ul>
</div>
`;

export default function Dropdown({
items = [],
placeholder = 'Select an option',
value = null,
cssClass = '',
} = {}) {
Component.call(this, { html, events });

this.placeholder = placeholder;
this.items = new Map();
this.itemSelected = null;

this.onSelect = (item) => {
const existsAndIsDifferent = this.itemSelected != null && this.itemSelected !== item;
if (existsAndIsDifferent) this.itemSelected.unselect();

this.itemSelected = item;
this.setValue(item.getValue());
this.emit('select', item);

if (this.isOpen()) this.close();
};

this.onUnselect = (item) => {
const isDifferent = this.itemSelected !== item;
if (isDifferent) return;

this.itemSelected = null;
this.setValue(null);
this.emit('unselect', item);
};

items.map((item) => this.addItem(item));
this.setValue(value);

this.selected
.get('dropdown-toggle')
.addEventListener('click', () => this.toggle());

const closeOnClickOutside = (event) => {
const isOutside = !event
.composedPath()
.includes(this.selected.get('dropdown-container'));

if (!isOutside) return;

if (this.isOpen()) this.close();
};

this.listen('mount', () => document.addEventListener('click', closeOnClickOutside),
);
this.listen('unmount', () => document.removeEventListener('click', closeOnClickOutside),
);

if (cssClass) {
this.selected.get('dropdown-container').classList.add(cssClass);
}
}

Dropdown.prototype = Object.assign(Dropdown.prototype, Component.prototype, {
addItem(props = {}) {
if (this.items.has(props.value)) return;

const item = new DropdownItem(props);
const $list = this.selected.get('dropdown-options');
item.mount($list);
item.listen('select', this.onSelect);
item.listen('unselect', this.onUnselect);
this.items.set(props.value, item);
this.emit('item:add', item);
},

removeItem(value = '') {
if (!this.items.has(value)) return;

const item = this.items.get(value);
item.unmount();
this.items.delete(value);

if (item.getValue() === this.getValue()) {
this.setValue(null);
}

this.emit('item:remove', item);
},

isOpen() {
return this.selected
.get('dropdown-container')
.classList.contains('dropdown--open');
},

toggle(condition = this.isOpen()) {
if (condition) this.close();
else this.open();
},

open() {
if (this.items.size === 0) return;

const $container = this.selected.get('dropdown-container');
$container.classList.add('dropdown--open');
this.emit('open');
},

close() {
const $container = this.selected.get('dropdown-container');
$container.classList.remove('dropdown--open');
this.emit('close');
},

getValue() {
const { value } = this.selected.get('dropdown-selected').dataset;
if (value == null) return null;
return value;
},

setValue(value = null) {
if (value == null) {
if (this.itemSelected != null) {
this.itemSelected.unselect();
return;
}

this.setText(this.placeholder, true);
this.emit('value:change', null);
delete this.selected.get('dropdown-selected').dataset.value;

return;
}

if (!this.items.has(value)) return;

const item = this.items.get(value);
this.setText(item.getText());
this.selected.get('dropdown-selected').dataset.value = item.getValue();
this.emit('value:change', item.getValue());
},

getText() {
return this.selected.get('dropdown-selected').textContent;
},

setText(text = '', isPlaceholder = false) {
const $container = this.selected.get('dropdown-selected');
$container.classList.toggle('dropdown__selected--label', isPlaceholder);
$container.textContent = text;
this.emit('text:change', text);
},

clearItems() {
this.items.forEach((item) => this.removeItem(item.getValue()));
this.close();
this.emit('items:clear', this);
},
});
94 changes: 94 additions & 0 deletions src/components/Dropdown/index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
@use '~styles/colors.scss' as colors;
@use '~styles/fonts.scss' as fonts;

.dropdown {
width: fit-content;

font-family: fonts.$primaryFont;
color: colors.$gray800;
font-size: 1.4rem;
font-weight: fonts.$semiBold;

position: relative;

&--open {
.dropdown__toggle {
.dropdown__icon {
rotate: 180deg;
}
}

.dropdown__options {
display: block;
}
}

&:hover {
cursor: pointer;
}

&__options {
width: 100%;

display: none;

margin: 0;
padding: 1rem;
box-sizing: border-box;

position: absolute;
z-index: 2;

list-style: none;

background-color: rgb(255, 255, 255);

box-shadow: 0 0 5px 0 rgba(0, 0, 0, 0.2);
border-radius: 1.4rem;

animation: dropdownAnimation 0.3s ease-out;
}

&__toggle {
width: 100%;

display: flex;
gap: 2rem;

justify-content: space-between;

padding: 1rem;

box-sizing: border-box;

background-color: rgb(255, 255, 255);

box-shadow: 0 0 5px 0 rgb(216, 216, 216);
border-radius: 1.4rem;

.dropdown__icon {
color: colors.$gray600;

transition: transform 0.4s ease-out;
}

.dropdown__selected {
&--label {
color: colors.$gray600;
font-weight: 400;
}
}
}
}

@keyframes dropdownAnimation {
from {
transform: translateY(-2.5rem);
opacity: 0;
}

to {
transform: translateY(0);
opacity: 1;
}
}
Loading

0 comments on commit 24c2ec9

Please sign in to comment.