Skip to content

Commit

Permalink
Merge branch 'main' into next [skip ci]
Browse files Browse the repository at this point in the history
  • Loading branch information
DRiFTy17 committed Nov 9, 2023
2 parents 6a7c86e + 66e4fb0 commit d81fd31
Show file tree
Hide file tree
Showing 25 changed files with 429 additions and 61 deletions.
31 changes: 31 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,34 @@
# v2.20.1 (Thu Nov 09 2023)

#### 🐛 Bug Fix

- fix: updated immutability patterns [#425](https://github.com/tyler-technologies-oss/forge/pull/425) ([@DRiFTy17](https://github.com/DRiFTy17))

#### Authors: 1

- Kieran Nichols ([@DRiFTy17](https://github.com/DRiFTy17))

---

# v2.20.0 (Fri Oct 27 2023)

#### 🚀 Enhancement

- feat(chip field): add member on blur [#408](https://github.com/tyler-technologies-oss/forge/pull/408) ([@nickonometry](https://github.com/nickonometry) [@DRiFTy17](https://github.com/DRiFTy17))

#### 🐛 Bug Fix

- fix(paginator): fix focus management to properly account for state updates while the element is already focused [#415](https://github.com/tyler-technologies-oss/forge/pull/415) ([@DRiFTy17](https://github.com/DRiFTy17))
- fix(table): sortable column headers will now use `<button>` element for a11y purposes [#416](https://github.com/tyler-technologies-oss/forge/pull/416) ([@DRiFTy17](https://github.com/DRiFTy17))
- fix(select): fixed a bug where options that contain leading whitespace could not be selected via keyboard filtering [#414](https://github.com/tyler-technologies-oss/forge/pull/414) ([@DRiFTy17](https://github.com/DRiFTy17))

#### Authors: 2

- Kieran Nichols ([@DRiFTy17](https://github.com/DRiFTy17))
- Nick Andrews ([@nickonometry](https://github.com/nickonometry))

---

# v2.19.1 (Thu Oct 19 2023)

#### 🐛 Bug Fix
Expand Down
1 change: 1 addition & 0 deletions src/dev/pages/chip-field/chip-field.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
{ type: 'switch', label: 'Invalid', id: 'opt-invalid' },
{ type: 'switch', label: 'Disabled', id: 'opt-disabled' },
{ type: 'switch', label: 'Dense', id: 'opt-dense' },
{ type: 'switch', label: 'Add on blur', id: 'opt-add-on-blur' },
{ type: 'button', label: 'Populate chips', id: 'opt-btn-populate' },
{ type: 'button', label: 'Clear chips', id: 'opt-btn-clear' },
]
Expand Down
6 changes: 6 additions & 0 deletions src/dev/pages/chip-field/chip-field.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@ const requiredToggle = document.querySelector('#opt-required') as ISwitchCompone
const invalidToggle = document.querySelector('#opt-invalid') as ISwitchComponent;
const disabledToggle = document.querySelector('#opt-disabled') as ISwitchComponent;
const denseToggle = document.querySelector('#opt-dense') as ISwitchComponent;
const onBlurToggle = document.querySelector('#opt-add-on-blur') as ISwitchComponent;
const populateButton = document.querySelector('#opt-btn-populate') as HTMLButtonElement;
const clearButton = document.querySelector('#opt-btn-clear') as HTMLButtonElement;

requiredToggle.addEventListener('forge-switch-change', updateRequiredState);
invalidToggle.addEventListener('forge-switch-change', updateInvalidState);
disabledToggle.addEventListener('forge-switch-change', updateDisabledState);
denseToggle.addEventListener('forge-switch-change', updateDenseState);
onBlurToggle.addEventListener('forge-switch-change', updateOnBlurProperty);
populateButton.addEventListener('click', () => populateMembers(45));
clearButton.addEventListener('click', removeAllMembers);

Expand Down Expand Up @@ -180,6 +182,10 @@ function updateDenseState({ detail: isDense }: CustomEvent<boolean>): void {
chips.forEach(({ dense }) => dense = isDense);
}

function updateOnBlurProperty({ detail: addOnBlur }: CustomEvent<boolean>): void {
simpleChipField.addOnBlur = addOnBlur;
}

function setChipsDisabledState(isDisabled: boolean): void {
const chips = autocompleteChipField.querySelectorAll('forge-chip');
chips.forEach(({ disabled }) => disabled = isDisabled);
Expand Down
4 changes: 2 additions & 2 deletions src/lib/autocomplete/autocomplete-foundation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -803,11 +803,11 @@ export class AutocompleteFoundation extends ListDropdownAwareFoundation implemen
if (value == null) {
values = [];
} else if (Array.isArray(value)) {
values = JSON.parse(JSON.stringify(value));
values = structuredClone(value);
} else if (isString(value)) {
values = [value];
} else {
values = [JSON.parse(JSON.stringify(value))];
values = [structuredClone(value)];
}

const hasNewValue = values.length !== this._values.length || values.some(v => !this._values.includes(v));
Expand Down
5 changes: 5 additions & 0 deletions src/lib/chip-field/chip-field-constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,12 @@ const events = {
MEMBER_REMOVED: `${elementName}-member-removed`
};

const attributes = {
ADD_ON_BLUR: 'add-on-blur'
};

export const CHIP_FIELD_CONSTANTS = {
attributes,
elementName,
classes,
slots,
Expand Down
29 changes: 26 additions & 3 deletions src/lib/chip-field/chip-field-foundation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@ import { IChipFieldAdapter } from './chip-field-adapter';
import { CHIP_FIELD_CONSTANTS } from './chip-field-constants';
import { IFieldFoundation, FieldFoundation } from '../field/field-foundation';

export interface IChipFieldFoundation extends IFieldFoundation {}
export interface IChipFieldFoundation extends IFieldFoundation {
addOnBlur: boolean;
}

export class ChipFieldFoundation extends FieldFoundation implements IChipFieldFoundation {
private _addOnBlur = false;
private _memberSlotListener: () => void;
private _inputContainerMouseDownListener: (evt: MouseEvent) => void;
private _handleRootKeyDown: (event: KeyboardEvent) => void;
Expand Down Expand Up @@ -35,6 +38,18 @@ export class ChipFieldFoundation extends FieldFoundation implements IChipFieldFo
this._adapter.removeInputListener('keydown', this._handleKeyDown);
}

/** Controls adding a member of entered text on blur. */
public get addOnBlur(): boolean {
return this._addOnBlur;
}
public set addOnBlur(value: boolean) {
value = Boolean(value);
if (this._addOnBlur !== value) {
this._addOnBlur = value;
this._adapter.toggleHostAttribute(CHIP_FIELD_CONSTANTS.attributes.ADD_ON_BLUR, this._addOnBlur);
}
}

private _onInputContainerMouseDown(evt: MouseEvent): void {
evt.preventDefault();
this._adapter.focusInput();
Expand All @@ -43,6 +58,11 @@ export class ChipFieldFoundation extends FieldFoundation implements IChipFieldFo

protected _onBlur(event: FocusEvent): void {
const input = event.target as HTMLInputElement;

if (this._addOnBlur) {
this._addMember(input);
}

input.value = '';
super._onBlur(event);
}
Expand Down Expand Up @@ -79,9 +99,13 @@ export class ChipFieldFoundation extends FieldFoundation implements IChipFieldFo
break;
case 'Esc':
case 'Escape':
case 'Tab':
input.value = '';
break;
case 'Tab':
if (!this._addOnBlur) {
input.value = '';
}
break;
default:
break;
}
Expand Down Expand Up @@ -152,7 +176,6 @@ export class ChipFieldFoundation extends FieldFoundation implements IChipFieldFo
if (cleanInputValue && cleanInputValue.length > 0) {
this._adapter.emitHostEvent(CHIP_FIELD_CONSTANTS.events.MEMBER_ADDED, cleanInputValue);
}

input.value = '';
}

Expand Down
27 changes: 25 additions & 2 deletions src/lib/chip-field/chip-field.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CustomElement, attachShadowTemplate } from '@tylertech/forge-core';
import { CustomElement, FoundationProperty, attachShadowTemplate, coerceBoolean } from '@tylertech/forge-core';
import { ChipFieldAdapter } from './chip-field-adapter';
import { ChipFieldFoundation } from './chip-field-foundation';
import { CHIP_FIELD_CONSTANTS } from './chip-field-constants';
Expand All @@ -7,8 +7,11 @@ import { FieldComponent, IFieldComponent } from '../field/field';

import template from './chip-field.html';
import styles from './chip-field.scss';
import { FIELD_CONSTANTS } from '../field/field-constants';

export interface IChipFieldComponent extends IFieldComponent { }
export interface IChipFieldComponent extends IFieldComponent {
addOnBlur: boolean;
}

declare global {
interface HTMLElementTagNameMap {
Expand All @@ -31,9 +34,29 @@ declare global {
dependencies: [ChipComponent]
})
export class ChipFieldComponent extends FieldComponent<ChipFieldFoundation> implements IChipFieldComponent {
public static get observedAttributes(): string[] {
return [
...Object.values(FIELD_CONSTANTS.attributes),
CHIP_FIELD_CONSTANTS.attributes.ADD_ON_BLUR
];
}

constructor() {
super();
attachShadowTemplate(this, template, styles);
this._foundation = new ChipFieldFoundation(new ChipFieldAdapter(this));
}

public attributeChangedCallback(name: string, oldValue: string, newValue: string): void {
switch (name) {
case CHIP_FIELD_CONSTANTS.attributes.ADD_ON_BLUR:
this.addOnBlur = coerceBoolean(newValue);
return;
}
super.attributeChangedCallback(name, oldValue, newValue);
}

/** Controls whether or not the value should be set onBlur */
@FoundationProperty()
public declare addOnBlur: boolean;
}
23 changes: 14 additions & 9 deletions src/lib/field/field-constants.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,3 @@
const attributes = {
DENSITY: 'density',
FLOAT_LABEL_TYPE: 'float-label-type',
SHAPE: 'shape',
INVALID: 'invalid',
REQUIRED: 'required',
HOST_LABEL_FLOATING: `forge-label-floating`
};

const selectors = {
INPUT: 'input,textarea'
};
Expand All @@ -31,8 +22,22 @@ const classes = {
LABEL: 'forge-field--label'
};

const observedAttributes = {
DENSITY: 'density',
FLOAT_LABEL_TYPE: 'float-label-type',
SHAPE: 'shape',
INVALID: 'invalid',
REQUIRED: 'required',
HOST_LABEL_FLOATING: `forge-label-floating`
};

const attributes = {
...observedAttributes
};

export const FIELD_CONSTANTS = {
attributes,
observedAttributes,
observedInputAttributes,
selectors,
classes
Expand Down
4 changes: 0 additions & 4 deletions src/lib/menu/menu-foundation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -501,8 +501,6 @@ export class MenuFoundation extends CascadingListDropdownAwareFoundation<IMenuOp
} else {
this.optionsFactory = undefined;
// Intentional shallow copy of member properties. These member objects have properties that are references to functions.
// The typical JSON.parse(JSON.stringify(object)) will not work here. If this becomes an issue we'll add a deepClone
// function to the core library.
this._options = options.map(o => ({ ...o }));

if (this._open) {
Expand All @@ -523,8 +521,6 @@ export class MenuFoundation extends CascadingListDropdownAwareFoundation<IMenuOp
}

// Intentional shallow copy of member properties. These member objects have properties that are references to functions.
// The typical JSON.parse(JSON.stringify(object)) will not work here. If this becomes an issue we'll add a deepClone
// function to the core library.
return this._flatOptions.map(o => ({ ...o }));
}

Expand Down
33 changes: 24 additions & 9 deletions src/lib/paginator/paginator-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ export interface IPaginatorAdapter extends IBaseAdapter {
enableLastPageButton(): void;
setAlternative(alternative: boolean): void;
setAlignment(alignment: PaginatorAlternativeAlignment): void;
handleFocusMove(from: PaginatorFieldIdentifier): void;
hasFocus(): boolean;
handleFocusMove(from?: PaginatorFieldIdentifier | null, options?: FocusOptions): void;
}

/**
Expand Down Expand Up @@ -260,8 +261,12 @@ export class PaginatorAdapter extends BaseAdapter<IPaginatorComponent> implement
}
}

public handleFocusMove(from: PaginatorFieldIdentifier): void {
if (!this._component.matches(':focus')) {
public hasFocus(): boolean {
return this._component.matches(':focus');
}

public handleFocusMove(from?: PaginatorFieldIdentifier, options?: FocusOptions): void {
if (from && !this.hasFocus()) {
return; // We can only move focus elsewhere within the element if the element already contains focus
}

Expand All @@ -272,15 +277,15 @@ export class PaginatorAdapter extends BaseAdapter<IPaginatorComponent> implement
this._lastPageButton,
this._previousPageButton,
this._pageSizeSelect
]);
], options);
break;
case 'last':
this._tryFocus([
this._previousPageButton,
this._firstPageButton,
this._nextPageButton,
this._pageSizeSelect
]);
], options);
break;
case 'previous':
this._tryFocus([
Expand All @@ -296,23 +301,33 @@ export class PaginatorAdapter extends BaseAdapter<IPaginatorComponent> implement
this._firstPageButton,
this._lastPageButton,
this._pageSizeSelect
]);
], options);
break;
case 'page-size':
this._tryFocus([
this._nextPageButton,
this._lastPageButton,
this._firstPageButton,
this._previousPageButton
]);
], options);
break;
default:
this._tryFocus([
this._firstPageButton,
this._previousPageButton,
this._nextPageButton,
this._lastPageButton,
this._pageSizeSelect
], options);
break;
}
}

private _tryFocus(elements: Array<HTMLButtonElement | ISelectComponent>): void {
private _tryFocus(elements: Array<HTMLButtonElement | ISelectComponent>, options?: FocusOptions): void {
const preventScroll = typeof options?.preventScroll === 'boolean' ? options.preventScroll : true;
for (const el of elements) {
if (el && el.isConnected && !el.disabled) {
el.focus();
el.focus({ ...options, preventScroll });
return;
}
}
Expand Down
Loading

0 comments on commit d81fd31

Please sign in to comment.