diff --git a/.changeset/ten-teachers-appear.md b/.changeset/ten-teachers-appear.md new file mode 100644 index 0000000000..362cefa739 --- /dev/null +++ b/.changeset/ten-teachers-appear.md @@ -0,0 +1,5 @@ +--- +'@baloise/ds-core': minor +--- + +**option**: new child component of option-list. Option list component will be used by drop-down, combobox and autocomplete. diff --git a/.gitignore b/.gitignore index d9084d5e31..2b8586d5dd 100644 --- a/.gitignore +++ b/.gitignore @@ -68,8 +68,8 @@ libs/output-targets-vue/CHANGELOG.md e2e/generated e2e/downloads e2e/reg.json -e2e/cypress/snapshots -!e2e/cypress/snapshots/base +e2e/cypress/snapshots/actual +e2e/cypress/snapshots/diff resources/data !resources/data/tags.json diff --git a/docs/stories/components/bal-option/testing.md b/docs/stories/components/bal-option/testing.md new file mode 100644 index 0000000000..98d460be9e --- /dev/null +++ b/docs/stories/components/bal-option/testing.md @@ -0,0 +1,12 @@ +## Testing + +The Baloise Design System provides a collection of custom cypress commands for the components. Moreover, some basic cypress commands like `should` or `click` have been overridden to work with the components. + +Go to testing guide + + + + + + + diff --git a/docs/stories/components/bal-option/theming.md b/docs/stories/components/bal-option/theming.md new file mode 100644 index 0000000000..a3fa61a158 --- /dev/null +++ b/docs/stories/components/bal-option/theming.md @@ -0,0 +1,32 @@ +## Theming + +The component can be customization by changing the CSS variables. + +Go to theming guide + + + + + + + +### Variables​ + +| Variable | +| ------------------------------------------ | +| `--bal-option-padding-x` | +| `--bal-option-min-height` | +| `--bal-option-background` | +| `--bal-option-background-hovered` | +| `--bal-option-background-selected` | +| `--bal-option-background-selected-hovered` | +| `--bal-option-background-invalid` | +| `--bal-option-background-invalid-hovered` | +| `--bal-option-border-width` | +| `--bal-option-border-color` | +| `--bal-option-font-family` | +| `--bal-option-font-weight` | +| `--bal-option-line-height` | +| `--bal-option-text-hovered` | +| `--bal-option-text-pressed` | +| `--bal-option-text-disabled` | diff --git a/e2e/cypress/e2e/a11y/bal-option.a11y.cy.ts b/e2e/cypress/e2e/a11y/bal-option.a11y.cy.ts new file mode 100644 index 0000000000..7fbfd18a0b --- /dev/null +++ b/e2e/cypress/e2e/a11y/bal-option.a11y.cy.ts @@ -0,0 +1,13 @@ +describe('bal-option', () => { + context('a11y', () => { + beforeEach(() => cy.platform('desktop').pageA11y('/components/bal-option/test/bal-option.a11y.html')) + + describe('have the AA standard', () => { + it('basic', () => cy.getByTestId('basic').testA11y()) + it('selected', () => cy.getByTestId('selected').testA11y()) + it('focused', () => cy.getByTestId('focused').testA11y()) + it('invalid', () => cy.getByTestId('invalid').testA11y()) + it('disabled', () => cy.getByTestId('disabled').testA11y()) + }) + }) +}) diff --git a/e2e/cypress/e2e/visual/bal-option.visual.cy.ts b/e2e/cypress/e2e/visual/bal-option.visual.cy.ts new file mode 100644 index 0000000000..c606b784be --- /dev/null +++ b/e2e/cypress/e2e/visual/bal-option.visual.cy.ts @@ -0,0 +1,17 @@ +describe('bal-option', () => { + describe('basic', () => { + beforeEach(() => cy.visit('/components/bal-option/test/bal-option.visual.html').waitForDesignSystem()) + + it('basic component', () => { + cy.getByTestId('basic').testVisual('option-basic') + }) + + it('listbox', () => { + cy.getByTestId('listbox').testVisual('option-listbox') + }) + + it('listbox-with-checkboxes', () => { + cy.getByTestId('listbox-checkbox').testVisual('option-listbox-checkbox') + }) + }) +}) diff --git a/e2e/cypress/snapshots/base/visual/bal-option.visual.cy.ts/option-basic.png b/e2e/cypress/snapshots/base/visual/bal-option.visual.cy.ts/option-basic.png new file mode 100644 index 0000000000..8b08496f1a Binary files /dev/null and b/e2e/cypress/snapshots/base/visual/bal-option.visual.cy.ts/option-basic.png differ diff --git a/e2e/cypress/snapshots/base/visual/bal-option.visual.cy.ts/option-listbox-checkbox.png b/e2e/cypress/snapshots/base/visual/bal-option.visual.cy.ts/option-listbox-checkbox.png new file mode 100644 index 0000000000..0964e2627d Binary files /dev/null and b/e2e/cypress/snapshots/base/visual/bal-option.visual.cy.ts/option-listbox-checkbox.png differ diff --git a/e2e/cypress/snapshots/base/visual/bal-option.visual.cy.ts/option-listbox.png b/e2e/cypress/snapshots/base/visual/bal-option.visual.cy.ts/option-listbox.png new file mode 100644 index 0000000000..08e3003efd Binary files /dev/null and b/e2e/cypress/snapshots/base/visual/bal-option.visual.cy.ts/option-listbox.png differ diff --git a/e2e/cypress/support/lib/visuals/command.ts b/e2e/cypress/support/lib/visuals/command.ts index 30b61e6aec..56001854fa 100644 --- a/e2e/cypress/support/lib/visuals/command.ts +++ b/e2e/cypress/support/lib/visuals/command.ts @@ -35,6 +35,10 @@ export type CypressConfigEnv = { /** Add custom cypress command to compare image snapshots of an element or the window. */ function addCompareSnapshotCommand(screenshotOptions?: ScreenshotOptions): void { + console.log('') + console.log('visualRegressionType => ', Cypress.env('visualRegressionType')) + console.log('') + Cypress.Commands.add( 'testVisual', { prevSubject: 'optional' }, diff --git a/packages/core/src/components.d.ts b/packages/core/src/components.d.ts index 49461c5925..2f7c6d15e5 100644 --- a/packages/core/src/components.d.ts +++ b/packages/core/src/components.d.ts @@ -2080,6 +2080,48 @@ export namespace Components { */ "value"?: number; } + interface BalOption { + /** + * If `true`, the user cannot interact with the option. + */ + "checkbox": boolean; + /** + * If `true`, the user cannot interact with the option. + */ + "disabled": boolean; + /** + * If `true`, the user cannot interact with the option. + */ + "focused": boolean; + /** + * If `true`, the option is hidden. + */ + "hidden": boolean; + /** + * If `true`, the user cannot interact with the option. + */ + "invalid": boolean; + /** + * Label will be shown in the input element when it got selected + */ + "label": string; + /** + * If `true`, the user cannot interact with the option. + */ + "multiline": boolean; + /** + * Selects or deselects the option and informs other components + */ + "select": (selected?: boolean) => Promise; + /** + * If `true`, the user cannot interact with the option. + */ + "selected": boolean; + /** + * The value of the select option. This value will be returned by the parent `` element. + */ + "value": string; + } interface BalPagination { /** * Align the buttons to start, center or end @@ -3332,6 +3374,10 @@ export interface BalNumberInputCustomEvent extends CustomEvent { detail: T; target: HTMLBalNumberInputElement; } +export interface BalOptionCustomEvent extends CustomEvent { + detail: T; + target: HTMLBalOptionElement; +} export interface BalPaginationCustomEvent extends CustomEvent { detail: T; target: HTMLBalPaginationElement; @@ -4208,6 +4254,24 @@ declare global { prototype: HTMLBalNumberInputElement; new (): HTMLBalNumberInputElement; }; + interface HTMLBalOptionElementEventMap { + "balOptionChange": BalEvents.BalOptionChangeDetail; + "balOptionFocus": BalEvents.BalOptionFocusDetail; + } + interface HTMLBalOptionElement extends Components.BalOption, HTMLStencilElement { + addEventListener(type: K, listener: (this: HTMLBalOptionElement, ev: BalOptionCustomEvent) => any, options?: boolean | AddEventListenerOptions): void; + addEventListener(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; + addEventListener(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; + addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void; + removeEventListener(type: K, listener: (this: HTMLBalOptionElement, ev: BalOptionCustomEvent) => any, options?: boolean | EventListenerOptions): void; + removeEventListener(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | EventListenerOptions): void; + removeEventListener(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | EventListenerOptions): void; + removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void; + } + var HTMLBalOptionElement: { + prototype: HTMLBalOptionElement; + new (): HTMLBalOptionElement; + }; interface HTMLBalPaginationElementEventMap { "balChange": BalEvents.BalPaginationChangeDetail; } @@ -4704,6 +4768,7 @@ declare global { "bal-notices": HTMLBalNoticesElement; "bal-notification": HTMLBalNotificationElement; "bal-number-input": HTMLBalNumberInputElement; + "bal-option": HTMLBalOptionElement; "bal-pagination": HTMLBalPaginationElement; "bal-popover": HTMLBalPopoverElement; "bal-popover-content": HTMLBalPopoverContentElement; @@ -6902,6 +6967,49 @@ declare namespace LocalJSX { */ "value"?: number; } + interface BalOption { + /** + * If `true`, the user cannot interact with the option. + */ + "checkbox"?: boolean; + /** + * If `true`, the user cannot interact with the option. + */ + "disabled"?: boolean; + /** + * If `true`, the user cannot interact with the option. + */ + "focused"?: boolean; + /** + * If `true`, the option is hidden. + */ + "hidden"?: boolean; + /** + * If `true`, the user cannot interact with the option. + */ + "invalid"?: boolean; + /** + * Label will be shown in the input element when it got selected + */ + "label"?: string; + /** + * If `true`, the user cannot interact with the option. + */ + "multiline"?: boolean; + /** + * Emitted when the option gets selected or unselected + */ + "onBalOptionChange"?: (event: BalOptionCustomEvent) => void; + "onBalOptionFocus"?: (event: BalOptionCustomEvent) => void; + /** + * If `true`, the user cannot interact with the option. + */ + "selected"?: boolean; + /** + * The value of the select option. This value will be returned by the parent `` element. + */ + "value"?: string; + } interface BalPagination { /** * Align the buttons to start, center or end @@ -8156,6 +8264,7 @@ declare namespace LocalJSX { "bal-notices": BalNotices; "bal-notification": BalNotification; "bal-number-input": BalNumberInput; + "bal-option": BalOption; "bal-pagination": BalPagination; "bal-popover": BalPopover; "bal-popover-content": BalPopoverContent; @@ -8275,6 +8384,7 @@ declare module "@stencil/core" { "bal-notices": LocalJSX.BalNotices & JSXBase.HTMLAttributes; "bal-notification": LocalJSX.BalNotification & JSXBase.HTMLAttributes; "bal-number-input": LocalJSX.BalNumberInput & JSXBase.HTMLAttributes; + "bal-option": LocalJSX.BalOption & JSXBase.HTMLAttributes; "bal-pagination": LocalJSX.BalPagination & JSXBase.HTMLAttributes; "bal-popover": LocalJSX.BalPopover & JSXBase.HTMLAttributes; "bal-popover-content": LocalJSX.BalPopoverContent & JSXBase.HTMLAttributes; diff --git a/packages/core/src/components/bal-option/bal-option.interfaces.ts b/packages/core/src/components/bal-option/bal-option.interfaces.ts new file mode 100644 index 0000000000..b3f9dbcdd5 --- /dev/null +++ b/packages/core/src/components/bal-option/bal-option.interfaces.ts @@ -0,0 +1,28 @@ +/* eslint-disable no-unused-vars */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +// eslint-disable-next-line @typescript-eslint/triple-slash-reference +/// + +namespace BalProps {} + +namespace BalEvents { + export interface BalOptionCustomEvent extends CustomEvent { + detail: T + target: HTMLBalOptionElement + } + + export interface BalOption { + value: string + label: string + selected: boolean + } + + export type BalOptionFocusDetail = BalOption + export type BalOptionFocus = BalOptionCustomEvent + + export type BalOptionBlurDetail = FocusEvent + export type BalOptionBlur = BalOptionCustomEvent + + export type BalOptionChangeDetail = BalOption + export type BalOptionChange = BalOptionCustomEvent +} diff --git a/packages/core/src/components/bal-option/bal-option.sass b/packages/core/src/components/bal-option/bal-option.sass new file mode 100644 index 0000000000..5a56349627 --- /dev/null +++ b/packages/core/src/components/bal-option/bal-option.sass @@ -0,0 +1,111 @@ +@import '@baloise/ds-styles/sass/mixins' +@import './bal-option.vars' + +// Option +// -------------------------------------------------- +.bal-option + font-size: 16px + +unselectable + position: relative + display: block + list-style: none + cursor: pointer + border-bottom-width: var(--bal-option-border-width) + border-bottom-color: var(--bal-option-border-color) + border-bottom-style: solid + background: var(--bal-option-background) + padding-left: var(--bal-option-padding-x) + padding-right: var(--bal-option-padding-x) + min-height: var(--bal-option-min-height) + & > bal-stack + min-height: var(--bal-option-min-height) + &:last-child + border-bottom: none + &::-moz-focus-inner + border: none + &--hidden + display: none + visibility: hidden + +// Option Content +// -------------------------------------------------- + +.bal-option__inner + font-family: var(--bal-option-font-family) + font-weight: var(--bal-option-font-weight) + line-height: var(--bal-option-line-height) + word-break: break-word + white-space: normal + hyphens: auto + overflow: hidden + text-overflow: ellipsis + white-space: nowrap + flex: 1 + +.bal-option__inner--multiline + white-space: normal + +// Focused Option +// -------------------------------------------------- +.bal-option--focused + background: var(--bal-option-background-hovered) + +.bal-option + +hover + &:hover + background: var(--bal-option-background-hovered) + color: var(--bal-option-text-hovered) + &:active + background: var(--bal-option-background-hovered) + color: var(--bal-option-text-pressed) + +// Selected Option +// -------------------------------------------------- +.bal-option--selected + background: var(--bal-option-background-selected) + +hover + &:hover + background: var(--bal-option-background-selected-hovered) + &:active + background: var(--bal-option-background-selected-hovered) + +.bal-option--selected .bal-option__inner + font-weight: var(--bal-font-weight-bold) + +.bal-option--selected.bal-option--focused + background: var(--bal-option-background-selected-hovered) + .bal-option__inner + font-weight: var(--bal-font-weight-bold) + +// Invalid Option +// -------------------------------------------------- +.bal-option--invalid + background: var(--bal-option-background-invalid) + +hover + &:hover + background: var(--bal-option-background-invalid-hovered) + &:active + background: var(--bal-option-background-invalid-hovered) + +.bal-option--invalid.bal-option--focused + background: var(--bal-option-background-invalid-hovered) + +.bal-option--invalid .bal-option__inner + color: var(--bal-color-text-danger-hovered) + ++hover + .bal-option--invalid:hover .bal-option__inner + color: var(--bal-color-text-danger-hovered) + +.bal-option--invalid:active .bal-option__inner + color: var(--bal-color-text-danger-pressed) + +// Disbaled Option +// -------------------------------------------------- +.bal-option--disabled, +.bal-option--disabled.bal-option--focused + background: var(--bal-option-background) !important + color: var(--bal-option-text-disabled) !important + cursor: default !important + &:hover + background: var(--bal-option-background) !important diff --git a/packages/core/src/components/bal-option/bal-option.tsx b/packages/core/src/components/bal-option/bal-option.tsx new file mode 100644 index 0000000000..69326118fa --- /dev/null +++ b/packages/core/src/components/bal-option/bal-option.tsx @@ -0,0 +1,228 @@ +import { + Component, + h, + ComponentInterface, + Host, + Element, + Prop, + Listen, + Event, + EventEmitter, + Method, + State, +} from '@stencil/core' +import { BEM } from '../../utils/bem' +import { Loggable, Logger, LogInstance } from '../../utils/log' +import { ariaBooleanToString } from '../../utils/aria' +import { BalElementStateInfo, BalElementStateObserver, ListenToElementStates } from '../../utils/element-states' +import { stopEventBubbling } from '../../utils/form-input' +import { BalElementStateListener } from '../../utils/element-states/element-states.listener' + +@Component({ + tag: 'bal-option', + styleUrl: 'bal-option.sass', + shadow: false, +}) +export class Option implements ComponentInterface, Loggable, BalElementStateObserver { + private id = `bal-option-${balOptionIds++}` + + @Element() el!: HTMLElement + + log!: LogInstance + + @Logger('bal-option') + createLogger(log: LogInstance) { + this.log = log + } + + @State() interactionState: BalElementStateInfo = BalElementStateListener.DefaultState + + /** + * PUBLIC PROPERTY API + * ------------------------------------------------------ + */ + + /** + * Label will be shown in the input element when it got selected + */ + @Prop({ reflect: true }) label = '' + + /** + * The value of the select option. This value will be returned by the parent `` element. + */ + @Prop({ reflect: true }) value = '' + + /** + * If `true`, the user cannot interact with the option. + */ + @Prop({ reflect: true }) disabled = false + + /** + * If `true`, the user cannot interact with the option. + */ + @Prop({ reflect: true }) multiline = false + + /** + * If `true`, the user cannot interact with the option. + */ + @Prop({ reflect: true }) invalid = false + + /** + * If `true`, the user cannot interact with the option. + */ + @Prop({ reflect: true, mutable: true }) selected = false + + /** + * If `true`, the user cannot interact with the option. + */ + @Prop({ reflect: true, mutable: true }) focused = false + + /** + * If `true`, the user cannot interact with the option. + */ + @Prop({ reflect: true }) checkbox = false + + /** + * If `true`, the option is hidden. + */ + @Prop({ reflect: true }) hidden = false + + /** + * Emitted when the option gets selected or unselected + */ + @Event() balOptionChange!: EventEmitter + + /** + * @internal + * Emitted when a option gets focused. + */ + @Event() balOptionFocus!: EventEmitter + + /** + * LISTENERS + * ------------------------------------------------------ + */ + + @Listen('mouseenter') + listenToMouseEnter() { + const { label, value, selected, disabled, hidden } = this + if (!hidden && !disabled) { + this.balOptionFocus.emit({ label, value, selected }) + } + } + + @ListenToElementStates() + elementStateListener(info: BalElementStateInfo): void { + this.interactionChildElements.forEach(element => { + element.hovered = info.hovered + element.pressed = info.pressed + }) + } + + /** + * PUBLIC METHODS + * ------------------------------------------------------ + */ + + /** + * Selects or deselects the option and informs other components + */ + @Method() async select(selected = true): Promise { + this.selected = selected + const { label, value } = this + this.balOptionChange.emit({ label, value, selected }) + } + + /** + * PRIVATE METHODS + * ------------------------------------------------------ + */ + + private get interactionChildElements(): Array { + return Array.from(this.el.querySelectorAll('bal-checkbox')) + } + + /** + * EVENT BINDING + * ------------------------------------------------------ + */ + + private onClick = (ev: MouseEvent) => { + if (this.disabled) { + stopEventBubbling(ev) + } else { + this.select(!this.selected) + } + } + + /** + * RENDER + * ------------------------------------------------------ + */ + + render() { + const block = BEM.block('option') + + return ( + + + {this.checkbox ? ( + + ) : ( + '' + )} +
+ +
+ {this.selected && !this.checkbox ? ( + + ) : ( + '' + )} +
+
+ ) + } +} + +let balOptionIds = 0 diff --git a/packages/core/src/components/bal-option/bal-option.vars.sass b/packages/core/src/components/bal-option/bal-option.vars.sass new file mode 100644 index 0000000000..d8619359f8 --- /dev/null +++ b/packages/core/src/components/bal-option/bal-option.vars.sass @@ -0,0 +1,42 @@ +/** + * @prop --bal-option-padding-x: TBD + * @prop --bal-option-min-height: TBD + * @prop --bal-option-background: TBD + * @prop --bal-option-background-hovered: TBD + * @prop --bal-option-background-selected: TBD + * @prop --bal-option-background-selected-hovered: TBD + * @prop --bal-option-background-invalid: TBD + * @prop --bal-option-background-invalid-hovered: TBD + * @prop --bal-option-border-width: TBD + * @prop --bal-option-border-color: TBD + * @prop --bal-option-font-family: TBD + * @prop --bal-option-font-weight: TBD + * @prop --bal-option-line-height: TBD + * @prop --bal-option-text-hovered: TBD + * @prop --bal-option-text-pressed: TBD + * @prop --bal-option-text-disabled: TBD + */ + +:root + --bal-option-padding-x: var(--bal-space-x-small) + --bal-option-min-height: 1.5rem + // + // background colors + --bal-option-background: var(--bal-color-white) + --bal-option-background-hovered: var(--bal-color-grey-2) + --bal-option-background-selected: var(--bal-color-primary-1) + --bal-option-background-selected-hovered: #dddfeb + --bal-option-background-invalid: var(--bal-color-danger-1) + --bal-option-background-invalid-hovered: #fbe0de + // + // border + --bal-option-border-width: var(--bal-border-width-normal) + --bal-option-border-color: var(--bal-color-grey-2) + // + // typography + --bal-option-font-family: var(--bal-font-family-text) + --bal-option-font-weight: var(--bal-font-weight-regular) + --bal-option-line-height: 1.125rem + --bal-option-text-hovered: var(--bal-color-text-primary-hovered) + --bal-option-text-pressed: var(--bal-color-text-primary-pressed) + --bal-option-text-disabled: var(--bal-color-text-grey-light) diff --git a/packages/core/src/components/bal-option/test/bal-option.a11y.html b/packages/core/src/components/bal-option/test/bal-option.a11y.html new file mode 100644 index 0000000000..1c466b50de --- /dev/null +++ b/packages/core/src/components/bal-option/test/bal-option.a11y.html @@ -0,0 +1,28 @@ + + + + + + + + + + + + + +
+
+ + Black Widow + S.H.I.E.L.D. + + Focused + Selected + Invalid + Disabled +
+
+
+ + diff --git a/packages/core/src/components/bal-option/test/bal-option.visual.html b/packages/core/src/components/bal-option/test/bal-option.visual.html new file mode 100644 index 0000000000..91a11ce950 --- /dev/null +++ b/packages/core/src/components/bal-option/test/bal-option.visual.html @@ -0,0 +1,55 @@ + + + + + + + + + + + + + +
+
+ + Black Widow + S.H.I.E.L.D. + + + Black Panter + Wakanda + + Lorem ipsum dolor, sit amet consectetur adipisicing elit. Ab perferendis porro est non esse quibusdam, + facere vel minus quam unde nisi vitae provident maxime maiores inventore amet culpa illum nam. + Lorem ipsum dolor, sit amet consectetur adipisicing elit. Ab perferendis porro est non esse quibusdam, + facere vel minus quam unde nisi vitae provident maxime maiores inventore amet culpa illum nam. +
+
+ Default + Focused + Selected + Disabled + Invalid +
+ +
+ Default + Focused + Selected + Disabled + Invalid +
+
+
+ + diff --git a/packages/core/src/interfaces.d.ts b/packages/core/src/interfaces.d.ts index 8b221bbaf6..c77accafa4 100644 --- a/packages/core/src/interfaces.d.ts +++ b/packages/core/src/interfaces.d.ts @@ -24,6 +24,7 @@ import './components/bal-nav/bal-nav-menu-bar/bal-nav-menu-bar.interfaces' import './components/bal-nav/bal-nav-menu-flyout/bal-nav-menu-flyout.interfaces' import './components/bal-nav/bal-nav.interfaces' import './components/bal-navbar/bal-navbar.interfaces' +import './components/bal-option/bal-option.interfaces' import './components/bal-pagination/bal-pagination.interfaces' import './components/bal-popover/bal-popover.interfaces' import './components/bal-popup/bal-popup.interfaces' diff --git a/packages/core/src/utils/aria.ts b/packages/core/src/utils/aria.ts new file mode 100644 index 0000000000..961c2e6c75 --- /dev/null +++ b/packages/core/src/utils/aria.ts @@ -0,0 +1 @@ +export const ariaBooleanToString = (bool?: boolean) => (!!bool ? 'true' : 'false') diff --git a/resources/data/tags.json b/resources/data/tags.json index d2c4d86c8e..9d5be2d65f 100644 --- a/resources/data/tags.json +++ b/resources/data/tags.json @@ -26,6 +26,7 @@ "bal-nav", "bal-notification", "bal-number-input", + "bal-option", "bal-pagination", "bal-popover", "bal-popup",