Skip to content

Commit

Permalink
feat(dropdown button): dropdown button adhering to Freshworks Design …
Browse files Browse the repository at this point in the history
…System

Dropdown button similar to Select component but build on top of fw-button. It can be searchable,
splittable and supports multi selects

fix #112
  • Loading branch information
shravan-balasubramanian committed Sep 30, 2020
1 parent 1fa96fe commit 43e8bda
Show file tree
Hide file tree
Showing 14 changed files with 952 additions and 13 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
"docs:build": "vuepress build src",
"docs:dev": "npm run build:components && vuepress dev src",
"generate": "plop",
"lint:styles": "stylelint 'src/**/*.scss' 'src/.vuepress/**/*.vue'",
"lint:styles": "stylelint 'src/**/*.scss' 'src/.vuepress/**/*.vue' --fix",
"lint:ts": "tslint -c tslint.json -p tsconfig.json -t grouped -s node_modules/custom-tslint-formatters/formatters",
"lint": "npm run lint:ts && npm run lint:styles",
"local": "stencil build --serve --dev --watch",
Expand Down
81 changes: 79 additions & 2 deletions src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,36 @@ export namespace Components {
*/
"value": string;
}
interface FwDropdownButton {
/**
* Dropdown Button color
*/
"color": string;
/**
* Label for the dropdown button
*/
"label": string;
/**
* Options to show in the dropdown button
*/
"options": any[];
/**
* Placeholder text for search input. Validated only if dropdown and searchable is true
*/
"placeholder": string;
/**
* Displays a searchable dropdown button
*/
"searchable": boolean;
/**
* Displays a split dropdown button
*/
"split": boolean;
/**
* Value of the dropdown button
*/
"value": any;
}
interface FwIcon {
/**
* Color in which the icon is displayed, specified as a standard CSS color or as a HEX code.
Expand All @@ -96,7 +126,7 @@ export namespace Components {
/**
* Size of the icon, specified in number of pixels.
*/
"size": number;
"size": any;
}
interface FwInput {
/**
Expand Down Expand Up @@ -542,6 +572,12 @@ declare global {
prototype: HTMLFwDatepickerElement;
new (): HTMLFwDatepickerElement;
};
interface HTMLFwDropdownButtonElement extends Components.FwDropdownButton, HTMLStencilElement {
}
var HTMLFwDropdownButtonElement: {
prototype: HTMLFwDropdownButtonElement;
new (): HTMLFwDropdownButtonElement;
};
interface HTMLFwIconElement extends Components.FwIcon, HTMLStencilElement {
}
var HTMLFwIconElement: {
Expand Down Expand Up @@ -642,6 +678,7 @@ declare global {
"fw-button": HTMLFwButtonElement;
"fw-checkbox": HTMLFwCheckboxElement;
"fw-datepicker": HTMLFwDatepickerElement;
"fw-dropdown-button": HTMLFwDropdownButtonElement;
"fw-icon": HTMLFwIconElement;
"fw-input": HTMLFwInputElement;
"fw-label": HTMLFwLabelElement;
Expand Down Expand Up @@ -767,6 +804,44 @@ declare namespace LocalJSX {
*/
"value"?: string;
}
interface FwDropdownButton {
/**
* Dropdown Button color
*/
"color"?: string;
/**
* Label for the dropdown button
*/
"label"?: string;
/**
* Triggered when an option is clicked
*/
"onFwOptionClick"?: (event: CustomEvent<any>) => void;
/**
* Triggered when Add button for searchable dropdown is clicked
*/
"onFwOptionsAdd"?: (event: CustomEvent<any>) => void;
/**
* Options to show in the dropdown button
*/
"options"?: any[];
/**
* Placeholder text for search input. Validated only if dropdown and searchable is true
*/
"placeholder"?: string;
/**
* Displays a searchable dropdown button
*/
"searchable"?: boolean;
/**
* Displays a split dropdown button
*/
"split"?: boolean;
/**
* Value of the dropdown button
*/
"value"?: any;
}
interface FwIcon {
/**
* Color in which the icon is displayed, specified as a standard CSS color or as a HEX code.
Expand All @@ -779,7 +854,7 @@ declare namespace LocalJSX {
/**
* Size of the icon, specified in number of pixels.
*/
"size"?: number;
"size"?: any;
}
interface FwInput {
/**
Expand Down Expand Up @@ -1295,6 +1370,7 @@ declare namespace LocalJSX {
"fw-button": FwButton;
"fw-checkbox": FwCheckbox;
"fw-datepicker": FwDatepicker;
"fw-dropdown-button": FwDropdownButton;
"fw-icon": FwIcon;
"fw-input": FwInput;
"fw-label": FwLabel;
Expand All @@ -1320,6 +1396,7 @@ declare module "@stencil/core" {
"fw-button": LocalJSX.FwButton & JSXBase.HTMLAttributes<HTMLFwButtonElement>;
"fw-checkbox": LocalJSX.FwCheckbox & JSXBase.HTMLAttributes<HTMLFwCheckboxElement>;
"fw-datepicker": LocalJSX.FwDatepicker & JSXBase.HTMLAttributes<HTMLFwDatepickerElement>;
"fw-dropdown-button": LocalJSX.FwDropdownButton & JSXBase.HTMLAttributes<HTMLFwDropdownButtonElement>;
"fw-icon": LocalJSX.FwIcon & JSXBase.HTMLAttributes<HTMLFwIconElement>;
"fw-input": LocalJSX.FwInput & JSXBase.HTMLAttributes<HTMLFwInputElement>;
"fw-label": LocalJSX.FwLabel & JSXBase.HTMLAttributes<HTMLFwLabelElement>;
Expand Down
2 changes: 2 additions & 0 deletions src/components/button/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,14 @@ fw-button displays a button on the user interface and enables performing specifi
### Used by

- [fw-datepicker](../datepicker)
- [fw-dropdown-button](../dropdown-button)
- [fw-modal](../modal)

### Graph
```mermaid
graph TD;
fw-datepicker --> fw-button
fw-dropdown-button --> fw-button
fw-modal --> fw-button
style fw-button fill:#f9f,stroke:#333,stroke-width:4px
```
Expand Down
13 changes: 13 additions & 0 deletions src/components/checkbox/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,19 @@ fw-checkbox displays a check box on the user interface and enables assigning a s
| `fwFocus` | Triggered when the check box comes into focus. | `CustomEvent<void>` |


## Dependencies

### Used by

- [fw-dropdown-button](../dropdown-button)

### Graph
```mermaid
graph TD;
fw-dropdown-button --> fw-checkbox
style fw-checkbox fill:#f9f,stroke:#333,stroke-width:4px
```

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

Built with ❤ at Freshworks
167 changes: 167 additions & 0 deletions src/components/dropdown-button/dropdown-button.e2e.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
import { newE2EPage } from '@stencil/core/testing';

describe('fw-dropdown-button', () => {
it('renders simple dropdown button', async () => {
const simpleDropdown = `
<fw-dropdown-button label="Click me">
<div slot="dropdown-options">
<option id="1" value="Ullu">Ullu</option>
<option id="2" value="Hotstar">Hotstar</option>
<option id="3" value="Amazon">Amazon</option>
<option id="4" value="Netflix">Netflix</option>
<option id="5" value="Mx player">Mx Player</option>
<option id="6" value="Share it">Share it</option>
<option id="7" value="Prime">Amazon Prime</option>
<option id="8" value="Watch32">Watch32</option>
<option id="9" value="YTS Movies">YTS Movies</option>
<option id="10" value="Telegram">Telegram</option>
<option id="11" value="Solar Movies">Solar Movies</option>
<option id="12" value="Yifi torrents">Yifi torrents</option>
</div>
</fw-dropdown-button>
`;
const expectedOutput = `
<div class="dropdown-container">
<div class="btn-container">
<fw-button class="hydrated">
Click me
<span class="down-arrow">
<fw-icon class="hydrated"></fw-icon>
</span>
</fw-button>
</div>
<ul class="dropdown-menu">
<li class="dropdown-item">
Ullu
</li>
<li class="dropdown-item">
Hotstar
</li>
<li class="dropdown-item">
Amazon
</li>
<li class="dropdown-item">
Netflix
</li>
<li class="dropdown-item">
Mx Player
</li>
<li class="dropdown-item">
Share it
</li>
<li class="dropdown-item">
Amazon Prime
</li>
<li class="dropdown-item">
Watch32
</li>
<li class="dropdown-item">
YTS Movies
</li>
<li class="dropdown-item">
Telegram
</li>
<li class="dropdown-item">
Solar Movies
</li>
<li class="dropdown-item">
Yifi torrents
</li>
</ul>
</div>
`;
const page = await newE2EPage();

await page.setContent(simpleDropdown);
const element = await page.find('fw-dropdown-button');
expect(element.shadowRoot).toEqualHtml(expectedOutput);
});

it('renders split dropdown button', async () => {
const splitDropdown = `
<fw-dropdown-button label="Click me" split>
<div slot="dropdown-options">
<option id="1" value="Ullu">Ullu</option>
<option id="2" value="Hotstar">Hotstar</option>
<option id="3" value="Amazon">Amazon</option>
<option id="4" value="Netflix">Netflix</option>
</div>
</fw-dropdown-button>
`;

const page = await newE2EPage();

await page.setContent(splitDropdown);
const element = await page.find('fw-dropdown-button');
expect(element.shadowRoot).toString().includes('<div class="dropdown-state">');
});

it('emits value if the option is selected', async () => {
const dropdown = `
<fw-dropdown-button label="Click me">
<div slot="dropdown-options">
<option id="1" value="Ullu">Ullu</option>
<option id="2" value="Hotstar">Hotstar</option>
<option id="3" value="Amazon">Amazon</option>
<option id="4" value="Netflix">Netflix</option>
</div>
</fw-dropdown-button>
`;

const page = await newE2EPage();

await page.setContent(dropdown);
const el = await page.find('fw-dropdown-button');
const fwOptionClick = await page.spyOnEvent('fwOptionClick');

const dropdownEl = await page.find('fw-dropdown-button');
await dropdownEl.click();
await page.waitForChanges();

const dropdownOptions = await page.findAll('fw-dropdown-button >>> li');
await dropdownOptions[0].click();
await page.waitForChanges();

expect(fwOptionClick).toHaveReceivedEvent();
const val = await el.getProperty('value');
expect(val).toEqual('Ullu');
});

it('emits value if the options are selected for search dropdown', async () => {
const dropdown = `
<fw-dropdown-button label="Click me" searchable>
<div slot="dropdown-options">
<option id="1" value="Ullu">Ullu</option>
<option id="2" value="Hotstar">Hotstar</option>
<option id="3" value="Amazon">Amazon</option>
<option id="4" value="Netflix">Netflix</option>
</div>
</fw-dropdown-button>
`;

const page = await newE2EPage();

await page.setContent(dropdown);
const el = await page.find('fw-dropdown-button');
const fwOptionsAdd = await page.spyOnEvent('fwOptionsAdd');

const dropdownEl = await page.find('fw-dropdown-button');
await dropdownEl.click();
await page.waitForChanges();

const addButton = await page.find('fw-dropdown-button >>> #addBtn');

// Check 1st and 3rd option
const dropdownCheckboxes = await page.findAll('fw-dropdown-button >>> fw-checkbox');
dropdownCheckboxes[0].setProperty('checked', true);
dropdownCheckboxes[2].setProperty('checked', true);

await addButton.click();

await page.waitForChanges();

expect(fwOptionsAdd).toHaveReceivedEvent();
const val = await el.getProperty('value');
expect(val).toEqual(['Ullu', 'Amazon']);
});
});
Loading

0 comments on commit 43e8bda

Please sign in to comment.