diff --git a/addon/-private/state-machine/-base.ts b/addon/-private/state-machine/-base.ts index c90c5138..9952c4a0 100644 --- a/addon/-private/state-machine/-base.ts +++ b/addon/-private/state-machine/-base.ts @@ -1,9 +1,9 @@ import EmberObject, { set } from '@ember/object'; import MutableArray from '@ember/array/mutable'; -import { isBlank, isPresent } from '@ember/utils'; import { A } from '@ember/array'; import { readOnly } from '@ember-decorators/object/computed'; import { assert } from '@ember/debug'; +import { isNone } from '@ember/utils'; import { StepName } from '../types'; import StepNode from '../step-node'; @@ -28,7 +28,7 @@ export default abstract class BaseStateMachine extends EmberObject { constructor(initialStepName?: StepName) { super(); - if (isPresent(initialStepName)) { + if (!isNone(initialStepName)) { set(this, 'currentStep', initialStepName!); } } @@ -37,7 +37,7 @@ export default abstract class BaseStateMachine extends EmberObject { const node = new StepNode(this, name, context); this.stepTransitions.pushObject(node); - if (isBlank(this.currentStep)) { + if (typeof this.currentStep === 'undefined') { set(this, 'currentStep', name); } } @@ -67,7 +67,7 @@ export default abstract class BaseStateMachine extends EmberObject { activate(step: StepNode | StepName) { const name = step instanceof StepNode ? step.name : step; - assert('No step name was provided', isPresent(step)); + assert('No step name was provided', !isNone(step)); assert( `"${name}" does not match an existing step`, this.stepTransitions.map(node => node.name).includes(name) diff --git a/addon/components/step-manager.ts b/addon/components/step-manager.ts index 21fd4fcf..3b2eb609 100644 --- a/addon/components/step-manager.ts +++ b/addon/components/step-manager.ts @@ -1,8 +1,8 @@ import Component from '@ember/component'; // @ts-ignore: Ignore import of compiled template import layout from '../templates/components/step-manager'; -import { get, set } from '@ember/object'; -import { isEmpty, isPresent } from '@ember/utils'; +import { get, getProperties, set } from '@ember/object'; +import { isPresent, isNone } from '@ember/utils'; import { schedule } from '@ember/runloop'; import { assert } from '@ember/debug'; import { action, computed } from '@ember-decorators/object'; @@ -77,7 +77,13 @@ export default class StepManagerComponent extends Component { constructor() { super(...arguments); - const initialStep = get(this, 'initialStep') || get(this, 'currentStep'); + const { initialStep, currentStep } = getProperties( + this, + 'initialStep', + 'currentStep' + ); + + const startingStep = isNone(initialStep) ? currentStep : initialStep; if (!isPresent(this.linear)) { this.linear = true; @@ -87,17 +93,17 @@ export default class StepManagerComponent extends Component { ? LinearStateMachine : CircularStateMachine; - set(this, 'transitions', new StateMachine(initialStep)); + set(this, 'transitions', new StateMachine(startingStep)); } @computed('transitions.{currentStep,length}') get hasNextStep() { - return isPresent(this.transitions.pickNext()); + return !isNone(this.transitions.pickNext()); } @computed('transitions.{currentStep,length}') get hasPreviousStep() { - return isPresent(this.transitions.pickPrevious()); + return !isNone(this.transitions.pickPrevious()); } didUpdateAttrs() { @@ -146,7 +152,7 @@ export default class StepManagerComponent extends Component { // If `currentStep` is present, it's probably something the user wants // two-way-bound with the new value - if (!isEmpty(this.currentStep)) { + if (!isNone(this.currentStep)) { set(this, 'currentStep', destination); } @@ -157,7 +163,7 @@ export default class StepManagerComponent extends Component { transitionToNext() { const to = this.transitions.pickNext(); - assert('There is no next step', !!to); + assert('There is no next step', !isNone(to)); this.transitionTo(to!); } @@ -166,7 +172,7 @@ export default class StepManagerComponent extends Component { transitionToPrevious() { const to = this.transitions.pickPrevious(); - assert('There is no previous step', !!to); + assert('There is no previous step', !isNone(to)); this.transitionTo(to!); } diff --git a/addon/components/step-manager/step.ts b/addon/components/step-manager/step.ts index 63cd333f..007202a9 100644 --- a/addon/components/step-manager/step.ts +++ b/addon/components/step-manager/step.ts @@ -2,7 +2,7 @@ import Component from '@ember/component'; // @ts-ignore: Ignore import of compiled template import layout from '../../templates/components/step-manager/step'; import { get, set } from '@ember/object'; -import { isEmpty, isPresent } from '@ember/utils'; +import { isPresent } from '@ember/utils'; import { assert } from '@ember/debug'; import { computed } from '@ember-decorators/object'; import { tagName } from '@ember-decorators/component'; @@ -33,9 +33,10 @@ export default class StepComponent extends Component { super(...arguments); const nameAttribute = get(this, 'name'); - const name = isEmpty(nameAttribute) - ? Symbol('generated step name') - : nameAttribute; + const name = + typeof nameAttribute === 'undefined' + ? Symbol('generated step name') + : nameAttribute; assert('Step name cannot be a boolean', typeof name !== 'boolean'); assert('Step name cannot be an object', typeof name !== 'object'); diff --git a/tests/integration/step-manager-test.js b/tests/integration/step-manager-test.js index b79bbb71..9a1b10ff 100644 --- a/tests/integration/step-manager-test.js +++ b/tests/integration/step-manager-test.js @@ -840,4 +840,44 @@ module('step-manager', function(hooks) { .exists('The initial step is still visible'); }); }); + + module('edge cases', function() { + test('it handles steps with falsy names', async function(assert) { + await render(hbs` + {{#step-manager initialStep='' as |w|}} + {{#w.step name=''}} +
+ {{/w.step}} + + {{#w.step name=0}} + + {{/w.step}} + + + + + {{/step-manager}} + `); + + assert + .dom('[data-test-empty-string]') + .exists('Can start on a step with a falsy name'); + + await click('[data-test-next]'); + + assert + .dom('[data-test-zero]') + .exists('Can transition to a next step with a falsy name'); + + await click('[data-test-previous]'); + + assert + .dom('[data-test-empty-string]') + .exists('Can transition to a previous step with a falsy name'); + }); + }); });