Skip to content

Commit

Permalink
✨ feat(date): Possibility to return invalid date (#1384)
Browse files Browse the repository at this point in the history
* Create PR for #1370

* add changeset

* feat(date): add prop allow-invalid-dates

* chore(): rename prop to generic so it can be used for other masks

* chore: export const INVALID_VALUE

* chore: fix formatting

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Gery Hirschfeld <[email protected]>
Co-authored-by: Yannick Holzenkamp <[email protected]>
  • Loading branch information
3 people authored May 16, 2024
1 parent d865fe3 commit 6619ba9
Show file tree
Hide file tree
Showing 14 changed files with 99 additions and 21 deletions.
5 changes: 5 additions & 0 deletions .changeset/metal-pants-retire.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@baloise/ds-core': minor
---

**date**: the new property `allow-invalid-value` includes a functionality where it returns the string `INVALID_VALUE` within the balChange event if the input provided is not valid.
1 change: 0 additions & 1 deletion docs/public/assets/css/baloise-design-system.min.css

This file was deleted.

21 changes: 21 additions & 0 deletions e2e/cypress/component/bal-date.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,4 +104,25 @@ describe('bal-date', () => {
cy.get('@balChange').should('have.been.calledOnce')
cy.get('@balChange').shouldHaveEventDetail('2022-12-11')
})

it('check if invalid date is returned', () => {
onBalChangeSpy = cy.spy().as('balChange')
cy.mount<Components.BalDate, HTMLBalDateElementEventMap>(`<bal-date allow-invalid-value></bal-date>`, {
props: {
defaultDate: '2023-01-01',
placeholder: 'Enter date',
},
events: {
balChange: onBalChangeSpy,
},
}).as('calendar')

cy.getByPlaceholder('Enter date')
.click()
.type('2.42.2023', { delay: 20 })
.blur({ force: true })
.should('have.value', '02.42.2023')
cy.get('@balChange').should('have.been.calledOnce')
cy.get('@balChange').shouldHaveEventDetail('INVALID_VALUE')
})
})
16 changes: 16 additions & 0 deletions packages/core/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -692,6 +692,10 @@ export namespace Components {
"multiline": boolean;
}
interface BalDate {
/**
* If `true`, it returns the string `INVALID_VALUE` within the balChange event if the input provided is not valid.
*/
"allowInvalidValue": boolean;
/**
* Callback to determine which date in the datepicker should be selectable.
*/
Expand Down Expand Up @@ -1535,6 +1539,10 @@ export namespace Components {
"value"?: string;
}
interface BalInputDate {
/**
* If `true`, it returns the string `INVALID_VALUE` within the balChange event if the input provided is not valid.
*/
"allowInvalidValue": boolean;
/**
* If `true`, in Angular reactive forms the control will not be set invalid
*/
Expand Down Expand Up @@ -5736,6 +5744,10 @@ declare namespace LocalJSX {
"onBalFocus"?: (event: BalDataValueCustomEvent<BalEvents.BalDataValueFocusDetail>) => void;
}
interface BalDate {
/**
* If `true`, it returns the string `INVALID_VALUE` within the balChange event if the input provided is not valid.
*/
"allowInvalidValue"?: boolean;
/**
* Callback to determine which date in the datepicker should be selectable.
*/
Expand Down Expand Up @@ -6575,6 +6587,10 @@ declare namespace LocalJSX {
"value"?: string;
}
interface BalInputDate {
/**
* If `true`, it returns the string `INVALID_VALUE` within the balChange event if the input provided is not valid.
*/
"allowInvalidValue"?: boolean;
/**
* If `true`, in Angular reactive forms the control will not be set invalid
*/
Expand Down
8 changes: 7 additions & 1 deletion packages/core/src/components/bal-date/bal-date.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,12 @@ export class Date implements ComponentInterface, Loggable, BalAriaFormLinking {
/**
* Callback to determine which date in the datepicker should be selectable.
*/
@Prop({ attribute: 'allowed-dates' }) allowedDates: BalProps.BalDateCallback | undefined = undefined
@Prop() allowedDates: BalProps.BalDateCallback | undefined = undefined

/**
* If `true`, it returns the string `INVALID_VALUE` within the balChange event if the input provided is not valid.
*/
@Prop() allowInvalidValue = false

/**
* Set the amount of time, in milliseconds, to wait to trigger the `ionChange` event after each keystroke. This also impacts form bindings such as `ngModel` or `v-model`.
Expand Down Expand Up @@ -492,6 +497,7 @@ export class Date implements ComponentInterface, Loggable, BalAriaFormLinking {
invalid={this.invalid}
readonly={this.readonly}
disabled={this.disabled}
allowInvalidValue={this.allowInvalidValue}
onClick={this.onInputClick}
onBalInput={this.onInputInput}
onBalChange={this.onInputChange}
Expand Down
37 changes: 21 additions & 16 deletions packages/core/src/components/bal-date/test/bal-date.cy.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,28 @@
</head>

<body>
<bal-doc-app>
<bal-doc-app log-components="bal-date">
<main class="container">
<form action="https://www.w3schools.com/action_page.php" target="_blank">
<bal-form-grid>
<bal-form-col>
<bal-field required>
<bal-field-control>
<bal-date data-testid="reset" value="2022-09-16"></bal-date>
</bal-field-control>
</bal-field>
</bal-form-col>
</bal-form-grid>
<bal-button-group>
<bal-button element-type="submit" color="primary">Submit</bal-button>
<bal-button data-testid="button-reset" element-type="reset" color="link">Reset</bal-button>
</bal-button-group>
</form>
<section>
<form action="https://www.w3schools.com/action_page.php" target="_blank">
<bal-form-grid>
<bal-form-col>
<bal-field required>
<bal-field-control>
<bal-date data-testid="reset" value="2022-09-16"></bal-date>
</bal-field-control>
</bal-field>
</bal-form-col>
</bal-form-grid>
<bal-button-group>
<bal-button element-type="submit" color="primary">Submit</bal-button>
<bal-button data-testid="button-reset" element-type="reset" color="link">Reset</bal-button>
</bal-button-group>
</form>
</section>
<section class="mt-large">
<bal-date data-testid="allow-invalid-value" value="2022-12-16" allow-invalid-value></bal-date>
</section>
</main>
</bal-doc-app>
</body>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,11 @@ export class InputDate implements ComponentInterface, Loggable, BalConfigObserve
*/
@Prop({ reflect: true }) autoInvalidOff = false

/**
* If `true`, it returns the string `INVALID_VALUE` within the balChange event if the input provided is not valid.
*/
@Prop() allowInvalidValue = false

/**
* Emitted when a keyboard key has pressed.
*/
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ export {
} from './utils/orientation'
export { BalSwipeObserver, BalSwipeInfo, BalSwipeSubject } from './utils/swipe'
export { BalDate } from './utils/date'
export { INVALID_VALUE } from './utils/mask/mask-util'

/**
* Controllers
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { MaskComponent } from '../component'

export class MaskComponentMock implements MaskComponent {
value: string | undefined = undefined
allowInvalidValue = false
inputValue: string | undefined = undefined
initialValue = ''
focused = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export interface MaskComponentAdapterType {

export interface MaskComponent {
value: string | undefined
allowInvalidValue: boolean
inputValue: string | undefined
initialValue: string
focused: boolean
Expand Down
3 changes: 3 additions & 0 deletions packages/core/src/utils/mask/context/mask-context.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import { MaskComponent } from '../component'
import { MaskContextEvent, MaskContextOptions } from './mask-context-interfaces'
import { MaskPosition } from './mask-position'

export abstract class MaskContext<T = MaskContextEvent> {
private _value = ''
public position!: MaskPosition
public component!: MaskComponent

constructor(protected _options: MaskContextOptions<T>) {
if (this._options.component && this._options.component.nativeInput) {
this._value = this._options.component.nativeInput.value
}
this.position = new MaskPosition(this._options)
this.component = this._options.component
}

get target(): HTMLInputElement | undefined {
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/utils/mask/mask-util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const INVALID_VALUE = 'INVALID_VALUE'
7 changes: 5 additions & 2 deletions packages/core/src/utils/mask/mask.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ export abstract class AbstractMask implements Mask {
context.position.toEnd()
}

public onParseValue(inputValue?: string) {
public onParseValue(inputValue?: string, options: { allowInvalidValue: boolean } = { allowInvalidValue: false }) {
if (inputValue) {
return inputValue.trim()
}
Expand Down Expand Up @@ -189,7 +189,10 @@ export abstract class AbstractMask implements Mask {
this.emptyInputValue(context)
} else {
this.onBlur(context)
context.submit('blur', this.onParseValue(context.value))
context.submit(
'blur',
this.onParseValue(context.value, { allowInvalidValue: context.component.allowInvalidValue }),
)
}
}
}
Expand Down
13 changes: 12 additions & 1 deletion packages/core/src/utils/mask/types/mask-date.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { AbstractMask } from '../mask'
import { BalDate } from '../../date'
import { MaskClipboardContext, MaskFocusContext } from '../context'
import { I18n, I18nKeys } from '../../../interfaces'
import { INVALID_VALUE } from '../mask-util'

export class DateMask extends AbstractMask {
public maxLength = 10
Expand Down Expand Up @@ -89,11 +90,16 @@ export class DateMask extends AbstractMask {
])
}

override onParseValue(inputValue?: string): string {
override onParseValue(
inputValue?: string,
options: { allowInvalidValue: boolean } = { allowInvalidValue: false },
): string {
if (inputValue) {
const date = BalDate.fromAnyFormat(this.blocks.getRawValueWithoutMask(inputValue))
if (date.isValid) {
return date.toISODate()
} else if (options && options.allowInvalidValue) {
return INVALID_VALUE
}
}
return ''
Expand Down Expand Up @@ -122,11 +128,16 @@ export class DateMask extends AbstractMask {
override onBlur(context: MaskFocusContext) {
const rawValue = this.blocks.getRawValueWithoutMaskByContext(context)
const date = BalDate.fromAnyFormat(rawValue)

if (date.isValid) {
const formattedDate = date.toFormat()
if (formattedDate !== context.value) {
context.value = formattedDate
}
} else if (context.component.allowInvalidValue) {
if (rawValue !== context.value) {
context.value = rawValue
}
}
}
}

0 comments on commit 6619ba9

Please sign in to comment.