Skip to content

Commit

Permalink
feat: add multiple selection feature #199
Browse files Browse the repository at this point in the history
- add test case for multiple selection
- add api docs
- add example for multiple selection
  • Loading branch information
fajar-apri-alaska committed May 15, 2024
1 parent 39ffd11 commit 7729eec
Show file tree
Hide file tree
Showing 12 changed files with 240 additions and 21 deletions.
7 changes: 7 additions & 0 deletions apiExamples/multiple.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<auro-menu multiSelect>
<auro-menuoption value="stops">Stops</auro-menuoption>
<auro-menuoption value="price">Price</auro-menuoption>
<auro-menuoption value="duration">Duration</auro-menuoption>
<auro-menuoption value="departure">Departure</auro-menuoption>
<auro-menuoption value="arrival">Arrival</auro-menuoption>
</auro-menu>
36 changes: 35 additions & 1 deletion demo/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@ The auro-menu element provides users a way to select from a list of options.
|------------------|------------------|-----------|-------------|--------------------------------------------------|
| [disabled](#disabled) | `disabled` | `Boolean` | | When true, the entire menu and all options are disabled; |
| [matchWord](#matchWord) | `matchWord` | `String` | "undefined" | Specifies the a string used to highlight matched string parts in options. |
| [multiSelect](#multiSelect) | `multiSelect` | `Boolean` | "undefined" | When true, the selected option can be multiple option. |
| [noCheckmark](#noCheckmark) | `noCheckmark` | `Boolean` | false | When true, selected option will not show the checkmark. |
| [optionActive](#optionActive) | `optionActive` | `object` | "undefined" | |
| [optionSelected](#optionSelected) | `optionSelected` | `Object` | "undefined" | Specifies the current selected menuOption. |
| [ready](#ready) | `ready` | `Boolean` | false | When false the component API should not be called. |
| [value](#value) | `value` | `String` | "undefined" | Value selected for the menu. |
| [value](#value) | `value` | `String` | "undefined" | Value selected for the menu, can be String or Array if `multiSelect` is true. |

## Methods

Expand Down Expand Up @@ -277,6 +278,39 @@ export function auroMenuMatchWordExample() {
<!-- AURO-GENERATED-CONTENT:END -->
</auro-accordion>

### Multi Select

The `auro-menu` supports a multi-select option. To use, place the `multiSelect` attribute on the `<auro-menu>` element tag. When applied, the `value` attribute will become an Array versus String value.

<div class="exampleWrapper">
<!-- AURO-GENERATED-CONTENT:START (FILE:src=./../../apiExamples/multiple.html) -->
<!-- The below content is automatically added from ./../../apiExamples/multiple.html -->
<auro-menu multiSelect>
<auro-menuoption value="stops">Stops</auro-menuoption>
<auro-menuoption value="price">Price</auro-menuoption>
<auro-menuoption value="duration">Duration</auro-menuoption>
<auro-menuoption value="departure">Departure</auro-menuoption>
<auro-menuoption value="arrival">Arrival</auro-menuoption>
</auro-menu>
<!-- AURO-GENERATED-CONTENT:END -->
</div>
<auro-accordion alignRight>
<span slot="trigger">See code</span>
<!-- AURO-GENERATED-CONTENT:START (CODE:src=./../../apiExamples/multiple.html) -->
<!-- The below code snippet is automatically added from ./../../apiExamples/multiple.html -->

```html
<auro-menu multiSelect>
<auro-menuoption value="stops">Stops</auro-menuoption>
<auro-menuoption value="price">Price</auro-menuoption>
<auro-menuoption value="duration">Duration</auro-menuoption>
<auro-menuoption value="departure">Departure</auro-menuoption>
<auro-menuoption value="arrival">Arrival</auro-menuoption>
</auro-menu>
```
<!-- AURO-GENERATED-CONTENT:END -->
</auro-accordion>

### Common Use Cases

#### Scroll
Expand Down
1 change: 0 additions & 1 deletion demo/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ A basic `auro-menu` element with nested `auro-menuoption` elements to generate a
```
<!-- AURO-GENERATED-CONTENT:END -->
</auro-accordion>
Having a closing statement about your example helps to really complete the thought with your reader.

## Recommended Use and Version Control

Expand Down
3 changes: 2 additions & 1 deletion docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ The auro-menu element provides users a way to select from a list of options.
|------------------|------------------|-----------|-------------|--------------------------------------------------|
| `disabled` | `disabled` | `Boolean` | | When true, the entire menu and all options are disabled; |
| `matchWord` | `matchWord` | `String` | "undefined" | Specifies the a string used to highlight matched string parts in options. |
| `multiSelect` | `multiSelect` | `Boolean` | "undefined" | When true, the selected option can be multiple option. |
| `noCheckmark` | `noCheckmark` | `Boolean` | false | When true, selected option will not show the checkmark. |
| `optionActive` | `optionActive` | `object` | "undefined" | |
| `optionSelected` | `optionSelected` | `Object` | "undefined" | Specifies the current selected menuOption. |
| `ready` | `ready` | `Boolean` | false | When false the component API should not be called. |
| `value` | `value` | `String` | "undefined" | Value selected for the menu. |
| `value` | `value` | `String` | "undefined" | Value selected for the menu, can be String or Array if `multiSelect` is true. |

## Methods

Expand Down
17 changes: 17 additions & 0 deletions docs/partials/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,23 @@ The `auro-menu` component supports the use of the `matchWord` attribute to highl

</auro-accordion>

### Multi Select

The `auro-menu` supports a multi-select option. To use, place the `multiSelect` attribute on the `<auro-menu>` element tag. When applied, the `value` attribute will become an Array versus String value.

<div class="exampleWrapper">
<!-- AURO-GENERATED-CONTENT:START (FILE:src=./../../apiExamples/multiple.html) -->
<!-- AURO-GENERATED-CONTENT:END -->
</div>

<auro-accordion alignRight>
<span slot="trigger">See code</span>

<!-- AURO-GENERATED-CONTENT:START (CODE:src=./../../apiExamples/multiple.html) -->
<!-- AURO-GENERATED-CONTENT:END -->

</auro-accordion>

### Common Use Cases

#### Scroll
Expand Down
2 changes: 0 additions & 2 deletions docs/partials/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,6 @@ A basic `auro-menu` element with nested `auro-menuoption` elements to generate a

</auro-accordion>

Having a closing statement about your example helps to really complete the thought with your reader.

## Recommended Use and Version Control

There are two important parts of every Auro component. The <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes">class</a> and the custom clement. The class is exported and then used as part of defining the Web Component. When importing this component as described in the <a href="#install">install</a> section, the class is imported and the `auro-menu` custom element is defined automatically.
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion scripts/generateDocs.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import markdownMagic from 'markdown-magic';
import fs from 'fs';
import https from 'https';

const __dirname = new URL('.', import.meta.url).pathname;
import { fileURLToPath } from 'url';

const __dirname = fileURLToPath(new URL('.', import.meta.url));

const readmeTemplateUrl = 'https://raw.githubusercontent.com/AlaskaAirlines/WC-Generator/master/componentDocs/README.md';
const dirDocTemplates = './docTemplates';
Expand Down
98 changes: 92 additions & 6 deletions src/auro-menu.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ import "mark.js/dist/mark.min";
* @attr {String} matchWord - Specifies the a string used to highlight matched string parts in options.
* @attr {Boolean} disabled - When true, the entire menu and all options are disabled;
* @attr {Boolean} noCheckmark - When true, selected option will not show the checkmark.
* @attr {String} value - Value selected for the menu.
* @attr {Boolean} multiSelect - When true, the selected option can be multiple option.
* @attr {String} value - Value selected for the menu, can be String or Array if `multiSelect` is true.
* @prop {Boolean} ready - When false the component API should not be called.
* @event auroMenu-selectedOption - Notifies that a new menuoption selection has been made.
* @event selectedOption - (DEPRECATED) Notifies that a new menuoption selection has been made.
Expand All @@ -41,6 +42,7 @@ export class AuroMenu extends LitElement {
this.noCheckmark = false;
this.ready = false;
this.optionActive = undefined;
this.multiSelect = undefined;

/**
* @private
Expand All @@ -62,6 +64,7 @@ export class AuroMenu extends LitElement {
optionActive: { type: Object },
matchWord: { type: String },
ready: { type: Boolean },
multiSelect: { type: Boolean },
value: { type: String }
};
}
Expand Down Expand Up @@ -100,10 +103,26 @@ export class AuroMenu extends LitElement {
this.markOptions();
}


if (changedProperties.has('value')) {
this.selectByValue(this.value);
if (!this.multiSelect) {
this.selectByValue(this.value);
} else {
try {
const values = JSON.parse(this.value);
if (this.value && Array.isArray(values)) {
values.forEach((val) => {
this.selectByValue(val);
});
}
} catch (error) {
// eslint-disable-next-line no-console
console.error(error);
}
}
}


if (changedProperties.has('disabled')) {
const options = Array.from(this.querySelectorAll('auro-menuoption'));

Expand Down Expand Up @@ -164,6 +183,50 @@ export class AuroMenu extends LitElement {
}
}

/**
* Toggle the selection state of the menuoption.
* @param {Object} option - The menuoption to toggle the selection state.
* @private
* @return {void}
*/
toggleSelectionState(option) {
if (option.hasAttribute('selected')) {
this.handleDeselectState(option);
} else {
this.handleLocalSelectState(option);
}
}

/**
* De-select the menuoption, the menu value and stored option.
* @param {Object} option - The menuoption to be deselected.
* @private
* @return {void}
*/
handleDeselectState(option) {
option.removeAttribute('selected');
option.classList.remove('active');
option.ariaSelected = false;

// remove from this.value if there is any, set to undefined if became empty array
if (this.multiSelect && Array.isArray(this.value)) {
this.value = this.value.filter((val) => val !== option.value);
if (this.value.length === 0) {
this.value = undefined;
}
}

// do the same for selected option DOM
if (this.multiSelect && Array.isArray(this.optionSelected)) {
this.optionSelected = this.optionSelected.filter((val) => val !== option);
if (this.optionSelected.length === 0) {
this.optionSelected = undefined;
}
}

this.index = this.items.indexOf(option);
}

/**
* Set the attributes on the selected menuoption, the menu value and stored option.
* @param {Object} option - The menuoption to be selected.
Expand All @@ -174,8 +237,29 @@ export class AuroMenu extends LitElement {
option.classList.add('active');
option.ariaSelected = true;

this.value = option.value;
this.optionSelected = option;
if (this.multiSelect) {
// push option.value to this.value as an array if there is none yet in the value
if (!this.value) {
this.value = [option.value];
} else if (Array.isArray(this.value)) {
if (!this.value.includes(option.value)) {
this.value.push(option.value);
}
}

// do the same for selected option DOM
if (!this.optionSelected) {
this.optionSelected = [option];
} else if (Array.isArray(this.optionSelected)) {
if (!this.optionSelected.includes(option)) {
this.optionSelected.push(option);
}
}
} else {
this.value = option.value;
this.optionSelected = option;
}

this.index = this.items.indexOf(option);
}

Expand Down Expand Up @@ -208,7 +292,9 @@ export class AuroMenu extends LitElement {
}

if (this.items[this.index] && !this.items[this.index].hasAttribute('disabled')) {
this.resetOptionsStates();
if (!this.multiSelect) {
this.resetOptionsStates();
}

if (this.index >= 0) {
const option = this.items[this.index];
Expand Down Expand Up @@ -236,7 +322,7 @@ export class AuroMenu extends LitElement {
composed: true,
}));
} else {
this.handleLocalSelectState(option);
this.toggleSelectionState(option);
}
}
}
Expand Down
8 changes: 4 additions & 4 deletions src/style-base.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@
/* stylelint-disable declaration-empty-line-before */

// Import Auro tokens
@import './../node_modules/@aurodesignsystem/design-tokens/dist/tokens/SCSSVariables';
@import './../node_modules/@aurodesignsystem/design-tokens/dist/tokens/SassCustomProperties';
@import './../node_modules/@aurodesignsystem/webcorestylesheets/src/core';
@import "./../node_modules/@aurodesignsystem/design-tokens/dist/tokens/SCSSVariables";
@import "./../node_modules/@aurodesignsystem/design-tokens/dist/tokens/SassCustomProperties";
@import "./../node_modules/@aurodesignsystem/webcorestylesheets/src/core";

// Support for auroElement styles
// @import "./../node_modules/@aurodesignsystem/webcorestylesheets/dist/auroElement/auroElement";

// Support for fallback values
@import './../node_modules/@aurodesignsystem/design-tokens/dist/tokens/SCSSVariables.scss';
@import "./../node_modules/@aurodesignsystem/design-tokens/dist/tokens/SCSSVariables.scss";

/* stylelint-disable declaration-no-important, order/properties-order */

Expand Down
6 changes: 3 additions & 3 deletions src/style-menuoption.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@

/* stylelint-disable declaration-empty-line-before */

@import './../node_modules/@aurodesignsystem/design-tokens/dist/tokens/SCSSVariables';
@import './../node_modules/@aurodesignsystem/webcorestylesheets/src/breakpoints';
@import "./../node_modules/@aurodesignsystem/design-tokens/dist/tokens/SCSSVariables";
@import "./../node_modules/@aurodesignsystem/webcorestylesheets/src/breakpoints";

// Support for fallback values
@import './../node_modules/@aurodesignsystem/design-tokens/dist/tokens/SCSSVariables.scss';
@import "./../node_modules/@aurodesignsystem/design-tokens/dist/tokens/SCSSVariables.scss";

/* stylelint-disable declaration-no-important, order/properties-order */

Expand Down
Loading

0 comments on commit 7729eec

Please sign in to comment.